Add cc: stable to revert-remove-block_device_ejected
[ext4-patch-queue.git] / avoid-infinite-loop-when-destroying-aborted-journal
blob1c88387aca54916039a3c6fbdee4d0b22fe49d57
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>
21 ---
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
34   *
35   * Find all the written-back checkpoint buffers in the given list and
36 - * release them.
37 + * release them. If 'destroy' is set, clean all buffers unconditionally.
38   *
39   * Called with j_list_lock held.
40   * Returns 1 if we freed the transaction, 0 otherwise.
41   */
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)
44  {
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)
48         do {
49                 jh = next_jh;
50                 next_jh = jh->b_cpnext;
51 -               ret = __try_to_free_cp_buf(jh);
52 +               if (!destroy)
53 +                       ret = __try_to_free_cp_buf(jh);
54 +               else
55 +                       ret = __jbd2_journal_remove_checkpoint(jh) + 1;
56                 if (!ret)
57                         return freed;
58                 if (ret == 2)
59 @@ -459,10 +462,11 @@ static int journal_clean_one_cp_list(struct journal_head *jh)
60   * journal_clean_checkpoint_list
61   *
62   * Find all the written-back checkpoint buffers in the journal and release them.
63 + * If 'destroy' is set, release all buffers unconditionally.
64   *
65   * Called with j_list_lock held.
66   */
67 -void __jbd2_journal_clean_checkpoint_list(journal_t *journal)
68 +void __jbd2_journal_clean_checkpoint_list(journal_t *journal, bool destroy)
69  {
70         transaction_t *transaction, *last_transaction, *next_transaction;
71         int ret;
72 @@ -476,7 +480,8 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal)
73         do {
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,
78 +                                               destroy);
79                 /*
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
84                  */
85                 ret = journal_clean_one_cp_list(transaction->
86 -                               t_checkpoint_io_list);
87 +                               t_checkpoint_io_list, destroy);
88                 if (need_resched())
89                         return;
90                 /*
91 @@ -506,6 +511,28 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal)
92  }
94  /*
95 + * Remove buffers from all checkpoint lists as journal is aborted and we just
96 + * need to free memory
97 + */
98 +void jbd2_journal_destroy_checkpoint(journal_t *journal)
100 +       /*
101 +        * We loop because __jbd2_journal_clean_checkpoint_list() may abort
102 +        * early due to a need of rescheduling.
103 +        */
104 +       while (1) {
105 +               spin_lock(&journal->j_list_lock);
106 +               if (!journal->j_checkpoint_transactions) {
107 +                       spin_unlock(&journal->j_list_lock);
108 +                       break;
109 +               }
110 +               __jbd2_journal_clean_checkpoint_list(journal, true);
111 +               spin_unlock(&journal->j_list_lock);
112 +               cond_resched();
113 +       }
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)
125          * frees some memory
126          */
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);
144 +               /*
145 +                * If checkpointing failed, just free the buffers to avoid
146 +                * looping forever
147 +                */
148 +               if (err) {
149 +                       jbd2_journal_destroy_checkpoint(journal);
150 +                       spin_lock(&journal->j_list_lock);
151 +                       break;
152 +               }
153                 spin_lock(&journal->j_list_lock);
154         }
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 *);
171 -- 
172 2.1.4