Skip to content

Commit 9715ed5

Browse files
author
Frederic Weisbecker
committed
rcu/tasks: Handle new PF_IDLE semantics
The commit: cff9b23 ("kernel/sched: Modify initial boot task idle setup") has changed the semantics of what is to be considered an idle task in such a way that CPU boot code preceding the actual idle loop is excluded from it. This has however introduced new potential RCU-tasks stalls when either: 1) Grace period is started before init/0 had a chance to set PF_IDLE, keeping it stuck in the holdout list until idle ever schedules. 2) Grace period is started when some possible CPUs have never been online, keeping their idle tasks stuck in the holdout list until the CPU ever boots up. 3) Similar to 1) but with secondary CPUs: Grace period is started concurrently with secondary CPU booting, putting its idle task in the holdout list because PF_IDLE isn't yet observed on it. It stays then stuck in the holdout list until that CPU ever schedules. The effect is mitigated here by the hotplug AP thread that must run to bring the CPU up. Fix this with handling the new semantics of PF_IDLE, keeping in mind that it may or may not be set on an idle task. Take advantage of that to strengthen the coverage of an RCU-tasks quiescent state within an idle task, excluding the CPU boot code from it. Only the code running within the idle loop is now a quiescent state, along with offline CPUs. Fixes: cff9b23 ("kernel/sched: Modify initial boot task idle setup") Suggested-by: Joel Fernandes <joel@joelfernandes.org> Suggested-by: Paul E . McKenney" <paulmck@kernel.org> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
1 parent 2be4686 commit 9715ed5

File tree

1 file changed

+28
-2
lines changed

1 file changed

+28
-2
lines changed

kernel/rcu/tasks.h

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -895,10 +895,36 @@ static void rcu_tasks_pregp_step(struct list_head *hop)
895895
synchronize_rcu();
896896
}
897897

898+
/* Check for quiescent states since the pregp's synchronize_rcu() */
899+
static bool rcu_tasks_is_holdout(struct task_struct *t)
900+
{
901+
int cpu;
902+
903+
/* Has the task been seen voluntarily sleeping? */
904+
if (!READ_ONCE(t->on_rq))
905+
return false;
906+
907+
/*
908+
* Idle tasks (or idle injection) within the idle loop are RCU-tasks
909+
* quiescent states. But CPU boot code performed by the idle task
910+
* isn't a quiescent state.
911+
*/
912+
if (is_idle_task(t))
913+
return false;
914+
915+
cpu = task_cpu(t);
916+
917+
/* Idle tasks on offline CPUs are RCU-tasks quiescent states. */
918+
if (t == idle_task(cpu) && !rcu_cpu_online(cpu))
919+
return false;
920+
921+
return true;
922+
}
923+
898924
/* Per-task initial processing. */
899925
static void rcu_tasks_pertask(struct task_struct *t, struct list_head *hop)
900926
{
901-
if (t != current && READ_ONCE(t->on_rq) && !is_idle_task(t)) {
927+
if (t != current && rcu_tasks_is_holdout(t)) {
902928
get_task_struct(t);
903929
t->rcu_tasks_nvcsw = READ_ONCE(t->nvcsw);
904930
WRITE_ONCE(t->rcu_tasks_holdout, true);
@@ -947,7 +973,7 @@ static void check_holdout_task(struct task_struct *t,
947973

948974
if (!READ_ONCE(t->rcu_tasks_holdout) ||
949975
t->rcu_tasks_nvcsw != READ_ONCE(t->nvcsw) ||
950-
!READ_ONCE(t->on_rq) ||
976+
!rcu_tasks_is_holdout(t) ||
951977
(IS_ENABLED(CONFIG_NO_HZ_FULL) &&
952978
!is_idle_task(t) && t->rcu_tasks_idle_cpu >= 0)) {
953979
WRITE_ONCE(t->rcu_tasks_holdout, false);

0 commit comments

Comments
 (0)