From: Aaron Tomlin <atomlin@atomlin.com>
To: mingo@redhat.com, peterz@infradead.org, juri.lelli@redhat.com,
vincent.guittot@linaro.org
Cc: dietmar.eggemann@arm.com, rostedt@goodmis.org,
bsegall@google.com, mgorman@suse.de, vschneid@redhat.com,
kprateek.nayak@amd.com, zhanxusheng1024@gmail.com,
neelx@suse.com, chjohnst@mail.com, mproche@mail.com,
sean@ashe.io, steve@abita.co, linux-kernel@vger.kernel.org
Subject: [PATCH v3 3/4] sched/fair: Use list_for_each_entry_rcu() in print_cfs_stats()
Date: Sat, 8 Aug 2026 19:55:21 -0400 [thread overview]
Message-ID: <20260808235522.380038-4-atomlin@atomlin.com> (raw)
In-Reply-To: <20260808235522.380038-1-atomlin@atomlin.com>
In print_cfs_stats(), rq->leaf_cfs_rq_list is traversed using
for_each_leaf_cfs_rq_safe(), which expands to list_for_each_entry_safe().
Although rq->leaf_cfs_rq_list is RCU-protected, list_for_each_entry_safe()
is a non-RCU iteration macro. It dereferences pointer links without
READ_ONCE() and pre-fetches the next pointer.
When a writer concurrently adds a new cfs_rq to the list using
list_add_rcu(), a reader traversing with list_for_each_entry_safe()
lacks READ_ONCE() protection. Without READ_ONCE(), the compiler is free
to re-fetch pointers or reorder instructions. As a result, the reader
can observe a newly inserted cfs_rq's pointer before its internal fields
are fully visible, leading to reading uninitialised data or
dereferencing invalid pointers. This is illustrated below:
CPU 0 CPU 1
Writer (under rq->lock) Reader (print_cfs_stats)
--------------------------- --------------------------
1. Initialise cfs_rq rcu_read_lock()
cfs_rq->tg = tg
cfs_rq->load = 1024
2. list_add_rcu(&cfs_rq->list, ...): 3. for_each_leaf_cfs_rq_safe():
smp_store_release() Reads prev->next (cfs_rq)
prev->next = cfs_rq -----> Missing READ_ONCE()
4. Reads cfs_rq fields; sees
garbage data
cfs_rq->tg
cfs_rq->load
Fix this by introducing for_each_leaf_cfs_rq_rcu(), which expands to
list_for_each_entry_rcu(). This uses READ_ONCE() during list traversal.
Fixes: 0601267ca673 ("sched/fair: Rewrite list_add_leaf_cfs_rq()")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
kernel/sched/fair.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index d78467ec6ee1..25f941ad637a 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -412,6 +412,10 @@ static inline void assert_list_leaf_cfs_rq(struct rq *rq)
list_for_each_entry_safe(cfs_rq, pos, &rq->leaf_cfs_rq_list, \
leaf_cfs_rq_list)
+#define for_each_leaf_cfs_rq_rcu(rq, cfs_rq) \
+ list_for_each_entry_rcu(cfs_rq, &(rq)->leaf_cfs_rq_list, \
+ leaf_cfs_rq_list)
+
/* Do the two (enqueued) entities belong to the same group ? */
static inline struct cfs_rq *
is_same_group(struct sched_entity *se, struct sched_entity *pse)
@@ -497,6 +501,9 @@ static inline void assert_list_leaf_cfs_rq(struct rq *rq)
#define for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) \
for (cfs_rq = &rq->cfs, pos = NULL; cfs_rq; cfs_rq = pos)
+#define for_each_leaf_cfs_rq_rcu(rq, cfs_rq) \
+ for (cfs_rq = &rq->cfs; cfs_rq; cfs_rq = NULL)
+
static inline struct sched_entity *parent_entity(struct sched_entity *se)
{
return NULL;
@@ -15401,10 +15408,10 @@ DEFINE_SCHED_CLASS(fair) = {
void print_cfs_stats(struct seq_file *m, int cpu)
{
- struct cfs_rq *cfs_rq, *pos;
+ struct cfs_rq *cfs_rq;
rcu_read_lock();
- for_each_leaf_cfs_rq_safe(cpu_rq(cpu), cfs_rq, pos)
+ for_each_leaf_cfs_rq_rcu(cpu_rq(cpu), cfs_rq)
print_cfs_rq(m, cpu, cfs_rq);
rcu_read_unlock();
}
--
2.55.0
next prev parent reply other threads:[~2026-08-08 23:55 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-08 23:55 [PATCH v3 0/4] sched/debug: Introduce per-CPU debugfs files Aaron Tomlin
2026-08-08 23:55 ` [PATCH v3 1/4] sched/debug: Protect lockless rq->curr access in print_cpu() Aaron Tomlin
2026-08-08 23:55 ` [PATCH v3 2/4] sched/debug: Protect lockless rq->rd access in print_dl_rq() Aaron Tomlin
2026-08-08 23:55 ` Aaron Tomlin [this message]
2026-08-08 23:55 ` [PATCH v3 4/4] sched/debug: Introduce per-CPU debugfs files Aaron Tomlin
2026-08-10 3:17 ` [PATCH v3 0/4] " Aaron Tomlin
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=20260808235522.380038-4-atomlin@atomlin.com \
--to=atomlin@atomlin.com \
--cc=bsegall@google.com \
--cc=chjohnst@mail.com \
--cc=dietmar.eggemann@arm.com \
--cc=juri.lelli@redhat.com \
--cc=kprateek.nayak@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=mproche@mail.com \
--cc=neelx@suse.com \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=sean@ashe.io \
--cc=steve@abita.co \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.com \
--cc=zhanxusheng1024@gmail.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