diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2022-02-26 21:35:16 -0500 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2022-03-12 20:14:07 -0500 |
commit | 1c4ff67d5c2b922cc6450b3a0c56365a6bd3eb11 (patch) | |
tree | c2547c2c22d50b04468d5e2f08cec10d965723c8 | |
parent | 58c27219b92d9b68834a489796b600c0d6ed4d7d (diff) |
bcachefs: Fix locking in btree_node_write_done()
There was a rare recursive locking bug, in __bch2_btree_node_write()
nowrite path -> btree_node_write_done(), in the path that kicks off
another write.
This splits out an inner __btree_node_write_done() that expects to be
run with the btree node lock held.
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r-- | fs/bcachefs/btree_io.c | 25 |
1 files changed, 7 insertions, 18 deletions
diff --git a/fs/bcachefs/btree_io.c b/fs/bcachefs/btree_io.c index ef1e301f201c..c0b59e727eaf 100644 --- a/fs/bcachefs/btree_io.c +++ b/fs/bcachefs/btree_io.c @@ -1596,7 +1596,7 @@ void bch2_btree_complete_write(struct bch_fs *c, struct btree *b, bch2_journal_pin_drop(&c->journal, &w->journal); } -static void btree_node_write_done(struct bch_fs *c, struct btree *b) +static void __btree_node_write_done(struct bch_fs *c, struct btree *b) { struct btree_write *w = btree_prev_write(b); unsigned long old, new, v; @@ -1607,22 +1607,6 @@ static void btree_node_write_done(struct bch_fs *c, struct btree *b) do { old = new = v; - if (old & (1U << BTREE_NODE_need_write)) - goto do_write; - - new &= ~(1U << BTREE_NODE_write_in_flight); - new &= ~(1U << BTREE_NODE_write_in_flight_inner); - } while ((v = cmpxchg(&b->flags, old, new)) != old); - - wake_up_bit(&b->flags, BTREE_NODE_write_in_flight); - return; - -do_write: - six_lock_read(&b->c.lock, NULL, NULL); - v = READ_ONCE(b->flags); - do { - old = new = v; - if ((old & (1U << BTREE_NODE_dirty)) && (old & (1U << BTREE_NODE_need_write)) && !(old & (1U << BTREE_NODE_never_write)) && @@ -1641,7 +1625,12 @@ do_write: if (new & (1U << BTREE_NODE_write_in_flight)) __bch2_btree_node_write(c, b, true); +} +static void btree_node_write_done(struct bch_fs *c, struct btree *b) +{ + six_lock_read(&b->c.lock, NULL, NULL); + __btree_node_write_done(c, b); six_unlock_read(&b->c.lock); } @@ -1995,7 +1984,7 @@ err: b->written += sectors_to_write; nowrite: btree_bounce_free(c, bytes, used_mempool, data); - btree_node_write_done(c, b); + __btree_node_write_done(c, b); } /* |