diff options
Diffstat (limited to 'kernel/stop_machine.c')
| -rw-r--r-- | kernel/stop_machine.c | 156 |
1 files changed, 60 insertions, 96 deletions
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); |
