mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
dmaengine: ste_dma40: Fix DMA registration unwind
d40_dmaengine_init() registers DMAengine devices using devres-managed
unregister actions. If probe fails after one of those registrations, the
DMAengine devices stay visible until devres unwinds after d40_probe()
returns.
The channel tasklets are also initialized before each DMAengine device is
registered. An interrupt can therefore have queued a tasklet by the time a
later registration step fails, but unregistering the DMAengine devices
does not drain that tasklet before probe-owned storage is released.
Add tasklet cleanup actions to the DMAengine registration devres group.
Install each action before publishing its DMAengine device. If action
registration fails, those tasklets have never been exposed and cannot have
been scheduled, so use devm_add_action() without immediate reset cleanup.
On later failure, free the IRQ before releasing the group so no new
tasklets can be scheduled, then unregister the DMAengine devices and drain
their tasklets before freeing the remaining probe resources. Keep the
managed registrations and cleanup actions in place on successful probe by
removing only the temporary group markers.
Fixes: 42ae6f1695 ("dmaengine: ste_dma40: Remove platform data")
Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/dmaengine/20260819225215.5EA2F1F000E9@smtp.kernel.org/
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260927-dma40-fixes-v7-11-89f595e8851d@kernel.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
parent
7ca608cf6b
commit
f71ef686f9
|
|
@ -3034,6 +3034,18 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
|
|||
}
|
||||
}
|
||||
|
||||
static void d40_kill_tasklets(void *data)
|
||||
{
|
||||
struct dma_device *dma = data;
|
||||
struct d40_chan *d40c;
|
||||
struct dma_chan *chan;
|
||||
|
||||
list_for_each_entry(chan, &dma->channels, device_node) {
|
||||
d40c = container_of(chan, struct d40_chan, chan);
|
||||
tasklet_kill(&d40c->tasklet);
|
||||
}
|
||||
}
|
||||
|
||||
static void d40_ops_init(struct d40_base *base, struct dma_device *dev)
|
||||
{
|
||||
if (dma_has_cap(DMA_SLAVE, dev->cap_mask)) {
|
||||
|
|
@ -3080,6 +3092,11 @@ static int __init d40_dmaengine_init(struct d40_base *base,
|
|||
|
||||
d40_ops_init(base, &base->dma_slave);
|
||||
|
||||
err = devm_add_action(base->dev, d40_kill_tasklets,
|
||||
&base->dma_slave);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
err = dmaenginem_async_device_register(&base->dma_slave);
|
||||
|
||||
if (err) {
|
||||
|
|
@ -3095,6 +3112,11 @@ static int __init d40_dmaengine_init(struct d40_base *base,
|
|||
|
||||
d40_ops_init(base, &base->dma_memcpy);
|
||||
|
||||
err = devm_add_action(base->dev, d40_kill_tasklets,
|
||||
&base->dma_memcpy);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
err = dmaenginem_async_device_register(&base->dma_memcpy);
|
||||
|
||||
if (err) {
|
||||
|
|
@ -3112,6 +3134,12 @@ static int __init d40_dmaengine_init(struct d40_base *base,
|
|||
dma_cap_set(DMA_CYCLIC, base->dma_both.cap_mask);
|
||||
|
||||
d40_ops_init(base, &base->dma_both);
|
||||
|
||||
err = devm_add_action(base->dev, d40_kill_tasklets,
|
||||
&base->dma_both);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
err = dmaenginem_async_device_register(&base->dma_both);
|
||||
|
||||
if (err) {
|
||||
|
|
@ -3709,6 +3737,7 @@ static int __init d40_probe(struct platform_device *pdev)
|
|||
struct d40_base *base;
|
||||
struct resource *res;
|
||||
struct resource res_lcpa;
|
||||
void *dmaenginem_reg_group;
|
||||
int num_reserved_chans;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
|
@ -3839,20 +3868,32 @@ static int __init d40_probe(struct platform_device *pdev)
|
|||
d40_err(dev, "No IRQ defined\n");
|
||||
goto destroy_cache;
|
||||
}
|
||||
|
||||
dmaenginem_reg_group = devres_open_group(dev, NULL, GFP_KERNEL);
|
||||
if (!dmaenginem_reg_group) {
|
||||
ret = -ENOMEM;
|
||||
goto release_irq;
|
||||
}
|
||||
|
||||
ret = d40_dmaengine_init(base, num_reserved_chans);
|
||||
if (ret)
|
||||
goto release_irq;
|
||||
goto release_dmaenginem;
|
||||
|
||||
ret = of_dma_controller_register(np, d40_xlate, NULL);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"could not register of_dma_controller\n");
|
||||
goto release_irq;
|
||||
goto release_dmaenginem;
|
||||
}
|
||||
devres_remove_group(dev, dmaenginem_reg_group);
|
||||
|
||||
dev_info(base->dev, "initialized\n");
|
||||
return 0;
|
||||
|
||||
release_dmaenginem:
|
||||
free_irq(base->irq, base);
|
||||
devres_release_group(dev, dmaenginem_reg_group);
|
||||
goto destroy_cache;
|
||||
release_irq:
|
||||
free_irq(base->irq, base);
|
||||
destroy_cache:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user