[PATCH 01/18 v2] sched/eevdf: Decay positive lag of sleeping entities

From: Vincent Guittot

Date: Fri Oct 02 2026 - 11:46:02 EST


Similarly to delayed dequeue that enables an entity to decay its negative
lag while sleeping, a task should not keep a positive lag forever.

The sleep duration and the weight of the entity is used to decay the
positive lag at wakeup.

Signed-off-by: Vincent Guittot <vincent.guittot@xxxxxxxxxx>
---
kernel/sched/fair.c | 51 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 49 insertions(+), 2 deletions(-)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 03206e15e6fe..8cda1d39b037 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -897,6 +897,51 @@ bool update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se)
return avruntime - vlag != se->vruntime;
}

+static inline unsigned long cfs_rq_load_avg(struct cfs_rq *cfs_rq);
+
+static __always_inline
+void decay_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
+{
+ s64 delta_exec, vlag = se->vlag;
+ unsigned long cfs_load;
+ struct rq *rq;
+
+ WARN_ON_ONCE(se->on_rq);
+
+ /* Negative lag implies delayed dequeue */
+ if (vlag <= 0)
+ return;
+
+ rq = rq_of(cfs_rq);
+
+ if (flags & ENQUEUE_MIGRATED)
+ return;
+
+ /* Compute sleep time */
+ delta_exec = rq_clock_task(rq) - se->exec_start;
+ if (unlikely(delta_exec <= 0))
+ return;
+
+ /* For anything above ~4 seconds, save computation and clear the lag */
+ if (unlikely(delta_exec >> 32)) {
+ se->vlag = 0;
+ return;
+ }
+
+ cfs_load = cfs_rq_load_avg(cfs_rq);
+ if (cfs_load) {
+ unsigned long weight = scale_load_down(se->h_load.weight);
+
+ delta_exec *= weight;
+ delta_exec = div64_long(delta_exec, cfs_load + weight);
+ }
+
+ vlag -= calc_delta_fair(delta_exec, se);
+
+ /* vlag can't become neg while sleeping */
+ se->vlag = max(0, vlag);
+}
+
/*
* Entity is eligible once it received less service than it ought to have,
* eg. lag >= 0.
@@ -7987,7 +8032,7 @@ static void
enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
{
int rq_h_nr_queued = rq->cfs.h_nr_queued;
- int task_new = !(flags & ENQUEUE_WAKEUP);
+ int task_wake = flags & ENQUEUE_WAKEUP;
struct sched_entity *se = &p->se;
struct cfs_rq *cfs_rq = &rq->cfs;
unsigned long weight;
@@ -8019,6 +8064,8 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
if (p->in_iowait)
cpufreq_update_util(rq, SCHED_CPUFREQ_IOWAIT);

+ if (task_wake)
+ decay_entity_lag(cfs_rq, se, flags);

if (se->on_rq && se->sched_delayed)
requeue_delayed_entity(cfs_rq, se);
@@ -8048,7 +8095,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
* into account, but that is not straightforward to implement,
* and the following generally works well enough in practice.
*/
- if (!task_new)
+ if (task_wake)
check_update_overutilized_status(rq);

assert_list_leaf_cfs_rq(rq);
--
2.53.0