Commit c17a3066 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'dmaengine-fix-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine

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
parents e70b911a ea9aadc0
...@@ -1086,6 +1086,7 @@ static int __dma_async_device_channel_register(struct dma_device *device, ...@@ -1086,6 +1086,7 @@ static int __dma_async_device_channel_register(struct dma_device *device,
kfree(chan->dev); kfree(chan->dev);
err_free_local: err_free_local:
free_percpu(chan->local); free_percpu(chan->local);
chan->local = NULL;
return rc; return rc;
} }
......
...@@ -10,6 +10,7 @@ config DW_DMAC_CORE ...@@ -10,6 +10,7 @@ config DW_DMAC_CORE
config DW_DMAC config DW_DMAC
tristate "Synopsys DesignWare AHB DMA platform driver" tristate "Synopsys DesignWare AHB DMA platform driver"
depends on HAS_IOMEM
select DW_DMAC_CORE select DW_DMAC_CORE
help help
Support the Synopsys DesignWare AHB DMA controller. This Support the Synopsys DesignWare AHB DMA controller. This
...@@ -18,6 +19,7 @@ config DW_DMAC ...@@ -18,6 +19,7 @@ config DW_DMAC
config DW_DMAC_PCI config DW_DMAC_PCI
tristate "Synopsys DesignWare AHB DMA PCI driver" tristate "Synopsys DesignWare AHB DMA PCI driver"
depends on PCI depends on PCI
depends on HAS_IOMEM
select DW_DMAC_CORE select DW_DMAC_CORE
help help
Support the Synopsys DesignWare AHB DMA controller on the Support the Synopsys DesignWare AHB DMA controller on the
......
...@@ -282,6 +282,22 @@ void idxd_wq_drain(struct idxd_wq *wq) ...@@ -282,6 +282,22 @@ void idxd_wq_drain(struct idxd_wq *wq)
idxd_cmd_exec(idxd, IDXD_CMD_DRAIN_WQ, operand, NULL); 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) int idxd_wq_map_portal(struct idxd_wq *wq)
{ {
struct idxd_device *idxd = wq->idxd; struct idxd_device *idxd = wq->idxd;
...@@ -363,8 +379,6 @@ int idxd_wq_disable_pasid(struct idxd_wq *wq) ...@@ -363,8 +379,6 @@ int idxd_wq_disable_pasid(struct idxd_wq *wq)
void idxd_wq_disable_cleanup(struct idxd_wq *wq) void idxd_wq_disable_cleanup(struct idxd_wq *wq)
{ {
struct idxd_device *idxd = wq->idxd; struct idxd_device *idxd = wq->idxd;
struct device *dev = &idxd->pdev->dev;
int i, wq_offset;
lockdep_assert_held(&idxd->dev_lock); lockdep_assert_held(&idxd->dev_lock);
memset(wq->wqcfg, 0, idxd->wqcfg_size); memset(wq->wqcfg, 0, idxd->wqcfg_size);
...@@ -376,14 +390,6 @@ void idxd_wq_disable_cleanup(struct idxd_wq *wq) ...@@ -376,14 +390,6 @@ void idxd_wq_disable_cleanup(struct idxd_wq *wq)
wq->ats_dis = 0; wq->ats_dis = 0;
clear_bit(WQ_FLAG_DEDICATED, &wq->flags); clear_bit(WQ_FLAG_DEDICATED, &wq->flags);
memset(wq->name, 0, WQ_NAME_SIZE); 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 */ /* Device control bits */
...@@ -574,6 +580,36 @@ void idxd_device_drain_pasid(struct idxd_device *idxd, int pasid) ...@@ -574,6 +580,36 @@ void idxd_device_drain_pasid(struct idxd_device *idxd, int pasid)
} }
/* Device configuration bits */ /* 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) static void idxd_group_config_write(struct idxd_group *group)
{ {
struct idxd_device *idxd = group->idxd; struct idxd_device *idxd = group->idxd;
...@@ -642,7 +678,14 @@ static int idxd_wq_config_write(struct idxd_wq *wq) ...@@ -642,7 +678,14 @@ static int idxd_wq_config_write(struct idxd_wq *wq)
if (!wq->group) if (!wq->group)
return 0; 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 */ /* byte 0-3 */
wq->wqcfg->wq_size = wq->size; wq->wqcfg->wq_size = wq->size;
......
...@@ -316,6 +316,8 @@ void idxd_unregister_driver(void); ...@@ -316,6 +316,8 @@ void idxd_unregister_driver(void);
struct bus_type *idxd_get_bus_type(struct idxd_device *idxd); struct bus_type *idxd_get_bus_type(struct idxd_device *idxd);
/* device interrupt control */ /* 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_irq_handler(int vec, void *data);
irqreturn_t idxd_misc_thread(int vec, void *data); irqreturn_t idxd_misc_thread(int vec, void *data);
irqreturn_t idxd_wq_thread(int irq, void *data); irqreturn_t idxd_wq_thread(int irq, void *data);
...@@ -341,6 +343,7 @@ void idxd_wq_free_resources(struct idxd_wq *wq); ...@@ -341,6 +343,7 @@ void idxd_wq_free_resources(struct idxd_wq *wq);
int idxd_wq_enable(struct idxd_wq *wq); int idxd_wq_enable(struct idxd_wq *wq);
int idxd_wq_disable(struct idxd_wq *wq); int idxd_wq_disable(struct idxd_wq *wq);
void idxd_wq_drain(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); int idxd_wq_map_portal(struct idxd_wq *wq);
void idxd_wq_unmap_portal(struct idxd_wq *wq); void idxd_wq_unmap_portal(struct idxd_wq *wq);
void idxd_wq_disable_cleanup(struct idxd_wq *wq); void idxd_wq_disable_cleanup(struct idxd_wq *wq);
......
...@@ -65,7 +65,6 @@ static int idxd_setup_interrupts(struct idxd_device *idxd) ...@@ -65,7 +65,6 @@ static int idxd_setup_interrupts(struct idxd_device *idxd)
struct idxd_irq_entry *irq_entry; struct idxd_irq_entry *irq_entry;
int i, msixcnt; int i, msixcnt;
int rc = 0; int rc = 0;
union msix_perm mperm;
msixcnt = pci_msix_vec_count(pdev); msixcnt = pci_msix_vec_count(pdev);
if (msixcnt < 0) { if (msixcnt < 0) {
...@@ -144,14 +143,7 @@ static int idxd_setup_interrupts(struct idxd_device *idxd) ...@@ -144,14 +143,7 @@ static int idxd_setup_interrupts(struct idxd_device *idxd)
} }
idxd_unmask_error_interrupts(idxd); idxd_unmask_error_interrupts(idxd);
idxd_msix_perm_setup(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);
return 0; return 0;
err_no_irq: err_no_irq:
...@@ -510,6 +502,7 @@ static void idxd_shutdown(struct pci_dev *pdev) ...@@ -510,6 +502,7 @@ static void idxd_shutdown(struct pci_dev *pdev)
idxd_flush_work_list(irq_entry); idxd_flush_work_list(irq_entry);
} }
idxd_msix_perm_clear(idxd);
destroy_workqueue(idxd->wq); destroy_workqueue(idxd->wq);
} }
......
...@@ -124,7 +124,9 @@ static int process_misc_interrupts(struct idxd_device *idxd, u32 cause) ...@@ -124,7 +124,9 @@ static int process_misc_interrupts(struct idxd_device *idxd, u32 cause)
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
idxd->sw_err.bits[i] = ioread64(idxd->reg_base + idxd->sw_err.bits[i] = ioread64(idxd->reg_base +
IDXD_SWERR_OFFSET + i * sizeof(u64)); 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) { if (idxd->sw_err.valid && idxd->sw_err.wq_idx_valid) {
int id = idxd->sw_err.wq_idx; int id = idxd->sw_err.wq_idx;
......
...@@ -275,7 +275,6 @@ static void disable_wq(struct idxd_wq *wq) ...@@ -275,7 +275,6 @@ static void disable_wq(struct idxd_wq *wq)
{ {
struct idxd_device *idxd = wq->idxd; struct idxd_device *idxd = wq->idxd;
struct device *dev = &idxd->pdev->dev; struct device *dev = &idxd->pdev->dev;
int rc;
mutex_lock(&wq->wq_lock); mutex_lock(&wq->wq_lock);
dev_dbg(dev, "%s removing WQ %s\n", __func__, dev_name(&wq->conf_dev)); dev_dbg(dev, "%s removing WQ %s\n", __func__, dev_name(&wq->conf_dev));
...@@ -296,16 +295,12 @@ static void disable_wq(struct idxd_wq *wq) ...@@ -296,16 +295,12 @@ static void disable_wq(struct idxd_wq *wq)
idxd_wq_unmap_portal(wq); idxd_wq_unmap_portal(wq);
idxd_wq_drain(wq); idxd_wq_drain(wq);
rc = idxd_wq_disable(wq); idxd_wq_reset(wq);
idxd_wq_free_resources(wq); idxd_wq_free_resources(wq);
wq->client_count = 0; wq->client_count = 0;
mutex_unlock(&wq->wq_lock); 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));
} }
...@@ -989,7 +984,7 @@ static ssize_t wq_size_store(struct device *dev, ...@@ -989,7 +984,7 @@ static ssize_t wq_size_store(struct device *dev,
if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags)) if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
return -EPERM; return -EPERM;
if (wq->state != IDXD_WQ_DISABLED) if (idxd->state == IDXD_DEV_ENABLED)
return -EPERM; return -EPERM;
if (size + total_claimed_wq_size(idxd) - wq->size > idxd->max_wq_size) 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, ...@@ -1449,8 +1444,14 @@ static ssize_t op_cap_show(struct device *dev,
{ {
struct idxd_device *idxd = struct idxd_device *idxd =
container_of(dev, struct idxd_device, conf_dev); 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); static DEVICE_ATTR_RO(op_cap);
......
...@@ -507,10 +507,8 @@ static int plx_dma_create(struct pci_dev *pdev) ...@@ -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, rc = request_irq(pci_irq_vector(pdev, 0), plx_dma_isr, 0,
KBUILD_MODNAME, plxdev); KBUILD_MODNAME, plxdev);
if (rc) { if (rc)
kfree(plxdev); goto free_plx;
return rc;
}
spin_lock_init(&plxdev->ring_lock); spin_lock_init(&plxdev->ring_lock);
tasklet_setup(&plxdev->desc_task, plx_dma_desc_task); tasklet_setup(&plxdev->desc_task, plx_dma_desc_task);
...@@ -540,14 +538,20 @@ static int plx_dma_create(struct pci_dev *pdev) ...@@ -540,14 +538,20 @@ static int plx_dma_create(struct pci_dev *pdev)
rc = dma_async_device_register(dma); rc = dma_async_device_register(dma);
if (rc) { if (rc) {
pci_err(pdev, "Failed to register dma device: %d\n", rc); pci_err(pdev, "Failed to register dma device: %d\n", rc);
free_irq(pci_irq_vector(pdev, 0), plxdev); goto put_device;
kfree(plxdev);
return rc;
} }
pci_set_drvdata(pdev, plxdev); pci_set_drvdata(pdev, plxdev);
return 0; 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, static int plx_dma_probe(struct pci_dev *pdev,
......
...@@ -723,7 +723,7 @@ static void tegra_dma_issue_pending(struct dma_chan *dc) ...@@ -723,7 +723,7 @@ static void tegra_dma_issue_pending(struct dma_chan *dc)
goto end; goto end;
} }
if (!tdc->busy) { if (!tdc->busy) {
err = pm_runtime_get_sync(tdc->tdma->dev); err = pm_runtime_resume_and_get(tdc->tdma->dev);
if (err < 0) { if (err < 0) {
dev_err(tdc2dev(tdc), "Failed to enable DMA\n"); dev_err(tdc2dev(tdc), "Failed to enable DMA\n");
goto end; goto end;
...@@ -818,7 +818,7 @@ static void tegra_dma_synchronize(struct dma_chan *dc) ...@@ -818,7 +818,7 @@ static void tegra_dma_synchronize(struct dma_chan *dc)
struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
int err; int err;
err = pm_runtime_get_sync(tdc->tdma->dev); err = pm_runtime_resume_and_get(tdc->tdma->dev);
if (err < 0) { if (err < 0) {
dev_err(tdc2dev(tdc), "Failed to synchronize DMA: %d\n", err); dev_err(tdc2dev(tdc), "Failed to synchronize DMA: %d\n", err);
return; return;
......
...@@ -839,6 +839,7 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan) ...@@ -839,6 +839,7 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan)
struct xilinx_dpdma_tx_desc *desc; struct xilinx_dpdma_tx_desc *desc;
struct virt_dma_desc *vdesc; struct virt_dma_desc *vdesc;
u32 reg, channels; u32 reg, channels;
bool first_frame;
lockdep_assert_held(&chan->lock); lockdep_assert_held(&chan->lock);
...@@ -852,14 +853,6 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan) ...@@ -852,14 +853,6 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan)
chan->running = true; 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); vdesc = vchan_next_desc(&chan->vchan);
if (!vdesc) if (!vdesc)
return; return;
...@@ -884,13 +877,26 @@ static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan) ...@@ -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, FIELD_PREP(XILINX_DPDMA_CH_DESC_START_ADDRE_MASK,
upper_32_bits(sw_desc->dma_addr))); 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); reg = XILINX_DPDMA_GBL_TRIG_MASK(channels);
else else
reg = XILINX_DPDMA_GBL_RETRIG_MASK(channels); reg = XILINX_DPDMA_GBL_RETRIG_MASK(channels);
chan->first_frame = false;
dpdma_write(xdev->reg, XILINX_DPDMA_GBL, reg); dpdma_write(xdev->reg, XILINX_DPDMA_GBL, reg);
} }
...@@ -1042,13 +1048,14 @@ static int xilinx_dpdma_chan_stop(struct xilinx_dpdma_chan *chan) ...@@ -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) 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; unsigned long flags;
spin_lock_irqsave(&chan->lock, flags); spin_lock_irqsave(&chan->lock, flags);
xilinx_dpdma_debugfs_desc_done_irq(chan); xilinx_dpdma_debugfs_desc_done_irq(chan);
active = chan->desc.active;
if (active) if (active)
vchan_cyclic_callback(&active->vdesc); vchan_cyclic_callback(&active->vdesc);
else else
......
...@@ -247,8 +247,8 @@ struct dsa_completion_record { ...@@ -247,8 +247,8 @@ struct dsa_completion_record {
uint32_t rsvd2:8; uint32_t rsvd2:8;
}; };
uint16_t delta_rec_size; uint32_t delta_rec_size;
uint16_t crc_val; uint32_t crc_val;
/* DIF check & strip */ /* DIF check & strip */
struct { struct {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment