History

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.

Ard Biesheuvel committed 12 years ago

Commit 67bad2f1 parent 91219a36 files · +135 −5Browse files

Unified · Split

6 files
  1. drivers/base/Kconfig +8 −0
  2. drivers/base/cpu.c +45 −5
  3. include/linux/cpufeature.h +60 −0
  4. include/linux/mod_devicetable.h +9 −0
  5. scripts/mod/devicetable-offsets.c +3 −0
  6. scripts/mod/file2alias.c +10 −0
drivers/base/Kconfig+8−0
@@ -185,6 +185,14 @@ config GENERIC_CPU_DEVICES · 184 unmodified lines
185185 bool
186186 default n
187187
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+
188196 config SOC_BUS
189197 bool
190198
drivers/base/cpu.c+45−5
@@ -15,6 +15,7 @@ · 14 unmodified lines
1515 #include <linux/percpu.h>
1616 #include <linux/acpi.h>
1717 #include <linux/of.h>
18+#include <linux/cpufeature.h>
1819
1920 #include "base.h"
2021
@@ -286,6 +287,45 @@ static void cpu_device_release(struct device *dev) · 265 unmodified lines
286287 */
287288 }
288289
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+
289329 /*
290330 * register_cpu - Setup a sysfs device for a CPU.
291331 * @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) · 14 unmodified lines
306346 cpu->dev.offline_disabled = !cpu->hotpluggable;
307347 cpu->dev.offline = !cpu_online(num);
308348 cpu->dev.of_node = of_get_cpu_node(num, NULL);
309−#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
310− cpu->dev.bus->uevent = arch_cpu_uevent;
349+#ifdef CONFIG_HAVE_CPU_AUTOPROBE
350+ cpu->dev.bus->uevent = cpu_uevent;
311351 #endif
312352 cpu->dev.groups = common_cpu_attr_groups;
313353 if (cpu->hotpluggable)
@@ -330,8 +370,8 @@ struct device *get_cpu_device(unsigned cpu) · 16 unmodified lines
330370 }
331371 EXPORT_SYMBOL_GPL(get_cpu_device);
332372
333−#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
334−static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL);
373+#ifdef CONFIG_HAVE_CPU_AUTOPROBE
374+static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL);
335375 #endif
336376
337377 static struct attribute *cpu_root_attrs[] = {
@@ -344,7 +384,7 @@ static struct attribute *cpu_root_attrs[] = { · 6 unmodified lines
344384 &cpu_attrs[2].attr.attr,
345385 &dev_attr_kernel_max.attr,
346386 &dev_attr_offline.attr,
347−#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
387+#ifdef CONFIG_HAVE_CPU_AUTOPROBE
348388 &dev_attr_modalias.attr,
349389 #endif
350390 NULL
include/linux/cpufeature.h+60−0
@@ -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
include/linux/mod_devicetable.h+9−0
@@ -564,6 +564,15 @@ struct x86_cpu_id { · 563 unmodified lines
564564 #define X86_MODEL_ANY 0
565565 #define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */
566566
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+
567576 #define IPACK_ANY_FORMAT 0xff
568577 #define IPACK_ANY_ID (~0)
569578 struct ipack_device_id {
scripts/mod/devicetable-offsets.c+3−0
@@ -174,6 +174,9 @@ int main(void) · 173 unmodified lines
174174 DEVID_FIELD(x86_cpu_id, model);
175175 DEVID_FIELD(x86_cpu_id, vendor);
176176
177+ DEVID(cpu_feature);
178+ DEVID_FIELD(cpu_feature, feature);
179+
177180 DEVID(mei_cl_device_id);
178181 DEVID_FIELD(mei_cl_device_id, name);
179182
scripts/mod/file2alias.c+10−0
@@ -1135,6 +1135,16 @@ static int do_x86cpu_entry(const char *filename, void *symval, · 1134 unmodified lines
11351135 }
11361136 ADD_TO_DEVTABLE("x86cpu", x86_cpu_id, do_x86cpu_entry);
11371137
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+
11381148 /* Looks like: mei:S */
11391149 static int do_mei_entry(const char *filename, void *symval,
11401150 char *alias)