mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 10:37:35 +03:00
Improved error handling for extreme rewinds
The vdev_checkpoint_sm_object(), vdev_obsolete_sm_object(), and vdev_obsolete_counts_are_precise() functions assume that the only way a zap_lookup() can fail is if the requested entry is missing. While this is the most common cause, it's not the only cause. Attemping to access a damaged ZAP will result in other errors. The most likely scenario for accessing a damaged ZAP is during an extreme rewind pool import. Under these conditions the pool is expected to contain damaged objects and the import code was updated to handle this gracefully. Getting an ECKSUM error from these ZAPs after the pool in import a far less likely, therefore the behavior for call paths was not modified. Reviewed-by: Tim Chase <tim@chase2k.com> Reviewed-by: Matthew Ahrens <mahrens@delphix.com> Reviewed-by: Serapheim Dimitropoulos <serapheim.dimitro@delphix.com> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #7809 Closes #7921
This commit is contained in:
+4
-3
@@ -7614,14 +7614,15 @@ vdev_indirect_state_sync_verify(vdev_t *vd)
|
||||
ASSERT(vib != NULL);
|
||||
}
|
||||
|
||||
if (vdev_obsolete_sm_object(vd) != 0) {
|
||||
uint64_t obsolete_sm_object = 0;
|
||||
ASSERT0(vdev_obsolete_sm_object(vd, &obsolete_sm_object));
|
||||
if (obsolete_sm_object != 0) {
|
||||
ASSERT(vd->vdev_obsolete_sm != NULL);
|
||||
ASSERT(vd->vdev_removing ||
|
||||
vd->vdev_ops == &vdev_indirect_ops);
|
||||
ASSERT(vdev_indirect_mapping_num_entries(vim) > 0);
|
||||
ASSERT(vdev_indirect_mapping_bytes_mapped(vim) > 0);
|
||||
|
||||
ASSERT3U(vdev_obsolete_sm_object(vd), ==,
|
||||
ASSERT3U(obsolete_sm_object, ==,
|
||||
space_map_object(vd->vdev_obsolete_sm));
|
||||
ASSERT3U(vdev_indirect_mapping_bytes_mapped(vim), >=,
|
||||
space_map_allocated(vd->vdev_obsolete_sm));
|
||||
|
||||
+29
-16
@@ -2895,26 +2895,28 @@ vdev_resilver_needed(vdev_t *vd, uint64_t *minp, uint64_t *maxp)
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the checkpoint space map object from the vdev's ZAP.
|
||||
* Returns the spacemap object, or 0 if it wasn't in the ZAP
|
||||
* or the ZAP doesn't exist yet.
|
||||
* Gets the checkpoint space map object from the vdev's ZAP. On success sm_obj
|
||||
* will contain either the checkpoint spacemap object or zero if none exists.
|
||||
* All other errors are returned to the caller.
|
||||
*/
|
||||
int
|
||||
vdev_checkpoint_sm_object(vdev_t *vd)
|
||||
vdev_checkpoint_sm_object(vdev_t *vd, uint64_t *sm_obj)
|
||||
{
|
||||
ASSERT0(spa_config_held(vd->vdev_spa, SCL_ALL, RW_WRITER));
|
||||
|
||||
if (vd->vdev_top_zap == 0) {
|
||||
*sm_obj = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
uint64_t sm_obj = 0;
|
||||
int err = zap_lookup(spa_meta_objset(vd->vdev_spa), vd->vdev_top_zap,
|
||||
VDEV_TOP_ZAP_POOL_CHECKPOINT_SM, sizeof (uint64_t), 1, &sm_obj);
|
||||
int error = zap_lookup(spa_meta_objset(vd->vdev_spa), vd->vdev_top_zap,
|
||||
VDEV_TOP_ZAP_POOL_CHECKPOINT_SM, sizeof (uint64_t), 1, sm_obj);
|
||||
if (error == ENOENT) {
|
||||
*sm_obj = 0;
|
||||
error = 0;
|
||||
}
|
||||
|
||||
if (err != 0)
|
||||
VERIFY3S(err, ==, ENOENT);
|
||||
|
||||
return (sm_obj);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -2970,8 +2972,9 @@ vdev_load(vdev_t *vd)
|
||||
return (error);
|
||||
}
|
||||
|
||||
uint64_t checkpoint_sm_obj = vdev_checkpoint_sm_object(vd);
|
||||
if (checkpoint_sm_obj != 0) {
|
||||
uint64_t checkpoint_sm_obj;
|
||||
error = vdev_checkpoint_sm_object(vd, &checkpoint_sm_obj);
|
||||
if (error == 0 && checkpoint_sm_obj != 0) {
|
||||
objset_t *mos = spa_meta_objset(vd->vdev_spa);
|
||||
ASSERT(vd->vdev_asize != 0);
|
||||
ASSERT3P(vd->vdev_checkpoint_sm, ==, NULL);
|
||||
@@ -2991,12 +2994,17 @@ vdev_load(vdev_t *vd)
|
||||
/*
|
||||
* Since the checkpoint_sm contains free entries
|
||||
* exclusively we can use sm_alloc to indicate the
|
||||
* culmulative checkpointed space that has been freed.
|
||||
* cumulative checkpointed space that has been freed.
|
||||
*/
|
||||
vd->vdev_stat.vs_checkpoint_space =
|
||||
-vd->vdev_checkpoint_sm->sm_alloc;
|
||||
vd->vdev_spa->spa_checkpoint_info.sci_dspace +=
|
||||
vd->vdev_stat.vs_checkpoint_space;
|
||||
} else if (error != 0) {
|
||||
vdev_dbgmsg(vd, "vdev_load: failed to retrieve "
|
||||
"checkpoint space map object from vdev ZAP "
|
||||
"[error=%d]", error);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3011,8 +3019,9 @@ vdev_load(vdev_t *vd)
|
||||
return (error);
|
||||
}
|
||||
|
||||
uint64_t obsolete_sm_object = vdev_obsolete_sm_object(vd);
|
||||
if (obsolete_sm_object != 0) {
|
||||
uint64_t obsolete_sm_object;
|
||||
error = vdev_obsolete_sm_object(vd, &obsolete_sm_object);
|
||||
if (error == 0 && obsolete_sm_object != 0) {
|
||||
objset_t *mos = vd->vdev_spa->spa_meta_objset;
|
||||
ASSERT(vd->vdev_asize != 0);
|
||||
ASSERT3P(vd->vdev_obsolete_sm, ==, NULL);
|
||||
@@ -3027,6 +3036,10 @@ vdev_load(vdev_t *vd)
|
||||
return (error);
|
||||
}
|
||||
space_map_update(vd->vdev_obsolete_sm);
|
||||
} else if (error != 0) {
|
||||
vdev_dbgmsg(vd, "vdev_load: failed to retrieve obsolete "
|
||||
"space map object from vdev ZAP [error=%d]", error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
+45
-29
@@ -420,15 +420,16 @@ vdev_indirect_should_condense(vdev_t *vd)
|
||||
* If nothing new has been marked obsolete, there is no
|
||||
* point in condensing.
|
||||
*/
|
||||
ASSERTV(uint64_t obsolete_sm_obj);
|
||||
ASSERT0(vdev_obsolete_sm_object(vd, &obsolete_sm_obj));
|
||||
if (vd->vdev_obsolete_sm == NULL) {
|
||||
ASSERT0(vdev_obsolete_sm_object(vd));
|
||||
ASSERT0(obsolete_sm_obj);
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
ASSERT(vd->vdev_obsolete_sm != NULL);
|
||||
|
||||
ASSERT3U(vdev_obsolete_sm_object(vd), ==,
|
||||
space_map_object(vd->vdev_obsolete_sm));
|
||||
ASSERT3U(obsolete_sm_obj, ==, space_map_object(vd->vdev_obsolete_sm));
|
||||
|
||||
uint64_t bytes_mapped = vdev_indirect_mapping_bytes_mapped(vim);
|
||||
uint64_t bytes_obsolete = space_map_allocated(vd->vdev_obsolete_sm);
|
||||
@@ -770,8 +771,9 @@ spa_condense_indirect_start_sync(vdev_t *vd, dmu_tx_t *tx)
|
||||
ASSERT(spa_feature_is_active(spa, SPA_FEATURE_OBSOLETE_COUNTS));
|
||||
ASSERT(vdev_indirect_mapping_num_entries(vd->vdev_indirect_mapping));
|
||||
|
||||
uint64_t obsolete_sm_obj = vdev_obsolete_sm_object(vd);
|
||||
ASSERT(obsolete_sm_obj != 0);
|
||||
uint64_t obsolete_sm_obj;
|
||||
VERIFY0(vdev_obsolete_sm_object(vd, &obsolete_sm_obj));
|
||||
ASSERT3U(obsolete_sm_obj, !=, 0);
|
||||
|
||||
scip->scip_vdev = vd->vdev_id;
|
||||
scip->scip_next_mapping_object =
|
||||
@@ -822,16 +824,18 @@ vdev_indirect_sync_obsolete(vdev_t *vd, dmu_tx_t *tx)
|
||||
ASSERT(vd->vdev_removing || vd->vdev_ops == &vdev_indirect_ops);
|
||||
ASSERT(spa_feature_is_enabled(spa, SPA_FEATURE_OBSOLETE_COUNTS));
|
||||
|
||||
if (vdev_obsolete_sm_object(vd) == 0) {
|
||||
uint64_t obsolete_sm_object =
|
||||
space_map_alloc(spa->spa_meta_objset,
|
||||
uint64_t obsolete_sm_object;
|
||||
VERIFY0(vdev_obsolete_sm_object(vd, &obsolete_sm_object));
|
||||
if (obsolete_sm_object == 0) {
|
||||
obsolete_sm_object = space_map_alloc(spa->spa_meta_objset,
|
||||
vdev_standard_sm_blksz, tx);
|
||||
|
||||
ASSERT(vd->vdev_top_zap != 0);
|
||||
VERIFY0(zap_add(vd->vdev_spa->spa_meta_objset, vd->vdev_top_zap,
|
||||
VDEV_TOP_ZAP_INDIRECT_OBSOLETE_SM,
|
||||
sizeof (obsolete_sm_object), 1, &obsolete_sm_object, tx));
|
||||
ASSERT3U(vdev_obsolete_sm_object(vd), !=, 0);
|
||||
ASSERT0(vdev_obsolete_sm_object(vd, &obsolete_sm_object));
|
||||
ASSERT3U(obsolete_sm_object, !=, 0);
|
||||
|
||||
spa_feature_incr(spa, SPA_FEATURE_OBSOLETE_COUNTS, tx);
|
||||
VERIFY0(space_map_open(&vd->vdev_obsolete_sm,
|
||||
@@ -841,7 +845,7 @@ vdev_indirect_sync_obsolete(vdev_t *vd, dmu_tx_t *tx)
|
||||
}
|
||||
|
||||
ASSERT(vd->vdev_obsolete_sm != NULL);
|
||||
ASSERT3U(vdev_obsolete_sm_object(vd), ==,
|
||||
ASSERT3U(obsolete_sm_object, ==,
|
||||
space_map_object(vd->vdev_obsolete_sm));
|
||||
|
||||
space_map_write(vd->vdev_obsolete_sm,
|
||||
@@ -889,44 +893,56 @@ spa_start_indirect_condensing_thread(spa_t *spa)
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the obsolete spacemap object from the vdev's ZAP.
|
||||
* Returns the spacemap object, or 0 if it wasn't in the ZAP or the ZAP doesn't
|
||||
* exist yet.
|
||||
* Gets the obsolete spacemap object from the vdev's ZAP. On success sm_obj
|
||||
* will contain either the obsolete spacemap object or zero if none exists.
|
||||
* All other errors are returned to the caller.
|
||||
*/
|
||||
int
|
||||
vdev_obsolete_sm_object(vdev_t *vd)
|
||||
vdev_obsolete_sm_object(vdev_t *vd, uint64_t *sm_obj)
|
||||
{
|
||||
ASSERT0(spa_config_held(vd->vdev_spa, SCL_ALL, RW_WRITER));
|
||||
|
||||
if (vd->vdev_top_zap == 0) {
|
||||
*sm_obj = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
uint64_t sm_obj = 0;
|
||||
int err;
|
||||
err = zap_lookup(vd->vdev_spa->spa_meta_objset, vd->vdev_top_zap,
|
||||
VDEV_TOP_ZAP_INDIRECT_OBSOLETE_SM, sizeof (sm_obj), 1, &sm_obj);
|
||||
int error = zap_lookup(vd->vdev_spa->spa_meta_objset, vd->vdev_top_zap,
|
||||
VDEV_TOP_ZAP_INDIRECT_OBSOLETE_SM, sizeof (sm_obj), 1, sm_obj);
|
||||
if (error == ENOENT) {
|
||||
*sm_obj = 0;
|
||||
error = 0;
|
||||
}
|
||||
|
||||
ASSERT(err == 0 || err == ENOENT);
|
||||
|
||||
return (sm_obj);
|
||||
return (error);
|
||||
}
|
||||
|
||||
boolean_t
|
||||
vdev_obsolete_counts_are_precise(vdev_t *vd)
|
||||
/*
|
||||
* Gets the obsolete count are precise spacemap object from the vdev's ZAP.
|
||||
* On success are_precise will be set to reflect if the counts are precise.
|
||||
* All other errors are returned to the caller.
|
||||
*/
|
||||
int
|
||||
vdev_obsolete_counts_are_precise(vdev_t *vd, boolean_t *are_precise)
|
||||
{
|
||||
ASSERT0(spa_config_held(vd->vdev_spa, SCL_ALL, RW_WRITER));
|
||||
|
||||
if (vd->vdev_top_zap == 0) {
|
||||
return (B_FALSE);
|
||||
*are_precise = B_FALSE;
|
||||
return (0);
|
||||
}
|
||||
|
||||
uint64_t val = 0;
|
||||
int err;
|
||||
err = zap_lookup(vd->vdev_spa->spa_meta_objset, vd->vdev_top_zap,
|
||||
int error = zap_lookup(vd->vdev_spa->spa_meta_objset, vd->vdev_top_zap,
|
||||
VDEV_TOP_ZAP_OBSOLETE_COUNTS_ARE_PRECISE, sizeof (val), 1, &val);
|
||||
if (error == 0) {
|
||||
*are_precise = (val != 0);
|
||||
} else if (error == ENOENT) {
|
||||
*are_precise = B_FALSE;
|
||||
error = 0;
|
||||
}
|
||||
|
||||
ASSERT(err == 0 || err == ENOENT);
|
||||
|
||||
return (val != 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
|
||||
@@ -251,7 +251,9 @@ vdev_remove_initiate_sync(void *arg, dmu_tx_t *tx)
|
||||
VERIFY0(zap_add(spa->spa_meta_objset, vd->vdev_top_zap,
|
||||
VDEV_TOP_ZAP_OBSOLETE_COUNTS_ARE_PRECISE, sizeof (one), 1,
|
||||
&one, tx));
|
||||
ASSERT3U(vdev_obsolete_counts_are_precise(vd), !=, 0);
|
||||
ASSERTV(boolean_t are_precise);
|
||||
ASSERT0(vdev_obsolete_counts_are_precise(vd, &are_precise));
|
||||
ASSERT3B(are_precise, ==, B_TRUE);
|
||||
}
|
||||
|
||||
vic->vic_mapping_object = vdev_indirect_mapping_alloc(mos, tx);
|
||||
@@ -1563,15 +1565,20 @@ spa_vdev_remove_cancel_sync(void *arg, dmu_tx_t *tx)
|
||||
ASSERT3P(svr->svr_thread, ==, NULL);
|
||||
|
||||
spa_feature_decr(spa, SPA_FEATURE_DEVICE_REMOVAL, tx);
|
||||
if (vdev_obsolete_counts_are_precise(vd)) {
|
||||
|
||||
boolean_t are_precise;
|
||||
VERIFY0(vdev_obsolete_counts_are_precise(vd, &are_precise));
|
||||
if (are_precise) {
|
||||
spa_feature_decr(spa, SPA_FEATURE_OBSOLETE_COUNTS, tx);
|
||||
VERIFY0(zap_remove(spa->spa_meta_objset, vd->vdev_top_zap,
|
||||
VDEV_TOP_ZAP_OBSOLETE_COUNTS_ARE_PRECISE, tx));
|
||||
}
|
||||
|
||||
if (vdev_obsolete_sm_object(vd) != 0) {
|
||||
uint64_t obsolete_sm_object;
|
||||
VERIFY0(vdev_obsolete_sm_object(vd, &obsolete_sm_object));
|
||||
if (obsolete_sm_object != 0) {
|
||||
ASSERT(vd->vdev_obsolete_sm != NULL);
|
||||
ASSERT3U(vdev_obsolete_sm_object(vd), ==,
|
||||
ASSERT3U(obsolete_sm_object, ==,
|
||||
space_map_object(vd->vdev_obsolete_sm));
|
||||
|
||||
space_map_free(vd->vdev_obsolete_sm, tx);
|
||||
|
||||
Reference in New Issue
Block a user