Re: sched/deadline: total_bw leak when SCHED_DEADLINE is set on a dead task
From: Juri Lelli
Date: Mon Nov 02 2026 - 08:10:55 EST
Hello,
On 31/10/26 17:02, Weiheng Qiu wrote:
> Hi,
>
> sched_setattr(SCHED_DEADLINE) on a task that has exited but has not
> been reaped returns 0 and charges the root domain's dl_bw->total_bw.
> The task never runs again, so nothing releases it, not even reaping.
>
> __sched_setscheduler() does not look at p->exit_state, so
> sched_dl_overflow() charges the task. A DL task's bandwidth is
> normally released at its last dequeue, which a task that has already
> done its final __schedule() never reaches again.
>
> Repeating it fills total_bw to the admission cap: a later
> sched_setattr(SCHED_DEADLINE) and CPU offline then fail with -EBUSY
> with no DL task left; only a root-domain rebuild recomputes it. A
> thread-group leader that exits while its other threads run stays
> unreaped and keeps the TGID, so a pid is enough. Needs CAP_SYS_NICE,
> as any DL admission does.
>
> Reproducer below; needs root and debugfs, assumes x86-64. On mainline
> c339f4010dea (0.25 CPU == 262144):
>
> task 63 state Z
> total_bw at start: 0.100 CPU (104856)
> SCHED_DEADLINE on the zombie: rc 0, policy 6, total_bw 0.350 CPU (367000)
> zombie reaped: total_bw 0.350 CPU (367000), +0.250 CPU (262144)
> 6 more zombies admitted, then: Device or resource busy
> total_bw 1.850 CPU (1939864)
> DL tasks left: 0; new 0.250-CPU request: Device or resource busy
> after a root-domain rebuild (recomputed): total_bw 0.100 CPU (104856)
Thanks for reporting and for the reproducer.
Something like what below should fix it. Can you please confirm?
I made it DEADLINE specific, as I'm not sure setscheduler after exit
might make sense for, say, sysrq reset to SCHED_NORMAL. Peter, others?
Best,
Juri
---
kernel/sched/syscalls.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c
index b215b0ead9a60..64e604affa288 100644
--- a/kernel/sched/syscalls.c
+++ b/kernel/sched/syscalls.c
@@ -583,6 +583,16 @@ int __sched_setscheduler(struct task_struct *p,
if (retval)
goto unlock;
+ /*
+ * Reject SCHED_DEADLINE on exited tasks. A zombie will never be
+ * scheduled again, so sched_dl_overflow() would charge total_bw
+ * that nothing will ever release.
+ */
+ if (dl_policy(policy) && p->exit_state) {
+ retval = -ESRCH;
+ goto unlock;
+ }
+
/*
* If not changing anything there's no need to proceed further,
* but store a possible modification of reset_on_fork.