[RFC PATCH v15 4/7] sched: Fix runtime accounting w/ split exec & sched contexts

John Stultz posted 7 patches 9 months, 1 week ago
[RFC PATCH v15 4/7] sched: Fix runtime accounting w/ split exec & sched contexts
Posted by John Stultz 9 months, 1 week ago
The idea here is we want to charge the scheduler-context task's
vruntime but charge the execution-context task's sum_exec_runtime.

This way cputime accounting goes against the task actually running
but vruntime accounting goes against the rq->donor task so we get
proper fairness.

Cc: Joel Fernandes <joelagnelf@nvidia.com>
Cc: Qais Yousef <qyousef@layalina.io>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Juri Lelli <juri.lelli@redhat.com>
Cc: Vincent Guittot <vincent.guittot@linaro.org>
Cc: Dietmar Eggemann <dietmar.eggemann@arm.com>
Cc: Valentin Schneider <vschneid@redhat.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ben Segall <bsegall@google.com>
Cc: Zimuzo Ezeozue <zezeozue@google.com>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Will Deacon <will@kernel.org>
Cc: Waiman Long <longman@redhat.com>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: "Paul E. McKenney" <paulmck@kernel.org>
Cc: Metin Kaya <Metin.Kaya@arm.com>
Cc: Xuewen Yan <xuewen.yan94@gmail.com>
Cc: K Prateek Nayak <kprateek.nayak@amd.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Suleiman Souhlal <suleiman@google.com>
Cc: kernel-team@android.com
Signed-off-by: John Stultz <jstultz@google.com>
---
 kernel/sched/fair.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index c798d27952431..f8ad3a44b3771 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -1129,22 +1129,33 @@ static void update_tg_load_avg(struct cfs_rq *cfs_rq)
 }
 #endif /* CONFIG_SMP */
 
