Commit ca43f73c authored by Kent Overstreet's avatar Kent Overstreet
Browse files

bcachefs: bch2_btree_write_buffer_flush_going_ro()



The write buffer needs to be specifically flushed when going RO: keys in
the journal that haven't yet been moved to the write buffer don't have a
journal pin yet.

This fixes numerous syzbot bugs, all with symptoms of still doing writes
after we've got RO.

Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 8440da93
Loading
Loading
Loading
Loading
+27 −3
Original line number Diff line number Diff line
@@ -277,6 +277,10 @@ static int bch2_btree_write_buffer_flush_locked(struct btree_trans *trans)
	bool accounting_replay_done = test_bit(BCH_FS_accounting_replay_done, &c->flags);
	int ret = 0;

	ret = bch2_journal_error(&c->journal);
	if (ret)
		return ret;

	bch2_trans_unlock(trans);
	bch2_trans_begin(trans);

@@ -491,7 +495,8 @@ static int fetch_wb_keys_from_journal(struct bch_fs *c, u64 seq)
	return ret;
}

static int btree_write_buffer_flush_seq(struct btree_trans *trans, u64 seq)
static int btree_write_buffer_flush_seq(struct btree_trans *trans, u64 seq,
					bool *did_work)
{
	struct bch_fs *c = trans->c;
	struct btree_write_buffer *wb = &c->btree_write_buffer;
@@ -502,6 +507,8 @@ static int btree_write_buffer_flush_seq(struct btree_trans *trans, u64 seq)

		fetch_from_journal_err = fetch_wb_keys_from_journal(c, seq);

		*did_work |= wb->inc.keys.nr || wb->flushing.keys.nr;

		/*
		 * On memory allocation failure, bch2_btree_write_buffer_flush_locked()
		 * is not guaranteed to empty wb->inc:
@@ -521,17 +528,34 @@ static int bch2_btree_write_buffer_journal_flush(struct journal *j,
				struct journal_entry_pin *_pin, u64 seq)
{
	struct bch_fs *c = container_of(j, struct bch_fs, journal);
	bool did_work = false;

	return bch2_trans_run(c, btree_write_buffer_flush_seq(trans, seq));
	return bch2_trans_run(c, btree_write_buffer_flush_seq(trans, seq, &did_work));
}

int bch2_btree_write_buffer_flush_sync(struct btree_trans *trans)
{
	struct bch_fs *c = trans->c;
	bool did_work = false;

	trace_and_count(c, write_buffer_flush_sync, trans, _RET_IP_);

	return btree_write_buffer_flush_seq(trans, journal_cur_seq(&c->journal));
	return btree_write_buffer_flush_seq(trans, journal_cur_seq(&c->journal), &did_work);
}

/*
 * The write buffer requires flushing when going RO: keys in the journal for the
 * write buffer don't have a journal pin yet
 */
bool bch2_btree_write_buffer_flush_going_ro(struct bch_fs *c)
{
	if (bch2_journal_error(&c->journal))
		return false;

	bool did_work = false;
	bch2_trans_run(c, btree_write_buffer_flush_seq(trans,
				journal_cur_seq(&c->journal), &did_work));
	return did_work;
}

int bch2_btree_write_buffer_flush_nocheck_rw(struct btree_trans *trans)
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ static inline bool bch2_btree_write_buffer_must_wait(struct bch_fs *c)

struct btree_trans;
int bch2_btree_write_buffer_flush_sync(struct btree_trans *);
bool bch2_btree_write_buffer_flush_going_ro(struct bch_fs *);
int bch2_btree_write_buffer_flush_nocheck_rw(struct btree_trans *);
int bch2_btree_write_buffer_tryflush(struct btree_trans *);

+1 −0
Original line number Diff line number Diff line
@@ -272,6 +272,7 @@ static void __bch2_fs_read_only(struct bch_fs *c)
		clean_passes++;

		if (bch2_btree_interior_updates_flush(c) ||
		    bch2_btree_write_buffer_flush_going_ro(c) ||
		    bch2_journal_flush_all_pins(&c->journal) ||
		    bch2_btree_flush_all_writes(c) ||
		    seq != atomic64_read(&c->journal.seq)) {