diff options
| -rw-r--r-- | kernel/cpu.c | 2 | ||||
| -rw-r--r-- | kernel/stop_machine.c | 136 |
2 files changed, 52 insertions, 86 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index 3046a503242c..c91e30d1ef05 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c | |||
| @@ -254,6 +254,8 @@ static int __ref take_cpu_down(void *_param) | |||
| 254 | return err; | 254 | return err; |
| 255 | 255 | ||
| 256 | cpu_notify(CPU_DYING | param->mod, param->hcpu); | 256 | cpu_notify(CPU_DYING | param->mod, param->hcpu); |
| 257 | /* Park the stopper thread */ | ||
| 258 | kthread_park(current); | ||
| 257 | return 0; | 259 | return 0; |
| 258 | } | 260 | } |
| 259 | 261 | ||
diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index aaac68c5c3be..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 | /* |
| @@ -245,20 +245,25 @@ int try_stop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg) | |||
| 245 | return ret; | 245 | return ret; |
| 246 | } | 246 | } |
| 247 | 247 | ||
| 248 | 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) | ||
| 249 | { | 261 | { |
| 250 | struct cpu_stopper *stopper = data; | 262 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); |
| 251 | struct cpu_stop_work *work; | 263 | struct cpu_stop_work *work; |
| 252 | int ret; | 264 | int ret; |
| 253 | 265 | ||
| 254 | repeat: | 266 | repeat: |
| 255 | set_current_state(TASK_INTERRUPTIBLE); /* mb paired w/ kthread_stop */ | ||
| 256 | |||
| 257 | if (kthread_should_stop()) { | ||
| 258 | __set_current_state(TASK_RUNNING); | ||
| 259 | return 0; | ||
| 260 | } | ||
| 261 | |||
| 262 | work = NULL; | 267 | work = NULL; |
| 263 | spin_lock_irq(&stopper->lock); | 268 | spin_lock_irq(&stopper->lock); |
| 264 | if (!list_empty(&stopper->works)) { | 269 | if (!list_empty(&stopper->works)) { |
| @@ -274,8 +279,6 @@ repeat: | |||
| 274 | struct cpu_stop_done *done = work->done; | 279 | struct cpu_stop_done *done = work->done; |
| 275 | char ksym_buf[KSYM_NAME_LEN] __maybe_unused; | 280 | char ksym_buf[KSYM_NAME_LEN] __maybe_unused; |
| 276 | 281 | ||
| 277 | __set_current_state(TASK_RUNNING); | ||
| 278 | |||
| 279 | /* cpu stop callbacks are not allowed to sleep */ | 282 | /* cpu stop callbacks are not allowed to sleep */ |
| 280 | preempt_disable(); | 283 | preempt_disable(); |
| 281 | 284 | ||
| @@ -291,87 +294,55 @@ repeat: | |||
| 291 | ksym_buf), arg); | 294 | ksym_buf), arg); |
| 292 | 295 | ||
| 293 | cpu_stop_signal_done(done, true); | 296 | cpu_stop_signal_done(done, true); |
| 294 | } else | 297 | goto repeat; |
| 295 | schedule(); | 298 | } |
| 296 | |||
| 297 | goto repeat; | ||
| 298 | } | 299 | } |
| 299 | 300 | ||
| 300 | 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); |
| 301 | 302 | ||
| 302 | /* manage stopper for a cpu, mostly lifted from sched migration thread mgmt */ | 303 | static void cpu_stop_create(unsigned int cpu) |
| 303 | static int __cpuinit cpu_stop_cpu_callback(struct notifier_block *nfb, | 304 | { |
| 304 | 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) | ||
| 305 | { | 309 | { |
| 306 | unsigned int cpu = (unsigned long)hcpu; | ||
| 307 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); | 310 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); |
| 308 | struct task_struct *p = per_cpu(cpu_stopper_task, cpu); | 311 | struct cpu_stop_work *work; |
| 312 | unsigned long flags; | ||
| 309 | 313 | ||
| 310 | switch (action & ~CPU_TASKS_FROZEN) { | 314 | /* drain remaining works */ |
| 311 | case CPU_UP_PREPARE: | 315 | spin_lock_irqsave(&stopper->lock, flags); |
| 312 | BUG_ON(p || stopper->enabled || !list_empty(&stopper->works)); | 316 | list_for_each_entry(work, &stopper->works, list) |
| 313 | p = kthread_create_on_node(cpu_stopper_thread, | 317 | cpu_stop_signal_done(work->done, false); |
| 314 | stopper, | 318 | stopper->enabled = false; |
| 315 | cpu_to_node(cpu), | 319 | spin_unlock_irqrestore(&stopper->lock, flags); |
| 316 | "migration/%d", cpu); | 320 | } |
| 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 | per_cpu(cpu_stopper_task, cpu) = p; | ||
| 323 | break; | ||
| 324 | |||
| 325 | case CPU_ONLINE: | ||
| 326 | /* strictly unnecessary, as first user will wake it */ | ||
| 327 | wake_up_process(p); | ||
| 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(p); | ||
| 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(p); | ||
| 351 | per_cpu(cpu_stopper_task, cpu) = NULL; | ||
| 352 | break; | ||
| 353 | } | ||
| 354 | #endif | ||
| 355 | } | ||
| 356 | 321 | ||
| 357 | return NOTIFY_OK; | 322 | static void cpu_stop_unpark(unsigned int cpu) |
| 323 | { | ||
| 324 | struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); | ||
| 325 | |||
| 326 | spin_lock_irq(&stopper->lock); | ||
| 327 | stopper->enabled = true; | ||
| 328 | spin_unlock_irq(&stopper->lock); | ||
| 358 | } | 329 | } |
| 359 | 330 | ||
| 360 | /* | 331 | static struct smp_hotplug_thread cpu_stop_threads = { |
| 361 | * Give it a higher priority so that cpu stopper is available to other | 332 | .store = &cpu_stopper_task, |
| 362 | * cpu notifiers. It currently shares the same priority as sched | 333 | .thread_should_run = cpu_stop_should_run, |
| 363 | * migration_notifier. | 334 | .thread_fn = cpu_stopper_thread, |
| 364 | */ | 335 | .thread_comm = "migration/%u", |
| 365 | static struct notifier_block __cpuinitdata cpu_stop_cpu_notifier = { | 336 | .create = cpu_stop_create, |
| 366 | .notifier_call = cpu_stop_cpu_callback, | 337 | .setup = cpu_stop_unpark, |
| 367 | .priority = 10, | 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); |
