cpu.c 5.8 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
/*
 * Print cpu online, possible, present, and system maps
 */
110
static ssize_t print_cpus_map(char *buf, const struct cpumask *map)
111
{
112
	int n = cpulist_scnprintf(buf, PAGE_SIZE-2, map);
113 114 115 116 117 118 119 120 121

	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)	\
{									\
122
	return print_cpus_map(buf, cpu_##type##_mask);			\
123
}									\
124
static struct sysdev_class_attribute attr_##type##_map = 		\
125 126 127 128 129 130
	_SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL)

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

131 132 133 134 135
/*
 * Print values for NR_CPUS and offlined cpus
 */
static ssize_t print_cpus_kernel_max(struct sysdev_class *class, char *buf)
{
136
	int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1);
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
	return n;
}
static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);

/* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */
unsigned int total_cpus;

static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf)
{
	int n = 0, len = PAGE_SIZE-2;
	cpumask_var_t offline;

	/* display offline cpus < nr_cpu_ids */
	if (!alloc_cpumask_var(&offline, GFP_KERNEL))
		return -ENOMEM;
	cpumask_complement(offline, cpu_online_mask);
	n = cpulist_scnprintf(buf, len, offline);
	free_cpumask_var(offline);

	/* display offline cpus >= nr_cpu_ids */
	if (total_cpus && nr_cpu_ids < total_cpus) {
		if (n && n < len)
			buf[n++] = ',';

		if (nr_cpu_ids == total_cpus-1)
			n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids);
		else
			n += snprintf(&buf[n], len - n, "%d-%d",
						      nr_cpu_ids, total_cpus-1);
	}

	n += snprintf(&buf[n], len - n, "\n");
	return n;
}
static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL);

173
static struct sysdev_class_attribute *cpu_state_attr[] = {
174 175 176
	&attr_online_map,
	&attr_possible_map,
	&attr_present_map,
177 178
	&attr_kernel_max,
	&attr_offline,
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
};

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 已提交
196
/*
197
 * register_cpu - Setup a sysfs device for a CPU.
198 199
 * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
 *	  sysfs for this CPU.
L
Linus Torvalds 已提交
200 201 202 203
 * @num - CPU number to use when creating the device.
 *
 * Initialize and register the CPU device.
 */
204
int __cpuinit register_cpu(struct cpu *cpu, int num)
L
Linus Torvalds 已提交
205 206 207 208 209 210 211
{
	int error;
	cpu->node_id = cpu_to_node(num);
	cpu->sysdev.id = num;
	cpu->sysdev.cls = &cpu_sysdev_class;

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

213
	if (!error && cpu->hotpluggable)
L
Linus Torvalds 已提交
214
		register_cpu_control(cpu);
215
	if (!error)
216
		per_cpu(cpu_sys_devices, num) = &cpu->sysdev;
217 218
	if (!error)
		register_cpu_under_node(num, cpu_to_node(num));
219 220 221 222 223

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

227
struct sys_device *get_cpu_sysdev(unsigned cpu)
228
{
229 230
	if (cpu < nr_cpu_ids && cpu_possible(cpu))
		return per_cpu(cpu_sys_devices, cpu);
231 232 233 234
	else
		return NULL;
}
EXPORT_SYMBOL_GPL(get_cpu_sysdev);
L
Linus Torvalds 已提交
235 236 237

int __init cpu_dev_init(void)
{
238 239 240
	int err;

	err = sysdev_class_register(&cpu_sysdev_class);
241 242 243
	if (!err)
		err = cpu_states_init();

244 245 246 247 248 249
#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 已提交
250
}