diff options
Diffstat (limited to 'kernel/cpu.c')
-rw-r--r-- | kernel/cpu.c | 170 |
1 files changed, 98 insertions, 72 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index 1c8ddd6ee940..8b92539b4754 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
@@ -14,11 +14,26 @@ | |||
14 | #include <linux/kthread.h> | 14 | #include <linux/kthread.h> |
15 | #include <linux/stop_machine.h> | 15 | #include <linux/stop_machine.h> |
16 | #include <linux/mutex.h> | 16 | #include <linux/mutex.h> |
17 | #include <linux/gfp.h> | ||
17 | 18 | ||
18 | #ifdef CONFIG_SMP | 19 | #ifdef CONFIG_SMP |
19 | /* Serializes the updates to cpu_online_mask, cpu_present_mask */ | 20 | /* Serializes the updates to cpu_online_mask, cpu_present_mask */ |
20 | static DEFINE_MUTEX(cpu_add_remove_lock); | 21 | static DEFINE_MUTEX(cpu_add_remove_lock); |
21 | 22 | ||
23 | /* | ||
24 | * The following two API's must be used when attempting | ||
25 | * to serialize the updates to cpu_online_mask, cpu_present_mask. | ||
26 | */ | ||
27 | void cpu_maps_update_begin(void) | ||
28 | { | ||
29 | mutex_lock(&cpu_add_remove_lock); | ||
30 | } | ||
31 | |||
32 | void cpu_maps_update_done(void) | ||
33 | { | ||
34 | mutex_unlock(&cpu_add_remove_lock); | ||
35 | } | ||
36 | |||
22 | static __cpuinitdata RAW_NOTIFIER_HEAD(cpu_chain); | 37 | static __cpuinitdata RAW_NOTIFIER_HEAD(cpu_chain); |
23 | 38 | ||
24 | /* If set, cpu_up and cpu_down will return -EBUSY and do nothing. | 39 | /* If set, cpu_up and cpu_down will return -EBUSY and do nothing. |
@@ -26,6 +41,8 @@ static __cpuinitdata RAW_NOTIFIER_HEAD(cpu_chain); | |||
26 | */ | 41 | */ |
27 | static int cpu_hotplug_disabled; | 42 | static int cpu_hotplug_disabled; |
28 | 43 | ||
44 | #ifdef CONFIG_HOTPLUG_CPU | ||
45 | |||
29 | static struct { | 46 | static struct { |
30 | struct task_struct *active_writer; | 47 | struct task_struct *active_writer; |
31 | struct mutex lock; /* Synchronizes accesses to refcount, */ | 48 | struct mutex lock; /* Synchronizes accesses to refcount, */ |
@@ -40,8 +57,6 @@ static struct { | |||
40 | .refcount = 0, | 57 | .refcount = 0, |
41 | }; | 58 | }; |
42 | 59 | ||
43 | #ifdef CONFIG_HOTPLUG_CPU | ||
44 | |||
45 | void get_online_cpus(void) | 60 | void get_online_cpus(void) |
46 | { | 61 | { |
47 | might_sleep(); | 62 | might_sleep(); |
@@ -66,22 +81,6 @@ void put_online_cpus(void) | |||
66 | } | 81 | } |
67 | EXPORT_SYMBOL_GPL(put_online_cpus); | 82 | EXPORT_SYMBOL_GPL(put_online_cpus); |
68 | 83 | ||
69 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
70 | |||
71 | /* | ||
72 | * The following two API's must be used when attempting | ||
73 | * to serialize the updates to cpu_online_mask, cpu_present_mask. | ||
74 | */ | ||
75 | void cpu_maps_update_begin(void) | ||
76 | { | ||
77 | mutex_lock(&cpu_add_remove_lock); | ||
78 | } | ||
79 | |||
80 | void cpu_maps_update_done(void) | ||
81 | { | ||
82 | mutex_unlock(&cpu_add_remove_lock); | ||
83 | } | ||
84 | |||
85 | /* | 84 | /* |
86 | * This ensures that the hotplug operation can begin only when the | 85 | * This ensures that the hotplug operation can begin only when the |
87 | * refcount goes to zero. | 86 | * refcount goes to zero. |
@@ -123,6 +122,12 @@ static void cpu_hotplug_done(void) | |||
123 | cpu_hotplug.active_writer = NULL; | 122 | cpu_hotplug.active_writer = NULL; |
124 | mutex_unlock(&cpu_hotplug.lock); | 123 | mutex_unlock(&cpu_hotplug.lock); |
125 | } | 124 | } |
125 | |||
126 | #else /* #if CONFIG_HOTPLUG_CPU */ | ||
127 | static void cpu_hotplug_begin(void) {} | ||
128 | static void cpu_hotplug_done(void) {} | ||
129 | #endif /* #esle #if CONFIG_HOTPLUG_CPU */ | ||
130 | |||
126 | /* Need to know about CPUs going up/down? */ | 131 | /* Need to know about CPUs going up/down? */ |
127 | int __ref register_cpu_notifier(struct notifier_block *nb) | 132 | int __ref register_cpu_notifier(struct notifier_block *nb) |
128 | { | 133 | { |
@@ -133,8 +138,29 @@ int __ref register_cpu_notifier(struct notifier_block *nb) | |||
133 | return ret; | 138 | return ret; |
134 | } | 139 | } |
135 | 140 | ||
141 | static int __cpu_notify(unsigned long val, void *v, int nr_to_call, | ||
142 | int *nr_calls) | ||
143 | { | ||
144 | int ret; | ||
145 | |||
146 | ret = __raw_notifier_call_chain(&cpu_chain, val, v, nr_to_call, | ||
147 | nr_calls); | ||
148 | |||
149 | return notifier_to_errno(ret); | ||
150 | } | ||
151 | |||
152 | static int cpu_notify(unsigned long val, void *v) | ||
153 | { | ||
154 | return __cpu_notify(val, v, -1, NULL); | ||
155 | } | ||
156 | |||
136 | #ifdef CONFIG_HOTPLUG_CPU | 157 | #ifdef CONFIG_HOTPLUG_CPU |
137 | 158 | ||
159 | static void cpu_notify_nofail(unsigned long val, void *v) | ||
160 | { | ||
161 | BUG_ON(cpu_notify(val, v)); | ||
162 | } | ||
163 | |||
138 | EXPORT_SYMBOL(register_cpu_notifier); | 164 | EXPORT_SYMBOL(register_cpu_notifier); |
139 | 165 | ||
140 | void __ref unregister_cpu_notifier(struct notifier_block *nb) | 166 | void __ref unregister_cpu_notifier(struct notifier_block *nb) |
@@ -151,18 +177,19 @@ static inline void check_for_tasks(int cpu) | |||
151 | 177 | ||
152 | write_lock_irq(&tasklist_lock); | 178 | write_lock_irq(&tasklist_lock); |
153 | for_each_process(p) { | 179 | for_each_process(p) { |
154 | if (task_cpu(p) == cpu && | 180 | if (task_cpu(p) == cpu && p->state == TASK_RUNNING && |
155 | (!cputime_eq(p->utime, cputime_zero) || | 181 | (!cputime_eq(p->utime, cputime_zero) || |
156 | !cputime_eq(p->stime, cputime_zero))) | 182 | !cputime_eq(p->stime, cputime_zero))) |
157 | printk(KERN_WARNING "Task %s (pid = %d) is on cpu %d\ | 183 | printk(KERN_WARNING "Task %s (pid = %d) is on cpu %d " |
158 | (state = %ld, flags = %x) \n", | 184 | "(state = %ld, flags = %x)\n", |
159 | p->comm, task_pid_nr(p), cpu, | 185 | p->comm, task_pid_nr(p), cpu, |
160 | p->state, p->flags); | 186 | p->state, p->flags); |
161 | } | 187 | } |
162 | write_unlock_irq(&tasklist_lock); | 188 | write_unlock_irq(&tasklist_lock); |
163 | } | 189 | } |
164 | 190 | ||
165 | struct take_cpu_down_param { | 191 | struct take_cpu_down_param { |
192 | struct task_struct *caller; | ||
166 | unsigned long mod; | 193 | unsigned long mod; |
167 | void *hcpu; | 194 | void *hcpu; |
168 | }; | 195 | }; |
@@ -171,6 +198,7 @@ struct take_cpu_down_param { | |||
171 | static int __ref take_cpu_down(void *_param) | 198 | static int __ref take_cpu_down(void *_param) |
172 | { | 199 | { |
173 | struct take_cpu_down_param *param = _param; | 200 | struct take_cpu_down_param *param = _param; |
201 | unsigned int cpu = (unsigned long)param->hcpu; | ||
174 | int err; | 202 | int err; |
175 | 203 | ||
176 | /* Ensure this CPU doesn't handle any more interrupts. */ | 204 | /* Ensure this CPU doesn't handle any more interrupts. */ |
@@ -178,9 +206,10 @@ static int __ref take_cpu_down(void *_param) | |||
178 | if (err < 0) | 206 | if (err < 0) |
179 | return err; | 207 | return err; |
180 | 208 | ||
181 | raw_notifier_call_chain(&cpu_chain, CPU_DYING | param->mod, | 209 | cpu_notify(CPU_DYING | param->mod, param->hcpu); |
182 | param->hcpu); | ||
183 | 210 | ||
211 | if (task_cpu(param->caller) == cpu) | ||
212 | move_task_off_dead_cpu(cpu, param->caller); | ||
184 | /* Force idle task to run as soon as we yield: it should | 213 | /* Force idle task to run as soon as we yield: it should |
185 | immediately notice cpu is offline and die quickly. */ | 214 | immediately notice cpu is offline and die quickly. */ |
186 | sched_idle_next(); | 215 | sched_idle_next(); |
@@ -191,10 +220,10 @@ static int __ref take_cpu_down(void *_param) | |||
191 | static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) | 220 | static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) |
192 | { | 221 | { |
193 | int err, nr_calls = 0; | 222 | int err, nr_calls = 0; |
194 | cpumask_var_t old_allowed; | ||
195 | void *hcpu = (void *)(long)cpu; | 223 | void *hcpu = (void *)(long)cpu; |
196 | unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0; | 224 | unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0; |
197 | struct take_cpu_down_param tcd_param = { | 225 | struct take_cpu_down_param tcd_param = { |
226 | .caller = current, | ||
198 | .mod = mod, | 227 | .mod = mod, |
199 | .hcpu = hcpu, | 228 | .hcpu = hcpu, |
200 | }; | 229 | }; |
@@ -205,38 +234,26 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) | |||
205 | if (!cpu_online(cpu)) | 234 | if (!cpu_online(cpu)) |
206 | return -EINVAL; | 235 | return -EINVAL; |
207 | 236 | ||
208 | if (!alloc_cpumask_var(&old_allowed, GFP_KERNEL)) | ||
209 | return -ENOMEM; | ||
210 | |||
211 | cpu_hotplug_begin(); | 237 | cpu_hotplug_begin(); |
212 | set_cpu_active(cpu, false); | 238 | set_cpu_active(cpu, false); |
213 | err = __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE | mod, | 239 | err = __cpu_notify(CPU_DOWN_PREPARE | mod, hcpu, -1, &nr_calls); |
214 | hcpu, -1, &nr_calls); | 240 | if (err) { |
215 | if (err == NOTIFY_BAD) { | ||
216 | set_cpu_active(cpu, true); | 241 | set_cpu_active(cpu, true); |
217 | 242 | ||
218 | nr_calls--; | 243 | nr_calls--; |
219 | __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, | 244 | __cpu_notify(CPU_DOWN_FAILED | mod, hcpu, nr_calls, NULL); |
220 | hcpu, nr_calls, NULL); | ||
221 | printk("%s: attempt to take down CPU %u failed\n", | 245 | printk("%s: attempt to take down CPU %u failed\n", |
222 | __func__, cpu); | 246 | __func__, cpu); |
223 | err = -EINVAL; | ||
224 | goto out_release; | 247 | goto out_release; |
225 | } | 248 | } |
226 | 249 | ||
227 | /* Ensure that we are not runnable on dying cpu */ | ||
228 | cpumask_copy(old_allowed, ¤t->cpus_allowed); | ||
229 | set_cpus_allowed_ptr(current, cpu_active_mask); | ||
230 | |||
231 | err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu)); | 250 | err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu)); |
232 | if (err) { | 251 | if (err) { |
233 | set_cpu_active(cpu, true); | 252 | set_cpu_active(cpu, true); |
234 | /* CPU didn't die: tell everyone. Can't complain. */ | 253 | /* CPU didn't die: tell everyone. Can't complain. */ |
235 | if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, | 254 | cpu_notify_nofail(CPU_DOWN_FAILED | mod, hcpu); |
236 | hcpu) == NOTIFY_BAD) | ||
237 | BUG(); | ||
238 | 255 | ||
239 | goto out_allowed; | 256 | goto out_release; |
240 | } | 257 | } |
241 | BUG_ON(cpu_online(cpu)); | 258 | BUG_ON(cpu_online(cpu)); |
242 | 259 | ||
@@ -248,22 +265,14 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) | |||
248 | __cpu_die(cpu); | 265 | __cpu_die(cpu); |
249 | 266 | ||
250 | /* CPU is completely dead: tell everyone. Too late to complain. */ | 267 | /* CPU is completely dead: tell everyone. Too late to complain. */ |
251 | if (raw_notifier_call_chain(&cpu_chain, CPU_DEAD | mod, | 268 | cpu_notify_nofail(CPU_DEAD | mod, hcpu); |
252 | hcpu) == NOTIFY_BAD) | ||
253 | BUG(); | ||
254 | 269 | ||
255 | check_for_tasks(cpu); | 270 | check_for_tasks(cpu); |
256 | 271 | ||
257 | out_allowed: | ||
258 | set_cpus_allowed_ptr(current, old_allowed); | ||
259 | out_release: | 272 | out_release: |
260 | cpu_hotplug_done(); | 273 | cpu_hotplug_done(); |
261 | if (!err) { | 274 | if (!err) |
262 | if (raw_notifier_call_chain(&cpu_chain, CPU_POST_DEAD | mod, | 275 | cpu_notify_nofail(CPU_POST_DEAD | mod, hcpu); |
263 | hcpu) == NOTIFY_BAD) | ||
264 | BUG(); | ||
265 | } | ||
266 | free_cpumask_var(old_allowed); | ||
267 | return err; | 276 | return err; |
268 | } | 277 | } |
269 | 278 | ||
@@ -271,9 +280,6 @@ int __ref cpu_down(unsigned int cpu) | |||
271 | { | 280 | { |
272 | int err; | 281 | int err; |
273 | 282 | ||
274 | err = stop_machine_create(); | ||
275 | if (err) | ||
276 | return err; | ||
277 | cpu_maps_update_begin(); | 283 | cpu_maps_update_begin(); |
278 | 284 | ||
279 | if (cpu_hotplug_disabled) { | 285 | if (cpu_hotplug_disabled) { |
@@ -285,7 +291,6 @@ int __ref cpu_down(unsigned int cpu) | |||
285 | 291 | ||
286 | out: | 292 | out: |
287 | cpu_maps_update_done(); | 293 | cpu_maps_update_done(); |
288 | stop_machine_destroy(); | ||
289 | return err; | 294 | return err; |
290 | } | 295 | } |
291 | EXPORT_SYMBOL(cpu_down); | 296 | EXPORT_SYMBOL(cpu_down); |
@@ -302,13 +307,11 @@ static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen) | |||
302 | return -EINVAL; | 307 | return -EINVAL; |
303 | 308 | ||
304 | cpu_hotplug_begin(); | 309 | cpu_hotplug_begin(); |
305 | ret = __raw_notifier_call_chain(&cpu_chain, CPU_UP_PREPARE | mod, hcpu, | 310 | ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls); |
306 | -1, &nr_calls); | 311 | if (ret) { |
307 | if (ret == NOTIFY_BAD) { | ||
308 | nr_calls--; | 312 | nr_calls--; |
309 | printk("%s: attempt to bring up CPU %u failed\n", | 313 | printk("%s: attempt to bring up CPU %u failed\n", |
310 | __func__, cpu); | 314 | __func__, cpu); |
311 | ret = -EINVAL; | ||
312 | goto out_notify; | 315 | goto out_notify; |
313 | } | 316 | } |
314 | 317 | ||
@@ -321,12 +324,11 @@ static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen) | |||
321 | set_cpu_active(cpu, true); | 324 | set_cpu_active(cpu, true); |
322 | 325 | ||
323 | /* Now call notifier in preparation. */ | 326 | /* Now call notifier in preparation. */ |
324 | raw_notifier_call_chain(&cpu_chain, CPU_ONLINE | mod, hcpu); | 327 | cpu_notify(CPU_ONLINE | mod, hcpu); |
325 | 328 | ||
326 | out_notify: | 329 | out_notify: |
327 | if (ret != 0) | 330 | if (ret != 0) |
328 | __raw_notifier_call_chain(&cpu_chain, | 331 | __cpu_notify(CPU_UP_CANCELED | mod, hcpu, nr_calls, NULL); |
329 | CPU_UP_CANCELED | mod, hcpu, nr_calls, NULL); | ||
330 | cpu_hotplug_done(); | 332 | cpu_hotplug_done(); |
331 | 333 | ||
332 | return ret; | 334 | return ret; |
@@ -335,16 +337,44 @@ out_notify: | |||
335 | int __cpuinit cpu_up(unsigned int cpu) | 337 | int __cpuinit cpu_up(unsigned int cpu) |
336 | { | 338 | { |
337 | int err = 0; | 339 | int err = 0; |
340 | |||
341 | #ifdef CONFIG_MEMORY_HOTPLUG | ||
342 | int nid; | ||
343 | pg_data_t *pgdat; | ||
344 | #endif | ||
345 | |||
338 | if (!cpu_possible(cpu)) { | 346 | if (!cpu_possible(cpu)) { |
339 | printk(KERN_ERR "can't online cpu %d because it is not " | 347 | printk(KERN_ERR "can't online cpu %d because it is not " |
340 | "configured as may-hotadd at boot time\n", cpu); | 348 | "configured as may-hotadd at boot time\n", cpu); |
341 | #if defined(CONFIG_IA64) || defined(CONFIG_X86_64) | 349 | #if defined(CONFIG_IA64) |
342 | printk(KERN_ERR "please check additional_cpus= boot " | 350 | printk(KERN_ERR "please check additional_cpus= boot " |
343 | "parameter\n"); | 351 | "parameter\n"); |
344 | #endif | 352 | #endif |
345 | return -EINVAL; | 353 | return -EINVAL; |
346 | } | 354 | } |
347 | 355 | ||
356 | #ifdef CONFIG_MEMORY_HOTPLUG | ||
357 | nid = cpu_to_node(cpu); | ||
358 | if (!node_online(nid)) { | ||
359 | err = mem_online_node(nid); | ||
360 | if (err) | ||
361 | return err; | ||
362 | } | ||
363 | |||
364 | pgdat = NODE_DATA(nid); | ||
365 | if (!pgdat) { | ||
366 | printk(KERN_ERR | ||
367 | "Can't online cpu %d due to NULL pgdat\n", cpu); | ||
368 | return -ENOMEM; | ||
369 | } | ||
370 | |||
371 | if (pgdat->node_zonelists->_zonerefs->zone == NULL) { | ||
372 | mutex_lock(&zonelists_mutex); | ||
373 | build_all_zonelists(NULL); | ||
374 | mutex_unlock(&zonelists_mutex); | ||
375 | } | ||
376 | #endif | ||
377 | |||
348 | cpu_maps_update_begin(); | 378 | cpu_maps_update_begin(); |
349 | 379 | ||
350 | if (cpu_hotplug_disabled) { | 380 | if (cpu_hotplug_disabled) { |
@@ -364,11 +394,8 @@ static cpumask_var_t frozen_cpus; | |||
364 | 394 | ||
365 | int disable_nonboot_cpus(void) | 395 | int disable_nonboot_cpus(void) |
366 | { | 396 | { |
367 | int cpu, first_cpu, error; | 397 | int cpu, first_cpu, error = 0; |
368 | 398 | ||
369 | error = stop_machine_create(); | ||
370 | if (error) | ||
371 | return error; | ||
372 | cpu_maps_update_begin(); | 399 | cpu_maps_update_begin(); |
373 | first_cpu = cpumask_first(cpu_online_mask); | 400 | first_cpu = cpumask_first(cpu_online_mask); |
374 | /* | 401 | /* |
@@ -399,7 +426,6 @@ int disable_nonboot_cpus(void) | |||
399 | printk(KERN_ERR "Non-boot CPUs are not disabled\n"); | 426 | printk(KERN_ERR "Non-boot CPUs are not disabled\n"); |
400 | } | 427 | } |
401 | cpu_maps_update_done(); | 428 | cpu_maps_update_done(); |
402 | stop_machine_destroy(); | ||
403 | return error; | 429 | return error; |
404 | } | 430 | } |
405 | 431 | ||
@@ -466,7 +492,7 @@ void __cpuinit notify_cpu_starting(unsigned int cpu) | |||
466 | if (frozen_cpus != NULL && cpumask_test_cpu(cpu, frozen_cpus)) | 492 | if (frozen_cpus != NULL && cpumask_test_cpu(cpu, frozen_cpus)) |
467 | val = CPU_STARTING_FROZEN; | 493 | val = CPU_STARTING_FROZEN; |
468 | #endif /* CONFIG_PM_SLEEP_SMP */ | 494 | #endif /* CONFIG_PM_SLEEP_SMP */ |
469 | raw_notifier_call_chain(&cpu_chain, val, (void *)(long)cpu); | 495 | cpu_notify(val, (void *)(long)cpu); |
470 | } | 496 | } |
471 | 497 | ||
472 | #endif /* CONFIG_SMP */ | 498 | #endif /* CONFIG_SMP */ |