* [PATCH 0/4] mm/vmscan: refactor shrink_folio_list()
@ 2026-09-20 14:24 Zhang Peng
2026-09-20 14:24 ` [PATCH 1/4] mm/vmscan: introduce folio_activate_locked() helper Zhang Peng
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Zhang Peng @ 2026-09-20 14:24 UTC (permalink / raw)
To: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt, Barry Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He, Baolin Wang,
Johannes Weiner, David Hildenbrand, Michal Hocko,
Lorenzo Stoakes
Cc: linux-mm, linux-kernel, Zhang Peng
shrink_folio_list() has grown into a very long function whose body mixes
several independent concerns: folio activation, the pageout() dispatch
state machine, the unmap step, and the final freeing path. The control
flow between them is expressed entirely through a chain of goto labels
(activate_locked, activate_locked_split, keep_locked, keep), which makes
it hard to see which decision each block actually reaches.
This series extracts four self-contained blocks into helpers, without
changing behaviour:
Patch 1: the activation block at activate_locked, into
folio_activate_locked().
Patch 2: the freeing path (buffer release, lazyfree, __remove_mapping,
folio_batch drain), into folio_try_reclaim_free().
Patch 3: the pageout() dispatch state machine, into
folio_try_pageout().
Patch 4: the TTU setup and try_to_unmap() block, into
folio_try_unmap().
Rather than returning a boolean, the extracted helpers return an explicit
result enum, so the reclaim decisions the caller has to make - activate,
keep, or reclaimed - stay visible at the call site instead of being
buried in the helper. Where a folio may be returned either locked or
unlocked, the result also records which, so the caller does not have to
infer the lock state from an invisible internal decision.
This is preparation for batching the dirty TLB flush before pageout,
which needs to call the pageout path from a second place. That change
will be posted separately once this cleanup has landed [1].
No functional change intended.
[1] https://lore.kernel.org/all/CAGsJ_4wrQRGjqavMzWgi2+PrRr1ztf4QRZEb6HKm02OF8nEBUQ@mail.gmail.com/
Suggested-by: Kairui Song <kasong@tencent.com>
Signed-off-by: Zhang Peng <bruzzhang@tencent.com>
---
Zhang Peng (4):
mm/vmscan: introduce folio_activate_locked() helper
mm/vmscan: extract folio reclaim freeing from shrink_folio_list()
mm/vmscan: extract folio pageout from shrink_folio_list()
mm/vmscan: extract folio unmap logic into folio_try_unmap()
mm/vmscan.c | 376 +++++++++++++++++++++++++++++++++++-------------------------
1 file changed, 217 insertions(+), 159 deletions(-)
---
base-commit: 498ee28e5ec4727f829507c4a1bde3ab1b7704cd
change-id: 20260919-vmscan-refactor-1b3e5588af52
Best regards,
--
bruzzhang <bruzzhang@tencent.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/4] mm/vmscan: introduce folio_activate_locked() helper
2026-09-20 14:24 [PATCH 0/4] mm/vmscan: refactor shrink_folio_list() Zhang Peng
@ 2026-09-20 14:24 ` Zhang Peng
2026-09-20 14:24 ` [PATCH 2/4] mm/vmscan: extract folio reclaim freeing from shrink_folio_list() Zhang Peng
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Zhang Peng @ 2026-09-20 14:24 UTC (permalink / raw)
To: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt, Barry Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He, Baolin Wang,
Johannes Weiner, David Hildenbrand, Michal Hocko,
Lorenzo Stoakes
Cc: linux-mm, linux-kernel, Zhang Peng
From: Zhang Peng <bruzzhang@tencent.com>
The activate_locked label in shrink_folio_list() reclaims swap cache
when needed, marks the folio active, and updates activation statistics.
Extract this block into folio_activate_locked() so it can be reused.
Replace the active-folio BUG with a warning so a bad caller is reported
without taking down the system. Otherwise, there is no functional change.
Reviewed-by: Barry Song <baohua@kernel.org>
Signed-off-by: Zhang Peng <bruzzhang@tencent.com>
---
mm/vmscan.c | 40 +++++++++++++++++++++++++---------------
1 file changed, 25 insertions(+), 15 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index e200ce3eb056..c6beca88079a 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1155,6 +1155,28 @@ static bool may_enter_fs(struct folio *folio, gfp_t gfp_mask)
return false;
}
+/* Activate an isolated, locked folio and account the activation. */
+static void folio_activate_locked(struct folio *folio,
+ struct reclaim_stat *stat)
+{
+ unsigned int nr_pages = folio_nr_pages(folio);
+
+ VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
+ VM_WARN_ON_ONCE_FOLIO(folio_test_active(folio), folio);
+
+ /* Not a candidate for swapping, so reclaim swap space. */
+ if (folio_test_swapcache(folio) &&
+ (mem_cgroup_swap_full(folio) || folio_test_mlocked(folio)))
+ folio_free_swap(folio);
+ if (!folio_test_mlocked(folio)) {
+ int type = folio_is_file_lru(folio);
+
+ folio_set_active(folio);
+ stat->nr_activate[type] += nr_pages;
+ count_memcg_folio_events(folio, PGACTIVATE, nr_pages);
+ }
+}
+
/*
* shrink_folio_list() returns the number of reclaimed pages
*/
@@ -1623,24 +1645,12 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
activate_locked_split:
/*
* The tail pages that are failed to add into swap cache
- * reach here. Fixup nr_scanned and nr_pages.
+ * reach here. Fixup nr_scanned.
*/
- if (nr_pages > 1) {
+ if (nr_pages > 1)
sc->nr_scanned -= (nr_pages - 1);
- nr_pages = 1;
- }
activate_locked:
- /* Not a candidate for swapping, so reclaim swap space. */
- if (folio_test_swapcache(folio) &&
- (mem_cgroup_swap_full(folio) || folio_test_mlocked(folio)))
- folio_free_swap(folio);
- VM_BUG_ON_FOLIO(folio_test_active(folio), folio);
- if (!folio_test_mlocked(folio)) {
- int type = folio_is_file_lru(folio);
- folio_set_active(folio);
- stat->nr_activate[type] += nr_pages;
- count_memcg_folio_events(folio, PGACTIVATE, nr_pages);
- }
+ folio_activate_locked(folio, stat);
keep_locked:
folio_unlock(folio);
keep:
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/4] mm/vmscan: extract folio reclaim freeing from shrink_folio_list()
2026-09-20 14:24 [PATCH 0/4] mm/vmscan: refactor shrink_folio_list() Zhang Peng
2026-09-20 14:24 ` [PATCH 1/4] mm/vmscan: introduce folio_activate_locked() helper Zhang Peng
@ 2026-09-20 14:24 ` Zhang Peng
2026-09-20 14:24 ` [PATCH 3/4] mm/vmscan: extract folio pageout " Zhang Peng
2026-09-20 14:24 ` [PATCH 4/4] mm/vmscan: extract folio unmap logic into folio_try_unmap() Zhang Peng
3 siblings, 0 replies; 5+ messages in thread
From: Zhang Peng @ 2026-09-20 14:24 UTC (permalink / raw)
To: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt, Barry Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He, Baolin Wang,
Johannes Weiner, David Hildenbrand, Michal Hocko,
Lorenzo Stoakes
Cc: linux-mm, linux-kernel, Zhang Peng
From: Zhang Peng <bruzzhang@tencent.com>
shrink_folio_list() contains a self-contained folio-freeing section:
buffer release, lazyfree, __remove_mapping(), and folio_batch draining.
Extract it into folio_try_reclaim_free() to reduce the size of
shrink_folio_list() and make the freeing step independently readable.
Return an explicit result so the caller retains the distinction between
activating a folio, keeping it on the inactive list, and reclaiming it.
The helper leaves the folio locked when it returns ACTIVATE or KEEP and
consumes it when it returns SUCCESS.
No functional change.
Signed-off-by: Zhang Peng <bruzzhang@tencent.com>
---
mm/vmscan.c | 171 ++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 96 insertions(+), 75 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index c6beca88079a..120085dfa2fe 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1177,6 +1177,95 @@ static void folio_activate_locked(struct folio *folio,
}
}
+enum folio_reclaim_result {
+ FOLIO_RECLAIM_KEEP,
+ FOLIO_RECLAIM_ACTIVATE,
+ FOLIO_RECLAIM_SUCCESS,
+};
+
+static enum folio_reclaim_result folio_try_reclaim_free(struct folio *folio,
+ struct folio_batch *free_folios,
+ struct scan_control *sc,
+ unsigned int *nr_reclaimed)
+{
+ const unsigned int nr_pages = folio_nr_pages(folio);
+ struct address_space *mapping = folio_mapping(folio);
+
+ /*
+ * If the folio has buffers, try to free the buffer mappings
+ * associated with this folio. If we succeed we try to free
+ * the folio as well.
+ *
+ * We do this even if the folio is dirty.
+ * filemap_release_folio() does not perform I/O, but it is
+ * possible for a folio to have the dirty flag set, but it
+ * is actually clean (all its buffers are clean). This
+ * happens if the buffers were written out directly, with
+ * bh_submit(). ext3 will do this, as well as the blockdev
+ * mapping. filemap_release_folio() will discover that
+ * cleanness and will drop the buffers and mark the folio
+ * clean - it can be freed.
+ *
+ * Rarely, folios can have buffers and no ->mapping. These
+ * are the folios which were not successfully invalidated in
+ * truncate_cleanup_folio(). We try to drop those buffers
+ * here and if that worked, and the folio is no longer
+ * mapped into process address space (refcount == 1) it can
+ * be freed. Otherwise, leave the folio on the LRU so it is
+ * swappable.
+ */
+ if (folio_needs_release(folio)) {
+ if (!filemap_release_folio(folio, sc->gfp_mask))
+ return FOLIO_RECLAIM_ACTIVATE;
+
+ if (!mapping && folio_ref_count(folio) == 1) {
+ folio_unlock(folio);
+ if (folio_put_testzero(folio))
+ goto free_it;
+
+ /*
+ * Rare race with speculative reference. The
+ * speculative reference will free this folio
+ * shortly, so we may increment nr_reclaimed here
+ * and leave it off the LRU.
+ */
+ *nr_reclaimed += nr_pages;
+ return FOLIO_RECLAIM_SUCCESS;
+ }
+ }
+
+ if (folio_test_lazyfree(folio)) {
+ /* follow __remove_mapping for reference */
+ if (!folio_ref_freeze(folio, 1))
+ return FOLIO_RECLAIM_KEEP;
+ /*
+ * The folio has only one reference left, which is
+ * from the isolation. After the caller puts the
+ * folio back on the lru and drops the reference, the
+ * folio will be freed anyway. It doesn't matter
+ * which lru it goes on. So we don't bother checking
+ * the dirty flag here.
+ */
+ count_vm_events(PGLAZYFREED, nr_pages);
+ count_memcg_folio_events(folio, PGLAZYFREED, nr_pages);
+ } else if (!mapping || !__remove_mapping(mapping, folio, true,
+ sc->target_mem_cgroup))
+ return FOLIO_RECLAIM_KEEP;
+
+ folio_unlock(folio);
+free_it:
+ VM_WARN_ON_ONCE_FOLIO(folio_ref_count(folio), folio);
+ *nr_reclaimed += nr_pages;
+
+ folio_unqueue_deferred_split(folio);
+ if (folio_batch_add(free_folios, folio) == 0) {
+ mem_cgroup_uncharge_folios(free_folios);
+ try_to_unmap_flush();
+ free_unref_folios(free_folios);
+ }
+ return FOLIO_RECLAIM_SUCCESS;
+}
+
/*
* shrink_folio_list() returns the number of reclaimed pages
*/
@@ -1564,83 +1653,15 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
}
}
- /*
- * If the folio has buffers, try to free the buffer
- * mappings associated with this folio. If we succeed
- * we try to free the folio as well.
- *
- * We do this even if the folio is dirty.
- * filemap_release_folio() does not perform I/O, but it
- * is possible for a folio to have the dirty flag set,
- * but it is actually clean (all its buffers are clean).
- * This happens if the buffers were written out directly,
- * with bh_submit(). ext3 will do this, as well as
- * the blockdev mapping. filemap_release_folio() will
- * discover that cleanness and will drop the buffers
- * and mark the folio clean - it can be freed.
- *
- * Rarely, folios can have buffers and no ->mapping.
- * These are the folios which were not successfully
- * invalidated in truncate_cleanup_folio(). We try to
- * drop those buffers here and if that worked, and the
- * folio is no longer mapped into process address space
- * (refcount == 1) it can be freed. Otherwise, leave
- * the folio on the LRU so it is swappable.
- */
- if (folio_needs_release(folio)) {
- if (!filemap_release_folio(folio, sc->gfp_mask))
- goto activate_locked;
- if (!mapping && folio_ref_count(folio) == 1) {
- folio_unlock(folio);
- if (folio_put_testzero(folio))
- goto free_it;
- else {
- /*
- * rare race with speculative reference.
- * the speculative reference will free
- * this folio shortly, so we may
- * increment nr_reclaimed here (and
- * leave it off the LRU).
- */
- nr_reclaimed += nr_pages;
- continue;
- }
- }
- }
-
- if (folio_test_lazyfree(folio)) {
- /* follow __remove_mapping for reference */
- if (!folio_ref_freeze(folio, 1))
- goto keep_locked;
- /*
- * The folio has only one reference left, which is
- * from the isolation. After the caller puts the
- * folio back on the lru and drops the reference, the
- * folio will be freed anyway. It doesn't matter
- * which lru it goes on. So we don't bother checking
- * the dirty flag here.
- */
- count_vm_events(PGLAZYFREED, nr_pages);
- count_memcg_folio_events(folio, PGLAZYFREED, nr_pages);
- } else if (!mapping || !__remove_mapping(mapping, folio, true,
- sc->target_mem_cgroup))
+ switch (folio_try_reclaim_free(folio, &free_folios, sc,
+ &nr_reclaimed)) {
+ case FOLIO_RECLAIM_ACTIVATE:
+ goto activate_locked;
+ case FOLIO_RECLAIM_KEEP:
goto keep_locked;
-
- folio_unlock(folio);
-free_it:
- /*
- * Folio may get swapped out as a whole, need to account
- * all pages in it.
- */
- nr_reclaimed += nr_pages;
-
- folio_unqueue_deferred_split(folio);
- if (folio_batch_add(&free_folios, folio) == 0) {
- mem_cgroup_uncharge_folios(&free_folios);
- try_to_unmap_flush();
- free_unref_folios(&free_folios);
+ case FOLIO_RECLAIM_SUCCESS:
+ continue;
}
- continue;
activate_locked_split:
/*
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 3/4] mm/vmscan: extract folio pageout from shrink_folio_list()
2026-09-20 14:24 [PATCH 0/4] mm/vmscan: refactor shrink_folio_list() Zhang Peng
2026-09-20 14:24 ` [PATCH 1/4] mm/vmscan: introduce folio_activate_locked() helper Zhang Peng
2026-09-20 14:24 ` [PATCH 2/4] mm/vmscan: extract folio reclaim freeing from shrink_folio_list() Zhang Peng
@ 2026-09-20 14:24 ` Zhang Peng
2026-09-20 14:24 ` [PATCH 4/4] mm/vmscan: extract folio unmap logic into folio_try_unmap() Zhang Peng
3 siblings, 0 replies; 5+ messages in thread
From: Zhang Peng @ 2026-09-20 14:24 UTC (permalink / raw)
To: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt, Barry Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He, Baolin Wang,
Johannes Weiner, David Hildenbrand, Michal Hocko,
Lorenzo Stoakes
Cc: linux-mm, linux-kernel, Zhang Peng
From: Zhang Peng <bruzzhang@tencent.com>
shrink_folio_list() contains a self-contained pageout() dispatch state
machine. Extract it into folio_try_pageout() so it can be reused by the
batched pageout path.
Return an explicit pageout result instead of a boolean. This keeps the
important distinction between activating and merely keeping a folio
visible to the caller, and also records whether a kept folio remains
locked after pageout().
The clean and synchronous-write cases return FOLIO_PAGEOUT_FREE: the
folio is still locked there, and shrink_folio_list() then runs its
single folio_try_reclaim_free() call, so the reclaim-free step keeps
exactly one caller.
The PAGE_ACTIVATE arm no longer normalises nr_pages to 1 after a split:
folio_activate_locked() derives the count from folio_nr_pages() itself,
which is equal by construction once the folio is order-0. Note this
makes the patch depend on patch 1; applying it alone would over-account
nr_activate for a split shmem folio.
No functional change.
---
mm/vmscan.c | 100 ++++++++++++++++++++++++++++++++++++------------------------
1 file changed, 61 insertions(+), 39 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 120085dfa2fe..3bce7ff89293 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1266,8 +1266,60 @@ static enum folio_reclaim_result folio_try_reclaim_free(struct folio *folio,
return FOLIO_RECLAIM_SUCCESS;
}
+enum folio_pageout_result {
+ FOLIO_PAGEOUT_KEEP_LOCKED,
+ FOLIO_PAGEOUT_KEEP_UNLOCKED,
+ FOLIO_PAGEOUT_ACTIVATE,
+ FOLIO_PAGEOUT_FREE, /* folio is locked, hand it to folio_try_reclaim_free() */
+};
+
+static enum folio_pageout_result folio_try_pageout(struct folio *folio,
+ struct scan_control *sc, struct swap_io_ctx *ctx,
+ struct list_head *folio_list)
+{
+ struct address_space *mapping = folio_mapping(folio);
+ unsigned int nr_pages = folio_nr_pages(folio);
+
+ switch (pageout(ctx, mapping, folio, folio_list)) {
+ case PAGE_ACTIVATE:
+ /*
+ * If shmem folio is split when writeback to swap, the
+ * tail pages will make their own pass through this
+ * function and be accounted then. There is no need to
+ * clamp nr_pages here: folio_activate_locked() derives
+ * the count from folio_nr_pages() itself.
+ */
+ if (nr_pages > 1 && !folio_test_large(folio))
+ sc->nr_scanned -= (nr_pages - 1);
+ return FOLIO_PAGEOUT_ACTIVATE;
+ case PAGE_KEEP:
+ return FOLIO_PAGEOUT_KEEP_LOCKED;
+ case PAGE_SUCCESS:
+ if (nr_pages > 1 && !folio_test_large(folio))
+ sc->nr_scanned -= (nr_pages - 1);
+
+ if (folio_test_writeback(folio))
+ return FOLIO_PAGEOUT_KEEP_UNLOCKED;
+ if (folio_test_dirty(folio))
+ return FOLIO_PAGEOUT_KEEP_UNLOCKED;
+
+ /*
+ * A synchronous write - probably a ramdisk. Go ahead
+ * and try to reclaim the folio.
+ */
+ if (!folio_trylock(folio))
+ return FOLIO_PAGEOUT_KEEP_UNLOCKED;
+ if (folio_test_dirty(folio) || folio_test_writeback(folio))
+ return FOLIO_PAGEOUT_KEEP_LOCKED;
+ fallthrough;
+ case PAGE_CLEAN:
+ return FOLIO_PAGEOUT_FREE;
+ }
+ unreachable();
+}
+
/*
- * shrink_folio_list() returns the number of reclaimed pages
+ * Reclaimed folios are counted in the return value.
*/
static unsigned int shrink_folio_list(struct list_head *folio_list,
struct pglist_data *pgdat, struct scan_control *sc,
@@ -1585,7 +1637,6 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
if (folio_maybe_dma_pinned(folio))
goto activate_locked;
- mapping = folio_mapping(folio);
if (folio_test_dirty(folio)) {
if (folio_is_file_lru(folio)) {
/*
@@ -1606,50 +1657,21 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
goto keep_locked;
if (!sc->may_writepage)
goto keep_locked;
-
/*
* Folio is dirty. Flush the TLB if a writable entry
* potentially exists to avoid CPU writes after I/O
* starts and then write it out here.
*/
try_to_unmap_flush_dirty();
- switch (pageout(&ctx, mapping, folio, folio_list)) {
- case PAGE_KEEP:
- goto keep_locked;
- case PAGE_ACTIVATE:
- /*
- * If shmem folio is split when writeback to swap,
- * the tail pages will make their own pass through
- * this function and be accounted then.
- */
- if (nr_pages > 1 && !folio_test_large(folio)) {
- sc->nr_scanned -= (nr_pages - 1);
- nr_pages = 1;
- }
+ switch (folio_try_pageout(folio, sc, &ctx, folio_list)) {
+ case FOLIO_PAGEOUT_ACTIVATE:
goto activate_locked;
- case PAGE_SUCCESS:
- if (nr_pages > 1 && !folio_test_large(folio)) {
- sc->nr_scanned -= (nr_pages - 1);
- nr_pages = 1;
- }
- if (folio_test_writeback(folio))
- goto keep;
- if (folio_test_dirty(folio))
- goto keep;
-
- /*
- * A synchronous write - probably a ramdisk. Go
- * ahead and try to reclaim the folio.
- */
- if (!folio_trylock(folio))
- goto keep;
- if (folio_test_dirty(folio) ||
- folio_test_writeback(folio))
- goto keep_locked;
- mapping = folio_mapping(folio);
- fallthrough;
- case PAGE_CLEAN:
- ; /* try to free the folio below */
+ case FOLIO_PAGEOUT_KEEP_LOCKED:
+ goto keep_locked;
+ case FOLIO_PAGEOUT_KEEP_UNLOCKED:
+ goto keep;
+ case FOLIO_PAGEOUT_FREE:
+ break; /* folio is locked; try to free it below */
}
}
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 4/4] mm/vmscan: extract folio unmap logic into folio_try_unmap()
2026-09-20 14:24 [PATCH 0/4] mm/vmscan: refactor shrink_folio_list() Zhang Peng
` (2 preceding siblings ...)
2026-09-20 14:24 ` [PATCH 3/4] mm/vmscan: extract folio pageout " Zhang Peng
@ 2026-09-20 14:24 ` Zhang Peng
3 siblings, 0 replies; 5+ messages in thread
From: Zhang Peng @ 2026-09-20 14:24 UTC (permalink / raw)
To: Andrew Morton, Kairui Song, Qi Zheng, Shakeel Butt, Barry Song,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Baoquan He, Baolin Wang,
Johannes Weiner, David Hildenbrand, Michal Hocko,
Lorenzo Stoakes
Cc: linux-mm, linux-kernel, Zhang Peng
From: Zhang Peng <bruzzhang@tencent.com>
shrink_folio_list() contains a self-contained block that sets up
TTU flags and calls try_to_unmap(), accounting for failures via
reclaim_stat. Extract it into folio_try_unmap() to reduce the size
of shrink_folio_list() and make the unmap step independently readable.
folio_try_unmap() is only called when the folio is actually mapped;
the !folio_mapped() check stays in the caller, keeping the function's
semantics clear: it tries to unmap a mapped folio and returns whether
the unmap succeeded.
No functional change.
Reviewed-by: Barry Song <baohua@kernel.org>
Signed-off-by: Zhang Peng <bruzzhang@tencent.com>
---
mm/vmscan.c | 65 +++++++++++++++++++++++++++++++++----------------------------
1 file changed, 35 insertions(+), 30 deletions(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 3bce7ff89293..71b507011603 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1318,6 +1318,38 @@ static enum folio_pageout_result folio_try_pageout(struct folio *folio,
unreachable();
}
+static bool folio_try_unmap(struct folio *folio, struct reclaim_stat *stat,
+ unsigned int nr_pages)
+{
+ enum ttu_flags flags = TTU_BATCH_FLUSH;
+ const bool was_swapbacked = folio_test_swapbacked(folio);
+
+ if (folio_test_pmd_mappable(folio))
+ flags |= TTU_SPLIT_HUGE_PMD;
+ /*
+ * Without TTU_SYNC, try_to_unmap will only begin to hold PTL
+ * from the first present PTE within a large folio. Some
+ * initial PTEs might be skipped due to races with parallel
+ * PTE writes in which PTEs can be cleared temporarily before
+ * being written new present values. This will lead to a large
+ * folio is still mapped while some subpages have been
+ * partially unmapped after try_to_unmap; TTU_SYNC helps
+ * try_to_unmap acquire PTL from the first PTE, eliminating the
+ * influence of temporary PTE values.
+ */
+ if (folio_test_large(folio))
+ flags |= TTU_SYNC;
+
+ try_to_unmap(folio, flags);
+ if (folio_mapped(folio)) {
+ stat->nr_unmap_fail += nr_pages;
+ if (!was_swapbacked && folio_test_swapbacked(folio))
+ stat->nr_lazyfree_fail += nr_pages;
+ return false;
+ }
+ return true;
+}
+
/*
* Reclaimed folios are counted in the return value.
*/
@@ -1596,36 +1628,9 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
* The folio is mapped into the page tables of one or more
* processes. Try to unmap it here.
*/
- if (folio_mapped(folio)) {
- enum ttu_flags flags = TTU_BATCH_FLUSH;
- bool was_swapbacked = folio_test_swapbacked(folio);
-
- if (folio_test_pmd_mappable(folio))
- flags |= TTU_SPLIT_HUGE_PMD;
- /*
- * Without TTU_SYNC, try_to_unmap will only begin to
- * hold PTL from the first present PTE within a large
- * folio. Some initial PTEs might be skipped due to
- * races with parallel PTE writes in which PTEs can be
- * cleared temporarily before being written new present
- * values. This will lead to a large folio is still
- * mapped while some subpages have been partially
- * unmapped after try_to_unmap; TTU_SYNC helps
- * try_to_unmap acquire PTL from the first PTE,
- * eliminating the influence of temporary PTE values.
- */
- if (folio_test_large(folio))
- flags |= TTU_SYNC;
-
- try_to_unmap(folio, flags);
- if (folio_mapped(folio)) {
- stat->nr_unmap_fail += nr_pages;
- if (!was_swapbacked &&
- folio_test_swapbacked(folio))
- stat->nr_lazyfree_fail += nr_pages;
- goto activate_locked;
- }
- }
+ if (folio_mapped(folio) &&
+ !folio_try_unmap(folio, stat, nr_pages))
+ goto activate_locked;
/*
* Folio is unmapped now so it cannot be newly pinned anymore.
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-20 14:25 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-20 14:24 [PATCH 0/4] mm/vmscan: refactor shrink_folio_list() Zhang Peng
2026-09-20 14:24 ` [PATCH 1/4] mm/vmscan: introduce folio_activate_locked() helper Zhang Peng
2026-09-20 14:24 ` [PATCH 2/4] mm/vmscan: extract folio reclaim freeing from shrink_folio_list() Zhang Peng
2026-09-20 14:24 ` [PATCH 3/4] mm/vmscan: extract folio pageout " Zhang Peng
2026-09-20 14:24 ` [PATCH 4/4] mm/vmscan: extract folio unmap logic into folio_try_unmap() Zhang Peng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®