cpu.c 4.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7
/*
 * drivers/base/cpu.c - basic CPU class support
 */

#include <linux/sysdev.h>
#include <linux/module.h>
#include <linux/init.h>
A
Al Viro 已提交
8
#include <linux/sched.h>
L
Linus Torvalds 已提交
9 10 11
#include <linux/cpu.h>
#include <linux/topology.h>
#include <linux/device.h>
12
#include <linux/node.h>
L
Linus Torvalds 已提交
13

14
#include "base.h"
L
Linus Torvalds 已提交
15 16

struct sysdev_class cpu_sysdev_class = {
17
	.name = "cpu",
L
Linus Torvalds 已提交
18 19 20
};
EXPORT_SYMBOL(cpu_sysdev_class);

21
static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices);
22

L
Linus Torvalds 已提交
23
#ifdef CONFIG_HOTPLUG_CPU
24 25
static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr,
			   char *buf)
L
Linus Torvalds 已提交
26 27 28 29 30 31
{
	struct cpu *cpu = container_of(dev, struct cpu, sysdev);

	return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id));
}

32 33
static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr,
				 const char *buf, size_t count)
L
Linus Torvalds 已提交
34 35 36 37 38 39 40 41
{
	struct cpu *cpu = container_of(dev, struct cpu, sysdev);
	ssize_t ret;

	switch (buf[0]) {
	case '0':
		ret = cpu_down(cpu->sysdev.id);
		if (!ret)
42
			kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
L
Linus Torvalds 已提交
43 44
		break;
	case '1':
45
		ret = cpu_up(cpu->sysdev.id);
46
		if (!ret)
47
			kobject_uevent(&dev->kobj, KOBJ_ONLINE);
L
Linus Torvalds 已提交
48 49 50 51 52 53 54 55 56
		break;
	default:
		ret = -EINVAL;
	}

	if (ret >= 0)
		ret = count;
	return ret;
}
U
Ulrich Drepper 已提交
57
static SYSDEV_ATTR(online, 0644, show_online, store_online);
L
Linus Torvalds 已提交
58

59
static void __cpuinit register_cpu_control(struct cpu *cpu)
L
Linus Torvalds 已提交
60 61 62
{
	sysdev_create_file(&cpu->sysdev, &attr_online);
}
63
void unregister_cpu(struct cpu *cpu)
L
Linus Torvalds 已提交
64
{
65
	int logical_cpu = cpu->sysdev.id;
L
Linus Torvalds 已提交
66

67 68
	unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));

L
Linus Torvalds 已提交
69 70 71
	sysdev_remove_file(&cpu->sysdev, &attr_online);

	sysdev_unregister(&cpu->sysdev);
72
	per_cpu(cpu_sys_devices, logical_cpu) = NULL;
L
Linus Torvalds 已提交
73 74 75 76 77 78 79 80
	return;
}
#else /* ... !CONFIG_HOTPLUG_CPU */
static inline void register_cpu_control(struct cpu *cpu)
{
}
#endif /* CONFIG_HOTPLUG_CPU */

81 82 83
#ifdef CONFIG_KEXEC
#include <linux/kexec.h>

84 85
static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr,
				char *buf)
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
{
	struct cpu *cpu = container_of(dev, struct cpu, sysdev);
	ssize_t rc;
	unsigned long long addr;
	int cpunum;

	cpunum = cpu->sysdev.id;

	/*
	 * Might be reading other cpu's data based on which cpu read thread
	 * has been scheduled. But cpu data (memory) is allocated once during
	 * boot up and this data does not change there after. Hence this
	 * operation should be safe. No locking required.
	 */
	addr = __pa(per_cpu_ptr(crash_notes, cpunum));
	rc = sprintf(buf, "%Lx\n", addr);
	return rc;
}
static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL);
#endif

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
/*
 * Print cpu online, possible, present, and system maps
 */
static ssize_t print_cpus_map(char *buf, cpumask_t *map)
{
	int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *map);

	buf[n++] = '\n';
	buf[n] = '\0';
	return n;
}

#define	print_cpus_func(type) \
static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf)	\
{									\
	return print_cpus_map(buf, &cpu_##type##_map);			\
}									\
struct sysdev_class_attribute attr_##type##_map = 			\
	_SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL)

print_cpus_func(online);
print_cpus_func(possible);
print_cpus_func(present);

struct sysdev_class_attribute *cpu_state_attr[] = {
	&attr_online_map,
	&attr_possible_map,
	&attr_present_map,
};

static int cpu_states_init(void)
{
	int i;
	int err = 0;

	for (i = 0;  i < ARRAY_SIZE(cpu_state_attr); i++) {
		int ret;
		ret = sysdev_class_create_file(&cpu_sysdev_class,
						cpu_state_attr[i]);
		if (!err)
			err = ret;
	}
	return err;
}

L
Linus Torvalds 已提交
152
/*
153
 * register_cpu - Setup a sysfs device for a CPU.
154 155
 * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
 *	  sysfs for this CPU.
L
Linus Torvalds 已提交
156 157 158 159
 * @num - CPU number to use when creating the device.
 *
 * Initialize and register the CPU device.
 */
160
int __cpuinit register_cpu(struct cpu *cpu, int num)
L
Linus Torvalds 已提交
161 162 163 164 165 166 167
{
	int error;
	cpu->node_id = cpu_to_node(num);
	cpu->sysdev.id = num;
	cpu->sysdev.cls = &cpu_sysdev_class;

	error = sysdev_register(&cpu->sysdev);
168

169
	if (!error && cpu->hotpluggable)
L
Linus Torvalds 已提交
170
		register_cpu_control(cpu);
171
	if (!error)
172
		per_cpu(cpu_sys_devices, num) = &cpu->sysdev;
173 174
	if (!error)
		register_cpu_under_node(num, cpu_to_node(num));
175 176 177 178 179

#ifdef CONFIG_KEXEC
	if (!error)
		error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes);
#endif
L
Linus Torvalds 已提交
180 181 182
	return error;
}

183
struct sys_device *get_cpu_sysdev(unsigned cpu)
184
{
185 186
	if (cpu < nr_cpu_ids && cpu_possible(cpu))
		return per_cpu(cpu_sys_devices, cpu);
187 188 189 190
	else
		return NULL;
}
EXPORT_SYMBOL_GPL(get_cpu_sysdev);
L
Linus Torvalds 已提交
191 192 193

int __init cpu_dev_init(void)
{
194 195 196
	int err;

	err = sysdev_class_register(&cpu_sysdev_class);
197 198 199
	if (!err)
		err = cpu_states_init();

200 201 202 203 204 205
#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
	if (!err)
		err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
#endif

	return err;
L
Linus Torvalds 已提交
206
}