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.
Unified · Split
| @@ -2965,6 +2965,7 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p) · 2964 unmodified lines | ||
| 2965 | 2965 | * Silence PROVE_RCU. |
| 2966 | 2966 | */ |
| 2967 | 2967 | raw_spin_lock_irqsave(&p->pi_lock, flags); |
| 2968 | + rseq_migrate(p); | |
| 2968 | 2969 | /* |
| 2969 | 2970 | * We're setting the CPU for the first time, we don't migrate, |
| 2970 | 2971 | * so use __set_task_cpu(). |
| @@ -3029,6 +3030,7 @@ void wake_up_new_task(struct task_struct *p) · 58 unmodified lines | ||
| 3029 | 3030 | * as we're not fully set-up yet. |
| 3030 | 3031 | */ |
| 3031 | 3032 | p->recent_used_cpu = task_cpu(p); |
| 3033 | + rseq_migrate(p); | |
| 3032 | 3034 | __set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0)); |
| 3033 | 3035 | #endif |
| 3034 | 3036 | rq = __task_rq_lock(p, &rf); |