aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpuidle
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-01-07 18:29:20 -0500
committerLen Brown <len.brown@intel.com>2011-01-12 12:47:30 -0500
commitd8c216cfa57e8a579f41729cbb88c97835d9ac8d (patch)
tree0085ebf22bf3a05607e512102a3dab065c5514a9 /drivers/cpuidle
parentddbd550d503c9cdefcd6674a0ef168d57d3f0917 (diff)
cpuidle: Make cpuidle_enable_device() call poll_idle_init()
The following scenario is possible with the current cpuidle code and the ACPI cpuidle driver: (1) acpi_processor_cst_has_changed() is called, (2) cpuidle_disable_device() is called, (3) cpuidle_remove_state_sysfs() is called to remove the (presumably outdated) states info from sysfs, (3) acpi_processor_get_power_info() is called, the first entry in the pr->power.states[] table is filled with zeros, (4) acpi_processor_setup_cpuidle() is called and it doesn't fill the first entry in pr->power.states[], (5) cpuidle_enable_device() is called, (6) __cpuidle_register_device() is _not_ called, since the device has already been registered, (7) Consequently, poll_idle_init() is _not_ called either, (8) cpuidle_add_state_sysfs() is called to create the sysfs attributes for the new states and it uses the bogus first table entry from acpi_processor_get_power_info() for creating state0. This problem is avoided if cpuidle_enable_device() unconditionally calls poll_idle_init(). Reported-by: Len Brown <len.brown@intel.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Len Brown <len.brown@intel.com> cc: stable@kernel.org
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r--drivers/cpuidle/cpuidle.c82
1 files changed, 41 insertions, 41 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index a50710843378..97df791c74cb 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -154,6 +154,45 @@ void cpuidle_resume_and_unlock(void)
154 154
155EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); 155EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock);
156 156
157#ifdef CONFIG_ARCH_HAS_CPU_RELAX
158static int poll_idle(struct cpuidle_device *dev, struct cpuidle_state *st)
159{
160 ktime_t t1, t2;
161 s64 diff;
162 int ret;
163
164 t1 = ktime_get();
165 local_irq_enable();
166 while (!need_resched())
167 cpu_relax();
168
169 t2 = ktime_get();
170 diff = ktime_to_us(ktime_sub(t2, t1));
171 if (diff > INT_MAX)
172 diff = INT_MAX;
173
174 ret = (int) diff;
175 return ret;
176}
177
178static void poll_idle_init(struct cpuidle_device *dev)
179{
180 struct cpuidle_state *state = &dev->states[0];
181
182 cpuidle_set_statedata(state, NULL);
183
184 snprintf(state->name, CPUIDLE_NAME_LEN, "C0");
185 snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE");
186 state->exit_latency = 0;
187 state->target_residency = 0;
188 state->power_usage = -1;
189 state->flags = CPUIDLE_FLAG_POLL;
190 state->enter = poll_idle;
191}
192#else
193static void poll_idle_init(struct cpuidle_device *dev) {}
194#endif /* CONFIG_ARCH_HAS_CPU_RELAX */
195
157/** 196/**
158 * cpuidle_enable_device - enables idle PM for a CPU 197 * cpuidle_enable_device - enables idle PM for a CPU
159 * @dev: the CPU 198 * @dev: the CPU
@@ -178,6 +217,8 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
178 return ret; 217 return ret;
179 } 218 }
180 219
220 poll_idle_init(dev);
221
181 if ((ret = cpuidle_add_state_sysfs(dev))) 222 if ((ret = cpuidle_add_state_sysfs(dev)))
182 return ret; 223 return ret;
183 224
@@ -232,45 +273,6 @@ void cpuidle_disable_device(struct cpuidle_device *dev)
232 273
233EXPORT_SYMBOL_GPL(cpuidle_disable_device); 274EXPORT_SYMBOL_GPL(cpuidle_disable_device);
234 275
235#ifdef CONFIG_ARCH_HAS_CPU_RELAX
236static int poll_idle(struct cpuidle_device *dev, struct cpuidle_state *st)
237{
238 ktime_t t1, t2;
239 s64 diff;
240 int ret;
241
242 t1 = ktime_get();
243 local_irq_enable();
244 while (!need_resched())
245 cpu_relax();
246
247 t2 = ktime_get();
248 diff = ktime_to_us(ktime_sub(t2, t1));
249 if (diff > INT_MAX)
250 diff = INT_MAX;
251
252 ret = (int) diff;
253 return ret;
254}
255
256static void poll_idle_init(struct cpuidle_device *dev)
257{
258 struct cpuidle_state *state = &dev->states[0];
259
260 cpuidle_set_statedata(state, NULL);
261
262 snprintf(state->name, CPUIDLE_NAME_LEN, "C0");
263 snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE");
264 state->exit_latency = 0;
265 state->target_residency = 0;
266 state->power_usage = -1;
267 state->flags = CPUIDLE_FLAG_POLL;
268 state->enter = poll_idle;
269}
270#else
271static void poll_idle_init(struct cpuidle_device *dev) {}
272#endif /* CONFIG_ARCH_HAS_CPU_RELAX */
273
274/** 276/**
275 * __cpuidle_register_device - internal register function called before register 277 * __cpuidle_register_device - internal register function called before register
276 * and enable routines 278 * and enable routines
@@ -291,8 +293,6 @@ static int __cpuidle_register_device(struct cpuidle_device *dev)
291 293
292 init_completion(&dev->kobj_unregister); 294 init_completion(&dev->kobj_unregister);
293 295
294 poll_idle_init(dev);
295
296 /* 296 /*
297 * cpuidle driver should set the dev->power_specified bit 297 * cpuidle driver should set the dev->power_specified bit
298 * before registering the device if the driver provides 298 * before registering the device if the driver provides