From nobody Sun May 5 10:50:47 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8905DC433F5 for ; Mon, 23 May 2022 15:52:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238172AbiEWPwq (ORCPT ); Mon, 23 May 2022 11:52:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54210 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238287AbiEWPwh (ORCPT ); Mon, 23 May 2022 11:52:37 -0400 Received: from mail-wr1-x449.google.com (mail-wr1-x449.google.com [IPv6:2a00:1450:4864:20::449]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C3CF82CCA1 for ; Mon, 23 May 2022 08:52:35 -0700 (PDT) Received: by mail-wr1-x449.google.com with SMTP id t9-20020a5d5349000000b0020d02cd51fbso4138654wrv.13 for ; Mon, 23 May 2022 08:52:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=1bvJMf+eQE9JXTqMmxzB/jk8K+ZJiPghravi+WESjRI=; b=ROvKT/aQ4bYn/HnYl2gfTbhZhN5g4X0vV5yPINSNtUa537Rh3Etm5BYVgjwo1QVenZ W/+P4+Kyj4/bAaOFXMyGQo6PSX8G2wSBe8SqtZgzHAQc6hjfo9i69nzFi5httNjafjRC ggLpD5rVs66Ou0hXUpC6qgUiEZE4il5ouItbOVJYMwe+EINR6yrcm0MiAmUcsyMIVCaQ lJ9Q1524pb8IZDwEGeU3rpcLn+kP971Wr1masVk/QHnmafMPF90pt1InBowKYIxNxK5p nCCrk5D0A0K1GKfGunjMYUltCDjaoWHRO6p8kJio9x+hDH0oGsPAYo8EX7zjO6z8r/Vl spdg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=1bvJMf+eQE9JXTqMmxzB/jk8K+ZJiPghravi+WESjRI=; b=ssIsJahmDE4afSH3ol8+A8Z+7/WG47GwiEqtynZXBLNRX+d9BSRmi11u2iyTwQa+e3 AffXXtak5lJrFv7phdbjGuw/b7y3sR0y7Vne+NZUXxbLmLI9CnQomRhYaCkUiCQsv8pM l23CH9ffT1dSJRecvNz26c9mT1BVPybrz8wJmrlqwEUswuZkz4E4rBmItm1o1I8QeU3F /6Uwn3iNd/lCDLitwHaV9hpkJBuq0ajmlCodiK22dzd4pNOsKXVpWpkNSip51okikBnb DJTVsuB8KpoKt9xjM24hOMdRCa/tccRHB/JiQuuFupC+lhehKZIhoRNbeqb7vT8Z/XGP O6VA== X-Gm-Message-State: AOAM532rJX4UUa437AvbNCqBWGCAmV58la+8G8SZTOs+K+SmdQtdlizF t5Ue/dt8UZRbp63QJLrPyEGn1znak27MgGC6 X-Google-Smtp-Source: ABdhPJxp2jhMLjXCnqy5Di2PkXZsEtXkyQ04Wly1DjnMq6xO0okNSNCALJCS3t2p8Njr7BlF+2bP9rq07Iwm8K7n X-Received: from vdonnefort.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:2eea]) (user=vdonnefort job=sendgmr) by 2002:a1c:35c7:0:b0:38e:4c59:6788 with SMTP id c190-20020a1c35c7000000b0038e4c596788mr49227wma.1.1653321153727; Mon, 23 May 2022 08:52:33 -0700 (PDT) Date: Mon, 23 May 2022 16:51:34 +0100 In-Reply-To: <20220523155140.2878563-1-vdonnefort@google.com> Message-Id: <20220523155140.2878563-2-vdonnefort@google.com> Mime-Version: 1.0 References: <20220523155140.2878563-1-vdonnefort@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH v9 1/7] sched/fair: Provide u64 read for 32-bits arch helper From: Vincent Donnefort To: peterz@infradead.org, mingo@redhat.com, vincent.guittot@linaro.org Cc: linux-kernel@vger.kernel.org, dietmar.eggemann@arm.com, morten.rasmussen@arm.com, chris.redpath@arm.com, qperret@google.com, tao.zhou@linux.dev, kernel-team@android.com, vdonnefort@google.com, Vincent Donnefort Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Vincent Donnefort Introducing macro helpers u64_u32_{store,load}() to factorize lockless accesses to u64 variables for 32-bits architectures. Users are for now cfs_rq.min_vruntime and sched_avg.last_update_time. To accommodate the later where the copy lies outside of the structure (cfs_rq.last_udpate_time_copy instead of sched_avg.last_update_time_copy), use the _copy() version of those helpers. Those new helpers encapsulate smp_rmb() and smp_wmb() synchronization and therefore, have a small penalty for 32-bits machines in set_task_rq_fair() and init_cfs_rq(). Signed-off-by: Vincent Donnefort Signed-off-by: Vincent Donnefort Reviewed-by: Dietmar Eggemann diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 77b2048a9326..05614d9b919c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -612,11 +612,8 @@ static void update_min_vruntime(struct cfs_rq *cfs_rq) } =20 /* ensure we never gain time by being placed backwards. */ - cfs_rq->min_vruntime =3D max_vruntime(cfs_rq->min_vruntime, vruntime); -#ifndef CONFIG_64BIT - smp_wmb(); - cfs_rq->min_vruntime_copy =3D cfs_rq->min_vruntime; -#endif + u64_u32_store(cfs_rq->min_vruntime, + max_vruntime(cfs_rq->min_vruntime, vruntime)); } =20 static inline bool __entity_less(struct rb_node *a, const struct rb_node *= b) @@ -3313,6 +3310,11 @@ static inline void cfs_rq_util_change(struct cfs_rq = *cfs_rq, int flags) } =20 #ifdef CONFIG_SMP +static inline u64 cfs_rq_last_update_time(struct cfs_rq *cfs_rq) +{ + return u64_u32_load_copy(cfs_rq->avg.last_update_time, + cfs_rq->last_update_time_copy); +} #ifdef CONFIG_FAIR_GROUP_SCHED /* * Because list_add_leaf_cfs_rq always places a child cfs_rq on the list @@ -3423,27 +3425,9 @@ void set_task_rq_fair(struct sched_entity *se, if (!(se->avg.last_update_time && prev)) return; =20 -#ifndef CONFIG_64BIT - { - u64 p_last_update_time_copy; - u64 n_last_update_time_copy; - - do { - p_last_update_time_copy =3D prev->load_last_update_time_copy; - n_last_update_time_copy =3D next->load_last_update_time_copy; - - smp_rmb(); - - p_last_update_time =3D prev->avg.last_update_time; - n_last_update_time =3D next->avg.last_update_time; + p_last_update_time =3D cfs_rq_last_update_time(prev); + n_last_update_time =3D cfs_rq_last_update_time(next); =20 - } while (p_last_update_time !=3D p_last_update_time_copy || - n_last_update_time !=3D n_last_update_time_copy); - } -#else - p_last_update_time =3D prev->avg.last_update_time; - n_last_update_time =3D next->avg.last_update_time; -#endif __update_load_avg_blocked_se(p_last_update_time, se); se->avg.last_update_time =3D n_last_update_time; } @@ -3796,12 +3780,9 @@ update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_r= q) } =20 decayed |=3D __update_load_avg_cfs_rq(now, cfs_rq); - -#ifndef CONFIG_64BIT - smp_wmb(); - cfs_rq->load_last_update_time_copy =3D sa->last_update_time; -#endif - + u64_u32_store_copy(sa->last_update_time, + cfs_rq->last_update_time_copy, + sa->last_update_time); return decayed; } =20 @@ -3933,27 +3914,6 @@ static inline void update_load_avg(struct cfs_rq *cf= s_rq, struct sched_entity *s } } =20 -#ifndef CONFIG_64BIT -static inline u64 cfs_rq_last_update_time(struct cfs_rq *cfs_rq) -{ - u64 last_update_time_copy; - u64 last_update_time; - - do { - last_update_time_copy =3D cfs_rq->load_last_update_time_copy; - smp_rmb(); - last_update_time =3D cfs_rq->avg.last_update_time; - } while (last_update_time !=3D last_update_time_copy); - - return last_update_time; -} -#else -static inline u64 cfs_rq_last_update_time(struct cfs_rq *cfs_rq) -{ - return cfs_rq->avg.last_update_time; -} -#endif - /* * Synchronize entity load avg of dequeued entity without locking * the previous rq. @@ -6960,21 +6920,8 @@ static void migrate_task_rq_fair(struct task_struct = *p, int new_cpu) if (READ_ONCE(p->__state) =3D=3D TASK_WAKING) { struct sched_entity *se =3D &p->se; struct cfs_rq *cfs_rq =3D cfs_rq_of(se); - u64 min_vruntime; - -#ifndef CONFIG_64BIT - u64 min_vruntime_copy; - - do { - min_vruntime_copy =3D cfs_rq->min_vruntime_copy; - smp_rmb(); - min_vruntime =3D cfs_rq->min_vruntime; - } while (min_vruntime !=3D min_vruntime_copy); -#else - min_vruntime =3D cfs_rq->min_vruntime; -#endif =20 - se->vruntime -=3D min_vruntime; + se->vruntime -=3D u64_u32_load(cfs_rq->min_vruntime); } =20 if (p->on_rq =3D=3D TASK_ON_RQ_MIGRATING) { @@ -11422,10 +11369,7 @@ static void set_next_task_fair(struct rq *rq, stru= ct task_struct *p, bool first) void init_cfs_rq(struct cfs_rq *cfs_rq) { cfs_rq->tasks_timeline =3D RB_ROOT_CACHED; - cfs_rq->min_vruntime =3D (u64)(-(1LL << 20)); -#ifndef CONFIG_64BIT - cfs_rq->min_vruntime_copy =3D cfs_rq->min_vruntime; -#endif + u64_u32_store(cfs_rq->min_vruntime, (u64)(-(1LL << 20))); #ifdef CONFIG_SMP raw_spin_lock_init(&cfs_rq->removed.lock); #endif diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 1f97f357aacd..bf4a0ec98678 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -520,6 +520,45 @@ struct cfs_bandwidth { }; =20 #endif /* CONFIG_CGROUP_SCHED */ =20 +/* + * u64_u32_load/u64_u32_store + * + * Use a copy of a u64 value to protect against data race. This is only + * applicable for 32-bits architectures. + */ +#ifdef CONFIG_64BIT +# define u64_u32_load_copy(var, copy) var +# define u64_u32_store_copy(var, copy, val) (var =3D val) +#else +# define u64_u32_load_copy(var, copy) \ +({ \ + u64 __val, __val_copy; \ + do { \ + __val_copy =3D copy; \ + /* \ + * paired with u64_u32_store, ordering access \ + * to var and copy. \ + */ \ + smp_rmb(); \ + __val =3D var; \ + } while (__val !=3D __val_copy); \ + __val; \ +}) +# define u64_u32_store_copy(var, copy, val) \ +do { \ + typeof(val) __val =3D (val); \ + var =3D __val; \ + /* \ + * paired with u64_u32_load, ordering access to var and \ + * copy. \ + */ \ + smp_wmb(); \ + copy =3D __val; \ +} while (0) +#endif +# define u64_u32_load(var) u64_u32_load_copy(var, var##_copy) +# define u64_u32_store(var, val) u64_u32_store_copy(var, var##_copy, val) + /* CFS-related fields in a runqueue */ struct cfs_rq { struct load_weight load; @@ -560,7 +599,7 @@ struct cfs_rq { */ struct sched_avg avg; #ifndef CONFIG_64BIT - u64 load_last_update_time_copy; + u64 last_update_time_copy; #endif struct { raw_spinlock_t lock ____cacheline_aligned; --=20 2.36.1.124.g0e6072fb45-goog From nobody Sun May 5 10:50:47 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 56EEBC433F5 for ; Mon, 23 May 2022 15:53:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238245AbiEWPxI (ORCPT ); Mon, 23 May 2022 11:53:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54272 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238280AbiEWPwk (ORCPT ); Mon, 23 May 2022 11:52:40 -0400 Received: from mail-wm1-x349.google.com (mail-wm1-x349.google.com [IPv6:2a00:1450:4864:20::349]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 22C0132073 for ; Mon, 23 May 2022 08:52:37 -0700 (PDT) Received: by mail-wm1-x349.google.com with SMTP id bi5-20020a05600c3d8500b0039489e1d18dso10745970wmb.5 for ; Mon, 23 May 2022 08:52:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=tRDi5jTRVbNIM2xVenGdkInBedwqyzv/oBdGL9Foj9E=; b=DiyvxO6sXyHaWQ6S+EmySQgx0bNyXdIhCGWAie9Sl6/sgq2Fi+2C441A/a2ATDwYOp K3snUHUoYskQuyjkZVApMHDJd6N8X0doGokJXwk1aQvU9EMbr2H5aoM/0acDmkS9xlY6 v/m0wBlGbB/h84XBe/ID9RuFLUCELn0QfaT8zfZrmkzuUMmjtAYNaRfLMXjbirJ2fdqV nJARZaL7Is6VvV7jzS9rLJ59X1pJ2mwfrrHFx/XcNzd36OIvka78sF5dIuH9VXwQeUdR jhamYmc29A7Knn4JXRSKz4a+JpmqaQTscO5FJIw//0dcE4o6+G3/vN3N4uoK/ymCe+/h p2+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=tRDi5jTRVbNIM2xVenGdkInBedwqyzv/oBdGL9Foj9E=; b=mhsicfpy83XFlpakGX4Ngbe/t1M7yubhhOZR5DrOF+r2lgZuW9eb/ERjif5GgqzLGg zit2kCxHUuSiOxwheujAsA8bmS2FhRDbx2lj2Ej3KZX9Yyi6co3PGIkAKH6ugGtxot6f gmdi73mTNnuHL9YecyWW4x7NHmG3uuxzR8q/RlnsBBTcMHRWGuKaZhiJVhEPBz6qxNnz 7Y0P6Vw9D4pddfPMvRXMFEo9PYZeTDj9cY2FKDeV3hRZ+38q5x7YdDf2LDyWBAM+bslH lEiqLBqqIGQ+y4Jn+bNVnxmJVteEaMZjqvY1pr4j9CaEj9RF8vhc86BRzXDeMKZWIbM0 Ef6A== X-Gm-Message-State: AOAM530po1Sv1kABMB7/rTrgYCjMLtAmVinCkUcs7RxIPRK7Jhnb5UT8 j9vl7f1TvKYcY+ecgogQ/Z3K4OxWRUSgfccR X-Google-Smtp-Source: ABdhPJyYWKY7B+DXTcK9OtyxLeYIAlb8S+VIMgxSWObqEwAszIBGssmx20pd5tGW1HDHKcDXci9Uo0vNfg62Cj+E X-Received: from vdonnefort.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:2eea]) (user=vdonnefort job=sendgmr) by 2002:a1c:f415:0:b0:397:2db0:ed73 with SMTP id z21-20020a1cf415000000b003972db0ed73mr19835156wma.19.1653321156402; Mon, 23 May 2022 08:52:36 -0700 (PDT) Date: Mon, 23 May 2022 16:51:35 +0100 In-Reply-To: <20220523155140.2878563-1-vdonnefort@google.com> Message-Id: <20220523155140.2878563-3-vdonnefort@google.com> Mime-Version: 1.0 References: <20220523155140.2878563-1-vdonnefort@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH v9 2/7] sched/fair: Decay task PELT values during wakeup migration From: Vincent Donnefort To: peterz@infradead.org, mingo@redhat.com, vincent.guittot@linaro.org Cc: linux-kernel@vger.kernel.org, dietmar.eggemann@arm.com, morten.rasmussen@arm.com, chris.redpath@arm.com, qperret@google.com, tao.zhou@linux.dev, kernel-team@android.com, vdonnefort@google.com, Vincent Donnefort Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Vincent Donnefort 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 will not be accounted for in util_avg and a discontinuity will appear. This issue is amplified by the PELT clock scaling. It takes currently one tick after the CPU being idle to let clock_pelt catching up clock_task. 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. To that end, we need sched_clock_cpu() but it is a costly function. 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. In this such case, let's call it cfs_idle_lag the delta time between the rq_clock_pelt value at rq idle and cfs_rq idle. And rq_idle_lag the delta between "now" and the rq_clock_pelt at rq idle. The estimated PELT clock is then: last_update_time + (the cfs_rq's last_update_time) cfs_idle_lag + (delta between cfs_rq's update and rq's update) rq_idle_lag (delta between rq's update and now) last_update_time =3D cfs_rq_clock_pelt() =3D rq_clock_pelt() - cfs->throttled_clock_pelt_time cfs_idle_lag =3D rq_clock_pelt()@rq_idle - rq_clock_pelt()@cfs_rq_idle rq_idle_lag =3D sched_clock_cpu() - rq_clock()@rq_idle The rq_clock_pelt() from last_update_time being the same as rq_clock_pelt()@cfs_rq_idle, we can write: estimation =3D rq_clock_pelt()@rq_idle - cfs->throttled_clock_pelt_time + sched_clock_cpu() - rq_clock()@rq_idle The clocks being not accessible without the rq lock taken, some timestamps are created: rq_clock_pelt()@rq_idle is rq->clock_pelt_idle rq_clock()@rq_idle is rq->enter_idle cfs->throttled_clock_pelt_time is cfs_rq->throttled_pelt_idle The rq_idle_lag part of the missing time is however an estimation that doesn't take into account IRQ and Paravirt time. [1] https://lore.kernel.org/all/20190709115759.10451-1-chris.redpath@arm.co= m/ Signed-off-by: Vincent Donnefort Signed-off-by: Vincent Donnefort diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 05614d9b919c..df5e6e565b4d 100644 Reviewed-by: Dietmar Eggemann Reviewed-by: Vincent Guittot --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3310,6 +3310,29 @@ static inline void cfs_rq_util_change(struct cfs_rq = *cfs_rq, int flags) } =20 #ifdef CONFIG_SMP +static inline bool load_avg_is_decayed(struct sched_avg *sa) +{ + if (sa->load_sum) + return false; + + if (sa->util_sum) + return false; + + if (sa->runnable_sum) + return false; + + /* + * _avg must be null when _sum are null because _avg =3D _sum / divider + * Make sure that rounding and/or propagation of PELT values never + * break this. + */ + SCHED_WARN_ON(sa->load_avg || + sa->util_avg || + sa->runnable_avg); + + return true; +} + static inline u64 cfs_rq_last_update_time(struct cfs_rq *cfs_rq) { return u64_u32_load_copy(cfs_rq->avg.last_update_time, @@ -3347,27 +3370,12 @@ static inline bool cfs_rq_is_decayed(struct cfs_rq = *cfs_rq) if (cfs_rq->load.weight) return false; =20 - if (cfs_rq->avg.load_sum) - return false; - - if (cfs_rq->avg.util_sum) - return false; - - if (cfs_rq->avg.runnable_sum) + if (!load_avg_is_decayed(&cfs_rq->avg)) return false; =20 if (child_cfs_rq_on_list(cfs_rq)) return false; =20 - /* - * _avg must be null when _sum are null because _avg =3D _sum / divider - * Make sure that rounding and/or propagation of PELT values never - * break this. - */ - SCHED_WARN_ON(cfs_rq->avg.load_avg || - cfs_rq->avg.util_avg || - cfs_rq->avg.runnable_avg); - return true; } =20 @@ -3706,6 +3714,88 @@ static inline void add_tg_cfs_propagate(struct cfs_r= q *cfs_rq, long runnable_sum =20 #endif /* CONFIG_FAIR_GROUP_SCHED */ =20 +#ifdef CONFIG_NO_HZ_COMMON +static inline void migrate_se_pelt_lag(struct sched_entity *se) +{ + u64 throttled =3D 0, now, lut; + struct cfs_rq *cfs_rq; + struct rq *rq; + bool is_idle; + + if (load_avg_is_decayed(&se->avg)) + return; + + cfs_rq =3D cfs_rq_of(se); + rq =3D rq_of(cfs_rq); + + rcu_read_lock(); + is_idle =3D is_idle_task(rcu_dereference(rq->curr)); + rcu_read_unlock(); + + /* + * The lag estimation comes with a cost we don't want to pay all the + * time. Hence, limiting to the case where the source CPU is idle and + * we know we are at the greatest risk to have an outdated clock. + */ + if (!is_idle) + return; + + /* + * Estimated "now" is: last_update_time + cfs_idle_lag + rq_idle_lag, whe= re: + * + * last_update_time (the cfs_rq's last_update_time) + * =3D cfs_rq_clock_pelt() + * =3D rq_clock_pelt() - cfs->throttled_clock_pelt_time + * + * cfs_idle_lag (delta between cfs_rq's update and rq's update) + * =3D rq_clock_pelt()@rq_idle - rq_clock_pelt()@cfs_rq_idle + * + * rq_idle_lag (delta between rq's update and now) + * =3D sched_clock_cpu() - rq_clock()@rq_idle + * + * The rq_clock_pelt() from last_update_time being the same as + * rq_clock_pelt()@cfs_rq_idle, we can write: + * + * now =3D rq_clock_pelt()@rq_idle - cfs->throttled_clock_pelt_time + + * sched_clock_cpu() - rq_clock()@rq_idle + * Where: + * rq_clock_pelt()@rq_idle is rq->clock_pelt_idle + * rq_clock()@rq_idle is rq->enter_idle + * cfs->throttled_clock_pelt_time is cfs_rq->throttled_pelt_idle + */ + +#ifdef CONFIG_CFS_BANDWIDTH + throttled =3D u64_u32_load(cfs_rq->throttled_pelt_idle); + /* The clock has been stopped for throttling */ + if (throttled =3D=3D U64_MAX) + return; +#endif + now =3D u64_u32_load(rq->clock_pelt_idle); + /* + * Paired with _update_idle_rq_clock_pelt. It ensures at the worst case + * is observed the old clock_pelt_idle value and the new enter_idle, + * which lead to an understimation. The opposite would lead to an + * overestimation. + */ + smp_rmb(); + lut =3D cfs_rq_last_update_time(cfs_rq); + + now -=3D throttled; + if (now < lut) + /* + * cfs_rq->avg.last_update_time is more recent than our + * estimation, let's use it. + */ + now =3D lut; + else + now +=3D sched_clock_cpu(cpu_of(rq)) - u64_u32_load(rq->enter_idle); + + __update_load_avg_blocked_se(now, se); +} +#else +static void migrate_se_pelt_lag(struct sched_entity *se) {} +#endif + /** * update_cfs_rq_load_avg - update the cfs_rq's load/util averages * @now: current time, as per cfs_rq_clock_pelt() @@ -4437,6 +4527,9 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_en= tity *se, int flags) */ if ((flags & (DEQUEUE_SAVE | DEQUEUE_MOVE)) !=3D DEQUEUE_SAVE) update_min_vruntime(cfs_rq); + + if (cfs_rq->nr_running =3D=3D 0) + update_idle_cfs_rq_clock_pelt(cfs_rq); } =20 /* @@ -6911,6 +7004,8 @@ static void detach_entity_cfs_rq(struct sched_entity = *se); */ static void migrate_task_rq_fair(struct task_struct *p, int new_cpu) { + struct sched_entity *se =3D &p->se; + /* * As blocked tasks retain absolute vruntime the migration needs to * deal with this by subtracting the old and adding the new @@ -6918,7 +7013,6 @@ static void migrate_task_rq_fair(struct task_struct *= p, int new_cpu) * the task on the new runqueue. */ if (READ_ONCE(p->__state) =3D=3D TASK_WAKING) { - struct sched_entity *se =3D &p->se; struct cfs_rq *cfs_rq =3D cfs_rq_of(se); =20 se->vruntime -=3D u64_u32_load(cfs_rq->min_vruntime); @@ -6930,25 +7024,29 @@ static void migrate_task_rq_fair(struct task_struct= *p, int new_cpu) * rq->lock and can modify state directly. */ lockdep_assert_rq_held(task_rq(p)); - detach_entity_cfs_rq(&p->se); + detach_entity_cfs_rq(se); =20 } else { + remove_entity_load_avg(se); + /* - * We are supposed to update the task to "current" time, then - * its up to date and ready to go to new CPU/cfs_rq. But we - * have difficulty in getting what current time is, so simply - * throw away the out-of-date time. This will result in the - * wakee task is less decayed, but giving the wakee more load - * sounds not bad. + * Here, the task's PELT values have been updated according to + * the current rq's clock. But if that clock hasn't been + * updated in a while, a substantial idle time will be missed, + * leading to an inflation after wake-up on the new rq. + * + * Estimate the missing time from the cfs_rq last_update_time + * and update sched_avg to improve the PELT continuity after + * migration. */ - remove_entity_load_avg(&p->se); + migrate_se_pelt_lag(se); } =20 /* Tell new CPU we are migrated */ - p->se.avg.last_update_time =3D 0; + se->avg.last_update_time =3D 0; =20 /* We have migrated, no longer consider this task hot */ - p->se.exec_start =3D 0; + se->exec_start =3D 0; =20 update_scan_period(p, new_cpu); } @@ -8114,6 +8212,10 @@ static bool __update_blocked_fair(struct rq *rq, boo= l *done) if (update_cfs_rq_load_avg(cfs_rq_clock_pelt(cfs_rq), cfs_rq)) { update_tg_load_avg(cfs_rq); =20 + /* sync clock_pelt_idle with last update */ + if (cfs_rq->nr_running =3D=3D 0) + update_idle_cfs_rq_clock_pelt(cfs_rq); + if (cfs_rq =3D=3D &rq->cfs) decayed =3D true; } diff --git a/kernel/sched/pelt.h b/kernel/sched/pelt.h index 4ff2ed4f8fa1..647e5fcc041b 100644 --- a/kernel/sched/pelt.h +++ b/kernel/sched/pelt.h @@ -61,6 +61,25 @@ static inline void cfs_se_util_change(struct sched_avg *= avg) WRITE_ONCE(avg->util_est.enqueued, enqueued); } =20 +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 to clock_task */ +static inline void _update_idle_rq_clock_pelt(struct rq *rq) +{ + rq->clock_pelt =3D rq_clock_task(rq); + + u64_u32_store(rq->enter_idle, rq_clock(rq)); + /* Paired with smp_rmb in migrate_se_pelt_lag */ + smp_wmb(); + u64_u32_store(rq->clock_pelt_idle, rq_clock_pelt(rq)); +} + /* * The clock_pelt scales the time to reflect the effective amount of * computation done during the running delta time but then sync back to @@ -76,8 +95,7 @@ static inline void cfs_se_util_change(struct sched_avg *a= vg) 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 =3D rq_clock_task(rq); + _update_idle_rq_clock_pelt(rq); return; } =20 @@ -130,17 +148,23 @@ static inline void update_idle_rq_clock_pelt(struct r= q *rq) */ if (util_sum >=3D divider) rq->lost_idle_time +=3D rq_clock_task(rq) - rq->clock_pelt; + + _update_idle_rq_clock_pelt(rq); } =20 -static inline u64 rq_clock_pelt(struct rq *rq) +#ifdef CONFIG_CFS_BANDWIDTH +static inline void update_idle_cfs_rq_clock_pelt(struct cfs_rq *cfs_rq) { - lockdep_assert_rq_held(rq); - assert_clock_updated(rq); + u64 throttled; =20 - return rq->clock_pelt - rq->lost_idle_time; + if (unlikely(cfs_rq->throttle_count)) + throttled =3D U64_MAX; + else + throttled =3D cfs_rq->throttled_clock_pelt_time; + + u64_u32_store(cfs_rq->throttled_pelt_idle, throttled); } =20 -#ifdef CONFIG_CFS_BANDWIDTH /* rq->task_clock normalized against any time this cfs_rq has spent thrott= led */ static inline u64 cfs_rq_clock_pelt(struct cfs_rq *cfs_rq) { @@ -150,6 +174,7 @@ static inline u64 cfs_rq_clock_pelt(struct cfs_rq *cfs_= rq) return rq_clock_pelt(rq_of(cfs_rq)) - cfs_rq->throttled_clock_pelt_time; } #else +static inline void update_idle_cfs_rq_clock_pelt(struct cfs_rq *cfs_rq) { } static inline u64 cfs_rq_clock_pelt(struct cfs_rq *cfs_rq) { return rq_clock_pelt(rq_of(cfs_rq)); @@ -204,6 +229,7 @@ update_rq_clock_pelt(struct rq *rq, s64 delta) { } static inline void update_idle_rq_clock_pelt(struct rq *rq) { } =20 +static inline void update_idle_cfs_rq_clock_pelt(struct cfs_rq *cfs_rq) { } #endif =20 =20 diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index bf4a0ec98678..97bc26e5c8af 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -648,6 +648,10 @@ struct cfs_rq { int runtime_enabled; s64 runtime_remaining; =20 + u64 throttled_pelt_idle; +#ifndef CONFIG_64BIT + u64 throttled_pelt_idle_copy; +#endif u64 throttled_clock; u64 throttled_clock_pelt; u64 throttled_clock_pelt_time; @@ -1020,6 +1024,12 @@ struct rq { u64 clock_task ____cacheline_aligned; u64 clock_pelt; unsigned long lost_idle_time; + u64 clock_pelt_idle; + u64 enter_idle; +#ifndef CONFIG_64BIT + u64 clock_pelt_idle_copy; + u64 enter_idle_copy; +#endif =20 atomic_t nr_iowait; =20 --=20 2.36.1.124.g0e6072fb45-goog From nobody Sun May 5 10:50:47 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7A2DFC433F5 for ; Mon, 23 May 2022 15:53:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238292AbiEWPxP (ORCPT ); Mon, 23 May 2022 11:53:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54976 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237202AbiEWPwl (ORCPT ); Mon, 23 May 2022 11:52:41 -0400 Received: from mail-wr1-x449.google.com (mail-wr1-x449.google.com [IPv6:2a00:1450:4864:20::449]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F18A735271 for ; Mon, 23 May 2022 08:52:39 -0700 (PDT) Received: by mail-wr1-x449.google.com with SMTP id bv12-20020a0560001f0c00b0020e359b3852so4117956wrb.14 for ; Mon, 23 May 2022 08:52:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=qAsrNl61R799m0R9dXJg9GXZ3YS+3h0QsLMp3kCdOTE=; b=JjxnURoHVKwtB3NEDRDMmujskrN0+vjKxRAASuVAQ5Zgk5UBjMU8A6THKPs2vAMobL PoVtb2j7HPIu+sh6eMMJstocQs5ZGxHDRaqhfbX/AXmVX7rE6eGHH/zPUVEKeZseBMT3 P+osc6wClTDBfeXj9RGaEqTjwOWmCbm8kR7nc8L6MVp9aRQ6QbZV6vLSKqcYLZ5qqBUP WJddAXNp80/tbMlKxQGQIh3wRkS8MhdaF3mNDjbDOBi4aT0kGx/mPwvEWxXtaWEgazQ/ hRY56E4vvYeEtx4EePqvENwz4it/x1yQcS4EMwV5D5Wq1eAscXy164HEgtpdcZjdZbYb eIIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=qAsrNl61R799m0R9dXJg9GXZ3YS+3h0QsLMp3kCdOTE=; b=7my80wFxkfl4woenba2kRP6Nm6EHe+ZAf7HyXOmCOwhdiauygTCRNl7nSUAYTYw8LM BrZm/FScOm71CPVdw7c9f6UrP+FQLDY0Iz+MtH2hkiB0GdGuhoXbFDQxacAt8R0qoD6j YDyPincmvvqISZalEHOzeyqOMEkYSa9lb1GllA6svr4hfDGMytOaPbofThIl2UBwBMYJ 3o46IoNynytUcYuqm+zRf+sIzaCIFrB9eIe8FK+fJ15ag9rPPvRO5HCO6waV6DXwdsSd rRbnmc/LBnmZebJY6QAmTIZjU577UIDIdRItSuoWT1B8XMCEaQuo83LoCUhqlRF/iJQo zxtg== X-Gm-Message-State: AOAM530mxwbNcsHCzIrOUZLdKsrrmm1Me5w7qqVvqUH6G7J7cF22Rq/4 ly2VzU3MslQaSOKJM4rvUbyvIXe0x8h8RN2L X-Google-Smtp-Source: ABdhPJw1yhdKjYcx43ZgUX2lj0p/e3mLZ/02uuyWx9+d7/lDdjScNvfLGZjVb6RsYitrbm0nXfyOUD6oMVIaxOhy X-Received: from vdonnefort.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:2eea]) (user=vdonnefort job=sendgmr) by 2002:a05:600c:3b04:b0:394:6150:db8f with SMTP id m4-20020a05600c3b0400b003946150db8fmr20470457wms.183.1653321158477; Mon, 23 May 2022 08:52:38 -0700 (PDT) Date: Mon, 23 May 2022 16:51:36 +0100 In-Reply-To: <20220523155140.2878563-1-vdonnefort@google.com> Message-Id: <20220523155140.2878563-4-vdonnefort@google.com> Mime-Version: 1.0 References: <20220523155140.2878563-1-vdonnefort@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH v9 3/7] sched, drivers: Remove max param from effective_cpu_util()/sched_cpu_util() From: Vincent Donnefort To: peterz@infradead.org, mingo@redhat.com, vincent.guittot@linaro.org Cc: linux-kernel@vger.kernel.org, dietmar.eggemann@arm.com, morten.rasmussen@arm.com, chris.redpath@arm.com, qperret@google.com, tao.zhou@linux.dev, kernel-team@android.com, vdonnefort@google.com Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Dietmar Eggemann effective_cpu_util() already has a `int cpu' parameter which allows to retrieve the CPU capacity scale factor (or maximum CPU capacity) inside this function via an arch_scale_cpu_capacity(cpu). A lot of code calling effective_cpu_util() (or the shim sched_cpu_util()) needs the maximum CPU capacity, i.e. it will call arch_scale_cpu_capacity() already. But not having to pass it into effective_cpu_util() will make the EAS wake-up code easier, especially when the maximum CPU capacity reduced by the thermal pressure is passed through the EAS wake-up functions. Due to the asymmetric CPU capacity support of arm/arm64 architectures, arch_scale_cpu_capacity(int cpu) is a per-CPU variable read access via per_cpu(cpu_scale, cpu) on such a system. On all other architectures it is a a compile-time constant (SCHED_CAPACITY_SCALE). Signed-off-by: Dietmar Eggemann diff --git a/drivers/powercap/dtpm_cpu.c b/drivers/powercap/dtpm_cpu.c index f5eced0842b3..6a88eb7e9f75 100644 Acked-by: Vincent Guittot --- a/drivers/powercap/dtpm_cpu.c +++ b/drivers/powercap/dtpm_cpu.c @@ -71,34 +71,19 @@ static u64 set_pd_power_limit(struct dtpm *dtpm, u64 po= wer_limit) =20 static u64 scale_pd_power_uw(struct cpumask *pd_mask, u64 power) { - unsigned long max =3D 0, sum_util =3D 0; + unsigned long max, sum_util =3D 0; int cpu; =20 - for_each_cpu_and(cpu, pd_mask, cpu_online_mask) { - - /* - * The capacity is the same for all CPUs belonging to - * the same perf domain, so a single call to - * arch_scale_cpu_capacity() is enough. However, we - * need the CPU parameter to be initialized by the - * loop, so the call ends up in this block. - * - * We can initialize 'max' with a cpumask_first() call - * before the loop but the bits computation is not - * worth given the arch_scale_cpu_capacity() just - * returns a value where the resulting assembly code - * will be optimized by the compiler. - */ - max =3D arch_scale_cpu_capacity(cpu); - sum_util +=3D sched_cpu_util(cpu, max); - } - /* - * In the improbable case where all the CPUs of the perf - * domain are offline, 'max' will be zero and will lead to an - * illegal operation with a zero division. + * The capacity is the same for all CPUs belonging to + * the same perf domain. */ - return max ? (power * ((sum_util << 10) / max)) >> 10 : 0; + max =3D arch_scale_cpu_capacity(cpumask_first(pd_mask)); + + for_each_cpu_and(cpu, pd_mask, cpu_online_mask) + sum_util +=3D sched_cpu_util(cpu); + + return (power * ((sum_util << 10) / max)) >> 10; } =20 static u64 get_pd_power_uw(struct dtpm *dtpm) diff --git a/drivers/thermal/cpufreq_cooling.c b/drivers/thermal/cpufreq_co= oling.c index b8151d95a806..b263b0fde03c 100644 --- a/drivers/thermal/cpufreq_cooling.c +++ b/drivers/thermal/cpufreq_cooling.c @@ -137,11 +137,9 @@ static u32 cpu_power_to_freq(struct cpufreq_cooling_de= vice *cpufreq_cdev, static u32 get_load(struct cpufreq_cooling_device *cpufreq_cdev, int cpu, int cpu_idx) { - unsigned long max =3D arch_scale_cpu_capacity(cpu); - unsigned long util; + unsigned long util =3D sched_cpu_util(cpu); =20 - util =3D sched_cpu_util(cpu, max); - return (util * 100) / max; + return (util * 100) / arch_scale_cpu_capacity(cpu); } #else /* !CONFIG_SMP */ static u32 get_load(struct cpufreq_cooling_device *cpufreq_cdev, int cpu, diff --git a/include/linux/sched.h b/include/linux/sched.h index c46f3a63b758..88b8817b827d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2257,7 +2257,7 @@ static inline bool owner_on_cpu(struct task_struct *o= wner) } =20 /* Returns effective CPU energy utilization, as seen by the scheduler */ -unsigned long sched_cpu_util(int cpu, unsigned long max); +unsigned long sched_cpu_util(int cpu); #endif /* CONFIG_SMP */ =20 #ifdef CONFIG_RSEQ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 53596842f0d8..c531976ee960 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7107,12 +7107,14 @@ struct task_struct *idle_task(int cpu) * required to meet deadlines. */ unsigned long effective_cpu_util(int cpu, unsigned long util_cfs, - unsigned long max, enum cpu_util_type type, + enum cpu_util_type type, struct task_struct *p) { - unsigned long dl_util, util, irq; + unsigned long dl_util, util, irq, max; struct rq *rq =3D cpu_rq(cpu); =20 + max =3D arch_scale_cpu_capacity(cpu); + if (!uclamp_is_used() && type =3D=3D FREQUENCY_UTIL && rt_rq_is_runnable(&rq->rt)) { return max; @@ -7192,10 +7194,9 @@ unsigned long effective_cpu_util(int cpu, unsigned l= ong util_cfs, return min(max, util); } =20 -unsigned long sched_cpu_util(int cpu, unsigned long max) +unsigned long sched_cpu_util(int cpu) { - return effective_cpu_util(cpu, cpu_util_cfs(cpu), max, - ENERGY_UTIL, NULL); + return effective_cpu_util(cpu, cpu_util_cfs(cpu), ENERGY_UTIL, NULL); } #endif /* CONFIG_SMP */ =20 diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedu= til.c index 3dbf351d12d5..1207c78f85c1 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -157,11 +157,10 @@ static unsigned int get_next_freq(struct sugov_policy= *sg_policy, static void sugov_get_util(struct sugov_cpu *sg_cpu) { struct rq *rq =3D cpu_rq(sg_cpu->cpu); - unsigned long max =3D arch_scale_cpu_capacity(sg_cpu->cpu); =20 - sg_cpu->max =3D max; + sg_cpu->max =3D arch_scale_cpu_capacity(sg_cpu->cpu); sg_cpu->bw_dl =3D cpu_bw_dl(rq); - sg_cpu->util =3D effective_cpu_util(sg_cpu->cpu, cpu_util_cfs(sg_cpu->cpu= ), max, + sg_cpu->util =3D effective_cpu_util(sg_cpu->cpu, cpu_util_cfs(sg_cpu->cpu= ), FREQUENCY_UTIL, NULL); } =20 diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index df5e6e565b4d..73a9dc522b73 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6703,12 +6703,11 @@ static long compute_energy(struct task_struct *p, int dst_cpu, struct perf_domain *pd) { struct cpumask *pd_mask =3D perf_domain_span(pd); - unsigned long cpu_cap =3D arch_scale_cpu_capacity(cpumask_first(pd_mask)); - unsigned long max_util =3D 0, sum_util =3D 0; - unsigned long _cpu_cap =3D cpu_cap; + unsigned long max_util =3D 0, sum_util =3D 0, cpu_cap; int cpu; =20 - _cpu_cap -=3D arch_scale_thermal_pressure(cpumask_first(pd_mask)); + cpu_cap =3D arch_scale_cpu_capacity(cpumask_first(pd_mask)); + cpu_cap -=3D arch_scale_thermal_pressure(cpumask_first(pd_mask)); =20 /* * The capacity state of CPUs of the current rd can be driven by CPUs @@ -6745,10 +6744,10 @@ compute_energy(struct task_struct *p, int dst_cpu, = struct perf_domain *pd) * is already enough to scale the EM reported power * consumption at the (eventually clamped) cpu_capacity. */ - cpu_util =3D effective_cpu_util(cpu, util_running, cpu_cap, - ENERGY_UTIL, NULL); + cpu_util =3D effective_cpu_util(cpu, util_running, ENERGY_UTIL, + NULL); =20 - sum_util +=3D min(cpu_util, _cpu_cap); + sum_util +=3D min(cpu_util, cpu_cap); =20 /* * Performance domain frequency: utilization clamping @@ -6757,12 +6756,12 @@ compute_energy(struct task_struct *p, int dst_cpu, = struct perf_domain *pd) * NOTE: in case RT tasks are running, by default the * FREQUENCY_UTIL's utilization can be max OPP. */ - cpu_util =3D effective_cpu_util(cpu, util_freq, cpu_cap, - FREQUENCY_UTIL, tsk); - max_util =3D max(max_util, min(cpu_util, _cpu_cap)); + cpu_util =3D effective_cpu_util(cpu, util_freq, FREQUENCY_UTIL, + tsk); + max_util =3D max(max_util, min(cpu_util, cpu_cap)); } =20 - return em_cpu_energy(pd->em_pd, max_util, sum_util, _cpu_cap); + return em_cpu_energy(pd->em_pd, max_util, sum_util, cpu_cap); } =20 /* diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 97bc26e5c8af..07b7c50bd987 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2895,7 +2895,7 @@ enum cpu_util_type { }; =20 unsigned long effective_cpu_util(int cpu, unsigned long util_cfs, - unsigned long max, enum cpu_util_type type, + enum cpu_util_type type, struct task_struct *p); =20 static inline unsigned long cpu_bw_dl(struct rq *rq) --=20 2.36.1.124.g0e6072fb45-goog From nobody Sun May 5 10:50:47 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 76804C433F5 for ; Mon, 23 May 2022 15:53:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238300AbiEWPxW (ORCPT ); Mon, 23 May 2022 11:53:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55078 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234924AbiEWPwm (ORCPT ); Mon, 23 May 2022 11:52:42 -0400 Received: from mail-wm1-x349.google.com (mail-wm1-x349.google.com [IPv6:2a00:1450:4864:20::349]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E85533C713 for ; Mon, 23 May 2022 08:52:41 -0700 (PDT) Received: by mail-wm1-x349.google.com with SMTP id l15-20020a05600c1d0f00b003973901d3b4so2946772wms.2 for ; Mon, 23 May 2022 08:52:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Xvz8pTdOu4fQbxYqVMwf3pWiSGQYuX7yCHLpo5kL/L4=; b=SKL1Kklk7s8dSDdvMgS8eeSgC61HXgt2xH5Wl/6cGOyIdVc1bvXM/TaE4dskgCo3zp YAOVkWLsMwZY1MdvQmgrY0g90vHbmFB7O5tilQa7XHCgxyoz5EQrunxngyJX0gRyJOnr j9v9J0KrSC7XPDIK6da5ZxOjfp20MHezR/XsRqgBRk09vz6b6xv7h53ygZv+fTNVKMq7 W9a/KrseFSAZx7jwXMUgYxVAx51lhTC6eHLHolzZ7OzK9oCy/VHNMnvfigheAqeDGBQo hEaG7P9i4u8XLEnqkBREH/5JAuNHp+eLPEhrYONoic0XEOM/sbse28D0teIvdp8Ouh3X iz0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Xvz8pTdOu4fQbxYqVMwf3pWiSGQYuX7yCHLpo5kL/L4=; b=ciMF1xW0ypoUYRKXWM4NSwJHWt5TzY2CHX/3SidWp107HOwkn4V1QyMedW24n/rzqO ZJd8qHgwSV2yw2STv9QLqZ/oXJs9WGHNOlr0NmrR3LlC5gkISQfvyGpI2ArUbbMmSMC/ yme7nPldoYYZTYsmMUfVPzGfHkBGO+7T+KfvONm9SV8Ci+HEY5/LGXnk3X3WCicy2j9x DYsnZDTavXNidhlR/sKGE1zPHQSXug10fPJFsHK3rAW5BwUZNE3bR+c921RpoVTV17lm dSwNxBGhgsj4Z35mZDVRWAFipok4lZZGiRLeLW0MUqOeGYQccDXJbct7qT33UJw9xQVU LArw== X-Gm-Message-State: AOAM531H4wRZRRQYnq+sgpjLwUA2NE4AX9qvkO5MQDbpmozRYWVvdmEe dQ/LRNKv04C2CGKhCmneH10++Al+3cDVeA/5 X-Google-Smtp-Source: ABdhPJzRWZfmINL35YWInJJwa4PhFHQMF9nvBoWzHY/4bHuTgOw/kokAQxUePqg8MGIpDYKtDFk2mcZWWiseKkQb X-Received: from vdonnefort.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:2eea]) (user=vdonnefort job=sendgmr) by 2002:a05:600c:3d8b:b0:397:469c:7bbd with SMTP id bi11-20020a05600c3d8b00b00397469c7bbdmr8345997wmb.62.1653321160524; Mon, 23 May 2022 08:52:40 -0700 (PDT) Date: Mon, 23 May 2022 16:51:37 +0100 In-Reply-To: <20220523155140.2878563-1-vdonnefort@google.com> Message-Id: <20220523155140.2878563-5-vdonnefort@google.com> Mime-Version: 1.0 References: <20220523155140.2878563-1-vdonnefort@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH v9 4/7] sched/fair: Rename select_idle_mask to select_rq_mask From: Vincent Donnefort To: peterz@infradead.org, mingo@redhat.com, vincent.guittot@linaro.org Cc: linux-kernel@vger.kernel.org, dietmar.eggemann@arm.com, morten.rasmussen@arm.com, chris.redpath@arm.com, qperret@google.com, tao.zhou@linux.dev, kernel-team@android.com, vdonnefort@google.com Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Dietmar Eggemann Decouple the name of the per-cpu cpumask select_idle_mask from its usage in select_idle_[cpu/capacity]() of the CFS run-queue selection (select_task_rq_fair()). This is to support the reuse of this cpumask in the Energy Aware Scheduling (EAS) path (find_energy_efficient_cpu()) of the CFS run-queue selection. Signed-off-by: Dietmar Eggemann diff --git a/kernel/sched/core.c b/kernel/sched/core.c index c531976ee960..68f5eb8a1de7 100644 Reviewed-by: Vincent Guittot --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -9502,7 +9502,7 @@ static struct kmem_cache *task_group_cache __read_mos= tly; #endif =20 DECLARE_PER_CPU(cpumask_var_t, load_balance_mask); -DECLARE_PER_CPU(cpumask_var_t, select_idle_mask); +DECLARE_PER_CPU(cpumask_var_t, select_rq_mask); =20 void __init sched_init(void) { @@ -9551,7 +9551,7 @@ void __init sched_init(void) for_each_possible_cpu(i) { per_cpu(load_balance_mask, i) =3D (cpumask_var_t)kzalloc_node( cpumask_size(), GFP_KERNEL, cpu_to_node(i)); - per_cpu(select_idle_mask, i) =3D (cpumask_var_t)kzalloc_node( + per_cpu(select_rq_mask, i) =3D (cpumask_var_t)kzalloc_node( cpumask_size(), GFP_KERNEL, cpu_to_node(i)); } #endif /* CONFIG_CPUMASK_OFFSTACK */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 73a9dc522b73..2d7bba2f1da2 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5897,7 +5897,7 @@ static void dequeue_task_fair(struct rq *rq, struct t= ask_struct *p, int flags) =20 /* Working cpumask for: load_balance, load_balance_newidle. */ DEFINE_PER_CPU(cpumask_var_t, load_balance_mask); -DEFINE_PER_CPU(cpumask_var_t, select_idle_mask); +DEFINE_PER_CPU(cpumask_var_t, select_rq_mask); =20 #ifdef CONFIG_NO_HZ_COMMON =20 @@ -6387,7 +6387,7 @@ static inline int select_idle_smt(struct task_struct = *p, struct sched_domain *sd */ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd,= bool has_idle_core, int target) { - struct cpumask *cpus =3D this_cpu_cpumask_var_ptr(select_idle_mask); + struct cpumask *cpus =3D this_cpu_cpumask_var_ptr(select_rq_mask); int i, cpu, idle_cpu =3D -1, nr =3D INT_MAX; struct rq *this_rq =3D this_rq(); int this =3D smp_processor_id(); @@ -6473,7 +6473,7 @@ select_idle_capacity(struct task_struct *p, struct sc= hed_domain *sd, int target) int cpu, best_cpu =3D -1; struct cpumask *cpus; =20 - cpus =3D this_cpu_cpumask_var_ptr(select_idle_mask); + cpus =3D this_cpu_cpumask_var_ptr(select_rq_mask); cpumask_and(cpus, sched_domain_span(sd), p->cpus_ptr); =20 task_util =3D uclamp_task_util(p); --=20 2.36.1.124.g0e6072fb45-goog From nobody Sun May 5 10:50:47 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3F1EDC433F5 for ; Mon, 23 May 2022 15:53:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238286AbiEWPx3 (ORCPT ); Mon, 23 May 2022 11:53:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54880 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238271AbiEWPwv (ORCPT ); Mon, 23 May 2022 11:52:51 -0400 Received: from mail-wm1-x34a.google.com (mail-wm1-x34a.google.com [IPv6:2a00:1450:4864:20::34a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1C9D722500 for ; Mon, 23 May 2022 08:52:44 -0700 (PDT) Received: by mail-wm1-x34a.google.com with SMTP id k5-20020a05600c0b4500b003941ca130f9so6064190wmr.0 for ; Mon, 23 May 2022 08:52:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=MjDJsLnT59g4872Q3lt9Zm37fZ4mNI7JXqzvW6ZT7os=; b=k6LzOD7lnD7O0uSf5smVZlRnelOmGfyMEvSaAQOqTDrf22Kp5q+M7woMSMG5dFKvyC oEB+yOM/2xn6X2wz8JGriYWz7wWQmRMRIG4/COPYtwK3ALt6G91UrlnadO2FVLUTtoW6 c2QbVEbLrgDUetpAxAPMp1eG8TcQwiyt8lvPevg6rfiPOXwMLG+mbktAJFMClLQTmzrs +Y0IB5JpNUKBW7uE296saAt6QIuwpPhH1lAJPwrH4JTvWK31Au8Dasg+8JxUPU7is9Uk o5VS6I1FcOKb8Zyx81mh5PMX+YmelX8x2Y9rBKtwIyZgUuKha2xgT60sKG4Km9mBeI/u MW+g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=MjDJsLnT59g4872Q3lt9Zm37fZ4mNI7JXqzvW6ZT7os=; b=NVZbcsJqiYkMYLpxZwKMzD3K8uNbgu8KsyH7iCrxhA06OEph68Nhr/010egDibAaHz xh3LN5JwcFlPvpAe/mWhoFRHe5cjlhsjFu4K19bHCrHZrSepJhuhEOPdxvr4DKpW/hBV cJave2HWH7aPn6i3+9ixp38hnPyyQVOL2PkoP4Fmx4VxyQgU4+0Kso6vueeCm7IcV00Y dxZXHrrois10QtL1EIX+1dMhR9PTzu5DfH0HExYDor3RjRLcW7PEhbr/3nWob/fuTMlh 8Sbj+mwQjUUKe/+YUJy907hwryxo7z34Tzso61117YOCLtTcvtYzLtjUoZJ5s4Kqq0vv sVSg== X-Gm-Message-State: AOAM532TxeAHeAW1wTg4KpbXXiYdpjghhGASYt/KxJHnxXOz7QEYMJ+W wMQ27DoNnS7xTKhoJkRWxecVatNEfskCzsfv X-Google-Smtp-Source: ABdhPJyjIfbild4KI6OWBXv83Cutzr0T1BPrCrMfX9mm09aGpM5I5h2QkCDpbC46depJfLEONBOxZ4ILZUQFvpaR X-Received: from vdonnefort.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:2eea]) (user=vdonnefort job=sendgmr) by 2002:a05:6000:1e1a:b0:20f:c403:9180 with SMTP id bj26-20020a0560001e1a00b0020fc4039180mr11277074wrb.147.1653321162622; Mon, 23 May 2022 08:52:42 -0700 (PDT) Date: Mon, 23 May 2022 16:51:38 +0100 In-Reply-To: <20220523155140.2878563-1-vdonnefort@google.com> Message-Id: <20220523155140.2878563-6-vdonnefort@google.com> Mime-Version: 1.0 References: <20220523155140.2878563-1-vdonnefort@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH v9 5/7] sched/fair: Use the same cpumask per-PD throughout find_energy_efficient_cpu() From: Vincent Donnefort To: peterz@infradead.org, mingo@redhat.com, vincent.guittot@linaro.org Cc: linux-kernel@vger.kernel.org, dietmar.eggemann@arm.com, morten.rasmussen@arm.com, chris.redpath@arm.com, qperret@google.com, tao.zhou@linux.dev, kernel-team@android.com, vdonnefort@google.com Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Dietmar Eggemann The Perf Domain (PD) cpumask (struct em_perf_domain.cpus) stays invariant after Energy Model creation, i.e. it is not updated after CPU hotplug operations. That's why the PD mask is used in conjunction with the cpu_online_mask (or Sched Domain cpumask). Thereby the cpu_online_mask is fetched multiple times (in compute_energy()) during a run-queue selection for a task. cpu_online_mask may change during this time which can lead to wrong energy calculations. To be able to avoid this, use the select_rq_mask per-cpu cpumask to create a cpumask out of PD cpumask and cpu_online_mask and pass it through the function calls of the EAS run-queue selection path. The PD cpumask for max_spare_cap_cpu/compute_prev_delta selection (find_energy_efficient_cpu()) is now ANDed not only with the SD mask but also with the cpu_online_mask. This is fine since this cpumask has to be in syc with the one used for energy computation (compute_energy()). An exclusive cpuset setup with at least one asymmetric CPU capacity island (hence the additional AND with the SD cpumask) is the obvious exception here. Signed-off-by: Dietmar Eggemann diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 2d7bba2f1da2..57074f27c0d2 100644 Reviewed-by: Vincent Guittot --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6700,14 +6700,14 @@ static unsigned long cpu_util_without(int cpu, stru= ct task_struct *p) * task. */ static long -compute_energy(struct task_struct *p, int dst_cpu, struct perf_domain *pd) +compute_energy(struct task_struct *p, int dst_cpu, struct cpumask *cpus, + struct perf_domain *pd) { - struct cpumask *pd_mask =3D perf_domain_span(pd); unsigned long max_util =3D 0, sum_util =3D 0, cpu_cap; int cpu; =20 - cpu_cap =3D arch_scale_cpu_capacity(cpumask_first(pd_mask)); - cpu_cap -=3D arch_scale_thermal_pressure(cpumask_first(pd_mask)); + cpu_cap =3D arch_scale_cpu_capacity(cpumask_first(cpus)); + cpu_cap -=3D arch_scale_thermal_pressure(cpumask_first(cpus)); =20 /* * The capacity state of CPUs of the current rd can be driven by CPUs @@ -6718,7 +6718,7 @@ compute_energy(struct task_struct *p, int dst_cpu, st= ruct perf_domain *pd) * If an entire pd is outside of the current rd, it will not appear in * its pd list and will not be accounted by compute_energy(). */ - for_each_cpu_and(cpu, pd_mask, cpu_online_mask) { + for_each_cpu(cpu, cpus) { unsigned long util_freq =3D cpu_util_next(cpu, p, dst_cpu); unsigned long cpu_util, util_running =3D util_freq; struct task_struct *tsk =3D NULL; @@ -6805,6 +6805,7 @@ compute_energy(struct task_struct *p, int dst_cpu, st= ruct perf_domain *pd) */ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu) { + struct cpumask *cpus =3D this_cpu_cpumask_var_ptr(select_rq_mask); unsigned long prev_delta =3D ULONG_MAX, best_delta =3D ULONG_MAX; struct root_domain *rd =3D cpu_rq(smp_processor_id())->rd; int cpu, best_energy_cpu =3D prev_cpu, target =3D -1; @@ -6839,7 +6840,9 @@ static int find_energy_efficient_cpu(struct task_stru= ct *p, int prev_cpu) unsigned long base_energy_pd; int max_spare_cap_cpu =3D -1; =20 - for_each_cpu_and(cpu, perf_domain_span(pd), sched_domain_span(sd)) { + cpumask_and(cpus, perf_domain_span(pd), cpu_online_mask); + + for_each_cpu_and(cpu, cpus, sched_domain_span(sd)) { if (!cpumask_test_cpu(cpu, p->cpus_ptr)) continue; =20 @@ -6876,12 +6879,12 @@ static int find_energy_efficient_cpu(struct task_st= ruct *p, int prev_cpu) continue; =20 /* Compute the 'base' energy of the pd, without @p */ - base_energy_pd =3D compute_energy(p, -1, pd); + base_energy_pd =3D compute_energy(p, -1, cpus, pd); base_energy +=3D base_energy_pd; =20 /* Evaluate the energy impact of using prev_cpu. */ if (compute_prev_delta) { - prev_delta =3D compute_energy(p, prev_cpu, pd); + prev_delta =3D compute_energy(p, prev_cpu, cpus, pd); if (prev_delta < base_energy_pd) goto unlock; prev_delta -=3D base_energy_pd; @@ -6890,7 +6893,8 @@ static int find_energy_efficient_cpu(struct task_stru= ct *p, int prev_cpu) =20 /* Evaluate the energy impact of using max_spare_cap_cpu. */ if (max_spare_cap_cpu >=3D 0) { - cur_delta =3D compute_energy(p, max_spare_cap_cpu, pd); + cur_delta =3D compute_energy(p, max_spare_cap_cpu, cpus, + pd); if (cur_delta < base_energy_pd) goto unlock; cur_delta -=3D base_energy_pd; --=20 2.36.1.124.g0e6072fb45-goog From nobody Sun May 5 10:50:47 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A1B39C433FE for ; Mon, 23 May 2022 15:53:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238355AbiEWPxc (ORCPT ); Mon, 23 May 2022 11:53:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55864 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238211AbiEWPwx (ORCPT ); Mon, 23 May 2022 11:52:53 -0400 Received: from mail-wm1-x349.google.com (mail-wm1-x349.google.com [IPv6:2a00:1450:4864:20::349]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 693833FD93 for ; Mon, 23 May 2022 08:52:46 -0700 (PDT) Received: by mail-wm1-x349.google.com with SMTP id r186-20020a1c2bc3000000b00396fee62505so89172wmr.0 for ; Mon, 23 May 2022 08:52:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=sq6qYob0wcZUZdQ86DK53vRSZb/VW+I5U1fiY6MCwtE=; b=eCUJCF/3zM4/hgFRtnORm6GFFFKSgBFQlTBXAdlLbx2lpv21dd+njQCLZu5DwXAjIL jcCXSWUUwDmbSM7NIds6YzO//vMx9sdVWA5kkAdKWb9XV6FVrytZc4labgb7PUnZPhWU 5F5VekjBMNmRuWjYr6M67tRJypQubRpHH4EDND6dUEJ1qXSatxAFz7bi9huTGzJrxtsh Kr1mRRRelc2e/jTtr9aAPnIu1+FgZy+tcOAweN0+3oofpIGoXfkDakKUC5uf+Jd6Crf1 g4Xwk+07k7+zfDKkgAqSETOjG5HR8mJQf/8T9ubiZz9XbJiLDPNL699nISXhVsZNdg+8 7GcQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=sq6qYob0wcZUZdQ86DK53vRSZb/VW+I5U1fiY6MCwtE=; b=7cSKz0Y0wM4QcAPweQZFrdHE33murQJYKtWC+2/8onAcBXCtUMGYYDkyIm/etT0XL5 kwHS9oHWIRrUB0ZX/IaFW1sNxBw+nWcKUdLxrKmFROF/OkBT8bZJJM1TVNNXLLzeudsR gu23uW2eRkbWm9YYZNkrwKGj2yfrJrMMQQvSJaQxSytBifnG/nQCjkGN2ro3YeG3MCH6 BS8f5KWtEsgITIEID3YTwQiB4+hteyFGuIzEg64yAhzXB52pg9BhWKYoBPuqvkDQnJOB gqqi135Gky58PqM7KyJHr5Pl3mMHbUzxg/pUpr1ELyiYyeDMjDQQERYjTRTdcJM9Pi7D bT9w== X-Gm-Message-State: AOAM530EgEqmnwtLmSBdHwJgaJAWXpdnTcPrQ/mVWGhAGW8lv4SdTw/3 59uFjkWbbwiY2hRhFxur6Cx682otgv2evRA2 X-Google-Smtp-Source: ABdhPJypLXUVf/srJDsmBXB3eA9X66+D0wMbz8GYQyPmig2ucaqFRcJ0tB1dcOIA7qnibN5L2z7m2LvT2LXO1dUc X-Received: from vdonnefort.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:2eea]) (user=vdonnefort job=sendgmr) by 2002:a1c:acc4:0:b0:392:9dd4:fbcc with SMTP id v187-20020a1cacc4000000b003929dd4fbccmr20218636wme.78.1653321164829; Mon, 23 May 2022 08:52:44 -0700 (PDT) Date: Mon, 23 May 2022 16:51:39 +0100 In-Reply-To: <20220523155140.2878563-1-vdonnefort@google.com> Message-Id: <20220523155140.2878563-7-vdonnefort@google.com> Mime-Version: 1.0 References: <20220523155140.2878563-1-vdonnefort@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH v9 6/7] sched/fair: Remove task_util from effective utilization in feec() From: Vincent Donnefort To: peterz@infradead.org, mingo@redhat.com, vincent.guittot@linaro.org Cc: linux-kernel@vger.kernel.org, dietmar.eggemann@arm.com, morten.rasmussen@arm.com, chris.redpath@arm.com, qperret@google.com, tao.zhou@linux.dev, kernel-team@android.com, vdonnefort@google.com, Vincent Donnefort Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Vincent Donnefort The energy estimation in find_energy_efficient_cpu() (feec()) relies on the computation of the effective utilization for each CPU of a perf domain (PD). This effective utilization is then used as an estimation of the busy time for this pd. The function effective_cpu_util() which gives this value, scales the utilization relative to IRQ pressure on the CPU to take into account that the IRQ time is hidden from the task clock. The IRQ scaling is as follow: effective_cpu_util =3D irq + (cpu_cap - irq)/cpu_cap * util Where util is the sum of CFS/RT/DL utilization, cpu_cap the capacity of the CPU and irq the IRQ avg time. If now we take as an example a task placement which doesn't raise the OPP on the candidate CPU, we can write the energy delta as: delta =3D OPPcost/cpu_cap * (effective_cpu_util(cpu_util + task_util) - effective_cpu_util(cpu_util)) =3D OPPcost/cpu_cap * (cpu_cap - irq)/cpu_cap * task_util We end-up with an energy delta depending on the IRQ avg time, which is a problem: first the time spent on IRQs by a CPU has no effect on the additional energy that would be consumed by a task. Second, we don't want to favour a CPU with a higher IRQ avg time value. Nonetheless, we need to take the IRQ avg time into account. If a task placement raises the PD's frequency, it will increase the energy cost for the entire time where the CPU is busy. A solution is to only use effective_cpu_util() with the CPU contribution part. The task contribution is added separately and scaled according to prev_cpu's IRQ time. No change for the FREQUENCY_UTIL component of the energy estimation. We still want to get the actual frequency that would be selected after the task placement. Signed-off-by: Vincent Donnefort Signed-off-by: Vincent Donnefort Reviewed-by: Dietmar Eggemann diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 57074f27c0d2..5586b6848858 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6693,61 +6693,96 @@ static unsigned long cpu_util_without(int cpu, stru= ct task_struct *p) } =20 /* - * compute_energy(): Estimates the energy that @pd would consume if @p was - * migrated to @dst_cpu. compute_energy() predicts what will be the utiliz= ation - * landscape of @pd's CPUs after the task migration, and uses the Energy M= odel - * to compute what would be the energy if we decided to actually migrate t= hat - * task. + * energy_env - Utilization landscape for energy estimation. + * @task_busy_time: Utilization contribution by the task for which we test= the + * placement. Given by eenv_task_busy_time(). + * @pd_busy_time: Utilization of the whole perf domain without the task + * contribution. Given by eenv_pd_busy_time(). + * @cpu_cap: Maximum CPU capacity for the perf domain. + * @pd_cap: Entire perf domain capacity. (pd->nr_cpus * cpu_cap). + */ +struct energy_env { + unsigned long task_busy_time; + unsigned long pd_busy_time; + unsigned long cpu_cap; + unsigned long pd_cap; +}; + +/* + * Compute the task busy time for compute_energy(). This time cannot be + * injected directly into effective_cpu_util() because of the IRQ scaling. + * The latter only makes sense with the most recent CPUs where the task has + * run. + */ +static inline void eenv_task_busy_time(struct energy_env *eenv, + struct task_struct *p, int prev_cpu) +{ + unsigned long busy_time, max_cap =3D arch_scale_cpu_capacity(prev_cpu); + unsigned long irq =3D cpu_util_irq(cpu_rq(prev_cpu)); + + if (unlikely(irq >=3D max_cap)) + busy_time =3D max_cap; + else + busy_time =3D scale_irq_capacity(task_util_est(p), irq, max_cap); + + eenv->task_busy_time =3D busy_time; +} + +/* + * Compute the perf_domain (PD) busy time for compute_energy(). Based on t= he + * utilization for each @pd_cpus, it however doesn't take into account + * clamping since the ratio (utilization / cpu_capacity) is already enough= to + * scale the EM reported power consumption at the (eventually clamped) + * cpu_capacity. + * + * The contribution of the task @p for which we want to estimate the + * energy cost is removed (by cpu_util_next()) and must be calculated + * separately (see eenv_task_busy_time). This ensures: + * + * - A stable PD utilization, no matter which CPU of that PD we want to = place + * the task on. + * + * - A fair comparison between CPUs as the task contribution (task_util(= )) + * will always be the same no matter which CPU utilization we rely on + * (util_avg or util_est). + * + * Set @eenv busy time for the PD that spans @pd_cpus. This busy time can't + * exceed @eenv->pd_cap. */ -static long -compute_energy(struct task_struct *p, int dst_cpu, struct cpumask *cpus, - struct perf_domain *pd) +static inline void eenv_pd_busy_time(struct energy_env *eenv, + struct cpumask *pd_cpus, + struct task_struct *p) { - unsigned long max_util =3D 0, sum_util =3D 0, cpu_cap; + unsigned long busy_time =3D 0; int cpu; =20 - cpu_cap =3D arch_scale_cpu_capacity(cpumask_first(cpus)); - cpu_cap -=3D arch_scale_thermal_pressure(cpumask_first(cpus)); + for_each_cpu(cpu, pd_cpus) { + unsigned long util =3D cpu_util_next(cpu, p, -1); =20 - /* - * The capacity state of CPUs of the current rd can be driven by CPUs - * of another rd if they belong to the same pd. So, account for the - * utilization of these CPUs too by masking pd with cpu_online_mask - * instead of the rd span. - * - * If an entire pd is outside of the current rd, it will not appear in - * its pd list and will not be accounted by compute_energy(). - */ - for_each_cpu(cpu, cpus) { - unsigned long util_freq =3D cpu_util_next(cpu, p, dst_cpu); - unsigned long cpu_util, util_running =3D util_freq; - struct task_struct *tsk =3D NULL; + busy_time +=3D effective_cpu_util(cpu, util, ENERGY_UTIL, NULL); + } =20 - /* - * When @p is placed on @cpu: - * - * util_running =3D max(cpu_util, cpu_util_est) + - * max(task_util, _task_util_est) - * - * while cpu_util_next is: max(cpu_util + task_util, - * cpu_util_est + _task_util_est) - */ - if (cpu =3D=3D dst_cpu) { - tsk =3D p; - util_running =3D - cpu_util_next(cpu, p, -1) + task_util_est(p); - } + eenv->pd_busy_time =3D min(eenv->pd_cap, busy_time); +} =20 - /* - * Busy time computation: utilization clamping is not - * required since the ratio (sum_util / cpu_capacity) - * is already enough to scale the EM reported power - * consumption at the (eventually clamped) cpu_capacity. - */ - cpu_util =3D effective_cpu_util(cpu, util_running, ENERGY_UTIL, - NULL); +/* + * Compute the maximum utilization for compute_energy() when the task @p + * is placed on the cpu @dst_cpu. + * + * Returns the maximum utilization among @eenv->cpus. This utilization can= 't + * exceed @eenv->cpu_cap. + */ +static inline unsigned long +eenv_pd_max_util(struct energy_env *eenv, struct cpumask *pd_cpus, + struct task_struct *p, int dst_cpu) +{ + unsigned long max_util =3D 0; + int cpu; =20 - sum_util +=3D min(cpu_util, cpu_cap); + for_each_cpu(cpu, pd_cpus) { + struct task_struct *tsk =3D (cpu =3D=3D dst_cpu) ? p : NULL; + unsigned long util =3D cpu_util_next(cpu, p, dst_cpu); + unsigned long cpu_util; =20 /* * Performance domain frequency: utilization clamping @@ -6756,12 +6791,29 @@ compute_energy(struct task_struct *p, int dst_cpu, = struct cpumask *cpus, * NOTE: in case RT tasks are running, by default the * FREQUENCY_UTIL's utilization can be max OPP. */ - cpu_util =3D effective_cpu_util(cpu, util_freq, FREQUENCY_UTIL, - tsk); - max_util =3D max(max_util, min(cpu_util, cpu_cap)); + cpu_util =3D effective_cpu_util(cpu, util, FREQUENCY_UTIL, tsk); + max_util =3D max(max_util, cpu_util); } =20 - return em_cpu_energy(pd->em_pd, max_util, sum_util, cpu_cap); + return min(max_util, eenv->cpu_cap); +} + +/* + * compute_energy(): Use the Energy Model to estimate the energy that @pd = would + * consume for a given utilization landscape @eenv. If @dst_cpu < 0 the ta= sk + * contribution is removed from the energy estimation. + */ +static inline unsigned long +compute_energy(struct energy_env *eenv, struct perf_domain *pd, + struct cpumask *pd_cpus, struct task_struct *p, int dst_cpu) +{ + unsigned long max_util =3D eenv_pd_max_util(eenv, pd_cpus, p, dst_cpu); + unsigned long busy_time =3D eenv->pd_busy_time; + + if (dst_cpu >=3D 0) + busy_time =3D min(eenv->pd_cap, busy_time + eenv->task_busy_time); + + return em_cpu_energy(pd->em_pd, max_util, busy_time, eenv->cpu_cap); } =20 /* @@ -6807,11 +6859,12 @@ static int find_energy_efficient_cpu(struct task_st= ruct *p, int prev_cpu) { struct cpumask *cpus =3D this_cpu_cpumask_var_ptr(select_rq_mask); unsigned long prev_delta =3D ULONG_MAX, best_delta =3D ULONG_MAX; - struct root_domain *rd =3D cpu_rq(smp_processor_id())->rd; int cpu, best_energy_cpu =3D prev_cpu, target =3D -1; - unsigned long cpu_cap, util, base_energy =3D 0; + struct root_domain *rd =3D this_rq()->rd; + unsigned long base_energy =3D 0; struct sched_domain *sd; struct perf_domain *pd; + struct energy_env eenv; =20 rcu_read_lock(); pd =3D rcu_dereference(rd->pd); @@ -6834,22 +6887,36 @@ static int find_energy_efficient_cpu(struct task_st= ruct *p, int prev_cpu) if (!task_util_est(p)) goto unlock; =20 + eenv_task_busy_time(&eenv, p, prev_cpu); + for (; pd; pd =3D pd->next) { - unsigned long cur_delta, spare_cap, max_spare_cap =3D 0; + unsigned long cpu_cap, cpu_thermal_cap, util; + unsigned long cur_delta, max_spare_cap =3D 0; bool compute_prev_delta =3D false; unsigned long base_energy_pd; int max_spare_cap_cpu =3D -1; =20 cpumask_and(cpus, perf_domain_span(pd), cpu_online_mask); =20 - for_each_cpu_and(cpu, cpus, sched_domain_span(sd)) { + /* Account thermal pressure for the energy estimation */ + cpu =3D cpumask_first(cpus); + cpu_thermal_cap =3D arch_scale_cpu_capacity(cpu); + cpu_thermal_cap -=3D arch_scale_thermal_pressure(cpu); + + eenv.cpu_cap =3D cpu_thermal_cap; + eenv.pd_cap =3D 0; + + for_each_cpu(cpu, cpus) { + eenv.pd_cap +=3D cpu_thermal_cap; + + if (!cpumask_test_cpu(cpu, sched_domain_span(sd))) + continue; + if (!cpumask_test_cpu(cpu, p->cpus_ptr)) continue; =20 util =3D cpu_util_next(cpu, p, cpu); cpu_cap =3D capacity_of(cpu); - spare_cap =3D cpu_cap; - lsub_positive(&spare_cap, util); =20 /* * Skip CPUs that cannot satisfy the capacity request. @@ -6862,15 +6929,17 @@ static int find_energy_efficient_cpu(struct task_st= ruct *p, int prev_cpu) if (!fits_capacity(util, cpu_cap)) continue; =20 + lsub_positive(&cpu_cap, util); + if (cpu =3D=3D prev_cpu) { /* Always use prev_cpu as a candidate. */ compute_prev_delta =3D true; - } else if (spare_cap > max_spare_cap) { + } else if (cpu_cap > max_spare_cap) { /* * Find the CPU with the maximum spare capacity * in the performance domain. */ - max_spare_cap =3D spare_cap; + max_spare_cap =3D cpu_cap; max_spare_cap_cpu =3D cpu; } } @@ -6878,13 +6947,15 @@ static int find_energy_efficient_cpu(struct task_st= ruct *p, int prev_cpu) if (max_spare_cap_cpu < 0 && !compute_prev_delta) continue; =20 + eenv_pd_busy_time(&eenv, cpus, p); /* Compute the 'base' energy of the pd, without @p */ - base_energy_pd =3D compute_energy(p, -1, cpus, pd); + base_energy_pd =3D compute_energy(&eenv, pd, cpus, p, -1); base_energy +=3D base_energy_pd; =20 /* Evaluate the energy impact of using prev_cpu. */ if (compute_prev_delta) { - prev_delta =3D compute_energy(p, prev_cpu, cpus, pd); + prev_delta =3D compute_energy(&eenv, pd, cpus, p, + prev_cpu); if (prev_delta < base_energy_pd) goto unlock; prev_delta -=3D base_energy_pd; @@ -6893,8 +6964,8 @@ static int find_energy_efficient_cpu(struct task_stru= ct *p, int prev_cpu) =20 /* Evaluate the energy impact of using max_spare_cap_cpu. */ if (max_spare_cap_cpu >=3D 0) { - cur_delta =3D compute_energy(p, max_spare_cap_cpu, cpus, - pd); + cur_delta =3D compute_energy(&eenv, pd, cpus, p, + max_spare_cap_cpu); if (cur_delta < base_energy_pd) goto unlock; cur_delta -=3D base_energy_pd; --=20 2.36.1.124.g0e6072fb45-goog From nobody Sun May 5 10:50:47 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 627E9C433F5 for ; Mon, 23 May 2022 15:53:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238364AbiEWPxj (ORCPT ); Mon, 23 May 2022 11:53:39 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54976 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238275AbiEWPxL (ORCPT ); Mon, 23 May 2022 11:53:11 -0400 Received: from mail-wm1-x349.google.com (mail-wm1-x349.google.com [IPv6:2a00:1450:4864:20::349]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6C15E403F5 for ; Mon, 23 May 2022 08:52:48 -0700 (PDT) Received: by mail-wm1-x349.google.com with SMTP id v124-20020a1cac82000000b003948b870a8dso10764841wme.2 for ; Mon, 23 May 2022 08:52:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=dVL5gjCgy3sq4Q5dk9TcMKWYTxrHdmNucgKBvsJek24=; b=BOrsiDkKxALDfSj2WNpuv5UPGYZgdAkk16vX9KGXvIW1K2WMewlMJRMqzBixXQ562b VYNaxeoEhROFQ/ydAmCPaZa17xTVOgwl7/E6tZfHcwpj69PTXpGq2fhpwyXJqGpkHe4z eOypQmsvGq5WLhnIptnSBOEp69TUIxM7TVYHYkYW18+MAlxt9RcfNK6eQxoB7SejyGgU dh1CDZHpWK3L3+u4ux6OkxqlwMkL66qpBi+yLmPCMyWzhH2o5QoCER9QnLk1vTHQLGWB juMdG2jnC/euQrHfDAmzVg5jF9oWc/EIdRVUxbQoSHqxeufIW0cK4Dd352iwBI+eaFUQ KvMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=dVL5gjCgy3sq4Q5dk9TcMKWYTxrHdmNucgKBvsJek24=; b=pZM7gN9jOlXv+vB2ccgVzu0nIFoA/zVRSTegeFaaY1tK2b+yYJqPgpdjugQesOFyFn CbVbYf8ZAaEFfdOgFRB/K2qr0HUVjJDL60qVJFGqDMOHQYdgBz7yGSxnDCkKwuBYZiTl B9g7EwSJcCVQ5d6LPh1lp6fRGCm7WYUnfgsWTaQ852iF9P9Xh/Nj6ZNytGKOGKzLoFbp r2P+QLyHNCgZH0KweeK0+aiFi7xRDpqFc8sLeyRKw1EckD7Tcu6vs08bVWNQZeRqisuF 7XhXx/HpBEE6a4fjvvWFD3aFRsLUFiN86Hbr2GWB3xbP2MHxnQg+GjPE7HpMdg3d763A kwuw== X-Gm-Message-State: AOAM531AQvCZsiBnZyiLHqcCORuQy+f4nJ2n8dY+wgF6M2fI0n4Z6ii7 lSKF15mbuYvGX07j4qDLfVNupIU/ZTrGL134 X-Google-Smtp-Source: ABdhPJyR3TB+4hayhAcWaCKe/mCt7B4Zm6P7+7sMH42O3nHRlG34sIjBrhOkANX2qlPjWej/Mm23qjz52TWc2mlR X-Received: from vdonnefort.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:2eea]) (user=vdonnefort job=sendgmr) by 2002:a05:600c:29d3:b0:397:4730:ee75 with SMTP id s19-20020a05600c29d300b003974730ee75mr7937400wmd.149.1653321166997; Mon, 23 May 2022 08:52:46 -0700 (PDT) Date: Mon, 23 May 2022 16:51:40 +0100 In-Reply-To: <20220523155140.2878563-1-vdonnefort@google.com> Message-Id: <20220523155140.2878563-8-vdonnefort@google.com> Mime-Version: 1.0 References: <20220523155140.2878563-1-vdonnefort@google.com> X-Mailer: git-send-email 2.36.1.124.g0e6072fb45-goog Subject: [PATCH v9 7/7] sched/fair: Remove the energy margin in feec() From: Vincent Donnefort To: peterz@infradead.org, mingo@redhat.com, vincent.guittot@linaro.org Cc: linux-kernel@vger.kernel.org, dietmar.eggemann@arm.com, morten.rasmussen@arm.com, chris.redpath@arm.com, qperret@google.com, tao.zhou@linux.dev, kernel-team@android.com, vdonnefort@google.com, Vincent Donnefort Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Vincent Donnefort find_energy_efficient_cpu() integrates a margin to protect tasks from bouncing back and forth from a CPU to another. This margin is set as being 6% of the total current energy estimated on the system. This however does not work for two reasons: 1. The energy estimation is not a good absolute value: compute_energy() used in feec() is a good estimation for task placement as it allows to compare the energy with and without a task. The computed delta will give a good overview of the cost for a certain task placement. It, however, doesn't work as an absolute estimation for the total energy of the system. First it adds the contribution to idle CPUs into the energy, second it mixes util_avg with util_est values. util_avg contains the near history for a CPU usage, it doesn't tell at all what the current utilization is. A system that has been quite busy in the near past will hold a very high energy and then a high margin preventing any task migration to a lower capacity CPU, wasting energy. It even creates a negative feedback loop: by holding the tasks on a less efficient CPU, the margin contributes in keeping the energy high. 2. The margin handicaps small tasks: On a system where the workload is composed mostly of small tasks (which is often the case on Android), the overall energy will be high enough to create a margin none of those tasks can cross. On a Pixel4, a small utilization of 5% on all the CPUs creates a global estimated energy of 140 joules, as per the Energy Model declaration of that same device. This means, after applying the 6% margin that any migration must save more than 8 joules to happen. No task with a utilization lower than 40 would then be able to migrate away from the biggest CPU of the system. The 6% of the overall system energy was brought by the following patch: (eb92692b2544 sched/fair: Speed-up energy-aware wake-ups) It was previously 6% of the prev_cpu energy. Also, the following one made this margin value conditional on the clusters where the task fits: (8d4c97c105ca sched/fair: Only compute base_energy_pd if necessary) We could simply revert that margin change to what it was, but the original version didn't have strong grounds neither and as demonstrated in (1.) the estimated energy isn't a good absolute value. Instead, removing it completely. It is indeed, made possible by recent changes that improved energy estimation comparison fairness (sched/fair: Remove task_util from effective utilization in feec()) (PM: EM: Increase energy calculation precision) and task utilization stabilization (sched/fair: Decay task util_avg during migration) Without a margin, we could have feared bouncing between CPUs. But running LISA's eas_behaviour test coverage on three different platforms (Hikey960, RB-5 and DB-845) showed no issue. Removing the energy margin enables more energy-optimized placements for a more energy efficient system. Signed-off-by: Vincent Donnefort Signed-off-by: Vincent Donnefort Reviewed-by: Dietmar Eggemann diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 5586b6848858..92907b384265 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6859,9 +6859,8 @@ static int find_energy_efficient_cpu(struct task_stru= ct *p, int prev_cpu) { struct cpumask *cpus =3D this_cpu_cpumask_var_ptr(select_rq_mask); unsigned long prev_delta =3D ULONG_MAX, best_delta =3D ULONG_MAX; - int cpu, best_energy_cpu =3D prev_cpu, target =3D -1; struct root_domain *rd =3D this_rq()->rd; - unsigned long base_energy =3D 0; + int cpu, best_energy_cpu, target =3D -1; struct sched_domain *sd; struct perf_domain *pd; struct energy_env eenv; @@ -6893,8 +6892,8 @@ static int find_energy_efficient_cpu(struct task_stru= ct *p, int prev_cpu) unsigned long cpu_cap, cpu_thermal_cap, util; unsigned long cur_delta, max_spare_cap =3D 0; bool compute_prev_delta =3D false; - unsigned long base_energy_pd; int max_spare_cap_cpu =3D -1; + unsigned long base_energy; =20 cpumask_and(cpus, perf_domain_span(pd), cpu_online_mask); =20 @@ -6949,16 +6948,15 @@ static int find_energy_efficient_cpu(struct task_st= ruct *p, int prev_cpu) =20 eenv_pd_busy_time(&eenv, cpus, p); /* Compute the 'base' energy of the pd, without @p */ - base_energy_pd =3D compute_energy(&eenv, pd, cpus, p, -1); - base_energy +=3D base_energy_pd; + base_energy =3D compute_energy(&eenv, pd, cpus, p, -1); =20 /* Evaluate the energy impact of using prev_cpu. */ if (compute_prev_delta) { prev_delta =3D compute_energy(&eenv, pd, cpus, p, prev_cpu); - if (prev_delta < base_energy_pd) + if (prev_delta < base_energy) goto unlock; - prev_delta -=3D base_energy_pd; + prev_delta -=3D base_energy; best_delta =3D min(best_delta, prev_delta); } =20 @@ -6966,9 +6964,9 @@ static int find_energy_efficient_cpu(struct task_stru= ct *p, int prev_cpu) if (max_spare_cap_cpu >=3D 0) { cur_delta =3D compute_energy(&eenv, pd, cpus, p, max_spare_cap_cpu); - if (cur_delta < base_energy_pd) + if (cur_delta < base_energy) goto unlock; - cur_delta -=3D base_energy_pd; + cur_delta -=3D base_energy; if (cur_delta < best_delta) { best_delta =3D cur_delta; best_energy_cpu =3D max_spare_cap_cpu; @@ -6977,12 +6975,7 @@ static int find_energy_efficient_cpu(struct task_str= uct *p, int prev_cpu) } rcu_read_unlock(); =20 - /* - * Pick the best CPU if prev_cpu cannot be used, or if it saves at - * least 6% of the energy used by prev_cpu. - */ - if ((prev_delta =3D=3D ULONG_MAX) || - (prev_delta - best_delta) > ((prev_delta + base_energy) >> 4)) + if (best_delta < prev_delta) target =3D best_energy_cpu; =20 return target; --=20 2.36.1.124.g0e6072fb45-goog