-static s64 update_curr_se(struct rq *rq, struct sched_entity *curr)
+static s64 update_curr_se(struct rq *rq, struct sched_entity *se)
 {
 	u64 now = rq_clock_task(rq);
 	s64 delta_exec;
 
-	delta_exec = now - curr->exec_start;
+	delta_exec = now - se->exec_start;
 	if (unlikely(delta_exec <= 0))
 		return delta_exec;
 
-	curr->exec_start = now;
-	curr->sum_exec_runtime += delta_exec;
+	se->exec_start = now;
+	if (entity_is_task(se)) {
+		struct task_struct *running = rq->curr;
+		/*
+		 * If se is a task, we account the time against the running
+		 * task, as w/ proxy-exec they may not be the same.
+		 */
+		running->se.exec_start = now;
+		running->se.sum_exec_runtime += delta_exec;
+	} else {
+		/* If not task, account the time against se */
+		se->sum_exec_runtime += delta_exec;
+	}
 
 	if (schedstat_enabled()) {
 		struct sched_statistics *stats;
 
-		stats = __schedstats_from_se(curr);
+		stats = __schedstats_from_se(se);
 		__schedstat_set(stats->exec_max,
 				max(delta_exec, stats->exec_max));
 	}
-- 
2.49.0.rc0.332.g42c0ae87b1-goog
Re: [RFC PATCH v15 4/7] sched: Fix runtime accounting w/ split exec & sched contexts
Posted by K Prateek Nayak 9 months, 1 week ago
Hello John,

On 3/13/2025 3:41 AM, John Stultz wrote:
> The idea here is we want to charge the scheduler-context task's
> vruntime but charge the execution-context task's sum_exec_runtime.
> 
> This way cputime accounting goes against the task actually running
> but vruntime accounting goes against the rq->donor task so we get
> proper fairness.
> 
> Cc: Joel Fernandes <joelagnelf@nvidia.com>
> Cc: Qais Yousef <qyousef@layalina.io>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Juri Lelli <juri.lelli@redhat.com>
> Cc: Vincent Guittot <vincent.guittot@linaro.org>
> Cc: Dietmar Eggemann <dietmar.eggemann@arm.com>
> Cc: Valentin Schneider <vschneid@redhat.com>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Ben Segall <bsegall@google.com>
> Cc: Zimuzo Ezeozue <zezeozue@google.com>
> Cc: Mel Gorman <mgorman@suse.de>
> Cc: Will Deacon <will@kernel.org>
> Cc: Waiman Long <longman@redhat.com>
> Cc: Boqun Feng <boqun.feng@gmail.com>
> Cc: "Paul E. McKenney" <paulmck@kernel.org>
> Cc: Metin Kaya <Metin.Kaya@arm.com>
> Cc: Xuewen Yan <xuewen.yan94@gmail.com>
> Cc: K Prateek Nayak <kprateek.nayak@amd.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
> Cc: Suleiman Souhlal <suleiman@google.com>
> Cc: kernel-team@android.com
> Signed-off-by: John Stultz <jstultz@google.com>
> ---
>   kernel/sched/fair.c | 21 ++++++++++++++++-----
>   1 file changed, 16 insertions(+), 5 deletions(-)
> 
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index c798d27952431..f8ad3a44b3771 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -1129,22 +1129,33 @@ static void update_tg_load_avg(struct cfs_rq *cfs_rq)
>   }
>   #endif /* CONFIG_SMP */
>   
> -static s64 update_curr_se(struct rq *rq, struct sched_entity *curr)
> +static s64 update_curr_se(struct rq *rq, struct sched_entity *se)
>   {
>   	u64 now = rq_clock_task(rq);
>   	s64 delta_exec;
>   
> -	delta_exec = now - curr->exec_start;
> +	delta_exec = now - se->exec_start;
>   	if (unlikely(delta_exec <= 0))
>   		return delta_exec;
>   
> -	curr->exec_start = now;
> -	curr->sum_exec_runtime += delta_exec;
> +	se->exec_start = now;
> +	if (entity_is_task(se)) {
> +		struct task_struct *running = rq->curr;
> +		/*
> +		 * If se is a task, we account the time against the running
> +		 * task, as w/ proxy-exec they may not be the same.
> +		 */
> +		running->se.exec_start = now;
> +		running->se.sum_exec_runtime += delta_exec;
> +	} else {
> +		/* If not task, account the time against se */
> +		se->sum_exec_runtime += delta_exec;
> +	}
>   
>   	if (schedstat_enabled()) {
>   		struct sched_statistics *stats;
>   
> -		stats = __schedstats_from_se(curr);
> +		stats = __schedstats_from_se(se);
>   		__schedstat_set(stats->exec_max,
>   				max(delta_exec, stats->exec_max));

So I'm slightly confused here - For the case of proxy where
entity_is_task(), we charge the delta_exec to the running task's se but
then we go ahead and update the exec_max against the stats of the
donor's se? That seems odd.

Could we just replace the se with &rq->curr->se if entity_is_task()
returns true and keep the rest as is? The calculations will be same as
what it is above (except for the stats bit) and you'll not require
updating "exec_start" for both current task and proxy's se. Thoughts?


>   	}

-- 
Thanks and Regards,
Prateek
Re: [RFC PATCH v15 4/7] sched: Fix runtime accounting w/ split exec & sched contexts
Posted by Steven Rostedt 9 months, 1 week ago
On Wed, 12 Mar 2025 15:11:34 -0700
John Stultz <jstultz@google.com> wrote:

> The idea here is we want to charge the scheduler-context task's
> vruntime but charge the execution-context task's sum_exec_runtime.

The "but" is confusing me. Do you mean, "and also"? The sentence
doesn't make sense with "but" unless it was:

  "The idea here is we DON'T want to charge the scheduler-context
  task's vruntime but charge the execution-context task's
  sum_exec_runtime INSTEAD."

> 
> This way cputime accounting goes against the task actually running
> but vruntime accounting goes against the rq->donor task so we get
> proper fairness.

But this shows that you want to do both, although, I would remove the
"but" here too. Replace it with "while".

Or maybe I'm just confused.


> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index c798d27952431..f8ad3a44b3771 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -1129,22 +1129,33 @@ static void update_tg_load_avg(struct cfs_rq *cfs_rq)
>  }
>  #endif /* CONFIG_SMP */
>  
> -static s64 update_curr_se(struct rq *rq, struct sched_entity *curr)
> +static s64 update_curr_se(struct rq *rq, struct sched_entity *se)

Should this be renamed to "update_se()" as it no longer appears to be
updating "curr_se".

