aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/acpica/evxface.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/evxface.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/evxface.c')
-rw-r--r--drivers/acpi/acpica/evxface.c77
1 files changed, 52 insertions, 25 deletions
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}