mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Kayra Cizmeci <kayracizmeci@gmail.com>
To: Peter Zijlstra <peterz@infradead.org>
Cc: Josh Don <joshdon@google.com>,
	 Vincent Guittot <vincent.guittot@linaro.org>,
	mingo@redhat.com, juri.lelli@redhat.com,
	 dietmar.eggemann@arm.com, rostedt@goodmis.org,
	bsegall@google.com, mgorman@suse.de,  vschneid@redhat.com,
	kprateek.nayak@amd.com, linux-kernel@vger.kernel.org,
	 jakub@okonski.org, longman@redhat.com, tj@kernel.org,
	hannes@cmpxchg.org,  mkoutny@suse.com, cgroups@vger.kernel.org,
	jstultz@google.com, qyousef@layalina.io
Subject: Re: [PATCH v2] sched/eevdf: Fix sched idle task group preemption
Date: Thu, 22 Oct 2026 17:04:00 +0300	[thread overview]
Message-ID: <atoPmKeaneMLOYCJ@scheduler.schedules> (raw)
In-Reply-To: <20261022114705.GW88198@noisy.programming.kicks-ass.net>

On Thu, Oct 22, 2026 at 01:47:05PM +0200, Peter Zijlstra wrote:
> On Wed, Oct 21, 2026 at 09:39:52AM -0700, Josh Don wrote:
> > On Wed, Oct 21, 2026 at 7:50 AM Peter Zijlstra <peterz@infradead.org> wrote:
> > >
> > > On Wed, Oct 21, 2026 at 04:41:52PM +0300, Kayra Cizmeci wrote:
> > > > On Wed, Oct 21, 2026 at 03:32:59PM +0200, Vincent Guittot wrote:
> > > > > On Wed, 21 Oct 2026 at 15:13, Peter Zijlstra <peterz@infradead.org> wrote:
> > >
> > > > > > What is the actual semantics of cpu.idle nonsense? Why do we care about
> > > > > > this common ancestor?
> > > > >
> > > > > AFAICT, cpu.idle applied to one level and against entities at the same level
> > > > >
> > > >
> > > > Yeah. Like on here:
> > > >
> > > >     ROOT
> > > >      /\
> > > >     /  \
> > > >    /    \
> > > >   GA*   GB
> > > >    |    /\
> > > >    V   /  \
> > > >   TA  GC*  GD
> > > >       |    |
> > > >       V    V
> > > >       TB   TC
> > >
> > > (marked GA and GC with * to denote their 'idleness')
> > >
> > > > If GA is idle and GB is not, while GC is idle and were looking to TA and
> > > > TB we gotta choose TB. But if we just look to GC and GA we would choose
> > > > neither. .idle just applies to one level as Vincent says.
> > >
> > > Yeah, that's what we used to do, but the more I think about this, the
> > > less sense it makes!
> > >
> > > Josh added this (added to Cc now), but we have a number of Google folks
> > > on Cc already. It would be good to hear how this is actually used.
> > >
> > >
> > > So in your example TA and TB are both inside an idle group, and
> > > therefore one can argue their behaviour should be like !idle. But their
> > > common ancestor is root, and so the fact that they're contained in idle
> > > groups gets lost (which seems crazeh).
> > 
> > TA and TB have a matching se of GA and GB, since those entities are
> > siblings under the common ancestor. Since GB is not idle but GA is, I
> > would argue that TB should preempt TA.
> 
> So I find this 'weird', at the end both TA and TB are marked idle,
> albeit at different levels in the hierarchy. And yes, with the old code
> this would lead to TB preempting TA, but I really don't see why that's a
> good thing.
> 
> > Consider GA and GB as groups representing different users, and user GB
> > has internally decided to create an idle cgroup GC to adjust
> > competition between its own tasks (TB and TC). This should not affect
> > GB's overall preemption ability against other users.
> 
> It doesn't; TC is not idle and will preempt just fine. But by marking
> GB, and thus TB, as idle, we basically say we don't care about it, so
> preempting should also be irrelevant.
> 
> Consider this:
> 
>      ROOT
>       /\
>      /  \
>     /    \
>    GA*   GB
>     |    /\
>     V   /  \
>    TA  GC*  GD
>        |    |
>        v    V
>        GE   TB
>        /\
>       /  \
>      TC   TD
> 
> Suppose TC runs and TD wakes, with the strict LCA rules we'd find that
> neither is idle and have one preempt the other. Even though they're well
> part of an 'idle' sub-tree.

