Skip to content

Commit 4cb28ce

Browse files
committed
cpu/hotplug: Create hotplug threads
In order to let the hotplugged cpu take care of the setup/teardown, we need a seperate hotplug thread. Signed-off-by: Thomas Gleixner <[email protected]> Cc: [email protected] Cc: Rik van Riel <[email protected]> Cc: Rafael Wysocki <[email protected]> Cc: "Srivatsa S. Bhat" <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Arjan van de Ven <[email protected]> Cc: Sebastian Siewior <[email protected]> Cc: Rusty Russell <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: Oleg Nesterov <[email protected]> Cc: Tejun Heo <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Paul McKenney <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Paul Turner <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]>
1 parent 2e1a348 commit 4cb28ce

File tree

3 files changed

+147
-1
lines changed

3 files changed

+147
-1
lines changed

kernel/cpu.c

Lines changed: 144 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <linux/lockdep.h>
2323
#include <linux/tick.h>
2424
#include <linux/irq.h>
25+
#include <linux/smpboot.h>
2526

2627
#include <trace/events/power.h>
2728
#define CREATE_TRACE_POINTS
@@ -33,10 +34,24 @@
3334
* cpuhp_cpu_state - Per cpu hotplug state storage
3435
* @state: The current cpu state
3536
* @target: The target state
37+
* @thread: Pointer to the hotplug thread
38+
* @should_run: Thread should execute
39+
* @cb_stat: The state for a single callback (install/uninstall)
40+
* @cb: Single callback function (install/uninstall)
41+
* @result: Result of the operation
42+
* @done: Signal completion to the issuer of the task
3643
*/
3744
struct cpuhp_cpu_state {
3845
enum cpuhp_state state;
3946
enum cpuhp_state target;
47+
#ifdef CONFIG_SMP
48+
struct task_struct *thread;
49+
bool should_run;
50+
enum cpuhp_state cb_state;
51+
int (*cb)(unsigned int cpu);
52+
int result;
53+
struct completion done;
54+
#endif
4055
};
4156

4257
static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state);
@@ -394,6 +409,134 @@ static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st,
394409
return ret;
395410
}
396411