>  {
>  	u64 now = rq_clock_task(rq);
>  	s64 delta_exec;
>  
> -	delta_exec = now - curr->exec_start;
> +	delta_exec = now - se->exec_start;
>  	if (unlikely(delta_exec <= 0))
>  		return delta_exec;
>  
> -	curr->exec_start = now;
> -	curr->sum_exec_runtime += delta_exec;
> +	se->exec_start = now;
> +	if (entity_is_task(se)) {
> +		struct task_struct *running = rq->curr;
> +		/*
> +		 * If se is a task, we account the time against the running
> +		 * task, as w/ proxy-exec they may not be the same.
> +		 */
> +		running->se.exec_start = now;
> +		running->se.sum_exec_runtime += delta_exec;
> +	} else {
> +		/* If not task, account the time against se */
> +		se->sum_exec_runtime += delta_exec;
> +	}

Or maybe: update_proxy_se() ?

-- Steve

>  
>  	if (schedstat_enabled()) {
>  		struct sched_statistics *stats;
>  
> -		stats = __schedstats_from_se(curr);
> +		stats = __schedstats_from_se(se);
>  		__schedstat_set(stats->exec_max,
>  				max(delta_exec, stats->exec_max));
>  	}
Re: [RFC PATCH v15 4/7] sched: Fix runtime accounting w/ split exec & sched contexts
Posted by John Stultz 9 months, 1 week ago
On Thu, Mar 13, 2025 at 3:26 AM Steven Rostedt <rostedt@goodmis.org> wrote:
>
> On Wed, 12 Mar 2025 15:11:34 -0700
> John Stultz <jstultz@google.com> wrote:
>
> > The idea here is we want to charge the scheduler-context task's
> > vruntime but charge the execution-context task's sum_exec_runtime.
>
> The "but" is confusing me. Do you mean, "and also"? The sentence
> doesn't make sense with "but" unless it was:
>
>   "The idea here is we DON'T want to charge the scheduler-context
>   task's vruntime but charge the execution-context task's
>   sum_exec_runtime INSTEAD."
>
> >
> > This way cputime accounting goes against the task actually running
> > but vruntime accounting goes against the rq->donor task so we get
> > proper fairness.
>
> But this shows that you want to do both, although, I would remove the
> "but" here too. Replace it with "while".
>
> Or maybe I'm just confused.

Apologies for beingconfusing. I know I can tangle my words sometimes. :)

Hrmm. I think maybe it's a bit more clear if I switch the order. ie:

Without proxy-exec, we normally charge the "current" task for both its
vruntime as well as its sum_exec_runtime.

With proxy, we want to charge the rq->curr (proxy/lock holder) time to
its sum_exec_runtime (so it's clear to userland the rq->curr task *is*
running).

*But*, instead of charging rq->curr for the vruntime, that is charged
against the rq->donor(lock waiter) task, because that is what it is
donating when it is used as the scheduler-context.

If the donor and curr tasks are the same, then it's the same as without proxy.

Your suggestion of "while" is good as well. I'll try to reword this.


> > diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> > index c798d27952431..f8ad3a44b3771 100644
> > --- a/kernel/sched/fair.c
> > +++ b/kernel/sched/fair.c
> > @@ -1129,22 +1129,33 @@ static void update_tg_load_avg(struct cfs_rq *cfs_rq)
> >  }
> >  #endif /* CONFIG_SMP */
> >
> > -static s64 update_curr_se(struct rq *rq, struct sched_entity *curr)
> > +static s64 update_curr_se(struct rq *rq, struct sched_entity *se)
>
> Should this be renamed to "update_se()" as it no longer appears to be
> updating "curr_se".
>
> >  {
> >       u64 now = rq_clock_task(rq);
> >       s64 delta_exec;
> >
> > -     delta_exec = now - curr->exec_start;
> > +     delta_exec = now - se->exec_start;
> >       if (unlikely(delta_exec <= 0))
> >               return delta_exec;
> >
> > -     curr->exec_start = now;
> > -     curr->sum_exec_runtime += delta_exec;
> > +     se->exec_start = now;
> > +     if (entity_is_task(se)) {
> > +             struct task_struct *running = rq->curr;
> > +             /*
> > +              * If se is a task, we account the time against the running
> > +              * task, as w/ proxy-exec they may not be the same.
> > +              */
> > +             running->se.exec_start = now;
> > +             running->se.sum_exec_runtime += delta_exec;
> > +     } else {
> > +             /* If not task, account the time against se */
> > +             se->sum_exec_runtime += delta_exec;
> > +     }
>
> Or maybe: update_proxy_se() ?

I'm hesitant to call it update_proxy_se() since it's still used even
when proxy-exec isn't enabled.

update_se() could work, but also feels a little generic.

Maybe update_se_times()? or account_time_se()?


Appreciate the review and feedback!

thanks
-john