History

sched: Fix unreliable rseq cpu_id for new tasks

While integrating rseq into glibc and replacing glibc's sched_getcpu implementation with rseq, glibc's tests discovered an issue with incorrect __rseq_abi.cpu_id field value right after the first time a newly created process issues sched_setaffinity. For the records, it triggers after building glibc and running tests, and then issuing: for x in {1..2000} ; do posix/tst-affinity-static & done and shows up as: error: Unexpected CPU 2, expected 0 error: Unexpected CPU 2, expected 0 error: Unexpected CPU 2, expected 0 error: Unexpected CPU 2, expected 0 error: Unexpected CPU 138, expected 0 error: Unexpected CPU 138, expected 0 error: Unexpected CPU 138, expected 0 error: Unexpected CPU 138, expected 0 This is caused by the scheduler invoking __set_task_cpu() directly from sched_fork() and wake_up_new_task(), thus bypassing rseq_migrate() which is done by set_task_cpu(). Add the missing rseq_migrate() to both functions. The only other direct use of __set_task_cpu() is done by init_idle(), which does not involve a user-space task. Based on my testing with the glibc test-case, just adding rseq_migrate() to wake_up_new_task() is sufficient to fix the observed issue. Also add it to sched_fork() to keep things consistent. The reason why this never triggered so far with the rseq/basic_test selftest is unclear. The current use of sched_getcpu(3) does not typically require it to be always accurate. However, use of the __rseq_abi.cpu_id field within rseq critical sections requires it to be accurate. If it is not accurate, it can cause corruption in the per-cpu data targeted by rseq critical sections in user-space.

Mathieu Desnoyers committed 6 years ago

Commit ce3614d1 parent dbfb0891 file · +2 −0Browse files

Unified · Split

kernel/sched/core.c+2−0
@@ -2965,6 +2965,7 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p) · 2964 unmodified lines
29652965 * Silence PROVE_RCU.
29662966 */
29672967 raw_spin_lock_irqsave(&p->pi_lock, flags);
2968+ rseq_migrate(p);
29682969 /*
29692970 * We're setting the CPU for the first time, we don't migrate,
29702971 * so use __set_task_cpu().
@@ -3029,6 +3030,7 @@ void wake_up_new_task(struct task_struct *p) · 58 unmodified lines
30293030 * as we're not fully set-up yet.
30303031 */
30313032 p->recent_used_cpu = task_cpu(p);
3033+ rseq_migrate(p);
30323034 __set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0));
30333035 #endif
30343036 rq = __task_rq_lock(p, &rf);