xref: /openbmc/linux/drivers/base/cpu.c (revision 8fd2d2d5)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * drivers/base/cpu.c - basic CPU class support
31da177e4SLinus Torvalds  */
41da177e4SLinus Torvalds 
51da177e4SLinus Torvalds #include <linux/sysdev.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>
131da177e4SLinus Torvalds 
14a1bdc7aaSBen Dooks #include "base.h"
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds struct sysdev_class cpu_sysdev_class = {
17af5ca3f4SKay Sievers 	.name = "cpu",
181da177e4SLinus Torvalds };
191da177e4SLinus Torvalds EXPORT_SYMBOL(cpu_sysdev_class);
201da177e4SLinus Torvalds 
21e37d05daSMike Travis static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices);
22ad74557aSAshok Raj 
231da177e4SLinus Torvalds #ifdef CONFIG_HOTPLUG_CPU
244a0b2b4dSAndi Kleen static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr,
254a0b2b4dSAndi Kleen 			   char *buf)
261da177e4SLinus Torvalds {
271da177e4SLinus Torvalds 	struct cpu *cpu = container_of(dev, struct cpu, sysdev);
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds 	return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id));
301da177e4SLinus Torvalds }
311da177e4SLinus Torvalds 
324a0b2b4dSAndi Kleen static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr,
334a0b2b4dSAndi Kleen 				 const char *buf, size_t count)
341da177e4SLinus Torvalds {
351da177e4SLinus Torvalds 	struct cpu *cpu = container_of(dev, struct cpu, sysdev);
361da177e4SLinus Torvalds 	ssize_t ret;
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds 	switch (buf[0]) {
391da177e4SLinus Torvalds 	case '0':
401da177e4SLinus Torvalds 		ret = cpu_down(cpu->sysdev.id);
411da177e4SLinus Torvalds 		if (!ret)
42312c004dSKay Sievers 			kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
431da177e4SLinus Torvalds 		break;
441da177e4SLinus Torvalds 	case '1':
451da177e4SLinus Torvalds 		ret = cpu_up(cpu->sysdev.id);
46fb69c390SNathan Lynch 		if (!ret)
47312c004dSKay Sievers 			kobject_uevent(&dev->kobj, KOBJ_ONLINE);
481da177e4SLinus Torvalds 		break;
491da177e4SLinus Torvalds 	default:
501da177e4SLinus Torvalds 		ret = -EINVAL;
511da177e4SLinus Torvalds 	}
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds 	if (ret >= 0)
541da177e4SLinus Torvalds 		ret = count;
551da177e4SLinus Torvalds 	return ret;
561da177e4SLinus Torvalds }
579eb3ff40SUlrich Drepper static SYSDEV_ATTR(online, 0644, show_online, store_online);
581da177e4SLinus Torvalds 
596c847402SSam Ravnborg static void __cpuinit register_cpu_control(struct cpu *cpu)
601da177e4SLinus Torvalds {
611da177e4SLinus Torvalds 	sysdev_create_file(&cpu->sysdev, &attr_online);
621da177e4SLinus Torvalds }
6376b67ed9SKAMEZAWA Hiroyuki void unregister_cpu(struct cpu *cpu)
641da177e4SLinus Torvalds {
65ad74557aSAshok Raj 	int logical_cpu = cpu->sysdev.id;
661da177e4SLinus Torvalds 
6776b67ed9SKAMEZAWA Hiroyuki 	unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
6876b67ed9SKAMEZAWA Hiroyuki 
691da177e4SLinus Torvalds 	sysdev_remove_file(&cpu->sysdev, &attr_online);
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	sysdev_unregister(&cpu->sysdev);
72e37d05daSMike Travis 	per_cpu(cpu_sys_devices, logical_cpu) = NULL;
731da177e4SLinus Torvalds 	return;
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds #else /* ... !CONFIG_HOTPLUG_CPU */
761da177e4SLinus Torvalds static inline void register_cpu_control(struct cpu *cpu)
771da177e4SLinus Torvalds {
781da177e4SLinus Torvalds }
791da177e4SLinus Torvalds #endif /* CONFIG_HOTPLUG_CPU */
801da177e4SLinus Torvalds 
8151be5606SVivek Goyal #ifdef CONFIG_KEXEC
8251be5606SVivek Goyal #include <linux/kexec.h>
8351be5606SVivek Goyal 
844a0b2b4dSAndi Kleen static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr,
854a0b2b4dSAndi Kleen 				char *buf)
8651be5606SVivek Goyal {
8751be5606SVivek Goyal 	struct cpu *cpu = container_of(dev, struct cpu, sysdev);
8851be5606SVivek Goyal 	ssize_t rc;
8951be5606SVivek Goyal 	unsigned long long addr;
9051be5606SVivek Goyal 	int cpunum;
9151be5606SVivek Goyal 
9251be5606SVivek Goyal 	cpunum = cpu->sysdev.id;
9351be5606SVivek Goyal 
9451be5606SVivek Goyal 	/*
9551be5606SVivek Goyal 	 * Might be reading other cpu's data based on which cpu read thread
9651be5606SVivek Goyal 	 * has been scheduled. But cpu data (memory) is allocated once during
9751be5606SVivek Goyal 	 * boot up and this data does not change there after. Hence this
9851be5606SVivek Goyal 	 * operation should be safe. No locking required.
9951be5606SVivek Goyal 	 */
10051be5606SVivek Goyal 	addr = __pa(per_cpu_ptr(crash_notes, cpunum));
10151be5606SVivek Goyal 	rc = sprintf(buf, "%Lx\n", addr);
10251be5606SVivek Goyal 	return rc;
10351be5606SVivek Goyal }
10451be5606SVivek Goyal static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL);
10551be5606SVivek Goyal #endif
10651be5606SVivek Goyal 
1071da177e4SLinus Torvalds /*
1089d1fe323SMike Travis  * Print cpu online, possible, present, and system maps
1099d1fe323SMike Travis  */
1109d1fe323SMike Travis static ssize_t print_cpus_map(char *buf, cpumask_t *map)
1119d1fe323SMike Travis {
11229c0177eSRusty Russell 	int n = cpulist_scnprintf(buf, PAGE_SIZE-2, map);
1139d1fe323SMike Travis 
1149d1fe323SMike Travis 	buf[n++] = '\n';
1159d1fe323SMike Travis 	buf[n] = '\0';
1169d1fe323SMike Travis 	return n;
1179d1fe323SMike Travis }
1189d1fe323SMike Travis 
1199d1fe323SMike Travis #define	print_cpus_func(type) \
1209d1fe323SMike Travis static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf)	\
1219d1fe323SMike Travis {									\
1229d1fe323SMike Travis 	return print_cpus_map(buf, &cpu_##type##_map);			\
1239d1fe323SMike Travis }									\
124143aa5c5SMike Travis static struct sysdev_class_attribute attr_##type##_map = 		\
1259d1fe323SMike Travis 	_SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL)
1269d1fe323SMike Travis 
1279d1fe323SMike Travis print_cpus_func(online);
1289d1fe323SMike Travis print_cpus_func(possible);
1299d1fe323SMike Travis print_cpus_func(present);
1309d1fe323SMike Travis 
131e057d7aeSMike Travis /*
132e057d7aeSMike Travis  * Print values for NR_CPUS and offlined cpus
133e057d7aeSMike Travis  */
134e057d7aeSMike Travis static ssize_t print_cpus_kernel_max(struct sysdev_class *class, char *buf)
135e057d7aeSMike Travis {
1368fd2d2d5SMike Travis 	int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1);
137e057d7aeSMike Travis 	return n;
138e057d7aeSMike Travis }
139e057d7aeSMike Travis static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
140e057d7aeSMike Travis 
141e057d7aeSMike Travis /* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */
142e057d7aeSMike Travis unsigned int total_cpus;
143e057d7aeSMike Travis 
144e057d7aeSMike Travis static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf)
145e057d7aeSMike Travis {
146e057d7aeSMike Travis 	int n = 0, len = PAGE_SIZE-2;
147e057d7aeSMike Travis 	cpumask_var_t offline;
148e057d7aeSMike Travis 
149e057d7aeSMike Travis 	/* display offline cpus < nr_cpu_ids */
150e057d7aeSMike Travis 	if (!alloc_cpumask_var(&offline, GFP_KERNEL))
151e057d7aeSMike Travis 		return -ENOMEM;
152e057d7aeSMike Travis 	cpumask_complement(offline, cpu_online_mask);
153e057d7aeSMike Travis 	n = cpulist_scnprintf(buf, len, offline);
154e057d7aeSMike Travis 	free_cpumask_var(offline);
155e057d7aeSMike Travis 
156e057d7aeSMike Travis 	/* display offline cpus >= nr_cpu_ids */
157e057d7aeSMike Travis 	if (total_cpus && nr_cpu_ids < total_cpus) {
158e057d7aeSMike Travis 		if (n && n < len)
159e057d7aeSMike Travis 			buf[n++] = ',';
160e057d7aeSMike Travis 
161e057d7aeSMike Travis 		if (nr_cpu_ids == total_cpus-1)
162e057d7aeSMike Travis 			n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids);
163e057d7aeSMike Travis 		else
164e057d7aeSMike Travis 			n += snprintf(&buf[n], len - n, "%d-%d",
165e057d7aeSMike Travis 						      nr_cpu_ids, total_cpus-1);
166e057d7aeSMike Travis 	}
167e057d7aeSMike Travis 
168e057d7aeSMike Travis 	n += snprintf(&buf[n], len - n, "\n");
169e057d7aeSMike Travis 	return n;
170e057d7aeSMike Travis }
171e057d7aeSMike Travis static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL);
172e057d7aeSMike Travis 
173143aa5c5SMike Travis static struct sysdev_class_attribute *cpu_state_attr[] = {
1749d1fe323SMike Travis 	&attr_online_map,
1759d1fe323SMike Travis 	&attr_possible_map,
1769d1fe323SMike Travis 	&attr_present_map,
177e057d7aeSMike Travis 	&attr_kernel_max,
178e057d7aeSMike Travis 	&attr_offline,
1799d1fe323SMike Travis };
1809d1fe323SMike Travis 
1819d1fe323SMike Travis static int cpu_states_init(void)
1829d1fe323SMike Travis {
1839d1fe323SMike Travis 	int i;
1849d1fe323SMike Travis 	int err = 0;
1859d1fe323SMike Travis 
1869d1fe323SMike Travis 	for (i = 0;  i < ARRAY_SIZE(cpu_state_attr); i++) {
1879d1fe323SMike Travis 		int ret;
1889d1fe323SMike Travis 		ret = sysdev_class_create_file(&cpu_sysdev_class,
1899d1fe323SMike Travis 						cpu_state_attr[i]);
1909d1fe323SMike Travis 		if (!err)
1919d1fe323SMike Travis 			err = ret;
1929d1fe323SMike Travis 	}
1939d1fe323SMike Travis 	return err;
1949d1fe323SMike Travis }
1959d1fe323SMike Travis 
1969d1fe323SMike Travis /*
197405ae7d3SRobert P. J. Day  * register_cpu - Setup a sysfs device for a CPU.
19872486f1fSSiddha, Suresh B  * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
19972486f1fSSiddha, Suresh B  *	  sysfs for this CPU.
2001da177e4SLinus Torvalds  * @num - CPU number to use when creating the device.
2011da177e4SLinus Torvalds  *
2021da177e4SLinus Torvalds  * Initialize and register the CPU device.
2031da177e4SLinus Torvalds  */
20433b5f31bSRandy Dunlap int __cpuinit register_cpu(struct cpu *cpu, int num)
2051da177e4SLinus Torvalds {
2061da177e4SLinus Torvalds 	int error;
2071da177e4SLinus Torvalds 	cpu->node_id = cpu_to_node(num);
2081da177e4SLinus Torvalds 	cpu->sysdev.id = num;
2091da177e4SLinus Torvalds 	cpu->sysdev.cls = &cpu_sysdev_class;
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	error = sysdev_register(&cpu->sysdev);
21276b67ed9SKAMEZAWA Hiroyuki 
21372486f1fSSiddha, Suresh B 	if (!error && cpu->hotpluggable)
2141da177e4SLinus Torvalds 		register_cpu_control(cpu);
215ad74557aSAshok Raj 	if (!error)
216e37d05daSMike Travis 		per_cpu(cpu_sys_devices, num) = &cpu->sysdev;
21776b67ed9SKAMEZAWA Hiroyuki 	if (!error)
21876b67ed9SKAMEZAWA Hiroyuki 		register_cpu_under_node(num, cpu_to_node(num));
21951be5606SVivek Goyal 
22051be5606SVivek Goyal #ifdef CONFIG_KEXEC
22151be5606SVivek Goyal 	if (!error)
22251be5606SVivek Goyal 		error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes);
22351be5606SVivek Goyal #endif
2241da177e4SLinus Torvalds 	return error;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
227a29d642aSAndrew Morton struct sys_device *get_cpu_sysdev(unsigned cpu)
228ad74557aSAshok Raj {
229e37d05daSMike Travis 	if (cpu < nr_cpu_ids && cpu_possible(cpu))
230e37d05daSMike Travis 		return per_cpu(cpu_sys_devices, cpu);
231ad74557aSAshok Raj 	else
232ad74557aSAshok Raj 		return NULL;
233ad74557aSAshok Raj }
234ad74557aSAshok Raj EXPORT_SYMBOL_GPL(get_cpu_sysdev);
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds int __init cpu_dev_init(void)
2371da177e4SLinus Torvalds {
2385c45bf27SSiddha, Suresh B 	int err;
2395c45bf27SSiddha, Suresh B 
2405c45bf27SSiddha, Suresh B 	err = sysdev_class_register(&cpu_sysdev_class);
2419d1fe323SMike Travis 	if (!err)
2429d1fe323SMike Travis 		err = cpu_states_init();
2439d1fe323SMike Travis 
2445c45bf27SSiddha, Suresh B #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
2455c45bf27SSiddha, Suresh B 	if (!err)
2465c45bf27SSiddha, Suresh B 		err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
2475c45bf27SSiddha, Suresh B #endif
2485c45bf27SSiddha, Suresh B 
2495c45bf27SSiddha, Suresh B 	return err;
2501da177e4SLinus Torvalds }
251