From 367d34b3aadcc02eb23d43948cf505c2c0c72c49 Mon Sep 17 00:00:00 2001 From: Paul Dagnelie Date: Tue, 1 Apr 2025 06:23:43 -0700 Subject: [PATCH] Fix dspace underflow bug Since spa_dspace accounts only normal allocation class space, spa_nonallocating_dspace should do the same. Otherwise we may get negative overflow or respective assertion spa_update_dspace() if removed special/dedup vdev is bigger than all normal class space. Reviewed-by: Alexander Motin Reviewed-by: Allan Jude Signed-off-by: Paul Dagnelie Closes #17183 --- include/sys/vdev.h | 1 + module/zfs/vdev.c | 22 ++++++++++++++++++++-- module/zfs/vdev_removal.c | 17 +++-------------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/include/sys/vdev.h b/include/sys/vdev.h index a6a41882d..5aad22dba 100644 --- a/include/sys/vdev.h +++ b/include/sys/vdev.h @@ -100,6 +100,7 @@ extern boolean_t vdev_replace_in_progress(vdev_t *vdev); extern void vdev_hold(vdev_t *); extern void vdev_rele(vdev_t *); +void vdev_update_nonallocating_space(vdev_t *vd, boolean_t add); extern int vdev_metaslab_init(vdev_t *vd, uint64_t txg); extern void vdev_metaslab_fini(vdev_t *vd); extern void vdev_metaslab_set_size(vdev_t *); diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index f0cca877a..257ac2b9d 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -1515,6 +1515,25 @@ vdev_metaslab_group_create(vdev_t *vd) } } +void +vdev_update_nonallocating_space(vdev_t *vd, boolean_t add) +{ + spa_t *spa = vd->vdev_spa; + + if (vd->vdev_mg->mg_class != spa_normal_class(spa)) + return; + + uint64_t raw_space = metaslab_group_get_space(vd->vdev_mg); + uint64_t dspace = spa_deflate(spa) ? + vdev_deflated_space(vd, raw_space) : raw_space; + if (add) { + spa->spa_nonallocating_dspace += dspace; + } else { + ASSERT3U(spa->spa_nonallocating_dspace, >=, dspace); + spa->spa_nonallocating_dspace -= dspace; + } +} + int vdev_metaslab_init(vdev_t *vd, uint64_t txg) { @@ -1626,8 +1645,7 @@ vdev_metaslab_init(vdev_t *vd, uint64_t txg) */ if (vd->vdev_noalloc) { /* track non-allocating vdev space */ - spa->spa_nonallocating_dspace += spa_deflate(spa) ? - vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space; + vdev_update_nonallocating_space(vd, B_TRUE); } else if (!expanding) { metaslab_group_activate(vd->vdev_mg); if (vd->vdev_log_mg != NULL) diff --git a/module/zfs/vdev_removal.c b/module/zfs/vdev_removal.c index a94ac9e60..31938d222 100644 --- a/module/zfs/vdev_removal.c +++ b/module/zfs/vdev_removal.c @@ -172,9 +172,6 @@ static void vdev_activate(vdev_t *vd) { metaslab_group_t *mg = vd->vdev_mg; - spa_t *spa = vd->vdev_spa; - uint64_t vdev_space = spa_deflate(spa) ? - vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space; ASSERT(!vd->vdev_islog); ASSERT(vd->vdev_noalloc); @@ -182,9 +179,7 @@ vdev_activate(vdev_t *vd) metaslab_group_activate(mg); metaslab_group_activate(vd->vdev_log_mg); - ASSERT3U(spa->spa_nonallocating_dspace, >=, vdev_space); - - spa->spa_nonallocating_dspace -= vdev_space; + vdev_update_nonallocating_space(vd, B_FALSE); vd->vdev_noalloc = B_FALSE; } @@ -256,8 +251,7 @@ vdev_passivate(vdev_t *vd, uint64_t *txg) return (error); } - spa->spa_nonallocating_dspace += spa_deflate(spa) ? - vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space; + vdev_update_nonallocating_space(vd, B_TRUE); vd->vdev_noalloc = B_TRUE; return (0); @@ -1370,8 +1364,6 @@ vdev_remove_complete(spa_t *spa) ASSERT3P(vd->vdev_autotrim_thread, ==, NULL); vdev_rebuild_stop_wait(vd); ASSERT3P(vd->vdev_rebuild_thread, ==, NULL); - uint64_t vdev_space = spa_deflate(spa) ? - vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space; sysevent_t *ev = spa_event_create(spa, vd, NULL, ESC_ZFS_VDEV_REMOVE_DEV); @@ -1379,11 +1371,8 @@ vdev_remove_complete(spa_t *spa) zfs_dbgmsg("finishing device removal for vdev %llu in txg %llu", (u_longlong_t)vd->vdev_id, (u_longlong_t)txg); - ASSERT3U(0, !=, vdev_space); - ASSERT3U(spa->spa_nonallocating_dspace, >=, vdev_space); - /* the vdev is no longer part of the dspace */ - spa->spa_nonallocating_dspace -= vdev_space; + vdev_update_nonallocating_space(vd, B_FALSE); /* * Discard allocation state.