mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
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:
commit
a10a019dd4
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user