412+
/*
413+
* The cpu hotplug threads manage the bringup and teardown of the cpus
414+
*/
415+
static void cpuhp_create(unsigned int cpu)
416+
{
417+
struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
418+
419+
init_completion(&st->done);
420+
}
421+
422+
static int cpuhp_should_run(unsigned int cpu)
423+
{
424+
struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state);
425+
426+
return st->should_run;
427+
}
428+
429+
/* Execute the teardown callbacks. Used to be CPU_DOWN_PREPARE */
430+
static int cpuhp_ap_offline(unsigned int cpu, struct cpuhp_cpu_state *st)
431+
{
432+
enum cpuhp_state target = max((int)st->target, CPUHP_AP_ONLINE);
433+
434+
return cpuhp_down_callbacks(cpu, st, cpuhp_ap_states, target);
435+
}
436+
437+
/* Execute the online startup callbacks. Used to be CPU_ONLINE */
438+
static int cpuhp_ap_online(unsigned int cpu, struct cpuhp_cpu_state *st)
439+
{
440+
return cpuhp_up_callbacks(cpu, st, cpuhp_ap_states, st->target);
441+
}
442+
443+
/*
444+
* Execute teardown/startup callbacks on the plugged cpu. Also used to invoke
445+
* callbacks when a state gets [un]installed at runtime.
446+
*/
447+
static void cpuhp_thread_fun(unsigned int cpu)
448+
{
449+
struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state);
450+
int ret = 0;
451+
452+
/*
453+
* Paired with the mb() in cpuhp_kick_ap_work and
454+
* cpuhp_invoke_ap_callback, so the work set is consistent visible.
455+
*/
456+
smp_mb();
457+
if (!st->should_run)
458+
return;
459+
460+
st->should_run = false;
461+
462+
/* Single callback invocation for [un]install ? */
463+
if (st->cb) {
464+
if (st->cb_state < CPUHP_AP_ONLINE) {
465+
local_irq_disable();
466+
ret = cpuhp_invoke_callback(cpu, st->cb_state, st->cb);
467+
local_irq_enable();
468+
} else {
469+
ret = cpuhp_invoke_callback(cpu, st->cb_state, st->cb);
470+
}
471+
} else {
472+
/* Regular hotplug work */
473+
if (st->state < st->target)
474+
ret = cpuhp_ap_online(cpu, st);
475+
else if (st->state > st->target)
476+
ret = cpuhp_ap_offline(cpu, st);
477+
}
478+
st->result = ret;
479+
complete(&st->done);
480+
}
481+
482+
/* Invoke a single callback on a remote cpu */
483+
static int cpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state,
484+
int (*cb)(unsigned int))
485+
{
486+
struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
487+
488+
if (!cpu_online(cpu))
489+
return 0;
490+
491+
st->cb_state = state;
492+
st->cb = cb;
493+
/*
494+
* Make sure the above stores are visible before should_run becomes
495+
* true. Paired with the mb() above in cpuhp_thread_fun()
496+
*/
497+
smp_mb();
498+
st->should_run = true;
499+
wake_up_process(st->thread);
500+
wait_for_completion(&st->done);
501+
return st->result;
502+
}
503+
504+
/* Regular hotplug invocation of the AP hotplug thread */
505+
static int cpuhp_kick_ap_work(unsigned int cpu)
506+
{
507+
struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
508+
enum cpuhp_state state = st->state;
509+
510+
trace_cpuhp_enter(cpu, st->target, state, cpuhp_kick_ap_work);
511+
st->result = 0;
512+
st->cb = NULL;
513+
/*
514+
* Make sure the above stores are visible before should_run becomes
515+
* true. Paired with the mb() above in cpuhp_thread_fun()
516+
*/
517+
smp_mb();
518+
st->should_run = true;
519+
wake_up_process(st->thread);
520+
wait_for_completion(&st->done);
521+
trace_cpuhp_exit(cpu, st->state, state, st->result);
522+
return st->result;
523+
}
524+
525+
static struct smp_hotplug_thread cpuhp_threads = {
526+
.store = &cpuhp_state.thread,
527+
.create = &cpuhp_create,
528+
.thread_should_run = cpuhp_should_run,
529+
.thread_fn = cpuhp_thread_fun,
530+
.thread_comm = "cpuhp/%u",
531+
.selfparking = true,
532+
};
533+
534+
void __init cpuhp_threads_init(void)
535+
{
536+
BUG_ON(smpboot_register_percpu_thread(&cpuhp_threads));
537+
kthread_unpark(this_cpu_read(cpuhp_state.thread));
538+
}
539+
397540
#ifdef CONFIG_HOTPLUG_CPU
398541
EXPORT_SYMBOL(register_cpu_notifier);
399542
EXPORT_SYMBOL(__register_cpu_notifier);
@@ -997,7 +1140,7 @@ static int cpuhp_cb_check(enum cpuhp_state state)
9971140

9981141
static bool cpuhp_is_ap_state(enum cpuhp_state state)
9991142
{
1000-
return (state > CPUHP_AP_OFFLINE && state < CPUHP_AP_ONLINE);
1143+
return (state >= CPUHP_AP_OFFLINE && state <= CPUHP_AP_ONLINE);
10011144
}
10021145

10031146
static struct cpuhp_step *cpuhp_get_step(enum cpuhp_state state)

kernel/smp.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ void __init smp_init(void)
569569
unsigned int cpu;
570570

571571
idle_threads_init();
572+
cpuhp_threads_init();
572573

573574
/* FIXME: This should be done in userspace --RR */
574575
for_each_present_cpu(cpu) {

kernel/smpboot.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ int smpboot_create_threads(unsigned int cpu);
1717
int smpboot_park_threads(unsigned int cpu);
1818
int smpboot_unpark_threads(unsigned int cpu);
1919

20+
void __init cpuhp_threads_init(void);
21+
2022
#endif

0 commit comments

Comments
 (0)