diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2018-04-04 21:34:58 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2018-05-22 00:44:18 -0400 |
commit | e4292330d09c65dfc4c3ff062ec5b7c1eec5b922 (patch) | |
tree | e8eeb82b9e6ffdb628d4ad043ad9f2a159040173 | |
parent | 8983de17ee1a221f8867c40d4fe4c1c3b15182a8 (diff) |
bcachefs: split out replicas.c and disk_groups.c
-rw-r--r-- | fs/bcachefs/Makefile | 2 | ||||
-rw-r--r-- | fs/bcachefs/alloc.c | 1 | ||||
-rw-r--r-- | fs/bcachefs/btree_gc.c | 1 | ||||
-rw-r--r-- | fs/bcachefs/btree_update_interior.c | 1 | ||||
-rw-r--r-- | fs/bcachefs/disk_groups.c | 346 | ||||
-rw-r--r-- | fs/bcachefs/disk_groups.h | 93 | ||||
-rw-r--r-- | fs/bcachefs/extents.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/io.c | 1 | ||||
-rw-r--r-- | fs/bcachefs/journal.c | 1 | ||||
-rw-r--r-- | fs/bcachefs/migrate.c | 1 | ||||
-rw-r--r-- | fs/bcachefs/move.c | 1 | ||||
-rw-r--r-- | fs/bcachefs/movinggc.c | 1 | ||||
-rw-r--r-- | fs/bcachefs/opts.c | 1 | ||||
-rw-r--r-- | fs/bcachefs/replicas.c | 694 | ||||
-rw-r--r-- | fs/bcachefs/replicas.h | 51 | ||||
-rw-r--r-- | fs/bcachefs/super-io.c | 986 | ||||
-rw-r--r-- | fs/bcachefs/super-io.h | 131 | ||||
-rw-r--r-- | fs/bcachefs/super.c | 70 | ||||
-rw-r--r-- | fs/bcachefs/super.h | 1 | ||||
-rw-r--r-- | fs/bcachefs/sysfs.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/tier.c | 1 |
21 files changed, 1213 insertions, 1175 deletions
diff --git a/fs/bcachefs/Makefile b/fs/bcachefs/Makefile index 36136ec0525a..aff607ec7c8c 100644 --- a/fs/bcachefs/Makefile +++ b/fs/bcachefs/Makefile @@ -20,6 +20,7 @@ bcachefs-y := \ compress.o \ debug.o \ dirent.o \ + disk_groups.o \ error.o \ extents.o \ fs.o \ @@ -36,6 +37,7 @@ bcachefs-y := \ movinggc.o \ opts.o \ quota.o \ + replicas.o \ siphash.o \ six.o \ super.o \ diff --git a/fs/bcachefs/alloc.c b/fs/bcachefs/alloc.c index 7bb495249f7e..a9668a6d3dbc 100644 --- a/fs/bcachefs/alloc.c +++ b/fs/bcachefs/alloc.c @@ -64,6 +64,7 @@ #include "checksum.h" #include "clock.h" #include "debug.h" +#include "disk_groups.h" #include "error.h" #include "extents.h" #include "io.h" diff --git a/fs/bcachefs/btree_gc.c b/fs/bcachefs/btree_gc.c index f2e9c10e4efe..70e4049f7daa 100644 --- a/fs/bcachefs/btree_gc.c +++ b/fs/bcachefs/btree_gc.c @@ -18,6 +18,7 @@ #include "journal.h" #include "keylist.h" #include "move.h" +#include "replicas.h" #include "super-io.h" #include <linux/slab.h> diff --git a/fs/bcachefs/btree_update_interior.c b/fs/bcachefs/btree_update_interior.c index 2c38f59f0b4f..c89f55c8cea4 100644 --- a/fs/bcachefs/btree_update_interior.c +++ b/fs/bcachefs/btree_update_interior.c @@ -13,6 +13,7 @@ #include "extents.h" #include "journal.h" #include "keylist.h" +#include "replicas.h" #include "super-io.h" #include <linux/random.h> diff --git a/fs/bcachefs/disk_groups.c b/fs/bcachefs/disk_groups.c new file mode 100644 index 000000000000..86d0b0b2612d --- /dev/null +++ b/fs/bcachefs/disk_groups.c @@ -0,0 +1,346 @@ +#include "bcachefs.h" +#include "disk_groups.h" +#include "super-io.h" + +#include <linux/sort.h> + +static int strcmp_void(const void *l, const void *r) +{ + return strcmp(l, r); +} + +const char *bch2_sb_validate_disk_groups(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_sb_field_members *mi; + struct bch_member *m; + unsigned i, nr_groups, nr_live = 0, len; + char **labels, *l; + const char *err = NULL; + + mi = bch2_sb_get_members(sb); + groups = bch2_sb_get_disk_groups(sb); + nr_groups = disk_groups_nr(groups); + + for (m = mi->members; + m < mi->members + sb->nr_devices; + m++) { + unsigned g; + + if (!BCH_MEMBER_GROUP(m)) + continue; + + g = BCH_MEMBER_GROUP(m) - 1; + + if (g >= nr_groups || + BCH_GROUP_DELETED(&groups->entries[g])) + return "disk has invalid group"; + } + + 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++) { + if (BCH_GROUP_DELETED(g)) + continue; + + len = strnlen(g->label, sizeof(g->label)); + + labels[nr_live++] = l = kmalloc(len + 1, GFP_KERNEL); + if (!l) { + err = "cannot allocate memory"; + goto err; + } + + memcpy(l, g->label, len); + l[len] = '\0'; + } + + sort(labels, nr_live, sizeof(labels[0]), strcmp_void, NULL); + + for (i = 0; i + 1 < nr_live; i++) + if (!strcmp(labels[i], labels[i + 1])) { + err = "duplicate group labels"; + goto err; + } + + err = NULL; +err: + for (i = 0; i < nr_live; i++) + kfree(labels[i]); + kfree(labels); + return err; +} + +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; + + lockdep_assert_held(&c->sb_lock); + + mi = bch2_sb_get_members(c->disk_sb); + groups = bch2_sb_get_disk_groups(c->disk_sb); + 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); + if (!cpu_g) + return -ENOMEM; + + cpu_g->nr = nr_groups; + + for (i = 0; i < nr_groups; i++) { + struct bch_disk_group *src = &groups->entries[i]; + struct bch_disk_group_cpu *dst = &cpu_g->entries[i]; + + dst->deleted = BCH_GROUP_DELETED(src); + } + + for (i = 0; i < c->disk_sb->nr_devices; i++) { + struct bch_member *m = mi->members + i; + struct bch_disk_group_cpu *dst = + &cpu_g->entries[BCH_MEMBER_GROUP(m)]; + + if (!bch2_member_exists(m)) + continue; + + dst = BCH_MEMBER_GROUP(m) + ? &cpu_g->entries[BCH_MEMBER_GROUP(m) - 1] + : NULL; + if (dst) + __set_bit(i, dst->devs.d); + } + + old_g = c->disk_groups; + rcu_assign_pointer(c->disk_groups, cpu_g); + if (old_g) + kfree_rcu(old_g, rcu); + + return 0; +} + +const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned target) +{ + struct target t = target_decode(target); + + switch (t.type) { + 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; + } + case TARGET_GROUP: { + struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups); + + return t.group < g->nr && !g->entries[t.group].deleted + ? &g->entries[t.group].devs + : NULL; + } + default: + BUG(); + } +} + +static int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups, + const char *name) +{ + unsigned i, nr_groups = disk_groups_nr(groups); + unsigned len = strlen(name); + + for (i = 0; i < nr_groups; i++) { + struct bch_disk_group *g = groups->entries + i; + + if (BCH_GROUP_DELETED(g)) + continue; + + if (strnlen(g->label, sizeof(g->label)) == len && + !memcmp(name, g->label, len)) + 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) +{ + struct bch_sb_field_disk_groups *groups; + struct bch_disk_group *g; + struct bch_member *mi; + unsigned i, v, nr_groups; + int ret; + + if (strlen(label) > 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++) + ; + + if (i == nr_groups) { + unsigned u64s = + (sizeof(struct bch_sb_field_disk_groups) + + sizeof(struct bch_disk_group) * (nr_groups + 1)) / + sizeof(u64); + + groups = bch2_fs_sb_resize_disk_groups(c, u64s); + if (!groups) { + mutex_unlock(&c->sb_lock); + return -ENOSPC; + } + + nr_groups = disk_groups_nr(groups); + } + + 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'; + SET_BCH_GROUP_DELETED(g, 0); + SET_BCH_GROUP_DATA_ALLOWED(g, ~0); +write_sb: + mi = &bch2_sb_get_members(c->disk_sb)->members[ca->dev_idx]; + SET_BCH_MEMBER_GROUP(mi, v); + + bch2_write_super(c); + mutex_unlock(&c->sb_lock); + + return 0; +} + +int bch2_opt_target_parse(struct bch_fs *c, const char *buf, u64 *v) +{ + struct bch_dev *ca; + int g; + + if (!strlen(buf) || !strcmp(buf, "none")) { + *v = 0; + return 0; + } + + /* Is it a device? */ + ca = bch2_dev_lookup(c, buf); + if (!IS_ERR(ca)) { + *v = dev_to_target(ca->dev_idx); + percpu_ref_put(&ca->ref); + return 0; + } + + g = bch2_disk_group_find(c, buf); + if (g >= 0) { + *v = group_to_target(g); + return 0; + } + + return -EINVAL; +} + +int bch2_opt_target_print(struct bch_fs *c, char *buf, size_t len, u64 v) +{ + struct target t = target_decode(v); + int ret; + + switch (t.type) { + case TARGET_NULL: + return scnprintf(buf, len, "none"); + 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]; + + ret = scnprintf(buf, len, "/dev/%s", + bdevname(ca->disk_sb.bdev, b)); + percpu_ref_put(&ca->io_ref); + } else if (ca) { + ret = scnprintf(buf, len, "offline device %u", t.dev); + } else { + ret = scnprintf(buf, len, "invalid device %u", t.dev); + } + + 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; + } + default: + BUG(); + } + + return ret; +} diff --git a/fs/bcachefs/disk_groups.h b/fs/bcachefs/disk_groups.h new file mode 100644 index 000000000000..3db5310e211d --- /dev/null +++ b/fs/bcachefs/disk_groups.h @@ -0,0 +1,93 @@ +#ifndef _BCACHEFS_DISK_GROUPS_H +#define _BCACHEFS_DISK_GROUPS_H + +static inline unsigned disk_groups_nr(struct bch_sb_field_disk_groups *groups) +{ + return groups + ? (vstruct_end(&groups->field) - + (void *) &groups->entries[0]) / sizeof(struct bch_disk_group) + : 0; +} + +struct target { + enum { + TARGET_NULL, + TARGET_DEV, + TARGET_GROUP, + } type; + union { + unsigned dev; + unsigned group; + }; +}; + +#define TARGET_DEV_START 1 +#define TARGET_GROUP_START (256 + TARGET_DEV_START) + +static inline u16 dev_to_target(unsigned dev) +{ + return TARGET_DEV_START + dev; +} + +static inline u16 group_to_target(unsigned group) +{ + return TARGET_GROUP_START + group; +} + +static inline struct target target_decode(unsigned target) +{ + if (target >= TARGET_GROUP_START) + return (struct target) { + .type = TARGET_GROUP, + .group = target - TARGET_GROUP_START + }; + + if (target >= TARGET_DEV_START) + return (struct target) { + .type = TARGET_DEV, + .group = target - TARGET_DEV_START + }; + + return (struct target) { .type = TARGET_NULL }; +} + +static inline bool dev_in_target(struct bch_dev *ca, unsigned target) +{ + struct target t = target_decode(target); + + switch (t.type) { + case TARGET_NULL: + return false; + case TARGET_DEV: + return ca->dev_idx == t.dev; + case TARGET_GROUP: + return ca->mi.group && ca->mi.group - 1 == t.group; + default: + BUG(); + } +} + +static inline bool dev_idx_in_target(struct bch_fs *c, unsigned dev, unsigned target) +{ + bool ret; + + rcu_read_lock(); + ret = dev_in_target(rcu_dereference(c->devs[dev]), target); + rcu_read_unlock(); + + return ret; +} + +const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *, unsigned); + +int bch2_opt_target_parse(struct bch_fs *, const char *, u64 *); +int bch2_opt_target_print(struct bch_fs *, char *, size_t, u64); + +int bch2_sb_disk_groups_to_cpu(struct bch_fs *); + +int bch2_dev_group_set(struct bch_fs *, struct bch_dev *, const char *); + +const char *bch2_sb_validate_disk_groups(struct bch_sb *, + struct bch_sb_field *); + +#endif /* _BCACHEFS_DISK_GROUPS_H */ diff --git a/fs/bcachefs/extents.c b/fs/bcachefs/extents.c index 8d9f8a1c9264..435f5b663427 100644 --- a/fs/bcachefs/extents.c +++ b/fs/bcachefs/extents.c @@ -14,10 +14,12 @@ #include "checksum.h" #include "debug.h" #include "dirent.h" +#include "disk_groups.h" #include "error.h" #include "extents.h" #include "inode.h" #include "journal.h" +#include "replicas.h" #include "super.h" #include "super-io.h" #include "util.h" diff --git a/fs/bcachefs/io.c b/fs/bcachefs/io.c index ad51891c3865..308ba4ddbe1b 100644 --- a/fs/bcachefs/io.c +++ b/fs/bcachefs/io.c @@ -20,6 +20,7 @@ #include "journal.h" #include "keylist.h" #include "move.h" +#include "replicas.h" #include "super.h" #include "super-io.h" #include "tier.h" diff --git a/fs/bcachefs/journal.c b/fs/bcachefs/journal.c index 90212fb03e0a..6ed348488c30 100644 --- a/fs/bcachefs/journal.c +++ b/fs/bcachefs/journal.c @@ -19,6 +19,7 @@ #include "io.h" #include "keylist.h" #include "journal.h" +#include "replicas.h" #include "super-io.h" #include "vstructs.h" diff --git a/fs/bcachefs/migrate.c b/fs/bcachefs/migrate.c index 1bc0e7142220..ea519102a228 100644 --- a/fs/bcachefs/migrate.c +++ b/fs/bcachefs/migrate.c @@ -11,6 +11,7 @@ #include "keylist.h" #include "migrate.h" #include "move.h" +#include "replicas.h" #include "super-io.h" static int drop_dev_ptrs(struct bch_fs *c, struct bkey_s_extent e, diff --git a/fs/bcachefs/move.c b/fs/bcachefs/move.c index f22670f0a901..38235d646a94 100644 --- a/fs/bcachefs/move.c +++ b/fs/bcachefs/move.c @@ -6,6 +6,7 @@ #include "inode.h" #include "io.h" #include "move.h" +#include "replicas.h" #include "super-io.h" #include "keylist.h" diff --git a/fs/bcachefs/movinggc.c b/fs/bcachefs/movinggc.c index 9c5dc7206a01..75113d6c6647 100644 --- a/fs/bcachefs/movinggc.c +++ b/fs/bcachefs/movinggc.c @@ -9,6 +9,7 @@ #include "btree_update.h" #include "buckets.h" #include "clock.h" +#include "disk_groups.h" #include "extents.h" #include "eytzinger.h" #include "io.h" diff --git a/fs/bcachefs/opts.c b/fs/bcachefs/opts.c index 326b8ad9caf2..8db8096e5ed4 100644 --- a/fs/bcachefs/opts.c +++ b/fs/bcachefs/opts.c @@ -2,6 +2,7 @@ #include <linux/kernel.h> #include "bcachefs.h" +#include "disk_groups.h" #include "opts.h" #include "super-io.h" #include "util.h" diff --git a/fs/bcachefs/replicas.c b/fs/bcachefs/replicas.c new file mode 100644 index 000000000000..c1cfcbdaa98f --- /dev/null +++ b/fs/bcachefs/replicas.c @@ -0,0 +1,694 @@ + +#include "bcachefs.h" +#include "replicas.h" +#include "super-io.h" + +static int bch2_cpu_replicas_to_sb_replicas(struct bch_fs *, + struct bch_replicas_cpu *); + +/* Replicas tracking - in memory: */ + +#define for_each_cpu_replicas_entry(_r, _i) \ + for (_i = (_r)->entries; \ + (void *) (_i) < (void *) (_r)->entries + (_r)->nr * (_r)->entry_size;\ + _i = (void *) (_i) + (_r)->entry_size) + +static inline struct bch_replicas_cpu_entry * +cpu_replicas_entry(struct bch_replicas_cpu *r, unsigned i) +{ + return (void *) r->entries + r->entry_size * i; +} + +static void bch2_cpu_replicas_sort(struct bch_replicas_cpu *r) +{ + eytzinger0_sort(r->entries, r->nr, r->entry_size, memcmp, NULL); +} + +static inline bool replicas_test_dev(struct bch_replicas_cpu_entry *e, + unsigned dev) +{ + return (e->devs[dev >> 3] & (1 << (dev & 7))) != 0; +} + +static inline void replicas_set_dev(struct bch_replicas_cpu_entry *e, + unsigned dev) +{ + e->devs[dev >> 3] |= 1 << (dev & 7); +} + +static inline unsigned replicas_dev_slots(struct bch_replicas_cpu *r) +{ + return (r->entry_size - + offsetof(struct bch_replicas_cpu_entry, devs)) * 8; +} + +int bch2_cpu_replicas_to_text(struct bch_replicas_cpu *r, + char *buf, size_t size) +{ + char *out = buf, *end = out + size; + struct bch_replicas_cpu_entry *e; + bool first = true; + unsigned i; + + for_each_cpu_replicas_entry(r, e) { + bool first_e = true; + + if (!first) + out += scnprintf(out, end - out, " "); + first = false; + + out += scnprintf(out, end - out, "%u: [", e->data_type); + + for (i = 0; i < replicas_dev_slots(r); i++) + if (replicas_test_dev(e, i)) { + if (!first_e) + out += scnprintf(out, end - out, " "); + first_e = false; + out += scnprintf(out, end - out, "%u", i); + } + out += scnprintf(out, end - out, "]"); + } + + return out - buf; +} + +static inline unsigned bkey_to_replicas(struct bkey_s_c_extent e, + enum bch_data_type data_type, + struct bch_replicas_cpu_entry *r, + unsigned *max_dev) +{ + const struct bch_extent_ptr *ptr; + unsigned nr = 0; + + BUG_ON(!data_type || + data_type == BCH_DATA_SB || + data_type >= BCH_DATA_NR); + + memset(r, 0, sizeof(*r)); + r->data_type = data_type; + + *max_dev = 0; + + extent_for_each_ptr(e, ptr) + if (!ptr->cached) { + *max_dev = max_t(unsigned, *max_dev, ptr->dev); + replicas_set_dev(r, ptr->dev); + nr++; + } + return nr; +} + +static inline void devlist_to_replicas(struct bch_devs_list devs, + enum bch_data_type data_type, + struct bch_replicas_cpu_entry *r, + unsigned *max_dev) +{ + unsigned i; + + BUG_ON(!data_type || + data_type == BCH_DATA_SB || + data_type >= BCH_DATA_NR); + + memset(r, 0, sizeof(*r)); + r->data_type = data_type; + + *max_dev = 0; + + for (i = 0; i < devs.nr; i++) { + *max_dev = max_t(unsigned, *max_dev, devs.devs[i]); + replicas_set_dev(r, devs.devs[i]); + } +} + +static struct bch_replicas_cpu * +cpu_replicas_add_entry(struct bch_replicas_cpu *old, + struct bch_replicas_cpu_entry new_entry, + unsigned max_dev) +{ + struct bch_replicas_cpu *new; + unsigned i, nr, entry_size; + + entry_size = offsetof(struct bch_replicas_cpu_entry, devs) + + DIV_ROUND_UP(max_dev + 1, 8); + entry_size = max(entry_size, old->entry_size); + nr = old->nr + 1; + + new = kzalloc(sizeof(struct bch_replicas_cpu) + + nr * entry_size, GFP_NOIO); + if (!new) + return NULL; + + new->nr = nr; + new->entry_size = entry_size; + + for (i = 0; i < old->nr; i++) + memcpy(cpu_replicas_entry(new, i), + cpu_replicas_entry(old, i), + min(new->entry_size, old->entry_size)); + + memcpy(cpu_replicas_entry(new, old->nr), + &new_entry, + new->entry_size); + + bch2_cpu_replicas_sort(new); + return new; +} + +static bool replicas_has_entry(struct bch_replicas_cpu *r, + struct bch_replicas_cpu_entry search, + unsigned max_dev) +{ + return max_dev < replicas_dev_slots(r) && + eytzinger0_find(r->entries, r->nr, + r->entry_size, + memcmp, &search) < r->nr; +} + +noinline +static int bch2_mark_replicas_slowpath(struct bch_fs *c, + struct bch_replicas_cpu_entry new_entry, + unsigned max_dev) +{ + struct bch_replicas_cpu *old_gc, *new_gc = NULL, *old_r, *new_r = NULL; + int ret = -ENOMEM; + + mutex_lock(&c->sb_lock); + + old_gc = rcu_dereference_protected(c->replicas_gc, + lockdep_is_held(&c->sb_lock)); + if (old_gc && !replicas_has_entry(old_gc, new_entry, max_dev)) { + new_gc = cpu_replicas_add_entry(old_gc, new_entry, max_dev); + if (!new_gc) + goto err; + } + + old_r = rcu_dereference_protected(c->replicas, + lockdep_is_held(&c->sb_lock)); + if (!replicas_has_entry(old_r, new_entry, max_dev)) { + new_r = cpu_replicas_add_entry(old_r, new_entry, max_dev); + if (!new_r) + goto err; + + ret = bch2_cpu_replicas_to_sb_replicas(c, new_r); + if (ret) + goto err; + } + + /* allocations done, now commit: */ + + if (new_r) + bch2_write_super(c); + + /* don't update in memory replicas until changes are persistent */ + + if (new_gc) { + rcu_assign_pointer(c->replicas_gc, new_gc); + kfree_rcu(old_gc, rcu); + } + + if (new_r) { + rcu_assign_pointer(c->replicas, new_r); + kfree_rcu(old_r, rcu); + } + + mutex_unlock(&c->sb_lock); + return 0; +err: + mutex_unlock(&c->sb_lock); + if (new_gc) + kfree(new_gc); + if (new_r) + kfree(new_r); + return ret; +} + +int bch2_mark_replicas(struct bch_fs *c, + enum bch_data_type data_type, + struct bch_devs_list devs) +{ + struct bch_replicas_cpu_entry search; + struct bch_replicas_cpu *r, *gc_r; + unsigned max_dev; + bool marked; + + if (!devs.nr) + return 0; + + BUG_ON(devs.nr >= BCH_REPLICAS_MAX); + + devlist_to_replicas(devs, data_type, &search, &max_dev); + + rcu_read_lock(); + r = rcu_dereference(c->replicas); + gc_r = rcu_dereference(c->replicas_gc); + marked = replicas_has_entry(r, search, max_dev) && + (!likely(gc_r) || replicas_has_entry(gc_r, search, max_dev)); + rcu_read_unlock(); + + return likely(marked) ? 0 + : bch2_mark_replicas_slowpath(c, search, max_dev); +} + +int bch2_mark_bkey_replicas(struct bch_fs *c, + enum bch_data_type data_type, + struct bkey_s_c k) +{ + struct bch_devs_list cached = bch2_bkey_cached_devs(k); + unsigned i; + int ret; + + for (i = 0; i < cached.nr; i++) + if ((ret = bch2_mark_replicas(c, BCH_DATA_CACHED, + bch2_dev_list_single(cached.devs[i])))) + return ret; + + return bch2_mark_replicas(c, data_type, bch2_bkey_dirty_devs(k)); +} + +int bch2_replicas_gc_end(struct bch_fs *c, int err) +{ + struct bch_replicas_cpu *new_r, *old_r; + int ret = 0; + + lockdep_assert_held(&c->replicas_gc_lock); + + mutex_lock(&c->sb_lock); + + new_r = rcu_dereference_protected(c->replicas_gc, + lockdep_is_held(&c->sb_lock)); + + if (err) { + rcu_assign_pointer(c->replicas_gc, NULL); + kfree_rcu(new_r, rcu); + goto err; + } + + if (bch2_cpu_replicas_to_sb_replicas(c, new_r)) { + ret = -ENOSPC; + goto err; + } + + old_r = rcu_dereference_protected(c->replicas, + lockdep_is_held(&c->sb_lock)); + + rcu_assign_pointer(c->replicas, new_r); + rcu_assign_pointer(c->replicas_gc, NULL); + kfree_rcu(old_r, rcu); + + bch2_write_super(c); +err: + mutex_unlock(&c->sb_lock); + return ret; +} + +int bch2_replicas_gc_start(struct bch_fs *c, unsigned typemask) +{ + struct bch_replicas_cpu *dst, *src; + struct bch_replicas_cpu_entry *e; + + lockdep_assert_held(&c->replicas_gc_lock); + + mutex_lock(&c->sb_lock); + BUG_ON(c->replicas_gc); + + src = rcu_dereference_protected(c->replicas, + lockdep_is_held(&c->sb_lock)); + + dst = kzalloc(sizeof(struct bch_replicas_cpu) + + src->nr * src->entry_size, GFP_NOIO); + if (!dst) { + mutex_unlock(&c->sb_lock); + return -ENOMEM; + } + + dst->nr = 0; + dst->entry_size = src->entry_size; + + for_each_cpu_replicas_entry(src, e) + if (!((1 << e->data_type) & typemask)) + memcpy(cpu_replicas_entry(dst, dst->nr++), + e, dst->entry_size); + + bch2_cpu_replicas_sort(dst); + + rcu_assign_pointer(c->replicas_gc, dst); + mutex_unlock(&c->sb_lock); + + return 0; +} + +/* Replicas tracking - superblock: */ + +static void bch2_sb_replicas_nr_entries(struct bch_sb_field_replicas *r, + unsigned *nr, + unsigned *bytes, + unsigned *max_dev) +{ + struct bch_replicas_entry *i; + unsigned j; + + *nr = 0; + *bytes = sizeof(*r); + *max_dev = 0; + + if (!r) + return; + + for_each_replicas_entry(r, i) { + for (j = 0; j < i->nr; j++) + *max_dev = max_t(unsigned, *max_dev, i->devs[j]); + (*nr)++; + } + + *bytes = (void *) i - (void *) r; +} + +static struct bch_replicas_cpu * +__bch2_sb_replicas_to_cpu_replicas(struct bch_sb_field_replicas *sb_r) +{ + struct bch_replicas_cpu *cpu_r; + unsigned i, nr, bytes, max_dev, entry_size; + + bch2_sb_replicas_nr_entries(sb_r, &nr, &bytes, &max_dev); + + entry_size = offsetof(struct bch_replicas_cpu_entry, devs) + + DIV_ROUND_UP(max_dev + 1, 8); + + cpu_r = kzalloc(sizeof(struct bch_replicas_cpu) + + nr * entry_size, GFP_NOIO); + if (!cpu_r) + return NULL; + + cpu_r->nr = nr; + cpu_r->entry_size = entry_size; + + if (nr) { + struct bch_replicas_cpu_entry *dst = + cpu_replicas_entry(cpu_r, 0); + struct bch_replicas_entry *src = sb_r->entries; + + while (dst < cpu_replicas_entry(cpu_r, nr)) { + dst->data_type = src->data_type; + for (i = 0; i < src->nr; i++) + replicas_set_dev(dst, src->devs[i]); + + src = replicas_entry_next(src); + dst = (void *) dst + entry_size; + } + } + + bch2_cpu_replicas_sort(cpu_r); + return cpu_r; +} + +int bch2_sb_replicas_to_cpu_replicas(struct bch_fs *c) +{ + struct bch_sb_field_replicas *sb_r; + struct bch_replicas_cpu *cpu_r, *old_r; + + sb_r = bch2_sb_get_replicas(c->disk_sb); + cpu_r = __bch2_sb_replicas_to_cpu_replicas(sb_r); + if (!cpu_r) + return -ENOMEM; + + old_r = rcu_dereference_check(c->replicas, lockdep_is_held(&c->sb_lock)); + rcu_assign_pointer(c->replicas, cpu_r); + if (old_r) + kfree_rcu(old_r, rcu); + + return 0; +} + +static int bch2_cpu_replicas_to_sb_replicas(struct bch_fs *c, + struct bch_replicas_cpu *r) +{ + struct bch_sb_field_replicas *sb_r; + struct bch_replicas_entry *sb_e; + struct bch_replicas_cpu_entry *e; + size_t i, bytes; + + bytes = sizeof(struct bch_sb_field_replicas); + + for_each_cpu_replicas_entry(r, e) { + bytes += sizeof(struct bch_replicas_entry); + for (i = 0; i < r->entry_size - 1; i++) + bytes += hweight8(e->devs[i]); + } + + sb_r = bch2_fs_sb_resize_replicas(c, + DIV_ROUND_UP(sizeof(*sb_r) + bytes, sizeof(u64))); + if (!sb_r) + return -ENOSPC; + + memset(&sb_r->entries, 0, + vstruct_end(&sb_r->field) - + (void *) &sb_r->entries); + + sb_e = sb_r->entries; + for_each_cpu_replicas_entry(r, e) { + sb_e->data_type = e->data_type; + + for (i = 0; i < replicas_dev_slots(r); i++) + if (replicas_test_dev(e, i)) + sb_e->devs[sb_e->nr++] = i; + + sb_e = replicas_entry_next(sb_e); + + BUG_ON((void *) sb_e > vstruct_end(&sb_r->field)); + } + + return 0; +} + +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); + struct bch_replicas_cpu *cpu_r = NULL; + struct bch_replicas_entry *e; + const char *err; + unsigned i; + + for_each_replicas_entry(sb_r, e) { + err = "invalid replicas entry: invalid data type"; + if (e->data_type >= BCH_DATA_NR) + goto err; + + err = "invalid replicas entry: no devices"; + if (!e->nr) + goto err; + + err = "invalid replicas entry: too many devices"; + if (e->nr >= BCH_REPLICAS_MAX) + goto err; + + err = "invalid replicas entry: invalid device"; + for (i = 0; i < e->nr; i++) + if (!bch2_dev_exists(sb, mi, e->devs[i])) + goto err; + } + + err = "cannot allocate memory"; + cpu_r = __bch2_sb_replicas_to_cpu_replicas(sb_r); + if (!cpu_r) + goto err; + + sort_cmp_size(cpu_r->entries, + cpu_r->nr, + cpu_r->entry_size, + memcmp, NULL); + + for (i = 0; i + 1 < cpu_r->nr; i++) { + struct bch_replicas_cpu_entry *l = + cpu_replicas_entry(cpu_r, i); + struct bch_replicas_cpu_entry *r = + cpu_replicas_entry(cpu_r, i + 1); + + BUG_ON(memcmp(l, r, cpu_r->entry_size) > 0); + + err = "duplicate replicas entry"; + if (!memcmp(l, r, cpu_r->entry_size)) + goto err; + } + + err = NULL; +err: + kfree(cpu_r); + return err; +} + +int bch2_sb_replicas_to_text(struct bch_sb_field_replicas *r, char *buf, size_t size) +{ + char *out = buf, *end = out + size; + struct bch_replicas_entry *e; + bool first = true; + unsigned i; + + if (!r) { + out += scnprintf(out, end - out, "(no replicas section found)"); + return out - buf; + } + + for_each_replicas_entry(r, e) { + if (!first) + out += scnprintf(out, end - out, " "); + first = false; + + out += scnprintf(out, end - out, "%u: [", e->data_type); + + for (i = 0; i < e->nr; i++) + out += scnprintf(out, end - out, + i ? " %u" : "%u", e->devs[i]); + out += scnprintf(out, end - out, "]"); + } + + return out - buf; +} + +/* Query replicas: */ + +bool bch2_replicas_marked(struct bch_fs *c, + enum bch_data_type data_type, + struct bch_devs_list devs) +{ + struct bch_replicas_cpu_entry search; + unsigned max_dev; + bool ret; + + if (!devs.nr) + return true; + + devlist_to_replicas(devs, data_type, &search, &max_dev); + + rcu_read_lock(); + ret = replicas_has_entry(rcu_dereference(c->replicas), + search, max_dev); + rcu_read_unlock(); + + return ret; +} + +bool bch2_bkey_replicas_marked(struct bch_fs *c, + enum bch_data_type data_type, + struct bkey_s_c k) +{ + struct bch_devs_list cached = bch2_bkey_cached_devs(k); + unsigned i; + + for (i = 0; i < cached.nr; i++) + if (!bch2_replicas_marked(c, BCH_DATA_CACHED, + bch2_dev_list_single(cached.devs[i]))) + return false; + + return bch2_replicas_marked(c, data_type, bch2_bkey_dirty_devs(k)); +} + +struct replicas_status __bch2_replicas_status(struct bch_fs *c, + struct bch_devs_mask online_devs) +{ + struct bch_sb_field_members *mi; + struct bch_replicas_cpu_entry *e; + struct bch_replicas_cpu *r; + unsigned i, dev, dev_slots, nr_online, nr_offline; + struct replicas_status ret; + + memset(&ret, 0, sizeof(ret)); + + for (i = 0; i < ARRAY_SIZE(ret.replicas); i++) + ret.replicas[i].nr_online = UINT_MAX; + + mi = bch2_sb_get_members(c->disk_sb); + rcu_read_lock(); + + r = rcu_dereference(c->replicas); + dev_slots = replicas_dev_slots(r); + + for_each_cpu_replicas_entry(r, e) { + if (e->data_type >= ARRAY_SIZE(ret.replicas)) + panic("e %p data_type %u\n", e, e->data_type); + + nr_online = nr_offline = 0; + + for (dev = 0; dev < dev_slots; dev++) { + if (!replicas_test_dev(e, dev)) + continue; + + BUG_ON(!bch2_dev_exists(c->disk_sb, mi, dev)); + + if (test_bit(dev, online_devs.d)) + nr_online++; + else + nr_offline++; + } + + ret.replicas[e->data_type].nr_online = + min(ret.replicas[e->data_type].nr_online, + nr_online); + + ret.replicas[e->data_type].nr_offline = + max(ret.replicas[e->data_type].nr_offline, + nr_offline); + } + + rcu_read_unlock(); + + return ret; +} + +struct replicas_status bch2_replicas_status(struct bch_fs *c) +{ + return __bch2_replicas_status(c, bch2_online_devs(c)); +} + +static bool have_enough_devs(struct replicas_status s, + enum bch_data_type type, + bool force_if_degraded, + bool force_if_lost) +{ + return (!s.replicas[type].nr_offline || force_if_degraded) && + (s.replicas[type].nr_online || force_if_lost); +} + +bool bch2_have_enough_devs(struct replicas_status s, unsigned flags) +{ + return (have_enough_devs(s, BCH_DATA_JOURNAL, + flags & BCH_FORCE_IF_METADATA_DEGRADED, + flags & BCH_FORCE_IF_METADATA_LOST) && + have_enough_devs(s, BCH_DATA_BTREE, + flags & BCH_FORCE_IF_METADATA_DEGRADED, + flags & BCH_FORCE_IF_METADATA_LOST) && + have_enough_devs(s, BCH_DATA_USER, + flags & BCH_FORCE_IF_DATA_DEGRADED, + flags & BCH_FORCE_IF_DATA_LOST)); +} + +unsigned bch2_replicas_online(struct bch_fs *c, bool meta) +{ + struct replicas_status s = bch2_replicas_status(c); + + return meta + ? min(s.replicas[BCH_DATA_JOURNAL].nr_online, + s.replicas[BCH_DATA_BTREE].nr_online) + : s.replicas[BCH_DATA_USER].nr_online; +} + +unsigned bch2_dev_has_data(struct bch_fs *c, struct bch_dev *ca) +{ + struct bch_replicas_cpu_entry *e; + struct bch_replicas_cpu *r; + unsigned ret = 0; + + rcu_read_lock(); + r = rcu_dereference(c->replicas); + + if (ca->dev_idx >= replicas_dev_slots(r)) + goto out; + + for_each_cpu_replicas_entry(r, e) + if (replicas_test_dev(e, ca->dev_idx)) + ret |= 1 << e->data_type; +out: + rcu_read_unlock(); + + return ret; +} diff --git a/fs/bcachefs/replicas.h b/fs/bcachefs/replicas.h new file mode 100644 index 000000000000..4006c63e652c --- /dev/null +++ b/fs/bcachefs/replicas.h @@ -0,0 +1,51 @@ +#ifndef _BCACHEFS_REPLICAS_H +#define _BCACHEFS_REPLICAS_H + +bool bch2_replicas_marked(struct bch_fs *, enum bch_data_type, + struct bch_devs_list); +bool bch2_bkey_replicas_marked(struct bch_fs *, enum bch_data_type, + struct bkey_s_c); +int bch2_mark_replicas(struct bch_fs *, enum bch_data_type, + struct bch_devs_list); +int bch2_mark_bkey_replicas(struct bch_fs *, enum bch_data_type, + struct bkey_s_c); + +int bch2_cpu_replicas_to_text(struct bch_replicas_cpu *, char *, size_t); +int bch2_sb_replicas_to_text(struct bch_sb_field_replicas *, char *, size_t); + +struct replicas_status { + struct { + unsigned nr_online; + unsigned nr_offline; + } replicas[BCH_DATA_NR]; +}; + +struct replicas_status __bch2_replicas_status(struct bch_fs *, + struct bch_devs_mask); +struct replicas_status bch2_replicas_status(struct bch_fs *); +bool bch2_have_enough_devs(struct replicas_status, unsigned); + +unsigned bch2_replicas_online(struct bch_fs *, bool); +unsigned bch2_dev_has_data(struct bch_fs *, struct bch_dev *); + +int bch2_replicas_gc_end(struct bch_fs *, int); +int bch2_replicas_gc_start(struct bch_fs *, unsigned); + +/* iterate over superblock replicas - used by userspace tools: */ + +static inline struct bch_replicas_entry * +replicas_entry_next(struct bch_replicas_entry *i) +{ + return (void *) i + offsetof(struct bch_replicas_entry, devs) + i->nr; +} + +#define for_each_replicas_entry(_r, _i) \ + for (_i = (_r)->entries; \ + (void *) (_i) < vstruct_end(&(_r)->field) && (_i)->data_type;\ + (_i) = replicas_entry_next(_i)) + +int bch2_sb_replicas_to_cpu_replicas(struct bch_fs *); + +const char *bch2_sb_validate_replicas(struct bch_sb *, struct bch_sb_field *); + +#endif /* _BCACHEFS_REPLICAS_H */ diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c index 69101f3a68a5..a8ba6af03e39 100644 --- a/fs/bcachefs/super-io.c +++ b/fs/bcachefs/super-io.c @@ -1,8 +1,10 @@ #include "bcachefs.h" #include "checksum.h" +#include "disk_groups.h" #include "error.h" #include "io.h" +#include "replicas.h" #include "super-io.h" #include "super.h" #include "vstructs.h" @@ -10,13 +12,17 @@ #include <linux/backing-dev.h> #include <linux/sort.h> -static int bch2_sb_replicas_to_cpu_replicas(struct bch_fs *); -static int bch2_cpu_replicas_to_sb_replicas(struct bch_fs *, - struct bch_replicas_cpu *); -static int bch2_sb_disk_groups_to_cpu(struct bch_fs *); - /* 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() @@ -24,11 +30,6 @@ const char * const bch2_sb_fields[] = { NULL }; -#define x(f, nr) \ -static const char *bch2_sb_validate_##f(struct bch_sb *, struct bch_sb_field *); - BCH_SB_FIELDS() -#undef x - struct bch_sb_field_ops { const char * (*validate)(struct bch_sb *, struct bch_sb_field *); }; @@ -896,696 +897,6 @@ static const char *bch2_sb_validate_crypt(struct bch_sb *sb, return NULL; } -/* BCH_SB_FIELD_replicas: */ - -/* Replicas tracking - in memory: */ - -#define for_each_cpu_replicas_entry(_r, _i) \ - for (_i = (_r)->entries; \ - (void *) (_i) < (void *) (_r)->entries + (_r)->nr * (_r)->entry_size;\ - _i = (void *) (_i) + (_r)->entry_size) - -static inline struct bch_replicas_cpu_entry * -cpu_replicas_entry(struct bch_replicas_cpu *r, unsigned i) -{ - return (void *) r->entries + r->entry_size * i; -} - -static void bch2_cpu_replicas_sort(struct bch_replicas_cpu *r) -{ - eytzinger0_sort(r->entries, r->nr, r->entry_size, memcmp, NULL); -} - -static inline bool replicas_test_dev(struct bch_replicas_cpu_entry *e, - unsigned dev) -{ - return (e->devs[dev >> 3] & (1 << (dev & 7))) != 0; -} - -static inline void replicas_set_dev(struct bch_replicas_cpu_entry *e, - unsigned dev) -{ - e->devs[dev >> 3] |= 1 << (dev & 7); -} - -static inline unsigned replicas_dev_slots(struct bch_replicas_cpu *r) -{ - return (r->entry_size - - offsetof(struct bch_replicas_cpu_entry, devs)) * 8; -} - -int bch2_cpu_replicas_to_text(struct bch_replicas_cpu *r, - char *buf, size_t size) -{ - char *out = buf, *end = out + size; - struct bch_replicas_cpu_entry *e; - bool first = true; - unsigned i; - - for_each_cpu_replicas_entry(r, e) { - bool first_e = true; - - if (!first) - out += scnprintf(out, end - out, " "); - first = false; - - out += scnprintf(out, end - out, "%u: [", e->data_type); - - for (i = 0; i < replicas_dev_slots(r); i++) - if (replicas_test_dev(e, i)) { - if (!first_e) - out += scnprintf(out, end - out, " "); - first_e = false; - out += scnprintf(out, end - out, "%u", i); - } - out += scnprintf(out, end - out, "]"); - } - - return out - buf; -} - -static inline unsigned bkey_to_replicas(struct bkey_s_c_extent e, - enum bch_data_type data_type, - struct bch_replicas_cpu_entry *r, - unsigned *max_dev) -{ - const struct bch_extent_ptr *ptr; - unsigned nr = 0; - - BUG_ON(!data_type || - data_type == BCH_DATA_SB || - data_type >= BCH_DATA_NR); - - memset(r, 0, sizeof(*r)); - r->data_type = data_type; - - *max_dev = 0; - - extent_for_each_ptr(e, ptr) - if (!ptr->cached) { - *max_dev = max_t(unsigned, *max_dev, ptr->dev); - replicas_set_dev(r, ptr->dev); - nr++; - } - return nr; -} - -static inline void devlist_to_replicas(struct bch_devs_list devs, - enum bch_data_type data_type, - struct bch_replicas_cpu_entry *r, - unsigned *max_dev) -{ - unsigned i; - - BUG_ON(!data_type || - data_type == BCH_DATA_SB || - data_type >= BCH_DATA_NR); - - memset(r, 0, sizeof(*r)); - r->data_type = data_type; - - *max_dev = 0; - - for (i = 0; i < devs.nr; i++) { - *max_dev = max_t(unsigned, *max_dev, devs.devs[i]); - replicas_set_dev(r, devs.devs[i]); - } -} - -static struct bch_replicas_cpu * -cpu_replicas_add_entry(struct bch_replicas_cpu *old, - struct bch_replicas_cpu_entry new_entry, - unsigned max_dev) -{ - struct bch_replicas_cpu *new; - unsigned i, nr, entry_size; - - entry_size = offsetof(struct bch_replicas_cpu_entry, devs) + - DIV_ROUND_UP(max_dev + 1, 8); - entry_size = max(entry_size, old->entry_size); - nr = old->nr + 1; - - new = kzalloc(sizeof(struct bch_replicas_cpu) + - nr * entry_size, GFP_NOIO); - if (!new) - return NULL; - - new->nr = nr; - new->entry_size = entry_size; - - for (i = 0; i < old->nr; i++) - memcpy(cpu_replicas_entry(new, i), - cpu_replicas_entry(old, i), - min(new->entry_size, old->entry_size)); - - memcpy(cpu_replicas_entry(new, old->nr), - &new_entry, - new->entry_size); - - bch2_cpu_replicas_sort(new); - return new; -} - -static bool replicas_has_entry(struct bch_replicas_cpu *r, - struct bch_replicas_cpu_entry search, - unsigned max_dev) -{ - return max_dev < replicas_dev_slots(r) && - eytzinger0_find(r->entries, r->nr, - r->entry_size, - memcmp, &search) < r->nr; -} - -noinline -static int bch2_mark_replicas_slowpath(struct bch_fs *c, - struct bch_replicas_cpu_entry new_entry, - unsigned max_dev) -{ - struct bch_replicas_cpu *old_gc, *new_gc = NULL, *old_r, *new_r = NULL; - int ret = -ENOMEM; - - mutex_lock(&c->sb_lock); - - old_gc = rcu_dereference_protected(c->replicas_gc, - lockdep_is_held(&c->sb_lock)); - if (old_gc && !replicas_has_entry(old_gc, new_entry, max_dev)) { - new_gc = cpu_replicas_add_entry(old_gc, new_entry, max_dev); - if (!new_gc) - goto err; - } - - old_r = rcu_dereference_protected(c->replicas, - lockdep_is_held(&c->sb_lock)); - if (!replicas_has_entry(old_r, new_entry, max_dev)) { - new_r = cpu_replicas_add_entry(old_r, new_entry, max_dev); - if (!new_r) - goto err; - - ret = bch2_cpu_replicas_to_sb_replicas(c, new_r); - if (ret) - goto err; - } - - /* allocations done, now commit: */ - - if (new_r) - bch2_write_super(c); - - /* don't update in memory replicas until changes are persistent */ - - if (new_gc) { - rcu_assign_pointer(c->replicas_gc, new_gc); - kfree_rcu(old_gc, rcu); - } - - if (new_r) { - rcu_assign_pointer(c->replicas, new_r); - kfree_rcu(old_r, rcu); - } - - mutex_unlock(&c->sb_lock); - return 0; -err: - mutex_unlock(&c->sb_lock); - if (new_gc) - kfree(new_gc); - if (new_r) - kfree(new_r); - return ret; -} - -int bch2_mark_replicas(struct bch_fs *c, - enum bch_data_type data_type, - struct bch_devs_list devs) -{ - struct bch_replicas_cpu_entry search; - struct bch_replicas_cpu *r, *gc_r; - unsigned max_dev; - bool marked; - - if (!devs.nr) - return 0; - - BUG_ON(devs.nr >= BCH_REPLICAS_MAX); - - devlist_to_replicas(devs, data_type, &search, &max_dev); - - rcu_read_lock(); - r = rcu_dereference(c->replicas); - gc_r = rcu_dereference(c->replicas_gc); - marked = replicas_has_entry(r, search, max_dev) && - (!likely(gc_r) || replicas_has_entry(gc_r, search, max_dev)); - rcu_read_unlock(); - - return likely(marked) ? 0 - : bch2_mark_replicas_slowpath(c, search, max_dev); -} - -int bch2_mark_bkey_replicas(struct bch_fs *c, - enum bch_data_type data_type, - struct bkey_s_c k) -{ - struct bch_devs_list cached = bch2_bkey_cached_devs(k); - unsigned i; - int ret; - - for (i = 0; i < cached.nr; i++) - if ((ret = bch2_mark_replicas(c, BCH_DATA_CACHED, - bch2_dev_list_single(cached.devs[i])))) - return ret; - - return bch2_mark_replicas(c, data_type, bch2_bkey_dirty_devs(k)); -} - -int bch2_replicas_gc_end(struct bch_fs *c, int err) -{ - struct bch_replicas_cpu *new_r, *old_r; - int ret = 0; - - lockdep_assert_held(&c->replicas_gc_lock); - - mutex_lock(&c->sb_lock); - - new_r = rcu_dereference_protected(c->replicas_gc, - lockdep_is_held(&c->sb_lock)); - - if (err) { - rcu_assign_pointer(c->replicas_gc, NULL); - kfree_rcu(new_r, rcu); - goto err; - } - - if (bch2_cpu_replicas_to_sb_replicas(c, new_r)) { - ret = -ENOSPC; - goto err; - } - - old_r = rcu_dereference_protected(c->replicas, - lockdep_is_held(&c->sb_lock)); - - rcu_assign_pointer(c->replicas, new_r); - rcu_assign_pointer(c->replicas_gc, NULL); - kfree_rcu(old_r, rcu); - - bch2_write_super(c); -err: - mutex_unlock(&c->sb_lock); - return ret; -} - -int bch2_replicas_gc_start(struct bch_fs *c, unsigned typemask) -{ - struct bch_replicas_cpu *dst, *src; - struct bch_replicas_cpu_entry *e; - - lockdep_assert_held(&c->replicas_gc_lock); - - mutex_lock(&c->sb_lock); - BUG_ON(c->replicas_gc); - - src = rcu_dereference_protected(c->replicas, - lockdep_is_held(&c->sb_lock)); - - dst = kzalloc(sizeof(struct bch_replicas_cpu) + - src->nr * src->entry_size, GFP_NOIO); - if (!dst) { - mutex_unlock(&c->sb_lock); - return -ENOMEM; - } - - dst->nr = 0; - dst->entry_size = src->entry_size; - - for_each_cpu_replicas_entry(src, e) - if (!((1 << e->data_type) & typemask)) - memcpy(cpu_replicas_entry(dst, dst->nr++), - e, dst->entry_size); - - bch2_cpu_replicas_sort(dst); - - rcu_assign_pointer(c->replicas_gc, dst); - mutex_unlock(&c->sb_lock); - - return 0; -} - -/* Replicas tracking - superblock: */ - -static void bch2_sb_replicas_nr_entries(struct bch_sb_field_replicas *r, - unsigned *nr, - unsigned *bytes, - unsigned *max_dev) -{ - struct bch_replicas_entry *i; - unsigned j; - - *nr = 0; - *bytes = sizeof(*r); - *max_dev = 0; - - if (!r) - return; - - for_each_replicas_entry(r, i) { - for (j = 0; j < i->nr; j++) - *max_dev = max_t(unsigned, *max_dev, i->devs[j]); - (*nr)++; - } - - *bytes = (void *) i - (void *) r; -} - -static struct bch_replicas_cpu * -__bch2_sb_replicas_to_cpu_replicas(struct bch_sb_field_replicas *sb_r) -{ - struct bch_replicas_cpu *cpu_r; - unsigned i, nr, bytes, max_dev, entry_size; - - bch2_sb_replicas_nr_entries(sb_r, &nr, &bytes, &max_dev); - - entry_size = offsetof(struct bch_replicas_cpu_entry, devs) + - DIV_ROUND_UP(max_dev + 1, 8); - - cpu_r = kzalloc(sizeof(struct bch_replicas_cpu) + - nr * entry_size, GFP_NOIO); - if (!cpu_r) - return NULL; - - cpu_r->nr = nr; - cpu_r->entry_size = entry_size; - - if (nr) { - struct bch_replicas_cpu_entry *dst = - cpu_replicas_entry(cpu_r, 0); - struct bch_replicas_entry *src = sb_r->entries; - - while (dst < cpu_replicas_entry(cpu_r, nr)) { - dst->data_type = src->data_type; - for (i = 0; i < src->nr; i++) - replicas_set_dev(dst, src->devs[i]); - - src = replicas_entry_next(src); - dst = (void *) dst + entry_size; - } - } - - bch2_cpu_replicas_sort(cpu_r); - return cpu_r; -} - -static int bch2_sb_replicas_to_cpu_replicas(struct bch_fs *c) -{ - struct bch_sb_field_replicas *sb_r; - struct bch_replicas_cpu *cpu_r, *old_r; - - sb_r = bch2_sb_get_replicas(c->disk_sb); - cpu_r = __bch2_sb_replicas_to_cpu_replicas(sb_r); - if (!cpu_r) - return -ENOMEM; - - old_r = rcu_dereference_check(c->replicas, lockdep_is_held(&c->sb_lock)); - rcu_assign_pointer(c->replicas, cpu_r); - if (old_r) - kfree_rcu(old_r, rcu); - - return 0; -} - -static int bch2_cpu_replicas_to_sb_replicas(struct bch_fs *c, - struct bch_replicas_cpu *r) -{ - struct bch_sb_field_replicas *sb_r; - struct bch_replicas_entry *sb_e; - struct bch_replicas_cpu_entry *e; - size_t i, bytes; - - bytes = sizeof(struct bch_sb_field_replicas); - - for_each_cpu_replicas_entry(r, e) { - bytes += sizeof(struct bch_replicas_entry); - for (i = 0; i < r->entry_size - 1; i++) - bytes += hweight8(e->devs[i]); - } - - sb_r = bch2_fs_sb_resize_replicas(c, - DIV_ROUND_UP(sizeof(*sb_r) + bytes, sizeof(u64))); - if (!sb_r) - return -ENOSPC; - - memset(&sb_r->entries, 0, - vstruct_end(&sb_r->field) - - (void *) &sb_r->entries); - - sb_e = sb_r->entries; - for_each_cpu_replicas_entry(r, e) { - sb_e->data_type = e->data_type; - - for (i = 0; i < replicas_dev_slots(r); i++) - if (replicas_test_dev(e, i)) - sb_e->devs[sb_e->nr++] = i; - - sb_e = replicas_entry_next(sb_e); - - BUG_ON((void *) sb_e > vstruct_end(&sb_r->field)); - } - - return 0; -} - -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); - struct bch_replicas_cpu *cpu_r = NULL; - struct bch_replicas_entry *e; - const char *err; - unsigned i; - - for_each_replicas_entry(sb_r, e) { - err = "invalid replicas entry: invalid data type"; - if (e->data_type >= BCH_DATA_NR) - goto err; - - err = "invalid replicas entry: no devices"; - if (!e->nr) - goto err; - - err = "invalid replicas entry: too many devices"; - if (e->nr >= BCH_REPLICAS_MAX) - goto err; - - err = "invalid replicas entry: invalid device"; - for (i = 0; i < e->nr; i++) - if (!bch2_dev_exists(sb, mi, e->devs[i])) - goto err; - } - - err = "cannot allocate memory"; - cpu_r = __bch2_sb_replicas_to_cpu_replicas(sb_r); - if (!cpu_r) - goto err; - - sort_cmp_size(cpu_r->entries, - cpu_r->nr, - cpu_r->entry_size, - memcmp, NULL); - - for (i = 0; i + 1 < cpu_r->nr; i++) { - struct bch_replicas_cpu_entry *l = - cpu_replicas_entry(cpu_r, i); - struct bch_replicas_cpu_entry *r = - cpu_replicas_entry(cpu_r, i + 1); - - BUG_ON(memcmp(l, r, cpu_r->entry_size) > 0); - - err = "duplicate replicas entry"; - if (!memcmp(l, r, cpu_r->entry_size)) - goto err; - } - - err = NULL; -err: - kfree(cpu_r); - return err; -} - -int bch2_sb_replicas_to_text(struct bch_sb_field_replicas *r, char *buf, size_t size) -{ - char *out = buf, *end = out + size; - struct bch_replicas_entry *e; - bool first = true; - unsigned i; - - if (!r) { - out += scnprintf(out, end - out, "(no replicas section found)"); - return out - buf; - } - - for_each_replicas_entry(r, e) { - if (!first) - out += scnprintf(out, end - out, " "); - first = false; - - out += scnprintf(out, end - out, "%u: [", e->data_type); - - for (i = 0; i < e->nr; i++) - out += scnprintf(out, end - out, - i ? " %u" : "%u", e->devs[i]); - out += scnprintf(out, end - out, "]"); - } - - return out - buf; -} - -/* Query replicas: */ - -bool bch2_replicas_marked(struct bch_fs *c, - enum bch_data_type data_type, - struct bch_devs_list devs) -{ - struct bch_replicas_cpu_entry search; - unsigned max_dev; - bool ret; - - if (!devs.nr) - return true; - - devlist_to_replicas(devs, data_type, &search, &max_dev); - - rcu_read_lock(); - ret = replicas_has_entry(rcu_dereference(c->replicas), - search, max_dev); - rcu_read_unlock(); - - return ret; -} - -bool bch2_bkey_replicas_marked(struct bch_fs *c, - enum bch_data_type data_type, - struct bkey_s_c k) -{ - struct bch_devs_list cached = bch2_bkey_cached_devs(k); - unsigned i; - - for (i = 0; i < cached.nr; i++) - if (!bch2_replicas_marked(c, BCH_DATA_CACHED, - bch2_dev_list_single(cached.devs[i]))) - return false; - - return bch2_replicas_marked(c, data_type, bch2_bkey_dirty_devs(k)); -} - -struct replicas_status __bch2_replicas_status(struct bch_fs *c, - struct bch_devs_mask online_devs) -{ - struct bch_sb_field_members *mi; - struct bch_replicas_cpu_entry *e; - struct bch_replicas_cpu *r; - unsigned i, dev, dev_slots, nr_online, nr_offline; - struct replicas_status ret; - - memset(&ret, 0, sizeof(ret)); - - for (i = 0; i < ARRAY_SIZE(ret.replicas); i++) - ret.replicas[i].nr_online = UINT_MAX; - - mi = bch2_sb_get_members(c->disk_sb); - rcu_read_lock(); - - r = rcu_dereference(c->replicas); - dev_slots = replicas_dev_slots(r); - - for_each_cpu_replicas_entry(r, e) { - if (e->data_type >= ARRAY_SIZE(ret.replicas)) - panic("e %p data_type %u\n", e, e->data_type); - - nr_online = nr_offline = 0; - - for (dev = 0; dev < dev_slots; dev++) { - if (!replicas_test_dev(e, dev)) - continue; - - BUG_ON(!bch2_dev_exists(c->disk_sb, mi, dev)); - - if (test_bit(dev, online_devs.d)) - nr_online++; - else - nr_offline++; - } - - ret.replicas[e->data_type].nr_online = - min(ret.replicas[e->data_type].nr_online, - nr_online); - - ret.replicas[e->data_type].nr_offline = - max(ret.replicas[e->data_type].nr_offline, - nr_offline); - } - - rcu_read_unlock(); - - return ret; -} - -struct replicas_status bch2_replicas_status(struct bch_fs *c) -{ - return __bch2_replicas_status(c, bch2_online_devs(c)); -} - -static bool have_enough_devs(struct replicas_status s, - enum bch_data_type type, - bool force_if_degraded, - bool force_if_lost) -{ - return (!s.replicas[type].nr_offline || force_if_degraded) && - (s.replicas[type].nr_online || force_if_lost); -} - -bool bch2_have_enough_devs(struct replicas_status s, unsigned flags) -{ - return (have_enough_devs(s, BCH_DATA_JOURNAL, - flags & BCH_FORCE_IF_METADATA_DEGRADED, - flags & BCH_FORCE_IF_METADATA_LOST) && - have_enough_devs(s, BCH_DATA_BTREE, - flags & BCH_FORCE_IF_METADATA_DEGRADED, - flags & BCH_FORCE_IF_METADATA_LOST) && - have_enough_devs(s, BCH_DATA_USER, - flags & BCH_FORCE_IF_DATA_DEGRADED, - flags & BCH_FORCE_IF_DATA_LOST)); -} - -unsigned bch2_replicas_online(struct bch_fs *c, bool meta) -{ - struct replicas_status s = bch2_replicas_status(c); - - return meta - ? min(s.replicas[BCH_DATA_JOURNAL].nr_online, - s.replicas[BCH_DATA_BTREE].nr_online) - : s.replicas[BCH_DATA_USER].nr_online; -} - -unsigned bch2_dev_has_data(struct bch_fs *c, struct bch_dev *ca) -{ - struct bch_replicas_cpu_entry *e; - struct bch_replicas_cpu *r; - unsigned ret = 0; - - rcu_read_lock(); - r = rcu_dereference(c->replicas); - - if (ca->dev_idx >= replicas_dev_slots(r)) - goto out; - - for_each_cpu_replicas_entry(r, e) - if (replicas_test_dev(e, ca->dev_idx)) - ret |= 1 << e->data_type; -out: - rcu_read_unlock(); - - return ret; -} - /* Quotas: */ static const char *bch2_sb_validate_quota(struct bch_sb *sb, @@ -1598,278 +909,3 @@ static const char *bch2_sb_validate_quota(struct bch_sb *sb, return NULL; } - -/* Disk groups: */ - -static int strcmp_void(const void *l, const void *r) -{ - return strcmp(l, r); -} - -static const char *bch2_sb_validate_disk_groups(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_sb_field_members *mi; - struct bch_member *m; - unsigned i, nr_groups, nr_live = 0, len; - char **labels, *l; - const char *err = NULL; - - mi = bch2_sb_get_members(sb); - groups = bch2_sb_get_disk_groups(sb); - nr_groups = disk_groups_nr(groups); - - for (m = mi->members; - m < mi->members + sb->nr_devices; - m++) { - unsigned g; - - if (!BCH_MEMBER_GROUP(m)) - continue; - - g = BCH_MEMBER_GROUP(m) - 1; - - if (g >= nr_groups || - BCH_GROUP_DELETED(&groups->entries[g])) - return "disk has invalid group"; - } - - 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++) { - if (BCH_GROUP_DELETED(g)) - continue; - - len = strnlen(g->label, sizeof(g->label)); - - labels[nr_live++] = l = kmalloc(len + 1, GFP_KERNEL); - if (!l) { - err = "cannot allocate memory"; - goto err; - } - - memcpy(l, g->label, len); - l[len] = '\0'; - } - - sort(labels, nr_live, sizeof(labels[0]), strcmp_void, NULL); - - for (i = 0; i + 1 < nr_live; i++) - if (!strcmp(labels[i], labels[i + 1])) { - err = "duplicate group labels"; - goto err; - } - - err = NULL; -err: - for (i = 0; i < nr_live; i++) - kfree(labels[i]); - kfree(labels); - return err; -} - -static 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; - - lockdep_assert_held(&c->sb_lock); - - mi = bch2_sb_get_members(c->disk_sb); - groups = bch2_sb_get_disk_groups(c->disk_sb); - 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); - if (!cpu_g) - return -ENOMEM; - - cpu_g->nr = nr_groups; - - for (i = 0; i < nr_groups; i++) { - struct bch_disk_group *src = &groups->entries[i]; - struct bch_disk_group_cpu *dst = &cpu_g->entries[i]; - - dst->deleted = BCH_GROUP_DELETED(src); - } - - for (i = 0; i < c->disk_sb->nr_devices; i++) { - struct bch_member *m = mi->members + i; - struct bch_disk_group_cpu *dst = - &cpu_g->entries[BCH_MEMBER_GROUP(m)]; - - if (!bch2_member_exists(m)) - continue; - - dst = BCH_MEMBER_GROUP(m) - ? &cpu_g->entries[BCH_MEMBER_GROUP(m) - 1] - : NULL; - if (dst) - __set_bit(i, dst->devs.d); - } - - old_g = c->disk_groups; - rcu_assign_pointer(c->disk_groups, cpu_g); - if (old_g) - kfree_rcu(old_g, rcu); - - return 0; -} - -const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned target) -{ - struct target t = target_decode(target); - - switch (t.type) { - 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; - } - case TARGET_GROUP: { - struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups); - - return t.group < g->nr && !g->entries[t.group].deleted - ? &g->entries[t.group].devs - : NULL; - } - default: - BUG(); - } -} - -int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups, - const char *name) -{ - unsigned i, nr_groups = disk_groups_nr(groups); - unsigned len = strlen(name); - - for (i = 0; i < nr_groups; i++) { - struct bch_disk_group *g = groups->entries + i; - - if (BCH_GROUP_DELETED(g)) - continue; - - if (strnlen(g->label, sizeof(g->label)) == len && - !memcmp(name, g->label, len)) - 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_opt_target_parse(struct bch_fs *c, const char *buf, u64 *v) -{ - struct bch_dev *ca; - int g; - - if (!strlen(buf) || !strcmp(buf, "none")) { - *v = 0; - return 0; - } - - /* Is it a device? */ - ca = bch2_dev_lookup(c, buf); - if (!IS_ERR(ca)) { - *v = dev_to_target(ca->dev_idx); - percpu_ref_put(&ca->ref); - return 0; - } - - g = bch2_disk_group_find(c, buf); - if (g >= 0) { - *v = group_to_target(g); - return 0; - } - - return -EINVAL; -} - -int bch2_opt_target_print(struct bch_fs *c, char *buf, size_t len, u64 v) -{ - struct target t = target_decode(v); - int ret; - - switch (t.type) { - case TARGET_NULL: - return scnprintf(buf, len, "none"); - 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]; - - ret = scnprintf(buf, len, "/dev/%s", - bdevname(ca->disk_sb.bdev, b)); - percpu_ref_put(&ca->io_ref); - } else if (ca) { - ret = scnprintf(buf, len, "offline device %u", t.dev); - } else { - ret = scnprintf(buf, len, "invalid device %u", t.dev); - } - - 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; - } - default: - BUG(); - } - - return ret; -} diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h index 2514ac8a7b8c..866404b05912 100644 --- a/fs/bcachefs/super-io.h +++ b/fs/bcachefs/super-io.h @@ -139,135 +139,4 @@ static inline struct bch_member_cpu bch2_mi_to_cpu(struct bch_member *mi) }; } -/* BCH_SB_FIELD_replicas: */ - -bool bch2_replicas_marked(struct bch_fs *, enum bch_data_type, - struct bch_devs_list); -bool bch2_bkey_replicas_marked(struct bch_fs *, enum bch_data_type, - struct bkey_s_c); -int bch2_mark_replicas(struct bch_fs *, enum bch_data_type, - struct bch_devs_list); -int bch2_mark_bkey_replicas(struct bch_fs *, enum bch_data_type, - struct bkey_s_c); - -int bch2_cpu_replicas_to_text(struct bch_replicas_cpu *, char *, size_t); -int bch2_sb_replicas_to_text(struct bch_sb_field_replicas *, char *, size_t); - -struct replicas_status { - struct { - unsigned nr_online; - unsigned nr_offline; - } replicas[BCH_DATA_NR]; -}; - -struct replicas_status __bch2_replicas_status(struct bch_fs *, - struct bch_devs_mask); -struct replicas_status bch2_replicas_status(struct bch_fs *); -bool bch2_have_enough_devs(struct replicas_status, unsigned); - -unsigned bch2_replicas_online(struct bch_fs *, bool); -unsigned bch2_dev_has_data(struct bch_fs *, struct bch_dev *); - -int bch2_replicas_gc_end(struct bch_fs *, int); -int bch2_replicas_gc_start(struct bch_fs *, unsigned); - -/* iterate over superblock replicas - used by userspace tools: */ - -static inline struct bch_replicas_entry * -replicas_entry_next(struct bch_replicas_entry *i) -{ - return (void *) i + offsetof(struct bch_replicas_entry, devs) + i->nr; -} - -#define for_each_replicas_entry(_r, _i) \ - for (_i = (_r)->entries; \ - (void *) (_i) < vstruct_end(&(_r)->field) && (_i)->data_type;\ - (_i) = replicas_entry_next(_i)) - -/* disk groups: */ - -static inline unsigned disk_groups_nr(struct bch_sb_field_disk_groups *groups) -{ - return groups - ? (vstruct_end(&groups->field) - - (void *) &groups->entries[0]) / sizeof(struct bch_disk_group) - : 0; -} - -struct target { - enum { - TARGET_NULL, - TARGET_DEV, - TARGET_GROUP, - } type; - union { - unsigned dev; - unsigned group; - }; -}; - -#define TARGET_DEV_START 1 -#define TARGET_GROUP_START (256 + TARGET_DEV_START) - -static inline u16 dev_to_target(unsigned dev) -{ - return TARGET_DEV_START + dev; -} - -static inline u16 group_to_target(unsigned group) -{ - return TARGET_GROUP_START + group; -} - -static inline struct target target_decode(unsigned target) -{ - if (target >= TARGET_GROUP_START) - return (struct target) { - .type = TARGET_GROUP, - .group = target - TARGET_GROUP_START - }; - - if (target >= TARGET_DEV_START) - return (struct target) { - .type = TARGET_DEV, - .group = target - TARGET_DEV_START - }; - - return (struct target) { .type = TARGET_NULL }; -} - -static inline bool dev_in_target(struct bch_dev *ca, unsigned target) -{ - struct target t = target_decode(target); - - switch (t.type) { - case TARGET_NULL: - return false; - case TARGET_DEV: - return ca->dev_idx == t.dev; - case TARGET_GROUP: - return ca->mi.group && ca->mi.group - 1 == t.group; - default: - BUG(); - } -} - -static inline bool dev_idx_in_target(struct bch_fs *c, unsigned dev, unsigned target) -{ - bool ret; - - rcu_read_lock(); - ret = dev_in_target(rcu_dereference(c->devs[dev]), target); - rcu_read_unlock(); - - return ret; -} - -const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *, unsigned); - -int __bch2_disk_group_find(struct bch_sb_field_disk_groups *, const char *); - -int bch2_opt_target_parse(struct bch_fs *, const char *, u64 *); -int bch2_opt_target_print(struct bch_fs *, char *, size_t, u64); - #endif /* _BCACHEFS_SUPER_IO_H */ diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c index 637bf659a21f..f88a313f72ae 100644 --- a/fs/bcachefs/super.c +++ b/fs/bcachefs/super.c @@ -18,6 +18,7 @@ #include "clock.h" #include "compress.h" #include "debug.h" +#include "disk_groups.h" #include "error.h" #include "fs.h" #include "fs-io.h" @@ -30,6 +31,7 @@ #include "migrate.h" #include "movinggc.h" #include "quota.h" +#include "replicas.h" #include "super.h" #include "super-io.h" #include "sysfs.h" @@ -1705,74 +1707,6 @@ found: return ca; } -int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *label) -{ - struct bch_sb_field_disk_groups *groups; - struct bch_disk_group *g; - struct bch_member *mi; - unsigned i, v, nr_groups; - int ret; - - if (strlen(label) > 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++) - ; - - if (i == nr_groups) { - unsigned u64s = - (sizeof(struct bch_sb_field_disk_groups) + - sizeof(struct bch_disk_group) * (nr_groups + 1)) / - sizeof(u64); - - groups = bch2_fs_sb_resize_disk_groups(c, u64s); - if (!groups) { - mutex_unlock(&c->sb_lock); - return -ENOSPC; - } - - nr_groups = disk_groups_nr(groups); - } - - 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'; - SET_BCH_GROUP_DELETED(g, 0); - SET_BCH_GROUP_DATA_ALLOWED(g, ~0); -write_sb: - mi = &bch2_sb_get_members(c->disk_sb)->members[ca->dev_idx]; - SET_BCH_MEMBER_GROUP(mi, v); - - bch2_write_super(c); - mutex_unlock(&c->sb_lock); - - return 0; -} - /* Filesystem open: */ struct bch_fs *bch2_fs_open(char * const *devices, unsigned nr_devices, diff --git a/fs/bcachefs/super.h b/fs/bcachefs/super.h index 652a572ff329..a52ee3bb37ee 100644 --- a/fs/bcachefs/super.h +++ b/fs/bcachefs/super.h @@ -195,7 +195,6 @@ int bch2_dev_online(struct bch_fs *, const char *); int bch2_dev_offline(struct bch_fs *, struct bch_dev *, int); int bch2_dev_resize(struct bch_fs *, struct bch_dev *, u64); struct bch_dev *bch2_dev_lookup(struct bch_fs *, const char *); -int bch2_dev_group_set(struct bch_fs *, struct bch_dev *, const char *); bool bch2_fs_emergency_read_only(struct bch_fs *); void bch2_fs_read_only(struct bch_fs *); diff --git a/fs/bcachefs/sysfs.c b/fs/bcachefs/sysfs.c index 82457348d062..c440eca4fb87 100644 --- a/fs/bcachefs/sysfs.c +++ b/fs/bcachefs/sysfs.c @@ -18,11 +18,13 @@ #include "btree_update_interior.h" #include "btree_gc.h" #include "buckets.h" +#include "disk_groups.h" #include "inode.h" #include "journal.h" #include "keylist.h" #include "move.h" #include "opts.h" +#include "replicas.h" #include "super-io.h" #include "tier.h" diff --git a/fs/bcachefs/tier.c b/fs/bcachefs/tier.c index 211a844c69cf..a15a0fa9dff9 100644 --- a/fs/bcachefs/tier.c +++ b/fs/bcachefs/tier.c @@ -4,6 +4,7 @@ #include "btree_iter.h" #include "buckets.h" #include "clock.h" +#include "disk_groups.h" #include "extents.h" #include "io.h" #include "move.h" |