|
@@ -348,14 +348,12 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
|
|
|
* If the tick is already stopped, the cost of possible short
|
|
|
* idle duration misprediction is much higher, because the CPU
|
|
|
* may be stuck in a shallow idle state for a long time as a
|
|
|
- * result of it. In that case say we might mispredict and try
|
|
|
- * to force the CPU into a state for which we would have stopped
|
|
|
- * the tick, unless a timer is going to expire really soon
|
|
|
- * anyway.
|
|
|
+ * result of it. In that case say we might mispredict and use
|
|
|
+ * the known time till the closest timer event for the idle
|
|
|
+ * state selection.
|
|
|
*/
|
|
|
if (data->predicted_us < TICK_USEC)
|
|
|
- data->predicted_us = min_t(unsigned int, TICK_USEC,
|
|
|
- ktime_to_us(delta_next));
|
|
|
+ data->predicted_us = ktime_to_us(delta_next);
|
|
|
} else {
|
|
|
/*
|
|
|
* Use the performance multiplier and the user-configurable
|
|
@@ -380,8 +378,22 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
|
|
|
continue;
|
|
|
if (idx == -1)
|
|
|
idx = i; /* first enabled state */
|
|
|
- if (s->target_residency > data->predicted_us)
|
|
|
- break;
|
|
|
+ if (s->target_residency > data->predicted_us) {
|
|
|
+ if (!tick_nohz_tick_stopped())
|
|
|
+ break;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the state selected so far is shallow and this
|
|
|
+ * state's target residency matches the time till the
|
|
|
+ * closest timer event, select this one to avoid getting
|
|
|
+ * stuck in the shallow one for too long.
|
|
|
+ */
|
|
|
+ if (drv->states[idx].target_residency < TICK_USEC &&
|
|
|
+ s->target_residency <= ktime_to_us(delta_next))
|
|
|
+ idx = i;
|
|
|
+
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
if (s->exit_latency > latency_req) {
|
|
|
/*
|
|
|
* If we break out of the loop for latency reasons, use
|
|
@@ -402,14 +414,13 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
|
|
|
* Don't stop the tick if the selected state is a polling one or if the
|
|
|
* expected idle duration is shorter than the tick period length.
|
|
|
*/
|
|
|
- if ((drv->states[idx].flags & CPUIDLE_FLAG_POLLING) ||
|
|
|
- expected_interval < TICK_USEC) {
|
|
|
+ if (((drv->states[idx].flags & CPUIDLE_FLAG_POLLING) ||
|
|
|
+ expected_interval < TICK_USEC) && !tick_nohz_tick_stopped()) {
|
|
|
unsigned int delta_next_us = ktime_to_us(delta_next);
|
|
|
|
|
|
*stop_tick = false;
|
|
|
|
|
|
- if (!tick_nohz_tick_stopped() && idx > 0 &&
|
|
|
- drv->states[idx].target_residency > delta_next_us) {
|
|
|
+ if (idx > 0 && drv->states[idx].target_residency > delta_next_us) {
|
|
|
/*
|
|
|
* The tick is not going to be stopped and the target
|
|
|
* residency of the state to be returned is not within
|
|
@@ -428,6 +439,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+out:
|
|
|
data->last_state_idx = idx;
|
|
|
|
|
|
return data->last_state_idx;
|