rbd: add force close option
authorMike Christie <mchristi@redhat.com>
Thu, 18 Aug 2016 16:38:45 +0000 (18:38 +0200)
committerIlya Dryomov <idryomov@gmail.com>
Wed, 24 Aug 2016 21:49:17 +0000 (23:49 +0200)
This adds a force close option, so we can force the unmapping
of a rbd device that is open. If a path/device is blacklisted, apps
like multipathd can map a new device and then unmap the old one.
The unmapping cleanup would then be handled by the generic hotunplug
code paths in multipahd like is done for iSCSI, FC/FCOE, SAS, etc.

Signed-off-by: Mike Christie <mchristi@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Documentation/ABI/testing/sysfs-bus-rbd
drivers/block/rbd.c

index 6dccbf8..f208ac5 100644 (file)
@@ -6,7 +6,7 @@ Description:
 
 Being used for adding and removing rbd block devices.
 
-Usage: <mon ip addr> <options> <pool name> <rbd image name> [snap name]
+Usage: <mon ip addr> <options> <pool name> <rbd image name> [<snap name>]
 
  $ echo "192.168.0.1 name=admin rbd foo" > /sys/bus/rbd/add
 
@@ -14,9 +14,13 @@ The snapshot name can be "-" or omitted to map the image read/write. A <dev-id>
 will be assigned for any registered block device. If snapshot is used, it will
 be mapped read-only.
 
-Removal of a device:
+Usage: <dev-id> [force]
 
-  $ echo <dev-id> > /sys/bus/rbd/remove
+ $ echo 2 > /sys/bus/rbd/remove
+
+Optional "force" argument which when passed will wait for running requests and
+then unmap the image. Requests sent to the driver after initiating the removal
+will be failed.  (August 2016, since 4.9.)
 
 What:          /sys/bus/rbd/add_single_major
 Date:          December 2013
index 8ff2dc8..35fc1da 100644 (file)
@@ -6347,18 +6347,26 @@ static ssize_t do_rbd_remove(struct bus_type *bus,
        struct rbd_device *rbd_dev = NULL;
        struct list_head *tmp;
        int dev_id;
-       unsigned long ul;
+       char opt_buf[6];
        bool already = false;
+       bool force = false;
        int ret;
 
-       ret = kstrtoul(buf, 10, &ul);
-       if (ret)
-               return ret;
-
-       /* convert to int; abort if we lost anything in the conversion */
-       dev_id = (int)ul;
-       if (dev_id != ul)
+       dev_id = -1;
+       opt_buf[0] = '\0';
+       sscanf(buf, "%d %5s", &dev_id, opt_buf);
+       if (dev_id < 0) {
+               pr_err("dev_id out of range\n");
                return -EINVAL;
+       }
+       if (opt_buf[0] != '\0') {
+               if (!strcmp(opt_buf, "force")) {
+                       force = true;
+               } else {
+                       pr_err("bad remove option at '%s'\n", opt_buf);
+                       return -EINVAL;
+               }
+       }
 
        ret = -ENOENT;
        spin_lock(&rbd_dev_list_lock);
@@ -6371,7 +6379,7 @@ static ssize_t do_rbd_remove(struct bus_type *bus,
        }
        if (!ret) {
                spin_lock_irq(&rbd_dev->lock);
-               if (rbd_dev->open_count)
+               if (rbd_dev->open_count && !force)
                        ret = -EBUSY;
                else
                        already = test_and_set_bit(RBD_DEV_FLAG_REMOVING,
@@ -6382,6 +6390,15 @@ static ssize_t do_rbd_remove(struct bus_type *bus,
        if (ret < 0 || already)
                return ret;
 
+       if (force) {
+               /*
+                * Prevent new IO from being queued and wait for existing
+                * IO to complete/fail.
+                */
+               blk_mq_freeze_queue(rbd_dev->disk->queue);
+               blk_set_queue_dying(rbd_dev->disk->queue);
+       }
+
        down_write(&rbd_dev->lock_rwsem);
        if (__rbd_is_lock_owner(rbd_dev))
                rbd_unlock(rbd_dev);