| From a8a27555c9f7f82114edc7e731fd752b9483bf61 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 11 Aug 2025 17:03:11 +0200 |
| Subject: cpuidle: governors: menu: Avoid using invalid recent intervals data |
| |
| From: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| |
| [ Upstream commit fa3fa55de0d6177fdcaf6fc254f13cc8f33c3eed ] |
| |
| Marc has reported that commit 85975daeaa4d ("cpuidle: menu: Avoid |
| discarding useful information") caused the number of wakeup interrupts |
| to increase on an idle system [1], which was not expected to happen |
| after merely allowing shallower idle states to be selected by the |
| governor in some cases. |
| |
| However, on the system in question, all of the idle states deeper than |
| WFI are rejected by the driver due to a firmware issue [2]. This causes |
| the governor to only consider the recent interval duriation data |
| corresponding to attempts to enter WFI that are successful and the |
| recent invervals table is filled with values lower than the scheduler |
| tick period. Consequently, the governor predicts an idle duration |
| below the scheduler tick period length and avoids stopping the tick |
| more often which leads to the observed symptom. |
| |
| Address it by modifying the governor to update the recent intervals |
| table also when entering the previously selected idle state fails, so |
| it knows that the short idle intervals might have been the minority |
| had the selected idle states been actually entered every time. |
| |
| Fixes: 85975daeaa4d ("cpuidle: menu: Avoid discarding useful information") |
| Link: https://lore.kernel.org/linux-pm/86o6sv6n94.wl-maz@kernel.org/ [1] |
| Link: https://lore.kernel.org/linux-pm/7ffcb716-9a1b-48c2-aaa4-469d0df7c792@arm.com/ [2] |
| Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| Tested-by: Christian Loehle <christian.loehle@arm.com> |
| Tested-by: Marc Zyngier <maz@kernel.org> |
| Reviewed-by: Christian Loehle <christian.loehle@arm.com> |
| Link: https://patch.msgid.link/2793874.mvXUDI8C0e@rafael.j.wysocki |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/cpuidle/governors/menu.c | 21 +++++++++++++++++---- |
| 1 file changed, 17 insertions(+), 4 deletions(-) |
| |
| diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c |
| index edd9a8fb9878..f3a071ac3b2a 100644 |
| --- a/drivers/cpuidle/governors/menu.c |
| +++ b/drivers/cpuidle/governors/menu.c |
| @@ -160,6 +160,14 @@ static inline int performance_multiplier(unsigned int nr_iowaiters) |
| |
| static DEFINE_PER_CPU(struct menu_device, menu_devices); |
| |
| +static void menu_update_intervals(struct menu_device *data, unsigned int interval_us) |
| +{ |
| + /* Update the repeating-pattern data. */ |
| + data->intervals[data->interval_ptr++] = interval_us; |
| + if (data->interval_ptr >= INTERVALS) |
| + data->interval_ptr = 0; |
| +} |
| + |
| static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev); |
| |
| /* |
| @@ -284,6 +292,14 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, |
| if (data->needs_update) { |
| menu_update(drv, dev); |
| data->needs_update = 0; |
| + } else if (!dev->last_residency_ns) { |
| + /* |
| + * This happens when the driver rejects the previously selected |
| + * idle state and returns an error, so update the recent |
| + * intervals table to prevent invalid information from being |
| + * used going forward. |
| + */ |
| + menu_update_intervals(data, UINT_MAX); |
| } |
| |
| nr_iowaiters = nr_iowait_cpu(dev->cpu); |
| @@ -553,10 +569,7 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) |
| |
| data->correction_factor[data->bucket] = new_factor; |
| |
| - /* update the repeating-pattern data */ |
| - data->intervals[data->interval_ptr++] = ktime_to_us(measured_ns); |
| - if (data->interval_ptr >= INTERVALS) |
| - data->interval_ptr = 0; |
| + menu_update_intervals(data, ktime_to_us(measured_ns)); |
| } |
| |
| /** |
| -- |
| 2.50.1 |
| |