summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2024-06-06 14:33:27 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2024-07-14 19:00:15 -0400
commit820b9efeb142a45b2c55df0806feb34936025c2a (patch)
treefb03d8bfa6b466ac0efd712aa23edbfacacf172f /fs
parentf73e6bb6d6c70b72aff021237b8c4722cc43a919 (diff)
downloadlwn-820b9efeb142a45b2c55df0806feb34936025c2a.tar.gz
lwn-820b9efeb142a45b2c55df0806feb34936025c2a.zip
bcachefs: Fix bch2_gc_accounting_done() locking
The transaction commit path takes mark_lock, so we shouldn't be holding it; use a bpos as an iterator so that we can drop and retake. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Diffstat (limited to 'fs')
-rw-r--r--fs/bcachefs/disk_accounting.c18
-rw-r--r--fs/bcachefs/eytzinger.h11
2 files changed, 25 insertions, 4 deletions
diff --git a/fs/bcachefs/disk_accounting.c b/fs/bcachefs/disk_accounting.c
index 5b1546d1a23d..dcdd59249c23 100644
--- a/fs/bcachefs/disk_accounting.c
+++ b/fs/bcachefs/disk_accounting.c
@@ -448,18 +448,26 @@ int bch2_gc_accounting_done(struct bch_fs *c)
struct bch_accounting_mem *acc = &c->accounting;
struct btree_trans *trans = bch2_trans_get(c);
struct printbuf buf = PRINTBUF;
+ struct bpos pos = POS_MIN;
int ret = 0;
- percpu_down_read(&c->mark_lock);
+ percpu_down_write(&c->mark_lock);
+ while (1) {
+ unsigned idx = eytzinger0_find_ge(acc->k.data, acc->k.nr, sizeof(acc->k.data[0]),
+ accounting_pos_cmp, &pos);
+
+ if (idx >= acc->k.nr)
+ break;
+
+ struct accounting_mem_entry *e = acc->k.data + idx;
+ pos = bpos_successor(e->pos);
- darray_for_each(acc->k, e) {
struct disk_accounting_pos acc_k;
bpos_to_disk_accounting_pos(&acc_k, e->pos);
u64 src_v[BCH_ACCOUNTING_MAX_COUNTERS];
u64 dst_v[BCH_ACCOUNTING_MAX_COUNTERS];
- unsigned idx = e - acc->k.data;
unsigned nr = e->nr_counters;
bch2_accounting_mem_read_counters(acc, idx, dst_v, nr, false);
bch2_accounting_mem_read_counters(acc, idx, src_v, nr, true);
@@ -481,8 +489,10 @@ int bch2_gc_accounting_done(struct bch_fs *c)
src_v[j] -= dst_v[j];
if (fsck_err(trans, accounting_mismatch, "%s", buf.buf)) {
+ percpu_up_write(&c->mark_lock);
ret = commit_do(trans, NULL, NULL, 0,
bch2_disk_accounting_mod(trans, &acc_k, src_v, nr, false));
+ percpu_down_write(&c->mark_lock);
if (ret)
goto err;
@@ -504,7 +514,7 @@ int bch2_gc_accounting_done(struct bch_fs *c)
}
err:
fsck_err:
- percpu_up_read(&c->mark_lock);
+ percpu_up_write(&c->mark_lock);
printbuf_exit(&buf);
bch2_trans_put(trans);
bch_err_fn(c, ret);
diff --git a/fs/bcachefs/eytzinger.h b/fs/bcachefs/eytzinger.h
index 795f4fc0bab1..0541192d7bc0 100644
--- a/fs/bcachefs/eytzinger.h
+++ b/fs/bcachefs/eytzinger.h
@@ -286,6 +286,17 @@ static inline int eytzinger0_find_gt(void *base, size_t nr, size_t size,
return eytzinger0_next(idx, nr);
}
+static inline int eytzinger0_find_ge(void *base, size_t nr, size_t size,
+ cmp_func_t cmp, const void *search)
+{
+ ssize_t idx = eytzinger0_find_le(base, nr, size, cmp, search);
+
+ if (idx < nr && !cmp(base + idx * size, search))
+ return idx;
+
+ return eytzinger0_next(idx, nr);
+}
+
#define eytzinger0_find(base, nr, size, _cmp, search) \
({ \
void *_base = (base); \