[PATCH 2/2] compat.bpf.h: add scx_bpf_reenqueue_local_from_anywhere() compat helper

From: Changwoo Min

Date: Mon Aug 17 2026 - 11:03:25 EST


scx_bpf_reenqueue_local()'s generic compat wrapper inlines a v1 fallback
that is only callable from ops.cpu_release, and veristat rejects it on
kernels without v2. Callers draining a local DSQ from an arbitrary
context (e.g. a tracepoint) must gate on a call-from-anywhere kfunc
directly.

Add scx_bpf_reenqueue_local_from_anywhere() to compat.bpf.h to
encapsulate that: call a call-from-anywhere kfunc when present and return
0, else return -ENOTSUP so the caller knows the drain did not run. Two
kfuncs qualify -- the v7.1 generic scx_bpf_dsq_reenq(), which will
eventually deprecate scx_bpf_reenqueue_local(), and the v6.19 v2
reenqueue-local variant. Prefer the generic one; v1 cannot be called from
anywhere, so it maps to -ENOTSUP. scx_bpf_reenqueue_local() itself grows
the same generic-first preference, and the scx_bpf_dsq_reenq___compat
declaration and __COMPAT_has_generic_reenq() helper move above the v6.19
block so both wrappers can use them.

Test each ksym in its own branch: ORing two bpf_ksym_exists() checks
folds into a bitwise OR of the two weak ksym addresses, which the
verifier rejects.

No functional change intended.

Suggested-by: Andrea Righi <arighi@xxxxxxxxxx>
Signed-off-by: Changwoo Min <changwoo@xxxxxxxxxx>
Signed-off-by: Tejun Heo <tj@xxxxxxxxxx>
---
tools/sched_ext/include/scx/compat.bpf.h | 39 ++++++++++++++++++------
1 file changed, 30 insertions(+), 9 deletions(-)

diff --git a/tools/sched_ext/include/scx/compat.bpf.h b/tools/sched_ext/include/scx/compat.bpf.h
index 03976f5851d9..55f118e40545 100644
--- a/tools/sched_ext/include/scx/compat.bpf.h
+++ b/tools/sched_ext/include/scx/compat.bpf.h
@@ -383,6 +383,17 @@ static inline void scx_bpf_task_set_dsq_vtime(struct task_struct *p, u64 vtime)
p->scx.dsq_vtime = vtime;
}

+/*
+ * v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
+ * will eventually deprecate scx_bpf_reenqueue_local().
+ */
+void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
+
+static inline bool __COMPAT_has_generic_reenq(void)
+{
+ return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
+}
+
/*
* v6.19: The new void variant can be called from anywhere while the older v1
* variant can only be called from ops.cpu_release(). The double ___ prefixes on
@@ -400,21 +411,31 @@ static inline bool __COMPAT_scx_bpf_reenqueue_local_from_anywhere(void)

static inline void scx_bpf_reenqueue_local(void)
{
- if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere())
+ if (__COMPAT_has_generic_reenq())
+ scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
+ else if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere())
scx_bpf_reenqueue_local___v2___compat();
else
scx_bpf_reenqueue_local___v1();
}

-/*
- * v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
- * will eventually deprecate scx_bpf_reenqueue_local().
- */
-void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
-
-static inline bool __COMPAT_has_generic_reenq(void)
+static inline int scx_bpf_reenqueue_local_from_anywhere(void)
{
- return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
+ /*
+ * The generic reenq kfunc and the v2 reenqueue-local variant can both be
+ * called from anywhere; v1 cannot. Test each ksym in its own branch with a
+ * distinct call: combining them with || would fold into a bitwise OR of the
+ * two ksym addresses, which the verifier rejects.
+ */
+ if (__COMPAT_has_generic_reenq()) {
+ scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
+ return 0;
+ }
+ if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere()) {
+ scx_bpf_reenqueue_local___v2___compat();
+ return 0;
+ }
+ return -ENOTSUP;
}

static inline void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags)
--
2.55.0