aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-03-02 16:26:55 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-03-05 17:13:19 -0500
commitef2b22ac540c018bd574d1846ab95b9bfcf38702 (patch)
treecfd0282b0bb687f84364d70333a89dafff1fb6ca
parentdfcacc154fb38fdb2c243c3dbbdc1f26a64cedc8 (diff)
cpuidle / sleep: Use broadcast timer for states that stop local timer
Commit 381063133246 (PM / sleep: Re-implement suspend-to-idle handling) overlooked the fact that entering some sufficiently deep idle states by CPUs may cause their local timers to stop and in those cases it is necessary to switch over to a broadcast timer prior to entering the idle state. If the cpuidle driver in use does not provide the new ->enter_freeze callback for any of the idle states, that problem affects suspend-to-idle too, but it is not taken into account after the changes made by commit 381063133246. Fix that by changing the definition of cpuidle_enter_freeze() and re-arranging of the code in cpuidle_idle_call(), so the former does not call cpuidle_enter() any more and the fallback case is handled by cpuidle_idle_call() directly. Fixes: 381063133246 (PM / sleep: Re-implement suspend-to-idle handling) Reported-and-tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
-rw-r--r--drivers/cpuidle/cpuidle.c62
-rw-r--r--include/linux/cpuidle.h17
-rw-r--r--kernel/sched/idle.c30
3 files changed, 58 insertions, 51 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 8b3e132b6a01..080bd2dbde4b 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -44,8 +44,8 @@ void disable_cpuidle(void)
44 off = 1; 44 off = 1;
45} 45}
46 46
47static bool cpuidle_not_available(struct cpuidle_driver *drv, 47bool cpuidle_not_available(struct cpuidle_driver *drv,
48 struct cpuidle_device *dev) 48 struct cpuidle_device *dev)
49{ 49{
50 return off || !initialized || !drv || !dev || !dev->enabled; 50 return off || !initialized || !drv || !dev || !dev->enabled;
51} 51}
@@ -72,14 +72,8 @@ int cpuidle_play_dead(void)
72 return -ENODEV; 72 return -ENODEV;
73} 73}
74 74
75/** 75static int find_deepest_state(struct cpuidle_driver *drv,
76 * cpuidle_find_deepest_state - Find deepest state meeting specific conditions. 76 struct cpuidle_device *dev, bool freeze)
77 * @drv: cpuidle driver for the given CPU.
78 * @dev: cpuidle device for the given CPU.
79 * @freeze: Whether or not the state should be suitable for suspend-to-idle.
80 */
81static int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
82 struct cpuidle_device *dev, bool freeze)
83{ 77{
84 unsigned int latency_req = 0; 78 unsigned int latency_req = 0;
85 int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1; 79 int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1;
@@ -98,6 +92,17 @@ static int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
98 return ret; 92 return ret;
99} 93}
100 94
95/**
96 * cpuidle_find_deepest_state - Find the deepest available idle state.
97 * @drv: cpuidle driver for the given CPU.
98 * @dev: cpuidle device for the given CPU.
99 */
100int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
101 struct cpuidle_device *dev)
102{
103 return find_deepest_state(drv, dev, false);
104}
105
101static void enter_freeze_proper(struct cpuidle_driver *drv, 106static void enter_freeze_proper(struct cpuidle_driver *drv,
102 struct cpuidle_device *dev, int index) 107 struct cpuidle_device *dev, int index)
103{ 108{
@@ -119,46 +124,26 @@ static void enter_freeze_proper(struct cpuidle_driver *drv,
119 124
120/** 125/**
121 * cpuidle_enter_freeze - Enter an idle state suitable for suspend-to-idle. 126 * cpuidle_enter_freeze - Enter an idle state suitable for suspend-to-idle.
127 * @drv: cpuidle driver for the given CPU.
128 * @dev: cpuidle device for the given CPU.
122 * 129 *
123 * If there are states with the ->enter_freeze callback, find the deepest of 130 * If there are states with the ->enter_freeze callback, find the deepest of
124 * them and enter it with frozen tick. Otherwise, find the deepest state 131 * them and enter it with frozen tick.
125 * available and enter it normally.
126 *
127 * Returns with enabled interrupts.
128 */ 132 */
129void cpuidle_enter_freeze(void) 133int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev)
130{ 134{
131 struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
132 struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
133 int index; 135 int index;
134 136
135 if (cpuidle_not_available(drv, dev))
136 goto fallback;
137
138 /* 137 /*
139 * Find the deepest state with ->enter_freeze present, which guarantees 138 * Find the deepest state with ->enter_freeze present, which guarantees
140 * that interrupts won't be enabled when it exits and allows the tick to 139 * that interrupts won't be enabled when it exits and allows the tick to
141 * be frozen safely. 140 * be frozen safely.
142 */ 141 */
143 index = cpuidle_find_deepest_state(drv, dev, true); 142 index = find_deepest_state(drv, dev, true);
144 if (index >= 0) { 143 if (index >= 0)
145 enter_freeze_proper(drv, dev, index); 144 enter_freeze_proper(drv, dev, index);
146 local_irq_enable();
147 return;
148 }
149 145
150 /* 146 return index;
151 * It is not safe to freeze the tick, find the deepest state available
152 * at all and try to enter it normally.
153 */
154 index = cpuidle_find_deepest_state(drv, dev, false);
155 if (index >= 0) {
156 cpuidle_enter(drv, dev, index);
157 return;
158 }
159
160 fallback:
161 arch_cpu_idle();
162} 147}
163 148
164/** 149/**
@@ -217,9 +202,6 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
217 */ 202 */
218int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) 203int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
219{ 204{
220 if (cpuidle_not_available(drv, dev))
221 return -ENODEV;
222
223 return cpuidle_curr_governor->select(drv, dev); 205 return cpuidle_curr_governor->select(drv, dev);
224} 206}
225 207
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index f551a9299ac9..306178d7309f 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -126,6 +126,8 @@ struct cpuidle_driver {
126 126
127#ifdef CONFIG_CPU_IDLE 127#ifdef CONFIG_CPU_IDLE
128extern void disable_cpuidle(void); 128extern void disable_cpuidle(void);
129extern bool cpuidle_not_available(struct cpuidle_driver *drv,
130 struct cpuidle_device *dev);
129 131
130extern int cpuidle_select(struct cpuidle_driver *drv, 132extern int cpuidle_select(struct cpuidle_driver *drv,
131 struct cpuidle_device *dev); 133 struct cpuidle_device *dev);
@@ -150,11 +152,17 @@ extern void cpuidle_resume(void);
150extern int cpuidle_enable_device(struct cpuidle_device *dev); 152extern int cpuidle_enable_device(struct cpuidle_device *dev);
151extern void cpuidle_disable_device(struct cpuidle_device *dev); 153extern void cpuidle_disable_device(struct cpuidle_device *dev);
152extern int cpuidle_play_dead(void); 154extern int cpuidle_play_dead(void);
153extern void cpuidle_enter_freeze(void); 155extern int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
156 struct cpuidle_device *dev);
157extern int cpuidle_enter_freeze(struct cpuidle_driver *drv,
158 struct cpuidle_device *dev);
154 159
155extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); 160extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev);
156#else 161#else
157static inline void disable_cpuidle(void) { } 162static inline void disable_cpuidle(void) { }
163static inline bool cpuidle_not_available(struct cpuidle_driver *drv,
164 struct cpuidle_device *dev)
165{return true; }
158static inline int cpuidle_select(struct cpuidle_driver *drv, 166static inline int cpuidle_select(struct cpuidle_driver *drv,
159 struct cpuidle_device *dev) 167 struct cpuidle_device *dev)
160{return -ENODEV; } 168{return -ENODEV; }
@@ -183,7 +191,12 @@ static inline int cpuidle_enable_device(struct cpuidle_device *dev)
183{return -ENODEV; } 191{return -ENODEV; }
184static inline void cpuidle_disable_device(struct cpuidle_device *dev) { } 192static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
185static inline int cpuidle_play_dead(void) {return -ENODEV; } 193static inline int cpuidle_play_dead(void) {return -ENODEV; }
186static inline void cpuidle_enter_freeze(void) { } 194static inline int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
195 struct cpuidle_device *dev)
196{return -ENODEV; }
197static inline int cpuidle_enter_freeze(struct cpuidle_driver *drv,
198 struct cpuidle_device *dev)
199{return -ENODEV; }
187static inline struct cpuidle_driver *cpuidle_get_cpu_driver( 200static inline struct cpuidle_driver *cpuidle_get_cpu_driver(
188 struct cpuidle_device *dev) {return NULL; } 201 struct cpuidle_device *dev) {return NULL; }
189#endif 202#endif
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index 84b93b68482a..80014a178342 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -82,6 +82,7 @@ static void cpuidle_idle_call(void)
82 struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); 82 struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
83 int next_state, entered_state; 83 int next_state, entered_state;
84 unsigned int broadcast; 84 unsigned int broadcast;
85 bool reflect;
85 86
86 /* 87 /*
87 * Check if the idle task must be rescheduled. If it is the 88 * Check if the idle task must be rescheduled. If it is the
@@ -105,6 +106,9 @@ static void cpuidle_idle_call(void)
105 */ 106 */
106 rcu_idle_enter(); 107 rcu_idle_enter();
107 108
109 if (cpuidle_not_available(drv, dev))
110 goto use_default;
111
108 /* 112 /*
109 * Suspend-to-idle ("freeze") is a system state in which all user space 113 * Suspend-to-idle ("freeze") is a system state in which all user space
110 * has been frozen, all I/O devices have been suspended and the only 114 * has been frozen, all I/O devices have been suspended and the only
@@ -115,15 +119,22 @@ static void cpuidle_idle_call(void)
115 * until a proper wakeup interrupt happens. 119 * until a proper wakeup interrupt happens.
116 */ 120 */
117 if (idle_should_freeze()) { 121 if (idle_should_freeze()) {
118 cpuidle_enter_freeze(); 122 entered_state = cpuidle_enter_freeze(drv, dev);
119 goto exit_idle; 123 if (entered_state >= 0) {
120 } 124 local_irq_enable();
125 goto exit_idle;
126 }
121 127
122 /* 128 reflect = false;
123 * Ask the cpuidle framework to choose a convenient idle state. 129 next_state = cpuidle_find_deepest_state(drv, dev);
124 * Fall back to the default arch idle method on errors. 130 } else {
125 */ 131 reflect = true;
126 next_state = cpuidle_select(drv, dev); 132 /*
133 * Ask the cpuidle framework to choose a convenient idle state.
134 */
135 next_state = cpuidle_select(drv, dev);
136 }
137 /* Fall back to the default arch idle method on errors. */
127 if (next_state < 0) 138 if (next_state < 0)
128 goto use_default; 139 goto use_default;
129 140
@@ -170,7 +181,8 @@ static void cpuidle_idle_call(void)
170 /* 181 /*
171 * Give the governor an opportunity to reflect on the outcome 182 * Give the governor an opportunity to reflect on the outcome
172 */ 183 */
173 cpuidle_reflect(dev, entered_state); 184 if (reflect)
185 cpuidle_reflect(dev, entered_state);
174 186
175exit_idle: 187exit_idle:
176 __current_set_polling(); 188 __current_set_polling();