aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/acpica/evxfevnt.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2010-08-03 17:55:14 -0400
committerLen Brown <len.brown@intel.com>2010-08-07 10:30:12 -0400
commit28f4f8a9def2b1f3a6066bae791c77043ec49524 (patch)
tree179cb5f8b64540e6f8e4440f46b2e930731a7fc3 /drivers/acpi/acpica/evxfevnt.c
parenta0d468718b9049f7396d101075a129a2d683ad66 (diff)
ACPI / ACPICA: Fix reference counting problems with GPE handlers
If a handler is installed for a GPE associated with an AML method and such that it cannot wake up the system from sleep states, the GPE remains enabled after the handler has been installed, although it should be disabled in that case to avoid spurious execution of the handler. Fix this issue by making acpi_install_gpe_handler() disable GPEs that were previously associated with AML methods and cannot wake up the system from sleep states. Analogously, make acpi_remove_gpe_handler() enable the GPEs that are associated with AML methods after their handlers have been removed and cannot wake up the system from sleep states. In addition to that, fix a code ordering issue in acpi_remove_gpe_handler() that renders the locking ineffective (ACPI_MTX_EVENTS is released temporarily in the middle of the routine to wait for the completion of events already in progress). For this purpose introduce acpi_raw_disable_gpe() and acpi_raw_enable_gpe() to be called with acpi_gbl_gpe_lock held and rework acpi_disable_gpe() and acpi_enable_gpe(), respectively, to use them. Also rework acpi_gpe_can_wake() to use acpi_raw_disable_gpe() instead of calling acpi_disable_gpe() after releasing the lock to avoid the possible theoretical race with acpi_install_gpe_handler(). Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Cc: "Moore, Robert" <robert.moore@intel.com> Cc: Lin Ming <ming.m.lin@intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/acpica/evxfevnt.c')
-rw-r--r--drivers/acpi/acpica/evxfevnt.c62
1 files changed, 9 insertions, 53 deletions
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index 53d591a11138..0fb9e94878d9 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -291,7 +291,7 @@ ACPI_EXPORT_SYMBOL(acpi_gpe_wakeup)
291 ******************************************************************************/ 291 ******************************************************************************/
292acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) 292acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number)
293{ 293{
294 acpi_status status = AE_OK; 294 acpi_status status = AE_BAD_PARAMETER;
295 struct acpi_gpe_event_info *gpe_event_info; 295 struct acpi_gpe_event_info *gpe_event_info;
296 acpi_cpu_flags flags; 296 acpi_cpu_flags flags;
297 297
@@ -302,28 +302,10 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number)
302 /* Ensure that we have a valid GPE number */ 302 /* Ensure that we have a valid GPE number */
303 303
304 gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); 304 gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
305 if (!gpe_event_info) { 305 if (gpe_event_info) {
306 status = AE_BAD_PARAMETER; 306 status = acpi_raw_enable_gpe(gpe_event_info);
307 goto unlock_and_exit;
308 } 307 }
309 308
310 if (gpe_event_info->runtime_count == ACPI_UINT8_MAX) {
311 status = AE_LIMIT; /* Too many references */
312 goto unlock_and_exit;
313 }
314
315 gpe_event_info->runtime_count++;
316 if (gpe_event_info->runtime_count == 1) {
317 status = acpi_ev_update_gpe_enable_mask(gpe_event_info);
318 if (ACPI_SUCCESS(status)) {
319 status = acpi_ev_enable_gpe(gpe_event_info);
320 }
321 if (ACPI_FAILURE(status)) {
322 gpe_event_info->runtime_count--;
323 }
324 }
325
326unlock_and_exit:
327 acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 309 acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
328 return_ACPI_STATUS(status); 310 return_ACPI_STATUS(status);
329} 311}
@@ -345,7 +327,7 @@ ACPI_EXPORT_SYMBOL(acpi_enable_gpe)
345 ******************************************************************************/ 327 ******************************************************************************/
346acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) 328acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number)
347{ 329{
348 acpi_status status = AE_OK; 330 acpi_status status = AE_BAD_PARAMETER;
349 struct acpi_gpe_event_info *gpe_event_info; 331 struct acpi_gpe_event_info *gpe_event_info;
350 acpi_cpu_flags flags; 332 acpi_cpu_flags flags;
351 333
@@ -356,32 +338,10 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number)
356 /* Ensure that we have a valid GPE number */ 338 /* Ensure that we have a valid GPE number */
357 339
358 gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); 340 gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
359 if (!gpe_event_info) { 341 if (gpe_event_info) {
360 status = AE_BAD_PARAMETER; 342 status = acpi_raw_disable_gpe(gpe_event_info) ;
361 goto unlock_and_exit;
362 }
363
364 /* Hardware-disable a runtime GPE on removal of the last reference */
365
366 if (!gpe_event_info->runtime_count) {
367 status = AE_LIMIT; /* There are no references to remove */
368 goto unlock_and_exit;
369 } 343 }
370 344
371 gpe_event_info->runtime_count--;
372 if (!gpe_event_info->runtime_count) {
373 status = acpi_ev_update_gpe_enable_mask(gpe_event_info);
374 if (ACPI_SUCCESS(status)) {
375 status =
376 acpi_hw_low_set_gpe(gpe_event_info,
377 ACPI_GPE_DISABLE);
378 }
379 if (ACPI_FAILURE(status)) {
380 gpe_event_info->runtime_count++;
381 }
382 }
383
384unlock_and_exit:
385 acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 345 acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
386 return_ACPI_STATUS(status); 346 return_ACPI_STATUS(status);
387} 347}
@@ -408,7 +368,6 @@ acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number)
408 acpi_status status = AE_OK; 368 acpi_status status = AE_OK;
409 struct acpi_gpe_event_info *gpe_event_info; 369 struct acpi_gpe_event_info *gpe_event_info;
410 acpi_cpu_flags flags; 370 acpi_cpu_flags flags;
411 u8 disable = 0;
412 371
413 ACPI_FUNCTION_TRACE(acpi_gpe_can_wake); 372 ACPI_FUNCTION_TRACE(acpi_gpe_can_wake);
414 373
@@ -427,15 +386,12 @@ acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number)
427 } 386 }
428 387
429 gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; 388 gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
430 disable = (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) 389 if (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) {
431 && gpe_event_info->runtime_count; 390 (void)acpi_raw_disable_gpe(gpe_event_info);
391 }
432 392
433unlock_and_exit: 393unlock_and_exit:
434 acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 394 acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
435
436 if (disable)
437 status = acpi_disable_gpe(gpe_device, gpe_number);
438
439 return_ACPI_STATUS(status); 395 return_ACPI_STATUS(status);
440} 396}
441ACPI_EXPORT_SYMBOL(acpi_gpe_can_wake) 397ACPI_EXPORT_SYMBOL(acpi_gpe_can_wake)