This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 23a0239795 arch/arm64/src/imx9/imx9_usdhc.c: Simplify eventwait logic 
and remove race condition
23a0239795 is described below

commit 23a023979534e1cf8a0ee1e44e6c8533334a47b2
Author: Jukka Laitinen <[email protected]>
AuthorDate: Tue Dec 3 14:56:03 2024 +0200

    arch/arm64/src/imx9/imx9_usdhc.c: Simplify eventwait logic and remove race 
condition
    
    There is a race condition when timeout and completion interrupts occur at 
the same time.
    
    Fix this and simplify the eventwait code.
    
    Signed-off-by: Jukka Laitinen <[email protected]>
---
 arch/arm64/src/imx9/imx9_usdhc.c | 89 +++++++++++++++-------------------------
 1 file changed, 33 insertions(+), 56 deletions(-)

diff --git a/arch/arm64/src/imx9/imx9_usdhc.c b/arch/arm64/src/imx9/imx9_usdhc.c
index f9d82d1d97..39cc9833ce 100644
--- a/arch/arm64/src/imx9/imx9_usdhc.c
+++ b/arch/arm64/src/imx9/imx9_usdhc.c
@@ -1116,7 +1116,7 @@ static void imx9_recvdma(struct imx9_dev_s *priv)
  *   None
  *
  * Assumptions:
- *   Always called from the interrupt level with interrupts disabled.
+ *   None
  *
  ****************************************************************************/
 
@@ -1135,6 +1135,14 @@ static void imx9_eventtimeout(wdparm_t arg)
 
       imx9_sample(priv, SAMPLENDX_END_TRANSFER);
 
+      /* Disable wait interrupts */
+
+      imx9_configwaitints(priv, 0, 0, SDIOWAIT_TIMEOUT);
+
+      /* Clear pending interrupt status on all wait interrupts */
+
+      putreg32(USDHC_WAITALL_INTS, priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+
       /* Wake up any waiting threads */
 
       imx9_endwait(priv, SDIOWAIT_TIMEOUT);
@@ -1161,16 +1169,12 @@ static void imx9_eventtimeout(wdparm_t arg)
  ****************************************************************************/
 
 static void imx9_endwait(struct imx9_dev_s *priv,
-                          sdio_eventset_t wkupevent)
+                         sdio_eventset_t wkupevent)
 {
   /* Cancel the watchdog timeout */
 
   wd_cancel(&priv->waitwdog);
 
-  /* Disable event-related interrupts */
-
-  imx9_configwaitints(priv, 0, 0, wkupevent);
-
   /* Wake up the waiting thread */
 
   nxsem_post(&priv->waitsem);
@@ -2386,7 +2390,8 @@ static int imx9_cancel(struct sdio_dev_s *dev)
 
   /* Disable all transfer- and event- related interrupts */
 
-  imx9_configxfrints(priv, 0); imx9_configwaitints(priv, 0, 0, 0);
+  imx9_configxfrints(priv, 0);
+  imx9_configwaitints(priv, 0, 0, 0);
 
   /* Clearing pending interrupt status on all transfer- and event- related
    * interrupts
@@ -2815,9 +2820,6 @@ static void imx9_waitenable(struct sdio_dev_s *dev,
  *
  * Input Parameters:
  *   dev     - An instance of the SDIO device interface
- *   timeout - Maximum time in milliseconds to wait.  Zero means immediate
- *             timeout with no wait.  The timeout value is ignored if
- *             SDIOWAIT_TIMEOUT is not included in the waited-for eventset.
  *
  * Returned Value:
  *   Event set containing the event(s) that ended the wait.  Should always
@@ -2828,65 +2830,40 @@ static void imx9_waitenable(struct sdio_dev_s *dev,
 static sdio_eventset_t imx9_eventwait(struct sdio_dev_s *dev)
 {
   struct imx9_dev_s *priv = (struct imx9_dev_s *)dev;
-  sdio_eventset_t wkupevent = 0; int ret;
-
-  /* There is a race condition here... the event may have completed before
-   * we get here.  In this case waitevents will be zero, but wkupevents
-   * will be non-zero (and, hopefully, the semaphore count will also be
-   * non-zero.
-   */
-
-  DEBUGASSERT((priv->waitevents != 0 && priv->wkupevent == 0) ||
-              (priv->waitevents == 0 && priv->wkupevent != 0));
+  int ret;
 
-  /* Loop until the event (or the timeout occurs). Race conditions are
-   * avoided by calling imx9_waitenable prior to triggering the logic
-   * that will cause the wait to terminate.  Under certain race
-   * conditions, the waited-for may have already occurred before this
-   * function was called!
+  /* Wait for an event in event set to occur.  If this the event has
+   * already occurred, then the semaphore will already have been
+   * incremented and there will be no wait.
    */
 
-  for (; ; )
+  ret = nxsem_wait_uninterruptible(&priv->waitsem);
+  if (ret < 0)
     {
-      /* Wait for an event in event set to occur.  If this the event has
-       * already occurred, then the semaphore will already have been
-       * incremented and there will be no wait.
-       */
+      /* Task canceled, disable and clear interrupts and cancel wdog */
 
-      ret = nxsem_wait_uninterruptible(&priv->waitsem);
-      if (ret < 0)
-        {
-          /* Task canceled.  Cancel the wdog (assuming it was started) and
-           * return an SDIO error.
-           */
-
-          wd_cancel(&priv->waitwdog);
-          return SDIOWAIT_ERROR;
-        }
-
-      wkupevent = priv->wkupevent;
-
-      /* Check if the event has occurred.  When the event has occurred, then
-       * evenset will be set to 0 and wkupevent will be set to a non-zero
-       * value.
-       */
+      imx9_configwaitints(priv, 0, 0, SDIOWAIT_ERROR);
+      putreg32(USDHC_WAITALL_INTS, priv->addr + IMX9_USDHC_IRQSTAT_OFFSET);
+      wd_cancel(&priv->waitwdog);
+    }
 
-      if (wkupevent != 0)
-        {
-          /* Yes... break out of the loop with wkupevent non-zero */
+  /* In case of timeout or task cancellation, we need to reset the semaphore;
+   * it might have been double-posted if interrupt occured at the same time
+   */
 
-          break;
-        }
+  if (ret < 0 ||
+      priv->wkupevent == SDIOWAIT_TIMEOUT)
+    {
+      nxsem_reset(&priv->waitsem, 0);
     }
 
-  /* Disable event-related interrupts */
-
-  imx9_configwaitints(priv, 0, 0, 0);
 #ifdef CONFIG_IMX9_USDHC_DMA
   priv->xfrflags = 0;
 #endif
+
   imx9_dumpsamples(priv);
-  return wkupevent;
+
+  return priv->wkupevent;
 }
 
 /****************************************************************************

Reply via email to