From 0916d3866454cc6f67cf4ea52cb79e9455138f7a Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 3 Jun 2024 19:33:03 -0400 Subject: [PATCH] cmd_fs_usage: Use now BCH_IOCTL_QUERY_ACCOUNTING We can now report on compression type/ratio, btree usage, and pending rebalance work. Signed-off-by: Kent Overstreet --- c_src/cmd_fs.c | 239 ++++++++++++++++++++++++++++++++++++++------ c_src/libbcachefs.h | 29 ++++++ 2 files changed, 240 insertions(+), 28 deletions(-) diff --git a/c_src/cmd_fs.c b/c_src/cmd_fs.c index dba0dd2e..b792cb85 100644 --- a/c_src/cmd_fs.c +++ b/c_src/cmd_fs.c @@ -9,6 +9,7 @@ #include "libbcachefs/bcachefs_ioctl.h" #include "libbcachefs/buckets.h" +#include "libbcachefs/disk_accounting.h" #include "libbcachefs/opts.h" #include "libbcachefs/super-io.h" @@ -151,11 +152,28 @@ static void devs_usage_to_text(struct printbuf *out, } } +static void persistent_reserved_to_text(struct printbuf *out, + unsigned nr_replicas, s64 sectors) +{ + if (!sectors) + return; + + prt_str(out, "reserved:"); + prt_tab(out); + prt_printf(out, "%u/%u ", 1, nr_replicas); + prt_tab(out); + prt_str(out, "[] "); + prt_units_u64(out, sectors << 9); + prt_tab_rjust(out); + prt_newline(out); +} + static void replicas_usage_to_text(struct printbuf *out, - const struct bch_replicas_usage *r, + const struct bch_replicas_entry_v1 *r, + s64 sectors, dev_names *dev_names) { - if (!r->sectors) + if (!sectors) return; char devs[4096], *d = devs; @@ -163,8 +181,8 @@ static void replicas_usage_to_text(struct printbuf *out, unsigned durability = 0; - for (unsigned i = 0; i < r->r.nr_devs; i++) { - unsigned dev_idx = r->r.devs[i]; + for (unsigned i = 0; i < r->nr_devs; i++) { + unsigned dev_idx = r->devs[i]; struct dev_name *dev = dev_idx_to_name(dev_names, dev_idx); durability += dev->durability; @@ -179,11 +197,11 @@ static void replicas_usage_to_text(struct printbuf *out, *d++ = ']'; *d++ = '\0'; - bch2_prt_data_type(out, r->r.data_type); + bch2_prt_data_type(out, r->data_type); prt_char(out, ':'); prt_tab(out); - prt_printf(out, "%u/%u ", r->r.nr_required, r->r.nr_devs); + prt_printf(out, "%u/%u ", r->nr_required, r->nr_devs); prt_tab(out); prt_printf(out, "%u ", durability); @@ -192,7 +210,7 @@ static void replicas_usage_to_text(struct printbuf *out, prt_printf(out, "%s ", devs); prt_tab(out); - prt_units_u64(out, r->sectors << 9); + prt_units_u64(out, sectors << 9); prt_tab_rjust(out); prt_newline(out); } @@ -203,14 +221,178 @@ static void replicas_usage_to_text(struct printbuf *out, _r = replicas_usage_next(_r), \ BUG_ON((void *) _r > (void *) (_u)->replicas + (_u)->replica_entries_bytes)) -static void fs_usage_to_text(struct printbuf *out, const char *path) +typedef DARRAY(struct bkey_i_accounting *) darray_accounting_p; + +static int accounting_p_cmp(const void *_l, const void *_r) { - unsigned i; + const struct bkey_i_accounting * const *l = _l; + const struct bkey_i_accounting * const *r = _r; - struct bchfs_handle fs = bcache_fs_open(path); + struct bpos lp = (*l)->k.p, rp = (*r)->k.p; - dev_names dev_names = bchu_fs_get_devices(fs); + bch2_bpos_swab(&lp); + bch2_bpos_swab(&rp); + return bpos_cmp(lp, rp); +} +static void accounting_sort(darray_accounting_p *sorted, + struct bch_ioctl_query_accounting *in) +{ + for (struct bkey_i_accounting *a = in->accounting; + a < (struct bkey_i_accounting *) ((u64 *) in->accounting + in->accounting_u64s); + a = bkey_i_to_accounting(bkey_next(&a->k_i))) + if (darray_push(sorted, a)) + die("memory allocation failure"); + + sort(sorted->data, sorted->nr, sizeof(sorted->data[0]), accounting_p_cmp, NULL); +} + +static int fs_usage_v1_to_text(struct printbuf *out, + struct bchfs_handle fs, + dev_names dev_names) +{ + struct bch_ioctl_query_accounting *a = + bchu_fs_accounting(fs, + BIT(BCH_DISK_ACCOUNTING_persistent_reserved)| + BIT(BCH_DISK_ACCOUNTING_replicas)| + BIT(BCH_DISK_ACCOUNTING_compression)| + BIT(BCH_DISK_ACCOUNTING_btree)| + BIT(BCH_DISK_ACCOUNTING_rebalance_work)); + if (!a) + return -1; + + darray_accounting_p a_sorted = {}; + + accounting_sort(&a_sorted, a); + + prt_str(out, "Filesystem: "); + pr_uuid(out, fs.uuid.b); + prt_newline(out); + + printbuf_tabstops_reset(out); + printbuf_tabstop_push(out, 20); + printbuf_tabstop_push(out, 16); + + prt_str(out, "Size:"); + prt_tab(out); + prt_units_u64(out, a->capacity << 9); + prt_tab_rjust(out); + prt_newline(out); + + prt_str(out, "Used:"); + prt_tab(out); + prt_units_u64(out, a->used << 9); + prt_tab_rjust(out); + prt_newline(out); + + prt_str(out, "Online reserved:"); + prt_tab(out); + prt_units_u64(out, a->online_reserved << 9); + prt_tab_rjust(out); + prt_newline(out); + + prt_newline(out); + + printbuf_tabstops_reset(out); + + printbuf_tabstop_push(out, 16); + prt_str(out, "Data type"); + prt_tab(out); + + printbuf_tabstop_push(out, 16); + prt_str(out, "Required/total"); + prt_tab(out); + + printbuf_tabstop_push(out, 14); + prt_str(out, "Durability"); + prt_tab(out); + + printbuf_tabstop_push(out, 14); + prt_str(out, "Devices"); + prt_newline(out); + + printbuf_tabstop_push(out, 14); + + unsigned prev_type = 0; + + darray_for_each(a_sorted, i) { + struct bkey_i_accounting *a = *i; + + struct disk_accounting_pos acc_k; + bpos_to_disk_accounting_pos(&acc_k, a->k.p); + + bool new_type = acc_k.type != prev_type; + prev_type = acc_k.type; + + switch (acc_k.type) { + case BCH_DISK_ACCOUNTING_persistent_reserved: + persistent_reserved_to_text(out, + acc_k.persistent_reserved.nr_replicas, + a->v.d[0]); + break; + case BCH_DISK_ACCOUNTING_replicas: + replicas_usage_to_text(out, &acc_k.replicas, a->v.d[0], &dev_names); + break; + case BCH_DISK_ACCOUNTING_compression: + if (new_type) { + prt_printf(out, "\nCompression:\n"); + printbuf_tabstops_reset(out); + printbuf_tabstop_push(out, 12); + printbuf_tabstop_push(out, 16); + printbuf_tabstop_push(out, 16); + printbuf_tabstop_push(out, 24); + prt_printf(out, "type\tcompressed\runcompressed\raverage extent size\r\n"); + } + + u64 nr_extents = a->v.d[0]; + u64 sectors_uncompressed = a->v.d[1]; + u64 sectors_compressed = a->v.d[2]; + + bch2_prt_compression_type(out, acc_k.compression.type); + prt_tab(out); + + prt_human_readable_u64(out, sectors_compressed << 9); + prt_tab_rjust(out); + + prt_human_readable_u64(out, sectors_uncompressed << 9); + prt_tab_rjust(out); + + prt_human_readable_u64(out, nr_extents + ? div_u64(sectors_uncompressed << 9, nr_extents) + : 0); + prt_tab_rjust(out); + prt_newline(out); + break; + case BCH_DISK_ACCOUNTING_btree: + if (new_type) { + prt_printf(out, "\nBtree usage:\n"); + printbuf_tabstops_reset(out); + printbuf_tabstop_push(out, 12); + printbuf_tabstop_push(out, 16); + } + prt_printf(out, "%s:\t", bch2_btree_id_str(acc_k.btree.id)); + prt_units_u64(out, a->v.d[0] << 9); + prt_tab_rjust(out); + prt_newline(out); + break; + case BCH_DISK_ACCOUNTING_rebalance_work: + if (new_type) + prt_printf(out, "\nPending rebalance work:\n"); + prt_units_u64(out, a->v.d[0] << 9); + prt_newline(out); + break; + } + } + + darray_exit(&a_sorted); + free(a); + return 0; +} + +static void fs_usage_v0_to_text(struct printbuf *out, + struct bchfs_handle fs, + dev_names dev_names) +{ struct bch_ioctl_fs_usage *u = bchu_fs_usage(fs); prt_str(out, "Filesystem: "); @@ -261,42 +443,43 @@ static void fs_usage_to_text(struct printbuf *out, const char *path) printbuf_tabstop_push(out, 14); - for (i = 0; i < BCH_REPLICAS_MAX; i++) { - if (!u->persistent_reserved[i]) - continue; - - prt_str(out, "reserved:"); - prt_tab(out); - prt_printf(out, "%u/%u ", 1, i); - prt_tab(out); - prt_str(out, "[] "); - prt_units_u64(out, u->persistent_reserved[i] << 9); - prt_tab_rjust(out); - prt_newline(out); - } + for (unsigned i = 0; i < BCH_REPLICAS_MAX; i++) + persistent_reserved_to_text(out, i, u->persistent_reserved[i]); struct bch_replicas_usage *r; for_each_usage_replica(u, r) if (r->r.data_type < BCH_DATA_user) - replicas_usage_to_text(out, r, &dev_names); + replicas_usage_to_text(out, &r->r, r->sectors, &dev_names); for_each_usage_replica(u, r) if (r->r.data_type == BCH_DATA_user && r->r.nr_required <= 1) - replicas_usage_to_text(out, r, &dev_names); + replicas_usage_to_text(out, &r->r, r->sectors, &dev_names); for_each_usage_replica(u, r) if (r->r.data_type == BCH_DATA_user && r->r.nr_required > 1) - replicas_usage_to_text(out, r, &dev_names); + replicas_usage_to_text(out, &r->r, r->sectors, &dev_names); for_each_usage_replica(u, r) if (r->r.data_type > BCH_DATA_user) - replicas_usage_to_text(out, r, &dev_names); + replicas_usage_to_text(out, &r->r, r->sectors, &dev_names); free(u); +} +static void fs_usage_to_text(struct printbuf *out, const char *path) +{ + struct bchfs_handle fs = bcache_fs_open(path); + + dev_names dev_names = bchu_fs_get_devices(fs); + + if (!fs_usage_v1_to_text(out, fs, dev_names)) + goto devs; + + fs_usage_v0_to_text(out, fs, dev_names); +devs: devs_usage_to_text(out, fs, dev_names); darray_exit(&dev_names); diff --git a/c_src/libbcachefs.h b/c_src/libbcachefs.h index 05720573..5c7ef6c7 100644 --- a/c_src/libbcachefs.h +++ b/c_src/libbcachefs.h @@ -168,6 +168,35 @@ static inline struct bch_ioctl_fs_usage *bchu_fs_usage(struct bchfs_handle fs) } } +static inline struct bch_ioctl_query_accounting *bchu_fs_accounting(struct bchfs_handle fs, + unsigned typemask) +{ + unsigned accounting_u64s = 128; + struct bch_ioctl_query_accounting *ret = NULL; + + while (1) { + ret = xrealloc(ret, sizeof(*ret) + accounting_u64s * sizeof(u64)); + + memset(ret, 0, sizeof(*ret)); + + ret->accounting_u64s = accounting_u64s; + ret->accounting_types_mask = typemask; + + if (!ioctl(fs.ioctl_fd, BCH_IOCTL_QUERY_ACCOUNTING, ret)) + return ret; + + if (errno == ENOTTY) + return NULL; + + if (errno == ERANGE) { + accounting_u64s *= 2; + continue; + } + + die("BCH_IOCTL_USAGE error: %m"); + } +} + static inline struct bch_ioctl_dev_usage_v2 *bchu_dev_usage(struct bchfs_handle fs, unsigned idx) {