Skip to content

Commit

Permalink
kernel/cpu: fix cpu down problem if kthread's cpu is going down
Browse files Browse the repository at this point in the history
If kthread is pinned to CPUx and CPUx is going down then we get into
trouble:
- first the unplug thread is created
- it will set itself to hp->unplug. As a result, every task that is
  going to take a lock, has to leave the CPU.
- the CPU_DOWN_PREPARE notifier are started. The worker thread will
  start a new process for the "high priority worker".
  Now kthread would like to take a lock but since it can't leave the CPU
  it will never complete its task.

We could fire the unplug thread after the notifier but then the cpu is
no longer marked "online" and the unplug thread will run on CPU0 which
was fixed before :)

So instead the unplug thread is started and kept waiting until the
notfier complete their work.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
  • Loading branch information
Sebastian Andrzej Siewior authored and jnettlet committed Sep 4, 2018
1 parent 9953060 commit ba05997
Showing 1 changed file with 13 additions and 2 deletions.
15 changes: 13 additions & 2 deletions kernel/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ struct hotplug_pcp {
int refcount;
int grab_lock;
struct completion synced;
struct completion unplug_wait;
#ifdef CONFIG_PREEMPT_RT_FULL
/*
* Note, on PREEMPT_RT, the hotplug lock must save the state of
Expand Down Expand Up @@ -369,6 +370,7 @@ static int sync_unplug_thread(void *data)
{
struct hotplug_pcp *hp = data;

wait_for_completion(&hp->unplug_wait);
preempt_disable();
hp->unplug = current;
wait_for_pinned_cpus(hp);
Expand Down Expand Up @@ -434,6 +436,14 @@ static void __cpu_unplug_sync(struct hotplug_pcp *hp)
wait_for_completion(&hp->synced);
}

static void __cpu_unplug_wait(unsigned int cpu)
{
struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu);

complete(&hp->unplug_wait);
wait_for_completion(&hp->synced);
}

/*
* Start the sync_unplug_thread on the target cpu and wait for it to
* complete.
Expand All @@ -457,6 +467,7 @@ static int cpu_unplug_begin(unsigned int cpu)
tell_sched_cpu_down_begin(cpu);

init_completion(&hp->synced);
init_completion(&hp->unplug_wait);

hp->sync_tsk = kthread_create(sync_unplug_thread, hp, "sync_unplug/%d", cpu);
if (IS_ERR(hp->sync_tsk)) {
Expand All @@ -472,8 +483,7 @@ static int cpu_unplug_begin(unsigned int cpu)
* wait for tasks that are going to enter these sections and
* we must not have them block.
*/
__cpu_unplug_sync(hp);

wake_up_process(hp->sync_tsk);
return 0;
}

Expand Down Expand Up @@ -1181,6 +1191,7 @@ static int takedown_cpu(unsigned int cpu)
struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
int err;

__cpu_unplug_wait(cpu);
/* Park the smpboot threads */
kthread_park(per_cpu_ptr(&cpuhp_state, cpu)->thread);

Expand Down

0 comments on commit ba05997

Please sign in to comment.