Re: [RFC PATCH 4/4] mm: adjust free_pages to make __zone_watermark_ok() mTHP-aware

From: Xueyuan Chen

Date: Wed Sep 02 2026 - 22:52:51 EST



On Tue, Aug 25, 2026 at 12:38:33PM +0800, Bo Zhang wrote:
>To improve the mTHP allocation success rate and reduce
>fragmentation over time, introduce
>zone_effective_free_pages() for mTHP-aware free page accounting.
>
>Refactor the free page counting logic used by pgdat_balanced() and
>compaction_suit_allocation_order() into a shared helper function
>zone_effective_free_pages().
>
>The function selects the appropriate free page metric based on context:
>- defrag_mode: use NR_FREE_PAGES_BLOCKS (whole pageblock accounting)
>- mTHP always-enabled: count only free pages in buddy blocks >= the
> minimum always-enabled mTHP order, since smaller fragments cannot
> satisfy mTHP allocations
>- otherwise: use NR_FREE_PAGES
>
>Signed-off-by: Bo Zhang <zhangbo56@xxxxxxxxxx>
>---
> mm/compaction.c | 38 ++++++++++++++++++++++++++++++++++----
> mm/internal.h | 3 +++
> mm/vmscan.c | 23 +++++------------------
> 3 files changed, 42 insertions(+), 22 deletions(-)
>
>diff --git a/mm/compaction.c b/mm/compaction.c
>index 15b92475562a..29be72597415 100644
>--- a/mm/compaction.c
>+++ b/mm/compaction.c
>@@ -2528,6 +2528,38 @@ bool compaction_zonelist_suitable(struct alloc_context *ac, int order,
> return false;
> }
>
>+/**
>+ * zone_effective_free_pages - get free pages relevant to allocation order
>+ * @zone: target zone
>+ * @order: allocation order
>+ * @use_blocks: if true, use NR_FREE_PAGES_BLOCKS
>+ *
>+ * In defrag_mode, watermarks must be met in whole blocks to avoid
>+ * polluting allocator fallbacks. kswapd usually cannot accomplish
>+ * this on its own and needs kcompactd support.
>+ *
>+ * When mTHP always-enabled orders are configured, count only free pages
>+ * in blocks >= min mTHP order, as smaller fragments cannot satisfy mTHP
>+ * allocations.
>+ */
>+unsigned long zone_effective_free_pages(struct zone *zone,
>+ unsigned int order,
>+ bool use_blocks)
>+{
>+ if (use_blocks)
>+ return zone_page_state(zone, NR_FREE_PAGES_BLOCKS);
>+
>+ if (READ_ONCE(huge_anon_orders_always) && order == compact_hpage_order()) {
>+ unsigned long free_pages = 0;
>+
>+ for (int o = order; o < NR_PAGE_ORDERS; o++)
>+ free_pages += zone->free_area[o].nr_free << o;
>+ return free_pages;
>+ }
>+
>+ return zone_page_state(zone, NR_FREE_PAGES);
>+}
>+
> /*
> * Should we do compaction for target allocation order.
> * Return COMPACT_SUCCESS if allocation for target order can be already
>@@ -2543,10 +2575,8 @@ compaction_suit_allocation_order(struct zone *zone, unsigned int order,
> unsigned long free_pages;
> unsigned long watermark;
>
>- if (kcompactd && defrag_mode)
>- free_pages = zone_page_state(zone, NR_FREE_PAGES_BLOCKS);
>- else
>- free_pages = zone_page_state(zone, NR_FREE_PAGES);
>+ free_pages = zone_effective_free_pages(zone, order,
>+ kcompactd && defrag_mode);
>
> watermark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK);
> if (__zone_watermark_ok(zone, order, watermark, highest_zoneidx,
>diff --git a/mm/internal.h b/mm/internal.h
>index 38b1165212c9..14bb9543879b 100644
>--- a/mm/internal.h
>+++ b/mm/internal.h
>@@ -1654,4 +1654,7 @@ static inline bool can_spin_trylock(void)
> return true;
> }
>
>+unsigned long zone_effective_free_pages(struct zone *zone,
>+ unsigned int order,
>+ bool use_blocks);
> #endif /* __MM_INTERNAL_H */
>diff --git a/mm/vmscan.c b/mm/vmscan.c
>index c1404a59523d..a419a2c2fca4 100644
>--- a/mm/vmscan.c
>+++ b/mm/vmscan.c
>@@ -6966,7 +6966,6 @@ static bool pgdat_balanced(pg_data_t *pgdat, int order, int highest_zoneidx)
> * meet watermarks.
> */
> for_each_managed_zone_pgdat(zone, pgdat, i, highest_zoneidx) {
>- enum zone_stat_item item;
> unsigned long free_pages;
>
> if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING)
>@@ -6974,21 +6973,6 @@ static bool pgdat_balanced(pg_data_t *pgdat, int order, int highest_zoneidx)
> else
> mark = high_wmark_pages(zone);
>
>- /*
>- * In defrag_mode, watermarks must be met in whole
>- * blocks to avoid polluting allocator fallbacks.
>- *
>- * However, kswapd usually cannot accomplish this on
>- * its own and needs kcompactd support. Once it's
>- * reclaimed a compaction gap, and kswapd_shrink_node
>- * has dropped order, simply ensure there are enough
>- * base pages for compaction, wake kcompactd & sleep.
>- */
>- if (defrag_mode && order)
>- item = NR_FREE_PAGES_BLOCKS;
>- else
>- item = NR_FREE_PAGES;
>-
> /*
> * When there is a high number of CPUs in the system,
> * the cumulative error from the vmstat per-cpu cache
>@@ -7001,9 +6985,12 @@ static bool pgdat_balanced(pg_data_t *pgdat, int order, int highest_zoneidx)
> * counter won't actually be per-cpu cached. But keep
> * things simple for now; revisit when somebody cares.
> */
>- free_pages = zone_page_state(zone, item);
>+ free_pages = zone_effective_free_pages(zone, order,
>+ defrag_mode & order);

Hi Bo,

zone_effective_free_pages(zone, order, defrag_mode & order);

"&" should be "&&". defrag_mode is 0 or 1, so this is just order & 1:
works for order-3 by accident, always 0 for order-2.

Same for the zone_page_state_snapshot() call below.

Thanks,
Xueyuan

> if (zone->percpu_drift_mark && free_pages < zone->percpu_drift_mark)
>- free_pages = zone_page_state_snapshot(zone, item);
>+ free_pages = zone_page_state_snapshot(zone,
>+ defrag_mode & order ?
>+ NR_FREE_PAGES_BLOCKS : NR_FREE_PAGES);
>
> if (__zone_watermark_ok(zone, order, mark, highest_zoneidx,
> 0, free_pages))
>--
>2.34.1
>
>