mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
cpu/hotplug: Add cpuhp_invoke_callback_range()
Factorizing and unifying cpuhp callback range invocations, especially for the hotunplug path, where two different ways of decrementing were used. The first one, decrements before the callback is called: cpuhp_thread_fun() state = st->state; st->state--; cpuhp_invoke_callback(state); The second one, after: take_down_cpu()|cpuhp_down_callbacks() cpuhp_invoke_callback(st->state); st->state--; This is problematic for rolling back the steps in case of error, as depending on the decrement, the rollback will start from N or N-1. It also makes tracing inconsistent, between steps run in the cpuhp thread and the others. Additionally, avoid useless cpuhp_thread_fun() loops by skipping empty steps. Signed-off-by: Vincent Donnefort <vincent.donnefort@arm.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Ingo Molnar <mingo@kernel.org> Link: https://lkml.kernel.org/r/20210216103506.416286-4-vincent.donnefort@arm.com
This commit is contained in:
parent
62f2506940
commit
453e410851
148
kernel/cpu.c
148
kernel/cpu.c
@ -135,6 +135,11 @@ static struct cpuhp_step *cpuhp_get_step(enum cpuhp_state state)
|
|||||||
return cpuhp_hp_states + state;
|
return cpuhp_hp_states + state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool cpuhp_step_empty(bool bringup, struct cpuhp_step *step)
|
||||||
|
{
|
||||||
|
return bringup ? !step->startup.single : !step->teardown.single;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cpuhp_invoke_callback _ Invoke the callbacks for a given state
|
* cpuhp_invoke_callback _ Invoke the callbacks for a given state
|
||||||
* @cpu: The cpu for which the callback should be invoked
|
* @cpu: The cpu for which the callback should be invoked
|
||||||
@ -157,26 +162,24 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state,
|
|||||||
|
|
||||||
if (st->fail == state) {
|
if (st->fail == state) {
|
||||||
st->fail = CPUHP_INVALID;
|
st->fail = CPUHP_INVALID;
|
||||||
|
|
||||||
if (!(bringup ? step->startup.single : step->teardown.single))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cpuhp_step_empty(bringup, step)) {
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!step->multi_instance) {
|
if (!step->multi_instance) {
|
||||||
WARN_ON_ONCE(lastp && *lastp);
|
WARN_ON_ONCE(lastp && *lastp);
|
||||||
cb = bringup ? step->startup.single : step->teardown.single;
|
cb = bringup ? step->startup.single : step->teardown.single;
|
||||||
if (!cb)
|
|
||||||
return 0;
|
|
||||||
trace_cpuhp_enter(cpu, st->target, state, cb);
|
trace_cpuhp_enter(cpu, st->target, state, cb);
|
||||||
ret = cb(cpu);
|
ret = cb(cpu);
|
||||||
trace_cpuhp_exit(cpu, st->state, state, ret);
|
trace_cpuhp_exit(cpu, st->state, state, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
cbm = bringup ? step->startup.multi : step->teardown.multi;
|
cbm = bringup ? step->startup.multi : step->teardown.multi;
|
||||||
if (!cbm)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Single invocation for instance add/remove */
|
/* Single invocation for instance add/remove */
|
||||||
if (node) {
|
if (node) {
|
||||||
@ -475,6 +478,15 @@ cpuhp_set_state(struct cpuhp_cpu_state *st, enum cpuhp_state target)
|
|||||||
static inline void
|
static inline void
|
||||||
cpuhp_reset_state(struct cpuhp_cpu_state *st, enum cpuhp_state prev_state)
|
cpuhp_reset_state(struct cpuhp_cpu_state *st, enum cpuhp_state prev_state)
|
||||||
{
|
{
|
||||||
|
st->target = prev_state;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Already rolling back. No need invert the bringup value or to change
|
||||||
|
* the current state.
|
||||||
|
*/
|
||||||
|
if (st->rollback)
|
||||||
|
return;
|
||||||
|
|
||||||
st->rollback = true;
|
st->rollback = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -488,7 +500,6 @@ cpuhp_reset_state(struct cpuhp_cpu_state *st, enum cpuhp_state prev_state)
|
|||||||
st->state++;
|
st->state++;
|
||||||
}
|
}
|
||||||
|
|
||||||
st->target = prev_state;
|
|
||||||
st->bringup = !st->bringup;
|
st->bringup = !st->bringup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,10 +602,53 @@ static int finish_cpu(unsigned int cpu)
|
|||||||
* Hotplug state machine related functions
|
* Hotplug state machine related functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void undo_cpu_up(unsigned int cpu, struct cpuhp_cpu_state *st)
|
/*
|
||||||
|
* Get the next state to run. Empty ones will be skipped. Returns true if a
|
||||||
|
* state must be run.
|
||||||
|
*
|
||||||
|
* st->state will be modified ahead of time, to match state_to_run, as if it
|
||||||
|
* has already ran.
|
||||||
|
*/
|
||||||
|
static bool cpuhp_next_state(bool bringup,
|
||||||
|
enum cpuhp_state *state_to_run,
|
||||||
|
struct cpuhp_cpu_state *st,
|
||||||
|
enum cpuhp_state target)
|
||||||
{
|
{
|
||||||
for (st->state--; st->state > st->target; st->state--)
|
do {
|
||||||
cpuhp_invoke_callback(cpu, st->state, false, NULL, NULL);
|
if (bringup) {
|
||||||
|
if (st->state >= target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*state_to_run = ++st->state;
|
||||||
|
} else {
|
||||||
|
if (st->state <= target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*state_to_run = st->state--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cpuhp_step_empty(bringup, cpuhp_get_step(*state_to_run)))
|
||||||
|
break;
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cpuhp_invoke_callback_range(bool bringup,
|
||||||
|
unsigned int cpu,
|
||||||
|
struct cpuhp_cpu_state *st,
|
||||||
|
enum cpuhp_state target)
|
||||||
|
{
|
||||||
|
enum cpuhp_state state;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
while (cpuhp_next_state(bringup, &state, st, target)) {
|
||||||
|
err = cpuhp_invoke_callback(cpu, state, bringup, NULL, NULL);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool can_rollback_cpu(struct cpuhp_cpu_state *st)
|
static inline bool can_rollback_cpu(struct cpuhp_cpu_state *st)
|
||||||
@ -617,16 +671,12 @@ static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st,
|
|||||||
enum cpuhp_state prev_state = st->state;
|
enum cpuhp_state prev_state = st->state;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
while (st->state < target) {
|
ret = cpuhp_invoke_callback_range(true, cpu, st, target);
|
||||||
st->state++;
|
|
||||||
ret = cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (can_rollback_cpu(st)) {
|
cpuhp_reset_state(st, prev_state);
|
||||||
st->target = prev_state;
|
if (can_rollback_cpu(st))
|
||||||
undo_cpu_up(cpu, st);
|
WARN_ON(cpuhp_invoke_callback_range(false, cpu, st,
|
||||||
}
|
prev_state));
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -690,17 +740,9 @@ static void cpuhp_thread_fun(unsigned int cpu)
|
|||||||
state = st->cb_state;
|
state = st->cb_state;
|
||||||
st->should_run = false;
|
st->should_run = false;
|
||||||
} else {
|
} else {
|
||||||
if (bringup) {
|
st->should_run = cpuhp_next_state(bringup, &state, st, st->target);
|
||||||
st->state++;
|
if (!st->should_run)
|
||||||
state = st->state;
|
goto end;
|
||||||
st->should_run = (st->state < st->target);
|
|
||||||
WARN_ON_ONCE(st->state > st->target);
|
|
||||||
} else {
|
|
||||||
state = st->state;
|
|
||||||
st->state--;
|
|
||||||
st->should_run = (st->state > st->target);
|
|
||||||
WARN_ON_ONCE(st->state < st->target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WARN_ON_ONCE(!cpuhp_is_ap_state(state));
|
WARN_ON_ONCE(!cpuhp_is_ap_state(state));
|
||||||
@ -728,6 +770,7 @@ static void cpuhp_thread_fun(unsigned int cpu)
|
|||||||
st->should_run = false;
|
st->should_run = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
cpuhp_lock_release(bringup);
|
cpuhp_lock_release(bringup);
|
||||||
lockdep_release_cpus_lock();
|
lockdep_release_cpus_lock();
|
||||||
|
|
||||||
@ -881,19 +924,18 @@ static int take_cpu_down(void *_param)
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We get here while we are in CPUHP_TEARDOWN_CPU state and we must not
|
* Must be called from CPUHP_TEARDOWN_CPU, which means, as we are going
|
||||||
* do this step again.
|
* down, that the current state is CPUHP_TEARDOWN_CPU - 1.
|
||||||
*/
|
*/
|
||||||
WARN_ON(st->state != CPUHP_TEARDOWN_CPU);
|
WARN_ON(st->state != (CPUHP_TEARDOWN_CPU - 1));
|
||||||
st->state--;
|
|
||||||
/* Invoke the former CPU_DYING callbacks */
|
/* Invoke the former CPU_DYING callbacks */
|
||||||
for (; st->state > target; st->state--) {
|
ret = cpuhp_invoke_callback_range(false, cpu, st, target);
|
||||||
ret = cpuhp_invoke_callback(cpu, st->state, false, NULL, NULL);
|
|
||||||
/*
|
/*
|
||||||
* DYING must not fail!
|
* DYING must not fail!
|
||||||
*/
|
*/
|
||||||
WARN_ON_ONCE(ret);
|
WARN_ON_ONCE(ret);
|
||||||
}
|
|
||||||
|
|
||||||
/* Give up timekeeping duties */
|
/* Give up timekeeping duties */
|
||||||
tick_handover_do_timer();
|
tick_handover_do_timer();
|
||||||
@ -975,27 +1017,22 @@ void cpuhp_report_idle_dead(void)
|
|||||||
cpuhp_complete_idle_dead, st, 0);
|
cpuhp_complete_idle_dead, st, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void undo_cpu_down(unsigned int cpu, struct cpuhp_cpu_state *st)
|
|
||||||
{
|
|
||||||
for (st->state++; st->state < st->target; st->state++)
|
|
||||||
cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cpuhp_down_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st,
|
static int cpuhp_down_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st,
|
||||||
enum cpuhp_state target)
|
enum cpuhp_state target)
|
||||||
{
|
{
|
||||||
enum cpuhp_state prev_state = st->state;
|
enum cpuhp_state prev_state = st->state;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
for (; st->state > target; st->state--) {
|
ret = cpuhp_invoke_callback_range(false, cpu, st, target);
|
||||||
ret = cpuhp_invoke_callback(cpu, st->state, false, NULL, NULL);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
st->target = prev_state;
|
|
||||||
|
cpuhp_reset_state(st, prev_state);
|
||||||
|
|
||||||
if (st->state < prev_state)
|
if (st->state < prev_state)
|
||||||
undo_cpu_down(cpu, st);
|
WARN_ON(cpuhp_invoke_callback_range(true, cpu, st,
|
||||||
break;
|
prev_state));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1168,14 +1205,12 @@ void notify_cpu_starting(unsigned int cpu)
|
|||||||
|
|
||||||
rcu_cpu_starting(cpu); /* Enables RCU usage on this CPU. */
|
rcu_cpu_starting(cpu); /* Enables RCU usage on this CPU. */
|
||||||
cpumask_set_cpu(cpu, &cpus_booted_once_mask);
|
cpumask_set_cpu(cpu, &cpus_booted_once_mask);
|
||||||
while (st->state < target) {
|
ret = cpuhp_invoke_callback_range(true, cpu, st, target);
|
||||||
st->state++;
|
|
||||||
ret = cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL);
|
|
||||||
/*
|
/*
|
||||||
* STARTING must not fail!
|
* STARTING must not fail!
|
||||||
*/
|
*/
|
||||||
WARN_ON_ONCE(ret);
|
WARN_ON_ONCE(ret);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1781,8 +1816,7 @@ static int cpuhp_issue_call(int cpu, enum cpuhp_state state, bool bringup,
|
|||||||
* If there's nothing to do, we done.
|
* If there's nothing to do, we done.
|
||||||
* Relies on the union for multi_instance.
|
* Relies on the union for multi_instance.
|
||||||
*/
|
*/
|
||||||
if ((bringup && !sp->startup.single) ||
|
if (cpuhp_step_empty(bringup, sp))
|
||||||
(!bringup && !sp->teardown.single))
|
|
||||||
return 0;
|
return 0;
|
||||||
/*
|
/*
|
||||||
* The non AP bound callbacks can fail on bringup. On teardown
|
* The non AP bound callbacks can fail on bringup. On teardown
|
||||||
|
Loading…
Reference in New Issue
Block a user