diff options
| author | Ard Biesheuvel <ard.biesheuvel@linaro.org> | 2014-02-08 07:34:09 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-02-18 15:38:37 -0500 |
| commit | 67bad2fdb754dbef14596c0b5d28b3a12c8dfe84 (patch) | |
| tree | 05c3cd5f809618319346f9642549460f95149f02 | |
| parent | 91219a3b20325689eb80f7598cce2dc745db171d (diff) | |
cpu: add generic support for CPU feature based module autoloading
This patch adds support for advertising optional CPU features over udev
using the modalias, and for declaring compatibility with/dependency upon
such a feature in a module.
The mapping between feature numbers and actual features should be provided
by the architecture in a file called <asm/cpufeature.h> which exports the
following functions/macros:
- cpu_feature(FEAT), a preprocessor macro that maps token FEAT to a
numeric index;
- bool cpu_have_feature(n), returning whether this CPU has support for
feature #n;
- MAX_CPU_FEATURES, an upper bound for 'n' in the previous function.
The feature can then be enabled by setting CONFIG_GENERIC_CPU_AUTOPROBE
for the architecture.
For instance, a module that registers its module init function using
module_cpu_feature_match(FEAT_X, module_init_function)
will be probed automatically when the CPU's support for the 'FEAT_X'
feature is advertised over udev, and will only allow the module to be
loaded by hand if the 'FEAT_X' feature is supported.
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/base/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/base/cpu.c | 50 | ||||
| -rw-r--r-- | include/linux/cpufeature.h | 60 | ||||
| -rw-r--r-- | include/linux/mod_devicetable.h | 9 | ||||
| -rw-r--r-- | scripts/mod/devicetable-offsets.c | 3 | ||||
| -rw-r--r-- | scripts/mod/file2alias.c | 10 |
6 files changed, 135 insertions, 5 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index ec36e7772e57..3f0d3732df7f 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig | |||
| @@ -185,6 +185,14 @@ config GENERIC_CPU_DEVICES | |||
| 185 | bool | 185 | bool |
| 186 | default n | 186 | default n |
| 187 | 187 | ||
| 188 | config HAVE_CPU_AUTOPROBE | ||
| 189 | def_bool ARCH_HAS_CPU_AUTOPROBE | ||
| 190 | |||
| 191 | config GENERIC_CPU_AUTOPROBE | ||
| 192 | bool | ||
| 193 | depends on !ARCH_HAS_CPU_AUTOPROBE | ||
| 194 | select HAVE_CPU_AUTOPROBE | ||
| 195 | |||
| 188 | config SOC_BUS | 196 | config SOC_BUS |
| 189 | bool | 197 | bool |
| 190 | 198 | ||
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index f48370dfc908..8a38bf8c792f 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <linux/percpu.h> | 15 | #include <linux/percpu.h> |
| 16 | #include <linux/acpi.h> | 16 | #include <linux/acpi.h> |
| 17 | #include <linux/of.h> | 17 | #include <linux/of.h> |
| 18 | #include <linux/cpufeature.h> | ||
| 18 | 19 | ||
| 19 | #include "base.h" | 20 | #include "base.h" |
| 20 | 21 | ||
| @@ -286,6 +287,45 @@ static void cpu_device_release(struct device *dev) | |||
| 286 | */ | 287 | */ |
| 287 | } | 288 | } |
| 288 | 289 | ||
| 290 | #ifdef CONFIG_HAVE_CPU_AUTOPROBE | ||
| 291 | #ifdef CONFIG_GENERIC_CPU_AUTOPROBE | ||
| 292 | static ssize_t print_cpu_modalias(struct device *dev, | ||
| 293 | struct device_attribute *attr, | ||
| 294 | char *buf) | ||
| 295 | { | ||
| 296 | ssize_t n; | ||
| 297 | u32 i; | ||
| 298 | |||
| 299 | n = sprintf(buf, "cpu:type:" CPU_FEATURE_TYPEFMT ":feature:", | ||
| 300 | CPU_FEATURE_TYPEVAL); | ||
| 301 | |||
| 302 | for (i = 0; i < MAX_CPU_FEATURES; i++) | ||
| 303 | if (cpu_have_feature(i)) { | ||
| 304 | if (PAGE_SIZE < n + sizeof(",XXXX\n")) { | ||
| 305 | WARN(1, "CPU features overflow page\n"); | ||
| 306 | break; | ||
| 307 | } | ||
| 308 | n += sprintf(&buf[n], ",%04X", i); | ||
| 309 | } | ||
| 310 | buf[n++] = '\n'; | ||
| 311 | return n; | ||
| 312 | } | ||
| 313 | #else | ||
| 314 | #define print_cpu_modalias arch_print_cpu_modalias | ||
| 315 | #endif | ||
| 316 | |||
| 317 | static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
| 318 | { | ||
| 319 | char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
| 320 | if (buf) { | ||
| 321 | print_cpu_modalias(NULL, NULL, buf); | ||
| 322 | add_uevent_var(env, "MODALIAS=%s", buf); | ||
| 323 | kfree(buf); | ||
| 324 | } | ||
| 325 | return 0; | ||
| 326 | } | ||
| 327 | #endif | ||
| 328 | |||
| 289 | /* | 329 | /* |
| 290 | * register_cpu - Setup a sysfs device for a CPU. | 330 | * register_cpu - Setup a sysfs device for a CPU. |
| 291 | * @cpu - cpu->hotpluggable field set to 1 will generate a control file in | 331 | * @cpu - cpu->hotpluggable field set to 1 will generate a control file in |
| @@ -306,8 +346,8 @@ int register_cpu(struct cpu *cpu, int num) | |||
| 306 | cpu->dev.offline_disabled = !cpu->hotpluggable; | 346 | cpu->dev.offline_disabled = !cpu->hotpluggable; |
| 307 | cpu->dev.offline = !cpu_online(num); | 347 | cpu->dev.offline = !cpu_online(num); |
| 308 | cpu->dev.of_node = of_get_cpu_node(num, NULL); | 348 | cpu->dev.of_node = of_get_cpu_node(num, NULL); |
| 309 | #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE | 349 | #ifdef CONFIG_HAVE_CPU_AUTOPROBE |
| 310 | cpu->dev.bus->uevent = arch_cpu_uevent; | 350 | cpu->dev.bus->uevent = cpu_uevent; |
| 311 | #endif | 351 | #endif |
| 312 | cpu->dev.groups = common_cpu_attr_groups; | 352 | cpu->dev.groups = common_cpu_attr_groups; |
| 313 | if (cpu->hotpluggable) | 353 | if (cpu->hotpluggable) |
| @@ -330,8 +370,8 @@ struct device *get_cpu_device(unsigned cpu) | |||
| 330 | } | 370 | } |
| 331 | EXPORT_SYMBOL_GPL(get_cpu_device); | 371 | EXPORT_SYMBOL_GPL(get_cpu_device); |
| 332 | 372 | ||
| 333 | #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE | 373 | #ifdef CONFIG_HAVE_CPU_AUTOPROBE |
| 334 | static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL); | 374 | static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); |
| 335 | #endif | 375 | #endif |
| 336 | 376 | ||
| 337 | static struct attribute *cpu_root_attrs[] = { | 377 | static struct attribute *cpu_root_attrs[] = { |
| @@ -344,7 +384,7 @@ static struct attribute *cpu_root_attrs[] = { | |||
| 344 | &cpu_attrs[2].attr.attr, | 384 | &cpu_attrs[2].attr.attr, |
| 345 | &dev_attr_kernel_max.attr, | 385 | &dev_attr_kernel_max.attr, |
| 346 | &dev_attr_offline.attr, | 386 | &dev_attr_offline.attr, |
| 347 | #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE | 387 | #ifdef CONFIG_HAVE_CPU_AUTOPROBE |
| 348 | &dev_attr_modalias.attr, | 388 | &dev_attr_modalias.attr, |
| 349 | #endif | 389 | #endif |
| 350 | NULL | 390 | NULL |
diff --git a/include/linux/cpufeature.h b/include/linux/cpufeature.h new file mode 100644 index 000000000000..c4d4eb8ac9fe --- /dev/null +++ b/include/linux/cpufeature.h | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef __LINUX_CPUFEATURE_H | ||
| 10 | #define __LINUX_CPUFEATURE_H | ||
| 11 | |||
| 12 | #ifdef CONFIG_GENERIC_CPU_AUTOPROBE | ||
| 13 | |||
| 14 | #include <linux/mod_devicetable.h> | ||
| 15 | #include <asm/cpufeature.h> | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Macros imported from <asm/cpufeature.h>: | ||
| 19 | * - cpu_feature(x) ordinal value of feature called 'x' | ||
| 20 | * - cpu_have_feature(u32 n) whether feature #n is available | ||
| 21 | * - MAX_CPU_FEATURES upper bound for feature ordinal values | ||
| 22 | * Optional: | ||
| 23 | * - CPU_FEATURE_TYPEFMT format string fragment for printing the cpu type | ||
| 24 | * - CPU_FEATURE_TYPEVAL set of values matching the format string above | ||
| 25 | */ | ||
| 26 | |||
| 27 | #ifndef CPU_FEATURE_TYPEFMT | ||
| 28 | #define CPU_FEATURE_TYPEFMT "%s" | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #ifndef CPU_FEATURE_TYPEVAL | ||
| 32 | #define CPU_FEATURE_TYPEVAL ELF_PLATFORM | ||
| 33 | #endif | ||
| 34 | |||
| 35 | /* | ||
| 36 | * Use module_cpu_feature_match(feature, module_init_function) to | ||
| 37 | * declare that | ||
| 38 | * a) the module shall be probed upon discovery of CPU feature 'feature' | ||
| 39 | * (typically at boot time using udev) | ||
| 40 | * b) the module must not be loaded if CPU feature 'feature' is not present | ||
| 41 | * (not even by manual insmod). | ||
| 42 | * | ||
| 43 | * For a list of legal values for 'feature', please consult the file | ||
| 44 | * 'asm/cpufeature.h' of your favorite architecture. | ||
| 45 | */ | ||
| 46 | #define module_cpu_feature_match(x, __init) \ | ||
| 47 | static struct cpu_feature const cpu_feature_match_ ## x[] = \ | ||
| 48 | { { .feature = cpu_feature(x) }, { } }; \ | ||
| 49 | MODULE_DEVICE_TABLE(cpu, cpu_feature_match_ ## x); \ | ||
| 50 | \ | ||
| 51 | static int cpu_feature_match_ ## x ## _init(void) \ | ||
| 52 | { \ | ||
| 53 | if (!cpu_have_feature(cpu_feature(x))) \ | ||
| 54 | return -ENODEV; \ | ||
| 55 | return __init(); \ | ||
| 56 | } \ | ||
| 57 | module_init(cpu_feature_match_ ## x ## _init) | ||
| 58 | |||
| 59 | #endif | ||
| 60 | #endif | ||
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 45e921401b06..f2ac87c613a5 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h | |||
| @@ -564,6 +564,15 @@ struct x86_cpu_id { | |||
| 564 | #define X86_MODEL_ANY 0 | 564 | #define X86_MODEL_ANY 0 |
| 565 | #define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */ | 565 | #define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */ |
| 566 | 566 | ||
| 567 | /* | ||
| 568 | * Generic table type for matching CPU features. | ||
| 569 | * @feature: the bit number of the feature (0 - 65535) | ||
| 570 | */ | ||
| 571 | |||
| 572 | struct cpu_feature { | ||
| 573 | __u16 feature; | ||
| 574 | }; | ||
| 575 | |||
| 567 | #define IPACK_ANY_FORMAT 0xff | 576 | #define IPACK_ANY_FORMAT 0xff |
| 568 | #define IPACK_ANY_ID (~0) | 577 | #define IPACK_ANY_ID (~0) |
| 569 | struct ipack_device_id { | 578 | struct ipack_device_id { |
diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index bb5d115ca671..f282516acc7b 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c | |||
| @@ -174,6 +174,9 @@ int main(void) | |||
| 174 | DEVID_FIELD(x86_cpu_id, model); | 174 | DEVID_FIELD(x86_cpu_id, model); |
| 175 | DEVID_FIELD(x86_cpu_id, vendor); | 175 | DEVID_FIELD(x86_cpu_id, vendor); |
| 176 | 176 | ||
| 177 | DEVID(cpu_feature); | ||
| 178 | DEVID_FIELD(cpu_feature, feature); | ||
| 179 | |||
| 177 | DEVID(mei_cl_device_id); | 180 | DEVID(mei_cl_device_id); |
| 178 | DEVID_FIELD(mei_cl_device_id, name); | 181 | DEVID_FIELD(mei_cl_device_id, name); |
| 179 | 182 | ||
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 25e5cb0aaef6..506146e5f4a8 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c | |||
| @@ -1135,6 +1135,16 @@ static int do_x86cpu_entry(const char *filename, void *symval, | |||
| 1135 | } | 1135 | } |
| 1136 | ADD_TO_DEVTABLE("x86cpu", x86_cpu_id, do_x86cpu_entry); | 1136 | ADD_TO_DEVTABLE("x86cpu", x86_cpu_id, do_x86cpu_entry); |
| 1137 | 1137 | ||
| 1138 | /* LOOKS like cpu:type:*:feature:*FEAT* */ | ||
| 1139 | static int do_cpu_entry(const char *filename, void *symval, char *alias) | ||
| 1140 | { | ||
| 1141 | DEF_FIELD(symval, cpu_feature, feature); | ||
| 1142 | |||
| 1143 | sprintf(alias, "cpu:type:*:feature:*%04X*", feature); | ||
| 1144 | return 1; | ||
| 1145 | } | ||
| 1146 | ADD_TO_DEVTABLE("cpu", cpu_feature, do_cpu_entry); | ||
| 1147 | |||
| 1138 | /* Looks like: mei:S */ | 1148 | /* Looks like: mei:S */ |
| 1139 | static int do_mei_entry(const char *filename, void *symval, | 1149 | static int do_mei_entry(const char *filename, void *symval, |
| 1140 | char *alias) | 1150 | char *alias) |
