[RFC PATCH v1 05/11] cpuidle: update idle statistics before cpuidle governor

Aubrey Li
2017-07-10T01:49:04Z

From: Aubrey Li <aubrey.li@linux.intel.com>

Promote menu governor update functionality into cpuidle governor,
so that cpuidle can make a prediction based on the fresh data.
---
drivers/cpuidle/cpuidle.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 77 insertions(+)

diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 0be7f75..2b7d7bf 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -297,6 +297,79 @@ static unsigned int get_typical_interval(struct cpuidle_device *dev)
}

/**
+ * cpuidle_update - attempts to guess what happened after entry
+ * @drv: cpuidle driver containing state data
+ * @dev: the CPU
+ */
+static void cpuidle_update(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{
+ struct cpuidle_governor_stat *gov_stat =
+ (struct cpuidle_governor_stat *)&(dev->gov_stat);
+ int last_idx = gov_stat->last_state_idx;
+ struct cpuidle_state *target = &drv->states[last_idx];
+ unsigned int measured_us;
+ unsigned int new_factor;
+
+ /*
+ * Try to figure out how much time passed between entry to low
+ * power state and occurrence of the wakeup event.
+ *
+ * If the entered idle state didn't support residency measurements,
+ * we use them anyway if they are short, and if long,
+ * truncate to the whole expected time.
+ *
+ * Any measured amount of time will include the exit latency.
+ * Since we are interested in when the wakeup begun, not when it
+ * was completed, we must subtract the exit latency. However, if
+ * the measured amount of time is less than the exit latency,
+ * assume the state was never reached and the exit latency is 0.
+ */
+
+ /* measured value */
+ measured_us = cpuidle_get_last_residency(dev);
+
+ /* Deduct exit latency */
+ if (measured_us > 2 * target->exit_latency)
+ measured_us -= target->exit_latency;
+ else
+ measured_us /= 2;
+
+ /* Make sure our coefficients do not exceed unity */
+ if (measured_us > gov_stat->next_timer_us)
+ measured_us = gov_stat->next_timer_us;
+
+ /* Update our correction ratio */
+ new_factor = gov_stat->correction_factor[gov_stat->bucket];
+ new_factor -= new_factor / DECAY;
+
+ if (gov_stat->next_timer_us > 0 && measured_us < MAX_INTERESTING)
+ new_factor += RESOLUTION * measured_us / gov_stat->next_timer_us;
+ else
+ /*
+ * we were idle so long that we count it as a perfect
+ * prediction
+ */
+ new_factor += RESOLUTION;
+
+ /*
+ * We don't want 0 as factor; we always want at least
+ * a tiny bit of estimated time. Fortunately, due to rounding,
+ * new_factor will stay nonzero regardless of measured_us values
+ * and the compiler can eliminate this test as long as DECAY > 1.
+ */
+ if (DECAY == 1 && unlikely(new_factor == 0))
+ new_factor = 1;
+
+ gov_stat->correction_factor[gov_stat->bucket] = new_factor;
+
+ /* update the repeating-pattern data */
+ gov_stat->intervals[gov_stat->interval_ptr++] = measured_us;
+ if (gov_stat->interval_ptr >= INTERVALS)
+ gov_stat->interval_ptr = 0;
+}
+
+/**
* cpuidle_predict - predict the next idle duration, in micro-second.
* There are two factors in the cpu idle governor, taken from menu:
* 1) Energy break even point
@@ -319,6 +392,10 @@ unsigned int cpuidle_predict(void)

if (cpuidle_not_available(drv, dev))
return -ENODEV;
+ /*
+ * Give the governor an opportunity to update on the outcome
+ */
+ cpuidle_update(drv, dev);

gov_stat = (struct cpuidle_governor_stat *)&(dev->gov_stat);

--
2.7.4