1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2019 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
13 #include "libfrog/convert.h"
15 static int refc_maxrecs(struct xfs_mount
*mp
, int blocklen
, int leaf
)
17 return libxfs_refcountbt_maxrecs(blocklen
, leaf
!= 0);
20 static int rmap_maxrecs(struct xfs_mount
*mp
, int blocklen
, int leaf
)
22 return libxfs_rmapbt_maxrecs(blocklen
, leaf
);
27 unsigned int (*maxlevels
)(void);
28 int (*maxrecs
)(struct xfs_mount
*mp
, int blocklen
,
33 .maxlevels
= libxfs_allocbt_maxlevels_ondisk
,
34 .maxrecs
= libxfs_allocbt_maxrecs
,
38 .maxlevels
= libxfs_allocbt_maxlevels_ondisk
,
39 .maxrecs
= libxfs_allocbt_maxrecs
,
43 .maxlevels
= libxfs_iallocbt_maxlevels_ondisk
,
44 .maxrecs
= libxfs_inobt_maxrecs
,
48 .maxlevels
= libxfs_iallocbt_maxlevels_ondisk
,
49 .maxrecs
= libxfs_inobt_maxrecs
,
53 .maxlevels
= libxfs_bmbt_maxlevels_ondisk
,
54 .maxrecs
= libxfs_bmbt_maxrecs
,
58 .maxlevels
= libxfs_refcountbt_maxlevels_ondisk
,
59 .maxrecs
= refc_maxrecs
,
63 .maxlevels
= libxfs_rmapbt_maxlevels_ondisk
,
64 .maxrecs
= rmap_maxrecs
,
76 " For a given number of btree records and a btree type, report the number of\n"
77 " records and blocks for each level of the btree, and the total btree size.\n"
78 " The btree type must be given after the options. A raw btree geometry can\n"
79 " be provided in the format \"record_bytes:key_bytes:ptr_bytes:header_type\"\n"
80 " where header_type is one of \"short\", \"long\", \"shortcrc\", or \"longcrc\".\n"
83 " -b -- Override the btree block size.\n"
84 " -n -- Number of records we want to store.\n"
85 " -w max -- Show only the best case scenario.\n"
86 " -w min -- Show only the worst case scenario.\n"
87 " -w absmax -- Print the maximum possible btree height for all filesystems.\n"
89 " Supported btree types:\n"
92 for (i
= 0, m
= maps
; i
< ARRAY_SIZE(maps
); i
++, m
++)
93 printf("%s ", m
->tag
);
99 unsigned long long nr_records
,
100 uint
*records_per_block
)
102 unsigned int level
= 0;
103 unsigned long long total_blocks
= 0;
104 unsigned long long blocks
;
105 char *levels_suffix
= "s";
106 char *totblocks_suffix
= "s";
109 unsigned int level_rpb
= records_per_block
[level
!= 0];
110 char *recs_suffix
= "s";
111 char *blocks_suffix
= "s";
113 blocks
= (nr_records
+ level_rpb
- 1) / level_rpb
;
120 printf(_("level %u: %llu record%s, %llu block%s\n"),
121 level
, nr_records
, recs_suffix
, blocks
,
124 total_blocks
+= blocks
;
125 nr_records
= blocks
== 1 ? 0 : blocks
;
131 if (total_blocks
== 1)
132 totblocks_suffix
= "";
134 printf(_("%u level%s, %llu block%s total\n"), level
, levels_suffix
,
135 total_blocks
, totblocks_suffix
);
139 construct_records_per_block(
142 unsigned int *records_per_block
)
146 unsigned int record_size
, key_size
, ptr_size
;
150 for (i
= 0, m
= maps
; i
< ARRAY_SIZE(maps
); i
++, m
++) {
151 if (!strcmp(m
->tag
, tag
)) {
152 records_per_block
[0] = m
->maxrecs(mp
, blocksize
, 1);
153 records_per_block
[1] = m
->maxrecs(mp
, blocksize
, 0);
158 toktag
= strdup(tag
);
161 p
= strtok(toktag
, ":");
163 fprintf(stderr
, _("%s: record size not found.\n"), tag
);
166 record_size
= cvt_u16(p
, 0);
171 if (record_size
== 0) {
172 fprintf(stderr
, _("%s: record size cannot be zero.\n"), tag
);
176 p
= strtok(NULL
, ":");
178 fprintf(stderr
, _("%s: key size not found.\n"), tag
);
181 key_size
= cvt_u16(p
, 0);
187 fprintf(stderr
, _("%s: key size cannot be zero.\n"), tag
);
191 p
= strtok(NULL
, ":");
193 fprintf(stderr
, _("%s: pointer size not found.\n"), tag
);
196 ptr_size
= cvt_u16(p
, 0);
202 fprintf(stderr
, _("%s: pointer size cannot be zero.\n"), tag
);
206 p
= strtok(NULL
, ":");
208 fprintf(stderr
, _("%s: header type not found.\n"), tag
);
211 if (!strcmp(p
, "short"))
212 blocksize
-= XFS_BTREE_SBLOCK_LEN
;
213 else if (!strcmp(p
, "shortcrc"))
214 blocksize
-= XFS_BTREE_SBLOCK_CRC_LEN
;
215 else if (!strcmp(p
, "long"))
216 blocksize
-= XFS_BTREE_LBLOCK_LEN
;
217 else if (!strcmp(p
, "longcrc"))
218 blocksize
-= XFS_BTREE_LBLOCK_CRC_LEN
;
220 fprintf(stderr
, _("%s: unrecognized btree header type."),
225 if (record_size
> blocksize
) {
227 _("%s: record size must be less than selected block size (%u bytes).\n"),
232 if (key_size
> blocksize
) {
234 _("%s: key size must be less than selected block size (%u bytes).\n"),
239 if (ptr_size
> blocksize
) {
241 _("%s: pointer size must be less than selected block size (%u bytes).\n"),
246 p
= strtok(NULL
, ":");
249 _("%s: unrecognized raw btree geometry."),
254 records_per_block
[0] = blocksize
/ record_size
;
255 records_per_block
[1] = blocksize
/ (key_size
+ ptr_size
);
262 #define REPORT_DEFAULT (-1U)
263 #define REPORT_MAX (1 << 0)
264 #define REPORT_MIN (1 << 1)
265 #define REPORT_ABSMAX (1 << 2)
268 report_absmax(const char *tag
)
273 for (i
= 0, m
= maps
; i
< ARRAY_SIZE(maps
); i
++, m
++) {
274 if (!strcmp(m
->tag
, tag
)) {
275 printf("%s: %u\n", tag
, m
->maxlevels());
279 printf(_("%s: Don't know how to report max height.\n"), tag
);
285 unsigned int report_what
,
286 unsigned long long nr_records
,
287 unsigned int blocksize
)
289 unsigned int records_per_block
[2];
292 if (report_what
== REPORT_ABSMAX
) {
297 ret
= construct_records_per_block(tag
, blocksize
, records_per_block
);
301 if (report_what
& REPORT_MAX
) {
302 if (records_per_block
[0] < 2) {
304 _("%s: cannot calculate best case scenario due to leaf geometry underflow.\n"),
309 if (records_per_block
[1] < 4) {
311 _("%s: cannot calculate best case scenario due to node geometry underflow.\n"),
317 _("%s: best case per %u-byte block: %u records (leaf) / %u keyptrs (node)\n"),
318 tag
, blocksize
, records_per_block
[0],
319 records_per_block
[1]);
321 calc_height(nr_records
, records_per_block
);
324 if (report_what
& REPORT_MIN
) {
325 records_per_block
[0] /= 2;
326 records_per_block
[1] /= 2;
328 if (records_per_block
[0] < 1) {
330 _("%s: cannot calculate worst case scenario due to leaf geometry underflow.\n"),
335 if (records_per_block
[1] < 2) {
337 _("%s: cannot calculate worst case scenario due to node geometry underflow.\n"),
343 _("%s: worst case per %u-byte block: %u records (leaf) / %u keyptrs (node)\n"),
344 tag
, blocksize
, records_per_block
[0],
345 records_per_block
[1]);
347 calc_height(nr_records
, records_per_block
);
353 unsigned int report_what
,
354 unsigned long long nr_records
,
355 unsigned int blocksize
)
360 for (i
= 0, m
= maps
; i
< ARRAY_SIZE(maps
); i
++, m
++)
361 report(m
->tag
, report_what
, nr_records
, blocksize
);
369 long long blocksize
= mp
->m_sb
.sb_blocksize
;
370 uint64_t nr_records
= 0;
371 int report_what
= REPORT_DEFAULT
;
374 while ((c
= getopt(argc
, argv
, "b:n:w:")) != -1) {
378 blocksize
= cvtnum(mp
->m_sb
.sb_blocksize
,
379 mp
->m_sb
.sb_sectsize
,
387 nr_records
= cvt_u64(optarg
, 0);
394 if (!strcmp(optarg
, "min"))
395 report_what
= REPORT_MIN
;
396 else if (!strcmp(optarg
, "max"))
397 report_what
= REPORT_MAX
;
398 else if (!strcmp(optarg
, "absmax"))
399 report_what
= REPORT_ABSMAX
;
411 if (report_what
!= REPORT_ABSMAX
&& nr_records
== 0) {
413 _("Number of records must be greater than zero.\n"));
417 if (report_what
!= REPORT_ABSMAX
&& blocksize
> INT_MAX
) {
419 _("The largest block size this command will consider is %u bytes.\n"),
424 if (report_what
!= REPORT_ABSMAX
&& blocksize
< 128) {
426 _("The smallest block size this command will consider is 128 bytes.\n"));
430 if (argc
== optind
) {
435 for (i
= optind
; i
< argc
; i
++) {
436 if (!strcmp(argv
[i
], "all")) {
437 report_all(report_what
, nr_records
, blocksize
);
442 for (i
= optind
; i
< argc
; i
++)
443 report(argv
[i
], report_what
, nr_records
, blocksize
);
448 static const cmdinfo_t btheight_cmd
=
449 { "btheight", "b", btheight_f
, 1, -1, 0,
450 "[-b blksz] [-n recs] [-w max|-w min] btree types...",
451 N_("compute btree heights"), btheight_help
};
456 add_command(&btheight_cmd
);