From 7d2baabdb15a564a89443614f5c6ad1205164943 Mon Sep 17 00:00:00 2001
From: Kent Overstreet <kent.overstreet@gmail.com>
Date: Wed, 18 Dec 2019 16:11:11 -0500
Subject: [PATCH] Redo cmd_fs_usage for new ioctls

---
 cmd_fs.c      | 135 ++++++++++++++++++++++++++------------------------
 libbcachefs.c |  36 ++++++++++++++
 libbcachefs.h |  35 ++++++++++---
 3 files changed, 134 insertions(+), 72 deletions(-)

diff --git a/cmd_fs.c b/cmd_fs.c
index 44316fc5..26fcdd03 100644
--- a/cmd_fs.c
+++ b/cmd_fs.c
@@ -14,123 +14,128 @@
 #include "cmds.h"
 #include "libbcachefs.h"
 
-static void print_dev_usage(struct bch_ioctl_dev_usage *d, unsigned idx,
-			    const char *label, enum units units)
+static void print_dev_usage(struct bchfs_handle fs,
+			    struct dev_name *d,
+			    enum units units)
 {
-	char *name = NULL;
-	u64 available = d->nr_buckets;
+	struct bch_ioctl_dev_usage u = bchu_dev_usage(fs, d->idx);
+	u64 available = u.nr_buckets;
 	unsigned i;
 
 	printf("\n");
-	printf_pad(20, "%s (device %u):", label, idx);
-
-	name = !d->dev ? strdup("(offline)")
-		: dev_to_path(d->dev)
-		?: strdup("(device not found)");
-	printf("%24s%12s\n", name, bch2_dev_state[d->state]);
-	free(name);
+	printf_pad(20, "%s (device %u):", d->label, d->idx);
+	printf("%24s%12s\n", d->dev ?: "(device not found)", bch2_dev_state[u.state]);
 
 	printf("%-20s%12s%12s%12s\n",
 	       "", "data", "buckets", "fragmented");
 
 	for (i = BCH_DATA_SB; i < BCH_DATA_NR; i++) {
-		u64 frag = max((s64) d->buckets[i] * d->bucket_size -
-			       (s64) d->sectors[i], 0LL);
+		u64 frag = max((s64) u.buckets[i] * u.bucket_size -
+			       (s64) u.sectors[i], 0LL);
 
 		printf_pad(20, "  %s:", bch2_data_types[i]);
 		printf("%12s%12llu%12s\n",
-		       pr_units(d->sectors[i], units),
-		       d->buckets[i],
+		       pr_units(u.sectors[i], units),
+		       u.buckets[i],
 		       pr_units(frag, units));
 
 		if (i != BCH_DATA_CACHED)
-			available -= d->buckets[i];
+			available -= u.buckets[i];
 	}
 
 	printf_pad(20, "  available:");
 	printf("%12s%12llu\n",
-	       pr_units(available * d->bucket_size, units),
+	       pr_units(available * u.bucket_size, units),
 	       available);
 
 	printf_pad(20, "  capacity:");
 	printf("%12s%12llu\n",
-	       pr_units(d->nr_buckets * d->bucket_size, units),
-	       d->nr_buckets);
+	       pr_units(u.nr_buckets * u.bucket_size, units),
+	       u.nr_buckets);
 }
 
