Commit 1f78160c authored by Xiao Guangrong's avatar Xiao Guangrong Committed by Chris Mason

Btrfs: using rcu lock in the reader side of devices list

fs_devices->devices is only updated on remove and add device paths, so we can
use rcu to protect it in the reader side
Signed-off-by: default avatarXiao Guangrong <xiaoguangrong@cn.fujitsu.com>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 46224705
...@@ -1410,8 +1410,8 @@ static int btrfs_congested_fn(void *congested_data, int bdi_bits) ...@@ -1410,8 +1410,8 @@ static int btrfs_congested_fn(void *congested_data, int bdi_bits)
struct btrfs_device *device; struct btrfs_device *device;
struct backing_dev_info *bdi; struct backing_dev_info *bdi;
mutex_lock(&info->fs_devices->device_list_mutex); rcu_read_lock();
list_for_each_entry(device, &info->fs_devices->devices, dev_list) { list_for_each_entry_rcu(device, &info->fs_devices->devices, dev_list) {
if (!device->bdev) if (!device->bdev)
continue; continue;
bdi = blk_get_backing_dev_info(device->bdev); bdi = blk_get_backing_dev_info(device->bdev);
...@@ -1420,7 +1420,7 @@ static int btrfs_congested_fn(void *congested_data, int bdi_bits) ...@@ -1420,7 +1420,7 @@ static int btrfs_congested_fn(void *congested_data, int bdi_bits)
break; break;
} }
} }
mutex_unlock(&info->fs_devices->device_list_mutex); rcu_read_unlock();
return ret; return ret;
} }
...@@ -2332,9 +2332,9 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors) ...@@ -2332,9 +2332,9 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors)
sb = &root->fs_info->super_for_commit; sb = &root->fs_info->super_for_commit;
dev_item = &sb->dev_item; dev_item = &sb->dev_item;
mutex_lock(&root->fs_info->fs_devices->device_list_mutex); rcu_read_lock();
head = &root->fs_info->fs_devices->devices; head = &root->fs_info->fs_devices->devices;
list_for_each_entry(dev, head, dev_list) { list_for_each_entry_rcu(dev, head, dev_list) {
if (!dev->bdev) { if (!dev->bdev) {
total_errors++; total_errors++;
continue; continue;
...@@ -2367,7 +2367,7 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors) ...@@ -2367,7 +2367,7 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors)
} }
total_errors = 0; total_errors = 0;
list_for_each_entry(dev, head, dev_list) { list_for_each_entry_rcu(dev, head, dev_list) {
if (!dev->bdev) if (!dev->bdev)
continue; continue;
if (!dev->in_fs_metadata || !dev->writeable) if (!dev->in_fs_metadata || !dev->writeable)
...@@ -2377,7 +2377,7 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors) ...@@ -2377,7 +2377,7 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors)
if (ret) if (ret)
total_errors++; total_errors++;
} }
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); rcu_read_unlock();
if (total_errors > max_errors) { if (total_errors > max_errors) {
printk(KERN_ERR "btrfs: %d errors while writing supers\n", printk(KERN_ERR "btrfs: %d errors while writing supers\n",
total_errors); total_errors);
......
...@@ -281,8 +281,9 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg) ...@@ -281,8 +281,9 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
mutex_lock(&fs_info->fs_devices->device_list_mutex); rcu_read_lock();
list_for_each_entry(device, &fs_info->fs_devices->devices, dev_list) { list_for_each_entry_rcu(device, &fs_info->fs_devices->devices,
dev_list) {
if (!device->bdev) if (!device->bdev)
continue; continue;
q = bdev_get_queue(device->bdev); q = bdev_get_queue(device->bdev);
...@@ -292,7 +293,7 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg) ...@@ -292,7 +293,7 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
minlen); minlen);
} }
} }
mutex_unlock(&fs_info->fs_devices->device_list_mutex); rcu_read_unlock();
if (!num_devices) if (!num_devices)
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -363,7 +363,7 @@ static noinline int device_list_add(const char *path, ...@@ -363,7 +363,7 @@ static noinline int device_list_add(const char *path,
INIT_LIST_HEAD(&device->dev_alloc_list); INIT_LIST_HEAD(&device->dev_alloc_list);
mutex_lock(&fs_devices->device_list_mutex); mutex_lock(&fs_devices->device_list_mutex);
list_add(&device->dev_list, &fs_devices->devices); list_add_rcu(&device->dev_list, &fs_devices->devices);
mutex_unlock(&fs_devices->device_list_mutex); mutex_unlock(&fs_devices->device_list_mutex);
device->fs_devices = fs_devices; device->fs_devices = fs_devices;
...@@ -471,6 +471,29 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices) ...@@ -471,6 +471,29 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
return 0; return 0;
} }
static void __free_device(struct work_struct *work)
{
struct btrfs_device *device;
device = container_of(work, struct btrfs_device, rcu_work);
if (device->bdev)
blkdev_put(device->bdev, device->mode);
kfree(device->name);
kfree(device);
}
static void free_device(struct rcu_head *head)
{
struct btrfs_device *device;
device = container_of(head, struct btrfs_device, rcu);
INIT_WORK(&device->rcu_work, __free_device);
schedule_work(&device->rcu_work);
}
static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
{ {
struct btrfs_device *device; struct btrfs_device *device;
...@@ -480,18 +503,27 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) ...@@ -480,18 +503,27 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
mutex_lock(&fs_devices->device_list_mutex); mutex_lock(&fs_devices->device_list_mutex);
list_for_each_entry(device, &fs_devices->devices, dev_list) { list_for_each_entry(device, &fs_devices->devices, dev_list) {
if (device->bdev) { struct btrfs_device *new_device;
blkdev_put(device->bdev, device->mode);
if (device->bdev)
fs_devices->open_devices--; fs_devices->open_devices--;
}
if (device->writeable) { if (device->writeable) {
list_del_init(&device->dev_alloc_list); list_del_init(&device->dev_alloc_list);
fs_devices->rw_devices--; fs_devices->rw_devices--;
} }
device->bdev = NULL; new_device = kmalloc(sizeof(*new_device), GFP_NOFS);
device->writeable = 0; BUG_ON(!new_device);
device->in_fs_metadata = 0; memcpy(new_device, device, sizeof(*new_device));
new_device->name = kstrdup(device->name, GFP_NOFS);
BUG_ON(!new_device->name);
new_device->bdev = NULL;
new_device->writeable = 0;
new_device->in_fs_metadata = 0;
list_replace_rcu(&device->dev_list, &new_device->dev_list);
call_rcu(&device->rcu, free_device);
} }
mutex_unlock(&fs_devices->device_list_mutex); mutex_unlock(&fs_devices->device_list_mutex);
...@@ -1204,11 +1236,13 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -1204,11 +1236,13 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
struct block_device *bdev; struct block_device *bdev;
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
struct btrfs_super_block *disk_super; struct btrfs_super_block *disk_super;
struct btrfs_fs_devices *cur_devices;
u64 all_avail; u64 all_avail;
u64 devid; u64 devid;
u64 num_devices; u64 num_devices;
u8 *dev_uuid; u8 *dev_uuid;
int ret = 0; int ret = 0;
bool clear_super = false;
mutex_lock(&uuid_mutex); mutex_lock(&uuid_mutex);
mutex_lock(&root->fs_info->volume_mutex); mutex_lock(&root->fs_info->volume_mutex);
...@@ -1294,6 +1328,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -1294,6 +1328,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
list_del_init(&device->dev_alloc_list); list_del_init(&device->dev_alloc_list);
unlock_chunks(root); unlock_chunks(root);
root->fs_info->fs_devices->rw_devices--; root->fs_info->fs_devices->rw_devices--;
clear_super = true;
} }
ret = btrfs_shrink_device(device, 0); ret = btrfs_shrink_device(device, 0);
...@@ -1304,16 +1339,15 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -1304,16 +1339,15 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
if (ret) if (ret)
goto error_undo; goto error_undo;
device->in_fs_metadata = 0;
/* /*
* the device list mutex makes sure that we don't change * the device list mutex makes sure that we don't change
* the device list while someone else is writing out all * the device list while someone else is writing out all
* the device supers. * the device supers.
*/ */
cur_devices = device->fs_devices;
mutex_lock(&root->fs_info->fs_devices->device_list_mutex); mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
list_del_init(&device->dev_list); list_del_rcu(&device->dev_list);
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
device->fs_devices->num_devices--; device->fs_devices->num_devices--;
...@@ -1327,36 +1361,36 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -1327,36 +1361,36 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
if (device->bdev == root->fs_info->fs_devices->latest_bdev) if (device->bdev == root->fs_info->fs_devices->latest_bdev)
root->fs_info->fs_devices->latest_bdev = next_device->bdev; root->fs_info->fs_devices->latest_bdev = next_device->bdev;
if (device->bdev) { if (device->bdev)
blkdev_put(device->bdev, device->mode);
device->bdev = NULL;
device->fs_devices->open_devices--; device->fs_devices->open_devices--;
}
call_rcu(&device->rcu, free_device);
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
num_devices = btrfs_super_num_devices(&root->fs_info->super_copy) - 1; num_devices = btrfs_super_num_devices(&root->fs_info->super_copy) - 1;
btrfs_set_super_num_devices(&root->fs_info->super_copy, num_devices); btrfs_set_super_num_devices(&root->fs_info->super_copy, num_devices);
if (device->fs_devices->open_devices == 0) { if (cur_devices->open_devices == 0) {
struct btrfs_fs_devices *fs_devices; struct btrfs_fs_devices *fs_devices;
fs_devices = root->fs_info->fs_devices; fs_devices = root->fs_info->fs_devices;
while (fs_devices) { while (fs_devices) {
if (fs_devices->seed == device->fs_devices) if (fs_devices->seed == cur_devices)
break; break;
fs_devices = fs_devices->seed; fs_devices = fs_devices->seed;
} }
fs_devices->seed = device->fs_devices->seed; fs_devices->seed = cur_devices->seed;
device->fs_devices->seed = NULL; cur_devices->seed = NULL;
lock_chunks(root); lock_chunks(root);
__btrfs_close_devices(device->fs_devices); __btrfs_close_devices(cur_devices);
unlock_chunks(root); unlock_chunks(root);
free_fs_devices(device->fs_devices); free_fs_devices(cur_devices);
} }
/* /*
* at this point, the device is zero sized. We want to * at this point, the device is zero sized. We want to
* remove it from the devices list and zero out the old super * remove it from the devices list and zero out the old super
*/ */
if (device->writeable) { if (clear_super) {
/* make sure this device isn't detected as part of /* make sure this device isn't detected as part of
* the FS anymore * the FS anymore
*/ */
...@@ -1365,8 +1399,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -1365,8 +1399,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
sync_dirty_buffer(bh); sync_dirty_buffer(bh);
} }
kfree(device->name);
kfree(device);
ret = 0; ret = 0;
error_brelse: error_brelse:
...@@ -1425,7 +1457,8 @@ static int btrfs_prepare_sprout(struct btrfs_trans_handle *trans, ...@@ -1425,7 +1457,8 @@ static int btrfs_prepare_sprout(struct btrfs_trans_handle *trans,
mutex_init(&seed_devices->device_list_mutex); mutex_init(&seed_devices->device_list_mutex);
mutex_lock(&root->fs_info->fs_devices->device_list_mutex); mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
list_splice_init(&fs_devices->devices, &seed_devices->devices); list_splice_init_rcu(&fs_devices->devices, &seed_devices->devices,
synchronize_rcu);
mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); mutex_unlock(&root->fs_info->fs_devices->device_list_mutex);
list_splice_init(&fs_devices->alloc_list, &seed_devices->alloc_list); list_splice_init(&fs_devices->alloc_list, &seed_devices->alloc_list);
...@@ -1624,7 +1657,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) ...@@ -1624,7 +1657,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
* half setup * half setup
*/ */
mutex_lock(&root->fs_info->fs_devices->device_list_mutex); mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
list_add(&device->dev_list, &root->fs_info->fs_devices->devices); list_add_rcu(&device->dev_list, &root->fs_info->fs_devices->devices);
list_add(&device->dev_alloc_list, list_add(&device->dev_alloc_list,
&root->fs_info->fs_devices->alloc_list); &root->fs_info->fs_devices->alloc_list);
root->fs_info->fs_devices->num_devices++; root->fs_info->fs_devices->num_devices++;
......
...@@ -86,6 +86,8 @@ struct btrfs_device { ...@@ -86,6 +86,8 @@ struct btrfs_device {
u8 uuid[BTRFS_UUID_SIZE]; u8 uuid[BTRFS_UUID_SIZE];
struct btrfs_work work; struct btrfs_work work;
struct rcu_head rcu;
struct work_struct rcu_work;
}; };
struct btrfs_fs_devices { struct btrfs_fs_devices {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment