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/volumes.c | |
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/volumes.c')
-rw-r--r-- | fs/btrfs/volumes.c | 85 |
1 files changed, 59 insertions, 26 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 0b5ca273726..e7844f8a347 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++; |