-struct dev_by_label {
-	unsigned	idx;
-	char		*label;
-};
-
 static int dev_by_label_cmp(const void *_l, const void *_r)
 {
-	const struct dev_by_label *l = _l, *r = _r;
+	const struct dev_name *l = _l, *r = _r;
 
 	return strcmp(l->label, r->label);
 }
 
 static void print_fs_usage(const char *path, enum units units)
 {
-	unsigned i, j;
+	unsigned i;
 	char uuid[40];
 
 	struct bchfs_handle fs = bcache_fs_open(path);
-	struct bch_ioctl_usage *u = bchu_usage(fs);
+
+	dev_names dev_names = bchu_fs_get_devices(fs);
+
+	struct bch_ioctl_fs_usage *u = bchu_fs_usage(fs);
 
 	uuid_unparse(fs.uuid.b, uuid);
 	printf("Filesystem %s:\n", uuid);
 
-	printf("%-20s%12s\n", "Size:", pr_units(u->fs.capacity, units));
-	printf("%-20s%12s\n", "Used:", pr_units(u->fs.used, units));
+	printf("%-20s%12s\n", "Size:", pr_units(u->capacity, units));
+	printf("%-20s%12s\n", "Used:", pr_units(u->used, units));
 
-	printf("%-20s%12s%12s%12s%12s\n",
-	       "By replicas:", "1x", "2x", "3x", "4x");
+	printf("%-20s%12s\n", "Online reserved:", pr_units(u->online_reserved, units));
 
-	for (j = BCH_DATA_SB; j < BCH_DATA_NR; j++) {
-		printf_pad(20, "  %s:", bch2_data_types[j]);
-
-		for (i = 0; i < BCH_REPLICAS_MAX; i++)
-			printf("%12s", pr_units(u->fs.sectors[j][i], units));
-		printf("\n");
-	}
-
-	printf_pad(20, "  %s:", "reserved");
-	for (i = 0; i < BCH_REPLICAS_MAX; i++)
-		printf("%12s", pr_units(u->fs.persistent_reserved[i], units));
 	printf("\n");
+	printf("%-16s%-16s%s\n", "Data type", "Required/total", "Devices");
 
-	printf("%-20s%12s\n", "  online reserved:", pr_units(u->fs.online_reserved, units));
-
-	darray(struct dev_by_label) devs_by_label;
-	darray_init(devs_by_label);
-
-	for (i = 0; i < u->nr_devices; i++) {
-		struct bch_ioctl_dev_usage *d = u->devs + i;
-
-		if (!d->alive)
+	for (i = 0; i < BCH_REPLICAS_MAX; i++) {
+		if (!u->persistent_reserved[i])
 			continue;
 
-		char *label_attr = mprintf("dev-%u/label", i);
-		char *label = read_file_str(fs.sysfs_fd, label_attr);
-		free(label_attr);
-
-		darray_append(devs_by_label,
-			(struct dev_by_label) { i, label });
+		printf_pad(16, "%s: ", "reserved");
+		printf_pad(16, "%u/%u ", 1, i);
+		printf_pad(32, "[] ");
+		printf("%s\n", pr_units(u->persistent_reserved[i], units));
 	}
 
-	sort(&darray_item(devs_by_label, 0), darray_size(devs_by_label),
-	     sizeof(darray_item(devs_by_label, 0)), dev_by_label_cmp, NULL);
+	struct bch_replicas_usage *r;
 
-	struct dev_by_label *d;
-	darray_foreach(d, devs_by_label)
-		print_dev_usage(u->devs + d->idx, d->idx, d->label, units);
+	for (r = u->replicas;
+	     r != (void *) u->replicas + u->replica_entries_bytes;
+	     r = replicas_usage_next(r)) {
+		BUG_ON((void *) r > (void *) u->replicas + u->replica_entries_bytes);
 
-	darray_foreach(d, devs_by_label)
-		free(d->label);
-	darray_free(devs_by_label);
+		if (!r->sectors)
+			continue;
+
+		char devs[4096], *d = devs;
+		*d++ = '[';
+
+		for (i = 0; i < r->r.nr_devs; i++) {
+			if (i)
+				*d++ = ' ';
+			strcpy(d, dev_names.item[r->r.devs[i]].dev);
+			d += strlen(dev_names.item[r->r.devs[i]].dev);
+		}
+		*d++ = ']';
+		*d++ = '\0';
+
+		printf_pad(16, "%s: ", bch2_data_types[r->r.data_type]);
+		printf_pad(16, "%u/%u ", r->r.nr_required, r->r.nr_devs);
+		printf_pad(32, "%s ", devs);
+		printf(" %s\n", pr_units(r->sectors, units));
+	}
 
 	free(u);
+
+	sort(&darray_item(dev_names, 0), darray_size(dev_names),
+	     sizeof(darray_item(dev_names, 0)), dev_by_label_cmp, NULL);
+
+	struct dev_name *d;
+	darray_foreach(d, dev_names)
+		print_dev_usage(fs, d, units);
+
+	darray_foreach(d, dev_names) {
+		free(d->dev);
+		free(d->label);
+	}
+	darray_free(dev_names);
+
 	bcache_fs_close(fs);
 }
 
diff --git a/libbcachefs.c b/libbcachefs.c
index 7c9687d3..89c203a1 100644
--- a/libbcachefs.c
+++ b/libbcachefs.c
@@ -1,4 +1,5 @@
 #include <ctype.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdbool.h>
@@ -1103,3 +1104,38 @@ void bch2_opts_usage(unsigned opt_types)
 		}
 	}
 }