I'm late by 2 hours or smtg :-(.

If we do this, TA would be 1, and T{C,D} would be 1 too. But at the old
code we would choose one of T{C,D} since GB is the same level as GA and
GA is idle.

I think your case is really weird too. But can't we fix this with just
checking the two tasks are already in the same sub-tree and walk that
sub-tree to the top and if we find a idle one we just don't preempt?

We could also do this fastly with this cfs_rq->idle thingie.

> 
> > However in practice doing the exact computation to determine
> > preemption for relative idle properties (especially on wakeup) is
> > expensive and perhaps not strictly required. This is also why e.g. the
> > whole sched_idle_rq stuff is imprecise. We keep an overall tally of
> > the number of tasks that have a cpu.idle ancestor cgroup and use that
> > for wakeup placement, but if we were trying to be perfect we would
> > need to consider the matching_se stuff for that as well. What I'd say
> > there though is that imprecise placement is better than imprecise
> > preemption.
> > 
> > So overall, I would say the find_matching_se() stuff gives the more
> > ideal preemption decision; my first preference would be to keep it. It
> > avoids a regression with users who delegate cpu.idle internally within
> > their own cgroup hierarchy. FWIW wakeup preemption already does a
> > cgroup ancestor walk via update_curr; find_matching_se is similar in
> > spirit.
> 
> So its probably not the end of the world -- and we sort of still have
> one in put_prev_task_fair(), but still, I find the semantics weird.
> 
> > But to be clear I also don't think it would be terrible if we did
> > something like you suggest, cache whether a cgroup has _any_ cpu.idle
> > ancestor and then just make a single decision on wakeup comparing this
> > for the two tasks. The result of which is TC preempts T{AB}, but TA
> > and TB don't preempt each other. In which case I would suggest we
> > cache that as a part of updating cpu.idle so that we can avoid a
> > pointer chasing loop in se_is_idle().
> 
> Just so. Let me try and implement that, see how horrible it looks.
> 
> > Either of these two will at least also restore some semblance of
> > respecting cgroup cpu.idle in wakeup, as the flattening made it only
> > consider task level SCHED_IDLE.
> 
> Right; I agree the current situation is somewhat broken.
> 
> Something like so ...


I'm late by 2 hours or smtg :-(.

If we do this, TA would be 1, and T{C,D} would be 1 too. But at the old
code we would choose one of T{C,D} since GB is the same level as GA and
GA is idle.

I think your case is really weird too. But can't we fix this with just
checking the two tasks are already in the same sub-tree and walk that
sub-tree to the top and if we find a idle one we just don't preempt?

We could also do this fastly with this cfs_rq->idle thingie.

Still, this all wouldn't be needed if losing the difference between 
TA and T{C,D} is acceptable for us. Is it? Well IDK :-).

Thoughts?


