Patch "jbd2: avoid infinite loop when destroying aborted journal" has been added to the 4.1-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
jbd2: avoid infinite loop when destroying aborted journal
to the 4.1-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
jbd2-avoid-infinite-loop-when-destroying-aborted-journal.patch
and it can be found in the queue-4.1 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
841df7df196237ea63233f0f9eaa41db53afd70fMon Sep 17 00:00:00 2001
From: Jan Kara <[email protected]>
Date: Tue, 28 Jul 2015 14:57:14 -0400
Subject: jbd2: avoid infinite loop when destroying aborted journal
From: Jan Kara <[email protected]>
commit 841df7df196237ea63233f0f9eaa41db53afd70f upstream.
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.
--- fs/jbd2/checkpoint.c | 39 +++++++++++++++++++++++++++++++++------ fs/jbd2/commit.c | 2 +- fs/jbd2/journal.c | 11 ++++++++++- include/linux/jbd2.h | 3 ++- 4 files changed, 46 insertions(+), 9 deletions(-)
fs/jbd2/checkpoint.c +33 -6
@@ -417,12 +417,12 @@ int jbd2_cleanup_journal_tail(journal_t* journal_clean_one_cp_list * * Find all the written-back checkpoint buffers in the given list and -* release them. +* release them. If 'destroy' is set, clean all buffers unconditionally. * * Called with j_list_lock held. * Returns 1 if we freed the transaction, 0 otherwise. */ -static int journal_clean_one_cp_list (struct journal_head *jh) +static int journal_clean_one_cp_list (struct journal_head *jh,bool destroy) {struct journal_head *last_jh;struct journal_head *next_jh = jh;
@@ -436,7 +436,10 @@ static int journal_clean_one_cp_list(str do { jh = next_jh; next_jh = jh->b_cpnext; - ret = __try_to_free_cp_buf(jh); + if (!destroy) + ret = __try_to_free_cp_buf(jh); + else + ret = __jbd2_journal_remove_checkpoint(jh) + 1; if (!ret) return freed; if (ret == 2)
@@ -459,10 +462,11 @@ static int journal_clean_one_cp_list(str* journal_clean_checkpoint_list * * Find all the written-back checkpoint buffers in the journal and release them. +* If 'destroy' is set, release all buffers unconditionally. * * Called with j_list_lock held. */ -void __jbd2_journal_clean_checkpoint_list (journal_t *journal) +void __jbd2_journal_clean_checkpoint_list (journal_t *journal,bool destroy) {transaction_t *transaction, *last_transaction, *next_transaction;int ret;
@@ -476,7 +480,8 @@ void __jbd2_journal_clean_checkpoint_lis do { transaction = next_transaction; next_transaction = transaction->t_cpnext; - ret = journal_clean_one_cp_list(transaction->t_checkpoint_list); + ret = journal_clean_one_cp_list(transaction->t_checkpoint_list, + destroy); /* * This function only frees up some memory if possible so we * dont have an obligation to finish processing. Bail out if
@@ -492,7 +497,7 @@ void __jbd2_journal_clean_checkpoint_lis* we can possibly see not yet submitted buffers on io_list */ ret =journal_clean_one_cp_list (transaction-> - t_checkpoint_io_list); + t_checkpoint_io_list, destroy);if (need_resched ())return ;/*
@@ -506,6 +511,28 @@ void __jbd2_journal_clean_checkpoint_lis }/* +* Remove buffers from all checkpoint lists as journal is aborted and we just +* need to free memory +*/ +void jbd2_journal_destroy_checkpoint (journal_t *journal) +{ +/* +* We loop because __jbd2_journal_clean_checkpoint_list() may abort +* early due to a need of rescheduling. +*/ +while (1 ) { +spin_lock (&journal->j_list_lock); +if (!journal->j_checkpoint_transactions) { +spin_unlock (&journal->j_list_lock); +break ; + } +__jbd2_journal_clean_checkpoint_list (journal, true); +spin_unlock (&journal->j_list_lock); +cond_resched (); + } +} + +/* * journal_remove_checkpoint: called after a buffer has been committed * to disk (either by being write-back flushed to disk, or being * committed to the log).
fs/jbd2/commit.c +1 -1
@@ -510,7 +510,7 @@ void jbd2_journal_commit_transaction(jou* frees some memory */ spin_lock (&journal->j_list_lock); -__jbd2_journal_clean_checkpoint_list (journal); +__jbd2_journal_clean_checkpoint_list (journal, false);spin_unlock (&journal->j_list_lock);jbd_debug (3 ,"JBD2: commit phase 1\n" );
fs/jbd2/journal.c +10 -1
@@ -1708,8 +1708,17 @@ int jbd2_journal_destroy(journal_t *jourwhile (journal->j_checkpoint_transactions !=NULL ) {spin_unlock (&journal->j_list_lock);mutex_lock (&journal->j_checkpoint_mutex); -jbd2_log_do_checkpoint (journal); + err =jbd2_log_do_checkpoint (journal);mutex_unlock (&journal->j_checkpoint_mutex); +/* +* If checkpointing failed, just free the buffers to avoid +* looping forever +*/ +if (err) { +jbd2_journal_destroy_checkpoint (journal); +spin_lock (&journal->j_list_lock); +break ; + }spin_lock (&journal->j_list_lock); }
include/linux/jbd2.h +2 -1
@@ -1042,8 +1042,9 @@ void jbd2_update_log_tail(journal_t *jouextern void jbd2_journal_commit_transaction (journal_t *);/* Checkpoint list management */ -void __jbd2_journal_clean_checkpoint_list (journal_t *journal); +void __jbd2_journal_clean_checkpoint_list (journal_t *journal,bool destroy);int __jbd2_journal_remove_checkpoint (struct journal_head *); +void jbd2_journal_destroy_checkpoint (journal_t *journal);void __jbd2_journal_insert_checkpoint (struct journal_head *,transaction_t *);
Patches currently in stable-queue which might be from [email protected] are
queue-4.1/ext4-don-t-manipulate-recovery-flag-when-freezing-no-journal-fs.patch queue-4.1/jbd2-avoid-infinite-loop-when-destroying-aborted-journal.patch