Re: [PATCH v3 24/26] mm/page_alloc: always direct compact for unmapped allocs

"Vlastimil Babka (SUSE)" <[email protected]> Mon, 3 Aug 2026 11:44:27 +0200
Newsgroups org.kvack.linux-mm,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On 7/27/26 00:22, Brendan Jackman wrote:
> This is the minimal solution for ensuring that compaction can service
> unmapped allocations. Without this, it's possible for compaction to just
> check watermarks and see plenty of free pages, without being aware of
> the direct map state, and thereby cause an ALLOC_UNMAPPED allocation to
> fail unnecessarily.
> 
> Instead, with this change, promote compact_order to pageblock order for
> unmapped allocations, much like defrag_mode. Then, check specifically in
> compaction for the presence of wholly mapped blocks that can be unmapped
> once direct compact is complete.
> 
> This all takes advantage of a major simplification: since unmapped
> blocks are currently always unmovable, this can be asymmetric. There is
> never a need to promote a !ALLOC_UNMAPPED allocation to compacting at
> pageblock_order, because compaction would be trying to generate a
> currently-unmapped block to map; that will always fail because it would
> require migrating unmapped pages, which is not supported at the moment.
> 
> Signed-off-by: Brendan Jackman <[email protected]>

Reviewed-by: Vlastimil Babka (SUSE) <[email protected]>

Nit:

> ---
>  mm/compaction.c | 22 ++++++++++++++++++----
>  mm/page_alloc.c |  9 +++++++++
>  2 files changed, 27 insertions(+), 4 deletions(-)
> 
> diff --git a/mm/compaction.c b/mm/compaction.c
> index ed12d2fc6fad3..fe1aaf293bbce 100644
> --- a/mm/compaction.c
> +++ b/mm/compaction.c
> @@ -2531,12 +2531,25 @@ 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)
> +				 bool unmapped, bool async, bool kcompactd)

Instead of the new bool parameter, can we check alloc_flags for ALLOC_UNMAPPED?

>  {
>  	unsigned long free_pages;
>  	unsigned long watermark;
>  
> -	if (kcompactd && defrag_mode)
> +	/*
> +	 * When trying to generate an unmapped block, check the counter for
> +	 * direct-mapped blocks specifically, since we'll need to unmap the
> +	 * whole block to service the allocation.
> +	 *
> +	 * Why doesn't this apply to the other way around too? (Mightn't we need
> +	 * to _map_ a whole block, to service a !ALLOC_UNMAPPED allocation?) No,
> +	 * because of a likely-temporary simplification: currently, unmapped
> +	 * blocks never contain movable pages, so compaction isn't going to free
> +	 * up one of those.
> +	 */
> +	if (unmapped)
> +		free_pages = zone_page_state(zone, NR_FREE_PAGES_BLOCKS_MAPPED);
> +	else if (kcompactd && defrag_mode)
>  		free_pages = zone_free_pages_blocks(zone);
>  	else
>  		free_pages = zone_page_state(zone, NR_FREE_PAGES);
> @@ -2599,6 +2612,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,
> +						       freetype_unmapped(cc->freetype),
>  						       cc->mode == MIGRATE_ASYNC,
>  						       !cc->direct_compaction);
>  		if (ret != COMPACT_CONTINUE)
> @@ -3084,7 +3098,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);
> +				false, false, true);
>  		if (ret == COMPACT_CONTINUE)
>  			return true;
>  	}
> @@ -3127,7 +3141,7 @@ static void kcompactd_do_work(pg_data_t *pgdat)
>  
>  		ret = compaction_suit_allocation_order(zone,
>  				cc.order, zoneid, cc.alloc_flags,
> -				false, true);
> +				false, false, true);
>  		if (ret != COMPACT_CONTINUE)
>  			continue;
>  
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index d12ce84662ab7..5f1dea7eee15b 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -827,6 +827,9 @@ compaction_capture(struct capture_control *capc, struct page *page,
>  	    capc_mt != MIGRATE_MOVABLE)
>  		return false;
>  
> +	if (freetype_flags(freetype) != freetype_flags(capc->freetype))
> +		return false;
> +
>  	if (migratetype != capc_mt)
>  		trace_mm_page_alloc_extfrag(page, capc->order, order,
>  					    capc_mt, migratetype);
> @@ -4523,6 +4526,12 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
>  	if ((alloc_flags & ALLOC_NOFRAGMENT) &&
>  	    free_to_migratetype(ac->freetype) != MIGRATE_MOVABLE)
>  		compact_order = max(order, pageblock_order);
> +	/*
> +	 * Unmapped allocations benefit from compaction even at order 0, because the
> +	 * allocator will actually grab a whole block.
> +	 */
> +	if (freetype_flags(ac->freetype) & FREETYPE_UNMAPPED)
> +		compact_order = max(order, pageblock_order);
>  
>  	if (!compact_order)
>  		return NULL;
>