rbd: fix and simplify rbd_ioctl_set_ro()
authorIlya Dryomov <idryomov@gmail.com>
Thu, 12 Oct 2017 10:35:19 +0000 (12:35 +0200)
committerIlya Dryomov <idryomov@gmail.com>
Mon, 13 Nov 2017 11:11:41 +0000 (12:11 +0100)
->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>
drivers/block/rbd.c

index adc877d..fb7cb38 100644 (file)
@@ -640,46 +640,24 @@ static void rbd_release(struct gendisk *disk, fmode_t mode)
 
 static int rbd_ioctl_set_ro(struct rbd_device *rbd_dev, unsigned long arg)
 {
-       int ret = 0;
-       int val;
-       bool ro;
-       bool ro_changed = false;
+       int ro;
 
-       /* get_user() may sleep, so call it before taking rbd_dev->lock */
-       if (get_user(val, (int __user *)(arg)))
+       if (get_user(ro, (int __user *)arg))
                return -EFAULT;
 
-       ro = val ? true : false;
-       /* Snapshot doesn't allow to write*/
+       /* Snapshots can't be marked read-write */
        if (rbd_dev->spec->snap_id != CEPH_NOSNAP && !ro)
                return -EROFS;
 
-       spin_lock_irq(&rbd_dev->lock);
-       /* prevent others open this device */
-       if (rbd_dev->open_count > 1) {
-               ret = -EBUSY;
-               goto out;
-       }
-
-       if (rbd_dev->mapping.read_only != ro) {
-               rbd_dev->mapping.read_only = ro;
-               ro_changed = true;
-       }
-
-out:
-       spin_unlock_irq(&rbd_dev->lock);
-       /* set_disk_ro() may sleep, so call it after releasing rbd_dev->lock */
-       if (ret == 0 && ro_changed)
-               set_disk_ro(rbd_dev->disk, ro ? 1 : 0);
-
-       return ret;
+       /* Let blkdev_roset() handle it */
+       return -ENOTTY;
 }
 
 static int rbd_ioctl(struct block_device *bdev, fmode_t mode,
                        unsigned int cmd, unsigned long arg)
 {
        struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
-       int ret = 0;
+       int ret;
 
        switch (cmd) {
        case BLKROSET: