<feed xmlns='http://www.w3.org/2005/Atom'>
<title>wireguard-linux/kernel/sched/ext/ext.c, branch stable</title>
<subtitle>WireGuard for the Linux kernel</subtitle>
<id>https://git.zx2c4.com/wireguard-linux/atom/kernel/sched/ext/ext.c?h=stable</id>
<link rel='self' href='https://git.zx2c4.com/wireguard-linux/atom/kernel/sched/ext/ext.c?h=stable'/>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/'/>
<updated>2026-09-24T22:04:11Z</updated>
<entry>
<title>Merge tag 'sched_ext-for-7.3-rc4-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext</title>
<updated>2026-09-24T22:04:11Z</updated>
<author>
<name>Linus Torvalds</name>
<email>torvalds@linux-foundation.org</email>
</author>
<published>2026-09-24T22:04:11Z</published>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/commit/?id=ee9c669f9bf5fd2c24206746ded9382fe810df89'/>
<id>urn:sha1:ee9c669f9bf5fd2c24206746ded9382fe810df89</id>
<content type='text'>
Pull sched_ext fixes from Tejun Heo:

 - A task reenqueued while its dispatch was still completing had its
   queued state clobbered by the dispatcher, dropping every later
   dispatch of the task. Wait for the in-flight dispatch to settle
   first

 - A wakeup activation on another CPU marked the destination runqueue as
   mid-wakeup, stranding a pending local reenqueue. If the scheduler was
   unloaded first, the stale request pointed into freed memory that the
   next scheduler dereferenced

 - ops.dequeue() ran with the source dispatch queue's lock held, so a
   scheduler iterating that queue from the callback deadlocked the CPU

 - Schedulers with their own CPU ID mapping had no way to learn a task's
   initial CPU mask and rebuilt it themselves, which went wrong across
   sub-scheduler enable and re-home. Pass it to ops.enable()

 - A bypass dispatch event counter missed the dispatches made by the
   end-of-dispatch fallback and under-reported

 - Selftests for the dequeue locking and initial mask changes

* tag 'sched_ext-for-7.3-rc4-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext:
  sched_ext: Count SCX_EV_SUB_BYPASS_DISPATCH in the dispatch fallback
  selftests/sched_ext: Check the cmask cid-form ops.enable() receives
  sched_ext: Pass the initial cmask to cid-form ops.enable()
  selftests/sched_ext: Test that ops.dequeue() can iterate the consumed DSQ
  sched_ext: Don't run ops.dequeue() with a DSQ lock held
  sched_ext: Derive SCX_RQ_IN_WAKEUP from the core enqueue flags
  sched_ext: Wait for SCX_OPSS_DISPATCHING before reenqueueing a task
</content>
</entry>
<entry>
<title>sched_ext: Pass the initial cmask to cid-form ops.enable()</title>
<updated>2026-09-19T14:08:58Z</updated>
<author>
<name>Tejun Heo</name>
<email>tj@kernel.org</email>
</author>
<published>2026-09-19T00:28:37Z</published>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/commit/?id=3bd46666cfe51e2bd333fb46a0234694ca594230'/>
<id>urn:sha1:3bd46666cfe51e2bd333fb46a0234694ca594230</id>
<content type='text'>
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-&gt;cpus_ptr equivalent to fall back on. Schedulers
work around it by seeding the mask in ops.init_task() from p-&gt;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 &lt;tj@kernel.org&gt;
Reviewed-by: Andrea Righi &lt;arighi@nvidia.com&gt;
</content>
</entry>
<entry>
<title>sched_ext: Don't run ops.dequeue() with a DSQ lock held</title>
<updated>2026-09-17T17:20:04Z</updated>
<author>
<name>fangqiurong</name>
<email>fangqiurong@kylinos.cn</email>
</author>
<published>2026-09-17T07:52:41Z</published>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/commit/?id=cb86607ada73185f321baa7f9d94a08a3a40bbf5'/>
<id>urn:sha1:cb86607ada73185f321baa7f9d94a08a3a40bbf5</id>
<content type='text'>
ops.dequeue() is invoked with the source user DSQ's lock still held on
the consume and move paths (scx_consume_dispatch_q(),
move_task_between_dsqs()). A BPF scheduler which locks the source user
DSQ from ops.dequeue() - e.g. by iterating it with bpf_iter_scx_dsq -
self-deadlocks.

