* [PATCHSET sched_ext/for-7.3-fixes] sched_ext: Sync tools headers from the scx repo
@ 2026-08-18 20:57 Tejun Heo
2026-08-18 20:57 ` [PATCH 1/2] sched_ext: Sync tools autogen enum " Tejun Heo
2026-08-18 20:57 ` [PATCH 2/2] sched_ext: Sync common and compat " Tejun Heo
0 siblings, 2 replies; 4+ messages in thread
From: Tejun Heo @ 2026-08-18 20:57 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min
Cc: Gavin Guo, Emil Tsalapatis, sched-ext, linux-kernel
Hello,
The scx repo carries copies of the tools/sched_ext/include/scx headers
which accumulated fixes and improvements that the kernel copies lack. Sync
them back:
- __COMPAT_read_enum() can now recover 64-bit scx enum values on kernels
whose BTF lacks BTF_KIND_ENUM64 encoding, backed by a new autogenerated
enumerator ABI table.
- is_migration_disabled() no longer under-reports for the current task on
v6.18+ !PREEMPT_RCU kernels where the BPF prolog stopped disabling
migration.
- The scx_bpf_dsq_peek() version gate and
scx_bpf_reenqueue_local_from_anywhere() which Gavin and Changwoo posted
are picked up with the review feedback folded in.
- __COMPAT_scx_bpf_cpu_curr() is restored for schedulers which still run
on pre-v6.18 kernels, and the rq clock helpers document their stale-read
behavior on idle CPUs.
This patchset contains the following two patches:
0001-sched_ext-Sync-tools-autogen-enum-headers-from-the-s.patch
0002-sched_ext-Sync-common-and-compat-headers-from-the-sc.patch
and is also available in the following git branch:
git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext.git scx-tools-header-sync
Based on sched_ext/for-7.3-fixes (5f01293930d1).
tools/sched_ext/include/scx/common.bpf.h | 140 ++++++++++++---
tools/sched_ext/include/scx/compat.bpf.h | 68 ++++++--
tools/sched_ext/include/scx/compat.h | 97 +++++++++++
tools/sched_ext/include/scx/enum_defs.autogen.h | 5 +-
tools/sched_ext/include/scx/enums_abi.autogen.h | 223 ++++++++++++++++++++++++
5 files changed, 501 insertions(+), 32 deletions(-)
Thanks.
--
tejun
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] sched_ext: Sync tools autogen enum headers from the scx repo
2026-08-18 20:57 [PATCHSET sched_ext/for-7.3-fixes] sched_ext: Sync tools headers from the scx repo Tejun Heo
@ 2026-08-18 20:57 ` Tejun Heo
2026-08-18 20:57 ` [PATCH 2/2] sched_ext: Sync common and compat " Tejun Heo
1 sibling, 0 replies; 4+ messages in thread
From: Tejun Heo @ 2026-08-18 20:57 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min
Cc: Gavin Guo, Emil Tsalapatis, sched-ext, linux-kernel, Tejun Heo
Regenerate enum_defs.autogen.h against the current tree, picking up the
dispatch verdict enums and dropping the marker for the removed
SCX_RQ_IN_BALANCE. Add enums_abi.autogen.h, a table of 64-bit scx enumerator
values generated from vmlinux.h, used as the substitution source when the
running kernel's BTF truncates 64-bit enum values to 32 bits.
Signed-off-by: Tejun Heo <tj@kernel.org>
---
.../sched_ext/include/scx/enum_defs.autogen.h | 5 +-
.../sched_ext/include/scx/enums_abi.autogen.h | 223 ++++++++++++++++++
2 files changed, 227 insertions(+), 1 deletion(-)
create mode 100644 tools/sched_ext/include/scx/enums_abi.autogen.h
diff --git a/tools/sched_ext/include/scx/enum_defs.autogen.h b/tools/sched_ext/include/scx/enum_defs.autogen.h
index 19aa1de3e700..63b6b14b19bd 100644
--- a/tools/sched_ext/include/scx/enum_defs.autogen.h
+++ b/tools/sched_ext/include/scx/enum_defs.autogen.h
@@ -56,6 +56,10 @@
#define HAVE_SCX_DEQ_SLEEP
#define HAVE_SCX_DEQ_CORE_SCHED_EXEC
#define HAVE_SCX_DEQ_SCHED_CHANGE
+#define HAVE_SCX_DSP_NONE
+#define HAVE_SCX_DSP_LOCAL
+#define HAVE_SCX_DSP_PREV
+#define HAVE_SCX_DSP_RETRY
#define HAVE_SCX_DSQ_FLAG_BUILTIN
#define HAVE_SCX_DSQ_FLAG_LOCAL_ON
#define HAVE_SCX_DSQ_INVALID
@@ -188,7 +192,6 @@
#define HAVE_SCX_RQ_SUB_IDLE_RENOTIFY
#define HAVE_SCX_RQ_ROOT_IDLE_RENOTIFY
#define HAVE_SCX_RQ_IN_WAKEUP
-#define HAVE_SCX_RQ_IN_BALANCE
#define HAVE_SCX_RQ_IN_DISPATCH
#define HAVE_SCX_SCHED_PCPU_BYPASSING
#define HAVE_SCX_SLICE_OOB_DUR_BITS
diff --git a/tools/sched_ext/include/scx/enums_abi.autogen.h b/tools/sched_ext/include/scx/enums_abi.autogen.h
new file mode 100644
index 000000000000..d53899764f5a
--- /dev/null
+++ b/tools/sched_ext/include/scx/enums_abi.autogen.h
@@ -0,0 +1,223 @@
+/*
+ * WARNING: This file is autogenerated from gen_enum_defs.py [1].
+ *
+ * scx enumerator values from the vmlinux.h this tree is built against.
+ * Used as the substitution source when the running kernel's BTF lacks
+ * BTF_KIND_ENUM64 encoding and 64-bit enum values are truncated.
+ *
+ * [1] https://github.com/sched-ext/scx/blob/main/scripts/gen_enum_defs.py
+ */
+
+#ifndef __ENUMS_ABI_AUTOGEN_H__
+#define __ENUMS_ABI_AUTOGEN_H__
+
+struct __scx_enum_abi_val {
+ const char *type;
+ const char *name;
+ u64 val;
+};
+
+static const struct __scx_enum_abi_val __scx_enum_abi_vals[]
+ __attribute__((unused)) = {
+ { "scx_arena_consts", "SCX_ARENA_MIN_ORDER", 0x3LLU },
+ { "scx_arena_consts", "SCX_ARENA_GROW_PAGES", 0x4LLU },
+ { "scx_cap_flags", "__SCX_CAP_ENQ_IMMED", 0x0LLU },
+ { "scx_cap_flags", "__SCX_CAP_ENQ", 0x1LLU },
+ { "scx_cap_flags", "__SCX_CAP_PREEMPT", 0x2LLU },
+ { "scx_cap_flags", "__SCX_CAP_PERF", 0x3LLU },
+ { "scx_cap_flags", "__SCX_NR_CAPS", 0x4LLU },
+ { "scx_cap_flags", "__SCX_CAP_ALL", 0xfLLU },
+ { "scx_cap_flags", "SCX_CAP_ENQ_IMMED", 0x1LLU },
+ { "scx_cap_flags", "SCX_CAP_ENQ", 0x2LLU },
+ { "scx_cap_flags", "SCX_CAP_PREEMPT", 0x4LLU },
+ { "scx_cap_flags", "SCX_CAP_PERF", 0x8LLU },
+ { "scx_cap_flags", "SCX_CAP_BASE", 0x1LLU },
+ { "scx_cap_flags", "SCX_CAPS_REENQ_ON_LOSS", 0x3LLU },
+ { "scx_cid_consts", "SCX_CID_SHARD_SIZE_DFL", 0x18LLU },
+ { "scx_cid_consts", "SCX_CID_SHARD_MAX_CPUS", 0x200LLU },
+ { "scx_consts", "SCX_DSP_DFL_MAX_BATCH", 0x20LLU },
+ { "scx_consts", "SCX_DSP_MAX_LOOPS", 0x20LLU },
+ { "scx_consts", "SCX_WATCHDOG_MAX_TIMEOUT", 0x7530LLU },
+ { "scx_consts", "SCX_RESCUE_DFL_BW_PPT", 0x14LLU },
+ { "scx_consts", "SCX_RESCUE_MAX_BW_PPT", 0xfaLLU },
+ { "scx_consts", "SCX_RESCUE_DISABLE", 0xffffffffLLU },
+ { "scx_consts", "SCX_RESCUE_DFL_QUANTUM_US", 0x1388LLU },
+ { "scx_consts", "SCX_RESCUE_MIN_QUANTUM_US", 0x3e8LLU },
+ { "scx_consts", "SCX_RESCUE_MAX_QUANTUM_US", 0x186a0LLU },
+ { "scx_consts", "SCX_RESCUE_MIN_SLICE_US", 0x3e8LLU },
+ { "scx_consts", "SCX_RESCUE_OVERLOAD_MULT", 0x10LLU },
+ { "scx_consts", "SCX_RESCUE_MIN_OVERLOAD_MS", 0x3e8LLU },
+ { "scx_consts", "SCX_RESCUE_MAX_OVERLOAD_MS", 0x3a98LLU },
+ { "scx_consts", "SCX_TID_CHUNK", 0x400LLU },
+ { "scx_consts", "SCX_EXIT_BT_LEN", 0x40LLU },
+ { "scx_consts", "SCX_EXIT_MSG_LEN", 0x400LLU },
+ { "scx_consts", "SCX_EXIT_DUMP_DFL_LEN", 0x8000LLU },
+ { "scx_consts", "SCX_CPUPERF_ONE", 0x400LLU },
+ { "scx_consts", "SCX_TASK_ITER_BATCH", 0x20LLU },
+ { "scx_consts", "SCX_BYPASS_HOST_NTH", 0x2LLU },
+ { "scx_consts", "SCX_BYPASS_LB_DFL_INTV_US", 0x7a120LLU },
+ { "scx_consts", "SCX_BYPASS_LB_DONOR_PCT", 0x7dLLU },
+ { "scx_consts", "SCX_BYPASS_LB_MIN_DELTA_DIV", 0x4LLU },
+ { "scx_consts", "SCX_BYPASS_LB_BATCH", 0x100LLU },
+ { "scx_consts", "SCX_REENQ_MAX_REPEAT", 0x100LLU },
+ { "scx_consts", "SCX_SUB_MAX_DEPTH", 0x4LLU },
+ { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_RT", 0x0LLU },
+ { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_DL", 0x1LLU },
+ { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_STOP", 0x2LLU },
+ { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_UNKNOWN", 0x3LLU },
+ { "scx_deq_flags", "SCX_DEQ_SLEEP", 0x1LLU },
+ { "scx_deq_flags", "SCX_DEQ_CORE_SCHED_EXEC", 0x100000000LLU },
+ { "scx_deq_flags", "SCX_DEQ_SCHED_CHANGE", 0x200000000LLU },
+ { "scx_dsp_verdict", "SCX_DSP_NONE", 0x0LLU },
+ { "scx_dsp_verdict", "SCX_DSP_LOCAL", 0x1LLU },
+ { "scx_dsp_verdict", "SCX_DSP_PREV", 0x2LLU },
+ { "scx_dsp_verdict", "SCX_DSP_RETRY", 0x3LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_FLAG_BUILTIN", 0x8000000000000000LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_FLAG_LOCAL_ON", 0x4000000000000000LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_INVALID", 0x8000000000000000LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_GLOBAL", 0x8000000000000001LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_LOCAL", 0x8000000000000002LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_BYPASS", 0x8000000000000003LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_REJECT", 0x8000000000000004LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_RESCUE", 0x8000000000000005LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_LOCAL_ON", 0xc000000000000000LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_LOCAL_CPU_MASK", 0xffffffffLLU },
+ { "scx_dsq_iter_flags", "SCX_DSQ_ITER_REV", 0x10000LLU },
+ { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_SLICE", 0x40000000LLU },
+ { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_VTIME", 0x80000000LLU },
+ { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_USER_FLAGS", 0x10000LLU },
+ { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_ALL_FLAGS", 0xc0010000LLU },
+ { "scx_dsq_lnode_flags", "SCX_DSQ_LNODE_ITER_CURSOR", 0x1LLU },
+ { "scx_dsq_lnode_flags", "__SCX_DSQ_LNODE_PRIV_SHIFT", 0x10LLU },
+ { "scx_enable_state", "SCX_ENABLING", 0x0LLU },
+ { "scx_enable_state", "SCX_ENABLED", 0x1LLU },
+ { "scx_enable_state", "SCX_DISABLING", 0x2LLU },
+ { "scx_enable_state", "SCX_DISABLED", 0x3LLU },
+ { "scx_enq_flags", "SCX_ENQ_WAKEUP", 0x1LLU },
+ { "scx_enq_flags", "SCX_ENQ_HEAD", 0x10000LLU },
+ { "scx_enq_flags", "SCX_ENQ_CPU_SELECTED", 0x100000LLU },
+ { "scx_enq_flags", "SCX_ENQ_PREEMPT", 0x100000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_IMMED", 0x200000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_RESCUE", 0x400000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_REENQ", 0x10000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_LAST", 0x20000000000LLU },
+ { "scx_enq_flags", "__SCX_ENQ_INTERNAL_MASK", 0xff00000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_CLEAR_OPSS", 0x100000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_DSQ_PRIQ", 0x200000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_NESTED", 0x400000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_GDSQ_FALLBACK", 0x800000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_IGNORE_CAPS", 0x1000000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_APPLY_SLICE", 0x2000000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_SLICE_DFL", 0x4000000000000000LLU },
+ { "scx_ent_dsq_flags", "SCX_TASK_DSQ_ON_PRIQ", 0x1LLU },
+ { "scx_ent_flags", "SCX_TASK_QUEUED", 0x1LLU },
+ { "scx_ent_flags", "SCX_TASK_IN_CUSTODY", 0x2LLU },
+ { "scx_ent_flags", "SCX_TASK_RESET_RUNNABLE_AT", 0x4LLU },
+ { "scx_ent_flags", "SCX_TASK_DEQD_FOR_SLEEP", 0x8LLU },
+ { "scx_ent_flags", "SCX_TASK_SUB_INIT", 0x10LLU },
+ { "scx_ent_flags", "SCX_TASK_IMMED", 0x20LLU },
+ { "scx_ent_flags", "SCX_TASK_PROTECTED", 0x40LLU },
+ { "scx_ent_flags", "SCX_TASK_STATE_SHIFT", 0x8LLU },
+ { "scx_ent_flags", "SCX_TASK_STATE_BITS", 0x3LLU },
+ { "scx_ent_flags", "SCX_TASK_STATE_MASK", 0x700LLU },
+ { "scx_ent_flags", "SCX_TASK_NONE", 0x0LLU },
+ { "scx_ent_flags", "SCX_TASK_INIT_BEGIN", 0x100LLU },
+ { "scx_ent_flags", "SCX_TASK_INIT", 0x200LLU },
+ { "scx_ent_flags", "SCX_TASK_READY", 0x300LLU },
+ { "scx_ent_flags", "SCX_TASK_ENABLED", 0x400LLU },
+ { "scx_ent_flags", "SCX_TASK_DEAD", 0x500LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_REASON_SHIFT", 0xcLLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_REASON_BITS", 0x3LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_REASON_MASK", 0x7000LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_NONE", 0x0LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_KFUNC", 0x1000LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_IMMED", 0x2000LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_PREEMPTED", 0x3000LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_CAP", 0x4000LLU },
+ { "scx_ent_flags", "SCX_TASK_CURSOR", 0xffffffff80000000LLU },
+ { "scx_exit_code", "SCX_ECODE_RSN_HOTPLUG", 0x100000000LLU },
+ { "scx_exit_code", "SCX_ECODE_RSN_CGROUP_OFFLINE", 0x200000000LLU },
+ { "scx_exit_code", "SCX_ECODE_ACT_RESTART", 0x1000000000000LLU },
+ { "scx_exit_flags", "SCX_EFLAG_INITIALIZED", 0x1LLU },
+ { "scx_exit_kind", "SCX_EXIT_NONE", 0x0LLU },
+ { "scx_exit_kind", "SCX_EXIT_DONE", 0x1LLU },
+ { "scx_exit_kind", "SCX_EXIT_UNREG", 0x40LLU },
+ { "scx_exit_kind", "SCX_EXIT_UNREG_BPF", 0x41LLU },
+ { "scx_exit_kind", "SCX_EXIT_UNREG_KERN", 0x42LLU },
+ { "scx_exit_kind", "SCX_EXIT_SYSRQ", 0x43LLU },
+ { "scx_exit_kind", "SCX_EXIT_PARENT", 0x44LLU },
+ { "scx_exit_kind", "SCX_EXIT_PARENT_KILL", 0x45LLU },
+ { "scx_exit_kind", "SCX_EXIT_ERROR", 0x400LLU },
+ { "scx_exit_kind", "SCX_EXIT_ERROR_BPF", 0x401LLU },
+ { "scx_exit_kind", "SCX_EXIT_ERROR_STALL", 0x402LLU },
+ { "scx_exit_kind", "SCX_EXIT_ERROR_REENQ", 0x403LLU },
+ { "scx_exit_kind", "SCX_EXIT_ERROR_RESCUE", 0x404LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_UNLOCKED", 0x1LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_INIT_CIDS", 0x2LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_CPU_RELEASE", 0x4LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_DISPATCH", 0x8LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_ENQUEUE", 0x10LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_SELECT_CPU", 0x20LLU },
+ { "scx_kick_flags", "SCX_KICK_IDLE", 0x1LLU },
+ { "scx_kick_flags", "SCX_KICK_PREEMPT", 0x2LLU },
+ { "scx_kick_flags", "SCX_KICK_WAIT", 0x4LLU },
+ { "scx_opi", "SCX_OPI_BEGIN", 0x0LLU },
+ { "scx_opi", "SCX_OPI_NORMAL_BEGIN", 0x0LLU },
+ { "scx_opi", "SCX_OPI_NORMAL_END", 0x21LLU },
+ { "scx_opi", "SCX_OPI_CPU_HOTPLUG_BEGIN", 0x21LLU },
+ { "scx_opi", "SCX_OPI_CPU_HOTPLUG_END", 0x23LLU },
+ { "scx_opi", "SCX_OPI_END", 0x23LLU },
+ { "scx_ops_flags", "SCX_OPS_KEEP_BUILTIN_IDLE", 0x1LLU },
+ { "scx_ops_flags", "SCX_OPS_ENQ_LAST", 0x2LLU },
+ { "scx_ops_flags", "SCX_OPS_ENQ_EXITING", 0x4LLU },
+ { "scx_ops_flags", "SCX_OPS_SWITCH_PARTIAL", 0x8LLU },
+ { "scx_ops_flags", "SCX_OPS_ENQ_MIGRATION_DISABLED", 0x10LLU },
+ { "scx_ops_flags", "SCX_OPS_ALLOW_QUEUED_WAKEUP", 0x20LLU },
+ { "scx_ops_flags", "SCX_OPS_BUILTIN_IDLE_PER_NODE", 0x40LLU },
+ { "scx_ops_flags", "SCX_OPS_ALWAYS_ENQ_IMMED", 0x80LLU },
+ { "scx_ops_flags", "SCX_OPS_TID_TO_TASK", 0x100LLU },
+ { "scx_ops_flags", "SCX_OPS_ALL_FLAGS", 0x1ffLLU },
+ { "scx_ops_flags", "__SCX_OPS_INTERNAL_MASK", 0xff00000000000000LLU },
+ { "scx_ops_flags", "SCX_OPS_HAS_CPU_PREEMPT", 0x100000000000000LLU },
+ { "scx_ops_state", "SCX_OPSS_NONE", 0x0LLU },
+ { "scx_ops_state", "SCX_OPSS_QUEUEING", 0x1LLU },
+ { "scx_ops_state", "SCX_OPSS_QUEUED", 0x2LLU },
+ { "scx_ops_state", "SCX_OPSS_DISPATCHING", 0x3LLU },
+ { "scx_ops_state", "SCX_OPSS_QSEQ_SHIFT", 0x2LLU },
+ { "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_CORE", 0x1LLU },
+ { "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_IN_NODE", 0x2LLU },
+ { "scx_public_consts", "SCX_OPS_NAME_LEN", 0x80LLU },
+ { "scx_public_consts", "SCX_SLICE_DFL", 0x1312d00LLU },
+ { "scx_public_consts", "SCX_SLICE_BYPASS", 0x4c4b40LLU },
+ { "scx_public_consts", "SCX_SLICE_INF", 0xffffffffffffffffLLU },
+ { "scx_reenq_flags", "SCX_REENQ_ANY", 0x1LLU },
+ { "scx_reenq_flags", "SCX_REENQ_CAP_REVOKE", 0x2LLU },
+ { "scx_reenq_flags", "__SCX_REENQ_FILTER_MASK", 0xffffLLU },
+ { "scx_reenq_flags", "__SCX_REENQ_USER_MASK", 0x1LLU },
+ { "scx_reenq_flags", "SCX_REENQ_TSR_RQ_OPEN", 0x100000000LLU },
+ { "scx_reenq_flags", "SCX_REENQ_TSR_NOT_FIRST", 0x200000000LLU },
+ { "scx_reenq_flags", "__SCX_REENQ_TSR_MASK", 0xf00000000LLU },
+ { "scx_rq_flags", "SCX_RQ_ONLINE", 0x1LLU },
+ { "scx_rq_flags", "SCX_RQ_CAN_STOP_TICK", 0x2LLU },
+ { "scx_rq_flags", "SCX_RQ_CLK_VALID", 0x20LLU },
+ { "scx_rq_flags", "SCX_RQ_BAL_CB_PENDING", 0x40LLU },
+ { "scx_rq_flags", "SCX_RQ_SUB_IDLE_RENOTIFY", 0x80LLU },
+ { "scx_rq_flags", "SCX_RQ_ROOT_IDLE_RENOTIFY", 0x100LLU },
+ { "scx_rq_flags", "SCX_RQ_IN_WAKEUP", 0x10000LLU },
+ { "scx_rq_flags", "SCX_RQ_IN_DISPATCH", 0x20000LLU },
+ { "scx_sched_pcpu_flags", "SCX_SCHED_PCPU_BYPASSING", 0x1LLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_BITS", 0x2bLLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_BITS", 0x14LLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_MASK", 0x7ffffffffffLLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_SHIFT", 0x2bLLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_MASK", 0xfffffLLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_PENDING", 0x8000000000000000LLU },
+ { "scx_tg_flags", "SCX_TG_ONLINE", 0x1LLU },
+ { "scx_tg_flags", "SCX_TG_INITED", 0x2LLU },
+ { "scx_tg_flags", "SCX_TG_SUB_INIT", 0x4LLU },
+ { "scx_wake_flags", "SCX_WAKE_FORK", 0x4LLU },
+ { "scx_wake_flags", "SCX_WAKE_TTWU", 0x8LLU },
+ { "scx_wake_flags", "SCX_WAKE_SYNC", 0x10LLU },
+};
+
+#endif /* __ENUMS_ABI_AUTOGEN_H__ */
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] sched_ext: Sync common and compat headers from the scx repo
2026-08-18 20:57 [PATCHSET sched_ext/for-7.3-fixes] sched_ext: Sync tools headers from the scx repo Tejun Heo
2026-08-18 20:57 ` [PATCH 1/2] sched_ext: Sync tools autogen enum " Tejun Heo
@ 2026-08-18 20:57 ` Tejun Heo
[not found] ` <20260818210732.AB27B1F000E9@smtp.kernel.org>
1 sibling, 1 reply; 4+ messages in thread
From: Tejun Heo @ 2026-08-18 20:57 UTC (permalink / raw)
To: David Vernet, Andrea Righi, Changwoo Min
Cc: Gavin Guo, Emil Tsalapatis, sched-ext, linux-kernel, Tejun Heo
Sync common.bpf.h, compat.bpf.h and compat.h with the scx repo, which
accumulated the following:
- __COMPAT_read_enum() can now recover 64-bit scx enum values from kernel
BTF generated without BTF_KIND_ENUM64 support (pahole < 1.24 or
--skip_encoding_btf_enum64, e.g. COS/GKE kernels), substituting values
from the build-time vmlinux.h cross-checked against the low 32 bits the
kernel does provide.
- is_migration_disabled() no longer assumes the BPF prolog always disables
migration. Since 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for
trampoline.c") the prolog only does so under CONFIG_PREEMPT_RCU, so the
old current-task test under-reported on v6.18+ !PREEMPT_RCU kernels. A
runtime probe on bpf_scx_reg() handles older kernels with backported
trampoline behavior.
- __COMPAT_scx_bpf_dsq_peek() is gated behind kernel v7.1 where 2f2ea7709266
("sched_ext: Use dsq->first_task instead of list_empty() in
dispatch_enqueue() FIFO-tail") fixed the kfunc spuriously returning NULL
on non-empty FIFO DSQs, and the new
scx_bpf_reenqueue_local_from_anywhere() provides a callable-from-anywhere
reenqueue which prefers the generic scx_bpf_dsq_reenq(). Both were first
posted by Gavin Guo and Changwoo Min and are picked up here with the
review feedback folded in.
- __COMPAT_scx_bpf_cpu_curr() and the scx_bpf_cpu_rq() declaration are
restored. Schedulers built from these headers still run on pre-v6.18
kernels where scx_bpf_cpu_curr() does not resolve and the scx_bpf_cpu_rq()
fallback still exists.
- scx_clock_task() and scx_clock_pelt() document their stale-read behavior
for remote idle CPUs under NO_HZ_IDLE.
Link: https://lore.kernel.org/all/20260817143126.562923-1-changwoo@igalia.com
Signed-off-by: Tejun Heo <tj@kernel.org>
---
tools/sched_ext/include/scx/common.bpf.h | 140 +++++++++++++++++++----
tools/sched_ext/include/scx/compat.bpf.h | 68 +++++++++--
tools/sched_ext/include/scx/compat.h | 97 ++++++++++++++++
3 files changed, 274 insertions(+), 31 deletions(-)
diff --git a/tools/sched_ext/include/scx/common.bpf.h b/tools/sched_ext/include/scx/common.bpf.h
index 979d4cabfaf9..76f5e025e107 100644
--- a/tools/sched_ext/include/scx/common.bpf.h
+++ b/tools/sched_ext/include/scx/common.bpf.h
@@ -48,6 +48,7 @@
extern int LINUX_KERNEL_VERSION __kconfig;
extern const char CONFIG_CC_VERSION_TEXT[64] __kconfig __weak;
extern const char CONFIG_LOCALVERSION[64] __kconfig __weak;
+extern bool CONFIG_PREEMPT_RCU __kconfig __weak;
/*
* Earlier versions of clang/pahole lost upper 32bits in 64bit enums which can
@@ -97,6 +98,7 @@ s32 scx_bpf_pick_any_cpu_node(const cpumask_t *cpus_allowed, int node, u64 flags
s32 scx_bpf_pick_any_cpu(const cpumask_t *cpus_allowed, u64 flags) __ksym;
bool scx_bpf_task_running(const struct task_struct *p) __ksym;
s32 scx_bpf_task_cpu(const struct task_struct *p) __ksym;
+struct rq *scx_bpf_cpu_rq(s32 cpu) __ksym __weak;
struct rq *scx_bpf_locked_rq(void) __ksym;
struct task_struct *scx_bpf_cpu_curr(s32 cpu) __ksym __weak;
struct task_struct *scx_bpf_tid_to_task(u64 tid) __ksym __weak;
@@ -527,32 +529,103 @@ static __always_inline const struct cpumask *cast_mask(struct bpf_cpumask *mask)
return (const struct cpumask *)mask;
}
+/*
+ * True if the non-sleepable BPF trampoline prolog (__bpf_prog_enter) calls
+ * migrate_disable() for the current task. Recorded once by
+ * scx_lib_init_probe, an fentry program on bpf_scx_reg() that fires during
+ * the natural scheduler-attach call chain (auto-attached by scx_ops_attach!).
+ *
+ * Defaults to true (conservative). Over-reporting in is_migration_disabled()
+ * causes local-only dispatch, which is safe. Under-reporting can crash the
+ * scheduler, so we err high if the probe somehow fails to run.
+ */
+bool __scx_prolog_disables_migration __weak = true;
+
+/*
+ * scx_lib_init_probe - non-sleepable prolog probe.
+ *
+ * Attached to bpf_scx_reg(), the .reg callback in bpf_sched_ext_ops
+ * (kernel/sched/ext.c). The kernel's struct_ops machinery invokes
+ * bpf_scx_reg when userspace creates the scheduler link, before
+ * ops.init() fires. Its address is taken in the vtable, so the symbol
+ * is non-inlinable and has been stable since introduction.
+ *
+ * Entering via fentry runs us through __bpf_prog_enter -- the
+ * non-sleepable prolog that consumers of is_migration_disabled() live
+ * under.
+ *
+ * Loud warning: the prolog adds at most 1 to migration_disabled.
+ * Reading > 1 means something upstream in the
+ * bpf_struct_ops_link_create -> bpf_scx_reg path disabled migration
+ * before the prolog ran, invalidating the probe; audit and adjust.
+ */
+SEC("fentry/bpf_scx_reg") __weak
+int scx_lib_init_probe(void *ctx)
+{
+ if (bpf_core_field_exists(((struct task_struct *)0)->migration_disabled)) {
+ const struct task_struct *p = bpf_get_current_task_btf();
+ unsigned int md = p->migration_disabled;
+
+ if (md > 1)
+ bpf_printk("scx_lib_init_probe: unexpected migration_disabled=%u "
+ "upstream of BPF prolog; probe result unreliable",
+ md);
+
+ __scx_prolog_disables_migration = md > 0;
+ }
+ return 0;
+}
+
/*
* Return true if task @p cannot migrate to a different CPU, false
* otherwise.
+ *
+ * IMPORTANT: designed for NON-SLEEPABLE BPF contexts only. Sleepable
+ * contexts (BPF_STRUCT_OPS_SLEEPABLE, SEC("syscall"),
+ * SEC("fentry.s/...")) enter via __bpf_prog_enter_sleepable() or
+ * __bpf_prog_enter_sleepable_recur(), both of which unconditionally
+ * call migrate_disable(); this helper can yield a false negative for
+ * p == current there, which can crash the scheduler.
*/
static inline bool is_migration_disabled(const struct task_struct *p)
{
/*
- * Testing p->migration_disabled in a BPF code is tricky because the
- * migration is _always_ disabled while running the BPF code.
- * The prolog (__bpf_prog_enter) and epilog (__bpf_prog_exit) for BPF
- * code execution disable and re-enable the migration of the current
- * task, respectively. So, the _current_ task of the sched_ext ops is
- * always migration-disabled. Moreover, p->migration_disabled could be
- * two or greater when a sched_ext ops BPF code (e.g., ops.tick) is
- * executed in the middle of the other BPF code execution.
+ * Testing p->migration_disabled in BPF is tricky because the BPF prolog
+ * (__bpf_prog_enter) may call migrate_disable() for the current task,
+ * making migration_disabled == 1 even for tasks that are not truly
+ * migration-disabled.
+ *
+ * Since commit 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for
+ * trampoline.c"), the BPF prolog calls migrate_disable() only when
+ * CONFIG_PREEMPT_RCU is enabled. Two fast paths cover the common cases:
+ *
+ * 1) CONFIG_PREEMPT_RCU: prolog always calls migrate_disable(), so
+ * migration_disabled == 1 for the current task is ambiguous.
+ * Disambiguate by checking p == current.
+ *
+ * 2) v6.18+ without CONFIG_PREEMPT_RCU: prolog never calls
+ * migrate_disable(), so migration_disabled == 1 is unambiguously
+ * a real migrate_disable() call.
*
- * Therefore, we should decide that the _current_ task is
- * migration-disabled only when its migration_disabled count is greater
- * than one. In other words, when p->migration_disabled == 1, there is
- * an ambiguity, so we should check if @p is the current task or not.
+ * A slow path handles pre-v6.18 kernels without CONFIG_PREEMPT_RCU,
+ * where the prolog historically called migrate_disable() unconditionally
+ * but a cherry-picked downstream kernel may not. The runtime-probed flag
+ * __scx_prolog_disables_migration (set by scx_lib_init_probe) distinguishes
+ * the two cases without relying on the kernel version alone.
*/
if (bpf_core_field_exists(p->migration_disabled)) {
- if (p->migration_disabled == 1)
- return bpf_get_current_task_btf() != p;
- else
- return p->migration_disabled;
+ if (p->migration_disabled == 1) {
+ /* Fast path: prolog always disables migration */
+ if (CONFIG_PREEMPT_RCU)
+ return bpf_get_current_task_btf() != p;
+ /* Fast path: prolog never disables migration */
+ if (LINUX_KERNEL_VERSION >= KERNEL_VERSION(6, 18, 0))
+ return true;
+ /* Slow path: pre-v6.18, !PREEMPT_RCU - use runtime flag */
+ return __scx_prolog_disables_migration ?
+ bpf_get_current_task_btf() != p : true;
+ }
+ return p->migration_disabled;
}
return false;
}
@@ -1021,7 +1094,20 @@ static inline u64 scx_clock_task(u32 cpu)
{
struct rq___local *rq = get_current_rq(cpu);
- /* Equivalent to the kernel's rq_clock_task(). */
+ /*
+ * Equivalent to the kernel's rq_clock_task(): wall-clock time minus
+ * cumulative IRQ time (CONFIG_IRQ_TIME_ACCOUNTING) and hypervisor
+ * steal time (CONFIG_PARAVIRT_TIME_ACCOUNTING). Without those configs,
+ * it equals rq->clock.
+ *
+ * Conceptually this clock advances during idle (the idle task counts
+ * as a running task), but rq->clock_task is only updated on scheduling
+ * events. With NO_HZ_IDLE (the default), the periodic tick is stopped
+ * on idle CPUs, so rq->clock_task is not refreshed while a CPU is
+ * idle. Reading this clock for a remote idle CPU from a BPF timer
+ * callback returns the value from when the CPU last went idle, making
+ * the delta over an idle interval effectively zero.
+ */
return rq ? rq->clock_task : 0;
}
@@ -1032,9 +1118,23 @@ static inline u64 scx_clock_pelt(u32 cpu)
/*
* Equivalent to the kernel's rq_clock_pelt(): subtracts
* lost_idle_time from clock_pelt to absorb the jump that occurs
- * when clock_pelt resyncs with clock_task at idle exit. The result
- * is a continuous, capacity-invariant clock safe for both task
- * execution time stamping and cross-idle measurements.
+ * when clock_pelt resyncs with clock_task at idle exit. The intent
+ * is a continuous, capacity- and frequency-invariant clock that is
+ * frozen during idle, IRQ, and hypervisor steal.
+ *
+ * However, like scx_clock_task(), this clock has a stale-read issue
+ * for remote idle CPUs with NO_HZ_IDLE (the default). clock_pelt
+ * itself advances at wall-clock rate (hardware-clock based), but
+ * lost_idle_time is only updated via update_rq_clock_pelt(), which
+ * requires update_rq_clock() to be called. With NO_HZ_IDLE, the
+ * periodic tick is stopped on idle CPUs, so lost_idle_time is not
+ * refreshed during idle. Reading this clock for a remote idle CPU
+ * from a BPF timer callback therefore returns a value that drifts
+ * at wall-clock rate -- the same stale behaviour as scx_clock_task().
+ *
+ * Without NO_HZ_IDLE, periodic ticks keep lost_idle_time nearly in
+ * sync (stale by at most one tick period, ~1 ms), so the result is
+ * accurate.
*/
return rq ? (rq->clock_pelt - rq->lost_idle_time) : 0;
}
diff --git a/tools/sched_ext/include/scx/compat.bpf.h b/tools/sched_ext/include/scx/compat.bpf.h
index 3ab642f92c8a..6944221f96cc 100644
--- a/tools/sched_ext/include/scx/compat.bpf.h
+++ b/tools/sched_ext/include/scx/compat.bpf.h
@@ -92,15 +92,20 @@ int bpf_cpumask_populate(struct bpf_cpumask *dst, void *src, size_t src__sz) __k
/*
* v6.19: Introduce lockless peek API for user DSQs.
+ * v7.1: Fix scx_bpf_dsq_peek() spuriously returning NULL on non-empty
+ * FIFO DSQs (2f2ea7709266).
*
- * Preserve the following macro until v6.21.
+ * The kfunc exists from v6.19 but can return NULL for a non-empty FIFO DSQ
+ * before the v7.1 fix. Require kernel version >= 7.1.0 before calling it;
+ * otherwise fall through to the bpf_iter_scx_dsq fallback below.
*/
static inline struct task_struct *__COMPAT_scx_bpf_dsq_peek(u64 dsq_id)
{
struct task_struct *p = NULL;
struct bpf_iter_scx_dsq it;
- if (bpf_ksym_exists(scx_bpf_dsq_peek))
+ if (bpf_ksym_exists(scx_bpf_dsq_peek) &&
+ LINUX_KERNEL_VERSION >= KERNEL_VERSION(7, 1, 0))
return scx_bpf_dsq_peek(dsq_id);
if (!bpf_iter_scx_dsq_new(&it, dsq_id, 0))
p = bpf_iter_scx_dsq_next(&it);
@@ -238,6 +243,26 @@ static inline bool __COMPAT_is_enq_cpu_selected(u64 enq_flags)
scx_bpf_pick_any_cpu_node(cpus_allowed, node, flags) : \
scx_bpf_pick_any_cpu(cpus_allowed, flags))
+/*
+ * v6.18: Add a helper to retrieve the current task running on a CPU.
+ *
+ * The kernel tree dropped this helper and scx_bpf_cpu_rq(), but schedulers in
+ * this tree still support pre-v6.18 kernels where scx_bpf_cpu_curr() doesn't
+ * resolve and the scx_bpf_cpu_rq() fallback still exists. Keep it until
+ * pre-v6.18 kernels fall out of the support window.
+ */
+static inline struct task_struct *__COMPAT_scx_bpf_cpu_curr(int cpu)
+{
+ struct rq *rq;
+
+ if (bpf_ksym_exists(scx_bpf_cpu_curr))
+ return scx_bpf_cpu_curr(cpu);
+
+ rq = scx_bpf_cpu_rq(cpu);
+
+ return rq ? rq->curr : NULL;
+}
+
/*
* v6.19: To work around BPF maximum parameter limit, the following kfuncs are
* replaced with variants that pack scalar arguments in a struct. Wrappers are
@@ -378,6 +403,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
@@ -395,21 +431,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 -EOPNOTSUPP;
}
static inline void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags)
diff --git a/tools/sched_ext/include/scx/compat.h b/tools/sched_ext/include/scx/compat.h
index d2e4384df5af..7c12df45fdba 100644
--- a/tools/sched_ext/include/scx/compat.h
+++ b/tools/sched_ext/include/scx/compat.h
@@ -10,9 +10,14 @@
#include <bpf/btf.h>
#include <bpf/libbpf.h>
#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
+#include "enums_abi.autogen.h"
+
struct btf *__COMPAT_vmlinux_btf __attribute__((weak));
static inline void __COMPAT_load_vmlinux_btf(void)
@@ -23,6 +28,85 @@ static inline void __COMPAT_load_vmlinux_btf(void)
}
}
+/*
+ * Recover the true value of a 64-bit enum enumerator whose kernel BTF entry
+ * was truncated to its low 32 bits.
+ *
+ * Kernels whose BTF was generated without BTF_KIND_ENUM64 support encode
+ * 64-bit enums as 8-byte BTF_KIND_ENUM entries whose enumerator values only
+ * carry the low 32 bits. This happens with pahole < 1.24, which predates
+ * ENUM64, and with pahole passing --skip_encoding_btf_enum64 (e.g. Google's
+ * Container-Optimized OS / GKE kernels deliberately pass it for backward
+ * compatibility with older BTF consumers). The high bits
+ * can't be recovered from kernel BTF, so substitute the value from the
+ * vmlinux.h this tree was built against, cross-checked against the low 32
+ * bits the kernel did provide.
+ *
+ * Note that this is a best-effort recovery, not a ground truth. The
+ * substitution assumes the running kernel agrees with this tree's vmlinux.h
+ * on the high 32 bits, but only the low 32 bits can actually be verified.
+ * The cross-check is vacuous for enumerators whose value has no low bits
+ * set (e.g. SCX_DSQ_FLAG_BUILTIN, __SCX_ENQ_INTERNAL_MASK,
+ * SCX_ENQ_CLEAR_OPSS, SCX_ECODE_*): their lo32 is 0 and matches anything,
+ * so those substitutions rest entirely on the high bits never moving. An
+ * enumerator missing from the table (a kernel newer than this tree's
+ * vmlinux.h, or a stale autogen table) can't be recovered at all. If a
+ * substitution is ever wrong, the scheduler operates on bogus values (e.g.
+ * dispatching to nonexistent DSQ ids or silently dropping flags) and can
+ * wildly malfunction, which is why the mismatch and table-miss paths refuse
+ * instead of guessing.
+ */
+static inline bool __COMPAT_recover_truncated_enum64(const char *type,
+ const char *name,
+ u32 lo32, u64 *v)
+{
+ static bool warned;
+ size_t i;
+
+ for (i = 0; i < sizeof(__scx_enum_abi_vals) / sizeof(__scx_enum_abi_vals[0]); i++) {
+ const struct __scx_enum_abi_val *e = &__scx_enum_abi_vals[i];
+
+ if (strcmp(e->type, type) || strcmp(e->name, name))
+ continue;
+
+ if (e->val <= (u64)UINT32_MAX) {
+ *v = lo32;
+ return true;
+ }
+
+ if ((u32)e->val != lo32) {
+ fprintf(stderr, "ERROR: kernel BTF value of %s::%s (0x%x) doesn't match the low 32 bits of the vmlinux.h value (0x%llx); refusing to substitute\n",
+ type, name, lo32, (unsigned long long)e->val);
+ return false;
+ }
+
+ if (!warned) {
+ fprintf(stderr,
+ "WARNING: kernel BTF lacks BTF_KIND_ENUM64 encoding (generated by\n"
+ "WARNING: pahole < 1.24 or with --skip_encoding_btf_enum64), so 64-bit\n"
+ "WARNING: scx enum values are truncated to their low 32 bits in kernel\n"
+ "WARNING: BTF. Substituting the full 64-bit values from the vmlinux.h\n"
+ "WARNING: this binary was built against, cross-checked against the low\n"
+ "WARNING: 32 bits the kernel does provide. The high 32 bits cannot be\n"
+ "WARNING: verified: if the running kernel's actual values differ from\n"
+ "WARNING: the build-time vmlinux.h (e.g. an enum that moved in a newer\n"
+ "WARNING: kernel), the scheduler will operate on bogus values, such as\n"
+ "WARNING: dispatching to nonexistent DSQ ids, and can wildly malfunction.\n");
+ warned = true;
+ }
+ *v = e->val;
+ return true;
+ }
+
+ /*
+ * Unknown enumerator (likely a stale autogen table). Fail
+ * pessimistically to avoid returning an invalid value.
+ */
+ fprintf(stderr, "ERROR: kernel BTF truncates 64-bit enum %s::%s to 0x%x; 64-bit variant not found in vmlinux.h\n",
+ type, name, lo32);
+ return false;
+}
+
static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v)
{
const struct btf_type *t;
@@ -46,6 +130,19 @@ static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v
n = btf__name_by_offset(__COMPAT_vmlinux_btf, e[i].name_off);
SCX_BUG_ON(!n, "btf__name_by_offset()");
if (!strcmp(n, name)) {
+ /*
+ * Try to recover a 64-bit enum from an 8-byte
+ * BTF_KIND_ENUM that was encoded without ENUM64
+ * support (old pahole or
+ * --skip_encoding_btf_enum64). Only scx_*
+ * types are covered by the substitution table;
+ * non-scx types fall through to the raw value
+ * so this generic utility keeps working for
+ * them.
+ */
+ if (t->size == 8 && !strncmp(type, "scx_", 4))
+ return __COMPAT_recover_truncated_enum64(type, name,
+ (u32)e[i].val, v);
*v = e[i].val;
return true;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 2/2] sched_ext: Sync common and compat headers from the scx repo
[not found] ` <20260818210732.AB27B1F000E9@smtp.kernel.org>
@ 2026-08-18 21:33 ` Tejun Heo
0 siblings, 0 replies; 4+ messages in thread
From: Tejun Heo @ 2026-08-18 21:33 UTC (permalink / raw)
To: Changwoo Min
Cc: sashiko-bot, David Vernet, Andrea Righi, Gavin Guo,
Emil Tsalapatis, sched-ext, linux-kernel
Hello,
On Tue, Aug 18, 2026 at 09:07:32PM +0000, sashiko-bot@kernel.org wrote:
> > +/*
> > + * True if the non-sleepable BPF trampoline prolog (__bpf_prog_enter) calls
> > + * migrate_disable() for the current task. Recorded once by
> > + * scx_lib_init_probe, an fentry program on bpf_scx_reg() that fires during
> > + * the natural scheduler-attach call chain (auto-attached by scx_ops_attach!).
> > + *
> > + * Defaults to true (conservative). Over-reporting in is_migration_disabled()
> > + * causes local-only dispatch, which is safe. Under-reporting can crash the
> > + * scheduler, so we err high if the probe somehow fails to run.
> > + */
> > +bool __scx_prolog_disables_migration __weak = true;
>
> [Severity: High]
> Does this default value actually under-report if the probe fails to run?
>
> The documentation indicates that the fallback should err high (over-report)
> to avoid crashing the scheduler. However, looking at the slow path in
> is_migration_disabled() below, when __scx_prolog_disables_migration is true,
> the macro evaluates bpf_get_current_task_btf() != p. This causes it to
> return false for the current task, which means it incorrectly assumes
> migration is not disabled (under-reporting).
>
> Should the default value of __scx_prolog_disables_migration be false
> instead, so that it falls back to returning true?
The comment and the default do disagree. true matches the stock pre-v6.18
prolog behavior, so a failed probe still yields correct results on stock
kernels. However, if the probe fails on a kernel that cherry-picked
8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for trampoline.c"),
is_migration_disabled(current) under-reports, which is the direction the
comment claims to avoid. Erring high would be defaulting to false.
Changwoo, this code is synced from the scx repo. Can you update the scx
side, either flipping the default or fixing the comment? I'll re-sync
afterwards.
> > +static inline struct task_struct *__COMPAT_scx_bpf_cpu_curr(int cpu)
> > +{
> > + struct rq *rq;
> > +
> > + if (bpf_ksym_exists(scx_bpf_cpu_curr))
> > + return scx_bpf_cpu_curr(cpu);
> > +
> > + rq = scx_bpf_cpu_rq(cpu);
>
> [Severity: High]
> Will this unconditionally call scx_bpf_cpu_rq() on newer kernels?
>
> If both scx_bpf_cpu_curr and scx_bpf_cpu_rq have been dropped from the kernel
> tree, on modern kernels bpf_ksym_exists(scx_bpf_cpu_curr) evaluates to false
> and the fallback path is taken.
>
> Because the call to scx_bpf_cpu_rq() is not guarded by its own
> bpf_ksym_exists() check, won't libbpf poison the missing call and cause the
> BPF verifier to reject the program on newer kernels? Should we guard the
> fallback call as well?
scx_bpf_cpu_curr() wasn't dropped. It was added in v6.18 and only
scx_bpf_cpu_rq() is being removed, so there is no kernel where both are
missing. On kernels without scx_bpf_cpu_rq(),
bpf_ksym_exists(scx_bpf_cpu_curr) is constant true, the fallback is dead
code, and the poisoned call to the missing __weak ksym never reaches the
verifier.
Thanks.
--
tejun
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-18 21:33 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-18 20:57 [PATCHSET sched_ext/for-7.3-fixes] sched_ext: Sync tools headers from the scx repo Tejun Heo
2026-08-18 20:57 ` [PATCH 1/2] sched_ext: Sync tools autogen enum " Tejun Heo
2026-08-18 20:57 ` [PATCH 2/2] sched_ext: Sync common and compat " Tejun Heo
[not found] ` <20260818210732.AB27B1F000E9@smtp.kernel.org>
2026-08-18 21:33 ` Tejun Heo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox