Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion include/sys/spa.h
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,8 @@ extern vdev_t *spa_lookup_by_guid(spa_t *spa, uint64_t guid,
boolean_t l2cache);
extern boolean_t spa_has_l2cache(spa_t *, uint64_t guid);
extern boolean_t spa_has_spare(spa_t *, uint64_t guid);
extern uint64_t dva_get_dsize_sync(spa_t *spa, const dva_t *dva);
extern uint64_t dva_get_dsize_sync(spa_t *spa, const dva_t *dva,
uint64_t birth_txg);
extern uint64_t bp_get_dsize_sync(spa_t *spa, const blkptr_t *bp);
extern uint64_t bp_get_dsize(spa_t *spa, const blkptr_t *bp);
extern boolean_t spa_has_dedup(spa_t *spa);
Expand Down
1 change: 1 addition & 0 deletions include/sys/spa_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ struct spa {

vdev_raidz_expand_t *spa_raidz_expand;
zthr_t *spa_raidz_expand_zthr;
uint64_t spa_raidz_expand_acct_txg; /* txg variable deflate on */

uint64_t spa_checkpoint_txg; /* the txg of the checkpoint */
spa_checkpoint_info_t spa_checkpoint_info; /* checkpoint accounting */
Expand Down
1 change: 1 addition & 0 deletions include/sys/vdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ extern void vdev_space_update(vdev_t *vd,
int64_t alloc_delta, int64_t defer_delta, int64_t space_delta);

extern int64_t vdev_deflated_space(vdev_t *vd, int64_t space);
extern uint64_t vdev_get_deflate_ratio(vdev_t *vd, uint64_t birth_txg);

extern uint64_t vdev_asize_to_psize_txg(vdev_t *vd, uint64_t asize,
uint64_t txg);
Expand Down
1 change: 1 addition & 0 deletions include/sys/vdev_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ struct vdev {
list_node_t vdev_config_dirty_node; /* config dirty list */
list_node_t vdev_state_dirty_node; /* state dirty list */
uint64_t vdev_deflate_ratio; /* deflation ratio (x512) */
uint64_t vdev_deflate_ratio_current; /* current geometry */
uint64_t vdev_islog; /* is an intent log device */
uint64_t vdev_noalloc; /* device is passivated? */
uint64_t vdev_removing; /* device is being removed? */
Expand Down
1 change: 1 addition & 0 deletions include/zfeature_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ typedef enum spa_feature {
SPA_FEATURE_BLOCK_CLONING_ENDIAN,
SPA_FEATURE_PHYSICAL_REWRITE,
SPA_FEATURE_DRAID_FAIL_DOMAINS,
SPA_FEATURE_RAIDZ_EXPANSION_ACCOUNTING,
SPA_FEATURES
} spa_feature_t;

Expand Down
11 changes: 6 additions & 5 deletions lib/libzfs/libzfs.abi
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@
<elf-symbol name='fletcher_4_superscalar4_ops' size='128' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='fletcher_4_superscalar_ops' size='128' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='libzfs_config_ops' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='spa_feature_table' size='2688' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='spa_feature_table' size='2744' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfeature_checks_disable' size='4' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_deleg_perm_tab' size='544' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_history_event_names' size='328' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
Expand Down Expand Up @@ -6710,7 +6710,8 @@
<enumerator name='SPA_FEATURE_BLOCK_CLONING_ENDIAN' value='45'/>
<enumerator name='SPA_FEATURE_PHYSICAL_REWRITE' value='46'/>
<enumerator name='SPA_FEATURE_DRAID_FAIL_DOMAINS' value='47'/>
<enumerator name='SPA_FEATURES' value='48'/>
<enumerator name='SPA_FEATURE_RAIDZ_EXPANSION_ACCOUNTING' value='48'/>
<enumerator name='SPA_FEATURES' value='49'/>
</enum-decl>
<typedef-decl name='spa_feature_t' type-id='33ecb627' id='d6618c78'/>
<qualified-type-def type-id='80f4b756' const='yes' id='b99c00c9'/>
Expand Down Expand Up @@ -10103,8 +10104,8 @@
</function-decl>
</abi-instr>
<abi-instr address-size='64' path='module/zcommon/zfeature_common.c' language='LANG_C99'>
<array-type-def dimensions='1' type-id='83f29ca2' size-in-bits='21504' id='bd288d11'>
<subrange length='48' type-id='7359adad' id='8f6d2a81'/>
<array-type-def dimensions='1' type-id='83f29ca2' size-in-bits='21952' id='f92b2a1c'>
<subrange length='49' type-id='7359adad' id='8b6f62cc'/>
</array-type-def>
<enum-decl name='zfeature_flags' id='6db816a4'>
<underlying-type type-id='9cac1fee'/>
Expand Down Expand Up @@ -10165,7 +10166,7 @@
<pointer-type-def type-id='c5c76c9c' size-in-bits='64' id='b7f9d8e6'/>
<qualified-type-def type-id='eaa32e2f' const='yes' id='83be723c'/>
<pointer-type-def type-id='83be723c' size-in-bits='64' id='7acd98a2'/>
<var-decl name='spa_feature_table' type-id='bd288d11' mangled-name='spa_feature_table' visibility='default' elf-symbol-id='spa_feature_table'/>
<var-decl name='spa_feature_table' type-id='f92b2a1c' mangled-name='spa_feature_table' visibility='default' elf-symbol-id='spa_feature_table'/>
<var-decl name='zfeature_checks_disable' type-id='c19b74c3' mangled-name='zfeature_checks_disable' visibility='default' elf-symbol-id='zfeature_checks_disable'/>
<function-decl name='tsearch' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='eaa32e2f'/>
Expand Down
16 changes: 16 additions & 0 deletions man/man7/zpool-features.7
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,22 @@ amount usable space in the pool.
See
.Xr zpool-attach 8 .
.
.feature org.openzfs raidz_expansion_accounting no enabled_txg
This feature enables correct per-block space accounting after RAIDZ
expansion.
When a RAIDZ vdev is expanded, the deflation ratio changes because
there are more data disks per parity disk.
Without this feature, all blocks use a single deflation ratio regardless
of when they were written, which causes inaccurate capacity reporting.
With this feature, each block's deflation ratio is determined by the
geometry that was active when it was written, identified via its birth
transaction group.
.Pp
This feature becomes
.Sy active
when a RAIDZ expansion is initiated and will never return to being
.Sy enabled .
.
.feature com.delphix redaction_bookmarks no bookmarks extensible_dataset
This feature enables the use of redacted
.Nm zfs Cm send Ns s ,
Expand Down
13 changes: 13 additions & 0 deletions module/zcommon/zfeature_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,19 @@ zpool_feature_init(void)
"Support for raidz expansion",
ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures);

{
static const spa_feature_t raidz_expand_acct_deps[] = {
SPA_FEATURE_ENABLED_TXG,
Comment thread
behlendorf marked this conversation as resolved.
SPA_FEATURE_NONE
};
zfeature_register(SPA_FEATURE_RAIDZ_EXPANSION_ACCOUNTING,
"org.openzfs:raidz_expansion_accounting",
"raidz_expansion_accounting",
"Per-block deflation ratio after raidz expansion",
ZFEATURE_FLAG_READONLY_COMPAT,
ZFEATURE_TYPE_BOOLEAN, raidz_expand_acct_deps, sfeatures);
}

zfeature_register(SPA_FEATURE_FAST_DEDUP,
"com.klarasystems:fast_dedup", "fast_dedup",
"Support for advanced deduplication",
Expand Down
11 changes: 10 additions & 1 deletion module/zfs/ddt_stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,17 @@ ddt_stat_generate(ddt_t *ddt, const ddt_lightweight_entry_t *ddlwe,
ddp->ddp_flat.ddp_dva : ddp->ddp_trad[p].ddp_dva;

uint64_t dsize = 0;
/*
* Use ddp_phys_birth for the deflation ratio lookup.
* If DDT later added copies after an expansion, those
* new DVAs use the new geometry but share this birth;
* the resulting dsize is slightly approximate for the
* extra copies but this only affects DDT statistics,
* not persistent accounting.
*/
uint64_t birth = ddt_phys_birth(ddp, v);
for (int d = 0; d < ndvas; d++)
dsize += dva_get_dsize_sync(spa, &dvas[d]);
dsize += dva_get_dsize_sync(spa, &dvas[d], birth);
Comment thread
Skountz marked this conversation as resolved.

uint64_t refcnt = ddt_phys_refcnt(ddp, v);

Expand Down
65 changes: 56 additions & 9 deletions module/zfs/spa.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,12 +423,12 @@ spa_prop_get_nvlist(spa_t *spa, char **props, unsigned int n_props,
*/
static void
spa_prop_add_metaslab_class(nvlist_t *nv, metaslab_class_t *mc,
zpool_mc_props_t mcp, uint64_t *sizep, uint64_t *allocp, uint64_t *usablep,
uint64_t *usedp)
zpool_mc_props_t mcp, int64_t dspace_correction,
uint64_t *sizep, uint64_t *allocp, uint64_t *usablep, uint64_t *usedp)
{
uint64_t size = metaslab_class_get_space(mc);
uint64_t alloc = metaslab_class_get_alloc(mc);
uint64_t dsize = metaslab_class_get_dspace(mc);
uint64_t dsize = metaslab_class_get_dspace(mc) + dspace_correction;
uint64_t dalloc = metaslab_class_get_dalloc(mc);
uint64_t cap = (size == 0) ? 0 : (alloc * 100 / size);
const zprop_source_t src = ZPROP_SRC_NONE;
Expand Down Expand Up @@ -490,20 +490,55 @@ spa_prop_get_config(spa_t *spa, nvlist_t *nv)
if (rvd != NULL) {
spa_prop_add_list(nv, ZPOOL_PROP_NAME, spa_name(spa), 0, src);

/*
* For expanded RAIDZ vdevs, the metaslab class dspace
* was computed using the original (txg 0) deflation
* ratio, which understates usable capacity. Compute
* a correction for the normal class using only the
* FREE portion of each vdev. See spa_update_dspace()
* for the rationale.
*/
int64_t normal_dspace_corr = 0;
if (spa_deflate(spa)) {
for (uint64_t c = 0; c < rvd->vdev_children; c++) {
vdev_t *vd = rvd->vdev_child[c];
if (vd->vdev_mg == NULL ||
vd->vdev_mg->mg_class != mc)
continue;
if (vd->vdev_deflate_ratio_current != 0 &&
vd->vdev_deflate_ratio_current !=
vd->vdev_deflate_ratio) {
uint64_t space =
vd->vdev_stat.vs_space;
uint64_t aspace =
vd->vdev_stat.vs_alloc;
uint64_t fspace = space - aspace;
int64_t old_fs = (fspace >>
SPA_MINBLOCKSHIFT) *
vd->vdev_deflate_ratio;
int64_t new_fs = (fspace >>
SPA_MINBLOCKSHIFT) *
vd->vdev_deflate_ratio_current;
normal_dspace_corr +=
(new_fs - old_fs);
}
}
}

size = alloc = usable = used = 0;
spa_prop_add_metaslab_class(nv, mc, ZPOOL_MC_PROPS_NORMAL,
&size, &alloc, &usable, &used);
normal_dspace_corr, &size, &alloc, &usable, &used);
spa_prop_add_metaslab_class(nv, spa_special_class(spa),
ZPOOL_MC_PROPS_SPECIAL, &size, &alloc, &usable, &used);
ZPOOL_MC_PROPS_SPECIAL, 0, &size, &alloc, &usable, &used);
spa_prop_add_metaslab_class(nv, spa_dedup_class(spa),
ZPOOL_MC_PROPS_DEDUP, &size, &alloc, &usable, &used);
ZPOOL_MC_PROPS_DEDUP, 0, &size, &alloc, &usable, &used);
spa_prop_add_metaslab_class(nv, spa_log_class(spa),
ZPOOL_MC_PROPS_LOG, NULL, NULL, NULL, NULL);
ZPOOL_MC_PROPS_LOG, 0, NULL, NULL, NULL, NULL);
spa_prop_add_metaslab_class(nv, spa_embedded_log_class(spa),
ZPOOL_MC_PROPS_ELOG, &size, &alloc, &usable, &used);
ZPOOL_MC_PROPS_ELOG, 0, &size, &alloc, &usable, &used);
spa_prop_add_metaslab_class(nv,
spa_special_embedded_log_class(spa), ZPOOL_MC_PROPS_SELOG,
&size, &alloc, &usable, &used);
0, &size, &alloc, &usable, &used);

