]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/super.c
New upstream release
[bcachefs-tools-debian] / libbcachefs / super.c
index eee56969c77934a7788b5e8083f1dc69d61cab6b..bb9451082e872ca6752e085ca1c884563821a157 100644 (file)
@@ -13,6 +13,7 @@
 #include "bkey_sort.h"
 #include "btree_cache.h"
 #include "btree_gc.h"
+#include "btree_journal_iter.h"
 #include "btree_key_cache.h"
 #include "btree_update_interior.h"
 #include "btree_io.h"
 #include "error.h"
 #include "fs.h"
 #include "fs-io.h"
+#include "fs-io-buffered.h"
+#include "fs-io-direct.h"
 #include "fsck.h"
 #include "inode.h"
-#include "io.h"
+#include "io_read.h"
+#include "io_write.h"
 #include "journal.h"
 #include "journal_reclaim.h"
 #include "journal_seq_blacklist.h"
 #include "rebalance.h"
 #include "recovery.h"
 #include "replicas.h"
+#include "sb-clean.h"
+#include "sb-errors.h"
+#include "sb-members.h"
+#include "snapshot.h"
 #include "subvolume.h"
 #include "super.h"
 #include "super-io.h"
@@ -63,6 +71,7 @@
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Kent Overstreet <kent.overstreet@gmail.com>");
+MODULE_DESCRIPTION("bcachefs filesystem");
 
 #define KTYPE(type)                                                    \
 static const struct attribute_group type ## _group = {                 \
@@ -392,6 +401,10 @@ static int __bch2_fs_read_write(struct bch_fs *c, bool early)
 
        bch_info(c, "going read-write");
 
+       ret = bch2_sb_members_v2_init(c);
+       if (ret)
+               goto err;
+
        ret = bch2_fs_mark_dirty(c);
        if (ret)
                goto err;
@@ -416,6 +429,10 @@ static int __bch2_fs_read_write(struct bch_fs *c, bool early)
                return ret;
        }
 
