Skip to content

Commit a103ae4

Browse files
ahrensbehlendorf
authored andcommitted
special device removal space accounting fixes
The space in special devices is not included in spa_dspace (or dsl_pool_adjustedsize(), or the zfs `available` property). Therefore there is always at least as much free space in the normal class, as there is allocated in the special class(es). And therefore, there is always enough free space to remove a special device. However, the checks for free space when removing special devices did not take this into account. This commit corrects that. Reviewed-by: Ryan Moeller <[email protected]> Reviewed-by: Don Brady <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Matthew Ahrens <[email protected]> Closes #11329
1 parent 2ab24df commit a103ae4

File tree

4 files changed

+43
-35
lines changed

4 files changed

+43
-35
lines changed

module/zfs/spa_misc.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,10 +1807,11 @@ spa_update_dspace(spa_t *spa)
18071807
ddt_get_dedup_dspace(spa);
18081808
if (spa->spa_vdev_removal != NULL) {
18091809
/*
1810-
* We can't allocate from the removing device, so
1811-
* subtract its size. This prevents the DMU/DSL from
1812-
* filling up the (now smaller) pool while we are in the
1813-
* middle of removing the device.
1810+
* We can't allocate from the removing device, so subtract
1811+
* its size if it was included in dspace (i.e. if this is a
1812+
* normal-class vdev, not special/dedup). This prevents the
1813+
* DMU/DSL from filling up the (now smaller) pool while we
1814+
* are in the middle of removing the device.
18141815
*
18151816
* Note that the DMU/DSL doesn't actually know or care
18161817
* how much space is allocated (it does its own tracking
@@ -1822,8 +1823,10 @@ spa_update_dspace(spa_t *spa)
18221823
spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
18231824
vdev_t *vd =
18241825
vdev_lookup_top(spa, spa->spa_vdev_removal->svr_vdev_id);
1825-
spa->spa_dspace -= spa_deflate(spa) ?
1826-
vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space;
1826+
if (vd->vdev_mg->mg_class == spa_normal_class(spa)) {
1827+
spa->spa_dspace -= spa_deflate(spa) ?
1828+
vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space;
1829+
}
18271830
spa_config_exit(spa, SCL_VDEV, FTAG);
18281831
}
18291832
}

module/zfs/vdev_removal.c

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,7 @@ spa_vdev_copy_segment(vdev_t *vd, range_tree_t *segs,
993993
* An allocation class might not have any remaining vdevs or space
994994
*/
995995
metaslab_class_t *mc = mg->mg_class;
996-
if (mc != spa_normal_class(spa) && mc->mc_groups <= 1)
996+
if (mc->mc_groups == 0)
997997
mc = spa_normal_class(spa);
998998
int error = metaslab_alloc_dva(spa, mc, size, &dst, 0, NULL, txg, 0,
999999
zal, 0);
@@ -1976,32 +1976,38 @@ spa_vdev_remove_top_check(vdev_t *vd)
19761976
if (!spa_feature_is_enabled(spa, SPA_FEATURE_DEVICE_REMOVAL))
19771977
return (SET_ERROR(ENOTSUP));
19781978

1979-
/* available space in the pool's normal class */
1980-
uint64_t available = dsl_dir_space_available(
1981-
spa->spa_dsl_pool->dp_root_dir, NULL, 0, B_TRUE);
19821979

19831980
metaslab_class_t *mc = vd->vdev_mg->mg_class;
1984-
1985-
/*
1986-
* When removing a vdev from an allocation class that has
1987-
* remaining vdevs, include available space from the class.
1988-
*/
1989-
if (mc != spa_normal_class(spa) && mc->mc_groups > 1) {
1990-
uint64_t class_avail = metaslab_class_get_space(mc) -
1991-
metaslab_class_get_alloc(mc);
1992-
1993-
/* add class space, adjusted for overhead */
1994-
available += (class_avail * 94) / 100;
1995-
}
1996-
1997-
/*
1998-
* There has to be enough free space to remove the
1999-
* device and leave double the "slop" space (i.e. we
2000-
* must leave at least 3% of the pool free, in addition to
2001-
* the normal slop space).
2002-
*/
2003-
if (available < vd->vdev_stat.vs_dspace + spa_get_slop_space(spa)) {
2004-
return (SET_ERROR(ENOSPC));
1981+
metaslab_class_t *normal = spa_normal_class(spa);
1982+
if (mc != normal) {
1983+
/*
1984+
* Space allocated from the special (or dedup) class is
1985+
* included in the DMU's space usage, but it's not included
1986+
* in spa_dspace (or dsl_pool_adjustedsize()). Therefore
1987+
* there is always at least as much free space in the normal
1988+
* class, as is allocated from the special (and dedup) class.
1989+
* As a backup check, we will return ENOSPC if this is
1990+
* violated. See also spa_update_dspace().
1991+
*/
1992+
uint64_t available = metaslab_class_get_space(normal) -
1993+
metaslab_class_get_alloc(normal);
1994+
ASSERT3U(available, >=, vd->vdev_stat.vs_alloc);
1995+
if (available < vd->vdev_stat.vs_alloc)
1996+
return (SET_ERROR(ENOSPC));
1997+
} else {
1998+
/* available space in the pool's normal class */
1999+
uint64_t available = dsl_dir_space_available(
2000+
spa->spa_dsl_pool->dp_root_dir, NULL, 0, B_TRUE);
2001+
if (available <
2002+
vd->vdev_stat.vs_dspace + spa_get_slop_space(spa)) {
2003+
/*
2004+
* This is a normal device. There has to be enough free
2005+
* space to remove the device and leave double the
2006+
* "slop" space (i.e. we must leave at least 3% of the
2007+
* pool free, in addition to the normal slop space).
2008+
*/
2009+
return (SET_ERROR(ENOSPC));
2010+
}
20052011
}
20062012

20072013
/*

tests/test-runner/bin/zts-report.py.in

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,6 @@ elif sys.platform.startswith('linux'):
187187
# reasons listed above can be used.
188188
#
189189
maybe = {
190-
'alloc_class/alloc_class_012_pos': ['FAIL', '9142'],
191-
'alloc_class/alloc_class_013_pos': ['FAIL', '9142'],
192190
'chattr/setup': ['SKIP', exec_reason],
193191
'cli_root/zdb/zdb_006_pos': ['FAIL', known_reason],
194192
'cli_root/zfs_get/zfs_get_004_pos': ['FAIL', known_reason],

tests/zfs-tests/tests/functional/alloc_class/alloc_class_012_pos.ksh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ function file_in_special_vdev # <dataset> <inode>
3333
{
3434
typeset dataset="$1"
3535
typeset inum="$2"
36+
typeset num_normal=$(echo $ZPOOL_DISKS | wc -w | xargs)
3637

37-
zdb -dddddd $dataset $inum | awk '{
38+
zdb -dddddd $dataset $inum | awk -v d=$num_normal '{
3839
# find DVAs from string "offset level dva" only for L0 (data) blocks
3940
if (match($0,"L0 [0-9]+")) {
4041
dvas[0]=$3
@@ -49,7 +50,7 @@ if (match($0,"L0 [0-9]+")) {
4950
exit 1;
5051
}
5152
# verify vdev is "special"
52-
if (arr[1] < 3) {
53+
if (arr[1] < d) {
5354
exit 1;
5455
}
5556
}

0 commit comments

Comments
 (0)