From: Alexander Graf <graf@amazon.com>
To: <linux-kernel@vger.kernel.org>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Arnd Bergmann <arnd@arndb.de>, <nh-open-source@amazon.com>,
"The AWS Nitro Enclaves Team"
<aws-nitro-enclaves-devel@amazon.com>
Subject: [PATCH 15/19] nitro_enclaves: Set up the CPU pool under the CPU hotplug read lock
Date: Thu, 12 Nov 2026 03:07:11 +0000 [thread overview]
Message-ID: <65b08827c7eb7417c98e458f06e51c6735ee3281.1794393487.git.graf@amazon.com> (raw)
In-Reply-To: <cover.1794300000.git.graf@amazon.com>
An admin who writes a CPU list to ne_cpus while a CPU goes offline can
get a pool built from the topology before and after that CPU went.
ne_setup_cpu_pool() reads cpu_is_offline(), cpu_to_node() and
topology_sibling_cpumask() for the list without the CPU hotplug lock.
Take cpus_read_lock() over those reads, and drop it before the CPUs are
offlined, because remove_cpu() takes the write side. The failure paths
share one unwind in ne_free_core_slots(), so that one cpus_read_unlock()
covers all of them.
Fixes: ff8a4d3e3a99 ("nitro_enclaves: Add logic for setting an enclave vCPU")
Signed-off-by: Alexander Graf <graf@amazon.com>
---
drivers/virt/nitro_enclaves/ne_misc_dev.c | 73 +++++++++++++++--------
1 file changed, 48 insertions(+), 25 deletions(-)
diff --git a/drivers/virt/nitro_enclaves/ne_misc_dev.c b/drivers/virt/nitro_enclaves/ne_misc_dev.c
index a897036dee2dd..f8b066c990ce1 100644
--- a/drivers/virt/nitro_enclaves/ne_misc_dev.c
+++ b/drivers/virt/nitro_enclaves/ne_misc_dev.c
@@ -147,6 +147,27 @@ struct ne_phys_contig_mem_regions {
struct range *regions;
};
+/**
+ * ne_free_core_slots() - Free the core slot arrays of the NE CPU pool.
+ * @void: No parameters provided.
+ *
+ * Context: Process context. This function is called with the ne_cpu_pool mutex
+ * held.
+ */
+static void ne_free_core_slots(void)
+{
+ unsigned int i = 0;
+
+ if (!ne_cpu_pool.avail_threads_per_core)
+ return;
+
+ for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++)
+ free_cpumask_var(ne_cpu_pool.avail_threads_per_core[i]);
+
+ kfree(ne_cpu_pool.avail_threads_per_core);
+ ne_cpu_pool.avail_threads_per_core = NULL;
+}
+
/**
* ne_setup_cpu_pool() - Set the NE CPU pool after handling sanity checks such
* as not sharing CPU cores with the primary / parent VM
@@ -155,6 +176,11 @@ struct ne_phys_contig_mem_regions {
* pool after the checks passed.
* @cpu_pool: The CPU mask used for setting NE CPU pool.
*
+ * The topology of the pool - online state, sibling masks, core ids - is read
+ * under the CPU hotplug read lock. The lock is dropped before the pool CPUs are
+ * offlined, because remove_cpu() takes the write side of the same percpu-rwsem.
+ * Nothing reads topology after the drop.
+ *
* Context: Process context. This function is called with the ne_pci_dev enclave
* mutex held, if there is a NE PCI device.
* Return:
@@ -171,6 +197,7 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
int rc = -EINVAL;
mutex_lock(&ne_cpu_pool.mutex);
+ cpus_read_lock();
cpu = cpumask_any(cpu_pool);
if (cpu >= nr_cpu_ids) {
@@ -178,7 +205,7 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
rc = -EINVAL;
- goto reset_cpu_pool;
+ goto unlock_hotplug;
}
/*
@@ -192,7 +219,7 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
rc = -EINVAL;
- goto reset_cpu_pool;
+ goto unlock_hotplug;
}
/*
@@ -207,7 +234,7 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
rc = -EINVAL;
- goto reset_cpu_pool;
+ goto unlock_hotplug;
}
} else {
if (numa_node != cpu_to_node(cpu)) {
@@ -216,7 +243,7 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
rc = -EINVAL;
- goto reset_cpu_pool;
+ goto unlock_hotplug;
}
}
@@ -229,7 +256,7 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
rc = -EINVAL;
- goto reset_cpu_pool;
+ goto unlock_hotplug;
}
for_each_cpu(cpu_sibling, topology_sibling_cpumask(0)) {
@@ -239,7 +266,7 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
rc = -EINVAL;
- goto reset_cpu_pool;
+ goto unlock_hotplug;
}
}
@@ -256,7 +283,7 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
rc = -EINVAL;
- goto reset_cpu_pool;
+ goto unlock_hotplug;
}
}
}
@@ -273,14 +300,14 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
if (!ne_cpu_pool.avail_threads_per_core) {
rc = -ENOMEM;
- goto reset_cpu_pool;
+ goto unlock_hotplug;
}
for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++)
if (!zalloc_cpumask_var(&ne_cpu_pool.avail_threads_per_core[i], GFP_KERNEL)) {
rc = -ENOMEM;
- goto free_cores_cpumask;
+ goto free_slots;
}
/*
@@ -295,12 +322,14 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
rc = -EINVAL;
- goto clear_cpumask;
+ goto free_slots;
}
cpumask_set_cpu(cpu, ne_cpu_pool.avail_threads_per_core[core_id]);
}
+ cpus_read_unlock();
+
/*
* CPUs that are given to enclave(s) should not be considered online
* by Linux anymore, as the hypervisor will degrade them to floating.
@@ -333,14 +362,13 @@ static int ne_setup_cpu_pool(const struct cpumask *cpu_pool)
online_cpus:
for_each_cpu(cpu, cpu_pool)
add_cpu(cpu);
-clear_cpumask:
- for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++)
- cpumask_clear(ne_cpu_pool.avail_threads_per_core[i]);
-free_cores_cpumask:
- for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++)
- free_cpumask_var(ne_cpu_pool.avail_threads_per_core[i]);
- kfree(ne_cpu_pool.avail_threads_per_core);
-reset_cpu_pool:
+ ne_free_core_slots();
+ goto reset_pool;
+free_slots:
+ ne_free_core_slots();
+unlock_hotplug:
+ cpus_read_unlock();
+reset_pool:
ne_cpu_pool.nr_parent_vm_cores = 0;
ne_cpu_pool.nr_threads_per_core = 0;
ne_cpu_pool.numa_node = -1;
@@ -371,7 +399,7 @@ static void ne_teardown_cpu_pool(void)
return;
}
- for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++) {
+ for (i = 0; i < ne_cpu_pool.nr_parent_vm_cores; i++)
for_each_cpu(cpu, ne_cpu_pool.avail_threads_per_core[i]) {
rc = add_cpu(cpu);
if (rc != 0)
@@ -379,12 +407,7 @@ static void ne_teardown_cpu_pool(void)
ne_misc_dev.name, cpu, rc);
}
- cpumask_clear(ne_cpu_pool.avail_threads_per_core[i]);
-
- free_cpumask_var(ne_cpu_pool.avail_threads_per_core[i]);
- }
-
- kfree(ne_cpu_pool.avail_threads_per_core);
+ ne_free_core_slots();
ne_cpu_pool.nr_parent_vm_cores = 0;
ne_cpu_pool.nr_threads_per_core = 0;
ne_cpu_pool.numa_node = -1;
--
2.47.1
next prev parent reply other threads:[~2026-11-12 3:11 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 13:49 [PATCH 00/19] nitro_enclaves: Fix enclave release, command retries and the CPU pool Alexander Graf
2026-11-11 13:49 ` [PATCH 01/19] nitro_enclaves: Release the reference on a rejected region page Alexander Graf
2026-11-11 13:49 ` [PATCH 02/19] nitro_enclaves: Split freeing the slot from freeing the enclave Alexander Graf
2026-11-11 13:49 ` [PATCH 03/19] nitro_enclaves: Ignore the reply of a failed slot info command Alexander Graf
2026-11-11 13:49 ` [PATCH 04/19] nitro_enclaves: Retry a command the device asks us to retry Alexander Graf
2026-11-11 13:49 ` [PATCH 05/19] nitro_enclaves: Stop sending commands to a broken device Alexander Graf
2026-11-11 13:49 ` [PATCH 06/19] nitro_enclaves: Set up the device state before requesting its interrupts Alexander Graf
2026-11-11 13:49 ` [PATCH 07/19] nitro_enclaves: Wait for file operations before unbinding the PCI device Alexander Graf
2026-11-12 3:07 ` [PATCH 08/19] nitro_enclaves: Let the region tracking arrays fall back to vmalloc Alexander Graf
2026-11-12 3:07 ` [PATCH 09/19] nitro_enclaves: Do not return enclave threads to a replaced CPU pool Alexander Graf
2026-11-12 3:07 ` [PATCH 10/19] nitro_enclaves: Do not report a torn down CPU pool as set Alexander Graf
2026-11-12 3:07 ` [PATCH 11/19] nitro_enclaves: Keep the CPU pool when the new CPU list does not parse Alexander Graf
2026-11-12 3:07 ` [PATCH 12/19] nitro_enclaves: Put the CPU pool back when the new CPU list is refused Alexander Graf
2026-11-12 3:07 ` [PATCH 13/19] nitro_enclaves: Initialise the CPU pool mutex statically Alexander Graf
2026-11-12 3:07 ` [PATCH 14/19] nitro_enclaves: Build the boot-time CPU pool once the CPUs are online Alexander Graf
2026-11-12 3:07 ` Alexander Graf [this message]
2026-11-12 3:07 ` [PATCH 16/19] nitro_enclaves: Let 32-bit processes use the ioctl interface Alexander Graf
2026-11-12 3:07 ` [PATCH 17/19] nitro_enclaves: Index CPU pool cores by sibling mask Alexander Graf
2026-11-12 3:07 ` [PATCH 18/19] nitro_enclaves: Time the wait for the device to turn off by the clock Alexander Graf
2026-11-12 3:07 ` [PATCH 19/19] MAINTAINERS: Take over the Nitro Enclaves driver Alexander Graf
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=65b08827c7eb7417c98e458f06e51c6735ee3281.1794393487.git.graf@amazon.com \
--to=graf@amazon.com \
--cc=arnd@arndb.de \
--cc=aws-nitro-enclaves-devel@amazon.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nh-open-source@amazon.com \
/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 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®