summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2018-04-09 15:28:16 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2018-04-10 16:48:44 -0400
commitb5f4f54df802e4bd97b90b5d1955fc1d021106f4 (patch)
treee8387c66a897edded03b9f4c1e814f9a323e869f
parentd75894a71b5a230e37e41da7436ed54cd174a37e (diff)
bcachefs: nested disk groups; rename groups -> labels
-rw-r--r--fs/bcachefs/bcachefs_format.h5
-rw-r--r--fs/bcachefs/disk_groups.c303
-rw-r--r--fs/bcachefs/disk_groups.h4
-rw-r--r--fs/bcachefs/quota.c15
-rw-r--r--fs/bcachefs/quota.h3
-rw-r--r--fs/bcachefs/replicas.c6
-rw-r--r--fs/bcachefs/replicas.h2
-rw-r--r--fs/bcachefs/super-io.c84
-rw-r--r--fs/bcachefs/super-io.h6
-rw-r--r--fs/bcachefs/super_types.h3
-rw-r--r--fs/bcachefs/sysfs.c31
11 files changed, 299 insertions, 163 deletions
diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h
index d89f7781acd9..eed6fb852b90 100644
--- a/fs/bcachefs/bcachefs_format.h
+++ b/fs/bcachefs/bcachefs_format.h
@@ -955,8 +955,9 @@ struct bch_disk_group {
__le64 flags[2];
};
-LE64_BITMASK(BCH_GROUP_DELETED, struct bch_disk_group, flags[0], 0, 1)
-LE64_BITMASK(BCH_GROUP_DATA_ALLOWED, struct bch_disk_group, flags[0], 1, 6)
+LE64_BITMASK(BCH_GROUP_DELETED, struct bch_disk_group, flags[0], 0, 1)
+LE64_BITMASK(BCH_GROUP_DATA_ALLOWED, struct bch_disk_group, flags[0], 1, 6)
+LE64_BITMASK(BCH_GROUP_PARENT, struct bch_disk_group, flags[0], 6, 24)
struct bch_sb_field_disk_groups {
struct bch_sb_field field;
diff --git a/fs/bcachefs/disk_groups.c b/fs/bcachefs/disk_groups.c
index 86d0b0b2612d..4a7b1587914b 100644
--- a/fs/bcachefs/disk_groups.c
+++ b/fs/bcachefs/disk_groups.c
@@ -4,21 +4,27 @@
#include <linux/sort.h>
-static int strcmp_void(const void *l, const void *r)
+static int group_cmp(const void *_l, const void *_r)
{
- return strcmp(l, r);
+ const struct bch_disk_group *l = _l;
+ const struct bch_disk_group *r = _r;
+
+ return ((BCH_GROUP_DELETED(l) > BCH_GROUP_DELETED(r)) -
+ (BCH_GROUP_DELETED(l) < BCH_GROUP_DELETED(r))) ?:
+ ((BCH_GROUP_PARENT(l) > BCH_GROUP_PARENT(r)) -
+ (BCH_GROUP_PARENT(l) < BCH_GROUP_PARENT(r))) ?:
+ strncmp(l->label, r->label, sizeof(l->label));
}
-const char *bch2_sb_validate_disk_groups(struct bch_sb *sb,
+const char *bch2_sb_disk_groups_validate(struct bch_sb *sb,
struct bch_sb_field *f)
{
struct bch_sb_field_disk_groups *groups =
field_to_type(f, disk_groups);
- struct bch_disk_group *g;
+ struct bch_disk_group *g, *sorted = NULL;
struct bch_sb_field_members *mi;
struct bch_member *m;
- unsigned i, nr_groups, nr_live = 0, len;
- char **labels, *l;
+ unsigned i, nr_groups, len;
const char *err = NULL;
mi = bch2_sb_get_members(sb);
@@ -43,10 +49,6 @@ const char *bch2_sb_validate_disk_groups(struct bch_sb *sb,
if (!nr_groups)
return NULL;
- labels = kcalloc(nr_groups, sizeof(char *), GFP_KERNEL);
- if (!labels)
- return "cannot allocate memory";
-
for (g = groups->entries;
g < groups->entries + nr_groups;
g++) {
@@ -54,39 +56,71 @@ const char *bch2_sb_validate_disk_groups(struct bch_sb *sb,
continue;
len = strnlen(g->label, sizeof(g->label));
-
- labels[nr_live++] = l = kmalloc(len + 1, GFP_KERNEL);
- if (!l) {
- err = "cannot allocate memory";
+ if (!len) {
+ err = "group with empty label";
goto err;
}
-
- memcpy(l, g->label, len);
- l[len] = '\0';
}
- sort(labels, nr_live, sizeof(labels[0]), strcmp_void, NULL);
+ sorted = kmalloc_array(nr_groups, sizeof(*sorted), GFP_KERNEL);
+ if (!sorted)
+ return "cannot allocate memory";
+
+ memcpy(sorted, groups->entries, nr_groups * sizeof(*sorted));
+ sort(sorted, nr_groups, sizeof(*sorted), group_cmp, NULL);
- for (i = 0; i + 1 < nr_live; i++)
- if (!strcmp(labels[i], labels[i + 1])) {
- err = "duplicate group labels";
+ for (i = 0; i + 1 < nr_groups; i++)
+ if (!BCH_GROUP_DELETED(sorted + i) &&
+ !group_cmp(sorted + i, sorted + i + 1)) {
+ err = "duplicate groups";
goto err;
}
err = NULL;
err:
- for (i = 0; i < nr_live; i++)
- kfree(labels[i]);
- kfree(labels);
+ kfree(sorted);
return err;
}
+static size_t bch2_sb_disk_groups_to_text(char *buf, size_t size,
+ struct bch_sb *sb,
+ struct bch_sb_field *f)
+{
+ char *out = buf, *end = buf + size;
+ struct bch_sb_field_disk_groups *groups =
+ field_to_type(f, disk_groups);
+ struct bch_disk_group *g;
+ unsigned nr_groups = disk_groups_nr(groups);
+
+ for (g = groups->entries;
+ g < groups->entries + nr_groups;
+ g++) {
+ if (g != groups->entries)
+ out += scnprintf(out, end - out, " ");
+
+ if (BCH_GROUP_DELETED(g))
+ out += scnprintf(out, end - out, "[deleted]");
+ else
+ out += scnprintf(out, end - out,
+ "[parent %llu name %s]",
+ BCH_GROUP_PARENT(g),
+ g->label);
+ }
+
+ return out - buf;
+}
+
+const struct bch_sb_field_ops bch_sb_field_ops_disk_groups = {
+ .validate = bch2_sb_disk_groups_validate,
+ .to_text = bch2_sb_disk_groups_to_text
+};
+
int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)
{
struct bch_sb_field_members *mi;
struct bch_sb_field_disk_groups *groups;
struct bch_disk_groups_cpu *cpu_g, *old_g;
- unsigned i, nr_groups;
+ unsigned i, g, nr_groups;
lockdep_assert_held(&c->sb_lock);
@@ -108,7 +142,8 @@ int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)
struct bch_disk_group *src = &groups->entries[i];
struct bch_disk_group_cpu *dst = &cpu_g->entries[i];
- dst->deleted = BCH_GROUP_DELETED(src);
+ dst->deleted = BCH_GROUP_DELETED(src);
+ dst->parent = BCH_GROUP_PARENT(src);
}
for (i = 0; i < c->disk_sb->nr_devices; i++) {
@@ -119,11 +154,12 @@ int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)
if (!bch2_member_exists(m))
continue;
- dst = BCH_MEMBER_GROUP(m)
- ? &cpu_g->entries[BCH_MEMBER_GROUP(m) - 1]
- : NULL;
- if (dst)
+ g = BCH_MEMBER_GROUP(m);
+ while (g) {
+ dst = &cpu_g->entries[g - 1];
__set_bit(i, dst->devs.d);
+ g = dst->parent;
+ }
}
old_g = c->disk_groups;
@@ -158,10 +194,13 @@ const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned targe
}
static int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups,
- const char *name)
+ unsigned parent,
+ const char *name, unsigned namelen)
{
unsigned i, nr_groups = disk_groups_nr(groups);
- unsigned len = strlen(name);
+
+ if (!namelen || namelen > BCH_SB_LABEL_SIZE)
+ return -EINVAL;
for (i = 0; i < nr_groups; i++) {
struct bch_disk_group *g = groups->entries + i;
@@ -169,53 +208,27 @@ static int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups,
if (BCH_GROUP_DELETED(g))
continue;
- if (strnlen(g->label, sizeof(g->label)) == len &&
- !memcmp(name, g->label, len))
+ if (!BCH_GROUP_DELETED(g) &&
+ BCH_GROUP_PARENT(g) == parent &&
+ strnlen(g->label, sizeof(g->label)) == namelen &&
+ !memcmp(name, g->label, namelen))
return i;
}
return -1;
}
-static int bch2_disk_group_find(struct bch_fs *c, const char *name)
-{
- int ret;
-
- mutex_lock(&c->sb_lock);
- ret = __bch2_disk_group_find(bch2_sb_get_disk_groups(c->disk_sb), name);
- mutex_unlock(&c->sb_lock);
-
- return ret;
-}
-
-int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *label)
+static int __bch2_disk_group_add(struct bch_fs *c, unsigned parent,
+ const char *name, unsigned namelen)
{
- struct bch_sb_field_disk_groups *groups;
+ struct bch_sb_field_disk_groups *groups =
+ bch2_sb_get_disk_groups(c->disk_sb);
+ unsigned i, nr_groups = disk_groups_nr(groups);
struct bch_disk_group *g;
- struct bch_member *mi;
- unsigned i, v, nr_groups;
- int ret;
- if (strlen(label) > BCH_SB_LABEL_SIZE)
+ if (!namelen || namelen > BCH_SB_LABEL_SIZE)
return -EINVAL;
- mutex_lock(&c->sb_lock);
- groups = bch2_sb_get_disk_groups(c->disk_sb);
- nr_groups = disk_groups_nr(groups);
-
- if (!strcmp(label, "none")) {
- v = 0;
- goto write_sb;
- }
-
- ret = __bch2_disk_group_find(groups, label);
- if (ret >= 0) {
- v = ret + 1;
- goto write_sb;
- }
-
- /* not found - create a new disk group: */
-
for (i = 0;
i < nr_groups && !BCH_GROUP_DELETED(&groups->entries[i]);
i++)
@@ -239,16 +252,132 @@ int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *label)
BUG_ON(i >= nr_groups);
g = &groups->entries[i];
- v = i + 1;
- memcpy(g->label, label, strlen(label));
- if (strlen(label) < sizeof(g->label))
- g->label[strlen(label)] = '\0';
+ memcpy(g->label, name, namelen);
+ if (namelen < sizeof(g->label))
+ g->label[namelen] = '\0';
SET_BCH_GROUP_DELETED(g, 0);
+ SET_BCH_GROUP_PARENT(g, parent);
SET_BCH_GROUP_DATA_ALLOWED(g, ~0);
+
+ return i;
+}
+
+static int bch2_disk_path_find(struct bch_fs *c, const char *name)
+{
+ struct bch_sb_field_disk_groups *groups;
+ int v = -1;
+
+ mutex_lock(&c->sb_lock);
+ groups = bch2_sb_get_disk_groups(c->disk_sb);
+
+ do {
+ const char *next = strchrnul(name, '.');
+ unsigned len = next - name;
+
+ if (*next == '.')
+ next++;
+
+ v = __bch2_disk_group_find(groups, v + 1, name, len);
+ name = next;
+ } while (*name && v >= 0);
+
+ mutex_unlock(&c->sb_lock);
+
+ return v;
+}
+
+int bch2_disk_path_print(struct bch_fs *c, char *buf, size_t len, unsigned v)
+{
+ char *out = buf, *end = out + len;
+ struct bch_sb_field_disk_groups *groups;
+ struct bch_disk_group *g;
+ unsigned nr = 0;
+ u16 path[32];
+
+ mutex_lock(&c->sb_lock);
+ groups = bch2_sb_get_disk_groups(c->disk_sb);
+
+ while (1) {
+ if (nr == ARRAY_SIZE(path))
+ goto inval;
+
+ if (v >= disk_groups_nr(groups))
+ goto inval;
+
+ g = groups->entries + v;
+
+ if (BCH_GROUP_DELETED(g))
+ goto inval;
+
+ path[nr++] = v;
+
+ if (!BCH_GROUP_PARENT(g))
+ break;
+
+ v = BCH_GROUP_PARENT(g) - 1;
+ }
+
+ while (nr) {
+ unsigned b = 0;
+
+ v = path[--nr];
+ g = groups->entries + v;
+
+ if (end != out)
+ b = min_t(size_t, end - out,
+ strnlen(g->label, sizeof(g->label)));
+ memcpy(out, g->label, b);
+ if (b < end - out)
+ out[b] = '\0';
+ out += b;
+
+ if (nr)
+ out += scnprintf(out, end - out, ".");
+ }
+out:
+ mutex_unlock(&c->sb_lock);
+ return out - buf;
+inval:
+ out += scnprintf(out, end - out, "invalid group %u", v);
+ goto out;
+}
+
+int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
+{
+ struct bch_sb_field_disk_groups *groups;
+ struct bch_member *mi;
+ unsigned parent = 0;
+ int v;
+
+ mutex_lock(&c->sb_lock);
+
+ if (!strlen(name) || !strcmp(name, "none"))
+ goto write_sb;
+
+ do {
+ const char *next = strchrnul(name, '.');
+ unsigned len = next - name;
+
+ if (*next == '.')
+ next++;
+
+ groups = bch2_sb_get_disk_groups(c->disk_sb);
+
+ v = __bch2_disk_group_find(groups, parent, name, len);
+ if (v < 0)
+ v = __bch2_disk_group_add(c, parent, name, len);
+ if (v < 0) {
+ mutex_unlock(&c->sb_lock);
+ return v;
+ }
+
+ parent = v + 1;
+ name = next;
+ } while (*name && v >= 0);
write_sb:
mi = &bch2_sb_get_members(c->disk_sb)->members[ca->dev_idx];
- SET_BCH_MEMBER_GROUP(mi, v);
+ SET_BCH_MEMBER_GROUP(mi, v + 1);
bch2_write_super(c);
mutex_unlock(&c->sb_lock);
@@ -274,7 +403,7 @@ int bch2_opt_target_parse(struct bch_fs *c, const char *buf, u64 *v)
return 0;
}
- g = bch2_disk_group_find(c, buf);
+ g = bch2_disk_path_find(c, buf);
if (g >= 0) {
*v = group_to_target(g);
return 0;
@@ -314,30 +443,8 @@ int bch2_opt_target_print(struct bch_fs *c, char *buf, size_t len, u64 v)
rcu_read_unlock();
break;
}
- case TARGET_GROUP: {
- struct bch_sb_field_disk_groups *groups;
- struct bch_disk_group *g;
-
- mutex_lock(&c->sb_lock);
- groups = bch2_sb_get_disk_groups(c->disk_sb);
-
- g = t.group < disk_groups_nr(groups)
- ? groups->entries + t.group
- : NULL;
-
- if (g && !BCH_GROUP_DELETED(g)) {
- ret = len ? min(len - 1, strnlen(g->label, sizeof(g->label))) : 0;
-
- memcpy(buf, g->label, ret);
- if (len)
- buf[ret] = '\0';
- } else {
- ret = scnprintf(buf, len, "invalid group %u", t.group);
- }
-
- mutex_unlock(&c->sb_lock);
- break;
- }
+ case TARGET_GROUP:
+ return bch2_disk_path_print(c, buf, len, t.group);
default:
BUG();
}
diff --git a/fs/bcachefs/disk_groups.h b/fs/bcachefs/disk_groups.h
index 3db5310e211d..0ee3144912fa 100644
--- a/fs/bcachefs/disk_groups.h
+++ b/fs/bcachefs/disk_groups.h
@@ -1,6 +1,8 @@
#ifndef _BCACHEFS_DISK_GROUPS_H
#define _BCACHEFS_DISK_GROUPS_H
+extern const struct bch_sb_field_ops bch_sb_field_ops_disk_groups;
+
static inline unsigned disk_groups_nr(struct bch_sb_field_disk_groups *groups)
{
return groups
@@ -85,6 +87,8 @@ int bch2_opt_target_print(struct bch_fs *, char *, size_t, u64);
int bch2_sb_disk_groups_to_cpu(struct bch_fs *);
+int bch2_disk_path_print(struct bch_fs *, char *, size_t, unsigned);
+
int bch2_dev_group_set(struct bch_fs *, struct bch_dev *, const char *);
const char *bch2_sb_validate_disk_groups(struct bch_sb *,
diff --git a/fs/bcachefs/quota.c b/fs/bcachefs/quota.c
index 35dce874de10..80178a29f7e6 100644
--- a/fs/bcachefs/quota.c
+++ b/fs/bcachefs/quota.c
@@ -4,6 +4,21 @@
#include "quota.h"
#include "super-io.h"
+static const char *bch2_sb_validate_quota(struct bch_sb *sb,
+ struct bch_sb_field *f)
+{
+ struct bch_sb_field_quota *q = field_to_type(f, quota);
+
+ if (vstruct_bytes(&q->field) != sizeof(*q))
+ return "invalid field quota: wrong size";
+
+ return NULL;
+}
+
+const struct bch_sb_field_ops bch_sb_field_ops_quota = {
+ .validate = bch2_sb_validate_quota,
+};
+
const char *bch2_quota_invalid(const struct bch_fs *c, struct bkey_s_c k)
{
struct bkey_s_c_quota dq;
diff --git a/fs/bcachefs/quota.h b/fs/bcachefs/quota.h
index 5289e13ba233..0b24f22cf4fb 100644
--- a/fs/bcachefs/quota.h
+++ b/fs/bcachefs/quota.h
@@ -1,8 +1,11 @@
#ifndef _BCACHEFS_QUOTA_H
#define _BCACHEFS_QUOTA_H
+#include "inode.h"
#include "quota_types.h"
+extern const struct bch_sb_field_ops bch_sb_field_ops_quota;
+
const char *bch2_quota_invalid(const struct bch_fs *, struct bkey_s_c);
void bch2_quota_to_text(struct bch_fs *, char *, size_t, struct bkey_s_c);
diff --git a/fs/bcachefs/replicas.c b/fs/bcachefs/replicas.c
index c1cfcbdaa98f..0e48e5ca4502 100644
--- a/fs/bcachefs/replicas.c
+++ b/fs/bcachefs/replicas.c
@@ -460,7 +460,7 @@ static int bch2_cpu_replicas_to_sb_replicas(struct bch_fs *c,
return 0;
}
-const char *bch2_sb_validate_replicas(struct bch_sb *sb, struct bch_sb_field *f)
+static const char *bch2_sb_validate_replicas(struct bch_sb *sb, struct bch_sb_field *f)
{
struct bch_sb_field_replicas *sb_r = field_to_type(f, replicas);
struct bch_sb_field_members *mi = bch2_sb_get_members(sb);
@@ -517,6 +517,10 @@ err:
return err;
}
+const struct bch_sb_field_ops bch_sb_field_ops_replicas = {
+ .validate = bch2_sb_validate_replicas,
+};
+
int bch2_sb_replicas_to_text(struct bch_sb_field_replicas *r, char *buf, size_t size)
{
char *out = buf, *end = out + size;
diff --git a/fs/bcachefs/replicas.h b/fs/bcachefs/replicas.h
index 4006c63e652c..49f114b01c1e 100644
--- a/fs/bcachefs/replicas.h
+++ b/fs/bcachefs/replicas.h
@@ -46,6 +46,6 @@ replicas_entry_next(struct bch_replicas_entry *i)
int bch2_sb_replicas_to_cpu_replicas(struct bch_fs *);
-const char *bch2_sb_validate_replicas(struct bch_sb *, struct bch_sb_field *);
+extern const struct bch_sb_field_ops bch_sb_field_ops_replicas;
#endif /* _BCACHEFS_REPLICAS_H */
diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c
index a8ba6af03e39..5104dda42673 100644
--- a/fs/bcachefs/super-io.c
+++ b/fs/bcachefs/super-io.c
@@ -5,6 +5,7 @@
#include "error.h"
#include "io.h"
#include "replicas.h"
+#include "quota.h"
#include "super-io.h"
#include "super.h"
#include "vstructs.h"
@@ -12,17 +13,6 @@
#include <linux/backing-dev.h>
#include <linux/sort.h>
-/* superblock fields (optional/variable size sections: */
-
-static const char *bch2_sb_validate_journal(struct bch_sb *,
- struct bch_sb_field *);
-static const char *bch2_sb_validate_members(struct bch_sb *,
- struct bch_sb_field *);
-static const char *bch2_sb_validate_crypt(struct bch_sb *,
- struct bch_sb_field *);
-static const char *bch2_sb_validate_quota(struct bch_sb *,
- struct bch_sb_field *);
-
const char * const bch2_sb_fields[] = {
#define x(name, nr) #name,
BCH_SB_FIELDS()
@@ -30,29 +20,8 @@ const char * const bch2_sb_fields[] = {
NULL
};
-struct bch_sb_field_ops {
- const char * (*validate)(struct bch_sb *, struct bch_sb_field *);
-};
-
-static const struct bch_sb_field_ops bch2_sb_field_ops[] = {
-#define x(f, nr) \
- [BCH_SB_FIELD_##f] = { \
- .validate = bch2_sb_validate_##f, \
- },
- BCH_SB_FIELDS()
-#undef x
-};
-
-static const char *bch2_sb_field_validate(struct bch_sb *sb,
- struct bch_sb_field *f)
-
-{
- unsigned type = le32_to_cpu(f->type);
-
- return type < BCH_SB_FIELD_NR
- ? bch2_sb_field_ops[type].validate(sb, f)
- : NULL;
-}
+static const char *bch2_sb_field_validate(struct bch_sb *,
+ struct bch_sb_field *);
struct bch_sb_field *bch2_sb_field_get(struct bch_sb *sb,
enum bch_sb_field_type type)
@@ -838,6 +807,10 @@ err:
return err;
}
+static const struct bch_sb_field_ops bch_sb_field_ops_journal = {
+ .validate = bch2_sb_validate_journal,
+};
+
/* BCH_SB_FIELD_members: */
static const char *bch2_sb_validate_members(struct bch_sb *sb,
@@ -881,6 +854,10 @@ static const char *bch2_sb_validate_members(struct bch_sb *sb,
return NULL;
}
+static const struct bch_sb_field_ops bch_sb_field_ops_members = {
+ .validate = bch2_sb_validate_members,
+};
+
/* BCH_SB_FIELD_crypt: */
static const char *bch2_sb_validate_crypt(struct bch_sb *sb,
@@ -897,15 +874,42 @@ static const char *bch2_sb_validate_crypt(struct bch_sb *sb,
return NULL;
}
-/* Quotas: */
+static const struct bch_sb_field_ops bch_sb_field_ops_crypt = {
+ .validate = bch2_sb_validate_crypt,
+};
-static const char *bch2_sb_validate_quota(struct bch_sb *sb,
+static const struct bch_sb_field_ops *bch2_sb_field_ops[] = {
+#define x(f, nr) \
+ [BCH_SB_FIELD_##f] = &bch_sb_field_ops_##f,
+ BCH_SB_FIELDS()
+#undef x
+};
+
+static const char *bch2_sb_field_validate(struct bch_sb *sb,
struct bch_sb_field *f)
{
- struct bch_sb_field_quota *q = field_to_type(f, quota);
+ unsigned type = le32_to_cpu(f->type);
+
+ return type < BCH_SB_FIELD_NR
+ ? bch2_sb_field_ops[type]->validate(sb, f)
+ : NULL;
+}
+
+size_t bch2_sb_field_to_text(char *buf, size_t size,
+ struct bch_sb *sb, struct bch_sb_field *f)
+{
+ unsigned type = le32_to_cpu(f->type);
+ size_t (*to_text)(char *, size_t, struct bch_sb *,
+ struct bch_sb_field *) =
+ type < BCH_SB_FIELD_NR
+ ? bch2_sb_field_ops[type]->to_text
+ : NULL;
- if (vstruct_bytes(&q->field) != sizeof(*q))
- return "invalid field quota: wrong size";
+ if (!to_text) {
+ if (size)
+ buf[0] = '\0';
+ return 0;
+ }
- return NULL;
+ return to_text(buf, size, sb, f);
}
diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h
index 866404b05912..ed351a201813 100644
--- a/fs/bcachefs/super-io.h
+++ b/fs/bcachefs/super-io.h
@@ -44,6 +44,12 @@ BCH_SB_FIELDS()
extern const char * const bch2_sb_fields[];
+struct bch_sb_field_ops {
+ const char * (*validate)(struct bch_sb *, struct bch_sb_field *);
+ size_t (*to_text)(char *, size_t, struct bch_sb *,
+ struct bch_sb_field *);
+};
+
static inline bool bch2_sb_test_feature(struct bch_sb *sb,
enum bch_sb_features f)
{
diff --git a/fs/bcachefs/super_types.h b/fs/bcachefs/super_types.h
index f5468182e485..a402c107aa71 100644
--- a/fs/bcachefs/super_types.h
+++ b/fs/bcachefs/super_types.h
@@ -44,8 +44,9 @@ struct bch_replicas_cpu {
};
struct bch_disk_group_cpu {
- struct bch_devs_mask devs;
bool deleted;
+ u16 parent;
+ struct bch_devs_mask devs;
};
struct bch_disk_groups_cpu {
diff --git a/fs/bcachefs/sysfs.c b/fs/bcachefs/sysfs.c
index f796f7729a80..95127cd35cb3 100644
--- a/fs/bcachefs/sysfs.c
+++ b/fs/bcachefs/sysfs.c
@@ -169,7 +169,7 @@ rw_attribute(journal_reclaim_delay_ms);
rw_attribute(discard);
rw_attribute(cache_replacement_policy);
-rw_attribute(group);
+rw_attribute(label);
rw_attribute(copy_gc_enabled);
sysfs_pd_controller_attribute(copy_gc);
@@ -811,24 +811,15 @@ SHOW(bch2_dev)
sysfs_print(durability, ca->mi.durability);
sysfs_print(discard, ca->mi.discard);
- if (attr == &sysfs_group) {
- struct bch_sb_field_disk_groups *groups;
- struct bch_disk_group *g;
- unsigned len;
+ if (attr == &sysfs_label) {
+ if (ca->mi.group)
+ out += bch2_disk_path_print(c, out, end - out,
+ ca->mi.group - 1);
+ else
+ out += scnprintf(out, end - out, "none");
- if (!ca->mi.group)
- return scnprintf(out, end - out, "none\n");
-
- mutex_lock(&c->sb_lock);
- groups = bch2_sb_get_disk_groups(c->disk_sb);
-
- g = &groups->entries[ca->mi.group - 1];
- len = strnlen(g->label, sizeof(g->label));
- memcpy(buf, g->label, len);
- mutex_unlock(&c->sb_lock);
-
- buf[len++] = '\n';
- return len;
+ out += scnprintf(out, end - out, "\n");
+ return out - buf;
}
if (attr == &sysfs_has_data) {
@@ -914,7 +905,7 @@ STORE(bch2_dev)
mutex_unlock(&c->sb_lock);
}
- if (attr == &sysfs_group) {
+ if (attr == &sysfs_label) {
char *tmp;
int ret;
@@ -947,7 +938,7 @@ struct attribute *bch2_dev_files[] = {
&sysfs_discard,
&sysfs_cache_replacement_policy,
&sysfs_state_rw,
- &sysfs_group,
+ &sysfs_label,
&sysfs_has_data,
&sysfs_iostats,