bcachefs: Fix early startup error path
authorKent Overstreet <kent.overstreet@linux.dev>
Fri, 18 Apr 2025 00:30:18 +0000 (20:30 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 20 Apr 2025 23:41:38 +0000 (19:41 -0400)
Don't set JOURNAL_running until we're also calling
journal_space_available() for the first time.

If JOURNAL_running is set, shutdown will write an empty journal entry -
but this will hit an assert in journal_entry_open() if we've never
called journal_space_available().

Reported-by: syzbot+53bb24d476ef8368a7f0@syzkaller.appspotmail.com
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/journal.c
fs/bcachefs/journal.h
fs/bcachefs/recovery.c
fs/bcachefs/super.c

index d8f74b6d0a75a8260269bd29811a14dfb4196de9..84cb74ba91e621b0eeb01a4b34938e278be56a8b 100644 (file)
@@ -1462,8 +1462,6 @@ int bch2_fs_journal_start(struct journal *j, u64 cur_seq)
                j->last_empty_seq = cur_seq - 1; /* to match j->seq */
 
        spin_lock(&j->lock);
-
-       set_bit(JOURNAL_running, &j->flags);
        j->last_flush_write = jiffies;
 
        j->reservations.idx = journal_cur_seq(j);
@@ -1474,6 +1472,21 @@ int bch2_fs_journal_start(struct journal *j, u64 cur_seq)
        return 0;
 }
 
+void bch2_journal_set_replay_done(struct journal *j)
+{
+       /*
+        * journal_space_available must happen before setting JOURNAL_running
+        * JOURNAL_running must happen before JOURNAL_replay_done
+        */
+       spin_lock(&j->lock);
+       bch2_journal_space_available(j);
+
+       set_bit(JOURNAL_need_flush_write, &j->flags);
+       set_bit(JOURNAL_running, &j->flags);
+       set_bit(JOURNAL_replay_done, &j->flags);
+       spin_unlock(&j->lock);
+}
+
 /* init/exit: */
 
 void bch2_dev_journal_exit(struct bch_dev *ca)
index 47828771f9c2395f070949614d24dfd0c69182fe..641e20c05a147d8cab51940a528492f1b748cc59 100644 (file)
@@ -437,12 +437,6 @@ static inline int bch2_journal_error(struct journal *j)
 
 struct bch_dev;
 
-static inline void bch2_journal_set_replay_done(struct journal *j)
-{
-       BUG_ON(!test_bit(JOURNAL_running, &j->flags));
-       set_bit(JOURNAL_replay_done, &j->flags);
-}
-
 void bch2_journal_unblock(struct journal *);
 void bch2_journal_block(struct journal *);
 struct journal_buf *bch2_next_write_buffer_flush_journal_buf(struct journal *, u64, bool *);
@@ -459,6 +453,7 @@ void bch2_dev_journal_stop(struct journal *, struct bch_dev *);
 
 void bch2_fs_journal_stop(struct journal *);
 int bch2_fs_journal_start(struct journal *, u64);
+void bch2_journal_set_replay_done(struct journal *);
 
 void bch2_dev_journal_exit(struct bch_dev *);
 int bch2_dev_journal_init(struct bch_dev *, struct bch_sb *);
index 606d684e6f23fc6a8055bbbcfe12270561a17869..bea578e339886c898c625e27e721dcd17673e155 100644 (file)
@@ -1129,13 +1129,13 @@ int bch2_fs_initialize(struct bch_fs *c)
        if (ret)
                goto err;
 
-       set_bit(BCH_FS_accounting_replay_done, &c->flags);
-       bch2_journal_set_replay_done(&c->journal);
-
        ret = bch2_fs_read_write_early(c);
        if (ret)
                goto err;
 
+       set_bit(BCH_FS_accounting_replay_done, &c->flags);
+       bch2_journal_set_replay_done(&c->journal);
+
        for_each_member_device(c, ca) {
                ret = bch2_dev_usage_init(ca, false);
                if (ret) {
index e8a17ed1615d30ebfd2bcfe0ef541d0903331027..a8bc02540cce750782bef7586668bc4d1e628dd0 100644 (file)
@@ -466,29 +466,28 @@ static int __bch2_fs_read_write(struct bch_fs *c, bool early)
 
        clear_bit(BCH_FS_clean_shutdown, &c->flags);
 
+       __for_each_online_member(c, ca, BIT(BCH_MEMBER_STATE_rw), READ) {
+               bch2_dev_allocator_add(c, ca);
+               percpu_ref_reinit(&ca->io_ref[WRITE]);
+       }
+       bch2_recalc_capacity(c);
+
        /*
         * First journal write must be a flush write: after a clean shutdown we
         * don't read the journal, so the first journal write may end up
         * overwriting whatever was there previously, and there must always be
         * at least one non-flush write in the journal or recovery will fail:
         */
+       spin_lock(&c->journal.lock);
        set_bit(JOURNAL_need_flush_write, &c->journal.flags);
        set_bit(JOURNAL_running, &c->journal.flags);
-
-       __for_each_online_member(c, ca, BIT(BCH_MEMBER_STATE_rw), READ) {
-               bch2_dev_allocator_add(c, ca);
-               percpu_ref_reinit(&ca->io_ref[WRITE]);
-       }
-       bch2_recalc_capacity(c);
+       bch2_journal_space_available(&c->journal);
+       spin_unlock(&c->journal.lock);
 
        ret = bch2_fs_mark_dirty(c);
        if (ret)
                goto err;
 
-       spin_lock(&c->journal.lock);
-       bch2_journal_space_available(&c->journal);
-       spin_unlock(&c->journal.lock);
-
        ret = bch2_journal_reclaim_start(&c->journal);
        if (ret)
                goto err;