dmaengine: ste_dma40: Reject direction changes after allocation

Logical channel allocation uses the configured direction to select the
source or destination allocation mask, derive the logical channel number,
and choose the LCPA location.

d40_set_runtime_config_write() nevertheless overwrites the configured
direction when a transfer is prepared for the opposite direction.
d40_free_dma() then clears the wrong allocation mask, leaking the original
resource and potentially releasing one used by another client.

Reject directions that differ from the direction used during allocation
and propagate runtime configuration errors to callers. Skip slave runtime
configuration for memcpy transfers, which also use d40_prep_sg() but do
not require it.

Fixes: 95e1400fa1 ("DMAENGINE: add runtime slave config to DMA40 v3")
Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/dmaengine/20260618051539.15E201F000E9@smtp.kernel.org/
Assisted-by: LLM
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
Link: https://patch.msgid.link/20260927-dma40-fixes-v7-18-89f595e8851d@kernel.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Linus Walleij 2026-09-27 11:03:31 +02:00 committed by Vinod Koul
parent 9f1064ebe1
commit 77ebc64f40

View File

@ -2405,7 +2405,13 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src,
return NULL;
}
d40_set_runtime_config_write(dchan, &chan->slave_config, direction);
if (direction != DMA_MEM_TO_MEM) {
ret = d40_set_runtime_config_write(dchan,
&chan->slave_config,
direction);
if (ret)
return NULL;
}
spin_lock_irqsave(&chan->lock, flags);
@ -2881,6 +2887,13 @@ static int d40_set_runtime_config_write(struct dma_chan *chan,
return -EINVAL;
}
if (direction != cfg->dir) {
chan_err(d40c,
"transfer direction %d differs from allocated direction %d\n",
direction, cfg->dir);
return -EINVAL;
}
src_addr_width = config->src_addr_width;
src_maxburst = config->src_maxburst;
dst_addr_width = config->dst_addr_width;
@ -2889,13 +2902,6 @@ static int d40_set_runtime_config_write(struct dma_chan *chan,
if (direction == DMA_DEV_TO_MEM) {
config_addr = config->src_addr;
if (cfg->dir != DMA_DEV_TO_MEM)
dev_dbg(d40c->base->dev,
"channel was not configured for peripheral "
"to memory transfer (%d) overriding\n",
cfg->dir);
cfg->dir = DMA_DEV_TO_MEM;
/* Configure the memory side */
if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
dst_addr_width = src_addr_width;
@ -2905,13 +2911,6 @@ static int d40_set_runtime_config_write(struct dma_chan *chan,
} else if (direction == DMA_MEM_TO_DEV) {
config_addr = config->dst_addr;
if (cfg->dir != DMA_MEM_TO_DEV)
dev_dbg(d40c->base->dev,
"channel was not configured for memory "
"to peripheral transfer (%d) overriding\n",
cfg->dir);
cfg->dir = DMA_MEM_TO_DEV;
/* Configure the memory side */
if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
src_addr_width = dst_addr_width;