Skip to content

Commit

Permalink
btrfs: clean the old superblocks before freeing the device
Browse files Browse the repository at this point in the history
btrfs_rm_device frees the block device but then re-opens it using
the saved device name.  A race exists between the close and the
re-open that allows the block size to be changed.  The result
is getting stuck forever in the reclaim loop in __getblk_slow.

This patch moves the superblock cleanup before closing the block
device, which is also consistent with other callers.  We also don't
need a private copy of dev_name as the whole routine operates under
the uuid_mutex.

Signed-off-by: Jeff Mahoney <[email protected]>
Reviewed-by: David Sterba <[email protected]>
Signed-off-by: David Sterba <[email protected]>
  • Loading branch information
jeffmahoney authored and kdave committed Sep 26, 2016
1 parent 0279422 commit cea67ab
Showing 1 changed file with 11 additions and 27 deletions.
38 changes: 11 additions & 27 deletions fs/btrfs/volumes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1846,7 +1846,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid)
u64 num_devices;
int ret = 0;
bool clear_super = false;
char *dev_name = NULL;

mutex_lock(&uuid_mutex);

Expand Down Expand Up @@ -1882,11 +1881,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid)
list_del_init(&device->dev_alloc_list);
device->fs_devices->rw_devices--;
unlock_chunks(root);
dev_name = kstrdup(device->name->str, GFP_KERNEL);
if (!dev_name) {
ret = -ENOMEM;
goto error_undo;
}
clear_super = true;
}

Expand Down Expand Up @@ -1936,14 +1930,21 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid)
btrfs_sysfs_rm_device_link(root->fs_info->fs_devices, device);
}

btrfs_close_bdev(device);

call_rcu(&device->rcu, free_device);

num_devices = btrfs_super_num_devices(root->fs_info->super_copy) - 1;
btrfs_set_super_num_devices(root->fs_info->super_copy, num_devices);
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);

/*
* at this point, the device is zero sized and detached from
* the devices list. All that's left is to zero out the old
* supers and free the device.
*/
if (device->writeable)
btrfs_scratch_superblocks(device->bdev, device->name->str);

btrfs_close_bdev(device);
call_rcu(&device->rcu, free_device);

if (cur_devices->open_devices == 0) {
struct btrfs_fs_devices *fs_devices;
fs_devices = root->fs_info->fs_devices;
Expand All @@ -1962,24 +1963,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid)
root->fs_info->num_tolerated_disk_barrier_failures =
btrfs_calc_num_tolerated_disk_barrier_failures(root->fs_info);

/*
* at this point, the device is zero sized. We want to
* remove it from the devices list and zero out the old super
*/
if (clear_super) {
struct block_device *bdev;

bdev = blkdev_get_by_path(dev_name, FMODE_READ | FMODE_EXCL,
root->fs_info->bdev_holder);
if (!IS_ERR(bdev)) {
btrfs_scratch_superblocks(bdev, dev_name);
blkdev_put(bdev, FMODE_READ | FMODE_EXCL);
}
}

out:
kfree(dev_name);

mutex_unlock(&uuid_mutex);
return ret;

Expand Down

0 comments on commit cea67ab

Please sign in to comment.