diff options
author | Mike Christie <mchristi@redhat.com> | 2016-08-18 18:38:45 +0200 |
---|---|---|
committer | Ilya Dryomov <idryomov@gmail.com> | 2016-08-24 23:49:17 +0200 |
commit | 0276dca6c1ecb9a665645ff573e70685a57759af (patch) | |
tree | 36498244b9e8de7e3d51cc494d32d4979ae94191 /drivers/block | |
parent | rbd: add 'config_info' sysfs rbd device attribute (diff) | |
download | linux-0276dca6c1ecb9a665645ff573e70685a57759af.tar.xz linux-0276dca6c1ecb9a665645ff573e70685a57759af.zip |
rbd: add force close option
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>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/rbd.c | 35 |
1 files changed, 26 insertions, 9 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 8ff2dc872008..35fc1da6c83d 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -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); |