cpu.c 7.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* CPU control.
 * (C) 2001, 2002, 2003, 2004 Rusty Russell
 *
 * This code is licenced under the GPL.
 */
#include <linux/proc_fs.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/notifier.h>
#include <linux/sched.h>
#include <linux/unistd.h>
#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/stop_machine.h>
16
#include <linux/mutex.h>
L
Linus Torvalds 已提交
17 18

/* This protects CPUs going up and down... */
19 20
static DEFINE_MUTEX(cpu_add_remove_lock);
static DEFINE_MUTEX(cpu_bitmask_lock);
L
Linus Torvalds 已提交
21

22
static __cpuinitdata BLOCKING_NOTIFIER_HEAD(cpu_chain);
L
Linus Torvalds 已提交
23

24 25 26 27 28
/* If set, cpu_up and cpu_down will return -EBUSY and do nothing.
 * Should always be manipulated under cpu_add_remove_lock
 */
static int cpu_hotplug_disabled;

29
#ifdef CONFIG_HOTPLUG_CPU
30

31 32 33
/* Crappy recursive lock-takers in cpufreq! Complain loudly about idiots */
static struct task_struct *recursive;
static int recursive_depth;
34

35 36
void lock_cpu_hotplug(void)
{
37 38 39 40 41 42 43 44 45 46 47 48 49 50
	struct task_struct *tsk = current;

	if (tsk == recursive) {
		static int warnings = 10;
		if (warnings) {
			printk(KERN_ERR "Lukewarm IQ detected in hotplug locking\n");
			WARN_ON(1);
			warnings--;
		}
		recursive_depth++;
		return;
	}
	mutex_lock(&cpu_bitmask_lock);
	recursive = tsk;
51 52
}
EXPORT_SYMBOL_GPL(lock_cpu_hotplug);
53

54 55
void unlock_cpu_hotplug(void)
{
56 57 58 59
	WARN_ON(recursive != current);
	if (recursive_depth) {
		recursive_depth--;
		return;
60
	}
61 62
	mutex_unlock(&cpu_bitmask_lock);
	recursive = NULL;
63 64 65 66
}
EXPORT_SYMBOL_GPL(unlock_cpu_hotplug);

#endif	/* CONFIG_HOTPLUG_CPU */
67

L
Linus Torvalds 已提交
68
/* Need to know about CPUs going up/down? */
69
int __cpuinit register_cpu_notifier(struct notifier_block *nb)
L
Linus Torvalds 已提交
70
{
71
	return blocking_notifier_chain_register(&cpu_chain, nb);
L
Linus Torvalds 已提交
72
}
73 74 75

#ifdef CONFIG_HOTPLUG_CPU

L
Linus Torvalds 已提交
76 77 78 79
EXPORT_SYMBOL(register_cpu_notifier);

void unregister_cpu_notifier(struct notifier_block *nb)
{
80
	blocking_notifier_chain_unregister(&cpu_chain, nb);
L
Linus Torvalds 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
}
EXPORT_SYMBOL(unregister_cpu_notifier);

static inline void check_for_tasks(int cpu)
{
	struct task_struct *p;

	write_lock_irq(&tasklist_lock);
	for_each_process(p) {
		if (task_cpu(p) == cpu &&
		    (!cputime_eq(p->utime, cputime_zero) ||
		     !cputime_eq(p->stime, cputime_zero)))
			printk(KERN_WARNING "Task %s (pid = %d) is on cpu %d\
				(state = %ld, flags = %lx) \n",
				 p->comm, p->pid, cpu, p->state, p->flags);
	}
	write_unlock_irq(&tasklist_lock);
}

/* Take this CPU down. */
static int take_cpu_down(void *unused)
{
	int err;

	/* Ensure this CPU doesn't handle any more interrupts. */
	err = __cpu_disable();
	if (err < 0)
Z
Zwane Mwaikambo 已提交
108
		return err;
L
Linus Torvalds 已提交
109

Z
Zwane Mwaikambo 已提交
110 111 112 113
	/* Force idle task to run as soon as we yield: it should
	   immediately notice cpu is offline and die quickly. */
	sched_idle_next();
	return 0;
L
Linus Torvalds 已提交
114 115
}

116 117
/* Requires cpu_add_remove_lock to be held */
static int _cpu_down(unsigned int cpu)
L
Linus Torvalds 已提交
118 119 120 121 122
{
	int err;
	struct task_struct *p;
	cpumask_t old_allowed, tmp;

123 124
	if (num_online_cpus() == 1)
		return -EBUSY;
L
Linus Torvalds 已提交
125

126 127
	if (!cpu_online(cpu))
		return -EINVAL;
L
Linus Torvalds 已提交
128

129
	err = blocking_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE,
L
Linus Torvalds 已提交
130 131 132 133
						(void *)(long)cpu);
	if (err == NOTIFY_BAD) {
		printk("%s: attempt to take down CPU %u failed\n",
				__FUNCTION__, cpu);
134
		return -EINVAL;
L
Linus Torvalds 已提交
135 136 137 138 139 140 141 142
	}

	/* Ensure that we are not runnable on dying cpu */
	old_allowed = current->cpus_allowed;
	tmp = CPU_MASK_ALL;
	cpu_clear(cpu, tmp);
	set_cpus_allowed(current, tmp);

143
	mutex_lock(&cpu_bitmask_lock);
L
Linus Torvalds 已提交
144
	p = __stop_machine_run(take_cpu_down, NULL, cpu);
145 146
	mutex_unlock(&cpu_bitmask_lock);

