[PATCH 13/15] sched_ext: Optimize schedule_dsq_reenq() with lockless fast path

From: Tejun Heo

Date: Fri Mar 06 2026 - 14:07:30 EST


schedule_dsq_reenq() always acquires deferred_reenq_lock to queue a reenqueue
request. Add a lockless fast-path to skip lock acquisition when the request is
already pending with the required flags set.

Signed-off-by: Tejun Heo <tj@xxxxxxxxxx>
---
kernel/sched/ext.c | 44 ++++++++++++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 8 deletions(-)

diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index f58afbc69cb4..8e7dffe4094c 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -1174,10 +1174,20 @@ static void schedule_dsq_reenq(struct scx_sched *sch, struct scx_dispatch_q *dsq
struct scx_sched_pcpu *sch_pcpu = per_cpu_ptr(sch->pcpu, cpu_of(rq));
struct scx_deferred_reenq_local *drl = &sch_pcpu->deferred_reenq_local;

- scoped_guard (raw_spinlock_irqsave, &rq->scx.deferred_reenq_lock) {
+ /*
+ * Pairs with smp_mb() in process_deferred_reenq_locals() and
+ * guarantees that there is a reenq_local() afterwards.
+ */
+ smp_mb();
+
+ if (list_empty(&drl->node) ||
+ (READ_ONCE(drl->flags) & reenq_flags) != reenq_flags) {
+
+ guard(raw_spinlock_irqsave)(&rq->scx.deferred_reenq_lock);
+
if (list_empty(&drl->node))
list_move_tail(&drl->node, &rq->scx.deferred_reenq_locals);
- drl->flags |= reenq_flags;
+ WRITE_ONCE(drl->flags, drl->flags | reenq_flags);
}

schedule_deferred(rq);
@@ -1186,10 +1196,20 @@ static void schedule_dsq_reenq(struct scx_sched *sch, struct scx_dispatch_q *dsq
struct scx_dsq_pcpu *dsq_pcpu = per_cpu_ptr(dsq->pcpu, cpu_of(rq));
struct scx_deferred_reenq_user *dru = &dsq_pcpu->deferred_reenq_user;

- scoped_guard (raw_spinlock_irqsave, &rq->scx.deferred_reenq_lock) {
+ /*
+ * Pairs with smp_mb() in process_deferred_reenq_users() and
+ * guarantees that there is a reenq_user() afterwards.
+ */
+ smp_mb();
+
+ if (list_empty(&dru->node) ||
+ (READ_ONCE(dru->flags) & reenq_flags) != reenq_flags) {
+
+ guard(raw_spinlock_irqsave)(&rq->scx.deferred_reenq_lock);
+
if (list_empty(&dru->node))
list_move_tail(&dru->node, &rq->scx.deferred_reenq_users);
- dru->flags |= reenq_flags;
+ WRITE_ONCE(dru->flags, dru->flags | reenq_flags);
}

schedule_deferred(rq);
@@ -3774,7 +3794,7 @@ static void process_deferred_reenq_locals(struct rq *rq)

while (true) {
struct scx_sched *sch;
- u64 reenq_flags = 0;
+ u64 reenq_flags;

scoped_guard (raw_spinlock, &rq->scx.deferred_reenq_lock) {
struct scx_deferred_reenq_local *drl =
@@ -3789,10 +3809,14 @@ static void process_deferred_reenq_locals(struct rq *rq)
sch_pcpu = container_of(drl, struct scx_sched_pcpu,
deferred_reenq_local);
sch = sch_pcpu->sch;
- swap(drl->flags, reenq_flags);
+ reenq_flags = drl->flags;
+ WRITE_ONCE(drl->flags, 0);
list_del_init(&drl->node);
}

+ /* see schedule_dsq_reenq() */
+ smp_mb();
+
reenq_local(sch, rq, reenq_flags);
}
}
@@ -3866,7 +3890,7 @@ static void process_deferred_reenq_users(struct rq *rq)

while (true) {
struct scx_dispatch_q *dsq;
- u64 reenq_flags = 0;
+ u64 reenq_flags;

scoped_guard (raw_spinlock, &rq->scx.deferred_reenq_lock) {
struct scx_deferred_reenq_user *dru =
@@ -3881,10 +3905,14 @@ static void process_deferred_reenq_users(struct rq *rq)
dsq_pcpu = container_of(dru, struct scx_dsq_pcpu,
deferred_reenq_user);
dsq = dsq_pcpu->dsq;
- swap(dru->flags, reenq_flags);
+ reenq_flags = dru->flags;
+ WRITE_ONCE(dru->flags, 0);
list_del_init(&dru->node);
}

+ /* see schedule_dsq_reenq() */
+ smp_mb();
+
BUG_ON(dsq->id & SCX_DSQ_FLAG_BUILTIN);
reenq_user(rq, dsq, reenq_flags);
}
--
2.53.0