summaryrefslogtreecommitdiff
path: root/tools/sched_ext
AgeCommit message (Collapse)Author
5 hoursMerge branch 'for-next' of ↵Mark Brown
https://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext.git
24 hourssched_ext: Add SCX_ECODE_RSN_CGROUP_OFFLINE to user_exit_info.hTao Cui
The kernel-side scx_exit_code enum gained SCX_ECODE_RSN_CGROUP_OFFLINE in ebeca1f930ea ("sched_ext: Introduce cgroup sub-sched support"), which exits a scheduler when its cgroup goes offline, but the tools-side user_exit_info.h was not updated. Userspace cannot decode exit codes carrying this reason. Add the missing enum value. Signed-off-by: Tao Cui <cuitao@kylinos.cn> Signed-off-by: Tejun Heo <tj@kernel.org>
6 dayssched_ext: scx_qmap: Reset a child's cpuperf targets once its PERF revoke ↵Tejun Heo
takes effect A child that ran a cid at a low cpuperf target leaves the target there when PERF is revoked, since the kernel resets targets only at root enable. Reset the target from ops.sub_child_ecaps_updated() once the revoke is in effect. A detach reports nothing, so ops.sub_detach() resets the child's cids, and the pool unless another child holds it. Count the deliveries in the hier stats. v2: Reset from ops.sub_detach() for a detaching child's cids. v3: Skip the pool at detach while another child holds it (Andrea Righi). Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
9 daysmm: replace PF_KCOMPACTD flag with kthread_func() checkKefeng Wang
PF_KCOMPACTD was introduced by commit ce6d9c1c2b5c ("NFS: fix nfs_release_folio() to not deadlock via kcompactd writeback") so nfs_release_folio() could detect kcompactd context and skip writeback. The flag is only consumed by current_is_kcompactd(), whose sole caller is nfs_release_folio(). Replace the flag-based check with kthread_func(current) == kcompactd, freeing the 0x00010000 PF flag bit. Link: https://lore.kernel.org/20260902131653.1338227-5-wangkefeng.wang@huawei.com Signed-off-by: Kefeng Wang <wangkefeng.wang@huawei.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Acked-by: Shakeel Butt <shakeel.butt@linux.dev> Acked-by: Zi Yan <ziy@nvidia.com> Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> Reviewed-by: David Hildenbrand (Arm) <david@kernel.org> Cc: Brendan Jackman <brendan.jackman@linux.dev> Cc: Carlos Maiolino <cem@kernel.org> Cc: Christian Brauner <brauner@kernel.org> Cc: Christoph Hellwig <hch@lst.de> Cc: "Darrick J. Wong" <djwong@kernel.org> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Lorenzo Stoakes <ljs@kernel.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Suren Baghdasaryan <surenb@google.com>
9 daysmm: replace PF_KSWAPD flag with kthread_func() checkKefeng Wang
The preceding commits removed the last consumer that propagated PF_KSWAPD beyond kswapd itself (XFS btree split worker inheritance). The only remaining setter of PF_KSWAPD is kswapd(), and every current_is_kswapd() caller only needs to check whether the current task *is* the kswapd thread, not whether it inherited the flag. Replace the flag-based test with kthread_func(current) == kswapd, freeing the 0x00020000 PF flag bit. Link: https://lore.kernel.org/20260902131653.1338227-4-wangkefeng.wang@huawei.com Signed-off-by: Kefeng Wang <wangkefeng.wang@huawei.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Acked-by: Shakeel Butt <shakeel.butt@linux.dev> Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> Acked-by: Zi Yan <ziy@nvidia.com> Cc: Brendan Jackman <brendan.jackman@linux.dev> Cc: Carlos Maiolino <cem@kernel.org> Cc: Christian Brauner <brauner@kernel.org> Cc: Christoph Hellwig <hch@lst.de> Cc: "Darrick J. Wong" <djwong@kernel.org> Cc: David Hildenbrand <david@kernel.org> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Lorenzo Stoakes <ljs@kernel.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Suren Baghdasaryan <surenb@google.com>
9 dayssched_ext: scx_qmap: Show actual cid use per participantTejun Heo
qmap's hier stats show how much cid-time the partition handed each participant, not how much of it was used. Track the sched running on each cid from ops.sub_cid_sched_updated() and charge the intervals to the participant, then print the use next to the allocation. Signed-off-by: Tejun Heo <tj@kernel.org>
9 dayssched_ext: scx_qmap: Size the cid range buffer for large machinesTejun Heo
The hier rows' cid range buffer holds 128 characters. When no two consecutive cids share an owner, which SMT siblings numbered far apart produce, every cid prints on its own and the list is cut off with "...". Size the buffer for that worst case at the build's cpu cap, off the stack. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-30sched_ext: Introduce scx_bpf_cgroup_nr_cpus()Andrea Righi
Hierarchical BPF schedulers may need to know how many CPUs a cgroup can run on when distributing group-wide weights. For example, fair's group share calculation scales a group's weight by the smaller of its estimated runnable task count and the number of CPUs in its effective cpuset. Without that count, a BPF scheduler can only use the machine-wide CPU count and may give a group confined to a few CPUs excessive shares. A task's effective affinity can include per-task restrictions, so it cannot reliably represent the group's cpuset. Reading the cpuset directly from BPF would require CO-RE access to private cgroup and cpuset structures, including inherited cpuset resolution, making the approach fragile across kernel versions. Add scx_bpf_cgroup_nr_cpus(), which returns the same count fair uses through cpuset_num_cpus(). The count is independent of CPU or CID numbering, so the kfunc is available to both cpu-form and cid-form schedulers from any context. Also add a compat wrapper which falls back to nr_cpu_ids on older kernels. Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-30sched_ext: Sync tools autogen enum headers from the scx repoTejun Heo
Sync the four autogen enum headers with the scx repo at c9b90b3ad3d0 ("scheds/include: Sync with kernel sched_ext/for-7.4 (c6fe97c34a1a)"). The enum definition and ABI tables pick up the enumerators the kernel gained since the last sync. The two lookup tables are taken verbatim from scx's copies so the files stay identical: their task state lookups move from the removed scx_task_state to scx_ent_flags, where 7203d77d6e04 ("sched_ext: Simplify task state handling") put the states, and until now resolved to zero. The only entry the kernel copy gains is SCX_RQ_BAL_KEEP, which scx keeps for older kernels and which zero-fills here. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-30sched_ext: Sync common and compat headers from the scx repoTejun Heo
Sync cid.bpf.h, common.bpf.h, compat.bpf.h and compat.h with the scx repo at c9b90b3ad3d0 ("scheds/include: Sync with kernel sched_ext/for-7.4 (c6fe97c34a1a)"), which accumulated the following since the last sync: - cid.bpf.h grew the cmask helpers the cid-form schedulers use: fill, empty and end tests, unchecked and word-level access, distribute picks over an arena rotor indexed by cid, range set and clear, and three-operand and, or and andnot that report whether the result is empty. Its word loops walk with bpf_arena_for() and its bit helpers take the fetching atomics when the loader's probe finds the JIT lowers them. - The prolog probe's default now makes is_migration_disabled() over-report, which errs toward local-only dispatch instead of crashing, and the cid-form open macros repoint the probe at bpf_scx_reg_cid(), which cid-form schedulers register through. - scx_bpf_kick_cid() and scx_bpf_cidperf_set() changed their return types without being renamed, so compat.bpf.h declares both flavors of each and lets the kernel pick, the way scx_bpf_dsq_insert() is handled. - A macro supplies scx_bpf_cid_topo()'s size argument from the output buffer, TRAILING_OVERLAP() embeds a struct that ends in a flexible array together with its storage, bpf_arena_for() walks a range in a form the verifier converges on, and features.bpf.h carries the kernel feature bits the loader probes before load. const-defs.h holds the per-architecture cacheline size that cid.bpf.h aligns its rotor slots to. scx_qmap already used the cmask helpers, and the three-operand forms of cmask_and() and cmask_andnot() had not reached its copy of cid.bpf.h, so tools/sched_ext no longer built against scx's headers. Its five calls take the three-operand form now, with the copy-then-operate pairs folded into one call each. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-28sched_ext: scx_qmap: Add proxy execution supportAndrea Righi
Add a -X option to opt scx_qmap into queueing mutex-blocked tasks for proxy execution. Without the option, SCX_OPS_ENQ_BLOCKED remains clear and mutex waiters block normally. With -X, blocked donors are passed to qmap_enqueue() with SCX_ENQ_BLOCKED. When scx_qmap receives a blocked donor, select a cid allowed by the task and held by qmap, preferring its current cid. Dispatch the donor to that cid's local DSQ with a fresh slice and SCX_ENQ_PREEMPT, adding SCX_ENQ_IMMED when the cid is time-shared. This places the donor at the head of an eligible DSQ and requests an immediate reschedule, allowing the core proxy-exec path to run the mutex owner using the donor's scheduling context as soon as the donor is selected. Partition ownership can change concurrently with enqueue, so the self-cid intersection can disappear between the initial check and cid selection. Validate the selected cid before indexing per-cid state or constructing a local DSQ id, and fall back to rescue placement if the intersection has disappeared. The blocked policy is intentionally unfair and can strongly prioritize tasks using contended mutexes, but scx_qmap is a demo scheduler and such aggressive behavior makes proxy-exec support easy to observe. Count all blocked dispatch attempts in nr_enq_blocked and report their per-interval delta. Acked-by: John Stultz <jstultz@google.com> Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-28sched_ext: Delegate proxy donor admission to BPF schedulersAndrea Righi
Proxy execution keeps a mutex-blocked donor runnable so that its scheduling context can execute the mutex owner. Define SCX_OPS_ENQ_BLOCKED as the admission contract for retained donors. Schedulers without the flag block EXT donors normally. Schedulers with the flag continue to own blocked donors and receive them through ops.enqueue() with SCX_ENQ_BLOCKED, allowing BPF to choose their DSQ, CPU and ordering. After BPF dispatches a donor, proxy execution may move its context to the rq of the mutex owner. This move is distinct from BPF placement: proxy_set_task_cpu() changes task_cpu() while preserving the donor's pre-proxy CPU in wake_cpu as the starting point for normal wakeup placement. A proxy-migrated donor returns through the full wakeup activation path when the mutex is released. Do not carry a retained proxy session across BPF scheduler ownership changes. Before root activation, parent-to-child takeover, rehome or punt, fully deactivate a retained donor unconditionally. The incoming scheduler then starts with clean task state and applies its admission policy the next time the task blocks. A retained donor can wake through ttwu_runnable() without another enqueue_task_scx() call. Use WF_TTWU_RQ to identify this path, where the wakeup skipped activation and enqueueing. wakeup_preempt_scx() requests dispatch reconsideration after is_blocked is cleared. A full wakeup activation has already enqueued the task and does not need another reschedule. Blocked-donor enqueueing takes precedence over the exiting and migration-disabled local-DSQ fallbacks, so that an opted-in scheduler sees every eligible donor request. Co-developed-by: John Stultz <jstultz@google.com> Signed-off-by: John Stultz <jstultz@google.com> Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-28sched_ext: Handle proxy-exec races in remote DSQ transfersAndrea Righi
Without proxy execution, the DSQ lock and holding_cpu handshake ensure that a task cannot be dequeued or start running during an rq-lock handoff without clearing holding_cpu. Proxy execution is an exception: a task can start physically executing as a lock owner while its scheduling context remains on a DSQ; its on-CPU or migration-disabled state can therefore change without clearing holding_cpu. Recheck these states after acquiring the source rq lock. If the transfer can no longer proceed, park the task on the source rq's reject DSQ and reenqueue it through its owning scheduler. This preserves the BPF scheduler's placement policy and keeps descendant tasks within their sub-scheduler's cap grants. Queue a deferred drain for every rejection. If a drain finds a proxy-rejected task still running or donating, leave it parked and record a retry request on the rq. scx_proxy_reenqueue_retry() consumes the request and schedules another drain when proxy state changes. Without this change and proxy execution enabled, stress-ng --pipeherd can trigger this race and migrate an active execution context, leading to sleeping-while-atomic warnings and subsequent lockdep corruption. This is a preparatory change to support proxy execution with sched_ext. Suggested-by: Tejun Heo <tj@kernel.org> Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-28sched_ext: Fix ops.running/stopping() pairing for proxy-exec donorsAndrea Righi
With proxy execution, pick_next_task() can select a blocked task as the scheduling context before find_proxy_task() resolves the execution context. From the BPF scheduler perspective, that donor is running while its scheduling context drives the lock owner; ops.tick() and other accounting must therefore remain enclosed by a matching ops.running()/ops.stopping() session. In this scenario, the session boundaries do not always match physical task switches. Keep the "running" session open when the same donor continues on the same CPU. When proxy execution migrates a donor's scheduling context to another CPU, end its running session on the source CPU and start a new session on the destination CPU only after proxy resolution succeeds. This prevents a failed resolution from exposing a provisional ops.running() event. Track these sessions with a new SCX_TASK_RUN_TRACKED flag. The explicit running-state tracking is also required by later donor-based accounting: it prevents an EXT owner executing for a non-EXT donor from being treated as the active EXT scheduling context when it is dequeued. This is a preparatory change for enabling proxy execution together with sched_ext. Acked-by: John Stultz <jstultz@google.com> Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-28tools/sched_ext: Add SCX_OPS_OPEN_OPTS for schedulers using open optsFuyu Zhao
Add SCX_OPS_OPEN_OPTS() and SCX_OPS_CID_OPEN_OPTS() to allow sched_ext schedulers to pass bpf_object_open_opts when opening BPF skeletons. This allows sched_ext schedulers using open opts to retain the existing compatibility handling instead of bypassing it. Signed-off-by: Fuyu Zhao <zhaofuyu@vivo.com> Reviewed-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-26sched_ext: Merge branch 'for-7.3-fixes' into for-7.4Tejun Heo
Pull to receive 94480606a677 ("sched_ext: Add a size argument to scx_bpf_cid_topo() so struct scx_cid_topo can grow") for the upcoming cluster level. Conflicts: kept both declarations where 5cadf7545cc6 ("sched_ext: Add a CID NUMA node lookup kfunc") added one next to the changed scx_bpf_cid_topo() line, and pointed 29871903f3a3 ("sched_ext: Don't deliver duplicate ops.cgroup_set_idle() for same value") at the renamed tg->scx.sched_idle. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-26sched_ext: Add a size argument to scx_bpf_cid_topo() so struct scx_cid_topo ↵Tejun Heo
can grow scx_bpf_cid_topo() copies struct scx_cid_topo into a buffer the BPF program sized from its own vmlinux.h while the verifier sizes the write from the running kernel's BTF. The struct may grow and each growth then breaks every scheduler built against the older layout, rejected at load or written past its buffer. This is the usual hole for a struct handed to BPF, closed elsewhere with a size argument, and it was missed here. Take the buffer size, copy the smaller of it and the kernel's struct and set the rest to -1. Accesses to the copy are CO-RE relocated, so the struct can grow by appending fields, which its comment now states. The kfunc changes in place: the cid interface is still being finalized and no released scheduler uses the current form. Fixes: e9b55af47edf ("sched_ext: Add topological CPU IDs (cids)") Cc: stable@vger.kernel.org # v7.2+ Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-09-26sched_ext: Add a CID NUMA node lookup kfuncAndrea Righi
CID-form schedulers may need the physical NUMA node for a CID, for example to allocate arena memory on that node. The CID topology's node index is not a physical NUMA node ID, and scx_bpf_cpu_node() is restricted to CPU-form schedulers. Add scx_bpf_cid_node() to return the physical NUMA node for a CID and provide a compatibility helper that returns NUMA_NO_NODE on kernels without this kfunc. Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-19sched_ext: Pass the initial cmask to cid-form ops.enable()Tejun Heo
The cid-form API has an obvious hole. A task's cid mask is only visible through ops.set_cmask(), which fires on affinity changes and class switches but not when a task enters a scheduler through fork, sub-sched enable or re-home, and there is no p->cpus_ptr equivalent to fall back on. Schedulers work around it by seeding the mask in ops.init_task() from p->cpus_ptr cid by cid, which is subtly wrong: on sub-sched enable and re-home, an affinity change between init_task() and enable() is delivered to the sched the task is still on, and nothing corrects the new sched's copy afterwards. Fix it by adding struct scx_enable_args to cid-form ops.enable() carrying the task's cmask, built in the per-cpu scratch under the rq lock as the task enters the scheduler, and calling set_cmask() with the same mask right after enable(), ahead of set_weight(). A scheduler can then track affinity in set_cmask() alone, and scx_qmap drops its init_task() seed. set_cmask() no longer fires for a cid-form task before it is enabled, and the class-switch republish in switching_to_scx() is limited to the cpu form. This changes the cid-form ops.enable() signature, which is fine as the cid-form API is still considered unreleased. An args struct rather than a bare cmask argument leaves room for more initial state without another signature change, and the cmask travels as a plain arena address because BTF can't type arena struct members yet. v2: The cmask travels as a u64 arena address, cmask_arena_addr, instead of a kernel-typed pointer, with the typing limitation and the planned typed alias documented (Sashiko review). v3: The initial set_cmask() is delivered before set_weight() so that the mask is in place when weight-dependent state is derived (Andrea Righi). Selftest added. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-09-18sched_ext: Add lazy preemption supportAndrea Righi
The fair scheduling class can request lazy rescheduling, deferring an in-kernel scheduling boundary until returning to user space or until the next scheduler tick. sched_ext only exposes immediate preemption, preventing BPF schedulers from making the same trade-off. Add SCX_ENQ_PREEMPT_LAZY and SCX_KICK_PREEMPT_LAZY. Both expire the current sched_ext task slice but request lazy rescheduling. Immediate preemption, WAIT and plain kicks take precedence when requests are combined, while a lazy enqueue to a non-local DSQ retains the head-insertion semantics of SCX_ENQ_PREEMPT. Add SCX_OPS_LAZY_RESCHED as the default slice-expiry policy for newly enabled tasks and initialize it before ops.enable(). Add scx_bpf_task_set_lazy_resched() so the owning scheduler can override the persistent policy per task from any callback while preserving sub-scheduler task ownership boundaries. Bypass continues to force immediate rescheduling. For an infinite-slice task whose tick is stopped, set the lazy reschedule request before restoring the scheduler tick dependency. This lets the dependency update's IPI serve the request on return to user space and guarantees that the next tick promotes it otherwise. Use pinned rq locking for remote kick delivery so RQCF_UPDATED reflects clock updates since lock acquisition. Accumulate kick requests independently and resolve precedence while holding the target rq lock. Reject invalid SCX_KICK_IDLE combinations and unknown kick flags, which were previously silently ignored. Tested-by: Tao Cui <cuitao@kylinos.cn> Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-15sched_ext: Maintain an online cid mask in the scheduler arenaTejun Heo
Schedulers on the default cid mapping treat [0, nr_online_cids) as the online set and restart on hotplug. Schedulers that install their own mapping with scx_bpf_cid_override() have no way to learn which cids are online: the count no longer identifies members and the CPU-form cpumask is unusable from cid programs. This is an obvious hole in the cid API. Add scx_bpf_online_cmask(), a kernel-maintained cmask in the scheduler's arena, allocated alongside the per-CPU scratch masks and populated after the cid mapping is finalized and before ops.init(), for child schedulers too. The pointer stays valid through ops.exit() with no reference to take. It is the arena offset as a void pointer, the same form struct_ops arena arguments arrive in. The verifier types the void return as a scalar for the program's arena cast. The mask follows the SCX hotplug notifications: seeded from cpu_active_mask and updated before ops.cid_online/offline() runs, so it lags cpu_online_mask only inside a hotplug transition. Updates walk the scheduler list under the lock that also serializes unlinking. Reads are live, not atomic snapshots. Root initialization excludes hotplug. v2: Reworded the getter kerneldoc (Andrea Righi). Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-09-15sched_ext: scx_qmap: Restore unused idle claims from ops.dispatch()Tejun Heo
scx_qmap tracks idle cids itself. pick_direct_dispatch_cid() claims a cid by clearing its bit and the task is inserted into that cid's local DSQ, which kicks the CPU. When the task does not arrive, for example because the insert fell back to the global DSQ after an affinity change, the CPU wakes, finds nothing and picks idle again. That is not an idle transition, so ops.update_idle() is not called and the cid stays marked busy until an unrelated task runs on it. Restore the claim from ops.dispatch(). The kick guarantees a dispatch on the kicked CPU, and when it finds nothing to run with a NULL @prev, the CPU is going back to idle. Document the pattern in ops.update_idle(), which reports only actual transitions. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com> Cc: Andrea Righi <arighi@nvidia.com>
2026-09-08tools/sched_ext: Fix repeated word 'be' in commentHemanth Selam
Drop the second 'be', reported by checkpatch.pl as a possible repeated word. Only touches a comment, no code changes. Assisted-by: Cursor:claude-opus-5 Signed-off-by: Hemanth Selam <hemanth.selam@gmail.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-08tools/sched_ext: Drop the no-op -rdynamic from CFLAGSCheng-Yang Chou
Because CFLAGS only applies to compilation ($(CC) -c), the link-time flag -rdynamic is inert during the build. Schedulers do not rely on exported dynamic symbols, and binaries remain byte-identical without it. Under -Werror, Clang treats this unused argument as a build error: $ make CC=clang-21 clang-21: error: argument unused during compilation: '-rdynamic' [-Werror,-Wunused-command-line-argument] Drop -rdynamic from CFLAGS to ensure clean builds across Clang versions. Signed-off-by: Cheng-Yang Chou <yphbchou0911@gmail.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-09-06sched_ext: scx_qmap: Fix pending partition work handoffTejun Heo
qmap can leave partition work pending with no runner. The effective-cap callback publishes its request after failing to acquire part_busy, while redistribute() checks for pending work before releasing it. Either ordering can miss a request arriving as the current runner finishes, delaying the update until the round-robin timer runs. Publish requests before trying to become the runner and release part_busy before checking for more work. Have all holders drain pending requests after releasing it, including the stats flush. Distinguish mask refreshes from repartitions so an effective-cap update only rebuilds the partition when a repartition was also requested. Fixes: e9151ed5c944 ("tools/sched_ext: scx_qmap - Expand hierarchical sub-scheduling") Reported-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-09-05sched_ext: scx_qmap: Place only on cids whose caps are in effectTejun Heo
qmap decides placements from self_cids, which redistribute() derives from the caps view at ops.sub_caps_updated() time. That view runs ahead of the cpus: a granted cid can be in self_cids before its cpu has reported the caps in effect through ops.sub_ecaps_updated(). ops.update_idle() only comes once BASE is in effect, so the idle-gated placements reach such a cid only through an idle bit left over from an earlier hold. The highpri scan has no gate at all: parent cpu Y, qmap cpu X grants ENQ on X to qmap sub_caps_updated() adds X to self_cids highpri scan moves a task to X with PREEMPT caps not in effect, move denied, task bounced with REENQ_CAP reject drain, enqueue the scan moves it to X again denied again dispatch syncs ecaps, sub_ecaps_updated(X) Every highpri move to X in that window is denied and bounced. The two callbacks are meant to split the roles: ops.sub_caps_updated() tracks what the node holds and drives what it delegates to its children, while ops.sub_ecaps_updated() says whether a task can run on a cpu now. qmap used the first for both. Track the caps in effect from ops.sub_ecaps_updated() as avail_cids and place only on self_cids & avail_cids, so that self_cids stays the delegation split and avail_cids gates the placement. The stranded tests keep self_cids, as they ask whether the split gives the task anywhere at all. A highpri task whose self_cids lack caps in effect waits for them instead of being moved and bounced. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-09-05sched_ext: scx_qmap: Do not add IMMED to rescue insertsTejun Heo
qmap's stranded fallback forces a task that can run on none of its self cids onto its first allowed cid with SCX_ENQ_RESCUE, and adds SCX_ENQ_IMMED when that cid is a time-share it holds. On such a cid the insert stops being a rescue request: 1. A task is enqueued while none of its allowed cids is in self_cids. At attach self_cids is still empty. 2. qmap inserts it into cid 0's local DSQ with SCX_ENQ_RESCUE | SCX_ENQ_IMMED. 3. The kernel finds ENQ_IMMED held on cid 0, admits the insert and skips the rescue diversion. 4. cid 0's cpu is busy, so the IMMED task is bounced back to qmap with SCX_ENQ_REENQ. 5. qmap's enqueue sees the same inputs and repeats step 2. Nothing runs in between. 6. The reenqueue limit ejects qmap with SCX_EXIT_ERROR_REENQ. The caps granted during the parent's ops.sub_attach() are delivered after the sub already holds its tasks, while the per-cid effective caps that mark the time-shares are delivered from the first dispatch after bypass lifts, so every attach that receives a time-share on a task's first allowed cid starts the loop. Drop IMMED from the rescue inserts so that step 3 diverts to the rescue path. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-09-01sched_ext/scx_flatcg: Make cgv_node_less() wraparound-safeTao Cui
cgv_node_less() compares cvtimes with a plain <, which misorders once cvtime wraps: the wrapped node lands at the front of the tree while the unwrapped ones get stuck behind it. Each CPU picking a cgroup charges it a full slice, making wrap occur earlier than a naive estimate might suggest. Use time_before() instead. cgrp_cap_budget() bounds the lag behind cvtime_now, while the lead is bounded by the slice charge plus pending cvtime_delta on re-insertion, so the cyclic ordering assumptions of time_before() hold. Fixes: a4103eacc2ab ("sched_ext: Add a cgroup scheduler which uses flattened hierarchy") Reported-by: Sashiko <sashiko-bot@kernel.org> Link: https://lore.kernel.org/r/3f1ce004-e259-4e72-a5f7-14a5050053bd@linux.dev Signed-off-by: Tao Cui <cuitao@kylinos.cn> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31sched_ext: Check bpf_timer_start return values in scx_qmapWanwu Li
monitor_timerfn(), lowpri_timerfn() and round_robin_timerfn() ignore bpf_timer_start()'s return value: a failed re-arm silently stops the periodic heartbeat, starving every task parked in LOWPRI_DSQ (lowpri) or freezing cid rotation (round-robin). Check the returns and raise scx_bpf_error(), matching the init paths. Signed-off-by: Wanwu Li <liwanwu@kylinos.cn> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31sched_ext: Fix vtime delta loss in scx_flatcg cgroup migrationWanwu Li
fcg_cgroup_move() lost the signed vtime offset across cgroup migration in the mechanical conversion to time helpers: time_delta() clamps negative deltas to 0, so a queued task (whose dsq_vtime is normally behind the source frontier) loses its accumulated vtime credit and lands exactly at the destination frontier instead of keeping its relative position. Restore the wrapping signed subtraction. Fixes: 62addc6dbf36 ("sched_ext: Use time helpers in BPF schedulers") Signed-off-by: Wanwu Li <liwanwu@kylinos.cn> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-31sched_ext: Fix timer pinning and return value in scx_centralWanwu Li
central_timerfn() re-arms the timer with a hardcoded BPF_F_TIMER_CPU_PIN flag and ignores the return value, defeating start_central_timer()'s -EINVAL fallback for kernels without the flag (<6.7): on such kernels the first tick kills the timer permanently with no diagnostic. Honor timer_pinned and check the return like the initial arm does. Fixes: 22a920209ab6 ("sched_ext: Implement tickless support") Signed-off-by: Wanwu Li <liwanwu@kylinos.cn> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-21sched_ext: Sync common and compat headers from the scx repoTejun 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>
2026-08-21sched_ext: Sync tools autogen enum headers from the scx repoTejun 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>
2026-08-17sched_ext: Merge branch 'for-7.3-arena-args' into for-7.3Tejun Heo
Pull to receive the __arena argument conversion: 67f1f4a48c24 ("sched_ext: Pass kernel arena pointers to ops_cid callbacks") a8dc810968af ("sched_ext: Convert sub-cap kfuncs to __arena cmask arguments") a05c5b5cb5cf ("sched_ext: Convert scx_bpf_cid_override() to __arena array arguments") along with the bpf-next branch carrying the __arena argument support they depend on. Conflict in kernel/sched/ext/ext.c between: c384ab8a0b13 ("sched_ext: Move the config-off sub-cap kfunc stubs into sub.c") and: a8dc810968af ("sched_ext: Convert sub-cap kfuncs to __arena cmask arguments") which updated the stubs in their old ext.c location. Resolved by keeping ext.c without the stubs and applying the prototype conversion to the relocated stubs in sub.c. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-15sched_ext: Make core-sched task ordering hierarchy-awareTejun Heo
With sub-schedulers, tasks of different schedulers routinely share rqs and SMT siblings, but scx_prio_less() consults ops.core_sched_before() only when both tasks belong to the same scheduler. Every pair spanning two schedulers falls back to the default ordering, so no scheduler can express ordering across a scheduler boundary, including a root over its sub-schedulers' tasks. Order a pair spanning schedulers by the nearest common ancestor that implements ops.core_sched_before(): both tasks are in its subtree, making this the one op where a scheduler is called on tasks it delegated to its sub-schedulers and may not be scheduling anymore. Same-scheduler pairs keep using the owning scheduler's op so a parent never orders inside a subtree it delegated. The op is skipped when the deciding scheduler is bypassing on either task's CPU. Update scx_qmap to fall back to the kernel's default ordering when handed a delegated task it has no task_ctx for. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-15sched_ext: Fix inverted ops.core_sched_before() invocationTejun Heo
scx_prio_less() implements prio_less() semantics - %true means that @a is the lower priority and should run after @b. ops.core_sched_before() is documented to return %true when @a should run before @b. scx_prio_less() returns the op's value as-is, inverting the documented semantics at runtime. Call the op with the arguments swapped. scx_qmap followed the wiring instead of the documentation and returned %true for the younger task, so the two inversions canceled out and it behaved as intended. Flip its comparison to match. scx_qmap is likely the only current user in or out of the kernel tree. Any scheduler written the same way needs the same flip, while schedulers following the documentation are fixed by this change. Fixes: 7b0888b7cc19 ("sched_ext: Implement core-sched support") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-15sched_ext: Rename balance-era identifiers to dispatch termsTejun Heo
sched_class->balance() is gone from sched_ext and what balance_one() does is run dispatch to produce something pickable. Update the balance-era names to dispatch terms: - balance_one() -> dispatch_one() - SCX_RQ_IN_BALANCE -> SCX_RQ_IN_DISPATCH No BPF scheduler reads the flag. The enum autogen headers gain the new name with the old entry retained like other removed enumerators, zero-filling at load time. No functional changes. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-14sched_ext/scx_flatcg: Fix cvtime true-up on slice expiryTao Cui
fcg_dispatch() true-ups the current cgroup's cvtime when its slice expires or its DSQ runs empty while the slice is still active: __sync_fetch_and_add(&cgc->cvtime_delta, (cpuc->cur_at + cgrp_slice_ns - now) * FCG_HWEIGHT_ONE / (cgc->hweight ?: 1)); The true-up should be actual minus charged: on CNS_EXPIRE, the overrun (now - cur_at - cgrp_slice_ns) should be added; on CNS_EMPTY, the unused portion of the slice should be subtracted. The expression above has the sign inverted, and in the CNS_EXPIRE case now is already past cur_at + cgrp_slice_ns, so the u64 subtraction wraps. The multiplication preserves the two's complement encoding but the unsigned division by hweight destroys it, adding roughly 2^64/hweight per expiry instead of a small correction. Under saturation the hweight budget clamp in cgrp_cap_budget() masks most of the garbage, so the weight distribution barely moves, but the accounting is broken all the same. Compute the delta as a signed value and use fetch_and_add()/fetch_and_sub() so that the dividends stay positive, as BPF division is unsigned. Instrumented the true-up and ran a saturated three-leaf cgroup tree on a 4-CPU VM: without the fix, each expiry added ~5e15 (2^64/hweight territory) to cvtime_delta; with it, the corrections are back to slice scale, with the overrun added and the unused portion subtracted. Fixes: a4103eacc2ab ("sched_ext: Add a cgroup scheduler which uses flattened hierarchy") Suggested-by: Tejun Heo <tj@kernel.org> Signed-off-by: Tao Cui <cuitao@kylinos.cn> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-14sched_ext: Fix scx_bpf_dsq_move_to_local___v2 compat detectionfangqiurong
libbpf strips the last ___flavor suffix when resolving kfunc externs, so the bare ___v2 declaration resolves to scx_bpf_dsq_move_to_local, whose BTF proto lacks @enq_flags. The extern never matches, bpf_ksym_exists() returns false on every kernel that has the ___v2 kfunc, and the macro falls back to ___v1, silently dropping @enq_flags. Add the trailing ___compat suffix used by the other versioned externs in this file (scx_bpf_dsq_insert___v2, scx_bpf_reenqueue_local___v2). Any caller passing non-zero @enq_flags through the compat macro silently loses them. Fixes: 860683763ebf ("sched_ext: Add enq_flags to scx_bpf_dsq_move_to_local()") Cc: stable@vger.kernel.org # v7.1+ Assisted-by: Z.ai:glm-5.2 Signed-off-by: fangqiurong <fangqiurong@kylinos.cn> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-14sched_ext: Fix scx_bpf_dsq_reenq___compat kfunc extern prototypeTejun Heo
scx_bpf_dsq_reenq() is registered with KF_IMPLICIT_ARGS and its kernel BTF prototype omits the trailing bpf_prog_aux argument. The ___compat extern declares the argument explicitly, so libbpf never matches the prototype and the weak extern silently stays unresolved on every kernel. The wrapper always takes the old fallback path, which disables generic reenq users like scx_qmap's lowpri mechanism and fails non-local reenq with "kernel too old" even on kernels that have the kfunc. Drop the explicit aux argument. Also correct the stale v6.20 reference, the kfunc was added in v7.1. Fixes: 9c34c5074d1b ("sched_ext: Introduce scx_bpf_dsq_reenq() for remote local DSQ reenqueue") Cc: stable@vger.kernel.org # v7.1+ Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-14sched_ext/scx_flatcg: expire cached hweights on weight changesTao Cui
fcg_cgroup_set_weight() updates cgc->weight and the parent's child_weight_sum but doesn't bump hweight_gen, so the hweights cached by cgrp_refresh_hweight() stay stale until some task activation bumps the generation. For cgroups whose tasks never go through a 0->n runnable transition (e.g. persistently busy ones), a cpu.weight change never propagates to scheduling at all. Bump hweight_gen on weight changes so the next refresh recomputes with the new weight. Verified on a flatcg VM: a live cpu.weight 100->800 change on a busy cgroup leaves HWT update at 0 and the distribution unchanged; with it, hweight_gen increments and the refresh recomputes. Signed-off-by: Tao Cui <cuitao@kylinos.cn> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-12sched_ext: Convert scx_bpf_cid_override() to __arena array argumentsTejun Heo
scx_bpf_cid_override() predates the cid-form arena transition and takes its arrays as verifier-checked mem+size buffers, forcing scx_qmap to keep the cpu_to_cid and shard_start arrays in writable bss while the rest of its state lives in the arena. Unify on arena arguments before cid-form schedulers start seeing real use. BPF now translates between BPF and kernel arena addresses for __arena arguments. Take the arrays as __arena arguments, with the counts passed in entries. The counts now size the snapshot copies and are bounds-checked before them. scx_qmap moves the arrays into struct qmap_arena. As the arena is mmapped at load, the loader populates them between load and attach instead of before load. The arena argument address translation is currently implemented only on x86-64. Schedulers calling this kfunc load only there for now. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-12sched_ext: Convert sub-cap kfuncs to __arena cmask argumentsTejun Heo
The sub-cap kfuncs take their cmask arguments as __ign pointers. The values cross the kfunc boundary as unchecked scalars and scx_cmask_ref_init() rebases them into the arena by hand. BPF now translates between BPF and kernel arena addresses for __arena arguments. Tag the cmask arguments __arena so the kfuncs receive kernel addresses and scx_cmask_ref_init() loses the hand-rolled conversion. The optional denied_out keeps its NULL not-provided signal via __arena__nullable. The mandatory masks use plain __arena. scx_qmap's call sites drop the (void *)(long) casts since the BPF-side declarations type the cmask arguments __arena and take arena pointers directly. The arena argument address translation is currently implemented only on x86-64. Schedulers calling these kfuncs load only there for now. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-12Merge branch 'for-7.3' into for-7.3-arena-argsTejun Heo
2026-08-12sched_ext: Merge branch 'for-7.2-fixes' into for-7.3Tejun Heo
Pull to receive: c10b216a072f ("sched/core: Handle pick_task() releasing the rq lock") f3629c63a4af ("sched/core: Make core-sched flips wait for in-flight selections") ffaab58d2175 ("sched_ext: Replace SCX_RQ_BAL_KEEP with a dispatch verdict return") 3dd52416e44a ("sched_ext: Fix this_rq() assumptions in dispatch kfuncs") f2da9587118d ("sched_ext: Count rq lock releases in rq->scx.lock_drop_seq") d954004205c1 ("sched_ext: Fix rq->core_pick corruption under core scheduling") for the pending core scheduling follow-ups and to resolve the conflicts with the code reorganization and cap gate work on for-7.3. ffaab58d2175 converts scx_dispatch_sched() to a dispatch verdict return which for-7.3 moved from ext.c into inlines.h. Resolved by applying the conversion to the relocated copy and combining balance_one()'s verdict returns with the scx_task_can_stay_on_cpu() gate from the cap work. ffaab58d2175 and 3dd52416e44a update scx_bpf_sub_dispatch() which for-7.3 moved into sub.c. Resolved by applying the scx_locked_rq() switch and the verdict test to the sub.c copy. f2da9587118d instruments the open-coded lock releases in consume_remote_task() which for-7.3 folded into switch_rq_lock(). Resolved by keeping the accounting in switch_rq_lock() which covers all its callers. d954004205c1 widens the put_prev_task_scx() WARN suppression to all core-sched rqs on the same condition that for-7.3 gated with scx_task_can_stay_on_cpu(). Resolved by combining both. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-11sched_ext: Replace SCX_RQ_BAL_KEEP with a dispatch verdict returnTejun Heo
SCX_RQ_BAL_KEEP tells the pick to keep running the previous task, a leftover from when balancing and picking were separate operations. An rq-level flag only works while dispatches and picks pair up one to one, which core scheduling breaks: selections interleave through dispatch's lock drops and a pick can consume a stale flag, keeping a task that has since been dequeued. Fixing core scheduling support requires the decision to travel with the dispatch that made it. Make scx_dispatch_sched() and balance_one() return an explicit verdict instead and drop the flag's plumbing from the tools autogen enum headers. Also factor the pick-side invocation, its follow-up queueing and the post-dispatch checks out of do_pick_task_scx() into dispatch_pick(). No functional changes intended. v2: Drop the SCX_RQ_BAL_KEEP plumbing from the tools autogen enum headers as well (Andrea). Fixes: 4c95380701f5 ("sched/ext: Fold balance_scx() into pick_task_scx()") Cc: stable@vger.kernel.org # v6.19+ Signed-off-by: Tejun Heo <tj@kernel.org>
2026-08-10Merge branch 'master' of ↵Tejun Heo
git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next into for-7.3-arena-args Pull bpf-next d114bb989367 ("Merge branch 'add-arena-argument-support-to-kfuncs-and-struct_ops'") to make the __arena and __arena__nullable kfunc and struct_ops argument suffixes available. The suffixed arguments will be used to convert sched_ext kfuncs and struct_ops callbacks that currently pass arena pointers as scalars and rebase them by hand.
2026-08-03sched_ext: scx_qmap - Add rescue supportTejun Heo
A sched holds only the cids its parent granted and nothing guarantees that they cover its tasks' affinities. A task that can run on none of them has nowhere to go and qmap stalls out: it force-inserts the task onto its first allowed cid, but the kernel bounces the insert back and the task parks in SHARED_DSQ, which is drained only on self cids it can't run on. Set SCX_ENQ_RESCUE on these inserts so the kernel diverts such tasks to its rescue path instead of bouncing them. The force-insert covers scheds with and without children and fires on re-enqueues, and the SHARED_DSQ scan on every dispatch rescues tasks stranded there - the enqueue-time check misses a task whose cids were lost while it was already queued. The wrong-cid fault injection carries the flag too and doubles as a deterministic rescue-traffic generator. -B and -q set the root-only rescue bandwidth and quantum ops knobs. -B 0 maps to SCX_RESCUE_DISABLE and turns rescue off kernel-side. Rescue inserts are counted and reported in the hier stats line. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-08-03sched_ext: scx_qmap - Idle-check pinned tasks before direct dispatchTejun Heo
pick_direct_dispatch_cid() sent a pinned task straight to its only cpu without an idle check. An insert onto a time-shared cid is forced IMMED, which may not wait on a busy cpu - the kernel bounces the task back to ops.enqueue(), the shortcut re-inserts it, and the loop runs into the reenqueue repeat limit. Run pinned tasks through the same idle test as everyone else and queue them when the cpu is busy. always_enq_immed forces IMMED on every local insert and already skipped the shortcut for the same reason. The check was the last reader of the always_enq_immed rodata mirror, so drop the variable. The -I option still sets SCX_OPS_ALWAYS_ENQ_IMMED. v2: Drop the now-unread always_enq_immed rodata mirror. (sashiko AI) Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-08-03sched_ext: Sync tools autogen enum headersTejun Heo
Mechanical regeneration for the enumerators added by the slice write rework and the rescue machinery. BPF schedulers reference SCX_ENQ_RESCUE, the rest are enum_defs presence markers. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>