return atomic_try_cmpxchg(&scx_ops_enable_state_var, &from_v, to);
}
-static bool scx_ops_bypassing(void)
+static bool scx_rq_bypassing(struct rq *rq)
{
- return unlikely(atomic_read(&scx_ops_bypass_depth));
+ return unlikely(rq->scx.flags & SCX_RQ_BYPASSING);
}
/**
if (!scx_rq_online(rq))
goto local;
- if (scx_ops_bypassing())
+ if (scx_rq_bypassing(rq))
goto global;
if (p->scx.ddsp_dsq_id != SCX_DSQ_INVALID)
* bypassing test.
*/
if ((prev->scx.flags & SCX_TASK_QUEUED) &&
- prev->scx.slice && !scx_ops_bypassing()) {
+ prev->scx.slice && !scx_rq_bypassing(rq)) {
rq->scx.flags |= SCX_RQ_BAL_KEEP;
goto has_tasks;
}
if (consume_dispatch_q(rq, &scx_dsq_global))
goto has_tasks;
- if (!SCX_HAS_OP(dispatch) || scx_ops_bypassing() || !scx_rq_online(rq))
+ if (!SCX_HAS_OP(dispatch) || scx_rq_bypassing(rq) || !scx_rq_online(rq))
goto no_tasks;
dspc->rq = rq;
* %SCX_OPS_ENQ_LAST is in effect.
*/
if ((prev->scx.flags & SCX_TASK_QUEUED) &&
- (!static_branch_unlikely(&scx_ops_enq_last) || scx_ops_bypassing())) {
+ (!static_branch_unlikely(&scx_ops_enq_last) ||
+ scx_rq_bypassing(rq))) {
rq->scx.flags |= SCX_RQ_BAL_KEEP;
goto has_tasks;
}
* forcing a different task. Leave it at the head of the local
* DSQ.
*/
- if (p->scx.slice && !scx_ops_bypassing()) {
+ if (p->scx.slice && !scx_rq_bypassing(rq)) {
dispatch_enqueue(&rq->scx.local_dsq, p, SCX_ENQ_HEAD);
return;
}
return NULL;
if (unlikely(!p->scx.slice)) {
- if (!scx_ops_bypassing() && !scx_warned_zero_slice) {
+ if (!scx_rq_bypassing(rq) && !scx_warned_zero_slice) {
printk_deferred(KERN_WARNING "sched_ext: %s[%d] has zero slice in pick_next_task_scx()\n",
p->comm, p->pid);
scx_warned_zero_slice = true;
* calling ops.core_sched_before(). Accesses are controlled by the
* verifier.
*/
- if (SCX_HAS_OP(core_sched_before) && !scx_ops_bypassing())
+ if (SCX_HAS_OP(core_sched_before) && !scx_rq_bypassing(task_rq(a)))
return SCX_CALL_OP_2TASKS_RET(SCX_KF_REST, core_sched_before,
(struct task_struct *)a,
(struct task_struct *)b);
* While disabling, always resched and refresh core-sched timestamp as
* we can't trust the slice management or ops.core_sched_before().
*/
- if (scx_ops_bypassing()) {
+ if (scx_rq_bypassing(rq)) {
curr->scx.slice = 0;
touch_core_sched(rq, curr);
} else if (SCX_HAS_OP(tick)) {
{
struct task_struct *p = rq->curr;
- if (scx_ops_bypassing())
+ if (scx_rq_bypassing(rq))
return false;
if (p->sched_class != &ext_sched_class)
return;
}
- /*
- * We need to guarantee that no tasks are on the BPF scheduler while
- * bypassing. Either we see enabled or the enable path sees the
- * increased bypass_depth before moving tasks to SCX.
- */
- if (!scx_enabled())
- return;
-
/*
* No task property is changing. We just need to make sure all currently
- * queued tasks are re-queued according to the new scx_ops_bypassing()
+ * queued tasks are re-queued according to the new scx_rq_bypassing()
* state. As an optimization, walk each rq's runnable_list instead of
* the scx_tasks list.
*
rq_lock_irqsave(rq, &rf);
+ if (bypass) {
+ WARN_ON_ONCE(rq->scx.flags & SCX_RQ_BYPASSING);
+ rq->scx.flags |= SCX_RQ_BYPASSING;
+ } else {
+ WARN_ON_ONCE(!(rq->scx.flags & SCX_RQ_BYPASSING));
+ rq->scx.flags &= ~SCX_RQ_BYPASSING;
+ }
+
+ /*
+ * We need to guarantee that no tasks are on the BPF scheduler
+ * while bypassing. Either we see enabled or the enable path
+ * sees scx_rq_bypassing() before moving tasks to SCX.
+ */
+ if (!scx_enabled()) {
+ rq_unlock_irqrestore(rq, &rf);
+ continue;
+ }
+
/*
* The use of list_for_each_entry_safe_reverse() is required
* because each task is going to be removed from and added back
if (!ops_cpu_valid(cpu, NULL))
return;
+ local_irq_save(irq_flags);
+
+ this_rq = this_rq();
+
/*
* While bypassing for PM ops, IRQ handling may not be online which can
* lead to irq_work_queue() malfunction such as infinite busy wait for
* IRQ status update. Suppress kicking.
*/
- if (scx_ops_bypassing())
- return;
-
- local_irq_save(irq_flags);
-
- this_rq = this_rq();
+ if (scx_rq_bypassing(this_rq))
+ goto out;
/*
* Actual kicking is bounced to kick_cpus_irq_workfn() to avoid nesting