X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libbcachefs%2Fdisk_groups.c;h=43aad8ba8cc970794dc57a2b548b279667923615;hb=1251ea58a84e365db3754e6f5f57bd442484dc89;hp=4a4ec8f4610849eaee4ce733ba9976b3fd0bd916;hpb=f96ba8e0aac91f2650270e9639359243cb9ac2d1;p=bcachefs-tools-debian diff --git a/libbcachefs/disk_groups.c b/libbcachefs/disk_groups.c index 4a4ec8f..43aad8b 100644 --- a/libbcachefs/disk_groups.c +++ b/libbcachefs/disk_groups.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "bcachefs.h" #include "disk_groups.h" +#include "sb-members.h" #include "super-io.h" #include @@ -17,70 +18,107 @@ 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 (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 (g >= nr_groups || - BCH_GROUP_DELETED(&groups->entries[g])) - return "disk has invalid group"; + 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) +{ + struct bch_disk_groups_cpu *g; + struct bch_dev *ca; + int i; + unsigned iter; + + out->atomic++; + rcu_read_lock(); + + g = rcu_dereference(c->disk_groups); + if (!g) + goto out; + + for (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(ca, c, iter, &g->entries[i].devs) + prt_printf(out, " %s", ca->name); + prt_printf(out, "]"); + } + +out: + rcu_read_unlock(); + out->atomic--; } static void bch2_sb_disk_groups_to_text(struct printbuf *out, @@ -96,12 +134,12 @@ static void bch2_sb_disk_groups_to_text(struct printbuf *out, g < groups->entries + nr_groups; g++) { if (g != groups->entries) - pr_buf(out, " "); + prt_printf(out, " "); if (BCH_GROUP_DELETED(g)) - pr_buf(out, "[deleted]"); + prt_printf(out, "[deleted]"); else - pr_buf(out, "[parent %llu name %s]", + prt_printf(out, "[parent %llu name %s]", BCH_GROUP_PARENT(g), g->label); } } @@ -113,14 +151,12 @@ 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); nr_groups = disk_groups_nr(groups); @@ -130,7 +166,7 @@ int bch2_sb_disk_groups_to_cpu(struct bch_fs *c) cpu_g = kzalloc(sizeof(*cpu_g) + sizeof(cpu_g->entries[0]) * nr_groups, GFP_KERNEL); if (!cpu_g) - return -ENOMEM; + return -BCH_ERR_ENOMEM_disk_groups_to_cpu; cpu_g->nr = nr_groups; @@ -143,14 +179,13 @@ int bch2_sb_disk_groups_to_cpu(struct bch_fs *c) } 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); @@ -170,26 +205,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: - return 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) @@ -208,7 +253,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; @@ -271,7 +316,7 @@ static int __bch2_disk_group_add(struct bch_sb_handle *sb, unsigned parent, groups = bch2_sb_resize_disk_groups(sb, u64s); if (!groups) - return -ENOSPC; + return -BCH_ERR_ENOSPC_disk_label_add; nr_groups = disk_groups_nr(groups); } @@ -338,12 +383,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]; @@ -372,108 +415,135 @@ void bch2_disk_path_to_text(struct printbuf *out, v = path[--nr]; g = groups->entries + v; - bch_scnmemcpy(out, g->label, - strnlen(g->label, sizeof(g->label))); - + prt_printf(out, "%.*s", (int) sizeof(g->label), g->label); if (nr) - pr_buf(out, "."); + prt_printf(out, "."); } return; inval: - pr_buf(out, "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; } -void bch2_opt_target_to_text(struct printbuf *out, struct bch_fs *c, u64 v) +void bch2_opt_target_to_text(struct printbuf *out, + struct bch_fs *c, + struct bch_sb *sb, + u64 v) { struct target t = target_decode(v); switch (t.type) { case TARGET_NULL: - pr_buf(out, "none"); + prt_printf(out, "none"); break; - case TARGET_DEV: { - struct bch_dev *ca; - - 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]; - - pr_buf(out, "/dev/%s", - bdevname(ca->disk_sb.bdev, b)); - percpu_ref_put(&ca->io_ref); - } else if (ca) { - pr_buf(out, "offline device %u", t.dev); + case TARGET_DEV: + if (c) { + struct bch_dev *ca; + + 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)) { + prt_printf(out, "/dev/%pg", ca->disk_sb.bdev); + percpu_ref_put(&ca->io_ref); + } else if (ca) { + prt_printf(out, "offline device %u", t.dev); + } else { + prt_printf(out, "invalid device %u", t.dev); + } + + rcu_read_unlock(); } else { - pr_buf(out, "invalid device %u", t.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); + } } - - rcu_read_unlock(); break; - } case TARGET_GROUP: - mutex_lock(&c->sb_lock); - bch2_disk_path_to_text(out, &c->disk_sb, t.group); - mutex_unlock(&c->sb_lock); + if (c) { + mutex_lock(&c->sb_lock); + bch2_disk_path_to_text(out, c->disk_sb.sb, t.group); + mutex_unlock(&c->sb_lock); + } else { + bch2_disk_path_to_text(out, sb, t.group); + } break; default: BUG();