diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-19 22:04:55 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-19 22:04:55 -0500 |
commit | bcbd818c069b9e1bf82517401225b152a33968e2 (patch) | |
tree | 3fcdcf02b15fdd77998589a6158d0e36ba137d1c | |
parent | d652e1eb8e7b739fccbfb503a3da3e9f640fbf3d (diff) | |
parent | 14e568e78f6f80ca1e27256641ddf524c7dbdc51 (diff) |
Merge branch 'smp-hotplug-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull preparatory smp/hotplug patches from Ingo Molnar:
"Some early preparatory changes for the WIP hotplug rework by Thomas
Gleixner."
* 'smp-hotplug-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
stop_machine: Use smpboot threads
stop_machine: Store task reference in a separate per cpu variable
smpboot: Allow selfparking per cpu threads
-rw-r--r-- | include/linux/smpboot.h | 5 | ||||
-rw-r--r-- | kernel/cpu.c | 2 | ||||
-rw-r--r-- | kernel/smpboot.c | 5 | ||||
-rw-r--r-- | kernel/stop_machine.c | 156 |
4 files changed, 70 insertions, 98 deletions
diff --git a/include/linux/smpboot.h b/include/linux/smpboot.h index e0106d8581d3..c65dee059913 100644 --- a/include/linux/smpboot.h +++ b/include/linux/smpboot.h | |||
@@ -14,6 +14,8 @@ struct smpboot_thread_data; | |||
14 | * @thread_should_run: Check whether the thread should run or not. Called with | 14 | * @thread_should_run: Check whether the thread should run or not. Called with |
15 | * preemption disabled. | 15 | * preemption disabled. |
16 | * @thread_fn: The associated thread function | 16 | * @thread_fn: The associated thread function |
17 | * @create: Optional setup function, called when the thread gets | ||
18 | * created (Not called from the thread context) | ||
17 | * @setup: Optional setup function, called when the thread gets | 19 | * @setup: Optional setup function, called when the thread gets |
18 | * operational the first time | 20 | * operational the first time |
19 | * @cleanup: Optional cleanup function, called when the thread | 21 | * @cleanup: Optional cleanup function, called when the thread |
@@ -22,6 +24,7 @@ struct smpboot_thread_data; | |||
22 | * parked (cpu offline) | 24 | * parked (cpu offline) |
23 | * @unpark: Optional unpark function, called when the thread is | 25 | * @unpark: Optional unpark function, called when the thread is |
24 | * unparked (cpu online) | 26 | * unparked (cpu online) |
27 | * @selfparking: Thread is not parked by the park function. | ||
25 | * @thread_comm: The base name of the thread | 28 | * @thread_comm: The base name of the thread |
26 | */ | 29 | */ |
27 | struct smp_hotplug_thread { | 30 | struct smp_hotplug_thread { |
@@ -29,10 +32,12 @@ struct smp_hotplug_thread { | |||
29 | struct list_head list; | 32 | struct list_head list; |
30 | int (*thread_should_run)(unsigned int cpu); | 33 | int (*thread_should_run)(unsigned int cpu); |
31 | void (*thread_fn)(unsigned int cpu); | 34 | void (*thread_fn)(unsigned int cpu); |
35 | void (*create)(unsigned int cpu); | ||
32 | void (*setup)(unsigned int cpu); | 36 | void (*setup)(unsigned int cpu); |
33 | void (*cleanup)(unsigned int cpu, bool online); | 37 | void (*cleanup)(unsigned int cpu, bool online); |
34 | void (*park)(unsigned int cpu); | 38 | void (*park)(unsigned int cpu); |
35 | void (*unpark)(unsigned int cpu); | 39 | void (*unpark)(unsigned int cpu); |
40 | bool selfparking; | ||
36 | const char *thread_comm; | 41 | const char *thread_comm; |
37 | }; | 42 | }; |
38 | 43 | ||
diff --git a/kernel/cpu.c b/kernel/cpu.c index e5d5e8e1e030..b5e4ab2d427e 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
@@ -256,6 +256,8 @@ static int __ref take_cpu_down(void *_param) | |||
256 | return err; | 256 | return err; |
257 | 257 | ||
258 | cpu_notify(CPU_DYING | param->mod, param->hcpu); | 258 | cpu_notify(CPU_DYING | param->mod, param->hcpu); |
259 | /* Park the stopper thread */ | ||
260 | kthread_park(current); | ||
259 | return 0; | 261 | return 0; |
260 | } | 262 | } |
261 | 263 | ||
diff --git a/kernel/smpboot.c b/kernel/smpboot.c index d6c5fc054242..d4abac261779 100644 --- a/kernel/smpboot.c +++ b/kernel/smpboot.c | |||
@@ -183,9 +183,10 @@ __smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu) | |||
183 | kfree(td); | 183 | kfree(td); |
184 | return PTR_ERR(tsk); | 184 | return PTR_ERR(tsk); |
185 | } | 185 | } |
186 | |||
187 | get_task_struct(tsk); | 186 | get_task_struct(tsk); |
188 | *per_cpu_ptr(ht->store, cpu) = tsk; | 187 | *per_cpu_ptr(ht->store, cpu) = tsk; |
188 | if (ht->create) | ||
189 | ht->create(cpu); | ||
189 | return 0; | 190 | return 0; |
190 | } | 191 | } |
191 | 192 | ||
@@ -225,7 +226,7 @@ static void smpboot_park_thread(struct smp_hotplug_thread *ht, unsigned int cpu) | |||
225 | { | 226 | { |
226 | struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu); | 227 | struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu); |
227 | 228 | ||
228 | if (tsk) | 229 | if (tsk && !ht->selfparking) |
229 | kthread_park(tsk); | 230 | kthread_park(tsk); |
230 | } | 231 | } |
231 | 232 | ||
diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index 2f194e965715..95d178c62d5a 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c | |||
@@ -18,7 +18,7 @@ | |||
18 | #include <linux/stop_machine.h> | 18 | #include <linux/stop_machine.h> |
19 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
20 | #include <linux/kallsyms.h> | 20 | #include <linux/kallsyms.h> |
21 | 21 | #include <linux/smpboot.h> | |
22 | #include <linux/atomic.h> | 22 | #include <linux/atomic.h> |
23 | 23 | ||
24 | /* | 24 | /* |
@@ -37,10 +37,10 @@ struct cpu_stopper { | |||
37 | spinlock_t lock; | 37 | spinlock_t lock; |
38 | bool enabled; /* is this stopper enabled? */ | 38 | bool enabled; /* is this stopper enabled? */ |
39 | struct list_head works; /* list of pending works */ | 39 | struct list_head works; /* list of pending works */ |
40 | struct task_struct *thread; /* stopper thread */ | ||
41 | }; | 40 | }; |
42 | 41 | ||
43 | static DEFINE_PER_CPU(struct cpu_stopper, cpu_stopper); | 42 | static DEFINE_PER_CPU(struct cpu_stopper, cpu_stopper); |
43 | static DEFINE_PER_CPU(struct task_struct *, cpu_stopper_task); | ||
44 | static bool stop_machine_initialized = false; | 44 | static bool stop_machine_initialized = false; |
45 | 45 | ||
46 | static void cpu_stop_init_done(struct cpu_stop_done *done, unsigned int nr_todo) | 46 | static void cpu_stop_init_done(struct cpu_stop_done *done, unsigned int nr_todo) |
@@ -62,16 +62,18 @@ static void cpu_stop_signal_done(struct cpu_stop_done *done, bool executed) | |||
62 | } | 62 | } |
63 | 63 | ||
64 | /* queue @work to @stopper. if offline, @work is completed immediately */ | 64 | /* queue @work to @stopper. if offline, @work is completed immediately */ |
65 | static void cpu_stop_queue_work(struct cpu_stopper *stopper, | 65 | static void cpu_stop_queue_work(unsigned int cpu, struct cpu_stop_work *work) |
66 | struct cpu_stop_work *work) | ||
67 | { | 66 | { |
67 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); | ||
68 | struct task_struct *p = per_cpu(cpu_stopper_task, cpu); | ||
69 | |||
68 | unsigned long flags; | 70 | unsigned long flags; |
69 | 71 | ||
70 | spin_lock_irqsave(&stopper->lock, flags); | 72 | spin_lock_irqsave(&stopper->lock, flags); |
71 | 73 | ||
72 | if (stopper->enabled) { | 74 | if (stopper->enabled) { |
73 | list_add_tail(&work->list, &stopper->works); | 75 | list_add_tail(&work->list, &stopper->works); |
74 | wake_up_process(stopper->thread); | 76 | wake_up_process(p); |
75 | } else | 77 | } else |
76 | cpu_stop_signal_done(work->done, false); | 78 | cpu_stop_signal_done(work->done, false); |
77 | 79 | ||
@@ -108,7 +110,7 @@ int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg) | |||
108 | struct cpu_stop_work work = { .fn = fn, .arg = arg, .done = &done }; | 110 | struct cpu_stop_work work = { .fn = fn, .arg = arg, .done = &done }; |
109 | 111 | ||
110 | cpu_stop_init_done(&done, 1); | 112 | cpu_stop_init_done(&done, 1); |
111 | cpu_stop_queue_work(&per_cpu(cpu_stopper, cpu), &work); | 113 | cpu_stop_queue_work(cpu, &work); |
112 | wait_for_completion(&done.completion); | 114 | wait_for_completion(&done.completion); |
113 | return done.executed ? done.ret : -ENOENT; | 115 | return done.executed ? done.ret : -ENOENT; |
114 | } | 116 | } |
@@ -130,7 +132,7 @@ void stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg, | |||
130 | struct cpu_stop_work *work_buf) | 132 | struct cpu_stop_work *work_buf) |
131 | { | 133 | { |
132 | *work_buf = (struct cpu_stop_work){ .fn = fn, .arg = arg, }; | 134 | *work_buf = (struct cpu_stop_work){ .fn = fn, .arg = arg, }; |
133 | cpu_stop_queue_work(&per_cpu(cpu_stopper, cpu), work_buf); | 135 | cpu_stop_queue_work(cpu, work_buf); |
134 | } | 136 | } |
135 | 137 | ||
136 | /* static data for stop_cpus */ | 138 | /* static data for stop_cpus */ |
@@ -159,8 +161,7 @@ static void queue_stop_cpus_work(const struct cpumask *cpumask, | |||
159 | */ | 161 | */ |
160 | preempt_disable(); | 162 | preempt_disable(); |
161 | for_each_cpu(cpu, cpumask) | 163 | for_each_cpu(cpu, cpumask) |
162 | cpu_stop_queue_work(&per_cpu(cpu_stopper, cpu), | 164 | cpu_stop_queue_work(cpu, &per_cpu(stop_cpus_work, cpu)); |
163 | &per_cpu(stop_cpus_work, cpu)); | ||
164 | preempt_enable(); | 165 | preempt_enable(); |
165 | } | 166 | } |
166 | 167 | ||
@@ -244,20 +245,25 @@ int try_stop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg) | |||
244 | return ret; | 245 | return ret; |
245 | } | 246 | } |
246 | 247 | ||
247 | static int cpu_stopper_thread(void *data) | 248 | static int cpu_stop_should_run(unsigned int cpu) |
249 | { | ||
250 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); | ||
251 | unsigned long flags; | ||
252 | int run; | ||
253 | |||
254 | spin_lock_irqsave(&stopper->lock, flags); | ||
255 | run = !list_empty(&stopper->works); | ||
256 | spin_unlock_irqrestore(&stopper->lock, flags); | ||
257 | return run; | ||
258 | } | ||
259 | |||
260 | static void cpu_stopper_thread(unsigned int cpu) | ||
248 | { | 261 | { |
249 | struct cpu_stopper *stopper = data; | 262 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); |
250 | struct cpu_stop_work *work; | 263 | struct cpu_stop_work *work; |
251 | int ret; | 264 | int ret; |
252 | 265 | ||
253 | repeat: | 266 | repeat: |
254 | set_current_state(TASK_INTERRUPTIBLE); /* mb paired w/ kthread_stop */ | ||
255 | |||
256 | if (kthread_should_stop()) { | ||
257 | __set_current_state(TASK_RUNNING); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | work = NULL; | 267 | work = NULL; |
262 | spin_lock_irq(&stopper->lock); | 268 | spin_lock_irq(&stopper->lock); |
263 | if (!list_empty(&stopper->works)) { | 269 | if (!list_empty(&stopper->works)) { |
@@ -273,8 +279,6 @@ repeat: | |||
273 | struct cpu_stop_done *done = work->done; | 279 | struct cpu_stop_done *done = work->done; |
274 | char ksym_buf[KSYM_NAME_LEN] __maybe_unused; | 280 | char ksym_buf[KSYM_NAME_LEN] __maybe_unused; |
275 | 281 | ||
276 | __set_current_state(TASK_RUNNING); | ||
277 | |||
278 | /* cpu stop callbacks are not allowed to sleep */ | 282 | /* cpu stop callbacks are not allowed to sleep */ |
279 | preempt_disable(); | 283 | preempt_disable(); |
280 | 284 | ||
@@ -290,88 +294,55 @@ repeat: | |||
290 | ksym_buf), arg); | 294 | ksym_buf), arg); |
291 | 295 | ||
292 | cpu_stop_signal_done(done, true); | 296 | cpu_stop_signal_done(done, true); |
293 | } else | 297 | goto repeat; |
294 | schedule(); | 298 | } |
295 | |||
296 | goto repeat; | ||
297 | } | 299 | } |
298 | 300 | ||
299 | extern void sched_set_stop_task(int cpu, struct task_struct *stop); | 301 | extern void sched_set_stop_task(int cpu, struct task_struct *stop); |
300 | 302 | ||
301 | /* manage stopper for a cpu, mostly lifted from sched migration thread mgmt */ | 303 | static void cpu_stop_create(unsigned int cpu) |
302 | static int __cpuinit cpu_stop_cpu_callback(struct notifier_block *nfb, | 304 | { |
303 | unsigned long action, void *hcpu) | 305 | sched_set_stop_task(cpu, per_cpu(cpu_stopper_task, cpu)); |
306 | } | ||
307 | |||
308 | static void cpu_stop_park(unsigned int cpu) | ||
304 | { | 309 | { |
305 | unsigned int cpu = (unsigned long)hcpu; | ||
306 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); | 310 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); |
307 | struct task_struct *p; | 311 | struct cpu_stop_work *work; |
308 | 312 | unsigned long flags; | |
309 | switch (action & ~CPU_TASKS_FROZEN) { | ||
310 | case CPU_UP_PREPARE: | ||
311 | BUG_ON(stopper->thread || stopper->enabled || | ||
312 | !list_empty(&stopper->works)); | ||
313 | p = kthread_create_on_node(cpu_stopper_thread, | ||
314 | stopper, | ||
315 | cpu_to_node(cpu), | ||
316 | "migration/%d", cpu); | ||
317 | if (IS_ERR(p)) | ||
318 | return notifier_from_errno(PTR_ERR(p)); | ||
319 | get_task_struct(p); | ||
320 | kthread_bind(p, cpu); | ||
321 | sched_set_stop_task(cpu, p); | ||
322 | stopper->thread = p; | ||
323 | break; | ||
324 | |||
325 | case CPU_ONLINE: | ||
326 | /* strictly unnecessary, as first user will wake it */ | ||
327 | wake_up_process(stopper->thread); | ||
328 | /* mark enabled */ | ||
329 | spin_lock_irq(&stopper->lock); | ||
330 | stopper->enabled = true; | ||
331 | spin_unlock_irq(&stopper->lock); | ||
332 | break; | ||
333 | |||
334 | #ifdef CONFIG_HOTPLUG_CPU | ||
335 | case CPU_UP_CANCELED: | ||
336 | case CPU_POST_DEAD: | ||
337 | { | ||
338 | struct cpu_stop_work *work; | ||
339 | |||
340 | sched_set_stop_task(cpu, NULL); | ||
341 | /* kill the stopper */ | ||
342 | kthread_stop(stopper->thread); | ||
343 | /* drain remaining works */ | ||
344 | spin_lock_irq(&stopper->lock); | ||
345 | list_for_each_entry(work, &stopper->works, list) | ||
346 | cpu_stop_signal_done(work->done, false); | ||
347 | stopper->enabled = false; | ||
348 | spin_unlock_irq(&stopper->lock); | ||
349 | /* release the stopper */ | ||
350 | put_task_struct(stopper->thread); | ||
351 | stopper->thread = NULL; | ||
352 | break; | ||
353 | } | ||
354 | #endif | ||
355 | } | ||
356 | 313 | ||
357 | return NOTIFY_OK; | 314 | /* drain remaining works */ |
315 | spin_lock_irqsave(&stopper->lock, flags); | ||
316 | list_for_each_entry(work, &stopper->works, list) | ||
317 | cpu_stop_signal_done(work->done, false); | ||
318 | stopper->enabled = false; | ||
319 | spin_unlock_irqrestore(&stopper->lock, flags); | ||
358 | } | 320 | } |
359 | 321 | ||
360 | /* | 322 | static void cpu_stop_unpark(unsigned int cpu) |
361 | * Give it a higher priority so that cpu stopper is available to other | 323 | { |
362 | * cpu notifiers. It currently shares the same priority as sched | 324 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); |
363 | * migration_notifier. | 325 | |
364 | */ | 326 | spin_lock_irq(&stopper->lock); |
365 | static struct notifier_block __cpuinitdata cpu_stop_cpu_notifier = { | 327 | stopper->enabled = true; |
366 | .notifier_call = cpu_stop_cpu_callback, | 328 | spin_unlock_irq(&stopper->lock); |
367 | .priority = 10, | 329 | } |
330 | |||
331 | static struct smp_hotplug_thread cpu_stop_threads = { | ||
332 | .store = &cpu_stopper_task, | ||
333 | .thread_should_run = cpu_stop_should_run, | ||
334 | .thread_fn = cpu_stopper_thread, | ||
335 | .thread_comm = "migration/%u", | ||
336 | .create = cpu_stop_create, | ||
337 | .setup = cpu_stop_unpark, | ||
338 | .park = cpu_stop_park, | ||
339 | .unpark = cpu_stop_unpark, | ||
340 | .selfparking = true, | ||
368 | }; | 341 | }; |
369 | 342 | ||
370 | static int __init cpu_stop_init(void) | 343 | static int __init cpu_stop_init(void) |
371 | { | 344 | { |
372 | void *bcpu = (void *)(long)smp_processor_id(); | ||
373 | unsigned int cpu; | 345 | unsigned int cpu; |
374 | int err; | ||
375 | 346 | ||
376 | for_each_possible_cpu(cpu) { | 347 | for_each_possible_cpu(cpu) { |
377 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); | 348 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); |
@@ -380,15 +351,8 @@ static int __init cpu_stop_init(void) | |||
380 | INIT_LIST_HEAD(&stopper->works); | 351 | INIT_LIST_HEAD(&stopper->works); |
381 | } | 352 | } |
382 | 353 | ||
383 | /* start one for the boot cpu */ | 354 | BUG_ON(smpboot_register_percpu_thread(&cpu_stop_threads)); |
384 | err = cpu_stop_cpu_callback(&cpu_stop_cpu_notifier, CPU_UP_PREPARE, | ||
385 | bcpu); | ||
386 | BUG_ON(err != NOTIFY_OK); | ||
387 | cpu_stop_cpu_callback(&cpu_stop_cpu_notifier, CPU_ONLINE, bcpu); | ||
388 | register_cpu_notifier(&cpu_stop_cpu_notifier); | ||
389 | |||
390 | stop_machine_initialized = true; | 355 | stop_machine_initialized = true; |
391 | |||
392 | return 0; | 356 | return 0; |
393 | } | 357 | } |
394 | early_initcall(cpu_stop_init); | 358 | early_initcall(cpu_stop_init); |