Re: [PATCH] sched/stats: Fix run_delay over-count for proxy-migrated blocked donors

From: K Prateek Nayak

Date: Tue Oct 27 2026 - 05:59:07 EST


On 10/27/2026 3:23 PM, Kayra Cizmeci wrote:
> Wei, what do you think? Will this work for you?

Full diff for convenience:

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 04b62c16fa8c..4b99ef5b3b2d 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3956,6 +3956,8 @@ static int ttwu_runnable(struct task_struct *p, int wake_flags)
* it should preempt the task that is current now.
*/
wakeup_preempt(rq, p, wake_flags | WF_TTWU_RQ);
+ /* Start accounting wait time. */
+ sched_info_enqueue(rq, p);
}
ttwu_do_wakeup(p);
return 1;
diff --git a/kernel/sched/stats.h b/kernel/sched/stats.h
index ebe0a7765f98..c4c624deb25b 100644
--- a/kernel/sched/stats.h
+++ b/kernel/sched/stats.h
@@ -239,6 +239,15 @@ static inline void psi_account_irqtime(struct rq *rq, struct task_struct *curr,
static inline void sched_info_dequeue(struct rq *rq, struct task_struct *t)
{
unsigned long long delta = 0;
+ /*
+ * If a task is blocked (blocked donor, delayed dequeue task),
+ * do not account the queued time as runnable. A task that is
+ * blocking would have already cleared the "last_queued"
+ * indicator from sched_info_arrive() when it got a chance to
+ * run on the CPU.
+ */
+ if (t->is_blocked)
+ t->sched_info.last_queued = 0;

if (!t->sched_info.last_queued)
return;
@@ -290,7 +299,23 @@ static void sched_info_arrive(struct rq *rq, struct task_struct *t)
*/
static inline void sched_info_enqueue(struct rq *rq, struct task_struct *t)
{
- if (!t->sched_info.last_queued)
+ /*
+ * "t->is_blocked" is only cleared after the enqueue is done and
+ * the task is woken up. If the task is blocked, update the
+ * "last_queued" timestamp to that of last enqueue - if the task
+ * is still blocked while it leaves the runqueue, this timestamp
+ * is ignored and is cleared at sched_info_dequeue().
+ *
+ * To transition a task back to blocked after wakeup, it has to
+ * run on the CPU and naturally reach a safe preemption point.
+ * sched_info_arrive() will do the correct wait time accounting
+ * based on the last "last_queued".
+ *
+ * Any movement of task after being marked not blocked will be
+ * correctly captured as queued time at enqueue + dequeue
+ * boundaries when "t->is_blocked" cleared.
+ */
+ if (!t->sched_info.last_queued || t->is_blocked)
t->sched_info.last_queued = rq_clock(rq);
}

--
Thanks and Regards,
Prateek