From: Andrea Righi <arighi@nvidia.com>
To: Tejun Heo <tj@kernel.org>
Cc: David Vernet <void@manifault.com>,
Changwoo Min <changwoo@igalia.com>,
sched-ext@lists.linux.dev, Emil Tsalapatis <emil@etsalapatis.com>,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH 1/5] sched_ext: Make exit claiming lock-free
Date: Mon, 27 Jul 2026 21:35:12 +0200 [thread overview]
Message-ID: <amey8FunQb8M7qwI@gpd4> (raw)
In-Reply-To: <20260725005019.1297049-2-tj@kernel.org>
Hi Tejun,
On Fri, Jul 24, 2026 at 02:50:15PM -1000, Tejun Heo wrote:
> scx_claim_exit() claims descendants' exits by walking the subtree under
> scx_sched_lock, making exit claiming, and thus scx_error(), unusable from
> NMI and from under scx_sched_lock. However, kfuncs raising errors can run
> from NMI-attached BPF progs, the hardlockup handler runs in NMI, and
> scx_link_sched() wants to report failures under the lock.
>
> The walk does two things with different urgencies: ->aborting must be
> asserted synchronously to break IRQs-off dispatch-path live-locks, while the
> descendants' exit_kind claims can happen later. Split them: sweep ->aborting
> locklessly under RCU to unwedge the system and defer the locked
> SCX_EXIT_PARENT walk to a new irq_work, both of which are NMI-safe.
>
> The sweep stores each node's ->aborting and then reads its children list
> while scx_link_sched() inserts and then checks the parent's ->aborting, the
> two sides paired by full barriers - one side always sees the other. A link
> that sees ->aborting undoes its insert and fails. As the undo's
> list_del_rcu() leaves ->sibling non-empty, list_empty() can no longer
> identify a never-linked sched during teardown - add sch->linked instead.
>
> trace_sched_ext_exit can now fire from NMI. The exit backtrace is skipped
> for NMI exits as stack_trace_save()'s NMI-safety is arch-dependent and
> undocumented.
>
> Signed-off-by: Tejun Heo <tj@kernel.org>
> ---
...
> diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
> index aca8d2380509..30ce4c9428cf 100644
> --- a/kernel/sched/ext/ext.c
> +++ b/kernel/sched/ext/ext.c
> @@ -5027,6 +5027,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
...
> /*
> * Claim the exit on @sch. The caller must ensure that the helper kthread work
> * is kicked before the current task can be preempted. Once exit_kind is
> * claimed, scx_error() can no longer trigger, so if the current task gets
> * preempted and the BPF scheduler fails to schedule it back, the helper work
> * will never be kicked and the whole system can wedge.
> + *
> + * Lock-free and safe to call from any context including NMI.
> */
> static bool scx_claim_exit(struct scx_sched *sch, enum scx_exit_kind kind)
> {
> @@ -6279,35 +6310,28 @@ static bool scx_claim_exit(struct scx_sched *sch, enum scx_exit_kind kind)
> if (!atomic_try_cmpxchg(&sch->exit_kind, &none, kind))
> return false;
>
> - /*
> - * Some CPUs may be trapped in the dispatch paths. Set the aborting
> - * flag to break potential live-lock scenarios, ensuring we can
> - * successfully reach scx_bypass().
> - */
> - WRITE_ONCE(sch->aborting, true);
> -
> trace_sched_ext_exit(sch, kind);
>
> - /*
> - * Propagate exits to descendants immediately. Each has a dedicated
> - * helper kthread and can run in parallel. While most of disabling is
> - * serialized, running them in separate threads allows parallelizing
> - * ops.exit(), which can take arbitrarily long prolonging bypass mode.
> - *
> - * To guarantee forward progress, this propagation must be in-line so
> - * that ->aborting is synchronously asserted for all sub-scheds. The
> - * propagation is also the interlocking point against sub-sched
> - * attachment. See scx_link_sched().
> - *
> - * This doesn't cause recursions as propagation only takes place for
> - * non-propagation exits.
> - */
> - if (kind != SCX_EXIT_PARENT) {
> - scoped_guard (raw_spinlock_irqsave, &scx_sched_lock) {
> - struct scx_sched *pos;
> + if (kind == SCX_EXIT_PARENT) {
> + /* an ancestor is already sweeping the subtree */
> + WRITE_ONCE(sch->aborting, true);
> + } else {
> + struct scx_sched *pos;
> +
> + /*
> + * CPUs may be live-locked in the dispatch paths of @sch or its
> + * descendants, which ->aborting breaks. Sweep the subtree
> + * locklessly so that this works from NMI. smp_store_mb() orders
> + * each node's ->aborting store before its children are walked -
> + * either we see a racing scx_link_sched() on ->children or it
> + * sees ->aborting.
> + */
> + scoped_guard (rcu) {
> scx_for_each_descendant_pre(pos, sch)
> - scx_disable(pos, SCX_EXIT_PARENT);
> + smp_store_mb(pos->aborting, true);
> }
> +
> + irq_work_queue(&sch->propagate_exit_irq_work);
> }
Should we move the trace_sched_ext_exit() after this block?
Before this patch, sch->aborting was set before the tracepoint. Now the
tracepoint callbacks run before any scheduler in the subtree is marked aborting.
Patch 4 also makes this path callable directly from the hardlockup NMI.
While the tracepoint is running, other CPUs may remain in the live-lock-prone
dispatch paths instead of observing ->aborting and backing out. Probably it
doesn't really matter much, but this should help reduce the recovery window a
bit.
Thanks,
-Andrea
next prev parent reply other threads:[~2026-07-27 19:35 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-25 0:50 [PATCHSET sched_ext/for-7.3] sched_ext: NMI-safe exit handling Tejun Heo
2026-07-25 0:50 ` [PATCH 1/5] sched_ext: Make exit claiming lock-free Tejun Heo
2026-07-27 19:35 ` Andrea Righi [this message]
2026-07-27 20:36 ` [PATCH v2 " Tejun Heo
2026-07-25 0:50 ` [PATCH 2/5] sched_ext: Format bstr exit messages after claiming the exit Tejun Heo
2026-07-25 0:50 ` [PATCH 3/5] sched_ext: Report NMI kicks with scx_error() Tejun Heo
2026-07-25 0:50 ` [PATCH 4/5] sched_ext: Abort directly from the hardlockup handler Tejun Heo
2026-07-25 0:50 ` [PATCH 5/5] sched_ext: Report scx_link_sched() failures inline Tejun Heo
2026-07-27 21:17 ` [PATCHSET sched_ext/for-7.3] sched_ext: NMI-safe exit handling Andrea Righi
2026-07-27 21:24 ` Tejun Heo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=amey8FunQb8M7qwI@gpd4 \
--to=arighi@nvidia.com \
--cc=changwoo@igalia.com \
--cc=emil@etsalapatis.com \
--cc=linux-kernel@vger.kernel.org \
--cc=sched-ext@lists.linux.dev \
--cc=tj@kernel.org \
--cc=void@manifault.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox