Fix metaslab count assertion in metaslab_group_alloc() for small vdevs

When creating a pool on a small vdev (< 1GB) with a low
zfs_vdev_min_ms_count value (e.g., 2), vdev_metaslab_set_size() could
compute a metaslab shift that results in fewer metaslabs than
zfs_vdev_min_ms_count, triggering an assertion failure in
metaslab_group_alloc():
ASSERT3U(mg->mg_vd->vdev_ms_count, >=, 2) failed (1 >= 2)

The root cause is that highbit64(asize / zfs_vdev_min_ms_count) returns
last set bit, which can yield a metaslab size larger than
asize / zfs_vdev_min_ms_count. For example, with a 500MB vdev and
zfs_vdev_min_ms_count=2:
- asize / 2 = 250MB
- highbit64(250MB) = 28, giving 256MB metaslabs
- 500MB / 256MB = 1 metaslab (violates minimum of 2)

Fix by subtracting 1 from highbit64() result to ensure the metaslab
size is at most asize / zfs_vdev_min_ms_count, guaranteeing at least
zfs_vdev_min_ms_count metaslabs are created.

Test fixes:

trim/autotrim_config.ksh:

Increase post-trim thresholds from 30% to 40% (45% for draid). With the
metaslab fix, smaller vdevs now create more metaslabs as intended,
which increases spacemap metadata overhead. Adjust the post-trim size
threshold to accommodate this expected overhead.

replacement/replace_resilver_sit_out.ksh:

Increase test vdev size from 150MB to 256MB. With the metaslab fix,
150MB vdevs create 18 metaslabs (vs 9 before), changing read
distribution patterns and preventing the sit_out outlier detection
from triggering reliably. Larger vdevs provide more consistent read
patterns for the latency-based sit_out mechanism.

Reviewed-by: Brian Behlendorf <[email protected]>
Reviewed-by: Alexander Motin <[email protected]>
Signed-off-by: Jitendra Patidar <[email protected]>
Closes #19031
This commit is contained in:
Jitendra Patidar authored and GitHub committed 2026-09-14 09:05:36 -07:00
1 parent 02b5baf13c
commit 994fb17039
3 files changed
+9 -4

No files matched your search

+5 -1
View File
@@ -3102,7 +3102,11 @@ vdev_metaslab_set_size(vdev_t *vd)
*/
if (ms_count < zfs_vdev_min_ms_count)
ms_shift = highbit64(asize / zfs_vdev_min_ms_count);
/*
* Subtract 1 from highbit64() to ensure ms_shift yields
* at least zfs_vdev_min_ms_count metaslabs.
*/
ms_shift = highbit64(asize / zfs_vdev_min_ms_count) - 1;
else if (ms_count > zfs_vdev_default_ms_count)
ms_shift = highbit64(asize / zfs_vdev_default_ms_count);
else
@@ -130,7 +130,7 @@ function replace_test
verify_pool $TESTPOOL1
}
DEVSIZE="150M"
DEVSIZE="256M"
specials_list=""
i=0
while [[ $i != 10 ]]; do
@@ -71,7 +71,7 @@ typeset vdev_min_ms_count=$(get_tunable VDEV_MIN_MS_COUNT)
log_must set_tunable64 VDEV_MIN_MS_COUNT 32
typeset VDEV_MAX_MB=$(( floor(4 * MINVDEVSIZE * 0.75 / 1024 / 1024) ))
typeset VDEV_MIN_MB=$(( floor(4 * MINVDEVSIZE * 0.30 / 1024 / 1024) ))
typeset VDEV_MIN_MB=$(( floor(4 * MINVDEVSIZE * 0.40 / 1024 / 1024) ))
for type in "" "mirror" "raidz2" "draid"; do
@@ -85,8 +85,9 @@ for type in "" "mirror" "raidz2" "draid"; do
VDEVS="$TRIM_VDEV1 $TRIM_VDEV2 $TRIM_VDEV3 $TRIM_VDEV4"
# The per-vdev utilization is lower due to the capacity
# resilverd for the distributed spare.
# reserved for the distributed spare.
VDEV_MAX_MB=$(( floor(4 * MINVDEVSIZE * 0.50 / 1024 / 1024) ))
VDEV_MIN_MB=$(( floor(4 * MINVDEVSIZE * 0.45 / 1024 / 1024) ))
fi
log_must truncate -s $((4 * MINVDEVSIZE)) $VDEVS