aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorStefan Behrens <sbehrens@giantdisaster.de>2012-11-05 12:29:28 -0500
committerJosef Bacik <jbacik@fusionio.com>2012-12-12 17:15:39 -0500
commit63a212abc2315972b245f93cb11ae3acf3c0b513 (patch)
tree65d9f4020795b6a3521bf0a94922a082ae4ede6f /fs
parent5ac00addc7ac09110995fe967071d191b5981cc1 (diff)
Btrfs: disallow some operations on the device replace target device
This patch adds some code to disallow operations on the device that is used as the target for the device replace operation. Signed-off-by: Stefan Behrens <sbehrens@giantdisaster.de> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/ctree.h2
-rw-r--r--fs/btrfs/extent-tree.c3
-rw-r--r--fs/btrfs/ioctl.c8
-rw-r--r--fs/btrfs/scrub.c14
-rw-r--r--fs/btrfs/super.c3
-rw-r--r--fs/btrfs/volumes.c41
-rw-r--r--fs/btrfs/volumes.h1
7 files changed, 54 insertions, 18 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index e9dc78014f0..746cb6aa1f6 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 */
3650int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, 3650int 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);
3653void btrfs_scrub_pause(struct btrfs_root *root); 3653void btrfs_scrub_pause(struct btrfs_root *root);
3654void btrfs_scrub_pause_super(struct btrfs_root *root); 3654void btrfs_scrub_pause_super(struct btrfs_root *root);
3655void btrfs_scrub_continue(struct btrfs_root *root); 3655void btrfs_scrub_continue(struct btrfs_root *root);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index b4d438f6c2b..98af8379895 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 26f46dad3b0..e54b5e50c92 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 6cf23f4f7bb..460e30bb188 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
286static noinline_for_stack 289static noinline_for_stack
287struct scrub_ctx *scrub_setup_ctx(struct btrfs_device *dev) 290struct 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
2294int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, 2298int 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 ef2415896b0..837ad2d2785 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 33ca36b37a6..31f7af878d9 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 802e2ba02f0..8fd5a4d8acc 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