[PATCH] sem2mutex: blockdev #2
[deliverable/linux.git] / drivers / s390 / block / dasd_ioctl.c
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 */
12 #include <linux/config.h>
13 #include <linux/interrupt.h>
14 #include <linux/major.h>
15 #include <linux/fs.h>
16 #include <linux/blkpg.h>
17
18 #include <asm/ccwdev.h>
19 #include <asm/uaccess.h>
20
21 /* This is ugly... */
22 #define PRINTK_HEADER "dasd_ioctl:"
23
24 #include "dasd_int.h"
25
26 /*
27 * SECTION: ioctl functions.
28 */
29 static struct list_head dasd_ioctl_list = LIST_HEAD_INIT(dasd_ioctl_list);
30
31 /*
32 * Find the ioctl with number no.
33 */
34 static struct dasd_ioctl *
35 dasd_find_ioctl(int no)
36 {
37 struct dasd_ioctl *ioctl;
38
39 list_for_each_entry (ioctl, &dasd_ioctl_list, list)
40 if (ioctl->no == no)
41 return ioctl;
42 return NULL;
43 }
44
45 /*
46 * Register ioctl with number no.
47 */
48 int
49 dasd_ioctl_no_register(struct module *owner, int no, dasd_ioctl_fn_t handler)
50 {
51 struct dasd_ioctl *new;
52 if (dasd_find_ioctl(no))
53 return -EBUSY;
54 new = kmalloc(sizeof (struct dasd_ioctl), GFP_KERNEL);
55 if (new == NULL)
56 return -ENOMEM;
57 new->owner = owner;
58 new->no = no;
59 new->handler = handler;
60 list_add(&new->list, &dasd_ioctl_list);
61 return 0;
62 }
63
64 /*
65 * Deregister ioctl with number no.
66 */
67 int
68 dasd_ioctl_no_unregister(struct module *owner, int no, dasd_ioctl_fn_t handler)
69 {
70 struct dasd_ioctl *old = dasd_find_ioctl(no);
71 if (old == NULL)
72 return -ENOENT;
73 if (old->no != no || old->handler != handler || owner != old->owner)
74 return -EINVAL;
75 list_del(&old->list);
76 kfree(old);
77 return 0;
78 }
79
80 int
81 dasd_ioctl(struct inode *inp, struct file *filp,
82 unsigned int no, unsigned long data)
83 {
84 struct block_device *bdev = inp->i_bdev;
85 struct dasd_device *device = bdev->bd_disk->private_data;
86 struct dasd_ioctl *ioctl;
87 const char *dir;
88 int rc;
89
90 if ((_IOC_DIR(no) != _IOC_NONE) && (data == 0)) {
91 PRINT_DEBUG("empty data ptr");
92 return -EINVAL;
93 }
94 dir = _IOC_DIR (no) == _IOC_NONE ? "0" :
95 _IOC_DIR (no) == _IOC_READ ? "r" :
96 _IOC_DIR (no) == _IOC_WRITE ? "w" :
97 _IOC_DIR (no) == (_IOC_READ | _IOC_WRITE) ? "rw" : "u";
98 DBF_DEV_EVENT(DBF_DEBUG, device,
99 "ioctl 0x%08x %s'0x%x'%d(%d) with data %8lx", no,
100 dir, _IOC_TYPE(no), _IOC_NR(no), _IOC_SIZE(no), data);
101 /* Search for ioctl no in the ioctl list. */
102 list_for_each_entry(ioctl, &dasd_ioctl_list, list) {
103 if (ioctl->no == no) {
104 /* Found a matching ioctl. Call it. */
105 if (!try_module_get(ioctl->owner))
106 continue;
107 rc = ioctl->handler(bdev, no, data);
108 module_put(ioctl->owner);
109 return rc;
110 }
111 }
112 /* No ioctl with number no. */
113 DBF_DEV_EVENT(DBF_INFO, device,
114 "unknown ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx", no,
115 dir, _IOC_TYPE(no), _IOC_NR(no), _IOC_SIZE(no), data);
116 return -EINVAL;
117 }
118
119 long
120 dasd_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
121 {
122 int rval;
123
124 lock_kernel();
125 rval = dasd_ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
126 unlock_kernel();
127
128 return (rval == -EINVAL) ? -ENOIOCTLCMD : rval;
129 }
130
131 static int
132 dasd_ioctl_api_version(struct block_device *bdev, int no, long args)
133 {
134 int ver = DASD_API_VERSION;
135 return put_user(ver, (int __user *) args);
136 }
137
138 /*
139 * Enable device.
140 * used by dasdfmt after BIODASDDISABLE to retrigger blocksize detection
141 */
142 static int
143 dasd_ioctl_enable(struct block_device *bdev, int no, long args)
144 {
145 struct dasd_device *device;
146
147 if (!capable(CAP_SYS_ADMIN))
148 return -EACCES;
149 device = bdev->bd_disk->private_data;
150 if (device == NULL)
151 return -ENODEV;
152 dasd_enable_device(device);
153 /* Formatting the dasd device can change the capacity. */
154 mutex_lock(&bdev->bd_mutex);
155 i_size_write(bdev->bd_inode, (loff_t)get_capacity(device->gdp) << 9);
156 mutex_unlock(&bdev->bd_mutex);
157 return 0;
158 }
159
160 /*
161 * Disable device.
162 * Used by dasdfmt. Disable I/O operations but allow ioctls.
163 */
164 static int
165 dasd_ioctl_disable(struct block_device *bdev, int no, long args)
166 {
167 struct dasd_device *device;
168
169 if (!capable(CAP_SYS_ADMIN))
170 return -EACCES;
171 device = bdev->bd_disk->private_data;
172 if (device == NULL)
173 return -ENODEV;
174 /*
175 * Man this is sick. We don't do a real disable but only downgrade
176 * the device to DASD_STATE_BASIC. The reason is that dasdfmt uses
177 * BIODASDDISABLE to disable accesses to the device via the block
178 * device layer but it still wants to do i/o on the device by
179 * using the BIODASDFMT ioctl. Therefore the correct state for the
180 * device is DASD_STATE_BASIC that allows to do basic i/o.
181 */
182 dasd_set_target_state(device, DASD_STATE_BASIC);
183 /*
184 * Set i_size to zero, since read, write, etc. check against this
185 * value.
186 */
187 mutex_lock(&bdev->bd_mutex);
188 i_size_write(bdev->bd_inode, 0);
189 mutex_unlock(&bdev->bd_mutex);
190 return 0;
191 }
192
193 /*
194 * Quiesce device.
195 */
196 static int
197 dasd_ioctl_quiesce(struct block_device *bdev, int no, long args)
198 {
199 struct dasd_device *device;
200 unsigned long flags;
201
202 if (!capable (CAP_SYS_ADMIN))
203 return -EACCES;
204
205 device = bdev->bd_disk->private_data;
206 if (device == NULL)
207 return -ENODEV;
208
209 DEV_MESSAGE (KERN_DEBUG, device, "%s",
210 "Quiesce IO on device");
211 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
212 device->stopped |= DASD_STOPPED_QUIESCE;
213 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
214 return 0;
215 }
216
217
218 /*
219 * Quiesce device.
220 */
221 static int
222 dasd_ioctl_resume(struct block_device *bdev, int no, long args)
223 {
224 struct dasd_device *device;
225 unsigned long flags;
226
227 if (!capable (CAP_SYS_ADMIN))
228 return -EACCES;
229
230 device = bdev->bd_disk->private_data;
231 if (device == NULL)
232 return -ENODEV;
233
234 DEV_MESSAGE (KERN_DEBUG, device, "%s",
235 "resume IO on device");
236
237 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
238 device->stopped &= ~DASD_STOPPED_QUIESCE;
239 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
240
241 dasd_schedule_bh (device);
242 return 0;
243 }
244
245 /*
246 * performs formatting of _device_ according to _fdata_
247 * Note: The discipline's format_function is assumed to deliver formatting
248 * commands to format a single unit of the device. In terms of the ECKD
249 * devices this means CCWs are generated to format a single track.
250 */
251 static int
252 dasd_format(struct dasd_device * device, struct format_data_t * fdata)
253 {
254 struct dasd_ccw_req *cqr;
255 int rc;
256
257 if (device->discipline->format_device == NULL)
258 return -EPERM;
259
260 if (device->state != DASD_STATE_BASIC) {
261 DEV_MESSAGE(KERN_WARNING, device, "%s",
262 "dasd_format: device is not disabled! ");
263 return -EBUSY;
264 }
265
266 DBF_DEV_EVENT(DBF_NOTICE, device,
267 "formatting units %d to %d (%d B blocks) flags %d",
268 fdata->start_unit,
269 fdata->stop_unit, fdata->blksize, fdata->intensity);
270
271 /* Since dasdfmt keeps the device open after it was disabled,
272 * there still exists an inode for this device.
273 * We must update i_blkbits, otherwise we might get errors when
274 * enabling the device later.
275 */
276 if (fdata->start_unit == 0) {
277 struct block_device *bdev = bdget_disk(device->gdp, 0);
278 bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize);
279 bdput(bdev);
280 }
281
282 while (fdata->start_unit <= fdata->stop_unit) {
283 cqr = device->discipline->format_device(device, fdata);
284 if (IS_ERR(cqr))
285 return PTR_ERR(cqr);
286 rc = dasd_sleep_on_interruptible(cqr);
287 dasd_sfree_request(cqr, cqr->device);
288 if (rc) {
289 if (rc != -ERESTARTSYS)
290 DEV_MESSAGE(KERN_ERR, device,
291 " Formatting of unit %d failed "
292 "with rc = %d",
293 fdata->start_unit, rc);
294 return rc;
295 }
296 fdata->start_unit++;
297 }
298 return 0;
299 }
300
301 /*
302 * Format device.
303 */
304 static int
305 dasd_ioctl_format(struct block_device *bdev, int no, long args)
306 {
307 struct dasd_device *device;
308 struct format_data_t fdata;
309
310 if (!capable(CAP_SYS_ADMIN))
311 return -EACCES;
312 if (!args)
313 return -EINVAL;
314 /* fdata == NULL is no longer a valid arg to dasd_format ! */
315 device = bdev->bd_disk->private_data;
316
317 if (device == NULL)
318 return -ENODEV;
319
320 if (device->features & DASD_FEATURE_READONLY)
321 return -EROFS;
322 if (copy_from_user(&fdata, (void __user *) args,
323 sizeof (struct format_data_t)))
324 return -EFAULT;
325 if (bdev != bdev->bd_contains) {
326 DEV_MESSAGE(KERN_WARNING, device, "%s",
327 "Cannot low-level format a partition");
328 return -EINVAL;
329 }
330 return dasd_format(device, &fdata);
331 }
332
333 #ifdef CONFIG_DASD_PROFILE
334 /*
335 * Reset device profile information
336 */
337 static int
338 dasd_ioctl_reset_profile(struct block_device *bdev, int no, long args)
339 {
340 struct dasd_device *device;
341
342 if (!capable(CAP_SYS_ADMIN))
343 return -EACCES;
344
345 device = bdev->bd_disk->private_data;
346 if (device == NULL)
347 return -ENODEV;
348
349 memset(&device->profile, 0, sizeof (struct dasd_profile_info_t));
350 return 0;
351 }
352
353 /*
354 * Return device profile information
355 */
356 static int
357 dasd_ioctl_read_profile(struct block_device *bdev, int no, long args)
358 {
359 struct dasd_device *device;
360
361 device = bdev->bd_disk->private_data;
362 if (device == NULL)
363 return -ENODEV;
364
365 if (dasd_profile_level == DASD_PROFILE_OFF)
366 return -EIO;
367
368 if (copy_to_user((long __user *) args, (long *) &device->profile,
369 sizeof (struct dasd_profile_info_t)))
370 return -EFAULT;
371 return 0;
372 }
373 #else
374 static int
375 dasd_ioctl_reset_profile(struct block_device *bdev, int no, long args)
376 {
377 return -ENOSYS;
378 }
379
380 static int
381 dasd_ioctl_read_profile(struct block_device *bdev, int no, long args)
382 {
383 return -ENOSYS;
384 }
385 #endif
386
387 /*
388 * Return dasd information. Used for BIODASDINFO and BIODASDINFO2.
389 */
390 static int
391 dasd_ioctl_information(struct block_device *bdev, int no, long args)
392 {
393 struct dasd_device *device;
394 struct dasd_information2_t *dasd_info;
395 unsigned long flags;
396 int rc;
397 struct ccw_device *cdev;
398
399 device = bdev->bd_disk->private_data;
400 if (device == NULL)
401 return -ENODEV;
402
403 if (!device->discipline->fill_info)
404 return -EINVAL;
405
406 dasd_info = kmalloc(sizeof(struct dasd_information2_t), GFP_KERNEL);
407 if (dasd_info == NULL)
408 return -ENOMEM;
409
410 rc = device->discipline->fill_info(device, dasd_info);
411 if (rc) {
412 kfree(dasd_info);
413 return rc;
414 }
415
416 cdev = device->cdev;
417
418 dasd_info->devno = _ccw_device_get_device_number(device->cdev);
419 dasd_info->schid = _ccw_device_get_subchannel_number(device->cdev);
420 dasd_info->cu_type = cdev->id.cu_type;
421 dasd_info->cu_model = cdev->id.cu_model;
422 dasd_info->dev_type = cdev->id.dev_type;
423 dasd_info->dev_model = cdev->id.dev_model;
424 dasd_info->status = device->state;
425 /*
426 * The open_count is increased for every opener, that includes
427 * the blkdev_get in dasd_scan_partitions.
428 * This must be hidden from user-space.
429 */
430 dasd_info->open_count = atomic_read(&device->open_count);
431 if (!device->bdev)
432 dasd_info->open_count++;
433
434 /*
435 * check if device is really formatted
436 * LDL / CDL was returned by 'fill_info'
437 */
438 if ((device->state < DASD_STATE_READY) ||
439 (dasd_check_blocksize(device->bp_block)))
440 dasd_info->format = DASD_FORMAT_NONE;
441
442 dasd_info->features |=
443 ((device->features & DASD_FEATURE_READONLY) != 0);
444
445 if (device->discipline)
446 memcpy(dasd_info->type, device->discipline->name, 4);
447 else
448 memcpy(dasd_info->type, "none", 4);
449 dasd_info->req_queue_len = 0;
450 dasd_info->chanq_len = 0;
451 if (device->request_queue->request_fn) {
452 struct list_head *l;
453 #ifdef DASD_EXTENDED_PROFILING
454 {
455 struct list_head *l;
456 spin_lock_irqsave(&device->lock, flags);
457 list_for_each(l, &device->request_queue->queue_head)
458 dasd_info->req_queue_len++;
459 spin_unlock_irqrestore(&device->lock, flags);
460 }
461 #endif /* DASD_EXTENDED_PROFILING */
462 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
463 list_for_each(l, &device->ccw_queue)
464 dasd_info->chanq_len++;
465 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
466 flags);
467 }
468
469 rc = 0;
470 if (copy_to_user((long __user *) args, (long *) dasd_info,
471 ((no == (unsigned int) BIODASDINFO2) ?
472 sizeof (struct dasd_information2_t) :
473 sizeof (struct dasd_information_t))))
474 rc = -EFAULT;
475 kfree(dasd_info);
476 return rc;
477 }
478
479 /*
480 * Set read only
481 */
482 static int
483 dasd_ioctl_set_ro(struct block_device *bdev, int no, long args)
484 {
485 struct dasd_device *device;
486 int intval, rc;
487
488 if (!capable(CAP_SYS_ADMIN))
489 return -EACCES;
490 if (bdev != bdev->bd_contains)
491 // ro setting is not allowed for partitions
492 return -EINVAL;
493 if (get_user(intval, (int __user *) args))
494 return -EFAULT;
495 device = bdev->bd_disk->private_data;
496 if (device == NULL)
497 return -ENODEV;
498
499 set_disk_ro(bdev->bd_disk, intval);
500 rc = dasd_set_feature(device->cdev, DASD_FEATURE_READONLY, intval);
501
502 return rc;
503 }
504
505 /*
506 * List of static ioctls.
507 */
508 static struct { int no; dasd_ioctl_fn_t fn; } dasd_ioctls[] =
509 {
510 { BIODASDDISABLE, dasd_ioctl_disable },
511 { BIODASDENABLE, dasd_ioctl_enable },
512 { BIODASDQUIESCE, dasd_ioctl_quiesce },
513 { BIODASDRESUME, dasd_ioctl_resume },
514 { BIODASDFMT, dasd_ioctl_format },
515 { BIODASDINFO, dasd_ioctl_information },
516 { BIODASDINFO2, dasd_ioctl_information },
517 { BIODASDPRRD, dasd_ioctl_read_profile },
518 { BIODASDPRRST, dasd_ioctl_reset_profile },
519 { BLKROSET, dasd_ioctl_set_ro },
520 { DASDAPIVER, dasd_ioctl_api_version },
521 { -1, NULL }
522 };
523
524 int
525 dasd_ioctl_init(void)
526 {
527 int i;
528
529 for (i = 0; dasd_ioctls[i].no != -1; i++)
530 dasd_ioctl_no_register(NULL, dasd_ioctls[i].no,
531 dasd_ioctls[i].fn);
532 return 0;
533
534 }
535
536 void
537 dasd_ioctl_exit(void)
538 {
539 int i;
540
541 for (i = 0; dasd_ioctls[i].no != -1; i++)
542 dasd_ioctl_no_unregister(NULL, dasd_ioctls[i].no,
543 dasd_ioctls[i].fn);
544
545 }
546
547 EXPORT_SYMBOL(dasd_ioctl_no_register);
548 EXPORT_SYMBOL(dasd_ioctl_no_unregister);
This page took 0.11748 seconds and 5 git commands to generate.