summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/volumes.c26
1 files changed, 20 insertions, 6 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 6500f9e23330..b4048c1b3d9b 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -859,7 +859,7 @@ static void btrfs_close_bdev(struct btrfs_device *device)
blkdev_put(device->bdev, device->mode);
}
-static void btrfs_close_one_device(struct btrfs_device *device)
+static void btrfs_prepare_close_one_device(struct btrfs_device *device)
{
struct btrfs_fs_devices *fs_devices = device->fs_devices;
struct btrfs_device *new_device;
@@ -877,8 +877,6 @@ static void btrfs_close_one_device(struct btrfs_device *device)
if (device->missing)
fs_devices->missing_devices--;
- btrfs_close_bdev(device);
-
new_device = btrfs_alloc_device(NULL, &device->devid,
device->uuid);
BUG_ON(IS_ERR(new_device)); /* -ENOMEM */
@@ -892,23 +890,39 @@ static void btrfs_close_one_device(struct btrfs_device *device)
list_replace_rcu(&device->dev_list, &new_device->dev_list);
new_device->fs_devices = device->fs_devices;
-
- call_rcu(&device->rcu, free_device);
}
static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
{
struct btrfs_device *device, *tmp;
+ struct list_head pending_put;
+
+ INIT_LIST_HEAD(&pending_put);
if (--fs_devices->opened > 0)
return 0;
mutex_lock(&fs_devices->device_list_mutex);
list_for_each_entry_safe(device, tmp, &fs_devices->devices, dev_list) {
- btrfs_close_one_device(device);
+ btrfs_prepare_close_one_device(device);
+ list_add(&device->dev_list, &pending_put);
}
mutex_unlock(&fs_devices->device_list_mutex);
+ /*
+ * btrfs_show_devname() is using the device_list_mutex,
+ * sometimes call to blkdev_put() leads vfs calling
+ * into this func. So do put outside of device_list_mutex,
+ * as of now.
+ */
+ while (!list_empty(&pending_put)) {
+ device = list_first_entry(&pending_put,
+ struct btrfs_device, dev_list);
+ list_del(&device->dev_list);
+ btrfs_close_bdev(device);
+ call_rcu(&device->rcu, free_device);
+ }
+
WARN_ON(fs_devices->open_devices);
WARN_ON(fs_devices->rw_devices);
fs_devices->opened = 0;