diff options
| -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 { |