L
Linus Torvalds 已提交
147 148
	if (IS_ERR(p)) {
		/* CPU didn't die: tell everyone.  Can't complain. */
149
		if (blocking_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED,
L
Linus Torvalds 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
				(void *)(long)cpu) == NOTIFY_BAD)
			BUG();

		err = PTR_ERR(p);
		goto out_allowed;
	}

	if (cpu_online(cpu))
		goto out_thread;

	/* Wait for it to sleep (leaving idle task). */
	while (!idle_cpu(cpu))
		yield();

	/* This actually kills the CPU. */
	__cpu_die(cpu);

	/* Move it here so it can run. */
	kthread_bind(p, get_cpu());
	put_cpu();

	/* CPU is completely dead: tell everyone.  Too late to complain. */
172 173
	if (blocking_notifier_call_chain(&cpu_chain, CPU_DEAD,
			(void *)(long)cpu) == NOTIFY_BAD)
L
Linus Torvalds 已提交
174 175 176 177 178 179 180 181
		BUG();

	check_for_tasks(cpu);

out_thread:
	err = kthread_stop(p);
out_allowed:
	set_cpus_allowed(current, old_allowed);
182 183 184 185 186 187 188 189 190 191 192 193 194
	return err;
}

int cpu_down(unsigned int cpu)
{
	int err = 0;

	mutex_lock(&cpu_add_remove_lock);
	if (cpu_hotplug_disabled)
		err = -EBUSY;
	else
		err = _cpu_down(cpu);

195
	mutex_unlock(&cpu_add_remove_lock);
L
Linus Torvalds 已提交
196 197 198 199
	return err;
}
#endif /*CONFIG_HOTPLUG_CPU*/

200 201
/* Requires cpu_add_remove_lock to be held */
static int __devinit _cpu_up(unsigned int cpu)
L
Linus Torvalds 已提交
202 203 204 205
{
	int ret;
	void *hcpu = (void *)(long)cpu;

206 207
	if (cpu_online(cpu) || !cpu_present(cpu))
		return -EINVAL;
208

209
	ret = blocking_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu);
L
Linus Torvalds 已提交
210 211 212 213 214 215 216 217
	if (ret == NOTIFY_BAD) {
		printk("%s: attempt to bring up CPU %u failed\n",
				__FUNCTION__, cpu);
		ret = -EINVAL;
		goto out_notify;
	}

	/* Arch-specific enabling code. */
218
	mutex_lock(&cpu_bitmask_lock);
L
Linus Torvalds 已提交
219
	ret = __cpu_up(cpu);
220
	mutex_unlock(&cpu_bitmask_lock);
L
Linus Torvalds 已提交
221 222
	if (ret != 0)
		goto out_notify;
223
	BUG_ON(!cpu_online(cpu));
L
Linus Torvalds 已提交
224 225

	/* Now call notifier in preparation. */
226
	blocking_notifier_call_chain(&cpu_chain, CPU_ONLINE, hcpu);
L
Linus Torvalds 已提交
227 228 229

out_notify:
	if (ret != 0)
230 231
		blocking_notifier_call_chain(&cpu_chain,
				CPU_UP_CANCELED, hcpu);
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296

	return ret;
}

int __devinit cpu_up(unsigned int cpu)
{
	int err = 0;

	mutex_lock(&cpu_add_remove_lock);
	if (cpu_hotplug_disabled)
		err = -EBUSY;
	else
		err = _cpu_up(cpu);

	mutex_unlock(&cpu_add_remove_lock);
	return err;
}

#ifdef CONFIG_SUSPEND_SMP
static cpumask_t frozen_cpus;

int disable_nonboot_cpus(void)
{
	int cpu, first_cpu, error;

	mutex_lock(&cpu_add_remove_lock);
	first_cpu = first_cpu(cpu_present_map);
	if (!cpu_online(first_cpu)) {
		error = _cpu_up(first_cpu);
		if (error) {
			printk(KERN_ERR "Could not bring CPU%d up.\n",
				first_cpu);
			goto out;
		}
	}
	error = set_cpus_allowed(current, cpumask_of_cpu(first_cpu));
	if (error) {
		printk(KERN_ERR "Could not run on CPU%d\n", first_cpu);
		goto out;
	}
	/* We take down all of the non-boot CPUs in one shot to avoid races
	 * with the userspace trying to use the CPU hotplug at the same time
	 */
	cpus_clear(frozen_cpus);
	printk("Disabling non-boot CPUs ...\n");
	for_each_online_cpu(cpu) {
		if (cpu == first_cpu)
			continue;
		error = _cpu_down(cpu);
		if (!error) {
			cpu_set(cpu, frozen_cpus);
			printk("CPU%d is down\n", cpu);
		} else {
			printk(KERN_ERR "Error taking CPU%d down: %d\n",
				cpu, error);
			break;
		}
	}
	if (!error) {
		BUG_ON(num_online_cpus() > 1);
		/* Make sure the CPUs won't be enabled by someone else */
		cpu_hotplug_disabled = 1;
	} else {
		printk(KERN_ERR "Non-boot CPUs are not disabled");
	}
L
Linus Torvalds 已提交
297
out:
298
	mutex_unlock(&cpu_add_remove_lock);
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
	return error;
}

void enable_nonboot_cpus(void)
{
	int cpu, error;

	/* Allow everyone to use the CPU hotplug again */
	mutex_lock(&cpu_add_remove_lock);
	cpu_hotplug_disabled = 0;
	mutex_unlock(&cpu_add_remove_lock);

	printk("Enabling non-boot CPUs ...\n");
	for_each_cpu_mask(cpu, frozen_cpus) {
		error = cpu_up(cpu);
		if (!error) {
			printk("CPU%d is up\n", cpu);
			continue;
		}
		printk(KERN_WARNING "Error taking CPU%d up: %d\n",
			cpu, error);
	}
	cpus_clear(frozen_cpus);
L
Linus Torvalds 已提交
322
}
323
#endif