From: Andrea Righi <arighi@nvidia.com>
To: Tejun Heo <tj@kernel.org>
Cc: linux-kernel@vger.kernel.org,
Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Juri Lelli <juri.lelli@redhat.com>,
Vincent Guittot <vincent.guittot@linaro.org>,
David Vernet <void@manifault.com>,
Changwoo Min <changwoo@igalia.com>,
Emil Tsalapatis <emil@etsalapatis.com>,
Linus Torvalds <torvalds@linux-foundation.org>,
sched-ext@lists.linux.dev
Subject: Re: [PATCH sched_ext/for-7.2 2/2] sched_ext: Move shared helpers from ext.c into internal.h and cid.h
Date: Mon, 22 Jun 2026 20:45:32 +0200 [thread overview]
Message-ID: <ajmCzLPU6Jo_2iB9@gpd4> (raw)
In-Reply-To: <20260622173904.1169407-2-tj@kernel.org>
Hi Tejun,
On Mon, Jun 22, 2026 at 07:39:04AM -1000, Tejun Heo wrote:
> idle.c and cid.c are included into build_policy.c together with ext.c and
> use helpers that ext.c defines. Because the helpers live in ext.c, the two
> files can not parse as standalone units and clangd reports errors in them.
>
> Move the helpers to the headers they belong to. The op-dispatch macros and
> helpers plus scx_parent() to internal.h, and scx_cpu_arg()/scx_cpu_ret() to
> cid.h. No functional change. idle.c and cid.c now parse clean standalone.
>
> Suggested-by: Peter Zijlstra <peterz@infradead.org>
> Signed-off-by: Tejun Heo <tj@kernel.org>
> ---
Looks good to me.
Reviewed-by: Andrea Righi <arighi@nvidia.com>
Thanks,
-Andrea
> kernel/sched/ext/cid.h | 21 ++++++
> kernel/sched/ext/ext.c | 141 ------------------------------------
> kernel/sched/ext/internal.h | 121 +++++++++++++++++++++++++++++++
> 3 files changed, 142 insertions(+), 141 deletions(-)
>
> diff --git a/kernel/sched/ext/cid.h b/kernel/sched/ext/cid.h
> index 41d0802c6af3..9c4f4b907f12 100644
> --- a/kernel/sched/ext/cid.h
> +++ b/kernel/sched/ext/cid.h
> @@ -270,4 +270,25 @@ static inline u32 scx_cmask_nr_used_words(const struct scx_cmask *m)
> __w && ((cid) = __bs + __wi * 64 + __ffs64(__w), true); \
> __w &= __w - 1)
>
> +/*
> + * scx_cpu_arg() wraps a cpu arg being handed to an SCX op. For cid-form
> + * schedulers it resolves to the matching cid; for cpu-form it passes @cpu
> + * through. scx_cpu_ret() is the inverse for a cpu/cid returned from an op
> + * (currently only ops.select_cpu); it validates the BPF-supplied cid and
> + * triggers scx_error() on @sch if invalid.
> + */
> +static inline s32 scx_cpu_arg(s32 cpu)
> +{
> + if (scx_is_cid_type())
> + return __scx_cpu_to_cid(cpu);
> + return cpu;
> +}
> +
> +static inline s32 scx_cpu_ret(struct scx_sched *sch, s32 cpu_or_cid)
> +{
> + if (cpu_or_cid < 0 || !scx_is_cid_type())
> + return cpu_or_cid;
> + return scx_cid_to_cpu(sch, cpu_or_cid);
> +}
> +
> #endif /* _KERNEL_SCHED_EXT_CID_H */
> diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
> index ebf6dc84cb4d..dd41b84ae680 100644
> --- a/kernel/sched/ext/ext.c
> +++ b/kernel/sched/ext/ext.c
> @@ -258,8 +258,6 @@ __printf(5, 6) bool __scx_exit(struct scx_sched *sch,
> return ret;
> }
>
> -#define SCX_HAS_OP(sch, op) test_bit(SCX_OP_IDX(op), (sch)->has_op)
> -
> static long jiffies_delta_msecs(unsigned long at, unsigned long now)
> {
> if (time_after(at, now))
> @@ -274,20 +272,6 @@ static bool u32_before(u32 a, u32 b)
> }
>
> #ifdef CONFIG_EXT_SUB_SCHED
> -/**
> - * scx_parent - Find the parent sched
> - * @sch: sched to find the parent of
> - *
> - * Returns the parent scheduler or %NULL if @sch is root.
> - */
> -static struct scx_sched *scx_parent(struct scx_sched *sch)
> -{
> - if (sch->level)
> - return sch->ancestors[sch->level - 1];
> - else
> - return NULL;
> -}
> -
> /**
> * scx_next_descendant_pre - find the next descendant for pre-order walk
> * @pos: the current position (%NULL to initiate traversal)
> @@ -335,7 +319,6 @@ static void scx_set_task_sched(struct task_struct *p, struct scx_sched *sch)
> rcu_assign_pointer(p->scx.sched, sch);
> }
> #else /* CONFIG_EXT_SUB_SCHED */
> -static inline struct scx_sched *scx_parent(struct scx_sched *sch) { return NULL; }
> static inline struct scx_sched *scx_next_descendant_pre(struct scx_sched *pos, struct scx_sched *root) { return pos ? NULL : root; }
> static inline void scx_set_task_sched(struct task_struct *p, struct scx_sched *sch) {}
> #endif /* CONFIG_EXT_SUB_SCHED */
> @@ -495,123 +478,12 @@ static bool rq_is_open(struct rq *rq, u64 enq_flags)
> */
> DEFINE_PER_CPU(struct rq *, scx_locked_rq_state);
>
> -static inline void update_locked_rq(struct rq *rq)
> -{
> - /*
> - * Check whether @rq is actually locked. This can help expose bugs
> - * or incorrect assumptions about the context in which a kfunc or
> - * callback is executed.
> - */
> - if (rq)
> - lockdep_assert_rq_held(rq);
> - __this_cpu_write(scx_locked_rq_state, rq);
> -}
> -
> -/*
> - * SCX ops can recurse via scx_bpf_sub_dispatch() - the inner call must not
> - * clobber the outer's scx_locked_rq_state. Save it on entry, restore on exit.
> - */
> -#define SCX_CALL_OP(sch, op, locked_rq, args...) \
> -do { \
> - struct rq *__prev_locked_rq; \
> - \
> - if (locked_rq) { \
> - __prev_locked_rq = scx_locked_rq(); \
> - update_locked_rq(locked_rq); \
> - } \
> - (sch)->ops.op(args); \
> - if (locked_rq) \
> - update_locked_rq(__prev_locked_rq); \
> -} while (0)
> -
> /*
> * Flipped on enable per sch->is_cid_type. Declared in internal.h so
> * subsystem inlines can read it.
> */
> DEFINE_STATIC_KEY_FALSE(__scx_is_cid_type);
>
> -/*
> - * scx_cpu_arg() wraps a cpu arg being handed to an SCX op. For cid-form
> - * schedulers it resolves to the matching cid; for cpu-form it passes @cpu
> - * through. scx_cpu_ret() is the inverse for a cpu/cid returned from an op
> - * (currently only ops.select_cpu); it validates the BPF-supplied cid and
> - * triggers scx_error() on @sch if invalid.
> - */
> -static s32 scx_cpu_arg(s32 cpu)
> -{
> - if (scx_is_cid_type())
> - return __scx_cpu_to_cid(cpu);
> - return cpu;
> -}
> -
> -static s32 scx_cpu_ret(struct scx_sched *sch, s32 cpu_or_cid)
> -{
> - if (cpu_or_cid < 0 || !scx_is_cid_type())
> - return cpu_or_cid;
> - return scx_cid_to_cpu(sch, cpu_or_cid);
> -}
> -
> -#define SCX_CALL_OP_RET(sch, op, locked_rq, args...) \
> -({ \
> - struct rq *__prev_locked_rq; \
> - __typeof__((sch)->ops.op(args)) __ret; \
> - \
> - if (locked_rq) { \
> - __prev_locked_rq = scx_locked_rq(); \
> - update_locked_rq(locked_rq); \
> - } \
> - __ret = (sch)->ops.op(args); \
> - if (locked_rq) \
> - update_locked_rq(__prev_locked_rq); \
> - __ret; \
> -})
> -
> -/*
> - * SCX_CALL_OP_TASK*() invokes an SCX op that takes one or two task arguments
> - * and records them in current->scx.kf_tasks[] for the duration of the call. A
> - * kfunc invoked from inside such an op can then use
> - * scx_kf_arg_task_ok() to verify that its task argument is one of
> - * those subject tasks.
> - *
> - * Every SCX_CALL_OP_TASK*() call site invokes its op with @p's rq lock held -
> - * either via the @locked_rq argument here, or (for ops.select_cpu()) via @p's
> - * pi_lock held by try_to_wake_up() with rq tracking via scx_rq.in_select_cpu.
> - * So if kf_tasks[] is set, @p's scheduler-protected fields are stable.
> - *
> - * kf_tasks[] can not stack, so task-based SCX ops must not nest. The
> - * WARN_ON_ONCE() in each macro catches a re-entry of any of the three variants
> - * while a previous one is still in progress.
> - */
> -#define SCX_CALL_OP_TASK(sch, op, locked_rq, task, args...) \
> -do { \
> - WARN_ON_ONCE(current->scx.kf_tasks[0]); \
> - current->scx.kf_tasks[0] = task; \
> - SCX_CALL_OP((sch), op, locked_rq, task, ##args); \
> - current->scx.kf_tasks[0] = NULL; \
> -} while (0)
> -
> -#define SCX_CALL_OP_TASK_RET(sch, op, locked_rq, task, args...) \
> -({ \
> - __typeof__((sch)->ops.op(task, ##args)) __ret; \
> - WARN_ON_ONCE(current->scx.kf_tasks[0]); \
> - current->scx.kf_tasks[0] = task; \
> - __ret = SCX_CALL_OP_RET((sch), op, locked_rq, task, ##args); \
> - current->scx.kf_tasks[0] = NULL; \
> - __ret; \
> -})
> -
> -#define SCX_CALL_OP_2TASKS_RET(sch, op, locked_rq, task0, task1, args...) \
> -({ \
> - __typeof__((sch)->ops.op(task0, task1, ##args)) __ret; \
> - WARN_ON_ONCE(current->scx.kf_tasks[0]); \
> - current->scx.kf_tasks[0] = task0; \
> - current->scx.kf_tasks[1] = task1; \
> - __ret = SCX_CALL_OP_RET((sch), op, locked_rq, task0, task1, ##args); \
> - current->scx.kf_tasks[0] = NULL; \
> - current->scx.kf_tasks[1] = NULL; \
> - __ret; \
> -})
> -
> /**
> * scx_call_op_set_cpumask - invoke ops.set_cpumask / ops_cid.set_cmask for @task
> * @sch: scx_sched being invoked
> @@ -650,19 +522,6 @@ static inline void scx_call_op_set_cpumask(struct scx_sched *sch, struct rq *rq,
> current->scx.kf_tasks[0] = NULL;
> }
>
> -/* see SCX_CALL_OP_TASK() */
> -static __always_inline bool scx_kf_arg_task_ok(struct scx_sched *sch,
> - struct task_struct *p)
> -{
> - if (unlikely((p != current->scx.kf_tasks[0] &&
> - p != current->scx.kf_tasks[1]))) {
> - scx_error(sch, "called on a task not being operated on");
> - return false;
> - }
> -
> - return true;
> -}
> -
> enum scx_dsq_iter_flags {
> /* iterate in the reverse dispatch order */
> SCX_DSQ_ITER_REV = 1U << 16,
> diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
> index 1f5312b3b387..145272cb4d8a 100644
> --- a/kernel/sched/ext/internal.h
> +++ b/kernel/sched/ext/internal.h
> @@ -1553,6 +1553,111 @@ static inline struct rq *scx_locked_rq(void)
> return __this_cpu_read(scx_locked_rq_state);
> }
>
> +static inline void update_locked_rq(struct rq *rq)
> +{
> + /*
> + * Check whether @rq is actually locked. This can help expose bugs
> + * or incorrect assumptions about the context in which a kfunc or
> + * callback is executed.
> + */
> + if (rq)
> + lockdep_assert_rq_held(rq);
> + __this_cpu_write(scx_locked_rq_state, rq);
> +}
> +
> +#define SCX_HAS_OP(sch, op) test_bit(SCX_OP_IDX(op), (sch)->has_op)
> +
> +/*
> + * SCX ops can recurse via scx_bpf_sub_dispatch() - the inner call must not
> + * clobber the outer's scx_locked_rq_state. Save it on entry, restore on exit.
> + */
> +#define SCX_CALL_OP(sch, op, locked_rq, args...) \
> +do { \
> + struct rq *__prev_locked_rq; \
> + \
> + if (locked_rq) { \
> + __prev_locked_rq = scx_locked_rq(); \
> + update_locked_rq(locked_rq); \
> + } \
> + (sch)->ops.op(args); \
> + if (locked_rq) \
> + update_locked_rq(__prev_locked_rq); \
> +} while (0)
> +
> +#define SCX_CALL_OP_RET(sch, op, locked_rq, args...) \
> +({ \
> + struct rq *__prev_locked_rq; \
> + __typeof__((sch)->ops.op(args)) __ret; \
> + \
> + if (locked_rq) { \
> + __prev_locked_rq = scx_locked_rq(); \
> + update_locked_rq(locked_rq); \
> + } \
> + __ret = (sch)->ops.op(args); \
> + if (locked_rq) \
> + update_locked_rq(__prev_locked_rq); \
> + __ret; \
> +})
> +
> +/*
> + * SCX_CALL_OP_TASK*() invokes an SCX op that takes one or two task arguments
> + * and records them in current->scx.kf_tasks[] for the duration of the call. A
> + * kfunc invoked from inside such an op can then use
> + * scx_kf_arg_task_ok() to verify that its task argument is one of
> + * those subject tasks.
> + *
> + * Every SCX_CALL_OP_TASK*() call site invokes its op with @p's rq lock held -
> + * either via the @locked_rq argument here, or (for ops.select_cpu()) via @p's
> + * pi_lock held by try_to_wake_up() with rq tracking via scx_rq.in_select_cpu.
> + * So if kf_tasks[] is set, @p's scheduler-protected fields are stable.
> + *
> + * kf_tasks[] can not stack, so task-based SCX ops must not nest. The
> + * WARN_ON_ONCE() in each macro catches a re-entry of any of the three variants
> + * while a previous one is still in progress.
> + */
> +#define SCX_CALL_OP_TASK(sch, op, locked_rq, task, args...) \
> +do { \
> + WARN_ON_ONCE(current->scx.kf_tasks[0]); \
> + current->scx.kf_tasks[0] = task; \
> + SCX_CALL_OP((sch), op, locked_rq, task, ##args); \
> + current->scx.kf_tasks[0] = NULL; \
> +} while (0)
> +
> +#define SCX_CALL_OP_TASK_RET(sch, op, locked_rq, task, args...) \
> +({ \
> + __typeof__((sch)->ops.op(task, ##args)) __ret; \
> + WARN_ON_ONCE(current->scx.kf_tasks[0]); \
> + current->scx.kf_tasks[0] = task; \
> + __ret = SCX_CALL_OP_RET((sch), op, locked_rq, task, ##args); \
> + current->scx.kf_tasks[0] = NULL; \
> + __ret; \
> +})
> +
> +#define SCX_CALL_OP_2TASKS_RET(sch, op, locked_rq, task0, task1, args...) \
> +({ \
> + __typeof__((sch)->ops.op(task0, task1, ##args)) __ret; \
> + WARN_ON_ONCE(current->scx.kf_tasks[0]); \
> + current->scx.kf_tasks[0] = task0; \
> + current->scx.kf_tasks[1] = task1; \
> + __ret = SCX_CALL_OP_RET((sch), op, locked_rq, task0, task1, ##args); \
> + current->scx.kf_tasks[0] = NULL; \
> + current->scx.kf_tasks[1] = NULL; \
> + __ret; \
> +})
> +
> +/* see SCX_CALL_OP_TASK() */
> +static __always_inline bool scx_kf_arg_task_ok(struct scx_sched *sch,
> + struct task_struct *p)
> +{
> + if (unlikely((p != current->scx.kf_tasks[0] &&
> + p != current->scx.kf_tasks[1]))) {
> + scx_error(sch, "called on a task not being operated on");
> + return false;
> + }
> +
> + return true;
> +}
> +
> static inline bool scx_bypassing(struct scx_sched *sch, s32 cpu)
> {
> return unlikely(per_cpu_ptr(sch->pcpu, cpu)->flags &
> @@ -1633,6 +1738,20 @@ static inline struct scx_sched *scx_prog_sched(const struct bpf_prog_aux *aux)
>
> return NULL;
> }
> +
> +/**
> + * scx_parent - Find the parent sched
> + * @sch: sched to find the parent of
> + *
> + * Returns the parent scheduler or %NULL if @sch is root.
> + */
> +static inline struct scx_sched *scx_parent(struct scx_sched *sch)
> +{
> + if (sch->level)
> + return sch->ancestors[sch->level - 1];
> + else
> + return NULL;
> +}
> #else /* CONFIG_EXT_SUB_SCHED */
> static inline struct scx_sched *scx_task_sched(const struct task_struct *p)
> {
> @@ -1656,6 +1775,8 @@ static inline struct scx_sched *scx_prog_sched(const struct bpf_prog_aux *aux)
> {
> return rcu_dereference_all(scx_root);
> }
> +
> +static inline struct scx_sched *scx_parent(struct scx_sched *sch) { return NULL; }
> #endif /* CONFIG_EXT_SUB_SCHED */
>
> #endif /* _KERNEL_SCHED_EXT_INTERNAL_H */
> --
> 2.54.0
>
next prev parent reply other threads:[~2026-06-22 18:45 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-22 17:39 [PATCH sched_ext/for-7.2 1/2] sched_ext: Make kernel/sched/ext/ sources self-contained for clangd Tejun Heo
2026-06-22 17:39 ` [PATCH sched_ext/for-7.2 2/2] sched_ext: Move shared helpers from ext.c into internal.h and cid.h Tejun Heo
2026-06-22 18:45 ` Andrea Righi [this message]
2026-06-22 18:26 ` [PATCH sched_ext/for-7.2 1/2] sched_ext: Make kernel/sched/ext/ sources self-contained for clangd Andrea Righi
2026-06-22 20:37 ` [PATCH v2 " Tejun Heo
2026-06-22 21:05 ` [PATCH " Tejun Heo
2026-06-23 7:52 ` Peter Zijlstra
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=ajmCzLPU6Jo_2iB9@gpd4 \
--to=arighi@nvidia.com \
--cc=changwoo@igalia.com \
--cc=emil@etsalapatis.com \
--cc=juri.lelli@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=sched-ext@lists.linux.dev \
--cc=tj@kernel.org \
--cc=torvalds@linux-foundation.org \
--cc=vincent.guittot@linaro.org \
--cc=void@manifault.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