All of lore.kernel.org
 help / color / mirror / Atom feed
From: Vincent Guittot <vincent.guittot@linaro.org>
To: Vincent Donnefort <vincent.donnefort@arm.com>
Cc: peterz@infradead.org, mingo@redhat.com,
	linux-kernel@vger.kernel.org, dietmar.eggemann@arm.com,
	morten.rasmussen@arm.com, chris.redpath@arm.com,
	qperret@google.com
Subject: Re: [PATCH v4 2/7] sched/fair: Decay task PELT values during wakeup migration
Date: Tue, 19 Apr 2022 18:27:58 +0200	[thread overview]
Message-ID: <20220419162758.GA26133@vingu-book> (raw)
In-Reply-To: <0d354ce1-9b43-feae-1065-5d8a78c56ccc@arm.com>

Le mardi 19 avril 2022 à 13:23:27 (+0100), Vincent Donnefort a écrit :
> 
> 
> On 19/04/2022 11:08, Vincent Guittot wrote:
> > On Tue, 12 Apr 2022 at 15:42, Vincent Donnefort
> > <vincent.donnefort@arm.com> wrote:
> > > 
> > > Before being migrated to a new CPU, a task sees its PELT values
> > > synchronized with rq last_update_time. Once done, that same task will also
> > > have its sched_avg last_update_time reset. This means the time between
> > > the migration and the last clock update (B) will not be accounted for in
> > > util_avg and a discontinuity will appear. This issue is amplified by the
> > > PELT clock scaling. If the clock hasn't been updated while the CPU is
> > > idle, clock_pelt will not be aligned with clock_task and that time (A)
> > > will be also lost.
> > > 
> > >     ---------|----- A -----|-----------|------- B -----|>
> > >          clock_pelt   clock_task     clock            now
> > > 
> > > This is especially problematic for asymmetric CPU capacity systems which
> > > need stable util_avg signals for task placement and energy estimation.
> > > 
> > > Ideally, this problem would be solved by updating the runqueue clocks
> > > before the migration. But that would require taking the runqueue lock
> > > which is quite expensive [1]. Instead estimate the missing time and update
> > > the task util_avg with that value:
> > > 
> > >    A + B = clock_task - clock_pelt + sched_clock_cpu() - clock
> > > 
> > > Neither clock_task, clock_pelt nor clock can be accessed without the
> > > runqueue lock. The new cfs_rq last_update_lag is therefore created and
> > > contains those three values when the last_update_time value for that very
> > > same cfs_rq is updated.
> > > 
> > >    last_update_lag = clock - clock_task + clock_pelt
> > > 
> > > And we can then write the missing time as follow:
> > > 
> > >    A + B = sched_clock_cpu() - last_update_lag
> > > 
> > > The B. part of the missing time is however an estimation that doesn't take
> > > into account IRQ and Paravirt time.
> > > 
> > > Now we have an estimation for A + B, we can create an estimator for the
> > > PELT value at the time of the migration. We need for this purpose to
> > > inject last_update_time which is a combination of both clock_pelt and
> > > lost_idle_time. The latter is a time value which is completely lost from a
> > > PELT point of view and must be ignored. And finally, we can write:
> > > 
> > >    now = last_update_time + A + B
> > >        = last_update_time + sched_clock_cpu() - last_update_lag
> > > 
> > > This estimation has a cost, mostly due to sched_clock_cpu(). Limit the
> > > usage to the case where the source CPU is idle as we know this is when the
> > > clock is having the biggest risk of being outdated.
> > > 
> > > [1] https://lore.kernel.org/all/20190709115759.10451-1-chris.redpath@arm.com/
> > > 
> > > Signed-off-by: Vincent Donnefort <vincent.donnefort@arm.com>

[...]

> > 
> > I'm worried that we will call this for each and every
> > update_cfs_rq_load_avg() whereas the content will be used only when
> > idle and not throttled. Can't we use these conditions to save values
> > only when needed and limit the number of useless updates ?
> 
> I don't think we can use idle here as a condition, once it is idle, it is
> too late to get those clock values.

As an example, the patch below should work. It doesn't handle the throttled case yet and still has to
make sure that rq->enter_idle and rq->clock_pelt_idle are coherent in regards to ILB that
update blocked load.

---
 kernel/sched/fair.c  | 30 ++++++++++++++++++++++++++++++
 kernel/sched/pelt.h  | 21 ++++++++++++++-------
 kernel/sched/sched.h |  3 ++-
 3 files changed, 46 insertions(+), 8 deletions(-)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index e6ecf530f19f..f00843f9dd01 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7005,6 +7005,35 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int wake_flags)
 
 static void detach_entity_cfs_rq(struct sched_entity *se);
 
+#ifdef CONFIG_NO_HZ_COMMON
+static inline void migrate_se_pelt_lag(struct sched_entity *se)
+{
+       u64 now;
+       struct cfs_rq *cfs_rq;
+       struct rq *rq;
+       bool is_idle;
+
+       cfs_rq = cfs_rq_of(se);
+       rq = rq_of(cfs_rq);
+
+       rcu_read_lock();
+       is_idle = is_idle_task(rcu_dereference(rq->curr));
+       rcu_read_unlock();
+
+       if (!is_idle)
+               return;
+
+	/* TODO handle throttled cfs */
+	/* TODO handle update ilb blocked load update */
+	now = READ_ONCE(rq->clock_pelt_idle);
+	now += sched_clock_cpu(cpu_of(rq)) - READ_ONCE(rq->enter_idle);
+
+       __update_load_avg_blocked_se(now, se);
+}
+#else
+static void migrate_se_pelt_lag(struct sched_entity *se) {}
+#endif
+
 /*
  * Called immediately before a task is migrated to a new CPU; task_cpu(p) and
  * cfs_rq_of(p) references at time of call are still valid and identify the
@@ -7056,6 +7085,7 @@ static void migrate_task_rq_fair(struct task_struct *p, int new_cpu)
 		 * sounds not bad.
 		 */
 		remove_entity_load_avg(&p->se);
