Merge tag 'dmaengine-fix-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 14 Apr 2021 16:36:54 +0000 (09:36 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 14 Apr 2021 16:36:54 +0000 (09:36 -0700)
Pull dmaengine fixes from Vinod Koul:
 "A couple of dmaengine driver fixes for:

   - race and descriptor issue for xilinx driver

   - fix interrupt handling, wq state & cleanup, field sizes for
     completion, msix permissions for idxd driver

   - runtime pm fix for tegra driver

   - double free fix in dma_async_device_register"

* tag 'dmaengine-fix-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine:
  dmaengine: idxd: fix wq cleanup of WQCFG registers
  dmaengine: idxd: clear MSIX permission entry on shutdown
  dmaengine: plx_dma: add a missing put_device() on error path
  dmaengine: tegra20: Fix runtime PM imbalance on error
  dmaengine: Fix a double free in dma_async_device_register
  dmaengine: dw: Make it dependent to HAS_IOMEM
  dmaengine: idxd: fix wq size store permission state
  dmaengine: idxd: fix opcap sysfs attribute output
  dmaengine: idxd: fix delta_rec and crc size field for completion record
  dmaengine: idxd: Fix clobbering of SWERR overflow bit on writeback
  dmaengine: xilinx: dpdma: Fix race condition in done IRQ
  dmaengine: xilinx: dpdma: Fix descriptor issuing on video group

drivers/dma/dmaengine.c
drivers/dma/dw/Kconfig
drivers/dma/idxd/device.c
drivers/dma/idxd/idxd.h
drivers/dma/idxd/init.c
drivers/dma/idxd/irq.c
drivers/dma/idxd/sysfs.c
drivers/dma/plx_dma.c
drivers/dma/tegra20-apb-dma.c
drivers/dma/xilinx/xilinx_dpdma.c
include/uapi/linux/idxd.h

index fe6a460..af3ee28 100644 (file)
@@ -1086,6 +1086,7 @@ static int __dma_async_device_channel_register(struct dma_device *device,
        kfree(chan->dev);
  err_free_local:
        free_percpu(chan->local);
+       chan->local = NULL;
        return rc;
 }
 
index e516269..db25f9b 100644 (file)
@@ -10,6 +10,7 @@ config DW_DMAC_CORE
 
 config DW_DMAC
        tristate "Synopsys DesignWare AHB DMA platform driver"
+       depends on HAS_IOMEM
        select DW_DMAC_CORE
        help
          Support the Synopsys DesignWare AHB DMA controller. This
@@ -18,6 +19,7 @@ config DW_DMAC
 config DW_DMAC_PCI
        tristate "Synopsys DesignWare AHB DMA PCI driver"
        depends on PCI
+       depends on HAS_IOMEM
        select DW_DMAC_CORE
        help
          Support the Synopsys DesignWare AHB DMA controller on the
index 84a6ea6..31c8195 100644 (file)
@@ -282,6 +282,22 @@ void idxd_wq_drain(struct idxd_wq *wq)
        idxd_cmd_exec(idxd, IDXD_CMD_DRAIN_WQ, operand, NULL);
 }
 
+void idxd_wq_reset(struct idxd_wq *wq)
+{
+       struct idxd_device *idxd = wq->idxd;
+       struct device *dev = &idxd->pdev->dev;
+       u32 operand;
+
+       if (wq->state != IDXD_WQ_ENABLED) {
+               dev_dbg(dev, "WQ %d in wrong state: %d\n", wq->id, wq->state);
+               return;
+       }
+
+       operand = BIT(wq->id % 16) | ((wq->id / 16) << 16);
+       idxd_cmd_exec(idxd, IDXD_CMD_RESET_WQ, operand, NULL);
+       wq->state = IDXD_WQ_DISABLED;
+}
+
 int idxd_wq_map_portal(struct idxd_wq *wq)
 {
        struct idxd_device *idxd = wq->idxd;
@@ -363,8 +379,6 @@ int idxd_wq_disable_pasid(struct idxd_wq *wq)
 void idxd_wq_disable_cleanup(struct idxd_wq *wq)
 {
        struct idxd_device *idxd = wq->idxd;
-       struct device *dev = &idxd->pdev->dev;
-       int i, wq_offset;
 
        lockdep_assert_held(&idxd->dev_lock);
        memset(wq->wqcfg, 0, idxd->wqcfg_size);
@@ -376,14 +390,6 @@ void idxd_wq_disable_cleanup(struct idxd_wq *wq)
        wq->ats_dis = 0;
        clear_bit(WQ_FLAG_DEDICATED, &wq->flags);
        memset(wq->name, 0, WQ_NAME_SIZE);
-
-       for (i = 0; i < WQCFG_STRIDES(idxd); i++) {
-               wq_offset = WQCFG_OFFSET(idxd, wq->id, i);
-               iowrite32(0, idxd->reg_base + wq_offset);
-               dev_dbg(dev, "WQ[%d][%d][%#x]: %#x\n",
-                       wq->id, i, wq_offset,
-                       ioread32(idxd->reg_base + wq_offset));
-       }
 }
 
 /* Device control bits */
@@ -574,6 +580,36 @@ void idxd_device_drain_pasid(struct idxd_device *idxd, int pasid)
 }
 
 /* Device configuration bits */
