diff options
author | Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> | 2008-05-19 19:09:27 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-06-11 19:13:45 -0400 |
commit | dcb84f335bee9c9a7781cfc5d74492dccaf066d2 (patch) | |
tree | e24d4ca7df49b2a87862aa69c09d21ad45a024b7 /drivers/cpuidle/cpuidle.c | |
parent | e1094bfa26e5e94af2fea79e004614dbce42b008 (diff) |
cpuidle acpi driver: fix oops on AC<->DC
cpuidle and acpi driver interaction bug with the way cpuidle_register_driver()
is called. Due to this bug, there will be oops on
AC<->DC on some systems, where they support C-states in one DC and not in AC.
The current code does
ON BOOT:
Look at CST and other C-state info to see whether more than C1 is
supported. If it is, then acpi processor_idle does a
cpuidle_register_driver() call, which internally enables the device.
ON CST change notification (AC<->DC) and on suspend-resume:
acpi driver temporarily disables device, updates the device with
any new C-states, and reenables the device.
The problem is is on boot, there are no C2, C3 states supported and we skip
the register. Later on AC<->DC, we may get a CST notification and we try
to reevaluate CST and enabled the device, without actually registering it.
This causes breakage as we try to create /sys fs sub directory, without the
parent directory which is created at register time.
Thanks to Sanjeev for reporting the problem here.
http://bugzilla.kernel.org/show_bug.cgi?id=10394
Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/cpuidle/cpuidle.c')
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 40 |
1 files changed, 35 insertions, 5 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index fc555a90bb21..23554b676d6e 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
@@ -38,6 +38,8 @@ static void cpuidle_kick_cpus(void) | |||
38 | static void cpuidle_kick_cpus(void) {} | 38 | static void cpuidle_kick_cpus(void) {} |
39 | #endif | 39 | #endif |
40 | 40 | ||
41 | static int __cpuidle_register_device(struct cpuidle_device *dev); | ||
42 | |||
41 | /** | 43 | /** |
42 | * cpuidle_idle_call - the main idle loop | 44 | * cpuidle_idle_call - the main idle loop |
43 | * | 45 | * |
@@ -138,6 +140,12 @@ int cpuidle_enable_device(struct cpuidle_device *dev) | |||
138 | if (!dev->state_count) | 140 | if (!dev->state_count) |
139 | return -EINVAL; | 141 | return -EINVAL; |
140 | 142 | ||
143 | if (dev->registered == 0) { | ||
144 | ret = __cpuidle_register_device(dev); | ||
145 | if (ret) | ||
146 | return ret; | ||
147 | } | ||
148 | |||
141 | if ((ret = cpuidle_add_state_sysfs(dev))) | 149 | if ((ret = cpuidle_add_state_sysfs(dev))) |
142 | return ret; | 150 | return ret; |
143 | 151 | ||
@@ -232,10 +240,13 @@ static void poll_idle_init(struct cpuidle_device *dev) {} | |||
232 | #endif /* CONFIG_ARCH_HAS_CPU_RELAX */ | 240 | #endif /* CONFIG_ARCH_HAS_CPU_RELAX */ |
233 | 241 | ||
234 | /** | 242 | /** |
235 | * cpuidle_register_device - registers a CPU's idle PM feature | 243 | * __cpuidle_register_device - internal register function called before register |
244 | * and enable routines | ||
236 | * @dev: the cpu | 245 | * @dev: the cpu |
246 | * | ||
247 | * cpuidle_lock mutex must be held before this is called | ||
237 | */ | 248 | */ |
238 | int cpuidle_register_device(struct cpuidle_device *dev) | 249 | static int __cpuidle_register_device(struct cpuidle_device *dev) |
239 | { | 250 | { |
240 | int ret; | 251 | int ret; |
241 | struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); | 252 | struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); |
@@ -247,18 +258,34 @@ int cpuidle_register_device(struct cpuidle_device *dev) | |||
247 | 258 | ||
248 | init_completion(&dev->kobj_unregister); | 259 | init_completion(&dev->kobj_unregister); |
249 | 260 | ||
250 | mutex_lock(&cpuidle_lock); | ||
251 | |||
252 | poll_idle_init(dev); | 261 | poll_idle_init(dev); |
253 | 262 | ||
254 | per_cpu(cpuidle_devices, dev->cpu) = dev; | 263 | per_cpu(cpuidle_devices, dev->cpu) = dev; |
255 | list_add(&dev->device_list, &cpuidle_detected_devices); | 264 | list_add(&dev->device_list, &cpuidle_detected_devices); |
256 | if ((ret = cpuidle_add_sysfs(sys_dev))) { | 265 | if ((ret = cpuidle_add_sysfs(sys_dev))) { |
257 | mutex_unlock(&cpuidle_lock); | ||
258 | module_put(cpuidle_curr_driver->owner); | 266 | module_put(cpuidle_curr_driver->owner); |
259 | return ret; | 267 | return ret; |
260 | } | 268 | } |
261 | 269 | ||
270 | dev->registered = 1; | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * cpuidle_register_device - registers a CPU's idle PM feature | ||
276 | * @dev: the cpu | ||
277 | */ | ||
278 | int cpuidle_register_device(struct cpuidle_device *dev) | ||
279 | { | ||
280 | int ret; | ||
281 | |||
282 | mutex_lock(&cpuidle_lock); | ||
283 | |||
284 | if ((ret = __cpuidle_register_device(dev))) { | ||
285 | mutex_unlock(&cpuidle_lock); | ||
286 | return ret; | ||
287 | } | ||
288 | |||
262 | cpuidle_enable_device(dev); | 289 | cpuidle_enable_device(dev); |
263 | cpuidle_install_idle_handler(); | 290 | cpuidle_install_idle_handler(); |
264 | 291 | ||
@@ -278,6 +305,9 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) | |||
278 | { | 305 | { |
279 | struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); | 306 | struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); |
280 | 307 | ||
308 | if (dev->registered == 0) | ||
309 | return; | ||
310 | |||
281 | cpuidle_pause_and_lock(); | 311 | cpuidle_pause_and_lock(); |
282 | 312 | ||
283 | cpuidle_disable_device(dev); | 313 | cpuidle_disable_device(dev); |