Re: [PATCH] mm/page_alloc: let the highest memory node recover from hopeless state

From: Vlastimil Babka (SUSE)

Date: Tue Oct 20 2026 - 03:27:05 EST


On 10/19/26 20:48, Gregory Price (Meta) wrote:
> free_frozen_page_commit() clears a hopeless node's kswapd failures once
> its zone recovers above the high watermark, but only if another memory
> node exists for allocations to fall back to.
>
> The test `next_memory_node(nid) < MAX_NUMNODES` finds only memory nodes
> with a higher id. The highest-numbered memory node therefore never
> recovers on this path, and its kswapd stays disabled until reclaim on
> that node makes progress and balances it, or demotion is re-enabled.
>
> In many cases, direct reclaim many never fully run against a lower
> node (CXL/kmem), and that means its kswapd can end up full disabled
> in rare situations.
>
> To reproduce, fill the highest memory node with bound, non-THP anonymous
> memory and no swap until kswapd gives up, then free it: /proc/zoneinfo
> keeps showing node_unreclaimable 1 for that node. A lower node recovers.
>
> Count the memory nodes instead. Adding first_memory_node != nid to the
> existing test would also work, but a count says directly what the check
> means: some other memory node exists.
>
> Fixes: e7a5f249e6db ("mm: re-enable kswapd when memory pressure subsides or demotion is toggled")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Gregory Price (Meta) <gourry@xxxxxxxxxx>

LGTM enough for a stable hotfix
Acked-by: Vlastimil Babka (SUSE) <vbabka@xxxxxxxxxx>

> ---
> mm/page_alloc.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index 417c427b196d9..6d4f03faf96ee 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -2929,7 +2929,7 @@ static bool free_frozen_page_commit(struct zone *zone,
> * kswapd work again by resetting kswapd_failures.
> */
> if (kswapd_test_hopeless(pgdat) &&
> - next_memory_node(pgdat->node_id) < MAX_NUMNODES)
> + num_node_state(N_MEMORY) > 1)
> kswapd_clear_hopeless(pgdat, KSWAPD_CLEAR_HOPELESS_PCP);
> }
> return ret;