Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * File...........: linux/drivers/s390/block/dasd_ioctl.c | |
3 | * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> | |
4 | * Horst Hummel <Horst.Hummel@de.ibm.com> | |
5 | * Carsten Otte <Cotte@de.ibm.com> | |
6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | |
7 | * Bugreports.to..: <Linux390@de.ibm.com> | |
8 | * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 | |
9 | * | |
10 | * i/o controls for the dasd driver. | |
11 | */ | |
1da177e4 LT |
12 | #include <linux/interrupt.h> |
13 | #include <linux/major.h> | |
14 | #include <linux/fs.h> | |
15 | #include <linux/blkpg.h> | |
16 | ||
17 | #include <asm/ccwdev.h> | |
8b2eb664 | 18 | #include <asm/cmb.h> |
1da177e4 LT |
19 | #include <asm/uaccess.h> |
20 | ||
21 | /* This is ugly... */ | |
22 | #define PRINTK_HEADER "dasd_ioctl:" | |
23 | ||
24 | #include "dasd_int.h" | |
25 | ||
1da177e4 | 26 | |
1da177e4 | 27 | static int |
13c6204f | 28 | dasd_ioctl_api_version(void __user *argp) |
1da177e4 LT |
29 | { |
30 | int ver = DASD_API_VERSION; | |
b5029622 | 31 | return put_user(ver, (int __user *)argp); |
1da177e4 LT |
32 | } |
33 | ||
34 | /* | |
35 | * Enable device. | |
36 | * used by dasdfmt after BIODASDDISABLE to retrigger blocksize detection | |
37 | */ | |
38 | static int | |
13c6204f | 39 | dasd_ioctl_enable(struct block_device *bdev) |
1da177e4 | 40 | { |
8e09f215 | 41 | struct dasd_block *block = bdev->bd_disk->private_data; |
1da177e4 LT |
42 | |
43 | if (!capable(CAP_SYS_ADMIN)) | |
44 | return -EACCES; | |
13c6204f | 45 | |
8e09f215 | 46 | dasd_enable_device(block->base); |
1da177e4 | 47 | /* Formatting the dasd device can change the capacity. */ |
c039e313 | 48 | mutex_lock(&bdev->bd_mutex); |
8e09f215 | 49 | i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9); |
c039e313 | 50 | mutex_unlock(&bdev->bd_mutex); |
1da177e4 LT |
51 | return 0; |
52 | } | |
53 | ||
54 | /* | |
55 | * Disable device. | |
56 | * Used by dasdfmt. Disable I/O operations but allow ioctls. | |
57 | */ | |
58 | static int | |
13c6204f | 59 | dasd_ioctl_disable(struct block_device *bdev) |
1da177e4 | 60 | { |
8e09f215 | 61 | struct dasd_block *block = bdev->bd_disk->private_data; |
1da177e4 LT |
62 | |
63 | if (!capable(CAP_SYS_ADMIN)) | |
64 | return -EACCES; | |
13c6204f | 65 | |
1da177e4 LT |
66 | /* |
67 | * Man this is sick. We don't do a real disable but only downgrade | |
68 | * the device to DASD_STATE_BASIC. The reason is that dasdfmt uses | |
69 | * BIODASDDISABLE to disable accesses to the device via the block | |
70 | * device layer but it still wants to do i/o on the device by | |
71 | * using the BIODASDFMT ioctl. Therefore the correct state for the | |
72 | * device is DASD_STATE_BASIC that allows to do basic i/o. | |
73 | */ | |
8e09f215 | 74 | dasd_set_target_state(block->base, DASD_STATE_BASIC); |
1da177e4 LT |
75 | /* |
76 | * Set i_size to zero, since read, write, etc. check against this | |
77 | * value. | |
78 | */ | |
c039e313 | 79 | mutex_lock(&bdev->bd_mutex); |
1da177e4 | 80 | i_size_write(bdev->bd_inode, 0); |
c039e313 | 81 | mutex_unlock(&bdev->bd_mutex); |
1da177e4 LT |
82 | return 0; |
83 | } | |
84 | ||
85 | /* | |
86 | * Quiesce device. | |
87 | */ | |
8e09f215 | 88 | static int dasd_ioctl_quiesce(struct dasd_block *block) |
1da177e4 | 89 | { |
1da177e4 | 90 | unsigned long flags; |
8e09f215 | 91 | struct dasd_device *base; |
138c014d | 92 | |
8e09f215 | 93 | base = block->base; |
1da177e4 LT |
94 | if (!capable (CAP_SYS_ADMIN)) |
95 | return -EACCES; | |
138c014d | 96 | |
8e09f215 SW |
97 | DEV_MESSAGE(KERN_DEBUG, base, "%s", "Quiesce IO on device"); |
98 | spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); | |
99 | base->stopped |= DASD_STOPPED_QUIESCE; | |
100 | spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); | |
1da177e4 LT |
101 | return 0; |
102 | } | |
103 | ||
104 | ||
105 | /* | |
106 | * Quiesce device. | |
107 | */ | |
8e09f215 | 108 | static int dasd_ioctl_resume(struct dasd_block *block) |
1da177e4 | 109 | { |
1da177e4 | 110 | unsigned long flags; |
8e09f215 | 111 | struct dasd_device *base; |
138c014d | 112 | |
8e09f215 | 113 | base = block->base; |
138c014d | 114 | if (!capable (CAP_SYS_ADMIN)) |
1da177e4 LT |
115 | return -EACCES; |
116 | ||
8e09f215 SW |
117 | DEV_MESSAGE(KERN_DEBUG, base, "%s", "resume IO on device"); |
118 | spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); | |
119 | base->stopped &= ~DASD_STOPPED_QUIESCE; | |
120 | spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); | |
1da177e4 | 121 | |
8e09f215 | 122 | dasd_schedule_block_bh(block); |
1da177e4 LT |
123 | return 0; |
124 | } | |
125 | ||
126 | /* | |
127 | * performs formatting of _device_ according to _fdata_ | |
128 | * Note: The discipline's format_function is assumed to deliver formatting | |
129 | * commands to format a single unit of the device. In terms of the ECKD | |
130 | * devices this means CCWs are generated to format a single track. | |
131 | */ | |
8e09f215 | 132 | static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) |
1da177e4 LT |
133 | { |
134 | struct dasd_ccw_req *cqr; | |
8e09f215 | 135 | struct dasd_device *base; |
1da177e4 LT |
136 | int rc; |
137 | ||
8e09f215 SW |
138 | base = block->base; |
139 | if (base->discipline->format_device == NULL) | |
1da177e4 LT |
140 | return -EPERM; |
141 | ||
8e09f215 SW |
142 | if (base->state != DASD_STATE_BASIC) { |
143 | DEV_MESSAGE(KERN_WARNING, base, "%s", | |
1da177e4 LT |
144 | "dasd_format: device is not disabled! "); |
145 | return -EBUSY; | |
146 | } | |
147 | ||
8e09f215 | 148 | DBF_DEV_EVENT(DBF_NOTICE, base, |
1da177e4 LT |
149 | "formatting units %d to %d (%d B blocks) flags %d", |
150 | fdata->start_unit, | |
151 | fdata->stop_unit, fdata->blksize, fdata->intensity); | |
152 | ||
153 | /* Since dasdfmt keeps the device open after it was disabled, | |
154 | * there still exists an inode for this device. | |
155 | * We must update i_blkbits, otherwise we might get errors when | |
156 | * enabling the device later. | |
157 | */ | |
158 | if (fdata->start_unit == 0) { | |
8e09f215 | 159 | struct block_device *bdev = bdget_disk(block->gdp, 0); |
1da177e4 LT |
160 | bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize); |
161 | bdput(bdev); | |
162 | } | |
163 | ||
164 | while (fdata->start_unit <= fdata->stop_unit) { | |
8e09f215 | 165 | cqr = base->discipline->format_device(base, fdata); |
1da177e4 LT |
166 | if (IS_ERR(cqr)) |
167 | return PTR_ERR(cqr); | |
168 | rc = dasd_sleep_on_interruptible(cqr); | |
8e09f215 | 169 | dasd_sfree_request(cqr, cqr->memdev); |
1da177e4 LT |
170 | if (rc) { |
171 | if (rc != -ERESTARTSYS) | |
8e09f215 | 172 | DEV_MESSAGE(KERN_ERR, base, |
1da177e4 LT |
173 | " Formatting of unit %d failed " |
174 | "with rc = %d", | |
175 | fdata->start_unit, rc); | |
176 | return rc; | |
177 | } | |
178 | fdata->start_unit++; | |
179 | } | |
180 | return 0; | |
181 | } | |
182 | ||
183 | /* | |
184 | * Format device. | |
185 | */ | |
186 | static int | |
13c6204f | 187 | dasd_ioctl_format(struct block_device *bdev, void __user *argp) |
1da177e4 | 188 | { |
8e09f215 | 189 | struct dasd_block *block = bdev->bd_disk->private_data; |
1da177e4 LT |
190 | struct format_data_t fdata; |
191 | ||
192 | if (!capable(CAP_SYS_ADMIN)) | |
193 | return -EACCES; | |
13c6204f | 194 | if (!argp) |
1da177e4 | 195 | return -EINVAL; |
f24acd45 | 196 | |
8e09f215 | 197 | if (block->base->features & DASD_FEATURE_READONLY) |
1da177e4 | 198 | return -EROFS; |
13c6204f | 199 | if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) |
1da177e4 LT |
200 | return -EFAULT; |
201 | if (bdev != bdev->bd_contains) { | |
8e09f215 | 202 | DEV_MESSAGE(KERN_WARNING, block->base, "%s", |
1da177e4 LT |
203 | "Cannot low-level format a partition"); |
204 | return -EINVAL; | |
205 | } | |
8e09f215 | 206 | return dasd_format(block, &fdata); |
1da177e4 LT |
207 | } |
208 | ||
209 | #ifdef CONFIG_DASD_PROFILE | |
210 | /* | |
211 | * Reset device profile information | |
212 | */ | |
8e09f215 | 213 | static int dasd_ioctl_reset_profile(struct dasd_block *block) |
1da177e4 | 214 | { |
8e09f215 | 215 | memset(&block->profile, 0, sizeof(struct dasd_profile_info_t)); |
1da177e4 LT |
216 | return 0; |
217 | } | |
218 | ||
219 | /* | |
220 | * Return device profile information | |
221 | */ | |
8e09f215 | 222 | static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) |
1da177e4 | 223 | { |
9a7af289 HH |
224 | if (dasd_profile_level == DASD_PROFILE_OFF) |
225 | return -EIO; | |
8e09f215 SW |
226 | if (copy_to_user(argp, &block->profile, |
227 | sizeof(struct dasd_profile_info_t))) | |
1da177e4 LT |
228 | return -EFAULT; |
229 | return 0; | |
230 | } | |
231 | #else | |
8e09f215 | 232 | static int dasd_ioctl_reset_profile(struct dasd_block *block) |
1da177e4 LT |
233 | { |
234 | return -ENOSYS; | |
235 | } | |
236 | ||
8e09f215 | 237 | static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) |
1da177e4 LT |
238 | { |
239 | return -ENOSYS; | |
240 | } | |
241 | #endif | |
242 | ||
243 | /* | |
244 | * Return dasd information. Used for BIODASDINFO and BIODASDINFO2. | |
245 | */ | |
8e09f215 SW |
246 | static int dasd_ioctl_information(struct dasd_block *block, |
247 | unsigned int cmd, void __user *argp) | |
1da177e4 | 248 | { |
1da177e4 LT |
249 | struct dasd_information2_t *dasd_info; |
250 | unsigned long flags; | |
c6eb7b77 | 251 | int rc; |
8e09f215 | 252 | struct dasd_device *base; |
1da177e4 | 253 | struct ccw_device *cdev; |
9a92fe48 | 254 | struct ccw_dev_id dev_id; |
1da177e4 | 255 | |
8e09f215 SW |
256 | base = block->base; |
257 | if (!base->discipline->fill_info) | |
1da177e4 LT |
258 | return -EINVAL; |
259 | ||
554a826e | 260 | dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL); |
1da177e4 LT |
261 | if (dasd_info == NULL) |
262 | return -ENOMEM; | |
263 | ||
8e09f215 | 264 | rc = base->discipline->fill_info(base, dasd_info); |
1da177e4 LT |
265 | if (rc) { |
266 | kfree(dasd_info); | |
267 | return rc; | |
268 | } | |
269 | ||
8e09f215 | 270 | cdev = base->cdev; |
9a92fe48 | 271 | ccw_device_get_id(cdev, &dev_id); |
1da177e4 | 272 | |
9a92fe48 | 273 | dasd_info->devno = dev_id.devno; |
8e09f215 | 274 | dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev); |
1da177e4 LT |
275 | dasd_info->cu_type = cdev->id.cu_type; |
276 | dasd_info->cu_model = cdev->id.cu_model; | |
277 | dasd_info->dev_type = cdev->id.dev_type; | |
278 | dasd_info->dev_model = cdev->id.dev_model; | |
8e09f215 | 279 | dasd_info->status = base->state; |
57467195 HH |
280 | /* |
281 | * The open_count is increased for every opener, that includes | |
282 | * the blkdev_get in dasd_scan_partitions. | |
283 | * This must be hidden from user-space. | |
284 | */ | |
8e09f215 SW |
285 | dasd_info->open_count = atomic_read(&block->open_count); |
286 | if (!block->bdev) | |
57467195 | 287 | dasd_info->open_count++; |
138c014d | 288 | |
1da177e4 LT |
289 | /* |
290 | * check if device is really formatted | |
291 | * LDL / CDL was returned by 'fill_info' | |
292 | */ | |
8e09f215 SW |
293 | if ((base->state < DASD_STATE_READY) || |
294 | (dasd_check_blocksize(block->bp_block))) | |
1da177e4 | 295 | dasd_info->format = DASD_FORMAT_NONE; |
f24acd45 | 296 | |
c6eb7b77 | 297 | dasd_info->features |= |
8e09f215 | 298 | ((base->features & DASD_FEATURE_READONLY) != 0); |
1da177e4 | 299 | |
8e09f215 SW |
300 | if (base->discipline) |
301 | memcpy(dasd_info->type, base->discipline->name, 4); | |
1da177e4 LT |
302 | else |
303 | memcpy(dasd_info->type, "none", 4); | |
554a826e | 304 | |
8e09f215 | 305 | if (block->request_queue->request_fn) { |
1da177e4 LT |
306 | struct list_head *l; |
307 | #ifdef DASD_EXTENDED_PROFILING | |
308 | { | |
309 | struct list_head *l; | |
8e09f215 SW |
310 | spin_lock_irqsave(&block->lock, flags); |
311 | list_for_each(l, &block->request_queue->queue_head) | |
1da177e4 | 312 | dasd_info->req_queue_len++; |
8e09f215 | 313 | spin_unlock_irqrestore(&block->lock, flags); |
1da177e4 LT |
314 | } |
315 | #endif /* DASD_EXTENDED_PROFILING */ | |
8e09f215 SW |
316 | spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); |
317 | list_for_each(l, &base->ccw_queue) | |
1da177e4 | 318 | dasd_info->chanq_len++; |
8e09f215 | 319 | spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), |
1da177e4 LT |
320 | flags); |
321 | } | |
322 | ||
323 | rc = 0; | |
13c6204f CH |
324 | if (copy_to_user(argp, dasd_info, |
325 | ((cmd == (unsigned int) BIODASDINFO2) ? | |
8e09f215 SW |
326 | sizeof(struct dasd_information2_t) : |
327 | sizeof(struct dasd_information_t)))) | |
1da177e4 LT |
328 | rc = -EFAULT; |
329 | kfree(dasd_info); | |
330 | return rc; | |
331 | } | |
332 | ||
333 | /* | |
334 | * Set read only | |
335 | */ | |
336 | static int | |
13c6204f | 337 | dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) |
1da177e4 | 338 | { |
8e09f215 | 339 | struct dasd_block *block = bdev->bd_disk->private_data; |
13c6204f | 340 | int intval; |
1da177e4 LT |
341 | |
342 | if (!capable(CAP_SYS_ADMIN)) | |
343 | return -EACCES; | |
344 | if (bdev != bdev->bd_contains) | |
345 | // ro setting is not allowed for partitions | |
346 | return -EINVAL; | |
d2c993d8 | 347 | if (get_user(intval, (int __user *)argp)) |
1da177e4 | 348 | return -EFAULT; |
f24acd45 | 349 | |
1da177e4 | 350 | set_disk_ro(bdev->bd_disk, intval); |
8e09f215 | 351 | return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval); |
1da177e4 LT |
352 | } |
353 | ||
8e09f215 | 354 | static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd, |
8b2eb664 CH |
355 | unsigned long arg) |
356 | { | |
357 | struct cmbdata __user *argp = (void __user *) arg; | |
358 | size_t size = _IOC_SIZE(cmd); | |
359 | struct cmbdata data; | |
360 | int ret; | |
361 | ||
8e09f215 | 362 | ret = cmf_readall(block->base->cdev, &data); |
8b2eb664 CH |
363 | if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp)))) |
364 | return -EFAULT; | |
365 | return ret; | |
366 | } | |
367 | ||
0000d031 HC |
368 | static int |
369 | dasd_do_ioctl(struct block_device *bdev, fmode_t mode, | |
370 | unsigned int cmd, unsigned long arg) | |
1da177e4 | 371 | { |
8e09f215 | 372 | struct dasd_block *block = bdev->bd_disk->private_data; |
13c6204f | 373 | void __user *argp = (void __user *)arg; |
1da177e4 | 374 | |
8e09f215 | 375 | if (!block) |
13c6204f CH |
376 | return -ENODEV; |
377 | ||
378 | if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) { | |
379 | PRINT_DEBUG("empty data ptr"); | |
380 | return -EINVAL; | |
381 | } | |
1da177e4 | 382 | |
13c6204f CH |
383 | switch (cmd) { |
384 | case BIODASDDISABLE: | |
385 | return dasd_ioctl_disable(bdev); | |
386 | case BIODASDENABLE: | |
387 | return dasd_ioctl_enable(bdev); | |
388 | case BIODASDQUIESCE: | |
8e09f215 | 389 | return dasd_ioctl_quiesce(block); |
13c6204f | 390 | case BIODASDRESUME: |
8e09f215 | 391 | return dasd_ioctl_resume(block); |
13c6204f CH |
392 | case BIODASDFMT: |
393 | return dasd_ioctl_format(bdev, argp); | |
394 | case BIODASDINFO: | |
8e09f215 | 395 | return dasd_ioctl_information(block, cmd, argp); |
13c6204f | 396 | case BIODASDINFO2: |
8e09f215 | 397 | return dasd_ioctl_information(block, cmd, argp); |
13c6204f | 398 | case BIODASDPRRD: |
8e09f215 | 399 | return dasd_ioctl_read_profile(block, argp); |
13c6204f | 400 | case BIODASDPRRST: |
8e09f215 | 401 | return dasd_ioctl_reset_profile(block); |
13c6204f CH |
402 | case BLKROSET: |
403 | return dasd_ioctl_set_ro(bdev, argp); | |
404 | case DASDAPIVER: | |
405 | return dasd_ioctl_api_version(argp); | |
8b2eb664 | 406 | case BIODASDCMFENABLE: |
8e09f215 | 407 | return enable_cmf(block->base->cdev); |
8b2eb664 | 408 | case BIODASDCMFDISABLE: |
8e09f215 | 409 | return disable_cmf(block->base->cdev); |
8b2eb664 | 410 | case BIODASDREADALLCMB: |
8e09f215 | 411 | return dasd_ioctl_readall_cmb(block, cmd, arg); |
13c6204f | 412 | default: |
1107ccfb | 413 | /* if the discipline has an ioctl method try it. */ |
8e09f215 SW |
414 | if (block->base->discipline->ioctl) { |
415 | int rval = block->base->discipline->ioctl(block, cmd, argp); | |
1107ccfb CH |
416 | if (rval != -ENOIOCTLCMD) |
417 | return rval; | |
418 | } | |
419 | ||
13c6204f CH |
420 | return -EINVAL; |
421 | } | |
1da177e4 | 422 | } |
0000d031 HC |
423 | |
424 | int dasd_ioctl(struct block_device *bdev, fmode_t mode, | |
425 | unsigned int cmd, unsigned long arg) | |
426 | { | |
427 | int rc; | |
428 | ||
429 | lock_kernel(); | |
430 | rc = dasd_do_ioctl(bdev, mode, cmd, arg); | |
431 | unlock_kernel(); | |
432 | return rc; | |
433 | } |