diff options
Diffstat (limited to 'drivers/base/cpu.c')
-rw-r--r-- | drivers/base/cpu.c | 101 |
1 files changed, 44 insertions, 57 deletions
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 3d48fc887ef4..1d110dc6f0c1 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c | |||
@@ -13,17 +13,21 @@ | |||
13 | #include <linux/gfp.h> | 13 | #include <linux/gfp.h> |
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/percpu.h> | 15 | #include <linux/percpu.h> |
16 | #include <linux/acpi.h> | ||
16 | 17 | ||
17 | #include "base.h" | 18 | #include "base.h" |
18 | 19 | ||
19 | struct bus_type cpu_subsys = { | ||
20 | .name = "cpu", | ||
21 | .dev_name = "cpu", | ||
22 | }; | ||
23 | EXPORT_SYMBOL_GPL(cpu_subsys); | ||
24 | |||
25 | static DEFINE_PER_CPU(struct device *, cpu_sys_devices); | 20 | static DEFINE_PER_CPU(struct device *, cpu_sys_devices); |
26 | 21 | ||
22 | static int cpu_subsys_match(struct device *dev, struct device_driver *drv) | ||
23 | { | ||
24 | /* ACPI style match is the only one that may succeed. */ | ||
25 | if (acpi_driver_match_device(dev, drv)) | ||
26 | return 1; | ||
27 | |||
28 | return 0; | ||
29 | } | ||
30 | |||
27 | #ifdef CONFIG_HOTPLUG_CPU | 31 | #ifdef CONFIG_HOTPLUG_CPU |
28 | static void change_cpu_under_node(struct cpu *cpu, | 32 | static void change_cpu_under_node(struct cpu *cpu, |
29 | unsigned int from_nid, unsigned int to_nid) | 33 | unsigned int from_nid, unsigned int to_nid) |
@@ -34,69 +38,45 @@ static void change_cpu_under_node(struct cpu *cpu, | |||
34 | cpu->node_id = to_nid; | 38 | cpu->node_id = to_nid; |
35 | } | 39 | } |
36 | 40 | ||
37 | static ssize_t show_online(struct device *dev, | 41 | static int __ref cpu_subsys_online(struct device *dev) |
38 | struct device_attribute *attr, | ||
39 | char *buf) | ||
40 | { | 42 | { |
41 | struct cpu *cpu = container_of(dev, struct cpu, dev); | 43 | struct cpu *cpu = container_of(dev, struct cpu, dev); |
44 | int cpuid = dev->id; | ||
45 | int from_nid, to_nid; | ||
46 | int ret; | ||
42 | 47 | ||
43 | return sprintf(buf, "%u\n", !!cpu_online(cpu->dev.id)); | 48 | cpu_hotplug_driver_lock(); |
49 | |||
50 | from_nid = cpu_to_node(cpuid); | ||
51 | ret = cpu_up(cpuid); | ||
52 | /* | ||
53 | * When hot adding memory to memoryless node and enabling a cpu | ||
54 | * on the node, node number of the cpu may internally change. | ||
55 | */ | ||
56 | to_nid = cpu_to_node(cpuid); | ||
57 | if (from_nid != to_nid) | ||
58 | change_cpu_under_node(cpu, from_nid, to_nid); | ||
59 | |||
60 | cpu_hotplug_driver_unlock(); | ||
61 | return ret; | ||
44 | } | 62 | } |
45 | 63 | ||
46 | static ssize_t __ref store_online(struct device *dev, | 64 | static int cpu_subsys_offline(struct device *dev) |
47 | struct device_attribute *attr, | ||
48 | const char *buf, size_t count) | ||
49 | { | 65 | { |
50 | struct cpu *cpu = container_of(dev, struct cpu, dev); | 66 | int ret; |
51 | int cpuid = cpu->dev.id; | ||
52 | int from_nid, to_nid; | ||
53 | ssize_t ret; | ||
54 | 67 | ||
55 | cpu_hotplug_driver_lock(); | 68 | cpu_hotplug_driver_lock(); |
56 | switch (buf[0]) { | 69 | ret = cpu_down(dev->id); |
57 | case '0': | ||
58 | ret = cpu_down(cpuid); | ||
59 | if (!ret) | ||
60 | kobject_uevent(&dev->kobj, KOBJ_OFFLINE); | ||
61 | break; | ||
62 | case '1': | ||
63 | from_nid = cpu_to_node(cpuid); | ||
64 | ret = cpu_up(cpuid); | ||
65 | |||
66 | /* | ||
67 | * When hot adding memory to memoryless node and enabling a cpu | ||
68 | * on the node, node number of the cpu may internally change. | ||
69 | */ | ||
70 | to_nid = cpu_to_node(cpuid); | ||
71 | if (from_nid != to_nid) | ||
72 | change_cpu_under_node(cpu, from_nid, to_nid); | ||
73 | |||
74 | if (!ret) | ||
75 | kobject_uevent(&dev->kobj, KOBJ_ONLINE); | ||
76 | break; | ||
77 | default: | ||
78 | ret = -EINVAL; | ||
79 | } | ||
80 | cpu_hotplug_driver_unlock(); | 70 | cpu_hotplug_driver_unlock(); |
81 | |||
82 | if (ret >= 0) | ||
83 | ret = count; | ||
84 | return ret; | 71 | return ret; |
85 | } | 72 | } |
86 | static DEVICE_ATTR(online, 0644, show_online, store_online); | ||
87 | 73 | ||
88 | static void __cpuinit register_cpu_control(struct cpu *cpu) | ||
89 | { | ||
90 | device_create_file(&cpu->dev, &dev_attr_online); | ||
91 | } | ||
92 | void unregister_cpu(struct cpu *cpu) | 74 | void unregister_cpu(struct cpu *cpu) |
93 | { | 75 | { |
94 | int logical_cpu = cpu->dev.id; | 76 | int logical_cpu = cpu->dev.id; |
95 | 77 | ||
96 | unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); | 78 | unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); |
97 | 79 | ||
98 | device_remove_file(&cpu->dev, &dev_attr_online); | ||
99 | |||
100 | device_unregister(&cpu->dev); | 80 | device_unregister(&cpu->dev); |
101 | per_cpu(cpu_sys_devices, logical_cpu) = NULL; | 81 | per_cpu(cpu_sys_devices, logical_cpu) = NULL; |
102 | return; | 82 | return; |
@@ -123,12 +103,19 @@ static DEVICE_ATTR(probe, S_IWUSR, NULL, cpu_probe_store); | |||
123 | static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store); | 103 | static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store); |
124 | #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ | 104 | #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ |
125 | 105 | ||
126 | #else /* ... !CONFIG_HOTPLUG_CPU */ | ||
127 | static inline void register_cpu_control(struct cpu *cpu) | ||
128 | { | ||
129 | } | ||
130 | #endif /* CONFIG_HOTPLUG_CPU */ | 106 | #endif /* CONFIG_HOTPLUG_CPU */ |
131 | 107 | ||
108 | struct bus_type cpu_subsys = { | ||
109 | .name = "cpu", | ||
110 | .dev_name = "cpu", | ||
111 | .match = cpu_subsys_match, | ||
112 | #ifdef CONFIG_HOTPLUG_CPU | ||
113 | .online = cpu_subsys_online, | ||
114 | .offline = cpu_subsys_offline, | ||
115 | #endif | ||
116 | }; | ||
117 | EXPORT_SYMBOL_GPL(cpu_subsys); | ||
118 | |||
132 | #ifdef CONFIG_KEXEC | 119 | #ifdef CONFIG_KEXEC |
133 | #include <linux/kexec.h> | 120 | #include <linux/kexec.h> |
134 | 121 | ||
@@ -277,12 +264,12 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) | |||
277 | cpu->dev.id = num; | 264 | cpu->dev.id = num; |
278 | cpu->dev.bus = &cpu_subsys; | 265 | cpu->dev.bus = &cpu_subsys; |
279 | cpu->dev.release = cpu_device_release; | 266 | cpu->dev.release = cpu_device_release; |
267 | cpu->dev.offline_disabled = !cpu->hotpluggable; | ||
268 | cpu->dev.offline = !cpu_online(num); | ||
280 | #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE | 269 | #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE |
281 | cpu->dev.bus->uevent = arch_cpu_uevent; | 270 | cpu->dev.bus->uevent = arch_cpu_uevent; |
282 | #endif | 271 | #endif |
283 | error = device_register(&cpu->dev); | 272 | error = device_register(&cpu->dev); |
284 | if (!error && cpu->hotpluggable) | ||
285 | register_cpu_control(cpu); | ||
286 | if (!error) | 273 | if (!error) |
287 | per_cpu(cpu_sys_devices, num) = &cpu->dev; | 274 | per_cpu(cpu_sys_devices, num) = &cpu->dev; |
288 | if (!error) | 275 | if (!error) |