summaryrefslogtreecommitdiff
path: root/kernel/sched/ext
AgeCommit message (Collapse)Author
2026-07-13sched_ext: Add per-shard scx_sched storage scaffoldingTejun Heo
Add struct scx_pshard and sch->pshard[] indexed by shard_idx, each entry allocated on its shard's NUMA node from scx_shard_node[si]. The struct starts empty (one dummy field). Follow-up patches will grow it as shard-local state lands. Only cid-type schedulers with an arena pool get pshards. Allocation happens after ops.init_cids() returns so any scx_bpf_cid_override() it issues has finalized scx_nr_cid_shards and scx_shard_node[]. sch->nr_pshards records the array size for the async RCU free path, which may run after a later scheduler's scx_cid_init() has rewritten the global. v3: Build and publish pshard[] fully-formed here rather than a later patch. v2: Free the partially-allocated pshard array on alloc failure. (sashiko AI) Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-13sched_ext: Defer scx_sched kobj sysfs add into the enable workfnsTejun Heo
Split kobject_init_and_add() in scx_alloc_and_add_sched(): only kobject_init() runs there. A new scx_sched_sysfs_add() helper does kobject_add() (and creates sub_kset when the scheduler implements ops.sub_attach), called by both enable workfns once @sch is linked and its sysfs-visible state is initialized. Prep so a future caps attribute can rely on @sch being fully built by the time it's sysfs-visible. Add early enough that a stall later in enable still leaves sysfs inspectable. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-13sched_ext: Add shard boundaries to scx_bpf_cid_override()Tejun Heo
An overridden cid mapping invalidates the auto-generated shard layout, so the override call has to provide both. Extend scx_bpf_cid_override() with a shard_start[] array that lists the first cid of each shard (starting at 0, strictly increasing, last shard implicitly extends to num_possible_cpus()). A scheduler that wants only custom shards with the auto-generated cid mapping can read the current mapping and pass it back unchanged. Overridden shards can span NUMA nodes, so scx_shard_node[] is rebuilt by majority count: each shard is assigned to the node that owns the most cpus in it. v2: Snapshot the caller's cpu_to_cid/shard_start arrays before validating. (sashiko AI) Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-13sched_ext: Add CID shardingTejun Heo
Sub-sched operations need a scalable locking / work domain smaller than the whole cid space. Carve the cid space into topology-respecting shards: each shard is a contiguous cid range that stays within one LLC, and LLCs larger than the per-shard cap (default 24 cids, configurable via ops.cid_shard_size) split into enough shards to fit. A hard cap of SCX_CID_SHARD_MAX_CPUS prevents pathological sizes under custom configurations. No-topo cids pack into their own shards so every cid has a shard assignment. Also build scx_cid_shard_ranges[] for O(1) shard-to-cid-range lookup and scx_shard_node[] so callers can size or place work by NUMA without walking cids. Auto-built shards inherit their LLC's node. No-topo shards carry NUMA_NO_NODE. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-13sched_ext: Add ops.init_cids() to finalize the cid layout before initTejun Heo
A cid-form scheduler that calls scx_bpf_cid_override() to install a custom cid layout can only do so from ops.init(). Enable-path setup that depends on the cid layout thus has to run after ops.init(), and ops.init() itself can't use anything derived from the final layout, which turned out to be too restrictive. Add an ops.init_cids() callback dedicated to finalizing the cid layout. It runs before the rest of the enable-path setup, so the final layout is in effect for everything that follows including ops.init(), which now runs after the arena pool and cmask scratch allocations. scx_bpf_cid_override() is restricted to ops.init_cids() at load time. It sits in a kfunc set gated by SCX_KF_ALLOW_INIT_CIDS, a flag set only on the init_cids op, so the verifier rejects a call from any other context. The runtime root-only check is dropped as ops.init_cids() only runs during root enable. The qmap demo moves its override into a dedicated qmap_init_cids() and, while at it, introduces an enum for the cid override modes instead of hard-coded integers. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-13sched_ext: Make the kick machinery per-schedTejun Heo
The kick machinery kept its targets in rq->scx shared by every sched on the cpu. A preempt kick carried no record of which scheduler requested it. A later patch needs preempt kicks scoped to the requesting scheduler so a sub-scheduler can preempt only tasks in its own subtree. Move the kick masks into the per-sched per-cpu scx_sched_pcpu and have scx_kick_cpu() link the sched onto a per-cpu list (rq->scx.sched_pcpus_to_kick). The cpu's single kick irq_work walks that list and kicks each sched's targets on its behalf, so a kick stays attributed to its scheduler. The SCX_KICK_WAIT sync set (cpus_to_sync, the kick_sync snapshot and the balance-callback trigger) stays in rq->scx: the waiter is the cpu, not the scheduler, and its only writers, the kick irq_work and the wait balance callback, are cpu-local. On disable, free_kick_syncs() flushes each cpu's pending kick irq_work before clearing @ksyncs, so a late kick unlinks its to_kick_node instead of early-returning on a NULL @ksyncs and leaving the node linked at free. v3: Flush the kick irq_work in free_kick_syncs() before clearing @ksyncs. (sashiko AI) v2: Warn once per sched on scx_bpf_kick_cpu() from NMI. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-13sched_ext: Assert per-task ops run on the task's ownerTejun Heo
A per-task op must be dispatched on the scheduler that owns the task. SCX_CALL_OP_TASK() and its _RET twin take @sch explicitly, and a caller that passes the wrong scheduler would silently run the op on it. Add a WARN_ON_ONCE() that @sch matches the task's owner so such a mismatch is caught rather than hidden. Two sites legitimately target a scheduler other than the task's owner: cgroup_move() runs on the root sched, and scx_sub_init_cancel_task() fires exit_task() on a task not yet associated with @sch. Both switch to the inner __SCX_CALL_OP_TASK(), which dispatches on the explicit @sch without the assert. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-10sched_ext: Skip ops.set_weight() for disabled tasksKuba Piecuch
When switching a task's sched_class away from sched_ext, we get the following sequence of events in __sched_setscheduler(): sched_change_begin() switched_from_scx() scx_disable_task(p) ops.disable(p) __setscheduler_params() set_load_weight() reweight_task_scx(p) ops.set_weight(p) p->sched_class = next_class; sched_change_end() ... Notably, ops.set_weight() is called _after_ ops.disable(). This violates the expected semantics of the callbacks, the expectation being that ops.disable() can only be followed by ops.exit_task() or ops.enable(). Skipping the weight adjustment for disabled tasks should be harmless since the weight will be recalculated in scx_enable_task() if the task ever rejoins SCX. Fixes: 637b0682821b ("sched: Fold sched_class::switch{ing,ed}_{to,from}() into the change pattern") Cc: stable@vger.kernel.org # v6.19+ Signed-off-by: Kuba Piecuch <jpiecuch@google.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-07-10sched_ext: Add tracepoint for scheduler exitPat Somaru
sched_ext schedulers have state in BPF programs and kernel. scx_dump provides kernel state and BPF program state on error, but this is static in what it can provide. Add a sched_ext_exit tracepoint in scx_claim_exit() so that BPF programs can dynamically inspect scheduler specific state at the moment of exit. Pass the exiting scx_sched so attached programs can read its state, and, since exits propagate through a hierarchy of sub-schedulers, identify which scheduler each event belongs to. Signed-off-by: Pat Somaru <patso@likewhatevs.io> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-07-09sched_ext: Keep rq tracking accurate in the consume pathAndrea Righi
consume_remote_task() drops this_rq before unlinking a remote task from its DSQ and locking src_rq. When called from ops.dispatch(), scx_locked_rq() keeps pointing to this_rq across the lock dance. The subsequent switch_rq_lock(src_rq, this_rq) cannot update the tracking because its guard does not match, leaving the tracking stale while src_rq is held. Keep this_rq locked until the task has been unlinked and the DSQ lock released, then use switch_rq_lock() to switch directly to src_rq. Use the same helper to restore this_rq after losing the dequeue race. The successful path already switches back through move_remote_task_to_local_dsq(), so scx_locked_rq() now follows the actually held rq throughout the consume path. Suggested-by: Tejun Heo <tj@kernel.org> Link: https://lore.kernel.org/all/455e701bca66bdecde530d225f4dba0a@kernel.org Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-07-09sched_ext: Make scx_bpf_kick_cid() return voidTejun Heo
scx_bpf_kick_cid() returned an error code, but the value conveys nothing actionable and no caller consumes it. The kick is asynchronous, so a successful return only means it was queued. An invalid @cid is already reported through scx_error() by scx_cid_to_cpu(), and a missing scheduler leaves nothing to kick. Make scx_bpf_kick_cid() return void to match scx_bpf_kick_cpu(). The cid-form kfuncs are not in practical use yet, so the ABI change is safe. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-09sched_ext: Reject direct slice and dsq_vtime writes for cid-form schedulersTejun Heo
Direct writes to p->scx.slice and p->scx.dsq_vtime bypass scx_bpf_task_set_slice/dsq_vtime() and the authority checks they carry. Those checks exist for sub-schedulers, which attach only through the cid-form struct_ops, so the direct writes only need to be closed there. Give sched_ext_ops_cid its own verifier ops that reject the two fields. cid-form is a new interface with no legacy users, so there is no compatibility to keep. The cpu-form keeps direct writes, and the deprecation warning they carried is dropped. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-09sched_ext: Rename extra_enq_flags to remote_activate_enq_flagsTejun Heo
scx_rq.extra_enq_flags carries scx-specific enqueue flags across the activate_task() boundary during a cross-rq task move in move_remote_task_to_local_dsq(). Rename it to remote_activate_enq_flags to name that role, and fix the stale comment that referenced the old move_task_to_local_dsq() name. No functional change. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-09sched_ext: Add SCX_CALL_CID_OP_TASK() for cid-form op dispatchTejun Heo
The cid-form ops overlay their cpu-form siblings at the same struct slot. Ops whose signature matches the sibling are invoked through the cpu-form call sites unchanged, but set_cmask() takes an arena cmask address rather than a cpumask, so scx_call_op_set_cpumask() calls ops_cid.set_cmask() directly and hand-rolled the kf_tasks[] and locked_rq bracket that SCX_CALL_OP_TASK() provides. The hand-rolled bracket reset locked_rq to NULL on exit instead of restoring the saved value, so a nested call would clobber the outer op's locked-rq tracking. Parameterize the dispatch macros by the ops-table member and add SCX_CALL_CID_OP_TASK(), which routes through sch->ops_cid. Convert scx_call_op_set_cpumask() to it and drop the hand-rolled bracket. The only behavioral change is that locked_rq is now saved and restored like every other op call site. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-09sched_ext: Use READ_ONCE/WRITE_ONCE in cmask word ops and drop _RACY variantsTejun Heo
The cmask ops can operate on BPF-arena cmasks which BPF programs may read and write concurrently. The _RACY op variants existed to make such lockless reads explicit but this turned out to be too restrictive. Mark the word accesses in all the two-cmask ops with READ_ONCE/WRITE_ONCE instead and drop the _RACY variants. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-09Merge branch 'for-7.2-fixes' into for-7.3Tejun Heo
Pull to receive: db4e9defd2e8 ("sched_ext: Record an error on errno-only sub-enable failure") 49b3378a750c ("sched_ext: Fix premature ops->priv publication in scx_alloc_and_add_sched()") e6979d05c6a6 ("tools/sched_ext: scx - Fix cmask_subset(), cmask_equal() and cmask_weight()") for further sub-sched changes and to resolve the conflicts with the sub-sched updates on for-7.3. db4e9defd2e8 adds scx_error() to the sub-enable err_disable sink which for-7.3 moved from ext.c into sub.c. Resolved by applying the fix to scx_sub_enable_workfn() in sub.c. 49b3378a750c drops RCU_INIT_POINTER() from an scx_alloc_and_add_sched() unwind label whose body changed with for-7.3's stall_cpus addition. Resolved by dropping the line from the updated unwind. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-07-09sched_ext: Fix premature ops->priv publication in scx_alloc_and_add_sched()Tejun Heo
scx_alloc_and_add_sched() publishes @sch through ops->priv before allocating the cgroup path. If that allocation fails, the unwind path clears ops->priv and frees @sch immediately. scx_prog_sched() callers can dereference ops->priv from RCU context the moment it is set, so freeing without a grace period can use-after-free a concurrent kfunc caller. Move the publication below the cgroup path allocation so that every failure path after publication frees @sch through kobject_put(), whose release path defers the freeing by a grace period. Fixes: 105dcd005be2 ("sched_ext: Introduce scx_prog_sched()") Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-09sched_ext: Record an error on errno-only sub-enable failureTejun Heo
scx_sub_enable_workfn() has several failure paths that only return an errno (e.g. -ENOMEM from an allocation) and jump to err_disable without calling scx_error(). scx_flush_disable_work() runs the disable, and thus ops.exit(), only when an error has been recorded, so an errno-only failure leaves the half-initialized sub-scheduler linked. Record an error at the err_disable sink so every errno-only failure runs the disable path. Fixes: ebeca1f930ea ("sched_ext: Introduce cgroup sub-sched support") Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-09sched_ext: Fix typo in scx_bpf_dsq_insert() commentLiang Luo
The comment for scx_bpf_dsq_insert() references "@dsp_id" in the description body, but the parameter is named "@dsq_id" in both the parameter list and the function signature. Signed-off-by: Liang Luo <luoliang@kylinos.cn> Acked-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-07-08sched_ext: Enable tick for finite slices on nohz_fullAndrea Righi
set_next_task_scx() updates the tick dependency before __schedule() updates rq->curr. When switching from a non-EXT task, such as idle, to an EXT task with a finite slice, sched_update_tick_dependency() checks the outgoing task and can allow the tick to remain stopped. The dependency can also be lost without a slice-type transition. After a finite-slice task leaves the CPU idle, the enqueue path can clear the dependency against the idle rq->curr. SCX_RQ_CAN_STOP_TICK still records a finite slice, so another finite task skips the transition block and can run without the ticks needed to expire its slice. The reverse mismatch can also happen when the last finite-slice EXT task is dequeued: sub_nr_running() updates the dependency before rq->curr changes, so the outgoing task state can keep the dependency set after the CPU goes idle. Fix this by unconditionally enabling the scheduler tick whenever a finite-slice EXT task is selected on a nohz_full CPU. Moreover, when the last runnable EXT task leaves, ignore the outgoing EXT slice state so the generic scheduler can correctly re-evaluate and clear the tick dependency. Fixes: 22a920209ab6 ("sched_ext: Implement tickless support") Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-07-08sched_ext: Preserve rq tracking across local DSQ dispatchAndrea Righi
dispatch_to_local_dsq() can run from scx_bpf_dsq_move_to_local() while ops.dispatch() has recorded the current rq. Moving a task to a local DSQ may switch to the source or destination rq before synchronously invoking ops.dequeue() through the following path: SCX_CALL_OP(dispatch, rq) ops.dispatch() scx_bpf_dsq_move_to_local() scx_flush_dispatch_buf() finish_dispatch() dispatch_to_local_dsq() scx_dispatch_enqueue() local_dsq_post_enq() call_task_dequeue() SCX_CALL_OP_TASK(dequeue, locked_rq, ...) The nested callback saves the recorded rq and restores it on return. If the rq tracking does not follow the lock switch, update_locked_rq() can trigger the following lockdep assertion while restoring an rq which is no longer held: WARNING: kernel/sched/sched.h:1641 at call_task_dequeue+0x160/0x170 Call Trace: scx_dispatch_enqueue+0x2b0/0x460 dispatch_to_local_dsq+0x138/0x230 scx_flush_dispatch_buf+0x1af/0x220 scx_bpf_dsq_move_to_local___v2+0xe2/0x1c0 bpf__sched_ext_ops_dispatch+0x4b/0xa7 do_pick_task_scx+0x3b6/0x910 __pick_next_task+0x105/0x1f0 __schedule+0x3e7/0x1980 Introduce switch_rq_lock() to update the tracking state together with each rq lock handoff. Use it in dispatch_to_local_dsq(), move_remote_task_to_local_dsq() and the in-balance paths of scx_dsq_move(), ensuring that scx_locked_rq() consistently refers to the rq whose lock is actually held throughout the lock dance. Fixes: 7fb39e4eb4c3 ("sched_ext: Save and restore scx_locked_rq across SCX_CALL_OP") Cc: stable@vger.kernel.org # 7.1+ Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-07-01sched_ext: Split sub-scheduler implementation into sub.cTejun Heo
The sub-scheduler implementation has grown and will continue to expand. Move the sub-scheduler functions from ext.c into a new kernel/sched/ext/sub.c. sub.h holds the prototypes and the !CONFIG_EXT_SUB_SCHED no-op stubs. scx_dispatch_sched() is shared: balance_one() in ext.c and the scx_bpf_sub_dispatch() kfunc in sub.c both call it, and the latter re-enters it as sub-scheduler dispatch nests. It moves into sub.h as a static __always_inline so both callers keep it inlined and per-level stack stays bounded across the recursion. The event macros it uses move to internal.h. No functional change. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-01sched_ext: Inline small ext.c helpers shared across the sub.c splitTejun Heo
The following trivial helpers in ext.c are called from both ext.c and the sub-scheduler code. Define them as static inline in internal.h. - scx_bypass_dsq() - scx_bypass_dsp_enabled() - scx_ops_sanitize_err() - scx_schedule_reenq_local() No functional change. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-01sched_ext: Expose the ext.c internals used by the sub.c splitTejun Heo
The sub-scheduler implementation is about to move into its own sub.c, from where it calls a set of ext.c helpers and shares a few ext.c globals. Make those reachable across the new file boundary ahead of the move. No functional change. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-01sched_ext: Prefix file-local ext.c helpers exposed by the sub.c splitTejun Heo
A later change moves the sub-scheduler implementation out of ext.c into its own file, from where it calls a number of file-local ext.c helpers. Give those helpers the scx_ prefix that cross-file sched_ext symbols carry, ahead of the move so the mechanical rename stays out of the code-motion patch. No functional change. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-07-01sched_ext: Merge branch 'for-7.2-fixes' into for-7.3Tejun Heo
for-7.3 will reorganize sub-sched code. Pull in for-7.2-fixes in preparation. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-30sched_ext: Parenthesize local SCX_EVENT defines to fix checkpatch errorCheng-Yang Chou
Fix checkpatch "Macros with complex values should be enclosed in parentheses" error. Suggested-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Cheng-Yang Chou <yphbchou0911@gmail.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-30sched_ext: Replace open-coded event lists with SCX_EVENTS_LISTCheng-Yang Chou
Three sites enumerate every scx_event_stats field by name: scx_read_events(), scx_attr_events_show(), and scx_dump_state(). Adding a new SCX_EV_* event requires three separate manual edits. Missing any one silently drops counts or omits the field from diagnostic output with no compile-time indication. SCX_EVENTS_LIST(SCX_EVENT) in internal.h becomes the single authoritative list of event names, so all three sites stay in sync. No functional changes. Signed-off-by: Cheng-Yang Chou <yphbchou0911@gmail.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-30sched_ext: Don't warn on core-sched forced idle in put_prev_task_scx()Tejun Heo
put_prev_task_scx() warns when a runnable task drops to a lower sched_class without SCX_OPS_ENQ_LAST, on the assumption that balance_one() would have kept it running. Core scheduling breaks that: a forced-idle SMT sibling reschedules through the core_pick fast path in pick_next_task(), which skips pick_task_scx() and thus balance_one(), so a runnable task can drop to idle with ENQ_LAST unset. Gate the warning on sched_cpu_cookie_match(): a cookie mismatch means core scheduling forced the idle, while a match (or core scheduling off) still catches a genuine missing-ENQ_LAST drop. Fixes: 7c65ae81ea86 ("sched_ext: Don't call put_prev_task_scx() before picking the next task") Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-06-29sched_ext: Pin parent scx_sched across a child sub-scheduler's lifetimeTejun Heo
A child sub-scheduler dereferences its parent scx_sched throughout its life, e.g., in scx_sub_disable() which reparents the child's tasks and calls parent->ops.sub_detach() after unlinking from the parent. However, the parent is pinned only through parent->sub_kset, which is dropped during disable. The parent scx_sched can be RCU-freed while a child is still disabling. Take a direct reference on the parent in scx_alloc_and_add_sched(), dropped in scx_sched_free_rcu_work(), so a parent always outlives its descendants. Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-25sched_ext: Convert scx_arena_grow() to use scx_arena_to_kaddr()Cheng-Yang Chou
scx_arena_grow() was left open-coded when 2e05f2fd0dd7 ("sched_ext: Add scx_arena_to_kaddr() / scx_kaddr_to_arena()") introduced the helper. Replace the manual bpf_arena_map_kern_vm_start() fetch and #ifdef guard with scx_arena_to_kaddr(sch, p). Signed-off-by: Cheng-Yang Chou <yphbchou0911@gmail.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-24sched_ext, rcu: Upgrade RCU stall paths to report cpumask of stalled CPUsCheng-Yang Chou
scx_rcu_cpu_stall() previously recorded the detector CPU rather than the stalled one, and the expedited grace period path had no stalled CPU to report at all. Thread a cpumask through panic_on_rcu_stall() and scx_rcu_cpu_stall() to capture all stalled CPUs. Report cpumask_first() as exit_cpu and the full CPU list in the exit message. Task-only stalls yield exit_cpu = -1. Store the stall mask in scx_sched rather than scx_exit_info, keeping the BPF-visible struct unchanged. scx_dump_state() reads sch->stall_cpus directly and dumps all stalled CPUs first to avoid losing them to truncation. Signed-off-by: Cheng-Yang Chou <yphbchou0911@gmail.com> Reviewed-by: Paul E. McKenney <paulmck@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-24sched_ext: Fix exit_cpu accuracy for lockup pathsCheng-Yang Chou
handle_lockup() uses raw_smp_processor_id() for exit_cpu, which is wrong for two paths: - scx_hardlockup_irq_workfn() has the hung CPU in a local variable but irq_work may run elsewhere. Pass the local cpu explicitly. - scx_rcu_cpu_stall() records the detector CPU rather than the stalled one. Pass -1 for now. The next patch fixes this properly. Signed-off-by: Cheng-Yang Chou <yphbchou0911@gmail.com> Reviewed-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-24sched_ext: Remove deprecated scx_bpf_cpu_rq()Christian Loehle
scx_bpf_cpu_rq() exposes rq pointers without requiring the rq lock and has emitted a deprecation warning since commit 5c48d88fe004 ("sched_ext: deprecation warn for scx_bpf_cpu_rq()"). The supported replacements cover the intended uses: scx_bpf_locked_rq() for locked rq access and scx_bpf_cpu_curr() for remote curr lookup. Remove the kfunc, its BTF registrations, the deprecation warning state, and the BPF-side prototype and compat fallback. Signed-off-by: Christian Loehle <christian.loehle@arm.com> Reviewed-by: Andrea Righi <arighi@nvidia.com> Reviewed-by: Hongyan Xia <hongyan.xia@transsion.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-24sched_ext: Annotate ksyncs with __rcu in alloc/free_kick_syncs()Tejun Heo
scx_kick_syncs is a per-CPU __rcu pointer, so per_cpu_ptr() returns struct scx_kick_syncs __rcu **. alloc_kick_syncs() and free_kick_syncs() stored it in a plain struct scx_kick_syncs **ksyncs, which sparse flags as an __rcu address-space mismatch. Annotate ksyncs to match. Its accesses already go through rcu_*_pointer(). Fixes: 987e00035c0e ("sched_ext: Rename pnt_seq to kick_sync") Reported-by: kernel test robot <lkp@intel.com> Closes: https://lore.kernel.org/oe-kbuild-all/202606122315.pbnDHP0n-lkp@intel.com/ Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-24sched_ext: Check remote rq eligibility under task's rq lockKuba Piecuch
task_can_run_on_remote_rq() operates under the assumption that p->migration_disabled is stable, i.e. if the kernel observed is_migration_disabled(p) == true, then the BPF scheduler must have also been able to see this when dispatching the task, and it's the BPF scheduler's fault that it tried to dispatch a task with migration disabled to a CPU other than the task's current CPU. This assumption does not always hold. It's possible that the BPF scheduler saw is_migration_disabled(p) == false, while the kernel observes is_migration_disabled(p) == true in dispatch_to_local_dsq() -> task_can_run_on_remote_rq(). The crucial thing here is that with CONFIG_PREEMPT_RCU, migration is disabled while a task is executing a BPF program. So, if there's a situation where the BPF scheduler checks a task while it's not executing a BPF program, while the kernel checks it while it is executing one, the BPF scheduler will be killed through no fault of its own. Consider the following scenario: 1. SCX task @p is executing on CPU A and CPU A gets preempted by a higher-priority scheduling class. On entry to __schedule(), p->migration_disabled == 0. 2. In put_prev_task_scx() @p is enqueued on the BPF scheduler's internal data structures, making it available for other CPUs to dispatch. 3. CPU B enters ops.dispatch(), pops @p from the BPF scheduler's data structures, checks is_migration_disabled(p) which returns false, and dispatches @p to CPU B's local DSQ. 4. On CPU A, @p hasn't been switched out yet. Execution reaches trace_sched_switch() which enters a BPF program, as the BPF scheduler hooks into the sched_switch tracepoint to detect idle->fair transitions. On entry into the BPF program, @p disables migration. 5. CPU B enters finish_dispatch() -> dispatch_to_local_dsq() -> task_can_run_on_remote_rq() which observes is_migration_disabled(p) == true, triggering scx_error(). This all happens while holding CPU B's rq lock, so it's not synchronized with @p switching out. This patch fixes this by moving the call to task_can_run_on_remote_rq() after @p's rq lock is acquired in dispatch_to_local_dsq(). This way, we synchronize with @p switching out, since @p holds its rq lock all the way until it's switched out. Thus, any BPF programs that are called between put_prev_task_scx() and the end of the context switch are guaranteed to have finished and cannot influence p->migration_disabled. Also add a lockdep assertion in task_can_run_on_remote_rq() which ensures the task rq lock is held if enforce == true. Signed-off-by: Kuba Piecuch <jpiecuch@google.com> Reviewed-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-24sched_ext: Reset dsq_vtime and slice when a task leaves SCXAndrea Righi
When a task leaves the BPF scheduler's control, p->scx.dsq_vtime and p->scx.slice keep whatever values they last held. The slice value is core-managed and is refilled on the next enqueue, but dsq_vtime is owned by the BPF scheduler and is never cleared by the core, so a task that leaves SCX and later returns carries a stale dsq_vtime across the round-trip. The stale values are also visible to other SCX schedulers that inspect the scx fields of non-SCX tasks. Fix this by resetting both dsq_vtime and slice in scx_disable_task(), after ops.disable(), so the BPF scheduler can still observe the task's final values and non-SCX tasks do not retain stale SCX state. Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-24sched_ext: Avoid flooding the log with deprecation warningsBreno Leitao
The deprecation notices for direct p->scx.slice/dsq_vtime writes and for ops->cpu_acquire/release() use plain pr_warn(), so they repeat on every scheduler (re)load and can flood the kernel log. The slice/dsq_vtime notice is the worst offender: it is emitted from the BPF verifier's btf_struct_access callback, which is re-evaluated as the verifier explores program paths, so a single scheduler load can print it many times -- hundreds of lines on some hosts, dozens within the same second. Switch both notices to pr_warn_ratelimited() so each deprecation is still reported but bursts no longer spam the log, and add the missing newline to the slice/dsq_vtime message. Signed-off-by: Breno Leitao <leitao@debian.org> Reviewed-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
2026-06-22sched_ext: Move shared helpers from ext.c into internal.h and cid.hTejun Heo
idle.c and cid.c are included into build_policy.c together with ext.c and use helpers that ext.c defines. Because the helpers live in ext.c, the two files can not parse as standalone units and clangd reports errors in them. Move the helpers to the headers they belong to. The op-dispatch macros and helpers plus scx_parent() to internal.h, and scx_cpu_arg()/scx_cpu_ret() to cid.h. No functional change. idle.c and cid.c now parse clean standalone. Suggested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-06-22sched_ext: Make kernel/sched/ext/ sources self-contained for clangdTejun Heo
The sources under kernel/sched/ext/ build as a single translation unit: build_policy.c includes the source files and headers. An LSP/clangd editor parses each as a standalone unit, sees no types, and reports a flood of errors. Give each header its dependencies and include guard, and have each source include the headers it uses. ext.c, arena.c and the ext headers now parse clean standalone. idle.c and cid.c still reference a few macros and helpers defined in ext.c. The next patch moves those to shared headers. Suggested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
2026-06-22sched_ext: Move sources under kernel/sched/ext/Tejun Heo
The sched_ext sources had grown to ten ext* files directly under kernel/sched/. Move them into a new kernel/sched/ext/ subdirectory and drop the now-redundant ext_ prefix. ext.c/h keep their names. kernel/sched/ext.{c,h} -> kernel/sched/ext/ext.{c,h} kernel/sched/ext_internal.h -> kernel/sched/ext/internal.h kernel/sched/ext_types.h -> kernel/sched/ext/types.h kernel/sched/ext_idle.{c,h} -> kernel/sched/ext/idle.{c,h} kernel/sched/ext_cid.{c,h} -> kernel/sched/ext/cid.{c,h} kernel/sched/ext_arena.{c,h} -> kernel/sched/ext/arena.{c,h} The include paths in build_policy.c and sched.h, the MAINTAINERS glob, and a few documentation and comment references are updated to match. No code or symbol changes. Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Reviewed-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>