]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_db: add a function to compute btree geometry
authorDarrick J. Wong <darrick.wong@oracle.com>
Mon, 9 Sep 2019 19:37:08 +0000 (15:37 -0400)
committerEric Sandeen <sandeen@redhat.com>
Mon, 9 Sep 2019 19:37:08 +0000 (15:37 -0400)
Add a new command to xfs_db that uses a btree type and a record count
to report the best and worst case height and level size.  This can be
used to estimate how much overhead a metadata index will add to a
filesystem.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
db/Makefile
db/btheight.c [new file with mode: 0644]
db/command.c
db/command.h
libxfs/libxfs_api_defs.h

index 0941b32ed8a554995f0308b80b50600f90f2cffe..ed9f56c2143e3e751d3420002997383d79b4a82b 100644 (file)
@@ -14,7 +14,7 @@ HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
        io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
        sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h \
        fuzz.h
-CFILES = $(HFILES:.h=.c) btdump.c convert.c info.c
+CFILES = $(HFILES:.h=.c) btdump.c btheight.c convert.c info.c
 LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
 
 LLDLIBS        = $(LIBXFS) $(LIBXLOG) $(LIBFROG) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
diff --git a/db/btheight.c b/db/btheight.c
new file mode 100644 (file)
index 0000000..289e5d8
--- /dev/null
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+#include "libxfs.h"
+#include "command.h"
+#include "output.h"
+#include "init.h"
+#include "io.h"
+#include "type.h"
+#include "input.h"
+#include "libfrog/convert.h"
+
+static int refc_maxrecs(struct xfs_mount *mp, int blocklen, int leaf)
+{
+       return libxfs_refcountbt_maxrecs(blocklen, leaf != 0);
+}
+
+static int rmap_maxrecs(struct xfs_mount *mp, int blocklen, int leaf)
+{
+       return libxfs_rmapbt_maxrecs(blocklen, leaf);
+}
+
+struct btmap {
+       const char      *tag;
+       int             (*maxrecs)(struct xfs_mount *mp, int blocklen,
+                                  int leaf);
+} maps[] = {
+       {"bnobt", libxfs_allocbt_maxrecs},
+       {"cntbt", libxfs_allocbt_maxrecs},
+       {"inobt", libxfs_inobt_maxrecs},
+       {"finobt", libxfs_inobt_maxrecs},
+       {"bmapbt", libxfs_bmbt_maxrecs},
+       {"refcountbt", refc_maxrecs},
+       {"rmapbt", rmap_maxrecs},
+};
+
+static void
+btheight_help(void)
+{
+       struct btmap    *m;
+       int             i;
+
+       dbprintf(_(
+"\n"
+" For a given number of btree records and a btree type, report the number of\n"
+" records and blocks for each level of the btree, and the total btree size.\n"
+" The btree type must be given after the options.  A raw btree geometry can\n"
+" be provided in the format \"record_bytes:key_bytes:ptr_bytes:header_type\"\n"
+" where header_type is one of \"short\", \"long\", \"shortcrc\", or \"longcrc\".\n"
+"\n"
+" Options:\n"
+"   -b -- Override the btree block size.\n"
+"   -n -- Number of records we want to store.\n"
+"   -w max -- Show only the best case scenario.\n"
+"   -w min -- Show only the worst case scenario.\n"
+"\n"
+" Supported btree types:\n"
+"   "
+));
+       for (i = 0, m = maps; i < ARRAY_SIZE(maps); i++, m++)
+               printf("%s ", m->tag);
+       printf("\n");
+}
+
+static void
+calc_height(
+       unsigned long long      nr_records,
+       uint                    *records_per_block)
+{
+       unsigned int            level = 0;
+       unsigned long long      total_blocks = 0;
+       unsigned long long      blocks;
+       char                    *levels_suffix = "s";
+       char                    *totblocks_suffix = "s";
+
+       while (nr_records) {
+               unsigned int    level_rpb = records_per_block[level != 0];
+               char            *recs_suffix = "s";
+               char            *blocks_suffix = "s";
+
+               blocks = (nr_records + level_rpb - 1) / level_rpb;
+
+               if (nr_records == 1)
+                       recs_suffix = "";
+               if (blocks == 1)
+                       blocks_suffix = "";
+
+               printf(_("level %u: %llu record%s, %llu block%s\n"),
+                               level, nr_records, recs_suffix, blocks,
+                               blocks_suffix);
+
+               total_blocks += blocks;
+               nr_records = blocks == 1 ? 0 : blocks;
+               level++;
+       }
+
+       if (level == 1)
+               levels_suffix = "";
+       if (total_blocks == 1)
+               totblocks_suffix = "";
+
+       printf(_("%u level%s, %llu block%s total\n"), level, levels_suffix,
+                       total_blocks, totblocks_suffix);
+}
+
+static int
+construct_records_per_block(
+       char            *tag,
+       int             blocksize,
+       unsigned int    *records_per_block)
+{
+       char            *toktag;
+       struct btmap    *m;
+       unsigned int    record_size, key_size, ptr_size;
+       char            *p;
+       int             i, ret;
+
+       for (i = 0, m = maps; i < ARRAY_SIZE(maps); i++, m++) {
+               if (!strcmp(m->tag, tag)) {
+                       records_per_block[0] = m->maxrecs(mp, blocksize, 1);
+                       records_per_block[1] = m->maxrecs(mp, blocksize, 0);
+                       return 0;
+               }
+       }
+
+       toktag = strdup(tag);
+       ret = -1;
+
+       p = strtok(toktag, ":");
+       if (!p) {
+               fprintf(stderr, _("%s: record size not found.\n"), tag);
+               goto out;
+       }
+       record_size = cvt_u16(p, 0);
+       if (errno) {
+               perror(p);
+               goto out;
+       }
+
+       p = strtok(NULL, ":");
+       if (!p) {
+               fprintf(stderr, _("%s: key size not found.\n"), tag);
+               goto out;
+       }
+       key_size = cvt_u16(p, 0);
+       if (errno) {
+               perror(p);
+               goto out;
+       }
+
+       p = strtok(NULL, ":");
+       if (!p) {
+               fprintf(stderr, _("%s: pointer size not found.\n"), tag);
+               goto out;
+       }
+       ptr_size = cvt_u16(p, 0);
+       if (errno) {
+               perror(p);
+               goto out;
+       }
+
+       p = strtok(NULL, ":");
+       if (!p) {
+               fprintf(stderr, _("%s: header type not found.\n"), tag);
+               goto out;
+       }
+       if (!strcmp(p, "short"))
+               blocksize -= XFS_BTREE_SBLOCK_LEN;
+       else if (!strcmp(p, "shortcrc"))
+               blocksize -= XFS_BTREE_SBLOCK_CRC_LEN;
+       else if (!strcmp(p, "long"))
+               blocksize -= XFS_BTREE_LBLOCK_LEN;
+       else if (!strcmp(p, "longcrc"))
+               blocksize -= XFS_BTREE_LBLOCK_CRC_LEN;
+       else {
+               fprintf(stderr, _("%s: unrecognized btree header type."),
+                               p);
+               goto out;
+       }
+
+       p = strtok(NULL, ":");
+       if (p) {
+               fprintf(stderr,
+                       _("%s: unrecognized raw btree geometry."),
+                               tag);
+               goto out;
+       }
+
+       records_per_block[0] = blocksize / record_size;
+       records_per_block[1] = blocksize / (key_size + ptr_size);
+       ret = 0;
+out:
+       free(toktag);
+       return ret;
+}
+
+#define REPORT_DEFAULT (-1U)
+#define REPORT_MAX     (1 << 0)
+#define REPORT_MIN     (1 << 1)
+
+static void
+report(
+       char                    *tag,
+       unsigned int            report_what,
+       unsigned long long      nr_records,
+       unsigned int            blocksize)
+{
+       unsigned int            records_per_block[2];
+       int                     ret;
+
+       ret = construct_records_per_block(tag, blocksize, records_per_block);
+       if (ret) {
+               printf(_("%s: Unable to determine records per block.\n"),
+                               tag);
+               return;
+       }
+
+       if (report_what & REPORT_MAX) {
+               printf(
+_("%s: best case per %u-byte block: %u records (leaf) / %u keyptrs (node)\n"),
+                               tag, blocksize, records_per_block[0],
+                               records_per_block[1]);
+
+               calc_height(nr_records, records_per_block);
+       }
+
+       if (report_what & REPORT_MIN) {
+               records_per_block[0] /= 2;
+               records_per_block[1] /= 2;
+
+               printf(
+_("%s: worst case per %u-byte block: %u records (leaf) / %u keyptrs (node)\n"),
+                               tag, blocksize, records_per_block[0],
+                               records_per_block[1]);
+
+               calc_height(nr_records, records_per_block);
+       }
+}
+
+static int
+btheight_f(
+       int             argc,
+       char            **argv)
+{
+       long long       blocksize = mp->m_sb.sb_blocksize;
+       uint64_t        nr_records = 0;
+       int             report_what = REPORT_DEFAULT;
+       int             i, c;
+
+       while ((c = getopt(argc, argv, "b:n:w:")) != -1) {
+               switch (c) {
+               case 'b':
+                       errno = 0;
+                       blocksize = cvtnum(mp->m_sb.sb_blocksize,
+                                       mp->m_sb.sb_sectsize,
+                                       optarg);
+                       if (errno) {
+                               perror(optarg);
+                               return 0;
+                       }
+                       break;
+               case 'n':
+                       nr_records = cvt_u64(optarg, 0);
+                       if (errno) {
+                               perror(optarg);
+                               return 0;
+                       }
+                       break;
+               case 'w':
+                       if (!strcmp(optarg, "min"))
+                               report_what = REPORT_MIN;
+                       else if (!strcmp(optarg, "max"))
+                               report_what = REPORT_MAX;
+                       else {
+                               btheight_help();
+                               return 0;
+                       }
+                       break;
+               default:
+                       btheight_help();
+                       return 0;
+               }
+       }
+
+       if (argc == optind || blocksize <= 0 || blocksize > INT_MAX ||
+           nr_records == 0) {
+               btheight_help();
+               return 0;
+       }
+
+       for (i = optind; i < argc; i++)
+               report(argv[i], report_what, nr_records, blocksize);
+
+       return 0;
+}
+
+static const cmdinfo_t btheight_cmd =
+       { "btheight", "b", btheight_f, 1, -1, 0,
+         "[-b blksz] [-n recs] [-w max|-w min] btree types...",
+         N_("compute btree heights"), btheight_help };
+
+void
+btheight_init(void)
+{
+       add_command(&btheight_cmd);
+}
index 89a78f03cb71e164aa7e642f840ab85f23ca846f..0fb44efaec59859a67cefd0c241b9cd9a6552f96 100644 (file)
@@ -113,6 +113,7 @@ init_commands(void)
        block_init();
        bmap_init();
        btdump_init();
+       btheight_init();
        check_init();
        convert_init();
        crc_init();
index 2f9a7e161e08296e30b59861ece3b1e6b6e7c485..b8499de0be17e04aa96cacf3ac2cd44bb3213086 100644 (file)
@@ -31,3 +31,4 @@ extern void           init_commands(void);
 extern void            convert_init(void);
 extern void            btdump_init(void);
 extern void            info_init(void);
+extern void            btheight_init(void);
index 7bd373f70bdaf11eec978707a989d16ee23d5075..0ae21318091f9589d2b4817e916f6ae2a6119e42 100644 (file)
 #define xfs_dir_ino_validate           libxfs_dir_ino_validate
 #define xfs_initialize_perag_data      libxfs_initialize_perag_data
 #define xfs_inobt_maxrecs              libxfs_inobt_maxrecs
+#define xfs_rmapbt_maxrecs             libxfs_rmapbt_maxrecs
+#define xfs_refcountbt_maxrecs         libxfs_refcountbt_maxrecs
 #define xfs_iread_extents              libxfs_iread_extents
 #define xfs_log_calc_minimum_size      libxfs_log_calc_minimum_size
 #define xfs_perag_get                  libxfs_perag_get