diff options
author | Jan Kara <jack@suse.com> | 2015-07-28 14:57:14 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2015-07-28 14:57:14 -0400 |
commit | 841df7df196237ea63233f0f9eaa41db53afd70f (patch) | |
tree | 0cd3bedda66c31f94c23db765d21b5a5c52162ee /fs/jbd2 | |
parent | 564bc402526e437729ecafe3c3511f7cab9f0327 (diff) |
jbd2: avoid infinite loop when destroying aborted journal
Commit 6f6a6fda2945 "jbd2: fix ocfs2 corrupt when updating journal
superblock fails" changed jbd2_cleanup_journal_tail() to return EIO
when the journal is aborted. That makes logic in
jbd2_log_do_checkpoint() bail out which is fine, except that
jbd2_journal_destroy() expects jbd2_log_do_checkpoint() to always make
a progress in cleaning the journal. Without it jbd2_journal_destroy()
just loops in an infinite loop.
Fix jbd2_journal_destroy() to cleanup journal checkpoint lists of
jbd2_log_do_checkpoint() fails with error.
Reported-by: Eryu Guan <guaneryu@gmail.com>
Tested-by: Eryu Guan <guaneryu@gmail.com>
Fixes: 6f6a6fda294506dfe0e3e0a253bb2d2923f28f0a
Signed-off-by: Jan Kara <jack@suse.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs/jbd2')
-rw-r--r-- | fs/jbd2/checkpoint.c | 39 | ||||
-rw-r--r-- | fs/jbd2/commit.c | 2 | ||||
-rw-r--r-- | fs/jbd2/journal.c | 11 |
3 files changed, 44 insertions, 8 deletions
diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 4227dc4f7437..8c44654ce274 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c | |||
@@ -417,12 +417,12 @@ int jbd2_cleanup_journal_tail(journal_t *journal) | |||
417 | * journal_clean_one_cp_list | 417 | * journal_clean_one_cp_list |
418 | * | 418 | * |
419 | * Find all the written-back checkpoint buffers in the given list and | 419 | * Find all the written-back checkpoint buffers in the given list and |
420 | * release them. | 420 | * release them. If 'destroy' is set, clean all buffers unconditionally. |
421 | * | 421 | * |
422 | * Called with j_list_lock held. | 422 | * Called with j_list_lock held. |
423 | * Returns 1 if we freed the transaction, 0 otherwise. | 423 | * Returns 1 if we freed the transaction, 0 otherwise. |
424 | */ | 424 | */ |
425 | static int journal_clean_one_cp_list(struct journal_head *jh) | 425 | static int journal_clean_one_cp_list(struct journal_head *jh, bool destroy) |
426 | { | 426 | { |
427 | struct journal_head *last_jh; | 427 | struct journal_head *last_jh; |
428 | struct journal_head *next_jh = jh; | 428 | struct journal_head *next_jh = jh; |
@@ -436,7 +436,10 @@ static int journal_clean_one_cp_list(struct journal_head *jh) | |||
436 | do { | 436 | do { |
437 | jh = next_jh; | 437 | jh = next_jh; |
438 | next_jh = jh->b_cpnext; | 438 | next_jh = jh->b_cpnext; |
439 | ret = __try_to_free_cp_buf(jh); | 439 | if (!destroy) |
440 | ret = __try_to_free_cp_buf(jh); | ||
441 | else | ||
442 | ret = __jbd2_journal_remove_checkpoint(jh) + 1; | ||
440 | if (!ret) | 443 | if (!ret) |
441 | return freed; | 444 | return freed; |
442 | if (ret == 2) | 445 | if (ret == 2) |
@@ -459,10 +462,11 @@ static int journal_clean_one_cp_list(struct journal_head *jh) | |||
459 | * journal_clean_checkpoint_list | 462 | * journal_clean_checkpoint_list |
460 | * | 463 | * |
461 | * Find all the written-back checkpoint buffers in the journal and release them. | 464 | * Find all the written-back checkpoint buffers in the journal and release them. |
465 | * If 'destroy' is set, release all buffers unconditionally. | ||
462 | * | 466 | * |
463 | * Called with j_list_lock held. | 467 | * Called with j_list_lock held. |
464 | */ | 468 | */ |
465 | void __jbd2_journal_clean_checkpoint_list(journal_t *journal) | 469 | void __jbd2_journal_clean_checkpoint_list(journal_t *journal, bool destroy) |
466 | { | 470 | { |
467 | transaction_t *transaction, *last_transaction, *next_transaction; | 471 | transaction_t *transaction, *last_transaction, *next_transaction; |
468 | int ret; | 472 | int ret; |
@@ -476,7 +480,8 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal) | |||
476 | do { | 480 | do { |
477 | transaction = next_transaction; | 481 | transaction = next_transaction; |
478 | next_transaction = transaction->t_cpnext; | 482 | next_transaction = transaction->t_cpnext; |
479 | ret = journal_clean_one_cp_list(transaction->t_checkpoint_list); | 483 | ret = journal_clean_one_cp_list(transaction->t_checkpoint_list, |
484 | destroy); | ||
480 | /* | 485 | /* |
481 | * This function only frees up some memory if possible so we | 486 | * This function only frees up some memory if possible so we |
482 | * dont have an obligation to finish processing. Bail out if | 487 | * dont have an obligation to finish processing. Bail out if |
@@ -492,7 +497,7 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal) | |||
492 | * we can possibly see not yet submitted buffers on io_list | 497 | * we can possibly see not yet submitted buffers on io_list |
493 | */ | 498 | */ |
494 | ret = journal_clean_one_cp_list(transaction-> | 499 | ret = journal_clean_one_cp_list(transaction-> |
495 | t_checkpoint_io_list); | 500 | t_checkpoint_io_list, destroy); |
496 | if (need_resched()) | 501 | if (need_resched()) |
497 | return; | 502 | return; |
498 | /* | 503 | /* |
@@ -506,6 +511,28 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal) | |||
506 | } | 511 | } |
507 | 512 | ||
508 | /* | 513 | /* |
514 | * Remove buffers from all checkpoint lists as journal is aborted and we just | ||
515 | * need to free memory | ||
516 | */ | ||
517 | void jbd2_journal_destroy_checkpoint(journal_t *journal) | ||
518 | { | ||
519 | /* | ||
520 | * We loop because __jbd2_journal_clean_checkpoint_list() may abort | ||
521 | * early due to a need of rescheduling. | ||
522 | */ | ||
523 | while (1) { | ||
524 | spin_lock(&journal->j_list_lock); | ||
525 | if (!journal->j_checkpoint_transactions) { | ||
526 | spin_unlock(&journal->j_list_lock); | ||
527 | break; | ||
528 | } | ||
529 | __jbd2_journal_clean_checkpoint_list(journal, true); | ||
530 | spin_unlock(&journal->j_list_lock); | ||
531 | cond_resched(); | ||
532 | } | ||
533 | } | ||
534 | |||
535 | /* | ||
509 | * journal_remove_checkpoint: called after a buffer has been committed | 536 | * journal_remove_checkpoint: called after a buffer has been committed |
510 | * to disk (either by being write-back flushed to disk, or being | 537 | * to disk (either by being write-back flushed to disk, or being |
511 | * committed to the log). | 538 | * committed to the log). |
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index b73e0215baa7..362e5f614450 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c | |||
@@ -510,7 +510,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) | |||
510 | * frees some memory | 510 | * frees some memory |
511 | */ | 511 | */ |
512 | spin_lock(&journal->j_list_lock); | 512 | spin_lock(&journal->j_list_lock); |
513 | __jbd2_journal_clean_checkpoint_list(journal); | 513 | __jbd2_journal_clean_checkpoint_list(journal, false); |
514 | spin_unlock(&journal->j_list_lock); | 514 | spin_unlock(&journal->j_list_lock); |
515 | 515 | ||
516 | jbd_debug(3, "JBD2: commit phase 1\n"); | 516 | jbd_debug(3, "JBD2: commit phase 1\n"); |
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index fe1b4bdecdfa..8270fe9e3641 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c | |||
@@ -1693,8 +1693,17 @@ int jbd2_journal_destroy(journal_t *journal) | |||
1693 | while (journal->j_checkpoint_transactions != NULL) { | 1693 | while (journal->j_checkpoint_transactions != NULL) { |
1694 | spin_unlock(&journal->j_list_lock); | 1694 | spin_unlock(&journal->j_list_lock); |
1695 | mutex_lock(&journal->j_checkpoint_mutex); | 1695 | mutex_lock(&journal->j_checkpoint_mutex); |
1696 | jbd2_log_do_checkpoint(journal); | 1696 | err = jbd2_log_do_checkpoint(journal); |
1697 | mutex_unlock(&journal->j_checkpoint_mutex); | 1697 | mutex_unlock(&journal->j_checkpoint_mutex); |
1698 | /* | ||
1699 | * If checkpointing failed, just free the buffers to avoid | ||
1700 | * looping forever | ||
1701 | */ | ||
1702 | if (err) { | ||
1703 | jbd2_journal_destroy_checkpoint(journal); | ||
1704 | spin_lock(&journal->j_list_lock); | ||
1705 | break; | ||
1706 | } | ||
1698 | spin_lock(&journal->j_list_lock); | 1707 | spin_lock(&journal->j_list_lock); |
1699 | } | 1708 | } |
1700 | 1709 | ||