dmaengine fixes for v7.3

Bunch of core and driver fixes:
  - Couple of fixes in core around dma_chan_put() for kref underflow,  user
    after free bug and waiting for rcu readers for dma devices
  - mmp sg length and wrong extended DRCMR base for SpacemiT K3
  - hardware buffer descriptor chain fix for xilinx dma
  - sun6i fixes for status behaviour and dma position registers
  - runtime pm reference leak fix for sprd driver
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmqwEtUACgkQfBQHDyUj
 g0dCWQ//ShhhOd2vQQttV+BSHB0xTdT/wENO4mhXcz50chHFh1diBFASMnhejOxG
 dv4ZkePAxV7PLpNqRQrCz2gx89wn5Uxf71PmnLbleuJ5lOuSUMGbFL0MhLzrlfqr
 2Di122aa5Va4Tp3zjNllQ4ihKMfCF02FLCXoZqelVIR/NQFFMeJhEjv/0P2foFvd
 nrf5jzGJuCCbhKiV47H4a8hSb1bG68ct/mV0ZfOT5Koe4B16qPTe7Z9kW5FP0do5
 ++BeHLbEheA/btWUSzvnLMtGHhhUywlAab6cKEkYAcTsuxtapMMRtJmAq9bYyX1O
 qS6hIC9qWMS4xc0+BUsIhwU8cXY6IINy8lPJmwc+/p4V+MJ8QMP+MrfWpWADFy0H
 rTvkIdzU+Lc4fqIr97UbW+pi8Naf2NCiV2NJSYF8JT6ufeG1PymtwbeUlbhkThtZ
 ISVF2/CIieyHr/eDfzNuGrYmdkSfJZgl6Rd6pCHdYQ5vgK6DImtaHfuBBm1OYXVG
 NwA4vkTk6hKu2Sx2JShrSJC2js5q5gdz+9jQuvb9zZZS4I1qOARWjoe/E0wowlir
 EB5bivcNX8VR49x2aYmT7Mg1CJovzs6hC1Aw9aqFIhp+1hQRku+iFOSki8Pwzl23
 EcpkC7liL8S6WCo73Zh7cmT0aBvw3Y6Gpvfozh+Rr7kLZy6cfmw=
 =9a97
 -----END PGP SIGNATURE-----

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

Pull dmaengine fixes from Vinod Koul:

 - A couple of fixes in core around dma_chan_put() for kref underflow,
   use-after-free and waiting for rcu readers for dma devices

 - mmp sg length and wrong extended DRCMR base for SpacemiT K3

 - hardware buffer descriptor chain fix for xilinx dma

 - sun6i fixes for status behaviour and dma position registers

 - runtime pm reference leak fix for sprd driver

* tag 'dmaengine-fix-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine:
  dmaengine: mmp_pdma: fix wrong sg length in mmp_pdma_prep_slave_sg()
  dmaengine: xilinx_dma: Fix hardware buffer descriptor chain after cyclic DMA
  dmaengine: pxa: fix double counting of the hw descriptors
  dmaengine: sun6i: fix undefined behaviour in sun6i_dma_tx_status
  dmaengine: sun6i: fix non-atomic read of DMA position registers
  dmaengine: xilinx_dma: Fix hardware buffer descriptor reuse order
  dmaengine: wait for RCU readers before releasing dma_device
  dmaengine: fix use-after-free in dma_chan_put() and dma_release_channel()
  dmaengine: Fix device kref underflow in dma_chan_put()
  dmaengine: add dma_device_get() helper
  dmaengine: sprd: Fix runtime PM reference leak in probe
  dmaengine: ti: k3-udma-glue: fix NULL dereference in k3_udma_glue_release_rx_chn()
  dmaengine: mmp_pdma: fix wrong extended DRCMR base for SpacemiT K3
This commit is contained in:
Linus Torvalds 2026-09-20 10:45:22 -07:00
commit a10a019dd4
7 changed files with 46 additions and 24 deletions

View File

