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 RAW_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
	recursive = NULL;
62
	mutex_unlock(&cpu_bitmask_lock);
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 72 73 74 75
	int ret;
	mutex_lock(&cpu_add_remove_lock);
	ret = raw_notifier_chain_register(&cpu_chain, nb);
	mutex_unlock(&cpu_add_remove_lock);
	return ret;
L
Linus Torvalds 已提交
76
}
77 78 79

#ifdef CONFIG_HOTPLUG_CPU

L
Linus Torvalds 已提交
80 81 82 83
EXPORT_SYMBOL(register_cpu_notifier);

void unregister_cpu_notifier(struct notifier_block *nb)
{
84 85 86
	mutex_lock(&cpu_add_remove_lock);
	raw_notifier_chain_unregister(&cpu_chain, nb);
	mutex_unlock(&cpu_add_remove_lock);
L
Linus Torvalds 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
}
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 已提交
114
		return err;
L
Linus Torvalds 已提交
115

Z
Zwane Mwaikambo 已提交
116 117 118 119
	/* 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 已提交
120 121
}

122 123
/* Requires cpu_add_remove_lock to be held */
static int _cpu_down(unsigned int cpu)
L
Linus Torvalds 已提交
124 125 126 127 128
{
	int err;
	struct task_struct *p;
	cpumask_t old_allowed, tmp;

129 130
	if (num_online_cpus() == 1)
		return -EBUSY;
L
Linus Torvalds 已提交
131

132 133
	if (!cpu_online(cpu))
		return -EINVAL;
L
Linus Torvalds 已提交
134

135
	err = raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE,
L
Linus Torvalds 已提交
136 137 138 139
						(void *)(long)cpu);
	if (err == NOTIFY_BAD) {
		printk("%s: attempt to take down CPU %u failed\n",
				__FUNCTION__, cpu);
140
		return -EINVAL;
L
Linus Torvalds 已提交
141 142 143 144 145 146 147 148
	}

	/* 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);

149
	mutex_lock(&cpu_bitmask_lock);
L
Linus Torvalds 已提交
150
	p = __stop_machine_run(take_cpu_down, NULL, cpu);
151 152
	mutex_unlock(&cpu_bitmask_lock);

153
	if (IS_ERR(p) || cpu_online(cpu)) {
L
Linus Torvalds 已提交
154
		/* CPU didn't die: tell everyone.  Can't complain. */
155
		if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED,
L
Linus Torvalds 已提交
156 157 158
				(void *)(long)cpu) == NOTIFY_BAD)
			BUG();

159 160 161 162
		if (IS_ERR(p)) {
			err = PTR_ERR(p);
			goto out_allowed;
		}
L
Linus Torvalds 已提交
163
		goto out_thread;
164
	}
L
Linus Torvalds 已提交
165 166 167 168 169 170 171 172 173 174 175 176 177

	/* 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. */
178
	if (raw_notifier_call_chain(&cpu_chain, CPU_DEAD,
179
			(void *)(long)cpu) == NOTIFY_BAD)
L
Linus Torvalds 已提交
180 181 182 183 184 185 186 187
		BUG();

	check_for_tasks(cpu);

out_thread:
	err = kthread_stop(p);
out_allowed:
	set_cpus_allowed(current, old_allowed);
188 189 190 191 192 193 194 195 196 197 198 199 200
	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);

201
	mutex_unlock(&cpu_add_remove_lock);
L
Linus Torvalds 已提交
202 203 204 205
	return err;
}
#endif /*CONFIG_HOTPLUG_CPU*/

206 207
/* Requires cpu_add_remove_lock to be held */
static int __devinit _cpu_up(unsigned int cpu)
L
Linus Torvalds 已提交
208 209 210 211
{
	int ret;
	void *hcpu = (void *)(long)cpu;

212 213
	if (cpu_online(cpu) || !cpu_present(cpu))
		return -EINVAL;
214

215
	ret = raw_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu);
L
Linus Torvalds 已提交
216 217 218 219 220 221 222 223
	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. */
224
	mutex_lock(&cpu_bitmask_lock);
L
Linus Torvalds 已提交
225
	ret = __cpu_up(cpu);
226
	mutex_unlock(&cpu_bitmask_lock);
L
Linus Torvalds 已提交
227 228
	if (ret != 0)
		goto out_notify;
229
	BUG_ON(!cpu_online(cpu));
L
Linus Torvalds 已提交
230 231

	/* Now call notifier in preparation. */
232
	raw_notifier_call_chain(&cpu_chain, CPU_ONLINE, hcpu);
L
Linus Torvalds 已提交
233 234 235

out_notify:
	if (ret != 0)
236
		raw_notifier_call_chain(&cpu_chain,
237
				CPU_UP_CANCELED, hcpu);
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

	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;
		}
	}
273

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
	/* 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 已提交
299
out:
300
	mutex_unlock(&cpu_add_remove_lock);
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
	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 已提交
324
}
325
#endif