xref: /openbmc/linux/drivers/base/cpu.c (revision 9f13a1fd)
11da177e4SLinus Torvalds /*
28a25a2fdSKay Sievers  * CPU subsystem support
31da177e4SLinus Torvalds  */
41da177e4SLinus Torvalds 
5024f7846SBen Hutchings #include <linux/kernel.h>
61da177e4SLinus Torvalds #include <linux/module.h>
71da177e4SLinus Torvalds #include <linux/init.h>
8f6a57033SAl Viro #include <linux/sched.h>
91da177e4SLinus Torvalds #include <linux/cpu.h>
101da177e4SLinus Torvalds #include <linux/topology.h>
111da177e4SLinus Torvalds #include <linux/device.h>
1276b67ed9SKAMEZAWA Hiroyuki #include <linux/node.h>
135a0e3ad6STejun Heo #include <linux/gfp.h>
149f13a1fdSBen Hutchings #include <linux/percpu.h>
151da177e4SLinus Torvalds 
16a1bdc7aaSBen Dooks #include "base.h"
171da177e4SLinus Torvalds 
188a25a2fdSKay Sievers struct bus_type cpu_subsys = {
19af5ca3f4SKay Sievers 	.name = "cpu",
208a25a2fdSKay Sievers 	.dev_name = "cpu",
211da177e4SLinus Torvalds };
228a25a2fdSKay Sievers EXPORT_SYMBOL_GPL(cpu_subsys);
231da177e4SLinus Torvalds 
248a25a2fdSKay Sievers static DEFINE_PER_CPU(struct device *, cpu_sys_devices);
25ad74557aSAshok Raj 
261da177e4SLinus Torvalds #ifdef CONFIG_HOTPLUG_CPU
278a25a2fdSKay Sievers static ssize_t show_online(struct device *dev,
288a25a2fdSKay Sievers 			   struct device_attribute *attr,
294a0b2b4dSAndi Kleen 			   char *buf)
301da177e4SLinus Torvalds {
318a25a2fdSKay Sievers 	struct cpu *cpu = container_of(dev, struct cpu, dev);
321da177e4SLinus Torvalds 
338a25a2fdSKay Sievers 	return sprintf(buf, "%u\n", !!cpu_online(cpu->dev.id));
341da177e4SLinus Torvalds }
351da177e4SLinus Torvalds 
368a25a2fdSKay Sievers static ssize_t __ref store_online(struct device *dev,
378a25a2fdSKay Sievers 				  struct device_attribute *attr,
384a0b2b4dSAndi Kleen 				  const char *buf, size_t count)
391da177e4SLinus Torvalds {
408a25a2fdSKay Sievers 	struct cpu *cpu = container_of(dev, struct cpu, dev);
411da177e4SLinus Torvalds 	ssize_t ret;
421da177e4SLinus Torvalds 
4351badebdSGautham R Shenoy 	cpu_hotplug_driver_lock();
441da177e4SLinus Torvalds 	switch (buf[0]) {
451da177e4SLinus Torvalds 	case '0':
468a25a2fdSKay Sievers 		ret = cpu_down(cpu->dev.id);
471da177e4SLinus Torvalds 		if (!ret)
48312c004dSKay Sievers 			kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
491da177e4SLinus Torvalds 		break;
501da177e4SLinus Torvalds 	case '1':
518a25a2fdSKay Sievers 		ret = cpu_up(cpu->dev.id);
52fb69c390SNathan Lynch 		if (!ret)
53312c004dSKay Sievers 			kobject_uevent(&dev->kobj, KOBJ_ONLINE);
541da177e4SLinus Torvalds 		break;
551da177e4SLinus Torvalds 	default:
561da177e4SLinus Torvalds 		ret = -EINVAL;
571da177e4SLinus Torvalds 	}
5851badebdSGautham R Shenoy 	cpu_hotplug_driver_unlock();
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	if (ret >= 0)
611da177e4SLinus Torvalds 		ret = count;
621da177e4SLinus Torvalds 	return ret;
631da177e4SLinus Torvalds }
648a25a2fdSKay Sievers static DEVICE_ATTR(online, 0644, show_online, store_online);
651da177e4SLinus Torvalds 
666c847402SSam Ravnborg static void __cpuinit register_cpu_control(struct cpu *cpu)
671da177e4SLinus Torvalds {
688a25a2fdSKay Sievers 	device_create_file(&cpu->dev, &dev_attr_online);
691da177e4SLinus Torvalds }
7076b67ed9SKAMEZAWA Hiroyuki void unregister_cpu(struct cpu *cpu)
711da177e4SLinus Torvalds {
728a25a2fdSKay Sievers 	int logical_cpu = cpu->dev.id;
731da177e4SLinus Torvalds 
7476b67ed9SKAMEZAWA Hiroyuki 	unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
7576b67ed9SKAMEZAWA Hiroyuki 
768a25a2fdSKay Sievers 	device_remove_file(&cpu->dev, &dev_attr_online);
771da177e4SLinus Torvalds 
788a25a2fdSKay Sievers 	device_unregister(&cpu->dev);
79e37d05daSMike Travis 	per_cpu(cpu_sys_devices, logical_cpu) = NULL;
801da177e4SLinus Torvalds 	return;
811da177e4SLinus Torvalds }
8212633e80SNathan Fontenot 
8312633e80SNathan Fontenot #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
848a25a2fdSKay Sievers static ssize_t cpu_probe_store(struct device *dev,
858a25a2fdSKay Sievers 			       struct device_attribute *attr,
8628812fe1SAndi Kleen 			       const char *buf,
8712633e80SNathan Fontenot 			       size_t count)
8812633e80SNathan Fontenot {
8912633e80SNathan Fontenot 	return arch_cpu_probe(buf, count);
9012633e80SNathan Fontenot }
9112633e80SNathan Fontenot 
928a25a2fdSKay Sievers static ssize_t cpu_release_store(struct device *dev,
938a25a2fdSKay Sievers 				 struct device_attribute *attr,
9428812fe1SAndi Kleen 				 const char *buf,
9512633e80SNathan Fontenot 				 size_t count)
9612633e80SNathan Fontenot {
9712633e80SNathan Fontenot 	return arch_cpu_release(buf, count);
9812633e80SNathan Fontenot }
9912633e80SNathan Fontenot 
1008a25a2fdSKay Sievers static DEVICE_ATTR(probe, S_IWUSR, NULL, cpu_probe_store);
1018a25a2fdSKay Sievers static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store);
10212633e80SNathan Fontenot #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
10312633e80SNathan Fontenot 
1041da177e4SLinus Torvalds #else /* ... !CONFIG_HOTPLUG_CPU */
1051da177e4SLinus Torvalds static inline void register_cpu_control(struct cpu *cpu)
1061da177e4SLinus Torvalds {
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds #endif /* CONFIG_HOTPLUG_CPU */
1091da177e4SLinus Torvalds 
11051be5606SVivek Goyal #ifdef CONFIG_KEXEC
11151be5606SVivek Goyal #include <linux/kexec.h>
11251be5606SVivek Goyal 
1138a25a2fdSKay Sievers static ssize_t show_crash_notes(struct device *dev, struct device_attribute *attr,
1144a0b2b4dSAndi Kleen 				char *buf)
11551be5606SVivek Goyal {
1168a25a2fdSKay Sievers 	struct cpu *cpu = container_of(dev, struct cpu, dev);
11751be5606SVivek Goyal 	ssize_t rc;
11851be5606SVivek Goyal 	unsigned long long addr;
11951be5606SVivek Goyal 	int cpunum;
12051be5606SVivek Goyal 
1218a25a2fdSKay Sievers 	cpunum = cpu->dev.id;
12251be5606SVivek Goyal 
12351be5606SVivek Goyal 	/*
12451be5606SVivek Goyal 	 * Might be reading other cpu's data based on which cpu read thread
12551be5606SVivek Goyal 	 * has been scheduled. But cpu data (memory) is allocated once during
12651be5606SVivek Goyal 	 * boot up and this data does not change there after. Hence this
12751be5606SVivek Goyal 	 * operation should be safe. No locking required.
12851be5606SVivek Goyal 	 */
1293b034b0dSVivek Goyal 	addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpunum));
13051be5606SVivek Goyal 	rc = sprintf(buf, "%Lx\n", addr);
13151be5606SVivek Goyal 	return rc;
13251be5606SVivek Goyal }
1338a25a2fdSKay Sievers static DEVICE_ATTR(crash_notes, 0400, show_crash_notes, NULL);
13451be5606SVivek Goyal #endif
13551be5606SVivek Goyal 
1361da177e4SLinus Torvalds /*
1379d1fe323SMike Travis  * Print cpu online, possible, present, and system maps
1389d1fe323SMike Travis  */
139265d2e2eSAndi Kleen 
140265d2e2eSAndi Kleen struct cpu_attr {
1418a25a2fdSKay Sievers 	struct device_attribute attr;
142265d2e2eSAndi Kleen 	const struct cpumask *const * const map;
143265d2e2eSAndi Kleen };
144265d2e2eSAndi Kleen 
1458a25a2fdSKay Sievers static ssize_t show_cpus_attr(struct device *dev,
1468a25a2fdSKay Sievers 			      struct device_attribute *attr,
147265d2e2eSAndi Kleen 			      char *buf)
1489d1fe323SMike Travis {
149265d2e2eSAndi Kleen 	struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr);
150265d2e2eSAndi Kleen 	int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *(ca->map));
1519d1fe323SMike Travis 
1529d1fe323SMike Travis 	buf[n++] = '\n';
1539d1fe323SMike Travis 	buf[n] = '\0';
1549d1fe323SMike Travis 	return n;
1559d1fe323SMike Travis }
1569d1fe323SMike Travis 
157265d2e2eSAndi Kleen #define _CPU_ATTR(name, map) \
1588a25a2fdSKay Sievers 	{ __ATTR(name, 0444, show_cpus_attr, NULL), map }
1599d1fe323SMike Travis 
1608a25a2fdSKay Sievers /* Keep in sync with cpu_subsys_attrs */
161265d2e2eSAndi Kleen static struct cpu_attr cpu_attrs[] = {
162265d2e2eSAndi Kleen 	_CPU_ATTR(online, &cpu_online_mask),
163265d2e2eSAndi Kleen 	_CPU_ATTR(possible, &cpu_possible_mask),
164265d2e2eSAndi Kleen 	_CPU_ATTR(present, &cpu_present_mask),
165265d2e2eSAndi Kleen };
1669d1fe323SMike Travis 
167e057d7aeSMike Travis /*
168e057d7aeSMike Travis  * Print values for NR_CPUS and offlined cpus
169e057d7aeSMike Travis  */
1708a25a2fdSKay Sievers static ssize_t print_cpus_kernel_max(struct device *dev,
1718a25a2fdSKay Sievers 				     struct device_attribute *attr, char *buf)
172e057d7aeSMike Travis {
1738fd2d2d5SMike Travis 	int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1);
174e057d7aeSMike Travis 	return n;
175e057d7aeSMike Travis }
1768a25a2fdSKay Sievers static DEVICE_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
177e057d7aeSMike Travis 
178e057d7aeSMike Travis /* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */
179e057d7aeSMike Travis unsigned int total_cpus;
180e057d7aeSMike Travis 
1818a25a2fdSKay Sievers static ssize_t print_cpus_offline(struct device *dev,
1828a25a2fdSKay Sievers 				  struct device_attribute *attr, char *buf)
183e057d7aeSMike Travis {
184e057d7aeSMike Travis 	int n = 0, len = PAGE_SIZE-2;
185e057d7aeSMike Travis 	cpumask_var_t offline;
186e057d7aeSMike Travis 
187e057d7aeSMike Travis 	/* display offline cpus < nr_cpu_ids */
188e057d7aeSMike Travis 	if (!alloc_cpumask_var(&offline, GFP_KERNEL))
189e057d7aeSMike Travis 		return -ENOMEM;
190cdc6e3d3SJan Beulich 	cpumask_andnot(offline, cpu_possible_mask, cpu_online_mask);
191e057d7aeSMike Travis 	n = cpulist_scnprintf(buf, len, offline);
192e057d7aeSMike Travis 	free_cpumask_var(offline);
193e057d7aeSMike Travis 
194e057d7aeSMike Travis 	/* display offline cpus >= nr_cpu_ids */
195e057d7aeSMike Travis 	if (total_cpus && nr_cpu_ids < total_cpus) {
196e057d7aeSMike Travis 		if (n && n < len)
197e057d7aeSMike Travis 			buf[n++] = ',';
198e057d7aeSMike Travis 
199e057d7aeSMike Travis 		if (nr_cpu_ids == total_cpus-1)
200e057d7aeSMike Travis 			n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids);
201e057d7aeSMike Travis 		else
202e057d7aeSMike Travis 			n += snprintf(&buf[n], len - n, "%d-%d",
203e057d7aeSMike Travis 						      nr_cpu_ids, total_cpus-1);
204e057d7aeSMike Travis 	}
205e057d7aeSMike Travis 
206e057d7aeSMike Travis 	n += snprintf(&buf[n], len - n, "\n");
207e057d7aeSMike Travis 	return n;
208e057d7aeSMike Travis }
2098a25a2fdSKay Sievers static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL);
210e057d7aeSMike Travis 
2119d1fe323SMike Travis /*
212405ae7d3SRobert P. J. Day  * register_cpu - Setup a sysfs device for a CPU.
21372486f1fSSiddha, Suresh B  * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
21472486f1fSSiddha, Suresh B  *	  sysfs for this CPU.
2151da177e4SLinus Torvalds  * @num - CPU number to use when creating the device.
2161da177e4SLinus Torvalds  *
2171da177e4SLinus Torvalds  * Initialize and register the CPU device.
2181da177e4SLinus Torvalds  */
21933b5f31bSRandy Dunlap int __cpuinit register_cpu(struct cpu *cpu, int num)
2201da177e4SLinus Torvalds {
2211da177e4SLinus Torvalds 	int error;
2228a25a2fdSKay Sievers 
2231da177e4SLinus Torvalds 	cpu->node_id = cpu_to_node(num);
2248a25a2fdSKay Sievers 	cpu->dev.id = num;
2258a25a2fdSKay Sievers 	cpu->dev.bus = &cpu_subsys;
2268a25a2fdSKay Sievers 	error = device_register(&cpu->dev);
22772486f1fSSiddha, Suresh B 	if (!error && cpu->hotpluggable)
2281da177e4SLinus Torvalds 		register_cpu_control(cpu);
229ad74557aSAshok Raj 	if (!error)
2308a25a2fdSKay Sievers 		per_cpu(cpu_sys_devices, num) = &cpu->dev;
23176b67ed9SKAMEZAWA Hiroyuki 	if (!error)
23276b67ed9SKAMEZAWA Hiroyuki 		register_cpu_under_node(num, cpu_to_node(num));
23351be5606SVivek Goyal 
23451be5606SVivek Goyal #ifdef CONFIG_KEXEC
23551be5606SVivek Goyal 	if (!error)
2368a25a2fdSKay Sievers 		error = device_create_file(&cpu->dev, &dev_attr_crash_notes);
23751be5606SVivek Goyal #endif
2381da177e4SLinus Torvalds 	return error;
2391da177e4SLinus Torvalds }
2401da177e4SLinus Torvalds 
2418a25a2fdSKay Sievers struct device *get_cpu_device(unsigned cpu)
242ad74557aSAshok Raj {
243e37d05daSMike Travis 	if (cpu < nr_cpu_ids && cpu_possible(cpu))
244e37d05daSMike Travis 		return per_cpu(cpu_sys_devices, cpu);
245ad74557aSAshok Raj 	else
246ad74557aSAshok Raj 		return NULL;
247ad74557aSAshok Raj }
2488a25a2fdSKay Sievers EXPORT_SYMBOL_GPL(get_cpu_device);
2498a25a2fdSKay Sievers 
2508a25a2fdSKay Sievers static struct attribute *cpu_root_attrs[] = {
2518a25a2fdSKay Sievers #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
2528a25a2fdSKay Sievers 	&dev_attr_probe.attr,
2538a25a2fdSKay Sievers 	&dev_attr_release.attr,
2548a25a2fdSKay Sievers #endif
2558a25a2fdSKay Sievers 	&cpu_attrs[0].attr.attr,
2568a25a2fdSKay Sievers 	&cpu_attrs[1].attr.attr,
2578a25a2fdSKay Sievers 	&cpu_attrs[2].attr.attr,
2588a25a2fdSKay Sievers 	&dev_attr_kernel_max.attr,
2598a25a2fdSKay Sievers 	&dev_attr_offline.attr,
2608a25a2fdSKay Sievers 	NULL
2618a25a2fdSKay Sievers };
2628a25a2fdSKay Sievers 
2638a25a2fdSKay Sievers static struct attribute_group cpu_root_attr_group = {
2648a25a2fdSKay Sievers 	.attrs = cpu_root_attrs,
2658a25a2fdSKay Sievers };
2668a25a2fdSKay Sievers 
2678a25a2fdSKay Sievers static const struct attribute_group *cpu_root_attr_groups[] = {
2688a25a2fdSKay Sievers 	&cpu_root_attr_group,
2698a25a2fdSKay Sievers 	NULL,
2708a25a2fdSKay Sievers };
2711da177e4SLinus Torvalds 
2722987557fSJosh Triplett bool cpu_is_hotpluggable(unsigned cpu)
2732987557fSJosh Triplett {
2747affca35SLinus Torvalds 	struct device *dev = get_cpu_device(cpu);
2757affca35SLinus Torvalds 	return dev && container_of(dev, struct cpu, dev)->hotpluggable;
2762987557fSJosh Triplett }
2772987557fSJosh Triplett EXPORT_SYMBOL_GPL(cpu_is_hotpluggable);
2782987557fSJosh Triplett 
2799f13a1fdSBen Hutchings #ifdef CONFIG_GENERIC_CPU_DEVICES
2809f13a1fdSBen Hutchings static DEFINE_PER_CPU(struct cpu, cpu_devices);
2819f13a1fdSBen Hutchings #endif
2829f13a1fdSBen Hutchings 
2839f13a1fdSBen Hutchings static void __init cpu_dev_register_generic(void)
2849f13a1fdSBen Hutchings {
2859f13a1fdSBen Hutchings #ifdef CONFIG_GENERIC_CPU_DEVICES
2869f13a1fdSBen Hutchings 	int i;
2879f13a1fdSBen Hutchings 
2889f13a1fdSBen Hutchings 	for_each_possible_cpu(i) {
2899f13a1fdSBen Hutchings 		if (register_cpu(&per_cpu(cpu_devices, i), i))
2909f13a1fdSBen Hutchings 			panic("Failed to register CPU device");
2919f13a1fdSBen Hutchings 	}
2929f13a1fdSBen Hutchings #endif
2939f13a1fdSBen Hutchings }
2949f13a1fdSBen Hutchings 
295024f7846SBen Hutchings void __init cpu_dev_init(void)
2961da177e4SLinus Torvalds {
297024f7846SBen Hutchings 	if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
298024f7846SBen Hutchings 		panic("Failed to register CPU subsystem");
2995c45bf27SSiddha, Suresh B 
3009f13a1fdSBen Hutchings 	cpu_dev_register_generic();
3019f13a1fdSBen Hutchings 
3028a25a2fdSKay Sievers #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
303024f7846SBen Hutchings 	sched_create_sysfs_power_savings_entries(cpu_subsys.dev_root);
3048a25a2fdSKay Sievers #endif
3051da177e4SLinus Torvalds }
306