Re: [PATCH v3] mm: page_alloc: make defrag_mode retries follow the promoted order

From: Kiryl Shutsemau

Date: Fri Oct 23 2026 - 07:01:34 EST


On Fri, Oct 23, 2026 at 12:09:07PM +0530, Dev Jain wrote:
> >> I ran a synthetic benchmark faulting in lots of 64k mTHP with swap enabled,
> >> defrag mode caused me to enter a warn_alloc (and 8 times more compact_stalls),
> >> which didn't happen without defrag.

Thanks for the benchmark. It reproduces on x86 in the same shape.

I think I found the problem.

Under pressure, the free order-4+ pages left are mostly in partly used
unmovable and reclaimable pageblocks. A movable request with
ALLOC_NOFRAGMENT cannot take them. But compaction_suit_allocation_order()
asks __zone_watermark_ok(), which counts free pages of every
migratetype, and returns COMPACT_SUCCESS without compacting anything.
The allocation then fails, and since compaction "succeeded", it is never
deferred.

With the diff below, compaction behaves the same with and without
defrag_mode. Medians of 15 runs of "compact-bench 99 64 16 20 0":

defrag_mode=0 defrag_mode=1 defrag_mode=1
+ diff
compact_stall 2135 32476 2290
compact_fail 668 32330 606
64K anon_fault_alloc 20689 6780 11804
64K anon_fault_fallback 776 14502 9687

64K allocation is still lower with defrag_mode. I will look into this
further.

Could you try the diff on your setup?

diff --git a/mm/compaction.c b/mm/compaction.c
index a049415512c6..09be677fff36 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -2365,6 +2365,10 @@ static enum compact_result __compact_finished(struct compact_control *cc)
!free_area_empty(area, MIGRATE_CMA))
return COMPACT_SUCCESS;
#endif
+ /* Under ALLOC_NOFRAGMENT it may only claim a whole block */
+ if ((cc->alloc_flags & ALLOC_NOFRAGMENT) && order < pageblock_order)
+ continue;
+
/*
* Job done if allocation would steal freepages from
* other migratetype buddy lists.
@@ -2510,6 +2514,43 @@ bool compaction_zonelist_suitable(struct alloc_context *ac, int order,
return false;
}

+/*
+ * The watermark check counts free pages of every migratetype. Under
+ * ALLOC_NOFRAGMENT, a request below pageblock_order cannot take a page from a
+ * block of another migratetype unless it claims the whole block, so look for
+ * a free page the request can actually use.
+ */
+static bool compaction_free_page_usable(struct zone *zone, unsigned int order,
+ int migratetype, unsigned int alloc_flags)
+{
+ unsigned int o;
+ int mt;
+
+ if (!(alloc_flags & ALLOC_NOFRAGMENT) || order >= pageblock_order)
+ return true;
+
+ for (o = order; o < NR_PAGE_ORDERS; o++) {
+ struct free_area *area = &zone->free_area[o];
+
+ if (!free_area_empty(area, migratetype))
+ return true;
+#ifdef CONFIG_CMA
+ if ((alloc_flags & ALLOC_CMA) &&
+ !free_area_empty(area, MIGRATE_CMA))
+ return true;
+#endif
+ /* From pageblock_order up, a free block can be claimed */
+ if (o < pageblock_order)
+ continue;
+ for (mt = 0; mt < MIGRATE_PCPTYPES; mt++) {
+ if (!free_area_empty(area, mt))
+ return true;
+ }
+ }
+
+ return false;
+}
+
/*
* Should we do compaction for target allocation order.
* Return COMPACT_SUCCESS if allocation for target order can be already
@@ -2520,7 +2561,7 @@ bool compaction_zonelist_suitable(struct alloc_context *ac, int order,
static enum compact_result
compaction_suit_allocation_order(struct zone *zone, unsigned int order,
int highest_zoneidx, unsigned int alloc_flags,
- bool async, bool kcompactd)
+ int migratetype, bool async, bool kcompactd)
{
unsigned long free_pages;
unsigned long watermark;
@@ -2532,7 +2573,8 @@ compaction_suit_allocation_order(struct zone *zone, unsigned int order,

watermark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK);
if (__zone_watermark_ok(zone, order, watermark, highest_zoneidx,
- alloc_flags, free_pages))
+ alloc_flags, free_pages) &&
+ compaction_free_page_usable(zone, order, migratetype, alloc_flags))
return COMPACT_SUCCESS;

/*
@@ -2588,6 +2630,7 @@ compact_zone(struct compact_control *cc, struct capture_control *capc)
ret = compaction_suit_allocation_order(cc->zone, cc->order,
cc->highest_zoneidx,
cc->alloc_flags,
+ cc->migratetype,
cc->mode == MIGRATE_ASYNC,
!cc->direct_compaction);
if (ret != COMPACT_CONTINUE)
@@ -3073,7 +3116,7 @@ static bool kcompactd_node_suitable(pg_data_t *pgdat)
ret = compaction_suit_allocation_order(zone,
pgdat->kcompactd_max_order,
highest_zoneidx, alloc_flags,
- false, true);
+ MIGRATE_MOVABLE, false, true);
if (ret == COMPACT_CONTINUE)
return true;
}
@@ -3116,7 +3159,7 @@ static void kcompactd_do_work(pg_data_t *pgdat)

ret = compaction_suit_allocation_order(zone,
cc.order, zoneid, cc.alloc_flags,
- false, true);
+ MIGRATE_MOVABLE, false, true);
if (ret != COMPACT_CONTINUE)
continue;

--
Kiryl Shutsemau / Kirill A. Shutemov