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
128 changes: 110 additions & 18 deletions cmd/zdb/zdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -6092,16 +6092,19 @@ typedef struct zdb_blkstats {
} zdb_blkstats_t;

/*
* Extended object types to report deferred frees and dedup auto-ditto blocks.
* Extended object types to report deferred frees, dedup auto-ditto blocks
* and pending DDT-log frees.
*/
#define ZDB_OT_DEFERRED (DMU_OT_NUMTYPES + 0)
#define ZDB_OT_DITTO (DMU_OT_NUMTYPES + 1)
#define ZDB_OT_OTHER (DMU_OT_NUMTYPES + 2)
#define ZDB_OT_TOTAL (DMU_OT_NUMTYPES + 3)
#define ZDB_OT_DDT_PENDING (DMU_OT_NUMTYPES + 2)
#define ZDB_OT_OTHER (DMU_OT_NUMTYPES + 3)
#define ZDB_OT_TOTAL (DMU_OT_NUMTYPES + 4)

static const char *zdb_ot_extname[] = {
"deferred free",
"dedup ditto",
"DDT pending free",
"other",
"Total",
};
Expand Down Expand Up @@ -6608,24 +6611,54 @@ zdb_count_block(zdb_cb_t *zcb, zilog_t *zilog, const blkptr_t *bp,
uint64_t offset = DVA_GET_OFFSET(&bp->blk_dva[0]);
vdev_t *vd = vdev_lookup_top(zcb->zcb_spa, vdev);
ASSERT(vd != NULL);
metaslab_t *ms = vd->vdev_ms[offset >> vd->vdev_ms_shift];
ASSERT(ms != NULL);
metaslab_group_t *mg = ms->ms_group;
ASSERT(mg != NULL);
metaslab_class_t *mc = mg->mg_class;
ASSERT(mc != NULL);

spa_config_exit(zcb->zcb_spa, SCL_CONFIG, FTAG);

int class;
if (mc == spa_normal_class(zcb->zcb_spa)) {
class = CLASS_NORMAL;
} else if (mc == spa_special_class(zcb->zcb_spa)) {
class = CLASS_SPECIAL;
} else if (mc == spa_dedup_class(zcb->zcb_spa)) {
class = CLASS_DEDUP;
if (vd->vdev_ops == &vdev_indirect_ops) {
/*
* A removed vdev has no metaslabs of its own. Without
* -L we synthesize them (see
* zdb_leak_init_prepare_indirect_vdevs()), but under
* -L there is no metaslab array to index. Classify
* from the allocation bias, which determines the
* vdev's primary metaslab group, and does not depend
* on whether those synthetic metaslabs happen to
* exist.
*/
Comment thread
amotin marked this conversation as resolved.
switch (vd->vdev_alloc_bias) {
case VDEV_BIAS_SPECIAL:
class = CLASS_SPECIAL;
break;
case VDEV_BIAS_DEDUP:
class = CLASS_DEDUP;
break;
case VDEV_BIAS_LOG:
class = CLASS_OTHER;
break;
default:
class = CLASS_NORMAL;
break;
}
spa_config_exit(zcb->zcb_spa, SCL_CONFIG, FTAG);
} else {
class = CLASS_OTHER;
metaslab_t *ms =
vd->vdev_ms[offset >> vd->vdev_ms_shift];
ASSERT(ms != NULL);
metaslab_group_t *mg = ms->ms_group;
ASSERT(mg != NULL);
metaslab_class_t *mc = mg->mg_class;
ASSERT(mc != NULL);

spa_config_exit(zcb->zcb_spa, SCL_CONFIG, FTAG);

if (mc == spa_normal_class(zcb->zcb_spa)) {
class = CLASS_NORMAL;
} else if (mc == spa_special_class(zcb->zcb_spa)) {
class = CLASS_SPECIAL;
} else if (mc == spa_dedup_class(zcb->zcb_spa)) {
class = CLASS_DEDUP;
} else {
class = CLASS_OTHER;
}
}

if (!(block_classes & class)) {
Expand Down Expand Up @@ -7633,6 +7666,63 @@ bpobj_count_block_cb(void *arg, const blkptr_t *bp, boolean_t bp_freed,
return (count_block_cb(arg, bp, tx));
}

/*
* With fast dedup, the last decref of a block lands in the DDT log, and
* the physical free happens only when the log entry is flushed back into
* the DDT (see ddt_sync_flush_entry()). Until then the block is not
* referenced by any block pointer but is still allocated, so the
* traversal would misreport it as leaked. Count the phys that the flush
* will free here, the same way as the deferred-free bplist: they are
* frees in flight. Entries behind the log checkpoint are already flushed
* and are not loaded into the in-memory log trees, so everything found
* here is genuinely pending.
*/
static void
zdb_count_ddt_log_frees(spa_t *spa, zdb_cb_t *zcb)
{
for (enum zio_checksum c = 0; c < ZIO_CHECKSUM_FUNCTIONS; c++) {
ddt_t *ddt = spa->spa_ddt[c];
if (ddt == NULL || !(ddt->ddt_flags & DDT_FLAG_LOG))
continue;
for (int n = 0; n < 2; n++) {
ddt_log_t *ddl = &ddt->ddt_log[n];
for (ddt_log_entry_t *ddle = avl_first(&ddl->ddl_tree);
ddle; ddle = AVL_NEXT(&ddl->ddl_tree, ddle)) {
ddt_lightweight_entry_t ddlwe;
DDT_LOG_ENTRY_TO_LIGHTWEIGHT(ddt, ddle,
&ddlwe);
for (int p = 0; p < DDT_NPHYS(ddt); p++) {
ddt_phys_variant_t v =
DDT_PHYS_VARIANT(ddt, p);
/*
* Mirror ddt_sync_flush_entry(): an
* unborn phys owns nothing, an
* obsolete ditto slot is freed
* whatever its refcount, and any
* other slot is freed once its last
* reference is gone.
*/
if (ddt_phys_birth(&ddlwe.ddlwe_phys,
v) == 0)
continue;
if (!DDT_PHYS_IS_DITTO(ddt, p) &&
ddt_phys_refcnt(&ddlwe.ddlwe_phys,
v) != 0)
continue;
blkptr_t blk;
ddt_bp_create(ddt->ddt_checksum,
&ddlwe.ddlwe_key, &ddlwe.ddlwe_phys,
v, &blk);
/* As ddt_phys_free() does at flush. */
BP_SET_DEDUP(&blk, 0);
zdb_count_block(zcb, NULL, &blk,
ZDB_OT_DDT_PENDING);
}
}
}
}
}

static int
livelist_entry_count_blocks_cb(void *args, dsl_deadlist_entry_t *dle)
{
Expand Down Expand Up @@ -7758,6 +7848,8 @@ dump_block_stats(spa_t *spa)
bpobj_count_block_cb, zcb, NULL);
}

zdb_count_ddt_log_frees(spa, zcb);

zdb_claim_removing(spa, zcb);

if (spa_feature_is_active(spa, SPA_FEATURE_ASYNC_DESTROY)) {
Expand Down
4 changes: 3 additions & 1 deletion tests/runfiles/common.run
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,7 @@ tags = ['functional', 'deadman']
tests = ['dedup_bclone', 'dedup_bclone_pruned', 'dedup_fdt_create',
'dedup_fdt_import',
'dedup_fdt_pacing', 'dedup_legacy_create', 'dedup_legacy_import',
'dedup_log_zdb_leak',
'dedup_legacy_fdt_upgrade', 'dedup_legacy_fdt_mixed', 'dedup_quota',
'dedup_prune', 'dedup_prune_leak', 'dedup_zap_shrink']
pre =
Expand Down Expand Up @@ -950,7 +951,8 @@ tags = ['functional', 'refreserv']
[tests/functional/removal]
pre =
tests = ['removal_all_vdev', 'removal_cancel', 'removal_check_space',
'removal_condense_export', 'removal_multiple_indirection',
'removal_condense_export', 'removal_indirect_class',
'removal_multiple_indirection',
'removal_nopwrite', 'removal_remap_deadlists',
'removal_resume_export', 'removal_sanity', 'removal_with_add',
'removal_with_create_fs', 'removal_with_dedup',
Expand Down
2 changes: 2 additions & 0 deletions tests/zfs-tests/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/dedup/dedup_fdt_pacing.ksh \
functional/dedup/dedup_legacy_create.ksh \
functional/dedup/dedup_legacy_import.ksh \
functional/dedup/dedup_log_zdb_leak.ksh \
functional/dedup/dedup_legacy_fdt_upgrade.ksh \
functional/dedup/dedup_legacy_fdt_mixed.ksh \
functional/dedup/dedup_prune.ksh \
Expand Down Expand Up @@ -2021,6 +2022,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/removal/removal_cancel.ksh \
functional/removal/removal_check_space.ksh \
functional/removal/removal_condense_export.ksh \
functional/removal/removal_indirect_class.ksh \
functional/removal/removal_multiple_indirection.ksh \
functional/removal/removal_nopwrite.ksh \
functional/removal/removal_remap_deadlists.ksh \
Expand Down
Loading
Loading