diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index 18221c4b92d2..e8726915cc8f 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -34,6 +34,7 @@ * Copyright (c) 2021 Allan Jude * Copyright (c) 2021 Toomas Soome * Copyright (c) 2023, Klara Inc. + * Copyright (c) 2023, Rob Norris */ #include @@ -80,6 +81,7 @@ #include #include #include +#include #include #include @@ -899,6 +901,8 @@ usage(void) "don't print label contents\n"); (void) fprintf(stderr, " -t --txg=INTEGER " "highest txg to use when searching for uberblocks\n"); + (void) fprintf(stderr, " -T --brt-stats " + "BRT statistics\n"); (void) fprintf(stderr, " -u --uberblock " "uberblock\n"); (void) fprintf(stderr, " -U --cachefile=PATH " @@ -999,6 +1003,15 @@ zdb_nicenum(uint64_t num, char *buf, size_t buflen) nicenum(num, buf, buflen); } +static void +zdb_nicebytes(uint64_t bytes, char *buf, size_t buflen) +{ + if (dump_opt['P']) + (void) snprintf(buf, buflen, "%llu", (longlong_t)bytes); + else + zfs_nicebytes(bytes, buf, buflen); +} + static const char histo_stars[] = "****************************************"; static const uint64_t histo_width = sizeof (histo_stars) - 1; @@ -2081,6 +2094,76 @@ dump_all_ddts(spa_t *spa) dump_dedup_ratio(&dds_total); } +static void +dump_brt(spa_t *spa) +{ + if (!spa_feature_is_enabled(spa, SPA_FEATURE_BLOCK_CLONING)) { + printf("BRT: unsupported on this pool\n"); + return; + } + + if (!spa_feature_is_active(spa, SPA_FEATURE_BLOCK_CLONING)) { + printf("BRT: empty\n"); + return; + } + + brt_t *brt = spa->spa_brt; + VERIFY(brt); + + char count[32], used[32], saved[32]; + zdb_nicebytes(brt_get_used(spa), used, sizeof (used)); + zdb_nicebytes(brt_get_saved(spa), saved, sizeof (saved)); + uint64_t ratio = brt_get_ratio(spa); + printf("BRT: used %s; saved %s; ratio %llu.%02llux\n", used, saved, + (u_longlong_t)(ratio / 100), (u_longlong_t)(ratio % 100)); + + if (dump_opt['T'] < 2) + return; + + for (uint64_t vdevid = 0; vdevid < brt->brt_nvdevs; vdevid++) { + brt_vdev_t *brtvd = &brt->brt_vdevs[vdevid]; + if (brtvd == NULL) + continue; + + if (!brtvd->bv_initiated) { + printf("BRT: vdev %lu: empty\n", vdevid); + continue; + } + + zdb_nicenum(brtvd->bv_totalcount, count, sizeof (count)); + zdb_nicebytes(brtvd->bv_usedspace, used, sizeof (used)); + zdb_nicebytes(brtvd->bv_savedspace, saved, sizeof (saved)); + printf("BRT: vdev %lu: refcnt %s; used %s; saved %s\n", + vdevid, count, used, saved); + } + + if (dump_opt['T'] < 3) + return; + + char dva[64]; + printf("\n%-16s %-10s\n", "DVA", "REFCNT"); + + for (uint64_t vdevid = 0; vdevid < brt->brt_nvdevs; vdevid++) { + brt_vdev_t *brtvd = &brt->brt_vdevs[vdevid]; + if (brtvd == NULL || !brtvd->bv_initiated) + continue; + + zap_cursor_t zc; + zap_attribute_t za; + for (zap_cursor_init(&zc, brt->brt_mos, brtvd->bv_mos_entries); + zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_advance(&zc)) { + uint64_t offset = *(uint64_t *)za.za_name; + uint64_t refcnt = za.za_first_integer; + + snprintf(dva, sizeof (dva), "%lu:%llx", vdevid, + (u_longlong_t)offset); + printf("%-16s %-10llu\n", dva, (u_longlong_t)refcnt); + } + zap_cursor_fini(&zc); + } +} + static void dump_dtl_seg(void *arg, uint64_t start, uint64_t size) { @@ -8108,6 +8191,9 @@ dump_zpool(spa_t *spa) if (dump_opt['D']) dump_all_ddts(spa); + if (dump_opt['T']) + dump_brt(spa); + if (dump_opt['d'] > 2 || dump_opt['m']) dump_metaslabs(spa); if (dump_opt['M']) @@ -8894,6 +8980,7 @@ main(int argc, char **argv) {"io-stats", no_argument, NULL, 's'}, {"simulate-dedup", no_argument, NULL, 'S'}, {"txg", required_argument, NULL, 't'}, + {"brt-stats", no_argument, NULL, 'T'}, {"uberblock", no_argument, NULL, 'u'}, {"cachefile", required_argument, NULL, 'U'}, {"verbose", no_argument, NULL, 'v'}, @@ -8907,7 +8994,7 @@ main(int argc, char **argv) }; while ((c = getopt_long(argc, argv, - "AbBcCdDeEFGhiI:kK:lLmMNo:Op:PqrRsSt:uU:vVx:XYyZ", + "AbBcCdDeEFGhiI:kK:lLmMNo:Op:PqrRsSt:TuU:vVx:XYyZ", long_options, NULL)) != -1) { switch (c) { case 'b': @@ -8929,6 +9016,7 @@ main(int argc, char **argv) case 'R': case 's': case 'S': + case 'T': case 'u': case 'y': case 'Z': diff --git a/man/man8/zdb.8 b/man/man8/zdb.8 index 52c8e452fa7c..d7f66d917ac7 100644 --- a/man/man8/zdb.8 +++ b/man/man8/zdb.8 @@ -14,7 +14,7 @@ .\" Copyright (c) 2017 Lawrence Livermore National Security, LLC. .\" Copyright (c) 2017 Intel Corporation. .\" -.Dd June 27, 2023 +.Dd November 18, 2023 .Dt ZDB 8 .Os . @@ -23,7 +23,7 @@ .Nd display ZFS storage pool debugging and consistency information .Sh SYNOPSIS .Nm -.Op Fl AbcdDFGhikLMNPsvXYy +.Op Fl AbcdDFGhikLMNPsTvXYy .Op Fl e Oo Fl V Oc Oo Fl p Ar path Oc Ns … .Op Fl I Ar inflight-I/O-ops .Oo Fl o Ar var Ns = Ns Ar value Oc Ns … @@ -403,6 +403,13 @@ Display operation counts, bandwidth, and error counts of I/O to the pool from Simulate the effects of deduplication, constructing a DDT and then display that DDT as with .Fl DD . +.It Fl T , -brt-stats +Display block reference table (BRT) statistics, including the size of uniques +blocks cloned, the space saving as a result of cloning, and the saving ratio. +.It Fl TT +Display the per-vdev BRT statistics, including total references. +.It Fl TTT +Dump the contents of the block reference tables. .It Fl u , -uberblock Display the current uberblock. .El diff --git a/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_args_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_args_neg.ksh index 168e7c18c3a3..688d488ceb62 100755 --- a/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_args_neg.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_args_neg.ksh @@ -58,7 +58,7 @@ set -A args "create" "add" "destroy" "import fakepool" \ "setvprop" "blah blah" "-%" "--?" "-*" "-=" \ "-a" "-f" "-g" "-j" "-n" "-o" "-p" "-p /tmp" \ "-t" "-w" "-z" "-E" "-H" "-I" "-J" \ - "-Q" "-R" "-T" "-W" + "-Q" "-R" "-W" log_assert "Execute zdb using invalid parameters."