]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
cpuidle: governors: menu: Avoid using invalid recent intervals data
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 11 Aug 2025 15:03:11 +0000 (17:03 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 11 Aug 2025 19:46:14 +0000 (21:46 +0200)
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/
Link: https://lore.kernel.org/linux-pm/7ffcb716-9a1b-48c2-aaa4-469d0df7c792@arm.com/
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
drivers/cpuidle/governors/menu.c

index 52d5d26fc7c6433e56f337986b018f8bcc2b7c45..81306612a5c67d297b22865de8b2efa9a6a5e7f3 100644 (file)
@@ -97,6 +97,14 @@ static inline int which_bucket(u64 duration_ns)
 
 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);
 
 /*
@@ -222,6 +230,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);
        }
 
        /* Find the shortest expected idle interval. */
@@ -482,10 +498,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));
 }
 
 /**