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 | ||
