Skip to content

Commit 63a212a

Browse files
Stefan BehrensJosef Bacik
authored andcommitted
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 <[email protected]> Signed-off-by: Chris Mason <[email protected]>
1 parent 5ac00ad commit 63a212a

File tree

7 files changed

+54
-18
lines changed

7 files changed

+54
-18
lines changed

fs/btrfs/ctree.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3649,7 +3649,7 @@ int btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
36493649
/* scrub.c */
36503650
int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
36513651
u64 end, struct btrfs_scrub_progress *progress,
3652-
int readonly);
3652+
int readonly, int is_dev_replace);
36533653
void btrfs_scrub_pause(struct btrfs_root *root);
36543654
void btrfs_scrub_pause_super(struct btrfs_root *root);
36553655
void btrfs_scrub_continue(struct btrfs_root *root);

fs/btrfs/extent-tree.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7468,7 +7468,8 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr)
74687468
* check to make sure we can actually find a chunk with enough
74697469
* space to fit our block group in.
74707470
*/
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) {
74727473
ret = find_free_dev_extent(device, min_free,
74737474
&dev_offset, NULL);
74747475
if (!ret)

fs/btrfs/ioctl.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,11 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,
13751375
}
13761376
}
13771377

1378+
if (device->is_tgtdev_for_dev_replace) {
1379+
ret = -EINVAL;
1380+
goto out_free;
1381+
}
1382+
13781383
old_size = device->total_bytes;
13791384

13801385
if (mod < 0) {
@@ -3102,7 +3107,8 @@ static long btrfs_ioctl_scrub(struct btrfs_root *root, void __user *arg)
31023107
return PTR_ERR(sa);
31033108

31043109
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);
31063112

31073113
if (copy_to_user(arg, sa, sizeof(*sa)))
31083114
ret = -EFAULT;

fs/btrfs/scrub.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ struct scrub_ctx {
116116
u32 sectorsize;
117117
u32 nodesize;
118118
u32 leafsize;
119+
120+
int is_dev_replace;
121+
119122
/*
120123
* statistics
121124
*/
@@ -284,7 +287,7 @@ static noinline_for_stack void scrub_free_ctx(struct scrub_ctx *sctx)
284287
}
285288

