#include <sys/dsl_crypt.h>
#include <sys/dsl_scan.h>
#include <sys/btree.h>
+#include <sys/brt.h>
#include <zfs_comutil.h>
#include <sys/zstd/zstd.h>
#define ZB_TOTAL DN_MAX_LEVELS
#define SPA_MAX_FOR_16M (SPA_MAXBLOCKSHIFT+1)
+typedef struct zdb_brt_entry {
+ dva_t zbre_dva;
+ uint64_t zbre_refcount;
+ avl_node_t zbre_node;
+} zdb_brt_entry_t;
+
typedef struct zdb_cb {
zdb_blkstats_t zcb_type[ZB_TOTAL + 1][ZDB_OT_TOTAL + 1];
uint64_t zcb_removing_size;
uint64_t zcb_checkpoint_size;
uint64_t zcb_dedup_asize;
uint64_t zcb_dedup_blocks;
+ uint64_t zcb_clone_asize;
+ uint64_t zcb_clone_blocks;
uint64_t zcb_psize_count[SPA_MAX_FOR_16M];
uint64_t zcb_lsize_count[SPA_MAX_FOR_16M];
uint64_t zcb_asize_count[SPA_MAX_FOR_16M];
int zcb_haderrors;
spa_t *zcb_spa;
uint32_t **zcb_vd_obsolete_counts;
+ avl_tree_t zcb_brt;
+ boolean_t zcb_brt_is_active;
} zdb_cb_t;
/* test if two DVA offsets from same vdev are within the same metaslab */
zcb->zcb_asize_len[bin] += BP_GET_ASIZE(bp);
zcb->zcb_asize_total += BP_GET_ASIZE(bp);
+ if (zcb->zcb_brt_is_active && brt_maybe_exists(zcb->zcb_spa, bp)) {
+ /*
+ * Cloned blocks are special. We need to count them, so we can
+ * later uncount them when reporting leaked space, and we must
+ * only claim them them once.
+ *
+ * To do this, we keep our own in-memory BRT. For each block
+ * we haven't seen before, we look it up in the real BRT and
+ * if its there, we note it and its refcount then proceed as
+ * normal. If we see the block again, we count it as a clone
+ * and then give it no further consideration.
+ */
+ zdb_brt_entry_t zbre_search, *zbre;
+ avl_index_t where;
+
+ zbre_search.zbre_dva = bp->blk_dva[0];
+ zbre = avl_find(&zcb->zcb_brt, &zbre_search, &where);
+ if (zbre != NULL) {
+ zcb->zcb_clone_asize += BP_GET_ASIZE(bp);
+ zcb->zcb_clone_blocks++;
+
+ zbre->zbre_refcount--;
+ if (zbre->zbre_refcount == 0) {
+ avl_remove(&zcb->zcb_brt, zbre);
+ umem_free(zbre, sizeof (zdb_brt_entry_t));
+ }
+ return;
+ }
+
+ uint64_t crefcnt = brt_entry_get_refcount(zcb->zcb_spa, bp);
+ if (crefcnt > 0) {
+ zbre = umem_zalloc(sizeof (zdb_brt_entry_t),
+ UMEM_NOFAIL);
+ zbre->zbre_dva = bp->blk_dva[0];
+ zbre->zbre_refcount = crefcnt;
+ avl_insert(&zcb->zcb_brt, zbre, where);
+ }
+ }
+
if (dump_opt['L'])
return;
iterate_deleted_livelists(spa, dump_livelist_cb, NULL);
}
+static int
+zdb_brt_entry_compare(const void *zcn1, const void *zcn2)
+{
+ const dva_t *dva1 = &((const zdb_brt_entry_t *)zcn1)->zbre_dva;
+ const dva_t *dva2 = &((const zdb_brt_entry_t *)zcn2)->zbre_dva;
+ int cmp;
+
+ cmp = TREE_CMP(DVA_GET_VDEV(dva1), DVA_GET_VDEV(dva2));
+ if (cmp == 0)
+ cmp = TREE_CMP(DVA_GET_OFFSET(dva1), DVA_GET_OFFSET(dva2));
+
+ return (cmp);
+}
+
static int
dump_block_stats(spa_t *spa)
{
zcb = umem_zalloc(sizeof (zdb_cb_t), UMEM_NOFAIL);
+ if (spa_feature_is_active(spa, SPA_FEATURE_BLOCK_CLONING)) {
+ avl_create(&zcb->zcb_brt, zdb_brt_entry_compare,
+ sizeof (zdb_brt_entry_t),
+ offsetof(zdb_brt_entry_t, zbre_node));
+ zcb->zcb_brt_is_active = B_TRUE;
+ }
+
(void) printf("\nTraversing all blocks %s%s%s%s%s...\n\n",
(dump_opt['c'] || !dump_opt['L']) ? "to verify " : "",
(dump_opt['c'] == 1) ? "metadata " : "",
metaslab_class_get_alloc(spa_special_class(spa)) +
metaslab_class_get_alloc(spa_dedup_class(spa)) +
get_unflushed_alloc_space(spa);
- total_found = tzb->zb_asize - zcb->zcb_dedup_asize +
+ total_found =
+ tzb->zb_asize - zcb->zcb_dedup_asize - zcb->zcb_clone_asize +
zcb->zcb_removing_size + zcb->zcb_checkpoint_size;
if (total_found == total_alloc && !dump_opt['L']) {
"bp deduped:", (u_longlong_t)zcb->zcb_dedup_asize,
(u_longlong_t)zcb->zcb_dedup_blocks,
(double)zcb->zcb_dedup_asize / tzb->zb_asize + 1.0);
+ (void) printf("\t%-16s %14llu count: %6llu\n",
+ "bp cloned:", (u_longlong_t)zcb->zcb_clone_asize,
+ (u_longlong_t)zcb->zcb_clone_blocks);
(void) printf("\t%-16s %14llu used: %5.2f%%\n", "Normal class:",
(u_longlong_t)norm_alloc, 100.0 * norm_alloc / norm_space);