Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d9b01934 authored by Ted Ts'o's avatar Ted Ts'o Committed by Jan Kara
Browse files

jbd: fix fsync() tid wraparound bug



If an application program does not make any changes to the indirect
blocks or extent tree, i_datasync_tid will not get updated.  If there
are enough commits (i.e., 2**31) such that tid_geq()'s calculations
wrap, and there isn't a currently active transaction at the time of
the fdatasync() call, this can end up triggering a BUG_ON in
fs/jbd/commit.c:

	J_ASSERT(journal->j_running_transaction != NULL);

It's pretty rare that this can happen, since it requires the use of
fdatasync() plus *very* frequent and excessive use of fsync().  But
with the right workload, it can.

We fix this by replacing the use of tid_geq() with an equality test,
since there's only one valid transaction id that is valid for us to
start: namely, the currently running transaction (if it exists).

CC: stable@kernel.org
Reported-by: default avatar <Martin_Zielinski@McAfee.com>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 86c4f6d8
Loading
Loading
Loading
Loading
+13 −3
Original line number Original line Diff line number Diff line
@@ -437,9 +437,12 @@ int __log_space_left(journal_t *journal)
int __log_start_commit(journal_t *journal, tid_t target)
int __log_start_commit(journal_t *journal, tid_t target)
{
{
	/*
	/*
	 * Are we already doing a recent enough commit?
	 * The only transaction we can possibly wait upon is the
	 * currently running transaction (if it exists).  Otherwise,
	 * the target tid must be an old one.
	 */
	 */
	if (!tid_geq(journal->j_commit_request, target)) {
	if (journal->j_running_transaction &&
	    journal->j_running_transaction->t_tid == target) {
		/*
		/*
		 * We want a new commit: OK, mark the request and wakeup the
		 * We want a new commit: OK, mark the request and wakeup the
		 * commit thread.  We do _not_ do the commit ourselves.
		 * commit thread.  We do _not_ do the commit ourselves.
@@ -451,7 +454,14 @@ int __log_start_commit(journal_t *journal, tid_t target)
			  journal->j_commit_sequence);
			  journal->j_commit_sequence);
		wake_up(&journal->j_wait_commit);
		wake_up(&journal->j_wait_commit);
		return 1;
		return 1;
	}
	} else if (!tid_geq(journal->j_commit_request, target))
		/* This should never happen, but if it does, preserve
		   the evidence before kjournald goes into a loop and
		   increments j_commit_sequence beyond all recognition. */
		WARN_ONCE(1, "jbd: bad log_start_commit: %u %u %u %u\n",
		    journal->j_commit_request, journal->j_commit_sequence,
		    target, journal->j_running_transaction ?
		    journal->j_running_transaction->t_tid : 0);
	return 0;
	return 0;
}
}