286289
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)
288291
{
289292
struct scrub_ctx *sctx;
290293
int i;
@@ -296,6 +299,7 @@ struct scrub_ctx *scrub_setup_ctx(struct btrfs_device *dev)
296299
sctx = kzalloc(sizeof(*sctx), GFP_NOFS);
297300
if (!sctx)
298301
goto nomem;
302+
sctx->is_dev_replace = is_dev_replace;
299303
sctx->pages_per_bio = pages_per_bio;
300304
sctx->curr = -1;
301305
sctx->dev_root = dev->dev_root;
@@ -2293,7 +2297,7 @@ static noinline_for_stack void scrub_workers_put(struct btrfs_fs_info *fs_info)
22932297

22942298
int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
22952299
u64 end, struct btrfs_scrub_progress *progress,
2296-
int readonly)
2300+
int readonly, int is_dev_replace)
22972301
{
22982302
struct scrub_ctx *sctx;
22992303
int ret;
@@ -2356,14 +2360,14 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
23562360

23572361
mutex_lock(&fs_info->fs_devices->device_list_mutex);
23582362
dev = btrfs_find_device(fs_info, devid, NULL, NULL);
2359-
if (!dev || dev->missing) {
2363+
if (!dev || (dev->missing && !is_dev_replace)) {
23602364
mutex_unlock(&fs_info->fs_devices->device_list_mutex);
23612365
scrub_workers_put(fs_info);
23622366
return -ENODEV;
23632367
}
23642368
mutex_lock(&fs_info->scrub_lock);
23652369

2366-
if (!dev->in_fs_metadata) {
2370+
if (!dev->in_fs_metadata || dev->is_tgtdev_for_dev_replace) {
23672371
mutex_unlock(&fs_info->scrub_lock);
23682372
mutex_unlock(&fs_info->fs_devices->device_list_mutex);
23692373
scrub_workers_put(fs_info);
@@ -2376,7 +2380,7 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
23762380
scrub_workers_put(fs_info);
23772381
return -EINPROGRESS;
23782382
}
2379-
sctx = scrub_setup_ctx(dev);
2383+
sctx = scrub_setup_ctx(dev, is_dev_replace);
23802384
if (IS_ERR(sctx)) {
23812385
mutex_unlock(&fs_info->scrub_lock);
23822386
mutex_unlock(&fs_info->fs_devices->device_list_mutex);

fs/btrfs/super.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1354,7 +1354,8 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes)
13541354
min_stripe_size = BTRFS_STRIPE_LEN;
13551355

13561356
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)
13581359
continue;
13591360

13601361
avail_space = device->total_bytes - device->bytes_used;

fs/btrfs/volumes.c

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -518,8 +518,9 @@ void btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
518518
/* This is the initialized path, it is safe to release the devices. */
519519
list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {
520520
if (device->in_fs_metadata) {
521-
if (!latest_transid ||
522-
device->generation > latest_transid) {
521+
if (!device->is_tgtdev_for_dev_replace &&
522+
(!latest_transid ||
523+
device->generation > latest_transid)) {
523524
latest_devid = device->devid;
524525
latest_transid = device->generation;
525526
latest_bdev = device->bdev;
@@ -814,7 +815,7 @@ int btrfs_account_dev_extents_size(struct btrfs_device *device, u64 start,
814815

815816
*length = 0;
816817

817-
if (start >= device->total_bytes)
818+
if (start >= device->total_bytes || device->is_tgtdev_for_dev_replace)
818819
return 0;
819820

820821
path = btrfs_alloc_path();
@@ -931,7 +932,7 @@ int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,
931932
max_hole_size = 0;
932933
hole_size = 0;
933934

934-
if (search_start >= search_end) {
935+
if (search_start >= search_end || device->is_tgtdev_for_dev_replace) {
935936
ret = -ENOSPC;
936937
goto error;
937938
}
@@ -1114,6 +1115,7 @@ int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
11141115
struct btrfs_key key;
11151116

11161117
WARN_ON(!device->in_fs_metadata);
1118+
WARN_ON(device->is_tgtdev_for_dev_replace);
11171119
path = btrfs_alloc_path();
11181120
if (!path)
11191121
return -ENOMEM;
@@ -1375,7 +1377,9 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
13751377
* is held.
13761378
*/
13771379
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) {
13791383
device = tmp;
13801384
break;
13811385
}
@@ -1406,6 +1410,12 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
14061410
}
14071411
}
14081412

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+
14091419
if (device->writeable && root->fs_info->fs_devices->rw_devices == 1) {
14101420
printk(KERN_ERR "btrfs: unable to remove the only writeable "
14111421
"device\n");
@@ -1425,6 +1435,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
14251435
if (ret)
14261436
goto error_undo;
14271437

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+
*/
14281443
ret = btrfs_rm_dev_item(root->fs_info->chunk_root, device);
14291444
if (ret)
14301445
goto error_undo;
@@ -1808,6 +1823,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
18081823
device->dev_root = root->fs_info->dev_root;
18091824
device->bdev = bdev;
18101825
device->in_fs_metadata = 1;
1826+
device->is_tgtdev_for_dev_replace = 0;
18111827
device->mode = FMODE_EXCL;
18121828
set_blocksize(device->bdev, 4096);
18131829

@@ -1971,7 +1987,8 @@ static int __btrfs_grow_device(struct btrfs_trans_handle *trans,
19711987

19721988
if (!device->writeable)
19731989
return -EACCES;
1974-
if (new_size <= device->total_bytes)
1990+
if (new_size <= device->total_bytes ||
1991+
device->is_tgtdev_for_dev_replace)
19751992
return -EINVAL;
19761993

19771994
btrfs_set_super_total_bytes(super_copy, old_total + diff);
@@ -2600,7 +2617,8 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info)
26002617
size_to_free = div_factor(old_size, 1);
26012618
size_to_free = min(size_to_free, (u64)1 * 1024 * 1024);
26022619
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)
26042622
continue;
26052623

26062624
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)
31323150
u64 old_size = device->total_bytes;
31333151
u64 diff = device->total_bytes - new_size;
31343152

3153+
if (device->is_tgtdev_for_dev_replace)
3154+
return -EINVAL;
3155+
31353156
path = btrfs_alloc_path();
31363157
if (!path)
31373158
return -ENOMEM;
@@ -3401,7 +3422,8 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
34013422
continue;
34023423
}
34033424

3404-
if (!device->in_fs_metadata)
3425+
if (!device->in_fs_metadata ||
3426+
device->is_tgtdev_for_dev_replace)
34053427
continue;
34063428

34073429
if (device->total_bytes > device->bytes_used)
@@ -4612,6 +4634,7 @@ static void fill_device_from_item(struct extent_buffer *leaf,
46124634
device->io_align = btrfs_device_io_align(leaf, dev_item);
46134635
device->io_width = btrfs_device_io_width(leaf, dev_item);
46144636
device->sector_size = btrfs_device_sector_size(leaf, dev_item);
4637+
device->is_tgtdev_for_dev_replace = 0;
46154638

46164639
ptr = (unsigned long)btrfs_device_uuid(dev_item);
46174640
read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
@@ -4722,7 +4745,7 @@ static int read_one_dev(struct btrfs_root *root,
47224745
fill_device_from_item(leaf, dev_item, device);
47234746
device->dev_root = root->fs_info->dev_root;
47244747
device->in_fs_metadata = 1;
4725-
if (device->writeable) {
4748+
if (device->writeable && !device->is_tgtdev_for_dev_replace) {
47264749
device->fs_devices->total_rw_bytes += device->total_bytes;
47274750
spin_lock(&root->fs_info->free_chunk_lock);
47284751
root->fs_info->free_chunk_space += device->total_bytes -

fs/btrfs/volumes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ struct btrfs_device {
5050
int in_fs_metadata;
5151
int missing;
5252
int can_discard;
53+
int is_tgtdev_for_dev_replace;
5354

5455
spinlock_t io_lock;
5556

0 commit comments

Comments
 (0)