aboutsummaryrefslogtreecommitdiff
path: root/fs/bcachefs
diff options
context:
space:
mode:
authorGravatar Kent Overstreet <kent.overstreet@gmail.com> 2022-02-20 05:00:45 -0500
committerGravatar Kent Overstreet <kent.overstreet@linux.dev> 2023-10-22 17:09:24 -0400
commit12bf93a429c981cf337ce2c27504ec0171157f76 (patch)
tree1b547aa4162109e412bfe8e6562e50ccc391ef90 /fs/bcachefs
parentbcachefs: Kill bch_scnmemcpy() (diff)
downloadlinux-12bf93a429c981cf337ce2c27504ec0171157f76.tar.gz
linux-12bf93a429c981cf337ce2c27504ec0171157f76.tar.bz2
linux-12bf93a429c981cf337ce2c27504ec0171157f76.zip
bcachefs: Add .to_text() methods for all superblock sections
This patch improves the superblock .to_text() methods and adds methods for all types that were missing them. It also improves printbufs by allowing them to specfiy what units we want to be printing in, and adds new wrapper methods for unifying our kernel and userspace environments. Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Diffstat (limited to 'fs/bcachefs')
-rw-r--r--fs/bcachefs/disk_groups.c40
-rw-r--r--fs/bcachefs/disk_groups.h5
-rw-r--r--fs/bcachefs/extents.c18
-rw-r--r--fs/bcachefs/journal_io.c2
-rw-r--r--fs/bcachefs/journal_seq_blacklist.c1
-rw-r--r--fs/bcachefs/quota.c43
-rw-r--r--fs/bcachefs/recovery.c2
-rw-r--r--fs/bcachefs/replicas.c85
-rw-r--r--fs/bcachefs/replicas.h1
-rw-r--r--fs/bcachefs/super-io.c364
-rw-r--r--fs/bcachefs/super-io.h4
-rw-r--r--fs/bcachefs/sysfs.c2
-rw-r--r--fs/bcachefs/util.c21
-rw-r--r--fs/bcachefs/util.h64
14 files changed, 588 insertions, 64 deletions
diff --git a/fs/bcachefs/disk_groups.c b/fs/bcachefs/disk_groups.c
index e411606fd38d..e9ee37f1e07d 100644
--- a/fs/bcachefs/disk_groups.c
+++ b/fs/bcachefs/disk_groups.c
@@ -343,12 +343,10 @@ int bch2_disk_path_find_or_create(struct bch_sb_handle *sb, const char *name)
return v;
}
-void bch2_disk_path_to_text(struct printbuf *out,
- struct bch_sb_handle *sb,
- unsigned v)
+void bch2_disk_path_to_text(struct printbuf *out, struct bch_sb *sb, unsigned v)
{
struct bch_sb_field_disk_groups *groups =
- bch2_sb_get_disk_groups(sb->sb);
+ bch2_sb_get_disk_groups(sb);
struct bch_disk_group *g;
unsigned nr = 0;
u16 path[32];
@@ -383,7 +381,7 @@ void bch2_disk_path_to_text(struct printbuf *out,
}
return;
inval:
- pr_buf(out, "invalid group %u", v);
+ pr_buf(out, "invalid label %u", v);
}
int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
@@ -447,6 +445,36 @@ int bch2_opt_target_parse(struct bch_fs *c, const char *buf, u64 *v)
return -EINVAL;
}
+void bch2_sb_target_to_text(struct printbuf *out, struct bch_sb *sb, u64 v)
+{
+ struct target t = target_decode(v);
+
+ switch (t.type) {
+ case TARGET_NULL:
+ pr_buf(out, "none");
+ break;
+ case TARGET_DEV: {
+ struct bch_sb_field_members *mi = bch2_sb_get_members(sb);
+ struct bch_member *m = mi->members + t.dev;
+
+ if (bch2_dev_exists(sb, mi, t.dev)) {
+ pr_buf(out, "Device ");
+ pr_uuid(out, m->uuid.b);
+ pr_buf(out, " (%u)", t.dev);
+ } else {
+ pr_buf(out, "Bad device %u", t.dev);
+ }
+
+ break;
+ }
+ case TARGET_GROUP:
+ bch2_disk_path_to_text(out, sb, t.group);
+ break;
+ default:
+ BUG();
+ }
+}
+
void bch2_opt_target_to_text(struct printbuf *out, struct bch_fs *c, u64 v)
{
struct target t = target_decode(v);
@@ -477,7 +505,7 @@ void bch2_opt_target_to_text(struct printbuf *out, struct bch_fs *c, u64 v)
}
case TARGET_GROUP:
mutex_lock(&c->sb_lock);
- bch2_disk_path_to_text(out, &c->disk_sb, t.group);
+ bch2_disk_path_to_text(out, c->disk_sb.sb, t.group);
mutex_unlock(&c->sb_lock);
break;
default:
diff --git a/fs/bcachefs/disk_groups.h b/fs/bcachefs/disk_groups.h
index 3d84f23c34ed..a274aacbdf92 100644
--- a/fs/bcachefs/disk_groups.h
+++ b/fs/bcachefs/disk_groups.h
@@ -75,8 +75,9 @@ int bch2_disk_path_find(struct bch_sb_handle *, const char *);
/* Exported for userspace bcachefs-tools: */
int bch2_disk_path_find_or_create(struct bch_sb_handle *, const char *);
-void bch2_disk_path_to_text(struct printbuf *, struct bch_sb_handle *,
- unsigned);
+void bch2_disk_path_to_text(struct printbuf *, struct bch_sb *, unsigned);
+
+void bch2_sb_target_to_text(struct printbuf *, struct bch_sb *, u64);
int bch2_opt_target_parse(struct bch_fs *, const char *, u64 *);
void bch2_opt_target_to_text(struct printbuf *, struct bch_fs *, u64);
diff --git a/fs/bcachefs/extents.c b/fs/bcachefs/extents.c
index 3ed724e1fc98..c78e10e8ec2c 100644
--- a/fs/bcachefs/extents.c
+++ b/fs/bcachefs/extents.c
@@ -953,15 +953,19 @@ void bch2_bkey_ptrs_to_text(struct printbuf *out, struct bch_fs *c,
switch (__extent_entry_type(entry)) {
case BCH_EXTENT_ENTRY_ptr:
ptr = entry_to_ptr(entry);
- ca = ptr->dev < c->sb.nr_devices && c->devs[ptr->dev]
- ? bch_dev_bkey_exists(c, ptr->dev)
- : NULL;
- pr_buf(out, "ptr: %u:%llu gen %u%s%s", ptr->dev,
+ pr_buf(out, "ptr: %u:%llu gen %u%s", ptr->dev,
(u64) ptr->offset, ptr->gen,
- ptr->cached ? " cached" : "",
- ca && ptr_stale(ca, ptr)
- ? " stale" : "");
+ ptr->cached ? " cached" : "");
+
+ if (c) {
+ ca = ptr->dev < c->sb.nr_devices && c->devs[ptr->dev]
+ ? bch_dev_bkey_exists(c, ptr->dev)
+ : NULL;
+
+ if (ca && ptr_stale(ca, ptr))
+ pr_buf(out, " stale");
+ }
break;
case BCH_EXTENT_ENTRY_crc32:
case BCH_EXTENT_ENTRY_crc64:
diff --git a/fs/bcachefs/journal_io.c b/fs/bcachefs/journal_io.c
index 4f0904a515a7..491300e3c48f 100644
--- a/fs/bcachefs/journal_io.c
+++ b/fs/bcachefs/journal_io.c
@@ -302,7 +302,7 @@ static void journal_entry_btree_keys_to_text(struct printbuf *out, struct bch_fs
vstruct_for_each(entry, k) {
if (!first) {
- printbuf_newline(out);
+ pr_newline(out);
pr_buf(out, "%s: ", bch2_jset_entry_types[entry->type]);
}
pr_buf(out, "btree=%s l=%u ", bch2_btree_ids[entry->btree_id], entry->level);
diff --git a/fs/bcachefs/journal_seq_blacklist.c b/fs/bcachefs/journal_seq_blacklist.c
index 3cc63fc202ab..3140c8731431 100644
--- a/fs/bcachefs/journal_seq_blacklist.c
+++ b/fs/bcachefs/journal_seq_blacklist.c
@@ -235,6 +235,7 @@ static void bch2_sb_journal_seq_blacklist_to_text(struct printbuf *out,
le64_to_cpu(i->start),
le64_to_cpu(i->end));
}
+ pr_newline(out);
}
const struct bch_sb_field_ops bch_sb_field_ops_journal_seq_blacklist = {
diff --git a/fs/bcachefs/quota.c b/fs/bcachefs/quota.c
index 6fb8224f565e..b7ef8fa7bbc9 100644
--- a/fs/bcachefs/quota.c
+++ b/fs/bcachefs/quota.c
@@ -6,7 +6,18 @@
#include "subvolume.h"
#include "super-io.h"
-static int bch2_sb_validate_quota(struct bch_sb *sb, struct bch_sb_field *f,
+static const char * const bch2_quota_types[] = {
+ "user",
+ "group",
+ "project",
+};
+
+static const char * const bch2_quota_counters[] = {
+ "space",
+ "inodes",
+};
+
+static int bch2_sb_quota_validate(struct bch_sb *sb, struct bch_sb_field *f,
struct printbuf *err)
{
struct bch_sb_field_quota *q = field_to_type(f, quota);
@@ -14,13 +25,36 @@ static int bch2_sb_validate_quota(struct bch_sb *sb, struct bch_sb_field *f,
if (vstruct_bytes(&q->field) < sizeof(*q)) {
pr_buf(err, "wrong size (got %llu should be %zu)",
vstruct_bytes(&q->field), sizeof(*q));
+ return -EINVAL;
}
return 0;
}
+static void bch2_sb_quota_to_text(struct printbuf *out, struct bch_sb *sb,
+ struct bch_sb_field *f)
+{
+ struct bch_sb_field_quota *q = field_to_type(f, quota);
+ unsigned qtyp, counter;
+
+ for (qtyp = 0; qtyp < ARRAY_SIZE(q->q); qtyp++) {
+ pr_buf(out, "%s: flags %llx",
+ bch2_quota_types[qtyp],
+ le64_to_cpu(q->q[qtyp].flags));
+
+ for (counter = 0; counter < Q_COUNTERS; counter++)
+ pr_buf(out, " %s timelimit %u warnlimit %u",
+ bch2_quota_counters[counter],
+ le32_to_cpu(q->q[qtyp].c[counter].timelimit),
+ le32_to_cpu(q->q[qtyp].c[counter].warnlimit));
+
+ pr_newline(out);
+ }
+}
+
const struct bch_sb_field_ops bch_sb_field_ops_quota = {
- .validate = bch2_sb_validate_quota,
+ .validate = bch2_sb_quota_validate,
+ .to_text = bch2_sb_quota_to_text,
};
const char *bch2_quota_invalid(const struct bch_fs *c, struct bkey_s_c k)
@@ -34,11 +68,6 @@ const char *bch2_quota_invalid(const struct bch_fs *c, struct bkey_s_c k)
return NULL;
}
-static const char * const bch2_quota_counters[] = {
- "space",
- "inodes",
-};
-
void bch2_quota_to_text(struct printbuf *out, struct bch_fs *c,
struct bkey_s_c k)
{
diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c
index 7def5938e24d..d33b9e2bb1e3 100644
--- a/fs/bcachefs/recovery.c
+++ b/fs/bcachefs/recovery.c
@@ -821,7 +821,7 @@ static struct bch_sb_field_clean *read_superblock_clean(struct bch_fs *c)
return ERR_PTR(-ENOMEM);
}
- ret = bch2_sb_clean_validate(c, clean, READ);
+ ret = bch2_sb_clean_validate_late(c, clean, READ);
if (ret) {
mutex_unlock(&c->sb_lock);
return ERR_PTR(ret);
diff --git a/fs/bcachefs/replicas.c b/fs/bcachefs/replicas.c
index c192e31d5d68..7cc2414893fc 100644
--- a/fs/bcachefs/replicas.c
+++ b/fs/bcachefs/replicas.c
@@ -36,6 +36,22 @@ static void bch2_cpu_replicas_sort(struct bch_replicas_cpu *r)
eytzinger0_sort(r->entries, r->nr, r->entry_size, memcmp, NULL);
}
+void bch2_replicas_entry_v0_to_text(struct printbuf *out,
+ struct bch_replicas_entry_v0 *e)
+{
+ unsigned i;
+
+ if (e->data_type < BCH_DATA_NR)
+ pr_buf(out, "%s", bch2_data_types[e->data_type]);
+ else
+ pr_buf(out, "(invalid data type %u)", e->data_type);
+
+ pr_buf(out, ": %u [", e->nr_devs);
+ for (i = 0; i < e->nr_devs; i++)
+ pr_buf(out, i ? " %u" : "%u", e->devs[i]);
+ pr_buf(out, "]");
+}
+
void bch2_replicas_entry_to_text(struct printbuf *out,
struct bch_replicas_entry *e)
{
@@ -867,7 +883,7 @@ static int bch2_cpu_replicas_validate(struct bch_replicas_cpu *cpu_r,
return 0;
}
-static int bch2_sb_validate_replicas(struct bch_sb *sb, struct bch_sb_field *f,
+static int bch2_sb_replicas_validate(struct bch_sb *sb, struct bch_sb_field *f,
struct printbuf *err)
{
struct bch_sb_field_replicas *sb_r = field_to_type(f, replicas);
@@ -897,14 +913,15 @@ static void bch2_sb_replicas_to_text(struct printbuf *out,
bch2_replicas_entry_to_text(out, e);
}
+ pr_newline(out);
}
const struct bch_sb_field_ops bch_sb_field_ops_replicas = {
- .validate = bch2_sb_validate_replicas,
+ .validate = bch2_sb_replicas_validate,
.to_text = bch2_sb_replicas_to_text,
};
-static int bch2_sb_validate_replicas_v0(struct bch_sb *sb, struct bch_sb_field *f,
+static int bch2_sb_replicas_v0_validate(struct bch_sb *sb, struct bch_sb_field *f,
struct printbuf *err)
{
struct bch_sb_field_replicas_v0 *sb_r = field_to_type(f, replicas_v0);
@@ -919,8 +936,27 @@ static int bch2_sb_validate_replicas_v0(struct bch_sb *sb, struct bch_sb_field *
return ret;
}
+static void bch2_sb_replicas_v0_to_text(struct printbuf *out,
+ struct bch_sb *sb,
+ struct bch_sb_field *f)
+{
+ struct bch_sb_field_replicas_v0 *sb_r = field_to_type(f, replicas_v0);
+ struct bch_replicas_entry_v0 *e;
+ bool first = true;
+
+ for_each_replicas_entry(sb_r, e) {
+ if (!first)
+ pr_buf(out, " ");
+ first = false;
+
+ bch2_replicas_entry_v0_to_text(out, e);
+ }
+ pr_newline(out);
+}
+
const struct bch_sb_field_ops bch_sb_field_ops_replicas_v0 = {
- .validate = bch2_sb_validate_replicas_v0,
+ .validate = bch2_sb_replicas_v0_validate,
+ .to_text = bch2_sb_replicas_v0_to_text,
};
/* Query replicas: */
@@ -977,19 +1013,42 @@ bool bch2_have_enough_devs(struct bch_fs *c, struct bch_devs_mask devs,
return ret;
}
-unsigned bch2_dev_has_data(struct bch_fs *c, struct bch_dev *ca)
+unsigned bch2_sb_dev_has_data(struct bch_sb *sb, unsigned dev)
{
- struct bch_replicas_entry *e;
- unsigned i, ret = 0;
+ struct bch_sb_field_replicas *replicas;
+ struct bch_sb_field_replicas_v0 *replicas_v0;
+ unsigned i, data_has = 0;
+
+ replicas = bch2_sb_get_replicas(sb);
+ replicas_v0 = bch2_sb_get_replicas_v0(sb);
+
+ if (replicas) {
+ struct bch_replicas_entry *r;
+
+ for_each_replicas_entry(replicas, r)
+ for (i = 0; i < r->nr_devs; i++)
+ if (r->devs[i] == dev)
+ data_has |= 1 << r->data_type;
+ } else if (replicas_v0) {
+ struct bch_replicas_entry_v0 *r;
+
+ for_each_replicas_entry_v0(replicas_v0, r)
+ for (i = 0; i < r->nr_devs; i++)
+ if (r->devs[i] == dev)
+ data_has |= 1 << r->data_type;
+ }
- percpu_down_read(&c->mark_lock);
- for_each_cpu_replicas_entry(&c->replicas, e)
- for (i = 0; i < e->nr_devs; i++)
- if (e->devs[i] == ca->dev_idx)
- ret |= 1 << e->data_type;
+ return data_has;
+}
- percpu_up_read(&c->mark_lock);
+unsigned bch2_dev_has_data(struct bch_fs *c, struct bch_dev *ca)
+{
+ unsigned ret;
+
+ mutex_lock(&c->sb_lock);
+ ret = bch2_sb_dev_has_data(c->disk_sb.sb, ca->dev_idx);
+ mutex_unlock(&c->sb_lock);
return ret;
}
diff --git a/fs/bcachefs/replicas.h b/fs/bcachefs/replicas.h
index d237d7c51ccb..87820b2e1ad3 100644
--- a/fs/bcachefs/replicas.h
+++ b/fs/bcachefs/replicas.h
@@ -64,6 +64,7 @@ static inline void bch2_replicas_entry_cached(struct bch_replicas_entry *e,
bool bch2_have_enough_devs(struct bch_fs *, struct bch_devs_mask,
unsigned, bool);
+unsigned bch2_sb_dev_has_data(struct bch_sb *, unsigned);
unsigned bch2_dev_has_data(struct bch_fs *, struct bch_dev *);
int bch2_replicas_gc_end(struct bch_fs *, int);
diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c
index f89e883ff2e2..e1ff14eedaea 100644
--- a/fs/bcachefs/super-io.c
+++ b/fs/bcachefs/super-io.c
@@ -920,7 +920,7 @@ static int u64_cmp(const void *_l, const void *_r)
return l < r ? -1 : l > r ? 1 : 0;
}
-static int bch2_sb_validate_journal(struct bch_sb *sb,
+static int bch2_sb_journal_validate(struct bch_sb *sb,
struct bch_sb_field *f,
struct printbuf *err)
{
@@ -973,13 +973,26 @@ err:
return ret;
}
+static void bch2_sb_journal_to_text(struct printbuf *out, struct bch_sb *sb,
+ struct bch_sb_field *f)
+{
+ struct bch_sb_field_journal *journal = field_to_type(f, journal);
+ unsigned i, nr = bch2_nr_journal_buckets(journal);
+
+ pr_buf(out, "Buckets: ");
+ for (i = 0; i < nr; i++)
+ pr_buf(out, " %llu", le64_to_cpu(journal->buckets[i]));
+ pr_newline(out);
+}
+
static const struct bch_sb_field_ops bch_sb_field_ops_journal = {
- .validate = bch2_sb_validate_journal,
+ .validate = bch2_sb_journal_validate,
+ .to_text = bch2_sb_journal_to_text,
};
/* BCH_SB_FIELD_members: */
-static int bch2_sb_validate_members(struct bch_sb *sb,
+static int bch2_sb_members_validate(struct bch_sb *sb,
struct bch_sb_field *f,
struct printbuf *err)
{
@@ -1029,13 +1042,105 @@ static int bch2_sb_validate_members(struct bch_sb *sb,
return 0;
}
+static void bch2_sb_members_to_text(struct printbuf *out, struct bch_sb *sb,
+ struct bch_sb_field *f)
+{
+ struct bch_sb_field_members *mi = field_to_type(f, members);
+ struct bch_sb_field_disk_groups *gi = bch2_sb_get_disk_groups(sb);
+ unsigned i;
+
+ for (i = 0; i < sb->nr_devices; i++) {
+ struct bch_member *m = mi->members + i;
+ unsigned data_have = bch2_sb_dev_has_data(sb, i);
+ u64 bucket_size = le16_to_cpu(m->bucket_size);
+ u64 device_size = le64_to_cpu(m->nbuckets) * bucket_size;
+
+ if (!bch2_member_exists(m))
+ continue;
+
+ pr_buf(out, "Device: %u", i);
+ pr_newline(out);
+
+ printbuf_indent_push(out, 2);
+
+ pr_buf(out, "UUID: ");
+ pr_uuid(out, m->uuid.b);
+ pr_newline(out);
+
+ pr_buf(out, "Size: ");
+ pr_units(out, device_size, device_size << 9);
+ pr_newline(out);
+
+ pr_buf(out, "Bucket size: ");
+ pr_units(out, bucket_size, bucket_size << 9);
+ pr_newline(out);
+
+ pr_buf(out, "First bucket: %u",
+ le16_to_cpu(m->first_bucket));
+ pr_newline(out);
+
+ pr_buf(out, "Buckets: %llu",
+ le64_to_cpu(m->nbuckets));
+ pr_newline(out);
+
+ pr_buf(out, "Last mount: ");
+ if (m->last_mount)
+ pr_time(out, le64_to_cpu(m->last_mount));
+ else
+ pr_buf(out, "(never)");
+ pr_newline(out);
+
+ pr_buf(out, "State: %s",
+ BCH_MEMBER_STATE(m) < BCH_MEMBER_STATE_NR
+ ? bch2_member_states[BCH_MEMBER_STATE(m)]
+ : "unknown");
+ pr_newline(out);
+
+ pr_buf(out, "Group: ");
+ if (BCH_MEMBER_GROUP(m)) {
+ unsigned idx = BCH_MEMBER_GROUP(m) - 1;
+
+ if (idx < disk_groups_nr(gi))
+ pr_buf(out, "%s (%u)",
+ gi->entries[idx].label, idx);
+ else
+ pr_buf(out, "(bad disk labels section)");
+ } else {
+ pr_buf(out, "(none)");
+ }
+ pr_newline(out);
+
+ pr_buf(out, "Data allowed: ");
+ if (BCH_MEMBER_DATA_ALLOWED(m))
+ bch2_flags_to_text(out, bch2_data_types,
+ BCH_MEMBER_DATA_ALLOWED(m));
+ else
+ pr_buf(out, "(none)");
+ pr_newline(out);
+
+ pr_buf(out, "Has data: ");
+ if (data_have)
+ bch2_flags_to_text(out, bch2_data_types, data_have);
+ else
+ pr_buf(out, "(none)");
+ pr_newline(out);
+
+ pr_buf(out, "Discard: %llu",
+ BCH_MEMBER_DISCARD(m));
+ pr_newline(out);
+
+ printbuf_indent_pop(out, 2);
+ }
+}
+
static const struct bch_sb_field_ops bch_sb_field_ops_members = {
- .validate = bch2_sb_validate_members,
+ .validate = bch2_sb_members_validate,
+ .to_text = bch2_sb_members_to_text,
};
/* BCH_SB_FIELD_crypt: */
-static int bch2_sb_validate_crypt(struct bch_sb *sb,
+static int bch2_sb_crypt_validate(struct bch_sb *sb,
struct bch_sb_field *f,
struct printbuf *err)
{
@@ -1055,13 +1160,29 @@ static int bch2_sb_validate_crypt(struct bch_sb *sb,
return 0;
}
+static void bch2_sb_crypt_to_text(struct printbuf *out, struct bch_sb *sb,
+ struct bch_sb_field *f)
+{
+ struct bch_sb_field_crypt *crypt = field_to_type(f, crypt);
+
+ pr_buf(out, "KFD: %llu", BCH_CRYPT_KDF_TYPE(crypt));
+ pr_newline(out);
+ pr_buf(out, "scrypt n: %llu", BCH_KDF_SCRYPT_N(crypt));
+ pr_newline(out);
+ pr_buf(out, "scrypt r: %llu", BCH_KDF_SCRYPT_R(crypt));
+ pr_newline(out);
+ pr_buf(out, "scrypt p: %llu", BCH_KDF_SCRYPT_P(crypt));
+ pr_newline(out);
+}
+
static const struct bch_sb_field_ops bch_sb_field_ops_crypt = {
- .validate = bch2_sb_validate_crypt,
+ .validate = bch2_sb_crypt_validate,
+ .to_text = bch2_sb_crypt_to_text,
};
/* BCH_SB_FIELD_clean: */
-int bch2_sb_clean_validate(struct bch_fs *c, struct bch_sb_field_clean *clean, int write)
+int bch2_sb_clean_validate_late(struct bch_fs *c, struct bch_sb_field_clean *clean, int write)
{
struct jset_entry *entry;
int ret;
@@ -1251,7 +1372,7 @@ void bch2_fs_mark_clean(struct bch_fs *c)
* this should be in the write path, and we should be validating every
* superblock section:
*/
- ret = bch2_sb_clean_validate(c, sb_clean, WRITE);
+ ret = bch2_sb_clean_validate_late(c, sb_clean, WRITE);
if (ret) {
bch_err(c, "error writing marking filesystem clean: validate error");
goto out;
@@ -1262,7 +1383,7 @@ out:
mutex_unlock(&c->sb_lock);
}
-static int bch2_sb_validate_clean(struct bch_sb *sb,
+static int bch2_sb_clean_validate(struct bch_sb *sb,
struct bch_sb_field *f,
struct printbuf *err)
{
@@ -1277,8 +1398,32 @@ static int bch2_sb_validate_clean(struct bch_sb *sb,
return 0;
}
+static void bch2_sb_clean_to_text(struct printbuf *out, struct bch_sb *sb,
+ struct bch_sb_field *f)
+{
+ struct bch_sb_field_clean *clean = field_to_type(f, clean);
+ struct jset_entry *entry;
+
+ pr_buf(out, "flags: %x", le32_to_cpu(clean->flags));
+ pr_newline(out);
+ pr_buf(out, "journal_seq: %llu", le64_to_cpu(clean->journal_seq));
+ pr_newline(out);
+
+ for (entry = clean->start;
+ entry != vstruct_end(&clean->field);
+ entry = vstruct_next(entry)) {
+ if (entry->type == BCH_JSET_ENTRY_btree_keys &&
+ !entry->u64s)
+ continue;
+
+ bch2_journal_entry_to_text(out, NULL, entry);
+ pr_newline(out);
+ }
+}
+
static const struct bch_sb_field_ops bch_sb_field_ops_clean = {
- .validate = bch2_sb_validate_clean,
+ .validate = bch2_sb_clean_validate,
+ .to_text = bch2_sb_clean_to_text,
};
static const struct bch_sb_field_ops *bch2_sb_field_ops[] = {
@@ -1302,7 +1447,7 @@ static int bch2_sb_field_validate(struct bch_sb *sb, struct bch_sb_field *f,
ret = bch2_sb_field_ops[type]->validate(sb, f, &err);
if (ret) {
- pr_buf(&err, "\n");
+ pr_newline(&err);
bch2_sb_field_to_text(&err, sb, f);
*orig_err = err;
}
@@ -1323,7 +1468,202 @@ void bch2_sb_field_to_text(struct printbuf *out, struct bch_sb *sb,
pr_buf(out, "(unknown field %u)", type);
pr_buf(out, " (size %llu):", vstruct_bytes(f));
+ pr_newline(out);
- if (ops && ops->to_text)
+ if (ops && ops->to_text) {
+ printbuf_indent_push(out, 2);
bch2_sb_field_ops[type]->to_text(out, sb, f);
+ printbuf_indent_pop(out, 2);
+ }
+}
+
+void bch2_sb_layout_to_text(struct printbuf *out, struct bch_sb_layout *l)
+{
+ unsigned i;
+
+ pr_buf(out, "Type: %u", l->layout_type);
+ pr_newline(out);
+
+ pr_buf(out, "Superblock max size: ");
+ pr_units(out,
+ 1 << l->sb_max_size_bits,
+ 512 << l->sb_max_size_bits);
+ pr_newline(out);
+
+ pr_buf(out, "Nr superblocks: %u", l->nr_superblocks);
+ pr_newline(out);
+
+ pr_buf(out, "Offsets: ");
+ for (i = 0; i < l->nr_superblocks; i++) {
+ if (i)
+ pr_buf(out, ", ");
+ pr_buf(out, "%llu", le64_to_cpu(l->sb_offset[i]));
+ }
+ pr_newline(out);
+}
+
+void bch2_sb_to_text(struct printbuf *out, struct bch_sb *sb,
+ bool print_layout, unsigned fields)
+{
+ struct bch_sb_field_members *mi;
+ struct bch_sb_field *f;
+ u64 fields_have = 0;
+ unsigned nr_devices = 0;
+
+ mi = bch2_sb_get_members(sb);
+ if (mi) {
+ struct bch_member *m;
+
+ for (m = mi->members;
+ m < mi->members + sb->nr_devices;
+ m++)
+ nr_devices += bch2_member_exists(m);
+ }
+
+ pr_buf(out, "External UUID: ");
+ pr_uuid(out, sb->user_uuid.b);
+ pr_newline(out);
+
+ pr_buf(out, "Internal UUID: ");
+ pr_uuid(out, sb->uuid.b);
+ pr_newline(out);
+
+ pr_buf(out, "Device index: %u", sb->dev_idx);
+ pr_newline(out);
+
+ pr_buf(out, "Label: ");
+ pr_buf(out, "%.*s", (int) sizeof(sb->label), sb->label);
+ pr_newline(out);
+
+ pr_buf(out, "Version: %u", le16_to_cpu(sb->version));
+ pr_newline(out);
+
+ pr_buf(out, "Oldest version on disk: %u", le16_to_cpu(sb->version_min));
+ pr_newline(out);
+
+ pr_buf(out, "Created: ");
+ if (sb->time_base_lo)
+ pr_time(out, le64_to_cpu(sb->time_base_lo) / NSEC_PER_SEC);
+ else
+ pr_buf(out, "(not set)");
+ pr_newline(out);
+
+ pr_buf(out, "Squence number: %llu", le64_to_cpu(sb->seq));
+ pr_newline(out);
+
+ pr_buf(out, "Block_size: ");
+ pr_units(out, le16_to_cpu(sb->block_size),
+ (u32) le16_to_cpu(sb->block_size) << 9);
+ pr_newline(out);
+
+ pr_buf(out, "Btree node size: ");
+ pr_units(out, BCH_SB_BTREE_NODE_SIZE(sb),
+ BCH_SB_BTREE_NODE_SIZE(sb) << 9);
+ pr_newline(out);
+
+ pr_buf(out, "Error action: %s",
+ BCH_SB_ERROR_ACTION(sb) < BCH_ON_ERROR_NR
+ ? bch2_error_actions[BCH_SB_ERROR_ACTION(sb)]
+ : "unknown");
+ pr_newline(out);
+
+ pr_buf(out, "Clean: %llu", BCH_SB_CLEAN(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Features: ");
+ bch2_flags_to_text(out, bch2_sb_features,
+ le64_to_cpu(sb->features[0]));
+ pr_newline(out);
+
+ pr_buf(out, "Compat features: ");
+ bch2_flags_to_text(out, bch2_sb_compat,
+ le64_to_cpu(sb->compat[0]));
+ pr_newline(out);
+
+ pr_buf(out, "Metadata replicas: %llu", BCH_SB_META_REPLICAS_WANT(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Data replicas: %llu", BCH_SB_DATA_REPLICAS_WANT(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Metadata checksum type: %s (%llu)",
+ BCH_SB_META_CSUM_TYPE(sb) < BCH_CSUM_OPT_NR
+ ? bch2_csum_opts[BCH_SB_META_CSUM_TYPE(sb)]
+ : "unknown",
+ BCH_SB_META_CSUM_TYPE(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Data checksum type: %s (%llu)",
+ BCH_SB_DATA_CSUM_TYPE(sb) < BCH_CSUM_OPT_NR
+ ? bch2_csum_opts[BCH_SB_DATA_CSUM_TYPE(sb)]
+ : "unknown",
+ BCH_SB_DATA_CSUM_TYPE(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Compression type: %s (%llu)",
+ BCH_SB_COMPRESSION_TYPE(sb) < BCH_COMPRESSION_OPT_NR
+ ? bch2_compression_opts[BCH_SB_COMPRESSION_TYPE(sb)]
+ : "unknown",
+ BCH_SB_COMPRESSION_TYPE(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Foreground write target: ");
+ bch2_sb_target_to_text(out, sb, BCH_SB_FOREGROUND_TARGET(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Background write target: ");
+ bch2_sb_target_to_text(out, sb, BCH_SB_BACKGROUND_TARGET(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Promote target: ");
+ bch2_sb_target_to_text(out, sb, BCH_SB_PROMOTE_TARGET(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Metadata target: ");
+ bch2_sb_target_to_text(out, sb, BCH_SB_METADATA_TARGET(sb));
+ pr_newline(out);
+
+ pr_buf(out, "String hash type: %s (%llu)",
+ BCH_SB_STR_HASH_TYPE(sb) < BCH_STR_HASH_NR
+ ? bch2_str_hash_types[BCH_SB_STR_HASH_TYPE(sb)]
+ : "unknown",
+ BCH_SB_STR_HASH_TYPE(sb));
+ pr_newline(out);
+
+ pr_buf(out, "32 bit inodes: %llu", BCH_SB_INODE_32BIT(sb));
+ pr_newline(out);
+
+ pr_buf(out, "GC reserve percentage: %llu%%", BCH_SB_GC_RESERVE(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Root reserve percentage: %llu%%", BCH_SB_ROOT_RESERVE(sb));
+ pr_newline(out);
+
+ pr_buf(out, "Devices: %u live, %u total",
+ nr_devices, sb->nr_devices);
+ pr_newline(out);
+
+ pr_buf(out, "Sections: ");
+ vstruct_for_each(sb, f)
+ fields_have |= 1 << le32_to_cpu(f->type);
+ bch2_flags_to_text(out, bch2_sb_fields, fields_have);
+ pr_newline(out);
+
+ pr_buf(out, "Superblock size: %llu", vstruct_bytes(sb));
+ pr_newline(out);
+
+ if (print_layout) {
+ pr_newline(out);
+ pr_buf(out, "layout:");
+ pr_newline(out);
+ printbuf_indent_push(out, 2);
+ bch2_sb_layout_to_text(out, &sb->layout);
+ printbuf_indent_pop(out, 2);
+ }
+
+ vstruct_for_each(sb, f)
+ if (fields & (1 << le32_to_cpu(f->type))) {
+ pr_newline(out);
+ bch2_sb_field_to_text(out, sb, f);
+ }
}
diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h
index 6170fa0990f1..ccd6fe7fdf29 100644
--- a/fs/bcachefs/super-io.h
+++ b/fs/bcachefs/super-io.h
@@ -121,12 +121,14 @@ static inline struct bch_member_cpu bch2_mi_to_cpu(struct bch_member *mi)
void bch2_journal_super_entries_add_common(struct bch_fs *,
struct jset_entry **, u64);
-int bch2_sb_clean_validate(struct bch_fs *, struct bch_sb_field_clean *, int);
+int bch2_sb_clean_validate_late(struct bch_fs *, struct bch_sb_field_clean *, int);
int bch2_fs_mark_dirty(struct bch_fs *);
void bch2_fs_mark_clean(struct bch_fs *);
void bch2_sb_field_to_text(struct printbuf *, struct bch_sb *,
struct bch_sb_field *);
+void bch2_sb_layout_to_text(struct printbuf *, struct bch_sb_layout *);
+void bch2_sb_to_text(struct printbuf *, struct bch_sb *, bool, unsigned);
#endif /* _BCACHEFS_SUPER_IO_H */
diff --git a/fs/bcachefs/sysfs.c b/fs/bcachefs/sysfs.c
index b727845dd64b..1a3068f658a1 100644
--- a/fs/bcachefs/sysfs.c
+++ b/fs/bcachefs/sysfs.c
@@ -825,7 +825,7 @@ SHOW(bch2_dev)
if (attr == &sysfs_label) {
if (ca->mi.group) {
mutex_lock(&c->sb_lock);
- bch2_disk_path_to_text(&out, &c->disk_sb,
+ bch2_disk_path_to_text(&out, c->disk_sb.sb,
ca->mi.group - 1);
mutex_unlock(&c->sb_lock);
}
diff --git a/fs/bcachefs/util.c b/fs/bcachefs/util.c
index 971f404a01e3..f170cf9d5052 100644
--- a/fs/bcachefs/util.c
+++ b/fs/bcachefs/util.c
@@ -120,6 +120,27 @@ void bch2_hprint(struct printbuf *buf, s64 v)
pr_buf(buf, "%c", si_units[u]);
}
+void bch2_pr_units(struct printbuf *out, s64 raw, s64 bytes)
+{
+ if (raw < 0) {
+ pr_buf(out, "-");
+ raw = -raw;
+ bytes = -bytes;
+ }
+
+ switch (out->units) {
+ case PRINTBUF_UNITS_RAW:
+ pr_buf(out, "%llu", raw);
+ break;
+ case PRINTBUF_UNITS_BYTES:
+ pr_buf(out, "%llu", bytes);
+ break;
+ case PRINTBUF_UNITS_HUMAN_READABLE:
+ bch2_hprint(out, bytes);
+ break;
+ }
+}
+
void bch2_string_opt_to_text(struct printbuf *out,
const char * const list[],
size_t selected)
diff --git a/fs/bcachefs/util.h b/fs/bcachefs/util.h
index fc8ffa61bbeb..3d5a9e04b3ad 100644
--- a/fs/bcachefs/util.h
+++ b/fs/bcachefs/util.h
@@ -235,10 +235,17 @@ do { \
#define ANYSINT_MAX(t) \
((((t) 1 << (sizeof(t) * 8 - 2)) - (t) 1) * (t) 2 + (t) 1)
+enum printbuf_units {
+ PRINTBUF_UNITS_RAW,
+ PRINTBUF_UNITS_BYTES,
+ PRINTBUF_UNITS_HUMAN_READABLE,
+};
+
struct printbuf {
- char *pos;
- char *end;
- unsigned indent;
+ char *pos;
+ char *end;
+ unsigned indent;
+ enum printbuf_units units;
};
static inline size_t printbuf_remaining(struct printbuf *buf)
@@ -272,7 +279,7 @@ static inline void printbuf_indent_pop(struct printbuf *buf, unsigned spaces)
buf->indent -= spaces;
}
-static inline void printbuf_newline(struct printbuf *buf)
+static inline void pr_newline(struct printbuf *buf)
{
unsigned i;
@@ -281,6 +288,46 @@ static inline void printbuf_newline(struct printbuf *buf)
pr_buf(buf, " ");
}
+void bch2_pr_units(struct printbuf *, s64, s64);
+#define pr_units(...) bch2_pr_units(__VA_ARGS__)
+
+#ifdef __KERNEL__
+static inline void pr_time(struct printbuf *out, u64 time)
+{
+ pr_buf(out, "%llu", time);
+}
+#else
+#include <time.h>
+static inline void pr_time(struct printbuf *out, u64 _time)
+{
+ char time_str[64];
+ time_t time = _time;
+ struct tm *tm = localtime(&time);
+ size_t err = strftime(time_str, sizeof(time_str), "%c", tm);
+ if (!err)
+ pr_buf(out, "(formatting error)");
+ else
+ pr_buf(out, "%s", time_str);
+}
+#endif
+
+#ifdef __KERNEL__
+static inline void uuid_unparse_lower(u8 *uuid, char *out)
+{
+ sprintf(out, "%plU", uuid);
+}
+#else
+#include <uuid/uuid.h>
+#endif
+
+static inline void pr_uuid(struct printbuf *out, u8 *uuid)
+{
+ char uuid_str[40];
+
+ uuid_unparse_lower(uuid, uuid_str);
+ pr_buf(out, uuid_str);
+}
+
int bch2_strtoint_h(const char *, int *);
int bch2_strtouint_h(const char *, unsigned int *);
int bch2_strtoll_h(const char *, long long *);
@@ -784,13 +831,4 @@ static inline int u8_cmp(u8 l, u8 r)
return cmp_int(l, r);
}
-#ifdef __KERNEL__
-static inline void uuid_unparse_lower(u8 *uuid, char *out)
-{
- sprintf(out, "%plU", uuid);
-}
-#else
-#include <uuid/uuid.h>
-#endif
-
#endif /* _BCACHEFS_UTIL_H */