1 jbd2: avoid infinite loop when destroying aborted journal
3 From: Jan Kara <jack@suse.com>
5 Commit 6f6a6fda2945 "jbd2: fix ocfs2 corrupt when updating journal
6 superblock fails" changed jbd2_cleanup_journal_tail() to return EIO
7 when the journal is aborted. That makes logic in
8 jbd2_log_do_checkpoint() bail out which is fine, except that
9 jbd2_journal_destroy() expects jbd2_log_do_checkpoint() to always make
10 a progress in cleaning the journal. Without it jbd2_journal_destroy()
11 just loops in an infinite loop.
13 Fix jbd2_journal_destroy() to cleanup journal checkpoint lists of
14 jbd2_log_do_checkpoint() fails with error.
16 Reported-by: Eryu Guan <guaneryu@gmail.com>
17 Tested-by: Eryu Guan <guaneryu@gmail.com>
18 Fixes: 6f6a6fda294506dfe0e3e0a253bb2d2923f28f0a
19 Signed-off-by: Jan Kara <jack@suse.com>
20 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
22 fs/jbd2/checkpoint.c | 39 +++++++++++++++++++++++++++++++++------
23 fs/jbd2/commit.c | 2 +-
24 fs/jbd2/journal.c | 11 ++++++++++-
25 include/linux/jbd2.h | 3 ++-
26 4 files changed, 46 insertions(+), 9 deletions(-)
28 diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c
29 index 4227dc4f7437..8c44654ce274 100644
30 --- a/fs/jbd2/checkpoint.c
31 +++ b/fs/jbd2/checkpoint.c
32 @@ -417,12 +417,12 @@ int jbd2_cleanup_journal_tail(journal_t *journal)
33 * journal_clean_one_cp_list
35 * Find all the written-back checkpoint buffers in the given list and
37 + * release them. If 'destroy' is set, clean all buffers unconditionally.
39 * Called with j_list_lock held.
40 * Returns 1 if we freed the transaction, 0 otherwise.
42 -static int journal_clean_one_cp_list(struct journal_head *jh)
43 +static int journal_clean_one_cp_list(struct journal_head *jh, bool destroy)
45 struct journal_head *last_jh;
46 struct journal_head *next_jh = jh;
47 @@ -436,7 +436,10 @@ static int journal_clean_one_cp_list(struct journal_head *jh)
50 next_jh = jh->b_cpnext;
51 - ret = __try_to_free_cp_buf(jh);
53 + ret = __try_to_free_cp_buf(jh);
55 + ret = __jbd2_journal_remove_checkpoint(jh) + 1;
59 @@ -459,10 +462,11 @@ static int journal_clean_one_cp_list(struct journal_head *jh)
60 * journal_clean_checkpoint_list
62 * Find all the written-back checkpoint buffers in the journal and release them.
63 + * If 'destroy' is set, release all buffers unconditionally.
65 * Called with j_list_lock held.
67 -void __jbd2_journal_clean_checkpoint_list(journal_t *journal)
68 +void __jbd2_journal_clean_checkpoint_list(journal_t *journal, bool destroy)
70 transaction_t *transaction, *last_transaction, *next_transaction;
72 @@ -476,7 +480,8 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal)
74 transaction = next_transaction;
75 next_transaction = transaction->t_cpnext;
76 - ret = journal_clean_one_cp_list(transaction->t_checkpoint_list);
77 + ret = journal_clean_one_cp_list(transaction->t_checkpoint_list,
80 * This function only frees up some memory if possible so we
81 * dont have an obligation to finish processing. Bail out if
82 @@ -492,7 +497,7 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal)
83 * we can possibly see not yet submitted buffers on io_list
85 ret = journal_clean_one_cp_list(transaction->
86 - t_checkpoint_io_list);
87 + t_checkpoint_io_list, destroy);
91 @@ -506,6 +511,28 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal)
95 + * Remove buffers from all checkpoint lists as journal is aborted and we just
96 + * need to free memory
98 +void jbd2_journal_destroy_checkpoint(journal_t *journal)
101 + * We loop because __jbd2_journal_clean_checkpoint_list() may abort
102 + * early due to a need of rescheduling.
105 + spin_lock(&journal->j_list_lock);
106 + if (!journal->j_checkpoint_transactions) {
107 + spin_unlock(&journal->j_list_lock);
110 + __jbd2_journal_clean_checkpoint_list(journal, true);
111 + spin_unlock(&journal->j_list_lock);
117 * journal_remove_checkpoint: called after a buffer has been committed
118 * to disk (either by being write-back flushed to disk, or being
119 * committed to the log).
120 diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
121 index b73e0215baa7..362e5f614450 100644
122 --- a/fs/jbd2/commit.c
123 +++ b/fs/jbd2/commit.c
124 @@ -510,7 +510,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
127 spin_lock(&journal->j_list_lock);
128 - __jbd2_journal_clean_checkpoint_list(journal);
129 + __jbd2_journal_clean_checkpoint_list(journal, false);
130 spin_unlock(&journal->j_list_lock);
132 jbd_debug(3, "JBD2: commit phase 1\n");
133 diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
134 index 4ff3fad4e9e3..2721513adb1f 100644
135 --- a/fs/jbd2/journal.c
136 +++ b/fs/jbd2/journal.c
137 @@ -1693,8 +1693,17 @@ int jbd2_journal_destroy(journal_t *journal)
138 while (journal->j_checkpoint_transactions != NULL) {
139 spin_unlock(&journal->j_list_lock);
140 mutex_lock(&journal->j_checkpoint_mutex);
141 - jbd2_log_do_checkpoint(journal);
142 + err = jbd2_log_do_checkpoint(journal);
143 mutex_unlock(&journal->j_checkpoint_mutex);
145 + * If checkpointing failed, just free the buffers to avoid
149 + jbd2_journal_destroy_checkpoint(journal);
150 + spin_lock(&journal->j_list_lock);
153 spin_lock(&journal->j_list_lock);
156 diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h
157 index edb640ae9a94..eb1cebed3f36 100644
158 --- a/include/linux/jbd2.h
159 +++ b/include/linux/jbd2.h
160 @@ -1042,8 +1042,9 @@ void jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block);
161 extern void jbd2_journal_commit_transaction(journal_t *);
163 /* Checkpoint list management */
164 -void __jbd2_journal_clean_checkpoint_list(journal_t *journal);
165 +void __jbd2_journal_clean_checkpoint_list(journal_t *journal, bool destroy);
166 int __jbd2_journal_remove_checkpoint(struct journal_head *);
167 +void jbd2_journal_destroy_checkpoint(journal_t *journal);
168 void __jbd2_journal_insert_checkpoint(struct journal_head *, transaction_t *);