bcachefs: Fix race between journal_seq_copy() and journal_seq_drop()
authorKent Overstreet <kent.overstreet@gmail.com>
Wed, 16 Dec 2020 20:41:29 +0000 (15:41 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:50 +0000 (17:08 -0400)
In bch2_btree_interior_update_will_free_node, we copy the journal pins
from outstanding writes on the btree node we're about to free. But, this
can race with the writes completing, and dropping their journal pins.

To guard against this, just use READ_ONCE() in bch2_journal_pin_copy().

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

index e8fd11abe4c3a535b7bb73ca3bbe7bdd55ee2f85..1141b7d3a06053f278fc53573167676fc9575909 100644 (file)
@@ -384,12 +384,22 @@ void bch2_journal_pin_set(struct journal *j, u64 seq,
        struct journal_entry_pin_list *pin_list;
 
        spin_lock(&j->lock);
+
+       if (seq < journal_last_seq(j)) {
+               /*
+                * bch2_journal_pin_copy() raced with bch2_journal_pin_drop() on
+                * the src pin - with the pin dropped, the entry to pin might no
+                * longer to exist, but that means there's no longer anything to
+                * copy and we can bail out here:
+                */
+               spin_unlock(&j->lock);
+               return;
+       }
+
        pin_list = journal_seq_pin(j, seq);
 
        __journal_pin_drop(j, pin);
 
-       BUG_ON(!atomic_read(&pin_list->count) && seq == journal_last_seq(j));
-
        atomic_inc(&pin_list->count);
        pin->seq        = seq;
        pin->flush      = flush_fn;
index f02caa3d49ea74daf97d1054cc3ddbfba250d254..adf1f5c981cdfa80e227516d7c203306a543eba3 100644 (file)
@@ -53,8 +53,11 @@ static inline void bch2_journal_pin_copy(struct journal *j,
                                         struct journal_entry_pin *src,
                                         journal_pin_flush_fn flush_fn)
 {
-       if (journal_pin_active(src))
-               bch2_journal_pin_add(j, src->seq, dst, flush_fn);
+       /* Guard against racing with journal_pin_drop(src): */
+       u64 seq = READ_ONCE(src->seq);
+
+       if (seq)
+               bch2_journal_pin_add(j, seq, dst, flush_fn);
 }
 
 static inline void bch2_journal_pin_update(struct journal *j, u64 seq,