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