rbd: don't return 0 on unmap if RBD_DEV_FLAG_REMOVING is set
authorIlya Dryomov <idryomov@gmail.com>
Tue, 8 Jan 2019 18:47:38 +0000 (19:47 +0100)
committerIlya Dryomov <idryomov@gmail.com>
Thu, 10 Jan 2019 08:45:09 +0000 (09:45 +0100)
There is a window between when RBD_DEV_FLAG_REMOVING is set and when
the device is removed from rbd_dev_list.  During this window, we set
"already" and return 0.

Returning 0 from write(2) can confuse userspace tools because
0 indicates that nothing was written.  In particular, "rbd unmap"
will retry the write multiple times a second:

  10:28:05.463299 write(4, "0", 1)        = 0
  10:28:05.463509 write(4, "0", 1)        = 0
  10:28:05.463720 write(4, "0", 1)        = 0
  10:28:05.463942 write(4, "0", 1)        = 0
  10:28:05.464155 write(4, "0", 1)        = 0

Cc: stable@vger.kernel.org
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Tested-by: Dongsheng Yang <dongsheng.yang@easystack.cn>
drivers/block/rbd.c

index 8e5140bbf24199873d5a62b7a4cf54386f722abe..1e92b61d0bd51cd481f06a112de4ed3f0f6718fd 100644 (file)
@@ -5986,7 +5986,6 @@ static ssize_t do_rbd_remove(struct bus_type *bus,
        struct list_head *tmp;
        int dev_id;
        char opt_buf[6];
-       bool already = false;
        bool force = false;
        int ret;
 
@@ -6019,13 +6018,13 @@ static ssize_t do_rbd_remove(struct bus_type *bus,
                spin_lock_irq(&rbd_dev->lock);
                if (rbd_dev->open_count && !force)
                        ret = -EBUSY;
-               else
-                       already = test_and_set_bit(RBD_DEV_FLAG_REMOVING,
-                                                       &rbd_dev->flags);
+               else if (test_and_set_bit(RBD_DEV_FLAG_REMOVING,
+                                         &rbd_dev->flags))
+                       ret = -EINPROGRESS;
                spin_unlock_irq(&rbd_dev->lock);
        }
        spin_unlock(&rbd_dev_list_lock);
-       if (ret < 0 || already)
+       if (ret)
                return ret;
 
        if (force) {