[PATCH 04/12] sched/deadline: Do not lose a throttled task switched out and back
From: Mathieu Desnoyers
Date: Wed Nov 04 2026 - 10:19:35 EST
A SCHED_DEADLINE task that is throttled, switched to another policy and
switched back before its 0-lag time can be left throttled with nothing
to replenish it: runnable, queued as far as the core is concerned, but
on no dl_rq and with no timer pending. It never runs again, and a fatal
signal cannot make it run; only another switch out of SCHED_DEADLINE
requeues it.
1. the task exhausts its runtime: dl_throttled is set, and the
replenishment timer armed for the start of its next period;
2. sched_setscheduler() switches it to SCHED_OTHER: switched_from_dl()
calls task_non_contending(), which arms the inactive timer for the
0-lag time; the parameters are kept until then, so that leaving and
reentering SCHED_DEADLINE gains no bandwidth;
3. the replenishment timer fires: dl_task_timer() finds a task that is
not a deadline task and returns, dl_throttled still set;
4. sched_setattr() switches it back before the 0-lag time:
switched_to_dl() cancels the inactive timer, and enqueue_dl_entity()
leaves a throttled task to its replenishment timer, which has fired
already.
The window between the deadline and the 0-lag time is the overrun
scaled by period/runtime: about 100us for an overrun of 20us at 20%
bandwidth. The replenishment timer cleared the parameters, dl_throttled
included, of a task it found out of SCHED_DEADLINE until
commit 209a0cbda7a0 ("sched/deadline: Improve the tracking of active
utilization") moved that reset to the inactive timer, at the 0-lag
time.
Clear dl_throttled there again. The deadline has passed by then, so a
return before the 0-lag time starts a new period when the task is
enqueued (ENQUEUE_MOVE, or the wakeup of a sleeper), as the
replenishment would have; a return after it finds the parameters reset
by the inactive timer, as before.
The selftest added two patches later fails its throttled_round_trip
case 8 times out of 8, on v7.3 as on v7.4-rc1 with the four deadline
fixes of sched/urgent, and passes it 8 times out of 8 with this change.
It was found by a cgroup selftest under development,
test_concurrency_deadline, which hung about once in 1350 runs.
Fixes: 209a0cbda7a0 ("sched/deadline: Improve the tracking of active utilization")
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@xxxxxxxxxxxx>
---
kernel/sched/deadline.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 30a52e42c26d..d474fbfd9be6 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -1232,10 +1232,17 @@ static enum hrtimer_restart dl_task_timer(struct hrtimer *timer)
/*
* The task might have changed its scheduling policy to something
- * different than SCHED_DEADLINE (through switched_from_dl()).
+ * different than SCHED_DEADLINE (through switched_from_dl()). It
+ * keeps its parameters until its 0-lag time and may come back to
+ * SCHED_DEADLINE before then, switched_to_dl() cancelling the
+ * inactive timer: it must not come back throttled, since this was
+ * its replenishment. Its deadline has passed: the enqueue starts a
+ * new period.
*/
- if (!dl_task(p))
+ if (!dl_task(p)) {
+ dl_se->dl_throttled = 0;
goto unlock;
+ }
/*
* The task might have been boosted by someone else and might be in the
--
2.47.3