Commit 5668e5de authored by Kent Overstreet's avatar Kent Overstreet
Browse files

bcachefs: bch2_verify_accounting_clean()



Verify that the in-memory accounting verifies the on-disk accounting
after a clean shutdown.

Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 00839add
Loading
Loading
Loading
Loading
+87 −0
Original line number Diff line number Diff line
@@ -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);
+3 −1
Original line number Diff line number Diff line
@@ -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 *);

+1 −0
Original line number Diff line number Diff line
@@ -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);