mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-25 03:37:45 +03:00
Add device rebuild feature
The device_rebuild feature enables sequential reconstruction when
resilvering. Mirror vdevs can be rebuilt in LBA order which may
more quickly restore redundancy depending on the pools average block
size, overall fragmentation and the performance characteristics
of the devices. However, block checksums cannot be verified
as part of the rebuild thus a scrub is automatically started after
the sequential resilver completes.
The new '-s' option has been added to the `zpool attach` and
`zpool replace` command to request sequential reconstruction
instead of healing reconstruction when resilvering.
zpool attach -s <pool> <existing vdev> <new vdev>
zpool replace -s <pool> <old vdev> <new vdev>
The `zpool status` output has been updated to report the progress
of sequential resilvering in the same way as healing resilvering.
The one notable difference is that multiple sequential resilvers
may be in progress as long as they're operating on different
top-level vdevs.
The `zpool wait -t resilver` command was extended to wait on
sequential resilvers. From this perspective they are no different
than healing resilvers.
Sequential resilvers cannot be supported for RAIDZ, but are
compatible with the dRAID feature being developed.
As part of this change the resilver_restart_* tests were moved
in to the functional/replacement directory. Additionally, the
replacement tests were renamed and extended to verify both
resilvering and rebuilding.
Original-patch-by: Isaac Huang <he.huang@intel.com>
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Reviewed-by: John Poduska <jpoduska@datto.com>
Co-authored-by: Mark Maybee <mmaybee@cray.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #10349
This commit is contained in:
+187
-51
@@ -39,6 +39,7 @@
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/dsl_dir.h>
|
||||
#include <sys/vdev_impl.h>
|
||||
#include <sys/vdev_rebuild.h>
|
||||
#include <sys/uberblock_impl.h>
|
||||
#include <sys/metaslab.h>
|
||||
#include <sys/metaslab_impl.h>
|
||||
@@ -551,10 +552,12 @@ vdev_alloc_common(spa_t *spa, uint_t id, uint64_t guid, vdev_ops_t *ops)
|
||||
mutex_init(&vd->vdev_stat_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
mutex_init(&vd->vdev_probe_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
mutex_init(&vd->vdev_scan_io_queue_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
|
||||
mutex_init(&vd->vdev_initialize_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
mutex_init(&vd->vdev_initialize_io_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&vd->vdev_initialize_cv, NULL, CV_DEFAULT, NULL);
|
||||
cv_init(&vd->vdev_initialize_io_cv, NULL, CV_DEFAULT, NULL);
|
||||
|
||||
mutex_init(&vd->vdev_trim_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
mutex_init(&vd->vdev_autotrim_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
mutex_init(&vd->vdev_trim_io_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
@@ -562,10 +565,16 @@ vdev_alloc_common(spa_t *spa, uint_t id, uint64_t guid, vdev_ops_t *ops)
|
||||
cv_init(&vd->vdev_autotrim_cv, NULL, CV_DEFAULT, NULL);
|
||||
cv_init(&vd->vdev_trim_io_cv, NULL, CV_DEFAULT, NULL);
|
||||
|
||||
mutex_init(&vd->vdev_rebuild_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
mutex_init(&vd->vdev_rebuild_io_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&vd->vdev_rebuild_cv, NULL, CV_DEFAULT, NULL);
|
||||
cv_init(&vd->vdev_rebuild_io_cv, NULL, CV_DEFAULT, NULL);
|
||||
|
||||
for (int t = 0; t < DTL_TYPES; t++) {
|
||||
vd->vdev_dtl[t] = range_tree_create(NULL, RANGE_SEG64, NULL, 0,
|
||||
0);
|
||||
}
|
||||
|
||||
txg_list_create(&vd->vdev_ms_list, spa,
|
||||
offsetof(struct metaslab, ms_txg_node));
|
||||
txg_list_create(&vd->vdev_dtl_list, spa,
|
||||
@@ -835,6 +844,9 @@ vdev_alloc(spa_t *spa, vdev_t **vdp, nvlist_t *nv, vdev_t *parent, uint_t id,
|
||||
(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_RESILVER_TXG,
|
||||
&vd->vdev_resilver_txg);
|
||||
|
||||
(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_REBUILD_TXG,
|
||||
&vd->vdev_rebuild_txg);
|
||||
|
||||
if (nvlist_exists(nv, ZPOOL_CONFIG_RESILVER_DEFER))
|
||||
vdev_defer_resilver(vd);
|
||||
|
||||
@@ -890,6 +902,7 @@ vdev_free(vdev_t *vd)
|
||||
ASSERT3P(vd->vdev_initialize_thread, ==, NULL);
|
||||
ASSERT3P(vd->vdev_trim_thread, ==, NULL);
|
||||
ASSERT3P(vd->vdev_autotrim_thread, ==, NULL);
|
||||
ASSERT3P(vd->vdev_rebuild_thread, ==, NULL);
|
||||
|
||||
/*
|
||||
* Scan queues are normally destroyed at the end of a scan. If the
|
||||
@@ -998,10 +1011,12 @@ vdev_free(vdev_t *vd)
|
||||
mutex_destroy(&vd->vdev_stat_lock);
|
||||
mutex_destroy(&vd->vdev_probe_lock);
|
||||
mutex_destroy(&vd->vdev_scan_io_queue_lock);
|
||||
|
||||
mutex_destroy(&vd->vdev_initialize_lock);
|
||||
mutex_destroy(&vd->vdev_initialize_io_lock);
|
||||
cv_destroy(&vd->vdev_initialize_io_cv);
|
||||
cv_destroy(&vd->vdev_initialize_cv);
|
||||
|
||||
mutex_destroy(&vd->vdev_trim_lock);
|
||||
mutex_destroy(&vd->vdev_autotrim_lock);
|
||||
mutex_destroy(&vd->vdev_trim_io_lock);
|
||||
@@ -1009,6 +1024,11 @@ vdev_free(vdev_t *vd)
|
||||
cv_destroy(&vd->vdev_autotrim_cv);
|
||||
cv_destroy(&vd->vdev_trim_io_cv);
|
||||
|
||||
mutex_destroy(&vd->vdev_rebuild_lock);
|
||||
mutex_destroy(&vd->vdev_rebuild_io_lock);
|
||||
cv_destroy(&vd->vdev_rebuild_cv);
|
||||
cv_destroy(&vd->vdev_rebuild_io_cv);
|
||||
|
||||
zfs_ratelimit_fini(&vd->vdev_delay_rl);
|
||||
zfs_ratelimit_fini(&vd->vdev_checksum_rl);
|
||||
|
||||
@@ -1078,7 +1098,10 @@ vdev_top_transfer(vdev_t *svd, vdev_t *tvd)
|
||||
ASSERT3P(tvd->vdev_indirect_births, ==, NULL);
|
||||
ASSERT3P(tvd->vdev_obsolete_sm, ==, NULL);
|
||||
ASSERT0(tvd->vdev_removing);
|
||||
ASSERT0(tvd->vdev_rebuilding);
|
||||
tvd->vdev_removing = svd->vdev_removing;
|
||||
tvd->vdev_rebuilding = svd->vdev_rebuilding;
|
||||
tvd->vdev_rebuild_config = svd->vdev_rebuild_config;
|
||||
tvd->vdev_indirect_config = svd->vdev_indirect_config;
|
||||
tvd->vdev_indirect_mapping = svd->vdev_indirect_mapping;
|
||||
tvd->vdev_indirect_births = svd->vdev_indirect_births;
|
||||
@@ -1092,6 +1115,7 @@ vdev_top_transfer(vdev_t *svd, vdev_t *tvd)
|
||||
svd->vdev_indirect_births = NULL;
|
||||
svd->vdev_obsolete_sm = NULL;
|
||||
svd->vdev_removing = 0;
|
||||
svd->vdev_rebuilding = 0;
|
||||
|
||||
for (t = 0; t < TXG_SIZE; t++) {
|
||||
while ((msp = txg_list_remove(&svd->vdev_ms_list, t)) != NULL)
|
||||
@@ -2576,11 +2600,8 @@ vdev_dtl_max(vdev_t *vd)
|
||||
* excise the DTLs.
|
||||
*/
|
||||
static boolean_t
|
||||
vdev_dtl_should_excise(vdev_t *vd)
|
||||
vdev_dtl_should_excise(vdev_t *vd, boolean_t rebuild_done)
|
||||
{
|
||||
spa_t *spa = vd->vdev_spa;
|
||||
dsl_scan_t *scn = spa->spa_dsl_pool->dp_scan;
|
||||
|
||||
ASSERT0(vd->vdev_children);
|
||||
|
||||
if (vd->vdev_state < VDEV_STATE_DEGRADED)
|
||||
@@ -2589,23 +2610,52 @@ vdev_dtl_should_excise(vdev_t *vd)
|
||||
if (vd->vdev_resilver_deferred)
|
||||
return (B_FALSE);
|
||||
|
||||
if (vd->vdev_resilver_txg == 0 ||
|
||||
range_tree_is_empty(vd->vdev_dtl[DTL_MISSING]))
|
||||
if (range_tree_is_empty(vd->vdev_dtl[DTL_MISSING]))
|
||||
return (B_TRUE);
|
||||
|
||||
/*
|
||||
* When a resilver is initiated the scan will assign the scn_max_txg
|
||||
* value to the highest txg value that exists in all DTLs. If this
|
||||
* device's max DTL is not part of this scan (i.e. it is not in
|
||||
* the range (scn_min_txg, scn_max_txg] then it is not eligible
|
||||
* for excision.
|
||||
*/
|
||||
if (vdev_dtl_max(vd) <= scn->scn_phys.scn_max_txg) {
|
||||
ASSERT3U(scn->scn_phys.scn_min_txg, <=, vdev_dtl_min(vd));
|
||||
ASSERT3U(scn->scn_phys.scn_min_txg, <, vd->vdev_resilver_txg);
|
||||
ASSERT3U(vd->vdev_resilver_txg, <=, scn->scn_phys.scn_max_txg);
|
||||
return (B_TRUE);
|
||||
if (rebuild_done) {
|
||||
vdev_rebuild_t *vr = &vd->vdev_top->vdev_rebuild_config;
|
||||
vdev_rebuild_phys_t *vrp = &vr->vr_rebuild_phys;
|
||||
|
||||
/* Rebuild not initiated by attach */
|
||||
if (vd->vdev_rebuild_txg == 0)
|
||||
return (B_TRUE);
|
||||
|
||||
/*
|
||||
* When a rebuild completes without error then all missing data
|
||||
* up to the rebuild max txg has been reconstructed and the DTL
|
||||
* is eligible for excision.
|
||||
*/
|
||||
if (vrp->vrp_rebuild_state == VDEV_REBUILD_COMPLETE &&
|
||||
vdev_dtl_max(vd) <= vrp->vrp_max_txg) {
|
||||
ASSERT3U(vrp->vrp_min_txg, <=, vdev_dtl_min(vd));
|
||||
ASSERT3U(vrp->vrp_min_txg, <, vd->vdev_rebuild_txg);
|
||||
ASSERT3U(vd->vdev_rebuild_txg, <=, vrp->vrp_max_txg);
|
||||
return (B_TRUE);
|
||||
}
|
||||
} else {
|
||||
dsl_scan_t *scn = vd->vdev_spa->spa_dsl_pool->dp_scan;
|
||||
dsl_scan_phys_t *scnp __maybe_unused = &scn->scn_phys;
|
||||
|
||||
/* Resilver not initiated by attach */
|
||||
if (vd->vdev_resilver_txg == 0)
|
||||
return (B_TRUE);
|
||||
|
||||
/*
|
||||
* When a resilver is initiated the scan will assign the
|
||||
* scn_max_txg value to the highest txg value that exists
|
||||
* in all DTLs. If this device's max DTL is not part of this
|
||||
* scan (i.e. it is not in the range (scn_min_txg, scn_max_txg]
|
||||
* then it is not eligible for excision.
|
||||
*/
|
||||
if (vdev_dtl_max(vd) <= scn->scn_phys.scn_max_txg) {
|
||||
ASSERT3U(scnp->scn_min_txg, <=, vdev_dtl_min(vd));
|
||||
ASSERT3U(scnp->scn_min_txg, <, vd->vdev_resilver_txg);
|
||||
ASSERT3U(vd->vdev_resilver_txg, <=, scnp->scn_max_txg);
|
||||
return (B_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
@@ -2614,7 +2664,8 @@ vdev_dtl_should_excise(vdev_t *vd)
|
||||
* write operations will be issued to the pool.
|
||||
*/
|
||||
void
|
||||
vdev_dtl_reassess(vdev_t *vd, uint64_t txg, uint64_t scrub_txg, int scrub_done)
|
||||
vdev_dtl_reassess(vdev_t *vd, uint64_t txg, uint64_t scrub_txg,
|
||||
boolean_t scrub_done, boolean_t rebuild_done)
|
||||
{
|
||||
spa_t *spa = vd->vdev_spa;
|
||||
avl_tree_t reftree;
|
||||
@@ -2624,22 +2675,28 @@ vdev_dtl_reassess(vdev_t *vd, uint64_t txg, uint64_t scrub_txg, int scrub_done)
|
||||
|
||||
for (int c = 0; c < vd->vdev_children; c++)
|
||||
vdev_dtl_reassess(vd->vdev_child[c], txg,
|
||||
scrub_txg, scrub_done);
|
||||
scrub_txg, scrub_done, rebuild_done);
|
||||
|
||||
if (vd == spa->spa_root_vdev || !vdev_is_concrete(vd) || vd->vdev_aux)
|
||||
return;
|
||||
|
||||
if (vd->vdev_ops->vdev_op_leaf) {
|
||||
dsl_scan_t *scn = spa->spa_dsl_pool->dp_scan;
|
||||
vdev_rebuild_t *vr = &vd->vdev_top->vdev_rebuild_config;
|
||||
boolean_t check_excise = B_FALSE;
|
||||
boolean_t wasempty = B_TRUE;
|
||||
|
||||
mutex_enter(&vd->vdev_dtl_lock);
|
||||
|
||||
/*
|
||||
* If requested, pretend the scan completed cleanly.
|
||||
* If requested, pretend the scan or rebuild completed cleanly.
|
||||
*/
|
||||
if (zfs_scan_ignore_errors && scn)
|
||||
scn->scn_phys.scn_errors = 0;
|
||||
if (zfs_scan_ignore_errors) {
|
||||
if (scn != NULL)
|
||||
scn->scn_phys.scn_errors = 0;
|
||||
if (vr != NULL)
|
||||
vr->vr_rebuild_phys.vrp_errors = 0;
|
||||
}
|
||||
|
||||
if (scrub_txg != 0 &&
|
||||
!range_tree_is_empty(vd->vdev_dtl[DTL_MISSING])) {
|
||||
@@ -2654,21 +2711,29 @@ vdev_dtl_reassess(vdev_t *vd, uint64_t txg, uint64_t scrub_txg, int scrub_done)
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've completed a scan cleanly then determine
|
||||
* if this vdev should remove any DTLs. We only want to
|
||||
* excise regions on vdevs that were available during
|
||||
* the entire duration of this scan.
|
||||
* If we've completed a scrub/resilver or a rebuild cleanly
|
||||
* then determine if this vdev should remove any DTLs. We
|
||||
* only want to excise regions on vdevs that were available
|
||||
* during the entire duration of this scan.
|
||||
*/
|
||||
if (scrub_txg != 0 &&
|
||||
(spa->spa_scrub_started ||
|
||||
(scn != NULL && scn->scn_phys.scn_errors == 0)) &&
|
||||
vdev_dtl_should_excise(vd)) {
|
||||
if (rebuild_done &&
|
||||
vr != NULL && vr->vr_rebuild_phys.vrp_errors == 0) {
|
||||
check_excise = B_TRUE;
|
||||
} else {
|
||||
if (spa->spa_scrub_started ||
|
||||
(scn != NULL && scn->scn_phys.scn_errors == 0)) {
|
||||
check_excise = B_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (scrub_txg && check_excise &&
|
||||
vdev_dtl_should_excise(vd, rebuild_done)) {
|
||||
/*
|
||||
* We completed a scrub up to scrub_txg. If we
|
||||
* did it without rebooting, then the scrub dtl
|
||||
* will be valid, so excise the old region and
|
||||
* fold in the scrub dtl. Otherwise, leave the
|
||||
* dtl as-is if there was an error.
|
||||
* We completed a scrub, resilver or rebuild up to
|
||||
* scrub_txg. If we did it without rebooting, then
|
||||
* the scrub dtl will be valid, so excise the old
|
||||
* region and fold in the scrub dtl. Otherwise,
|
||||
* leave the dtl as-is if there was an error.
|
||||
*
|
||||
* There's little trick here: to excise the beginning
|
||||
* of the DTL_MISSING map, we put it into a reference
|
||||
@@ -2711,15 +2776,20 @@ vdev_dtl_reassess(vdev_t *vd, uint64_t txg, uint64_t scrub_txg, int scrub_done)
|
||||
range_tree_add, vd->vdev_dtl[DTL_OUTAGE]);
|
||||
|
||||
/*
|
||||
* If the vdev was resilvering and no longer has any
|
||||
* DTLs then reset its resilvering flag and dirty
|
||||
* If the vdev was resilvering or rebuilding and no longer
|
||||
* has any DTLs then reset the appropriate flag and dirty
|
||||
* the top level so that we persist the change.
|
||||
*/
|
||||
if (txg != 0 && vd->vdev_resilver_txg != 0 &&
|
||||
if (txg != 0 &&
|
||||
range_tree_is_empty(vd->vdev_dtl[DTL_MISSING]) &&
|
||||
range_tree_is_empty(vd->vdev_dtl[DTL_OUTAGE])) {
|
||||
vd->vdev_resilver_txg = 0;
|
||||
vdev_config_dirty(vd->vdev_top);
|
||||
if (vd->vdev_rebuild_txg != 0) {
|
||||
vd->vdev_rebuild_txg = 0;
|
||||
vdev_config_dirty(vd->vdev_top);
|
||||
} else if (vd->vdev_resilver_txg != 0) {
|
||||
vd->vdev_resilver_txg = 0;
|
||||
vdev_config_dirty(vd->vdev_top);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_exit(&vd->vdev_dtl_lock);
|
||||
@@ -2955,10 +3025,10 @@ vdev_dtl_required(vdev_t *vd)
|
||||
* If not, we can safely offline/detach/remove the device.
|
||||
*/
|
||||
vd->vdev_cant_read = B_TRUE;
|
||||
vdev_dtl_reassess(tvd, 0, 0, B_FALSE);
|
||||
vdev_dtl_reassess(tvd, 0, 0, B_FALSE, B_FALSE);
|
||||
required = !vdev_dtl_empty(tvd, DTL_OUTAGE);
|
||||
vd->vdev_cant_read = cant_read;
|
||||
vdev_dtl_reassess(tvd, 0, 0, B_FALSE);
|
||||
vdev_dtl_reassess(tvd, 0, 0, B_FALSE, B_FALSE);
|
||||
|
||||
if (!required && zio_injection_enabled) {
|
||||
required = !!zio_handle_device_injection(vd, NULL,
|
||||
@@ -3065,6 +3135,20 @@ vdev_load(vdev_t *vd)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Load any rebuild state from the top-level vdev zap.
|
||||
*/
|
||||
if (vd == vd->vdev_top && vd->vdev_top_zap != 0) {
|
||||
error = vdev_rebuild_load(vd);
|
||||
if (error && error != ENOTSUP) {
|
||||
vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN,
|
||||
VDEV_AUX_CORRUPT_DATA);
|
||||
vdev_dbgmsg(vd, "vdev_load: vdev_rebuild_load "
|
||||
"failed [error=%d]", error);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a top-level vdev, initialize its metaslabs.
|
||||
*/
|
||||
@@ -3947,6 +4031,7 @@ vdev_get_stats_ex(vdev_t *vd, vdev_stat_t *vs, vdev_stat_ex_t *vsx)
|
||||
vs->vs_timestamp = gethrtime() - vs->vs_timestamp;
|
||||
vs->vs_state = vd->vdev_state;
|
||||
vs->vs_rsize = vdev_get_min_asize(vd);
|
||||
|
||||
if (vd->vdev_ops->vdev_op_leaf) {
|
||||
vs->vs_rsize += VDEV_LABEL_START_SIZE +
|
||||
VDEV_LABEL_END_SIZE;
|
||||
@@ -3973,7 +4058,11 @@ vdev_get_stats_ex(vdev_t *vd, vdev_stat_t *vs, vdev_stat_ex_t *vsx)
|
||||
vs->vs_trim_bytes_est = vd->vdev_trim_bytes_est;
|
||||
vs->vs_trim_state = vd->vdev_trim_state;
|
||||
vs->vs_trim_action_time = vd->vdev_trim_action_time;
|
||||
|
||||
/* Set when there is a deferred resilver. */
|
||||
vs->vs_resilver_deferred = vd->vdev_resilver_deferred;
|
||||
}
|
||||
|
||||
/*
|
||||
* Report expandable space on top-level, non-auxiliary devices
|
||||
* only. The expandable space is reported in terms of metaslab
|
||||
@@ -3985,13 +4074,16 @@ vdev_get_stats_ex(vdev_t *vd, vdev_stat_t *vs, vdev_stat_ex_t *vsx)
|
||||
vd->vdev_max_asize - vd->vdev_asize,
|
||||
1ULL << tvd->vdev_ms_shift);
|
||||
}
|
||||
|
||||
/*
|
||||
* Report fragmentation and rebuild progress for top-level,
|
||||
* non-auxiliary, concrete devices.
|
||||
*/
|
||||
if (vd->vdev_aux == NULL && vd == vd->vdev_top &&
|
||||
vdev_is_concrete(vd)) {
|
||||
vs->vs_fragmentation = (vd->vdev_mg != NULL) ?
|
||||
vd->vdev_mg->mg_fragmentation : 0;
|
||||
}
|
||||
if (vd->vdev_ops->vdev_op_leaf)
|
||||
vs->vs_resilver_deferred = vd->vdev_resilver_deferred;
|
||||
}
|
||||
|
||||
vdev_get_stats_ex_impl(vd, vs, vsx);
|
||||
@@ -4072,17 +4164,35 @@ vdev_stat_update(zio_t *zio, uint64_t psize)
|
||||
mutex_enter(&vd->vdev_stat_lock);
|
||||
|
||||
if (flags & ZIO_FLAG_IO_REPAIR) {
|
||||
/*
|
||||
* Repair is the result of a resilver issued by the
|
||||
* scan thread (spa_sync).
|
||||
*/
|
||||
if (flags & ZIO_FLAG_SCAN_THREAD) {
|
||||
dsl_scan_phys_t *scn_phys =
|
||||
&spa->spa_dsl_pool->dp_scan->scn_phys;
|
||||
dsl_scan_t *scn = spa->spa_dsl_pool->dp_scan;
|
||||
dsl_scan_phys_t *scn_phys = &scn->scn_phys;
|
||||
uint64_t *processed = &scn_phys->scn_processed;
|
||||
|
||||
/* XXX cleanup? */
|
||||
if (vd->vdev_ops->vdev_op_leaf)
|
||||
atomic_add_64(processed, psize);
|
||||
vs->vs_scan_processed += psize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Repair is the result of a rebuild issued by the
|
||||
* rebuild thread (vdev_rebuild_thread).
|
||||
*/
|
||||
if (zio->io_priority == ZIO_PRIORITY_REBUILD) {
|
||||
vdev_t *tvd = vd->vdev_top;
|
||||
vdev_rebuild_t *vr = &tvd->vdev_rebuild_config;
|
||||
vdev_rebuild_phys_t *vrp = &vr->vr_rebuild_phys;
|
||||
uint64_t *rebuilt = &vrp->vrp_bytes_rebuilt;
|
||||
|
||||
if (vd->vdev_ops->vdev_op_leaf)
|
||||
atomic_add_64(rebuilt, psize);
|
||||
vs->vs_rebuild_processed += psize;
|
||||
}
|
||||
|
||||
if (flags & ZIO_FLAG_SELF_HEAL)
|
||||
vs->vs_self_healed += psize;
|
||||
}
|
||||
@@ -4094,6 +4204,7 @@ vdev_stat_update(zio_t *zio, uint64_t psize)
|
||||
if (vd->vdev_ops->vdev_op_leaf &&
|
||||
(zio->io_priority < ZIO_PRIORITY_NUM_QUEUEABLE)) {
|
||||
zio_type_t vs_type = type;
|
||||
zio_priority_t priority = zio->io_priority;
|
||||
|
||||
/*
|
||||
* TRIM ops and bytes are reported to user space as
|
||||
@@ -4103,19 +4214,44 @@ vdev_stat_update(zio_t *zio, uint64_t psize)
|
||||
if (type == ZIO_TYPE_TRIM)
|
||||
vs_type = ZIO_TYPE_IOCTL;
|
||||
|
||||
/*
|
||||
* Solely for the purposes of 'zpool iostat -lqrw'
|
||||
* reporting use the priority to catagorize the IO.
|
||||
* Only the following are reported to user space:
|
||||
*
|
||||
* ZIO_PRIORITY_SYNC_READ,
|
||||
* ZIO_PRIORITY_SYNC_WRITE,
|
||||
* ZIO_PRIORITY_ASYNC_READ,
|
||||
* ZIO_PRIORITY_ASYNC_WRITE,
|
||||
* ZIO_PRIORITY_SCRUB,
|
||||
* ZIO_PRIORITY_TRIM.
|
||||
*/
|
||||
if (priority == ZIO_PRIORITY_REBUILD) {
|
||||
priority = ((type == ZIO_TYPE_WRITE) ?
|
||||
ZIO_PRIORITY_ASYNC_WRITE :
|
||||
ZIO_PRIORITY_SCRUB);
|
||||
} else if (priority == ZIO_PRIORITY_INITIALIZING) {
|
||||
ASSERT3U(type, ==, ZIO_TYPE_WRITE);
|
||||
priority = ZIO_PRIORITY_ASYNC_WRITE;
|
||||
} else if (priority == ZIO_PRIORITY_REMOVAL) {
|
||||
priority = ((type == ZIO_TYPE_WRITE) ?
|
||||
ZIO_PRIORITY_ASYNC_WRITE :
|
||||
ZIO_PRIORITY_ASYNC_READ);
|
||||
}
|
||||
|
||||
vs->vs_ops[vs_type]++;
|
||||
vs->vs_bytes[vs_type] += psize;
|
||||
|
||||
if (flags & ZIO_FLAG_DELEGATED) {
|
||||
vsx->vsx_agg_histo[zio->io_priority]
|
||||
vsx->vsx_agg_histo[priority]
|
||||
[RQ_HISTO(zio->io_size)]++;
|
||||
} else {
|
||||
vsx->vsx_ind_histo[zio->io_priority]
|
||||
vsx->vsx_ind_histo[priority]
|
||||
[RQ_HISTO(zio->io_size)]++;
|
||||
}
|
||||
|
||||
if (zio->io_delta && zio->io_delay) {
|
||||
vsx->vsx_queue_histo[zio->io_priority]
|
||||
vsx->vsx_queue_histo[priority]
|
||||
[L_HISTO(zio->io_delta - zio->io_delay)]++;
|
||||
vsx->vsx_disk_histo[type]
|
||||
[L_HISTO(zio->io_delay)]++;
|
||||
|
||||
Reference in New Issue
Block a user