The zonelist_update_seq retry was limited to CONFIG_MEMORY_HOTREMOVE, presumably because only hot-remove takes zones out of a zonelist. However, build_zonerefs_node() rewrites the zonelists in place, so onlining the first block of a zone shifts the entries that follow it in every zonelist containing that zone. This can cause a spurious allocation failure for a __GFP_THISNODE caller. For a node with only ZONE_NORMAL, onlining its first ZONE_MOVABLE block rewrites the node's ZONELIST_NOFALLBACK list as: [NORMAL, NULL] -> [MOVABLE, NULL] -> [MOVABLE, NORMAL, NULL] A concurrent GFP_KERNEL | __GFP_THISNODE allocation can observe the intermediate list, skip ZONE_MOVABLE, and find no eligible zone. With CONFIG_MEMORY_HOTREMOVE=n, check_retry_zonelist() returns 0 and the allocation fails even though the node has free ZONE_NORMAL memory. Gate the sequence checks on CONFIG_MEMORY_HOTPLUG, which covers every configuration that rebuilds zonelists after boot. Fixes: 3d36424b3b58 ("mm/page_alloc: fix race condition between build_all_zonelists and page allocation") Cc: stable@vger.kernel.org Reviewed-by: Vlastimil Babka (SUSE) Signed-off-by: Gregory Price (Meta) --- mm/page_alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2daf358269ae7..c7125f6693574 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4404,7 +4404,7 @@ static DEFINE_SEQLOCK(zonelist_update_seq); static unsigned int zonelist_iter_begin(void) { - if (IS_ENABLED(CONFIG_MEMORY_HOTREMOVE)) + if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG)) return read_seqbegin(&zonelist_update_seq); return 0; @@ -4412,7 +4412,7 @@ static unsigned int zonelist_iter_begin(void) static unsigned int check_retry_zonelist(unsigned int seq) { - if (IS_ENABLED(CONFIG_MEMORY_HOTREMOVE)) + if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG)) return read_seqretry(&zonelist_update_seq, seq); return seq; -- 2.53.0-Meta