From: Andrea Righi <arighi@nvidia.com>
To: Tejun Heo <tj@kernel.org>, David Vernet <void@manifault.com>,
Changwoo Min <changwoo@igalia.com>
Cc: Emil Tsalapatis <etsal@meta.com>,
sched-ext@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: [PATCH 1/2] sched_ext: Add lazy preemption support
Date: Fri, 11 Sep 2026 21:56:53 +0200 [thread overview]
Message-ID: <20260911195800.974364-2-arighi@nvidia.com> (raw)
In-Reply-To: <20260911195800.974364-1-arighi@nvidia.com>
The fair scheduling class supports lazy rescheduling: when lazy
preemption is enabled, a lazy reschedule request does not preempt the
current task immediately at the next kernel preemption point. Instead,
it is serviced when returning to user space or promoted to an immediate
reschedule by the next scheduler tick. This avoids unnecessary in-kernel
preemption while still bounding the scheduling delay.
sched_ext only supports immediate preemption, so BPF schedulers cannot
make the same trade-off.
Add lazy preemption support to sched_ext through SCX_ENQ_PREEMPT_LAZY
and SCX_KICK_PREEMPT_LAZY. Both operations still expire the current
sched_ext task's slice, but use resched_curr_lazy() so the scheduling
boundary can be deferred when lazy preemption is available.
On a non-local DSQ %SCX_ENQ_PREEMPT_LAZY means a head insertion, as
%SCX_ENQ_PREEMPT does there.
fair.c also expires a slice lazily: update_curr() calls
resched_curr_lazy() when the slice has run out at the tick. Let a BPF
scheduler opt into the same with SCX_OPS_LAZY_SLICE_EXPIRY, which makes
task_tick_scx() request lazy rescheduling for a depleted slice. A task
in user space still reschedules on the way back from the tick, a task in
the kernel runs on to its next return to user space or to the next tick.
The default stays immediate, as does rescheduling while the scheduler is
being disabled.
Reject requests which combine immediate and lazy preemption. Also reject
lazy kicks combined with SCX_KICK_WAIT, as waiting for a lazy scheduling
boundary would have a potentially unbounded contract. Unknown kick flags
are now rejected as well, where they used to be ignored.
Track lazy kicks in a separate mask, outside cpus_to_kick, so that a
lazy-only request can be told from one that coincides with a plain or
preempting kick. An immediate preemption supersedes a queued lazy one. A
plain kick reschedules at once, but leaves the slice alone, so a lazy
request next to it keeps clearing the slice while the reschedule stays
immediate: both are served.
Signed-off-by: Andrea Righi <arighi@nvidia.com>
---
kernel/sched/ext/ext.c | 115 ++++++++++++++----
kernel/sched/ext/internal.h | 36 +++++-
kernel/sched/ext/sub.c | 15 +--
.../sched_ext/include/scx/enum_defs.autogen.h | 3 +
.../sched_ext/include/scx/enums.autogen.bpf.h | 6 +
tools/sched_ext/include/scx/enums.autogen.h | 2 +
.../sched_ext/include/scx/enums_abi.autogen.h | 5 +-
7 files changed, 149 insertions(+), 33 deletions(-)
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 4080902a528cd..a918e3ade1148 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -383,11 +383,11 @@ static bool rq_is_open(struct rq *rq, u64 enq_flags)
return true;
/*
- * %SCX_ENQ_PREEMPT clears $curr's slice if on SCX and kicks dispatch,
- * so allow it to avoid spuriously triggering reenq on a combined
+ * The preemption flags clear $curr's slice if on SCX and kick dispatch,
+ * so allow them to avoid spuriously triggering reenq on a combined
* PREEMPT|IMMED insertion.
*/
- if (enq_flags & SCX_ENQ_PREEMPT) {
+ if (enq_flags & (SCX_ENQ_PREEMPT | SCX_ENQ_PREEMPT_LAZY)) {
struct task_struct *curr = rq->curr;
/*
@@ -1577,12 +1577,16 @@ static void rq_owned_post_enq(struct scx_sched *sch, struct rq *rq,
if (rq->scx.flags & SCX_RQ_IN_DISPATCH)
return;
- if ((enq_flags & SCX_ENQ_PREEMPT) && p != rq->curr &&
+ if ((enq_flags & (SCX_ENQ_PREEMPT | SCX_ENQ_PREEMPT_LAZY)) && p != rq->curr &&
rq->curr->sched_class == &ext_sched_class) {
- if (likely(scx_set_task_slice(rq->curr, 0)))
- resched_curr(rq);
- else
+ if (likely(scx_set_task_slice(rq->curr, 0))) {
+ if (enq_flags & SCX_ENQ_PREEMPT)
+ resched_curr(rq);
+ else
+ resched_curr_lazy(rq);
+ } else {
__scx_add_event(sch, SCX_EV_SLICE_DENIED, 1);
+ }
}
}
@@ -1672,7 +1676,7 @@ static void scx_dispatch_enqueue(struct scx_sched *sch, struct rq *rq,
scx_error(sch, "DSQ ID 0x%016llx already had PRIQ-enqueued tasks",
dsq->id);
- if (enq_flags & (SCX_ENQ_HEAD | SCX_ENQ_PREEMPT)) {
+ if (enq_flags & (SCX_ENQ_HEAD | SCX_ENQ_PREEMPT | SCX_ENQ_PREEMPT_LAZY)) {
/* new task inserted at head - use fastpath */
if (dsq_insert_head(dsq, p) && !(dsq->id & SCX_DSQ_FLAG_BUILTIN))
rcu_assign_pointer(dsq->first_task, p);
@@ -2386,7 +2390,7 @@ void scx_move_local_task_to_local_dsq(struct scx_sched *sch, struct task_struct
WARN_ON_ONCE(p->scx.holding_cpu >= 0);
- if (enq_flags & (SCX_ENQ_HEAD | SCX_ENQ_PREEMPT))
+ if (enq_flags & (SCX_ENQ_HEAD | SCX_ENQ_PREEMPT | SCX_ENQ_PREEMPT_LAZY))
dsq_insert_head(dst_dsq, p);
else
list_add_tail(&p->scx.dsq_list.node, &dst_dsq->list);
@@ -3804,8 +3808,14 @@ static void task_tick_scx(struct rq *rq, struct task_struct *curr, int queued)
else if (SCX_HAS_OP(sch, tick))
SCX_CALL_OP_TASK(sch, tick, rq, curr);
- if (!curr->scx.slice)
- resched_curr(rq);
+ if (!curr->scx.slice) {
+ /* see SCX_OPS_LAZY_SLICE_EXPIRY; the slice can't be trusted while bypassing */
+ if ((sch->ops.flags & SCX_OPS_LAZY_SLICE_EXPIRY) &&
+ !scx_bypassing(sch, cpu_of(rq)))
+ resched_curr_lazy(rq);
+ else
+ resched_curr(rq);
+ }
}
#ifdef CONFIG_EXT_GROUP_SCHED
@@ -5371,6 +5381,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
free_cpumask_var(pcpu->cpus_to_kick);
free_cpumask_var(pcpu->cpus_to_kick_if_idle);
free_cpumask_var(pcpu->cpus_to_preempt);
+ free_cpumask_var(pcpu->cpus_to_preempt_lazy);
free_cpumask_var(pcpu->cpus_to_wait);
exit_dsq(scx_bypass_dsq(sch, cpu));
@@ -6883,6 +6894,9 @@ static void scx_dump_cpu(struct scx_sched *sch, struct seq_buf *s,
if (!cpumask_empty(pcpu->cpus_to_preempt))
scx_dump_line(&ns, " cpus_to_preempt: %*pb",
cpumask_pr_args(pcpu->cpus_to_preempt));
+ if (!cpumask_empty(pcpu->cpus_to_preempt_lazy))
+ scx_dump_line(&ns, " preempt_lazy : %*pb",
+ cpumask_pr_args(pcpu->cpus_to_preempt_lazy));
if (!cpumask_empty(pcpu->cpus_to_wait))
scx_dump_line(&ns, " cpus_to_wait : %*pb",
cpumask_pr_args(pcpu->cpus_to_wait));
@@ -7200,6 +7214,7 @@ struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd,
if (!zalloc_cpumask_var_node(&pcpu->cpus_to_kick, GFP_KERNEL, node) ||
!zalloc_cpumask_var_node(&pcpu->cpus_to_kick_if_idle, GFP_KERNEL, node) ||
!zalloc_cpumask_var_node(&pcpu->cpus_to_preempt, GFP_KERNEL, node) ||
+ !zalloc_cpumask_var_node(&pcpu->cpus_to_preempt_lazy, GFP_KERNEL, node) ||
!zalloc_cpumask_var_node(&pcpu->cpus_to_wait, GFP_KERNEL, node)) {
ret = -ENOMEM;
goto err_free_pcpu;
@@ -7335,6 +7350,7 @@ struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd,
free_cpumask_var(pcpu->cpus_to_kick);
free_cpumask_var(pcpu->cpus_to_kick_if_idle);
free_cpumask_var(pcpu->cpus_to_preempt);
+ free_cpumask_var(pcpu->cpus_to_preempt_lazy);
free_cpumask_var(pcpu->cpus_to_wait);
}
for_each_possible_cpu(cpu) {
@@ -8462,10 +8478,20 @@ static bool kick_one_cpu(s32 cpu, struct scx_sched_pcpu *pcpu, struct rq *this_r
const struct sched_class *cur_class;
bool should_wait = false;
bool kickable;
+ bool preempt, preempt_lazy, immediate;
unsigned long flags;
raw_spin_rq_lock_irqsave(rq, flags);
cur_class = rq->curr->sched_class;
+ preempt = cpumask_test_cpu(cpu, pcpu->cpus_to_preempt);
+ preempt_lazy = cpumask_test_cpu(cpu, pcpu->cpus_to_preempt_lazy);
+ /*
+ * A lazy-only request sits in cpus_to_preempt_lazy alone. Any other
+ * kick for the same cpu, plain or preempting, is in cpus_to_kick and
+ * asks for an immediate reschedule; the lazy request still clears the
+ * slice, the immediate one still reschedules now, so both are served.
+ */
+ immediate = !preempt_lazy || cpumask_test_cpu(cpu, pcpu->cpus_to_kick);
/*
* During CPU hotplug, a CPU may depend on kicking itself to make
@@ -8479,7 +8505,7 @@ static bool kick_one_cpu(s32 cpu, struct scx_sched_pcpu *pcpu, struct rq *this_r
!sched_class_above(cur_class, &ext_sched_class);
if (kickable && !scx_missing_caps(pcpu->sch, cpu, SCX_CAP_BASE)) {
- if (cpumask_test_cpu(cpu, pcpu->cpus_to_preempt)) {
+ if (preempt || preempt_lazy) {
if (cur_class == &ext_sched_class) {
u64 caps = scx_caps_for_preempt(pcpu->sch, rq, 0);
@@ -8488,7 +8514,6 @@ static bool kick_one_cpu(s32 cpu, struct scx_sched_pcpu *pcpu, struct rq *this_r
else if (unlikely(!scx_set_task_slice(rq->curr, 0)))
__scx_add_event(pcpu->sch, SCX_EV_SLICE_DENIED, 1);
}
- cpumask_clear_cpu(cpu, pcpu->cpus_to_preempt);
}
if (cpumask_test_cpu(cpu, pcpu->cpus_to_wait)) {
@@ -8500,14 +8525,20 @@ static bool kick_one_cpu(s32 cpu, struct scx_sched_pcpu *pcpu, struct rq *this_r
cpumask_clear_cpu(cpu, pcpu->cpus_to_wait);
}
- resched_curr(rq);
+ if (immediate)
+ resched_curr(rq);
+ else
+ resched_curr_lazy(rq);
} else {
/* a kickable cpu was skipped solely for the missing caps */
if (kickable)
__scx_add_event(pcpu->sch, SCX_EV_SUB_KICK_DENIED, 1);
- cpumask_clear_cpu(cpu, pcpu->cpus_to_preempt);
cpumask_clear_cpu(cpu, pcpu->cpus_to_wait);
}
+ if (preempt)
+ cpumask_clear_cpu(cpu, pcpu->cpus_to_preempt);
+ if (preempt_lazy)
+ cpumask_clear_cpu(cpu, pcpu->cpus_to_preempt_lazy);
scx_rq_lock_drop(rq);
raw_spin_rq_unlock_irqrestore(rq, flags);
@@ -8566,6 +8597,13 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work)
cpumask_clear_cpu(cpu, pcpu->cpus_to_kick_if_idle);
}
+ /*
+ * kick_one_cpu() clears the lazy bit of every cpu it visited
+ * above; what remains are lazy-only requests, see scx_kick_cpu().
+ */
+ for_each_cpu(cpu, pcpu->cpus_to_preempt_lazy)
+ kick_one_cpu(cpu, pcpu, this_rq, ksyncs);
+
for_each_cpu(cpu, pcpu->cpus_to_kick_if_idle) {
kick_one_cpu_if_idle(cpu, pcpu, this_rq);
cpumask_clear_cpu(cpu, pcpu->cpus_to_kick_if_idle);
@@ -8734,6 +8772,11 @@ static bool scx_vet_enq_flags(struct scx_sched *sch, u64 dsq_id, u64 *enq_flags)
return false;
}
+ if (unlikely((*enq_flags & SCX_ENQ_PREEMPT) && (*enq_flags & SCX_ENQ_PREEMPT_LAZY))) {
+ scx_error(sch, "SCX_ENQ_PREEMPT and SCX_ENQ_PREEMPT_LAZY cannot be combined");
+ return false;
+ }
+
if (*enq_flags & SCX_ENQ_IMMED) {
if (unlikely(!is_local)) {
scx_error(sch, "SCX_ENQ_IMMED on a non-local DSQ 0x%llx", dsq_id);
@@ -9539,6 +9582,24 @@ void scx_kick_cpu(struct scx_sched *sch, s32 cpu, u64 flags)
if (!scx_kf_allowed_ctx(sch))
return;
+ if (unlikely(flags & ~(SCX_KICK_IDLE | SCX_KICK_PREEMPT | SCX_KICK_WAIT |
+ SCX_KICK_PREEMPT_LAZY))) {
+ scx_error(sch, "invalid kick flags 0x%llx", flags);
+ return;
+ }
+ if (unlikely((flags & SCX_KICK_PREEMPT) && (flags & SCX_KICK_PREEMPT_LAZY))) {
+ scx_error(sch, "SCX_KICK_PREEMPT and SCX_KICK_PREEMPT_LAZY cannot be combined");
+ return;
+ }
+ if (unlikely((flags & SCX_KICK_PREEMPT_LAZY) && (flags & SCX_KICK_WAIT))) {
+ scx_error(sch, "SCX_KICK_PREEMPT_LAZY cannot be used with SCX_KICK_WAIT");
+ return;
+ }
+ if (unlikely((flags & SCX_KICK_IDLE) &&
+ (flags & (SCX_KICK_PREEMPT | SCX_KICK_PREEMPT_LAZY | SCX_KICK_WAIT)))) {
+ scx_error(sch, "PREEMPT/WAIT cannot be used with SCX_KICK_IDLE");
+ return;
+ }
local_irq_save(irq_flags);
@@ -9564,9 +9625,6 @@ void scx_kick_cpu(struct scx_sched *sch, s32 cpu, u64 flags)
if (flags & SCX_KICK_IDLE) {
struct rq *target_rq = cpu_rq(cpu);
- if (unlikely(flags & (SCX_KICK_PREEMPT | SCX_KICK_WAIT)))
- scx_error(sch, "PREEMPT/WAIT cannot be used with SCX_KICK_IDLE");
-
if (raw_spin_rq_trylock(target_rq)) {
if (can_skip_idle_kick(target_rq)) {
scx_rq_lock_drop(target_rq);
@@ -9577,11 +9635,25 @@ void scx_kick_cpu(struct scx_sched *sch, s32 cpu, u64 flags)
raw_spin_rq_unlock(target_rq);
}
cpumask_set_cpu(cpu, pcpu->cpus_to_kick_if_idle);
+ } else if (flags & SCX_KICK_PREEMPT_LAZY) {
+ /*
+ * Recorded in its own mask and not in cpus_to_kick, so that
+ * kick_one_cpu() can tell a lazy-only request from one that
+ * coincides with a plain or preempting kick. A queued immediate
+ * preemption already clears the slice and reschedules now,
+ * which is more than asked for; a plain kick doesn't touch the
+ * slice, so the lazy request must stay recorded next to it.
+ */
+ if (!cpumask_test_cpu(cpu, pcpu->cpus_to_preempt))
+ cpumask_set_cpu(cpu, pcpu->cpus_to_preempt_lazy);
} else {
cpumask_set_cpu(cpu, pcpu->cpus_to_kick);
- if (flags & SCX_KICK_PREEMPT)
+ if (flags & SCX_KICK_PREEMPT) {
+ /* an immediate preemption subsumes a queued lazy one */
cpumask_set_cpu(cpu, pcpu->cpus_to_preempt);
+ cpumask_clear_cpu(cpu, pcpu->cpus_to_preempt_lazy);
+ }
if (flags & SCX_KICK_WAIT)
cpumask_set_cpu(cpu, pcpu->cpus_to_wait);
}
@@ -9623,8 +9695,9 @@ __bpf_kfunc void scx_bpf_kick_cpu(s32 cpu, u64 flags, const struct bpf_prog_aux
* cid-addressed equivalent of scx_bpf_kick_cpu(). An invalid @cid aborts the
* scheduler via scx_cid_to_cpu(). Caps are enforced on the delivery path: a
* kick is dropped if the caller lacks baseline access on @cid, and a
- * %SCX_KICK_PREEMPT degrades to a plain reschedule if the caller lacks
- * %SCX_CAP_PREEMPT for a task outside its subtree.
+ * %SCX_KICK_PREEMPT or %SCX_KICK_PREEMPT_LAZY request degrades to a plain
+ * reschedule if the caller lacks %SCX_CAP_PREEMPT for a task outside its
+ * subtree.
*/
__bpf_kfunc void scx_bpf_kick_cid(s32 cid, u64 flags, const struct bpf_prog_aux *aux)
{
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index e1eb3a0d456cb..0b38f84353eb8 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -215,6 +215,17 @@ enum scx_ops_flags {
*/
SCX_OPS_TID_TO_TASK = 1LLU << 8,
+ /*
+ * If set, a slice that runs out at the tick requests lazy rescheduling
+ * instead of an immediate one, the way fair.c expires a slice from
+ * update_curr(): a task in user space still reschedules on the way
+ * back from the tick, a task in the kernel runs on to its next return
+ * to user space or to the next tick, which promotes the request. No
+ * effect on kernels without lazy preemption. Rescheduling while the
+ * scheduler is being disabled stays immediate.
+ */
+ SCX_OPS_LAZY_SLICE_EXPIRY = 1LLU << 9,
+
SCX_OPS_ALL_FLAGS = SCX_OPS_KEEP_BUILTIN_IDLE |
SCX_OPS_ENQ_LAST |
SCX_OPS_ENQ_EXITING |
@@ -223,7 +234,8 @@ enum scx_ops_flags {
SCX_OPS_SWITCH_PARTIAL |
SCX_OPS_BUILTIN_IDLE_PER_NODE |
SCX_OPS_ALWAYS_ENQ_IMMED |
- SCX_OPS_TID_TO_TASK,
+ SCX_OPS_TID_TO_TASK |
+ SCX_OPS_LAZY_SLICE_EXPIRY,
/* high 8 bits are internal, don't include in SCX_OPS_ALL_FLAGS */
__SCX_OPS_INTERNAL_MASK = 0xffLLU << 56,
@@ -1327,6 +1339,7 @@ struct scx_sched_pcpu {
cpumask_var_t cpus_to_kick;
cpumask_var_t cpus_to_kick_if_idle;
cpumask_var_t cpus_to_preempt;
+ cpumask_var_t cpus_to_preempt_lazy;
cpumask_var_t cpus_to_wait;
struct list_head to_kick_node;
@@ -1407,15 +1420,16 @@ struct scx_sched_pnode {
* the allocation pattern.
*
* ENQ_IMMED insert an IMMED task onto the cid's local DSQ
- * - kick the cid's cpu (except SCX_KICK_PREEMPT)
+ * - kick the cid's cpu (except SCX_KICK_PREEMPT and
+ * SCX_KICK_PREEMPT_LAZY)
*
* ENQ insert any task onto the cid's local DSQ (implies ENQ_IMMED)
*
* PREEMPT preempt any task running on the cid regardless of the owning
* sched (implies ENQ). Preempting a task in the sched's own subtree
* doesn't require any cap.
- * - SCX_ENQ_PREEMPT inserts
- * - SCX_KICK_PREEMPT kicks
+ * - SCX_ENQ_PREEMPT and SCX_ENQ_PREEMPT_LAZY inserts
+ * - SCX_KICK_PREEMPT and SCX_KICK_PREEMPT_LAZY kicks
*
* PERF control the cid's cpu power/perf management state, currently the
* cpufreq target set through scx_bpf_cidperf_set(). Hardware
@@ -1685,6 +1699,14 @@ enum scx_enq_flags {
*/
SCX_ENQ_PREEMPT = 1LLU << 32,
+ /*
+ * Like %SCX_ENQ_PREEMPT, but request lazy rescheduling. The current
+ * task's slice is still cleared immediately so that the next scheduling
+ * boundary observes the new ordering. Implies %SCX_ENQ_HEAD, which is
+ * all it means on a non-local DSQ, as with %SCX_ENQ_PREEMPT.
+ */
+ SCX_ENQ_PREEMPT_LAZY = 1LLU << 35,
+
/*
* Only allowed on local DSQs. Guarantees that the task either gets
* on the CPU immediately and stays on it, or gets reenqueued back
@@ -1811,6 +1833,12 @@ enum scx_kick_flags {
* is not on SCX.
*/
SCX_KICK_WAIT = 1LLU << 2,
+
+ /*
+ * Like %SCX_KICK_PREEMPT, but request lazy rescheduling. This cannot be
+ * combined with %SCX_KICK_WAIT.
+ */
+ SCX_KICK_PREEMPT_LAZY = 1LLU << 3,
};
enum scx_tg_flags {
diff --git a/kernel/sched/ext/sub.c b/kernel/sched/ext/sub.c
index 380a5653dc529..a88b87614b55e 100644
--- a/kernel/sched/ext/sub.c
+++ b/kernel/sched/ext/sub.c
@@ -686,9 +686,10 @@ void scx_rescue_init(struct rq *rq)
* rescue is enabled, or @rq's reject DSQ after recording the reenq reason on
* @p.
*
- * %SCX_ENQ_IMMED, %SCX_ENQ_PREEMPT and %SCX_ENQ_HEAD are cleared when diverting
- * to rescue or reject. %SCX_ENQ_PREEMPT is also cleared on a fallback
- * migration-disabled admission.
+ * %SCX_ENQ_IMMED, %SCX_ENQ_PREEMPT, %SCX_ENQ_PREEMPT_LAZY and %SCX_ENQ_HEAD
+ * are cleared when diverting to rescue or reject. %SCX_ENQ_PREEMPT and
+ * %SCX_ENQ_PREEMPT_LAZY are also cleared on a fallback migration-disabled
+ * admission.
*
* Bypass doesn't need special-casing as a bypassing sched's tasks are enqueued
* to and run by its nearest non-bypassing ancestor. If root is bypassing, it
@@ -709,7 +710,7 @@ struct scx_dispatch_q *scx_resolve_local_dsq(struct scx_sched *sch, struct rq *r
* On a remote activation the scheduling sched (@asch) differs from
* @p's owner (@sch). Check caps against the scheduling sched.
*/
- if (*enq_flags & SCX_ENQ_PREEMPT)
+ if (*enq_flags & (SCX_ENQ_PREEMPT | SCX_ENQ_PREEMPT_LAZY))
needed |= scx_caps_for_preempt(asch, rq, *enq_flags);
missing = scx_missing_caps(asch, cpu_of(rq), needed);
@@ -726,7 +727,7 @@ struct scx_dispatch_q *scx_resolve_local_dsq(struct scx_sched *sch, struct rq *r
if (unlikely(!scx_rq_online(rq) || is_migration_disabled(p) ||
p->migration_pending)) {
__scx_add_event(sch, SCX_EV_SUB_FORCED_ADMIT, 1);
- *enq_flags &= ~SCX_ENQ_PREEMPT;
+ *enq_flags &= ~(SCX_ENQ_PREEMPT | SCX_ENQ_PREEMPT_LAZY);
return &rq->scx.local_dsq;
}
@@ -735,8 +736,8 @@ struct scx_dispatch_q *scx_resolve_local_dsq(struct scx_sched *sch, struct rq *r
* or HEAD - a diversion has no priority and IMMED is not allowed on
* non-local DSQs. Strip the enq and task flags along with the slice.
*/
- *enq_flags &= ~(SCX_ENQ_IMMED | SCX_ENQ_PREEMPT | SCX_ENQ_HEAD |
- SCX_ENQ_APPLY_SLICE | SCX_ENQ_SLICE_DFL);
+ *enq_flags &= ~(SCX_ENQ_IMMED | SCX_ENQ_PREEMPT | SCX_ENQ_PREEMPT_LAZY |
+ SCX_ENQ_HEAD | SCX_ENQ_APPLY_SLICE | SCX_ENQ_SLICE_DFL);
p->scx.flags &= ~SCX_TASK_IMMED;
/* the enqueuer opted for rescue instead of rejection and reenqueue */
diff --git a/tools/sched_ext/include/scx/enum_defs.autogen.h b/tools/sched_ext/include/scx/enum_defs.autogen.h
index 63b6b14b19bd4..f1554c22071ff 100644
--- a/tools/sched_ext/include/scx/enum_defs.autogen.h
+++ b/tools/sched_ext/include/scx/enum_defs.autogen.h
@@ -85,6 +85,7 @@
#define HAVE_SCX_ENQ_HEAD
#define HAVE_SCX_ENQ_CPU_SELECTED
#define HAVE_SCX_ENQ_PREEMPT
+#define HAVE_SCX_ENQ_PREEMPT_LAZY
#define HAVE_SCX_ENQ_IMMED
#define HAVE_SCX_ENQ_RESCUE
#define HAVE_SCX_ENQ_REENQ
@@ -148,6 +149,7 @@
#define HAVE_SCX_KF_ALLOW_SELECT_CPU
#define HAVE_SCX_KICK_IDLE
#define HAVE_SCX_KICK_PREEMPT
+#define HAVE_SCX_KICK_PREEMPT_LAZY
#define HAVE_SCX_KICK_WAIT
#define HAVE_SCX_OPI_BEGIN
#define HAVE_SCX_OPI_NORMAL_BEGIN
@@ -164,6 +166,7 @@
#define HAVE_SCX_OPS_BUILTIN_IDLE_PER_NODE
#define HAVE_SCX_OPS_ALWAYS_ENQ_IMMED
#define HAVE_SCX_OPS_TID_TO_TASK
+#define HAVE_SCX_OPS_LAZY_SLICE_EXPIRY
#define HAVE_SCX_OPS_ALL_FLAGS
#define HAVE___SCX_OPS_INTERNAL_MASK
#define HAVE_SCX_OPS_HAS_CPU_PREEMPT
diff --git a/tools/sched_ext/include/scx/enums.autogen.bpf.h b/tools/sched_ext/include/scx/enums.autogen.bpf.h
index 7268131010de3..2cec24beb2d80 100644
--- a/tools/sched_ext/include/scx/enums.autogen.bpf.h
+++ b/tools/sched_ext/include/scx/enums.autogen.bpf.h
@@ -109,6 +109,9 @@ const volatile u64 __SCX_KICK_IDLE __weak;
const volatile u64 __SCX_KICK_PREEMPT __weak;
#define SCX_KICK_PREEMPT __SCX_KICK_PREEMPT
+const volatile u64 __SCX_KICK_PREEMPT_LAZY __weak;
+#define SCX_KICK_PREEMPT_LAZY __SCX_KICK_PREEMPT_LAZY
+
const volatile u64 __SCX_KICK_WAIT __weak;
#define SCX_KICK_WAIT __SCX_KICK_WAIT
@@ -121,6 +124,9 @@ const volatile u64 __SCX_ENQ_HEAD __weak;
const volatile u64 __SCX_ENQ_PREEMPT __weak;
#define SCX_ENQ_PREEMPT __SCX_ENQ_PREEMPT
+const volatile u64 __SCX_ENQ_PREEMPT_LAZY __weak;
+#define SCX_ENQ_PREEMPT_LAZY __SCX_ENQ_PREEMPT_LAZY
+
const volatile u64 __SCX_ENQ_IMMED __weak;
#define SCX_ENQ_IMMED __SCX_ENQ_IMMED
diff --git a/tools/sched_ext/include/scx/enums.autogen.h b/tools/sched_ext/include/scx/enums.autogen.h
index e616326545172..dd762ee0ddb36 100644
--- a/tools/sched_ext/include/scx/enums.autogen.h
+++ b/tools/sched_ext/include/scx/enums.autogen.h
@@ -40,10 +40,12 @@
SCX_ENUM_SET(skel, scx_ent_dsq_flags, SCX_TASK_DSQ_ON_PRIQ); \
SCX_ENUM_SET(skel, scx_kick_flags, SCX_KICK_IDLE); \
SCX_ENUM_SET(skel, scx_kick_flags, SCX_KICK_PREEMPT); \
+ SCX_ENUM_SET(skel, scx_kick_flags, SCX_KICK_PREEMPT_LAZY); \
SCX_ENUM_SET(skel, scx_kick_flags, SCX_KICK_WAIT); \
SCX_ENUM_SET(skel, scx_enq_flags, SCX_ENQ_WAKEUP); \
SCX_ENUM_SET(skel, scx_enq_flags, SCX_ENQ_HEAD); \
SCX_ENUM_SET(skel, scx_enq_flags, SCX_ENQ_PREEMPT); \
+ SCX_ENUM_SET(skel, scx_enq_flags, SCX_ENQ_PREEMPT_LAZY); \
SCX_ENUM_SET(skel, scx_enq_flags, SCX_ENQ_IMMED); \
SCX_ENUM_SET(skel, scx_enq_flags, SCX_ENQ_RESCUE); \
SCX_ENUM_SET(skel, scx_enq_flags, SCX_ENQ_REENQ); \
diff --git a/tools/sched_ext/include/scx/enums_abi.autogen.h b/tools/sched_ext/include/scx/enums_abi.autogen.h
index d53899764f5ac..672c02a497d14 100644
--- a/tools/sched_ext/include/scx/enums_abi.autogen.h
+++ b/tools/sched_ext/include/scx/enums_abi.autogen.h
@@ -97,6 +97,7 @@ static const struct __scx_enum_abi_val __scx_enum_abi_vals[]
{ "scx_enq_flags", "SCX_ENQ_HEAD", 0x10000LLU },
{ "scx_enq_flags", "SCX_ENQ_CPU_SELECTED", 0x100000LLU },
{ "scx_enq_flags", "SCX_ENQ_PREEMPT", 0x100000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_PREEMPT_LAZY", 0x800000000LLU },
{ "scx_enq_flags", "SCX_ENQ_IMMED", 0x200000000LLU },
{ "scx_enq_flags", "SCX_ENQ_RESCUE", 0x400000000LLU },
{ "scx_enq_flags", "SCX_ENQ_REENQ", 0x10000000000LLU },
@@ -160,6 +161,7 @@ static const struct __scx_enum_abi_val __scx_enum_abi_vals[]
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_SELECT_CPU", 0x20LLU },
{ "scx_kick_flags", "SCX_KICK_IDLE", 0x1LLU },
{ "scx_kick_flags", "SCX_KICK_PREEMPT", 0x2LLU },
+ { "scx_kick_flags", "SCX_KICK_PREEMPT_LAZY", 0x8LLU },
{ "scx_kick_flags", "SCX_KICK_WAIT", 0x4LLU },
{ "scx_opi", "SCX_OPI_BEGIN", 0x0LLU },
{ "scx_opi", "SCX_OPI_NORMAL_BEGIN", 0x0LLU },
@@ -176,7 +178,8 @@ static const struct __scx_enum_abi_val __scx_enum_abi_vals[]
{ "scx_ops_flags", "SCX_OPS_BUILTIN_IDLE_PER_NODE", 0x40LLU },
{ "scx_ops_flags", "SCX_OPS_ALWAYS_ENQ_IMMED", 0x80LLU },
{ "scx_ops_flags", "SCX_OPS_TID_TO_TASK", 0x100LLU },
- { "scx_ops_flags", "SCX_OPS_ALL_FLAGS", 0x1ffLLU },
+ { "scx_ops_flags", "SCX_OPS_LAZY_SLICE_EXPIRY", 0x200LLU },
+ { "scx_ops_flags", "SCX_OPS_ALL_FLAGS", 0x3ffLLU },
{ "scx_ops_flags", "__SCX_OPS_INTERNAL_MASK", 0xff00000000000000LLU },
{ "scx_ops_flags", "SCX_OPS_HAS_CPU_PREEMPT", 0x100000000000000LLU },
{ "scx_ops_state", "SCX_OPSS_NONE", 0x0LLU },
--
2.55.0
next prev parent reply other threads:[~2026-09-11 19:58 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-11 19:56 [PATCHSET sched_ext/for-7.4] " Andrea Righi
2026-09-11 19:56 ` Andrea Righi [this message]
2026-09-13 16:50 ` [PATCH 1/2] " Tejun Heo
2026-09-14 6:05 ` Andrea Righi
2026-09-11 19:56 ` [PATCH 2/2] selftests/sched_ext: Add kick selftest Andrea Righi
2026-09-14 8:47 [PATCHSET v2 sched_ext/for-7.4] sched_ext: Add lazy preemption support Andrea Righi
2026-09-14 8:47 ` [PATCH 1/2] " Andrea Righi
2026-09-14 14:44 [PATCHSET v3 sched_ext/for-7.4] " Andrea Righi
2026-09-14 14:44 ` [PATCH 1/2] " Andrea Righi
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=20260911195800.974364-2-arighi@nvidia.com \
--to=arighi@nvidia.com \
--cc=changwoo@igalia.com \
--cc=etsal@meta.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
all inboxes | Powered by JetHome®