diff mbox series

[v5,16/18] ACPI: add support to (un)register CPUs based on the _STA enabled bit

Message ID 20240412143719.11398-17-Jonathan.Cameron@huawei.com (mailing list archive)
State Changes Requested, archived
Headers show
Series ACPI/arm64: add support for virtual cpu hotplug | expand

Commit Message

Jonathan Cameron April 12, 2024, 2:37 p.m. UTC
From: James Morse <james.morse@arm.com>

acpi_processor_get_info() registers all present CPUs. Registering a
CPU is what creates the sysfs entries and triggers the udev
notifications.

arm64 virtual machines that support 'virtual cpu hotplug' use the
enabled bit to indicate whether the CPU can be brought online, as
the existing ACPI tables require all hardware to be described and
present.

If firmware describes a CPU as present, but disabled, skip the
registration. Such CPUs are present, but can't be brought online for
whatever reason. (e.g. firmware/hypervisor policy).

Once firmware sets the enabled bit, the CPU can be registered and
brought online by user-space. Online CPUs, or CPUs that are missing
an _STA method must always be registered.

When firmware clears the enabled bit, we need to unregister the CPU
for symetry. As this is dependent on hotplug CPU being support, and
arch_unregister_cpu() only exists when hotplug CPU is supported,
we need to add a check for that configuration symbol.

Note that some elements in the *make_present() and *make_not_present()
paths are not appropriate for the *enabled() paths beause they
are related to elements such as interrupt controller setup that
are done for all present (but not enabled) CPUs at boot.

Signed-off-by: James Morse <james.morse@arm.com>
Tested-by: Miguel Luis <miguel.luis@oracle.com>
Tested-by: Vishnu Pajjuri <vishnu@os.amperecomputing.com>
Tested-by: Jianyong Wu <jianyong.wu@arm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Co-developed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

---
v5:
   Make the enable and present paths look much more like each other.
   Whilst similar, I think combining the two paths any more will
   lead to less readable code by implying they are more similar than
   they actually should be.
---
 drivers/acpi/acpi_processor.c | 46 +++++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index 3fb167ee9807..ffa2bc63da40 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -226,6 +226,24 @@  static int acpi_processor_make_present(struct acpi_processor *pr)
 	return ret;
 }
 
+static int acpi_processor_make_enabled(struct acpi_processor *pr)
+{
+	int ret;
+
+	if (invalid_phys_cpuid(pr->phys_id))
+		return -ENODEV;
+
+	cpus_write_lock();
+	ret = arch_register_cpu(pr->id);
+	cpus_write_unlock();
+
+	if (ret)
+		return ret;
+
+	pr_info("CPU%d has been hot-added (onlined)\n", pr->id);
+	return 0;
+}
+
 static int acpi_processor_get_info(struct acpi_device *device)
 {
 	union acpi_object object = { 0 };
@@ -319,7 +337,7 @@  static int acpi_processor_get_info(struct acpi_device *device)
 	 */
 	if (!invalid_logical_cpuid(pr->id) && cpu_present(pr->id) &&
 	    !get_cpu_device(pr->id)) {
-		int ret = arch_register_cpu(pr->id);
+		int ret = acpi_processor_make_enabled(pr);
 
 		if (ret)
 			return ret;
@@ -463,6 +481,27 @@  static int acpi_processor_add(struct acpi_device *device,
 	return result;
 }
 
+static void acpi_processor_make_not_enabled(struct acpi_device *device)
+{
+	struct acpi_processor *pr;
+
+	pr = acpi_driver_data(device);
+	if (pr->id >= nr_cpu_ids)
+		goto out;
+
+	device_release_driver(pr->dev);
+	per_cpu(processor_device_array, pr->id) = NULL;
+	per_cpu(processors, pr->id) = NULL;
+	cpus_write_lock();
+	arch_unregister_cpu(pr->id);
+	cpus_write_unlock();
+
+	try_offline_node(cpu_to_node(pr->id));
+out:
+	free_cpumask_var(pr->throttling.shared_cpu_map);
+	kfree(pr);
+}
+
 /* Removal */
 static void acpi_processor_make_not_present(struct acpi_device *device)
 {
@@ -515,7 +554,7 @@  static void acpi_processor_post_eject(struct acpi_device *device)
 	unsigned long long sta;
 	acpi_status status;
 
-	if (!device)
+	if (!IS_ENABLED(CONFIG_HOTPLUG_CPU) || !device)
 		return;
 
 	pr = acpi_driver_data(device);
@@ -530,6 +569,9 @@  static void acpi_processor_post_eject(struct acpi_device *device)
 		acpi_processor_make_not_present(device);
 		return;
 	}
+
+	if (cpu_present(pr->id) && !(sta & ACPI_STA_DEVICE_ENABLED))
+		acpi_processor_make_not_enabled(device);
 }
 
 #ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC