zonelist_node_idx() looks like it returns a field of the zoneref, as zonelist_zone_idx() does, but it loads the zone from the slot again.
Memory hotplug rewrites zonelists in place, so a caller that has already checked the slot can get a different zone, or the NULL terminator, from that second load. __next_zones_zonelist() and mempolicy_slab_node() had this bug. Remove the helper and open-code it in its two remaining callers, neither of which can race with a rebuild. local_memory_node() runs either inside __build_all_zonelists() or during CPU bring-up, which holds cpu_hotplug_lock for write and so excludes mem_hotplug_begin(). The lookup in mpol_misplaced() is unreachable: it needs a policy with MPOL_F_MOF but not MPOL_F_MORON, and nothing sets one without the other. Signed-off-by: Gregory Price (Meta) <[email protected]> --- include/linux/mmzone.h | 6 ------ mm/mempolicy.c | 2 +- mm/page_alloc.c | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 4bc2318ac37f..72ffb2959c4f 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -1439,7 +1439,6 @@ struct zoneref { * * zonelist_zone() - Return the struct zone * for an entry in _zonerefs * zonelist_zone_idx() - Return the index of the zone for an entry - * zonelist_node_idx() - Return the index of the node for an entry */ struct zonelist { struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1]; @@ -1864,11 +1863,6 @@ static inline int zonelist_zone_idx(const struct zoneref *zoneref) return zoneref->zone_idx; } -static inline int zonelist_node_idx(const struct zoneref *zoneref) -{ - return zone_to_nid(zoneref->zone); -} - struct zoneref *__next_zones_zonelist(struct zoneref *z, enum zone_type highest_zoneidx, const nodemask_t *nodes); diff --git a/mm/mempolicy.c b/mm/mempolicy.c index cd63c3e2c029..267576054980 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -3087,7 +3087,7 @@ int mpol_misplaced(struct folio *folio, struct vm_fault *vmf, node_zonelist(thisnid, GFP_HIGHUSER), gfp_zone(GFP_HIGHUSER), &pol->nodes); - polnid = zonelist_node_idx(z); + polnid = zone_to_nid(zonelist_zone(z)); break; default: diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 91ad979409e5..81c1de714658 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5973,7 +5973,7 @@ int local_memory_node(int node) z = first_zones_zonelist(node_zonelist(node, GFP_KERNEL), gfp_zone(GFP_KERNEL), NULL); - return zonelist_node_idx(z); + return zone_to_nid(zonelist_zone(z)); } #endif -- 2.56.0
