summaryrefslogtreecommitdiff
path: root/drivers/acpi/acpica/evxfevnt.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2010-08-04 01:55:14 +0400
committerLen Brown <len.brown@intel.com>2010-08-07 18:30:12 +0400
commit28f4f8a9def2b1f3a6066bae791c77043ec49524 (patch)
tree179cb5f8b64540e6f8e4440f46b2e930731a7fc3 /drivers/acpi/acpica/evxfevnt.c
parenta0d468718b9049f7396d101075a129a2d683ad66 (diff)
downloadlinux-28f4f8a9def2b1f3a6066bae791c77043ec49524.tar.xz
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)
******************************************************************************/
acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number)
{
- acpi_status status = AE_OK;
+ acpi_status status = AE_BAD_PARAMETER;
struct acpi_gpe_event_info *gpe_event_info;
acpi_cpu_flags flags;
@@ -302,28 +302,10 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number)
/* Ensure that we have a valid GPE number */
gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
- if (!gpe_event_info) {
- status = AE_BAD_PARAMETER;
- goto unlock_and_exit;
+ if (gpe_event_info) {
+ status = acpi_raw_enable_gpe(gpe_event_info);
}
- if (gpe_event_info->runtime_count == ACPI_UINT8_MAX) {
- status = AE_LIMIT; /* Too many references */
- goto unlock_and_exit;
- }
-
- gpe_event_info->runtime_count++;
- if (gpe_event_info->runtime_count == 1) {
- status = acpi_ev_update_gpe_enable_mask(gpe_event_info);
- if (ACPI_SUCCESS(status)) {
- status = acpi_ev_enable_gpe(gpe_event_info);
- }
- if (ACPI_FAILURE(status)) {
- gpe_event_info->runtime_count--;
- }
- }
-
-unlock_and_exit:
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
return_ACPI_STATUS(status);
}
@@ -345,7 +327,7 @@ ACPI_EXPORT_SYMBOL(acpi_enable_gpe)
******************************************************************************/
acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number)
{
- acpi_status status = AE_OK;
+ acpi_status status = AE_BAD_PARAMETER;
struct acpi_gpe_event_info *gpe_event_info;
acpi_cpu_flags flags;
@@ -356,32 +338,10 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number)
/* Ensure that we have a valid GPE number */
gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
- if (!gpe_event_info) {
- status = AE_BAD_PARAMETER;
- goto unlock_and_exit;
- }
-
- /* Hardware-disable a runtime GPE on removal of the last reference */
-
- if (!gpe_event_info->runtime_count) {
- status = AE_LIMIT; /* There are no references to remove */
- goto unlock_and_exit;
+ if (gpe_event_info) {
+ status = acpi_raw_disable_gpe(gpe_event_info) ;
}
- gpe_event_info->runtime_count--;
- if (!gpe_event_info->runtime_count) {
- status = acpi_ev_update_gpe_enable_mask(gpe_event_info);
- if (ACPI_SUCCESS(status)) {
- status =
- acpi_hw_low_set_gpe(gpe_event_info,
- ACPI_GPE_DISABLE);
- }
- if (ACPI_FAILURE(status)) {
- gpe_event_info->runtime_count++;
- }
- }
-
-unlock_and_exit:
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
return_ACPI_STATUS(status);
}
@@ -408,7 +368,6 @@ acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number)
acpi_status status = AE_OK;
struct acpi_gpe_event_info *gpe_event_info;
acpi_cpu_flags flags;
- u8 disable = 0;
ACPI_FUNCTION_TRACE(acpi_gpe_can_wake);
@@ -427,15 +386,12 @@ acpi_status acpi_gpe_can_wake(acpi_handle gpe_device, u32 gpe_number)
}
gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
- disable = (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD)
- && gpe_event_info->runtime_count;
+ if (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) {
+ (void)acpi_raw_disable_gpe(gpe_event_info);
+ }
unlock_and_exit:
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
-
- if (disable)
- status = acpi_disable_gpe(gpe_device, gpe_number);
-
return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL(acpi_gpe_can_wake)