sashiko complained about possible teardown problem. The scenario

 CPU 0                              CPU 1
  imx_mu_isr()                   imx_mu_shutdown()
                                   imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, 
IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
    imx_mu_specific_rx()
      imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0);
                                   free_irq()

The RX event remains enabled because in this short window the RX event
was disabled in ->shutdown() while the interrupt was active and then
enabled again by the ISR while ->shutdown waited in free_irq().

This race requires timing and if happens can be problematic on shared
handlers if the "removed" channel triggers an interrupt. In this case
the irq-core will shutdown the interrupt with the "nobody cared"
message.

Introduce imx_mu_con_priv::shutdown to signal that the channel is
shutting down. This flag is set with the lock held (by
imx_mu_xcr_clr_shut()). The unmask side uses imx_mu_xcr_set_act() which
only enables the event if the channel has not been shutdown and
serialises on the same lock.

Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
---
 drivers/mailbox/imx-mailbox.c | 40 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 36 insertions(+), 4 deletions(-)

diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c
index 246a9a9e39520..34edc2f31dcb5 100644
--- a/drivers/mailbox/imx-mailbox.c
+++ b/drivers/mailbox/imx-mailbox.c
@@ -80,6 +80,7 @@ struct imx_mu_con_priv {
        enum imx_mu_chan_type   type;
        struct mbox_chan        *chan;
        struct work_struct      txdb_work;
+       bool                    shutdown;
 };
 
 struct imx_mu_priv {
@@ -219,6 +220,36 @@ static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum 
imx_mu_xcr type, u32 se
        return val;
 }
 
+static void imx_mu_xcr_clr_shut(struct imx_mu_priv *priv, struct 
imx_mu_con_priv *cp,
+                               enum imx_mu_xcr type, u32 clr)
+{
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&priv->xcr_lock, flags);
+       cp->shutdown = true;
+
+       val = imx_mu_read(priv, priv->dcfg->xCR[type]);
+       val &= ~clr;
+       imx_mu_write(priv, val, priv->dcfg->xCR[type]);
+       spin_unlock_irqrestore(&priv->xcr_lock, flags);
+}
+
+static void imx_mu_xcr_set_act(struct imx_mu_priv *priv, struct 
imx_mu_con_priv *cp,
+                              enum imx_mu_xcr type, u32 set)
+{
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&priv->xcr_lock, flags);
+       if (!cp->shutdown) {
+               val = imx_mu_read(priv, priv->dcfg->xCR[type]);
+               val |= set;
+               imx_mu_write(priv, val, priv->dcfg->xCR[type]);
+       }
+       spin_unlock_irqrestore(&priv->xcr_lock, flags);
+}
+
 static int imx_mu_generic_tx(struct imx_mu_priv *priv,
                             struct imx_mu_con_priv *cp,
                             void *data)
@@ -376,7 +407,7 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, 
struct imx_mu_con_priv *
                *data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % num_rr) * 4);
        }
 
-       imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 
0);
+       imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, 
IMX_MU_xCR_RIEn(priv->dcfg->type, 0));
        mbox_chan_received_data(cp->chan, (void *)priv->msg);
 
        return 0;
@@ -604,6 +635,7 @@ static int imx_mu_startup(struct mbox_chan *chan)
                return ret;
        }
 
+       cp->shutdown = false;
        switch (cp->type) {
        case IMX_MU_TYPE_RX:
                imx_mu_xcr_rmw(priv, IMX_MU_RCR, 
IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx), 0);
@@ -638,13 +670,13 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
 
        switch (cp->type) {
        case IMX_MU_TYPE_TX:
-               imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, 
IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
+               imx_mu_xcr_clr_shut(priv, cp, IMX_MU_TCR, 
IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
                break;
        case IMX_MU_TYPE_RX:
-               imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, 
IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
+               imx_mu_xcr_clr_shut(priv, cp, IMX_MU_RCR, 
IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
                break;
        case IMX_MU_TYPE_RXDB:
-               imx_mu_xcr_rmw(priv, IMX_MU_GIER, 0, 
IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx));
+               imx_mu_xcr_clr_shut(priv, cp, IMX_MU_GIER, 
IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx));
                break;
        case IMX_MU_TYPE_RST:
                imx_mu_xcr_rmw(priv, IMX_MU_CR, 
IMX_MU_xCR_RST(priv->dcfg->type), 0);

-- 
2.53.0


Reply via email to