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