aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/acpica
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
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')
-rw-r--r--drivers/acpi/acpica/acevents.h4
-rw-r--r--drivers/acpi/acpica/aclocal.h1
-rw-r--r--drivers/acpi/acpica/evgpe.c73
-rw-r--r--drivers/acpi/acpica/evxface.c77
-rw-r--r--drivers/acpi/acpica/evxfevnt.c62
5 files changed, 139 insertions, 78 deletions
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index e0e6affb0d80..36867cd70eac 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -82,6 +82,10 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info);
82 82
83acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info); 83acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info);
84 84
85acpi_status acpi_raw_enable_gpe(struct acpi_gpe_event_info *gpe_event_info);
86
87acpi_status acpi_raw_disable_gpe(struct acpi_gpe_event_info *gpe_event_info);
88
85struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, 89struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device,
86 u32 gpe_number); 90 u32 gpe_number);
87 91
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 1ee0bcf399aa..df85b53a674f 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -412,6 +412,7 @@ struct acpi_handler_info {
412 acpi_event_handler address; /* Address of handler, if any */ 412 acpi_event_handler address; /* Address of handler, if any */
413 void *context; /* Context to be passed to handler */ 413 void *context; /* Context to be passed to handler */
414 struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ 414 struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */
415 u8 orig_flags; /* Original misc info about this GPE */
415}; 416};
416 417
417union acpi_gpe_dispatch_info { 418union acpi_gpe_dispatch_info {
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
index 7a6a3e6f4be0..f226eac314db 100644
--- a/drivers/acpi/acpica/evgpe.c
+++ b/drivers/acpi/acpica/evgpe.c
@@ -137,6 +137,79 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
137 137
138/******************************************************************************* 138/*******************************************************************************
139 * 139 *
140 * FUNCTION: acpi_raw_enable_gpe
141 *
142 * PARAMETERS: gpe_event_info - GPE to enable
143 *
144 * RETURN: Status
145 *
146 * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is
147 * hardware-enabled.
148 *
149 ******************************************************************************/
150
151acpi_status acpi_raw_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
152{
153 acpi_status status = AE_OK;
154
155 if (gpe_event_info->runtime_count == ACPI_UINT8_MAX) {
156 return_ACPI_STATUS(AE_LIMIT);
157 }
158
159 gpe_event_info->runtime_count++;
160 if (gpe_event_info->runtime_count == 1) {
161 status = acpi_ev_update_gpe_enable_mask(gpe_event_info);
162 if (ACPI_SUCCESS(status)) {
163 status = acpi_ev_enable_gpe(gpe_event_info);
164 }
165
166 if (ACPI_FAILURE(status)) {
167 gpe_event_info->runtime_count--;
168 }
169 }
170
171 return_ACPI_STATUS(status);
172}
173
174/*******************************************************************************
175 *
176 * FUNCTION: acpi_raw_disable_gpe
177 *
178 * PARAMETERS: gpe_event_info - GPE to disable
179 *
180 * RETURN: Status
181 *
182 * DESCRIPTION: Remove a reference to a GPE. When the last reference is
183 * removed, the GPE is hardware-disabled.
184 *
185 ******************************************************************************/
186
187acpi_status acpi_raw_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)
188{
189 acpi_status status = AE_OK;
190
191 if (!gpe_event_info->runtime_count) {
192 return_ACPI_STATUS(AE_LIMIT);
193 }
194
195 gpe_event_info->runtime_count--;
196 if (!gpe_event_info->runtime_count) {
197 status = acpi_ev_update_gpe_enable_mask(gpe_event_info);
198 if (ACPI_SUCCESS(status)) {
199 status = acpi_hw_low_set_gpe(gpe_event_info,
200 ACPI_GPE_DISABLE);
201 }
202
203 if (ACPI_FAILURE(status)) {
204 gpe_event_info->runtime_count++;
205 }
206 }
207
208 return_ACPI_STATUS(status);
209}
210
211/*******************************************************************************
212 *
140 * FUNCTION: acpi_ev_low_get_gpe_info 213 * FUNCTION: acpi_ev_low_get_gpe_info
141 * 214 *
142 * PARAMETERS: gpe_number - Raw GPE number 215 * PARAMETERS: gpe_number - Raw GPE number
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
index 4a531cdf7942..14e48add32fa 100644
--- a/drivers/acpi/acpica/evxface.c
+++ b/drivers/acpi/acpica/evxface.c
@@ -691,12 +691,22 @@ acpi_install_gpe_handler(acpi_handle gpe_device,
691 return_ACPI_STATUS(status); 691 return_ACPI_STATUS(status);
692 } 692 }
693 693
694 /* Allocate memory for the handler object */
695
696 handler = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_handler_info));
697 if (!handler) {
698 status = AE_NO_MEMORY;
699 goto unlock_and_exit;
700 }
701
702 flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
703
694 /* Ensure that we have a valid GPE number */ 704 /* Ensure that we have a valid GPE number */
695 705
696 gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); 706 gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
697 if (!gpe_event_info) { 707 if (!gpe_event_info) {
698 status = AE_BAD_PARAMETER; 708 status = AE_BAD_PARAMETER;
699 goto unlock_and_exit; 709 goto free_and_exit;
700 } 710 }
701 711
702 /* Make sure that there isn't a handler there already */ 712 /* Make sure that there isn't a handler there already */
@@ -704,24 +714,30 @@ acpi_install_gpe_handler(acpi_handle gpe_device,
704 if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == 714 if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
705 ACPI_GPE_DISPATCH_HANDLER) { 715 ACPI_GPE_DISPATCH_HANDLER) {
706 status = AE_ALREADY_EXISTS; 716 status = AE_ALREADY_EXISTS;
707 goto unlock_and_exit; 717 goto free_and_exit;
708 } 718 }
709 719
710 /* Allocate and init handler object */ 720 /* Allocate and init handler object */
711 721
712 handler = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_handler_info));
713 if (!handler) {
714 status = AE_NO_MEMORY;
715 goto unlock_and_exit;
716 }
717
718 handler->address = address; 722 handler->address = address;
719 handler->context = context; 723 handler->context = context;
720 handler->method_node = gpe_event_info->dispatch.method_node; 724 handler->method_node = gpe_event_info->dispatch.method_node;
725 handler->orig_flags = gpe_event_info->flags &
726 (ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK);
727
728 /*
729 * If the GPE is associated with a method and it cannot wake up the
730 * system from sleep states, it was enabled automatically during
731 * initialization, so it has to be disabled now to avoid spurious
732 * execution of the handler.
733 */
734
735 if ((handler->orig_flags & ACPI_GPE_DISPATCH_METHOD)
736 && !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE))
737 (void)acpi_raw_disable_gpe(gpe_event_info);
721 738
722 /* Install the handler */ 739 /* Install the handler */
723 740
724 flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
725 gpe_event_info->dispatch.handler = handler; 741 gpe_event_info->dispatch.handler = handler;
726 742
727 /* Setup up dispatch flags to indicate handler (vs. method) */ 743 /* Setup up dispatch flags to indicate handler (vs. method) */
@@ -735,6 +751,11 @@ acpi_install_gpe_handler(acpi_handle gpe_device,
735unlock_and_exit: 751unlock_and_exit:
736 (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); 752 (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
737 return_ACPI_STATUS(status); 753 return_ACPI_STATUS(status);
754
755free_and_exit:
756 acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
757 ACPI_FREE(handler);
758 goto unlock_and_exit;
738} 759}
739 760
740ACPI_EXPORT_SYMBOL(acpi_install_gpe_handler) 761ACPI_EXPORT_SYMBOL(acpi_install_gpe_handler)
@@ -770,11 +791,17 @@ acpi_remove_gpe_handler(acpi_handle gpe_device,
770 return_ACPI_STATUS(AE_BAD_PARAMETER); 791 return_ACPI_STATUS(AE_BAD_PARAMETER);
771 } 792 }
772 793
794 /* Make sure all deferred tasks are completed */
795
796 acpi_os_wait_events_complete(NULL);
797
773 status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); 798 status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
774 if (ACPI_FAILURE(status)) { 799 if (ACPI_FAILURE(status)) {
775 return_ACPI_STATUS(status); 800 return_ACPI_STATUS(status);
776 } 801 }
777 802
803 flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
804
778 /* Ensure that we have a valid GPE number */ 805 /* Ensure that we have a valid GPE number */
779 806
780 gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); 807 gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
@@ -798,34 +825,34 @@ acpi_remove_gpe_handler(acpi_handle gpe_device,
798 goto unlock_and_exit; 825 goto unlock_and_exit;
799 } 826 }
800 827
801 /* Make sure all deferred tasks are completed */
802
803 (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
804 acpi_os_wait_events_complete(NULL);
805 status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
806 if (ACPI_FAILURE(status)) {
807 return_ACPI_STATUS(status);
808 }
809
810 /* Remove the handler */ 828 /* Remove the handler */
811 829
812 flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
813 handler = gpe_event_info->dispatch.handler; 830 handler = gpe_event_info->dispatch.handler;
814 831
815 /* Restore Method node (if any), set dispatch flags */ 832 /* Restore Method node (if any), set dispatch flags */
816 833
817 gpe_event_info->dispatch.method_node = handler->method_node; 834 gpe_event_info->dispatch.method_node = handler->method_node;
818 gpe_event_info->flags &= ~ACPI_GPE_DISPATCH_MASK; /* Clear bits */ 835 gpe_event_info->flags &=
819 if (handler->method_node) { 836 ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK);
820 gpe_event_info->flags |= ACPI_GPE_DISPATCH_METHOD; 837 gpe_event_info->flags |= handler->orig_flags;
821 } 838
822 acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 839 /*
840 * If the GPE was previously associated with a method and it cannot wake
841 * up the system from sleep states, it should be enabled at this point
842 * to restore the post-initialization configuration.
843 */
844
845 if ((handler->orig_flags & ACPI_GPE_DISPATCH_METHOD)
846 && !(gpe_event_info->flags & ACPI_GPE_CAN_WAKE))
847 (void)acpi_raw_enable_gpe(gpe_event_info);
823 848
824 /* Now we can free the handler object */ 849 /* Now we can free the handler object */
825 850
826 ACPI_FREE(handler); 851 ACPI_FREE(handler);
827 852
828 unlock_and_exit: 853unlock_and_exit:
854 acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
855
829 (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); 856 (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
830 return_ACPI_STATUS(status); 857 return_ACPI_STATUS(status);
831} 858}
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)