* [PATCH] sched/fair: Clear throttle state when leaving the fair class
@ 2026-10-22 23:52 Daehyeon Ko
2026-10-23 11:52 ` Aaron Lu
2026-10-28 22:24 ` Benjamin Segall
0 siblings, 2 replies; 5+ messages in thread
From: Daehyeon Ko @ 2026-10-22 23:52 UTC (permalink / raw)
To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot
Cc: Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Aaron Lu,
Linux Kernel Mailing List
Task-based CFS bandwidth throttling removes a task's fair sched_entity
while the scheduler core still considers the task queued. p->throttled
carries the fair-specific state across dequeue/enqueue operations.
DEQUEUE_CLASS removes the task from cfs_rq->throttled_limbo_list
but retains the bit. If the last finite CFS quota is disabled while
the task runs in another class, it is absent from the unthrottle walk and
the bit survives.
Returning to fair while cfs_bandwidth_used() is false then performs a real
fair enqueue with p->throttled still set.
After bandwidth is re-enabled, a class-change dequeue treats the
stale bit as limbo state and skips __dequeue_task(). p->se.on_rq therefore
remains set, and put_prev_task_fair() places the entity in the EEVDF tree
while the task moves to RT. On exit, the fair picker can select the
TASK_DEAD task. __schedule() then returns to do_task_dead(),
which hits BUG().
Keep p->throttled across migrations and group moves, but clear it on
DEQUEUE_CLASS because fair-specific state must not outlive fair-class
membership. A later return to fair uses the normal enqueue path. That path
still respects a throttled cfs_rq.
A KASAN, LOCKDEP and DEBUG_LIST v7.3 kernel reproduced the do_task_dead()
BUG in 3/3 root and 3/3 delegated uid-1000 runs with CapPrm=CapEff=0 and
RLIMIT_RTPRIO=1. The fix passed both matrices plus a 3/3 class-switch
control with bandwidth continuously enabled, without a scheduler warning,
KASAN report, lockdep splat or crash.
An AI coding assistant analyzed the state mismatch and drafted the initial
reproducer, fix candidate and this changelog. The source paths, trigger,
controls and results were rechecked against the cited revisions.
Fixes: e1fad12dcb66 ("sched/fair: Switch to task based throttle model")
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
Cc: stable@vger.kernel.org
---
kernel/sched/fair.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 0feec172fd37eb..524fe88bbb381a 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -6957,8 +6957,8 @@ static void dequeue_throttled_task(struct task_struct *p, int flags)
WARN_ON_ONCE(p->se.on_rq);
list_del_init(&p->throttle_node);
- /* task blocked after throttled */
- if (flags & DEQUEUE_SLEEP) {
+ /* Task blocked after throttling or is leaving the fair class. */
+ if (flags & (DEQUEUE_SLEEP | DEQUEUE_CLASS)) {
p->throttled = false;
return;
}
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] sched/fair: Clear throttle state when leaving the fair class
2026-10-22 23:52 [PATCH] sched/fair: Clear throttle state when leaving the fair class Daehyeon Ko
@ 2026-10-23 11:52 ` Aaron Lu
2026-10-28 22:24 ` Benjamin Segall
1 sibling, 0 replies; 5+ messages in thread
From: Aaron Lu @ 2026-10-23 11:52 UTC (permalink / raw)
To: Daehyeon Ko
Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Linux Kernel Mailing List
On Fri, Oct 23, 2026 at 08:52:33AM +0900, Daehyeon Ko wrote:
> Task-based CFS bandwidth throttling removes a task's fair sched_entity
> while the scheduler core still considers the task queued. p->throttled
> carries the fair-specific state across dequeue/enqueue operations.
>
> DEQUEUE_CLASS removes the task from cfs_rq->throttled_limbo_list
> but retains the bit. If the last finite CFS quota is disabled while
> the task runs in another class, it is absent from the unthrottle walk and
> the bit survives.
> Returning to fair while cfs_bandwidth_used() is false then performs a real
> fair enqueue with p->throttled still set.
>
> After bandwidth is re-enabled, a class-change dequeue treats the
> stale bit as limbo state and skips __dequeue_task(). p->se.on_rq therefore
> remains set, and put_prev_task_fair() places the entity in the EEVDF tree
> while the task moves to RT. On exit, the fair picker can select the
> TASK_DEAD task. __schedule() then returns to do_task_dead(),
> which hits BUG().
>
> Keep p->throttled across migrations and group moves, but clear it on
> DEQUEUE_CLASS because fair-specific state must not outlive fair-class
> membership. A later return to fair uses the normal enqueue path. That path
> still respects a throttled cfs_rq.
>
> A KASAN, LOCKDEP and DEBUG_LIST v7.3 kernel reproduced the do_task_dead()
> BUG in 3/3 root and 3/3 delegated uid-1000 runs with CapPrm=CapEff=0 and
> RLIMIT_RTPRIO=1. The fix passed both matrices plus a 3/3 class-switch
> control with bandwidth continuously enabled, without a scheduler warning,
> KASAN report, lockdep splat or crash.
>
> An AI coding assistant analyzed the state mismatch and drafted the initial
> reproducer, fix candidate and this changelog. The source paths, trigger,
> controls and results were rechecked against the cited revisions.
>
> Fixes: e1fad12dcb66 ("sched/fair: Switch to task based throttle model")
> Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
> Cc: stable@vger.kernel.org
Reviewed-by: Aaron Lu <ziqianlu@bytedance.com>
Thanks!
> ---
> kernel/sched/fair.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index 0feec172fd37eb..524fe88bbb381a 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -6957,8 +6957,8 @@ static void dequeue_throttled_task(struct task_struct *p, int flags)
> WARN_ON_ONCE(p->se.on_rq);
> list_del_init(&p->throttle_node);
>
> - /* task blocked after throttled */
> - if (flags & DEQUEUE_SLEEP) {
> + /* Task blocked after throttling or is leaving the fair class. */
> + if (flags & (DEQUEUE_SLEEP | DEQUEUE_CLASS)) {
> p->throttled = false;
> return;
> }
> --
> 2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] sched/fair: Clear throttle state when leaving the fair class
2026-10-22 23:52 [PATCH] sched/fair: Clear throttle state when leaving the fair class Daehyeon Ko
2026-10-23 11:52 ` Aaron Lu
@ 2026-10-28 22:24 ` Benjamin Segall
2026-10-29 3:27 ` Aaron Lu
2026-10-29 3:39 ` K Prateek Nayak
1 sibling, 2 replies; 5+ messages in thread
From: Benjamin Segall @ 2026-10-28 22:24 UTC (permalink / raw)
To: Daehyeon Ko
Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Aaron Lu, Linux Kernel Mailing List
Daehyeon Ko <4ncienth@gmail.com> writes:
> Task-based CFS bandwidth throttling removes a task's fair sched_entity
> while the scheduler core still considers the task queued. p->throttled
> carries the fair-specific state across dequeue/enqueue operations.
>
> DEQUEUE_CLASS removes the task from cfs_rq->throttled_limbo_list
> but retains the bit. If the last finite CFS quota is disabled while
> the task runs in another class, it is absent from the unthrottle walk and
> the bit survives.
> Returning to fair while cfs_bandwidth_used() is false then performs a real
> fair enqueue with p->throttled still set.
>
> After bandwidth is re-enabled, a class-change dequeue treats the
> stale bit as limbo state and skips __dequeue_task(). p->se.on_rq therefore
> remains set, and put_prev_task_fair() places the entity in the EEVDF tree
> while the task moves to RT. On exit, the fair picker can select the
> TASK_DEAD task. __schedule() then returns to do_task_dead(),
> which hits BUG().
>
> Keep p->throttled across migrations and group moves, but clear it on
> DEQUEUE_CLASS because fair-specific state must not outlive fair-class
> membership. A later return to fair uses the normal enqueue path. That path
> still respects a throttled cfs_rq.
>
> A KASAN, LOCKDEP and DEBUG_LIST v7.3 kernel reproduced the do_task_dead()
> BUG in 3/3 root and 3/3 delegated uid-1000 runs with CapPrm=CapEff=0 and
> RLIMIT_RTPRIO=1. The fix passed both matrices plus a 3/3 class-switch
> control with bandwidth continuously enabled, without a scheduler warning,
> KASAN report, lockdep splat or crash.
>
> An AI coding assistant analyzed the state mismatch and drafted the initial
> reproducer, fix candidate and this changelog. The source paths, trigger,
> controls and results were rechecked against the cited revisions.
>
> Fixes: e1fad12dcb66 ("sched/fair: Switch to task based throttle model")
> Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
> Cc: stable@vger.kernel.org
> ---
Yep, sched_class switch lets you smuggle ->throttled into
!cfs_bandwidth_used(), and then that check on enqueue_throttled lets you
smuggle it onto actually being enqueued, which leads to corruption.
That's fun.
I think we need to use switched_from_fair though - if we are currently
TASK_ON_RQ_MIGRATING, sched_class changes will skip going into
dequeue_task_fair at all, so we will not see the DEQUEUE_CLASS.
Triggering a migration on a throttled task can still happen I'm fairly
sure.
It's too bad, since it would be nice to just add to this existing
touchpoint, but it's at least it's trivial:
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index cfc982205b98c..db57ac52eb7f7 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -15500,10 +15500,14 @@ static void switching_from_fair(struct rq *rq, struct task_struct *p)
}
static void switched_from_fair(struct rq *rq, struct task_struct *p)
{
detach_task_cfs_rq(p);
+
+#ifdef CONFIG_CFS_BANDWIDTH
+ p->throttled = false;
+#endif
}
static void switched_to_fair(struct rq *rq, struct task_struct *p)
{
WARN_ON_ONCE(p->se.sched_delayed);
(Or we could pull it out into its own function for cleanliness if it
seems worthwhile)
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] sched/fair: Clear throttle state when leaving the fair class
2026-10-28 22:24 ` Benjamin Segall
@ 2026-10-29 3:27 ` Aaron Lu
2026-10-29 3:39 ` K Prateek Nayak
1 sibling, 0 replies; 5+ messages in thread
From: Aaron Lu @ 2026-10-29 3:27 UTC (permalink / raw)
To: Benjamin Segall
Cc: Daehyeon Ko, Ingo Molnar, Peter Zijlstra, Juri Lelli,
Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Linux Kernel Mailing List
On Wed, Oct 28, 2026 at 03:24:11PM -0700, Benjamin Segall wrote:
> Daehyeon Ko <4ncienth@gmail.com> writes:
>
> > Task-based CFS bandwidth throttling removes a task's fair sched_entity
> > while the scheduler core still considers the task queued. p->throttled
> > carries the fair-specific state across dequeue/enqueue operations.
> >
> > DEQUEUE_CLASS removes the task from cfs_rq->throttled_limbo_list
> > but retains the bit. If the last finite CFS quota is disabled while
> > the task runs in another class, it is absent from the unthrottle walk and
> > the bit survives.
> > Returning to fair while cfs_bandwidth_used() is false then performs a real
> > fair enqueue with p->throttled still set.
> >
> > After bandwidth is re-enabled, a class-change dequeue treats the
> > stale bit as limbo state and skips __dequeue_task(). p->se.on_rq therefore
> > remains set, and put_prev_task_fair() places the entity in the EEVDF tree
> > while the task moves to RT. On exit, the fair picker can select the
> > TASK_DEAD task. __schedule() then returns to do_task_dead(),
> > which hits BUG().
> >
> > Keep p->throttled across migrations and group moves, but clear it on
> > DEQUEUE_CLASS because fair-specific state must not outlive fair-class
> > membership. A later return to fair uses the normal enqueue path. That path
> > still respects a throttled cfs_rq.
> >
> > A KASAN, LOCKDEP and DEBUG_LIST v7.3 kernel reproduced the do_task_dead()
> > BUG in 3/3 root and 3/3 delegated uid-1000 runs with CapPrm=CapEff=0 and
> > RLIMIT_RTPRIO=1. The fix passed both matrices plus a 3/3 class-switch
> > control with bandwidth continuously enabled, without a scheduler warning,
> > KASAN report, lockdep splat or crash.
> >
> > An AI coding assistant analyzed the state mismatch and drafted the initial
> > reproducer, fix candidate and this changelog. The source paths, trigger,
> > controls and results were rechecked against the cited revisions.
> >
> > Fixes: e1fad12dcb66 ("sched/fair: Switch to task based throttle model")
> > Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
> > Cc: stable@vger.kernel.org
> > ---
>
> Yep, sched_class switch lets you smuggle ->throttled into
> !cfs_bandwidth_used(), and then that check on enqueue_throttled lets you
> smuggle it onto actually being enqueued, which leads to corruption.
> That's fun.
>
> I think we need to use switched_from_fair though - if we are currently
> TASK_ON_RQ_MIGRATING, sched_class changes will skip going into
> dequeue_task_fair at all, so we will not see the DEQUEUE_CLASS.
sched class change serializes with migration I think, with task_rq_lock()
which waits for migration to settle, so this should not happen?
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] sched/fair: Clear throttle state when leaving the fair class
2026-10-28 22:24 ` Benjamin Segall
2026-10-29 3:27 ` Aaron Lu
@ 2026-10-29 3:39 ` K Prateek Nayak
1 sibling, 0 replies; 5+ messages in thread
From: K Prateek Nayak @ 2026-10-29 3:39 UTC (permalink / raw)
To: Benjamin Segall, Daehyeon Ko
Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Mel Gorman, Valentin Schneider,
Aaron Lu, Linux Kernel Mailing List
Hello Ben,
On 10/29/2026 3:54 AM, Benjamin Segall wrote:
> Yep, sched_class switch lets you smuggle ->throttled into
> !cfs_bandwidth_used(), and then that check on enqueue_throttled lets you
> smuggle it onto actually being enqueued, which leads to corruption.
> That's fun.
>
> I think we need to use switched_from_fair though - if we are currently
> TASK_ON_RQ_MIGRATING, sched_class changes will skip going into
> dequeue_task_fair at all, so we will not see the DEQUEUE_CLASS.
> Triggering a migration on a throttled task can still happen I'm fairly
> sure.
We cannot grab the task_rq_lock() when the task is ON_RQ_MIGRATING and
guard(sched_change) should never see TASK_ON_RQ_MIGRATING - the task is
either fully blocked (!p->on_rq) or is queued (p->on_rq = 1) on a CPU
when we are doing a class change.
--
Thanks and Regards,
Prateek
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-29 3:39 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-22 23:52 [PATCH] sched/fair: Clear throttle state when leaving the fair class Daehyeon Ko
2026-10-23 11:52 ` Aaron Lu
2026-10-28 22:24 ` Benjamin Segall
2026-10-29 3:27 ` Aaron Lu
2026-10-29 3:39 ` K Prateek Nayak
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®