All of lore.kernel.org
 help / color / mirror / Atom feed
From: Guopeng Zhang <guopeng.zhang@linux.dev>
To: Ingo Molnar <mingo@redhat.com>,
	Peter Zijlstra <peterz@infradead.org>,
	Juri Lelli <juri.lelli@redhat.com>,
	Vincent Guittot <vincent.guittot@linaro.org>
Cc: Dietmar Eggemann <dietmar.eggemann@arm.com>,
	Steven Rostedt <rostedt@goodmis.org>,
	Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
	Valentin Schneider <vschneid@redhat.com>,
	K Prateek Nayak <kprateek.nayak@amd.com>,
	Ying Huang <huang.ying.caritas@gmail.com>,
	linux-kernel@vger.kernel.org,
	Guopeng Zhang <zhangguopeng@kylinos.cn>
Subject: [PATCH] sched/numa: Free partial topology after a failed rebuild
Date: Sat, 18 Jul 2026 17:50:53 +0800	[thread overview]
Message-ID: <20260718095053.1256186-1-guopeng.zhang@linux.dev> (raw)

From: Guopeng Zhang <zhangguopeng@kylinos.cn>

sched_init_numa() builds a replacement NUMA topology in several
allocation steps. If allocation of the mask array, a per-node mask, or
the topology-level array fails, it returns without freeing objects
allocated earlier in the same attempt.

The topology is rebuilt when CPU hotplug adds the first CPU to a node or
removes the last one. Repeated failures can therefore leak a partial
topology on every rebuild.

Keep the topology under construction in local variables. Free the
distance arrays and all allocated masks on failure, and publish them only
after all allocations succeed. Use the same mask cleanup helper when
resetting an installed topology.

Fixes: 0fb3978b0aac ("sched/numa: Fix NUMA topology for systems with CPU-less nodes")
Signed-off-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
---
 kernel/sched/topology.c | 69 +++++++++++++++++++++++------------------
 1 file changed, 39 insertions(+), 30 deletions(-)

diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 622e2e01974c..895bf2dfaa19 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -2312,13 +2312,30 @@ static int sched_record_numa_dist(int offline_node, int (*n_dist)(int, int),
 	return 0;
 }
 
