diff options
| author | Rusty Russell <rusty@rustcorp.com.au> | 2004-03-18 16:02:28 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-03-18 16:02:28 -0800 |
| commit | 036f21379d26fb60b6c490803abf6c7c735c4f6a (patch) | |
| tree | 089b1500a73e8edb5a64087ff7983833e9503765 /kernel/cpu.c | |
| parent | c884a1a3d78bc9a0bf56b11e6d84d7447d93bf00 (diff) | |
[PATCH] Hotplug CPUs: cpu_down()
Implement cpu_down(): uses stop_machine to freeze the machine, then
uses (arch-specific) __cpu_disable() and migrate_all_tasks().
Whole thing under CONFIG_HOTPLUG_CPU, so doesn't break archs which
don't define that.
Diffstat (limited to 'kernel/cpu.c')
| -rw-r--r-- | kernel/cpu.c | 125 |
1 files changed, 124 insertions, 1 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index bbef5f773f75..fcbb0d5d2817 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -1,5 +1,6 @@ /* CPU control. - * (C) 2001 Rusty Russell + * (C) 2001, 2002, 2003, 2004 Rusty Russell + * * This code is licenced under the GPL. */ #include <linux/proc_fs.h> @@ -10,6 +11,9 @@ #include <linux/unistd.h> #include <linux/cpu.h> #include <linux/module.h> +#include <linux/kmod.h> /* for hotplug_path */ +#include <linux/kthread.h> +#include <linux/stop_machine.h> #include <asm/semaphore.h> /* This protects CPUs going up and down... */ @@ -38,6 +42,125 @@ void unregister_cpu_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(unregister_cpu_notifier); +#ifdef CONFIG_HOTPLUG_CPU +static inline void check_for_tasks(int cpu, struct task_struct *k) +{ + struct task_struct *p; + + write_lock_irq(&tasklist_lock); + for_each_process(p) { + if (task_cpu(p) == cpu && p != k) + printk(KERN_WARNING "Task %s is on cpu %d\n", + p->comm, cpu); + } + write_unlock_irq(&tasklist_lock); +} + +/* Notify userspace when a cpu event occurs, by running '/sbin/hotplug + * cpu' with certain environment variables set. */ +static int cpu_run_sbin_hotplug(unsigned int cpu, const char *action) +{ + char *argv[3], *envp[5], cpu_str[12], action_str[32]; + int i; + + sprintf(cpu_str, "CPU=%d", cpu); + sprintf(action_str, "ACTION=%s", action); + /* FIXME: Add DEVPATH. --RR */ + + i = 0; + argv[i++] = hotplug_path; + argv[i++] = "cpu"; + argv[i] = NULL; + + i = 0; + /* minimal command environment */ + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp[i++] = cpu_str; + envp[i++] = action_str; + envp[i] = NULL; + + return call_usermodehelper(argv[0], argv, envp, 0); +} + +/* Take this CPU down. */ +static int take_cpu_down(void *unused) +{ + int err; + + /* Take offline: makes arch_cpu_down somewhat easier. */ + cpu_clear(smp_processor_id(), cpu_online_map); + + /* Ensure this CPU doesn't handle any more interrupts. */ + err = __cpu_disable(); + if (err < 0) + cpu_set(smp_processor_id(), cpu_online_map); + else + /* Everyone else gets kicked off. */ + migrate_all_tasks(); + + return err; +} + +int cpu_down(unsigned int cpu) +{ + int err; + struct task_struct *p; + + if ((err = lock_cpu_hotplug_interruptible()) != 0) + return err; + + if (num_online_cpus() == 1) { + err = -EBUSY; + goto out; + } + + if (!cpu_online(cpu)) { + err = -EINVAL; + goto out; + } + + p = __stop_machine_run(take_cpu_down, NULL, cpu); + if (IS_ERR(p)) { + err = PTR_ERR(p); + goto out; + } + + if (cpu_online(cpu)) + goto out_thread; + + check_for_tasks(cpu, p); + + /* 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, smp_processor_id()); + + /* CPU is completely dead: tell everyone. Too late to complain. */ + if (notifier_call_chain(&cpu_chain, CPU_DEAD, (void *)(long)cpu) + == NOTIFY_BAD) + BUG(); + + cpu_run_sbin_hotplug(cpu, "offline"); + +out_thread: + err = kthread_stop(p); +out: + unlock_cpu_hotplug(); + return err; +} +#else +static inline int cpu_run_sbin_hotplug(unsigned int cpu, const char *action) +{ + return 0; +} +#endif /*CONFIG_HOTPLUG_CPU*/ + int __devinit cpu_up(unsigned int cpu) { int ret; |
