Re: [PATCH] cpuset: Fix multi-source deadline task accounting and bandwidth bypass

From: Aaron Tomlin

Date: Wed May 13 2026 - 19:11:16 EST


On Wed, May 13, 2026 at 06:22:25PM +0200, Dietmar Eggemann wrote:
> Is there a testcase to provoke this issue in the current code?
>
> I tried to move a process with 6 DL tasks from one cpuset to another by:
>
> echo $PID > /sys/fs/cgroup/B/cgroup.procs
>
> but in this case old_cs is the same for all these tasks.
>
> [ 1991.852034] cgroup_migrate() (7) leader=[dl_batch_cgroup 823] threadgroup=1
> [ 1991.852068] cgroup_migrate_execute() tset->nr_tasks=7
> [ 1991.852238] cpuset_can_attach() (4) [dl_batch_cgroup 832] nr_migrate_dl_tasks=1 sum_migrate_dl_bw=104857 old_cs=ffff0000c4955200
> [ 1991.852246] cpuset_can_attach() (4) [dl_batch_cgroup 833] nr_migrate_dl_tasks=2 sum_migrate_dl_bw=209714 old_cs=ffff0000c4955200
> [ 1991.852248] cpuset_can_attach() (4) [dl_batch_cgroup 834] nr_migrate_dl_tasks=3 sum_migrate_dl_bw=314571 old_cs=ffff0000c4955200
> [ 1991.852249] cpuset_can_attach() (4) [dl_batch_cgroup 835] nr_migrate_dl_tasks=4 sum_migrate_dl_bw=419428 old_cs=ffff0000c4955200
> [ 1991.852249] cpuset_can_attach() (4) [dl_batch_cgroup 836] nr_migrate_dl_tasks=5 sum_migrate_dl_bw=524285 old_cs=ffff0000c4955200
> [ 1991.852250] cpuset_can_attach() (4) [dl_batch_cgroup 837] nr_migrate_dl_tasks=6 sum_migrate_dl_bw=629142 old_cs=ffff0000c4955200
> [ 1991.852328] cpuset_attach() (5) cs=ffff0000c1e9fc00 oldcs=ffff0000c4955200 cs->nr_deadline_tasks=6 oldcs->nr_deadline_tasks=6 cs->nr_migrate_dl_tasks=6
>
> dl_batch_cgroup 823 823 19 - 0 TS
> dl_batch_cgroup 823 832 140 0 - DLN
> dl_batch_cgroup 823 833 140 0 - DLN
> dl_batch_cgroup 823 834 140 0 - DLN
> dl_batch_cgroup 823 835 140 0 - DLN
> dl_batch_cgroup 823 836 140 0 - DLN
> dl_batch_cgroup 823 837 140 0 - DLN
>
> [...]

Hi Dietmar,

Thank you for your feedback.

When you write a PID to cgroup.procs, the cgroup core gathers all threads
in that threadgroup into a single cgroup_taskset. If those threads were
spawned normally and never individually moved, they will all share the
exact same old_cs, which is why your test yielded identical source cpusets.

To provoke this specific BUG, you have to split the threads across
different cgroups before you trigger the batch migration that pulls them
all back together.

Here is the test case to reproduce the multi-source edge case:

1. Create two source cpusets and one target cpuset

mkdir /sys/fs/cgroup/SRC_A
mkdir /sys/fs/cgroup/SRC_B
mkdir /sys/fs/cgroup/TARGET

2. Start your Multithreaded DL Application

Run your dl_batch_cgroup app. Let's assume it has PID 1000 and
spawns two SCHED_DEADLINE threads: TID 1001 and TID 1002.

3. Split the Threads

Instead of moving the whole process, move the individual threads
into different source cpusets using the thread-level interface

echo 1001 > /sys/fs/cgroup/SRC_A/cgroup.threads
echo 1002 > /sys/fs/cgroup/SRC_B/cgroup.threads

At this point, SRC_A has nr_deadline_tasks = 1 and SRC_B has
nr_deadline_tasks = 1.

4. Trigger the Batch Migration

Now, trigger a process-level migration by writing the main
threadgroup ID to the target cpuset's cgroup.procs file.

echo 1000 > /sys/fs/cgroup/TARGET/cgroup.procs


Now, when you execute Step 4, the cgroup core gathers TID 1001 and 1002 into a
single cgroup_taskset. Because they originated from different cgroups, they
have different old_cs pointers.

However, the unpatched cpuset_can_attach() loops through the taskset, finds
the oldcs of the first task (e.g., SRC_A), and caches it. It then counts
that there are 2 migrating DL tasks (i.e., cs->nr_migrate_dl_tasks = 2).

In cpuset_attach(), it blindly subtracts the total migrating DL count from
the cached oldcs:

oldcs->nr_deadline_tasks -= cs->nr_migrate_dl_tasks;

/*
* SRC_A count becomes 1 - 2 = -1 (Underflow)
* SRC_B count remains 1 (Permanent leak)
*/

The patch resolves this by evaluating task_cs(task) individually for every
single task as the loop iterates through the cgroup_taskset.


Kind regards,
--
Aaron Tomlin

Attachment: signature.asc
Description: PGP signature