Skip to content

Commit

Permalink
jbd2: fix fsync() tid wraparound bug
Browse files Browse the repository at this point in the history
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/jbd2/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 we is valid for us to
wait until it is commited: namely, the currently running transaction
(if it exists).

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
  • Loading branch information
Theodore Ts'o committed May 1, 2011
1 parent 59802db commit deeeaf1
Showing 1 changed file with 13 additions and 3 deletions.
16 changes: 13 additions & 3 deletions fs/jbd2/journal.c
Original file line number Diff line number Diff line change
Expand Up @@ -479,9 +479,12 @@ int __jbd2_log_space_left(journal_t *journal)
int __jbd2_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
* commit thread. We do _not_ do the commit ourselves.
Expand All @@ -493,7 +496,14 @@ int __jbd2_log_start_commit(journal_t *journal, tid_t target)
journal->j_commit_sequence);
wake_up(&journal->j_wait_commit);
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(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;
}

Expand Down

0 comments on commit deeeaf1

Please sign in to comment.