aboutsummaryrefslogtreecommitdiffstats
path: root/fs/jbd2
diff options
context:
space:
mode:
authorJan Kara <jack@suse.com>2015-07-28 14:57:14 -0400
committerTheodore Ts'o <tytso@mit.edu>2015-07-28 14:57:14 -0400
commit841df7df196237ea63233f0f9eaa41db53afd70f (patch)
tree0cd3bedda66c31f94c23db765d21b5a5c52162ee /fs/jbd2
parent564bc402526e437729ecafe3c3511f7cab9f0327 (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.c39
-rw-r--r--fs/jbd2/commit.c2
-rw-r--r--fs/jbd2/journal.c11
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 */
425static int journal_clean_one_cp_list(struct journal_head *jh) 425static 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 */
465void __jbd2_journal_clean_checkpoint_list(journal_t *journal) 469void __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 */
517void 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