[PATCH 05/25] mm/fbatch: lru_add_del_folio()+folio_add_lru() after clear_lru()
From: Hugh Dickins
Date: Mon Aug 24 2026 - 10:08:37 EST
Most callers of folio_test_clear_lru() then proceed to remove the folio
from its lru, and add it back at the end when they're done (if still in
use). But isolate_migratepages_block() and check_move_unevictable_pages()
sometimes decide against, and release immediately with a folio_set_lru().
Which usually works fine: but there's now a small chance that while they
held the folio with lru bit cleared, an lru_add fbatch drain came along,
and had to skip that folio because its lru bit was transiently cleared
(previously, the lru_add fbatch drain relied on finding lru bit never yet
set). This risks leaving that folio off lru, unreclaimable until freed.
Fix such cases by trying lru_add_del_folio() (which only takes action and
returns true if the folio was on an lru_add fbatch), then folio_add_lru()
if it succeeded: invalidating the old fbatch slot, appending in a new one.
Signed-off-by: Hugh Dickins <hughd@xxxxxxxxxx>
---
mm/compaction.c | 13 +++++++++++--
mm/vmscan.c | 16 +++++++++-------
2 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/mm/compaction.c b/mm/compaction.c
index a049415512c6..9e045a90ba21 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -1204,7 +1204,13 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
!cc->alloc_contig)) {
low_pfn += folio_nr_pages(folio) - 1;
nr_scanned += folio_nr_pages(folio) - 1;
- folio_set_lru(folio);
+ if (lru_add_del_folio(folio)) {
+ lruvec_unlock_irqrestore(locked, flags);
+ folio_add_lru(folio);
+ locked = NULL;
+ } else {
+ folio_set_lru(folio);
+ }
goto isolate_fail_put;
}
}
@@ -1293,7 +1299,10 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
if (locked)
lruvec_unlock_irqrestore(locked, flags);
if (folio) {
- folio_set_lru(folio);
+ if (lru_add_del_folio(folio))
+ folio_add_lru(folio);
+ else
+ folio_set_lru(folio);
folio_put(folio);
}
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 4ca9775ceee8..7da12ffbd3bd 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -8021,17 +8021,19 @@ void check_move_unevictable_folios(struct folio_batch *fbatch)
folio_clear_unevictable(folio);
lruvec_add_folio(lruvec, folio);
pgrescued += nr_pages;
+ } else if (lru_add_del_folio(folio)) {
+ lruvec_unlock_irq(lruvec);
+ folio_add_lru(folio);
+ lruvec = NULL;
}
- folio_set_lru(folio);
+ if (lruvec)
+ folio_set_lru(folio);
}
- if (lruvec) {
- __count_vm_events(UNEVICTABLE_PGRESCUED, pgrescued);
- __count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned);
+ if (lruvec)
lruvec_unlock_irq(lruvec);
- } else if (pgscanned) {
- count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned);
- }
+ count_vm_events(UNEVICTABLE_PGRESCUED, pgrescued);
+ count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned);
}
EXPORT_SYMBOL_GPL(check_move_unevictable_folios);
--
2.51.0