diff mbox series

[v2,06/17] PM: EM: Add update_power() callback for runtime modifications

Message ID 20230512095743.3393563-7-lukasz.luba@arm.com
State Superseded
Headers show
Series Introduce runtime modifiable Energy Model | expand

Commit Message

Lukasz Luba May 12, 2023, 9:57 a.m. UTC
The Energy Model (EM) is going to support runtime modifications. This
new callback would be used in the upcoming EM changes. The drivers
or frameworks which want to modify the EM have to implement the
update_power() callback and provide it via EM API
em_dev_update_perf_domain(). The callback is then used by the EM
framework to get new power values for each frequency in existing EM.

Signed-off-by: Lukasz Luba <lukasz.luba@arm.com>
---
 include/linux/energy_model.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

Comments

Dietmar Eggemann May 30, 2023, 9:31 a.m. UTC | #1
On 12/05/2023 11:57, Lukasz Luba wrote:
> The Energy Model (EM) is going to support runtime modifications. This
> new callback would be used in the upcoming EM changes. The drivers
> or frameworks which want to modify the EM have to implement the
> update_power() callback and provide it via EM API
> em_dev_update_perf_domain(). The callback is then used by the EM
> framework to get new power values for each frequency in existing EM.

Do we have any numbers or feedback that the chosen design (i.e. update
per performance state through update_power()) is performant enough for
the anticipated use case on real devices?

> Signed-off-by: Lukasz Luba <lukasz.luba@arm.com>
> ---
>  include/linux/energy_model.h | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h
> index 8069f526c9d8..cc2bf607191e 100644
> --- a/include/linux/energy_model.h
> +++ b/include/linux/energy_model.h
> @@ -158,6 +158,26 @@ struct em_data_callback {
>  	 */
>  	int (*get_cost)(struct device *dev, unsigned long freq,
>  			unsigned long *cost);
> +
> +	/**
> +	 * update_power() - Provide new power at the given performance state of
> +	 *		a device
> +	 * @dev		: Device for which we do this operation (can be a CPU)
> +	 * @freq	: Frequency at the performance state in kHz
> +	 * @power	: New power value at the performance state
> +	 *		(modified)
> +	 * @priv	: Pointer to private data useful for tracking context
> +	 *		during run-time modifications of EM.
> +	 *
> +	 * The update_power() is used by run-time modifiable EM. It aims to
> +	 * provide updated power value for a given frequency, which is stored
> +	 * in the performance state. The power value provided by this callback
> +	 * should fit in the [0, EM_MAX_POWER] range.
> +	 *
> +	 * Return 0 on success, or appropriate error value in case of failure.
> +	 */
> +	int (*update_power)(struct device *dev, unsigned long freq,
> +			    unsigned long *power, void *priv);
>  };
>  #define EM_SET_ACTIVE_POWER_CB(em_cb, cb) ((em_cb).active_power = cb)
>  #define EM_ADV_DATA_CB(_active_power_cb, _cost_cb)	\
> @@ -165,6 +185,7 @@ struct em_data_callback {
>  	  .get_cost = _cost_cb }
>  #define EM_DATA_CB(_active_power_cb)			\
>  		EM_ADV_DATA_CB(_active_power_cb, NULL)
> +#define EM_UPDATE_CB(_update_power_cb) { .update_power = &_update_power_cb }
>  
>  struct em_perf_domain *em_cpu_get(int cpu);
>  struct em_perf_domain *em_pd_get(struct device *dev);
Lukasz Luba July 3, 2023, 3:06 p.m. UTC | #2
Hi Dietmar,

On 5/30/23 10:31, Dietmar Eggemann wrote:
> On 12/05/2023 11:57, Lukasz Luba wrote:
>> The Energy Model (EM) is going to support runtime modifications. This
>> new callback would be used in the upcoming EM changes. The drivers
>> or frameworks which want to modify the EM have to implement the
>> update_power() callback and provide it via EM API
>> em_dev_update_perf_domain(). The callback is then used by the EM
>> framework to get new power values for each frequency in existing EM.
> 
> Do we have any numbers or feedback that the chosen design (i.e. update
> per performance state through update_power()) is performant enough for
> the anticipated use case on real devices?
> 