+static void sched_free_numa_masks(struct cpumask ***masks, int nr_levels)
+{
+	int i, j;
+
+	if (!masks)
+		return;
+
+	for (i = 0; i < nr_levels; i++) {
+		if (!masks[i])
+			continue;
+		for_each_node(j)
+			kfree(masks[i][j]);
+		kfree(masks[i]);
+	}
+	kfree(masks);
+}
+
 void sched_init_numa(int offline_node)
 {
 	struct sched_domain_topology_level *tl;
 	int nr_levels, nr_node_levels;
 	int i, j;
 	int *distances, *domain_distances;
-	struct cpumask ***masks;
+	struct cpumask ***masks = NULL;
 
 	/* Record the NUMA distances from SLIT table */
 	if (sched_record_numa_dist(offline_node, numa_node_dist, &distances,
@@ -2336,10 +2353,6 @@ void sched_init_numa(int offline_node)
 		domain_distances = distances;
 		nr_levels = nr_node_levels;
 	}
-	rcu_assign_pointer(sched_numa_node_distance, distances);
-	WRITE_ONCE(sched_max_numa_distance, distances[nr_node_levels - 1]);
-	WRITE_ONCE(sched_numa_node_levels, nr_node_levels);
-
 	/*
 	 * 'nr_levels' contains the number of unique distances
 	 *
@@ -2348,21 +2361,14 @@ void sched_init_numa(int offline_node)
 	 */
 
 	/*
-	 * Here, we should temporarily reset sched_domains_numa_levels to 0.
-	 * If it fails to allocate memory for array sched_domains_numa_masks[][],
-	 * the array will contain less then 'nr_levels' members. This could be
-	 * dangerous when we use it to iterate array sched_domains_numa_masks[][]
-	 * in other functions.
-	 *
-	 * We reset it to 'nr_levels' at the end of this function.
+	 * Keep the level count at zero until all allocations have succeeded and
+	 * the new NUMA topology has been published.
 	 */
-	rcu_assign_pointer(sched_domains_numa_distance, domain_distances);
-
 	sched_domains_numa_levels = 0;
 
 	masks = kzalloc(sizeof(void *) * nr_levels, GFP_KERNEL);
 	if (!masks)
-		return;
+		goto free;
 
 	/*
 	 * Now for each level, construct a mask per node which contains all
@@ -2371,14 +2377,14 @@ void sched_init_numa(int offline_node)
 	for (i = 0; i < nr_levels; i++) {
 		masks[i] = kzalloc(nr_node_ids * sizeof(void *), GFP_KERNEL);
 		if (!masks[i])
-			return;
+			goto free;
 
 		for_each_cpu_node_but(j, offline_node) {
 			struct cpumask *mask = kzalloc(cpumask_size(), GFP_KERNEL);
 			int k;
 
 			if (!mask)
-				return;
+				goto free;
 
 			masks[i][j] = mask;
 
@@ -2389,14 +2395,13 @@ void sched_init_numa(int offline_node)
 					sched_numa_warn("Node-distance not symmetric");
 
 				if (arch_sched_node_distance(j, k) >
-				    sched_domains_numa_distance[i])
+				    domain_distances[i])
 					continue;
 
 				cpumask_or(mask, mask, cpumask_of_node(k));
 			}
 		}
 	}
-	rcu_assign_pointer(sched_domains_numa_masks, masks);
 
 	/* Compute default topology size */
 	for (i = 0; sched_domain_topology[i].mask; i++);
@@ -2404,7 +2409,7 @@ void sched_init_numa(int offline_node)
 	tl = kzalloc((i + nr_levels + 1) *
 			sizeof(struct sched_domain_topology_level), GFP_KERNEL);
 	if (!tl)
-		return;
+		goto free;
 
 	/*
 	 * Copy the default topology bits..
@@ -2425,12 +2430,25 @@ void sched_init_numa(int offline_node)
 		tl[i].numa_level = j;
 	}
 
+	rcu_assign_pointer(sched_numa_node_distance, distances);
+	WRITE_ONCE(sched_max_numa_distance, distances[nr_node_levels - 1]);
+	WRITE_ONCE(sched_numa_node_levels, nr_node_levels);
+	rcu_assign_pointer(sched_domains_numa_distance, domain_distances);
+	rcu_assign_pointer(sched_domains_numa_masks, masks);
+
 	sched_domain_topology_saved = sched_domain_topology;
 	sched_domain_topology = tl;
 
 	sched_domains_numa_levels = nr_levels;
 
 	init_numa_topology_type(offline_node);
+	return;
+
+free:
+	sched_free_numa_masks(masks, nr_levels);
+	if (domain_distances != distances)
+		kfree(domain_distances);
+	kfree(distances);
 }
 
 
@@ -2452,19 +2470,10 @@ static void sched_reset_numa(void)
 	masks = sched_domains_numa_masks;
 	rcu_assign_pointer(sched_domains_numa_masks, NULL);
 	if (distances || masks) {
-		int i, j;
-
 		synchronize_rcu();
 		kfree(distances);
 		kfree(dom_distances);
-		for (i = 0; i < nr_levels && masks; i++) {
-			if (!masks[i])
-				continue;
-			for_each_node(j)
-				kfree(masks[i][j]);
-			kfree(masks[i]);
-		}
-		kfree(masks);
+		sched_free_numa_masks(masks, nr_levels);
 	}
 	if (sched_domain_topology_saved) {
 		kfree(sched_domain_topology);
-- 
2.43.0


                 reply	other threads:[~2026-07-18  9:51 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260718095053.1256186-1-guopeng.zhang@linux.dev \
    --to=guopeng.zhang@linux.dev \
    --cc=bsegall@google.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=huang.ying.caritas@gmail.com \
    --cc=juri.lelli@redhat.com \
    --cc=kprateek.nayak@amd.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mgorman@suse.de \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    --cc=rostedt@goodmis.org \
    --cc=vincent.guittot@linaro.org \
    --cc=vschneid@redhat.com \
    --cc=zhangguopeng@kylinos.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.