Merge branch 'for-7.0-fixes' into for-7.1

Conflict in kernel/sched/ext.c between:

  7e0ffb72de ("sched_ext: Fix stale direct dispatch state in
  ddsp_dsq_id")

which clears ddsp state at individual call sites instead of
dispatch_enqueue(), and sub-sched related code reorg and API updates on
for-7.1. Resolved by applying the ddsp fix with for-7.1's signatures.

Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
Tejun Heo 2026-04-03 07:48:28 -10:00
commit 744ab12a5b
2 changed files with 54 additions and 26 deletions

View File

@ -1589,15 +1589,6 @@ static void dispatch_enqueue(struct scx_sched *sch, struct rq *rq,
dsq_inc_nr(dsq, p, enq_flags);
p->scx.dsq = dsq;
/*
* scx.ddsp_dsq_id and scx.ddsp_enq_flags are only relevant on the
* direct dispatch path, but we clear them here because the direct
* dispatch verdict may be overridden on the enqueue path during e.g.
* bypass.
*/
p->scx.ddsp_dsq_id = SCX_DSQ_INVALID;
p->scx.ddsp_enq_flags = 0;
/*
* Update custody and call ops.dequeue() before clearing ops_state:
* once ops_state is cleared, waiters in ops_dequeue() can proceed
@ -1779,12 +1770,34 @@ static void mark_direct_dispatch(struct scx_sched *sch,
p->scx.ddsp_enq_flags = enq_flags;
}
/*
* Clear @p direct dispatch state when leaving the scheduler.
*
* Direct dispatch state must be cleared in the following cases:
* - direct_dispatch(): cleared on the synchronous enqueue path, deferred
* dispatch keeps the state until consumed
* - process_ddsp_deferred_locals(): cleared after consuming deferred state,
* - do_enqueue_task(): cleared on enqueue fallbacks where the dispatch
* verdict is ignored (local/global/bypass)
* - dequeue_task_scx(): cleared after dispatch_dequeue(), covering deferred
* cancellation and holding_cpu races
* - scx_disable_task(): cleared for queued wakeup tasks, which are excluded by
* the scx_bypass() loop, so that stale state is not reused by a subsequent
* scheduler instance
*/
static inline void clear_direct_dispatch(struct task_struct *p)
{
p->scx.ddsp_dsq_id = SCX_DSQ_INVALID;
p->scx.ddsp_enq_flags = 0;
}
static void direct_dispatch(struct scx_sched *sch, struct task_struct *p,
u64 enq_flags)
{
struct rq *rq = task_rq(p);
struct scx_dispatch_q *dsq =
find_dsq_for_dispatch(sch, rq, p->scx.ddsp_dsq_id, task_cpu(p));
u64 ddsp_enq_flags;
touch_core_sched_dispatch(rq, p);
@ -1825,8 +1838,10 @@ static void direct_dispatch(struct scx_sched *sch, struct task_struct *p,
return;
}
dispatch_enqueue(sch, rq, dsq, p,
p->scx.ddsp_enq_flags | SCX_ENQ_CLEAR_OPSS);
ddsp_enq_flags = p->scx.ddsp_enq_flags;
clear_direct_dispatch(p);
dispatch_enqueue(sch, rq, dsq, p, ddsp_enq_flags | SCX_ENQ_CLEAR_OPSS);
}
static bool scx_rq_online(struct rq *rq)
@ -1949,6 +1964,7 @@ static void do_enqueue_task(struct rq *rq, struct task_struct *p, u64 enq_flags,
*/
touch_core_sched(rq, p);
refill_task_slice_dfl(sch, p);
clear_direct_dispatch(p);
dispatch_enqueue(sch, rq, dsq, p, enq_flags);
}
@ -2142,6 +2158,7 @@ static bool dequeue_task_scx(struct rq *rq, struct task_struct *p, int core_deq_
sub_nr_running(rq, 1);
dispatch_dequeue(rq, p);
clear_direct_dispatch(p);
return true;
}
@ -3664,6 +3681,8 @@ static void scx_disable_task(struct scx_sched *sch, struct task_struct *p)
lockdep_assert_rq_held(rq);
WARN_ON_ONCE(scx_get_task_state(p) != SCX_TASK_ENABLED);
clear_direct_dispatch(p);
if (SCX_HAS_OP(sch, disable))
SCX_CALL_OP_TASK(sch, SCX_KF_REST, disable, rq, p);
scx_set_task_state(p, SCX_TASK_READY);
@ -3948,13 +3967,15 @@ static void process_ddsp_deferred_locals(struct rq *rq)
struct task_struct, scx.dsq_list.node))) {
struct scx_sched *sch = scx_task_sched(p);
struct scx_dispatch_q *dsq;
u64 dsq_id = p->scx.ddsp_dsq_id;
u64 enq_flags = p->scx.ddsp_enq_flags;
list_del_init(&p->scx.dsq_list.node);
clear_direct_dispatch(p);
dsq = find_dsq_for_dispatch(sch, rq, p->scx.ddsp_dsq_id, task_cpu(p));
dsq = find_dsq_for_dispatch(sch, rq, dsq_id, task_cpu(p));
if (!WARN_ON_ONCE(dsq->id != SCX_DSQ_LOCAL))
dispatch_to_local_dsq(sch, rq, dsq, p,
p->scx.ddsp_enq_flags);
dispatch_to_local_dsq(sch, rq, dsq, p, enq_flags);
}
}

View File

@ -881,25 +881,32 @@ static bool check_builtin_idle_enabled(struct scx_sched *sch)
* code.
*
* We can't simply check whether @p->migration_disabled is set in a
* sched_ext callback, because migration is always disabled for the current
* task while running BPF code.
* sched_ext callback, because the BPF prolog (__bpf_prog_enter) may disable
* migration for the current task while running BPF code.
*
* The prolog (__bpf_prog_enter) and epilog (__bpf_prog_exit) respectively
* disable and re-enable migration. For this reason, the current task
* inside a sched_ext callback is always a migration-disabled task.
* Since the BPF prolog calls migrate_disable() only when CONFIG_PREEMPT_RCU
* is enabled (via rcu_read_lock_dont_migrate()), migration_disabled == 1 for
* the current task is ambiguous only in that case: it could be from the BPF
* prolog rather than a real migrate_disable() call.
*
* Therefore, when @p->migration_disabled == 1, check whether @p is the
* current task or not: if it is, then migration was not disabled before
* entering the callback, otherwise migration was disabled.
* Without CONFIG_PREEMPT_RCU, the BPF prolog never calls migrate_disable(),
* so migration_disabled == 1 always means the task is truly
* migration-disabled.
*
* Therefore, when migration_disabled == 1 and CONFIG_PREEMPT_RCU is enabled,
* check whether @p is the current task or not: if it is, then migration was
* not disabled before entering the callback, otherwise migration was disabled.
*
* Returns true if @p is migration-disabled, false otherwise.
*/
static bool is_bpf_migration_disabled(const struct task_struct *p)
{
if (p->migration_disabled == 1)
return p != current;
else
return p->migration_disabled;
if (p->migration_disabled == 1) {
if (IS_ENABLED(CONFIG_PREEMPT_RCU))
return p != current;
return true;
}
return p->migration_disabled;
}
static s32 select_cpu_from_kfunc(struct scx_sched *sch, struct task_struct *p,