]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
cpuidle: menu: Use residency threshold in polling state override decisions
authorAboorva Devarajan <aboorvad@linux.ibm.com>
Mon, 6 Oct 2025 01:39:54 +0000 (07:09 +0530)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 27 Oct 2025 13:53:46 +0000 (14:53 +0100)
On virtualized PowerPC (pseries) systems, where only one polling state
(Snooze) and one deep state (CEDE) are available, selecting CEDE when
the predicted idle duration is less than the target residency of CEDE
state can hurt performance. In such cases, the entry/exit overhead of
CEDE outweighs the power savings, leading to unnecessary state
transitions and higher latency.

Menu governor currently contains a special-case rule that prioritizes
the first non-polling state over polling, even when its target residency
is much longer than the predicted idle duration. On PowerPC/pseries,
where the gap between the polling state (Snooze) and the first non-polling
state (CEDE) is large, this behavior causes performance regressions.

Refine that special case by adding an extra requirement: the first
non-polling state can only be chosen if its target residency is below
the defined RESIDENCY_THRESHOLD_NS. If this condition is not satisfied,
polling is allowed instead, avoiding suboptimal non-polling state
entries.

This change is limited to the single special-case rule for the first
non-polling state. The general non-polling state selection logic in the
menu governor remains unchanged.

Performance improvement observed with pgbench on PowerPC (pseries)
system:
+---------------------------+------------+------------+------------+
| Metric                    | Baseline   | Patched    | Change (%) |
+---------------------------+------------+------------+------------+
| Transactions/sec (TPS)    | 495,210    | 536,982    | +8.45%     |
| Avg latency (ms)          | 0.163      | 0.150      | -7.98%     |
+---------------------------+------------+------------+------------+

CPUIdle state usage:
+--------------+--------------+-------------+
| Metric       | Baseline     | Patched     |
+--------------+--------------+-------------+
| Total usage  | 12,735,820   | 13,918,442  |
| Above usage  | 11,401,520   | 1,598,210   |
| Below usage  | 20,145       | 702,395     |
+--------------+--------------+-------------+

Above/Total and Below/Total usage percentages:
+------------------------+-----------+---------+
| Metric                 | Baseline  | Patched |
+------------------------+-----------+---------+
| Above % (Above/Total)  | 89.56%    | 11.49%  |
| Below % (Below/Total)  | 0.16%     | 5.05%   |
| Total cpuidle miss (%) | 89.72%    | 16.54%  |
+------------------------+-----------+---------+

The results indicate that restricting CEDE selection to cases where
its residency matches the predicted idle time reduces mispredictions,
lowers unnecessary state transitions, and improves overall throughput.

Reviewed-by: Christian Loehle <christian.loehle@arm.com>
Signed-off-by: Aboorva Devarajan <aboorvad@linux.ibm.com>
[ rjw: Changelog edits, rebase ]
Link: https://patch.msgid.link/20251006013954.17972-1-aboorvad@linux.ibm.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/cpuidle/governors/menu.c

index 23239b0c04f95b05845e97c97116800fe0c89125..64d6f7a1c7766325da05248b5e210a7dab4509d3 100644 (file)
@@ -317,12 +317,13 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
                }
 
                /*
-                * Use a physical idle state, not busy polling, unless a timer
-                * is going to trigger soon enough or the exit latency of the
-                * idle state in question is greater than the predicted idle
-                * duration.
+                * Use a physical idle state instead of busy polling so long as
+                * its target residency is below the residency threshold, its
+                * exit latency is not greater than the predicted idle duration,
+                * and the next timer doesn't expire soon.
                 */
                if ((drv->states[idx].flags & CPUIDLE_FLAG_POLLING) &&
+                   s->target_residency_ns < RESIDENCY_THRESHOLD_NS &&
                    s->target_residency_ns <= data->next_timer_ns &&
                    s->exit_latency_ns <= predicted_ns) {
                        predicted_ns = s->target_residency_ns;