Hello, Waiman. On Thu, Jul 02, 2026 at 05:47:56PM -0400, Waiman Long wrote: > It is assumed that a given cpuset cannot be both a source and a > destination cpuset. [...] it will print a warning and fail the attach > operation in these unexpected cases [...]
This assumption doesn't hold - the WARN_ON_ONCE() and -EINVAL fire on a legitimate migration. It's the case sashiko flagged, and it does reach can_attach(). Threaded subtree with partial cpuset delegation: P (+cpuset) |- R (cpuset) <- destination | `- C (no cpuset) -> effective cpuset == R `- W (cpuset) Group leader in R, thread_a in C, thread_b in W; migrate the whole process into R (echo $PID > R/cgroup.procs). thread_a moves C->R: its cgroup changes so compare_css_sets() keeps it in the taskset, but its cpuset css is unchanged (C inherits R's), so task_cs() == cs == R. cpuset is in ss_mask because thread_b (W->R) changed. can_attach() then tags R as a source (thread_a) and the destination (thread_b): WARNING: kernel/cgroup/cpuset.c:3054 at cpuset_can_attach_check+0xcd/0x130 cpuset_can_attach+0x131/0x2f0 cgroup_migrate_execute+0x367/0x450 cgroup.procs write returns -EINVAL So a thread already in the destination's effective cpuset does show up in the iteration when it reaches that cpuset through a different, non-cpuset child - compare_css_sets() keys on the cgroup, not the cpuset css. 1-9 handle this correctly (the migration succeeds); the patch 10 guard regresses it. I've taken 1-9 into for-7.3 - please respin 10-11. One option: when oldcs == cs the pair is a cpuset no-op, so skip it and add it to neither list. Thanks. -- tejun

