dmaengine: at_xdmac: Fix race over irq_status
authorTudor Ambarus <tudor.ambarus@microchip.com>
Wed, 15 Dec 2021 11:01:15 +0000 (13:01 +0200)
committerVinod Koul <vkoul@kernel.org>
Wed, 5 Jan 2022 10:20:04 +0000 (15:50 +0530)
Tasklets run with interrupts enabled, so we need to protect
atchan->irq_status with spin_lock_irq() otherwise the tasklet can be
interrupted by the IRQ that modifies irq_status. Move the dev_dbg that
prints the irq_status in at_xdmac_handle_cyclic() and lower in
at_xdmac_tasklet() where the IRQ is disabled.

Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
Link: https://lore.kernel.org/r/20211215110115.191749-13-tudor.ambarus@microchip.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/at_xdmac.c

index ba727751a9f659dffdbf9524cfc0c211b151c8f0..a1da2b4b6d7323a3391d5e83edc4e1a6b571b8d0 100644 (file)
@@ -1611,6 +1611,8 @@ static void at_xdmac_handle_cyclic(struct at_xdmac_chan *atchan)
        struct dma_async_tx_descriptor  *txd;
 
        spin_lock_irq(&atchan->lock);
+       dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08x\n",
+               __func__, atchan->irq_status);
        if (list_empty(&atchan->xfers_list)) {
                spin_unlock_irq(&atchan->lock);
                return;
@@ -1623,6 +1625,7 @@ static void at_xdmac_handle_cyclic(struct at_xdmac_chan *atchan)
                dmaengine_desc_get_callback_invoke(txd, NULL);
 }
 
+/* Called with atchan->lock held. */
 static void at_xdmac_handle_error(struct at_xdmac_chan *atchan)
 {
        struct at_xdmac         *atxdmac = to_at_xdmac(atchan->chan.device);
@@ -1641,8 +1644,6 @@ static void at_xdmac_handle_error(struct at_xdmac_chan *atchan)
        if (atchan->irq_status & AT_XDMAC_CIS_ROIS)
                dev_err(chan2dev(&atchan->chan), "request overflow error!!!");
 
-       spin_lock_irq(&atchan->lock);
-
        /* Channel must be disabled first as it's not done automatically */
        at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask);
        while (at_xdmac_read(atxdmac, AT_XDMAC_GS) & atchan->mask)
@@ -1652,8 +1653,6 @@ static void at_xdmac_handle_error(struct at_xdmac_chan *atchan)
                                    struct at_xdmac_desc,
                                    xfer_node);
 
-       spin_unlock_irq(&atchan->lock);
-
        /* Print bad descriptor's details if needed */
        dev_dbg(chan2dev(&atchan->chan),
                "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n",
@@ -1670,15 +1669,17 @@ static void at_xdmac_tasklet(struct tasklet_struct *t)
        struct dma_async_tx_descriptor *txd;
        u32                     error_mask;
 
-       dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08x\n",
-               __func__, atchan->irq_status);
-
        if (at_xdmac_chan_is_cyclic(atchan))
                return at_xdmac_handle_cyclic(atchan);
 
        error_mask = AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS |
                AT_XDMAC_CIS_ROIS;
 
+       spin_lock_irq(&atchan->lock);
+
+       dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08x\n",
+               __func__, atchan->irq_status);
+
        if (!(atchan->irq_status & AT_XDMAC_CIS_LIS) &&
            !(atchan->irq_status & error_mask))
                return;
@@ -1686,7 +1687,6 @@ static void at_xdmac_tasklet(struct tasklet_struct *t)
        if (atchan->irq_status & error_mask)
                at_xdmac_handle_error(atchan);
 
-       spin_lock_irq(&atchan->lock);
        desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc,
                                xfer_node);
        dev_vdbg(chan2dev(&atchan->chan), "%s: desc 0x%p\n", __func__, desc);