diff options
Diffstat (limited to 'fs/btrfs/dev-replace.c')
-rw-r--r-- | fs/btrfs/dev-replace.c | 101 |
1 files changed, 45 insertions, 56 deletions
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 26bcb487f958..85f12e6e28d2 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c | |||
@@ -44,9 +44,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree( | |||
44 | struct btrfs_fs_info *fs_info, | 44 | struct btrfs_fs_info *fs_info, |
45 | struct btrfs_device *srcdev, | 45 | struct btrfs_device *srcdev, |
46 | struct btrfs_device *tgtdev); | 46 | struct btrfs_device *tgtdev); |
47 | static int btrfs_dev_replace_find_srcdev(struct btrfs_root *root, u64 srcdevid, | ||
48 | char *srcdev_name, | ||
49 | struct btrfs_device **device); | ||
50 | static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info); | 47 | static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info); |
51 | static int btrfs_dev_replace_kthread(void *data); | 48 | static int btrfs_dev_replace_kthread(void *data); |
52 | static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info); | 49 | static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info); |
@@ -305,8 +302,8 @@ void btrfs_after_dev_replace_commit(struct btrfs_fs_info *fs_info) | |||
305 | dev_replace->cursor_left_last_write_of_item; | 302 | dev_replace->cursor_left_last_write_of_item; |
306 | } | 303 | } |
307 | 304 | ||
308 | int btrfs_dev_replace_start(struct btrfs_root *root, | 305 | int btrfs_dev_replace_start(struct btrfs_root *root, char *tgtdev_name, |
309 | struct btrfs_ioctl_dev_replace_args *args) | 306 | u64 srcdevid, char *srcdev_name, int read_src) |
310 | { | 307 | { |
311 | struct btrfs_trans_handle *trans; | 308 | struct btrfs_trans_handle *trans; |
312 | struct btrfs_fs_info *fs_info = root->fs_info; | 309 | struct btrfs_fs_info *fs_info = root->fs_info; |
@@ -315,29 +312,16 @@ int btrfs_dev_replace_start(struct btrfs_root *root, | |||
315 | struct btrfs_device *tgt_device = NULL; | 312 | struct btrfs_device *tgt_device = NULL; |
316 | struct btrfs_device *src_device = NULL; | 313 | struct btrfs_device *src_device = NULL; |
317 | 314 | ||
318 | switch (args->start.cont_reading_from_srcdev_mode) { | ||
319 | case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS: | ||
320 | case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID: | ||
321 | break; | ||
322 | default: | ||
323 | return -EINVAL; | ||
324 | } | ||
325 | |||
326 | if ((args->start.srcdevid == 0 && args->start.srcdev_name[0] == '\0') || | ||
327 | args->start.tgtdev_name[0] == '\0') | ||
328 | return -EINVAL; | ||
329 | |||
330 | /* the disk copy procedure reuses the scrub code */ | 315 | /* the disk copy procedure reuses the scrub code */ |
331 | mutex_lock(&fs_info->volume_mutex); | 316 | mutex_lock(&fs_info->volume_mutex); |
332 | ret = btrfs_dev_replace_find_srcdev(root, args->start.srcdevid, | 317 | ret = btrfs_find_device_by_devspec(root, srcdevid, |
333 | args->start.srcdev_name, | 318 | srcdev_name, &src_device); |
334 | &src_device); | ||
335 | if (ret) { | 319 | if (ret) { |
336 | mutex_unlock(&fs_info->volume_mutex); | 320 | mutex_unlock(&fs_info->volume_mutex); |
337 | return ret; | 321 | return ret; |
338 | } | 322 | } |
339 | 323 | ||
340 | ret = btrfs_init_dev_replace_tgtdev(root, args->start.tgtdev_name, | 324 | ret = btrfs_init_dev_replace_tgtdev(root, tgtdev_name, |
341 | src_device, &tgt_device); | 325 | src_device, &tgt_device); |
342 | mutex_unlock(&fs_info->volume_mutex); | 326 | mutex_unlock(&fs_info->volume_mutex); |
343 | if (ret) | 327 | if (ret) |
@@ -364,18 +348,17 @@ int btrfs_dev_replace_start(struct btrfs_root *root, | |||
364 | break; | 348 | break; |
365 | case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: | 349 | case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: |
366 | case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: | 350 | case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: |
367 | args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED; | 351 | ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED; |
368 | goto leave; | 352 | goto leave; |
369 | } | 353 | } |
370 | 354 | ||
371 | dev_replace->cont_reading_from_srcdev_mode = | 355 | dev_replace->cont_reading_from_srcdev_mode = read_src; |
372 | args->start.cont_reading_from_srcdev_mode; | ||
373 | WARN_ON(!src_device); | 356 | WARN_ON(!src_device); |
374 | dev_replace->srcdev = src_device; | 357 | dev_replace->srcdev = src_device; |
375 | WARN_ON(!tgt_device); | 358 | WARN_ON(!tgt_device); |
376 | dev_replace->tgtdev = tgt_device; | 359 | dev_replace->tgtdev = tgt_device; |
377 | 360 | ||
378 | btrfs_info_in_rcu(root->fs_info, | 361 | btrfs_info_in_rcu(fs_info, |
379 | "dev_replace from %s (devid %llu) to %s started", | 362 | "dev_replace from %s (devid %llu) to %s started", |
380 | src_device->missing ? "<missing disk>" : | 363 | src_device->missing ? "<missing disk>" : |
381 | rcu_str_deref(src_device->name), | 364 | rcu_str_deref(src_device->name), |
@@ -396,14 +379,13 @@ int btrfs_dev_replace_start(struct btrfs_root *root, | |||
396 | dev_replace->item_needs_writeback = 1; | 379 | dev_replace->item_needs_writeback = 1; |
397 | atomic64_set(&dev_replace->num_write_errors, 0); | 380 | atomic64_set(&dev_replace->num_write_errors, 0); |
398 | atomic64_set(&dev_replace->num_uncorrectable_read_errors, 0); | 381 | atomic64_set(&dev_replace->num_uncorrectable_read_errors, 0); |
399 | args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; | ||
400 | btrfs_dev_replace_unlock(dev_replace, 1); | 382 | btrfs_dev_replace_unlock(dev_replace, 1); |
401 | 383 | ||
402 | ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device); | 384 | ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device); |
403 | if (ret) | 385 | if (ret) |
404 | btrfs_err(root->fs_info, "kobj add dev failed %d\n", ret); | 386 | btrfs_err(fs_info, "kobj add dev failed %d\n", ret); |
405 | 387 | ||
406 | btrfs_wait_ordered_roots(root->fs_info, -1); | 388 | btrfs_wait_ordered_roots(root->fs_info, -1, 0, (u64)-1); |
407 | 389 | ||
408 | /* force writing the updated state information to disk */ | 390 | /* force writing the updated state information to disk */ |
409 | trans = btrfs_start_transaction(root, 0); | 391 | trans = btrfs_start_transaction(root, 0); |
@@ -421,11 +403,9 @@ int btrfs_dev_replace_start(struct btrfs_root *root, | |||
421 | btrfs_device_get_total_bytes(src_device), | 403 | btrfs_device_get_total_bytes(src_device), |
422 | &dev_replace->scrub_progress, 0, 1); | 404 | &dev_replace->scrub_progress, 0, 1); |
423 | 405 | ||
424 | ret = btrfs_dev_replace_finishing(root->fs_info, ret); | 406 | ret = btrfs_dev_replace_finishing(fs_info, ret); |
425 | /* don't warn if EINPROGRESS, someone else might be running scrub */ | ||
426 | if (ret == -EINPROGRESS) { | 407 | if (ret == -EINPROGRESS) { |
427 | args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS; | 408 | ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS; |
428 | ret = 0; | ||
429 | } else { | 409 | } else { |
430 | WARN_ON(ret); | 410 | WARN_ON(ret); |
431 | } | 411 | } |
@@ -440,6 +420,35 @@ leave: | |||
440 | return ret; | 420 | return ret; |
441 | } | 421 | } |
442 | 422 | ||
423 | int btrfs_dev_replace_by_ioctl(struct btrfs_root *root, | ||
424 | struct btrfs_ioctl_dev_replace_args *args) | ||
425 | { | ||
426 | int ret; | ||
427 | |||
428 | switch (args->start.cont_reading_from_srcdev_mode) { | ||
429 | case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS: | ||
430 | case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID: | ||
431 | break; | ||
432 | default: | ||
433 | return -EINVAL; | ||
434 | } | ||
435 | |||
436 | if ((args->start.srcdevid == 0 && args->start.srcdev_name[0] == '\0') || | ||
437 | args->start.tgtdev_name[0] == '\0') | ||
438 | return -EINVAL; | ||
439 | |||
440 | ret = btrfs_dev_replace_start(root, args->start.tgtdev_name, | ||
441 | args->start.srcdevid, | ||
442 | args->start.srcdev_name, | ||
443 | args->start.cont_reading_from_srcdev_mode); | ||
444 | args->result = ret; | ||
445 | /* don't warn if EINPROGRESS, someone else might be running scrub */ | ||
446 | if (ret == BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS) | ||
447 | ret = 0; | ||
448 | |||
449 | return ret; | ||
450 | } | ||
451 | |||
443 | /* | 452 | /* |
444 | * blocked until all flighting bios are finished. | 453 | * blocked until all flighting bios are finished. |
445 | */ | 454 | */ |
@@ -495,7 +504,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, | |||
495 | mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); | 504 | mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); |
496 | return ret; | 505 | return ret; |
497 | } | 506 | } |
498 | btrfs_wait_ordered_roots(root->fs_info, -1); | 507 | btrfs_wait_ordered_roots(root->fs_info, -1, 0, (u64)-1); |
499 | 508 | ||
500 | trans = btrfs_start_transaction(root, 0); | 509 | trans = btrfs_start_transaction(root, 0); |
501 | if (IS_ERR(trans)) { | 510 | if (IS_ERR(trans)) { |
@@ -560,10 +569,9 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, | |||
560 | ASSERT(list_empty(&src_device->resized_list)); | 569 | ASSERT(list_empty(&src_device->resized_list)); |
561 | tgt_device->commit_total_bytes = src_device->commit_total_bytes; | 570 | tgt_device->commit_total_bytes = src_device->commit_total_bytes; |
562 | tgt_device->commit_bytes_used = src_device->bytes_used; | 571 | tgt_device->commit_bytes_used = src_device->bytes_used; |
563 | if (fs_info->sb->s_bdev == src_device->bdev) | 572 | |
564 | fs_info->sb->s_bdev = tgt_device->bdev; | 573 | btrfs_assign_next_active_device(fs_info, src_device, tgt_device); |
565 | if (fs_info->fs_devices->latest_bdev == src_device->bdev) | 574 | |
566 | fs_info->fs_devices->latest_bdev = tgt_device->bdev; | ||
567 | list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list); | 575 | list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list); |
568 | fs_info->fs_devices->rw_devices++; | 576 | fs_info->fs_devices->rw_devices++; |
569 | 577 | ||
@@ -626,25 +634,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree( | |||
626 | write_unlock(&em_tree->lock); | 634 | write_unlock(&em_tree->lock); |
627 | } | 635 | } |
628 | 636 | ||
629 | static int btrfs_dev_replace_find_srcdev(struct btrfs_root *root, u64 srcdevid, | ||
630 | char *srcdev_name, | ||
631 | struct btrfs_device **device) | ||
632 | { | ||
633 | int ret; | ||
634 | |||
635 | if (srcdevid) { | ||
636 | ret = 0; | ||
637 | *device = btrfs_find_device(root->fs_info, srcdevid, NULL, | ||
638 | NULL); | ||
639 | if (!*device) | ||
640 | ret = -ENOENT; | ||
641 | } else { | ||
642 | ret = btrfs_find_device_missing_or_by_path(root, srcdev_name, | ||
643 | device); | ||
644 | } | ||
645 | return ret; | ||
646 | } | ||
647 | |||
648 | void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info, | 637 | void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info, |
649 | struct btrfs_ioctl_dev_replace_args *args) | 638 | struct btrfs_ioctl_dev_replace_args *args) |
650 | { | 639 | { |