spa_prop_add_list(nv, ZPOOL_PROP_SIZE, NULL, size, src);
spa_prop_add_list(nv, ZPOOL_PROP_ALLOCATED, NULL, alloc, src);
Expand Down Expand Up @@ -5307,6 +5342,18 @@ spa_ld_check_features(spa_t *spa, boolean_t *missing_feat_writep)
return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO));
}

/*
* Cache the txg at which per-block deflate ratio accounting was
* enabled. Blocks born before this txg use the legacy fixed ratio;
* blocks born at or after this txg use per-birth-txg ratios.
*/
if (spa_feature_is_active(spa,
SPA_FEATURE_RAIDZ_EXPANSION_ACCOUNTING)) {
(void) spa_feature_enabled_txg(spa,
SPA_FEATURE_RAIDZ_EXPANSION_ACCOUNTING,
&spa->spa_raidz_expand_acct_txg);
}

/*
* Encryption was added before bookmark_v2, even though bookmark_v2
* is now a dependency. If this pool has encryption enabled without
Expand Down
80 changes: 74 additions & 6 deletions module/zfs/spa_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2017,6 +2017,55 @@ void
spa_update_dspace(spa_t *spa)
{
spa->spa_rdspace = metaslab_class_get_dspace(spa_normal_class(spa));

/*
* For expanded RAIDZ vdevs, the metaslab class dspace was computed
* using the original (txg 0) deflation ratio, which understates the
* usable capacity. Apply a correction using the current geometry
* ratio, but only for the FREE portion of the vdev.
*
* The allocated portion must remain at the old ratio because
* dd_used_bytes (tracked per-block via bp_get_dsize_sync) also
* uses the old ratio for blocks born before the expansion
* accounting feature was enabled. Correcting only the free
* portion keeps spa_rdspace - dd_used_bytes = true available.
*
* As old blocks are rewritten with the new geometry, vs_alloc
* shrinks (less physical space per block) and the correction
* automatically grows, converging to the full correction after
* a complete rewrite.
*
* Only correct vdevs in the normal class, matching spa_rdspace's
* derivation from spa_normal_class(). RAIDZ vdevs in the special
* or dedup class contribute only allocated (not free) space to
* spa_dspace via metaslab_class_get_dalloc(), so they do not need
* a free-space correction.
*/
if (spa_deflate(spa)) {
metaslab_class_t *mc = spa_normal_class(spa);
vdev_t *rvd = spa->spa_root_vdev;
for (uint64_t c = 0; c < rvd->vdev_children; c++) {
vdev_t *vd = rvd->vdev_child[c];
if (vd->vdev_mg == NULL ||
vd->vdev_mg->mg_class != mc)
continue;
if (vd->vdev_deflate_ratio_current != 0 &&
vd->vdev_deflate_ratio_current !=
vd->vdev_deflate_ratio) {
uint64_t space = vd->vdev_stat.vs_space;
uint64_t aspace = vd->vdev_stat.vs_alloc;
uint64_t fspace = space - aspace;
int64_t old_fs = (fspace >>
SPA_MINBLOCKSHIFT) *
vd->vdev_deflate_ratio;
int64_t new_fs = (fspace >>
SPA_MINBLOCKSHIFT) *
vd->vdev_deflate_ratio_current;
spa->spa_rdspace += (new_fs - old_fs);
}
}
}

