diff options
-rw-r--r-- | fs/btrfs/ctree.h | 2 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 3 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 8 | ||||
-rw-r--r-- | fs/btrfs/scrub.c | 14 | ||||
-rw-r--r-- | fs/btrfs/super.c | 3 | ||||
-rw-r--r-- | fs/btrfs/volumes.c | 41 | ||||
-rw-r--r-- | fs/btrfs/volumes.h | 1 |
7 files changed, 54 insertions, 18 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index e9dc78014f09..746cb6aa1f62 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -3649,7 +3649,7 @@ int btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans, | |||
3649 | /* scrub.c */ | 3649 | /* scrub.c */ |
3650 | int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, | 3650 | int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, |
3651 | u64 end, struct btrfs_scrub_progress *progress, | 3651 | u64 end, struct btrfs_scrub_progress *progress, |
3652 | int readonly); | 3652 | int readonly, int is_dev_replace); |
3653 | void btrfs_scrub_pause(struct btrfs_root *root); | 3653 | void btrfs_scrub_pause(struct btrfs_root *root); |
3654 | void btrfs_scrub_pause_super(struct btrfs_root *root); | 3654 | void btrfs_scrub_pause_super(struct btrfs_root *root); |
3655 | void btrfs_scrub_continue(struct btrfs_root *root); | 3655 | void btrfs_scrub_continue(struct btrfs_root *root); |
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index b4d438f6c2b3..98af8379895a 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -7468,7 +7468,8 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr) | |||
7468 | * check to make sure we can actually find a chunk with enough | 7468 | * check to make sure we can actually find a chunk with enough |
7469 | * space to fit our block group in. | 7469 | * space to fit our block group in. |
7470 | */ | 7470 | */ |
7471 | if (device->total_bytes > device->bytes_used + min_free) { | 7471 | if (device->total_bytes > device->bytes_used + min_free && |
7472 | !device->is_tgtdev_for_dev_replace) { | ||
7472 | ret = find_free_dev_extent(device, min_free, | 7473 | ret = find_free_dev_extent(device, min_free, |
7473 | &dev_offset, NULL); | 7474 | &dev_offset, NULL); |
7474 | if (!ret) | 7475 | if (!ret) |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 26f46dad3b0e..e54b5e50c927 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -1375,6 +1375,11 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root, | |||
1375 | } | 1375 | } |
1376 | } | 1376 | } |
1377 | 1377 | ||
1378 | if (device->is_tgtdev_for_dev_replace) { | ||
1379 | ret = -EINVAL; | ||
1380 | goto out_free; | ||
1381 | } | ||
1382 | |||
1378 | old_size = device->total_bytes; | 1383 | old_size = device->total_bytes; |
1379 | 1384 | ||
1380 | if (mod < 0) { | 1385 | if (mod < 0) { |
@@ -3102,7 +3107,8 @@ static long btrfs_ioctl_scrub(struct btrfs_root *root, void __user *arg) | |||
3102 | return PTR_ERR(sa); | 3107 | return PTR_ERR(sa); |
3103 | 3108 | ||
3104 | ret = btrfs_scrub_dev(root->fs_info, sa->devid, sa->start, sa->end, | 3109 | ret = btrfs_scrub_dev(root->fs_info, sa->devid, sa->start, sa->end, |
3105 | &sa->progress, sa->flags & BTRFS_SCRUB_READONLY); | 3110 | &sa->progress, sa->flags & BTRFS_SCRUB_READONLY, |
3111 | 0); | ||
3106 | 3112 | ||
3107 | if (copy_to_user(arg, sa, sizeof(*sa))) | 3113 | if (copy_to_user(arg, sa, sizeof(*sa))) |
3108 | ret = -EFAULT; | 3114 | ret = -EFAULT; |
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 6cf23f4f7bb7..460e30bb1884 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c | |||
@@ -116,6 +116,9 @@ struct scrub_ctx { | |||
116 | u32 sectorsize; | 116 | u32 sectorsize; |
117 | u32 nodesize; | 117 | u32 nodesize; |
118 | u32 leafsize; | 118 | u32 leafsize; |
119 | |||
120 | int is_dev_replace; | ||
121 | |||
119 | /* | 122 | /* |
120 | * statistics | 123 | * statistics |
121 | */ | 124 | */ |
@@ -284,7 +287,7 @@ static noinline_for_stack void scrub_free_ctx(struct scrub_ctx *sctx) | |||
284 | } | 287 | } |
285 | 288 | ||
286 | static noinline_for_stack | 289 | static noinline_for_stack |
287 | struct scrub_ctx *scrub_setup_ctx(struct btrfs_device *dev) | 290 | struct scrub_ctx *scrub_setup_ctx(struct btrfs_device *dev, int is_dev_replace) |
288 | { | 291 | { |
289 | struct scrub_ctx *sctx; | 292 | struct scrub_ctx *sctx; |
290 | int i; | 293 | int i; |
@@ -296,6 +299,7 @@ struct scrub_ctx *scrub_setup_ctx(struct btrfs_device *dev) | |||
296 | sctx = kzalloc(sizeof(*sctx), GFP_NOFS); | 299 | sctx = kzalloc(sizeof(*sctx), GFP_NOFS); |
297 | if (!sctx) | 300 | if (!sctx) |
298 | goto nomem; | 301 | goto nomem; |
302 | sctx->is_dev_replace = is_dev_replace; | ||
299 | sctx->pages_per_bio = pages_per_bio; | 303 | sctx->pages_per_bio = pages_per_bio; |
300 | sctx->curr = -1; | 304 | sctx->curr = -1; |
301 | sctx->dev_root = dev->dev_root; | 305 | sctx->dev_root = dev->dev_root; |
@@ -2293,7 +2297,7 @@ static noinline_for_stack void scrub_workers_put(struct btrfs_fs_info *fs_info) | |||
2293 | 2297 | ||
2294 | int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, | 2298 | int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, |
2295 | u64 end, struct btrfs_scrub_progress *progress, | 2299 | u64 end, struct btrfs_scrub_progress *progress, |
2296 | int readonly) | 2300 | int readonly, int is_dev_replace) |
2297 | { | 2301 | { |
2298 | struct scrub_ctx *sctx; | 2302 | struct scrub_ctx *sctx; |
2299 | int ret; | 2303 | int ret; |
@@ -2356,14 +2360,14 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, | |||
2356 | 2360 | ||
2357 | mutex_lock(&fs_info->fs_devices->device_list_mutex); | 2361 | mutex_lock(&fs_info->fs_devices->device_list_mutex); |
2358 | dev = btrfs_find_device(fs_info, devid, NULL, NULL); | 2362 | dev = btrfs_find_device(fs_info, devid, NULL, NULL); |
2359 | if (!dev || dev->missing) { | 2363 | if (!dev || (dev->missing && !is_dev_replace)) { |
2360 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | 2364 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); |
2361 | scrub_workers_put(fs_info); | 2365 | scrub_workers_put(fs_info); |
2362 | return -ENODEV; | 2366 | return -ENODEV; |
2363 | } | 2367 | } |
2364 | mutex_lock(&fs_info->scrub_lock); | 2368 | mutex_lock(&fs_info->scrub_lock); |
2365 | 2369 | ||
2366 | if (!dev->in_fs_metadata) { | 2370 | if (!dev->in_fs_metadata || dev->is_tgtdev_for_dev_replace) { |
2367 | mutex_unlock(&fs_info->scrub_lock); | 2371 | mutex_unlock(&fs_info->scrub_lock); |
2368 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | 2372 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); |
2369 | scrub_workers_put(fs_info); | 2373 | scrub_workers_put(fs_info); |
@@ -2376,7 +2380,7 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, | |||
2376 | scrub_workers_put(fs_info); | 2380 | scrub_workers_put(fs_info); |
2377 | return -EINPROGRESS; | 2381 | return -EINPROGRESS; |
2378 | } | 2382 | } |
2379 | sctx = scrub_setup_ctx(dev); | 2383 | sctx = scrub_setup_ctx(dev, is_dev_replace); |
2380 | if (IS_ERR(sctx)) { | 2384 | if (IS_ERR(sctx)) { |
2381 | mutex_unlock(&fs_info->scrub_lock); | 2385 | mutex_unlock(&fs_info->scrub_lock); |
2382 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | 2386 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); |
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index ef2415896b06..837ad2d27853 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -1354,7 +1354,8 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes) | |||
1354 | min_stripe_size = BTRFS_STRIPE_LEN; | 1354 | min_stripe_size = BTRFS_STRIPE_LEN; |
1355 | 1355 | ||
1356 | list_for_each_entry(device, &fs_devices->devices, dev_list) { | 1356 | list_for_each_entry(device, &fs_devices->devices, dev_list) { |
1357 | if (!device->in_fs_metadata || !device->bdev) | 1357 | if (!device->in_fs_metadata || !device->bdev || |
1358 | device->is_tgtdev_for_dev_replace) | ||
1358 | continue; | 1359 | continue; |
1359 | 1360 | ||
1360 | avail_space = device->total_bytes - device->bytes_used; | 1361 | avail_space = device->total_bytes - device->bytes_used; |
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 33ca36b37a6a..31f7af878d96 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -518,8 +518,9 @@ again: | |||
518 | /* This is the initialized path, it is safe to release the devices. */ | 518 | /* This is the initialized path, it is safe to release the devices. */ |
519 | list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) { | 519 | list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) { |
520 | if (device->in_fs_metadata) { | 520 | if (device->in_fs_metadata) { |
521 | if (!latest_transid || | 521 | if (!device->is_tgtdev_for_dev_replace && |
522 | device->generation > latest_transid) { | 522 | (!latest_transid || |
523 | device->generation > latest_transid)) { | ||
523 | latest_devid = device->devid; | 524 | latest_devid = device->devid; |
524 | latest_transid = device->generation; | 525 | latest_transid = device->generation; |
525 | latest_bdev = device->bdev; | 526 | latest_bdev = device->bdev; |
@@ -814,7 +815,7 @@ int btrfs_account_dev_extents_size(struct btrfs_device *device, u64 start, | |||
814 | 815 | ||
815 | *length = 0; | 816 | *length = 0; |
816 | 817 | ||
817 | if (start >= device->total_bytes) | 818 | if (start >= device->total_bytes || device->is_tgtdev_for_dev_replace) |
818 | return 0; | 819 | return 0; |
819 | 820 | ||
820 | path = btrfs_alloc_path(); | 821 | path = btrfs_alloc_path(); |
@@ -931,7 +932,7 @@ int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, | |||
931 | max_hole_size = 0; | 932 | max_hole_size = 0; |
932 | hole_size = 0; | 933 | hole_size = 0; |
933 | 934 | ||
934 | if (search_start >= search_end) { | 935 | if (search_start >= search_end || device->is_tgtdev_for_dev_replace) { |
935 | ret = -ENOSPC; | 936 | ret = -ENOSPC; |
936 | goto error; | 937 | goto error; |
937 | } | 938 | } |
@@ -1114,6 +1115,7 @@ int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans, | |||
1114 | struct btrfs_key key; | 1115 | struct btrfs_key key; |
1115 | 1116 | ||
1116 | WARN_ON(!device->in_fs_metadata); | 1117 | WARN_ON(!device->in_fs_metadata); |
1118 | WARN_ON(device->is_tgtdev_for_dev_replace); | ||
1117 | path = btrfs_alloc_path(); | 1119 | path = btrfs_alloc_path(); |
1118 | if (!path) | 1120 | if (!path) |
1119 | return -ENOMEM; | 1121 | return -ENOMEM; |
@@ -1375,7 +1377,9 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) | |||
1375 | * is held. | 1377 | * is held. |
1376 | */ | 1378 | */ |
1377 | list_for_each_entry(tmp, devices, dev_list) { | 1379 | list_for_each_entry(tmp, devices, dev_list) { |
1378 | if (tmp->in_fs_metadata && !tmp->bdev) { | 1380 | if (tmp->in_fs_metadata && |
1381 | !tmp->is_tgtdev_for_dev_replace && | ||
1382 | !tmp->bdev) { | ||
1379 | device = tmp; | 1383 | device = tmp; |
1380 | break; | 1384 | break; |
1381 | } | 1385 | } |
@@ -1406,6 +1410,12 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) | |||
1406 | } | 1410 | } |
1407 | } | 1411 | } |
1408 | 1412 | ||
1413 | if (device->is_tgtdev_for_dev_replace) { | ||
1414 | pr_err("btrfs: unable to remove the dev_replace target dev\n"); | ||
1415 | ret = -EINVAL; | ||
1416 | goto error_brelse; | ||
1417 | } | ||
1418 | |||
1409 | if (device->writeable && root->fs_info->fs_devices->rw_devices == 1) { | 1419 | if (device->writeable && root->fs_info->fs_devices->rw_devices == 1) { |
1410 | printk(KERN_ERR "btrfs: unable to remove the only writeable " | 1420 | printk(KERN_ERR "btrfs: unable to remove the only writeable " |
1411 | "device\n"); | 1421 | "device\n"); |
@@ -1425,6 +1435,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) | |||
1425 | if (ret) | 1435 | if (ret) |
1426 | goto error_undo; | 1436 | goto error_undo; |
1427 | 1437 | ||
1438 | /* | ||
1439 | * TODO: the superblock still includes this device in its num_devices | ||
1440 | * counter although write_all_supers() is not locked out. This | ||
1441 | * could give a filesystem state which requires a degraded mount. | ||
1442 | */ | ||
1428 | ret = btrfs_rm_dev_item(root->fs_info->chunk_root, device); | 1443 | ret = btrfs_rm_dev_item(root->fs_info->chunk_root, device); |
1429 | if (ret) | 1444 | if (ret) |
1430 | goto error_undo; | 1445 | goto error_undo; |
@@ -1808,6 +1823,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) | |||
1808 | device->dev_root = root->fs_info->dev_root; | 1823 | device->dev_root = root->fs_info->dev_root; |
1809 | device->bdev = bdev; | 1824 | device->bdev = bdev; |
1810 | device->in_fs_metadata = 1; | 1825 | device->in_fs_metadata = 1; |
1826 | device->is_tgtdev_for_dev_replace = 0; | ||
1811 | device->mode = FMODE_EXCL; | 1827 | device->mode = FMODE_EXCL; |
1812 | set_blocksize(device->bdev, 4096); | 1828 | set_blocksize(device->bdev, 4096); |
1813 | 1829 | ||
@@ -1971,7 +1987,8 @@ static int __btrfs_grow_device(struct btrfs_trans_handle *trans, | |||
1971 | 1987 | ||
1972 | if (!device->writeable) | 1988 | if (!device->writeable) |
1973 | return -EACCES; | 1989 | return -EACCES; |
1974 | if (new_size <= device->total_bytes) | 1990 | if (new_size <= device->total_bytes || |
1991 | device->is_tgtdev_for_dev_replace) | ||
1975 | return -EINVAL; | 1992 | return -EINVAL; |
1976 | 1993 | ||
1977 | btrfs_set_super_total_bytes(super_copy, old_total + diff); | 1994 | btrfs_set_super_total_bytes(super_copy, old_total + diff); |
@@ -2600,7 +2617,8 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) | |||
2600 | size_to_free = div_factor(old_size, 1); | 2617 | size_to_free = div_factor(old_size, 1); |
2601 | size_to_free = min(size_to_free, (u64)1 * 1024 * 1024); | 2618 | size_to_free = min(size_to_free, (u64)1 * 1024 * 1024); |
2602 | if (!device->writeable || | 2619 | if (!device->writeable || |
2603 | device->total_bytes - device->bytes_used > size_to_free) | 2620 | device->total_bytes - device->bytes_used > size_to_free || |
2621 | device->is_tgtdev_for_dev_replace) | ||
2604 | continue; | 2622 | continue; |
2605 | 2623 | ||
2606 | ret = btrfs_shrink_device(device, old_size - size_to_free); | 2624 | ret = btrfs_shrink_device(device, old_size - size_to_free); |
@@ -3132,6 +3150,9 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) | |||
3132 | u64 old_size = device->total_bytes; | 3150 | u64 old_size = device->total_bytes; |
3133 | u64 diff = device->total_bytes - new_size; | 3151 | u64 diff = device->total_bytes - new_size; |
3134 | 3152 | ||
3153 | if (device->is_tgtdev_for_dev_replace) | ||
3154 | return -EINVAL; | ||
3155 | |||
3135 | path = btrfs_alloc_path(); | 3156 | path = btrfs_alloc_path(); |
3136 | if (!path) | 3157 | if (!path) |
3137 | return -ENOMEM; | 3158 | return -ENOMEM; |
@@ -3401,7 +3422,8 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, | |||
3401 | continue; | 3422 | continue; |
3402 | } | 3423 | } |
3403 | 3424 | ||
3404 | if (!device->in_fs_metadata) | 3425 | if (!device->in_fs_metadata || |
3426 | device->is_tgtdev_for_dev_replace) | ||
3405 | continue; | 3427 | continue; |
3406 | 3428 | ||
3407 | if (device->total_bytes > device->bytes_used) | 3429 | if (device->total_bytes > device->bytes_used) |
@@ -4612,6 +4634,7 @@ static void fill_device_from_item(struct extent_buffer *leaf, | |||
4612 | device->io_align = btrfs_device_io_align(leaf, dev_item); | 4634 | device->io_align = btrfs_device_io_align(leaf, dev_item); |
4613 | device->io_width = btrfs_device_io_width(leaf, dev_item); | 4635 | device->io_width = btrfs_device_io_width(leaf, dev_item); |
4614 | device->sector_size = btrfs_device_sector_size(leaf, dev_item); | 4636 | device->sector_size = btrfs_device_sector_size(leaf, dev_item); |
4637 | device->is_tgtdev_for_dev_replace = 0; | ||
4615 | 4638 | ||
4616 | ptr = (unsigned long)btrfs_device_uuid(dev_item); | 4639 | ptr = (unsigned long)btrfs_device_uuid(dev_item); |
4617 | read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE); | 4640 | read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE); |
@@ -4722,7 +4745,7 @@ static int read_one_dev(struct btrfs_root *root, | |||
4722 | fill_device_from_item(leaf, dev_item, device); | 4745 | fill_device_from_item(leaf, dev_item, device); |
4723 | device->dev_root = root->fs_info->dev_root; | 4746 | device->dev_root = root->fs_info->dev_root; |
4724 | device->in_fs_metadata = 1; | 4747 | device->in_fs_metadata = 1; |
4725 | if (device->writeable) { | 4748 | if (device->writeable && !device->is_tgtdev_for_dev_replace) { |
4726 | device->fs_devices->total_rw_bytes += device->total_bytes; | 4749 | device->fs_devices->total_rw_bytes += device->total_bytes; |
4727 | spin_lock(&root->fs_info->free_chunk_lock); | 4750 | spin_lock(&root->fs_info->free_chunk_lock); |
4728 | root->fs_info->free_chunk_space += device->total_bytes - | 4751 | root->fs_info->free_chunk_space += device->total_bytes - |
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 802e2ba02f09..8fd5a4d8acc8 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h | |||
@@ -50,6 +50,7 @@ struct btrfs_device { | |||
50 | int in_fs_metadata; | 50 | int in_fs_metadata; |
51 | int missing; | 51 | int missing; |
52 | int can_discard; | 52 | int can_discard; |
53 | int is_tgtdev_for_dev_replace; | ||
53 | 54 | ||
54 | spinlock_t io_lock; | 55 | spinlock_t io_lock; |
55 | 56 | ||