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