]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/disk_groups.c
Move c_src dirs back to toplevel
[bcachefs-tools-debian] / libbcachefs / disk_groups.c
index cd200cbed3e6ec9fef93b2092b1409a8c3e823d1..06a7df529b401c2f8665c17d66803b4649692bc9 100644 (file)
@@ -1,5 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
 #include "bcachefs.h"
 #include "disk_groups.h"
+#include "sb-members.h"
 #include "super-io.h"
 
 #include <linux/sort.h>
@@ -16,77 +18,108 @@ static int group_cmp(const void *_l, const void *_r)
                strncmp(l->label, r->label, sizeof(l->label));
 }
 
-static const char *bch2_sb_disk_groups_validate(struct bch_sb *sb,
-                                               struct bch_sb_field *f)
+static int bch2_sb_disk_groups_validate(struct bch_sb *sb,
+                                       struct bch_sb_field *f,
+                                       struct printbuf *err)
 {
        struct bch_sb_field_disk_groups *groups =
                field_to_type(f, disk_groups);
        struct bch_disk_group *g, *sorted = NULL;
-       struct bch_sb_field_members *mi;
-       struct bch_member *m;
-       unsigned i, nr_groups, len;
-       const char *err = NULL;
-
-       mi              = bch2_sb_get_members(sb);
-       groups          = bch2_sb_get_disk_groups(sb);
-       nr_groups       = disk_groups_nr(groups);
+       unsigned nr_groups = disk_groups_nr(groups);
+       unsigned i, len;
+       int ret = 0;
 
-       for (m = mi->members;
-            m < mi->members + sb->nr_devices;
-            m++) {
-               unsigned g;
+       for (i = 0; i < sb->nr_devices; i++) {
+               struct bch_member m = bch2_sb_member_get(sb, i);
+               unsigned group_id;
 
-               if (!BCH_MEMBER_GROUP(m))
+               if (!BCH_MEMBER_GROUP(&m))
                        continue;
 
-               g = BCH_MEMBER_GROUP(m) - 1;
+               group_id = BCH_MEMBER_GROUP(&m) - 1;
 
-               if (g >= nr_groups ||
-                   BCH_GROUP_DELETED(&groups->entries[g]))
-                       return "disk has invalid group";
+               if (group_id >= nr_groups) {
+                       prt_printf(err, "disk %u has invalid label %u (have %u)",
+                                  i, group_id, nr_groups);
+                       return -BCH_ERR_invalid_sb_disk_groups;
+               }
+
+               if (BCH_GROUP_DELETED(&groups->entries[group_id])) {
+                       prt_printf(err, "disk %u has deleted label %u", i, group_id);
+                       return -BCH_ERR_invalid_sb_disk_groups;
+               }
        }
 
        if (!nr_groups)
-               return NULL;
+               return 0;
+
+       for (i = 0; i < nr_groups; i++) {
+               g = groups->entries + i;
 
-       for (g = groups->entries;
-            g < groups->entries + nr_groups;
-            g++) {
                if (BCH_GROUP_DELETED(g))
                        continue;
 
                len = strnlen(g->label, sizeof(g->label));
                if (!len) {
-                       err = "group with empty label";
-                       goto err;
+                       prt_printf(err, "label %u empty", i);
+                       return -BCH_ERR_invalid_sb_disk_groups;
                }
        }
 
        sorted = kmalloc_array(nr_groups, sizeof(*sorted), GFP_KERNEL);
        if (!sorted)
-               return "cannot allocate memory";
+               return -BCH_ERR_ENOMEM_disk_groups_validate;
 
        memcpy(sorted, groups->entries, nr_groups * sizeof(*sorted));
        sort(sorted, nr_groups, sizeof(*sorted), group_cmp, NULL);
 
-       for (i = 0; i + 1 < nr_groups; i++)
-               if (!BCH_GROUP_DELETED(sorted + i) &&
-                   !group_cmp(sorted + i, sorted + i + 1)) {
-                       err = "duplicate groups";
+       for (g = sorted; g + 1 < sorted + nr_groups; g++)
+               if (!BCH_GROUP_DELETED(g) &&
+                   !group_cmp(&g[0], &g[1])) {
+                       prt_printf(err, "duplicate label %llu.%.*s",
+                              BCH_GROUP_PARENT(g),
+                              (int) sizeof(g->label), g->label);
+                       ret = -BCH_ERR_invalid_sb_disk_groups;
                        goto err;
                }
-
-       err = NULL;
 err:
        kfree(sorted);
-       return err;
+       return ret;
+}
+
+void bch2_disk_groups_to_text(struct printbuf *out, struct bch_fs *c)
+{
+       out->atomic++;
+       rcu_read_lock();
+
+       struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);
+       if (!g)
+               goto out;
+
+       for (unsigned i = 0; i < g->nr; i++) {
+               if (i)
+                       prt_printf(out, " ");
+
+               if (g->entries[i].deleted) {
+                       prt_printf(out, "[deleted]");
+                       continue;
+               }
+
+               prt_printf(out, "[parent %d devs", g->entries[i].parent);
+               for_each_member_device_rcu(c, ca, &g->entries[i].devs)
+                       prt_printf(out, " %s", ca->name);
+               prt_printf(out, "]");
+       }
+
+out:
+       rcu_read_unlock();
+       out->atomic--;
 }
 
-static size_t bch2_sb_disk_groups_to_text(char *buf, size_t size,
+static void bch2_sb_disk_groups_to_text(struct printbuf *out,
                                        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;
@@ -96,18 +129,14 @@ static size_t bch2_sb_disk_groups_to_text(char *buf, size_t size,
             g < groups->entries + nr_groups;
             g++) {
                if (g != groups->entries)
-                       out += scnprintf(out, end - out, " ");
+                       prt_printf(out, " ");
 
                if (BCH_GROUP_DELETED(g))
-                       out += scnprintf(out, end - out, "[deleted]");
+                       prt_printf(out, "[deleted]");
                else
-                       out += scnprintf(out, end - out,
-                                        "[parent %llu name %s]",
-                                        BCH_GROUP_PARENT(g),
-                                        g->label);
+                       prt_printf(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 = {
@@ -117,24 +146,21 @@ const struct bch_sb_field_ops bch_sb_field_ops_disk_groups = {
 
 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, g, nr_groups;
 
        lockdep_assert_held(&c->sb_lock);
 
-       mi              = bch2_sb_get_members(c->disk_sb.sb);
-       groups          = bch2_sb_get_disk_groups(c->disk_sb.sb);
+       groups          = bch2_sb_field_get(c->disk_sb.sb, disk_groups);
        nr_groups       = disk_groups_nr(groups);
 
        if (!groups)
                return 0;
 
-       cpu_g = kzalloc(sizeof(*cpu_g) +
-                       sizeof(cpu_g->entries[0]) * nr_groups, GFP_KERNEL);
+       cpu_g = kzalloc(struct_size(cpu_g, entries, nr_groups), GFP_KERNEL);
        if (!cpu_g)
-               return -ENOMEM;
+               return -BCH_ERR_ENOMEM_disk_groups_to_cpu;
 
        cpu_g->nr = nr_groups;
 
@@ -144,17 +170,17 @@ int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)
 
                dst->deleted    = BCH_GROUP_DELETED(src);
                dst->parent     = BCH_GROUP_PARENT(src);
+               memcpy(dst->label, src->label, sizeof(dst->label));
        }
 
        for (i = 0; i < c->disk_sb.sb->nr_devices; i++) {
-               struct bch_member *m = mi->members + i;
-               struct bch_disk_group_cpu *dst =
-                       &cpu_g->entries[BCH_MEMBER_GROUP(m)];
+               struct bch_member m = bch2_sb_member_get(c->disk_sb.sb, i);
+               struct bch_disk_group_cpu *dst;
 
-               if (!bch2_member_exists(m))
+               if (!bch2_member_exists(&m))
                        continue;
 
-               g = BCH_MEMBER_GROUP(m);
+               g = BCH_MEMBER_GROUP(&m);
                while (g) {
                        dst = &cpu_g->entries[g - 1];
                        __set_bit(i, dst->devs.d);
@@ -174,24 +200,36 @@ int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)
 const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned target)
 {
        struct target t = target_decode(target);
+       struct bch_devs_mask *devs;
+
+       rcu_read_lock();
 
        switch (t.type) {
+       case TARGET_NULL:
+               devs = NULL;
+               break;
        case TARGET_DEV: {
                struct bch_dev *ca = t.dev < c->sb.nr_devices
                        ? rcu_dereference(c->devs[t.dev])
                        : NULL;
-               return ca ? &ca->self : NULL;
+               devs = ca ? &ca->self : NULL;
+               break;
        }
        case TARGET_GROUP: {
                struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);
 
-               return t.group < g->nr && !g->entries[t.group].deleted
+               devs = g && t.group < g->nr && !g->entries[t.group].deleted
                        ? &g->entries[t.group].devs
                        : NULL;
+               break;
        }
        default:
                BUG();
        }
+
+       rcu_read_unlock();
+
+       return devs;
 }
 
 bool bch2_dev_in_target(struct bch_fs *c, unsigned dev, unsigned target)
@@ -210,7 +248,7 @@ bool bch2_dev_in_target(struct bch_fs *c, unsigned dev, unsigned target)
 
                rcu_read_lock();
                g = rcu_dereference(c->disk_groups);
-               m = t.group < g->nr && !g->entries[t.group].deleted
+               m = g && t.group < g->nr && !g->entries[t.group].deleted
                        ? &g->entries[t.group].devs
                        : NULL;
 
@@ -253,7 +291,7 @@ static int __bch2_disk_group_add(struct bch_sb_handle *sb, unsigned parent,
                                 const char *name, unsigned namelen)
 {
        struct bch_sb_field_disk_groups *groups =
-               bch2_sb_get_disk_groups(sb->sb);
+               bch2_sb_field_get(sb->sb, disk_groups);
        unsigned i, nr_groups = disk_groups_nr(groups);
        struct bch_disk_group *g;
 
@@ -271,9 +309,9 @@ static int __bch2_disk_group_add(struct bch_sb_handle *sb, unsigned parent,
                         sizeof(struct bch_disk_group) * (nr_groups + 1)) /
                        sizeof(u64);
 
-               groups = bch2_sb_resize_disk_groups(sb, u64s);
+               groups = bch2_sb_field_resize(sb, disk_groups, u64s);
                if (!groups)
-                       return -ENOSPC;
+                       return -BCH_ERR_ENOSPC_disk_label_add;
 
                nr_groups = disk_groups_nr(groups);
        }
@@ -295,7 +333,7 @@ static int __bch2_disk_group_add(struct bch_sb_handle *sb, unsigned parent,
 int bch2_disk_path_find(struct bch_sb_handle *sb, const char *name)
 {
        struct bch_sb_field_disk_groups *groups =
-               bch2_sb_get_disk_groups(sb->sb);
+               bch2_sb_field_get(sb->sb, disk_groups);
        int v = -1;
 
        do {
@@ -325,7 +363,7 @@ int bch2_disk_path_find_or_create(struct bch_sb_handle *sb, const char *name)
                if (*next == '.')
                        next++;
 
-               groups = bch2_sb_get_disk_groups(sb->sb);
+               groups = bch2_sb_field_get(sb->sb, disk_groups);
 
                v = __bch2_disk_group_find(groups, parent, name, len);
                if (v < 0)
@@ -340,12 +378,60 @@ int bch2_disk_path_find_or_create(struct bch_sb_handle *sb, const char *name)
        return v;
 }
 
-int bch2_disk_path_print(struct bch_sb_handle *sb,
-                        char *buf, size_t len, unsigned v)
+void bch2_disk_path_to_text(struct printbuf *out, struct bch_fs *c, unsigned v)
+{
+       struct bch_disk_groups_cpu *groups;
+       struct bch_disk_group_cpu *g;
+       unsigned nr = 0;
+       u16 path[32];
+
+       out->atomic++;
+       rcu_read_lock();
+       groups = rcu_dereference(c->disk_groups);
+       if (!groups)
+               goto invalid;
+
+       while (1) {
+               if (nr == ARRAY_SIZE(path))
+                       goto invalid;
+
+               if (v >= groups->nr)
+                       goto invalid;
+
+               g = groups->entries + v;
+
+               if (g->deleted)
+                       goto invalid;
+
+               path[nr++] = v;
+
+               if (!g->parent)
+                       break;
+
+               v = g->parent - 1;
+       }
+
+       while (nr) {
+               v = path[--nr];
+               g = groups->entries + v;
+
+               prt_printf(out, "%.*s", (int) sizeof(g->label), g->label);
+               if (nr)
+                       prt_printf(out, ".");
+       }
+out:
+       rcu_read_unlock();
+       out->atomic--;
+       return;
+invalid:
+       prt_printf(out, "invalid label %u", v);
+       goto out;
+}
+
+void bch2_disk_path_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v)
 {
-       char *out = buf, *end = out + len;
        struct bch_sb_field_disk_groups *groups =
-               bch2_sb_get_disk_groups(sb->sb);
+               bch2_sb_field_get(sb, disk_groups);
        struct bch_disk_group *g;
        unsigned nr = 0;
        u16 path[32];
@@ -371,123 +457,161 @@ int bch2_disk_path_print(struct bch_sb_handle *sb,
        }
 
        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;
-
+               prt_printf(out, "%.*s", (int) sizeof(g->label), g->label);
                if (nr)
-                       out += scnprintf(out, end - out, ".");
+                       prt_printf(out, ".");
        }
-
-       return out - buf;
+       return;
 inval:
-       return scnprintf(buf, len, "invalid group %u", v);
+       prt_printf(out, "invalid label %u", v);
 }
 
-int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
+int __bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
 {
        struct bch_member *mi;
-       int v = -1;
-
-       mutex_lock(&c->sb_lock);
+       int ret, v = -1;
 
        if (!strlen(name) || !strcmp(name, "none"))
-               goto write_sb;
+               return 0;
 
        v = bch2_disk_path_find_or_create(&c->disk_sb, name);
-       if (v < 0) {
-               mutex_unlock(&c->sb_lock);
+       if (v < 0)
                return v;
-       }
 
-write_sb:
-       mi = &bch2_sb_get_members(c->disk_sb.sb)->members[ca->dev_idx];
+       ret = bch2_sb_disk_groups_to_cpu(c);
+       if (ret)
+               return ret;
+
+       mi = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);
        SET_BCH_MEMBER_GROUP(mi, v + 1);
+       return 0;
+}
+
+int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
+{
+       int ret;
 
-       bch2_write_super(c);
+       mutex_lock(&c->sb_lock);
+       ret = __bch2_dev_group_set(c, ca, name) ?:
+               bch2_write_super(c);
        mutex_unlock(&c->sb_lock);
 
-       return 0;
+       return ret;
 }
 
-int bch2_opt_target_parse(struct bch_fs *c, const char *buf, u64 *v)
+int bch2_opt_target_parse(struct bch_fs *c, const char *val, u64 *res,
+                         struct printbuf *err)
 {
        struct bch_dev *ca;
        int g;
 
-       if (!strlen(buf) || !strcmp(buf, "none")) {
-               *v = 0;
+       if (!val)
+               return -EINVAL;
+
+       if (!c)
+               return 0;
+
+       if (!strlen(val) || !strcmp(val, "none")) {
+               *res = 0;
                return 0;
        }
 
        /* Is it a device? */
-       ca = bch2_dev_lookup(c, buf);
+       ca = bch2_dev_lookup(c, val);
        if (!IS_ERR(ca)) {
-               *v = dev_to_target(ca->dev_idx);
+               *res = dev_to_target(ca->dev_idx);
                percpu_ref_put(&ca->ref);
                return 0;
        }
 
        mutex_lock(&c->sb_lock);
-       g = bch2_disk_path_find(&c->disk_sb, buf);
+       g = bch2_disk_path_find(&c->disk_sb, val);
        mutex_unlock(&c->sb_lock);
 
        if (g >= 0) {
-               *v = group_to_target(g);
+               *res = group_to_target(g);
                return 0;
        }
 
        return -EINVAL;
 }
 
-int bch2_opt_target_print(struct bch_fs *c, char *buf, size_t len, u64 v)
+void bch2_target_to_text(struct printbuf *out, struct bch_fs *c, unsigned v)
 {
        struct target t = target_decode(v);
-       int ret;
 
        switch (t.type) {
        case TARGET_NULL:
-               return scnprintf(buf, len, "none");
+               prt_printf(out, "none");
+               break;
        case TARGET_DEV: {
                struct bch_dev *ca;
 
+               out->atomic++;
                rcu_read_lock();
                ca = t.dev < c->sb.nr_devices
                        ? rcu_dereference(c->devs[t.dev])
                        : NULL;
 
                if (ca && percpu_ref_tryget(&ca->io_ref)) {
-                       char b[BDEVNAME_SIZE];
-
-                       ret = scnprintf(buf, len, "/dev/%s",
-                                       bdevname(ca->disk_sb.bdev, b));
+                       prt_printf(out, "/dev/%s", ca->name);
                        percpu_ref_put(&ca->io_ref);
                } else if (ca) {
-                       ret = scnprintf(buf, len, "offline device %u", t.dev);
+                       prt_printf(out, "offline device %u", t.dev);
                } else {
-                       ret = scnprintf(buf, len, "invalid device %u", t.dev);
+                       prt_printf(out, "invalid device %u", t.dev);
                }
 
                rcu_read_unlock();
+               out->atomic--;
                break;
        }
        case TARGET_GROUP:
-               mutex_lock(&c->sb_lock);
-               ret = bch2_disk_path_print(&c->disk_sb, buf, len, t.group);
-               mutex_unlock(&c->sb_lock);
+               bch2_disk_path_to_text(out, c, t.group);
                break;
        default:
                BUG();
        }
+}
 
-       return ret;
+static void bch2_target_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v)
+{
+       struct target t = target_decode(v);
+
+       switch (t.type) {
+       case TARGET_NULL:
+               prt_printf(out, "none");
+               break;
+       case TARGET_DEV: {
+               struct bch_member m = bch2_sb_member_get(sb, t.dev);
+
+               if (bch2_dev_exists(sb, t.dev)) {
+                       prt_printf(out, "Device ");
+                       pr_uuid(out, m.uuid.b);
+                       prt_printf(out, " (%u)", t.dev);
+               } else {
+                       prt_printf(out, "Bad device %u", t.dev);
+               }
+               break;
+       }
+       case TARGET_GROUP:
+               bch2_disk_path_to_text_sb(out, sb, t.group);
+               break;
+       default:
+               BUG();
+       }
+}
+
+void bch2_opt_target_to_text(struct printbuf *out,
+                            struct bch_fs *c,
+                            struct bch_sb *sb,
+                            u64 v)
+{
+       if (c)
+               bch2_target_to_text(out, c, v);
+       else
+               bch2_target_to_text_sb(out, sb, v);
 }