kernel/sched/deadline.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
The deadline server can currently stop due to idle although fair tasks
are runnable. This happens essentially when:
* the server is set to idle, a task wakes up, the server stops
* a task wakes up, the server sets itself to idle and stops right away
Address both cases by clearing the server idle flag whenever a fair task
wakes up and accounting also for pending tasks in the definition of idle.
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
This is really quick and dirty but seems to fix it according to the
models. I also updated the chart to show what is happening here.
I added the same event (server_resume) to the model.
kernel/sched/deadline.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 138136742871..1070e93350df 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -1376,7 +1376,7 @@ update_stats_dequeue_dl(struct dl_rq *dl_rq, struct sched_dl_entity *dl_se, int
static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64 delta_exec)
{
- bool idle = rq->curr == rq->idle;
+ bool idle = rq->curr == rq->idle && !rq->nr_running && !rq->ttwu_pending;
s64 scaled_delta_exec;
if (unlikely(delta_exec <= 0)) {
@@ -1560,8 +1560,8 @@ void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
* | 8 | B:zero_laxity-wait | | |
* | | | <---+ |
* | +--------------------------------+ |
- * | | ^ ^ 2 |
- * | | 7 | 2 +--------------------+
+ * | | ^ ^ 2 |
+ * | | 7 | 2, 1 +----------------+
* | v |
* | +-------------+ |
* +-- | C:idle-wait | -+
@@ -1606,8 +1606,11 @@ void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
* dl_defer_idle = 0
*
*
- * [1] A->B, A->D
+ * [1] A->B, A->D, C->B
* dl_server_start()
+ * dl_defer_idle = 0;
+ * if (dl_server_active)
+ * return; // [B]
* dl_server_active = 1;
* enqueue_dl_entity()
* update_dl_entity(WAKEUP)
@@ -1741,6 +1744,7 @@ void dl_server_start(struct sched_dl_entity *dl_se)
{
struct rq *rq = dl_se->rq;
+ dl_se->dl_defer_idle = 0;
if (!dl_server(dl_se) || dl_se->dl_server_active)
return;
base-commit: 0f61b1860cc3f52aef9036d7235ed1f017632193
--
2.52.0
On Tue, Jan 13, 2026 at 09:52:01AM +0100, Gabriele Monaco wrote:
> The deadline server can currently stop due to idle although fair tasks
> are runnable. This happens essentially when:
>
> * the server is set to idle, a task wakes up, the server stops
> * a task wakes up, the server sets itself to idle and stops right away
>
> Address both cases by clearing the server idle flag whenever a fair task
> wakes up and accounting also for pending tasks in the definition of idle.
>
> Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
> ---
>
> This is really quick and dirty but seems to fix it according to the
> models. I also updated the chart to show what is happening here.
> I added the same event (server_resume) to the model.
>
> kernel/sched/deadline.c | 12 ++++++++----
> 1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
> index 138136742871..1070e93350df 100644
> --- a/kernel/sched/deadline.c
> +++ b/kernel/sched/deadline.c
> @@ -1376,7 +1376,7 @@ update_stats_dequeue_dl(struct dl_rq *dl_rq, struct sched_dl_entity *dl_se, int
>
> static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64 delta_exec)
> {
> - bool idle = rq->curr == rq->idle;
> + bool idle = rq->curr == rq->idle && !rq->nr_running && !rq->ttwu_pending;
This is idle_cpu(), perhaps we can lift that thing into sched.h or so.
> s64 scaled_delta_exec;
>
> if (unlikely(delta_exec <= 0)) {
> @@ -1560,8 +1560,8 @@ void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
> * | 8 | B:zero_laxity-wait | | |
> * | | | <---+ |
> * | +--------------------------------+ |
> - * | | ^ ^ 2 |
> - * | | 7 | 2 +--------------------+
> + * | | ^ ^ 2 |
> + * | | 7 | 2, 1 +----------------+
> * | v |
> * | +-------------+ |
> * +-- | C:idle-wait | -+
> @@ -1606,8 +1606,11 @@ void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
> * dl_defer_idle = 0
> *
> *
> - * [1] A->B, A->D
> + * [1] A->B, A->D, C->B
> * dl_server_start()
> + * dl_defer_idle = 0;
> + * if (dl_server_active)
> + * return; // [B]
> * dl_server_active = 1;
> * enqueue_dl_entity()
> * update_dl_entity(WAKEUP)
> @@ -1741,6 +1744,7 @@ void dl_server_start(struct sched_dl_entity *dl_se)
> {
> struct rq *rq = dl_se->rq;
>
> + dl_se->dl_defer_idle = 0;
> if (!dl_server(dl_se) || dl_se->dl_server_active)
> return;
Yeah, this seems sensible.
Let me pick it up and do that idle_cpu() thing.
On Tue, 2026-01-13 at 10:37 +0100, Peter Zijlstra wrote:
> > static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se,
> > s64 delta_exec)
> > {
> > - bool idle = rq->curr == rq->idle;
> > + bool idle = rq->curr == rq->idle && !rq->nr_running && !rq-
> > >ttwu_pending;
>
> This is idle_cpu(), perhaps we can lift that thing into sched.h or so.
>
Yeah, that's what I meant by quick and dirty.. I have idle_cpu() on the model
side (and have them matching simplifies a lot of things).
I just wasn't sure if we wanted a function call in there. But we can probably do
something nicer without it too.
Thanks,
Gabriele
On Tue, Jan 13, 2026 at 10:42:02AM +0100, Gabriele Monaco wrote:
> On Tue, 2026-01-13 at 10:37 +0100, Peter Zijlstra wrote:
> > > static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se,
> > > s64 delta_exec)
> > > {
> > > - bool idle = rq->curr == rq->idle;
> > > + bool idle = rq->curr == rq->idle && !rq->nr_running && !rq-
> > > >ttwu_pending;
> >
> > This is idle_cpu(), perhaps we can lift that thing into sched.h or so.
> >
>
> Yeah, that's what I meant by quick and dirty.. I have idle_cpu() on the model
> side (and have them matching simplifies a lot of things).
>
> I just wasn't sure if we wanted a function call in there. But we can probably do
> something nicer without it too.
I'm test building this...
---
diff --git a/include/linux/sched.h b/include/linux/sched.h
index bf96a7d595e2..524bdc0ca53f 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1869,7 +1869,6 @@ static inline int task_nice(const struct task_struct *p)
extern int can_nice(const struct task_struct *p, const int nice);
extern int task_curr(const struct task_struct *p);
extern int idle_cpu(int cpu);
-extern int available_idle_cpu(int cpu);
extern int sched_setscheduler(struct task_struct *, int, const struct sched_param *);
extern int sched_setscheduler_nocheck(struct task_struct *, int, const struct sched_param *);
extern void sched_set_fifo(struct task_struct *p);
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 80c9559a3e30..d06b29ae961e 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -1420,7 +1420,7 @@ update_stats_dequeue_dl(struct dl_rq *dl_rq, struct sched_dl_entity *dl_se, int
static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64 delta_exec)
{
- bool idle = rq->curr == rq->idle;
+ bool idle = idle_rq(rq);
s64 scaled_delta_exec;
if (unlikely(delta_exec <= 0)) {
@@ -1603,8 +1603,8 @@ void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
* | 8 | B:zero_laxity-wait | | |
* | | | <---+ |
* | +--------------------------------+ |
- * | | ^ ^ 2 |
- * | | 7 | 2 +--------------------+
+ * | | ^ ^ 2 |
+ * | | 7 | 2, 1 +----------------+
* | v |
* | +-------------+ |
* +-- | C:idle-wait | -+
@@ -1649,8 +1649,11 @@ void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
* dl_defer_idle = 0
*
*
- * [1] A->B, A->D
+ * [1] A->B, A->D, C->B
* dl_server_start()
+ * dl_defer_idle = 0;
+ * if (dl_server_active)
+ * return; // [B]
* dl_server_active = 1;
* enqueue_dl_entity()
* update_dl_entity(WAKEUP)
@@ -1784,6 +1787,7 @@ void dl_server_start(struct sched_dl_entity *dl_se)
{
struct rq *rq = dl_se->rq;
+ dl_se->dl_defer_idle = 0;
if (!dl_server(dl_se) || dl_se->dl_server_active)
return;
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 58c9d244f12b..ea791550af0e 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1380,6 +1380,28 @@ static inline u32 sched_rng(void)
#define cpu_curr(cpu) (cpu_rq(cpu)->curr)
#define raw_rq() raw_cpu_ptr(&runqueues)
+static inline bool idle_rq(struct rq *rq)
+{
+ return rq->curr == rq->idle && !rq->nr_running && !rq->ttwu_pending;
+}
+
+/**
+ * available_idle_cpu - is a given CPU idle for enqueuing work.
+ * @cpu: the CPU in question.
+ *
+ * Return: 1 if the CPU is currently idle. 0 otherwise.
+ */
+static inline bool available_idle_cpu(int cpu)
+{
+ if (!idle_rq(cpu_rq(cpu)))
+ return 0;
+
+ if (vcpu_is_preempted(cpu))
+ return 0;
+
+ return 1;
+}
+
#ifdef CONFIG_SCHED_PROXY_EXEC
static inline void rq_set_donor(struct rq *rq, struct task_struct *t)
{
diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c
index 0496dc29ed0f..cb337de679b8 100644
--- a/kernel/sched/syscalls.c
+++ b/kernel/sched/syscalls.c
@@ -180,35 +180,7 @@ int task_prio(const struct task_struct *p)
*/
int idle_cpu(int cpu)
{
- struct rq *rq = cpu_rq(cpu);
-
- if (rq->curr != rq->idle)
- return 0;
-
- if (rq->nr_running)
- return 0;
-
- if (rq->ttwu_pending)
- return 0;
-
- return 1;
-}
-
-/**
- * available_idle_cpu - is a given CPU idle for enqueuing work.
- * @cpu: the CPU in question.
- *
- * Return: 1 if the CPU is currently idle. 0 otherwise.
- */
-int available_idle_cpu(int cpu)
-{
- if (!idle_cpu(cpu))
- return 0;
-
- if (vcpu_is_preempted(cpu))
- return 0;
-
- return 1;
+ return idle_rq(cpu_rq(cpu));
}
/**
On 13/01/26 10:42, Gabriele Monaco wrote:
> On Tue, 2026-01-13 at 10:37 +0100, Peter Zijlstra wrote:
> > > static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se,
> > > s64 delta_exec)
> > > {
> > > - bool idle = rq->curr == rq->idle;
> > > + bool idle = rq->curr == rq->idle && !rq->nr_running && !rq-
> > > >ttwu_pending;
> >
> > This is idle_cpu(), perhaps we can lift that thing into sched.h or so.
> >
>
> Yeah, that's what I meant by quick and dirty.. I have idle_cpu() on the model
> side (and have them matching simplifies a lot of things).
>
> I just wasn't sure if we wanted a function call in there. But we can probably do
> something nicer without it too.
Thanks for the patch (looks sensible to me too) and nice catch from the
model!
The following commit has been merged into the sched/urgent branch of tip:
Commit-ID: ca1e8eede4fc68ce85a9fdce1a6c13ad64933318
Gitweb: https://git.kernel.org/tip/ca1e8eede4fc68ce85a9fdce1a6c13ad64933318
Author: Gabriele Monaco <gmonaco@redhat.com>
AuthorDate: Tue, 13 Jan 2026 09:52:01 +01:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Tue, 13 Jan 2026 11:37:52 +01:00
sched/deadline: Fix server stopping with runnable tasks
The deadline server can currently stop due to idle although fair tasks
are runnable. This happens essentially when:
* the server is set to idle, a task wakes up, the server stops
* a task wakes up, the server sets itself to idle and stops right away
Address both cases by clearing the server idle flag whenever a fair task
wakes up and accounting also for pending tasks in the definition of idle.
Fixes: f5a538c07df2 ("sched/deadline: Fix dl_server stop condition")
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20260113085159.114226-3-gmonaco@redhat.com
---
kernel/sched/deadline.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index e3efc40..b5c19b1 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -1420,7 +1420,7 @@ update_stats_dequeue_dl(struct dl_rq *dl_rq, struct sched_dl_entity *dl_se, int
static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64 delta_exec)
{
- bool idle = rq->curr == rq->idle;
+ bool idle = idle_rq(rq);
s64 scaled_delta_exec;
if (unlikely(delta_exec <= 0)) {
@@ -1603,8 +1603,8 @@ void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
* | 8 | B:zero_laxity-wait | | |
* | | | <---+ |
* | +--------------------------------+ |
- * | | ^ ^ 2 |
- * | | 7 | 2 +--------------------+
+ * | | ^ ^ 2 |
+ * | | 7 | 2, 1 +----------------+
* | v |
* | +-------------+ |
* +-- | C:idle-wait | -+
@@ -1649,8 +1649,11 @@ void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
* dl_defer_idle = 0
*
*
- * [1] A->B, A->D
+ * [1] A->B, A->D, C->B
* dl_server_start()
+ * dl_defer_idle = 0;
+ * if (dl_server_active)
+ * return; // [B]
* dl_server_active = 1;
* enqueue_dl_entity()
* update_dl_entity(WAKEUP)
@@ -1759,6 +1762,7 @@ void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
* "B:zero_laxity-wait" -> "C:idle-wait" [label="7:dl_server_update_idle"]
* "B:zero_laxity-wait" -> "D:running" [label="3:dl_server_timer"]
* "C:idle-wait" -> "A:init" [label="8:dl_server_timer"]
+ * "C:idle-wait" -> "B:zero_laxity-wait" [label="1:dl_server_start"]
* "C:idle-wait" -> "B:zero_laxity-wait" [label="2:dl_server_update"]
* "C:idle-wait" -> "C:idle-wait" [label="7:dl_server_update_idle"]
* "D:running" -> "A:init" [label="4:pick_task_dl"]
@@ -1784,6 +1788,7 @@ void dl_server_start(struct sched_dl_entity *dl_se)
{
struct rq *rq = dl_se->rq;
+ dl_se->dl_defer_idle = 0;
if (!dl_server(dl_se) || dl_se->dl_server_active)
return;
© 2016 - 2026 Red Hat, Inc.