From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 666834EB864; Tue, 13 Oct 2026 11:59:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791892741; cv=none; b=Lf4VLzzIyJG+kqIeQpROPLOWUVdaUgVaKKfztdEkGzKfzYdXh06UbgVGnR5A12N7qzwbi5sF9RHBEhCdI9kqSR2yhh2swEvd5/bAPiuLVxwU2NP73zlwPFj1FnpUMwaVXduA/oUuS46VhvvDf8jpoif5a0cZatrr5ILDY9Eq42o= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791892741; c=relaxed/simple; bh=mP85N7K7gKAKy8y1Xjle+O2wxffyzJ1yBeFnSPaDL9M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cGQPCMl15I0l3dy3aj68c4mk5GiSllN/oTFyZNcjRmAB9XsxqjQjtGc5+gCh1jgSQE/7OnYYlB7tv/zFxr4zDMcj0Wc0yi96GvAJvM4vz4UCYkcEya6Xr/6+0wONufmlx3ljSqUAgGiWfHPXHM2Vf421aqW00hUf2QG/T6LyPA8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=eTLSNGpJ; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="eTLSNGpJ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6CF001F00893; Tue, 13 Oct 2026 11:58:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1791892740; bh=QgdzzNuzTsdKMLwvJsIJk46wR5tsYVyWpdnhuYZLRUQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=eTLSNGpJzpCETI1OVGWgttu7r7SHX6qWjbTtiKZsvuzmRjTB1ZU5lBmMmvGxt2YXI kLLrolYbLuaISuEQTFELQP8gQq/iOu2VTYFU0aEpOJKzMEJYAaNNXJjH6ANHELwoxu G5EW0FQyFjLmiPokybx1b6Ko6TV6Xb+TiMRc+UJc= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Kuba Piecuch , Tejun Heo Subject: [PATCH 7.2 275/281] sched_ext: Generate qseq from a per-task counter Date: Tue, 13 Oct 2026 13:39:51 +0200 Message-ID: <20261013112901.810925923@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261013112855.045212108@linuxfoundation.org> References: <20261013112855.045212108@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 7.2-stable review patch. If anyone has any objections, please let me know. ------------------ From: Kuba Piecuch commit e6ac89b8b1c12e9104df45a14a26e2dfa96ded06 upstream. finish_dispatch() uses the qseq embedded in p->scx.ops_state to tell whether the QUEUED instance of a task it's about to claim is the one scx_bpf_dsq_insert() saw. qseq is generated from rq->scx.ops_qseq, but the counters of different rqs are independent, so if a task is dequeued and re-enqueued on a different rq between scx_bpf_dsq_insert() and finish_dispatch(), the new QUEUED instance can end up with the same qseq as the old one: CPU X CPU Z ----- ----- enqueue p on rq A, qseq = N ops.dispatch() scx_bpf_dsq_insert(p) records qseq N sched_setaffinity(p) dequeue p from rq A enqueue p on rq B, qseq = N finish_dispatch(p, N) qseq matches, p is claimed The claim itself is still atomic so the core stays consistent, but an insert issued for a previous QUEUED instance gets applied to a new one which the BPF scheduler has just received through ops.enqueue(). This breaks the guarantee that dispatches targeting a stale instance are ignored. Generate qseq from a per-task counter, p->scx.ops_qseq, instead so that consecutive QUEUED instances of a task never share a qseq regardless of which rq they're on. The counter is only updated in scx_do_enqueue_task() with the task's rq locked, so no additional synchronization is needed, and it fits in an existing hole in struct sched_ext_entity on 64bit. Remove the now unused rq->scx.ops_qseq. Never generate qseq 0. NONE and DISPATCHING don't carry a qseq, so scx_bpf_dsq_insert() on a task in either state records 0. With a per-task counter, every task's first QUEUED instance would otherwise get qseq 0 and could be claimed by such an insert. Wrap the counter where the QSEQ field wraps so that it can't reach a value that shifts to 0 on 32bit either. Fixes: f0e1a0643a59 ("sched_ext: Implement BPF extensible scheduler class") Cc: stable@vger.kernel.org # v6.12+ Assisted-by: Claude:claude-opus-5.5 Signed-off-by: Kuba Piecuch Signed-off-by: Tejun Heo [ jpiecuch: 7.2.y lacks runnable_cpu in sched_ext_entity, so ops_qseq goes after selected_cpu; adjust context in scx_rq and the dump_line() CPU line ] Signed-off-by: Kuba Piecuch Signed-off-by: Greg Kroah-Hartman --- include/linux/sched/ext.h | 1 + kernel/sched/ext/ext.c | 15 ++++++++++----- kernel/sched/ext/internal.h | 5 +++++ kernel/sched/sched.h | 1 - 4 files changed, 16 insertions(+), 6 deletions(-) --- a/include/linux/sched/ext.h +++ b/include/linux/sched/ext.h @@ -198,6 +198,7 @@ struct sched_ext_entity { s32 sticky_cpu; s32 holding_cpu; s32 selected_cpu; + u32 ops_qseq; /* protected by rq lock */ struct task_struct *kf_tasks[2]; /* see SCX_CALL_OP_TASK() */ struct list_head runnable_node; /* rq->scx.runnable_list */ --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -1953,8 +1953,14 @@ static void do_enqueue_task(struct rq *r if (unlikely(!SCX_HAS_OP(sch, enqueue))) goto global; - /* DSQ bypass didn't trigger, enqueue on the BPF scheduler */ - qseq = rq->scx.ops_qseq++ << SCX_OPSS_QSEQ_SHIFT; + /* + * DSQ bypass didn't trigger, enqueue on the BPF scheduler. Wrap the + * per-task qseq counter where the QSEQ field wraps and skip 0, which is + * what scx_bpf_dsq_insert() records for a task in NONE or DISPATCHING. + */ + p->scx.ops_qseq = ((p->scx.ops_qseq + 1) & + (SCX_OPSS_QSEQ_MASK >> SCX_OPSS_QSEQ_SHIFT)) ?: 1; + qseq = (unsigned long)p->scx.ops_qseq << SCX_OPSS_QSEQ_SHIFT; WARN_ON_ONCE(atomic_long_read(&p->scx.ops_state) != SCX_OPSS_NONE); atomic_long_set(&p->scx.ops_state, SCX_OPSS_QUEUEING | qseq); @@ -6690,10 +6696,9 @@ static void scx_dump_cpu(struct scx_sche seq_buf_init(&ns, buf, avail); dump_newline(&ns); - dump_line(&ns, "CPU %-4d: nr_run=%u flags=0x%x cpu_rel=%d ops_qseq=%lu ksync=%lu", + dump_line(&ns, "CPU %-4d: nr_run=%u flags=0x%x cpu_rel=%d ksync=%lu", cpu, rq->scx.nr_running, rq->scx.flags, - rq->scx.cpu_released, rq->scx.ops_qseq, - rq->scx.kick_sync); + rq->scx.cpu_released, rq->scx.kick_sync); dump_line(&ns, " curr=%s[%d] class=%ps", rq->curr->comm, rq->curr->pid, rq->curr->sched_class); --- a/kernel/sched/ext/internal.h +++ b/kernel/sched/ext/internal.h @@ -1508,6 +1508,11 @@ enum scx_ops_state { * dequeue/requeue, the dispatcher can tell whether it still has a claim * on the task being dispatched. * + * QSEQ is generated from the per-task p->scx.ops_qseq counter so that + * it doesn't repeat across QUEUED instances of the same task even if + * the task moves between rqs. 0 is never used as a valid QSEQ since + * NONE and DISPATCHING map to this value. + * * As some 32bit archs can't do 64bit store_release/load_acquire, * p->scx.ops_state is atomic_long_t which leaves 30 bits for QSEQ on * 32bit machines. The dispatch race window QSEQ protects is very narrow --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -795,7 +795,6 @@ struct scx_rq { struct scx_dispatch_q local_dsq; struct list_head runnable_list; /* runnable tasks on this rq */ struct list_head ddsp_deferred_locals; /* deferred ddsps from enq */ - unsigned long ops_qseq; u64 extra_enq_flags; /* see move_task_to_local_dsq() */ u32 nr_running; u32 cpuperf_target; /* [0, SCHED_CAPACITY_SCALE] */