Commit 49dfe2a6 authored by Thomas Gleixner's avatar Thomas Gleixner

cpuhotplug: Link lock stacks for hotplug callbacks

The CPU hotplug callbacks are not covered by lockdep versus the cpu hotplug
rwsem.

CPU0						CPU1
cpuhp_setup_state(STATE, startup, teardown);
 cpus_read_lock();
  invoke_callback_on_ap();
    kick_hotplug_thread(ap);
    wait_for_completion();			hotplug_thread_fn()
    						  lock(m);
						  do_stuff();
						  unlock(m);

Lockdep does not know about this dependency and will not trigger on the
following code sequence:

	  lock(m);
	  cpus_read_lock();
	  
Add a lockdep map and connect the initiators lock chain with the hotplug
thread lock chain, so potential deadlocks can be detected.
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Tested-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Acked-by: default avatarIngo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sebastian Siewior <bigeasy@linutronix.de>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/20170524081549.709375845@linutronix.de
parent 0266d81e
...@@ -66,6 +66,12 @@ struct cpuhp_cpu_state { ...@@ -66,6 +66,12 @@ struct cpuhp_cpu_state {
static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state); static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state);
#if defined(CONFIG_LOCKDEP) && defined(CONFIG_SMP)
static struct lock_class_key cpuhp_state_key;
static struct lockdep_map cpuhp_state_lock_map =
STATIC_LOCKDEP_MAP_INIT("cpuhp_state", &cpuhp_state_key);
#endif
/** /**
* cpuhp_step - Hotplug state machine step * cpuhp_step - Hotplug state machine step
* @name: Name of the step * @name: Name of the step
...@@ -403,6 +409,7 @@ static void cpuhp_thread_fun(unsigned int cpu) ...@@ -403,6 +409,7 @@ static void cpuhp_thread_fun(unsigned int cpu)
st->should_run = false; st->should_run = false;
lock_map_acquire(&cpuhp_state_lock_map);
/* Single callback invocation for [un]install ? */ /* Single callback invocation for [un]install ? */
if (st->single) { if (st->single) {
if (st->cb_state < CPUHP_AP_ONLINE) { if (st->cb_state < CPUHP_AP_ONLINE) {
...@@ -429,6 +436,7 @@ static void cpuhp_thread_fun(unsigned int cpu) ...@@ -429,6 +436,7 @@ static void cpuhp_thread_fun(unsigned int cpu)
else if (st->state > st->target) else if (st->state > st->target)
ret = cpuhp_ap_offline(cpu, st); ret = cpuhp_ap_offline(cpu, st);
} }
lock_map_release(&cpuhp_state_lock_map);
st->result = ret; st->result = ret;
complete(&st->done); complete(&st->done);
} }
...@@ -443,6 +451,9 @@ cpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state, bool bringup, ...@@ -443,6 +451,9 @@ cpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state, bool bringup,
if (!cpu_online(cpu)) if (!cpu_online(cpu))
return 0; return 0;
lock_map_acquire(&cpuhp_state_lock_map);
lock_map_release(&cpuhp_state_lock_map);
/* /*
* If we are up and running, use the hotplug thread. For early calls * If we are up and running, use the hotplug thread. For early calls
* we invoke the thread function directly. * we invoke the thread function directly.
...@@ -486,6 +497,8 @@ static int cpuhp_kick_ap_work(unsigned int cpu) ...@@ -486,6 +497,8 @@ static int cpuhp_kick_ap_work(unsigned int cpu)
enum cpuhp_state state = st->state; enum cpuhp_state state = st->state;
trace_cpuhp_enter(cpu, st->target, state, cpuhp_kick_ap_work); trace_cpuhp_enter(cpu, st->target, state, cpuhp_kick_ap_work);
lock_map_acquire(&cpuhp_state_lock_map);
lock_map_release(&cpuhp_state_lock_map);
__cpuhp_kick_ap_work(st); __cpuhp_kick_ap_work(st);
wait_for_completion(&st->done); wait_for_completion(&st->done);
trace_cpuhp_exit(cpu, st->state, state, st->result); trace_cpuhp_exit(cpu, st->state, state, st->result);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment