summaryrefslogtreecommitdiff
path: root/kernel/sched/ext/internal.h
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2026-06-22 07:29:39 -1000
committerTejun Heo <tj@kernel.org>2026-06-22 10:41:34 -1000
commit4437ad129cf5b37c00a5bc9fa5989d1da4d64d07 (patch)
treea69f00dce59f6d66e1a621d90d113c8e1b38543a /kernel/sched/ext/internal.h
parent3cd1f76be638b7386201171e7bb4c88095774dd5 (diff)
downloadlwn-4437ad129cf5b37c00a5bc9fa5989d1da4d64d07.tar.gz
lwn-4437ad129cf5b37c00a5bc9fa5989d1da4d64d07.zip
sched_ext: Move shared helpers from ext.c into internal.h and cid.h
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> Reviewed-by: Andrea Righi <arighi@nvidia.com>
Diffstat (limited to 'kernel/sched/ext/internal.h')
-rw-r--r--kernel/sched/ext/internal.h121
1 files changed, 121 insertions, 0 deletions
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 */