+void idxd_msix_perm_setup(struct idxd_device *idxd)
+{
+       union msix_perm mperm;
+       int i, msixcnt;
+
+       msixcnt = pci_msix_vec_count(idxd->pdev);
+       if (msixcnt < 0)
+               return;
+
+       mperm.bits = 0;
+       mperm.pasid = idxd->pasid;
+       mperm.pasid_en = device_pasid_enabled(idxd);
+       for (i = 1; i < msixcnt; i++)
+               iowrite32(mperm.bits, idxd->reg_base + idxd->msix_perm_offset + i * 8);
+}
+
+void idxd_msix_perm_clear(struct idxd_device *idxd)
+{
+       union msix_perm mperm;
+       int i, msixcnt;
+
+       msixcnt = pci_msix_vec_count(idxd->pdev);
+       if (msixcnt < 0)
+               return;
+
+       mperm.bits = 0;
+       for (i = 1; i < msixcnt; i++)
+               iowrite32(mperm.bits, idxd->reg_base + idxd->msix_perm_offset + i * 8);
+}
+
 static void idxd_group_config_write(struct idxd_group *group)
 {
        struct idxd_device *idxd = group->idxd;
@@ -642,7 +678,14 @@ static int idxd_wq_config_write(struct idxd_wq *wq)
        if (!wq->group)
                return 0;
 
-       memset(wq->wqcfg, 0, idxd->wqcfg_size);
+       /*
+        * Instead of memset the entire shadow copy of WQCFG, copy from the hardware after
+        * wq reset. This will copy back the sticky values that are present on some devices.
+        */
+       for (i = 0; i < WQCFG_STRIDES(idxd); i++) {
+               wq_offset = WQCFG_OFFSET(idxd, wq->id, i);
+               wq->wqcfg->bits[i] = ioread32(idxd->reg_base + wq_offset);
+       }
 
        /* byte 0-3 */
        wq->wqcfg->wq_size = wq->size;
index 81a0e65..76014c1 100644 (file)
@@ -316,6 +316,8 @@ void idxd_unregister_driver(void);
 struct bus_type *idxd_get_bus_type(struct idxd_device *idxd);
 
 /* device interrupt control */
+void idxd_msix_perm_setup(struct idxd_device *idxd);
+void idxd_msix_perm_clear(struct idxd_device *idxd);
 irqreturn_t idxd_irq_handler(int vec, void *data);
 irqreturn_t idxd_misc_thread(int vec, void *data);
 irqreturn_t idxd_wq_thread(int irq, void *data);
@@ -341,6 +343,7 @@ void idxd_wq_free_resources(struct idxd_wq *wq);
 int idxd_wq_enable(struct idxd_wq *wq);
 int idxd_wq_disable(struct idxd_wq *wq);
 void idxd_wq_drain(struct idxd_wq *wq);
+void idxd_wq_reset(struct idxd_wq *wq);
 int idxd_wq_map_portal(struct idxd_wq *wq);
 void idxd_wq_unmap_portal(struct idxd_wq *wq);
 void idxd_wq_disable_cleanup(struct idxd_wq *wq);
index 085a0c3..6584b0e 100644 (file)
@@ -65,7 +65,6 @@ static int idxd_setup_interrupts(struct idxd_device *idxd)
        struct idxd_irq_entry *irq_entry;
        int i, msixcnt;
        int rc = 0;
-       union msix_perm mperm;
 
        msixcnt = pci_msix_vec_count(pdev);
        if (msixcnt < 0) {
@@ -144,14 +143,7 @@ static int idxd_setup_interrupts(struct idxd_device *idxd)
        }
 
        idxd_unmask_error_interrupts(idxd);
-
-       /* Setup MSIX permission table */
-       mperm.bits = 0;
-       mperm.pasid = idxd->pasid;
-       mperm.pasid_en = device_pasid_enabled(idxd);
-       for (i = 1; i < msixcnt; i++)
-               iowrite32(mperm.bits, idxd->reg_base + idxd->msix_perm_offset + i * 8);
-
+       idxd_msix_perm_setup(idxd);
        return 0;
 
  err_no_irq:
@@ -510,6 +502,7 @@ static void idxd_shutdown(struct pci_dev *pdev)
                idxd_flush_work_list(irq_entry);
        }
 
