diff mbox series

[1/6] cpuidle: menu: Fix wakeup statistics updates for polling state

Message ID 1561295.epLgDtImU0@aspire.rjw.lan (mailing list archive)
State Mainlined
Delegated to: Rafael Wysocki
Headers show
Series cpuidle: menu: Fixes, optimizations and cleanups | expand

Commit Message

Rafael J. Wysocki Oct. 2, 2018, 9:42 p.m. UTC
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

If the CPU exits the "polling" state due to the time limit in the
loop in poll_idle(), this is not a real wakeup and it just means
that the "polling" state selection was not adequate.  The governor
mispredicted short idle duration, but had a more suitable state been
selected, the CPU might have spent more time in it.  In fact, there
is no reason to expect that there would have been a wakeup event
earlier than the next timer in that case.

Handling such cases as regular wakeups in menu_update() may cause the
menu governor to make suboptimal decisions going forward, but ignoring
them altogether would not be correct either, because every time
menu_select() is invoked, it makes a separate new attempt to predict
the idle duration taking distinct time to the closest timer event as
input and the outcomes of all those attempts should be recorded.

For this reason, make menu_update() always assume that if the
"polling" state was exited due to the time limit, the next proper
wakeup event for the CPU would be the next timer event (not
including the tick).

Fixes: a37b969a61c1 "cpuidle: poll_state: Add time limit to poll_idle()"
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/cpuidle/governors/menu.c |   10 ++++++++++
 drivers/cpuidle/poll_state.c     |    6 +++++-
 include/linux/cpuidle.h          |    1 +
 3 files changed, 16 insertions(+), 1 deletion(-)

Comments

Daniel Lezcano Oct. 4, 2018, 8:19 a.m. UTC | #1
On 02/10/2018 23:42, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> 
> If the CPU exits the "polling" state due to the time limit in the
> loop in poll_idle(), this is not a real wakeup and it just means
> that the "polling" state selection was not adequate.  The governor
> mispredicted short idle duration, but had a more suitable state been
> selected, the CPU might have spent more time in it.  In fact, there
> is no reason to expect that there would have been a wakeup event
> earlier than the next timer in that case.
> 
> Handling such cases as regular wakeups in menu_update() may cause the
> menu governor to make suboptimal decisions going forward, but ignoring
> them altogether would not be correct either, because every time
> menu_select() is invoked, it makes a separate new attempt to predict
> the idle duration taking distinct time to the closest timer event as
> input and the outcomes of all those attempts should be recorded.
> 
> For this reason, make menu_update() always assume that if the
> "polling" state was exited due to the time limit, the next proper
> wakeup event for the CPU would be the next timer event (not
> including the tick).
> 
> Fixes: a37b969a61c1 "cpuidle: poll_state: Add time limit to poll_idle()"
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> ---

Reviewed-by: Daniel Lezcano <daniel.lezcano@linaro.org>


