| From 02e352287a40bd456eb78df705bf888bc3161d3f Mon Sep 17 00:00:00 2001 |
| From: Tejun Heo <tj@kernel.org> |
| Date: Fri, 29 Apr 2011 10:15:20 +0200 |
| Subject: block: rescan partitions on invalidated devices on -ENOMEDIA |
| too |
| |
| From: Tejun Heo <tj@kernel.org> |
| |
| commit 02e352287a40bd456eb78df705bf888bc3161d3f upstream. |
| |
| __blkdev_get() doesn't rescan partitions if disk->fops->open() fails, |
| which leads to ghost partition devices lingering after medimum removal |
| is known to both the kernel and userland. The behavior also creates a |
| subtle inconsistency where O_NONBLOCK open, which doesn't fail even if |
| there's no medium, clears the ghots partitions, which is exploited to |
| work around the problem from userland. |
| |
| Fix it by updating __blkdev_get() to issue partition rescan after |
| -ENOMEDIA too. |
| |
| This was reported in the following bz. |
| |
| https://bugzilla.kernel.org/show_bug.cgi?id=13029 |
| |
| Stable: 2.6.38 |
| |
| Signed-off-by: Tejun Heo <tj@kernel.org> |
| Reported-by: David Zeuthen <zeuthen@gmail.com> |
| Reported-by: Martin Pitt <martin.pitt@ubuntu.com> |
| Reported-by: Kay Sievers <kay.sievers@vrfy.org> |
| Tested-by: Kay Sievers <kay.sievers@vrfy.org> |
| Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> |
| Signed-off-by: Jens Axboe <jaxboe@fusionio.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| fs/block_dev.c | 27 ++++++++++++++++++--------- |
| 1 file changed, 18 insertions(+), 9 deletions(-) |
| |
| --- a/fs/block_dev.c |
| +++ b/fs/block_dev.c |
| @@ -1211,6 +1211,7 @@ static int __blkdev_get(struct block_dev |
| if (!bdev->bd_part) |
| goto out_clear; |
| |
| + ret = 0; |
| if (disk->fops->open) { |
| ret = disk->fops->open(bdev, mode); |
| if (ret == -ERESTARTSYS) { |
| @@ -1226,9 +1227,18 @@ static int __blkdev_get(struct block_dev |
| mutex_unlock(&bdev->bd_mutex); |
| goto restart; |
| } |
| - if (ret) |
| - goto out_clear; |
| } |
| + /* |
| + * If the device is invalidated, rescan partition |
| + * if open succeeded or failed with -ENOMEDIUM. |
| + * The latter is necessary to prevent ghost |
| + * partitions on a removed medium. |
| + */ |
| + if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM)) |
| + rescan_partitions(disk, bdev); |
| + if (ret) |
| + goto out_clear; |
| + |
| if (!bdev->bd_openers) { |
| bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); |
| bdi = blk_get_backing_dev_info(bdev); |
| @@ -1236,8 +1246,6 @@ static int __blkdev_get(struct block_dev |
| bdi = &default_backing_dev_info; |
| bdev->bd_inode->i_data.backing_dev_info = bdi; |
| } |
| - if (bdev->bd_invalidated) |
| - rescan_partitions(disk, bdev); |
| } else { |
| struct block_device *whole; |
| whole = bdget_disk(disk, 0); |
| @@ -1264,13 +1272,14 @@ static int __blkdev_get(struct block_dev |
| put_disk(disk); |
| disk = NULL; |
| if (bdev->bd_contains == bdev) { |
| - if (bdev->bd_disk->fops->open) { |
| + ret = 0; |
| + if (bdev->bd_disk->fops->open) |
| ret = bdev->bd_disk->fops->open(bdev, mode); |
| - if (ret) |
| - goto out_unlock_bdev; |
| - } |
| - if (bdev->bd_invalidated) |
| + /* the same as first opener case, read comment there */ |
| + if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM)) |
| rescan_partitions(bdev->bd_disk, bdev); |
| + if (ret) |
| + goto out_unlock_bdev; |
| } |
| } |
| bdev->bd_openers++; |