@ -428,11 +428,18 @@ static void dma_device_release(struct kref *ref)
list_del_rcu(&device->global_node);
dma_channel_rebalance();
synchronize_rcu();
if (device->device_release)
device->device_release(device);
}
static int __must_check dma_device_get(struct dma_device *device)
{
lockdep_assert_held(&dma_list_mutex);
return kref_get_unless_zero(&device->ref);
}
static void dma_device_put(struct dma_device *device)
{
lockdep_assert_held(&dma_list_mutex);
@ -460,8 +467,7 @@ static int dma_chan_get(struct dma_chan *chan)
if (!try_module_get(owner))
return -ENODEV;
ret = kref_get_unless_zero(&chan->device->ref);
if (!ret) {
if (!dma_device_get(chan->device)) {
ret = -ENODEV;
goto module_put_out;
}
@ -495,10 +501,13 @@ static int dma_chan_get(struct dma_chan *chan)
*/
static void dma_chan_put(struct dma_chan *chan)
{
struct module *owner;
/* This channel is not in use, bail out */
if (!chan->client_count)
return;
owner = dma_chan_to_owner(chan);
chan->client_count--;
/* This channel is not in use anymore, free it */
@ -515,8 +524,10 @@ static void dma_chan_put(struct dma_chan *chan)
chan->route_data = NULL;
}
dma_device_put(chan->device);
module_put(dma_chan_to_owner(chan));
/* This channel is not in use anymore, drop the device ref */
if (!chan->client_count)
dma_device_put(chan->device);
module_put(owner);
}
enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie)

View File

