* [PATCH v2] mm/mempolicy: recompute automatic interleave weights on memory hotplug
@ 2026-10-30 16:12 Gregory Price (Meta)
2026-10-30 16:35 ` Joshua Hahn
0 siblings, 1 reply; 3+ messages in thread
From: Gregory Price (Meta) @ 2026-10-30 16:12 UTC (permalink / raw)
To: linux-mm
Cc: linux-kernel, kernel-team, akpm, david, gourry, ziy,
joshua.hahnjy, rakie.kim, ying.huang, harry, stable
In auto mode, weighted interleave derives each node's weight from its
bandwidth, reduced over the nodes in N_MEMORY. HMAT and CXL report a
node's bandwidth once, when the node first gains memory, and nothing
else on the hotplug path recomputes the weights.
This can result in a major skew in distribution and overload tiers
with more data than their respective bandwidth can support, resulting
in a performance regression.
With HMAT bandwidths of 100G for nodes 0-3, and 50G and 25G for kmem
nodes 4 and 5, weighted_interleave/nodeN reads:
event node 0-3 node 4 node 5
node 4 online 7 3 -
node 4 removed 7 - -
node 5 online 7 - 1
node 4 back 7 1 1
Expected: 6 3 1
Toggling auto off and on generates the correct result, but we should
detect this and fix it during hotplug. Recompute the weights from the
recorded bandwidth whenever a node gains/loses its first/last memory.
The notifier cannot fail the hotplug, which has already happened, so
warn once if the new weights cannot be allocated. The next recompute
starts from the full bandwidth table and corrects them.
mempolicy_set_node_perf() now only records the bandwidth and calls the
same helper, so the bandwidth is kept even if allocating the new weights
fails. wi_state_lock is dropped between the two steps, which should be
safe because racing writer simply causes it to run with newer state.
Fixes: e341f9c3c841 ("mm/mempolicy: Weighted Interleave Auto-tuning")
Cc: stable@vger.kernel.org
Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
mm/mempolicy.c | 84 ++++++++++++++++++++++++++++++++++----------------
1 file changed, 57 insertions(+), 27 deletions(-)
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 0884c629670d..6f92621e9135 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -238,27 +238,49 @@ static void reduce_interleave_weights(unsigned int *bw, u8 *new_iw)
new_iw[nid] /= iw_gcd;
}
+/* Recompute the weights from node_bw_table, unless they were set manually */
+static int wi_state_refresh(void)
+{
+ struct weighted_interleave_state *new_wi_state, *old_wi_state;
+ int i;
+
+ new_wi_state = kmalloc_flex(*new_wi_state, iw_table, nr_node_ids);
+ if (!new_wi_state)
+ return -ENOMEM;
+ new_wi_state->mode_auto = true;
+ for (i = 0; i < nr_node_ids; i++)
+ new_wi_state->iw_table[i] = 1;
+
+ mutex_lock(&wi_state_lock);
+ old_wi_state = rcu_dereference_protected(wi_state,
+ lockdep_is_held(&wi_state_lock));
+ /*
+ * Hotplug events can arrive before any node reported its bandwidth,
+ * leaving nothing to compute weights from. NULL wi_state assumes
+ * auto=true.
+ */
+ if (!node_bw_table || (old_wi_state && !old_wi_state->mode_auto)) {
+ mutex_unlock(&wi_state_lock);
+ kfree(new_wi_state);
+ return 0;
+ }
+ reduce_interleave_weights(node_bw_table, new_wi_state->iw_table);
+ rcu_assign_pointer(wi_state, new_wi_state);
+ mutex_unlock(&wi_state_lock);
+ wi_state_retire(old_wi_state);
+ return 0;
+}
+
int mempolicy_set_node_perf(unsigned int node, struct access_coordinate *coords)
{
- struct weighted_interleave_state *new_wi_state, *old_wi_state = NULL;
unsigned int *old_bw, *new_bw;
unsigned int bw_val;
- int i;
bw_val = min(coords->read_bandwidth, coords->write_bandwidth);
new_bw = kcalloc(nr_node_ids, sizeof(unsigned int), GFP_KERNEL);
if (!new_bw)
return -ENOMEM;
- new_wi_state = kmalloc_flex(*new_wi_state, iw_table, nr_node_ids);
- if (!new_wi_state) {
- kfree(new_bw);
- return -ENOMEM;
- }
- new_wi_state->mode_auto = true;
- for (i = 0; i < nr_node_ids; i++)
- new_wi_state->iw_table[i] = 1;
-
/*
* Update bandwidth info, even in manual mode. That way, when switching
* to auto mode in the future, iw_table can be overwritten using
@@ -271,25 +293,32 @@ int mempolicy_set_node_perf(unsigned int node, struct access_coordinate *coords)
memcpy(new_bw, old_bw, nr_node_ids * sizeof(*old_bw));
new_bw[node] = bw_val;
node_bw_table = new_bw;
+ mutex_unlock(&wi_state_lock);
- old_wi_state = rcu_dereference_protected(wi_state,
- lockdep_is_held(&wi_state_lock));
- if (old_wi_state && !old_wi_state->mode_auto) {
- /* Manual mode; skip reducing weights and updating wi_state */
- mutex_unlock(&wi_state_lock);
- kfree(new_wi_state);
- goto out;
- }
+ kfree(old_bw);
+ return wi_state_refresh();
+}
- /* NULL wi_state assumes auto=true; reduce weights and update wi_state*/
- reduce_interleave_weights(new_bw, new_wi_state->iw_table);
- rcu_assign_pointer(wi_state, new_wi_state);
+/*
+ * HMAT and CXL report a node's bandwidth only when it first gains memory, so
+ * recompute the weights whenever N_MEMORY changes.
+ */
+static int wi_state_node_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct node_notify *nn = data;
+ int err;
- mutex_unlock(&wi_state_lock);
- wi_state_retire(old_wi_state);
-out:
- kfree(old_bw);
- return 0;
+ if (action != NODE_ADDED_FIRST_MEMORY &&
+ action != NODE_REMOVED_LAST_MEMORY)
+ return NOTIFY_OK;
+
+ /* The hotplug is done, failing the notification would not undo it */
+ err = wi_state_refresh();
+ if (err)
+ pr_warn_once("failed to recompute weighted interleave weights for node%d: %d\n",
+ nn->nid, err);
+ return NOTIFY_OK;
}
/**
@@ -3414,6 +3443,7 @@ void __init numa_policy_init(void)
if (do_set_mempolicy(MPOL_INTERLEAVE, 0, &interleave_nodes))
pr_err("%s: interleaving failed\n", __func__);
+ hotplug_node_notifier(wi_state_node_notifier, DEFAULT_CALLBACK_PRI);
check_numabalancing_enable();
}
--
2.56.0
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v2] mm/mempolicy: recompute automatic interleave weights on memory hotplug
2026-10-30 16:12 [PATCH v2] mm/mempolicy: recompute automatic interleave weights on memory hotplug Gregory Price (Meta)
@ 2026-10-30 16:35 ` Joshua Hahn
2026-10-30 23:43 ` Andrew Morton
0 siblings, 1 reply; 3+ messages in thread
From: Joshua Hahn @ 2026-10-30 16:35 UTC (permalink / raw)
To: Gregory Price (Meta)
Cc: linux-mm, linux-kernel, kernel-team, akpm, david, ziy, rakie.kim,
ying.huang, harry, stable
On Fri, Oct 30, 2026 at 12:12 PM Gregory Price (Meta) <gourry@gourry.net> wrote:
>
> In auto mode, weighted interleave derives each node's weight from its
> bandwidth, reduced over the nodes in N_MEMORY. HMAT and CXL report a
> node's bandwidth once, when the node first gains memory, and nothing
> else on the hotplug path recomputes the weights.
>
> This can result in a major skew in distribution and overload tiers
> with more data than their respective bandwidth can support, resulting
> in a performance regression.
>
> With HMAT bandwidths of 100G for nodes 0-3, and 50G and 25G for kmem
> nodes 4 and 5, weighted_interleave/nodeN reads:
>
> event node 0-3 node 4 node 5
> node 4 online 7 3 -
> node 4 removed 7 - -
> node 5 online 7 - 1
> node 4 back 7 1 1
> Expected: 6 3 1
>
> Toggling auto off and on generates the correct result, but we should
> detect this and fix it during hotplug. Recompute the weights from the
> recorded bandwidth whenever a node gains/loses its first/last memory.
>
> The notifier cannot fail the hotplug, which has already happened, so
> warn once if the new weights cannot be allocated. The next recompute
> starts from the full bandwidth table and corrects them.
>
> mempolicy_set_node_perf() now only records the bandwidth and calls the
> same helper, so the bandwidth is kept even if allocating the new weights
> fails. wi_state_lock is dropped between the two steps, which should be
> safe because racing writer simply causes it to run with newer state.
>
> Fixes: e341f9c3c841 ("mm/mempolicy: Weighted Interleave Auto-tuning")
> Cc: stable@vger.kernel.org
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
Hello Gregory,
I think you may have dropped my review tag from the previous
version!!! I believe the only thing that changes is the warn, so
please feel free to add:
Reviewed-by: Joshua Hahn <joshua.hahnjy@gmail.com>
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v2] mm/mempolicy: recompute automatic interleave weights on memory hotplug
2026-10-30 16:35 ` Joshua Hahn
@ 2026-10-30 23:43 ` Andrew Morton
0 siblings, 0 replies; 3+ messages in thread
From: Andrew Morton @ 2026-10-30 23:43 UTC (permalink / raw)
To: Joshua Hahn
Cc: Gregory Price (Meta),
linux-mm, linux-kernel, kernel-team, david, ziy, rakie.kim,
ying.huang, harry, stable
On Fri, 30 Oct 2026 12:35:38 -0400 Joshua Hahn <joshua.hahnjy@gmail.com> wrote:
> On Fri, Oct 30, 2026 at 12:12 PM Gregory Price (Meta) <gourry@gourry.net> wrote:
> >
> > In auto mode, weighted interleave derives each node's weight from its
> > bandwidth, reduced over the nodes in N_MEMORY. HMAT and CXL report a
> > node's bandwidth once, when the node first gains memory, and nothing
> > else on the hotplug path recomputes the weights.
> >
> > This can result in a major skew in distribution and overload tiers
> > with more data than their respective bandwidth can support, resulting
> > in a performance regression.
>
> ...
>
> I think you may have dropped my review tag from the previous
> version!!! I believe the only thing that changes is the warn, so
> please feel free to add:
> Reviewed-by: Joshua Hahn <joshua.hahnjy@gmail.com>
Actually, quite a lot changed, so dropping the v1 tags seems
appropriate.
v1->v2:
mm/mempolicy.c | 36 ++++++++++++++++++++++++++++--------
1 file changed, 28 insertions(+), 8 deletions(-)
--- a/mm/mempolicy.c~mm-mempolicy-recompute-automatic-interleave-weights-on-memory-hotplug-v2
+++ a/mm/mempolicy.c
@@ -261,7 +261,11 @@ static int wi_state_refresh(void)
mutex_lock(&wi_state_lock);
old_wi_state = rcu_dereference_protected(wi_state,
lockdep_is_held(&wi_state_lock));
- /* NULL wi_state assumes auto=true */
+ /*
+ * Hotplug events can arrive before any node reported its bandwidth,
+ * leaving nothing to compute weights from. NULL wi_state assumes
+ * auto=true.
+ */
if (!node_bw_table || (old_wi_state && !old_wi_state->mode_auto)) {
mutex_unlock(&wi_state_lock);
kfree(new_wi_state);
@@ -302,6 +306,28 @@ int mempolicy_set_node_perf(unsigned int
return wi_state_refresh();
}
+/*
+ * HMAT and CXL report a node's bandwidth only when it first gains memory, so
+ * recompute the weights whenever N_MEMORY changes.
+ */
+static int wi_state_node_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct node_notify *nn = data;
+ int err;
+
+ if (action != NODE_ADDED_FIRST_MEMORY &&
+ action != NODE_REMOVED_LAST_MEMORY)
+ return NOTIFY_OK;
+
+ /* The hotplug is done, failing the notification would not undo it */
+ err = wi_state_refresh();
+ if (err)
+ pr_warn_once("failed to recompute weighted interleave weights for node%d: %d\n",
+ nn->nid, err);
+ return NOTIFY_OK;
+}
+
/**
* numa_nearest_node - Find nearest node by state
* @node: Node id to start the search
@@ -3424,6 +3450,7 @@ void __init numa_policy_init(void)
if (do_set_mempolicy(MPOL_INTERLEAVE, 0, &interleave_nodes))
pr_err("%s: interleaving failed\n", __func__);
+ hotplug_node_notifier(wi_state_node_notifier, DEFAULT_CALLBACK_PRI);
check_numabalancing_enable();
}
@@ -3910,15 +3937,8 @@ static int wi_node_notifier(struct notif
case NODE_REMOVED_LAST_MEMORY:
sysfs_wi_node_delete(nid);
break;
- default:
- return NOTIFY_OK;
}
- /*
- * HMAT and CXL report a node's bandwidth only when it first gains
- * memory, so recompute the weights whenever N_MEMORY changes.
- */
- wi_state_refresh();
return NOTIFY_OK;
}
_
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-30 23:43 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-30 16:12 [PATCH v2] mm/mempolicy: recompute automatic interleave weights on memory hotplug Gregory Price (Meta)
2026-10-30 16:35 ` Joshua Hahn
2026-10-30 23:43 ` Andrew Morton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®