Rewrite fsck/gc for the new accounting scheme.

This adds a second set of in-memory accounting counters for gc to use;
like with other parts of gc we run all trigger in TRIGGER_GC mode, then
compare what we calculated to existing in-memory accounting at the end.

Signed-off-by: Kent Overstreet <[email protected]>
---
 fs/bcachefs/alloc_background.c   | 181 +++++++++++-----------
 fs/bcachefs/alloc_background.h   |   2 +
 fs/bcachefs/bcachefs.h           |   4 +-
 fs/bcachefs/btree_gc.c           | 257 ++++++++++---------------------
 fs/bcachefs/btree_trans_commit.c |   4 +-
 fs/bcachefs/buckets.c            | 182 ++++------------------
 fs/bcachefs/buckets.h            |  20 +--
 fs/bcachefs/buckets_types.h      |   7 -
 fs/bcachefs/disk_accounting.c    | 171 +++++++++++++++++---
 fs/bcachefs/disk_accounting.h    |  81 ++++++----
 fs/bcachefs/ec.c                 | 148 +++++++++---------
 fs/bcachefs/inode.c              |  43 ++----
 fs/bcachefs/recovery.c           |   3 +-
 fs/bcachefs/replicas.c           |  86 +----------
 fs/bcachefs/replicas.h           |   1 -
 fs/bcachefs/super.c              |   9 +-
 16 files changed, 508 insertions(+), 691 deletions(-)

diff --git a/fs/bcachefs/alloc_background.c b/fs/bcachefs/alloc_background.c
index d8ad5bb28a7f..54cb345b104c 100644
--- a/fs/bcachefs/alloc_background.c
+++ b/fs/bcachefs/alloc_background.c
@@ -731,6 +731,96 @@ static noinline int bch2_bucket_gen_update(struct 
btree_trans *trans,
        return ret;
 }
 
