diff options
author | Xiao Guangrong <xiaoguangrong@cn.fujitsu.com> | 2011-04-20 06:09:16 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2011-05-23 13:24:43 -0400 |
commit | 1f78160ce1b1b8e657e2248118c4d91f881763f0 (patch) | |
tree | 98c5496a9ab3e77e4cd682c0487f4c0127198396 /fs/btrfs | |
parent | 46224705656633466ca7dc71d81b3c0abc76cae4 (diff) |
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>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/disk-io.c | 14 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 7 | ||||
-rw-r--r-- | fs/btrfs/volumes.c | 85 | ||||
-rw-r--r-- | fs/btrfs/volumes.h | 2 |
4 files changed, 72 insertions, 36 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 4e53a4fc467f..deba3d9c8853 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -1410,8 +1410,8 @@ static int btrfs_congested_fn(void *congested_data, int bdi_bits) | |||
1410 | struct btrfs_device *device; | 1410 | struct btrfs_device *device; |
1411 | struct backing_dev_info *bdi; | 1411 | struct backing_dev_info *bdi; |
1412 | 1412 | ||
1413 | mutex_lock(&info->fs_devices->device_list_mutex); | 1413 | rcu_read_lock(); |
1414 | list_for_each_entry(device, &info->fs_devices->devices, dev_list) { | 1414 | list_for_each_entry_rcu(device, &info->fs_devices->devices, dev_list) { |
1415 | if (!device->bdev) | 1415 | if (!device->bdev) |
1416 | continue; | 1416 | continue; |
1417 | bdi = blk_get_backing_dev_info(device->bdev); | 1417 | bdi = blk_get_backing_dev_info(device->bdev); |
@@ -1420,7 +1420,7 @@ static int btrfs_congested_fn(void *congested_data, int bdi_bits) | |||
1420 | break; | 1420 | break; |
1421 | } | 1421 | } |
1422 | } | 1422 | } |
1423 | mutex_unlock(&info->fs_devices->device_list_mutex); | 1423 | rcu_read_unlock(); |
1424 | return ret; | 1424 | return ret; |
1425 | } | 1425 | } |
1426 | 1426 | ||
@@ -2332,9 +2332,9 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors) | |||
2332 | sb = &root->fs_info->super_for_commit; | 2332 | sb = &root->fs_info->super_for_commit; |
2333 | dev_item = &sb->dev_item; | 2333 | dev_item = &sb->dev_item; |
2334 | 2334 | ||
2335 | mutex_lock(&root->fs_info->fs_devices->device_list_mutex); | 2335 | rcu_read_lock(); |
2336 | head = &root->fs_info->fs_devices->devices; | 2336 | head = &root->fs_info->fs_devices->devices; |
2337 | list_for_each_entry(dev, head, dev_list) { | 2337 | list_for_each_entry_rcu(dev, head, dev_list) { |
2338 | if (!dev->bdev) { | 2338 | if (!dev->bdev) { |
2339 | total_errors++; | 2339 | total_errors++; |
2340 | continue; | 2340 | continue; |
@@ -2367,7 +2367,7 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors) | |||
2367 | } | 2367 | } |
2368 | 2368 | ||
2369 | total_errors = 0; | 2369 | total_errors = 0; |
2370 | list_for_each_entry(dev, head, dev_list) { | 2370 | list_for_each_entry_rcu(dev, head, dev_list) { |
2371 | if (!dev->bdev) | 2371 | if (!dev->bdev) |
2372 | continue; | 2372 | continue; |
2373 | if (!dev->in_fs_metadata || !dev->writeable) | 2373 | if (!dev->in_fs_metadata || !dev->writeable) |
@@ -2377,7 +2377,7 @@ int write_all_supers(struct btrfs_root *root, int max_mirrors) | |||
2377 | if (ret) | 2377 | if (ret) |
2378 | total_errors++; | 2378 | total_errors++; |
2379 | } | 2379 | } |
2380 | mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); | 2380 | rcu_read_unlock(); |
2381 | if (total_errors > max_errors) { | 2381 | if (total_errors > max_errors) { |
2382 | printk(KERN_ERR "btrfs: %d errors while writing supers\n", | 2382 | printk(KERN_ERR "btrfs: %d errors while writing supers\n", |
2383 | total_errors); | 2383 | total_errors); |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index ce773fb736a1..0de71feb8e1c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -281,8 +281,9 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg) | |||
281 | if (!capable(CAP_SYS_ADMIN)) | 281 | if (!capable(CAP_SYS_ADMIN)) |
282 | return -EPERM; | 282 | return -EPERM; |
283 | 283 | ||
284 | mutex_lock(&fs_info->fs_devices->device_list_mutex); | 284 | rcu_read_lock(); |
285 | list_for_each_entry(device, &fs_info->fs_devices->devices, dev_list) { | 285 | list_for_each_entry_rcu(device, &fs_info->fs_devices->devices, |
286 | dev_list) { | ||
286 | if (!device->bdev) | 287 | if (!device->bdev) |
287 | continue; | 288 | continue; |
288 | q = bdev_get_queue(device->bdev); | 289 | q = bdev_get_queue(device->bdev); |
@@ -292,7 +293,7 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg) | |||
292 | minlen); | 293 | minlen); |
293 | } | 294 | } |
294 | } | 295 | } |
295 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | 296 | rcu_read_unlock(); |
296 | if (!num_devices) | 297 | if (!num_devices) |
297 | return -EOPNOTSUPP; | 298 | return -EOPNOTSUPP; |
298 | 299 | ||
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 0b5ca2737268..e7844f8a347a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -363,7 +363,7 @@ static noinline int device_list_add(const char *path, | |||
363 | INIT_LIST_HEAD(&device->dev_alloc_list); | 363 | INIT_LIST_HEAD(&device->dev_alloc_list); |
364 | 364 | ||
365 | mutex_lock(&fs_devices->device_list_mutex); | 365 | mutex_lock(&fs_devices->device_list_mutex); |
366 | list_add(&device->dev_list, &fs_devices->devices); | 366 | list_add_rcu(&device->dev_list, &fs_devices->devices); |
367 | mutex_unlock(&fs_devices->device_list_mutex); | 367 | mutex_unlock(&fs_devices->device_list_mutex); |
368 | 368 | ||
369 | device->fs_devices = fs_devices; | 369 | device->fs_devices = fs_devices; |
@@ -471,6 +471,29 @@ again: | |||
471 | return 0; | 471 | return 0; |
472 | } | 472 | } |
473 | 473 | ||
474 | static void __free_device(struct work_struct *work) | ||
475 | { | ||
476 | struct btrfs_device *device; | ||
477 | |||
478 | device = container_of(work, struct btrfs_device, rcu_work); | ||
479 | |||
480 | if (device->bdev) | ||
481 | blkdev_put(device->bdev, device->mode); | ||
482 | |||
483 | kfree(device->name); | ||
484 | kfree(device); | ||
485 | } | ||
486 | |||
487 | static void free_device(struct rcu_head *head) | ||
488 | { | ||
489 | struct btrfs_device *device; | ||
490 | |||
491 | device = container_of(head, struct btrfs_device, rcu); | ||
492 | |||
493 | INIT_WORK(&device->rcu_work, __free_device); | ||
494 | schedule_work(&device->rcu_work); | ||
495 | } | ||
496 | |||
474 | static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) | 497 | static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) |
475 | { | 498 | { |
476 | struct btrfs_device *device; | 499 | struct btrfs_device *device; |
@@ -480,18 +503,27 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) | |||
480 | 503 | ||
481 | mutex_lock(&fs_devices->device_list_mutex); | 504 | mutex_lock(&fs_devices->device_list_mutex); |
482 | list_for_each_entry(device, &fs_devices->devices, dev_list) { | 505 | list_for_each_entry(device, &fs_devices->devices, dev_list) { |
483 | if (device->bdev) { | 506 | struct btrfs_device *new_device; |
484 | blkdev_put(device->bdev, device->mode); | 507 | |
508 | if (device->bdev) | ||
485 | fs_devices->open_devices--; | 509 | fs_devices->open_devices--; |
486 | } | 510 | |
487 | if (device->writeable) { | 511 | if (device->writeable) { |
488 | list_del_init(&device->dev_alloc_list); | 512 | list_del_init(&device->dev_alloc_list); |
489 | fs_devices->rw_devices--; | 513 | fs_devices->rw_devices--; |
490 | } | 514 | } |
491 | 515 | ||
492 | device->bdev = NULL; | 516 | new_device = kmalloc(sizeof(*new_device), GFP_NOFS); |
493 | device->writeable = 0; | 517 | BUG_ON(!new_device); |
494 | device->in_fs_metadata = 0; | 518 | memcpy(new_device, device, sizeof(*new_device)); |
519 | new_device->name = kstrdup(device->name, GFP_NOFS); | ||
520 | BUG_ON(!new_device->name); | ||
521 | new_device->bdev = NULL; | ||
522 | new_device->writeable = 0; | ||
523 | new_device->in_fs_metadata = 0; | ||
524 | list_replace_rcu(&device->dev_list, &new_device->dev_list); | ||
525 | |||
526 | call_rcu(&device->rcu, free_device); | ||
495 | } | 527 | } |
496 | mutex_unlock(&fs_devices->device_list_mutex); | 528 | mutex_unlock(&fs_devices->device_list_mutex); |
497 | 529 | ||
@@ -1204,11 +1236,13 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) | |||
1204 | struct block_device *bdev; | 1236 | struct block_device *bdev; |
1205 | struct buffer_head *bh = NULL; | 1237 | struct buffer_head *bh = NULL; |
1206 | struct btrfs_super_block *disk_super; | 1238 | struct btrfs_super_block *disk_super; |
1239 | struct btrfs_fs_devices *cur_devices; | ||
1207 | u64 all_avail; | 1240 | u64 all_avail; |
1208 | u64 devid; | 1241 | u64 devid; |
1209 | u64 num_devices; | 1242 | u64 num_devices; |
1210 | u8 *dev_uuid; | 1243 | u8 *dev_uuid; |
1211 | int ret = 0; | 1244 | int ret = 0; |
1245 | bool clear_super = false; | ||
1212 | 1246 | ||
1213 | mutex_lock(&uuid_mutex); | 1247 | mutex_lock(&uuid_mutex); |
1214 | mutex_lock(&root->fs_info->volume_mutex); | 1248 | mutex_lock(&root->fs_info->volume_mutex); |
@@ -1294,6 +1328,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) | |||
1294 | list_del_init(&device->dev_alloc_list); | 1328 | list_del_init(&device->dev_alloc_list); |
1295 | unlock_chunks(root); | 1329 | unlock_chunks(root); |
1296 | root->fs_info->fs_devices->rw_devices--; | 1330 | root->fs_info->fs_devices->rw_devices--; |
1331 | clear_super = true; | ||
1297 | } | 1332 | } |
1298 | 1333 | ||
1299 | ret = btrfs_shrink_device(device, 0); | 1334 | ret = btrfs_shrink_device(device, 0); |
@@ -1304,16 +1339,15 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) | |||
1304 | if (ret) | 1339 | if (ret) |
1305 | goto error_undo; | 1340 | goto error_undo; |
1306 | 1341 | ||
1307 | device->in_fs_metadata = 0; | ||
1308 | |||
1309 | /* | 1342 | /* |
1310 | * the device list mutex makes sure that we don't change | 1343 | * the device list mutex makes sure that we don't change |
1311 | * the device list while someone else is writing out all | 1344 | * the device list while someone else is writing out all |
1312 | * the device supers. | 1345 | * the device supers. |
1313 | */ | 1346 | */ |
1347 | |||
1348 | cur_devices = device->fs_devices; | ||
1314 | mutex_lock(&root->fs_info->fs_devices->device_list_mutex); | 1349 | mutex_lock(&root->fs_info->fs_devices->device_list_mutex); |
1315 | list_del_init(&device->dev_list); | 1350 | list_del_rcu(&device->dev_list); |
1316 | mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); | ||
1317 | 1351 | ||
1318 | device->fs_devices->num_devices--; | 1352 | device->fs_devices->num_devices--; |
1319 | 1353 | ||
@@ -1327,36 +1361,36 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) | |||
1327 | if (device->bdev == root->fs_info->fs_devices->latest_bdev) | 1361 | if (device->bdev == root->fs_info->fs_devices->latest_bdev) |
1328 | root->fs_info->fs_devices->latest_bdev = next_device->bdev; | 1362 | root->fs_info->fs_devices->latest_bdev = next_device->bdev; |
1329 | 1363 | ||
1330 | if (device->bdev) { | 1364 | if (device->bdev) |
1331 | blkdev_put(device->bdev, device->mode); | ||
1332 | device->bdev = NULL; | ||
1333 | device->fs_devices->open_devices--; | 1365 | device->fs_devices->open_devices--; |
1334 | } | 1366 | |
1367 | call_rcu(&device->rcu, free_device); | ||
1368 | mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); | ||
1335 | 1369 | ||
1336 | num_devices = btrfs_super_num_devices(&root->fs_info->super_copy) - 1; | 1370 | num_devices = btrfs_super_num_devices(&root->fs_info->super_copy) - 1; |
1337 | btrfs_set_super_num_devices(&root->fs_info->super_copy, num_devices); | 1371 | btrfs_set_super_num_devices(&root->fs_info->super_copy, num_devices); |
1338 | 1372 | ||
1339 | if (device->fs_devices->open_devices == 0) { | 1373 | if (cur_devices->open_devices == 0) { |
1340 | struct btrfs_fs_devices *fs_devices; | 1374 | struct btrfs_fs_devices *fs_devices; |
1341 | fs_devices = root->fs_info->fs_devices; | 1375 | fs_devices = root->fs_info->fs_devices; |
1342 | while (fs_devices) { | 1376 | while (fs_devices) { |
1343 | if (fs_devices->seed == device->fs_devices) | 1377 | if (fs_devices->seed == cur_devices) |
1344 | break; | 1378 | break; |
1345 | fs_devices = fs_devices->seed; | 1379 | fs_devices = fs_devices->seed; |
1346 | } | 1380 | } |
1347 | fs_devices->seed = device->fs_devices->seed; | 1381 | fs_devices->seed = cur_devices->seed; |
1348 | device->fs_devices->seed = NULL; | 1382 | cur_devices->seed = NULL; |
1349 | lock_chunks(root); | 1383 | lock_chunks(root); |
1350 | __btrfs_close_devices(device->fs_devices); | 1384 | __btrfs_close_devices(cur_devices); |
1351 | unlock_chunks(root); | 1385 | unlock_chunks(root); |
1352 | free_fs_devices(device->fs_devices); | 1386 | free_fs_devices(cur_devices); |
1353 | } | 1387 | } |
1354 | 1388 | ||
1355 | /* | 1389 | /* |
1356 | * at this point, the device is zero sized. We want to | 1390 | * at this point, the device is zero sized. We want to |
1357 | * remove it from the devices list and zero out the old super | 1391 | * remove it from the devices list and zero out the old super |
1358 | */ | 1392 | */ |
1359 | if (device->writeable) { | 1393 | if (clear_super) { |
1360 | /* make sure this device isn't detected as part of | 1394 | /* make sure this device isn't detected as part of |
1361 | * the FS anymore | 1395 | * the FS anymore |
1362 | */ | 1396 | */ |
@@ -1365,8 +1399,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) | |||
1365 | sync_dirty_buffer(bh); | 1399 | sync_dirty_buffer(bh); |
1366 | } | 1400 | } |
1367 | 1401 | ||
1368 | kfree(device->name); | ||
1369 | kfree(device); | ||
1370 | ret = 0; | 1402 | ret = 0; |
1371 | 1403 | ||
1372 | error_brelse: | 1404 | error_brelse: |
@@ -1425,7 +1457,8 @@ static int btrfs_prepare_sprout(struct btrfs_trans_handle *trans, | |||
1425 | mutex_init(&seed_devices->device_list_mutex); | 1457 | mutex_init(&seed_devices->device_list_mutex); |
1426 | 1458 | ||
1427 | mutex_lock(&root->fs_info->fs_devices->device_list_mutex); | 1459 | mutex_lock(&root->fs_info->fs_devices->device_list_mutex); |
1428 | list_splice_init(&fs_devices->devices, &seed_devices->devices); | 1460 | list_splice_init_rcu(&fs_devices->devices, &seed_devices->devices, |
1461 | synchronize_rcu); | ||
1429 | mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); | 1462 | mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); |
1430 | 1463 | ||
1431 | list_splice_init(&fs_devices->alloc_list, &seed_devices->alloc_list); | 1464 | 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 | * half setup | 1657 | * half setup |
1625 | */ | 1658 | */ |
1626 | mutex_lock(&root->fs_info->fs_devices->device_list_mutex); | 1659 | mutex_lock(&root->fs_info->fs_devices->device_list_mutex); |
1627 | list_add(&device->dev_list, &root->fs_info->fs_devices->devices); | 1660 | list_add_rcu(&device->dev_list, &root->fs_info->fs_devices->devices); |
1628 | list_add(&device->dev_alloc_list, | 1661 | list_add(&device->dev_alloc_list, |
1629 | &root->fs_info->fs_devices->alloc_list); | 1662 | &root->fs_info->fs_devices->alloc_list); |
1630 | root->fs_info->fs_devices->num_devices++; | 1663 | root->fs_info->fs_devices->num_devices++; |
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index cc2eadaf7a27..f1b2e4f53fc2 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h | |||
@@ -86,6 +86,8 @@ struct btrfs_device { | |||
86 | u8 uuid[BTRFS_UUID_SIZE]; | 86 | u8 uuid[BTRFS_UUID_SIZE]; |
87 | 87 | ||
88 | struct btrfs_work work; | 88 | struct btrfs_work work; |
89 | struct rcu_head rcu; | ||
90 | struct work_struct rcu_work; | ||
89 | }; | 91 | }; |
90 | 92 | ||
91 | struct btrfs_fs_devices { | 93 | struct btrfs_fs_devices { |