ops.dequeue() can only call the "any" kfuncs and none of them can lock a
builtin DSQ, so the global and bypass paths can't deadlock; however,
all DSQ locks share one lockdep class, so iterating any user DSQ from
ops.dequeue() on those paths trips the recursion check.

Move the invocation after the DSQ unlock on all three paths.
SCX_TASK_IN_CUSTODY is cleared under the lock serializing the transfer
so that the callback is invoked exactly once.

Fixes: ebf1ccff79c4 ("sched_ext: Fix ops.dequeue() semantics")
Cc: stable@vger.kernel.org # v7.1+
Acked-by: Andrea Righi &lt;arighi@nvidia.com&gt;
Signed-off-by: fangqiurong &lt;fangqiurong@kylinos.cn&gt;
Signed-off-by: Tejun Heo &lt;tj@kernel.org&gt;
</content>
</entry>
<entry>
<title>sched_ext: Derive SCX_RQ_IN_WAKEUP from the core enqueue flags</title>
<updated>2026-09-17T16:45:32Z</updated>
<author>
<name>Tejun Heo</name>
<email>tj@kernel.org</email>
</author>
<published>2026-09-16T22:00:24Z</published>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/commit/?id=df5cdc2c832ca4e8a6d774596b9005558761a403'/>
<id>urn:sha1:df5cdc2c832ca4e8a6d774596b9005558761a403</id>
<content type='text'>
schedule_deferred_locked() skips scheduling a deferred action while
SCX_RQ_IN_WAKEUP is set and relies on the task_woken_scx() call that follows
a wakeup enqueue to run it. enqueue_task_scx() sets the flag from the merged
enqueue flags, which include the flags stashed for a remote activation.
move_remote_task_to_local_dsq() thus sets SCX_RQ_IN_WAKEUP on the
destination rq when the moved task was woken up, although no
task_woken_scx() follows that activation.

An IMMED insert into a busy destination requests a local reenqueue during
that enqueue. The request gets linked but not scheduled and stays pending
until an unrelated wakeup or preemption on that CPU runs the deferred
actions. The IMMED task sits behind the running task in the meantime. If
nothing runs them before the scheduler is disabled, the request outlives the
scheduler and points into its freed per-cpu area, which the next scheduler
dereferences from run_deferred().

Test the core enqueue flags for the wakeup bit. Only the core's wakeup path
is followed by task_woken_scx().

Fixes: 57ccf5ccdc56 ("sched_ext: Fix enqueue_task_scx() truncation of upper enqueue flags")
Cc: stable@vger.kernel.org # v7.1+
Reported-by: Andrea Righi &lt;arighi@nvidia.com&gt;
Link: https://lore.kernel.org/all/20260916145807.3250167-1-arighi@nvidia.com/
Signed-off-by: Tejun Heo &lt;tj@kernel.org&gt;
Reviewed-by: Andrea Righi &lt;arighi@nvidia.com&gt;
</content>
</entry>
<entry>
<title>sched_ext: Wait for SCX_OPSS_DISPATCHING before reenqueueing a task</title>
<updated>2026-09-15T22:34:44Z</updated>
<author>
<name>Tejun Heo</name>
<email>tj@kernel.org</email>
</author>
<published>2026-09-15T22:34:44Z</published>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/commit/?id=7de9a6fb44eae4f05e68c805d58b9c618815adfa'/>
<id>urn:sha1:7de9a6fb44eae4f05e68c805d58b9c618815adfa</id>
<content type='text'>
ebf1ccff79c4 ("sched_ext: Fix ops.dequeue() semantics") moved the final
ops_state store in scx_dispatch_enqueue() after the DSQ unlock so that the
custody update and ops.dequeue() precede it. A task can thus be found on a
DSQ while still SCX_OPSS_DISPATCHING.

The dequeue and core-sched pick paths wait for the state to clear in
ops_dequeue() but the reenqueue paths don't. A reenqueue in that window runs
ops.enqueue() and sets SCX_OPSS_QUEUED before the dispatch has completed.
The dispatcher's final store then overwrites it with SCX_OPSS_NONE and
finish_dispatch() drops every later dispatch of the task.