+       idxd_msix_perm_clear(idxd);
        destroy_workqueue(idxd->wq);
 }
 
index a60ca11..f1463fc 100644 (file)
@@ -124,7 +124,9 @@ static int process_misc_interrupts(struct idxd_device *idxd, u32 cause)
                for (i = 0; i < 4; i++)
                        idxd->sw_err.bits[i] = ioread64(idxd->reg_base +
                                        IDXD_SWERR_OFFSET + i * sizeof(u64));
-               iowrite64(IDXD_SWERR_ACK, idxd->reg_base + IDXD_SWERR_OFFSET);
+
+               iowrite64(idxd->sw_err.bits[0] & IDXD_SWERR_ACK,
+                         idxd->reg_base + IDXD_SWERR_OFFSET);
 
                if (idxd->sw_err.valid && idxd->sw_err.wq_idx_valid) {
                        int id = idxd->sw_err.wq_idx;
index 4dbb03c..18bf4d1 100644 (file)
@@ -275,7 +275,6 @@ static void disable_wq(struct idxd_wq *wq)
 {
        struct idxd_device *idxd = wq->idxd;
        struct device *dev = &idxd->pdev->dev;
-       int rc;
 
        mutex_lock(&wq->wq_lock);
        dev_dbg(dev, "%s removing WQ %s\n", __func__, dev_name(&wq->conf_dev));
@@ -296,17 +295,13 @@ static void disable_wq(struct idxd_wq *wq)
        idxd_wq_unmap_portal(wq);
 
        idxd_wq_drain(wq);
-       rc = idxd_wq_disable(wq);
+       idxd_wq_reset(wq);
 
        idxd_wq_free_resources(wq);
        wq->client_count = 0;
        mutex_unlock(&wq->wq_lock);
 
-       if (rc < 0)
-               dev_warn(dev, "Failed to disable %s: %d\n",
-                        dev_name(&wq->conf_dev), rc);
-       else
-               dev_info(dev, "wq %s disabled\n", dev_name(&wq->conf_dev));
+       dev_info(dev, "wq %s disabled\n", dev_name(&wq->conf_dev));
 }
 
 static int idxd_config_bus_remove(struct device *dev)
@@ -989,7 +984,7 @@ static ssize_t wq_size_store(struct device *dev,
        if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
                return -EPERM;
 
-       if (wq->state != IDXD_WQ_DISABLED)
+       if (idxd->state == IDXD_DEV_ENABLED)
                return -EPERM;
 
        if (size + total_claimed_wq_size(idxd) - wq->size > idxd->max_wq_size)
@@ -1449,8 +1444,14 @@ static ssize_t op_cap_show(struct device *dev,
 {
        struct idxd_device *idxd =
                container_of(dev, struct idxd_device, conf_dev);
+       int i, rc = 0;
+
+       for (i = 0; i < 4; i++)
+               rc += sysfs_emit_at(buf, rc, "%#llx ", idxd->hw.opcap.bits[i]);
 
-       return sprintf(buf, "%#llx\n", idxd->hw.opcap.bits[0]);
+       rc--;
+       rc += sysfs_emit_at(buf, rc, "\n");
+       return rc;
 }
 static DEVICE_ATTR_RO(op_cap);
 
index f387c5b..1669345 100644 (file)
@@ -507,10 +507,8 @@ static int plx_dma_create(struct pci_dev *pdev)
 
        rc = request_irq(pci_irq_vector(pdev, 0), plx_dma_isr, 0,
                         KBUILD_MODNAME, plxdev);
-       if (rc) {
-               kfree(plxdev);
-               return rc;
-       }
+       if (rc)
+               goto free_plx;
 
        spin_lock_init(&plxdev->ring_lock);
        tasklet_setup(&plxdev->desc_task, plx_dma_desc_task);
@@ -540,14 +538,20 @@ static int plx_dma_create(struct pci_dev *pdev)
        rc = dma_async_device_register(dma);
        if (rc) {
                pci_err(pdev, "Failed to register dma device: %d\n", rc);
-               free_irq(pci_irq_vector(pdev, 0),  plxdev);
-               kfree(plxdev);
-               return rc;
+               goto put_device;
        }
 
        pci_set_drvdata(pdev, plxdev);
 
        return 0;
+
+put_device:
+       put_device(&pdev->dev);
+       free_irq(pci_irq_vector(pdev, 0),  plxdev);
+free_plx:
+       kfree(plxdev);
+
+       return rc;
 }
 
 static int plx_dma_probe(struct pci_dev *pdev,
index 71827d9..b726074 100644 (file)
@@ -723,7 +723,7 @@ static void tegra_dma_issue_pending(struct dma_chan *dc)
                goto end;
        }
        if (!tdc->busy) {
-               err = pm_runtime_get_sync(tdc->tdma->dev);
+               err = pm_runtime_resume_and_get(tdc->tdma->dev);
                if (err < 0) {
                        dev_err(tdc2dev(tdc), "Failed to enable DMA\n");
                        goto end;
@@ -818,7 +818,7 @@ static void tegra_dma_synchronize(struct dma_chan *dc)
        struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
        int err;
 
-       err = pm_runtime_get_sync(tdc->tdma->dev);
+       err = pm_runtime_resume_and_get(tdc->tdma->dev);
        if (err < 0) {
                dev_err(tdc2dev(tdc), "Failed to synchronize DMA: %d\n", err);
                return;
index 55df63d..70b29bd 100644 (file)
@@ -839,6 +839,7 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan)
        struct xilinx_dpdma_tx_desc *desc;
        struct virt_dma_desc *vdesc;
        u32 reg, channels;
+       bool first_frame;
 
        lockdep_assert_held(&chan->lock);
 
@@ -852,14 +853,6 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan)
                chan->running = true;
        }
 
-       if (chan->video_group)
-               channels = xilinx_dpdma_chan_video_group_ready(chan);
-       else
-               channels = BIT(chan->id);
-
-       if (!channels)
-               return;
-
        vdesc = vchan_next_desc(&chan->vchan);
        if (!vdesc)
                return;
@@ -884,13 +877,26 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan)
                            FIELD_PREP(XILINX_DPDMA_CH_DESC_START_ADDRE_MASK,
                                       upper_32_bits(sw_desc->dma_addr)));
 
