aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXiao Guangrong <xiaoguangrong@cn.fujitsu.com>2011-04-20 06:09:16 -0400
committerChris Mason <chris.mason@oracle.com>2011-05-23 13:24:43 -0400
commit1f78160ce1b1b8e657e2248118c4d91f881763f0 (patch)
tree98c5496a9ab3e77e4cd682c0487f4c0127198396
parent46224705656633466ca7dc71d81b3c0abc76cae4 (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>
-rw-r--r--fs/btrfs/disk-io.c14
-rw-r--r--fs/btrfs/ioctl.c7
-rw-r--r--fs/btrfs/volumes.c85
-rw-r--r--fs/btrfs/volumes.h2
4 files changed, 72 insertions, 36 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 4e53a4fc467..deba3d9c885 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 ce773fb736a..0de71feb8e1 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 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
474static 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
487static 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
474static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) 497static 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
1372error_brelse: 1404error_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 cc2eadaf7a2..f1b2e4f53fc 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
91struct btrfs_fs_devices { 93struct btrfs_fs_devices {