Wait for SCX_OPSS_DISPATCHING to clear before dequeueing a task for
reenqueue, the same way ops_dequeue() does.

Fixes: ebf1ccff79c4 ("sched_ext: Fix ops.dequeue() semantics")
Cc: stable@vger.kernel.org # v7.1+
Signed-off-by: Tejun Heo &lt;tj@kernel.org&gt;
Reviewed-by: Andrea Righi &lt;arighi@nvidia.com&gt;
</content>
</entry>
<entry>
<title>Merge tag 'sched_ext-for-7.3-rc3-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext</title>
<updated>2026-09-15T18:57:51Z</updated>
<author>
<name>Linus Torvalds</name>
<email>torvalds@linux-foundation.org</email>
</author>
<published>2026-09-15T18:57:51Z</published>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/commit/?id=9b87fdc9af2fbfcdb5c24a64139685ef80f6573f'/>
<id>urn:sha1:9b87fdc9af2fbfcdb5c24a64139685ef80f6573f</id>
<content type='text'>
Pull sched_ext fixes from Tejun Heo:

 - An error raised by a BPF program before the scheduler finished
   enabling was consumed by the disable path's pre-enable shortcut,
   leaving a running scheduler that couldn't be disabled and was later
   freed while in use.

 - Two compat kfuncs dereferenced a NULL scheduler when handed an exited
   or idle task, oopsing the kernel.

 - Keep-running decisions in the dispatch path used the root scheduler's
   flags for tasks belonging to a sub-scheduler, causing warnings and
   stalls.

 - Schedulers with their own CPU ID mapping had no way to learn which
   IDs are online. Add a kernel-maintained online mask to plug the hole.

 - Cgroup idle state: the initial cpu.idle state wasn't passed on cgroup
   init and same-value rewrites delivered spurious callbacks.

 - Example scheduler fixes for a reenqueue loop on attach, placements on
   CPUs without effective grants, stalled partition work and stale idle
   tracking.

* tag 'sched_ext-for-7.3-rc3-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext:
  sched_ext: Maintain an online cid mask in the scheduler arena
  sched_ext: scx_qmap: Restore unused idle claims from ops.dispatch()
  sched_ext: Close the pre-enable ops error claim window
  sched_ext: scx_qmap: Fix pending partition work handoff
  sched_ext: scx_qmap: Place only on cids whose caps are in effect
  sched_ext: scx_qmap: Do not add IMMED to rescue inserts
  sched_ext: Use @prev's scheduler for the keep decisions in dispatch_one()
  sched_ext: Rename sch to root_sch in dispatch_one()
  sched_ext: Fix NULL sched deref in kfunc sub-sched error paths
  sched_ext: Don't deliver duplicate ops.cgroup_set_idle() for same value
  sched_ext: Pass the initial cpu.idle state in scx_cgroup_init_args
</content>
</entry>
<entry>
<title>sched_ext: Maintain an online cid mask in the scheduler arena</title>
<updated>2026-09-15T16:57:20Z</updated>
<author>
<name>Tejun Heo</name>
<email>tj@kernel.org</email>
</author>
<published>2026-09-15T08:12:34Z</published>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/commit/?id=a9e3760b0838299649c0d57cca44daaf40ba3c33'/>
<id>urn:sha1:a9e3760b0838299649c0d57cca44daaf40ba3c33</id>
<content type='text'>
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 &lt;tj@kernel.org&gt;
Reviewed-by: Andrea Righi &lt;arighi@nvidia.com&gt;
</content>
</entry>
<entry>
<title>sched_ext: Close the pre-enable ops error claim window</title>
<updated>2026-09-13T15:53:33Z</updated>
<author>
<name>fangqiurong</name>
<email>fangqiurong@kylinos.cn</email>
</author>
<published>2026-09-12T13:15:18Z</published>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/commit/?id=c7a1c6e8004ab12a9c9bfdcb603f60f9bf4a3cee'/>
<id>urn:sha1:c7a1c6e8004ab12a9c9bfdcb603f60f9bf4a3cee</id>
<content type='text'>
scx_alloc_and_add_sched() publishes ops-&gt;priv before
scx_root_enable_workfn() switches the state to SCX_ENABLING. An error
claimed via scx_bpf_error_bstr() from an associated BPF program in that
window is consumed by scx_disable_workfn(), which takes the pre-enable
shortcut in scx_root_disable(). The shortcut returns without any teardown
and restores SCX_DISABLED with an unconditional scx_set_enable_state() xchg
racing the enable workfn's own transition. The enable then completes with
the claim consumed: the scheduler stays up but can never be disabled again,
and bpf_scx_unreg() frees it while still in use, resulting in a
use-after-free. Both WARN_ON_ONCE()s fire back to back:

  WARNING: kernel/sched/ext/ext.c:7522 at
  scx_root_enable_workfn+0xeec/0x1be0, CPU#3: scx_enable_help/276

  WARNING: kernel/sched/ext/ext.c:6398 at scx_root_disable+0xb50/0xdb8,
  CPU#0: sched_ext_helpe/664

