Commit 2ef995df authored by Jeongjun Park's avatar Jeongjun Park Committed by Kent Overstreet
Browse files

bcachefs: fix deadlock in journal_entry_open()



In the previous commit b3d82c2f, code was added to prevent journal sequence
overflow. Among them, the code added to journal_entry_open() uses the
bch2_fs_fatal_err_on() function to handle errors.

However, __journal_res_get() , which calls journal_entry_open() , calls
journal_entry_open() while holding journal->lock , but bch2_fs_fatal_err_on()
internally tries to acquire journal->lock , which results in a deadlock.

So we need to add a locked helper to handle fatal errors even when the
journal->lock is held.

Fixes: b3d82c2f ("bcachefs: Guard against journal seq overflow")
Signed-off-by: default avatarJeongjun Park <aha310510@gmail.com>
Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent 6b37037d
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -319,6 +319,16 @@ void bch2_journal_halt(struct journal *j)
	spin_unlock(&j->lock);
}

void bch2_journal_halt_locked(struct journal *j)
{
	lockdep_assert_held(&j->lock);

	__journal_entry_close(j, JOURNAL_ENTRY_ERROR_VAL, true);
	if (!j->err_seq)
		j->err_seq = journal_cur_seq(j);
	journal_wake(j);
}

static bool journal_entry_want_write(struct journal *j)
{
	bool ret = !journal_entry_is_open(j) ||
@@ -381,9 +391,12 @@ static int journal_entry_open(struct journal *j)
	if (nr_unwritten_journal_entries(j) == ARRAY_SIZE(j->buf))
		return JOURNAL_ERR_max_in_flight;

	if (bch2_fs_fatal_err_on(journal_cur_seq(j) >= JOURNAL_SEQ_MAX,
				 c, "cannot start: journal seq overflow"))
	if (journal_cur_seq(j) >= JOURNAL_SEQ_MAX) {
		bch_err(c, "cannot start: journal seq overflow");
		if (bch2_fs_emergency_read_only_locked(c))
			bch_err(c, "fatal error - emergency read only");
		return JOURNAL_ERR_insufficient_devices; /* -EROFS */
	}

	BUG_ON(!j->cur_entry_sectors);

+1 −0
Original line number Diff line number Diff line
@@ -409,6 +409,7 @@ bool bch2_journal_noflush_seq(struct journal *, u64, u64);
int bch2_journal_meta(struct journal *);

void bch2_journal_halt(struct journal *);
void bch2_journal_halt_locked(struct journal *);

static inline int bch2_journal_error(struct journal *j)
{
+11 −0
Original line number Diff line number Diff line
@@ -411,6 +411,17 @@ bool bch2_fs_emergency_read_only(struct bch_fs *c)
	return ret;
}

bool bch2_fs_emergency_read_only_locked(struct bch_fs *c)
{
	bool ret = !test_and_set_bit(BCH_FS_emergency_ro, &c->flags);

	bch2_journal_halt_locked(&c->journal);
	bch2_fs_read_only_async(c);

	wake_up(&bch2_read_only_wait);
	return ret;
}

static int bch2_fs_read_write_late(struct bch_fs *c)
{
	int ret;
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ int bch2_dev_resize(struct bch_fs *, struct bch_dev *, u64);
struct bch_dev *bch2_dev_lookup(struct bch_fs *, const char *);

bool bch2_fs_emergency_read_only(struct bch_fs *);
bool bch2_fs_emergency_read_only_locked(struct bch_fs *);
void bch2_fs_read_only(struct bch_fs *);

int bch2_fs_read_write(struct bch_fs *);