All of lore.kernel.org
 help / color / mirror / Atom feed
From: K Prateek Nayak <kprateek.nayak@amd.com>
To: Aaron Tomlin <atomlin@atomlin.com>, <mingo@redhat.com>,
	<peterz@infradead.org>, <juri.lelli@redhat.com>,
	<vincent.guittot@linaro.org>
Cc: <paulmck@kernel.org>, <dietmar.eggemann@arm.com>,
	<rostedt@goodmis.org>, <bsegall@google.com>, <mgorman@suse.de>,
	<vschneid@redhat.com>, <zhanxusheng1024@gmail.com>,
	<neelx@suse.com>, <chjohnst@mail.com>, <mproche@mail.com>,
	<sean@ashe.io>, <steve@abita.co>, <rishil1999@outlook.com>,
	<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v10 tip/sched/core 1/6] sched: Annotate rq->rd with __rcu and update lockless readers
Date: Fri, 30 Oct 2026 10:11:12 +0530	[thread overview]
Message-ID: <848a0612-61d4-4f59-9f39-e7aaaac7cb5b@amd.com> (raw)
In-Reply-To: <20261030013817.753804-2-atomlin@atomlin.com>

Hello Aaron,

On 10/30/2026 7:08 AM, Aaron Tomlin wrote:
> The root_domain pointer rd field in struct rq is updated dynamically
> using RCU, and its memory reclamation is deferred via call_rcu() in
> rq_attach_root(). However, struct rq's rd field was missing the __rcu
> compiler annotation, and several lockless readers across the scheduler
> subsystem accessed rq->rd directly without using RCU dereference
> primitives.
> 
> Add the __rcu annotation to struct rq's rd field. For code clarity,
> introduce the rcu_dereference_root_domain() helper macro to validate
> access under sched_domains_mutex or active RCU-sched read-side critical
> sections. While mechanically identical to
> rcu_dereference_sched_domain(), defining rcu_dereference_root_domain()
> preserves the natural symmetry of rq->rd and rq->sd in struct rq, while
> keeping grep-ability straightforward. Update readers and accessors
> across kernel/sched/ to use rcu_dereference_root_domain().
> 
> This ensures proper memory ordering, enables Sparse static analysis
> validation, and avoids false Lockdep warnings under CONFIG_PROVE_RCU.
> 
> Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
> ---
>  kernel/sched/core.c     | 24 ++++++++-----
>  kernel/sched/deadline.c | 77 ++++++++++++++++++++++-------------------
>  kernel/sched/fair.c     | 27 +++++++++------
>  kernel/sched/rt.c       | 64 +++++++++++++++++++---------------
>  kernel/sched/sched.h    |  7 ++--
>  kernel/sched/syscalls.c |  8 ++---
>  kernel/sched/topology.c | 13 +++----
>  7 files changed, 127 insertions(+), 93 deletions(-)
> 
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index d7c3b43158de..4ead07fd0f6d 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -8542,8 +8542,10 @@ void set_rq_online(struct rq *rq)
>  {
>  	if (!rq->online) {
>  		const struct sched_class *class;
> +		struct root_domain *rd;
>  
> -		cpumask_set_cpu(rq->cpu, rq->rd->online);
> +		rd = rcu_dereference_root_domain(rq->rd);

nit. Using rcu_dereference_root_domain() everywhere is a bit mouthful.
How about a couple helpers like like:

#define rq_rd(rq)	rcu_dereference_root_domain(rq->rd)
#define cpu_rd(cpu)	rcu_dereference_root_domain(cpu_rq(cpu)->rd)

... and those makes the usage much shorter. Thoughts?

I've attached a coccinelle script at the end that I used to do the
conversion.

> +		cpumask_set_cpu(rq->cpu, rd->online);
>  		rq->online = 1;
>  
>  		for_each_class(class) {
> @@ -8557,6 +8559,7 @@ void set_rq_offline(struct rq *rq)
>  {
>  	if (rq->online) {
>  		const struct sched_class *class;
> +		struct root_domain *rd;
>  
>  		update_rq_clock(rq);
>  		for_each_class(class) {
> @@ -8564,7 +8567,8 @@ void set_rq_offline(struct rq *rq)
>  				class->rq_offline(rq);
>  		}
>  
> -		cpumask_clear_cpu(rq->cpu, rq->rd->online);
> +		rd = rcu_dereference_root_domain(rq->rd);

nit. If there is no rq_lock() / rcu dependnecy, maybe you can
initialize rd up top where you declare it for.

> +		cpumask_clear_cpu(rq->cpu, rd->online);
>  		rq->online = 0;
>  	}
>  }
> @@ -8572,10 +8576,12 @@ void set_rq_offline(struct rq *rq)
>  static inline void sched_set_rq_online(struct rq *rq, int cpu)
>  {
>  	struct rq_flags rf;
> +	struct root_domain *rd;

nit. reverse x-mas declaration style would be better IMO.

>  
>  	rq_lock_irqsave(rq, &rf);

... and since you are touching these bits, maybe you can also
covert these to guard(rq_lock_irqsave)(rq) and save on needing to
have explicit rq_flags.

> -	if (rq->rd) {
> -		BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span));
> +	rd = rcu_dereference_root_domain(rq->rd);
> +	if (rd) {
> +		BUG_ON(!cpumask_test_cpu(cpu, rd->span));
>  		set_rq_online(rq);
>  	}
>  	rq_unlock_irqrestore(rq, &rf);
> @@ -8584,10 +8590,12 @@ static inline void sched_set_rq_online(struct rq *rq, int cpu)
>  static inline void sched_set_rq_offline(struct rq *rq, int cpu)
>  {
>  	struct rq_flags rf;
> +	struct root_domain *rd;
>  
>  	rq_lock_irqsave(rq, &rf);
> -	if (rq->rd) {
> -		BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span));
> +	rd = rcu_dereference_root_domain(rq->rd);
> +	if (rd) {
> +		BUG_ON(!cpumask_test_cpu(cpu, rd->span));
>  		set_rq_offline(rq);
>  	}
>  	rq_unlock_irqrestore(rq, &rf);
> @@ -9009,8 +9017,8 @@ void __init sched_init(void)
>  #endif
>  		rq->next_class = &idle_sched_class;
>  
> -		rq->sd = NULL;
> -		rq->rd = NULL;
> +		RCU_INIT_POINTER(rq->sd, NULL);
> +		RCU_INIT_POINTER(rq->rd, NULL);
>  		rq->cpu_capacity = SCHED_CAPACITY_SCALE;
>  		rq->balance_callback = &balance_push_callback;
>  		rq->active_balance = 0;
> diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
> index c0ebdcde5fe5..2dcb3722c317 100644
> --- a/kernel/sched/deadline.c
> +++ b/kernel/sched/deadline.c
> @@ -122,12 +122,12 @@ static inline struct dl_bw *dl_bw_of(int i)
>  {
>  	RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held(),
>  			 "sched RCU must be held");

Do we need this warbing given we are using rcu_dereference_all_check()
which checks for RCU too?

> -	return &cpu_rq(i)->rd->dl_bw;
> +	return &rcu_dereference_root_domain(cpu_rq(i)->rd)->dl_bw;
>  }
>  
>  static inline int dl_bw_cpus(int i)
>  {
> -	struct root_domain *rd = cpu_rq(i)->rd;
> +	struct root_domain *rd = rcu_dereference_root_domain(cpu_rq(i)->rd);
>  
>  	RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held(),
>  			 "sched RCU must be held");

this warning may not be required either.

[..snip..]

> @@ -2167,6 +2167,9 @@ queue_balance_callback(struct rq *rq,
>  	rq->balance_callback = head;
>  }
>  
> +#define rcu_dereference_root_domain(p) \

                                       ^
This argument here can be 'rd' for better clarity
                                   v

> +	rcu_dereference_all_check((p), lockdep_is_held(&sched_domains_mutex))
> +
>  #define rcu_dereference_sched_domain(p) \
>  	rcu_dereference_all_check((p), lockdep_is_held(&sched_domains_mutex))
>  

-- 
Thanks and Regards,
Prateek

Assisted-by: LLM

root_domain_helpers.cocci
--------------------------
// SPDX-License-Identifier: GPL-2.0-only
/// Replace open-coded rcu_dereference_root_domain() with rq_rd()/cpu_rd().
///
///   rcu_dereference_root_domain(cpu_rq(cpu)->rd)  ->  cpu_rd(cpu)
///   rcu_dereference_root_domain(rq->rd)           ->  rq_rd(rq)
//
// Confidence: High
// Options: --include-headers

virtual patch
virtual report

@initialize:python@
@@
import re
import linecache

HELPER_DEF = re.compile(r'^\s*#\s*define\s+(rq_rd|cpu_rd)\b')

def in_helper_define(p):
    """True if the match is inside the #define of rq_rd/cpu_rd
    (handles backslash-continued macro bodies too)."""
    f, n = p[0].file, int(p[0].line)
    while n > 1 and linecache.getline(f, n - 1).rstrip().endswith('\\'):
        n -= 1
    return bool(HELPER_DEF.match(linecache.getline(f, n)))

// ---------------------------------------------------------------
// Rule 1: cpu_rq(cpu)->rd  ->  cpu_rd(cpu)   (must run before rule 2)
// ---------------------------------------------------------------
@cpu_m@
expression cpu;
position p;
@@
rcu_dereference_root_domain@p(cpu_rq(cpu)->rd)

@script:python cpu_filter@
p << cpu_m.p;
@@
if in_helper_define(p):
    cocci.include_match(False)

@cpu_fix depends on patch@
expression cpu;
position cpu_m.p;
@@
- rcu_dereference_root_domain@p(cpu_rq(cpu)->rd)
+ cpu_rd(cpu)

// ---------------------------------------------------------------
// Rule 2: rq->rd  ->  rq_rd(rq)
// ---------------------------------------------------------------
@rq_m@
expression rq;
position p;
@@
rcu_dereference_root_domain@p(rq->rd)

@script:python rq_filter@
p << rq_m.p;
@@
if in_helper_define(p):
    cocci.include_match(False)

@rq_fix depends on patch@
expression rq;
position rq_m.p;
@@
- rcu_dereference_root_domain@p(rq->rd)
+ rq_rd(rq)

// ---------------------------------------------------------------
// Report mode
// ---------------------------------------------------------------
@script:python depends on report@
p << cpu_m.p;
@@
coccilib.report.print_report(p[0], "use cpu_rd() helper")

@script:python depends on report@
p << rq_m.p;
@@
coccilib.report.print_report(p[0], "use rq_rd() helper")
---

Build and boot tested after applying with:

spatch --python python3 --sp-file root_domain_helpers.cocci -D patch \
       --dir kernel/sched --include-headers --in-place -j $(nproc)


  reply	other threads:[~2026-10-30  4:41 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30  1:38 [PATCH v10 tip/sched/core 0/6] Introduce per-CPU debugfs files Aaron Tomlin
2026-10-30  1:38 ` [PATCH v10 tip/sched/core 1/6] sched: Annotate rq->rd with __rcu and update lockless readers Aaron Tomlin
2026-10-30  4:41   ` K Prateek Nayak [this message]
2026-10-30 13:51     ` Aaron Tomlin
2026-10-30  1:38 ` [PATCH v10 tip/sched/core 2/6] sched/debug: Protect lockless rq->rd access in print_dl_rq() Aaron Tomlin
2026-10-30  1:38 ` [PATCH v10 tip/sched/core 3/6] sched/debug: Protect lockless rq->curr access in print_cpu() Aaron Tomlin
2026-10-30  1:38 ` [PATCH v10 tip/sched/core 4/6] sched/debug: Protect p->mm access in sched_show_numa() Aaron Tomlin
2026-10-30  1:38 ` [PATCH v10 tip/sched/core 5/6] sched/fair: Use list_for_each_entry_rcu() in print_cfs_stats() Aaron Tomlin
2026-10-30  1:38 ` [PATCH v10 tip/sched/core 6/6] sched/debug: Introduce per-CPU debugfs files Aaron Tomlin
2026-10-30  6:05   ` K Prateek Nayak
2026-10-30 13:07     ` 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=848a0612-61d4-4f59-9f39-e7aaaac7cb5b@amd.com \
    --to=kprateek.nayak@amd.com \
    --cc=atomlin@atomlin.com \
    --cc=bsegall@google.com \
    --cc=chjohnst@mail.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=juri.lelli@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mgorman@suse.de \
    --cc=mingo@redhat.com \
    --cc=mproche@mail.com \
    --cc=neelx@suse.com \
    --cc=paulmck@kernel.org \
    --cc=peterz@infradead.org \
    --cc=rishil1999@outlook.com \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.