[PATCH v5 24/29] sched/deadline: Cancel the replenishment timer with the parameters
From: Mathieu Desnoyers
Date: Sun Nov 08 2026 - 14:03:10 EST
A task that leaves SCHED_DEADLINE has its parameters cleared at its
0-lag time. Its replenishment timer can still be armed then. If the
task comes back and throttles before that timer fires, start_dl_timer()
does not restart it, since it is queued. The replenishment would then
come at the period of the reservation the task left. Too early, it would
give the task runtime it has not earned. Too late, it would delay the
task by up to a period.
Commit a649f237db18 ("sched,dl: Fix sched class hopping CBS hole") armed
the replenishment timer when a task leaves the class. The timer then
reset the task if it was still out. Commit 209a0cbda7a0
("sched/deadline: Improve the tracking of active utilization") moved
that reset to the inactive timer, at the 0-lag time. Nothing cancels
the replenishment timer there.
The timer of a throttled task fires at its next period, which can come
after the 0-lag time:
- A task that yielded is throttled with runtime left. Its 0-lag time
comes before its deadline.
- A constrained-deadline task that overran by little reaches its 0-lag
time before the end of its period.
Fix this by cancelling the timer where the parameters are cleared. This
includes switched_from_dl(), when it releases the bandwidth of a task
that leaves asleep past its 0-lag time. A constrained task that overran
as it blocked has the timer armed there too.
The Sashiko AI review of the first posting of this series found this.
It has no reproducer.
Fixes: 209a0cbda7a0 ("sched/deadline: Improve the tracking of active utilization")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@xxxxxxxxxxxx>
---
kernel/sched/deadline.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index a35609a10ec5..47adff100392 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -637,6 +637,7 @@ static void task_non_contending(struct sched_dl_entity *dl_se, bool dl_task)
if (dl_task && READ_ONCE(p->__state) == TASK_DEAD)
sub_rq_bw(dl_se, &rq->dl);
dl_task_release_bw(p);
+ cancel_replenish_timer(dl_se);
__dl_clear_params(dl_se);
}
}
@@ -2403,6 +2404,7 @@ static enum hrtimer_restart inactive_task_timer(struct hrtimer *timer)
}
dl_task_release_bw(p);
+ cancel_replenish_timer(dl_se);
__dl_clear_params(dl_se);
goto unlock;
@@ -4135,8 +4137,10 @@ static void switched_from_dl(struct rq *rq, struct task_struct *p)
* task_non_contending() skip it, and its deboost releases
* nothing.
*/
- if (released)
+ if (released) {
+ cancel_replenish_timer(&p->dl);
__dl_clear_params(&p->dl);
+ }
}
/*
--
2.47.3