FAILED: patch "[PATCH] sched_ext: Wait for SCX_OPSS_DISPATCHING before reenqueueing" failed to apply to 7.2-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 7.2-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-7.2.y
git checkout FETCH_HEAD
git cherry-pick -x 7de9a6fb44eae4f05e68c805d58b9c618815adfa
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026092920-protector-confused-2f3e@gregkh' --subject-prefix 'PATCH 7.2.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 7de9a6fb44eae4f05e68c805d58b9c618815adfa Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Tue, 15 Sep 2026 12:34:44 -1000
Subject: [PATCH] sched_ext: Wait for SCX_OPSS_DISPATCHING before reenqueueing
a task
ebf1ccff79c4 ("sched_ext: Fix ops.dequeue() semantics") moved the final ops_state store in scx_dispatch_enqueue() after the DSQ unlock so that the custody update and ops.dequeue() precede it. A task can thus be found on a DSQ while still SCX_OPSS_DISPATCHING.
The dequeue and core-sched pick paths wait for the state to clear in ops_dequeue() but the reenqueue paths don't. A reenqueue in that window runs ops.enqueue() and sets SCX_OPSS_QUEUED before the dispatch has completed. The dispatcher's final store then overwrites it with SCX_OPSS_NONE and finish_dispatch() drops every later dispatch of the task.
Wait for SCX_OPSS_DISPATCHING to clear before dequeueing a task for reenqueue, the same way ops_dequeue() does.
kernel/sched/ext/ext.c +13 -0
@@ -4404,6 +4404,17 @@ static bool local_task_should_reenq(struct rq *rq, struct task_struct *p,return *reenq_flags &SCX_REENQ_ANY ; } +/* +* The dispatcher stores the final ops_state after dropping the DSQ lock, so @p +* can be found on a DSQ while still %SCX_OPSS_DISPATCHING. Reenqueueing @p +* before that store lands would have it clobber the new %SCX_OPSS_QUEUED. +*/ +void scx_reenq_wait_dispatching (struct task_struct *p) +{ +if (unlikely (atomic_long_read_acquire (&p->scx.ops_state) ==SCX_OPSS_DISPATCHING )) +wait_ops_state (p,SCX_OPSS_DISPATCHING ); +} +static u32 reenq_local (struct scx_sched *sch,struct rq *rq,u64 reenq_flags) {LIST_HEAD (tasks);
@@ -4447,6 +4458,7 @@ static u32 reenq_local(struct scx_sched *sch, struct rq *rq, u64 reenq_flags)if (!local_task_should_reenq (rq, p, &reenq_flags, &reason))continue ; +scx_reenq_wait_dispatching (p);scx_dispatch_dequeue (rq, p);if (WARN_ON_ONCE (p->scx.flags &SCX_TASK_REENQ_REASON_MASK ))
@@ -4570,6 +4582,7 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag }/* @p is on @dsq, its rq and @dsq are locked */ +scx_reenq_wait_dispatching (p);dispatch_dequeue_locked (p, dsq);raw_spin_unlock (&dsq->lock);
kernel/sched/ext/internal.h +1 -0
@@ -2078,6 +2078,7 @@ void scx_kick_cpu(struct scx_sched *sch, s32 cpu, u64 flags);u64 __scx_bpf_now (struct rq *rq);void schedule_dsq_reenq (struct scx_sched *sch,struct scx_dispatch_q *dsq,u64 reenq_flags,struct rq *locked_rq); +void scx_reenq_wait_dispatching (struct task_struct *p);int __scx_init_task (struct scx_sched *sch,struct task_struct *p,struct cgroup *cgrp,bool fork);void scx_enable_task (struct scx_sched *sch,struct task_struct *p);
kernel/sched/ext/sub.c +1 -0
@@ -801,6 +801,7 @@ void scx_reenq_reject(struct rq *rq)if (WARN_ON_ONCE (p->migration_pending))continue ; +scx_reenq_wait_dispatching (p);scx_dispatch_dequeue (rq, p);if (WARN_ON_ONCE (p->scx.flags &SCX_TASK_REENQ_REASON_MASK ))
Sasha Levin
maintainerPATCH 1/2 sched_ext: Generalize local-DSQ handling to rq-owned DSQs
From: Tejun Heo <[email protected]>
[ Upstream commit bf6cf1886a56eaf929d8193a3f4b30be59a778e2 ]
The local DSQ is synchronized by the containing rq lock rather than its own dsq->lock. A later patch adds a second such DSQ. In preparation, factor the "rq owns the lock" test into dsq_is_rq_owned() and rename local_dsq_post_enq() to rq_owned_post_enq(), taking @rq explicitly.
No behavior change.
Stable-dep-of: 7de9a6fb44ea ("sched_ext: Wait for SCX_OPSS_DISPATCHING before reenqueueing a task")
--- kernel/sched/ext/ext.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-)
kernel/sched/ext/ext.c +22 -12
@@ -95,6 +95,17 @@ static atomic_long_t scx_hotplug_seq = ATOMIC_LONG_INIT(0);/* Global cursor for the per-CPU tid allocator. Starts at 1; tid 0 is reserved. */ static atomic64_t scx_tid_cursor =ATOMIC64_INIT (1 ); +/* is @dsq synchronized by the containing rq lock instead of dsq->lock? */ +static bool dsq_is_rq_owned (struct scx_dispatch_q *dsq) +{ +switch (dsq->id) { +case SCX_DSQ_LOCAL : +return true; +default : +return false; + } +} +#ifdef CONFIG_EXT_SUB_SCHED /* * The sub sched being enabled. Used by scx_disable_and_exit_task() to exit
@@ -1466,11 +1477,10 @@ static void call_task_dequeue(struct scx_sched *sch, struct rq *rq, p->scx.flags &= ~SCX_TASK_IN_CUSTODY ; } -static void local_dsq_post_enq (struct scx_sched *sch,struct scx_dispatch_q *dsq, -struct task_struct *p,u64 enq_flags) +static void rq_owned_post_enq (struct scx_sched *sch,struct rq *rq, +struct scx_dispatch_q *dsq,struct task_struct *p, +u64 enq_flags) { - struct rq *rq = container_of(dsq, struct rq, scx.local_dsq); -call_task_dequeue (sch, rq, p,0 );/*
@@ -1530,13 +1540,13 @@ static void dispatch_enqueue(struct scx_sched *sch, struct rq *rq, struct scx_dispatch_q *dsq, struct task_struct *p, u64 enq_flags) { - bool is_local = dsq->id == SCX_DSQ_LOCAL; + bool is_rq_owned = dsq_is_rq_owned(dsq);WARN_ON_ONCE (p->scx.dsq || !list_empty (&p->scx.dsq_list.node));WARN_ON_ONCE ((p->scx.dsq_flags &SCX_TASK_DSQ_ON_PRIQ ) || !RB_EMPTY_NODE (&p->scx.dsq_priq)); -if (!is_local) { +if (!is_rq_owned) {raw_spin_lock_nested (&dsq->lock, (enq_flags &SCX_ENQ_NESTED ) ?SINGLE_DEPTH_NESTING :0 );
@@ -1631,8 +1641,8 @@ static void dispatch_enqueue(struct scx_sched *sch, struct rq *rq,* ops_state first, both sides would modify p->scx.flags * concurrently in a non-atomic way. */ -if (is_local) { -local_dsq_post_enq (sch, dsq, p, enq_flags); +if (is_rq_owned) { +rq_owned_post_enq (sch, rq, dsq, p, enq_flags); }else {/* * Task on global/bypass DSQ: leave custody, task on
@@ -1679,7 +1689,7 @@ static void task_unlink_from_dsq(struct task_struct *p,static void dispatch_dequeue (struct rq *rq,struct task_struct *p) {struct scx_dispatch_q *dsq = p->scx.dsq; -bool is_local = dsq == &rq->scx.local_dsq; +bool is_rq_owned = dsq &&dsq_is_rq_owned (dsq);lockdep_assert_rq_held (rq);
@@ -1703,7 +1713,7 @@ static void dispatch_dequeue(struct rq *rq, struct task_struct *p)return ; } -if (!is_local) +if (!is_rq_owned)raw_spin_lock (&dsq->lock);/*
@@ -1725,7 +1735,7 @@ static void dispatch_dequeue(struct rq *rq, struct task_struct *p) } p->scx.dsq =NULL ; -if (!is_local) +if (!is_rq_owned)raw_spin_unlock (&dsq->lock); }
@@ -2277,7 +2287,7 @@ static void move_local_task_to_local_dsq(struct scx_sched *sch,dsq_inc_nr (dst_dsq, p, enq_flags); p->scx.dsq = dst_dsq; -local_dsq_post_enq (sch, dst_dsq, p, enq_flags); +rq_owned_post_enq (sch, dst_rq, dst_dsq, p, enq_flags); }/**
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 sched_ext: Wait for SCX_OPSS_DISPATCHING before reenqueueing a task
From: Tejun Heo <[email protected]>
[ Upstream commit 7de9a6fb44eae4f05e68c805d58b9c618815adfa ]
ebf1ccff79c4 ("sched_ext: Fix ops.dequeue() semantics") moved the final ops_state store in scx_dispatch_enqueue() after the DSQ unlock so that the custody update and ops.dequeue() precede it. A task can thus be found on a DSQ while still SCX_OPSS_DISPATCHING.
The dequeue and core-sched pick paths wait for the state to clear in ops_dequeue() but the reenqueue paths don't. A reenqueue in that window runs ops.enqueue() and sets SCX_OPSS_QUEUED before the dispatch has completed. The dispatcher's final store then overwrites it with SCX_OPSS_NONE and finish_dispatch() drops every later dispatch of the task.
Wait for SCX_OPSS_DISPATCHING to clear before dequeueing a task for reenqueue, the same way ops_dequeue() does.
--- kernel/sched/ext/ext.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
kernel/sched/ext/ext.c +10 -0
@@ -4295,6 +4295,14 @@ static u32 reenq_local(struct scx_sched *sch, struct rq *rq, u64 reenq_flags)if (!local_task_should_reenq (p, &reenq_flags, &reason))continue ; +/* +* The dispatcher stores the final ops_state after dropping the DSQ +* lock, so @p can be found on a DSQ while still +* %SCX_OPSS_DISPATCHING. Reenqueueing @p before that store lands +* would have it clobber the new %SCX_OPSS_QUEUED. +*/ +if (unlikely (atomic_long_read_acquire (&p->scx.ops_state) ==SCX_OPSS_DISPATCHING )) +wait_ops_state (p,SCX_OPSS_DISPATCHING );dispatch_dequeue (rq, p);if (WARN_ON_ONCE (p->scx.flags &SCX_TASK_REENQ_REASON_MASK ))
@@ -4417,6 +4425,8 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag }/* @p is on @dsq, its rq and @dsq are locked */ +if (unlikely (atomic_long_read_acquire (&p->scx.ops_state) ==SCX_OPSS_DISPATCHING )) +wait_ops_state (p,SCX_OPSS_DISPATCHING );dispatch_dequeue_locked (p, dsq);raw_spin_unlock (&dsq->lock);
Signature · 1 line
2.53.0