>  drivers/cpuidle/governors/menu.c |   10 ++++++++++
>  drivers/cpuidle/poll_state.c     |    6 +++++-
>  include/linux/cpuidle.h          |    1 +
>  3 files changed, 16 insertions(+), 1 deletion(-)
> 
> Index: linux-pm/drivers/cpuidle/governors/menu.c
> ===================================================================
> --- linux-pm.orig/drivers/cpuidle/governors/menu.c
> +++ linux-pm/drivers/cpuidle/governors/menu.c
> @@ -511,6 +511,16 @@ static void menu_update(struct cpuidle_d
>  		 * duration predictor do a better job next time.
>  		 */
>  		measured_us = 9 * MAX_INTERESTING / 10;
> +	} else if ((drv->states[last_idx].flags & CPUIDLE_FLAG_POLLING) &&
> +		   dev->poll_time_limit) {
> +		/*
> +		 * The CPU exited the "polling" state due to a time limit, so
> +		 * the idle duration prediction leading to the selection of that
> +		 * state was inaccurate.  If a better prediction had been made,
> +		 * the CPU might have been woken up from idle by the next timer.
> +		 * Assume that to be the case.
> +		 */
> +		measured_us = data->next_timer_us;
>  	} else {
>  		/* measured value */
>  		measured_us = dev->last_residency;
> Index: linux-pm/include/linux/cpuidle.h
> ===================================================================
> --- linux-pm.orig/include/linux/cpuidle.h
> +++ linux-pm/include/linux/cpuidle.h
> @@ -81,6 +81,7 @@ struct cpuidle_device {
>  	unsigned int		registered:1;
>  	unsigned int		enabled:1;
>  	unsigned int		use_deepest_state:1;
> +	unsigned int		poll_time_limit:1;
>  	unsigned int		cpu;
>  
>  	int			last_residency;
> Index: linux-pm/drivers/cpuidle/poll_state.c
> ===================================================================
> --- linux-pm.orig/drivers/cpuidle/poll_state.c
> +++ linux-pm/drivers/cpuidle/poll_state.c
> @@ -17,6 +17,8 @@ static int __cpuidle poll_idle(struct cp
>  {
>  	u64 time_start = local_clock();
>  
> +	dev->poll_time_limit = false;
> +
>  	local_irq_enable();
>  	if (!current_set_polling_and_test()) {
>  		unsigned int loop_count = 0;
> @@ -27,8 +29,10 @@ static int __cpuidle poll_idle(struct cp
>  				continue;
>  
>  			loop_count = 0;
> -			if (local_clock() - time_start > POLL_IDLE_TIME_LIMIT)
> +			if (local_clock() - time_start > POLL_IDLE_TIME_LIMIT) {
> +				dev->poll_time_limit = true;
>  				break;
> +			}
>  		}
>  	}
>  	current_clr_polling();
>
diff mbox series

Patch

Index: linux-pm/drivers/cpuidle/governors/menu.c
===================================================================
--- linux-pm.orig/drivers/cpuidle/governors/menu.c
+++ linux-pm/drivers/cpuidle/governors/menu.c
@@ -511,6 +511,16 @@  static void menu_update(struct cpuidle_d
 		 * duration predictor do a better job next time.
 		 */
 		measured_us = 9 * MAX_INTERESTING / 10;
+	} else if ((drv->states[last_idx].flags & CPUIDLE_FLAG_POLLING) &&
+		   dev->poll_time_limit) {
+		/*
+		 * The CPU exited the "polling" state due to a time limit, so
+		 * the idle duration prediction leading to the selection of that
+		 * state was inaccurate.  If a better prediction had been made,
+		 * the CPU might have been woken up from idle by the next timer.
+		 * Assume that to be the case.
+		 */
+		measured_us = data->next_timer_us;
 	} else {
 		/* measured value */
 		measured_us = dev->last_residency;
Index: linux-pm/include/linux/cpuidle.h
===================================================================
--- linux-pm.orig/include/linux/cpuidle.h
+++ linux-pm/include/linux/cpuidle.h
@@ -81,6 +81,7 @@  struct cpuidle_device {
 	unsigned int		registered:1;
 	unsigned int		enabled:1;
 	unsigned int		use_deepest_state:1;
+	unsigned int		poll_time_limit:1;
 	unsigned int		cpu;
 
 	int			last_residency;
Index: linux-pm/drivers/cpuidle/poll_state.c
===================================================================
--- linux-pm.orig/drivers/cpuidle/poll_state.c
+++ linux-pm/drivers/cpuidle/poll_state.c
@@ -17,6 +17,8 @@  static int __cpuidle poll_idle(struct cp
 {
 	u64 time_start = local_clock();
 
+	dev->poll_time_limit = false;
+
 	local_irq_enable();
 	if (!current_set_polling_and_test()) {
 		unsigned int loop_count = 0;
@@ -27,8 +29,10 @@  static int __cpuidle poll_idle(struct cp
 				continue;
 
 			loop_count = 0;
-			if (local_clock() - time_start > POLL_IDLE_TIME_LIMIT)
+			if (local_clock() - time_start > POLL_IDLE_TIME_LIMIT) {
+				dev->poll_time_limit = true;
 				break;
+			}
 		}
 	}
 	current_clr_polling();