]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * drivers/base/cpu.c - basic CPU class support | |
3 | */ | |
4 | ||
5 | #include <linux/sysdev.h> | |
6 | #include <linux/module.h> | |
7 | #include <linux/init.h> | |
f6a57033 | 8 | #include <linux/sched.h> |
1da177e4 LT |
9 | #include <linux/cpu.h> |
10 | #include <linux/topology.h> | |
11 | #include <linux/device.h> | |
76b67ed9 | 12 | #include <linux/node.h> |
5a0e3ad6 | 13 | #include <linux/gfp.h> |
1da177e4 | 14 | |
a1bdc7aa | 15 | #include "base.h" |
1da177e4 | 16 | |
e1a7e29a AK |
17 | static struct sysdev_class_attribute *cpu_sysdev_class_attrs[]; |
18 | ||
1da177e4 | 19 | struct sysdev_class cpu_sysdev_class = { |
af5ca3f4 | 20 | .name = "cpu", |
e1a7e29a | 21 | .attrs = cpu_sysdev_class_attrs, |
1da177e4 LT |
22 | }; |
23 | EXPORT_SYMBOL(cpu_sysdev_class); | |
24 | ||
e37d05da | 25 | static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices); |
ad74557a | 26 | |
1da177e4 | 27 | #ifdef CONFIG_HOTPLUG_CPU |
4a0b2b4d AK |
28 | static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr, |
29 | char *buf) | |
1da177e4 LT |
30 | { |
31 | struct cpu *cpu = container_of(dev, struct cpu, sysdev); | |
32 | ||
33 | return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id)); | |
34 | } | |
35 | ||
4a0b2b4d AK |
36 | static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr, |
37 | const char *buf, size_t count) | |
1da177e4 LT |
38 | { |
39 | struct cpu *cpu = container_of(dev, struct cpu, sysdev); | |
40 | ssize_t ret; | |
41 | ||
51badebd | 42 | cpu_hotplug_driver_lock(); |
1da177e4 LT |
43 | switch (buf[0]) { |
44 | case '0': | |
45 | ret = cpu_down(cpu->sysdev.id); | |
46 | if (!ret) | |
312c004d | 47 | kobject_uevent(&dev->kobj, KOBJ_OFFLINE); |
1da177e4 LT |
48 | break; |
49 | case '1': | |
34f361ad | 50 | ret = cpu_up(cpu->sysdev.id); |
fb69c390 | 51 | if (!ret) |
312c004d | 52 | kobject_uevent(&dev->kobj, KOBJ_ONLINE); |
1da177e4 LT |
53 | break; |
54 | default: | |
55 | ret = -EINVAL; | |
56 | } | |
51badebd | 57 | cpu_hotplug_driver_unlock(); |
1da177e4 LT |
58 | |
59 | if (ret >= 0) | |
60 | ret = count; | |
61 | return ret; | |
62 | } | |
9eb3ff40 | 63 | static SYSDEV_ATTR(online, 0644, show_online, store_online); |
1da177e4 | 64 | |
6c847402 | 65 | static void __cpuinit register_cpu_control(struct cpu *cpu) |
1da177e4 LT |
66 | { |
67 | sysdev_create_file(&cpu->sysdev, &attr_online); | |
68 | } | |
76b67ed9 | 69 | void unregister_cpu(struct cpu *cpu) |
1da177e4 | 70 | { |
ad74557a | 71 | int logical_cpu = cpu->sysdev.id; |
1da177e4 | 72 | |
76b67ed9 KH |
73 | unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); |
74 | ||
1da177e4 LT |
75 | sysdev_remove_file(&cpu->sysdev, &attr_online); |
76 | ||
77 | sysdev_unregister(&cpu->sysdev); | |
e37d05da | 78 | per_cpu(cpu_sys_devices, logical_cpu) = NULL; |
1da177e4 LT |
79 | return; |
80 | } | |
12633e80 NF |
81 | |
82 | #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE | |
67fc233f SR |
83 | static ssize_t cpu_probe_store(struct sysdev_class *class, |
84 | struct sysdev_class_attribute *attr, | |
85 | const char *buf, | |
12633e80 NF |
86 | size_t count) |
87 | { | |
88 | return arch_cpu_probe(buf, count); | |
89 | } | |
90 | ||
67fc233f SR |
91 | static ssize_t cpu_release_store(struct sysdev_class *class, |
92 | struct sysdev_class_attribute *attr, | |
93 | const char *buf, | |
12633e80 NF |
94 | size_t count) |
95 | { | |
96 | return arch_cpu_release(buf, count); | |
97 | } | |
98 | ||
67fc233f SR |
99 | static SYSDEV_CLASS_ATTR(probe, S_IWUSR, NULL, cpu_probe_store); |
100 | static SYSDEV_CLASS_ATTR(release, S_IWUSR, NULL, cpu_release_store); | |
12633e80 NF |
101 | #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ |
102 | ||
1da177e4 LT |
103 | #else /* ... !CONFIG_HOTPLUG_CPU */ |
104 | static inline void register_cpu_control(struct cpu *cpu) | |
105 | { | |
106 | } | |
107 | #endif /* CONFIG_HOTPLUG_CPU */ | |
108 | ||
51be5606 VG |
109 | #ifdef CONFIG_KEXEC |
110 | #include <linux/kexec.h> | |
111 | ||
4a0b2b4d AK |
112 | static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr, |
113 | char *buf) | |
51be5606 VG |
114 | { |
115 | struct cpu *cpu = container_of(dev, struct cpu, sysdev); | |
116 | ssize_t rc; | |
117 | unsigned long long addr; | |
118 | int cpunum; | |
119 | ||
120 | cpunum = cpu->sysdev.id; | |
121 | ||
122 | /* | |
123 | * Might be reading other cpu's data based on which cpu read thread | |
124 | * has been scheduled. But cpu data (memory) is allocated once during | |
125 | * boot up and this data does not change there after. Hence this | |
126 | * operation should be safe. No locking required. | |
127 | */ | |
3b034b0d | 128 | addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpunum)); |
51be5606 | 129 | rc = sprintf(buf, "%Lx\n", addr); |
51be5606 VG |
130 | return rc; |
131 | } | |
132 | static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL); | |
133 | #endif | |
134 | ||
9d1fe323 MT |
135 | /* |
136 | * Print cpu online, possible, present, and system maps | |
137 | */ | |
265d2e2e AK |
138 | |
139 | struct cpu_attr { | |
140 | struct sysdev_class_attribute attr; | |
141 | const struct cpumask *const * const map; | |
142 | }; | |
143 | ||
144 | static ssize_t show_cpus_attr(struct sysdev_class *class, | |
145 | struct sysdev_class_attribute *attr, | |
146 | char *buf) | |
9d1fe323 | 147 | { |
265d2e2e AK |
148 | struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr); |
149 | int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *(ca->map)); | |
9d1fe323 MT |
150 | |
151 | buf[n++] = '\n'; | |
152 | buf[n] = '\0'; | |
153 | return n; | |
154 | } | |
155 | ||
265d2e2e AK |
156 | #define _CPU_ATTR(name, map) \ |
157 | { _SYSDEV_CLASS_ATTR(name, 0444, show_cpus_attr, NULL), map } | |
9d1fe323 | 158 | |
e1a7e29a | 159 | /* Keep in sync with cpu_sysdev_class_attrs */ |
265d2e2e AK |
160 | static struct cpu_attr cpu_attrs[] = { |
161 | _CPU_ATTR(online, &cpu_online_mask), | |
162 | _CPU_ATTR(possible, &cpu_possible_mask), | |
163 | _CPU_ATTR(present, &cpu_present_mask), | |
164 | }; | |
9d1fe323 | 165 | |
e057d7ae MT |
166 | /* |
167 | * Print values for NR_CPUS and offlined cpus | |
168 | */ | |
c9be0a36 AK |
169 | static ssize_t print_cpus_kernel_max(struct sysdev_class *class, |
170 | struct sysdev_class_attribute *attr, char *buf) | |
e057d7ae | 171 | { |
8fd2d2d5 | 172 | int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1); |
e057d7ae MT |
173 | return n; |
174 | } | |
175 | static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL); | |
176 | ||
177 | /* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */ | |
178 | unsigned int total_cpus; | |
179 | ||
c9be0a36 AK |
180 | static ssize_t print_cpus_offline(struct sysdev_class *class, |
181 | struct sysdev_class_attribute *attr, char *buf) | |
e057d7ae MT |
182 | { |
183 | int n = 0, len = PAGE_SIZE-2; | |
184 | cpumask_var_t offline; | |
185 | ||
186 | /* display offline cpus < nr_cpu_ids */ | |
187 | if (!alloc_cpumask_var(&offline, GFP_KERNEL)) | |
188 | return -ENOMEM; | |
189 | cpumask_complement(offline, cpu_online_mask); | |
190 | n = cpulist_scnprintf(buf, len, offline); | |
191 | free_cpumask_var(offline); | |
192 | ||
193 | /* display offline cpus >= nr_cpu_ids */ | |
194 | if (total_cpus && nr_cpu_ids < total_cpus) { | |
195 | if (n && n < len) | |
196 | buf[n++] = ','; | |
197 | ||
198 | if (nr_cpu_ids == total_cpus-1) | |
199 | n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids); | |
200 | else | |
201 | n += snprintf(&buf[n], len - n, "%d-%d", | |
202 | nr_cpu_ids, total_cpus-1); | |
203 | } | |
204 | ||
205 | n += snprintf(&buf[n], len - n, "\n"); | |
206 | return n; | |
207 | } | |
208 | static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL); | |
209 | ||
1da177e4 | 210 | /* |
405ae7d3 | 211 | * register_cpu - Setup a sysfs device for a CPU. |
72486f1f SS |
212 | * @cpu - cpu->hotpluggable field set to 1 will generate a control file in |
213 | * sysfs for this CPU. | |
1da177e4 LT |
214 | * @num - CPU number to use when creating the device. |
215 | * | |
216 | * Initialize and register the CPU device. | |
217 | */ | |
33b5f31b | 218 | int __cpuinit register_cpu(struct cpu *cpu, int num) |
1da177e4 LT |
219 | { |
220 | int error; | |
1da177e4 LT |
221 | cpu->node_id = cpu_to_node(num); |
222 | cpu->sysdev.id = num; | |
223 | cpu->sysdev.cls = &cpu_sysdev_class; | |
224 | ||
225 | error = sysdev_register(&cpu->sysdev); | |
76b67ed9 | 226 | |
72486f1f | 227 | if (!error && cpu->hotpluggable) |
1da177e4 | 228 | register_cpu_control(cpu); |
ad74557a | 229 | if (!error) |
e37d05da | 230 | per_cpu(cpu_sys_devices, num) = &cpu->sysdev; |
76b67ed9 KH |
231 | if (!error) |
232 | register_cpu_under_node(num, cpu_to_node(num)); | |
51be5606 VG |
233 | |
234 | #ifdef CONFIG_KEXEC | |
235 | if (!error) | |
236 | error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes); | |
237 | #endif | |
1da177e4 LT |
238 | return error; |
239 | } | |
240 | ||
a29d642a | 241 | struct sys_device *get_cpu_sysdev(unsigned cpu) |
ad74557a | 242 | { |
e37d05da MT |
243 | if (cpu < nr_cpu_ids && cpu_possible(cpu)) |
244 | return per_cpu(cpu_sys_devices, cpu); | |
ad74557a AR |
245 | else |
246 | return NULL; | |
247 | } | |
248 | EXPORT_SYMBOL_GPL(get_cpu_sysdev); | |
1da177e4 LT |
249 | |
250 | int __init cpu_dev_init(void) | |
251 | { | |
5c45bf27 SS |
252 | int err; |
253 | ||
254 | err = sysdev_class_register(&cpu_sysdev_class); | |
255 | #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) | |
256 | if (!err) | |
257 | err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class); | |
258 | #endif | |
259 | ||
260 | return err; | |
1da177e4 | 261 | } |
e1a7e29a AK |
262 | |
263 | static struct sysdev_class_attribute *cpu_sysdev_class_attrs[] = { | |
264 | #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE | |
6c1733ac GKH |
265 | &attr_probe, |
266 | &attr_release, | |
e1a7e29a AK |
267 | #endif |
268 | &cpu_attrs[0].attr, | |
269 | &cpu_attrs[1].attr, | |
270 | &cpu_attrs[2].attr, | |
271 | &attr_kernel_max, | |
272 | &attr_offline, | |
273 | NULL | |
274 | }; |