+		migrate_se_pelt_lag(&p->se);
 	}
 
 	/* Tell new CPU we are migrated */
diff --git a/kernel/sched/pelt.h b/kernel/sched/pelt.h
index c336f5f481bc..ece4423026e5 100644
--- a/kernel/sched/pelt.h
+++ b/kernel/sched/pelt.h
@@ -61,6 +61,14 @@ static inline void cfs_se_util_change(struct sched_avg *avg)
 	WRITE_ONCE(avg->util_est.enqueued, enqueued);
 }
 
+static inline u64 rq_clock_pelt(struct rq *rq)
+{
+	lockdep_assert_rq_held(rq);
+	assert_clock_updated(rq);
+
+	return rq->clock_pelt - rq->lost_idle_time;
+}
+
 /*
  * The clock_pelt scales the time to reflect the effective amount of
  * computation done during the running delta time but then sync back to
@@ -78,6 +86,8 @@ static inline void update_rq_clock_pelt(struct rq *rq, s64 delta)
 	if (unlikely(is_idle_task(rq->curr))) {
 		/* The rq is idle, we can sync to clock_task */
 		rq->clock_pelt  = rq_clock_task(rq);
+		WRITE_ONCE(rq->enter_idle, rq_clock(rq)); /* this could be factorized with idle_stamp */
+		WRITE_ONCE(rq->clock_pelt_idle, rq_clock_pelt(rq)); /* last pelt clock update when idle */
 		return;
 	}
 
@@ -130,14 +140,11 @@ static inline void update_idle_rq_clock_pelt(struct rq *rq)
 	 */
 	if (util_sum >= divider)
 		rq->lost_idle_time += rq_clock_task(rq) - rq->clock_pelt;
-}
 
-static inline u64 rq_clock_pelt(struct rq *rq)
-{
-	lockdep_assert_rq_held(rq);
-	assert_clock_updated(rq);
-
-	return rq->clock_pelt - rq->lost_idle_time;
+	/* The rq is idle, we can sync with clock_task */
+	rq->clock_pelt  = rq_clock_task(rq);
+	WRITE_ONCE(rq->enter_idle, rq_clock(rq)); /* this could be factorized with idle_stamp */
+	WRITE_ONCE(rq->clock_pelt_idle, rq_clock_pelt(rq)); /* last pelt clock update when idle */
 }
 
 #ifdef CONFIG_CFS_BANDWIDTH
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 6ab77b171656..108698446762 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1007,7 +1007,8 @@ struct rq {
 	u64			clock_task ____cacheline_aligned;
 	u64			clock_pelt;
 	unsigned long		lost_idle_time;
-
+	u64			clock_pelt_idle;
+	u64			enter_idle;
 	atomic_t		nr_iowait;
 
 #ifdef CONFIG_SCHED_DEBUG
-- 
2.17.1

> > 
> > A quick test with hackbench on a 8 cpus system gives
> > around 60k call for a duration 550 msec run a root level
> > and 180k from a 3rd level cgroups
> 
> If you think this is too much to have this store on this path, we could
> though either:
> 
> a. restrict the feature to when we know it is the most important: EAS?
> 
> b. store the updated value only when "decayed" is non 0.
> 
> [...]

  reply	other threads:[~2022-04-19 16:28 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-12 13:42 [PATCH v3 0/7] feec() energy margin removal Vincent Donnefort
2022-04-12 13:42 ` [PATCH v4 1/7] sched/fair: Provide u64 read for 32-bits arch helper Vincent Donnefort
2022-04-19 12:42   ` Tao Zhou
2022-04-22 11:10     ` Vincent Donnefort
2022-04-12 13:42 ` [PATCH v4 2/7] sched/fair: Decay task PELT values during wakeup migration Vincent Donnefort
2022-04-19 10:08   ` Vincent Guittot
2022-04-19 12:23     ` Vincent Donnefort
2022-04-19 16:27       ` Vincent Guittot [this message]
2022-04-20  9:34         ` Vincent Donnefort
2022-04-20 10:25           ` Vincent Donnefort
2022-04-21  7:36           ` Vincent Guittot
2022-04-22 15:14   ` Tao Zhou
2022-04-12 13:42 ` [PATCH v4 3/7] sched, drivers: Remove max param from effective_cpu_util()/sched_cpu_util() Vincent Donnefort
2022-04-12 13:42 ` [PATCH v4 4/7] sched/fair: Rename select_idle_mask to select_rq_mask Vincent Donnefort
2022-04-12 13:42 ` [PATCH v4 5/7] sched/fair: Use the same cpumask per-PD throughout find_energy_efficient_cpu() Vincent Donnefort
2022-04-12 13:42 ` [PATCH v4 6/7] sched/fair: Remove task_util from effective utilization in feec() Vincent Donnefort
2022-04-12 13:42 ` [PATCH v4 7/7] sched/fair: Remove the energy margin " Vincent Donnefort
2022-04-14 19:32 ` [PATCH v3 0/7] feec() energy margin removal Dietmar Eggemann

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=20220419162758.GA26133@vingu-book \
    --to=vincent.guittot@linaro.org \
    --cc=chris.redpath@arm.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=morten.rasmussen@arm.com \
    --cc=peterz@infradead.org \
    --cc=qperret@google.com \
    --cc=vincent.donnefort@arm.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.