+
+dev_names bchu_fs_get_devices(struct bchfs_handle fs)
+{
+	DIR *dir = fdopendir(fs.sysfs_fd);
+	struct dirent *d;
+	dev_names devs;
+
+	darray_init(devs);
+
+	while ((errno = 0), (d = readdir(dir))) {
+		struct dev_name n;
+
+		if (sscanf(d->d_name, "dev-%u", &n.idx) != 1)
+			continue;
+
+		char *block_attr = mprintf("dev-%u/block", n.idx);
+
+		char sysfs_block_buf[4096];
+		if (readlinkat(fs.sysfs_fd, block_attr,
+			       sysfs_block_buf, sizeof(sysfs_block_buf)) > 0)
+			n.dev = strdup(basename(sysfs_block_buf));
+
+		free(block_attr);
+
+		char *label_attr = mprintf("dev-%u/label", n.idx);
+		n.label = read_file_str(fs.sysfs_fd, label_attr);
+		free(label_attr);
+
+		darray_append(devs, n);
+	}
+
+	closedir(dir);
+
+	return devs;
+}
diff --git a/libbcachefs.h b/libbcachefs.h
index 61d0ea8d..30add92c 100644
--- a/libbcachefs.h
+++ b/libbcachefs.h
@@ -140,24 +140,35 @@ static inline void bchu_disk_set_state(struct bchfs_handle fs, unsigned dev,
 	xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_SET_STATE, &i);
 }
 
-static inline struct bch_ioctl_usage *bchu_usage(struct bchfs_handle fs)
+static inline struct bch_ioctl_fs_usage *bchu_fs_usage(struct bchfs_handle fs)
 {
-	struct bch_ioctl_usage *u = NULL;
-	unsigned nr_devices = 4;
+	struct bch_ioctl_fs_usage *u = NULL;
+	size_t replica_entries_bytes = 4096;
 
 	while (1) {
-		u = xrealloc(u, sizeof(*u) + sizeof(u->devs[0]) * nr_devices);
-		u->nr_devices = nr_devices;
+		u = xrealloc(u, sizeof(*u) + replica_entries_bytes);
+		u->replica_entries_bytes = replica_entries_bytes;
 
-		if (!ioctl(fs.ioctl_fd, BCH_IOCTL_USAGE, u))
+		if (!ioctl(fs.ioctl_fd, BCH_IOCTL_FS_USAGE, u))
 			return u;
 
 		if (errno != ERANGE)
 			die("BCH_IOCTL_USAGE error: %m");
-		nr_devices *= 2;
+
+		replica_entries_bytes *= 2;
 	}
 }
 
+static inline struct bch_ioctl_dev_usage bchu_dev_usage(struct bchfs_handle fs,
+							unsigned idx)
+{
+	struct bch_ioctl_dev_usage i = { .dev = idx, .flags = BCH_BY_INDEX};
+
+	if (xioctl(fs.ioctl_fd, BCH_IOCTL_DEV_USAGE, &i))
+		die("BCH_IOCTL_DEV_USAGE error: %m");
+	return i;
+}
+
 static inline struct bch_sb *bchu_read_super(struct bchfs_handle fs, unsigned idx)
 {
 	size_t size = 4096;
@@ -205,4 +216,14 @@ static inline void bchu_disk_resize(struct bchfs_handle fs,
 
 int bchu_data(struct bchfs_handle, struct bch_ioctl_data);
 
+struct dev_name {
+	unsigned	idx;
+	char		*dev;
+	char		*label;
+	uuid_le		uuid;
+};
+typedef darray(struct dev_name) dev_names;
+
+dev_names bchu_fs_get_devices(struct bchfs_handle);
+
 #endif /* _LIBBCACHE_H */