diff options
| author | Ilya Dryomov <idryomov@gmail.com> | 2017-10-12 06:35:19 -0400 |
|---|---|---|
| committer | Ilya Dryomov <idryomov@gmail.com> | 2017-11-13 06:11:41 -0500 |
| commit | 1de797bb248d2276337139fecaffbd3bbc0f736d (patch) | |
| tree | c8ee0b09435866327547ce07b5603346588e079a | |
| parent | bb0581f01c38ff525295fc6128bc3a49202dabae (diff) | |
rbd: fix and simplify rbd_ioctl_set_ro()
->open_count/-EBUSY check is bogus and wrong: when an open device is
set read-only, blkdev_write_iter() refuses further writes with -EPERM.
This is standard behaviour and all other block devices allow this.
set_disk_ro() call is also problematic: we affect the entire device
when called on a single partition.
All rbd_ioctl_set_ro() needs to do is refuse ro -> rw transition for
mapped snapshots. Everything else can be handled by generic code.
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
| -rw-r--r-- | drivers/block/rbd.c | 34 |
1 files changed, 6 insertions, 28 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index adc877dfef5c..fb7cb38a6d83 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c | |||
| @@ -640,46 +640,24 @@ static void rbd_release(struct gendisk *disk, fmode_t mode) | |||
| 640 | 640 | ||
| 641 | static int rbd_ioctl_set_ro(struct rbd_device *rbd_dev, unsigned long arg) | 641 | static int rbd_ioctl_set_ro(struct rbd_device *rbd_dev, unsigned long arg) |
| 642 | { | 642 | { |
| 643 | int ret = 0; | 643 | int ro; |
| 644 | int val; | ||
| 645 | bool ro; | ||
| 646 | bool ro_changed = false; | ||
| 647 | 644 | ||
| 648 | /* get_user() may sleep, so call it before taking rbd_dev->lock */ | 645 | if (get_user(ro, (int __user *)arg)) |
| 649 | if (get_user(val, (int __user *)(arg))) | ||
| 650 | return -EFAULT; | 646 | return -EFAULT; |
| 651 | 647 | ||
| 652 | ro = val ? true : false; | 648 | /* Snapshots can't be marked read-write */ |
| 653 | /* Snapshot doesn't allow to write*/ | ||
| 654 | if (rbd_dev->spec->snap_id != CEPH_NOSNAP && !ro) | 649 | if (rbd_dev->spec->snap_id != CEPH_NOSNAP && !ro) |
| 655 | return -EROFS; | 650 | return -EROFS; |
| 656 | 651 | ||
| 657 | spin_lock_irq(&rbd_dev->lock); | 652 | /* Let blkdev_roset() handle it */ |
| 658 | /* prevent others open this device */ | 653 | return -ENOTTY; |
| 659 | if (rbd_dev->open_count > 1) { | ||
| 660 | ret = -EBUSY; | ||
| 661 | goto out; | ||
| 662 | } | ||
| 663 | |||
| 664 | if (rbd_dev->mapping.read_only != ro) { | ||
| 665 | rbd_dev->mapping.read_only = ro; | ||
| 666 | ro_changed = true; | ||
| 667 | } | ||
| 668 | |||
| 669 | out: | ||
| 670 | spin_unlock_irq(&rbd_dev->lock); | ||
| 671 | /* set_disk_ro() may sleep, so call it after releasing rbd_dev->lock */ | ||
| 672 | if (ret == 0 && ro_changed) | ||
| 673 | set_disk_ro(rbd_dev->disk, ro ? 1 : 0); | ||
| 674 | |||
| 675 | return ret; | ||
| 676 | } | 654 | } |
| 677 | 655 | ||
| 678 | static int rbd_ioctl(struct block_device *bdev, fmode_t mode, | 656 | static int rbd_ioctl(struct block_device *bdev, fmode_t mode, |
| 679 | unsigned int cmd, unsigned long arg) | 657 | unsigned int cmd, unsigned long arg) |
| 680 | { | 658 | { |
| 681 | struct rbd_device *rbd_dev = bdev->bd_disk->private_data; | 659 | struct rbd_device *rbd_dev = bdev->bd_disk->private_data; |
| 682 | int ret = 0; | 660 | int ret; |
| 683 | 661 | ||
| 684 | switch (cmd) { | 662 | switch (cmd) { |
| 685 | case BLKROSET: | 663 | case BLKROSET: |
