Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Ard Biesheuvel and committed by
Greg Kroah-Hartman
67bad2fd 91219a3b

+135 -5
+8
drivers/base/Kconfig
··· 185 185 bool 186 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 196 config SOC_BUS 189 197 bool 190 198
+45 -5
drivers/base/cpu.c
··· 15 15 #include <linux/percpu.h> 16 16 #include <linux/acpi.h> 17 17 #include <linux/of.h> 18 + #include <linux/cpufeature.h> 18 19 19 20 #include "base.h" 20 21 ··· 287 286 */ 288 287 } 289 288 289 + #ifdef CONFIG_HAVE_CPU_AUTOPROBE 290 + #ifdef CONFIG_GENERIC_CPU_AUTOPROBE 291 + static ssize_t print_cpu_modalias(struct device *dev, 292 + struct device_attribute *attr, 293 + char *buf) 294 + { 295 + ssize_t n; 296 + u32 i; 297 + 298 + n = sprintf(buf, "cpu:type:" CPU_FEATURE_TYPEFMT ":feature:", 299 + CPU_FEATURE_TYPEVAL); 300 + 301 + for (i = 0; i < MAX_CPU_FEATURES; i++) 302 + if (cpu_have_feature(i)) { 303 + if (PAGE_SIZE < n + sizeof(",XXXX\n")) { 304 + WARN(1, "CPU features overflow page\n"); 305 + break; 306 + } 307 + n += sprintf(&buf[n], ",%04X", i); 308 + } 309 + buf[n++] = '\n'; 310 + return n; 311 + } 312 + #else 313 + #define print_cpu_modalias arch_print_cpu_modalias 314 + #endif 315 + 316 + static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env) 317 + { 318 + char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); 319 + if (buf) { 320 + print_cpu_modalias(NULL, NULL, buf); 321 + add_uevent_var(env, "MODALIAS=%s", buf); 322 + kfree(buf); 323 + } 324 + return 0; 325 + } 326 + #endif 327 + 290 328 /* 291 329 * register_cpu - Setup a sysfs device for a CPU. 292 330 * @cpu - cpu->hotpluggable field set to 1 will generate a control file in ··· 346 306 cpu->dev.offline_disabled = !cpu->hotpluggable; 347 307 cpu->dev.offline = !cpu_online(num); 348 308 cpu->dev.of_node = of_get_cpu_node(num, NULL); 349 - #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE 350 - cpu->dev.bus->uevent = arch_cpu_uevent; 309 + #ifdef CONFIG_HAVE_CPU_AUTOPROBE 310 + cpu->dev.bus->uevent = cpu_uevent; 351 311 #endif 352 312 cpu->dev.groups = common_cpu_attr_groups; 353 313 if (cpu->hotpluggable) ··· 370 330 } 371 331 EXPORT_SYMBOL_GPL(get_cpu_device); 372 332 373 - #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE 374 - static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL); 333 + #ifdef CONFIG_HAVE_CPU_AUTOPROBE 334 + static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); 375 335 #endif 376 336 377 337 static struct attribute *cpu_root_attrs[] = { ··· 384 344 &cpu_attrs[2].attr.attr, 385 345 &dev_attr_kernel_max.attr, 386 346 &dev_attr_offline.attr, 387 - #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE 347 + #ifdef CONFIG_HAVE_CPU_AUTOPROBE 388 348 &dev_attr_modalias.attr, 389 349 #endif 390 350 NULL
+60
include/linux/cpufeature.h
··· 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
+9
include/linux/mod_devicetable.h
··· 564 564 #define X86_MODEL_ANY 0 565 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 576 #define IPACK_ANY_FORMAT 0xff 568 577 #define IPACK_ANY_ID (~0) 569 578 struct ipack_device_id {
+3
scripts/mod/devicetable-offsets.c
··· 174 174 DEVID_FIELD(x86_cpu_id, model); 175 175 DEVID_FIELD(x86_cpu_id, vendor); 176 176 177 + DEVID(cpu_feature); 178 + DEVID_FIELD(cpu_feature, feature); 179 + 177 180 DEVID(mei_cl_device_id); 178 181 DEVID_FIELD(mei_cl_device_id, name); 179 182
+10
scripts/mod/file2alias.c
··· 1135 1135 } 1136 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 1148 /* Looks like: mei:S */ 1139 1149 static int do_mei_entry(const char *filename, void *symval, 1140 1150 char *alias)