2021-03-29 18:40:08

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v1 0/5] cpuidle: Take possible negative "sleep length" values into account

Hi All,

As follows from the discussion triggered by the patch at

https://lore.kernel.org/lkml/[email protected]/

the cpuidle governors using tick_nohz_get_sleep_length() assume it to always
return positive values which is not correct in general.

To address this issues, first document the fact that negative values can
be returned by tick_nohz_get_sleep_length() (patch [1/5]). Then, in
preparation for more substantial changes, change the data type of two
fields in struct cpuidle_state to s64 so they can be used in computations
involving negative numbers safely (patch [2/5]).

Next, adjust the teo governor a bit so that negative "sleep length" values
are counted like zero by it (patch [3/5]) and modify it so as to avoid
mishandling negative "sleep length" values (patch [4/5]).

Finally, make the menu governor take negative "sleep length" values into
account properly (patch [5/5]).

Please see the changelogs of the patches for details.

Thanks!




2021-03-29 18:41:16

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v1 4/5] cpuidle: teo: Take negative "sleep length" values into account

From: "Rafael J. Wysocki" <[email protected]>

Modify the TEO governor to take possible negative return values of
tick_nohz_get_next_hrtimer() into account by changing the data type
of some variables used by it to s64 which allows it to carry out
computations without potentially problematic data type conversions
into u64.

Also change the computations in teo_select() so that the negative
values themselves are handled in a natural way to avoid adding extra
negative value checks to that function.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/cpuidle/governors/teo.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)

Index: linux-pm/drivers/cpuidle/governors/teo.c
===================================================================
--- linux-pm.orig/drivers/cpuidle/governors/teo.c
+++ linux-pm/drivers/cpuidle/governors/teo.c
@@ -100,8 +100,8 @@ struct teo_idle_state {
* @intervals: Saved idle duration values.
*/
struct teo_cpu {
- u64 time_span_ns;
- u64 sleep_length_ns;
+ s64 time_span_ns;
+ s64 sleep_length_ns;
struct teo_idle_state states[CPUIDLE_STATE_MAX];
int interval_idx;
u64 intervals[INTERVALS];
@@ -214,7 +214,7 @@ static bool teo_time_ok(u64 interval_ns)
*/
static int teo_find_shallower_state(struct cpuidle_driver *drv,
struct cpuidle_device *dev, int state_idx,
- u64 duration_ns)
+ s64 duration_ns)
{
int i;

@@ -240,10 +240,10 @@ static int teo_select(struct cpuidle_dri
{
struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
- u64 duration_ns;
+ int max_early_idx, prev_max_early_idx, constraint_idx, idx0, idx, i;
unsigned int hits, misses, early_hits;
- int max_early_idx, prev_max_early_idx, constraint_idx, idx, i;
ktime_t delta_tick;
+ s64 duration_ns;

if (dev->last_state_idx >= 0) {
teo_update(drv, dev);
@@ -262,6 +262,7 @@ static int teo_select(struct cpuidle_dri
prev_max_early_idx = -1;
constraint_idx = drv->state_count;
idx = -1;
+ idx0 = idx;

for (i = 0; i < drv->state_count; i++) {
struct cpuidle_state *s = &drv->states[i];
@@ -322,6 +323,7 @@ static int teo_select(struct cpuidle_dri
idx = i; /* first enabled state */
hits = cpu_data->states[i].hits;
misses = cpu_data->states[i].misses;
+ idx0 = i;
}

if (s->target_residency_ns > duration_ns)
@@ -374,11 +376,16 @@ static int teo_select(struct cpuidle_dri

if (idx < 0) {
idx = 0; /* No states enabled. Must use 0. */
- } else if (idx > 0) {
+ } else if (idx > idx0) {
unsigned int count = 0;
u64 sum = 0;

/*
+ * The target residencies of at least two different enabled idle
+ * states are less than or equal to the current expected idle
+ * duration. Try to refine the selection using the most recent
+ * measured idle duration values.
+ *
* Count and sum the most recent idle duration values less than
* the current expected idle duration value.
*/
@@ -426,7 +433,8 @@ static int teo_select(struct cpuidle_dri
* till the closest timer including the tick, try to correct
* that.
*/
- if (idx > 0 && drv->states[idx].target_residency_ns > delta_tick)
+ if (idx > idx0 &&
+ drv->states[idx].target_residency_ns > delta_tick)
idx = teo_find_shallower_state(drv, dev, idx, delta_tick);
}




2021-03-29 18:41:17

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH v1 1/5] tick/nohz: Improve tick_nohz_get_next_hrtimer() kerneldoc

From: "Rafael J. Wysocki" <[email protected]>

Make the tick_nohz_get_next_hrtimer() kerneldoc comment state clearly
that the function may return negative numbers.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
kernel/time/tick-sched.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

Index: linux-pm/kernel/time/tick-sched.c
===================================================================
--- linux-pm.orig/kernel/time/tick-sched.c
+++ linux-pm/kernel/time/tick-sched.c
@@ -1124,7 +1124,11 @@ ktime_t tick_nohz_get_next_hrtimer(void)
* tick_nohz_get_sleep_length - return the expected length of the current sleep
* @delta_next: duration until the next event if the tick cannot be stopped
*
- * Called from power state control code with interrupts disabled
+ * Called from power state control code with interrupts disabled.
+ *
+ * The return value of this function and/or the value returned by it through the
+ * @delta_next pointer can be negative which must be taken into account by its
+ * callers.
*/
ktime_t tick_nohz_get_sleep_length(ktime_t *delta_next)
{



2021-04-07 21:45:03

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH v1 0/5] cpuidle: Take possible negative "sleep length" values into account

On Mon, Mar 29, 2021 at 8:38 PM Rafael J. Wysocki <[email protected]> wrote:
>
> Hi All,
>
> As follows from the discussion triggered by the patch at
>
> https://lore.kernel.org/lkml/[email protected]/
>
> the cpuidle governors using tick_nohz_get_sleep_length() assume it to always
> return positive values which is not correct in general.
>
> To address this issues, first document the fact that negative values can
> be returned by tick_nohz_get_sleep_length() (patch [1/5]). Then, in
> preparation for more substantial changes, change the data type of two
> fields in struct cpuidle_state to s64 so they can be used in computations
> involving negative numbers safely (patch [2/5]).
>
> Next, adjust the teo governor a bit so that negative "sleep length" values
> are counted like zero by it (patch [3/5]) and modify it so as to avoid
> mishandling negative "sleep length" values (patch [4/5]).
>
> Finally, make the menu governor take negative "sleep length" values into
> account properly (patch [5/5]).
>
> Please see the changelogs of the patches for details.

Given no objections or concerns regarding this lot, let me queue it up.

Thanks!