aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Pan <jacob.jun.pan@linux.intel.com>2016-11-29 02:03:04 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-11-29 08:02:21 -0500
commitbb8313b603eb8fd52de48a079bfcd72dcab2ef1e (patch)
tree909fb13b3c68408b772568d2e1be11fb99d5e2cb
parented61390bff3ad43166a2552651b09ebd95dd1da5 (diff)
cpuidle: Allow enforcing deepest idle state selection
When idle injection is used to cap power, we need to override the governor's choice of idle states. For this reason, make it possible the deepest idle state selection to be enforced by setting a flag on a given CPU to achieve the maximum potential power draw reduction. Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com> [ rjw: Subject & changelog ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/cpuidle/cpuidle.c13
-rw-r--r--include/linux/cpuidle.h7
-rw-r--r--kernel/sched/idle.c13
3 files changed, 26 insertions, 7 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index c73207abb5a4..afc005b917fe 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -97,7 +97,17 @@ static int find_deepest_state(struct cpuidle_driver *drv,
97 return ret; 97 return ret;
98} 98}
99 99
100#ifdef CONFIG_SUSPEND 100/* Set the current cpu to use the deepest idle state, override governors */
101void cpuidle_use_deepest_state(bool enable)
102{
103 struct cpuidle_device *dev;
104
105 preempt_disable();
106 dev = cpuidle_get_device();
107 dev->use_deepest_state = enable;
108 preempt_enable();
109}
110
101/** 111/**
102 * cpuidle_find_deepest_state - Find the deepest available idle state. 112 * cpuidle_find_deepest_state - Find the deepest available idle state.
103 * @drv: cpuidle driver for the given CPU. 113 * @drv: cpuidle driver for the given CPU.
@@ -109,6 +119,7 @@ int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
109 return find_deepest_state(drv, dev, UINT_MAX, 0, false); 119 return find_deepest_state(drv, dev, UINT_MAX, 0, false);
110} 120}
111 121
122#ifdef CONFIG_SUSPEND
112static void enter_freeze_proper(struct cpuidle_driver *drv, 123static void enter_freeze_proper(struct cpuidle_driver *drv,
113 struct cpuidle_device *dev, int index) 124 struct cpuidle_device *dev, int index)
114{ 125{
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 15deea449edc..da346f2817a8 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -74,6 +74,7 @@ struct cpuidle_driver_kobj;
74struct cpuidle_device { 74struct cpuidle_device {
75 unsigned int registered:1; 75 unsigned int registered:1;
76 unsigned int enabled:1; 76 unsigned int enabled:1;
77 unsigned int use_deepest_state:1;
77 unsigned int cpu; 78 unsigned int cpu;
78 79
79 int last_residency; 80 int last_residency;
@@ -192,11 +193,12 @@ static inline struct cpuidle_driver *cpuidle_get_cpu_driver(
192static inline struct cpuidle_device *cpuidle_get_device(void) {return NULL; } 193static inline struct cpuidle_device *cpuidle_get_device(void) {return NULL; }
193#endif 194#endif
194 195
195#if defined(CONFIG_CPU_IDLE) && defined(CONFIG_SUSPEND) 196#ifdef CONFIG_CPU_IDLE
196extern int cpuidle_find_deepest_state(struct cpuidle_driver *drv, 197extern int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
197 struct cpuidle_device *dev); 198 struct cpuidle_device *dev);
198extern int cpuidle_enter_freeze(struct cpuidle_driver *drv, 199extern int cpuidle_enter_freeze(struct cpuidle_driver *drv,
199 struct cpuidle_device *dev); 200 struct cpuidle_device *dev);
201extern void cpuidle_use_deepest_state(bool enable);
200#else 202#else
201static inline int cpuidle_find_deepest_state(struct cpuidle_driver *drv, 203static inline int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
202 struct cpuidle_device *dev) 204 struct cpuidle_device *dev)
@@ -204,6 +206,9 @@ static inline int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
204static inline int cpuidle_enter_freeze(struct cpuidle_driver *drv, 206static inline int cpuidle_enter_freeze(struct cpuidle_driver *drv,
205 struct cpuidle_device *dev) 207 struct cpuidle_device *dev)
206{return -ENODEV; } 208{return -ENODEV; }
209static inline void cpuidle_use_deepest_state(bool enable)
210{
211}
207#endif 212#endif
208 213
209/* kernel/sched/idle.c */ 214/* kernel/sched/idle.c */
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index 1d8718d5300d..513e4dfeeae7 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -164,11 +164,14 @@ static void cpuidle_idle_call(void)
164 * timekeeping to prevent timer interrupts from kicking us out of idle 164 * timekeeping to prevent timer interrupts from kicking us out of idle
165 * until a proper wakeup interrupt happens. 165 * until a proper wakeup interrupt happens.
166 */ 166 */
167 if (idle_should_freeze()) { 167
168 entered_state = cpuidle_enter_freeze(drv, dev); 168 if (idle_should_freeze() || dev->use_deepest_state) {
169 if (entered_state > 0) { 169 if (idle_should_freeze()) {
170 local_irq_enable(); 170 entered_state = cpuidle_enter_freeze(drv, dev);
171 goto exit_idle; 171 if (entered_state > 0) {
172 local_irq_enable();
173 goto exit_idle;
174 }
172 } 175 }
173 176
174 next_state = cpuidle_find_deepest_state(drv, dev); 177 next_state = cpuidle_find_deepest_state(drv, dev);