Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 2 Dec 2016 08:21:08 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r309400 - head/sys/dev/acpica
Message-ID:  <201612020821.uB28L8s2000195@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Fri Dec  2 08:21:08 2016
New Revision: 309400
URL: https://svnweb.freebsd.org/changeset/base/309400

Log:
  Fix for endless recursion in the ACPI GPE handler during boot.
  
  When handling a GPE ACPI interrupt object the EcSpaceHandler()
  function can be called which checks the EC_EVENT_SCI bit and then
  recurse on the EcGpeQueryHandler() function. If there are multiple GPE
  events pending the EC_EVENT_SCI bit will be set at the next call to
  EcSpaceHandler() causing it to recurse again via the
  EcGpeQueryHandler() function. This leads to a slow never ending
  recursion during boot which prevents proper system startup, because
  the EC_EVENT_SCI bit never gets cleared in this scenario.
  
  The behaviour is reproducible with the ALASKA AMI in combination with
  a newer Skylake based mainboard in the following way:
  
  Enter BIOS and adjust the clock one hour forward. Save and exit the
  BIOS. System fails to boot due to the above mentioned bug in
  EcGpeQueryHandler() which was observed recursing multiple times.
  
  This patch adds a simple recursion guard to the EcGpeQueryHandler()
  function and also also adds logic to detect if new GPE events occurred
  during the execution of EcGpeQueryHandler() and then loop on this
  function instead of recursing.
  
  Reviewed by:		jhb
  MFC after:		2 weeks

Modified:
  head/sys/dev/acpica/acpi_ec.c

Modified: head/sys/dev/acpica/acpi_ec.c
==============================================================================
--- head/sys/dev/acpica/acpi_ec.c	Fri Dec  2 08:15:52 2016	(r309399)
+++ head/sys/dev/acpica/acpi_ec.c	Fri Dec  2 08:21:08 2016	(r309400)
@@ -613,16 +613,14 @@ EcCheckStatus(struct acpi_ec_softc *sc, 
 }
 
 static void
-EcGpeQueryHandler(void *Context)
+EcGpeQueryHandlerSub(struct acpi_ec_softc *sc)
 {
-    struct acpi_ec_softc	*sc = (struct acpi_ec_softc *)Context;
     UINT8			Data;
     ACPI_STATUS			Status;
     int				retry;
     char			qxx[5];
 
     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-    KASSERT(Context != NULL, ("EcGpeQueryHandler called with NULL"));
 
     /* Serialize user access with EcSpaceHandler(). */
     Status = EcLock(sc);
@@ -647,7 +645,6 @@ EcGpeQueryHandler(void *Context)
 	    EC_EVENT_INPUT_BUFFER_EMPTY)))
 	    break;
     }
-    sc->ec_sci_pend = FALSE;
     if (ACPI_FAILURE(Status)) {
 	EcUnlock(sc);
 	device_printf(sc->ec_dev, "GPE query failed: %s\n",
@@ -678,6 +675,29 @@ EcGpeQueryHandler(void *Context)
     }
 }
 
+static void
+EcGpeQueryHandler(void *Context)
+{
+    struct acpi_ec_softc *sc = (struct acpi_ec_softc *)Context;
+    int pending;
+
+    KASSERT(Context != NULL, ("EcGpeQueryHandler called with NULL"));
+
+    do {
+	/* Read the current pending count */
+	pending = atomic_load_acq_int(&sc->ec_sci_pend);
+
+	/* Call GPE handler function */
+	EcGpeQueryHandlerSub(sc);
+
+	/*
+	 * Try to reset the pending count to zero. If this fails we
+	 * know another GPE event has occurred while handling the
+	 * current GPE event and need to loop.
+	 */
+    } while (!atomic_cmpset_int(&sc->ec_sci_pend, pending, 0));
+}
+
 /*
  * The GPE handler is called when IBE/OBF or SCI events occur.  We are
  * called from an unknown lock context.
@@ -706,13 +726,14 @@ EcGpeHandler(ACPI_HANDLE GpeDevice, UINT
      * It will run the query and _Qxx method later, under the lock.
      */
     EcStatus = EC_GET_CSR(sc);
-    if ((EcStatus & EC_EVENT_SCI) && !sc->ec_sci_pend) {
+    if ((EcStatus & EC_EVENT_SCI) &&
+	atomic_fetchadd_int(&sc->ec_sci_pend, 1) == 0) {
 	CTR0(KTR_ACPI, "ec gpe queueing query handler");
 	Status = AcpiOsExecute(OSL_GPE_HANDLER, EcGpeQueryHandler, Context);
-	if (ACPI_SUCCESS(Status))
-	    sc->ec_sci_pend = TRUE;
-	else
+	if (ACPI_FAILURE(Status)) {
 	    printf("EcGpeHandler: queuing GPE query handler failed\n");
+	    atomic_store_rel_int(&sc->ec_sci_pend, 0);
+	}
     }
     return (ACPI_REENABLE_GPE);
 }
@@ -759,7 +780,8 @@ EcSpaceHandler(UINT32 Function, ACPI_PHY
      * we call it directly here since our thread taskq is not active yet.
      */
     if (cold || rebooting || sc->ec_suspending) {
-	if ((EC_GET_CSR(sc) & EC_EVENT_SCI)) {
+	if ((EC_GET_CSR(sc) & EC_EVENT_SCI) &&
+	    atomic_fetchadd_int(&sc->ec_sci_pend, 1) == 0) {
 	    CTR0(KTR_ACPI, "ec running gpe handler directly");
 	    EcGpeQueryHandler(sc);
 	}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201612020821.uB28L8s2000195>