| From stable+bounces-172593-greg=kroah.com@vger.kernel.org Sat Aug 23 15:34:57 2025 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sat, 23 Aug 2025 09:34:48 -0400 |
| Subject: cpuidle: menu: Remove iowait influence |
| To: stable@vger.kernel.org |
| Cc: Christian Loehle <christian.loehle@arm.com>, "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Sasha Levin <sashal@kernel.org> |
| Message-ID: <20250823133449.2131644-1-sashal@kernel.org> |
| |
| From: Christian Loehle <christian.loehle@arm.com> |
| |
| [ Upstream commit 38f83090f515b4b5d59382dfada1e7457f19aa47 ] |
| |
| Remove CPU iowaiters influence on idle state selection. |
| |
| Remove the menu notion of performance multiplier which increased with |
| the number of tasks that went to iowait sleep on this CPU and haven't |
| woken up yet. |
| |
| Relying on iowait for cpuidle is problematic for a few reasons: |
| |
| 1. There is no guarantee that an iowaiting task will wake up on the |
| same CPU. |
| |
| 2. The task being in iowait says nothing about the idle duration, we |
| could be selecting shallower states for a long time. |
| |
| 3. The task being in iowait doesn't always imply a performance hit |
| with increased latency. |
| |
| 4. If there is such a performance hit, the number of iowaiting tasks |
| doesn't directly correlate. |
| |
| 5. The definition of iowait altogether is vague at best, it is |
| sprinkled across kernel code. |
| |
| Signed-off-by: Christian Loehle <christian.loehle@arm.com> |
| Link: https://patch.msgid.link/20240905092645.2885200-2-christian.loehle@arm.com |
| [ rjw: Minor edits in the changelog ] |
| Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| Stable-dep-of: 779b1a1cb13a ("cpuidle: governors: menu: Avoid selecting states with too much latency") |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/cpuidle/governors/menu.c | 52 ++++++--------------------------------- |
| 1 file changed, 9 insertions(+), 43 deletions(-) |
| |
| --- a/drivers/cpuidle/governors/menu.c |
| +++ b/drivers/cpuidle/governors/menu.c |
| @@ -21,7 +21,7 @@ |
| |
| #include "gov.h" |
| |
| -#define BUCKETS 12 |
| +#define BUCKETS 6 |
| #define INTERVAL_SHIFT 3 |
| #define INTERVALS (1UL << INTERVAL_SHIFT) |
| #define RESOLUTION 1024 |
| @@ -31,12 +31,11 @@ |
| /* |
| * Concepts and ideas behind the menu governor |
| * |
| - * For the menu governor, there are 3 decision factors for picking a C |
| + * For the menu governor, there are 2 decision factors for picking a C |
| * state: |
| * 1) Energy break even point |
| - * 2) Performance impact |
| - * 3) Latency tolerance (from pmqos infrastructure) |
| - * These three factors are treated independently. |
| + * 2) Latency tolerance (from pmqos infrastructure) |
| + * These two factors are treated independently. |
| * |
| * Energy break even point |
| * ----------------------- |
| @@ -119,19 +118,10 @@ struct menu_device { |
| int interval_ptr; |
| }; |
| |
| -static inline int which_bucket(u64 duration_ns, unsigned int nr_iowaiters) |
| +static inline int which_bucket(u64 duration_ns) |
| { |
| int bucket = 0; |
| |
| - /* |
| - * We keep two groups of stats; one with no |
| - * IO pending, one without. |
| - * This allows us to calculate |
| - * E(duration)|iowait |
| - */ |
| - if (nr_iowaiters) |
| - bucket = BUCKETS/2; |
| - |
| if (duration_ns < 10ULL * NSEC_PER_USEC) |
| return bucket; |
| if (duration_ns < 100ULL * NSEC_PER_USEC) |
| @@ -145,19 +135,6 @@ static inline int which_bucket(u64 durat |
| return bucket + 5; |
| } |
| |
| -/* |
| - * Return a multiplier for the exit latency that is intended |
| - * to take performance requirements into account. |
| - * The more performance critical we estimate the system |
| - * to be, the higher this multiplier, and thus the higher |
| - * the barrier to go to an expensive C state. |
| - */ |
| -static inline int performance_multiplier(unsigned int nr_iowaiters) |
| -{ |
| - /* for IO wait tasks (per cpu!) we add 10x each */ |
| - return 1 + 10 * nr_iowaiters; |
| -} |
| - |
| static DEFINE_PER_CPU(struct menu_device, menu_devices); |
| |
| static void menu_update_intervals(struct menu_device *data, unsigned int interval_us) |
| @@ -284,8 +261,6 @@ static int menu_select(struct cpuidle_dr |
| struct menu_device *data = this_cpu_ptr(&menu_devices); |
| s64 latency_req = cpuidle_governor_latency_req(dev->cpu); |
| u64 predicted_ns; |
| - u64 interactivity_req; |
| - unsigned int nr_iowaiters; |
| ktime_t delta, delta_tick; |
| int i, idx; |
| |
| @@ -302,8 +277,6 @@ static int menu_select(struct cpuidle_dr |
| menu_update_intervals(data, UINT_MAX); |
| } |
| |
| - nr_iowaiters = nr_iowait_cpu(dev->cpu); |
| - |
| /* Find the shortest expected idle interval. */ |
| predicted_ns = get_typical_interval(data) * NSEC_PER_USEC; |
| if (predicted_ns > RESIDENCY_THRESHOLD_NS) { |
| @@ -317,7 +290,7 @@ static int menu_select(struct cpuidle_dr |
| } |
| |
| data->next_timer_ns = delta; |
| - data->bucket = which_bucket(data->next_timer_ns, nr_iowaiters); |
| + data->bucket = which_bucket(data->next_timer_ns); |
| |
| /* Round up the result for half microseconds. */ |
| timer_us = div_u64((RESOLUTION * DECAY * NSEC_PER_USEC) / 2 + |
| @@ -335,7 +308,7 @@ static int menu_select(struct cpuidle_dr |
| */ |
| data->next_timer_ns = KTIME_MAX; |
| delta_tick = TICK_NSEC / 2; |
| - data->bucket = which_bucket(KTIME_MAX, nr_iowaiters); |
| + data->bucket = which_bucket(KTIME_MAX); |
| } |
| |
| if (unlikely(drv->state_count <= 1 || latency_req == 0) || |
| @@ -362,15 +335,8 @@ static int menu_select(struct cpuidle_dr |
| */ |
| if (predicted_ns < TICK_NSEC) |
| predicted_ns = data->next_timer_ns; |
| - } else { |
| - /* |
| - * Use the performance multiplier and the user-configurable |
| - * latency_req to determine the maximum exit latency. |
| - */ |
| - interactivity_req = div64_u64(predicted_ns, |
| - performance_multiplier(nr_iowaiters)); |
| - if (latency_req > interactivity_req) |
| - latency_req = interactivity_req; |
| + } else if (latency_req > predicted_ns) { |
| + latency_req = predicted_ns; |
| } |
| |
| /* |