+       ret = bch2_journal_reclaim_start(&c->journal);
+       if (ret)
+               goto err;
+
        if (!early) {
                ret = bch2_fs_read_write_late(c);
                if (ret)
@@ -425,7 +442,7 @@ static int __bch2_fs_read_write(struct bch_fs *c, bool early)
 #ifndef BCH_WRITE_REF_DEBUG
        percpu_ref_reinit(&c->writes);
 #else
-       for (unsigned i = 0; i < BCH_WRITE_REF_NR; i++) {
+       for (i = 0; i < BCH_WRITE_REF_NR; i++) {
                BUG_ON(atomic_long_read(&c->writes[i]));
                atomic_long_inc(&c->writes[i]);
        }
@@ -460,19 +477,23 @@ int bch2_fs_read_write_early(struct bch_fs *c)
 static void __bch2_fs_free(struct bch_fs *c)
 {
        unsigned i;
-       int cpu;
 
        for (i = 0; i < BCH_TIME_STAT_NR; i++)
                bch2_time_stats_exit(&c->times[i]);
 
        bch2_free_pending_node_rewrites(c);
+       bch2_fs_sb_errors_exit(c);
        bch2_fs_counters_exit(c);
        bch2_fs_snapshots_exit(c);
        bch2_fs_quota_exit(c);
+       bch2_fs_fs_io_direct_exit(c);
+       bch2_fs_fs_io_buffered_exit(c);
        bch2_fs_fsio_exit(c);
        bch2_fs_ec_exit(c);
        bch2_fs_encryption_exit(c);
-       bch2_fs_io_exit(c);
+       bch2_fs_nocow_locking_exit(c);
+       bch2_fs_io_write_exit(c);
+       bch2_fs_io_read_exit(c);
        bch2_fs_buckets_waiting_for_journal_exit(c);
        bch2_fs_btree_interior_update_exit(c);
        bch2_fs_btree_iter_exit(c);
@@ -489,12 +510,7 @@ static void __bch2_fs_free(struct bch_fs *c)
        percpu_free_rwsem(&c->mark_lock);
        free_percpu(c->online_reserved);
 
-       if (c->btree_paths_bufs)
-               for_each_possible_cpu(cpu)
-                       kfree(per_cpu_ptr(c->btree_paths_bufs, cpu)->path);
-
        darray_exit(&c->btree_roots_extra);
-       free_percpu(c->btree_paths_bufs);
        free_percpu(c->pcpu);
        mempool_exit(&c->large_bkey_pool);
        mempool_exit(&c->btree_bounce_pool);
@@ -568,13 +584,6 @@ void __bch2_fs_stop(struct bch_fs *c)
                cancel_work_sync(&ca->io_error_work);
 
        cancel_work_sync(&c->read_only_work);
-
-       for (i = 0; i < c->sb.nr_devices; i++) {
-               struct bch_dev *ca = rcu_dereference_protected(c->devs[i], true);
-
-               if (ca)
-                       bch2_free_super(&ca->disk_sb);
-       }
 }
 
 void bch2_fs_free(struct bch_fs *c)
@@ -588,9 +597,14 @@ void bch2_fs_free(struct bch_fs *c)
        closure_sync(&c->cl);
        closure_debug_destroy(&c->cl);
 
-       for (i = 0; i < c->sb.nr_devices; i++)
-               if (c->devs[i])
-                       bch2_dev_free(rcu_dereference_protected(c->devs[i], 1));
+       for (i = 0; i < c->sb.nr_devices; i++) {
+               struct bch_dev *ca = rcu_dereference_protected(c->devs[i], true);
+
+               if (ca) {
+                       bch2_free_super(&ca->disk_sb);
+                       bch2_dev_free(ca);
+               }
+       }
 
        bch_verbose(c, "shutdown complete");
 
@@ -627,7 +641,9 @@ static int bch2_fs_online(struct bch_fs *c)
        ret = kobject_add(&c->kobj, NULL, "%pU", c->sb.user_uuid.b) ?:
            kobject_add(&c->internal, &c->kobj, "internal") ?:
            kobject_add(&c->opts_dir, &c->kobj, "options") ?:
+#ifndef CONFIG_BCACHEFS_NO_LATENCY_ACCT
            kobject_add(&c->time_stats, &c->kobj, "time_stats") ?:
+#endif
            kobject_add(&c->counters_kobj, &c->kobj, "counters") ?:
            bch2_opts_create_sysfs_files(&c->opts_dir);
        if (ret) {
@@ -655,7 +671,6 @@ err:
 
 static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
 {
-       struct bch_sb_field_members *mi;
        struct bch_fs *c;
        struct printbuf name = PRINTBUF;
        unsigned i, iter_size;
@@ -702,6 +717,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
        bch2_fs_quota_init(c);
        bch2_fs_ec_init_early(c);
        bch2_fs_move_init(c);
+       bch2_fs_sb_errors_init_early(c);
 
        INIT_LIST_HEAD(&c->list);
 
@@ -709,6 +725,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
 
        mutex_init(&c->bio_bounce_pages_lock);
        mutex_init(&c->snapshot_table_lock);
+       init_rwsem(&c->snapshot_create_lock);
 
        spin_lock_init(&c->btree_write_error_lock);
 
@@ -717,8 +734,8 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
 
        INIT_LIST_HEAD(&c->journal_iters);
 
-       INIT_LIST_HEAD(&c->fsck_errors);
-       mutex_init(&c->fsck_error_lock);
+       INIT_LIST_HEAD(&c->fsck_error_msgs);
+       mutex_init(&c->fsck_error_msgs_lock);
 
        seqcount_init(&c->gc_pos_lock);
 
@@ -735,7 +752,6 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
 
        c->journal.flush_write_time     = &c->times[BCH_TIME_journal_flush_write];
        c->journal.noflush_write_time   = &c->times[BCH_TIME_journal_noflush_write];
-       c->journal.blocked_time         = &c->times[BCH_TIME_blocked_journal];
        c->journal.flush_seq_time       = &c->times[BCH_TIME_journal_flush_seq];
 
        bch2_fs_btree_cache_init_early(&c->btree_cache);
@@ -780,6 +796,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
        c->btree_key_cache_btrees |= 1U << BTREE_ID_alloc;
        if (c->opts.inodes_use_key_cache)
                c->btree_key_cache_btrees |= 1U << BTREE_ID_inodes;
+       c->btree_key_cache_btrees |= 1U << BTREE_ID_logged_ops;
 
        c->block_bits           = ilog2(block_sectors(c));
        c->btree_foreground_merge_threshold = BTREE_FOREGROUND_MERGE_THRESHOLD(c);
@@ -817,7 +834,6 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
                        BIOSET_NEED_BVECS) ||
            !(c->pcpu = alloc_percpu(struct bch_fs_pcpu)) ||
            !(c->online_reserved = alloc_percpu(u64)) ||
-           !(c->btree_paths_bufs = alloc_percpu(struct btree_path_buf)) ||
            mempool_init_kvpmalloc_pool(&c->btree_bounce_pool, 1,
                                        btree_bytes(c)) ||
            mempool_init_kmalloc_pool(&c->large_bkey_pool, 1, 2048) ||
@@ -828,6 +844,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
        }
 
        ret = bch2_fs_counters_init(c) ?:
+           bch2_fs_sb_errors_init(c) ?:
            bch2_io_clock_init(&c->io_clock[READ]) ?:
            bch2_io_clock_init(&c->io_clock[WRITE]) ?:
            bch2_fs_journal_init(&c->journal) ?:
@@ -839,18 +856,20 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
            bch2_fs_buckets_waiting_for_journal_init(c) ?:
            bch2_fs_btree_write_buffer_init(c) ?:
            bch2_fs_subvolumes_init(c) ?:
-           bch2_fs_io_init(c) ?:
+           bch2_fs_io_read_init(c) ?:
+           bch2_fs_io_write_init(c) ?:
            bch2_fs_nocow_locking_init(c) ?:
            bch2_fs_encryption_init(c) ?:
            bch2_fs_compress_init(c) ?:
            bch2_fs_ec_init(c) ?:
-           bch2_fs_fsio_init(c);
+           bch2_fs_fsio_init(c) ?:
+           bch2_fs_fs_io_buffered_init(c) ?:
+           bch2_fs_fs_io_direct_init(c);
        if (ret)
                goto err;
 
-       mi = bch2_sb_get_members(c->disk_sb.sb);
        for (i = 0; i < c->sb.nr_devices; i++)
-               if (bch2_dev_exists(c->disk_sb.sb, mi, i) &&
+               if (bch2_dev_exists(c->disk_sb.sb, i) &&
                    bch2_dev_alloc(c, i)) {
                        ret = -EEXIST;
                        goto err;
@@ -915,7 +934,6 @@ static void print_mount_opts(struct bch_fs *c)
 
 int bch2_fs_start(struct bch_fs *c)
 {
-       struct bch_sb_field_members *mi;
        struct bch_dev *ca;
        time64_t now = ktime_get_real_seconds();
        unsigned i;
@@ -929,12 +947,14 @@ int bch2_fs_start(struct bch_fs *c)
 
        mutex_lock(&c->sb_lock);
 
-       for_each_online_member(ca, c, i)
-               bch2_sb_from_fs(c, ca);
+       ret = bch2_sb_members_v2_init(c);
+       if (ret) {
+               mutex_unlock(&c->sb_lock);
+               goto err;
+       }
 
-       mi = bch2_sb_get_members(c->disk_sb.sb);
        for_each_online_member(ca, c, i)
-               mi->members[ca->dev_idx].last_mount = cpu_to_le64(now);
+               bch2_members_v2_get_mut(c->disk_sb.sb, i)->last_mount = cpu_to_le64(now);
 
        mutex_unlock(&c->sb_lock);
 
@@ -942,12 +962,6 @@ int bch2_fs_start(struct bch_fs *c)
                bch2_dev_allocator_add(c, ca);
        bch2_recalc_capacity(c);
 
-       for (i = 0; i < BCH_TRANSACTIONS_NR; i++) {
-               mutex_lock(&c->btree_transaction_stats[i].lock);
-               bch2_time_stats_init(&c->btree_transaction_stats[i].lock_hold_times);
-               mutex_unlock(&c->btree_transaction_stats[i].lock);
-       }
-
        ret = BCH_SB_INITIALIZED(c->disk_sb.sb)
                ? bch2_fs_recovery(c)
                : bch2_fs_initialize(c);
@@ -981,22 +995,18 @@ out:
        up_write(&c->state_lock);
        return ret;
 err:
-       bch_err(c, "error starting filesystem: %s", bch2_err_str(ret));
+       bch_err_msg(c, ret, "starting filesystem");
        goto out;
 }
 
 static int bch2_dev_may_add(struct bch_sb *sb, struct bch_fs *c)
 {
-       struct bch_sb_field_members *sb_mi;
-
-       sb_mi = bch2_sb_get_members(sb);
-       if (!sb_mi)
-               return -BCH_ERR_member_info_missing;
+       struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx);
 
        if (le16_to_cpu(sb->block_size) != block_sectors(c))
                return -BCH_ERR_mismatched_block_size;
 
-       if (le16_to_cpu(sb_mi->members[sb->dev_idx].bucket_size) <
+       if (le16_to_cpu(m.bucket_size) <
            BCH_SB_BTREE_NODE_SIZE(c->disk_sb.sb))
                return -BCH_ERR_bucket_size_too_small;
 
@@ -1007,12 +1017,11 @@ static int bch2_dev_in_fs(struct bch_sb *fs, struct bch_sb *sb)
 {
        struct bch_sb *newest =
                le64_to_cpu(fs->seq) > le64_to_cpu(sb->seq) ? fs : sb;
-       struct bch_sb_field_members *mi = bch2_sb_get_members(newest);
 
        if (!uuid_equal(&fs->uuid, &sb->uuid))
                return -BCH_ERR_device_not_a_member_of_filesystem;
 
-       if (!bch2_dev_exists(newest, mi, sb->dev_idx))
+       if (!bch2_dev_exists(newest, sb->dev_idx))
                return -BCH_ERR_device_has_been_removed;
 
        if (fs->block_size != sb->block_size)
@@ -1127,6 +1136,7 @@ static struct bch_dev *__bch2_dev_alloc(struct bch_fs *c,
                                        struct bch_member *member)
 {
        struct bch_dev *ca;
+       unsigned i;
 
        ca = kzalloc(sizeof(*ca), GFP_KERNEL);
        if (!ca)
@@ -1144,6 +1154,10 @@ static struct bch_dev *__bch2_dev_alloc(struct bch_fs *c,
        bch2_time_stats_init(&ca->io_latency[WRITE]);
 
        ca->mi = bch2_mi_to_cpu(member);
+
+       for (i = 0; i < ARRAY_SIZE(member->errors); i++)
+               atomic64_set(&ca->errors[i], le64_to_cpu(member->errors[i]));
+
        ca->uuid = member->uuid;
 
        ca->nr_btree_reserve = DIV_ROUND_UP(BTREE_NODE_RESERVE,
@@ -1182,15 +1196,14 @@ static void bch2_dev_attach(struct bch_fs *c, struct bch_dev *ca,
 
 static int bch2_dev_alloc(struct bch_fs *c, unsigned dev_idx)
 {
-       struct bch_member *member =
-               bch2_sb_get_members(c->disk_sb.sb)->members + dev_idx;
+       struct bch_member member = bch2_sb_member_get(c->disk_sb.sb, dev_idx);
        struct bch_dev *ca = NULL;
        int ret = 0;
 
        if (bch2_fs_init_fault("dev_alloc"))
                goto err;
 
-       ca = __bch2_dev_alloc(c, member);
+       ca = __bch2_dev_alloc(c, &member);
        if (!ca)
                goto err;
 
@@ -1228,8 +1241,6 @@ static int __bch2_dev_attach_bdev(struct bch_dev *ca, struct bch_sb_handle *sb)
 
        /* Commit: */
        ca->disk_sb = *sb;
-       if (sb->mode & FMODE_EXCL)
-               ca->disk_sb.bdev->bd_holder = ca;
        memset(sb, 0, sizeof(*sb));
 
        ca->dev = ca->disk_sb.bdev->bd_dev;
@@ -1327,7 +1338,6 @@ bool bch2_dev_state_allowed(struct bch_fs *c, struct bch_dev *ca,
 
 static bool bch2_fs_may_start(struct bch_fs *c)
 {
-       struct bch_sb_field_members *mi;
        struct bch_dev *ca;
        unsigned i, flags = 0;
 
@@ -1340,10 +1350,9 @@ static bool bch2_fs_may_start(struct bch_fs *c)
        if (!c->opts.degraded &&
            !c->opts.very_degraded) {
                mutex_lock(&c->sb_lock);
-               mi = bch2_sb_get_members(c->disk_sb.sb);
 
                for (i = 0; i < c->disk_sb.sb->nr_devices; i++) {
-                       if (!bch2_dev_exists(c->disk_sb.sb, mi, i))
+                       if (!bch2_dev_exists(c->disk_sb.sb, i))
                                continue;
 
                        ca = bch_dev_locked(c, i);
@@ -1383,7 +1392,7 @@ static void __bch2_dev_read_write(struct bch_fs *c, struct bch_dev *ca)
 int __bch2_dev_set_state(struct bch_fs *c, struct bch_dev *ca,
                         enum bch_member_state new_state, int flags)
 {
-       struct bch_sb_field_members *mi;
+       struct bch_member *m;
        int ret = 0;
 
        if (ca->mi.state == new_state)
@@ -1398,8 +1407,8 @@ int __bch2_dev_set_state(struct bch_fs *c, struct bch_dev *ca,
        bch_notice(ca, "%s", bch2_member_states[new_state]);
 
        mutex_lock(&c->sb_lock);
-       mi = bch2_sb_get_members(c->disk_sb.sb);
-       SET_BCH_MEMBER_STATE(&mi->members[ca->dev_idx], new_state);
+       m = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);
+       SET_BCH_MEMBER_STATE(m, new_state);
        bch2_write_super(c);
        mutex_unlock(&c->sb_lock);
 
@@ -1448,14 +1457,14 @@ static int bch2_dev_remove_alloc(struct bch_fs *c, struct bch_dev *ca)
                bch2_btree_delete_range(c, BTREE_ID_bucket_gens, start, end,
                                        BTREE_TRIGGER_NORUN, NULL);
        if (ret)
-               bch_err(c, "error removing dev alloc info: %s", bch2_err_str(ret));
+               bch_err_msg(c, ret, "removing dev alloc info");
 
        return ret;
 }
 
 int bch2_dev_remove(struct bch_fs *c, struct bch_dev *ca, int flags)
 {
-       struct bch_sb_field_members *mi;
+       struct bch_member *m;
        unsigned dev_idx = ca->dev_idx, data;
        int ret;
 
@@ -1477,31 +1486,31 @@ int bch2_dev_remove(struct bch_fs *c, struct bch_dev *ca, int flags)
 
        ret = bch2_dev_data_drop(c, ca->dev_idx, flags);
        if (ret) {
-               bch_err(ca, "Remove failed: error dropping data: %s", bch2_err_str(ret));
+               bch_err_msg(ca, ret, "dropping data");
                goto err;
        }
 
        ret = bch2_dev_remove_alloc(c, ca);
        if (ret) {
-               bch_err(ca, "Remove failed, error deleting alloc info");
+               bch_err_msg(ca, ret, "deleting alloc info");
                goto err;
        }
 
        ret = bch2_journal_flush_device_pins(&c->journal, ca->dev_idx);
        if (ret) {
-               bch_err(ca, "Remove failed: error flushing journal: %s", bch2_err_str(ret));
+               bch_err_msg(ca, ret, "flushing journal");
                goto err;
        }
 
        ret = bch2_journal_flush(&c->journal);
        if (ret) {
-               bch_err(ca, "Remove failed, journal error");
+               bch_err(ca, "journal error");
                goto err;
        }
 
        ret = bch2_replicas_gc2(c);
        if (ret) {
-               bch_err(ca, "Remove failed: error from replicas gc: %s", bch2_err_str(ret));
+               bch_err_msg(ca, ret, "in replicas_gc2()");
                goto err;
        }
 
@@ -1543,8 +1552,8 @@ int bch2_dev_remove(struct bch_fs *c, struct bch_dev *ca, int flags)
         * this device must be gone:
         */
        mutex_lock(&c->sb_lock);
-       mi = bch2_sb_get_members(c->disk_sb.sb);
-       memset(&mi->members[dev_idx].uuid, 0, sizeof(mi->members[dev_idx].uuid));
+       m = bch2_members_v2_get_mut(c->disk_sb.sb, dev_idx);
+       memset(&m->uuid, 0, sizeof(m->uuid));
 
        bch2_write_super(c);
 
@@ -1567,7 +1576,7 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
        struct bch_opts opts = bch2_opts_empty();
        struct bch_sb_handle sb;
        struct bch_dev *ca = NULL;
-       struct bch_sb_field_members *mi;
+       struct bch_sb_field_members_v2 *mi;
        struct bch_member dev_mi;
        unsigned dev_idx, nr_devices, u64s;
        struct printbuf errbuf = PRINTBUF;
@@ -1576,14 +1585,14 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
 
        ret = bch2_read_super(path, &opts, &sb);
        if (ret) {
-               bch_err(c, "device add error: error reading super: %s", bch2_err_str(ret));
+               bch_err_msg(c, ret, "reading super");
                goto err;
        }
 
-       dev_mi = bch2_sb_get_members(sb.sb)->members[sb.sb->dev_idx];
+       dev_mi = bch2_sb_member_get(sb.sb, sb.sb->dev_idx);
 
        if (BCH_MEMBER_GROUP(&dev_mi)) {
-               bch2_disk_path_to_text(&label, sb.sb, BCH_MEMBER_GROUP(&dev_mi) - 1);
+               bch2_disk_path_to_text_sb(&label, sb.sb, BCH_MEMBER_GROUP(&dev_mi) - 1);
                if (label.allocation_failure) {
                        ret = -ENOMEM;
                        goto err;
@@ -1592,13 +1601,12 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
 
        ret = bch2_dev_may_add(sb.sb, c);
        if (ret) {
-               bch_err(c, "device add error: %s", bch2_err_str(ret));
+               bch_err_fn(c, ret);
                goto err;
        }
 
        ca = __bch2_dev_alloc(c, &dev_mi);
        if (!ca) {
-               bch2_free_super(&sb);
                ret = -ENOMEM;
                goto err;
        }
@@ -1606,14 +1614,12 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
        bch2_dev_usage_init(ca);
 
        ret = __bch2_dev_attach_bdev(ca, &sb);
-       if (ret) {
-               bch2_dev_free(ca);
+       if (ret)
                goto err;
-       }
 
        ret = bch2_dev_journal_alloc(ca);
        if (ret) {
-               bch_err(c, "device add error: journal alloc failed");
+               bch_err_msg(c, ret, "allocating journal");
                goto err;
        }
 
@@ -1622,48 +1628,40 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
 
        ret = bch2_sb_from_fs(c, ca);
        if (ret) {
-               bch_err(c, "device add error: new device superblock too small");
-               goto err_unlock;
-       }
-
-       mi = bch2_sb_get_members(ca->disk_sb.sb);
-
-       if (!bch2_sb_resize_members(&ca->disk_sb,
-                               le32_to_cpu(mi->field.u64s) +
-                               sizeof(dev_mi) / sizeof(u64))) {
-               bch_err(c, "device add error: new device superblock too small");
-               ret = -BCH_ERR_ENOSPC_sb_members;
+               bch_err_msg(c, ret, "setting up new superblock");
                goto err_unlock;
        }
 
        if (dynamic_fault("bcachefs:add:no_slot"))
                goto no_slot;
 
-       mi = bch2_sb_get_members(c->disk_sb.sb);
        for (dev_idx = 0; dev_idx < BCH_SB_MEMBERS_MAX; dev_idx++)
-               if (!bch2_dev_exists(c->disk_sb.sb, mi, dev_idx))
+               if (!bch2_dev_exists(c->disk_sb.sb, dev_idx))
                        goto have_slot;
 no_slot:
-       bch_err(c, "device add error: already have maximum number of devices");
        ret = -BCH_ERR_ENOSPC_sb_members;
+       bch_err_msg(c, ret, "setting up new superblock");
        goto err_unlock;
 
 have_slot:
        nr_devices = max_t(unsigned, dev_idx + 1, c->sb.nr_devices);
-       u64s = (sizeof(struct bch_sb_field_members) +
-               sizeof(struct bch_member) * nr_devices) / sizeof(u64);
 
-       mi = bch2_sb_resize_members(&c->disk_sb, u64s);
+       mi = bch2_sb_field_get(c->disk_sb.sb, members_v2);
+       u64s = DIV_ROUND_UP(sizeof(struct bch_sb_field_members_v2) +
+                           le16_to_cpu(mi->member_bytes) * nr_devices, sizeof(u64));
+
+       mi = bch2_sb_field_resize(&c->disk_sb, members_v2, u64s);
        if (!mi) {
-               bch_err(c, "device add error: no room in superblock for member info");
                ret = -BCH_ERR_ENOSPC_sb_members;
+               bch_err_msg(c, ret, "setting up new superblock");
                goto err_unlock;
        }
+       struct bch_member *m = bch2_members_v2_get_mut(c->disk_sb.sb, dev_idx);
 
        /* success: */
 
-       mi->members[dev_idx] = dev_mi;
-       mi->members[dev_idx].last_mount = cpu_to_le64(ktime_get_real_seconds());
+       *m = dev_mi;
+       m->last_mount = cpu_to_le64(ktime_get_real_seconds());
        c->disk_sb.sb->nr_devices       = nr_devices;
 
        ca->disk_sb.sb->dev_idx = dev_idx;
@@ -1672,7 +1670,7 @@ have_slot:
        if (BCH_MEMBER_GROUP(&dev_mi)) {
                ret = __bch2_dev_group_set(c, ca, label.buf);
                if (ret) {
-                       bch_err(c, "device add error: error setting label");
+                       bch_err_msg(c, ret, "creating new label");
                        goto err_unlock;
                }
        }
@@ -1684,13 +1682,13 @@ have_slot:
 
        ret = bch2_trans_mark_dev_sb(c, ca);
        if (ret) {
-               bch_err(c, "device add error: error marking new superblock: %s", bch2_err_str(ret));
+               bch_err_msg(ca, ret, "marking new superblock");
                goto err_late;
        }
 
        ret = bch2_fs_freespace_init(c);
        if (ret) {
-               bch_err(c, "device add error: error initializing free space: %s", bch2_err_str(ret));
+               bch_err_msg(ca, ret, "initializing free space");
                goto err_late;
        }
 
@@ -1723,7 +1721,6 @@ int bch2_dev_online(struct bch_fs *c, const char *path)
 {
        struct bch_opts opts = bch2_opts_empty();
        struct bch_sb_handle sb = { NULL };
-       struct bch_sb_field_members *mi;
        struct bch_dev *ca;
        unsigned dev_idx;
        int ret;
@@ -1740,7 +1737,7 @@ int bch2_dev_online(struct bch_fs *c, const char *path)
 
        ret = bch2_dev_in_fs(c->disk_sb.sb, sb.sb);
        if (ret) {
-               bch_err(c, "error bringing %s online: %s", path, bch2_err_str(ret));
+               bch_err_msg(c, ret, "bringing %s online", path);
                goto err;
        }
 
@@ -1752,27 +1749,33 @@ int bch2_dev_online(struct bch_fs *c, const char *path)
 
        ret = bch2_trans_mark_dev_sb(c, ca);
        if (ret) {
-               bch_err(c, "error bringing %s online: error from bch2_trans_mark_dev_sb: %s",
-                       path, bch2_err_str(ret));
+               bch_err_msg(c, ret, "bringing %s online: error from bch2_trans_mark_dev_sb", path);
                goto err;
        }
 
        if (ca->mi.state == BCH_MEMBER_STATE_rw)
                __bch2_dev_read_write(c, ca);
 
-       mutex_lock(&c->sb_lock);
-       mi = bch2_sb_get_members(c->disk_sb.sb);
+       if (!ca->mi.freespace_initialized) {
+               ret = bch2_dev_freespace_init(c, ca, 0, ca->mi.nbuckets);
+               bch_err_msg(ca, ret, "initializing free space");
+               if (ret)
+                       goto err;
+       }
 
-       mi->members[ca->dev_idx].last_mount =
-               cpu_to_le64(ktime_get_real_seconds());
+       if (!ca->journal.nr) {
+               ret = bch2_dev_journal_alloc(ca);
+               bch_err_msg(ca, ret, "allocating journal");
+               if (ret)
+                       goto err;
+       }
 
+       mutex_lock(&c->sb_lock);
+       bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx)->last_mount =
+               cpu_to_le64(ktime_get_real_seconds());
        bch2_write_super(c);
        mutex_unlock(&c->sb_lock);
 
-       ret = bch2_fs_freespace_init(c);
-       if (ret)
-               bch_err(c, "device add error: error initializing free space: %s", bch2_err_str(ret));
-
        up_write(&c->state_lock);
        return 0;
 err:
@@ -1805,10 +1808,12 @@ int bch2_dev_offline(struct bch_fs *c, struct bch_dev *ca, int flags)
 
 int bch2_dev_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
 {
-       struct bch_member *mi;
+       struct bch_member *m;
+       u64 old_nbuckets;
        int ret = 0;
 
        down_write(&c->state_lock);
+       old_nbuckets = ca->mi.nbuckets;
 
        if (nbuckets < ca->mi.nbuckets) {
                bch_err(ca, "Cannot shrink yet");
@@ -1826,7 +1831,7 @@ int bch2_dev_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
 
        ret = bch2_dev_buckets_resize(c, ca, nbuckets);
        if (ret) {
-               bch_err(ca, "Resize error: %s", bch2_err_str(ret));
+               bch_err_msg(ca, ret, "resizing buckets");
                goto err;
        }
 
@@ -1835,12 +1840,24 @@ int bch2_dev_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
                goto err;
 
        mutex_lock(&c->sb_lock);
-       mi = &bch2_sb_get_members(c->disk_sb.sb)->members[ca->dev_idx];
-       mi->nbuckets = cpu_to_le64(nbuckets);
+       m = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);
+       m->nbuckets = cpu_to_le64(nbuckets);
 
        bch2_write_super(c);
        mutex_unlock(&c->sb_lock);
 
+       if (ca->mi.freespace_initialized) {
+               ret = bch2_dev_freespace_init(c, ca, old_nbuckets, nbuckets);
+               if (ret)
+                       goto err;
+
+               /*
+                * XXX: this is all wrong transactionally - we'll be able to do
+                * this correctly after the disk space accounting rewrite
+                */
+               ca->usage_base->d[BCH_DATA_free].buckets += nbuckets - old_nbuckets;
+       }
+
        bch2_recalc_capacity(c);
 err:
        up_write(&c->state_lock);
@@ -1869,10 +1886,9 @@ found:
 struct bch_fs *bch2_fs_open(char * const *devices, unsigned nr_devices,
                            struct bch_opts opts)
 {
-       struct bch_sb_handle *sb = NULL;
+       DARRAY(struct bch_sb_handle) sbs = { 0 };
        struct bch_fs *c = NULL;
-       struct bch_sb_field_members *mi;
-       unsigned i, best_sb = 0;
+       struct bch_sb_handle *sb, *best = NULL;
        struct printbuf errbuf = PRINTBUF;
        int ret = 0;
 
@@ -1884,51 +1900,46 @@ struct bch_fs *bch2_fs_open(char * const *devices, unsigned nr_devices,
                goto err;
        }
 
-       sb = kcalloc(nr_devices, sizeof(*sb), GFP_KERNEL);
-       if (!sb) {
-               ret = -ENOMEM;
+       ret = darray_make_room(&sbs, nr_devices);
+       if (ret)
                goto err;
-       }
 
-       for (i = 0; i < nr_devices; i++) {
-               ret = bch2_read_super(devices[i], &opts, &sb[i]);
+       for (unsigned i = 0; i < nr_devices; i++) {
+               struct bch_sb_handle sb = { NULL };
+
+               ret = bch2_read_super(devices[i], &opts, &sb);
                if (ret)
                        goto err;
 
+               BUG_ON(darray_push(&sbs, sb));
        }
 
-       for (i = 1; i < nr_devices; i++)
-               if (le64_to_cpu(sb[i].sb->seq) >
-                   le64_to_cpu(sb[best_sb].sb->seq))
-                       best_sb = i;
-
-       mi = bch2_sb_get_members(sb[best_sb].sb);
+       darray_for_each(sbs, sb)
+               if (!best || le64_to_cpu(sb->sb->seq) > le64_to_cpu(best->sb->seq))
+                       best = sb;
 
-       i = 0;
-       while (i < nr_devices) {
-               if (i != best_sb &&
-                   !bch2_dev_exists(sb[best_sb].sb, mi, sb[i].sb->dev_idx)) {
-                       pr_info("%pg has been removed, skipping", sb[i].bdev);
-                       bch2_free_super(&sb[i]);
-                       array_remove_item(sb, nr_devices, i);
+       darray_for_each_reverse(sbs, sb) {
+               if (sb != best && !bch2_dev_exists(best->sb, sb->sb->dev_idx)) {
+                       pr_info("%pg has been removed, skipping", sb->bdev);
+                       bch2_free_super(sb);
+                       darray_remove_item(&sbs, sb);
+                       best -= best > sb;
                        continue;
                }
 
-               ret = bch2_dev_in_fs(sb[best_sb].sb, sb[i].sb);
+               ret = bch2_dev_in_fs(best->sb, sb->sb);
                if (ret)
                        goto err_print;
-               i++;
        }
 
-       c = bch2_fs_alloc(sb[best_sb].sb, opts);
-       if (IS_ERR(c)) {
-               ret = PTR_ERR(c);
+       c = bch2_fs_alloc(best->sb, opts);
+       ret = PTR_ERR_OR_ZERO(c);
+       if (ret)
                goto err;
-       }
 
        down_write(&c->state_lock);
-       for (i = 0; i < nr_devices; i++) {
-               ret = bch2_dev_attach_bdev(c, &sb[i]);
+       darray_for_each(sbs, sb) {
+               ret = bch2_dev_attach_bdev(c, sb);
                if (ret) {
                        up_write(&c->state_lock);
                        goto err;
@@ -1947,7 +1958,9 @@ struct bch_fs *bch2_fs_open(char * const *devices, unsigned nr_devices,
                        goto err;
        }
 out:
-       kfree(sb);
+       darray_for_each(sbs, sb)
+               bch2_free_super(sb);
+       darray_exit(&sbs);
        printbuf_exit(&errbuf);
        module_put(THIS_MODULE);
        return c;
@@ -1957,9 +1970,6 @@ err_print:
 err:
        if (!IS_ERR_OR_NULL(c))
                bch2_fs_stop(c);
-       if (sb)
-               for (i = 0; i < nr_devices; i++)
-                       bch2_free_super(&sb[i]);
        c = ERR_PTR(ret);
        goto out;
 }
@@ -2000,6 +2010,7 @@ err:
 BCH_DEBUG_PARAMS()
 #undef BCH_DEBUG_PARAM
 
+__maybe_unused
 static unsigned bch2_metadata_version = bcachefs_metadata_version_current;
 module_param_named(version, bch2_metadata_version, uint, 0400);