* Attach a device to a mirror. The arguments are the path to any device * in the mirror, and the nvroot for the new device. If the path specifies * a device that is not mirrored, we automatically insert the mirror vdev. * * If 'replacing' is specified, the new device is intended to replace the * existing device; in this case the two devices are made into their own * mirror using the 'repla
| 6595 | * an administrators perspective these are both resilver operations. |
| 6596 | */ |
| 6597 | int |
| 6598 | spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing, |
| 6599 | int rebuild) |
| 6600 | { |
| 6601 | uint64_t txg, dtl_max_txg; |
| 6602 | vdev_t *rvd = spa->spa_root_vdev; |
| 6603 | vdev_t *oldvd, *newvd, *newrootvd, *pvd, *tvd; |
| 6604 | vdev_ops_t *pvops; |
| 6605 | char *oldvdpath, *newvdpath; |
| 6606 | int newvd_isspare; |
| 6607 | int error; |
| 6608 | |
| 6609 | ASSERT(spa_writeable(spa)); |
| 6610 | |
| 6611 | txg = spa_vdev_enter(spa); |
| 6612 | |
| 6613 | oldvd = spa_lookup_by_guid(spa, guid, B_FALSE); |
| 6614 | |
| 6615 | ASSERT(MUTEX_HELD(&spa_namespace_lock)); |
| 6616 | if (spa_feature_is_active(spa, SPA_FEATURE_POOL_CHECKPOINT)) { |
| 6617 | error = (spa_has_checkpoint(spa)) ? |
| 6618 | ZFS_ERR_CHECKPOINT_EXISTS : ZFS_ERR_DISCARDING_CHECKPOINT; |
| 6619 | return (spa_vdev_exit(spa, NULL, txg, error)); |
| 6620 | } |
| 6621 | |
| 6622 | if (rebuild) { |
| 6623 | if (!spa_feature_is_enabled(spa, SPA_FEATURE_DEVICE_REBUILD)) |
| 6624 | return (spa_vdev_exit(spa, NULL, txg, ENOTSUP)); |
| 6625 | |
| 6626 | if (dsl_scan_resilvering(spa_get_dsl(spa))) |
| 6627 | return (spa_vdev_exit(spa, NULL, txg, |
| 6628 | ZFS_ERR_RESILVER_IN_PROGRESS)); |
| 6629 | } else { |
| 6630 | if (vdev_rebuild_active(rvd)) |
| 6631 | return (spa_vdev_exit(spa, NULL, txg, |
| 6632 | ZFS_ERR_REBUILD_IN_PROGRESS)); |
| 6633 | } |
| 6634 | |
| 6635 | if (spa->spa_vdev_removal != NULL) |
| 6636 | return (spa_vdev_exit(spa, NULL, txg, EBUSY)); |
| 6637 | |
| 6638 | if (oldvd == NULL) |
| 6639 | return (spa_vdev_exit(spa, NULL, txg, ENODEV)); |
| 6640 | |
| 6641 | if (!oldvd->vdev_ops->vdev_op_leaf) |
| 6642 | return (spa_vdev_exit(spa, NULL, txg, ENOTSUP)); |
| 6643 | |
| 6644 | pvd = oldvd->vdev_parent; |
| 6645 | |
| 6646 | if ((error = spa_config_parse(spa, &newrootvd, nvroot, NULL, 0, |
| 6647 | VDEV_ALLOC_ATTACH)) != 0) |
| 6648 | return (spa_vdev_exit(spa, NULL, txg, EINVAL)); |
| 6649 | |
| 6650 | if (newrootvd->vdev_children != 1) |
| 6651 | return (spa_vdev_exit(spa, newrootvd, txg, EINVAL)); |
| 6652 | |
| 6653 | newvd = newrootvd->vdev_child[0]; |
| 6654 |