> 
> ---
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index b5546cd27dba..779fcd5a2830 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -15767,6 +15767,40 @@ int sched_group_set_shares(struct task_group *tg, unsigned long shares)
>  	return ret;
>  }
>  
> +struct tg_set_idle_struct {
> +	long idle;
> +	int cpu;
> +};
> +
> +static int tg_set_idle_up(struct task_group *tg, void *data)
> +{
> +	struct tg_set_idle_struct *tsi = data;
> +	struct cfs_rq *grp_cfs_rq = tg_cfs_rq(tg, tsi->cpu);
> +	struct sched_entity *se = tg_se(tg, tsi->cpu);
> +	int old_idle = grp_cfs_rq->idle;
> +	long task_delta;
> +
> +	grp_cfs_rq->idle += tsi->idle;
> +
> +	if (!!old_idle == !!grp_cfs_rq->idle)
> +		return 0;
> +
> +	task_delta = grp_cfs_rq->h_nr_queued - grp_cfs_rq->h_nr_idle;
> +	if (!task_delta)
> +		return 0;
> +
> +	task_delta *= tsi->idle;
> +
> +	for_each_sched_entity(se) {
> +		if (!se->on_rq)
> +			break;
> +
> +		cfs_rq_of(se)->h_nr_idle += task_delta;
> +	}
> +
> +	return 0;
> +}
> +
>  int sched_group_set_idle(struct task_group *tg, long idle)
>  {
>  	int i;
> @@ -15777,49 +15811,22 @@ int sched_group_set_idle(struct task_group *tg, long idle)
>  	if (idle < 0 || idle > 1)
>  		return -EINVAL;
>  
> -	mutex_lock(&shares_mutex);
> -
> -	if (tg->idle == idle) {
> -		mutex_unlock(&shares_mutex);
> +	guard(mutex)(&shares_mutex);
> +	if (tg->idle == idle)
>  		return 0;
> -	}
>  
>  	tg->idle = idle;
>  
>  	for_each_possible_cpu(i) {
>  		struct rq *rq = cpu_rq(i);
> -		struct sched_entity *se = tg_se(tg, i);
> -		struct cfs_rq *grp_cfs_rq = tg_cfs_rq(tg, i);
> -		bool was_idle = cfs_rq_is_idle(grp_cfs_rq);
> -		long idle_task_delta;
> -		struct rq_flags rf;
>  
> -		rq_lock_irqsave(rq, &rf);
> +		guard(rq_lock_irqsave)(rq);
>  
> -		grp_cfs_rq->idle = idle;
> -		if (WARN_ON_ONCE(was_idle == cfs_rq_is_idle(grp_cfs_rq)))
> -			goto next_cpu;
> -
> -		idle_task_delta = grp_cfs_rq->h_nr_queued -
> -				  grp_cfs_rq->h_nr_idle;
> -		if (!cfs_rq_is_idle(grp_cfs_rq))
> -			idle_task_delta *= -1;
> -
> -		for_each_sched_entity(se) {
> -			struct cfs_rq *cfs_rq = cfs_rq_of(se);
> -
> -			if (!se->on_rq)
> -				break;
> -
> -			cfs_rq->h_nr_idle += idle_task_delta;
> -
> -			/* Already accounted at parent level and above. */
> -			if (cfs_rq_is_idle(cfs_rq))
> -				break;
> -		}
> -
> -next_cpu:
> -		rq_unlock_irqrestore(rq, &rf);
> +		struct tg_set_idle_struct tsi = {
> +			.idle = idle ? 1 : -1,
> +			.cpu = i,
> +		};
> +		walk_tg_tree_from(tg, tg_nop, tg_set_idle_up, &tsi);
>  	}
>  
>  	/* Idle groups have minimum weight. */
> @@ -15828,7 +15835,6 @@ int sched_group_set_idle(struct task_group *tg, long idle)
>  	else
>  		__sched_group_set_shares(tg, NICE_0_LOAD);
>  
> -	mutex_unlock(&shares_mutex);
>  	return 0;
>  }
>  
> diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
> index 499a5649582e..06bcee5bb89f 100644
> --- a/kernel/sched/sched.h
> +++ b/kernel/sched/sched.h
> @@ -751,7 +751,7 @@ struct cfs_rq {
>  	struct list_head	leaf_cfs_rq_list;
>  	struct task_group	*tg;	/* Group that "owns" this runqueue */
>  
> -	/* Locally cached copy of our task_group's idle value */
> +	/* Sum of all ancestor tg->idle. */
>  	int			idle;
>  
>  # ifdef CONFIG_CFS_BANDWIDTH

  reply	other threads:[~2026-10-22 14:04 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-19 18:26 Vincent Guittot
2026-10-21  7:19 ` Kayra Cizmeci
2026-10-21 13:13 ` Peter Zijlstra
2026-10-21 13:32   ` Vincent Guittot
2026-10-21 13:41     ` Kayra Cizmeci
2026-10-21 14:50       ` Peter Zijlstra
2026-10-21 15:18         ` Kayra Cizmeci
2026-10-21 16:39         ` Josh Don
2026-10-22 11:47           ` Peter Zijlstra
2026-10-22 14:04             ` Kayra Cizmeci [this message]
2026-10-22 17:49             ` Josh Don

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=atoPmKeaneMLOYCJ@scheduler.schedules \
    --to=kayracizmeci@gmail.com \
    --cc=bsegall@google.com \
    --cc=cgroups@vger.kernel.org \
    --cc=dietmar.eggemann@arm.com \
    --cc=hannes@cmpxchg.org \
    --cc=jakub@okonski.org \
    --cc=joshdon@google.com \
    --cc=jstultz@google.com \
    --cc=juri.lelli@redhat.com \
    --cc=kprateek.nayak@amd.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=longman@redhat.com \
    --cc=mgorman@suse.de \
    --cc=mingo@redhat.com \
    --cc=mkoutny@suse.com \
    --cc=peterz@infradead.org \
    --cc=qyousef@layalina.io \
    --cc=rostedt@goodmis.org \
    --cc=tj@kernel.org \
    --cc=vincent.guittot@linaro.org \
    --cc=vschneid@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®