Re: [PATCH 1/2] jbd2: check need_resched() when skipping busy checkpoint buffers
Andreas Dilger <[email protected]>
| Newsgroups | org.kernel.vger.linux-ext4,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
On Jul 13, 2026, at 04:22, Max Kellermann <[email protected]> wrote: > > journal_shrink_one_cp_list() skips busy checkpoint buffers when called > with JBD2_SHRINK_BUSY_SKIP. The continue statement on this path also > skips the need_resched() check at the end of the loop body. > > Consequently, when a checkpoint list contains mostly busy buffers, the > shrinker can walk the entire list while holding journal->j_list_lock, > even when a reschedule has been requested. Large checkpoint lists under > memory pressure can therefore cause long lock hold times and leave other > CPUs spinning on j_list_lock, resulting in soft lockups or RCU stalls. > > Route the busy-buffer path through the need_resched() check so that the > shrinker can release j_list_lock and reschedule promptly, restoring > parity with the clean-buffer path, which already checks need_resched(). > This does not change which checkpoint buffers are eligible for removal. One question I had about this patch is whether the list cleanup could get into a livelock if it becomes too large? If the iterator is processing a large number of jh that cannot be freed, then `need_resched()` is hit, if it restarts from the beginning of the list each time without processing at least one jh it will not make forward progress. Cheers, Andreas > Fixes: b98dba273a0e ("jbd2: remove journal_clean_one_cp_list()") > Cc: [email protected] > Signed-off-by: Max Kellermann <[email protected]> > --- > fs/jbd2/checkpoint.c | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) > > diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c > index 1508e2f54462..5266017565ac 100644 > --- a/fs/jbd2/checkpoint.c > +++ b/fs/jbd2/checkpoint.c > @@ -389,7 +389,7 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh, > ret = jbd2_journal_try_remove_checkpoint(jh); > if (ret < 0) { > if (type == JBD2_SHRINK_BUSY_SKIP) > - continue; > + goto next; > break; > } > } > @@ -400,6 +400,7 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh, > break; > } > > +next: > if (need_resched()) > break; > } while (jh != last_jh); > -- > 2.47.3 > > Cheers, Andreas