Commit deeeaf13b291420fe4a4a52606b9fc9128387340
1 parent
59802db074
Exists in
master
and in
7 other branches
jbd2: 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/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>
Showing 1 changed file with 13 additions and 3 deletions Side-by-side Diff
fs/jbd2/journal.c
... | ... | @@ -479,9 +479,12 @@ |
479 | 479 | int __jbd2_log_start_commit(journal_t *journal, tid_t target) |
480 | 480 | { |
481 | 481 | /* |
482 | - * Are we already doing a recent enough commit? | |
482 | + * The only transaction we can possibly wait upon is the | |
483 | + * currently running transaction (if it exists). Otherwise, | |
484 | + * the target tid must be an old one. | |
483 | 485 | */ |
484 | - if (!tid_geq(journal->j_commit_request, target)) { | |
486 | + if (journal->j_running_transaction && | |
487 | + journal->j_running_transaction->t_tid == target) { | |
485 | 488 | /* |
486 | 489 | * We want a new commit: OK, mark the request and wakeup the |
487 | 490 | * commit thread. We do _not_ do the commit ourselves. |
... | ... | @@ -493,7 +496,14 @@ |
493 | 496 | journal->j_commit_sequence); |
494 | 497 | wake_up(&journal->j_wait_commit); |
495 | 498 | return 1; |
496 | - } | |
499 | + } else if (!tid_geq(journal->j_commit_request, target)) | |
500 | + /* This should never happen, but if it does, preserve | |
501 | + the evidence before kjournald goes into a loop and | |
502 | + increments j_commit_sequence beyond all recognition. */ | |
503 | + WARN(1, "jbd: bad log_start_commit: %u %u %u %u\n", | |
504 | + journal->j_commit_request, journal->j_commit_sequence, | |
505 | + target, journal->j_running_transaction ? | |
506 | + journal->j_running_transaction->t_tid : 0); | |
497 | 507 | return 0; |
498 | 508 | } |
499 | 509 |