[PATCH 14/14] ACPI: Provide /sys/devices/system/cpu/cpuN/deconfigure

From: Alex Chiang
Date: Mon Jul 14 2008 - 22:39:56 EST


Provide a new sysfs interface for CPU deconfiguration.

Since no vendors can agree on terminology for related but slightly
different features, provide a method for a platform to implement
its own version of what it thinks 'deconfiguring' a CPU might be.

Provide an HP-specific CPU deconfiguration implementation.

Signed-off-by: Alex Chiang <achiang@xxxxxx>
Cc: Andi Kleen <ak@xxxxxxxxxxxxxxx>
---

drivers/acpi/Kconfig | 18 ++
drivers/acpi/Makefile | 4
drivers/acpi/processor_core.c | 8 +
drivers/acpi/processor_deconfigure.c | 275 ++++++++++++++++++++++++++++++++++
include/acpi/processor.h | 6 +
5 files changed, 311 insertions(+), 0 deletions(-)
create mode 100644 drivers/acpi/processor_deconfigure.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index c52fca8..36ad177 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -188,6 +188,24 @@ config ACPI_HOTPLUG_CPU
select ACPI_CONTAINER
default y

+config ACPI_DECONFIGURE_CPU
+ bool "Processor deconfiguration"
+ depends on ACPI_PROCESSOR
+ default n
+ help
+ This processor driver submodule allows a user to mark a CPU
+ for firmware disabling/enabling. It will create the following
+ sysfs file:
+
+ /sys/devices/system/cpu/cpuN/deconfigure
+
+ Behavior of this interface is highly vendor-dependent and
+ requires firmware support.
+
+ This option is NOT required for CPU hotplug support.
+
+ If unsure, say N.
+
config ACPI_THERMAL
tristate "Thermal Zone"
depends on ACPI_PROCESSOR
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 40b0fca..92a5037 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -35,6 +35,10 @@ ifdef CONFIG_CPU_FREQ
processor-objs += processor_perflib.o
endif

+ifdef CONFIG_ACPI_DECONFIGURE_CPU
+processor-objs += processor_deconfigure.o
+endif
+
obj-y += sleep/
obj-y += bus.o glue.o
obj-y += scan.o
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 9dd0fa9..ef582ca 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -1099,6 +1099,10 @@ static int __init acpi_processor_init(void)

acpi_processor_throttling_init();

+#ifdef CONFIG_ACPI_DECONFIGURE_CPU
+ acpi_processor_deconfigure_init();
+#endif
+
return 0;

out_cpuidle:
@@ -1112,6 +1116,10 @@ out_proc:

