]> www.infradead.org Git - nvme.git/commitdiff
bcachefs: bch2_verify_accounting_clean()
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 18 Feb 2024 05:13:22 +0000 (00:13 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 14 Jul 2024 23:00:13 +0000 (19:00 -0400)
Verify that the in-memory accounting verifies the on-disk accounting
after a clean shutdown.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/disk_accounting.c
fs/bcachefs/disk_accounting.h
fs/bcachefs/super.c

index e8dfd67eab8a34f228c463d8cb9eac8f63235b58..1d45d828061895486d8d437f533b6eb59788dfdf 100644 (file)
@@ -588,6 +588,93 @@ int bch2_dev_usage_init(struct bch_dev *ca, bool gc)
        return ret;
 }
 
+void bch2_verify_accounting_clean(struct bch_fs *c)
+{
+       bool mismatch = false;
+       struct bch_fs_usage_base base = {}, base_inmem = {};
+
+       bch2_trans_run(c,
+               for_each_btree_key(trans, iter,
+                                  BTREE_ID_accounting, POS_MIN,
+                                  BTREE_ITER_all_snapshots, k, ({
+                       u64 v[BCH_ACCOUNTING_MAX_COUNTERS];
+                       struct bkey_s_c_accounting a = bkey_s_c_to_accounting(k);
+                       unsigned nr = bch2_accounting_counters(k.k);
+
+                       bch2_accounting_mem_read(c, k.k->p, v, nr);
+
+                       if (memcmp(a.v->d, v, nr * sizeof(u64))) {
+                               struct printbuf buf = PRINTBUF;
+
+                               bch2_bkey_val_to_text(&buf, c, k);
+                               prt_str(&buf, " !=");
+                               for (unsigned j = 0; j < nr; j++)
+                                       prt_printf(&buf, " %llu", v[j]);
+
+                               pr_err("%s", buf.buf);
+                               printbuf_exit(&buf);
+                               mismatch = true;
+                       }
+
+                       struct disk_accounting_pos acc_k;
+                       bpos_to_disk_accounting_pos(&acc_k, a.k->p);
+
+                       switch (acc_k.type) {
+                       case BCH_DISK_ACCOUNTING_persistent_reserved:
+                               base.reserved += acc_k.persistent_reserved.nr_replicas * a.v->d[0];
+                               break;
+                       case BCH_DISK_ACCOUNTING_replicas:
+                               fs_usage_data_type_to_base(&base, acc_k.replicas.data_type, a.v->d[0]);
+                               break;
+                       case BCH_DISK_ACCOUNTING_dev_data_type: {
+                               rcu_read_lock();
+                               struct bch_dev *ca = bch2_dev_rcu(c, acc_k.dev_data_type.dev);
+                               if (!ca) {
+                                       rcu_read_unlock();
+                                       continue;
+                               }
+
+                               v[0] = percpu_u64_get(&ca->usage->d[acc_k.dev_data_type.data_type].buckets);
+                               v[1] = percpu_u64_get(&ca->usage->d[acc_k.dev_data_type.data_type].sectors);
+                               v[2] = percpu_u64_get(&ca->usage->d[acc_k.dev_data_type.data_type].fragmented);
+                               rcu_read_unlock();
+
+                               if (memcmp(a.v->d, v, 3 * sizeof(u64))) {
+                                       struct printbuf buf = PRINTBUF;
+
+                                       bch2_bkey_val_to_text(&buf, c, k);
+                                       prt_str(&buf, " in mem");
+                                       for (unsigned j = 0; j < nr; j++)
+                                               prt_printf(&buf, " %llu", v[j]);
+
+                                       pr_err("dev accounting mismatch: %s", buf.buf);
+                                       printbuf_exit(&buf);
+                                       mismatch = true;
+                               }
+                       }
+                       }
+
+                       0;
+               })));
+
+       acc_u64s_percpu(&base_inmem.hidden, &c->usage->hidden, sizeof(base_inmem) / sizeof(u64));
+
+#define check(x)                                                                               \
+       if (base.x != base_inmem.x) {                                                           \
+               pr_err("fs_usage_base.%s mismatch: %llu != %llu", #x, base.x, base_inmem.x);    \
+               mismatch = true;                                                                \
+       }
+
+       //check(hidden);
+       check(btree);
+       check(data);
+       check(cached);
+       check(reserved);
+       check(nr_inodes);
+
+       WARN_ON(mismatch);
+}
+
 void bch2_accounting_free(struct bch_accounting_mem *acc)
 {
        darray_exit(&acc->k);
index 76445ffd91725c80044319237f5e8fbf7922a9ea..5132b3dd17456ec0e6163b307fa5c704bb8dd70c 100644 (file)
@@ -171,7 +171,7 @@ static inline void bch2_accounting_mem_read_counters(struct bch_fs *c, unsigned
 {
        memset(v, 0, sizeof(*v) * nr);
 
-       struct bch_accounting_mem *acc = &c->accounting[0];
+       struct bch_accounting_mem *acc = &c->accounting[gc];
        if (unlikely(idx >= acc->k.nr))
                return;
 
@@ -201,6 +201,8 @@ int bch2_accounting_read(struct bch_fs *);
 int bch2_dev_usage_remove(struct bch_fs *, unsigned);
 int bch2_dev_usage_init(struct bch_dev *, bool);
 
+void bch2_verify_accounting_clean(struct bch_fs *c);
+
 void bch2_accounting_free(struct bch_accounting_mem *);
 void bch2_fs_accounting_exit(struct bch_fs *);
 
index af947f19e38899ea97cedc821f615f354a04127d..b9f851103297e9467cf479ffae6e65f341f6ec06 100644 (file)
@@ -361,6 +361,7 @@ void bch2_fs_read_only(struct bch_fs *c)
                BUG_ON(atomic_long_read(&c->btree_key_cache.nr_dirty));
                BUG_ON(c->btree_write_buffer.inc.keys.nr);
                BUG_ON(c->btree_write_buffer.flushing.keys.nr);
+               bch2_verify_accounting_clean(c);
 
                bch_verbose(c, "marking filesystem clean");
                bch2_fs_mark_clean(c);