aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Weinhuber <wein@de.ibm.com>2011-04-20 04:15:30 -0400
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2011-04-20 04:15:43 -0400
commit65f8da475995f667af5298c644707dbd9d646ca6 (patch)
treebca8597443060e79f09a8843903bbc55b2dece52
parent2f666bcf757cb72549f360ef6da02f03620a48b6 (diff)
[S390] dasd: fix race between open and offline
The dasd_open function uses the private_data pointer of the gendisk to find the dasd_block structure that matches the gendisk. When a DASD device is set offline, we set the private_data pointer of the gendisk to NULL and later remove the dasd_block structure, but there is still a small race window, in which dasd_open could first read a pointer from the private_data field and then try to use it, after the structure has already been freed. To close this race window, we will store a pointer to the dasd_devmap structure of the base device in the private_data field. The devmap entries are not deleted, and we already have proper locking and reference counting in place, so that we can safely get from a devmap pointer to the dasd_device and dasd_block structures of the device. Signed-off-by: Stefan Weinhuber <wein@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--drivers/s390/block/dasd.c40
-rw-r--r--drivers/s390/block/dasd_devmap.c30
-rw-r--r--drivers/s390/block/dasd_genhd.c2
-rw-r--r--drivers/s390/block/dasd_int.h3
-rw-r--r--drivers/s390/block/dasd_ioctl.c128
5 files changed, 143 insertions, 60 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 4d2df2f76ea0..475e603fc584 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -2314,15 +2314,14 @@ static void dasd_flush_request_queue(struct dasd_block *block)
2314 2314
2315static int dasd_open(struct block_device *bdev, fmode_t mode) 2315static int dasd_open(struct block_device *bdev, fmode_t mode)
2316{ 2316{
2317 struct dasd_block *block = bdev->bd_disk->private_data;
2318 struct dasd_device *base; 2317 struct dasd_device *base;
2319 int rc; 2318 int rc;
2320 2319
2321 if (!block) 2320 base = dasd_device_from_gendisk(bdev->bd_disk);
2321 if (!base)
2322 return -ENODEV; 2322 return -ENODEV;
2323 2323
2324 base = block->base; 2324 atomic_inc(&base->block->open_count);
2325 atomic_inc(&block->open_count);
2326 if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) { 2325 if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
2327 rc = -ENODEV; 2326 rc = -ENODEV;
2328 goto unlock; 2327 goto unlock;
@@ -2355,21 +2354,28 @@ static int dasd_open(struct block_device *bdev, fmode_t mode)
2355 goto out; 2354 goto out;
2356 } 2355 }
2357 2356
2357 dasd_put_device(base);
2358 return 0; 2358 return 0;
2359 2359
2360out: 2360out:
2361 module_put(base->discipline->owner); 2361 module_put(base->discipline->owner);
2362unlock: 2362unlock:
2363 atomic_dec(&block->open_count); 2363 atomic_dec(&base->block->open_count);
2364 dasd_put_device(base);
2364 return rc; 2365 return rc;
2365} 2366}
2366 2367
2367static int dasd_release(struct gendisk *disk, fmode_t mode) 2368static int dasd_release(struct gendisk *disk, fmode_t mode)
2368{ 2369{
2369 struct dasd_block *block = disk->private_data; 2370 struct dasd_device *base;
2370 2371
2371 atomic_dec(&block->open_count); 2372 base = dasd_device_from_gendisk(disk);
2372 module_put(block->base->discipline->owner); 2373 if (!base)
2374 return -ENODEV;
2375
2376 atomic_dec(&base->block->open_count);
2377 module_put(base->discipline->owner);
2378 dasd_put_device(base);
2373 return 0; 2379 return 0;
2374} 2380}
2375 2381
@@ -2378,20 +2384,20 @@ static int dasd_release(struct gendisk *disk, fmode_t mode)
2378 */ 2384 */
2379static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) 2385static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
2380{ 2386{
2381 struct dasd_block *block;
2382 struct dasd_device *base; 2387 struct dasd_device *base;
2383 2388
2384 block = bdev->bd_disk->private_data; 2389 base = dasd_device_from_gendisk(bdev->bd_disk);
2385 if (!block) 2390 if (!base)
2386 return -ENODEV; 2391 return -ENODEV;
2387 base = block->base;
2388 2392
2389 if (!base->discipline || 2393 if (!base->discipline ||
2390 !base->discipline->fill_geometry) 2394 !base->discipline->fill_geometry) {
2395 dasd_put_device(base);
2391 return -EINVAL; 2396 return -EINVAL;
2392 2397 }
2393 base->discipline->fill_geometry(block, geo); 2398 base->discipline->fill_geometry(base->block, geo);
2394 geo->start = get_start_sect(bdev) >> block->s2b_shift; 2399 geo->start = get_start_sect(bdev) >> base->block->s2b_shift;
2400 dasd_put_device(base);
2395 return 0; 2401 return 0;
2396} 2402}
2397 2403
@@ -2528,7 +2534,6 @@ void dasd_generic_remove(struct ccw_device *cdev)
2528 dasd_set_target_state(device, DASD_STATE_NEW); 2534 dasd_set_target_state(device, DASD_STATE_NEW);
2529 /* dasd_delete_device destroys the device reference. */ 2535 /* dasd_delete_device destroys the device reference. */
2530 block = device->block; 2536 block = device->block;
2531 device->block = NULL;
2532 dasd_delete_device(device); 2537 dasd_delete_device(device);
2533 /* 2538 /*
2534 * life cycle of block is bound to device, so delete it after 2539 * life cycle of block is bound to device, so delete it after
@@ -2650,7 +2655,6 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
2650 dasd_set_target_state(device, DASD_STATE_NEW); 2655 dasd_set_target_state(device, DASD_STATE_NEW);
2651 /* dasd_delete_device destroys the device reference. */ 2656 /* dasd_delete_device destroys the device reference. */
2652 block = device->block; 2657 block = device->block;
2653 device->block = NULL;
2654 dasd_delete_device(device); 2658 dasd_delete_device(device);
2655 /* 2659 /*
2656 * life cycle of block is bound to device, so delete it after 2660 * life cycle of block is bound to device, so delete it after
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 42e1bf35f689..d71511c7850a 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -674,6 +674,36 @@ dasd_device_from_cdev(struct ccw_device *cdev)
674 return device; 674 return device;
675} 675}
676 676
677void dasd_add_link_to_gendisk(struct gendisk *gdp, struct dasd_device *device)
678{
679 struct dasd_devmap *devmap;
680
681 devmap = dasd_find_busid(dev_name(&device->cdev->dev));
682 if (IS_ERR(devmap))
683 return;
684 spin_lock(&dasd_devmap_lock);
685 gdp->private_data = devmap;
686 spin_unlock(&dasd_devmap_lock);
687}
688
689struct dasd_device *dasd_device_from_gendisk(struct gendisk *gdp)
690{
691 struct dasd_device *device;
692 struct dasd_devmap *devmap;
693
694 if (!gdp->private_data)
695 return NULL;
696 device = NULL;
697 spin_lock(&dasd_devmap_lock);
698 devmap = gdp->private_data;
699 if (devmap && devmap->device) {
700 device = devmap->device;
701 dasd_get_device(device);
702 }
703 spin_unlock(&dasd_devmap_lock);
704 return device;
705}
706
677/* 707/*
678 * SECTION: files in sysfs 708 * SECTION: files in sysfs
679 */ 709 */
diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c
index 5505bc07e1e7..19a1ff03d65e 100644
--- a/drivers/s390/block/dasd_genhd.c
+++ b/drivers/s390/block/dasd_genhd.c
@@ -73,7 +73,7 @@ int dasd_gendisk_alloc(struct dasd_block *block)
73 if (base->features & DASD_FEATURE_READONLY || 73 if (base->features & DASD_FEATURE_READONLY ||
74 test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) 74 test_bit(DASD_FLAG_DEVICE_RO, &base->flags))
75 set_disk_ro(gdp, 1); 75 set_disk_ro(gdp, 1);
76 gdp->private_data = block; 76 dasd_add_link_to_gendisk(gdp, base);
77 gdp->queue = block->request_queue; 77 gdp->queue = block->request_queue;
78 block->gdp = gdp; 78 block->gdp = gdp;
79 set_capacity(block->gdp, 0); 79 set_capacity(block->gdp, 0);
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index df9f6999411d..d1e4f2c1264c 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -686,6 +686,9 @@ struct dasd_device *dasd_device_from_cdev(struct ccw_device *);
686struct dasd_device *dasd_device_from_cdev_locked(struct ccw_device *); 686struct dasd_device *dasd_device_from_cdev_locked(struct ccw_device *);
687struct dasd_device *dasd_device_from_devindex(int); 687struct dasd_device *dasd_device_from_devindex(int);
688 688
689void dasd_add_link_to_gendisk(struct gendisk *, struct dasd_device *);
690struct dasd_device *dasd_device_from_gendisk(struct gendisk *);
691
689int dasd_parse(void); 692int dasd_parse(void);
690int dasd_busid_known(const char *); 693int dasd_busid_known(const char *);
691 694
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index 26075e95b1ba..72261e4c516d 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -42,16 +42,22 @@ dasd_ioctl_api_version(void __user *argp)
42static int 42static int
43dasd_ioctl_enable(struct block_device *bdev) 43dasd_ioctl_enable(struct block_device *bdev)
44{ 44{
45 struct dasd_block *block = bdev->bd_disk->private_data; 45 struct dasd_device *base;
46 46
47 if (!capable(CAP_SYS_ADMIN)) 47 if (!capable(CAP_SYS_ADMIN))
48 return -EACCES; 48 return -EACCES;
49 49
50 dasd_enable_device(block->base); 50 base = dasd_device_from_gendisk(bdev->bd_disk);
51 if (!base)
52 return -ENODEV;
53
54 dasd_enable_device(base);
51 /* Formatting the dasd device can change the capacity. */ 55 /* Formatting the dasd device can change the capacity. */
52 mutex_lock(&bdev->bd_mutex); 56 mutex_lock(&bdev->bd_mutex);
53 i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9); 57 i_size_write(bdev->bd_inode,
58 (loff_t)get_capacity(base->block->gdp) << 9);
54 mutex_unlock(&bdev->bd_mutex); 59 mutex_unlock(&bdev->bd_mutex);
60 dasd_put_device(base);
55 return 0; 61 return 0;
56} 62}
57 63
@@ -62,11 +68,14 @@ dasd_ioctl_enable(struct block_device *bdev)
62static int 68static int
63dasd_ioctl_disable(struct block_device *bdev) 69dasd_ioctl_disable(struct block_device *bdev)
64{ 70{
65 struct dasd_block *block = bdev->bd_disk->private_data; 71 struct dasd_device *base;
66 72
67 if (!capable(CAP_SYS_ADMIN)) 73 if (!capable(CAP_SYS_ADMIN))
68 return -EACCES; 74 return -EACCES;
69 75
76 base = dasd_device_from_gendisk(bdev->bd_disk);
77 if (!base)
78 return -ENODEV;
70 /* 79 /*
71 * Man this is sick. We don't do a real disable but only downgrade 80 * Man this is sick. We don't do a real disable but only downgrade
72 * the device to DASD_STATE_BASIC. The reason is that dasdfmt uses 81 * the device to DASD_STATE_BASIC. The reason is that dasdfmt uses
@@ -75,7 +84,7 @@ dasd_ioctl_disable(struct block_device *bdev)
75 * using the BIODASDFMT ioctl. Therefore the correct state for the 84 * using the BIODASDFMT ioctl. Therefore the correct state for the
76 * device is DASD_STATE_BASIC that allows to do basic i/o. 85 * device is DASD_STATE_BASIC that allows to do basic i/o.
77 */ 86 */
78 dasd_set_target_state(block->base, DASD_STATE_BASIC); 87 dasd_set_target_state(base, DASD_STATE_BASIC);
79 /* 88 /*
80 * Set i_size to zero, since read, write, etc. check against this 89 * Set i_size to zero, since read, write, etc. check against this
81 * value. 90 * value.
@@ -83,6 +92,7 @@ dasd_ioctl_disable(struct block_device *bdev)
83 mutex_lock(&bdev->bd_mutex); 92 mutex_lock(&bdev->bd_mutex);
84 i_size_write(bdev->bd_inode, 0); 93 i_size_write(bdev->bd_inode, 0);
85 mutex_unlock(&bdev->bd_mutex); 94 mutex_unlock(&bdev->bd_mutex);
95 dasd_put_device(base);
86 return 0; 96 return 0;
87} 97}
88 98
@@ -191,26 +201,36 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
191static int 201static int
192dasd_ioctl_format(struct block_device *bdev, void __user *argp) 202dasd_ioctl_format(struct block_device *bdev, void __user *argp)
193{ 203{
194 struct dasd_block *block = bdev->bd_disk->private_data; 204 struct dasd_device *base;
195 struct format_data_t fdata; 205 struct format_data_t fdata;
206 int rc;
196 207
197 if (!capable(CAP_SYS_ADMIN)) 208 if (!capable(CAP_SYS_ADMIN))
198 return -EACCES; 209 return -EACCES;
199 if (!argp) 210 if (!argp)
200 return -EINVAL; 211 return -EINVAL;
201 212 base = dasd_device_from_gendisk(bdev->bd_disk);
202 if (block->base->features & DASD_FEATURE_READONLY || 213 if (!base)
203 test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags)) 214 return -ENODEV;
215 if (base->features & DASD_FEATURE_READONLY ||
216 test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) {
217 dasd_put_device(base);
204 return -EROFS; 218 return -EROFS;
205 if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) 219 }
220 if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) {
221 dasd_put_device(base);
206 return -EFAULT; 222 return -EFAULT;
223 }
207 if (bdev != bdev->bd_contains) { 224 if (bdev != bdev->bd_contains) {
208 pr_warning("%s: The specified DASD is a partition and cannot " 225 pr_warning("%s: The specified DASD is a partition and cannot "
209 "be formatted\n", 226 "be formatted\n",
210 dev_name(&block->base->cdev->dev)); 227 dev_name(&base->cdev->dev));
228 dasd_put_device(base);
211 return -EINVAL; 229 return -EINVAL;
212 } 230 }
213 return dasd_format(block, &fdata); 231 rc = dasd_format(base->block, &fdata);
232 dasd_put_device(base);
233 return rc;
214} 234}
215 235
216#ifdef CONFIG_DASD_PROFILE 236#ifdef CONFIG_DASD_PROFILE
@@ -340,8 +360,8 @@ static int dasd_ioctl_information(struct dasd_block *block,
340static int 360static int
341dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) 361dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
342{ 362{
343 struct dasd_block *block = bdev->bd_disk->private_data; 363 struct dasd_device *base;
344 int intval; 364 int intval, rc;
345 365
346 if (!capable(CAP_SYS_ADMIN)) 366 if (!capable(CAP_SYS_ADMIN))
347 return -EACCES; 367 return -EACCES;
@@ -350,10 +370,17 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
350 return -EINVAL; 370 return -EINVAL;
351 if (get_user(intval, (int __user *)argp)) 371 if (get_user(intval, (int __user *)argp))
352 return -EFAULT; 372 return -EFAULT;
353 if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags)) 373 base = dasd_device_from_gendisk(bdev->bd_disk);
374 if (!base)
375 return -ENODEV;
376 if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) {
377 dasd_put_device(base);
354 return -EROFS; 378 return -EROFS;
379 }
355 set_disk_ro(bdev->bd_disk, intval); 380 set_disk_ro(bdev->bd_disk, intval);
356 return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval); 381 rc = dasd_set_feature(base->cdev, DASD_FEATURE_READONLY, intval);
382 dasd_put_device(base);
383 return rc;
357} 384}
358 385
359static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd, 386static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
@@ -372,59 +399,78 @@ static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
372int dasd_ioctl(struct block_device *bdev, fmode_t mode, 399int dasd_ioctl(struct block_device *bdev, fmode_t mode,
373 unsigned int cmd, unsigned long arg) 400 unsigned int cmd, unsigned long arg)
374{ 401{
375 struct dasd_block *block = bdev->bd_disk->private_data; 402 struct dasd_block *block;
403 struct dasd_device *base;
376 void __user *argp; 404 void __user *argp;
405 int rc;
377 406
378 if (is_compat_task()) 407 if (is_compat_task())
379 argp = compat_ptr(arg); 408 argp = compat_ptr(arg);
380 else 409 else
381 argp = (void __user *)arg; 410 argp = (void __user *)arg;
382 411
383 if (!block)
384 return -ENODEV;
385
386 if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) { 412 if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) {
387 PRINT_DEBUG("empty data ptr"); 413 PRINT_DEBUG("empty data ptr");
388 return -EINVAL; 414 return -EINVAL;
389 } 415 }
390 416
417 base = dasd_device_from_gendisk(bdev->bd_disk);
418 if (!base)
419 return -ENODEV;
420 block = base->block;
421 rc = 0;
391 switch (cmd) { 422 switch (cmd) {
392 case BIODASDDISABLE: 423 case BIODASDDISABLE:
393 return dasd_ioctl_disable(bdev); 424 rc = dasd_ioctl_disable(bdev);
425 break;
394 case BIODASDENABLE: 426 case BIODASDENABLE:
395 return dasd_ioctl_enable(bdev); 427 rc = dasd_ioctl_enable(bdev);
428 break;
396 case BIODASDQUIESCE: 429 case BIODASDQUIESCE:
397 return dasd_ioctl_quiesce(block); 430 rc = dasd_ioctl_quiesce(block);
431 break;
398 case BIODASDRESUME: 432 case BIODASDRESUME:
399 return dasd_ioctl_resume(block); 433 rc = dasd_ioctl_resume(block);
434 break;
400 case BIODASDFMT: 435 case BIODASDFMT:
401 return dasd_ioctl_format(bdev, argp); 436 rc = dasd_ioctl_format(bdev, argp);
437 break;
402 case BIODASDINFO: 438 case BIODASDINFO:
403 return dasd_ioctl_information(block, cmd, argp); 439 rc = dasd_ioctl_information(block, cmd, argp);
440 break;
404 case BIODASDINFO2: 441 case BIODASDINFO2:
405 return dasd_ioctl_information(block, cmd, argp); 442 rc = dasd_ioctl_information(block, cmd, argp);
443 break;
406 case BIODASDPRRD: 444 case BIODASDPRRD:
407 return dasd_ioctl_read_profile(block, argp); 445 rc = dasd_ioctl_read_profile(block, argp);
446 break;
408 case BIODASDPRRST: 447 case BIODASDPRRST:
409 return dasd_ioctl_reset_profile(block); 448 rc = dasd_ioctl_reset_profile(block);
449 break;
410 case BLKROSET: 450 case BLKROSET:
411 return dasd_ioctl_set_ro(bdev, argp); 451 rc = dasd_ioctl_set_ro(bdev, argp);
452 break;
412 case DASDAPIVER: 453 case DASDAPIVER:
413 return dasd_ioctl_api_version(argp); 454 rc = dasd_ioctl_api_version(argp);
455 break;
414 case BIODASDCMFENABLE: 456 case BIODASDCMFENABLE:
415 return enable_cmf(block->base->cdev); 457 rc = enable_cmf(base->cdev);
458 break;
416 case BIODASDCMFDISABLE: 459 case BIODASDCMFDISABLE:
417 return disable_cmf(block->base->cdev); 460 rc = disable_cmf(base->cdev);
461 break;
418 case BIODASDREADALLCMB: 462 case BIODASDREADALLCMB:
419 return dasd_ioctl_readall_cmb(block, cmd, argp); 463 rc = dasd_ioctl_readall_cmb(block, cmd, argp);
464 break;
420 default: 465 default:
421 /* if the discipline has an ioctl method try it. */ 466 /* if the discipline has an ioctl method try it. */
422 if (block->base->discipline->ioctl) { 467 if (base->discipline->ioctl) {
423 int rval = block->base->discipline->ioctl(block, cmd, argp); 468 rc = base->discipline->ioctl(block, cmd, argp);
424 if (rval != -ENOIOCTLCMD) 469 if (rc == -ENOIOCTLCMD)
425 return rval; 470 rc = -EINVAL;
426 } 471 } else
427 472 rc = -EINVAL;
428 return -EINVAL;
429 } 473 }
474 dasd_put_device(base);
475 return rc;
430} 476}