static void __exit acpi_processor_exit(void)
{
+
+#ifdef CONFIG_ACPI_DECONFIGURE_CPU
+ acpi_processor_deconfigure_exit();
+#endif
acpi_processor_ppc_exit();

acpi_thermal_cpufreq_exit();
diff --git a/drivers/acpi/processor_deconfigure.c b/drivers/acpi/processor_deconfigure.c
new file mode 100644
index 0000000..e656f97
--- /dev/null
+++ b/drivers/acpi/processor_deconfigure.c
@@ -0,0 +1,275 @@
+/*
+ * processor_deconfigure.c - CPU deconfiguration submodule of the
+ * ACPI processor driver
+ *
+ * (c) Copyright 2008 Hewlett-Packard Development Company, L.P.
+ * Alex Chiang <achiang@xxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <acpi/acpi.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/processor.h>
+
+static int supports_cpu_deconfigure;
+
+/*
+ * These function pointers must be overwritten by platforms supporting
+ * cpu deconfigure.
+ */
+static ssize_t (*show_deconfigure)(struct sys_device *, char *);
+static ssize_t (*store_deconfigure)(struct sys_device *, const char *, size_t);
+
+/*
+ * Under HP semantics, CPU deconfiguration is defined as removing a
+ * processor core or socket from operation at boot time, typically
+ * due to managability concerns, such as excessive detected errors.
+ *
+ * The HP semantics of 'deconfigure' are defined as:
+ *
+ * Mark CPU for deconfiguration at next boot.
+ * # echo 1 > /sys/devices/system/cpu/cpuN/deconfigure
+ *
+ * Mark CPU as enabled at next boot.
+ * # echo 0 > /sys/devices/system/cpu/cpuN/deconfigure
+ *
+ * Display next boot's deconfigure status
+ * 0x0 - not marked for deconfiguration
+ * 0x1 - scheduled deconfig at next boot
+ * 0x3 - scheduled, OS-requested deconfig at next boot
+ * 0x4 - thread disabled by firmware
+ * # cat /sys/devices/system/cpu/cpuN/deconfigure
+ *
+ * After echo'ing 0 or 1 into deconfigure, cat'ing the file will
+ * return the next boot's status. However, the CPU will not actually
+ * be deconfigured until the next boot.
+ *
+ * Attempting to configure or deconfigure a disabled thread is disallowed.
+ */
+struct hp_deconfigure_cb_args {
+ int cpu;
+ char *method;
+};
+
+static acpi_status hp_deconfigure_cb(acpi_handle handle,
+ u32 lvl,
+ void *context,
+ void **rv)
+{
+ int cpu;
+ acpi_status status;
+ acpi_integer scfg;
+ struct hp_deconfigure_cb_args *args = context;
+ union acpi_object object = { 0 };
+ struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
+
+ status = acpi_evaluate_object(handle, NULL, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ cpu = object.processor.proc_id;
+ if (cpu != args->cpu)
+ return AE_OK;
+
+ /*
+ * Always check SCFG. If this is what the user actually wanted,
+ * great, just return the answer. If the user wanted something
+ * else, check to see if they were trying to poke a disabled
+ * hardware thread and disallow it if so.
+ */
+ status = acpi_evaluate_object(handle, "SCFG", NULL, &buffer);
+ scfg = object.integer.value;
+ if (!strcmp(args->method, "SCFG"))
+ **(int **)rv = ACPI_SUCCESS(status) ? scfg : -1;
+ /*
+ * Disallow E/DCFG on disabled threads
+ */
+ else if (scfg == 0x4)
+ **(int **)rv = -1;
+ else {
+ status = acpi_evaluate_object(handle, args->method,
+ NULL, &buffer);
+ **(int **)rv = ACPI_SUCCESS(status) ? status : -1;
+ }
+
+ return AE_CTRL_TERMINATE;
+}
+
+/*
+ * We can do this the easy way or the hard way. The easy way is,
+ * if the CPU is online, we have easy access to its ACPI handle
+ * via its per_cpu() data area, and we can call SCFG directly.
+ *
+ * The hard way is when the CPU is not online, and does not have
+ * a valid per_cpu() data area. In that case, we have to walk the
+ * ACPI namespace, looking for the CPU and calling SCFG that way.
+ */
+static ssize_t hp_show_deconfigure(struct sys_device *dev, char *buf)
+{
+ int logical_cpu;
+ unsigned long cfg;
+ struct cpu *cpu = container_of(dev, struct cpu, sysdev);
+
+ logical_cpu = cpu->sysdev.id;
+
+ if (cpu_isset(logical_cpu, cpu_online_map)) {
+ unsigned long tmp;
+ acpi_status status;
+ struct acpi_processor *pr;
+
+ pr = processors[logical_cpu];
+ status = acpi_evaluate_integer(pr->handle, "SCFG", NULL, &tmp);
+ cfg = ACPI_SUCCESS(status) ? tmp : -1;
+ } else {
+ int ret;
+ void *ret_ptr = &ret;
+ struct hp_deconfigure_cb_args args;
+
+ args.cpu = logical_cpu;
+ args.method = "SCFG";
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
+ ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ hp_deconfigure_cb,
+ &args,
+ (void *)&ret_ptr);
+ cfg = ret;
+ }
+
+ return sprintf(buf, "%#lx\n", cfg);
+}
+
+/*
+ * We can do this the easy way or the hard way. The easy way is,
+ * if the CPU is online, we have easy access to its ACPI handle
+ * via its per_cpu() data area, and we can call E/D-CFG directly.
+ *
+ * The hard way is when the CPU is not online, and does not have
+ * a valid per_cpu() data area. In that case, we have to walk the
+ * ACPI namespace, looking for the CPU and calling E/D-CFG that way.
+ */
+static ssize_t hp_store_deconfigure(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ ssize_t ret;
+ char *method;
+ int logical_cpu;
+ struct cpu *cpu = container_of(dev, struct cpu, sysdev);
+
+ logical_cpu = cpu->sysdev.id;
+ switch (buf[0]) {
+ case '0':
+ method = "ECFG";
+ break;
+ case '1':
+ method = "DCFG";
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (cpu_isset(logical_cpu, cpu_online_map)) {
+ struct acpi_processor *pr;
+ pr = processors[logical_cpu];
+ ret = acpi_evaluate_object(pr->handle, method, NULL, NULL);
+ } else {
+ int r;
+ void *ret_ptr = &r;
+ struct hp_deconfigure_cb_args args;
+
+ args.cpu = logical_cpu;
+ args.method = method;
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
+ ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ hp_deconfigure_cb,
+ &args,
+ (void *)&ret_ptr);
+ ret = r;
+ }
+
+ if (ret == 0)
+ if (!strcmp(method, "ECFG"))
+ cpu_set(logical_cpu, cpu_enabled_map);
+ else
+ cpu_clear(logical_cpu, cpu_enabled_map);
+
+out:
+ if (ret >= 0)
+ ret = count;
+ return ret;
+}
+
+static int hp_check_cpu_deconfigure(const struct dmi_system_id *d)
+{
+ acpi_handle hnd;
+ struct acpi_processor *pr;
+
+ /*
+ * Operating assumption is that either all or none of the CPUs
+ * will support deconfiguration.
+ */
+ pr = processors[0];
+ if (ACPI_SUCCESS(acpi_get_handle(pr->handle, "SCFG", &hnd))) {
+ supports_cpu_deconfigure = 1;
+ show_deconfigure = hp_show_deconfigure;
+ store_deconfigure = hp_store_deconfigure;
+ }
+
+ return 0;
+}
+
+static struct dmi_system_id cpu_deconfigure_dmi_table[] __initdata = {
+ {
+ .callback = hp_check_cpu_deconfigure,
+ .ident = "Hewlett-Packard",
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VENDOR, "HP"),
+ },
+ },
+ {
+ .callback = hp_check_cpu_deconfigure,
+ .ident = "Hewlett-Packard",
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VENDOR, "Hewlett-Packard"),
+ },
+ },
+ {}
+};
+
+static SYSDEV_ATTR(deconfigure, 0644, NULL, NULL);
+
+void __init acpi_processor_deconfigure_init(void)
+{
+ int i;
+ struct sys_device *sysdev;
+
+ dmi_check_system(cpu_deconfigure_dmi_table);
+
+ if (supports_cpu_deconfigure) {
+ attr_deconfigure.show = show_deconfigure;
+ attr_deconfigure.store = store_deconfigure;
+
+ for_each_present_cpu(i) {
+ sysdev = get_cpu_sysdev(i);
+ sysdev_create_file(sysdev, &attr_deconfigure);
+ }
+ }
+}
+
+void acpi_processor_deconfigure_exit(void)
+{
+ int i;
+ struct sys_device *sysdev;
+
+ if (supports_cpu_deconfigure) {
+ for_each_present_cpu(i) {
+ sysdev = get_cpu_sysdev(i);
+ sysdev_remove_file(sysdev, &attr_deconfigure);
+ }
+ }
+}
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index 06ebb6e..071fd42 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -289,6 +289,12 @@ static inline void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx
}
#endif

+#ifdef CONFIG_ACPI_DECONFIGURE_CPU
+/* in processor_deconfigure.c */
+void __init acpi_processor_deconfigure_init(void);
+void acpi_processor_deconfigure_exit(void);
+#endif
+
/* in processor_perflib.c */

#ifdef CONFIG_CPU_FREQ

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/