mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-26 22:24:09 +08:00
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: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
4622470565
commit
1f78160ce1
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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 @@ again:
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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)
|
|||||||
* 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 {
|
|||||||
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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user