OpenVZ / Virtuozzo kernel development (devel@openvz.org)
 help / color / mirror / Atom feed
From: Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
To: Konstantin Khorenko <khorenko@virtuozzo.com>,
	Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Cc: devel@openvz.org
Subject: [Devel] [PATCH hci-8.1 1/4] sched: fix cfs_rq::nr_iowait leak on the remote wakeup path
Date: Wed, 14 Oct 2026 11:16:24 +0000	[thread overview]
Message-ID: <20261014-ct-iowait-v1-1-9d65fe37cf89@virtuozzo.com> (raw)
In-Reply-To: <20261014-ct-iowait-v1-0-9d65fe37cf89@virtuozzo.com>

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@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

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

  reply	other threads:[~2026-10-14 11:18 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-14 11:16 [Devel] [PATCH hci-8.1 0/4] sched: restore Container iowait accounting Vasileios Almpanis
2026-10-14 11:16 ` Vasileios Almpanis [this message]
2026-10-14 11:23   ` [Devel] [PATCH hci-8.1 1/4] sched: fix cfs_rq::nr_iowait leak on the remote wakeup path no-reply
2026-10-14 11:16 ` [Devel] [PATCH hci-8.1 2/4] sched: keep cfs_rq::nr_iowait balanced for throttled and class changing tasks Vasileios Almpanis
2026-10-14 11:24   ` no-reply
2026-10-14 11:16 ` [Devel] [PATCH hci-8.1 3/4] sched: account sleep and block time of a task group entity Vasileios Almpanis
2026-10-14 11:28   ` no-reply
2026-10-14 11:36     ` Vasileios Almpanis
2026-10-14 11:16 ` [Devel] [PATCH hci-8.1 4/4] sched: switch a sleeping task group entity between idle and iowait Vasileios Almpanis
2026-10-14 11:27   ` no-reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261014-ct-iowait-v1-1-9d65fe37cf89@virtuozzo.com \
    --to=vasileios.almpanis@virtuozzo.com \
    --cc=devel@openvz.org \
    --cc=khorenko@virtuozzo.com \
    --cc=ptikhomirov@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox