From 2e8d686a4a13c01d9a2b329507a0f5ce6455b5a8 Mon Sep 17 00:00:00 2001
From: Kent Overstreet <kent.overstreet@linux.dev>
Date: Tue, 4 Jun 2024 18:31:13 -0400
Subject: [PATCH] bcachefs: Coalesce accounting keys before journal replay

This fixes a performance regression in journal replay; without
colaescing accounting keys we have multiple keys at the same position,
which means journal_keys_peek_upto() has to skip past many overwritten
keys - turning journal replay into an O(n^2) algorithm.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
---
 fs/bcachefs/btree_journal_iter.h |  2 ++
 fs/bcachefs/disk_accounting.c    | 18 ++++++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/fs/bcachefs/btree_journal_iter.h b/fs/bcachefs/btree_journal_iter.h
index 5b66c8f85fc1..1653de9d609b 100644
--- a/fs/bcachefs/btree_journal_iter.h
+++ b/fs/bcachefs/btree_journal_iter.h
@@ -2,6 +2,8 @@
 #ifndef _BCACHEFS_BTREE_JOURNAL_ITER_H
 #define _BCACHEFS_BTREE_JOURNAL_ITER_H
 
+#include "bkey.h"
+
 struct journal_iter {
 	struct list_head	list;
 	enum btree_id		btree_id;
diff --git a/fs/bcachefs/disk_accounting.c b/fs/bcachefs/disk_accounting.c
index 2cc2e0f8cb53..dbdc16f2fc1c 100644
--- a/fs/bcachefs/disk_accounting.c
+++ b/fs/bcachefs/disk_accounting.c
@@ -2,6 +2,7 @@
 
 #include "bcachefs.h"
 #include "bcachefs_ioctl.h"
+#include "btree_journal_iter.h"
 #include "btree_update.h"
 #include "btree_write_buffer.h"
 #include "buckets.h"
@@ -344,7 +345,9 @@ int bch2_accounting_read(struct bch_fs *c)
 		goto err;
 
 	struct journal_keys *keys = &c->journal_keys;
+	struct journal_key *dst = keys->data;
 	move_gap(keys, keys->nr);
+
 	darray_for_each(*keys, i) {
 		if (i->k->k.type == KEY_TYPE_accounting) {
 			struct bkey_s_c k = bkey_i_to_s_c(i->k);
@@ -358,11 +361,26 @@ int bch2_accounting_read(struct bch_fs *c)
 			if (applied)
 				continue;
 
+			if (i + 1 < &darray_top(*keys) &&
+			    i[1].k->k.type == KEY_TYPE_accounting &&
+			    !journal_key_cmp(i, i + 1)) {
+				BUG_ON(bversion_cmp(i[0].k->k.version, i[1].k->k.version) >= 0);
+
+				i[1].journal_seq = i[0].journal_seq;
+
+				bch2_accounting_accumulate(bkey_i_to_accounting(i[1].k),
+							   bkey_s_c_to_accounting(k));
+				continue;
+			}
+
 			ret = accounting_read_key(c, k);
 			if (ret)
 				goto err;
 		}
+
+		*dst++ = *i;
 	}
+	keys->gap = keys->nr = dst - keys->data;
 
 	percpu_down_read(&c->mark_lock);
 	preempt_disable();