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@vger.kernel.org Signed-off-by: Gregory Price (Meta) --- 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; -- 2.53.0-Meta