[Devel] [PATCH hci-8.1 1/4] sched: fix cfs_rq::nr_iowait leak on the remote wakeup path
Vasileios Almpanis
vasileios.almpanis at virtuozzo.com
Wed Oct 14 14:16:24 MSK 2026
try_to_wake_up() decrements cfs_rq::nr_iowait only after the
p->on_cpu check. If the task's CPU is still in schedule(), the wakeup
is queued with ttwu_queue_wakelist() and try_to_wake_up() returns
early, so the counter is never decremented. It keeps growing and shows
up as a bogus procs_blocked in the CT's /proc/stat:
# grep procs_blocked /proc/stat
procs_blocked 70
With NO_TTWU_QUEUE the leak goes away.
Fix it by doing the decrement where rq::nr_iowait is handled: in
try_to_wake_up() before set_task_cpu() for migrating wakeups, and in
ttwu_do_activate() for all others, which also covers the wakelist.
This way non migrating wakeups don't take the rq lock for it anymore.
A task on the wakelist is TASK_WAKING but still counted, so add
task_in_nr_iowait() and use it in sched_move_task() instead of
skipping every TASK_WAKING task.
https://virtuozzo.atlassian.net/browse/VSTOR-147823
Fixes: 415fe063c53e ("sched: Account cfs_rq::nr_iowait")
Signed-off-by: Vasileios Almpanis <vasileios.almpanis at virtuozzo.com>
Feature: sched: enhance task states granularity and accounting
---
kernel/sched/core.c | 26 +++++++++++++-------------
kernel/sched/sched.h | 12 ++++++++++++
2 files changed, 25 insertions(+), 13 deletions(-)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 9275501bc3ad..dc016d264d19 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3775,6 +3775,8 @@ ttwu_do_activate(struct rq *rq, struct task_struct *p, int wake_flags,
if (p->in_iowait) {
delayacct_blkio_end(p);
atomic_dec(&task_rq(p)->nr_iowait);
+ if (p->sched_class->nr_iowait_dec)
+ p->sched_class->nr_iowait_dec(p);
}
if (p->sched_interruptible_sleep)
@@ -4355,20 +4357,20 @@ int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
*/
smp_cond_load_acquire(&p->on_cpu, !VAL);
- if (p->in_iowait && p->sched_class->nr_iowait_dec) {
- struct rq_flags rf;
- struct rq *rq;
-
- rq = __task_rq_lock(p, &rf);
- p->sched_class->nr_iowait_dec(p);
- __task_rq_unlock(rq, &rf);
- }
-
cpu = select_task_rq(p, p->wake_cpu, &wake_flags);
if (task_cpu(p) != cpu) {
if (p->in_iowait) {
delayacct_blkio_end(p);
atomic_dec(&task_rq(p)->nr_iowait);
+ /* On the old CPU, see also ttwu_do_activate() */
+ if (p->sched_class->nr_iowait_dec) {
+ struct rq_flags rf;
+ struct rq *rq;
+
+ rq = __task_rq_lock(p, &rf);
+ p->sched_class->nr_iowait_dec(p);
+ __task_rq_unlock(rq, &rf);
+ }
}
wake_flags |= WF_MIGRATED;
@@ -9139,8 +9141,7 @@ void sched_move_task(struct task_struct *tsk, bool for_autogroup)
if (queued)
dequeue_task(rq, tsk, queue_flags);
else {
- if (!(READ_ONCE(tsk->__state) == TASK_WAKING) && tsk->in_iowait &&
- tsk->sched_class->nr_iowait_dec)
+ if (task_in_nr_iowait(tsk) && tsk->sched_class->nr_iowait_dec)
tsk->sched_class->nr_iowait_dec(tsk);
if (tsk->sched_contributes_to_load)
@@ -9159,8 +9160,7 @@ void sched_move_task(struct task_struct *tsk, bool for_autogroup)
if (queued)
enqueue_task(rq, tsk, queue_flags);
else {
- if (!(READ_ONCE(tsk->__state) == TASK_WAKING) && tsk->in_iowait &&
- tsk->sched_class->nr_iowait_inc)
+ if (task_in_nr_iowait(tsk) && tsk->sched_class->nr_iowait_inc)
tsk->sched_class->nr_iowait_inc(tsk);
if (tsk->sched_contributes_to_load)
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 2a3f73933456..176077ba4201 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2337,6 +2337,18 @@ static inline int task_on_rq_queued(struct task_struct *p)
return READ_ONCE(p->on_rq) == TASK_ON_RQ_QUEUED;
}
+/*
+ * A sleeping iowait task stays counted in nr_iowait of its sched class until
+ * it is woken up: a migrating wakeup drops it in try_to_wake_up(), any other
+ * in ttwu_do_activate(), which for a wakelist wakeup runs later while the
+ * task is TASK_WAKING. Called with p->pi_lock and the task's rq lock held.
+ */
+static inline bool task_in_nr_iowait(struct task_struct *p)
+{
+ return p->in_iowait && !task_on_rq_queued(p) &&
+ (READ_ONCE(p->__state) != TASK_WAKING || !p->sched_remote_wakeup);
+}
+
static inline int task_on_rq_migrating(struct task_struct *p)
{
return READ_ONCE(p->on_rq) == TASK_ON_RQ_MIGRATING;
--
2.43.0
More information about the Devel
mailing list