[PATCH] sched/deadline: Fix server stopping with runnable tasks

Gabriele Monaco posted 1 patch 3 weeks, 4 days ago
kernel/sched/deadline.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
[PATCH] sched/deadline: Fix server stopping with runnable tasks
Posted by Gabriele Monaco 3 weeks, 4 days ago
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
Re: [PATCH] sched/deadline: Fix server stopping with runnable tasks
Posted by Peter Zijlstra 3 weeks, 4 days ago
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.
Re: [PATCH] sched/deadline: Fix server stopping with runnable tasks
Posted by Gabriele Monaco 3 weeks, 4 days ago
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
Re: [PATCH] sched/deadline: Fix server stopping with runnable tasks
Posted by Peter Zijlstra 3 weeks, 4 days ago
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));
 }
 
 /**
Re: [PATCH] sched/deadline: Fix server stopping with runnable tasks
Posted by Juri Lelli 3 weeks, 4 days ago
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!
[tip: sched/urgent] sched/deadline: Fix server stopping with runnable tasks
Posted by tip-bot2 for Gabriele Monaco 3 weeks, 4 days ago
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;