On Tue, Mar 20, 2018 at 4:45 PM, Rafael J. Wysocki <r...@rjwysocki.net> wrote: > From: Rafael J. Wysocki <rafael.j.wyso...@intel.com> > > Add a new pointer argument to cpuidle_select() and to the ->select > cpuidle governor callback to allow a boolean value indicating > whether or not the tick should be stopped before entering the > selected state to be returned from there. > > Make the ladder governor ignore that pointer (to preserve its > current behavior) and make the menu governor return 'false" through > it if: > (1) the idle exit latency is constrained at 0, or > (2) the selected state is a polling one, or > (3) the expected idle period duration is within the tick period > range. > > In addition to that, the correction factor computations in the menu > governor need to take the possibility that the tick may not be > stopped into account to avoid artificially small correction factor > values. To that end, add a mechanism to record tick wakeups, as > suggested by Peter Zijlstra, and use it to modify the menu_update() > behavior when tick wakeup occurs. Namely, make it add a (sufficiently > large) constant value to the correction factor in these cases (instead > of increasing the correction factor by a value based on the > measured idle time). > > Since the value returned through the new argument pointer of > cpuidle_select() is not used by its caller yet, this change by > itself is not expected to alter the functionality of the code. > > Signed-off-by: Rafael J. Wysocki <rafael.j.wyso...@intel.com> > --- > > v5 -> v7: > * Rename the new cpuidle_select() arg (and some related things) from > "nohz" to "stop_tick" (as requested by Peter). > * Use TICK_USEC from the previous patch. > * Record tick wakeups (as suggested by Peter) and use them to take the > tick into account in menu_update().
[cut] > @@ -427,31 +449,44 @@ static void menu_update(struct cpuidle_d > * 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 > data->next_timer_us) > - measured_us = data->next_timer_us; > - > /* Update our correction ratio */ > new_factor = data->correction_factor[data->bucket]; > new_factor -= new_factor / DECAY; > > - if (data->next_timer_us > 0 && measured_us < MAX_INTERESTING) > - new_factor += RESOLUTION * measured_us / data->next_timer_us; > - else > + if (data->tick_wakeup) { This should check if data->next_timer_us is greater than TICK_USEC too, but also the measured_us computation needs to go before it (or uninitialized measured_us will be used later on if this branch is executed). So please disregard this one entirely and take the v7.2 replacement instead of it: https://patchwork.kernel.org/patch/10299429/ The current versions (including the above) is in the git branch at git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git \ idle-loop-v7.2 > /* > - * we were idle so long that we count it as a perfect > - * prediction > + * If the CPU was woken up by the tick, it might have been > idle > + * for a much longer time if the tick had been stopped. That > + * time cannot be determined, so asssume that it would have > been > + * long, but not as long as the original return value of > + * tick_nohz_get_sleep_length(). Use a number between 0.5 and > + * 1, something like 0.75 (which is easy enough to get), that > + * should work on the average. > */ > - new_factor += RESOLUTION; > + new_factor += RESOLUTION / 2 + RESOLUTION / 4; > + } else { > + /* 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 > data->next_timer_us) > + measured_us = data->next_timer_us; > + > + if (data->next_timer_us > 0 && measured_us < MAX_INTERESTING) > + new_factor += RESOLUTION * measured_us / > data->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