ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / kernel / cpu.c
1 /* CPU control.
2  * (C) 2001, 2002, 2003, 2004 Rusty Russell
3  *
4  * This code is licenced under the GPL.
5  */
6 #include <linux/proc_fs.h>
7 #include <linux/smp.h>
8 #include <linux/init.h>
9 #include <linux/notifier.h>
10 #include <linux/sched.h>
11 #include <linux/unistd.h>
12 #include <linux/cpu.h>
13 #include <linux/module.h>
14 #include <linux/kmod.h>         /* for hotplug_path */
15 #include <linux/kthread.h>
16 #include <linux/stop_machine.h>
17 #include <asm/semaphore.h>
18
19 /* This protects CPUs going up and down... */
20 DECLARE_MUTEX(cpucontrol);
21
22 static struct notifier_block *cpu_chain;
23
24 /* Need to know about CPUs going up/down? */
25 int register_cpu_notifier(struct notifier_block *nb)
26 {
27         int ret;
28
29         if ((ret = down_interruptible(&cpucontrol)) != 0)
30                 return ret;
31         ret = notifier_chain_register(&cpu_chain, nb);
32         up(&cpucontrol);
33         return ret;
34 }
35 EXPORT_SYMBOL(register_cpu_notifier);
36
37 void unregister_cpu_notifier(struct notifier_block *nb)
38 {
39         down(&cpucontrol);
40         notifier_chain_unregister(&cpu_chain, nb);
41         up(&cpucontrol);
42 }
43 EXPORT_SYMBOL(unregister_cpu_notifier);
44
45 #ifdef CONFIG_HOTPLUG_CPU
46 static inline void check_for_tasks(int cpu, struct task_struct *k)
47 {
48         struct task_struct *p;
49
50         write_lock_irq(&tasklist_lock);
51         for_each_process(p) {
52                 if (task_cpu(p) == cpu && p != k)
53                         printk(KERN_WARNING "Task %s is on cpu %d\n",
54                                 p->comm, cpu);
55         }
56         write_unlock_irq(&tasklist_lock);
57 }
58
59 /* Notify userspace when a cpu event occurs, by running '/sbin/hotplug
60  * cpu' with certain environment variables set.  */
61 static int cpu_run_sbin_hotplug(unsigned int cpu, const char *action)
62 {
63         char *argv[3], *envp[5], cpu_str[12], action_str[32];
64         int i;
65
66         sprintf(cpu_str, "CPU=%d", cpu);
67         sprintf(action_str, "ACTION=%s", action);
68         /* FIXME: Add DEVPATH. --RR */
69
70         i = 0;
71         argv[i++] = hotplug_path;
72         argv[i++] = "cpu";
73         argv[i] = NULL;
74
75         i = 0;
76         /* minimal command environment */
77         envp[i++] = "HOME=/";
78         envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
79         envp[i++] = cpu_str;
80         envp[i++] = action_str;
81         envp[i] = NULL;
82
83         return call_usermodehelper(argv[0], argv, envp, 0);
84 }
85
86 /* Take this CPU down. */
87 static int take_cpu_down(void *unused)
88 {
89         int err;
90
91         /* Take offline: makes arch_cpu_down somewhat easier. */
92         cpu_clear(smp_processor_id(), cpu_online_map);
93
94         /* Ensure this CPU doesn't handle any more interrupts. */
95         err = __cpu_disable();
96         if (err < 0)
97                 cpu_set(smp_processor_id(), cpu_online_map);
98         else
99                 /* Everyone else gets kicked off. */
100                 migrate_all_tasks();
101
102         return err;
103 }
104
105 int cpu_down(unsigned int cpu)
106 {
107         int err;
108         struct task_struct *p;
109
110         if ((err = lock_cpu_hotplug_interruptible()) != 0)
111                 return err;
112
113         if (num_online_cpus() == 1) {
114                 err = -EBUSY;
115                 goto out;
116         }
117
118         if (!cpu_online(cpu)) {
119                 err = -EINVAL;
120                 goto out;
121         }
122
123         p = __stop_machine_run(take_cpu_down, NULL, cpu);
124         if (IS_ERR(p)) {
125                 err = PTR_ERR(p);
126                 goto out;
127         }
128
129         if (cpu_online(cpu))
130                 goto out_thread;
131
132         check_for_tasks(cpu, p);
133
134         /* Wait for it to sleep (leaving idle task). */
135         while (!idle_cpu(cpu))
136                 yield();
137
138         /* This actually kills the CPU. */
139         __cpu_die(cpu);
140
141         /* Move it here so it can run. */
142         kthread_bind(p, smp_processor_id());
143
144         /* CPU is completely dead: tell everyone.  Too late to complain. */
145         if (notifier_call_chain(&cpu_chain, CPU_DEAD, (void *)(long)cpu)
146             == NOTIFY_BAD)
147                 BUG();
148
149         cpu_run_sbin_hotplug(cpu, "offline");
150
151 out_thread:
152         err = kthread_stop(p);
153 out:
154         unlock_cpu_hotplug();
155         return err;
156 }
157 #else
158 static inline int cpu_run_sbin_hotplug(unsigned int cpu, const char *action)
159 {
160         return 0;
161 }
162 #endif /*CONFIG_HOTPLUG_CPU*/
163
164 int __devinit cpu_up(unsigned int cpu)
165 {
166         int ret;
167         void *hcpu = (void *)(long)cpu;
168
169         if ((ret = down_interruptible(&cpucontrol)) != 0)
170                 return ret;
171
172         if (cpu_online(cpu)) {
173                 ret = -EINVAL;
174                 goto out;
175         }
176         ret = notifier_call_chain(&cpu_chain, CPU_UP_PREPARE, hcpu);
177         if (ret == NOTIFY_BAD) {
178                 printk("%s: attempt to bring up CPU %u failed\n",
179                                 __FUNCTION__, cpu);
180                 ret = -EINVAL;
181                 goto out_notify;
182         }
183
184         /* Arch-specific enabling code. */
185         ret = __cpu_up(cpu);
186         if (ret != 0)
187                 goto out_notify;
188         if (!cpu_online(cpu))
189                 BUG();
190
191         /* Now call notifier in preparation. */
192         notifier_call_chain(&cpu_chain, CPU_ONLINE, hcpu);
193
194 out_notify:
195         if (ret != 0)
196                 notifier_call_chain(&cpu_chain, CPU_UP_CANCELED, hcpu);
197 out:
198         up(&cpucontrol);
199         return ret;
200 }