-       if (chan->first_frame)
+       first_frame = chan->first_frame;
+       chan->first_frame = false;
+
+       if (chan->video_group) {
+               channels = xilinx_dpdma_chan_video_group_ready(chan);
+               /*
+                * Trigger the transfer only when all channels in the group are
+                * ready.
+                */
+               if (!channels)
+                       return;
+       } else {
+               channels = BIT(chan->id);
+       }
+
+       if (first_frame)
                reg = XILINX_DPDMA_GBL_TRIG_MASK(channels);
        else
                reg = XILINX_DPDMA_GBL_RETRIG_MASK(channels);
 
-       chan->first_frame = false;
-
        dpdma_write(xdev->reg, XILINX_DPDMA_GBL, reg);
 }
 
@@ -1042,13 +1048,14 @@ static int xilinx_dpdma_chan_stop(struct xilinx_dpdma_chan *chan)
  */
 static void xilinx_dpdma_chan_done_irq(struct xilinx_dpdma_chan *chan)
 {
-       struct xilinx_dpdma_tx_desc *active = chan->desc.active;
+       struct xilinx_dpdma_tx_desc *active;
        unsigned long flags;
 
        spin_lock_irqsave(&chan->lock, flags);
 
        xilinx_dpdma_debugfs_desc_done_irq(chan);
 
+       active = chan->desc.active;
        if (active)
                vchan_cyclic_callback(&active->vdesc);
        else
index 236d437..e33997b 100644 (file)
@@ -247,8 +247,8 @@ struct dsa_completion_record {
                        uint32_t        rsvd2:8;
                };
 
-               uint16_t        delta_rec_size;
-               uint16_t        crc_val;
+               uint32_t        delta_rec_size;
+               uint32_t        crc_val;
 
                /* DIF check & strip */
                struct {