scx_root_enable_workfn() switches to SCX_ENABLING before the scheduler
allocation, so ops-&gt;priv is never visible while SCX_DISABLED. The allocation
failure path restores SCX_DISABLED.

Fixes: 105dcd005be2 ("sched_ext: Introduce scx_prog_sched()")
Cc: stable@vger.kernel.org
Signed-off-by: fangqiurong &lt;fangqiurong@kylinos.cn&gt;
Signed-off-by: Tejun Heo &lt;tj@kernel.org&gt;
</content>
</entry>
<entry>
<title>sched_ext: Use @prev's scheduler for the keep decisions in dispatch_one()</title>
<updated>2026-09-05T22:12:12Z</updated>
<author>
<name>Tejun Heo</name>
<email>tj@kernel.org</email>
</author>
<published>2026-09-05T16:09:56Z</published>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/commit/?id=90f19b2816f5d243a8daf36ca83d9d9d04f00e4c'/>
<id>urn:sha1:90f19b2816f5d243a8daf36ca83d9d9d04f00e4c</id>
<content type='text'>
dispatch_one() tests ops flags and bypass state against the root scheduler
in both places where it decides to keep running @prev: the early keep of a
@prev with slice left tests the root's bypass state, and the keep-last at
the end tests the root's SCX_OPS_ENQ_LAST and bypass state. Both are
properties of the scheduler @prev belongs to, and put_prev_task_scx(), which
acts on the outcome, reads them from that scheduler. When @prev belongs to a
sub-scheduler the two sides disagree.

The keep-last case is visible. The root set SCX_OPS_ENQ_LAST, so a lone
@prev of a sub-scheduler is not kept and is enqueued with SCX_ENQ_LAST to a
sub-scheduler that never opted in. This trips the WARN_ON_ONCE in
put_prev_task_scx() for the missing flag, and the sub-scheduler queues the
task like any other and triggers no follow-up scheduling event, which can
lead to stalls.

Test SCX_OPS_ENQ_LAST and bypass state on @prev's sched in both places and
charge SCX_EV_DISPATCH_KEEP_LAST to it. Read the sched at each decision, as
the dispatch in between can drop the rq lock.

Fixes: 88234b075c3f ("sched_ext: Introduce scx_task_sched[_rcu]()")
Signed-off-by: Tejun Heo &lt;tj@kernel.org&gt;
Reviewed-by: Andrea Righi &lt;arighi@nvidia.com&gt;
</content>
</entry>
<entry>
<title>sched_ext: Rename sch to root_sch in dispatch_one()</title>
<updated>2026-09-05T22:12:04Z</updated>
<author>
<name>Tejun Heo</name>
<email>tj@kernel.org</email>
</author>
<published>2026-09-05T16:09:55Z</published>
<link rel='alternate' type='text/html' href='https://git.zx2c4.com/wireguard-linux/commit/?id=3265ef0b670180b0b946d73ee9825d6d91e98a08'/>
<id>urn:sha1:3265ef0b670180b0b946d73ee9825d6d91e98a08</id>
<content type='text'>
dispatch_one() uses the root scheduler for everything it does, including the
two decisions to keep running @prev, which are wrong when @prev belongs to a
sub-scheduler. The function has to deal with @prev's scheduler too. Rename
the root's local from sch to root_sch for clarity and to make room for it.
No functional change.

Signed-off-by: Tejun Heo &lt;tj@kernel.org&gt;
Reviewed-by: Andrea Righi &lt;arighi@nvidia.com&gt;
</content>
</entry>
</feed>