Yes, we have. I have a testing kernel module which updates the EM
with queue_delayed_work() every 100ms. That update is for Little's EM
where we have 11 OPPs. We call the new callback for each OPP
in the em_dev_update_perf_domain(). I have measured that total function
time.

When we fix all CPUs freq to max freq on pixel6 and disable deep idle
states and leave only WFI, then we can run some tracing and capture the
results:

(The 4 CPUs from top are the little (1.8MHz), than 2 Mid (2.2GHz) and
then 2 big (2.8GHz))
------------------------------------
   Function                               Hit    Time            Avg 
         s^2
   --------                               ---    ----            --- 
         ---
   em_dev_update_perf_domain             3104    51236.39 us     16.506 
us       75.344 us
   Function                               Hit    Time            Avg 
         s^2
   --------                               ---    ----            --- 
         ---
   em_dev_update_perf_domain             1264    20768.15 us     16.430 
us       62.257 us
   Function                               Hit    Time            Avg 
         s^2
   --------                               ---    ----            --- 
         ---
   em_dev_update_perf_domain             1166    18632.95 us     15.980 
us       70.707 us
   Function                               Hit    Time            Avg 
         s^2
   --------                               ---    ----            --- 
         ---
   em_dev_update_perf_domain              770    12334.43 us     16.018 
us       66.337 us
   Function                               Hit    Time            Avg 
         s^2
   --------                               ---    ----            --- 
         ---
   em_dev_update_perf_domain              101    920.613 us      9.114 
us        21.380 us
   Function                               Hit    Time            Avg 
         s^2
   --------                               ---    ----            --- 
         ---
   em_dev_update_perf_domain               20    211.830 us      10.591 
us       23.998 us
   Function                               Hit    Time            Avg 
         s^2
   --------                               ---    ----            --- 
         ---
   Function                               Hit    Time            Avg 
         s^2
   --------                               ---    ----            --- 
         ---
   em_dev_update_perf_domain               15    78.085 us       5.205 
us        7.444 us

------------------------------------

As you can see in avg on Little CPUs it takes ~16us, on Mid ~10us and on
Big ~5us.

If such updating kernel module is implemented correctly, it would be
most often scheduled on the Littles as you can see based on 'Hit'
column.

Therefore, IMO this cost can be OK for the upstream. This EM runtime
change won't be triggered very often. If it would be e.g. every
100ms than the cost ~1.5us per 1 OPP is negligible.

Regards,
Lukasz
diff mbox series

Patch

diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h
index 8069f526c9d8..cc2bf607191e 100644
--- a/include/linux/energy_model.h
+++ b/include/linux/energy_model.h
@@ -158,6 +158,26 @@  struct em_data_callback {
 	 */
 	int (*get_cost)(struct device *dev, unsigned long freq,
 			unsigned long *cost);
+
+	/**
+	 * update_power() - Provide new power at the given performance state of
+	 *		a device
+	 * @dev		: Device for which we do this operation (can be a CPU)
+	 * @freq	: Frequency at the performance state in kHz
+	 * @power	: New power value at the performance state
+	 *		(modified)
+	 * @priv	: Pointer to private data useful for tracking context
+	 *		during run-time modifications of EM.
+	 *
+	 * The update_power() is used by run-time modifiable EM. It aims to
+	 * provide updated power value for a given frequency, which is stored
+	 * in the performance state. The power value provided by this callback
+	 * should fit in the [0, EM_MAX_POWER] range.
+	 *
+	 * Return 0 on success, or appropriate error value in case of failure.
+	 */
+	int (*update_power)(struct device *dev, unsigned long freq,
+			    unsigned long *power, void *priv);
 };
 #define EM_SET_ACTIVE_POWER_CB(em_cb, cb) ((em_cb).active_power = cb)
 #define EM_ADV_DATA_CB(_active_power_cb, _cost_cb)	\
@@ -165,6 +185,7 @@  struct em_data_callback {
 	  .get_cost = _cost_cb }
 #define EM_DATA_CB(_active_power_cb)			\
 		EM_ADV_DATA_CB(_active_power_cb, NULL)
+#define EM_UPDATE_CB(_update_power_cb) { .update_power = &_update_power_cb }
 
 struct em_perf_domain *em_cpu_get(int cpu);
 struct em_perf_domain *em_pd_get(struct device *dev);