@ -52,7 +52,6 @@
#define DCSR_EORINTR BIT(9) /* The end of Receive */
#define DRCMR_BASE 0x0100
#define DRCMR_EXT_BASE_K3 0x1000
#define DRCMR_EXT_BASE_DEFAULT 0x1100
#define DRCMR_REQ_LIMIT 64
#define DRCMR_MAPVLD BIT(7) /* Map Valid (read / write) */
@ -713,7 +712,7 @@ mmp_pdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
for_each_sg(sgl, sg, sg_len, i) {
addr = sg_dma_address(sg);
avail = sg_dma_len(sgl);
avail = sg_dma_len(sg);
do {
len = min_t(size_t, avail, PDMA_MAX_DESC_BYTES);
@ -1219,7 +1218,7 @@ static const struct mmp_pdma_ops spacemit_k3_pdma_ops = {
.get_desc_dst_addr = get_desc_dst_addr_64,
.run_bits = (DCSR_RUN | DCSR_LPAEEN | DCSR_EORIRQEN | DCSR_EORSTOPEN),
.dma_width = 64,
.drcmr_ext_base = DRCMR_EXT_BASE_K3,
.drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT,
};
static const struct of_device_id mmp_pdma_dt_ids[] = {

View File

@ -744,6 +744,7 @@ pxad_alloc_desc(struct pxad_chan *chan, unsigned int nb_hw_desc)
sw_desc = kzalloc_flex(*sw_desc, hw_desc, nb_hw_desc, GFP_NOWAIT);
if (!sw_desc)
return NULL;
sw_desc->nb_desc = nb_hw_desc;
sw_desc->desc_pool = chan->desc_pool;
for (i = 0; i < nb_hw_desc; i++) {
@ -752,10 +753,10 @@ pxad_alloc_desc(struct pxad_chan *chan, unsigned int nb_hw_desc)
dev_err(&chan->vc.chan.dev->device,
"%s(): Couldn't allocate the %dth hw_desc from dma_pool %p\n",
__func__, i, sw_desc->desc_pool);
sw_desc->nb_desc = i;
goto err;
}
sw_desc->nb_desc++;
sw_desc->hw_desc[i] = desc;
if (i == 0)

View File

@ -1212,7 +1212,7 @@ static int sprd_dma_probe(struct platform_device *pdev)
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0)
goto err_rpm;
goto err_register;
ret = dma_async_device_register(&sdev->dma_dev);
if (ret < 0) {
@ -1234,7 +1234,6 @@ static int sprd_dma_probe(struct platform_device *pdev)
err_register:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
err_rpm:
sprd_dma_disable(sdev);
return ret;
}

View File

@ -354,8 +354,10 @@ static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan)
size_t bytes;
dma_addr_t pos;
pos = readl(pchan->base + DMA_CHAN_LLI_ADDR);
bytes = readl(pchan->base + DMA_CHAN_CUR_CNT);
do {
pos = readl(pchan->base + DMA_CHAN_LLI_ADDR);
bytes = readl(pchan->base + DMA_CHAN_CUR_CNT);
} while (pos != readl(pchan->base + DMA_CHAN_LLI_ADDR));
if (pos == LLI_LAST_ITEM)
return bytes;
@ -979,7 +981,6 @@ static enum dma_status sun6i_dma_tx_status(struct dma_chan *chan,
struct sun6i_pchan *pchan = vchan->phy;
struct sun6i_dma_lli *lli;
struct virt_dma_desc *vd;
struct sun6i_desc *txd;
enum dma_status ret;
unsigned long flags;
size_t bytes = 0;
@ -991,9 +992,9 @@ static enum dma_status sun6i_dma_tx_status(struct dma_chan *chan,
spin_lock_irqsave(&vchan->vc.lock, flags);
vd = vchan_find_desc(&vchan->vc, cookie);
txd = to_sun6i_desc(&vd->tx);
if (vd) {
struct sun6i_desc *txd = to_sun6i_desc(&vd->tx);
for (lli = txd->v_lli; lli != NULL; lli = lli->v_lli_next)
bytes += lli->len;
} else if (!pchan || !pchan->desc) {

View File

@ -1243,8 +1243,9 @@ void k3_udma_glue_release_rx_chn(struct k3_udma_glue_rx_channel *rx_chn)
rx_chn->psil_paired = false;
}
for (i = 0; i < rx_chn->flow_num; i++)
k3_udma_glue_release_rx_flow(rx_chn, i);
if (rx_chn->flows)
for (i = 0; i < rx_chn->flow_num; i++)
k3_udma_glue_release_rx_flow(rx_chn, i);
if (xudma_rflow_is_gp(rx_chn->common.udmax, rx_chn->flow_id_base))
xudma_free_gp_rflow_range(rx_chn->common.udmax,

View File

@ -756,15 +756,25 @@ xilinx_aximcdma_alloc_tx_segment(struct xilinx_dma_chan *chan)
return segment;
}
static void xilinx_dma_clean_hw_desc(struct xilinx_axidma_desc_hw *hw)
static void xilinx_dma_clean_hw_desc(struct xilinx_dma_chan *chan,
struct xilinx_axidma_tx_segment *segment)
{
u32 next_desc = hw->next_desc;
u32 next_desc_msb = hw->next_desc_msb;
dma_addr_t next;
u32 i;
memset(hw, 0, sizeof(struct xilinx_axidma_desc_hw));
/*
* Restore the buffer descriptor's next descriptor pointer to the value
* set up in xilinx_dma_alloc_chan_resources(). Otherwise using the DMA
* in cyclic mode leaves the next descriptor pointer altered and
* prevents subsequent non-cyclic transfers.
*/
i = segment - chan->seg_v;
next = chan->seg_p +
sizeof(*chan->seg_v) * ((i + 1) % XILINX_DMA_NUM_DESCS);
hw->next_desc = next_desc;
hw->next_desc_msb = next_desc_msb;
memset(&segment->hw, 0, sizeof(segment->hw));
segment->hw.next_desc = lower_32_bits(next);
segment->hw.next_desc_msb = upper_32_bits(next);
}
static void xilinx_mcdma_clean_hw_desc(struct xilinx_aximcdma_desc_hw *hw)
@ -786,7 +796,7 @@ static void xilinx_mcdma_clean_hw_desc(struct xilinx_aximcdma_desc_hw *hw)
static void xilinx_dma_free_tx_segment(struct xilinx_dma_chan *chan,
struct xilinx_axidma_tx_segment *segment)
{
xilinx_dma_clean_hw_desc(&segment->hw);
xilinx_dma_clean_hw_desc(chan, segment);
list_add_tail(&segment->node, &chan->free_seg_list);
}
@ -920,9 +930,9 @@ static void xilinx_dma_free_descriptors(struct xilinx_dma_chan *chan)
spin_lock_irqsave(&chan->lock, flags);
xilinx_dma_free_desc_list(chan, &chan->pending_list);
xilinx_dma_free_desc_list(chan, &chan->done_list);
xilinx_dma_free_desc_list(chan, &chan->active_list);
xilinx_dma_free_desc_list(chan, &chan->pending_list);
spin_unlock_irqrestore(&chan->lock, flags);
}