if (spa->spa_nonallocating_dspace > 0) {
/*
* Subtract the space provided by all non-allocating vdevs that
Expand Down Expand Up @@ -2290,8 +2339,17 @@ spa_set_deadman_synctime(hrtime_t ns)
}
}

/*
* Compute the deflated size of a DVA. The birth_txg parameter is used to
* determine the correct deflation ratio for RAIDZ vdevs that have been
* expanded. Blocks born under an older (narrower) geometry used more
* physical space per unit of data; using the birth-txg-appropriate ratio
* ensures accurate per-block accounting. For callers that do not have a
* birth txg (or for non-expanded vdevs), the cached vdev_deflate_ratio
* based on current geometry is used.
*/
uint64_t
dva_get_dsize_sync(spa_t *spa, const dva_t *dva)
dva_get_dsize_sync(spa_t *spa, const dva_t *dva, uint64_t birth_txg)
{
uint64_t asize = DVA_GET_ASIZE(dva);
uint64_t dsize = asize;
Expand All @@ -2300,9 +2358,10 @@ dva_get_dsize_sync(spa_t *spa, const dva_t *dva)

if (asize != 0 && spa->spa_deflate) {
vdev_t *vd = vdev_lookup_top(spa, DVA_GET_VDEV(dva));
if (vd != NULL)
dsize = (asize >> SPA_MINBLOCKSHIFT) *
vd->vdev_deflate_ratio;
if (vd != NULL) {
uint64_t ratio = vdev_get_deflate_ratio(vd, birth_txg);
dsize = (asize >> SPA_MINBLOCKSHIFT) * ratio;
}
}

return (dsize);
Expand All @@ -2313,8 +2372,16 @@ bp_get_dsize_sync(spa_t *spa, const blkptr_t *bp)
{
uint64_t dsize = 0;

/*
* Use physical birth: this reflects when the DVAs were actually
* allocated, and thus which RAIDZ geometry was in effect. For
* dedup/clones the logical birth may differ, but the physical
* birth matches the on-disk allocation geometry.
*/
uint64_t birth_txg = BP_GET_PHYSICAL_BIRTH(bp);

for (int d = 0; d < BP_GET_NDVAS(bp); d++)
dsize += dva_get_dsize_sync(spa, &bp->blk_dva[d]);
dsize += dva_get_dsize_sync(spa, &bp->blk_dva[d], birth_txg);

return (dsize);
}
Expand All @@ -2323,11 +2390,12 @@ uint64_t
bp_get_dsize(spa_t *spa, const blkptr_t *bp)
{
uint64_t dsize = 0;
uint64_t birth_txg = BP_GET_PHYSICAL_BIRTH(bp);
Comment thread
Skountz marked this conversation as resolved.

spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);

for (int d = 0; d < BP_GET_NDVAS(bp); d++)
dsize += dva_get_dsize_sync(spa, &bp->blk_dva[d]);
dsize += dva_get_dsize_sync(spa, &bp->blk_dva[d], birth_txg);

spa_config_exit(spa, SCL_VDEV, FTAG);

Expand Down
Loading
Loading