* [PATCH 0/2] mm: refactor zonelist constructors and iterators
@ 2026-09-02 21:58 Gregory Price
2026-09-02 21:58 ` [PATCH 1/2] mm: refactor find_next_best_node to find_next_best_node_in Gregory Price
2026-09-02 21:58 ` [PATCH 2/2] mm/page_alloc: refactor build_node_zonelist() out of build_zonelists() Gregory Price
0 siblings, 2 replies; 8+ messages in thread
From: Gregory Price @ 2026-09-02 21:58 UTC (permalink / raw)
To: linux-mm
Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt,
surenb, mhocko, brendan.jackman, hannes, ziy
find_next_best_node() picks the next-closest node when building a
fallback list, and hardcodes N_MEMORY as the set it picks from.
Refactor it into find_next_best_node_in(), which takes the candidate
set explicitly.
This makes the existing behaviour explicit at both mm/memory-tiers.c
call sites - they select demotion targets in fallback order from
N_MEMORY - and lets callers narrow that set.
Then extract the per-node construction loop out of build_zonelists()
into build_node_zonelist(), parameterised on the candidate nodemask
and on the destination zonelist index.
Together these allow a zonelist to be built over a candidate set other
than N_MEMORY, into a zonelist other than FALLBACK, and iterated in
fallback order over a caller-defined subset.
These are prerequisites for generating a private node zonelist (nodes
unreachable by default), but are otherwise general improvements to
the existing interfaces so I'm proposing them separately.
No functional change intended - purely refactor commits.
Verified on x86_64:
- find_next_best_node_in() compiles to the same 319 bytes as
find_next_best_node().
- Both mm/memory-tiers.c callers grow a single instruction - the
added argument.
- Boot-time fallback orders are identical on a 4-node guest with an
asymmetric distance matrix.
Gregory Price (2):
mm: refactor find_next_best_node to find_next_best_node_in
mm/page_alloc: refactor build_node_zonelist() out of build_zonelists()
mm/internal.h | 6 ++++--
mm/memory-tiers.c | 7 +++---
mm/page_alloc.c | 55 +++++++++++++++++++++++++++++++----------------
3 files changed, 45 insertions(+), 23 deletions(-)
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 1/2] mm: refactor find_next_best_node to find_next_best_node_in 2026-09-02 21:58 [PATCH 0/2] mm: refactor zonelist constructors and iterators Gregory Price @ 2026-09-02 21:58 ` Gregory Price 2026-09-02 23:48 ` Gregory Price 2026-09-03 15:15 ` Vlastimil Babka (SUSE) 2026-09-02 21:58 ` [PATCH 2/2] mm/page_alloc: refactor build_node_zonelist() out of build_zonelists() Gregory Price 1 sibling, 2 replies; 8+ messages in thread From: Gregory Price @ 2026-09-02 21:58 UTC (permalink / raw) To: linux-mm Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt, surenb, mhocko, brendan.jackman, hannes, ziy find_next_best_node() picks the next-closest node for a fallback list from the full N_MEMORY set. Refactor it into find_next_best_node_in(), which takes an explicit candidates nodemask. This enables building fallback lists with non-N_MEMORY candidates. No functional change: every caller still selects from N_MEMORY. Signed-off-by: Gregory Price <gourry@gourry.net> --- mm/internal.h | 6 ++++-- mm/memory-tiers.c | 7 ++++--- mm/page_alloc.c | 13 ++++++++----- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/mm/internal.h b/mm/internal.h index e16f1250b25c8..18d041ff5c52c 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -1130,7 +1130,8 @@ extern int node_reclaim_mode; extern unsigned long node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned int order); -extern int find_next_best_node(int node, nodemask_t *used_node_mask); +extern int find_next_best_node_in(int node, nodemask_t *used_node_mask, + const nodemask_t *candidates); #else #define node_reclaim_mode 0 @@ -1139,7 +1140,8 @@ static inline unsigned long node_reclaim(struct pglist_data *pgdat, { return 0; } -static inline int find_next_best_node(int node, nodemask_t *used_node_mask) +static inline int find_next_best_node_in(int node, nodemask_t *used_node_mask, + const nodemask_t *candidates) { return NUMA_NO_NODE; } diff --git a/mm/memory-tiers.c b/mm/memory-tiers.c index 54851d8a195b0..25e121851b586 100644 --- a/mm/memory-tiers.c +++ b/mm/memory-tiers.c @@ -370,7 +370,7 @@ int next_demotion_node(int node, const nodemask_t *allowed_mask) * closest demotion target. */ nodes_complement(mask, *allowed_mask); - return find_next_best_node(node, &mask); + return find_next_best_node_in(node, &mask, &node_states[N_MEMORY]); } static void disable_all_demotion_targets(void) @@ -450,7 +450,7 @@ static void establish_demotion_targets(void) memtier = list_next_entry(memtier, list); tier_nodes = get_memtier_nodemask(memtier); /* - * find_next_best_node, use 'used' nodemask as a skip list. + * find_next_best_node_in, use 'used' nodemask as a skip list. * Add all memory nodes except the selected memory tier * nodelist to skip list so that we find the best node from the * memtier nodelist. @@ -463,7 +463,8 @@ static void establish_demotion_targets(void) * in the preferred mask when allocating pages during demotion. */ do { - target = find_next_best_node(node, &tier_nodes); + target = find_next_best_node_in(node, &tier_nodes, + &node_states[N_MEMORY]); if (target == NUMA_NO_NODE) break; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index c4dc61ec663ee..4dde1cbe2fd43 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5779,9 +5779,10 @@ static int numa_zonelist_order_handler(const struct ctl_table *table, int write, static int node_load[MAX_NUMNODES]; /** - * find_next_best_node - find the next node that should appear in a given node's fallback list + * find_next_best_node_in - find the next node that should appear in a given node's fallback list * @node: node whose fallback list we're appending * @used_node_mask: nodemask_t of already used nodes + * @candidates: nodemask_t of nodes eligible for selection * * We use a number of factors to determine which is the next node that should * appear on a given node's fallback list. The node should not have appeared @@ -5793,7 +5794,8 @@ static int node_load[MAX_NUMNODES]; * * Return: node id of the found node or %NUMA_NO_NODE if no node is found. */ -int find_next_best_node(int node, nodemask_t *used_node_mask) +int find_next_best_node_in(int node, nodemask_t *used_node_mask, + const nodemask_t *candidates) { int n, val; int min_val = INT_MAX; @@ -5803,12 +5805,12 @@ int find_next_best_node(int node, nodemask_t *used_node_mask) * Use the local node if we haven't already, but for memoryless local * node, we should skip it and fall back to other nodes. */ - if (!node_isset(node, *used_node_mask) && node_state(node, N_MEMORY)) { + if (!node_isset(node, *used_node_mask) && node_isset(node, *candidates)) { node_set(node, *used_node_mask); return node; } - for_each_node_state(n, N_MEMORY) { + for_each_node_mask(n, *candidates) { /* Don't want a node to appear more than once */ if (node_isset(n, *used_node_mask)) @@ -5893,7 +5895,8 @@ static void build_zonelists(pg_data_t *pgdat) prev_node = local_node; memset(node_order, 0, sizeof(node_order)); - while ((node = find_next_best_node(local_node, &used_mask)) >= 0) { + while ((node = find_next_best_node_in(local_node, &used_mask, + &node_states[N_MEMORY])) >= 0) { /* * We don't want to pressure a particular node. * So adding penalty to the first node in same -- 2.53.0-Meta ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] mm: refactor find_next_best_node to find_next_best_node_in 2026-09-02 21:58 ` [PATCH 1/2] mm: refactor find_next_best_node to find_next_best_node_in Gregory Price @ 2026-09-02 23:48 ` Gregory Price 2026-09-03 15:15 ` Vlastimil Babka (SUSE) 1 sibling, 0 replies; 8+ messages in thread From: Gregory Price @ 2026-09-02 23:48 UTC (permalink / raw) To: linux-mm Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt, surenb, mhocko, brendan.jackman, hannes, ziy, balbirs On Wed, Sep 02, 2026 at 05:58:52PM -0400, Gregory Price wrote: > find_next_best_node() picks the next-closest node for a fallback list > from the full N_MEMORY set. Refactor it into find_next_best_node_in(), > which takes an explicit candidates nodemask. > > This enables building fallback lists with non-N_MEMORY candidates. > > No functional change: every caller still selects from N_MEMORY. > > Signed-off-by: Gregory Price <gourry@gourry.net> Just realized this should carry an Ack from Balbir Acked-by: Balbir Singh <balbirs@nvidia.com> see - https://lore.kernel.org/linux-mm/62bf02dc-dab1-41bf-8204-eb3566628b93@nvidia.com/ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] mm: refactor find_next_best_node to find_next_best_node_in 2026-09-02 21:58 ` [PATCH 1/2] mm: refactor find_next_best_node to find_next_best_node_in Gregory Price 2026-09-02 23:48 ` Gregory Price @ 2026-09-03 15:15 ` Vlastimil Babka (SUSE) 2026-09-03 16:46 ` Gregory Price 1 sibling, 1 reply; 8+ messages in thread From: Vlastimil Babka (SUSE) @ 2026-09-03 15:15 UTC (permalink / raw) To: Gregory Price, linux-mm Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, rppt, surenb, mhocko, brendan.jackman, hannes, ziy On 9/2/26 23:58, Gregory Price wrote: > find_next_best_node() picks the next-closest node for a fallback list > from the full N_MEMORY set. Refactor it into find_next_best_node_in(), > which takes an explicit candidates nodemask. > > This enables building fallback lists with non-N_MEMORY candidates. > > No functional change: every caller still selects from N_MEMORY. > > Signed-off-by: Gregory Price <gourry@gourry.net> Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> Nit: > --- > mm/internal.h | 6 ++++-- > mm/memory-tiers.c | 7 ++++--- > mm/page_alloc.c | 13 ++++++++----- > 3 files changed, 16 insertions(+), 10 deletions(-) > > diff --git a/mm/internal.h b/mm/internal.h > index e16f1250b25c8..18d041ff5c52c 100644 > --- a/mm/internal.h > +++ b/mm/internal.h > @@ -1130,7 +1130,8 @@ extern int node_reclaim_mode; > > extern unsigned long node_reclaim(struct pglist_data *pgdat, > gfp_t gfp_mask, unsigned int order); > -extern int find_next_best_node(int node, nodemask_t *used_node_mask); > +extern int find_next_best_node_in(int node, nodemask_t *used_node_mask, > + const nodemask_t *candidates); Drop the extern while touching a line. > #else > #define node_reclaim_mode 0 > > @@ -1139,7 +1140,8 @@ static inline unsigned long node_reclaim(struct pglist_data *pgdat, > { > return 0; > } > -static inline int find_next_best_node(int node, nodemask_t *used_node_mask) > +static inline int find_next_best_node_in(int node, nodemask_t *used_node_mask, > + const nodemask_t *candidates) > { > return NUMA_NO_NODE; > } > diff --git a/mm/memory-tiers.c b/mm/memory-tiers.c > index 54851d8a195b0..25e121851b586 100644 > --- a/mm/memory-tiers.c > +++ b/mm/memory-tiers.c > @@ -370,7 +370,7 @@ int next_demotion_node(int node, const nodemask_t *allowed_mask) > * closest demotion target. > */ > nodes_complement(mask, *allowed_mask); > - return find_next_best_node(node, &mask); > + return find_next_best_node_in(node, &mask, &node_states[N_MEMORY]); > } > > static void disable_all_demotion_targets(void) > @@ -450,7 +450,7 @@ static void establish_demotion_targets(void) > memtier = list_next_entry(memtier, list); > tier_nodes = get_memtier_nodemask(memtier); > /* > - * find_next_best_node, use 'used' nodemask as a skip list. > + * find_next_best_node_in, use 'used' nodemask as a skip list. > * Add all memory nodes except the selected memory tier > * nodelist to skip list so that we find the best node from the > * memtier nodelist. > @@ -463,7 +463,8 @@ static void establish_demotion_targets(void) > * in the preferred mask when allocating pages during demotion. > */ > do { > - target = find_next_best_node(node, &tier_nodes); > + target = find_next_best_node_in(node, &tier_nodes, > + &node_states[N_MEMORY]); > if (target == NUMA_NO_NODE) > break; > > diff --git a/mm/page_alloc.c b/mm/page_alloc.c > index c4dc61ec663ee..4dde1cbe2fd43 100644 > --- a/mm/page_alloc.c > +++ b/mm/page_alloc.c > @@ -5779,9 +5779,10 @@ static int numa_zonelist_order_handler(const struct ctl_table *table, int write, > static int node_load[MAX_NUMNODES]; > > /** > - * find_next_best_node - find the next node that should appear in a given node's fallback list > + * find_next_best_node_in - find the next node that should appear in a given node's fallback list > * @node: node whose fallback list we're appending > * @used_node_mask: nodemask_t of already used nodes > + * @candidates: nodemask_t of nodes eligible for selection > * > * We use a number of factors to determine which is the next node that should > * appear on a given node's fallback list. The node should not have appeared > @@ -5793,7 +5794,8 @@ static int node_load[MAX_NUMNODES]; > * > * Return: node id of the found node or %NUMA_NO_NODE if no node is found. > */ > -int find_next_best_node(int node, nodemask_t *used_node_mask) > +int find_next_best_node_in(int node, nodemask_t *used_node_mask, > + const nodemask_t *candidates) > { > int n, val; > int min_val = INT_MAX; > @@ -5803,12 +5805,12 @@ int find_next_best_node(int node, nodemask_t *used_node_mask) > * Use the local node if we haven't already, but for memoryless local > * node, we should skip it and fall back to other nodes. > */ > - if (!node_isset(node, *used_node_mask) && node_state(node, N_MEMORY)) { > + if (!node_isset(node, *used_node_mask) && node_isset(node, *candidates)) { > node_set(node, *used_node_mask); > return node; > } > > - for_each_node_state(n, N_MEMORY) { > + for_each_node_mask(n, *candidates) { > > /* Don't want a node to appear more than once */ > if (node_isset(n, *used_node_mask)) > @@ -5893,7 +5895,8 @@ static void build_zonelists(pg_data_t *pgdat) > prev_node = local_node; > > memset(node_order, 0, sizeof(node_order)); > - while ((node = find_next_best_node(local_node, &used_mask)) >= 0) { > + while ((node = find_next_best_node_in(local_node, &used_mask, > + &node_states[N_MEMORY])) >= 0) { > /* > * We don't want to pressure a particular node. > * So adding penalty to the first node in same ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] mm: refactor find_next_best_node to find_next_best_node_in 2026-09-03 15:15 ` Vlastimil Babka (SUSE) @ 2026-09-03 16:46 ` Gregory Price 0 siblings, 0 replies; 8+ messages in thread From: Gregory Price @ 2026-09-03 16:46 UTC (permalink / raw) To: Vlastimil Babka (SUSE) Cc: linux-mm, linux-kernel, kernel-team, akpm, david, ljs, liam, rppt, surenb, mhocko, brendan.jackman, hannes, ziy On Thu, Sep 03, 2026 at 05:15:23PM +0200, Vlastimil Babka (SUSE) wrote: > On 9/2/26 23:58, Gregory Price wrote: > > find_next_best_node() picks the next-closest node for a fallback list > > from the full N_MEMORY set. Refactor it into find_next_best_node_in(), > > which takes an explicit candidates nodemask. > > > > This enables building fallback lists with non-N_MEMORY candidates. > > > > No functional change: every caller still selects from N_MEMORY. > > > > Signed-off-by: Gregory Price <gourry@gourry.net> > > Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> > > Nit: > > > --- > > mm/internal.h | 6 ++++-- > > mm/memory-tiers.c | 7 ++++--- > > mm/page_alloc.c | 13 ++++++++----- > > 3 files changed, 16 insertions(+), 10 deletions(-) > > > > diff --git a/mm/internal.h b/mm/internal.h > > index e16f1250b25c8..18d041ff5c52c 100644 > > --- a/mm/internal.h > > +++ b/mm/internal.h > > @@ -1130,7 +1130,8 @@ extern int node_reclaim_mode; > > > > extern unsigned long node_reclaim(struct pglist_data *pgdat, > > gfp_t gfp_mask, unsigned int order); > > -extern int find_next_best_node(int node, nodemask_t *used_node_mask); > > +extern int find_next_best_node_in(int node, nodemask_t *used_node_mask, > > + const nodemask_t *candidates); > > Drop the extern while touching a line. > ack. ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/2] mm/page_alloc: refactor build_node_zonelist() out of build_zonelists() 2026-09-02 21:58 [PATCH 0/2] mm: refactor zonelist constructors and iterators Gregory Price 2026-09-02 21:58 ` [PATCH 1/2] mm: refactor find_next_best_node to find_next_best_node_in Gregory Price @ 2026-09-02 21:58 ` Gregory Price 2026-09-03 15:33 ` Vlastimil Babka (SUSE) 1 sibling, 1 reply; 8+ messages in thread From: Gregory Price @ 2026-09-02 21:58 UTC (permalink / raw) To: linux-mm Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt, surenb, mhocko, brendan.jackman, hannes, ziy Extract per-node fallback-list construction into build_node_zonelist(). This lets us build new zonelists from candidate nodemasks instead of just the default N_MEMORY node state list. No functional change: build_zonelists() builds the same FALLBACK list over N_MEMORY with node_load updates as before. Signed-off-by: Gregory Price <gourry@gourry.net> --- mm/page_alloc.c | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 4dde1cbe2fd43..e1d7c8b221d0d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5849,12 +5849,12 @@ int find_next_best_node_in(int node, nodemask_t *used_node_mask, * DMA zone, if any--but risks exhausting DMA zone. */ static void build_zonelists_in_node_order(pg_data_t *pgdat, int *node_order, - unsigned nr_nodes) + unsigned int nr_nodes, int zlidx) { struct zoneref *zonerefs; int i; - zonerefs = pgdat->node_zonelists[ZONELIST_FALLBACK]._zonerefs; + zonerefs = pgdat->node_zonelists[zlidx]._zonerefs; for (i = 0; i < nr_nodes; i++) { int nr_zones; @@ -5883,26 +5883,28 @@ static void build_thisnode_zonelists(pg_data_t *pgdat) zonerefs->zone_idx = 0; } -static void build_zonelists(pg_data_t *pgdat) +/* + * Build one node-ordered fallback list from a candidate nodemask. + * update_load round-robins node_load across equidistant nodes. + */ +static void build_node_zonelist(pg_data_t *pgdat, const nodemask_t *candidates, + int zlidx, bool update_load, + int *node_order, int *nr) { - static int node_order[MAX_NUMNODES]; - int node, nr_nodes = 0; nodemask_t used_mask = NODE_MASK_NONE; - int local_node, prev_node; - - /* NUMA-aware ordering of nodes */ - local_node = pgdat->node_id; - prev_node = local_node; + int local_node = pgdat->node_id; + int prev_node = local_node; + int node, nr_nodes = 0; - memset(node_order, 0, sizeof(node_order)); while ((node = find_next_best_node_in(local_node, &used_mask, - &node_states[N_MEMORY])) >= 0) { + candidates)) >= 0) { /* * We don't want to pressure a particular node. * So adding penalty to the first node in same * distance group to make it round-robin. */ - if (node_distance(local_node, node) != + if (update_load && + node_distance(local_node, node) != node_distance(local_node, prev_node)) node_load[node] += 1; @@ -5910,8 +5912,22 @@ static void build_zonelists(pg_data_t *pgdat) prev_node = node; } - build_zonelists_in_node_order(pgdat, node_order, nr_nodes); + build_zonelists_in_node_order(pgdat, node_order, nr_nodes, zlidx); + *nr = nr_nodes; +} + +static void build_zonelists(pg_data_t *pgdat) +{ + static int node_order[MAX_NUMNODES]; + int local_node = pgdat->node_id; + int node, nr_nodes = 0; + + memset(node_order, 0, sizeof(node_order)); + + build_node_zonelist(pgdat, &node_states[N_MEMORY], ZONELIST_FALLBACK, + true, node_order, &nr_nodes); build_thisnode_zonelists(pgdat); + pr_info("Fallback order for Node %d: ", local_node); for (node = 0; node < nr_nodes; node++) pr_cont("%d ", node_order[node]); -- 2.53.0-Meta ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] mm/page_alloc: refactor build_node_zonelist() out of build_zonelists() 2026-09-02 21:58 ` [PATCH 2/2] mm/page_alloc: refactor build_node_zonelist() out of build_zonelists() Gregory Price @ 2026-09-03 15:33 ` Vlastimil Babka (SUSE) 2026-09-03 16:02 ` Gregory Price 0 siblings, 1 reply; 8+ messages in thread From: Vlastimil Babka (SUSE) @ 2026-09-03 15:33 UTC (permalink / raw) To: Gregory Price, linux-mm Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, rppt, surenb, mhocko, brendan.jackman, hannes, ziy On 9/2/26 23:58, Gregory Price wrote: > Extract per-node fallback-list construction into build_node_zonelist(). > > This lets us build new zonelists from candidate nodemasks instead of > just the default N_MEMORY node state list. > > No functional change: build_zonelists() builds the same FALLBACK list over > N_MEMORY with node_load updates as before. > > Signed-off-by: Gregory Price <gourry@gourry.net> LGTM, but, while we're at it, could we just do the pr_cont() printing in build_node_zonelist() itself (maybe behind a flag if you don't want to print from future new caller) so it doesn't need to pass nr_nodes back to build_zonelists(). Then also the node_order array could live in build_node_zonelist() itself? Actually I wonder if we could get ride of the node_order array completely. It would mean the loop processing in build_zonelists_in_node_order() would have to be done piece-meal in build_node_zonelist() itself. But seems feasible? Depends on how you intend to reuse/extend the new functions later, I guess... > --- > mm/page_alloc.c | 44 ++++++++++++++++++++++++++++++-------------- > 1 file changed, 30 insertions(+), 14 deletions(-) > > diff --git a/mm/page_alloc.c b/mm/page_alloc.c > index 4dde1cbe2fd43..e1d7c8b221d0d 100644 > --- a/mm/page_alloc.c > +++ b/mm/page_alloc.c > @@ -5849,12 +5849,12 @@ int find_next_best_node_in(int node, nodemask_t *used_node_mask, > * DMA zone, if any--but risks exhausting DMA zone. > */ > static void build_zonelists_in_node_order(pg_data_t *pgdat, int *node_order, > - unsigned nr_nodes) > + unsigned int nr_nodes, int zlidx) > { > struct zoneref *zonerefs; > int i; > > - zonerefs = pgdat->node_zonelists[ZONELIST_FALLBACK]._zonerefs; > + zonerefs = pgdat->node_zonelists[zlidx]._zonerefs; > > for (i = 0; i < nr_nodes; i++) { > int nr_zones; > @@ -5883,26 +5883,28 @@ static void build_thisnode_zonelists(pg_data_t *pgdat) > zonerefs->zone_idx = 0; > } > > -static void build_zonelists(pg_data_t *pgdat) > +/* > + * Build one node-ordered fallback list from a candidate nodemask. > + * update_load round-robins node_load across equidistant nodes. > + */ > +static void build_node_zonelist(pg_data_t *pgdat, const nodemask_t *candidates, > + int zlidx, bool update_load, > + int *node_order, int *nr) > { > - static int node_order[MAX_NUMNODES]; > - int node, nr_nodes = 0; > nodemask_t used_mask = NODE_MASK_NONE; > - int local_node, prev_node; > - > - /* NUMA-aware ordering of nodes */ > - local_node = pgdat->node_id; > - prev_node = local_node; > + int local_node = pgdat->node_id; > + int prev_node = local_node; > + int node, nr_nodes = 0; > > - memset(node_order, 0, sizeof(node_order)); > while ((node = find_next_best_node_in(local_node, &used_mask, > - &node_states[N_MEMORY])) >= 0) { > + candidates)) >= 0) { > /* > * We don't want to pressure a particular node. > * So adding penalty to the first node in same > * distance group to make it round-robin. > */ > - if (node_distance(local_node, node) != > + if (update_load && > + node_distance(local_node, node) != > node_distance(local_node, prev_node)) > node_load[node] += 1; > > @@ -5910,8 +5912,22 @@ static void build_zonelists(pg_data_t *pgdat) > prev_node = node; > } > > - build_zonelists_in_node_order(pgdat, node_order, nr_nodes); > + build_zonelists_in_node_order(pgdat, node_order, nr_nodes, zlidx); > + *nr = nr_nodes; > +} > + > +static void build_zonelists(pg_data_t *pgdat) > +{ > + static int node_order[MAX_NUMNODES]; > + int local_node = pgdat->node_id; > + int node, nr_nodes = 0; > + > + memset(node_order, 0, sizeof(node_order)); > + > + build_node_zonelist(pgdat, &node_states[N_MEMORY], ZONELIST_FALLBACK, > + true, node_order, &nr_nodes); > build_thisnode_zonelists(pgdat); > + > pr_info("Fallback order for Node %d: ", local_node); > for (node = 0; node < nr_nodes; node++) > pr_cont("%d ", node_order[node]); ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] mm/page_alloc: refactor build_node_zonelist() out of build_zonelists() 2026-09-03 15:33 ` Vlastimil Babka (SUSE) @ 2026-09-03 16:02 ` Gregory Price 0 siblings, 0 replies; 8+ messages in thread From: Gregory Price @ 2026-09-03 16:02 UTC (permalink / raw) To: Vlastimil Babka (SUSE) Cc: linux-mm, linux-kernel, kernel-team, akpm, david, ljs, liam, rppt, surenb, mhocko, brendan.jackman, hannes, ziy On Thu, Sep 03, 2026 at 05:33:42PM +0200, Vlastimil Babka (SUSE) wrote: > On 9/2/26 23:58, Gregory Price wrote: > > Extract per-node fallback-list construction into build_node_zonelist(). > > > > This lets us build new zonelists from candidate nodemasks instead of > > just the default N_MEMORY node state list. > > > > No functional change: build_zonelists() builds the same FALLBACK list over > > N_MEMORY with node_load updates as before. > > > > Signed-off-by: Gregory Price <gourry@gourry.net> > > LGTM, but, while we're at it, could we just do the pr_cont() printing in > build_node_zonelist() itself (maybe behind a flag if you don't want to print > from future new caller) so it doesn't need to pass nr_nodes back to > build_zonelists(). Then also the node_order array could live in > build_node_zonelist() itself? > Seems trivial enough, will let this sit for a minute and spin a v2. > Actually I wonder if we could get ride of the node_order array completely. > It would mean the loop processing in build_zonelists_in_node_order() would > have to be done piece-meal in build_node_zonelist() itself. But seems > feasible? Depends on how you intend to reuse/extend the new functions later, > I guess... > I can take a look. We've been testing a couple different things, will see how well things generalize (private node fallback lists, inverted fallback lists for proactive reclaim node ordering, some other stuff). ~Gregory ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-03 16:46 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-09-02 21:58 [PATCH 0/2] mm: refactor zonelist constructors and iterators Gregory Price 2026-09-02 21:58 ` [PATCH 1/2] mm: refactor find_next_best_node to find_next_best_node_in Gregory Price 2026-09-02 23:48 ` Gregory Price 2026-09-03 15:15 ` Vlastimil Babka (SUSE) 2026-09-03 16:46 ` Gregory Price 2026-09-02 21:58 ` [PATCH 2/2] mm/page_alloc: refactor build_node_zonelist() out of build_zonelists() Gregory Price 2026-09-03 15:33 ` Vlastimil Babka (SUSE) 2026-09-03 16:02 ` Gregory Price
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox