Skip to content

Commit 399ced9

Browse files
Frederic Weisbeckerpaulmckrcu
authored andcommitted
rcu/tasks: Fix stale task snaphot for Tasks Trace
When RCU-TASKS-TRACE pre-gp takes a snapshot of the current task running on all online CPUs, no explicit ordering synchronizes properly with a context switch. This lack of ordering can permit the new task to miss pre-grace-period update-side accesses. The following diagram, courtesy of Paul, shows the possible bad scenario: CPU 0 CPU 1 ----- ----- // Pre-GP update side access WRITE_ONCE(*X, 1); smp_mb(); r0 = rq->curr; RCU_INIT_POINTER(rq->curr, TASK_B) spin_unlock(rq) rcu_read_lock_trace() r1 = X; /* ignore TASK_B */ Either r0==TASK_B or r1==1 is needed but neither is guaranteed. One possible solution to solve this is to wait for an RCU grace period at the beginning of the RCU-tasks-trace grace period before taking the current tasks snaphot. However this would introduce large additional latencies to RCU-tasks-trace grace periods. Another solution is to lock the target runqueue while taking the current task snapshot. This ensures that the update side sees the latest context switch and subsequent context switches will see the pre-grace-period update side accesses. This commit therefore adds runqueue locking to cpu_curr_snapshot(). Fixes: e386b67 ("rcu-tasks: Eliminate RCU Tasks Trace IPIs to online CPUs") Signed-off-by: Frederic Weisbecker <[email protected]> Signed-off-by: Paul E. McKenney <[email protected]>
1 parent 9855c37 commit 399ced9

File tree

2 files changed

+17
-7
lines changed

2 files changed

+17
-7
lines changed

kernel/rcu/tasks.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1747,6 +1747,16 @@ static void rcu_tasks_trace_pregp_step(struct list_head *hop)
17471747
// allow safe access to the hop list.
17481748
for_each_online_cpu(cpu) {
17491749
rcu_read_lock();
1750+
// Note that cpu_curr_snapshot() picks up the target
1751+
// CPU's current task while its runqueue is locked with
1752+
// an smp_mb__after_spinlock(). This ensures that either
1753+
// the grace-period kthread will see that task's read-side
1754+
// critical section or the task will see the updater's pre-GP
1755+
// accesses. The trailing smp_mb() in cpu_curr_snapshot()
1756+
// does not currently play a role other than simplify
1757+
// that function's ordering semantics. If these simplified
1758+
// ordering semantics continue to be redundant, that smp_mb()
1759+
// might be removed.
17501760
t = cpu_curr_snapshot(cpu);
17511761
if (rcu_tasks_trace_pertask_prep(t, true))
17521762
trc_add_holdout(t, hop);

kernel/sched/core.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4467,12 +4467,7 @@ int task_call_func(struct task_struct *p, task_call_f func, void *arg)
44674467
* @cpu: The CPU on which to snapshot the task.
44684468
*
44694469
* Returns the task_struct pointer of the task "currently" running on
4470-
* the specified CPU. If the same task is running on that CPU throughout,
4471-
* the return value will be a pointer to that task's task_struct structure.
4472-
* If the CPU did any context switches even vaguely concurrently with the
4473-
* execution of this function, the return value will be a pointer to the
4474-
* task_struct structure of a randomly chosen task that was running on
4475-
* that CPU somewhere around the time that this function was executing.
4470+
* the specified CPU.
44764471
*
44774472
* If the specified CPU was offline, the return value is whatever it
44784473
* is, perhaps a pointer to the task_struct structure of that CPU's idle
@@ -4486,11 +4481,16 @@ int task_call_func(struct task_struct *p, task_call_f func, void *arg)
44864481
*/
44874482
struct task_struct *cpu_curr_snapshot(int cpu)
44884483
{
4484+
struct rq *rq = cpu_rq(cpu);
44894485
struct task_struct *t;
4486+
struct rq_flags rf;
44904487

4491-
smp_mb(); /* Pairing determined by caller's synchronization design. */
4488+
rq_lock_irqsave(rq, &rf);
4489+
smp_mb__after_spinlock(); /* Pairing determined by caller's synchronization design. */
44924490
t = rcu_dereference(cpu_curr(cpu));
4491+
rq_unlock_irqrestore(rq, &rf);
44934492
smp_mb(); /* Pairing determined by caller's synchronization design. */
4493+
44944494
return t;
44954495
}
44964496

0 commit comments

Comments
 (0)