+static int bch2_alloc_key_to_dev_counters(struct btree_trans *trans, struct 
bch_dev *ca,
+                                         const struct bch_alloc_v4 *old_a,
+                                         const struct bch_alloc_v4 *new_a,
+                                         unsigned flags)
+{
+       bool gc = flags & BTREE_TRIGGER_GC;
+
+       if ((flags & BTREE_TRIGGER_BUCKET_INVALIDATE) &&
+           old_a->cached_sectors) {
+               int ret = bch2_mod_dev_cached_sectors(trans, ca->dev_idx,
+                                -((s64) old_a->cached_sectors), gc);
+               if (ret)
+                       return ret;
+       }
+
+       if (old_a->data_type != new_a->data_type ||
+           old_a->dirty_sectors != new_a->dirty_sectors) {
+               struct disk_accounting_key acc = {
+                       .type = BCH_DISK_ACCOUNTING_dev_data_type,
+                       .dev_data_type.dev = ca->dev_idx,
+                       .dev_data_type.data_type = new_a->data_type,
+               };
+               s64 d[3];
+
+               if (old_a->data_type == new_a->data_type) {
+                       d[0] = 0;
+                       d[1] = (s64) new_a->dirty_sectors - (s64) 
old_a->dirty_sectors;
+                       d[2] =  bucket_sectors_fragmented(ca, *new_a) -
+                               bucket_sectors_fragmented(ca, *old_a);
+
+                       int ret = bch2_disk_accounting_mod(trans, &acc, d, 3, 
gc);
+                       if (ret)
+                               return ret;
+               } else {
+                       d[0] = 1;
+                       d[1] = new_a->dirty_sectors;
+                       d[2] = bucket_sectors_fragmented(ca, *new_a);
+
+                       int ret = bch2_disk_accounting_mod(trans, &acc, d, 3, 
gc);
+                       if (ret)
+                               return ret;
+
+                       acc.dev_data_type.data_type = old_a->data_type;
+                       d[0] = -1;
+                       d[1] = -(s64) old_a->dirty_sectors;
+                       d[2] = -bucket_sectors_fragmented(ca, *old_a);
+
+                       ret = bch2_disk_accounting_mod(trans, &acc, d, 3, gc);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       if (!!old_a->stripe != !!new_a->stripe) {
+               struct disk_accounting_key acc = {
+                       .type = BCH_DISK_ACCOUNTING_dev_stripe_buckets,
+                       .dev_stripe_buckets.dev = ca->dev_idx,
+               };
+               u64 d[1];
+
+               d[0] = (s64) !!new_a->stripe - (s64) !!old_a->stripe;
+               int ret = bch2_disk_accounting_mod(trans, &acc, d, 1, gc);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static inline struct bch_alloc_v4 bucket_m_to_alloc(struct bucket b)
+{
+       return (struct bch_alloc_v4) {
+               .gen            = b.gen,
+               .data_type      = b.data_type,
+               .dirty_sectors  = b.dirty_sectors,
+               .cached_sectors = b.cached_sectors,
+               .stripe         = b.stripe,
+       };
+}
+
+int bch2_bucket_to_dev_counters(struct btree_trans *trans, struct bch_dev *ca,
+                               struct bucket *old, struct bucket *new,
+                               unsigned flags)
+{
+       struct bch_alloc_v4 old_a = bucket_m_to_alloc(*old);
+       struct bch_alloc_v4 new_a = bucket_m_to_alloc(*new);
+
+       return bch2_alloc_key_to_dev_counters(trans, ca, &old_a, &new_a, flags);
+}
+
 int bch2_trigger_alloc(struct btree_trans *trans,
                       enum btree_id btree, unsigned level,
                       struct bkey_s_c old, struct bkey_s new,
@@ -807,70 +897,9 @@ int bch2_trigger_alloc(struct btree_trans *trans,
                                return ret;
                }
 
-               /*
-                * need to know if we're getting called from the invalidate 
path or
-                * not:
-                */
-
-               if ((flags & BTREE_TRIGGER_BUCKET_INVALIDATE) &&
-                   old_a->cached_sectors) {
-                       ret = bch2_mod_dev_cached_sectors(trans, new.k->p.inode,
-                                                         -((s64) 
old_a->cached_sectors));
-                       if (ret)
-                               return ret;
-               }
-
-
-               if (old_a->data_type != new_a->data_type ||
-                   old_a->dirty_sectors != new_a->dirty_sectors) {
-                       struct disk_accounting_key acc = {
-                               .type = BCH_DISK_ACCOUNTING_dev_data_type,
-                               .dev_data_type.dev = new.k->p.inode,
-                               .dev_data_type.data_type = new_a->data_type,
-                       };
-                       s64 d[3];
-
-                       if (old_a->data_type == new_a->data_type) {
-                               d[0] = 0;
-                               d[1] = (s64) new_a->dirty_sectors - (s64) 
old_a->dirty_sectors;
-                               d[2] =  bucket_sectors_fragmented(ca, *new_a) -
-                                       bucket_sectors_fragmented(ca, *old_a);
-
-                               ret = bch2_disk_accounting_mod(trans, &acc, d, 
3);
-                               if (ret)
-                                       return ret;
-                       } else {
-                               d[0] = 1;
-                               d[1] = new_a->dirty_sectors;
-                               d[2] = bucket_sectors_fragmented(ca, *new_a);
-
-                               ret = bch2_disk_accounting_mod(trans, &acc, d, 
3);
-                               if (ret)
-                                       return ret;
-
-                               acc.dev_data_type.data_type = old_a->data_type;
-                               d[0] = -1;
-                               d[1] = -(s64) old_a->dirty_sectors;
-                               d[2] = -bucket_sectors_fragmented(ca, *old_a);
-
-                               ret = bch2_disk_accounting_mod(trans, &acc, d, 
3);
-                               if (ret)
-                                       return ret;
-                       }
-               }
-
-               if (!!old_a->stripe != !!new_a->stripe) {
-                       struct disk_accounting_key acc = {
-                               .type = BCH_DISK_ACCOUNTING_dev_stripe_buckets,
-                               .dev_stripe_buckets.dev = new.k->p.inode,
-                       };
-                       u64 d[1];
-
-                       d[0] = (s64) !!new_a->stripe - (s64) !!old_a->stripe;
-                       ret = bch2_disk_accounting_mod(trans, &acc, d, 1);
-                       if (ret)
-                               return ret;
-               }
+               ret = bch2_alloc_key_to_dev_counters(trans, ca, old_a, new_a, 
flags);
+               if (ret)
+                       return ret;
        }
 
        if ((flags & BTREE_TRIGGER_ATOMIC) && (flags & BTREE_TRIGGER_INSERT)) {
@@ -938,30 +967,6 @@ int bch2_trigger_alloc(struct btree_trans *trans,
                        bch2_do_gc_gens(c);
        }
 
-       if ((flags & BTREE_TRIGGER_GC) &&
-           (flags & BTREE_TRIGGER_BUCKET_INVALIDATE)) {
-               struct bch_alloc_v4 new_a_convert;
-               const struct bch_alloc_v4 *new_a = bch2_alloc_to_v4(new.s_c, 
&new_a_convert);
-
-               percpu_down_read(&c->mark_lock);
-               struct bucket *g = gc_bucket(ca, new.k->p.offset);
-
-               bucket_lock(g);
-
-               g->gen_valid            = 1;
-               g->gen                  = new_a->gen;
-               g->data_type            = new_a->data_type;
-               g->stripe               = new_a->stripe;
-               g->stripe_redundancy    = new_a->stripe_redundancy;
-               g->dirty_sectors        = new_a->dirty_sectors;
-               g->cached_sectors       = new_a->cached_sectors;
-
-               bucket_unlock(g);
-               percpu_up_read(&c->mark_lock);
-
-               bch2_dev_usage_update(c, ca, old_a, new_a);
-       }
-
        return 0;
 }
 
diff --git a/fs/bcachefs/alloc_background.h b/fs/bcachefs/alloc_background.h
index 052b2fac25d6..6f273a456a6d 100644
--- a/fs/bcachefs/alloc_background.h
+++ b/fs/bcachefs/alloc_background.h
@@ -228,6 +228,8 @@ static inline bool bkey_is_alloc(const struct bkey *k)
 
 int bch2_alloc_read(struct bch_fs *);
 
+int bch2_bucket_to_dev_counters(struct btree_trans *, struct bch_dev *,
+                               struct bucket *, struct bucket *, unsigned);
 int bch2_trigger_alloc(struct btree_trans *, enum btree_id, unsigned,
                       struct bkey_s_c, struct bkey_s, unsigned);
 int bch2_check_alloc_info(struct bch_fs *);
diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h
index 22dc455cb436..41c436c608cf 100644
--- a/fs/bcachefs/bcachefs.h
+++ b/fs/bcachefs/bcachefs.h
@@ -577,7 +577,6 @@ struct bch_dev {
        struct rw_semaphore     bucket_lock;
 
        struct bch_dev_usage __percpu   *usage;
-       struct bch_dev_usage __percpu   *usage_gc;
 
        /* Allocator: */
        u64                     new_fs_bucket_idx;
@@ -761,7 +760,7 @@ struct bch_fs {
 
        struct bch_dev __rcu    *devs[BCH_SB_MEMBERS_MAX];
 
-       struct bch_accounting_mem accounting;
+       struct bch_accounting_mem accounting[2];
 
        struct bch_replicas_cpu replicas;
        struct bch_replicas_cpu replicas_gc;
@@ -906,7 +905,6 @@ struct bch_fs {
 
        seqcount_t                      usage_lock;
        struct bch_fs_usage_base __percpu *usage;
-       struct bch_fs_usage __percpu    *usage_gc;
        u64 __percpu            *online_reserved;
 
        struct io_clock         io_clock[2];
diff --git a/fs/bcachefs/btree_gc.c b/fs/bcachefs/btree_gc.c
index 15a8796197f3..54a90e88f5b8 100644
--- a/fs/bcachefs/btree_gc.c
+++ b/fs/bcachefs/btree_gc.c
@@ -18,6 +18,7 @@
 #include "buckets.h"
 #include "clock.h"
 #include "debug.h"
+#include "disk_accounting.h"
 #include "ec.h"
 #include "error.h"
 #include "extents.h"
@@ -1115,10 +1116,10 @@ static int bch2_gc_btrees(struct bch_fs *c, bool 
initial, bool metadata_only)
        return ret;
 }
 
-static void mark_metadata_sectors(struct bch_fs *c, struct bch_dev *ca,
-                                 u64 start, u64 end,
-                                 enum bch_data_type type,
-                                 unsigned flags)
+static int mark_metadata_sectors(struct btree_trans *trans, struct bch_dev *ca,
+                                u64 start, u64 end,
+                                enum bch_data_type type,
+                                unsigned flags)
 {
        u64 b = sector_to_bucket(ca, start);
 
@@ -1126,48 +1127,68 @@ static void mark_metadata_sectors(struct bch_fs *c, 
struct bch_dev *ca,
                unsigned sectors =
                        min_t(u64, bucket_to_sector(ca, b + 1), end) - start;
 
-               bch2_mark_metadata_bucket(c, ca, b, type, sectors,
-                                         gc_phase(GC_PHASE_SB), flags);
+               int ret = bch2_mark_metadata_bucket(trans, ca, b, type, sectors,
+                                                   gc_phase(GC_PHASE_SB), 
flags);
+               if (ret)
+                       return ret;
+
                b++;
                start += sectors;
        } while (start < end);
+
+       return 0;
 }
 
-static void bch2_mark_dev_superblock(struct bch_fs *c, struct bch_dev *ca,
-                                    unsigned flags)
+static int bch2_mark_dev_superblock(struct btree_trans *trans, struct bch_dev 
*ca,
+                                   unsigned flags)
 {
        struct bch_sb_layout *layout = &ca->disk_sb.sb->layout;
-       unsigned i;
-       u64 b;
 
-       for (i = 0; i < layout->nr_superblocks; i++) {
+       for (unsigned i = 0; i < layout->nr_superblocks; i++) {
                u64 offset = le64_to_cpu(layout->sb_offset[i]);
 
-               if (offset == BCH_SB_SECTOR)
-                       mark_metadata_sectors(c, ca, 0, BCH_SB_SECTOR,
-                                             BCH_DATA_sb, flags);
+               if (offset == BCH_SB_SECTOR) {
+                       int ret = mark_metadata_sectors(trans, ca, 0, 
BCH_SB_SECTOR,
+                                                       BCH_DATA_sb, flags);
+                       if (ret)
+                               return ret;
+               }
 
-               mark_metadata_sectors(c, ca, offset,
+               int ret = mark_metadata_sectors(trans, ca, offset,
                                      offset + (1 << layout->sb_max_size_bits),
                                      BCH_DATA_sb, flags);
+                       if (ret)
+                               return ret;
        }
 
-       for (i = 0; i < ca->journal.nr; i++) {
-               b = ca->journal.buckets[i];
-               bch2_mark_metadata_bucket(c, ca, b, BCH_DATA_journal,
-                                         ca->mi.bucket_size,
+       for (unsigned i = 0; i < ca->journal.nr; i++) {
+               int ret = bch2_mark_metadata_bucket(trans, ca, 
ca->journal.buckets[i],
+                                         BCH_DATA_journal, ca->mi.bucket_size,
                                          gc_phase(GC_PHASE_SB), flags);
+               if (ret)
+                       return ret;
        }
+
+       return 0;
 }
 
-static void bch2_mark_superblocks(struct bch_fs *c)
+static int bch2_mark_superblocks(struct btree_trans *trans)
 {
+       struct bch_fs *c = trans->c;
+
        mutex_lock(&c->sb_lock);
        gc_pos_set(c, gc_phase(GC_PHASE_SB));
 
-       for_each_online_member(c, ca)
-               bch2_mark_dev_superblock(c, ca, BTREE_TRIGGER_GC);
+       for_each_online_member(c, ca) {
+               int ret = bch2_mark_dev_superblock(trans, ca, BTREE_TRIGGER_GC);
+               if (ret) {
+                       percpu_ref_put(&ca->io_ref);
+                       return ret;
+               }
+       }
        mutex_unlock(&c->sb_lock);
+
+       return 0;
 }
 
 #if 0
@@ -1190,146 +1211,25 @@ static void bch2_mark_pending_btree_node_frees(struct 
bch_fs *c)
 
 static void bch2_gc_free(struct bch_fs *c)
 {
+       bch2_accounting_free(&c->accounting[1]);
+
        genradix_free(&c->reflink_gc_table);
        genradix_free(&c->gc_stripes);
 
        for_each_member_device(c, ca) {
                kvfree(rcu_dereference_protected(ca->buckets_gc, 1));
                ca->buckets_gc = NULL;
-
-               free_percpu(ca->usage_gc);
-               ca->usage_gc = NULL;
-       }
-
-       free_percpu(c->usage_gc);
-       c->usage_gc = NULL;
-}
-
-static int bch2_gc_done(struct bch_fs *c,
-                       bool initial, bool metadata_only)
-{
-       struct bch_dev *ca = NULL;
-       struct printbuf buf = PRINTBUF;
-       bool verify = !metadata_only &&
-               !c->opts.reconstruct_alloc &&
-               (!initial || (c->sb.compat & (1ULL << BCH_COMPAT_alloc_info)));
-       unsigned i;
-       int ret = 0;
-
-       percpu_down_write(&c->mark_lock);
-
-#define copy_field(_err, _f, _msg, ...)                                        
\
-       if (dst->_f != src->_f &&                                       \
-           (!verify ||                                                 \
-            fsck_err(c, _err, _msg ": got %llu, should be %llu"        \
-                     , ##__VA_ARGS__, dst->_f, src->_f)))              \
-               dst->_f = src->_f
-#define copy_dev_field(_err, _f, _msg, ...)                            \
-       copy_field(_err, _f, "dev %u has wrong " _msg, ca->dev_idx, 
##__VA_ARGS__)
-#define copy_fs_field(_err, _f, _msg, ...)                             \
-       copy_field(_err, _f, "fs has wrong " _msg, ##__VA_ARGS__)
-
-       __for_each_member_device(c, ca) {
-               /* XXX */
-               struct bch_dev_usage *dst = this_cpu_ptr(ca->usage);
-               struct bch_dev_usage *src = (void *)
-                       bch2_acc_percpu_u64s((u64 __percpu *) ca->usage_gc,
-                                            dev_usage_u64s());
-
-               for (i = 0; i < BCH_DATA_NR; i++) {
-                       copy_dev_field(dev_usage_buckets_wrong,
-                                      d[i].buckets,    "%s buckets", 
bch2_data_type_str(i));
-                       copy_dev_field(dev_usage_sectors_wrong,
-                                      d[i].sectors,    "%s sectors", 
bch2_data_type_str(i));
-                       copy_dev_field(dev_usage_fragmented_wrong,
-                                      d[i].fragmented, "%s fragmented", 
bch2_data_type_str(i));
-               }
-       }
-
-       {
-#if 0
-               unsigned nr = fs_usage_u64s(c);
-               /* XX: */
-               struct bch_fs_usage *dst = this_cpu_ptr(c->usage);
-               struct bch_fs_usage *src = (void *)
-                       bch2_acc_percpu_u64s((u64 __percpu *) c->usage_gc, nr);
-
-               copy_fs_field(fs_usage_hidden_wrong,
-                             b.hidden,         "hidden");
-               copy_fs_field(fs_usage_btree_wrong,
-                             b.btree,          "btree");
-
-               if (!metadata_only) {
-                       copy_fs_field(fs_usage_data_wrong,
-                                     b.data,   "data");
-                       copy_fs_field(fs_usage_cached_wrong,
-                                     b.cached, "cached");
-                       copy_fs_field(fs_usage_reserved_wrong,
-                                     b.reserved,       "reserved");
-                       copy_fs_field(fs_usage_nr_inodes_wrong,
-                                     b.nr_inodes,"nr_inodes");
-
-                       for (i = 0; i < BCH_REPLICAS_MAX; i++)
-                               
copy_fs_field(fs_usage_persistent_reserved_wrong,
-                                             persistent_reserved[i],
-                                             "persistent_reserved[%i]", i);
-               }
-
-               for (i = 0; i < c->replicas.nr; i++) {
-                       struct bch_replicas_entry_v1 *e =
-                               cpu_replicas_entry(&c->replicas, i);
-
-                       if (metadata_only &&
-                           (e->data_type == BCH_DATA_user ||
-                            e->data_type == BCH_DATA_cached))
-                               continue;
-
-                       printbuf_reset(&buf);
-                       bch2_replicas_entry_to_text(&buf, e);
-
-                       copy_fs_field(fs_usage_replicas_wrong,
-                                     replicas[i], "%s", buf.buf);
-               }
-#endif
        }
-
-#undef copy_fs_field
-#undef copy_dev_field
-#undef copy_stripe_field
-#undef copy_field
-fsck_err:
-       if (ca)
-               percpu_ref_put(&ca->ref);
-       bch_err_fn(c, ret);
-
-       percpu_up_write(&c->mark_lock);
-       printbuf_exit(&buf);
-       return ret;
 }
 
 static int bch2_gc_start(struct bch_fs *c)
 {
-       BUG_ON(c->usage_gc);
-
-       c->usage_gc = __alloc_percpu_gfp(fs_usage_u64s(c) * sizeof(u64),
-                                        sizeof(u64), GFP_KERNEL);
-       if (!c->usage_gc) {
-               bch_err(c, "error allocating c->usage_gc");
-               return -BCH_ERR_ENOMEM_gc_start;
-       }
-
        for_each_member_device(c, ca) {
-               BUG_ON(ca->usage_gc);
-
-               ca->usage_gc = alloc_percpu(struct bch_dev_usage);
-               if (!ca->usage_gc) {
-                       bch_err(c, "error allocating ca->usage_gc");
+               int ret = bch2_dev_usage_init(ca, true);
+               if (ret) {
                        percpu_ref_put(&ca->ref);
-                       return -BCH_ERR_ENOMEM_gc_start;
+                       return ret;
                }
-
-               this_cpu_write(ca->usage_gc->d[BCH_DATA_free].buckets,
-                              ca->mi.nbuckets - ca->mi.first_bucket);
        }
 
        return 0;
@@ -1337,13 +1237,7 @@ static int bch2_gc_start(struct bch_fs *c)
 
 static int bch2_gc_reset(struct bch_fs *c)
 {
-       for_each_member_device(c, ca) {
-               free_percpu(ca->usage_gc);
-               ca->usage_gc = NULL;
-       }
-
-       free_percpu(c->usage_gc);
-       c->usage_gc = NULL;
+       bch2_accounting_free(&c->accounting[1]);
 
        return bch2_gc_start(c);
 }
@@ -1368,7 +1262,7 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
 {
        struct bch_fs *c = trans->c;
        struct bch_dev *ca = bch_dev_bkey_exists(c, iter->pos.inode);
-       struct bucket gc, *b;
+       struct bucket gc;
        struct bkey_i_alloc_v4 *a;
        struct bch_alloc_v4 old_convert, new;
        const struct bch_alloc_v4 *old;
@@ -1379,30 +1273,39 @@ static int bch2_alloc_write_key(struct btree_trans 
*trans,
        new = *old;
 
        percpu_down_read(&c->mark_lock);
-       b = gc_bucket(ca, iter->pos.offset);
+       gc = *gc_bucket(ca, iter->pos.offset);
+       percpu_up_read(&c->mark_lock);
 
        /*
         * b->data_type doesn't yet include need_discard & need_gc_gen states -
         * fix that here:
         */
-       type = __alloc_data_type(b->dirty_sectors,
-                                b->cached_sectors,
-                                b->stripe,
+       type = __alloc_data_type(gc.dirty_sectors,
+                                gc.cached_sectors,
+                                gc.stripe,
                                 *old,
-                                b->data_type);
-       if (b->data_type != type) {
-               struct bch_dev_usage *u;
-
-               preempt_disable();
-               u = this_cpu_ptr(ca->usage_gc);
-               u->d[b->data_type].buckets--;
-               b->data_type = type;
-               u->d[b->data_type].buckets++;
-               preempt_enable();
-       }
+                                gc.data_type);
 
-       gc = *b;
-       percpu_up_read(&c->mark_lock);
+       if (gc.data_type != type) {
+               struct disk_accounting_key acc = {
+                       .type = BCH_DISK_ACCOUNTING_dev_data_type,
+                       .dev_data_type.dev = ca->dev_idx,
+                       .dev_data_type.data_type = type,
+               };
+               u64 d[3] = { 1, 0, 0 };
+
+               ret = bch2_disk_accounting_mod(trans, &acc, d, 3, true);
+               if (ret)
+                       return ret;
+
+               acc.dev_data_type.data_type = gc.data_type;
+               d[0] = -1;
+               ret = bch2_disk_accounting_mod(trans, &acc, d, 3, true);
+               if (ret)
+                       return ret;
+
+               gc.data_type = type;
+       }
 
        if (metadata_only &&
            gc.data_type != BCH_DATA_sb &&
@@ -1778,10 +1681,12 @@ int bch2_gc(struct bch_fs *c, bool initial, bool 
metadata_only)
 again:
        gc_pos_set(c, gc_phase(GC_PHASE_START));
 
-       bch2_mark_superblocks(c);
+       ret = bch2_trans_run(c, bch2_mark_superblocks(trans));
+       bch_err_msg(c, ret, "marking superblocks");
+       if (ret)
+               goto out;
 
        ret = bch2_gc_btrees(c, initial, metadata_only);
-
        if (ret)
                goto out;
 
@@ -1823,7 +1728,7 @@ int bch2_gc(struct bch_fs *c, bool initial, bool 
metadata_only)
                ret   = bch2_gc_stripes_done(c, metadata_only) ?:
                        bch2_gc_reflink_done(c, metadata_only) ?:
                        bch2_gc_alloc_done(c, metadata_only) ?:
-                       bch2_gc_done(c, initial, metadata_only);
+                       bch2_accounting_gc_done(c);
 
                bch2_journal_unblock(&c->journal);
        }
diff --git a/fs/bcachefs/btree_trans_commit.c b/fs/bcachefs/btree_trans_commit.c
index b005e20039bb..eac9d45bcc8c 100644
--- a/fs/bcachefs/btree_trans_commit.c
+++ b/fs/bcachefs/btree_trans_commit.c
@@ -697,7 +697,7 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, 
unsigned flags,
                        a->k.version = 
journal_pos_to_bversion(&trans->journal_res,
                                                        (u64 *) entry - (u64 *) 
trans->journal_entries);
                        BUG_ON(bversion_zero(a->k.version));
-                       ret = bch2_accounting_mem_add(trans, 
accounting_i_to_s_c(a));
+                       ret = bch2_accounting_mem_add_locked(trans, 
accounting_i_to_s_c(a), false);
                        if (ret)
                                goto revert_fs_usage;
                }
@@ -784,7 +784,7 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, 
unsigned flags,
                        struct bkey_s_accounting a = 
bkey_i_to_s_accounting(entry2->start);
 
                        bch2_accounting_neg(a);
-                       bch2_accounting_mem_add(trans, a.c);
+                       bch2_accounting_mem_add_locked(trans, a.c, false);
                        bch2_accounting_neg(a);
                }
        percpu_up_read(&c->mark_lock);
diff --git a/fs/bcachefs/buckets.c b/fs/bcachefs/buckets.c
index 5e2b9aa93241..506bb580bff4 100644
--- a/fs/bcachefs/buckets.c
+++ b/fs/bcachefs/buckets.c
@@ -95,113 +95,12 @@ void bch2_dev_usage_to_text(struct printbuf *out, struct 
bch_dev_usage *usage)
        }
 }
 
-void bch2_dev_usage_update(struct bch_fs *c, struct bch_dev *ca,
-                          const struct bch_alloc_v4 *old,
-                          const struct bch_alloc_v4 *new)
-{
-       struct bch_fs_usage *fs_usage;
-       struct bch_dev_usage *u;
-
-       preempt_disable();
-       fs_usage = this_cpu_ptr(c->usage_gc);
-
-       if (data_type_is_hidden(old->data_type))
-               fs_usage->b.hidden -= ca->mi.bucket_size;
-       if (data_type_is_hidden(new->data_type))
-               fs_usage->b.hidden += ca->mi.bucket_size;
-
-       u = this_cpu_ptr(ca->usage_gc);
-
-       u->d[old->data_type].buckets--;
-       u->d[new->data_type].buckets++;
-
-       u->d[old->data_type].sectors -= bch2_bucket_sectors_dirty(*old);
-       u->d[new->data_type].sectors += bch2_bucket_sectors_dirty(*new);
-
-       u->d[BCH_DATA_cached].sectors += new->cached_sectors;
-       u->d[BCH_DATA_cached].sectors -= old->cached_sectors;
-
-       u->d[old->data_type].fragmented -= bch2_bucket_sectors_fragmented(ca, 
*old);
-       u->d[new->data_type].fragmented += bch2_bucket_sectors_fragmented(ca, 
*new);
-
-       preempt_enable();
-}
-
-static inline struct bch_alloc_v4 bucket_m_to_alloc(struct bucket b)
-{
-       return (struct bch_alloc_v4) {
-               .gen            = b.gen,
-               .data_type      = b.data_type,
-               .dirty_sectors  = b.dirty_sectors,
-               .cached_sectors = b.cached_sectors,
-               .stripe         = b.stripe,
-       };
-}
-
-void bch2_dev_usage_update_m(struct bch_fs *c, struct bch_dev *ca,
-                            struct bucket *old, struct bucket *new)
-{
-       struct bch_alloc_v4 old_a = bucket_m_to_alloc(*old);
-       struct bch_alloc_v4 new_a = bucket_m_to_alloc(*new);
-
-       bch2_dev_usage_update(c, ca, &old_a, &new_a);
-}
-
-int bch2_update_replicas(struct bch_fs *c, struct bkey_s_c k,
-                        struct bch_replicas_entry_v1 *r, s64 sectors)
-{
-       struct bch_fs_usage *fs_usage;
-       int idx, ret = 0;
-       struct printbuf buf = PRINTBUF;
-
-       percpu_down_read(&c->mark_lock);
-
-       idx = bch2_replicas_entry_idx(c, r);
-       if (idx < 0 &&
-           fsck_err(c, ptr_to_missing_replicas_entry,
-                    "no replicas entry\n  while marking %s",
-                    (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
-               percpu_up_read(&c->mark_lock);
-               ret = bch2_mark_replicas(c, r);
-               percpu_down_read(&c->mark_lock);
-
-               if (ret)
-                       goto err;
-               idx = bch2_replicas_entry_idx(c, r);
-       }
-       if (idx < 0) {
-               ret = -1;
-               goto err;
-       }
-
-       preempt_disable();
-       fs_usage = this_cpu_ptr(c->usage_gc);
-       fs_usage_data_type_to_base(&fs_usage->b, r->data_type, sectors);
-       fs_usage->replicas[idx]         += sectors;
-       preempt_enable();
-err:
-fsck_err:
-       percpu_up_read(&c->mark_lock);
-       printbuf_exit(&buf);
-       return ret;
-}
-
-static inline int update_cached_sectors(struct bch_fs *c,
-                       struct bkey_s_c k,
-                       unsigned dev, s64 sectors)
-{
-       struct bch_replicas_padded r;
-
-       bch2_replicas_entry_cached(&r.e, dev);
-
-       return bch2_update_replicas(c, k, &r.e, sectors);
-}
-
-int bch2_mark_metadata_bucket(struct bch_fs *c, struct bch_dev *ca,
+int bch2_mark_metadata_bucket(struct btree_trans *trans, struct bch_dev *ca,
                              size_t b, enum bch_data_type data_type,
                              unsigned sectors, struct gc_pos pos,
                              unsigned flags)
 {
+       struct bch_fs *c = trans->c;
        struct bucket old, new, *g;
        int ret = 0;
 
@@ -242,12 +141,15 @@ int bch2_mark_metadata_bucket(struct bch_fs *c, struct 
bch_dev *ca,
        g->data_type = data_type;
        g->dirty_sectors += sectors;
        new = *g;
-err:
        bucket_unlock(g);
-       if (!ret)
-               bch2_dev_usage_update_m(c, ca, &old, &new);
        percpu_up_read(&c->mark_lock);
+       ret = bch2_bucket_to_dev_counters(trans, ca, &old, &new, flags);
+out:
        return ret;
+err:
+       bucket_unlock(g);
+       percpu_up_read(&c->mark_lock);
+       goto out;
 }
 
 int bch2_check_bucket_ref(struct btree_trans *trans,
@@ -496,8 +398,11 @@ static int bch2_trigger_pointer(struct btree_trans *trans,
                g->data_type = bucket_data_type;
                struct bucket new = *g;
                bucket_unlock(g);
-               bch2_dev_usage_update_m(c, ca, &old, &new);
                percpu_up_read(&c->mark_lock);
+
+               ret = bch2_bucket_to_dev_counters(trans, ca, &old, &new, flags);
+               if (ret)
+                       return ret;
        }
 
        return 0;
@@ -539,7 +444,7 @@ static int bch2_trigger_stripe_ptr(struct btree_trans 
*trans,
                };
                bch2_bkey_to_replicas(&acc.replicas, bkey_i_to_s_c(&s->k_i));
                acc.replicas.data_type = data_type;
-               ret = bch2_disk_accounting_mod(trans, &acc, &sectors, 1);
+               ret = bch2_disk_accounting_mod(trans, &acc, &sectors, 1, false);
 err:
                bch2_trans_iter_exit(trans, &iter);
                return ret;
@@ -548,8 +453,6 @@ static int bch2_trigger_stripe_ptr(struct btree_trans 
*trans,
        if (flags & BTREE_TRIGGER_GC) {
                struct bch_fs *c = trans->c;
 
-               BUG_ON(!(flags & BTREE_TRIGGER_GC));
-
                struct gc_stripe *m = genradix_ptr_alloc(&c->gc_stripes, 
p.ec.idx, GFP_KERNEL);
                if (!m) {
                        bch_err(c, "error allocating memory for gc_stripes, idx 
%llu",
@@ -572,11 +475,16 @@ static int bch2_trigger_stripe_ptr(struct btree_trans 
*trans,
 
                m->block_sectors[p.ec.block] += sectors;
 
-               struct bch_replicas_padded r = m->r;
+               struct disk_accounting_key acc = {
+                       .type = BCH_DISK_ACCOUNTING_replicas,
+               };
+               memcpy(&acc.replicas, &m->r.e, replicas_entry_bytes(&m->r.e));
                mutex_unlock(&c->ec_stripes_heap_lock);
 
-               r.e.data_type = data_type;
-               bch2_update_replicas(c, k, &r.e, sectors);
+               acc.replicas.data_type = data_type;
+               int ret = bch2_disk_accounting_mod(trans, &acc, &sectors, 1, 
true);
+               if (ret)
+                       return ret;
        }
 
        return 0;
@@ -587,7 +495,6 @@ static int __trigger_extent(struct btree_trans *trans,
                            struct bkey_s_c k, unsigned flags)
 {
        bool gc = flags & BTREE_TRIGGER_GC;
-       struct bch_fs *c = trans->c;
        struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
        const union bch_extent_entry *entry;
        struct extent_ptr_decoded p;
@@ -614,11 +521,7 @@ static int __trigger_extent(struct btree_trans *trans,
 
                if (p.ptr.cached) {
                        if (!stale) {
-                               ret = !gc
-                                       ? bch2_mod_dev_cached_sectors(trans, 
p.ptr.dev, disk_sectors)
-                                       : update_cached_sectors(c, k, 
p.ptr.dev, disk_sectors);
-                               bch2_fs_fatal_err_on(ret && gc, c, "%s(): no 
replicas entry while updating cached sectors",
-                                                    __func__);
+                               ret = bch2_mod_dev_cached_sectors(trans, 
p.ptr.dev, disk_sectors, gc);
                                if (ret)
                                        return ret;
                        }
@@ -640,16 +543,7 @@ static int __trigger_extent(struct btree_trans *trans,
        }
 
        if (acc.replicas.nr_devs) {
-               ret = !gc
-                       ? bch2_disk_accounting_mod(trans, &acc, &dirty_sectors, 
1)
-                       : bch2_update_replicas(c, k, &acc.replicas, 
dirty_sectors);
-               if (unlikely(ret && gc)) {
-                       struct printbuf buf = PRINTBUF;
-
-                       bch2_bkey_val_to_text(&buf, c, k);
-                       bch2_fs_fatal_error(c, "%s(): no replicas entry for 
%s", __func__, buf.buf);
-                       printbuf_exit(&buf);
-               }
+               ret = bch2_disk_accounting_mod(trans, &acc, &dirty_sectors, 1, 
gc);
                if (ret)
                        return ret;
        }
@@ -699,36 +593,18 @@ static int __trigger_reservation(struct btree_trans 
*trans,
                                 enum btree_id btree_id, unsigned level,
                                 struct bkey_s_c k, unsigned flags)
 {
-       struct bch_fs *c = trans->c;
-       unsigned replicas = bkey_s_c_to_reservation(k).v->nr_replicas;
-       s64 sectors = (s64) k.k->size;
+       if (flags & (BTREE_TRIGGER_TRANSACTIONAL|BTREE_TRIGGER_GC)) {
+               s64 sectors = k.k->size;
 
-       if (flags & BTREE_TRIGGER_OVERWRITE)
-               sectors = -sectors;
+               if (flags & BTREE_TRIGGER_OVERWRITE)
+                       sectors = -sectors;
 
-       if (flags & BTREE_TRIGGER_TRANSACTIONAL) {
                struct disk_accounting_key acc = {
                        .type = BCH_DISK_ACCOUNTING_persistent_reserved,
-                       .persistent_reserved.nr_replicas = replicas,
+                       .persistent_reserved.nr_replicas = 
bkey_s_c_to_reservation(k).v->nr_replicas,
                };
 
-               return bch2_disk_accounting_mod(trans, &acc, &sectors, 1);
-       }
-
-       if (flags & BTREE_TRIGGER_GC) {
-               sectors *= replicas;
-
-               percpu_down_read(&c->mark_lock);
-               preempt_disable();
-
-               struct bch_fs_usage *fs_usage = this_cpu_ptr(c->usage_gc);
-
-               replicas = min(replicas, 
ARRAY_SIZE(fs_usage->persistent_reserved));
-               fs_usage->b.reserved                            += sectors;
-               fs_usage->persistent_reserved[replicas - 1]     += sectors;
-
-               preempt_enable();
-               percpu_up_read(&c->mark_lock);
+               return bch2_disk_accounting_mod(trans, &acc, &sectors, 1, flags 
& BTREE_TRIGGER_GC);
        }
 
        return 0;
diff --git a/fs/bcachefs/buckets.h b/fs/bcachefs/buckets.h
index f9d8d7b9fbd1..7b8b10f74be0 100644
--- a/fs/bcachefs/buckets.h
+++ b/fs/bcachefs/buckets.h
@@ -269,16 +269,6 @@ static inline s64 bucket_sectors_fragmented(struct bch_dev 
*ca, struct bch_alloc
 
 /* Filesystem usage: */
 
-static inline unsigned __fs_usage_u64s(unsigned nr_replicas)
-{
-       return sizeof(struct bch_fs_usage) / sizeof(u64) + nr_replicas;
-}
-
-static inline unsigned fs_usage_u64s(struct bch_fs *c)
-{
-       return __fs_usage_u64s(READ_ONCE(c->replicas.nr));
-}
-
 static inline unsigned dev_usage_u64s(void)
 {
        return sizeof(struct bch_dev_usage) / sizeof(u64);
@@ -287,19 +277,11 @@ static inline unsigned dev_usage_u64s(void)
 struct bch_fs_usage_short
 bch2_fs_usage_read_short(struct bch_fs *);
 
-void bch2_dev_usage_update(struct bch_fs *, struct bch_dev *,
-                          const struct bch_alloc_v4 *,
-                          const struct bch_alloc_v4 *);
-void bch2_dev_usage_update_m(struct bch_fs *, struct bch_dev *,
-                            struct bucket *, struct bucket *);
-int bch2_update_replicas(struct bch_fs *, struct bkey_s_c,
-                        struct bch_replicas_entry_v1 *, s64);
-
 int bch2_check_bucket_ref(struct btree_trans *, struct bkey_s_c,
                          const struct bch_extent_ptr *,
                          s64, enum bch_data_type, u8, u8, u32);
 
-int bch2_mark_metadata_bucket(struct bch_fs *, struct bch_dev *,
+int bch2_mark_metadata_bucket(struct btree_trans *, struct bch_dev *,
                              size_t, enum bch_data_type, unsigned,
                              struct gc_pos, unsigned);
 
diff --git a/fs/bcachefs/buckets_types.h b/fs/bcachefs/buckets_types.h
index 570acdf455bb..7bd1a117afe4 100644
--- a/fs/bcachefs/buckets_types.h
+++ b/fs/bcachefs/buckets_types.h
@@ -54,13 +54,6 @@ struct bch_fs_usage_base {
        u64                     nr_inodes;
 };
 
-struct bch_fs_usage {
-       /* all fields are in units of 512 byte sectors: */
-       struct bch_fs_usage_base b;
-       u64                     persistent_reserved[BCH_REPLICAS_MAX];
-       u64                     replicas[];
-};
-
 struct bch_fs_usage_short {
        u64                     capacity;
        u64                     used;
diff --git a/fs/bcachefs/disk_accounting.c b/fs/bcachefs/disk_accounting.c
index f898323f72c7..2884615adc1e 100644
--- a/fs/bcachefs/disk_accounting.c
+++ b/fs/bcachefs/disk_accounting.c
@@ -19,7 +19,7 @@ static const char * const disk_accounting_type_strs[] = {
 
 int bch2_disk_accounting_mod(struct btree_trans *trans,
                             struct disk_accounting_key *k,
-                            s64 *d, unsigned nr)
+                            s64 *d, unsigned nr, bool gc)
 {
        /* Normalize: */
        switch (k->type) {
@@ -40,11 +40,14 @@ int bch2_disk_accounting_mod(struct btree_trans *trans,
 
        memcpy_u64s_small(acc->v.d, d, nr);
 
-       return bch2_trans_update_buffered(trans, BTREE_ID_accounting, 
&acc->k_i);
+       return likely(!gc)
+               ? bch2_trans_update_buffered(trans, BTREE_ID_accounting, 
&acc->k_i)
+               : bch2_accounting_mem_add(trans, accounting_i_to_s_c(acc), 
true);
 }
 
 int bch2_mod_dev_cached_sectors(struct btree_trans *trans,
-                               unsigned dev, s64 sectors)
+                               unsigned dev, s64 sectors,
+                               bool gc)
 {
        struct disk_accounting_key acc = {
                .type = BCH_DISK_ACCOUNTING_replicas,
@@ -52,7 +55,7 @@ int bch2_mod_dev_cached_sectors(struct btree_trans *trans,
 
        bch2_replicas_entry_cached(&acc.replicas, dev);
 
-       return bch2_disk_accounting_mod(trans, &acc, &sectors, 1);
+       return bch2_disk_accounting_mod(trans, &acc, &sectors, 1, gc);
 }
 
 int bch2_accounting_invalid(struct bch_fs *c, struct bkey_s_c k,
@@ -147,7 +150,7 @@ int bch2_accounting_update_sb(struct btree_trans *trans)
        return 0;
 }
 
-static int __bch2_accounting_mem_add_slowpath(struct bch_fs *c, struct 
bkey_s_c_accounting a)
+static int __bch2_accounting_mem_add_slowpath(struct bch_fs *c, struct 
bkey_s_c_accounting a, bool gc)
 {
        struct bch_replicas_padded r;
 
@@ -155,7 +158,7 @@ static int __bch2_accounting_mem_add_slowpath(struct bch_fs 
*c, struct bkey_s_c_
            !bch2_replicas_marked_locked(c, &r.e))
                return -BCH_ERR_btree_insert_need_mark_replicas;
 
-       struct bch_accounting_mem *acc = &c->accounting;
+       struct bch_accounting_mem *acc = &c->accounting[gc];
        unsigned new_nr_counters = acc->nr_counters + 
bch2_accounting_counters(a.k);
 
        u64 __percpu *new_counters = __alloc_percpu_gfp(new_nr_counters * 
sizeof(u64),
@@ -191,19 +194,64 @@ static int __bch2_accounting_mem_add_slowpath(struct 
bch_fs *c, struct bkey_s_c_
        return 0;
 }
 
-int bch2_accounting_mem_add_slowpath(struct bch_fs *c, struct 
bkey_s_c_accounting a)
+int bch2_accounting_mem_add_slowpath(struct bch_fs *c, struct 
bkey_s_c_accounting a, bool gc)
 {
        percpu_up_read(&c->mark_lock);
        percpu_down_write(&c->mark_lock);
-       int ret = __bch2_accounting_mem_add_slowpath(c, a);
+       int ret = __bch2_accounting_mem_add_slowpath(c, a, gc);
        percpu_up_write(&c->mark_lock);
        percpu_down_read(&c->mark_lock);
        return ret;
 }
 
+/* Ensures all counters in @src exist in @dst: */
+static int copy_counters(struct bch_accounting_mem *dst,
+                        struct bch_accounting_mem *src)
+{
+       unsigned orig_dst_k_nr = dst->k.nr;
+       unsigned dst_counters = dst->nr_counters;
+
+       darray_for_each(src->k, i)
+               if (eytzinger0_find(dst->k.data, orig_dst_k_nr, 
sizeof(dst->k.data[0]),
+                                   accounting_pos_cmp, &i->pos) >= 
orig_dst_k_nr) {
+                       if (darray_push(&dst->k, ((struct 
accounting_pos_offset) {
+                                       .pos            = i->pos,
+                                       .offset         = dst_counters,
+                                       .nr_counters    = i->nr_counters })))
+                               goto err;
+
+                       dst_counters += i->nr_counters;
+               }
+
+       if (dst->k.nr == orig_dst_k_nr)
+               return 0;
+
+       u64 __percpu *new_counters = __alloc_percpu_gfp(dst_counters * 
sizeof(u64),
+                                                       sizeof(u64), 
GFP_KERNEL);
+       if (!new_counters)
+               goto err;
+
+       preempt_disable();
+       memcpy(this_cpu_ptr(new_counters),
+              bch2_acc_percpu_u64s(dst->v, dst->nr_counters),
+              dst->nr_counters * sizeof(u64));
+       preempt_enable();
+
+       free_percpu(dst->v);
+       dst->v = new_counters;
+       dst->nr_counters = dst_counters;
+
+       eytzinger0_sort(dst->k.data, dst->k.nr, sizeof(dst->k.data[0]), 
accounting_pos_cmp, NULL);
+
+       return 0;
+err:
+       dst->k.nr = orig_dst_k_nr;
+       return -BCH_ERR_ENOMEM_disk_accounting;
+}
+
 int bch2_fs_replicas_usage_read(struct bch_fs *c, darray_char *usage)
 {
-       struct bch_accounting_mem *acc = &c->accounting;
+       struct bch_accounting_mem *acc = &c->accounting[0];
        int ret = 0;
 
        darray_init(usage);
@@ -234,6 +282,85 @@ int bch2_fs_replicas_usage_read(struct bch_fs *c, 
darray_char *usage)
        return ret;
 }
 
+static int accounting_write_key(struct btree_trans *trans, struct bpos pos, 
u64 *v, unsigned nr_counters)
+{
+       struct bkey_i_accounting *a = bch2_trans_kmalloc(trans, sizeof(*a) + 
sizeof(*v) * nr_counters);
+       int ret = PTR_ERR_OR_ZERO(a);
+       if (ret)
+               return ret;
+
+       bkey_accounting_init(&a->k_i);
+       a->k.p = pos;
+       set_bkey_val_bytes(&a->k, sizeof(a->v) + sizeof(*v) * nr_counters);
+       memcpy(a->v.d, v, sizeof(*v) * nr_counters);
+
+       return bch2_btree_insert_trans(trans, BTREE_ID_accounting, &a->k_i, 0);
+}
+
+int bch2_accounting_gc_done(struct bch_fs *c)
+{
+       struct bch_accounting_mem *dst = &c->accounting[0];
+       struct bch_accounting_mem *src = &c->accounting[1];
+       struct btree_trans *trans = bch2_trans_get(c);
+       struct printbuf buf = PRINTBUF;
+       int ret = 0;
+
+       percpu_down_write(&c->mark_lock);
+
+       ret   = copy_counters(dst, src) ?:
+               copy_counters(src, dst);
+       if (ret)
+               goto err;
+
+       BUG_ON(dst->k.nr != src->k.nr);
+
+       for (unsigned i = 0; i < src->k.nr; i++) {
+               BUG_ON(src->k.data[i].nr_counters != 
dst->k.data[i].nr_counters);
+               BUG_ON(!bpos_eq(dst->k.data[i].pos, src->k.data[i].pos));
+
+               struct disk_accounting_key acc_k;
+               bpos_to_disk_accounting_key(&acc_k, src->k.data[i].pos);
+
+               unsigned nr = src->k.data[i].nr_counters;
+               u64 src_v[BCH_ACCOUNTING_MAX_COUNTERS];
+               u64 dst_v[BCH_ACCOUNTING_MAX_COUNTERS];
+
+               bch2_accounting_mem_read_counters(c, i, dst_v, nr, false);
+               bch2_accounting_mem_read_counters(c, i, src_v, nr, true);
+
+               if (memcmp(dst_v, src_v, nr * sizeof(u64))) {
+                       printbuf_reset(&buf);
+                       prt_str(&buf, "accounting mismatch for ");
+                       bch2_accounting_key_to_text(&buf, &acc_k);
+
+                       prt_str(&buf, ": got");
+                       for (unsigned j = 0; j < nr; j++)
+                               prt_printf(&buf, " %llu", dst_v[j]);
+
+                       prt_str(&buf, " should be");
+                       for (unsigned j = 0; j < nr; j++)
+                               prt_printf(&buf, " %llu", src_v[j]);
+
+                       if (fsck_err(c, accounting_mismatch, "%s", buf.buf)) {
+                               for (unsigned j = 0; j < 
dst->k.data[i].nr_counters; j++)
+                                       percpu_u64_set(dst->v + 
dst->k.data[i].offset + j, src_v[j]);
+
+                               ret = commit_do(trans, NULL, NULL, 0,
+                                               accounting_write_key(trans, 
src->k.data[i].pos, src_v, nr));
+                               if (ret)
+                                       goto err;
+                       }
+               }
+       }
+err:
+fsck_err:
+       percpu_up_write(&c->mark_lock);
+       printbuf_exit(&buf);
+       bch2_trans_put(trans);
+       bch_err_fn(c, ret);
+       return ret;
+}
+
 static bool accounting_key_is_zero(struct bkey_s_c_accounting a)
 {
 
@@ -251,7 +378,7 @@ static int accounting_read_key(struct bch_fs *c, struct 
bkey_s_c k)
                return 0;
 
        percpu_down_read(&c->mark_lock);
-       int ret = __bch2_accounting_mem_add(c, bkey_s_c_to_accounting(k));
+       int ret = __bch2_accounting_mem_add(c, bkey_s_c_to_accounting(k), 
false);
        percpu_up_read(&c->mark_lock);
 
        if (accounting_key_is_zero(bkey_s_c_to_accounting(k)) &&
@@ -274,7 +401,7 @@ static int accounting_read_key(struct bch_fs *c, struct 
bkey_s_c k)
 
 int bch2_accounting_read(struct bch_fs *c)
 {
-       struct bch_accounting_mem *acc = &c->accounting;
+       struct bch_accounting_mem *acc = &c->accounting[0];
 
        int ret = bch2_trans_run(c,
                for_each_btree_key(trans, iter,
@@ -321,7 +448,7 @@ int bch2_accounting_read(struct bch_fs *c)
                bpos_to_disk_accounting_key(&k, acc->k.data[i].pos);
 
                u64 v[BCH_ACCOUNTING_MAX_COUNTERS];
-               bch2_accounting_mem_read_counters(c, i, v, ARRAY_SIZE(v));
+               bch2_accounting_mem_read_counters(c, i, v, ARRAY_SIZE(v), 
false);
 
                switch (k.type) {
                case BCH_DISK_ACCOUNTING_persistent_reserved:
@@ -370,8 +497,9 @@ int bch2_dev_usage_remove(struct bch_fs *c, unsigned dev)
                bch2_btree_write_buffer_flush_sync(trans));
 }
 
-int bch2_dev_usage_init(struct bch_dev *ca)
+int bch2_dev_usage_init(struct bch_dev *ca, bool gc)
 {
+       struct bch_fs *c = ca->fs;
        struct disk_accounting_key acc = {
                .type = BCH_DISK_ACCOUNTING_dev_data_type,
                .dev_data_type.dev = ca->dev_idx,
@@ -379,14 +507,21 @@ int bch2_dev_usage_init(struct bch_dev *ca)
        };
        u64 v[3] = { ca->mi.nbuckets - ca->mi.first_bucket, 0, 0 };
 
-       return bch2_trans_do(ca->fs, NULL, NULL, 0,
-                            bch2_disk_accounting_mod(trans, &acc, v, 
ARRAY_SIZE(v)));
+       int ret = bch2_trans_do(c, NULL, NULL, 0,
+                       bch2_disk_accounting_mod(trans, &acc, v, ARRAY_SIZE(v), 
gc));
+       bch_err_fn(c, ret);
+       return ret;
 }
 
-void bch2_fs_accounting_exit(struct bch_fs *c)
+void bch2_accounting_free(struct bch_accounting_mem *acc)
 {
-       struct bch_accounting_mem *acc = &c->accounting;
-
        darray_exit(&acc->k);
        free_percpu(acc->v);
+       acc->v = NULL;
+       acc->nr_counters = 0;
+}
+
+void bch2_fs_accounting_exit(struct bch_fs *c)
+{
+       bch2_accounting_free(&c->accounting[0]);
 }
diff --git a/fs/bcachefs/disk_accounting.h b/fs/bcachefs/disk_accounting.h
index a8526bf43207..70ac67f4a3cb 100644
--- a/fs/bcachefs/disk_accounting.h
+++ b/fs/bcachefs/disk_accounting.h
@@ -78,11 +78,9 @@ static inline struct bpos disk_accounting_key_to_bpos(struct 
disk_accounting_key
        return ret;
 }
 
-int bch2_disk_accounting_mod(struct btree_trans *,
-                            struct disk_accounting_key *,
-                            s64 *, unsigned);
-int bch2_mod_dev_cached_sectors(struct btree_trans *trans,
-                               unsigned dev, s64 sectors);
+int bch2_disk_accounting_mod(struct btree_trans *, struct disk_accounting_key 
*,
+                            s64 *, unsigned, bool);
+int bch2_mod_dev_cached_sectors(struct btree_trans *, unsigned, s64, bool);
 
 int bch2_accounting_invalid(struct bch_fs *, struct bkey_s_c,
                            enum bkey_invalid_flags, struct printbuf *);
@@ -106,15 +104,15 @@ static inline int accounting_pos_cmp(const void *_l, 
const void *_r)
        return bpos_cmp(*l, *r);
 }
 
-int bch2_accounting_mem_add_slowpath(struct bch_fs *, struct 
bkey_s_c_accounting);
+int bch2_accounting_mem_add_slowpath(struct bch_fs *, struct 
bkey_s_c_accounting, bool);
 
-static inline int __bch2_accounting_mem_add(struct bch_fs *c, struct 
bkey_s_c_accounting a)
+static inline int __bch2_accounting_mem_add(struct bch_fs *c, struct 
bkey_s_c_accounting a, bool gc)
 {
-       struct bch_accounting_mem *acc = &c->accounting;
+       struct bch_accounting_mem *acc = &c->accounting[gc];
        unsigned idx = eytzinger0_find(acc->k.data, acc->k.nr, 
sizeof(acc->k.data[0]),
                                       accounting_pos_cmp, &a.k->p);
        if (unlikely(idx >= acc->k.nr))
-               return bch2_accounting_mem_add_slowpath(c, a);
+               return bch2_accounting_mem_add_slowpath(c, a, gc);
 
        unsigned offset = acc->k.data[idx].offset;
 
@@ -125,37 +123,48 @@ static inline int __bch2_accounting_mem_add(struct bch_fs 
*c, struct bkey_s_c_ac
        return 0;
 }
 
-static inline int bch2_accounting_mem_add(struct btree_trans *trans, struct 
bkey_s_c_accounting a)
+static inline int bch2_accounting_mem_add_locked(struct btree_trans *trans, 
struct bkey_s_c_accounting a, bool gc)
 {
        struct bch_fs *c = trans->c;
-       struct disk_accounting_key acc_k;
-       bpos_to_disk_accounting_key(&acc_k, a.k->p);
 
-       switch (acc_k.type) {
-       case BCH_DISK_ACCOUNTING_persistent_reserved:
-               trans->fs_usage_delta.reserved += 
acc_k.persistent_reserved.nr_replicas * a.v->d[0];
-               break;
-       case BCH_DISK_ACCOUNTING_replicas:
-               fs_usage_data_type_to_base(&trans->fs_usage_delta, 
acc_k.replicas.data_type, a.v->d[0]);
-               break;
-       case BCH_DISK_ACCOUNTING_dev_data_type: {
-               struct bch_dev *ca = bch_dev_bkey_exists(c, 
acc_k.dev_data_type.dev);
-
-               
this_cpu_add(ca->usage->d[acc_k.dev_data_type.data_type].buckets, a.v->d[0]);
-               
this_cpu_add(ca->usage->d[acc_k.dev_data_type.data_type].sectors, a.v->d[1]);
-               
this_cpu_add(ca->usage->d[acc_k.dev_data_type.data_type].fragmented, a.v->d[2]);
+       if (!gc) {
+               struct disk_accounting_key acc_k;
+               bpos_to_disk_accounting_key(&acc_k, a.k->p);
+
+               switch (acc_k.type) {
+               case BCH_DISK_ACCOUNTING_persistent_reserved:
+                       trans->fs_usage_delta.reserved += 
acc_k.persistent_reserved.nr_replicas * a.v->d[0];
+                       break;
+               case BCH_DISK_ACCOUNTING_replicas:
+                       fs_usage_data_type_to_base(&trans->fs_usage_delta, 
acc_k.replicas.data_type, a.v->d[0]);
+                       break;
+               case BCH_DISK_ACCOUNTING_dev_data_type: {
+                       struct bch_dev *ca = bch_dev_bkey_exists(c, 
acc_k.dev_data_type.dev);
+
+                       
this_cpu_add(ca->usage->d[acc_k.dev_data_type.data_type].buckets, a.v->d[0]);
+                       
this_cpu_add(ca->usage->d[acc_k.dev_data_type.data_type].sectors, a.v->d[1]);
+                       
this_cpu_add(ca->usage->d[acc_k.dev_data_type.data_type].fragmented, a.v->d[2]);
+               }
+               }
        }
-       }
-       return __bch2_accounting_mem_add(c, a);
+
+       return __bch2_accounting_mem_add(c, a, gc);
 }
 
-static inline void bch2_accounting_mem_read_counters(struct bch_fs *c,
-                                                    unsigned idx,
-                                                    u64 *v, unsigned nr)
+static inline int bch2_accounting_mem_add(struct btree_trans *trans, struct 
bkey_s_c_accounting a, bool gc)
+{
+       percpu_down_read(&trans->c->mark_lock);
+       int ret = bch2_accounting_mem_add_locked(trans, a, gc);
+       percpu_up_read(&trans->c->mark_lock);
+       return ret;
+}
+
+static inline void bch2_accounting_mem_read_counters(struct bch_fs *c, 
unsigned idx,
+                                                    u64 *v, unsigned nr, bool 
gc)
 {
        memset(v, 0, sizeof(*v) * nr);
 
-       struct bch_accounting_mem *acc = &c->accounting;
+       struct bch_accounting_mem *acc = &c->accounting[0];
        if (unlikely(idx >= acc->k.nr))
                return;
 
@@ -169,19 +178,23 @@ static inline void 
bch2_accounting_mem_read_counters(struct bch_fs *c,
 static inline void bch2_accounting_mem_read(struct bch_fs *c, struct bpos p,
                                            u64 *v, unsigned nr)
 {
-       struct bch_accounting_mem *acc = &c->accounting;
+       struct bch_accounting_mem *acc = &c->accounting[0];
        unsigned idx = eytzinger0_find(acc->k.data, acc->k.nr, 
sizeof(acc->k.data[0]),
                                       accounting_pos_cmp, &p);
 
-       bch2_accounting_mem_read_counters(c, idx, v, nr);
+       bch2_accounting_mem_read_counters(c, idx, v, nr, false);
 }
 
 int bch2_fs_replicas_usage_read(struct bch_fs *, darray_char *);
 
+int bch2_accounting_gc_done(struct bch_fs *);
+
 int bch2_accounting_read(struct bch_fs *);
 
 int bch2_dev_usage_remove(struct bch_fs *, unsigned);
-int bch2_dev_usage_init(struct bch_dev *);
+int bch2_dev_usage_init(struct bch_dev *, bool);
+
+void bch2_accounting_free(struct bch_accounting_mem *);
 void bch2_fs_accounting_exit(struct bch_fs *);
 
 #endif /* _BCACHEFS_DISK_ACCOUNTING_H */
diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c
index 38e5e882f4a4..bd435d385559 100644
--- a/fs/bcachefs/ec.c
+++ b/fs/bcachefs/ec.c
@@ -238,10 +238,8 @@ static int bch2_trans_mark_stripe_bucket(struct 
btree_trans *trans,
        return ret;
 }
 
-static int mark_stripe_bucket(struct btree_trans *trans,
-                             struct bkey_s_c k,
-                             unsigned ptr_idx,
-                             unsigned flags)
+static int mark_stripe_bucket(struct btree_trans *trans, struct bkey_s_c k,
+                             unsigned ptr_idx, unsigned flags)
 {
        struct bch_fs *c = trans->c;
        const struct bch_stripe *s = bkey_s_c_to_stripe(k).v;
@@ -287,13 +285,16 @@ static int mark_stripe_bucket(struct btree_trans *trans,
        g->stripe               = k.k->p.offset;
        g->stripe_redundancy    = s->nr_redundant;
        new = *g;
-err:
        bucket_unlock(g);
-       if (!ret)
-               bch2_dev_usage_update_m(c, ca, &old, &new);
        percpu_up_read(&c->mark_lock);
+       ret = bch2_bucket_to_dev_counters(trans, ca, &old, &new, flags);
+out:
        printbuf_exit(&buf);
        return ret;
+err:
+       bucket_unlock(g);
+       percpu_up_read(&c->mark_lock);
+       goto out;
 }
 
 int bch2_trigger_stripe(struct btree_trans *trans,
@@ -309,7 +310,12 @@ int bch2_trigger_stripe(struct btree_trans *trans,
        const struct bch_stripe *new_s = new.k->type == KEY_TYPE_stripe
                ? bkey_s_c_to_stripe(new).v : NULL;
 
-       if (flags & BTREE_TRIGGER_TRANSACTIONAL) {
+       BUG_ON(new_s && old_s &&
+              (new_s->nr_blocks        != old_s->nr_blocks ||
+               new_s->nr_redundant     != old_s->nr_redundant));
+
+
+       if (flags & (BTREE_TRIGGER_TRANSACTIONAL|BTREE_TRIGGER_GC)) {
                /*
                 * If the pointers aren't changing, we don't need to do 
anything:
                 */
@@ -320,9 +326,34 @@ int bch2_trigger_stripe(struct btree_trans *trans,
                            new_s->nr_blocks * sizeof(struct bch_extent_ptr)))
                        return 0;
 
-               BUG_ON(new_s && old_s &&
-                      (new_s->nr_blocks        != old_s->nr_blocks ||
-                       new_s->nr_redundant     != old_s->nr_redundant));
+               struct gc_stripe *gc = NULL;
+               if (flags & BTREE_TRIGGER_GC) {
+                       gc = genradix_ptr_alloc(&c->gc_stripes, idx, 
GFP_KERNEL);
+                       if (!gc) {
+                               bch_err(c, "error allocating memory for 
gc_stripes, idx %llu", idx);
+                               return -BCH_ERR_ENOMEM_mark_stripe;
+                       }
+
+                       /*
+                        * This will be wrong when we bring back runtime gc: we 
should
+                        * be unmarking the old key and then marking the new key
+                        *
+                        * Also: when we bring back runtime gc, locking
+                        */
+                       gc->alive       = true;
+                       gc->sectors     = le16_to_cpu(new_s->sectors);
+                       gc->nr_blocks   = new_s->nr_blocks;
+                       gc->nr_redundant        = new_s->nr_redundant;
+
+                       for (unsigned i = 0; i < new_s->nr_blocks; i++)
+                               gc->ptrs[i] = new_s->ptrs[i];
+
+                       /*
+                        * gc recalculates this field from stripe ptr
+                        * references:
+                        */
+                       memset(gc->block_sectors, 0, sizeof(gc->block_sectors));
+               }
 
                if (new_s) {
                        s64 sectors = (u64) le16_to_cpu(new_s->sectors) * 
new_s->nr_redundant;
@@ -331,9 +362,12 @@ int bch2_trigger_stripe(struct btree_trans *trans,
                                .type = BCH_DISK_ACCOUNTING_replicas,
                        };
                        bch2_bkey_to_replicas(&acc.replicas, new);
-                       int ret = bch2_disk_accounting_mod(trans, &acc, 
&sectors, 1);
+                       int ret = bch2_disk_accounting_mod(trans, &acc, 
&sectors, 1, gc);
                        if (ret)
                                return ret;
+
+                       if (gc)
+                               memcpy(&gc->r.e, &acc.replicas, 
replicas_entry_bytes(&acc.replicas));
                }
 
                if (old_s) {
@@ -343,29 +377,42 @@ int bch2_trigger_stripe(struct btree_trans *trans,
                                .type = BCH_DISK_ACCOUNTING_replicas,
                        };
                        bch2_bkey_to_replicas(&acc.replicas, old);
-                       int ret = bch2_disk_accounting_mod(trans, &acc, 
&sectors, 1);
+                       int ret = bch2_disk_accounting_mod(trans, &acc, 
&sectors, 1, gc);
                        if (ret)
                                return ret;
                }
 
                unsigned nr_blocks = new_s ? new_s->nr_blocks : 
old_s->nr_blocks;
-               for (unsigned i = 0; i < nr_blocks; i++) {
-                       if (new_s && old_s &&
-                           !memcmp(&new_s->ptrs[i],
-                                   &old_s->ptrs[i],
-                                   sizeof(new_s->ptrs[i])))
-                               continue;
 
-                       if (new_s) {
-                               int ret = bch2_trans_mark_stripe_bucket(trans,
-                                               bkey_s_c_to_stripe(new), i, 
false);
-                               if (ret)
-                                       return ret;
+               if (flags & BTREE_TRIGGER_TRANSACTIONAL) {
+                       for (unsigned i = 0; i < nr_blocks; i++) {
+                               if (new_s && old_s &&
+                                   !memcmp(&new_s->ptrs[i],
+                                           &old_s->ptrs[i],
+                                           sizeof(new_s->ptrs[i])))
+                                       continue;
+
+                               if (new_s) {
+                                       int ret = 
bch2_trans_mark_stripe_bucket(trans,
+                                                       
bkey_s_c_to_stripe(new), i, false);
+                                       if (ret)
+                                               return ret;
+                               }
+
+                               if (old_s) {
+                                       int ret = 
bch2_trans_mark_stripe_bucket(trans,
+                                                       
bkey_s_c_to_stripe(old), i, true);
+                                       if (ret)
+                                               return ret;
+                               }
                        }
+               }
 
-                       if (old_s) {
-                               int ret = bch2_trans_mark_stripe_bucket(trans,
-                                               bkey_s_c_to_stripe(old), i, 
true);
+               if (flags & BTREE_TRIGGER_GC) {
+                       BUG_ON(old_s);
+
+                       for (unsigned i = 0; i < nr_blocks; i++) {
+                               int ret = mark_stripe_bucket(trans, new, i, 
flags);
                                if (ret)
                                        return ret;
                        }
@@ -411,53 +458,6 @@ int bch2_trigger_stripe(struct btree_trans *trans,
                }
        }
 
-       if (flags & BTREE_TRIGGER_GC) {
-               struct gc_stripe *m =
-                       genradix_ptr_alloc(&c->gc_stripes, idx, GFP_KERNEL);
-
-               if (!m) {
-                       bch_err(c, "error allocating memory for gc_stripes, idx 
%llu",
-                               idx);
-                       return -BCH_ERR_ENOMEM_mark_stripe;
-               }
-               /*
-                * This will be wrong when we bring back runtime gc: we should
-                * be unmarking the old key and then marking the new key
-                */
-               m->alive        = true;
-               m->sectors      = le16_to_cpu(new_s->sectors);
-               m->nr_blocks    = new_s->nr_blocks;
-               m->nr_redundant = new_s->nr_redundant;
-
-               for (unsigned i = 0; i < new_s->nr_blocks; i++)
-                       m->ptrs[i] = new_s->ptrs[i];
-
-               bch2_bkey_to_replicas(&m->r.e, new);
-
-               /*
-                * gc recalculates this field from stripe ptr
-                * references:
-                */
-               memset(m->block_sectors, 0, sizeof(m->block_sectors));
-
-               for (unsigned i = 0; i < new_s->nr_blocks; i++) {
-                       int ret = mark_stripe_bucket(trans, new, i, flags);
-                       if (ret)
-                               return ret;
-               }
-
-               int ret = bch2_update_replicas(c, new, &m->r.e,
-                                     ((s64) m->sectors * m->nr_redundant));
-               if (ret) {
-                       struct printbuf buf = PRINTBUF;
-
-                       bch2_bkey_val_to_text(&buf, c, new);
-                       bch2_fs_fatal_error(c, "no replicas entry for %s", 
buf.buf);
-                       printbuf_exit(&buf);
-                       return ret;
-               }
-       }
-
        return 0;
 }
 
diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c
index 3dfa9f77c739..e8f128d6b703 100644
--- a/fs/bcachefs/inode.c
+++ b/fs/bcachefs/inode.c
@@ -607,41 +607,26 @@ int bch2_trigger_inode(struct btree_trans *trans,
                       struct bkey_s new,
                       unsigned flags)
 {
-       s64 nr = bkey_is_inode(new.k) - bkey_is_inode(old.k);
-
-       if (flags & BTREE_TRIGGER_TRANSACTIONAL) {
-               if (nr) {
-                       struct disk_accounting_key acc = {
-                               .type = BCH_DISK_ACCOUNTING_nr_inodes
-                       };
-
-                       int ret = bch2_disk_accounting_mod(trans, &acc, &nr, 1);
-                       if (ret)
-                               return ret;
-               }
-
-               bool old_deleted = bkey_is_deleted_inode(old);
-               bool new_deleted = bkey_is_deleted_inode(new.s_c);
-               if (old_deleted != new_deleted) {
-                       int ret = bch2_btree_bit_mod_buffered(trans, 
BTREE_ID_deleted_inodes,
-                                                             new.k->p, 
new_deleted);
-                       if (ret)
-                               return ret;
-               }
-       }
-
        if ((flags & BTREE_TRIGGER_ATOMIC) && (flags & BTREE_TRIGGER_INSERT)) {
                BUG_ON(!trans->journal_res.seq);
-
                bkey_s_to_inode_v3(new).v->bi_journal_seq = 
cpu_to_le64(trans->journal_res.seq);
        }
 
-       if (flags & BTREE_TRIGGER_GC) {
-               struct bch_fs *c = trans->c;
+       s64 nr = bkey_is_inode(new.k) - bkey_is_inode(old.k);
+       if ((flags & (BTREE_TRIGGER_TRANSACTIONAL|BTREE_TRIGGER_GC)) && nr) {
+               struct disk_accounting_key acc = { .type = 
BCH_DISK_ACCOUNTING_nr_inodes };
+               int ret = bch2_disk_accounting_mod(trans, &acc, &nr, 1, flags & 
BTREE_TRIGGER_GC);
+               if (ret)
+                       return ret;
+       }
 
-               percpu_down_read(&c->mark_lock);
-               this_cpu_add(c->usage_gc->b.nr_inodes, nr);
-               percpu_up_read(&c->mark_lock);
+       int deleted_delta =     (int) bkey_is_deleted_inode(new.s_c) -
+                               (int) bkey_is_deleted_inode(old);
+       if ((flags & BTREE_TRIGGER_TRANSACTIONAL) && deleted_delta) {
+               int ret = bch2_btree_bit_mod_buffered(trans, 
BTREE_ID_deleted_inodes,
+                                                     new.k->p, deleted_delta > 
0);
+               if (ret)
+                       return ret;
        }
 
        return 0;
diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c
index 18fd71960d2e..6a8b2c753688 100644
--- a/fs/bcachefs/recovery.c
+++ b/fs/bcachefs/recovery.c
@@ -1177,8 +1177,7 @@ int bch2_fs_initialize(struct bch_fs *c)
                goto err;
 
        for_each_member_device(c, ca) {
-               ret = bch2_dev_usage_init(ca);
-               bch_err_msg(c, ret, "initializing device usage");
+               ret = bch2_dev_usage_init(ca, false);
                if (ret) {
                        percpu_ref_put(&ca->ref);
                        goto err;
diff --git a/fs/bcachefs/replicas.c b/fs/bcachefs/replicas.c
index 427dc6711427..cba5ba44cfd8 100644
--- a/fs/bcachefs/replicas.c
+++ b/fs/bcachefs/replicas.c
@@ -275,73 +275,6 @@ bool bch2_replicas_marked(struct bch_fs *c,
        return ret;
 }
 
-static void __replicas_table_update(struct bch_fs_usage *dst,
-                                   struct bch_replicas_cpu *dst_r,
-                                   struct bch_fs_usage *src,
-                                   struct bch_replicas_cpu *src_r)
-{
-       int src_idx, dst_idx;
-
-       *dst = *src;
-
-       for (src_idx = 0; src_idx < src_r->nr; src_idx++) {
-               if (!src->replicas[src_idx])
-                       continue;
-
-               dst_idx = __replicas_entry_idx(dst_r,
-                               cpu_replicas_entry(src_r, src_idx));
-               BUG_ON(dst_idx < 0);
-
-               dst->replicas[dst_idx] = src->replicas[src_idx];
-       }
-}
-
-static void __replicas_table_update_pcpu(struct bch_fs_usage __percpu *dst_p,
-                                   struct bch_replicas_cpu *dst_r,
-                                   struct bch_fs_usage __percpu *src_p,
-                                   struct bch_replicas_cpu *src_r)
-{
-       unsigned src_nr = sizeof(struct bch_fs_usage) / sizeof(u64) + src_r->nr;
-       struct bch_fs_usage *dst, *src = (void *)
-               bch2_acc_percpu_u64s((u64 __percpu *) src_p, src_nr);
-
-       preempt_disable();
-       dst = this_cpu_ptr(dst_p);
-       preempt_enable();
-
-       __replicas_table_update(dst, dst_r, src, src_r);
-}
-
-/*
- * Resize filesystem accounting:
- */
-static int replicas_table_update(struct bch_fs *c,
-                                struct bch_replicas_cpu *new_r)
-{
-       struct bch_fs_usage __percpu *new_gc = NULL;
-       unsigned bytes = sizeof(struct bch_fs_usage) +
-               sizeof(u64) * new_r->nr;
-       int ret = 0;
-
-       if ((c->usage_gc &&
-            !(new_gc = __alloc_percpu_gfp(bytes, sizeof(u64), GFP_KERNEL))))
-               goto err;
-
-       if (c->usage_gc)
-               __replicas_table_update_pcpu(new_gc,            new_r,
-                                            c->usage_gc,       &c->replicas);
-
-       swap(c->usage_gc,       new_gc);
-       swap(c->replicas,       *new_r);
-out:
-       free_percpu(new_gc);
-       return ret;
-err:
-       bch_err(c, "error updating replicas table: memory allocation failure");
-       ret = -BCH_ERR_ENOMEM_replicas_table;
-       goto out;
-}
-
 noinline
 static int bch2_mark_replicas_slowpath(struct bch_fs *c,
                                struct bch_replicas_entry_v1 *new_entry)
@@ -389,7 +322,7 @@ static int bch2_mark_replicas_slowpath(struct bch_fs *c,
        /* don't update in memory replicas until changes are persistent */
        percpu_down_write(&c->mark_lock);
        if (new_r.entries)
-               ret = replicas_table_update(c, &new_r);
+               swap(c->replicas, new_r);
        if (new_gc.entries)
                swap(new_gc, c->replicas_gc);
        percpu_up_write(&c->mark_lock);
@@ -424,8 +357,9 @@ int bch2_replicas_gc_end(struct bch_fs *c, int ret)
        percpu_down_write(&c->mark_lock);
 
        ret =   ret ?:
-               bch2_cpu_replicas_to_sb_replicas(c, &c->replicas_gc) ?:
-               replicas_table_update(c, &c->replicas_gc);
+               bch2_cpu_replicas_to_sb_replicas(c, &c->replicas_gc);
+       if (!ret)
+               swap(c->replicas, c->replicas_gc);
 
        kfree(c->replicas_gc.entries);
        c->replicas_gc.entries = NULL;
@@ -635,8 +569,7 @@ int bch2_sb_replicas_to_cpu_replicas(struct bch_fs *c)
        bch2_cpu_replicas_sort(&new_r);
 
        percpu_down_write(&c->mark_lock);
-
-       ret = replicas_table_update(c, &new_r);
+       swap(c->replicas, new_r);
        percpu_up_write(&c->mark_lock);
 
        kfree(new_r.entries);
@@ -927,10 +860,8 @@ unsigned bch2_sb_dev_has_data(struct bch_sb *sb, unsigned 
dev)
 
 unsigned bch2_dev_has_data(struct bch_fs *c, struct bch_dev *ca)
 {
-       unsigned ret;
-
        mutex_lock(&c->sb_lock);
-       ret = bch2_sb_dev_has_data(c->disk_sb.sb, ca->dev_idx);
+       unsigned ret = bch2_sb_dev_has_data(c->disk_sb.sb, ca->dev_idx);
        mutex_unlock(&c->sb_lock);
 
        return ret;
@@ -941,8 +872,3 @@ void bch2_fs_replicas_exit(struct bch_fs *c)
        kfree(c->replicas.entries);
        kfree(c->replicas_gc.entries);
 }
-
-int bch2_fs_replicas_init(struct bch_fs *c)
-{
-       return replicas_table_update(c, &c->replicas);
-}
diff --git a/fs/bcachefs/replicas.h b/fs/bcachefs/replicas.h
index eac2dff20423..ab2d00e4865c 100644
--- a/fs/bcachefs/replicas.h
+++ b/fs/bcachefs/replicas.h
@@ -80,6 +80,5 @@ extern const struct bch_sb_field_ops 
bch_sb_field_ops_replicas;
 extern const struct bch_sb_field_ops bch_sb_field_ops_replicas_v0;
 
 void bch2_fs_replicas_exit(struct bch_fs *);
-int bch2_fs_replicas_init(struct bch_fs *);
 
 #endif /* _BCACHEFS_REPLICAS_H */
diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c
index 89c481831608..6617c8912e51 100644
--- a/fs/bcachefs/super.c
+++ b/fs/bcachefs/super.c
@@ -894,7 +894,6 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, 
struct bch_opts opts)
            bch2_io_clock_init(&c->io_clock[READ]) ?:
            bch2_io_clock_init(&c->io_clock[WRITE]) ?:
            bch2_fs_journal_init(&c->journal) ?:
-           bch2_fs_replicas_init(c) ?:
            bch2_fs_btree_cache_init(c) ?:
            bch2_fs_btree_key_cache_init(&c->btree_key_cache) ?:
            bch2_fs_btree_iter_init(c) ?:
@@ -1772,7 +1771,7 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
        bch2_write_super(c);
        mutex_unlock(&c->sb_lock);
 
-       ret = bch2_dev_usage_init(ca);
+       ret = bch2_dev_usage_init(ca, false);
        if (ret)
                goto err_late;
 
@@ -1946,9 +1945,9 @@ int bch2_dev_resize(struct bch_fs *c, struct bch_dev *ca, 
u64 nbuckets)
                };
                u64 v[3] = { nbuckets - old_nbuckets, 0, 0 };
 
-               ret   = bch2_dev_freespace_init(c, ca, old_nbuckets, nbuckets) 
?:
-                       bch2_trans_do(ca->fs, NULL, NULL, 0,
-                               bch2_disk_accounting_mod(trans, &acc, v, 
ARRAY_SIZE(v)));
+               ret   = bch2_trans_do(ca->fs, NULL, NULL, 0,
+                               bch2_disk_accounting_mod(trans, &acc, v, 
ARRAY_SIZE(v), false)) ?:
+                       bch2_dev_freespace_init(c, ca, old_nbuckets, nbuckets);
                if (ret)
                        goto err;
        }
-- 
2.43.0


Reply via email to