* [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH
@ 2026-11-10 13:13 Allen Pais
2026-11-10 13:13 ` [PATCH v4 01/33] dmaengine: add tasklet-backed channel BH helpers Allen Pais
` (32 more replies)
0 siblings, 33 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, Andy Shevchenko,
Bartosz Golaszewski, Dave Jiang, Linus Walleij, Logan Gunthorpe,
dmaengine, linux-kernel
Hi Vinod, Frank and Arnd,
This series moves DMAengine client completion bottom halves from private
tasklets to a common per-channel API backed by WQ_BH.
The first patch introduces the dmaengine_*_bh() API with a tasklet backend
and converts virt-dma to it. It also updates every driver that directly
accesses the removed virt_dma_chan tasklet, keeping the patch buildable on
its own. This separates the driver-facing API from its implementation
without changing callback context.
The second patch switches that backend to a dedicated WQ_BH | WQ_PERCPU
workqueue. WQ_BH keeps callbacks in softirq context, while the common API
centralizes initialization, scheduling, and synchronization. The
workqueue helpers remain internal to DMAengine, and dmaengine_kill_bh()
drains scheduled callbacks to preserve tasklet_kill() semantics.
The remaining patches convert driver-owned channel completion tasklets.
Tasklets used for controller-level processing, recovery, or other
non-client-callback work are deliberately left alone because the
appropriate replacement may differ by driver.
As agreed in the RFC discussion, selecting hardirq callback delivery is a
separate API change and is not part of this series.
Frank asked about DMAengine users outside drivers/dma/ that still
open-code completion tasklets. Those will be converted in a follow-up
series once this one has landed, to keep this set from growing further.
Earlier discussion:
https://lore.kernel.org/all/20260108080332.2341725-1-allen.lkml@gmail.com/
v2:
https://lore.kernel.org/all/cover.1785183549.git.allen.lkml@gmail.com/
v3:
https://lore.kernel.org/all/cover.1786384168.git.allen.lkml@gmail.com/
Changes since v3:
- post as a new thread instead of in reply to v3 (Andy)
- rebase onto v7.4-rc2. The ste_dma40 rework merged for 7.4-rc1 added
tasklet_schedule() calls in the d40_queue_start() error paths and a
devm action that kills the channel tasklets; both now use the BH
helpers. fsldma and mv_xor gained tasklet_kill() calls in their channel
remove and error paths, which are converted as well. zynqmp_dma and
altera-msgdma only needed context fixes. The new amlogic-dma driver and
the new at_hdmac channel cleanup path kill the virt-dma channel tasklet
directly and are converted in 01/33.
- drop the DW DMA patch (v3 31/34). Andy considers it unneeded churn
ahead of a larger cleanup of that driver.
- pl330: initialize the channel BH at probe time instead of in
alloc_chan_resources(). dmaengine_init_bh() warns when called on an
already initialized channel, and channels can be re-requested.
- txx9dmac: channels served by the shared controller interrupt keep
scanning descriptors from the controller tasklet. v3 scheduled a BH
that was never initialized for them.
- timb_dma: recompute TIMBDMA_IER from the channel BH once the channel
has been finished, and drain the channel BHs on remove and in the probe
error path.
- mpc512x: drain the channel BHs in mpc_dma_remove().
- sf-pdma: reset the retry budget when abandoning a descriptor, and
spell out in the changelog how the new error path mirrors the done
tasklet (Frank).
- hidma: leave hidma_free() unchanged (Frank). Drain the BH in
free_chan_resources() and in remove(), after killing the low-level
completion tasklet and before hidma_ll_uninit(), and add a
device_synchronize() callback.
- qcom-gpi: synchronize the vchan before the channel ring is freed, and
add a device_synchronize() callback.
- collect Frank Li's Reviewed-by from v3. I kept it on patches that only
picked up mechanical conversions from the rebase (01, 06, 12, 17, 21,
27, 30). timb_dma, txx9dmac, mpc512x, pl330 and qcom-gpi changed
enough that I have not carried his tag forward on those.
The driver changes above came out of going through the Sashiko reports on
v3. Most of what it flagged are pre-existing teardown and locking issues
in the individual drivers that this series does not make worse; those are
out of scope here.
Review tags retained on unchanged standalone patches:
- 09/33 IOAT: Acked-by Dave Jiang
- 17/33 STE DMA40: Reviewed-by Linus Walleij (the rebase only added the
mechanical conversions mentioned above)
- 27/33 PLX DMA and 33/33 Switchtec: Reviewed-by Logan Gunthorpe
Review tags received on v2 patches now folded into 01/33:
- v2 41/64 HSU: Reviewed-by Andy Shevchenko
- v2 60/64 BAM DMA: Acked-by Bartosz Golaszewski
The HSU and BAM DMA changes remain in 01/33, but those patch-specific
trailers are not attached to the substantially broader 37-file patch.
Andy and Bartosz are copied here and on 01/33 for visibility.
Testing:
- build-tested on top of v7.4-rc2 with W=1 for drivers/dma/, with no new
warnings compared to the base commit:
- arm64, x86_64 and arm allmodconfig
- powerpc 44x/katmai_defconfig + AMCC_PPC440SPE_ADMA,
mpc512x_defconfig + MPC512X_DMA, mpc85xx_smp_defconfig + FSL_DMA, and
the same with FSL_DMA replaced by FSL_RAID
- mips rbtx49xx_defconfig + TXX9_DMAC
- every patch builds on its own (drivers/dma/) with the arm64 and x86_64
allmodconfig configurations
- the series also applies cleanly on dmaengine/next (currently v7.4-rc1)
- scripts/checkpatch.pl --strict is clean on all 33 patches
- the sf-pdma and hidma changes are compile-tested only; testing on
hardware would be appreciated
Allen Pais (33):
dmaengine: add tasklet-backed channel BH helpers
dmaengine: back channel BH helpers with WQ_BH
dmaengine: apple-admac: use dmaengine BH callback
dmaengine: at_xdmac: move irq bottom half to dmaengine BH
dmaengine: ep93xx: hook callbacks via dmaengine BH
dmaengine: fsldma: migrate tasklet to dmaengine BH
dmaengine: fsl_raid: run completions via dmaengine BH
dmaengine: imx-dma: flip per-chan tasklet to dmaengine BH
dmaengine: ioat: convert cleanup tasklet to dmaengine BH
dmaengine: mmp_pdma: replace per-chan tasklet with dmaengine BH
dmaengine: mmp_tdma: hook completions to dmaengine BH
dmaengine: mv_xor: convert irq tasklet to dmaengine BH
dmaengine: mxs-dma: use dmaengine BH scheduling
dmaengine: nbpfaxi: switch callbacks to dmaengine BH
dmaengine: pch_dma: convert tasklet to dmaengine BH
dmaengine: ppc4xx: replace irq tasklet with dmaengine BH
dmaengine: ste_dma40: convert per-channel tasklet to dmaengine BH
dmaengine: xgene-dma: wire descriptor cleanup to dmaengine BH
dmaengine: xilinx-dma: use dmaengine BH instead of tasklets
dmaengine: xilinx-dpdma: kill vchan BH on remove
dmaengine: zynqmp-dma: switch completion tasklet to dmaengine BH
dmaengine: tegra20-apb: use channel BH helpers
dmaengine: timb_dma: route callbacks via channel BH
dmaengine: txx9dmac: route callbacks via channel BH
dmaengine: mv_xor_v2: use channel BH helpers
dmaengine: mpc512x: route callbacks via channel BH
dmaengine: plx_dma: use channel BH helpers
dmaengine: sf-pdma: route error callbacks through channel BH
dmaengine: pl330: route callbacks via channel BH
dmaengine: altera-msgdma: use channel BH helpers
dmaengine: hidma: defer callbacks via channel BH
dmaengine: qcom-gpi: defer callbacks via vchan
dmaengine: switchtec: use channel BH helpers
drivers/dma/altera-msgdma.c | 14 +--
drivers/dma/amlogic-dma.c | 4 +-
drivers/dma/apple-admac.c | 16 ++--
drivers/dma/at_hdmac.c | 2 +-
drivers/dma/at_xdmac.c | 13 ++-
drivers/dma/bcm2835-dma.c | 2 +-
drivers/dma/dma-axi-dmac.c | 10 ++-
drivers/dma/dma-jz4780.c | 2 +-
drivers/dma/dmaengine.c | 89 ++++++++++++++++++-
.../dma/dw-axi-dmac/dw-axi-dmac-platform.c | 2 +-
drivers/dma/dw-edma/dw-edma-core.c | 2 +-
drivers/dma/ep93xx_dma.c | 12 ++-
drivers/dma/fsl-edma-common.c | 2 +-
drivers/dma/fsl-qdma.c | 2 +-
drivers/dma/fsl_raid.c | 11 +--
drivers/dma/fsl_raid.h | 1 -
drivers/dma/fsldma.c | 12 +--
drivers/dma/fsldma.h | 1 -
drivers/dma/hisi_dma.c | 2 +-
drivers/dma/hsu/hsu.c | 2 +-
drivers/dma/idma64.c | 4 +-
drivers/dma/img-mdc-dma.c | 2 +-
drivers/dma/imx-dma.c | 26 +++---
drivers/dma/imx-sdma.c | 4 +-
drivers/dma/ioat/dma.c | 14 +--
drivers/dma/ioat/dma.h | 3 +-
drivers/dma/ioat/init.c | 2 +-
drivers/dma/k3dma.c | 2 +-
drivers/dma/loongson/loongson1-apb-dma.c | 2 +-
drivers/dma/mediatek/mtk-cqdma.c | 2 +-
drivers/dma/mediatek/mtk-hsdma.c | 2 +-
drivers/dma/mediatek/mtk-uart-apdma.c | 4 +-
drivers/dma/mmp_pdma.c | 13 ++-
drivers/dma/mmp_tdma.c | 9 +-
drivers/dma/mpc512x_dma.c | 78 ++++++++++------
drivers/dma/mv_xor.c | 14 +--
drivers/dma/mv_xor.h | 2 -
drivers/dma/mv_xor_v2.c | 20 ++---
drivers/dma/mxs-dma.c | 11 ++-
drivers/dma/nbpfaxi.c | 13 ++-
drivers/dma/owl-dma.c | 2 +-
drivers/dma/pch_dma.c | 14 +--
drivers/dma/pl330.c | 19 ++--
drivers/dma/plx_dma.c | 12 +--
drivers/dma/ppc4xx/adma.c | 14 +--
drivers/dma/ppc4xx/adma.h | 2 -
drivers/dma/pxa_dma.c | 2 +-
drivers/dma/qcom/bam_dma.c | 4 +-
drivers/dma/qcom/gpi.c | 27 +++++-
drivers/dma/qcom/hidma.c | 24 ++++-
drivers/dma/qcom/qcom_adm.c | 4 +-
drivers/dma/sa11x0-dma.c | 2 +-
drivers/dma/sf-pdma/sf-pdma.c | 18 +++-
drivers/dma/sprd-dma.c | 2 +-
drivers/dma/st_fdma.c | 2 +-
drivers/dma/ste_dma40.c | 29 +++---
drivers/dma/sun6i-dma.c | 2 +-
drivers/dma/switchtec_dma.c | 16 ++--
drivers/dma/tegra186-gpc-dma.c | 2 +-
drivers/dma/tegra20-apb-dma.c | 18 ++--
drivers/dma/tegra210-adma.c | 2 +-
drivers/dma/ti/edma.c | 2 +-
drivers/dma/ti/k3-udma.c | 12 +--
drivers/dma/ti/omap-dma.c | 2 +-
drivers/dma/timb_dma.c | 41 +++++++--
drivers/dma/txx9dmac.c | 10 +--
drivers/dma/txx9dmac.h | 1 -
drivers/dma/virt-dma.c | 12 +--
drivers/dma/virt-dma.h | 7 +-
drivers/dma/xgene-dma.c | 19 ++--
drivers/dma/xilinx/xilinx_dma.c | 20 ++---
drivers/dma/xilinx/xilinx_dpdma.c | 1 +
drivers/dma/xilinx/zynqmp_dma.c | 19 ++--
include/linux/dmaengine.h | 28 ++++++
74 files changed, 503 insertions(+), 316 deletions(-)
base-commit: 7475ea8b58616b33c04afb976cf0fbaad9dd63fb
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 01/33] dmaengine: add tasklet-backed channel BH helpers
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 02/33] dmaengine: back channel BH helpers with WQ_BH Allen Pais
` (31 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Frank Li, Ludovic Desroches, Florian Fainelli,
Broadcom internal kernel review list, Ray Jui, Scott Branden,
Lars-Peter Clausen, Paul Cercueil, Eugeniy Paltsev,
Manivannan Sadhasivam, Zhou Wang, Longfang Liu, Andy Shevchenko,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Keguang Zhang, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, Andreas Färber, Daniel Mack,
Haojian Zhuang, Robert Jarzmik, Paul Walmsley, Samuel Holland,
Orson Zhai, Baolin Wang, Chunyan Zhang, Patrice Chotard,
Chen-Yu Tsai, Jernej Skrabec, Laxman Dewangan, Jon Hunter,
Thierry Reding, Vignesh Raghavendra, Bartosz Golaszewski,
Kees Cook, Bjorn Andersson, Guangshuo Li, Kuldeep Singh,
linux-arm-kernel, linux-rpi-kernel, linux-mips, imx,
linux-mediatek, linux-actions, linux-arm-msm, linux-riscv,
linux-sunxi, linux-tegra
DMAengine drivers commonly use a per-channel tasklet to invoke client
callbacks. Add helpers that initialize, schedule, and kill a channel
bottom half, with an initial tasklet-backed implementation that preserves
the existing execution context.
Convert virt-dma to the new API and remove its private tasklet. Update all
drivers that directly kill or override that tasklet in the same change so
no stale users remain. While touching the completion handler, avoid forming
a result pointer from a NULL cyclic descriptor.
This establishes a backend-independent API before changing how channel
bottom halves are dispatched.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/amlogic-dma.c | 4 +-
drivers/dma/at_hdmac.c | 2 +-
drivers/dma/bcm2835-dma.c | 2 +-
drivers/dma/dma-axi-dmac.c | 10 +++--
drivers/dma/dma-jz4780.c | 2 +-
drivers/dma/dmaengine.c | 42 +++++++++++++++++++
.../dma/dw-axi-dmac/dw-axi-dmac-platform.c | 2 +-
drivers/dma/dw-edma/dw-edma-core.c | 2 +-
drivers/dma/fsl-edma-common.c | 2 +-
drivers/dma/fsl-qdma.c | 2 +-
drivers/dma/hisi_dma.c | 2 +-
drivers/dma/hsu/hsu.c | 2 +-
drivers/dma/idma64.c | 4 +-
drivers/dma/img-mdc-dma.c | 2 +-
drivers/dma/imx-sdma.c | 4 +-
drivers/dma/k3dma.c | 2 +-
drivers/dma/loongson/loongson1-apb-dma.c | 2 +-
drivers/dma/mediatek/mtk-cqdma.c | 2 +-
drivers/dma/mediatek/mtk-hsdma.c | 2 +-
drivers/dma/mediatek/mtk-uart-apdma.c | 4 +-
drivers/dma/owl-dma.c | 2 +-
drivers/dma/pxa_dma.c | 2 +-
drivers/dma/qcom/bam_dma.c | 4 +-
drivers/dma/qcom/qcom_adm.c | 4 +-
drivers/dma/sa11x0-dma.c | 2 +-
drivers/dma/sf-pdma/sf-pdma.c | 2 +-
drivers/dma/sprd-dma.c | 2 +-
drivers/dma/st_fdma.c | 2 +-
drivers/dma/sun6i-dma.c | 2 +-
drivers/dma/tegra186-gpc-dma.c | 2 +-
drivers/dma/tegra210-adma.c | 2 +-
drivers/dma/ti/edma.c | 2 +-
drivers/dma/ti/k3-udma.c | 12 +++---
drivers/dma/ti/omap-dma.c | 2 +-
drivers/dma/virt-dma.c | 12 +++---
drivers/dma/virt-dma.h | 7 ++--
include/linux/dmaengine.h | 28 +++++++++++++
37 files changed, 128 insertions(+), 57 deletions(-)
diff --git a/drivers/dma/amlogic-dma.c b/drivers/dma/amlogic-dma.c
index 684121d13fca..4a14b2f777c6 100644
--- a/drivers/dma/amlogic-dma.c
+++ b/drivers/dma/amlogic-dma.c
@@ -707,10 +707,10 @@ static void aml_dma_remove(struct platform_device *pdev)
for (i = 0; i < MAX_CHAN_ID; i++) {
aml_chan = aml_dma->aml_rch[i];
if (aml_chan)
- tasklet_kill(&aml_chan->vchan.task);
+ dmaengine_kill_bh(&aml_chan->vchan.chan);
aml_chan = aml_dma->aml_wch[i];
if (aml_chan)
- tasklet_kill(&aml_chan->vchan.task);
+ dmaengine_kill_bh(&aml_chan->vchan.chan);
}
destroy_workqueue(aml_dma->free_wq);
}
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index b024aa2d34b2..991c572d5c19 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -1944,7 +1944,7 @@ static void at_dma_cleanup_channels(struct at_dma *atdma)
device_node) {
/* Disable interrupts */
atc_disable_chan_irq(atdma, i++);
- tasklet_kill(&to_at_dma_chan(chan)->vc.task);
+ dmaengine_kill_bh(chan);
list_del(&chan->device_node);
}
}
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index d41b0f6ec427..62844b44696b 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -829,7 +829,7 @@ static void bcm2835_dma_free(struct bcm2835_dmadev *od)
list_for_each_entry_safe(c, next, &od->ddev.channels,
vc.chan.device_node) {
list_del(&c->vc.chan.device_node);
- tasklet_kill(&c->vc.task);
+ dmaengine_kill_bh(&c->vc.chan);
}
dma_unmap_page_attrs(od->ddev.dev, od->zero_page, PAGE_SIZE,
diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index dbc31b185c4d..91ec19dd4df8 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -1195,9 +1195,11 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
return 0;
}
-static void axi_dmac_tasklet_kill(void *task)
+static void axi_dmac_kill_bh(void *data)
{
- tasklet_kill(task);
+ struct dma_chan *chan = data;
+
+ dmaengine_kill_bh(chan);
}
static void axi_dmac_free_dma_controller(void *of_node)
@@ -1302,8 +1304,8 @@ static int axi_dmac_probe(struct platform_device *pdev)
* Put the action in here so it get's done before unregistering the DMA
* device.
*/
- ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_tasklet_kill,
- &dmac->chan.vchan.task);
+ ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_kill_bh,
+ &dmac->chan.vchan.chan);
if (ret)
return ret;
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index e610aba09418..27eaf2fa077c 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -1018,7 +1018,7 @@ static void jz4780_dma_remove(struct platform_device *pdev)
free_irq(jzdma->irq, jzdma);
for (i = 0; i < jzdma->soc_data->nb_channels; i++)
- tasklet_kill(&jzdma->chan[i].vchan.task);
+ dmaengine_kill_bh(&jzdma->chan[i].vchan.chan);
}
static const struct jz4780_dma_soc_data jz4740_dma_soc_data = {
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 34be53487a21..f3c19fbb2a01 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -1449,6 +1449,48 @@ static void dmaengine_destroy_unmap_pool(void)
}
}
+static void dma_chan_bh_entry(struct tasklet_struct *tasklet)
+{
+ struct dma_chan *chan = from_tasklet(chan, tasklet, bh_tasklet);
+ dmaengine_bh_work_fn fn = READ_ONCE(chan->bh_work_fn);
+
+ if (fn)
+ fn(chan);
+}
+
+void dmaengine_init_bh(struct dma_chan *chan, dmaengine_bh_work_fn fn)
+{
+ if (WARN_ON(!fn))
+ return;
+
+ if (WARN_ON(chan->bh_work_initialized))
+ return;
+
+ chan->bh_work_fn = fn;
+ tasklet_setup(&chan->bh_tasklet, dma_chan_bh_entry);
+ chan->bh_work_initialized = true;
+}
+EXPORT_SYMBOL_GPL(dmaengine_init_bh);
+
+bool dmaengine_schedule_bh(struct dma_chan *chan)
+{
+ if (WARN_ON(!chan->bh_work_initialized))
+ return false;
+
+ tasklet_schedule(&chan->bh_tasklet);
+ return true;
+}
+EXPORT_SYMBOL_GPL(dmaengine_schedule_bh);
+
+void dmaengine_kill_bh(struct dma_chan *chan)
+{
+ if (!chan->bh_work_initialized)
+ return;
+
+ tasklet_kill(&chan->bh_tasklet);
+}
+EXPORT_SYMBOL_GPL(dmaengine_kill_bh);
+
static int __init dmaengine_init_unmap_pool(void)
{
int i;
diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
index 940b196b69d1..425c25028a49 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -1657,7 +1657,7 @@ static void dw_remove(struct platform_device *pdev)
list_for_each_entry_safe(chan, _chan, &dw->dma.channels,
vc.chan.device_node) {
list_del(&chan->vc.chan.device_node);
- tasklet_kill(&chan->vc.task);
+ dmaengine_kill_bh(&chan->vc.chan);
}
}
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index d682074eaa0e..2cceb87dffb8 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -1505,7 +1505,7 @@ int dw_edma_remove(struct dw_edma_chip *chip)
dma_async_device_unregister(&dw->dma);
list_for_each_entry_safe(chan, _chan, &dw->dma.channels,
vc.chan.device_node) {
- tasklet_kill(&chan->vc.task);
+ dmaengine_kill_bh(&chan->vc.chan);
list_del(&chan->vc.chan.device_node);
}
diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
index 513b0f4e5be2..85cc8791659d 100644
--- a/drivers/dma/fsl-edma-common.c
+++ b/drivers/dma/fsl-edma-common.c
@@ -1158,7 +1158,7 @@ void fsl_edma_cleanup_vchan(struct dma_device *dmadev)
list_for_each_entry_safe(chan, _chan,
&dmadev->channels, vchan.chan.device_node) {
list_del(&chan->vchan.chan.device_node);
- tasklet_kill(&chan->vchan.task);
+ dmaengine_kill_bh(&chan->vchan.chan);
}
}
diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c
index 27c632e6fa78..b901fe1046ef 100644
--- a/drivers/dma/fsl-qdma.c
+++ b/drivers/dma/fsl-qdma.c
@@ -1254,7 +1254,7 @@ static void fsl_qdma_cleanup_vchan(struct dma_device *dmadev)
list_for_each_entry_safe(chan, _chan,
&dmadev->channels, vchan.chan.device_node) {
list_del(&chan->vchan.chan.device_node);
- tasklet_kill(&chan->vchan.task);
+ dmaengine_kill_bh(&chan->vchan.chan);
}
}
diff --git a/drivers/dma/hisi_dma.c b/drivers/dma/hisi_dma.c
index d3ec1819aa5d..16279c332e9d 100644
--- a/drivers/dma/hisi_dma.c
+++ b/drivers/dma/hisi_dma.c
@@ -720,7 +720,7 @@ static void hisi_dma_disable_qps(struct hisi_dma_dev *hdma_dev)
for (i = 0; i < hdma_dev->chan_num; i++) {
hisi_dma_disable_qp(hdma_dev, i);
- tasklet_kill(&hdma_dev->chan[i].vc.task);
+ dmaengine_kill_bh(&hdma_dev->chan[i].vc.chan);
}
}
diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c
index ce24e840af59..ec530e0e1296 100644
--- a/drivers/dma/hsu/hsu.c
+++ b/drivers/dma/hsu/hsu.c
@@ -499,7 +499,7 @@ int hsu_dma_remove(struct hsu_dma_chip *chip)
for (i = 0; i < hsu->nr_channels; i++) {
struct hsu_dma_chan *hsuc = &hsu->chan[i];
- tasklet_kill(&hsuc->vchan.task);
+ dmaengine_kill_bh(&hsuc->vchan.chan);
}
return 0;
diff --git a/drivers/dma/idma64.c b/drivers/dma/idma64.c
index e3db213e564b..d7089c4b3787 100644
--- a/drivers/dma/idma64.c
+++ b/drivers/dma/idma64.c
@@ -610,14 +610,14 @@ static void idma64_remove(struct idma64_chip *chip)
/*
* Explicitly call devm_request_irq() to avoid the side effects with
- * the scheduled tasklets.
+ * scheduled BH work.
*/
devm_free_irq(chip->dev, chip->irq, idma64);
for (i = 0; i < idma64->dma.chancnt; i++) {
struct idma64_chan *idma64c = &idma64->chan[i];
- tasklet_kill(&idma64c->vchan.task);
+ dmaengine_kill_bh(&idma64c->vchan.chan);
}
}
diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c
index b91807d5ae82..22288f3bae55 100644
--- a/drivers/dma/img-mdc-dma.c
+++ b/drivers/dma/img-mdc-dma.c
@@ -1031,7 +1031,7 @@ static void mdc_dma_remove(struct platform_device *pdev)
devm_free_irq(&pdev->dev, mchan->irq, mchan);
- tasklet_kill(&mchan->vc.task);
+ dmaengine_kill_bh(&mchan->vc.chan);
}
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 749f1fe77907..9bf70a682e22 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -2399,11 +2399,11 @@ static void sdma_remove(struct platform_device *pdev)
int i;
devm_free_irq(&pdev->dev, sdma->irq, sdma);
- /* Kill the tasklet */
+ /* Kill the channel BH */
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
struct sdma_channel *sdmac = &sdma->channel[i];
- tasklet_kill(&sdmac->vc.task);
+ dmaengine_kill_bh(&sdmac->vc.chan);
sdma_free_chan_resources(&sdmac->vc.chan);
}
diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c
index 079dda13cf0c..571237b15182 100644
--- a/drivers/dma/k3dma.c
+++ b/drivers/dma/k3dma.c
@@ -975,7 +975,7 @@ static void k3_dma_remove(struct platform_device *op)
list_for_each_entry_safe(c, cn, &d->slave.channels, vc.chan.device_node) {
list_del(&c->vc.chan.device_node);
- tasklet_kill(&c->vc.task);
+ dmaengine_kill_bh(&c->vc.chan);
}
tasklet_kill(&d->task);
clk_disable_unprepare(d->clk);
diff --git a/drivers/dma/loongson/loongson1-apb-dma.c b/drivers/dma/loongson/loongson1-apb-dma.c
index 10a1920ff6c2..c91e933b186a 100644
--- a/drivers/dma/loongson/loongson1-apb-dma.c
+++ b/drivers/dma/loongson/loongson1-apb-dma.c
@@ -547,7 +547,7 @@ static void ls1x_dma_chan_remove(struct ls1x_dma *dma)
if (chan->vc.chan.device == &dma->ddev) {
list_del(&chan->vc.chan.device_node);
- tasklet_kill(&chan->vc.task);
+ dmaengine_kill_bh(&chan->vc.chan);
}
}
}
diff --git a/drivers/dma/mediatek/mtk-cqdma.c b/drivers/dma/mediatek/mtk-cqdma.c
index a7000608ddea..8c536c4e04ec 100644
--- a/drivers/dma/mediatek/mtk-cqdma.c
+++ b/drivers/dma/mediatek/mtk-cqdma.c
@@ -894,7 +894,7 @@ static void mtk_cqdma_remove(struct platform_device *pdev)
vc = &cqdma->vc[i];
list_del(&vc->vc.chan.device_node);
- tasklet_kill(&vc->vc.task);
+ dmaengine_kill_bh(&vc->vc.chan);
}
/* disable interrupt */
diff --git a/drivers/dma/mediatek/mtk-hsdma.c b/drivers/dma/mediatek/mtk-hsdma.c
index ad5883cf6165..2a2b4c38a3f5 100644
--- a/drivers/dma/mediatek/mtk-hsdma.c
+++ b/drivers/dma/mediatek/mtk-hsdma.c
@@ -1032,7 +1032,7 @@ static void mtk_hsdma_remove(struct platform_device *pdev)
vc = &hsdma->vc[i];
list_del(&vc->vc.chan.device_node);
- tasklet_kill(&vc->vc.task);
+ dmaengine_kill_bh(&vc->vc.chan);
}
/* Disable DMA interrupt */
diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c
index bf242c4c262f..127e1d6a30a1 100644
--- a/drivers/dma/mediatek/mtk-uart-apdma.c
+++ b/drivers/dma/mediatek/mtk-uart-apdma.c
@@ -312,7 +312,7 @@ static void mtk_uart_apdma_free_chan_resources(struct dma_chan *chan)
free_irq(c->irq, chan);
- tasklet_kill(&c->vc.task);
+ dmaengine_kill_bh(&c->vc.chan);
vchan_free_chan_resources(&c->vc);
@@ -463,7 +463,7 @@ static void mtk_uart_apdma_free(struct mtk_uart_apdmadev *mtkd)
struct mtk_chan, vc.chan.device_node);
list_del(&c->vc.chan.device_node);
- tasklet_kill(&c->vc.task);
+ dmaengine_kill_bh(&c->vc.chan);
}
}
diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c
index 31a6c6e01a75..43663f4539f0 100644
--- a/drivers/dma/owl-dma.c
+++ b/drivers/dma/owl-dma.c
@@ -1050,7 +1050,7 @@ static inline void owl_dma_free(struct owl_dma *od)
list_for_each_entry_safe(vchan,
next, &od->dma.channels, vc.chan.device_node) {
list_del(&vchan->vc.chan.device_node);
- tasklet_kill(&vchan->vc.task);
+ dmaengine_kill_bh(&vchan->vc.chan);
}
}
diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c
index bf03009bb966..29bcda3f785b 100644
--- a/drivers/dma/pxa_dma.c
+++ b/drivers/dma/pxa_dma.c
@@ -1216,7 +1216,7 @@ static void pxad_free_channels(struct dma_device *dmadev)
list_for_each_entry_safe(c, cn, &dmadev->channels,
vc.chan.device_node) {
list_del(&c->vc.chan.device_node);
- tasklet_kill(&c->vc.task);
+ dmaengine_kill_bh(&c->vc.chan);
}
}
diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c
index e62e411dc8f8..823b92099bcc 100644
--- a/drivers/dma/qcom/bam_dma.c
+++ b/drivers/dma/qcom/bam_dma.c
@@ -1389,7 +1389,7 @@ static int bam_dma_probe(struct platform_device *pdev)
free_irq(bdev->irq, bdev);
err_bam_channel_exit:
for (i = 0; i < bdev->num_channels; i++)
- tasklet_kill(&bdev->channels[i].vc.task);
+ dmaengine_kill_bh(&bdev->channels[i].vc.chan);
err_tasklet_kill:
tasklet_kill(&bdev->task);
err_disable_clk:
@@ -1416,7 +1416,7 @@ static void bam_dma_remove(struct platform_device *pdev)
for (i = 0; i < bdev->num_channels; i++) {
bam_dma_terminate_all(&bdev->channels[i].vc.chan);
- tasklet_kill(&bdev->channels[i].vc.task);
+ dmaengine_kill_bh(&bdev->channels[i].vc.chan);
if (!bdev->channels[i].fifo_virt)
continue;
diff --git a/drivers/dma/qcom/qcom_adm.c b/drivers/dma/qcom/qcom_adm.c
index b38ee75a9637..f306b3c841de 100644
--- a/drivers/dma/qcom/qcom_adm.c
+++ b/drivers/dma/qcom/qcom_adm.c
@@ -918,8 +918,8 @@ static void adm_dma_remove(struct platform_device *pdev)
/* mask IRQs for this channel/EE pair */
writel(0, adev->regs + ADM_CH_RSLT_CONF(achan->id, adev->ee));
- tasklet_kill(&adev->channels[i].vc.task);
- adm_terminate_all(&adev->channels[i].vc.chan);
+ dmaengine_kill_bh(&achan->vc.chan);
+ adm_terminate_all(&achan->vc.chan);
}
devm_free_irq(adev->dev, adev->irq, adev);
diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c
index a6fa431530e3..e14566fa2d74 100644
--- a/drivers/dma/sa11x0-dma.c
+++ b/drivers/dma/sa11x0-dma.c
@@ -891,7 +891,7 @@ static void sa11x0_dma_free_channels(struct dma_device *dmadev)
list_for_each_entry_safe(c, cn, &dmadev->channels, vc.chan.device_node) {
list_del(&c->vc.chan.device_node);
- tasklet_kill(&c->vc.task);
+ dmaengine_kill_bh(&c->vc.chan);
kfree(c);
}
}
diff --git a/drivers/dma/sf-pdma/sf-pdma.c b/drivers/dma/sf-pdma/sf-pdma.c
index 4770ec3470cb..ca60fb46ade0 100644
--- a/drivers/dma/sf-pdma/sf-pdma.c
+++ b/drivers/dma/sf-pdma/sf-pdma.c
@@ -601,7 +601,7 @@ static void sf_pdma_remove(struct platform_device *pdev)
devm_free_irq(&pdev->dev, ch->txirq, ch);
devm_free_irq(&pdev->dev, ch->errirq, ch);
list_del(&ch->vchan.chan.device_node);
- tasklet_kill(&ch->vchan.task);
+ dmaengine_kill_bh(&ch->vchan.chan);
tasklet_kill(&ch->done_tasklet);
tasklet_kill(&ch->err_tasklet);
}
diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c
index 55ccbb902926..704b05885cd9 100644
--- a/drivers/dma/sprd-dma.c
+++ b/drivers/dma/sprd-dma.c
@@ -1231,7 +1231,7 @@ static void sprd_dma_remove(struct platform_device *pdev)
list_for_each_entry_safe(c, cn, &sdev->dma_dev.channels,
vc.chan.device_node) {
list_del(&c->vc.chan.device_node);
- tasklet_kill(&c->vc.task);
+ dmaengine_kill_bh(&c->vc.chan);
}
of_dma_controller_free(pdev->dev.of_node);
diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c
index a82058e27aa4..875d506d4686 100644
--- a/drivers/dma/st_fdma.c
+++ b/drivers/dma/st_fdma.c
@@ -733,7 +733,7 @@ static void st_fdma_free(struct st_fdma_dev *fdev)
for (i = 0; i < fdev->nr_channels; i++) {
fchan = &fdev->chans[i];
list_del(&fchan->vchan.chan.device_node);
- tasklet_kill(&fchan->vchan.task);
+ dmaengine_kill_bh(&fchan->vchan.chan);
}
}
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index 3b849833e08f..f4543e8af789 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -1136,7 +1136,7 @@ static inline void sun6i_dma_free(struct sun6i_dma_dev *sdev)
struct sun6i_vchan *vchan = &sdev->vchans[i];
list_del(&vchan->vc.chan.device_node);
- tasklet_kill(&vchan->vc.task);
+ dmaengine_kill_bh(&vchan->vc.chan);
}
}
diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index ec12b7288b43..30fe6cfcc82a 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -1284,7 +1284,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc)
tegra_dma_terminate_all(dc);
synchronize_irq(tdc->irq);
- tasklet_kill(&tdc->vc.task);
+ dmaengine_kill_bh(&tdc->vc.chan);
tdc->config_init = false;
tdc->slave_id = -1;
tdc->sid_dir = DMA_TRANS_NONE;
diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c
index 607f8e0848ac..0c73dc61622a 100644
--- a/drivers/dma/tegra210-adma.c
+++ b/drivers/dma/tegra210-adma.c
@@ -811,7 +811,7 @@ static void tegra_adma_free_chan_resources(struct dma_chan *dc)
tegra_adma_terminate_all(dc);
vchan_free_chan_resources(&tdc->vc);
- tasklet_kill(&tdc->vc.task);
+ dmaengine_kill_bh(&tdc->vc.chan);
free_irq(tdc->irq, tdc);
pm_runtime_put(tdc2dev(tdc));
diff --git a/drivers/dma/ti/edma.c b/drivers/dma/ti/edma.c
index f3a98654d126..01f9739f120c 100644
--- a/drivers/dma/ti/edma.c
+++ b/drivers/dma/ti/edma.c
@@ -2557,7 +2557,7 @@ static void edma_cleanupp_vchan(struct dma_device *dmadev)
list_for_each_entry_safe(echan, _echan,
&dmadev->channels, vchan.chan.device_node) {
list_del(&echan->vchan.chan.device_node);
- tasklet_kill(&echan->vchan.task);
+ dmaengine_kill_bh(&echan->vchan.chan);
}
}
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index 75bd1977fb42..b58c9bb22ddf 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -4042,12 +4042,12 @@ static void udma_desc_pre_callback(struct virt_dma_chan *vc,
}
/*
- * This tasklet handles the completion of a DMA descriptor by
+ * This BH handles the completion of a DMA descriptor by
* calling its callback and freeing it.
*/
-static void udma_vchan_complete(struct tasklet_struct *t)
+static void udma_vchan_complete(struct dma_chan *chan)
{
- struct virt_dma_chan *vc = from_tasklet(vc, t, task);
+ struct virt_dma_chan *vc = to_virt_chan(chan);
struct virt_dma_desc *vd, *_vd;
struct dmaengine_desc_callback cb;
LIST_HEAD(head);
@@ -4112,7 +4112,7 @@ static void udma_free_chan_resources(struct dma_chan *chan)
}
vchan_free_chan_resources(&uc->vc);
- tasklet_kill(&uc->vc.task);
+ dmaengine_kill_bh(&uc->vc.chan);
bcdma_free_bchan_resources(uc);
udma_free_tx_resources(uc);
@@ -5610,8 +5610,8 @@ static int udma_probe(struct platform_device *pdev)
if (!uc->name)
return -ENOMEM;
vchan_init(&uc->vc, &ud->ddev);
- /* Use custom vchan completion handling */
- tasklet_setup(&uc->vc.task, udma_vchan_complete);
+ /* Override the default vchan completion handler */
+ uc->vc.chan.bh_work_fn = udma_vchan_complete;
init_completion(&uc->teardown_completed);
INIT_DELAYED_WORK(&uc->tx_drain.work, udma_check_tx_completion);
}
diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c
index 93b6a4682085..aad968331162 100644
--- a/drivers/dma/ti/omap-dma.c
+++ b/drivers/dma/ti/omap-dma.c
@@ -1521,7 +1521,7 @@ static void omap_dma_free(struct omap_dmadev *od)
struct omap_chan, vc.chan.device_node);
list_del(&c->vc.chan.device_node);
- tasklet_kill(&c->vc.task);
+ dmaengine_kill_bh(&c->vc.chan);
kfree(c);
}
}
diff --git a/drivers/dma/virt-dma.c b/drivers/dma/virt-dma.c
index 7961172a780d..d407af6cc1da 100644
--- a/drivers/dma/virt-dma.c
+++ b/drivers/dma/virt-dma.c
@@ -77,12 +77,12 @@ struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc,
EXPORT_SYMBOL_GPL(vchan_find_desc);
/*
- * This tasklet handles the completion of a DMA descriptor by
- * calling its callback and freeing it.
+ * This bottom-half handler completes a DMA descriptor by invoking its
+ * callback and freeing it.
*/
-static void vchan_complete(struct tasklet_struct *t)
+static void vchan_complete(struct dma_chan *chan)
{
- struct virt_dma_chan *vc = from_tasklet(vc, t, task);
+ struct virt_dma_chan *vc = to_virt_chan(chan);
struct virt_dma_desc *vd, *_vd;
struct dmaengine_desc_callback cb;
LIST_HEAD(head);
@@ -98,7 +98,7 @@ static void vchan_complete(struct tasklet_struct *t)
}
spin_unlock_irq(&vc->lock);
- dmaengine_desc_callback_invoke(&cb, &vd->tx_result);
+ dmaengine_desc_callback_invoke(&cb, vd ? &vd->tx_result : NULL);
list_for_each_entry_safe(vd, _vd, &head, node) {
dmaengine_desc_get_callback(&vd->tx, &cb);
@@ -131,7 +131,7 @@ void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
INIT_LIST_HEAD(&vc->desc_completed);
INIT_LIST_HEAD(&vc->desc_terminated);
- tasklet_setup(&vc->task, vchan_complete);
+ dmaengine_init_bh(&vc->chan, vchan_complete);
vc->chan.device = dmadev;
list_add_tail(&vc->chan.device_node, &dmadev->channels);
diff --git a/drivers/dma/virt-dma.h b/drivers/dma/virt-dma.h
index abf2ae737c30..8e7c0ca8623a 100644
--- a/drivers/dma/virt-dma.h
+++ b/drivers/dma/virt-dma.h
@@ -21,7 +21,6 @@ struct virt_dma_desc {
struct virt_dma_chan {
struct dma_chan chan;
- struct tasklet_struct task;
void (*desc_free)(struct virt_dma_desc *);
spinlock_t lock;
@@ -111,7 +110,7 @@ static inline void vchan_cookie_complete(struct virt_dma_desc *vd)
vd, cookie);
list_add_tail(&vd->node, &vc->desc_completed);
- tasklet_schedule(&vc->task);
+ dmaengine_schedule_bh(&vc->chan);
}
/**
@@ -142,7 +141,7 @@ static inline void vchan_cyclic_callback(struct virt_dma_desc *vd)
struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
vc->cyclic = vd;
- tasklet_schedule(&vc->task);
+ dmaengine_schedule_bh(&vc->chan);
}
/**
@@ -239,7 +238,7 @@ static inline void vchan_synchronize(struct virt_dma_chan *vc)
LIST_HEAD(head);
unsigned long flags;
- tasklet_kill(&vc->task);
+ dmaengine_kill_bh(&vc->chan);
spin_lock_irqsave(&vc->lock, flags);
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 805668fbe437..b0d69e3083d6 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -12,6 +12,7 @@
#include <linux/scatterlist.h>
#include <linux/bitmap.h>
#include <linux/types.h>
+#include <linux/interrupt.h>
#include <asm/page.h>
/**
@@ -295,6 +296,10 @@ enum dma_desc_metadata_mode {
DESC_METADATA_ENGINE = BIT(1),
};
+struct dma_chan;
+
+typedef void (*dmaengine_bh_work_fn)(struct dma_chan *chan);
+
/**
* struct dma_chan_percpu - the per-CPU part of struct dma_chan
* @memcpy_count: transaction counter
@@ -337,6 +342,9 @@ struct dma_router {
* @router: pointer to the DMA router structure
* @route_data: channel specific data for the router
* @private: private data for certain client-channel associations
+ * @bh_tasklet: bottom-half tasklet stored per-channel
+ * @bh_work_fn: callback executed when @bh_tasklet runs
+ * @bh_work_initialized: indicates whether @bh_tasklet has been initialized
*/
struct dma_chan {
struct dma_device *device;
@@ -375,6 +383,9 @@ struct dma_chan {
void *route_data;
void *private;
+ struct tasklet_struct bh_tasklet;
+ dmaengine_bh_work_fn bh_work_fn;
+ bool bh_work_initialized;
};
#define DMA_CHAN_ID_STATIC BIT(30)
@@ -1970,6 +1981,9 @@ struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name);
void dma_release_channel(struct dma_chan *chan);
int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps);
+void dmaengine_init_bh(struct dma_chan *chan, dmaengine_bh_work_fn fn);
+bool dmaengine_schedule_bh(struct dma_chan *chan);
+void dmaengine_kill_bh(struct dma_chan *chan);
#else
static inline struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type)
{
@@ -2017,6 +2031,20 @@ static inline int dma_get_slave_caps(struct dma_chan *chan,
{
return -ENXIO;
}
+
+static inline void dmaengine_init_bh(struct dma_chan *chan,
+ dmaengine_bh_work_fn fn)
+{
+}
+
+static inline bool dmaengine_schedule_bh(struct dma_chan *chan)
+{
+ return false;
+}
+
+static inline void dmaengine_kill_bh(struct dma_chan *chan)
+{
+}
#endif
static inline int dmaengine_desc_set_reuse(struct dma_async_tx_descriptor *tx)
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 02/33] dmaengine: back channel BH helpers with WQ_BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 01/33] dmaengine: add tasklet-backed channel BH helpers Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 03/33] dmaengine: apple-admac: use dmaengine BH callback Allen Pais
` (30 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Replace the tasklet implementation of the channel BH helpers with a
dedicated WQ_BH | WQ_PERCPU workqueue. The public dmaengine_*_bh() API and
its softirq execution context remain unchanged.
Keep the workqueue operations internal to dmaengine. Drain scheduled work
in dmaengine_kill_bh() to preserve the completion semantics of
tasklet_kill().
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/dmaengine.c | 59 ++++++++++++++++++++++++++++++++-------
include/linux/dmaengine.h | 10 +++----
2 files changed, 54 insertions(+), 15 deletions(-)
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index f3c19fbb2a01..97d8f9667ae5 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -54,6 +54,7 @@
#include <linux/rcupdate.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/workqueue.h>
#include "dmaengine.h"
@@ -61,6 +62,7 @@ static DEFINE_MUTEX(dma_list_mutex);
static DEFINE_IDA(dma_ida);
static LIST_HEAD(dma_device_list);
static long dmaengine_ref_count;
+static struct workqueue_struct *dmaengine_bh_wq;
/* --- debugfs implementation --- */
#ifdef CONFIG_DEBUG_FS
@@ -1449,9 +1451,34 @@ static void dmaengine_destroy_unmap_pool(void)
}
}
-static void dma_chan_bh_entry(struct tasklet_struct *tasklet)
+static void dmaengine_destroy_bh_wq(void)
{
- struct dma_chan *chan = from_tasklet(chan, tasklet, bh_tasklet);
+ if (!dmaengine_bh_wq)
+ return;
+
+ destroy_workqueue(dmaengine_bh_wq);
+ dmaengine_bh_wq = NULL;
+}
+
+static bool dmaengine_queue_bh_work(struct work_struct *work)
+{
+ if (WARN_ON(!dmaengine_bh_wq))
+ return false;
+
+ return queue_work(dmaengine_bh_wq, work);
+}
+
+static void dmaengine_flush_bh_work(struct work_struct *work)
+{
+ if (!work)
+ return;
+
+ flush_work(work);
+}
+
+static void dma_chan_bh_entry(struct work_struct *work)
+{
+ struct dma_chan *chan = container_of(work, struct dma_chan, bh_work);
dmaengine_bh_work_fn fn = READ_ONCE(chan->bh_work_fn);
if (fn)
@@ -1467,7 +1494,7 @@ void dmaengine_init_bh(struct dma_chan *chan, dmaengine_bh_work_fn fn)
return;
chan->bh_work_fn = fn;
- tasklet_setup(&chan->bh_tasklet, dma_chan_bh_entry);
+ INIT_WORK(&chan->bh_work, dma_chan_bh_entry);
chan->bh_work_initialized = true;
}
EXPORT_SYMBOL_GPL(dmaengine_init_bh);
@@ -1477,8 +1504,7 @@ bool dmaengine_schedule_bh(struct dma_chan *chan)
if (WARN_ON(!chan->bh_work_initialized))
return false;
- tasklet_schedule(&chan->bh_tasklet);
- return true;
+ return dmaengine_queue_bh_work(&chan->bh_work);
}
EXPORT_SYMBOL_GPL(dmaengine_schedule_bh);
@@ -1487,7 +1513,7 @@ void dmaengine_kill_bh(struct dma_chan *chan)
if (!chan->bh_work_initialized)
return;
- tasklet_kill(&chan->bh_tasklet);
+ dmaengine_flush_bh_work(&chan->bh_work);
}
EXPORT_SYMBOL_GPL(dmaengine_kill_bh);
@@ -1687,15 +1713,28 @@ EXPORT_SYMBOL_GPL(dma_run_dependencies);
static int __init dma_bus_init(void)
{
- int err = dmaengine_init_unmap_pool();
+ int err;
+ dmaengine_bh_wq = alloc_workqueue("dmaengine_bh",
+ WQ_BH | WQ_PERCPU, 0);
+ if (!dmaengine_bh_wq)
+ return -ENOMEM;
+
+ err = dmaengine_init_unmap_pool();
if (err)
- return err;
+ goto err_destroy_wq;
err = class_register(&dma_devclass);
- if (!err)
- dmaengine_debugfs_init();
+ if (err)
+ goto err_destroy_pool;
+ dmaengine_debugfs_init();
+ return 0;
+
+err_destroy_pool:
+ dmaengine_destroy_unmap_pool();
+err_destroy_wq:
+ dmaengine_destroy_bh_wq();
return err;
}
arch_initcall(dma_bus_init);
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index b0d69e3083d6..a3f3dad8b91c 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -12,7 +12,7 @@
#include <linux/scatterlist.h>
#include <linux/bitmap.h>
#include <linux/types.h>
-#include <linux/interrupt.h>
+#include <linux/workqueue.h>
#include <asm/page.h>
/**
@@ -342,9 +342,9 @@ struct dma_router {
* @router: pointer to the DMA router structure
* @route_data: channel specific data for the router
* @private: private data for certain client-channel associations
- * @bh_tasklet: bottom-half tasklet stored per-channel
- * @bh_work_fn: callback executed when @bh_tasklet runs
- * @bh_work_initialized: indicates whether @bh_tasklet has been initialized
+ * @bh_work: bottom-half work item stored per-channel
+ * @bh_work_fn: callback executed when @bh_work runs
+ * @bh_work_initialized: indicates whether @bh_work has been initialized
*/
struct dma_chan {
struct dma_device *device;
@@ -383,7 +383,7 @@ struct dma_chan {
void *route_data;
void *private;
- struct tasklet_struct bh_tasklet;
+ struct work_struct bh_work;
dmaengine_bh_work_fn bh_work_fn;
bool bh_work_initialized;
};
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 03/33] dmaengine: apple-admac: use dmaengine BH callback
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 01/33] dmaengine: add tasklet-backed channel BH helpers Allen Pais
2026-11-10 13:13 ` [PATCH v4 02/33] dmaengine: back channel BH helpers with WQ_BH Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 04/33] dmaengine: at_xdmac: move irq bottom half to dmaengine BH Allen Pais
` (29 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Sven Peter, Janne Grunau, Neal Gompa, Frank Li, asahi,
linux-arm-kernel
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/apple-admac.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/apple-admac.c b/drivers/dma/apple-admac.c
index 4531a301c018..2eb863d1bb58 100644
--- a/drivers/dma/apple-admac.c
+++ b/drivers/dma/apple-admac.c
@@ -97,7 +97,6 @@ struct admac_chan {
unsigned int no;
struct admac_data *host;
struct dma_chan chan;
- struct tasklet_struct tasklet;
u32 carveout;
@@ -530,10 +529,7 @@ static int admac_terminate_all(struct dma_chan *chan)
list_add_tail(&adchan->current_tx->node, &adchan->to_free);
adchan->current_tx = NULL;
}
- /*
- * Descriptors can only be freed after the tasklet
- * has been killed (in admac_synchronize).
- */
+ /* Descriptors are released once the BH is flushed in admac_synchronize */
list_splice_tail_init(&adchan->submitted, &adchan->to_free);
list_splice_tail_init(&adchan->issued, &adchan->to_free);
spin_unlock_irqrestore(&adchan->lock, flags);
@@ -552,7 +548,7 @@ static void admac_synchronize(struct dma_chan *chan)
list_splice_tail_init(&adchan->to_free, &head);
spin_unlock_irqrestore(&adchan->lock, flags);
- tasklet_kill(&adchan->tasklet);
+ dmaengine_kill_bh(&adchan->chan);
list_for_each_entry_safe(adtx, _adtx, &head, node) {
list_del(&adtx->node);
@@ -671,7 +667,7 @@ static void admac_handle_status_desc_done(struct admac_data *ad, int channo)
tx->reclaimed_pos %= 2 * tx->buf_len;
admac_cyclic_write_desc(ad, channo, tx);
- tasklet_schedule(&adchan->tasklet);
+ dmaengine_schedule_bh(&adchan->chan);
}
spin_unlock_irqrestore(&adchan->lock, flags);
}
@@ -721,9 +717,9 @@ static irqreturn_t admac_interrupt(int irq, void *devid)
return IRQ_HANDLED;
}
-static void admac_chan_tasklet(struct tasklet_struct *t)
+static void admac_chan_bh(struct dma_chan *chan)
{
- struct admac_chan *adchan = from_tasklet(adchan, t, tasklet);
+ struct admac_chan *adchan = to_admac_chan(chan);
struct admac_tx *adtx;
struct dmaengine_desc_callback cb;
struct dmaengine_result tx_result;
@@ -909,7 +905,7 @@ static int admac_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&adchan->issued);
INIT_LIST_HEAD(&adchan->to_free);
list_add_tail(&adchan->chan.device_node, &dma->channels);
- tasklet_setup(&adchan->tasklet, admac_chan_tasklet);
+ dmaengine_init_bh(&adchan->chan, admac_chan_bh);
}
err = reset_control_reset(ad->rstc);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 04/33] dmaengine: at_xdmac: move irq bottom half to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (2 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 03/33] dmaengine: apple-admac: use dmaengine BH callback Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 05/33] dmaengine: ep93xx: hook callbacks via " Allen Pais
` (28 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Ludovic Desroches, Frank Li, linux-arm-kernel
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/at_xdmac.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index ef0dfe9d37ba..ae2ff7c0bd56 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -228,7 +228,6 @@ struct at_xdmac_chan {
u32 save_cndc;
u32 irq_status;
unsigned long status;
- struct tasklet_struct tasklet;
struct dma_slave_config sconfig;
spinlock_t lock;
@@ -1754,9 +1753,9 @@ static void at_xdmac_handle_error(struct at_xdmac_chan *atchan)
/* Then continue with usual descriptor management */
}
-static void at_xdmac_tasklet(struct tasklet_struct *t)
+static void at_xdmac_tasklet(struct dma_chan *chan)
{
- struct at_xdmac_chan *atchan = from_tasklet(atchan, t, tasklet);
+ struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device);
struct at_xdmac_desc *desc;
struct dma_async_tx_descriptor *txd;
@@ -1860,7 +1859,7 @@ static irqreturn_t at_xdmac_interrupt(int irq, void *dev_id)
if (atchan->irq_status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS))
at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask);
- tasklet_schedule(&atchan->tasklet);
+ dmaengine_schedule_bh(&atchan->chan);
ret = IRQ_HANDLED;
}
@@ -2310,7 +2309,7 @@ static int at_xdmac_probe(struct platform_device *pdev)
return PTR_ERR(atxdmac->clk);
}
- /* Do not use dev res to prevent races with tasklet */
+ /* Do not use devm resources to prevent races with the BH worker */
ret = request_irq(atxdmac->irq, at_xdmac_interrupt, 0, "at_xdmac", atxdmac);
if (ret) {
dev_err(&pdev->dev, "can't request irq\n");
@@ -2390,7 +2389,7 @@ static int at_xdmac_probe(struct platform_device *pdev)
spin_lock_init(&atchan->lock);
INIT_LIST_HEAD(&atchan->xfers_list);
INIT_LIST_HEAD(&atchan->free_descs_list);
- tasklet_setup(&atchan->tasklet, at_xdmac_tasklet);
+ dmaengine_init_bh(&atchan->chan, at_xdmac_tasklet);
/* Clear pending interrupts. */
while (at_xdmac_chan_read(atchan, AT_XDMAC_CIS))
@@ -2451,7 +2450,7 @@ static void at_xdmac_remove(struct platform_device *pdev)
for (i = 0; i < atxdmac->dma.chancnt; i++) {
struct at_xdmac_chan *atchan = &atxdmac->chan[i];
- tasklet_kill(&atchan->tasklet);
+ dmaengine_kill_bh(&atchan->chan);
at_xdmac_free_chan_resources(&atchan->chan);
}
}
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 05/33] dmaengine: ep93xx: hook callbacks via dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (3 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 04/33] dmaengine: at_xdmac: move irq bottom half to dmaengine BH Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 06/33] dmaengine: fsldma: migrate tasklet to " Allen Pais
` (27 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/ep93xx_dma.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index 8a0b66a7d886..b073d9164e83 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -167,7 +167,6 @@ struct ep93xx_dma_chan_cfg {
* @dma_cfg: channel number, direction
* @irq: interrupt number of the channel
* @clk: clock used by this channel
- * @tasklet: channel specific tasklet used for callbacks
* @lock: lock protecting the fields following
* @flags: flags for the channel
* @buffer: which buffer to use next (0/1)
@@ -196,7 +195,6 @@ struct ep93xx_dma_chan {
struct ep93xx_dma_chan_cfg dma_cfg;
int irq;
struct clk *clk;
- struct tasklet_struct tasklet;
/* protects the fields following */
spinlock_t lock;
unsigned long flags;
@@ -796,9 +794,9 @@ static void ep93xx_dma_advance_work(struct ep93xx_dma_chan *edmac)
spin_unlock_irqrestore(&edmac->lock, flags);
}
-static void ep93xx_dma_tasklet(struct tasklet_struct *t)
+static void ep93xx_dma_tasklet(struct dma_chan *chan)
{
- struct ep93xx_dma_chan *edmac = from_tasklet(edmac, t, tasklet);
+ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
struct ep93xx_dma_desc *desc, *d;
struct dmaengine_desc_callback cb;
LIST_HEAD(list);
@@ -853,12 +851,12 @@ static irqreturn_t ep93xx_dma_interrupt(int irq, void *dev_id)
switch (edmac->edma->hw_interrupt(edmac)) {
case INTERRUPT_DONE:
desc->complete = true;
- tasklet_schedule(&edmac->tasklet);
+ dmaengine_schedule_bh(&edmac->chan);
break;
case INTERRUPT_NEXT_BUFFER:
if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags))
- tasklet_schedule(&edmac->tasklet);
+ dmaengine_schedule_bh(&edmac->chan);
break;
default:
@@ -1414,7 +1412,7 @@ static struct ep93xx_dma_engine *ep93xx_dma_of_probe(struct platform_device *pde
INIT_LIST_HEAD(&edmac->active);
INIT_LIST_HEAD(&edmac->queue);
INIT_LIST_HEAD(&edmac->free_list);
- tasklet_setup(&edmac->tasklet, ep93xx_dma_tasklet);
+ dmaengine_init_bh(&edmac->chan, ep93xx_dma_tasklet);
list_add_tail(&edmac->chan.device_node,
&dma_dev->channels);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 06/33] dmaengine: fsldma: migrate tasklet to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (4 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 05/33] dmaengine: ep93xx: hook callbacks via " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 07/33] dmaengine: fsl_raid: run completions via " Allen Pais
` (26 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Zhang Wei, Frank Li, linuxppc-dev
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/fsldma.c | 12 ++++++------
drivers/dma/fsldma.h | 1 -
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 99a878ed1444..f069d1bbff17 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -970,18 +970,18 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data)
chan_err(chan, "irq: unhandled sr 0x%08x\n", stat);
/*
- * Schedule the tasklet to handle all cleanup of the current
+ * Queue the BH worker to handle all cleanup of the current
* transaction. It will start a new transaction if there is
* one pending.
*/
- tasklet_schedule(&chan->tasklet);
+ dmaengine_schedule_bh(&chan->common);
chan_dbg(chan, "irq: Exit\n");
return IRQ_HANDLED;
}
-static void dma_do_tasklet(struct tasklet_struct *t)
+static void dma_do_tasklet(struct dma_chan *c)
{
- struct fsldma_chan *chan = from_tasklet(chan, t, tasklet);
+ struct fsldma_chan *chan = to_fsl_chan(c);
chan_dbg(chan, "tasklet entry\n");
@@ -1154,7 +1154,7 @@ static int fsl_dma_chan_probe(struct fsldma_device *fdev,
}
fdev->chan[chan->id] = chan;
- tasklet_setup(&chan->tasklet, dma_do_tasklet);
+ dmaengine_init_bh(&chan->common, dma_do_tasklet);
snprintf(chan->name, sizeof(chan->name), "chan%d", chan->id);
/* Initialize the channel */
@@ -1207,7 +1207,7 @@ static int fsl_dma_chan_probe(struct fsldma_device *fdev,
static void fsl_dma_chan_remove(struct fsldma_chan *chan)
{
- tasklet_kill(&chan->tasklet);
+ dmaengine_kill_bh(&chan->common);
irq_dispose_mapping(chan->irq);
list_del(&chan->common.device_node);
iounmap(chan->regs);
diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h
index d7b7a3138b85..cab33d010d51 100644
--- a/drivers/dma/fsldma.h
+++ b/drivers/dma/fsldma.h
@@ -173,7 +173,6 @@ struct fsldma_chan {
struct device *dev; /* Channel device */
int irq; /* Channel IRQ */
int id; /* Raw id of this channel */
- struct tasklet_struct tasklet;
u32 feature;
bool idle; /* DMA controller is idle */
#ifdef CONFIG_PM
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 07/33] dmaengine: fsl_raid: run completions via dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (5 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 06/33] dmaengine: fsldma: migrate tasklet to " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 08/33] dmaengine: imx-dma: flip per-chan tasklet to " Allen Pais
` (25 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
drivers/dma/fsl_raid.c | 11 ++++++-----
drivers/dma/fsl_raid.h | 1 -
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/fsl_raid.c b/drivers/dma/fsl_raid.c
index fbfd3b6fa586..26c6c84619ec 100644
--- a/drivers/dma/fsl_raid.c
+++ b/drivers/dma/fsl_raid.c
@@ -155,9 +155,10 @@ static void fsl_re_cleanup_descs(struct fsl_re_chan *re_chan)
fsl_re_issue_pending(&re_chan->chan);
}
-static void fsl_re_dequeue(struct tasklet_struct *t)
+static void fsl_re_dequeue(struct dma_chan *chan)
{
- struct fsl_re_chan *re_chan = from_tasklet(re_chan, t, irqtask);
+ struct fsl_re_chan *re_chan = container_of(chan, struct fsl_re_chan,
+ chan);
struct fsl_re_desc *desc, *_desc;
struct fsl_re_hw_desc *hwdesc;
unsigned long flags;
@@ -235,7 +236,7 @@ static irqreturn_t fsl_re_isr(int irq, void *data)
/* Clear interrupt */
out_be32(&re_chan->jrregs->jr_interrupt_status, FSL_RE_CLR_INTR);
- tasklet_schedule(&re_chan->irqtask);
+ dmaengine_schedule_bh(&re_chan->chan);
return IRQ_HANDLED;
}
@@ -678,7 +679,7 @@ static int fsl_re_chan_probe(struct platform_device *ofdev,
snprintf(chan->name, sizeof(chan->name), "re_jr%02d", q);
chandev = &chan_ofdev->dev;
- tasklet_setup(&chan->irqtask, fsl_re_dequeue);
+ dmaengine_init_bh(&chan->chan, fsl_re_dequeue);
ret = request_irq(chan->irq, fsl_re_isr, 0, chan->name, chandev);
if (ret) {
@@ -852,7 +853,7 @@ static int fsl_re_probe(struct platform_device *ofdev)
static void fsl_re_remove_chan(struct fsl_re_chan *chan)
{
- tasklet_kill(&chan->irqtask);
+ dmaengine_kill_bh(&chan->chan);
dma_pool_free(chan->re_dev->hw_desc_pool, chan->inb_ring_virt_addr,
chan->inb_phys_addr);
diff --git a/drivers/dma/fsl_raid.h b/drivers/dma/fsl_raid.h
index adbfede330a7..3bc11b12fa37 100644
--- a/drivers/dma/fsl_raid.h
+++ b/drivers/dma/fsl_raid.h
@@ -275,7 +275,6 @@ struct fsl_re_chan {
struct dma_chan chan;
struct fsl_re_chan_cfg __iomem *jrregs;
int irq;
- struct tasklet_struct irqtask;
u32 alloc_count;
/* hw descriptor ring for inbound queue*/
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 08/33] dmaengine: imx-dma: flip per-chan tasklet to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (6 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 07/33] dmaengine: fsl_raid: run completions via " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 09/33] dmaengine: ioat: convert cleanup " Allen Pais
` (24 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
imx, linux-arm-kernel
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/imx-dma.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index ca1d08e90a06..a9d05b2e2205 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -145,7 +145,6 @@ struct imxdma_channel {
struct imxdma_engine *imxdma;
unsigned int channel;
- struct tasklet_struct dma_tasklet;
struct list_head ld_free;
struct list_head ld_queue;
struct list_head ld_active;
@@ -344,8 +343,8 @@ static void imxdma_watchdog(struct timer_list *t)
imx_dmav1_writel(imxdma, 0, DMA_CCR(channel));
- /* Tasklet watchdog error handler */
- tasklet_schedule(&imxdmac->dma_tasklet);
+ /* BH watchdog error handler */
+ dmaengine_schedule_bh(&imxdmac->chan);
dev_dbg(imxdma->dev, "channel %d: watchdog timeout!\n",
imxdmac->channel);
}
@@ -390,8 +389,8 @@ static irqreturn_t imxdma_err_handler(int irq, void *dev_id)
imx_dmav1_writel(imxdma, 1 << i, DMA_DBOSR);
errcode |= IMX_DMA_ERR_BUFFER;
}
- /* Tasklet error handler */
- tasklet_schedule(&imxdma->channel[i].dma_tasklet);
+ /* BH error handler */
+ dmaengine_schedule_bh(&imxdma->channel[i].chan);
dev_warn(imxdma->dev,
"DMA timeout on channel %d -%s%s%s%s\n", i,
@@ -448,8 +447,8 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac)
imx_dmav1_writel(imxdma, tmp, DMA_CCR(chno));
if (imxdma_chan_is_doing_cyclic(imxdmac))
- /* Tasklet progression */
- tasklet_schedule(&imxdmac->dma_tasklet);
+ /* BH progression */
+ dmaengine_schedule_bh(&imxdmac->chan);
return;
}
@@ -462,8 +461,8 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac)
out:
imx_dmav1_writel(imxdma, 0, DMA_CCR(chno));
- /* Tasklet irq */
- tasklet_schedule(&imxdmac->dma_tasklet);
+ /* Schedule the IRQ BH */
+ dmaengine_schedule_bh(&imxdmac->chan);
}
static irqreturn_t dma_irq_handler(int irq, void *dev_id)
@@ -592,9 +591,10 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
return 0;
}
-static void imxdma_tasklet(struct tasklet_struct *t)
+static void imxdma_tasklet(struct dma_chan *chan)
{
- struct imxdma_channel *imxdmac = from_tasklet(imxdmac, t, dma_tasklet);
+ struct imxdma_channel *imxdmac = container_of(chan, struct imxdma_channel,
+ chan);
struct imxdma_engine *imxdma = imxdmac->imxdma;
struct imxdma_desc *desc, *next_desc;
unsigned long flags;
@@ -1142,7 +1142,7 @@ static int __init imxdma_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&imxdmac->ld_free);
INIT_LIST_HEAD(&imxdmac->ld_active);
- tasklet_setup(&imxdmac->dma_tasklet, imxdma_tasklet);
+ dmaengine_init_bh(&imxdmac->chan, imxdma_tasklet);
imxdmac->chan.device = &imxdma->dma_device;
dma_cookie_init(&imxdmac->chan);
imxdmac->channel = i;
@@ -1211,7 +1211,7 @@ static void imxdma_free_irq(struct platform_device *pdev, struct imxdma_engine *
if (!is_imx1_dma(imxdma))
disable_irq(imxdmac->irq);
- tasklet_kill(&imxdmac->dma_tasklet);
+ dmaengine_kill_bh(&imxdmac->chan);
}
}
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 09/33] dmaengine: ioat: convert cleanup tasklet to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (7 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 08/33] dmaengine: imx-dma: flip per-chan tasklet to " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 10/33] dmaengine: mmp_pdma: replace per-chan tasklet with " Allen Pais
` (23 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Dave Jiang, Frank Li, Kees Cook, Thomas Weißschuh,
Giovanni Cabiddu, Bjorn Helgaas, Lukas Wunner
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Acked-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/ioat/dma.c | 14 +++++++-------
drivers/dma/ioat/dma.h | 3 +--
drivers/dma/ioat/init.c | 2 +-
3 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c
index ee93b029f9e3..c7a63dbac6e2 100644
--- a/drivers/dma/ioat/dma.c
+++ b/drivers/dma/ioat/dma.c
@@ -110,7 +110,7 @@ irqreturn_t ioat_dma_do_interrupt(int irq, void *data)
for_each_set_bit(bit, &attnstatus, BITS_PER_LONG) {
ioat_chan = ioat_chan_by_index(instance, bit);
if (test_bit(IOAT_RUN, &ioat_chan->state))
- tasklet_schedule(&ioat_chan->cleanup_task);
+ dmaengine_schedule_bh(&ioat_chan->dma_chan);
}
writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET);
@@ -127,7 +127,7 @@ irqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data)
struct ioatdma_chan *ioat_chan = data;
if (test_bit(IOAT_RUN, &ioat_chan->state))
- tasklet_schedule(&ioat_chan->cleanup_task);
+ dmaengine_schedule_bh(&ioat_chan->dma_chan);
return IRQ_HANDLED;
}
@@ -161,11 +161,11 @@ void ioat_stop(struct ioatdma_chan *ioat_chan)
/* flush inflight timers */
timer_delete_sync(&ioat_chan->timer);
- /* flush inflight tasklet runs */
- tasklet_kill(&ioat_chan->cleanup_task);
+ /* flush inflight BH runs */
+ dmaengine_kill_bh(&ioat_chan->dma_chan);
/* final cleanup now that everything is quiesced and can't re-arm */
- ioat_cleanup_event(&ioat_chan->cleanup_task);
+ ioat_cleanup_event(&ioat_chan->dma_chan);
}
static void __ioat_issue_pending(struct ioatdma_chan *ioat_chan)
@@ -690,9 +690,9 @@ static void ioat_cleanup(struct ioatdma_chan *ioat_chan)
spin_unlock_bh(&ioat_chan->cleanup_lock);
}
-void ioat_cleanup_event(struct tasklet_struct *t)
+void ioat_cleanup_event(struct dma_chan *c)
{
- struct ioatdma_chan *ioat_chan = from_tasklet(ioat_chan, t, cleanup_task);
+ struct ioatdma_chan *ioat_chan = to_ioat_chan(c);
ioat_cleanup(ioat_chan);
if (!test_bit(IOAT_RUN, &ioat_chan->state))
diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h
index e8a880f338c6..4bdb1ae937e4 100644
--- a/drivers/dma/ioat/dma.h
+++ b/drivers/dma/ioat/dma.h
@@ -111,7 +111,6 @@ struct ioatdma_chan {
struct ioatdma_device *ioat_dma;
dma_addr_t completion_dma;
u64 *completion;
- struct tasklet_struct cleanup_task;
struct kobject kobj;
/* ioat v2 / v3 channel attributes
@@ -385,7 +384,7 @@ int ioat_reset_hw(struct ioatdma_chan *ioat_chan);
enum dma_status
ioat_tx_status(struct dma_chan *c, dma_cookie_t cookie,
struct dma_tx_state *txstate);
-void ioat_cleanup_event(struct tasklet_struct *t);
+void ioat_cleanup_event(struct dma_chan *c);
void ioat_timer_event(struct timer_list *t);
int ioat_check_space_lock(struct ioatdma_chan *ioat_chan, int num_descs);
void ioat_issue_pending(struct dma_chan *chan);
diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c
index a57024c4b066..ab5068d60110 100644
--- a/drivers/dma/ioat/init.c
+++ b/drivers/dma/ioat/init.c
@@ -765,7 +765,7 @@ ioat_init_channel(struct ioatdma_device *ioat_dma,
list_add_tail(&ioat_chan->dma_chan.device_node, &dma->channels);
ioat_dma->idx[idx] = ioat_chan;
timer_setup(&ioat_chan->timer, ioat_timer_event, 0);
- tasklet_setup(&ioat_chan->cleanup_task, ioat_cleanup_event);
+ dmaengine_init_bh(&ioat_chan->dma_chan, ioat_cleanup_event);
}
#define IOAT_NUM_SRC_TEST 6 /* must be <= 8 */
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 10/33] dmaengine: mmp_pdma: replace per-chan tasklet with dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (8 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 09/33] dmaengine: ioat: convert cleanup " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 11/33] dmaengine: mmp_tdma: hook completions to " Allen Pais
` (22 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/mmp_pdma.c | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c
index 6045d6d1cc79..abea70dea890 100644
--- a/drivers/dma/mmp_pdma.c
+++ b/drivers/dma/mmp_pdma.c
@@ -115,7 +115,6 @@ struct mmp_pdma_chan {
* is in cyclic mode */
/* channel's basic info */
- struct tasklet_struct tasklet;
u32 dcmd;
u32 drcmr;
u32 dev_addr;
@@ -373,7 +372,7 @@ static irqreturn_t mmp_pdma_chan_handler(int irq, void *dev_id)
if (clear_chan_irq(phy) != 0)
return IRQ_NONE;
- tasklet_schedule(&phy->vchan->tasklet);
+ dmaengine_schedule_bh(&phy->vchan->chan);
return IRQ_HANDLED;
}
@@ -1045,13 +1044,11 @@ static void mmp_pdma_issue_pending(struct dma_chan *dchan)
}
/*
- * dma_do_tasklet
- * Do call back
- * Start pending list
+ * dma_do_tasklet - BH worker to process completions and kick pending work
*/
-static void dma_do_tasklet(struct tasklet_struct *t)
+static void dma_do_tasklet(struct dma_chan *c)
{
- struct mmp_pdma_chan *chan = from_tasklet(chan, t, tasklet);
+ struct mmp_pdma_chan *chan = container_of(c, struct mmp_pdma_chan, chan);
struct mmp_pdma_desc_sw *desc, *_desc;
LIST_HEAD(chain_cleanup);
unsigned long flags;
@@ -1168,7 +1165,7 @@ static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev, int idx, int irq)
spin_lock_init(&chan->desc_lock);
chan->dev = pdev->dev;
chan->chan.device = &pdev->device;
- tasklet_setup(&chan->tasklet, dma_do_tasklet);
+ dmaengine_init_bh(&chan->chan, dma_do_tasklet);
INIT_LIST_HEAD(&chan->chain_pending);
INIT_LIST_HEAD(&chan->chain_running);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 11/33] dmaengine: mmp_tdma: hook completions to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (9 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 10/33] dmaengine: mmp_pdma: replace per-chan tasklet with " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 12/33] dmaengine: mv_xor: convert irq tasklet " Allen Pais
` (21 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/mmp_tdma.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c
index d7dd58970060..873b383e405a 100644
--- a/drivers/dma/mmp_tdma.c
+++ b/drivers/dma/mmp_tdma.c
@@ -102,7 +102,6 @@ struct mmp_tdma_chan {
struct device *dev;
struct dma_chan chan;
struct dma_async_tx_descriptor desc;
- struct tasklet_struct tasklet;
struct mmp_tdma_desc *desc_arr;
dma_addr_t desc_arr_phys;
@@ -320,7 +319,7 @@ static irqreturn_t mmp_tdma_chan_handler(int irq, void *dev_id)
struct mmp_tdma_chan *tdmac = dev_id;
if (mmp_tdma_clear_chan_irq(tdmac) == 0) {
- tasklet_schedule(&tdmac->tasklet);
+ dmaengine_schedule_bh(&tdmac->chan);
return IRQ_HANDLED;
} else
return IRQ_NONE;
@@ -346,9 +345,9 @@ static irqreturn_t mmp_tdma_int_handler(int irq, void *dev_id)
return IRQ_NONE;
}
-static void dma_do_tasklet(struct tasklet_struct *t)
+static void dma_do_tasklet(struct dma_chan *c)
{
- struct mmp_tdma_chan *tdmac = from_tasklet(tdmac, t, tasklet);
+ struct mmp_tdma_chan *tdmac = container_of(c, struct mmp_tdma_chan, chan);
dmaengine_desc_get_callback_invoke(&tdmac->desc, NULL);
}
@@ -583,7 +582,7 @@ static int mmp_tdma_chan_init(struct mmp_tdma_device *tdev,
tdmac->pool = pool;
tdmac->status = DMA_COMPLETE;
tdev->tdmac[tdmac->idx] = tdmac;
- tasklet_setup(&tdmac->tasklet, dma_do_tasklet);
+ dmaengine_init_bh(&tdmac->chan, dma_do_tasklet);
/* add the channel to tdma_chan list */
list_add_tail(&tdmac->chan.device_node,
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 12/33] dmaengine: mv_xor: convert irq tasklet to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (10 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 11/33] dmaengine: mmp_tdma: hook completions to " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 13/33] dmaengine: mxs-dma: use dmaengine BH scheduling Allen Pais
` (20 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/mv_xor.c | 14 +++++++-------
drivers/dma/mv_xor.h | 2 --
2 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 92fb996f5b3f..41fe35b3aab0 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -327,7 +327,7 @@ static void mv_chan_slot_cleanup(struct mv_xor_chan *mv_chan)
* some descriptors are still waiting
* to be cleaned
*/
- tasklet_schedule(&mv_chan->irq_tasklet);
+ dmaengine_schedule_bh(&mv_chan->dmachan);
}
}
}
@@ -336,9 +336,9 @@ static void mv_chan_slot_cleanup(struct mv_xor_chan *mv_chan)
mv_chan->dmachan.completed_cookie = cookie;
}
-static void mv_xor_tasklet(struct tasklet_struct *t)
+static void mv_xor_tasklet(struct dma_chan *c)
{
- struct mv_xor_chan *chan = from_tasklet(chan, t, irq_tasklet);
+ struct mv_xor_chan *chan = to_mv_xor_chan(c);
spin_lock(&chan->lock);
mv_chan_slot_cleanup(chan);
@@ -372,7 +372,7 @@ mv_chan_alloc_slot(struct mv_xor_chan *mv_chan)
spin_unlock_bh(&mv_chan->lock);
/* try to free some slots if the allocation fails */
- tasklet_schedule(&mv_chan->irq_tasklet);
+ dmaengine_schedule_bh(&mv_chan->dmachan);
return NULL;
}
@@ -742,7 +742,7 @@ static irqreturn_t mv_xor_interrupt_handler(int irq, void *data)
if (intr_cause & XOR_INTR_ERRORS)
mv_chan_err_interrupt_handler(chan, intr_cause);
- tasklet_schedule(&chan->irq_tasklet);
+ dmaengine_schedule_bh(&chan->dmachan);
mv_chan_clear_eoc_cause(chan);
@@ -1112,7 +1112,7 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
mv_chan->mmr_base = xordev->xor_base;
mv_chan->mmr_high_base = xordev->xor_high_base;
- tasklet_setup(&mv_chan->irq_tasklet, mv_xor_tasklet);
+ dmaengine_init_bh(&mv_chan->dmachan, mv_xor_tasklet);
/* clear errors before enabling interrupts */
mv_chan_clear_err_status(mv_chan);
@@ -1167,7 +1167,7 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
err_free_irq:
free_irq(mv_chan->irq, mv_chan);
- tasklet_kill(&mv_chan->irq_tasklet);
+ dmaengine_kill_bh(&mv_chan->dmachan);
err_free_dma:
dma_free_wc(&pdev->dev, MV_XOR_POOL_SIZE,
mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h
index c87cefd38a07..27774abe34a6 100644
--- a/drivers/dma/mv_xor.h
+++ b/drivers/dma/mv_xor.h
@@ -98,7 +98,6 @@ struct mv_xor_device {
* @device: parent device
* @common: common dmaengine channel object members
* @slots_allocated: records the actual size of the descriptor slot pool
- * @irq_tasklet: bottom half where mv_xor_slot_cleanup runs
* @op_in_desc: new mode of driver, each op is written to descriptor.
*/
struct mv_xor_chan {
@@ -118,7 +117,6 @@ struct mv_xor_chan {
struct dma_device dmadev;
struct dma_chan dmachan;
int slots_allocated;
- struct tasklet_struct irq_tasklet;
int op_in_desc;
char dummy_src[MV_XOR_MIN_BYTE_COUNT];
char dummy_dst[MV_XOR_MIN_BYTE_COUNT];
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 13/33] dmaengine: mxs-dma: use dmaengine BH scheduling
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (11 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 12/33] dmaengine: mv_xor: convert irq tasklet " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 14/33] dmaengine: nbpfaxi: switch callbacks to dmaengine BH Allen Pais
` (19 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
imx, linux-arm-kernel
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/mxs-dma.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
index cfe4d759d65f..3dfd56a53414 100644
--- a/drivers/dma/mxs-dma.c
+++ b/drivers/dma/mxs-dma.c
@@ -109,7 +109,6 @@ struct mxs_dma_chan {
struct mxs_dma_engine *mxs_dma;
struct dma_chan chan;
struct dma_async_tx_descriptor desc;
- struct tasklet_struct tasklet;
unsigned int chan_irq;
struct mxs_dma_ccw *ccw;
dma_addr_t ccw_phys;
@@ -300,9 +299,9 @@ static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx)
return dma_cookie_assign(tx);
}
-static void mxs_dma_tasklet(struct tasklet_struct *t)
+static void mxs_dma_tasklet(struct dma_chan *chan)
{
- struct mxs_dma_chan *mxs_chan = from_tasklet(mxs_chan, t, tasklet);
+ struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
dmaengine_desc_get_callback_invoke(&mxs_chan->desc, NULL);
}
@@ -386,8 +385,8 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id)
dma_cookie_complete(&mxs_chan->desc);
}
- /* schedule tasklet on this channel */
- tasklet_schedule(&mxs_chan->tasklet);
+ /* schedule BH on this channel */
+ dmaengine_schedule_bh(&mxs_chan->chan);
return IRQ_HANDLED;
}
@@ -781,7 +780,7 @@ static int mxs_dma_probe(struct platform_device *pdev)
mxs_chan->chan.device = &mxs_dma->dma_device;
dma_cookie_init(&mxs_chan->chan);
- tasklet_setup(&mxs_chan->tasklet, mxs_dma_tasklet);
+ dmaengine_init_bh(&mxs_chan->chan, mxs_dma_tasklet);
/* Add the channel to mxs_chan list */
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 14/33] dmaengine: nbpfaxi: switch callbacks to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (12 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 13/33] dmaengine: mxs-dma: use dmaengine BH scheduling Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 15/33] dmaengine: pch_dma: convert tasklet " Allen Pais
` (18 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
drivers/dma/nbpfaxi.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c
index c9010938c30f..80e607065e8e 100644
--- a/drivers/dma/nbpfaxi.c
+++ b/drivers/dma/nbpfaxi.c
@@ -174,7 +174,6 @@ struct nbpf_desc_page {
/**
* struct nbpf_channel - one DMAC channel
* @dma_chan: standard dmaengine channel object
- * @tasklet: channel specific tasklet used for callbacks
* @base: register address base
* @nbpf: DMAC
* @name: IRQ name
@@ -200,7 +199,6 @@ struct nbpf_desc_page {
*/
struct nbpf_channel {
struct dma_chan dma_chan;
- struct tasklet_struct tasklet;
void __iomem *base;
struct nbpf_device *nbpf;
char name[16];
@@ -1125,9 +1123,10 @@ static struct dma_chan *nbpf_of_xlate(struct of_phandle_args *dma_spec,
return dchan;
}
-static void nbpf_chan_tasklet(struct tasklet_struct *t)
+static void nbpf_chan_tasklet(struct dma_chan *c)
{
- struct nbpf_channel *chan = from_tasklet(chan, t, tasklet);
+ struct nbpf_channel *chan = container_of(c, struct nbpf_channel,
+ dma_chan);
struct nbpf_desc *desc, *tmp;
struct dmaengine_desc_callback cb;
@@ -1228,7 +1227,7 @@ static irqreturn_t nbpf_chan_irq(int irq, void *dev)
spin_unlock(&chan->lock);
if (bh)
- tasklet_schedule(&chan->tasklet);
+ dmaengine_schedule_bh(&chan->dma_chan);
return ret;
}
@@ -1272,7 +1271,7 @@ static int nbpf_chan_probe(struct nbpf_device *nbpf, int n)
snprintf(chan->name, sizeof(chan->name), "nbpf %d", n);
- tasklet_setup(&chan->tasklet, nbpf_chan_tasklet);
+ dmaengine_init_bh(&chan->dma_chan, nbpf_chan_tasklet);
ret = devm_request_irq(dma_dev->dev, chan->irq,
nbpf_chan_irq, IRQF_SHARED,
chan->name, chan);
@@ -1477,7 +1476,7 @@ static void nbpf_remove(struct platform_device *pdev)
devm_free_irq(&pdev->dev, chan->irq, chan);
- tasklet_kill(&chan->tasklet);
+ dmaengine_kill_bh(&chan->dma_chan);
}
of_dma_controller_free(pdev->dev.of_node);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 15/33] dmaengine: pch_dma: convert tasklet to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (13 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 14/33] dmaengine: nbpfaxi: switch callbacks to dmaengine BH Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 16/33] dmaengine: ppc4xx: replace irq tasklet with " Allen Pais
` (17 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/pch_dma.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c
index 88baf7ffec37..28d5e25d904b 100644
--- a/drivers/dma/pch_dma.c
+++ b/drivers/dma/pch_dma.c
@@ -91,7 +91,6 @@ struct pch_dma_chan {
struct dma_chan chan;
void __iomem *membase;
enum dma_transfer_direction dir;
- struct tasklet_struct tasklet;
unsigned long err_status;
spinlock_t lock;
@@ -660,9 +659,10 @@ static int pd_device_terminate_all(struct dma_chan *chan)
return 0;
}
-static void pdc_tasklet(struct tasklet_struct *t)
+static void pdc_tasklet(struct dma_chan *c)
{
- struct pch_dma_chan *pd_chan = from_tasklet(pd_chan, t, tasklet);
+ struct pch_dma_chan *pd_chan = container_of(c, struct pch_dma_chan,
+ chan);
unsigned long flags;
if (!pdc_is_idle(pd_chan)) {
@@ -702,7 +702,7 @@ static irqreturn_t pd_irq(int irq, void *devid)
if (sts0 & DMA_STATUS0_ERR(i))
set_bit(0, &pd_chan->err_status);
- tasklet_schedule(&pd_chan->tasklet);
+ dmaengine_schedule_bh(&pd_chan->chan);
ret0 = IRQ_HANDLED;
}
} else {
@@ -710,7 +710,7 @@ static irqreturn_t pd_irq(int irq, void *devid)
if (sts2 & DMA_STATUS2_ERR(i))
set_bit(0, &pd_chan->err_status);
- tasklet_schedule(&pd_chan->tasklet);
+ dmaengine_schedule_bh(&pd_chan->chan);
ret2 = IRQ_HANDLED;
}
}
@@ -872,7 +872,7 @@ static int pch_dma_probe(struct pci_dev *pdev,
INIT_LIST_HEAD(&pd_chan->queue);
INIT_LIST_HEAD(&pd_chan->free_list);
- tasklet_setup(&pd_chan->tasklet, pdc_tasklet);
+ dmaengine_init_bh(&pd_chan->chan, pdc_tasklet);
list_add_tail(&pd_chan->chan.device_node, &pd->dma.channels);
}
@@ -925,7 +925,7 @@ static void pch_dma_remove(struct pci_dev *pdev)
device_node) {
pd_chan = to_pd_chan(chan);
- tasklet_kill(&pd_chan->tasklet);
+ dmaengine_kill_bh(&pd_chan->chan);
}
dma_pool_destroy(pd->pool);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 16/33] dmaengine: ppc4xx: replace irq tasklet with dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (14 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 15/33] dmaengine: pch_dma: convert tasklet " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 17/33] dmaengine: ste_dma40: convert per-channel tasklet to " Allen Pais
` (16 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Frank Li, Kees Cook
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/ppc4xx/adma.c | 14 +++++++-------
drivers/dma/ppc4xx/adma.h | 2 --
2 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c
index bcc54d584e2b..8fa36d72b145 100644
--- a/drivers/dma/ppc4xx/adma.c
+++ b/drivers/dma/ppc4xx/adma.c
@@ -1658,9 +1658,9 @@ static void __ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan)
/**
* ppc440spe_adma_tasklet - clean up watch-dog initiator
*/
-static void ppc440spe_adma_tasklet(struct tasklet_struct *t)
+static void ppc440spe_adma_tasklet(struct dma_chan *c)
{
- struct ppc440spe_adma_chan *chan = from_tasklet(chan, t, irq_tasklet);
+ struct ppc440spe_adma_chan *chan = to_ppc440spe_adma_chan(c);
spin_lock_nested(&chan->lock, SINGLE_DEPTH_NESTING);
__ppc440spe_adma_slot_cleanup(chan);
@@ -1754,7 +1754,7 @@ static struct ppc440spe_adma_desc_slot *ppc440spe_adma_alloc_slots(
goto retry;
/* try to free some slots if the allocation fails */
- tasklet_schedule(&chan->irq_tasklet);
+ dmaengine_schedule_bh(&chan->common);
return NULL;
}
@@ -3595,7 +3595,7 @@ static irqreturn_t ppc440spe_adma_eot_handler(int irq, void *data)
dev_dbg(chan->device->common.dev,
"ppc440spe adma%d: %s\n", chan->device->id, __func__);
- tasklet_schedule(&chan->irq_tasklet);
+ dmaengine_schedule_bh(&chan->common);
ppc440spe_adma_device_clear_eot_status(chan);
return IRQ_HANDLED;
@@ -3612,7 +3612,7 @@ static irqreturn_t ppc440spe_adma_err_handler(int irq, void *data)
dev_dbg(chan->device->common.dev,
"ppc440spe adma%d: %s\n", chan->device->id, __func__);
- tasklet_schedule(&chan->irq_tasklet);
+ dmaengine_schedule_bh(&chan->common);
ppc440spe_adma_device_clear_eot_status(chan);
return IRQ_HANDLED;
@@ -4137,7 +4137,7 @@ static int ppc440spe_adma_probe(struct platform_device *ofdev)
chan->common.device = &adev->common;
dma_cookie_init(&chan->common);
list_add_tail(&chan->common.device_node, &adev->common.channels);
- tasklet_setup(&chan->irq_tasklet, ppc440spe_adma_tasklet);
+ dmaengine_init_bh(&chan->common, ppc440spe_adma_tasklet);
/* allocate and map helper pages for async validation or
* async_mult/async_sum_product operations on DMA0/1.
@@ -4247,7 +4247,7 @@ static void ppc440spe_adma_remove(struct platform_device *ofdev)
device_node) {
ppc440spe_chan = to_ppc440spe_adma_chan(chan);
ppc440spe_adma_release_irqs(adev, ppc440spe_chan);
- tasklet_kill(&ppc440spe_chan->irq_tasklet);
+ dmaengine_kill_bh(&ppc440spe_chan->common);
if (adev->id != PPC440SPE_XOR_ID) {
dma_unmap_page(&ofdev->dev, ppc440spe_chan->pdest,
PAGE_SIZE, DMA_BIDIRECTIONAL);
diff --git a/drivers/dma/ppc4xx/adma.h b/drivers/dma/ppc4xx/adma.h
index f8a5d7c1fb40..9af1feaf0b9a 100644
--- a/drivers/dma/ppc4xx/adma.h
+++ b/drivers/dma/ppc4xx/adma.h
@@ -80,7 +80,6 @@ struct ppc440spe_adma_device {
* @pending: allows batching of hardware operations
* @slots_allocated: records the actual size of the descriptor slot pool
* @hw_chain_inited: h/w descriptor chain initialization flag
- * @irq_tasklet: bottom half where ppc440spe_adma_slot_cleanup runs
* @needs_unmap: if buffers should not be unmapped upon final processing
* @pdest_page: P destination page for async validate operation
* @qdest_page: Q destination page for async validate operation
@@ -97,7 +96,6 @@ struct ppc440spe_adma_chan {
int pending;
int slots_allocated;
int hw_chain_inited;
- struct tasklet_struct irq_tasklet;
u8 needs_unmap;
struct page *pdest_page;
struct page *qdest_page;
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 17/33] dmaengine: ste_dma40: convert per-channel tasklet to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (15 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 16/33] dmaengine: ppc4xx: replace irq tasklet with " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 18/33] dmaengine: xgene-dma: wire descriptor cleanup " Allen Pais
` (15 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Linus Walleij, Frank Li, linux-arm-kernel
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/ste_dma40.c | 29 +++++++++++++----------------
1 file changed, 13 insertions(+), 16 deletions(-)
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index 8b89d44f9b0d..a06008d8dd27 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -461,13 +461,11 @@ struct d40_base;
* @lock: A spinlock to protect this struct.
* @log_num: The logical number, if any of this channel.
* @pending_tx: The number of pending transfers. Used between interrupt handler
- * and tasklet.
+ * and bottom half.
* @busy: Set to true when transfer is ongoing on this channel.
* @phy_chan: Pointer to physical channel which this instance runs on. If this
* point is NULL, then the channel is not allocated.
* @chan: DMA engine handle.
- * @tasklet: Tasklet that gets scheduled from interrupt context to complete a
- * transfer and call client callback.
* @client: Cliented owned descriptor list.
* @pending_queue: Submitted jobs, to be issued by issue_pending()
* @active: Active descriptor.
@@ -494,7 +492,6 @@ struct d40_chan {
bool busy;
struct d40_phy_res *phy_chan;
struct dma_chan chan;
- struct tasklet_struct tasklet;
struct list_head client;
struct list_head pending_queue;
struct list_head active;
@@ -1646,7 +1643,7 @@ static struct d40_desc *d40_queue_start(struct d40_chan *d40c)
d40c->pending_tx++;
d40d = d40_first_queued(d40c);
} while (d40d);
- tasklet_schedule(&d40c->tasklet);
+ dmaengine_schedule_bh(&d40c->chan);
return ERR_PTR(err);
}
d40c->busy = true;
@@ -1670,7 +1667,7 @@ static struct d40_desc *d40_queue_start(struct d40_chan *d40c)
d40c->pending_tx++;
d40c->busy = false;
pm_runtime_put_autosuspend(d40c->base->dev);
- tasklet_schedule(&d40c->tasklet);
+ dmaengine_schedule_bh(&d40c->chan);
return ERR_PTR(err);
}
}
@@ -1730,13 +1727,13 @@ static void dma_tc_handle(struct d40_chan *d40c)
}
d40c->pending_tx += callbacks;
- tasklet_schedule(&d40c->tasklet);
+ dmaengine_schedule_bh(&d40c->chan);
}
-static void dma_tasklet(struct tasklet_struct *t)
+static void dma_tasklet(struct dma_chan *chan)
{
- struct d40_chan *d40c = from_tasklet(d40c, t, tasklet);
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
struct d40_desc *d40d;
unsigned long flags;
bool callback_active;
@@ -1786,7 +1783,7 @@ static void dma_tasklet(struct tasklet_struct *t)
d40c->pending_tx--;
if (d40c->pending_tx)
- tasklet_schedule(&d40c->tasklet);
+ dmaengine_schedule_bh(&d40c->chan);
spin_unlock_irqrestore(&d40c->lock, flags);
@@ -3076,14 +3073,14 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
INIT_LIST_HEAD(&d40c->client);
INIT_LIST_HEAD(&d40c->prepare_queue);
- tasklet_setup(&d40c->tasklet, dma_tasklet);
+ dmaengine_init_bh(&d40c->chan, dma_tasklet);
list_add_tail(&d40c->chan.device_node,
&dma->channels);
}
}
-static void d40_kill_tasklets(void *data)
+static void d40_kill_bh(void *data)
{
struct dma_device *dma = data;
struct d40_chan *d40c;
@@ -3091,7 +3088,7 @@ static void d40_kill_tasklets(void *data)
list_for_each_entry(chan, &dma->channels, device_node) {
d40c = container_of(chan, struct d40_chan, chan);
- tasklet_kill(&d40c->tasklet);
+ dmaengine_kill_bh(&d40c->chan);
}
}
@@ -3141,7 +3138,7 @@ 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,
+ err = devm_add_action(base->dev, d40_kill_bh,
&base->dma_slave);
if (err)
goto exit;
@@ -3161,7 +3158,7 @@ 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,
+ err = devm_add_action(base->dev, d40_kill_bh,
&base->dma_memcpy);
if (err)
goto exit;
@@ -3184,7 +3181,7 @@ static int __init d40_dmaengine_init(struct d40_base *base,
d40_ops_init(base, &base->dma_both);
- err = devm_add_action(base->dev, d40_kill_tasklets,
+ err = devm_add_action(base->dev, d40_kill_bh,
&base->dma_both);
if (err)
goto exit;
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 18/33] dmaengine: xgene-dma: wire descriptor cleanup to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (16 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 17/33] dmaengine: ste_dma40: convert per-channel tasklet to " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 19/33] dmaengine: xilinx-dma: use dmaengine BH instead of tasklets Allen Pais
` (14 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/xgene-dma.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c
index 3714099aef07..a10db8623f46 100644
--- a/drivers/dma/xgene-dma.c
+++ b/drivers/dma/xgene-dma.c
@@ -260,7 +260,6 @@ struct xgene_dma_desc_sw {
* These descriptors have already had their cleanup actions run. They
* are waiting for the ACK bit to be set by the async tx API.
* @desc_pool: descriptor pool for DMA operations
- * @tasklet: bottom half where all completed descriptors cleans
* @tx_ring: transmit ring descriptor that we use to prepare actual
* descriptors for further executions
* @rx_ring: receive ring descriptor that we use to get completed DMA
@@ -280,7 +279,6 @@ struct xgene_dma_chan {
struct list_head ld_running;
struct list_head ld_completed;
struct dma_pool *desc_pool;
- struct tasklet_struct tasklet;
struct xgene_dma_ring tx_ring;
struct xgene_dma_ring rx_ring;
};
@@ -975,9 +973,10 @@ static enum dma_status xgene_dma_tx_status(struct dma_chan *dchan,
return dma_cookie_status(dchan, cookie, txstate);
}
-static void xgene_dma_tasklet_cb(struct tasklet_struct *t)
+static void xgene_dma_tasklet_cb(struct dma_chan *c)
{
- struct xgene_dma_chan *chan = from_tasklet(chan, t, tasklet);
+ struct xgene_dma_chan *chan = container_of(c, struct xgene_dma_chan,
+ dma_chan);
/* Run all cleanup for descriptors which have been completed */
xgene_dma_cleanup_descriptors(chan);
@@ -999,11 +998,11 @@ static irqreturn_t xgene_dma_chan_ring_isr(int irq, void *id)
disable_irq_nosync(chan->rx_irq);
/*
- * Schedule the tasklet to handle all cleanup of the current
+ * Schedule the bottom half to handle all cleanup of the current
* transaction. It will start a new transaction if there is
* one pending.
*/
- tasklet_schedule(&chan->tasklet);
+ dmaengine_schedule_bh(&chan->dma_chan);
return IRQ_HANDLED;
}
@@ -1539,7 +1538,7 @@ static int xgene_dma_async_register(struct xgene_dma *pdma, int id)
INIT_LIST_HEAD(&chan->ld_pending);
INIT_LIST_HEAD(&chan->ld_running);
INIT_LIST_HEAD(&chan->ld_completed);
- tasklet_setup(&chan->tasklet, xgene_dma_tasklet_cb);
+ dmaengine_init_bh(&chan->dma_chan, xgene_dma_tasklet_cb);
chan->pending = 0;
chan->desc_pool = NULL;
@@ -1556,7 +1555,7 @@ static int xgene_dma_async_register(struct xgene_dma *pdma, int id)
ret = dma_async_device_register(dma_dev);
if (ret) {
chan_err(chan, "Failed to register async device %d", ret);
- tasklet_kill(&chan->tasklet);
+ dmaengine_kill_bh(&chan->dma_chan);
return ret;
}
@@ -1579,7 +1578,7 @@ static int xgene_dma_init_async(struct xgene_dma *pdma)
if (ret) {
for (j = 0; j < i; j++) {
dma_async_device_unregister(&pdma->dma_dev[j]);
- tasklet_kill(&pdma->chan[j].tasklet);
+ dmaengine_kill_bh(&pdma->chan[j].dma_chan);
}
return ret;
@@ -1790,7 +1789,7 @@ static void xgene_dma_remove(struct platform_device *pdev)
for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) {
chan = &pdma->chan[i];
- tasklet_kill(&chan->tasklet);
+ dmaengine_kill_bh(&chan->dma_chan);
xgene_dma_delete_chan_rings(chan);
}
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 19/33] dmaengine: xilinx-dma: use dmaengine BH instead of tasklets
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (17 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 18/33] dmaengine: xgene-dma: wire descriptor cleanup " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 20/33] dmaengine: xilinx-dpdma: kill vchan BH on remove Allen Pais
` (13 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Frank Li, Michal Simek, Suraj Gupta, Radhey Shyam Pandey,
Alex Bereza, Marek Vasut, Tomi Valkeinen, Christophe JAILLET,
Krzysztof Kozlowski, linux-arm-kernel
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/xilinx/xilinx_dma.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index b89f2c3c1579..d23658c927c7 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -414,7 +414,6 @@ struct xilinx_dma_tx_descriptor {
* @err: Channel has errors
* @idle: Check for channel idle
* @terminating: Check for channel being synchronized by user
- * @tasklet: Cleanup work after irq
* @config: Device configuration info
* @flush_on_fsync: Flush on Frame sync
* @desc_pendingcount: Descriptor pending count
@@ -453,7 +452,6 @@ struct xilinx_dma_chan {
bool err;
bool idle;
bool terminating;
- struct tasklet_struct tasklet;
struct xilinx_vdma_config config;
bool flush_on_fsync;
u32 desc_pendingcount;
@@ -1173,11 +1171,12 @@ static void xilinx_dma_chan_desc_cleanup(struct xilinx_dma_chan *chan)
/**
* xilinx_dma_do_tasklet - Schedule completion tasklet
- * @t: Pointer to the Xilinx DMA channel structure
+ * @c: Pointer to the Xilinx DMA channel structure
*/
-static void xilinx_dma_do_tasklet(struct tasklet_struct *t)
+static void xilinx_dma_do_tasklet(struct dma_chan *c)
{
- struct xilinx_dma_chan *chan = from_tasklet(chan, t, tasklet);
+ struct xilinx_dma_chan *chan = container_of(c,
+ struct xilinx_dma_chan, common);
xilinx_dma_chan_desc_cleanup(chan);
}
@@ -1979,7 +1978,7 @@ static irqreturn_t xilinx_mcdma_irq_handler(int irq, void *data)
spin_unlock(&chan->lock);
}
- tasklet_hi_schedule(&chan->tasklet);
+ dmaengine_schedule_bh(&chan->common);
return IRQ_HANDLED;
}
@@ -2038,7 +2037,7 @@ static irqreturn_t xilinx_dma_irq_handler(int irq, void *data)
spin_unlock(&chan->lock);
}
- tasklet_schedule(&chan->tasklet);
+ dmaengine_schedule_bh(&chan->common);
return IRQ_HANDLED;
}
@@ -2740,7 +2739,7 @@ static void xilinx_dma_synchronize(struct dma_chan *dchan)
{
struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
- tasklet_kill(&chan->tasklet);
+ dmaengine_kill_bh(&chan->common);
}
/**
@@ -2831,7 +2830,7 @@ static void xilinx_dma_chan_remove(struct xilinx_dma_chan *chan)
if (chan->irq > 0)
free_irq(chan->irq, chan);
- tasklet_kill(&chan->tasklet);
+ dmaengine_kill_bh(&chan->common);
list_del(&chan->common.device_node);
}
@@ -3161,8 +3160,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
str_enabled_disabled(chan->has_sg));
}
- /* Initialize the tasklet */
- tasklet_setup(&chan->tasklet, xilinx_dma_do_tasklet);
+ dmaengine_init_bh(&chan->common, xilinx_dma_do_tasklet);
/*
* Initialize the DMA channel and add it to the DMA engine channels
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 20/33] dmaengine: xilinx-dpdma: kill vchan BH on remove
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (18 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 19/33] dmaengine: xilinx-dma: use dmaengine BH instead of tasklets Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 21/33] dmaengine: zynqmp-dma: switch completion tasklet to dmaengine BH Allen Pais
` (12 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Laurent Pinchart, Frank Li, Michal Simek, linux-arm-kernel
virt-dma now dispatches completion callbacks through per-channel BH
work. Cancel that work when removing a channel, while retaining the
driver's separate error-handling tasklet.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/xilinx/xilinx_dpdma.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c
index 687fc8ef3d8e..219ba9a63050 100644
--- a/drivers/dma/xilinx/xilinx_dpdma.c
+++ b/drivers/dma/xilinx/xilinx_dpdma.c
@@ -1685,6 +1685,7 @@ static void xilinx_dpdma_chan_remove(struct xilinx_dpdma_chan *chan)
return;
tasklet_kill(&chan->err_task);
+ dmaengine_kill_bh(&chan->vchan.chan);
list_del(&chan->vchan.chan.device_node);
}
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 21/33] dmaengine: zynqmp-dma: switch completion tasklet to dmaengine BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (19 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 20/33] dmaengine: xilinx-dpdma: kill vchan BH on remove Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 22/33] dmaengine: tegra20-apb: use channel BH helpers Allen Pais
` (11 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Frank Li, Michal Simek, Radhey Shyam Pandey, Golla Nagendra,
Abin Joseph, Prasanna Kumar T S M, Kees Cook, Sakari Ailus,
linux-arm-kernel
Replace the per-channel tasklet with the shared dmaengine BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/xilinx/zynqmp_dma.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index bc85dca47412..aca32eb175bc 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -205,7 +205,6 @@ struct zynqmp_dma_desc_sw {
* @dev: The dma device
* @irq: Linux IRQ number, or -1 when not registered
* @is_dmacoherent: Tells whether dma operations are coherent or not
- * @tasklet: Cleanup work after irq
* @idle: Channel status
* @desc_size: Size of the low level descriptor
* @err: Channel has errors
@@ -230,7 +229,6 @@ struct zynqmp_dma_chan {
struct device *dev;
int irq;
bool is_dmacoherent;
- struct tasklet_struct tasklet;
bool idle;
size_t desc_size;
bool err;
@@ -743,7 +741,7 @@ static irqreturn_t zynqmp_dma_irq_handler(int irq, void *data)
writel(isr, chan->regs + ZYNQMP_DMA_ISR);
if (status & ZYNQMP_DMA_INT_DONE) {
- tasklet_schedule(&chan->tasklet);
+ dmaengine_schedule_bh(&chan->common);
ret = IRQ_HANDLED;
}
@@ -752,7 +750,7 @@ static irqreturn_t zynqmp_dma_irq_handler(int irq, void *data)
if (status & ZYNQMP_DMA_INT_ERR) {
chan->err = true;
- tasklet_schedule(&chan->tasklet);
+ dmaengine_schedule_bh(&chan->common);
dev_err(chan->dev, "Channel %p has errors\n", chan);
ret = IRQ_HANDLED;
}
@@ -768,11 +766,12 @@ static irqreturn_t zynqmp_dma_irq_handler(int irq, void *data)
/**
* zynqmp_dma_do_tasklet - Schedule completion tasklet
- * @t: Pointer to the ZynqMP DMA channel structure
+ * @c: Pointer to the ZynqMP DMA channel structure
*/
-static void zynqmp_dma_do_tasklet(struct tasklet_struct *t)
+static void zynqmp_dma_do_tasklet(struct dma_chan *c)
{
- struct zynqmp_dma_chan *chan = from_tasklet(chan, t, tasklet);
+ struct zynqmp_dma_chan *chan = container_of(c,
+ struct zynqmp_dma_chan, common);
u32 count;
unsigned long irqflags;
@@ -823,7 +822,7 @@ static void zynqmp_dma_synchronize(struct dma_chan *dchan)
{
struct zynqmp_dma_chan *chan = to_chan(dchan);
- tasklet_kill(&chan->tasklet);
+ dmaengine_kill_bh(&chan->common);
}
/**
@@ -899,7 +898,7 @@ static void zynqmp_dma_chan_remove(struct zynqmp_dma_chan *chan)
if (chan->irq >= 0)
devm_free_irq(chan->zdev->dev, chan->irq, chan);
- tasklet_kill(&chan->tasklet);
+ dmaengine_kill_bh(&chan->common);
if (!list_empty(&chan->common.device_node))
list_del(&chan->common.device_node);
}
@@ -951,7 +950,7 @@ static int zynqmp_dma_chan_probe(struct zynqmp_dma_device *zdev,
chan->is_dmacoherent = of_dma_is_coherent(node);
zdev->chan = chan;
- tasklet_setup(&chan->tasklet, zynqmp_dma_do_tasklet);
+ dmaengine_init_bh(&chan->common, zynqmp_dma_do_tasklet);
spin_lock_init(&chan->lock);
INIT_LIST_HEAD(&chan->active_list);
INIT_LIST_HEAD(&chan->pending_list);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 22/33] dmaengine: tegra20-apb: use channel BH helpers
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (20 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 21/33] dmaengine: zynqmp-dma: switch completion tasklet to dmaengine BH Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 23/33] dmaengine: timb_dma: route callbacks via channel BH Allen Pais
` (10 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Laxman Dewangan, Jon Hunter, Frank Li, Thierry Reding,
linux-tegra
Switch the per-channel tasklet to dmaengine_*_bh so completion handling
runs through the shared per-channel deferral mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
drivers/dma/tegra20-apb-dma.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
index 7ff8c0241f61..2eca4a2567cb 100644
--- a/drivers/dma/tegra20-apb-dma.c
+++ b/drivers/dma/tegra20-apb-dma.c
@@ -194,9 +194,8 @@ struct tegra_dma_channel {
struct list_head free_dma_desc;
struct list_head cb_desc;
- /* ISR handler and tasklet for bottom half of isr handling */
+ /* ISR handler for bottom-half processing */
dma_isr_handler isr_handler;
- struct tasklet_struct tasklet;
/* Channel-slave specific configuration */
unsigned int slave_id;
@@ -632,9 +631,10 @@ static void handle_cont_sngl_cycle_dma_done(struct tegra_dma_channel *tdc,
}
}
-static void tegra_dma_tasklet(struct tasklet_struct *t)
+static void tegra_dma_tasklet(struct dma_chan *chan)
{
- struct tegra_dma_channel *tdc = from_tasklet(tdc, t, tasklet);
+ struct tegra_dma_channel *tdc = container_of(chan, struct tegra_dma_channel,
+ dma_chan);
struct dmaengine_desc_callback cb;
struct tegra_dma_desc *dma_desc;
unsigned int cb_count;
@@ -670,7 +670,7 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id)
if (status & TEGRA_APBDMA_STATUS_ISE_EOC) {
tdc_write(tdc, TEGRA_APBDMA_CHAN_STATUS, status);
tdc->isr_handler(tdc, false);
- tasklet_schedule(&tdc->tasklet);
+ dmaengine_schedule_bh(&tdc->dma_chan);
wake_up_all(&tdc->wq);
spin_unlock(&tdc->lock);
return IRQ_HANDLED;
@@ -819,7 +819,7 @@ static void tegra_dma_synchronize(struct dma_chan *dc)
*/
wait_event(tdc->wq, tegra_dma_eoc_interrupt_deasserted(tdc));
- tasklet_kill(&tdc->tasklet);
+ dmaengine_kill_bh(&tdc->dma_chan);
pm_runtime_put(tdc->tdma->dev);
}
@@ -1317,7 +1317,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc)
dev_dbg(tdc2dev(tdc), "Freeing channel %d\n", tdc->id);
tegra_dma_terminate_all(dc);
- tasklet_kill(&tdc->tasklet);
+ dmaengine_kill_bh(&tdc->dma_chan);
list_splice_init(&tdc->pending_sg_req, &sg_req_list);
list_splice_init(&tdc->free_sg_req, &sg_req_list);
@@ -1511,7 +1511,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
tdc->id = i;
tdc->slave_id = TEGRA_APBDMA_SLAVE_ID_INVALID;
- tasklet_setup(&tdc->tasklet, tegra_dma_tasklet);
+ dmaengine_init_bh(&tdc->dma_chan, tegra_dma_tasklet);
spin_lock_init(&tdc->lock);
init_waitqueue_head(&tdc->wq);
@@ -1617,7 +1617,7 @@ static int __maybe_unused tegra_dma_dev_suspend(struct device *dev)
for (i = 0; i < tdma->chip_data->nr_channels; i++) {
struct tegra_dma_channel *tdc = &tdma->channels[i];
- tasklet_kill(&tdc->tasklet);
+ dmaengine_kill_bh(&tdc->dma_chan);
spin_lock_irqsave(&tdc->lock, flags);
busy = tdc->busy;
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 23/33] dmaengine: timb_dma: route callbacks via channel BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (21 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 22/33] dmaengine: tegra20-apb: use channel BH helpers Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 24/33] dmaengine: txx9dmac: " Allen Pais
` (9 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Schedule per-channel BH work for completions so callbacks run through
the dma_chan deferral mechanism while keeping the controller tasklet for
IRQ handling.
The controller tasklet recomputes TIMBDMA_IER before the deferred work
has finished the channel, so have the channel BH recompute and rewrite
the mask once the channel state has settled. This keeps the interrupt
enable mask consistent with what the single tasklet used to produce.
Drain the channel BHs in td_remove() and in the probe error path so no
completion work can run after the controller memory is freed.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
drivers/dma/timb_dma.c | 41 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 36 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c
index 0acd7a36a3d0..d24508e2a0bb 100644
--- a/drivers/dma/timb_dma.c
+++ b/drivers/dma/timb_dma.c
@@ -433,6 +433,8 @@ static void td_free_chan_resources(struct dma_chan *chan)
dev_dbg(dmaengine_chan_dev(chan), "%s: Entry\n", __func__);
+ dmaengine_kill_bh(&td_chan->chan);
+
/* check that all descriptors are free */
BUG_ON(!list_empty(&td_chan->active_list));
BUG_ON(!list_empty(&td_chan->queue));
@@ -575,17 +577,42 @@ static void td_tasklet(struct tasklet_struct *t)
for (i = 0; i < td->dma.chancnt; i++)
if (ipr & (1 << i)) {
struct timb_dma_chan *td_chan = td->channels + i;
- spin_lock(&td_chan->lock);
- __td_finish(td_chan);
- if (!list_empty(&td_chan->queue))
- __td_start_next(td_chan);
- spin_unlock(&td_chan->lock);
+ dmaengine_schedule_bh(&td_chan->chan);
}
ier = __td_ier_mask(td);
iowrite32(ier, td->membase + TIMBDMA_IER);
}
+static void timb_dma_chan_bh(struct dma_chan *chan)
+{
+ struct timb_dma_chan *td_chan = container_of(chan, struct timb_dma_chan,
+ chan);
+ struct timb_dma *td = tdchantotd(td_chan);
+ u32 ier;
+
+ spin_lock(&td_chan->lock);
+ __td_finish(td_chan);
+ if (!list_empty(&td_chan->queue))
+ __td_start_next(td_chan);
+ spin_unlock(&td_chan->lock);
+
+ /*
+ * The controller tasklet re-armed IER before this channel was
+ * finished. Recompute it now that the channel state has changed.
+ */
+ ier = __td_ier_mask(td);
+ iowrite32(ier, td->membase + TIMBDMA_IER);
+}
+
+static void td_kill_chan_bh(struct timb_dma *td, int nr_channels)
+{
+ int i;
+
+ for (i = 0; i < nr_channels; i++)
+ dmaengine_kill_bh(&td->channels[i].chan);
+}
+
static irqreturn_t td_irq(int irq, void *devid)
{
@@ -692,6 +719,7 @@ static int td_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&td_chan->active_list);
INIT_LIST_HEAD(&td_chan->queue);
INIT_LIST_HEAD(&td_chan->free_list);
+ dmaengine_init_bh(&td_chan->chan, timb_dma_chan_bh);
td_chan->descs = pchan->descriptors;
td_chan->desc_elems = pchan->descriptor_elements;
@@ -724,6 +752,7 @@ static int td_probe(struct platform_device *pdev)
free_irq(irq, td);
err_tasklet_kill:
tasklet_kill(&td->tasklet);
+ td_kill_chan_bh(td, pdata->nr_channels);
iounmap(td->membase);
err_free_mem:
kfree(td);
@@ -736,6 +765,7 @@ static int td_probe(struct platform_device *pdev)
static void td_remove(struct platform_device *pdev)
{
+ struct timb_dma_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct timb_dma *td = platform_get_drvdata(pdev);
struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int irq = platform_get_irq(pdev, 0);
@@ -743,6 +773,7 @@ static void td_remove(struct platform_device *pdev)
dma_async_device_unregister(&td->dma);
free_irq(irq, td);
tasklet_kill(&td->tasklet);
+ td_kill_chan_bh(td, pdata->nr_channels);
iounmap(td->membase);
kfree(td);
release_mem_region(iomem->start, resource_size(iomem));
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 24/33] dmaengine: txx9dmac: route callbacks via channel BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (22 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 23/33] dmaengine: timb_dma: route callbacks via channel BH Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 25/33] dmaengine: mv_xor_v2: use channel BH helpers Allen Pais
` (8 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Use the per-channel BH helpers for descriptor completion handling on
channels that own an interrupt line. The channel interrupt schedules the
BH, which scans the descriptors and re-enables the interrupt, exactly as
the per-channel tasklet did.
Channels served by the shared controller interrupt do not initialize a
channel BH, so the controller tasklet keeps scanning their descriptors
directly, as before. Routing that path through a BH that was never set
up would trip the WARN_ON() in dmaengine_schedule_bh() and stall the
channel.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
drivers/dma/txx9dmac.c | 10 +++++-----
drivers/dma/txx9dmac.h | 1 -
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c
index 04de7f7ef31d..5c4d6ee7d720 100644
--- a/drivers/dma/txx9dmac.c
+++ b/drivers/dma/txx9dmac.c
@@ -596,13 +596,13 @@ static void txx9dmac_scan_descriptors(struct txx9dmac_chan *dc)
}
}
-static void txx9dmac_chan_tasklet(struct tasklet_struct *t)
+static void txx9dmac_chan_tasklet(struct dma_chan *chan)
{
int irq;
u32 csr;
struct txx9dmac_chan *dc;
- dc = from_tasklet(dc, t, tasklet);
+ dc = container_of(chan, struct txx9dmac_chan, chan);
csr = channel_readl(dc, CSR);
dev_vdbg(dmaengine_chan_dev(&dc->chan), "tasklet: status=%x\n", csr);
@@ -623,7 +623,7 @@ static irqreturn_t txx9dmac_chan_interrupt(int irq, void *dev_id)
dev_vdbg(dmaengine_chan_dev(&dc->chan), "interrupt: status=%#x\n",
channel_readl(dc, CSR));
- tasklet_schedule(&dc->tasklet);
+ dmaengine_schedule_bh(&dc->chan);
/*
* Just disable the interrupts. We'll turn them back on in the
* softirq handler.
@@ -1108,7 +1108,7 @@ static int __init txx9dmac_chan_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
- tasklet_setup(&dc->tasklet, txx9dmac_chan_tasklet);
+ dmaengine_init_bh(&dc->chan, txx9dmac_chan_tasklet);
dc->irq = irq;
err = devm_request_irq(&pdev->dev, dc->irq,
txx9dmac_chan_interrupt, 0, dev_name(&pdev->dev), dc);
@@ -1154,7 +1154,7 @@ static void txx9dmac_chan_remove(struct platform_device *pdev)
dma_async_device_unregister(&dc->dma);
if (dc->irq >= 0) {
devm_free_irq(&pdev->dev, dc->irq, dc);
- tasklet_kill(&dc->tasklet);
+ dmaengine_kill_bh(&dc->chan);
}
dc->ddev->chan[pdev->id % TXX9_DMA_MAX_NR_CHANNELS] = NULL;
}
diff --git a/drivers/dma/txx9dmac.h b/drivers/dma/txx9dmac.h
index aa53eafb1519..f2abedb73d49 100644
--- a/drivers/dma/txx9dmac.h
+++ b/drivers/dma/txx9dmac.h
@@ -162,7 +162,6 @@ struct txx9dmac_chan {
struct dma_device dma;
struct txx9dmac_dev *ddev;
void __iomem *ch_regs;
- struct tasklet_struct tasklet;
int irq;
u32 ccr;
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 25/33] dmaengine: mv_xor_v2: use channel BH helpers
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (23 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 24/33] dmaengine: txx9dmac: " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 26/33] dmaengine: mpc512x: route callbacks via channel BH Allen Pais
` (7 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Replace the device tasklet with the per-channel BH helper so descriptor
callbacks run through dmaengine_*_bh.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/mv_xor_v2.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/drivers/dma/mv_xor_v2.c b/drivers/dma/mv_xor_v2.c
index 1920dcd5886e..3c8d7b065d3f 100644
--- a/drivers/dma/mv_xor_v2.c
+++ b/drivers/dma/mv_xor_v2.c
@@ -139,7 +139,6 @@ struct mv_xor_v2_descriptor {
* @reg_clk: reference to the 'reg' clock
* @dma_base: memory mapped DMA register base
* @glob_base: memory mapped global register base
- * @irq_tasklet: tasklet used for IRQ handling call-backs
* @free_sw_desc: linked list of free SW descriptors
* @dmadev: dma device
* @dmachan: dma channel
@@ -158,7 +157,6 @@ struct mv_xor_v2_device {
void __iomem *glob_base;
struct clk *clk;
struct clk *reg_clk;
- struct tasklet_struct irq_tasklet;
struct list_head free_sw_desc;
struct dma_device dmadev;
struct dma_chan dmachan;
@@ -290,8 +288,8 @@ static irqreturn_t mv_xor_v2_interrupt_handler(int irq, void *data)
if (!ndescs)
return IRQ_NONE;
- /* schedule a tasklet to handle descriptors callbacks */
- tasklet_schedule(&xor_dev->irq_tasklet);
+ /* schedule a bottom half to handle descriptors callbacks */
+ dmaengine_schedule_bh(&xor_dev->dmachan);
return IRQ_HANDLED;
}
@@ -346,8 +344,8 @@ mv_xor_v2_prep_sw_desc(struct mv_xor_v2_device *xor_dev)
if (list_empty(&xor_dev->free_sw_desc)) {
spin_unlock_bh(&xor_dev->lock);
- /* schedule tasklet to free some descriptors */
- tasklet_schedule(&xor_dev->irq_tasklet);
+ /* schedule bottom half to free some descriptors */
+ dmaengine_schedule_bh(&xor_dev->dmachan);
return NULL;
}
@@ -553,10 +551,10 @@ int mv_xor_v2_get_pending_params(struct mv_xor_v2_device *xor_dev,
/*
* handle the descriptors after HW process
*/
-static void mv_xor_v2_tasklet(struct tasklet_struct *t)
+static void mv_xor_v2_tasklet(struct dma_chan *chan)
{
- struct mv_xor_v2_device *xor_dev = from_tasklet(xor_dev, t,
- irq_tasklet);
+ struct mv_xor_v2_device *xor_dev =
+ container_of(chan, struct mv_xor_v2_device, dmachan);
int pending_ptr, num_of_pending, i;
struct mv_xor_v2_sw_desc *next_pending_sw_desc = NULL;
@@ -760,7 +758,7 @@ static int mv_xor_v2_probe(struct platform_device *pdev)
if (ret)
goto free_msi_irqs;
- tasklet_setup(&xor_dev->irq_tasklet, mv_xor_v2_tasklet);
+ dmaengine_init_bh(&xor_dev->dmachan, mv_xor_v2_tasklet);
xor_dev->desc_size = mv_xor_v2_set_desc_size(xor_dev);
@@ -868,7 +866,7 @@ static void mv_xor_v2_remove(struct platform_device *pdev)
platform_device_msi_free_irqs_all(&pdev->dev);
- tasklet_kill(&xor_dev->irq_tasklet);
+ dmaengine_kill_bh(&xor_dev->dmachan);
}
#ifdef CONFIG_OF
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 26/33] dmaengine: mpc512x: route callbacks via channel BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (24 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 25/33] dmaengine: mv_xor_v2: use channel BH helpers Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 27/33] dmaengine: plx_dma: use channel BH helpers Allen Pais
` (6 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Schedule per-channel BH work for completions and process callbacks there,
leaving the controller tasklet for IRQ/error handling.
Drain the per-channel BH work in mpc_dma_remove() after the controller
tasklet is killed, so no callback can run once the device memory is
released.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
drivers/dma/mpc512x_dma.c | 78 +++++++++++++++++++++++++--------------
1 file changed, 51 insertions(+), 27 deletions(-)
diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c
index f67421f2d6d3..b6a1b3c94da4 100644
--- a/drivers/dma/mpc512x_dma.c
+++ b/drivers/dma/mpc512x_dma.c
@@ -372,45 +372,47 @@ static irqreturn_t mpc_dma_irq(int irq, void *data)
return IRQ_HANDLED;
}
-/* process completed descriptors */
-static void mpc_dma_process_completed(struct mpc_dma *mdma)
+static void mpc_dma_process_chan_completed(struct mpc_dma_chan *mchan)
{
dma_cookie_t last_cookie = 0;
- struct mpc_dma_chan *mchan;
struct mpc_dma_desc *mdesc;
struct dma_async_tx_descriptor *desc;
unsigned long flags;
LIST_HEAD(list);
- int i;
- for (i = 0; i < mdma->dma.chancnt; i++) {
- mchan = &mdma->channels[i];
+ /* Get all completed descriptors */
+ spin_lock_irqsave(&mchan->lock, flags);
+ if (!list_empty(&mchan->completed))
+ list_splice_tail_init(&mchan->completed, &list);
+ spin_unlock_irqrestore(&mchan->lock, flags);
- /* Get all completed descriptors */
- spin_lock_irqsave(&mchan->lock, flags);
- if (!list_empty(&mchan->completed))
- list_splice_tail_init(&mchan->completed, &list);
- spin_unlock_irqrestore(&mchan->lock, flags);
+ if (list_empty(&list))
+ return;
- if (list_empty(&list))
- continue;
+ /* Execute callbacks and run dependencies */
+ list_for_each_entry(mdesc, &list, node) {
+ desc = &mdesc->desc;
- /* Execute callbacks and run dependencies */
- list_for_each_entry(mdesc, &list, node) {
- desc = &mdesc->desc;
+ dmaengine_desc_get_callback_invoke(desc, NULL);
- dmaengine_desc_get_callback_invoke(desc, NULL);
+ last_cookie = desc->cookie;
+ dma_run_dependencies(desc);
+ }
- last_cookie = desc->cookie;
- dma_run_dependencies(desc);
- }
+ /* Free descriptors */
+ spin_lock_irqsave(&mchan->lock, flags);
+ list_splice_tail_init(&list, &mchan->free);
+ mchan->chan.completed_cookie = last_cookie;
+ spin_unlock_irqrestore(&mchan->lock, flags);
+}
- /* Free descriptors */
- spin_lock_irqsave(&mchan->lock, flags);
- list_splice_tail_init(&list, &mchan->free);
- mchan->chan.completed_cookie = last_cookie;
- spin_unlock_irqrestore(&mchan->lock, flags);
- }
+/* process completed descriptors */
+static void mpc_dma_process_completed(struct mpc_dma *mdma)
+{
+ int i;
+
+ for (i = 0; i < mdma->dma.chancnt; i++)
+ mpc_dma_process_chan_completed(&mdma->channels[i]);
}
/* DMA Tasklet */
@@ -419,6 +421,7 @@ static void mpc_dma_tasklet(struct tasklet_struct *t)
struct mpc_dma *mdma = from_tasklet(mdma, t, tasklet);
unsigned long flags;
uint es;
+ int i;
spin_lock_irqsave(&mdma->error_status_lock, flags);
es = mdma->error_status;
@@ -453,7 +456,21 @@ static void mpc_dma_tasklet(struct tasklet_struct *t)
dev_err(mdma->dma.dev, "- Destination Bus Error\n");
}
- mpc_dma_process_completed(mdma);
+ for (i = 0; i < mdma->dma.chancnt; i++) {
+ struct mpc_dma_chan *mchan = &mdma->channels[i];
+
+ spin_lock_irqsave(&mchan->lock, flags);
+ if (!list_empty(&mchan->completed))
+ dmaengine_schedule_bh(&mchan->chan);
+ spin_unlock_irqrestore(&mchan->lock, flags);
+ }
+}
+
+static void mpc_dma_chan_bh(struct dma_chan *chan)
+{
+ struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
+
+ mpc_dma_process_chan_completed(mchan);
}
/* Submit descriptor to hardware */
@@ -551,6 +568,8 @@ static void mpc_dma_free_chan_resources(struct dma_chan *chan)
unsigned long flags;
LIST_HEAD(descs);
+ dmaengine_kill_bh(&mchan->chan);
+
spin_lock_irqsave(&mchan->lock, flags);
/* Channel must be idle */
@@ -1007,6 +1026,7 @@ static int mpc_dma_probe(struct platform_device *op)
INIT_LIST_HEAD(&mchan->completed);
spin_lock_init(&mchan->lock);
+ dmaengine_init_bh(&mchan->chan, mpc_dma_chan_bh);
list_add_tail(&mchan->chan.device_node, &dma->channels);
}
@@ -1088,6 +1108,7 @@ static void mpc_dma_remove(struct platform_device *op)
{
struct device *dev = &op->dev;
struct mpc_dma *mdma = dev_get_drvdata(dev);
+ int i;
if (dev->of_node)
of_dma_controller_free(dev->of_node);
@@ -1099,6 +1120,9 @@ static void mpc_dma_remove(struct platform_device *op)
free_irq(mdma->irq, mdma);
irq_dispose_mapping(mdma->irq);
tasklet_kill(&mdma->tasklet);
+
+ for (i = 0; i < MPC_DMA_CHANNELS; i++)
+ dmaengine_kill_bh(&mdma->channels[i].chan);
}
static const struct of_device_id mpc_dma_match[] = {
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 27/33] dmaengine: plx_dma: use channel BH helpers
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (25 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 26/33] dmaengine: mpc512x: route callbacks via channel BH Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 28/33] dmaengine: sf-pdma: route error callbacks through channel BH Allen Pais
` (5 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Logan Gunthorpe, Frank Li
Replace the device tasklet with the per-channel BH helper so completion
callbacks run via dmaengine_*_bh.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/plx_dma.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/dma/plx_dma.c b/drivers/dma/plx_dma.c
index 409898e92c32..00187327e86a 100644
--- a/drivers/dma/plx_dma.c
+++ b/drivers/dma/plx_dma.c
@@ -105,7 +105,6 @@ struct plx_dma_dev {
struct dma_chan dma_chan;
struct pci_dev __rcu *pdev;
void __iomem *bar;
- struct tasklet_struct desc_task;
spinlock_t ring_lock;
bool ring_active;
@@ -241,9 +240,10 @@ static void plx_dma_stop(struct plx_dma_dev *plxdev)
rcu_read_unlock();
}
-static void plx_dma_desc_task(struct tasklet_struct *t)
+static void plx_dma_desc_task(struct dma_chan *chan)
{
- struct plx_dma_dev *plxdev = from_tasklet(plxdev, t, desc_task);
+ struct plx_dma_dev *plxdev = container_of(chan, struct plx_dma_dev,
+ dma_chan);
plx_dma_process_desc(plxdev);
}
@@ -366,7 +366,7 @@ static irqreturn_t plx_dma_isr(int irq, void *devid)
return IRQ_NONE;
if (status & PLX_REG_INTR_STATUS_DESC_DONE && plxdev->ring_active)
- tasklet_schedule(&plxdev->desc_task);
+ dmaengine_schedule_bh(&plxdev->dma_chan);
writew(status, plxdev->bar + PLX_REG_INTR_STATUS);
@@ -471,7 +471,7 @@ static void plx_dma_free_chan_resources(struct dma_chan *chan)
if (irq > 0)
synchronize_irq(irq);
- tasklet_kill(&plxdev->desc_task);
+ dmaengine_kill_bh(&plxdev->dma_chan);
plx_dma_abort_desc(plxdev);
@@ -505,7 +505,7 @@ static int plx_dma_create(struct pci_dev *pdev)
return -ENOMEM;
spin_lock_init(&plxdev->ring_lock);
- tasklet_setup(&plxdev->desc_task, plx_dma_desc_task);
+ dmaengine_init_bh(&plxdev->dma_chan, plx_dma_desc_task);
RCU_INIT_POINTER(plxdev->pdev, pdev);
plxdev->bar = pcim_iomap_table(pdev)[0];
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 28/33] dmaengine: sf-pdma: route error callbacks through channel BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (26 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 27/33] dmaengine: plx_dma: use channel BH helpers Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 29/33] dmaengine: pl330: route callbacks via " Allen Pais
` (4 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Paul Walmsley, Samuel Holland, Frank Li, linux-riscv
virt-dma now dispatches completion callbacks through per-channel BH work,
but SF-PDMA invokes the client callback directly from its error tasklet
after exhausting its retries. That descriptor is never actually
completed: it stays on the issued list and in chan->desc, the cookie is
never advanced, and the channel does not move on to the next transfer.
Complete the failed descriptor through virt-dma instead, mirroring what
the done tasklet does for a successful transfer: unlink it from the
issued list, mark it DMA_TRANS_ABORTED with the untransferred size as
residue, hand it to vchan_cookie_complete() so the client callback runs
from the channel BH, and start the next issued descriptor if there is
one. Reset the retry budget at the same point, as the done tasklet does
after a successful retry, so the next descriptor does not inherit an
exhausted retry count.
During removal, kill the producer tasklets before the channel BH so they
cannot enqueue callback work after it has been drained.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
drivers/dma/sf-pdma/sf-pdma.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/drivers/dma/sf-pdma/sf-pdma.c b/drivers/dma/sf-pdma/sf-pdma.c
index ca60fb46ade0..992f9f103702 100644
--- a/drivers/dma/sf-pdma/sf-pdma.c
+++ b/drivers/dma/sf-pdma/sf-pdma.c
@@ -326,9 +326,21 @@ static void sf_pdma_errbh_tasklet(struct tasklet_struct *t)
spin_lock_irqsave(&chan->lock, flags);
if (chan->retries <= 0) {
- /* fail to recover */
+ /* fail to recover: abort this descriptor and move on */
+ chan->retries = MAX_RETRY;
+ chan->xfer_err = false;
spin_unlock_irqrestore(&chan->lock, flags);
- dmaengine_desc_get_callback_invoke(desc->async_tx, NULL);
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ list_del(&desc->vdesc.node);
+ desc->vdesc.tx_result.result = DMA_TRANS_ABORTED;
+ desc->vdesc.tx_result.residue = desc->xfer_size;
+ vchan_cookie_complete(&desc->vdesc);
+
+ chan->desc = sf_pdma_get_first_pending_desc(chan);
+ if (chan->desc)
+ sf_pdma_xfer_desc(chan);
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
} else {
/* retry */
chan->retries--;
@@ -601,9 +613,9 @@ static void sf_pdma_remove(struct platform_device *pdev)
devm_free_irq(&pdev->dev, ch->txirq, ch);
devm_free_irq(&pdev->dev, ch->errirq, ch);
list_del(&ch->vchan.chan.device_node);
- dmaengine_kill_bh(&ch->vchan.chan);
tasklet_kill(&ch->done_tasklet);
tasklet_kill(&ch->err_tasklet);
+ dmaengine_kill_bh(&ch->vchan.chan);
}
if (pdev->dev.of_node)
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 29/33] dmaengine: pl330: route callbacks via channel BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (27 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 28/33] dmaengine: sf-pdma: route error callbacks through channel BH Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 30/33] dmaengine: altera-msgdma: use channel BH helpers Allen Pais
` (3 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel, Frank Li
Convert the per-channel tasklet to dmaengine_*_bh while keeping the
controller tasklet for non-callback events.
Initialize the channel BH once at probe time, alongside the other
per-channel state, rather than in pl330_alloc_chan_resources(). Unlike
tasklet_setup(), dmaengine_init_bh() must not be called again on an
already initialized channel, and a channel can be released and
re-requested many times. pl330_free_chan_resources() still drains the BH
when the channel is released.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
drivers/dma/pl330.c | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 977a9fde80ad..6e88f5b45fb4 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -417,9 +417,6 @@ enum desc_status {
};
struct dma_pl330_chan {
- /* Schedule desc completion */
- struct tasklet_struct task;
-
/* DMA-Engine Channel */
struct dma_chan chan;
@@ -1575,7 +1572,7 @@ static void dma_pl330_rqcb(struct dma_pl330_desc *desc, enum pl330_op_err err)
spin_unlock_irqrestore(&pch->lock, flags);
- tasklet_schedule(&pch->task);
+ dmaengine_schedule_bh(&pch->chan);
}
static void pl330_dotask(struct tasklet_struct *t)
@@ -2061,14 +2058,15 @@ static inline void fill_queue(struct dma_pl330_chan *pch)
desc->status = DONE;
dev_err(pch->dmac->ddma.dev, "%s:%d Bad Desc(%d)\n",
__func__, __LINE__, desc->txd.cookie);
- tasklet_schedule(&pch->task);
+ dmaengine_schedule_bh(&pch->chan);
}
}
}
-static void pl330_tasklet(struct tasklet_struct *t)
+static void pl330_tasklet(struct dma_chan *chan)
{
- struct dma_pl330_chan *pch = from_tasklet(pch, t, task);
+ struct dma_pl330_chan *pch = container_of(chan, struct dma_pl330_chan,
+ chan);
struct dma_pl330_desc *desc, *_dt;
unsigned long flags;
bool power_down = false;
@@ -2174,8 +2172,6 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
return -ENOMEM;
}
- tasklet_setup(&pch->task, pl330_tasklet);
-
spin_unlock_irqrestore(&pl330->lock, flags);
return 1;
@@ -2355,7 +2351,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
struct pl330_dmac *pl330 = pch->dmac;
unsigned long flags;
- tasklet_kill(&pch->task);
+ dmaengine_kill_bh(&pch->chan);
pm_runtime_get_sync(pch->dmac->ddma.dev);
spin_lock_irqsave(&pl330->lock, flags);
@@ -2491,7 +2487,7 @@ static void pl330_issue_pending(struct dma_chan *chan)
list_splice_tail_init(&pch->submitted_list, &pch->work_list);
spin_unlock_irqrestore(&pch->lock, flags);
- pl330_tasklet(&pch->task);
+ pl330_tasklet(&pch->chan);
}
/*
@@ -3121,6 +3117,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pch->chan.device = pd;
pch->dmac = pl330;
pch->dir = DMA_NONE;
+ dmaengine_init_bh(&pch->chan, pl330_tasklet);
/* Add the channel to the DMAC list */
list_add_tail(&pch->chan.device_node, &pd->channels);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 30/33] dmaengine: altera-msgdma: use channel BH helpers
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (28 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 29/33] dmaengine: pl330: route callbacks via " Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 31/33] dmaengine: hidma: defer callbacks via channel BH Allen Pais
` (2 subsequent siblings)
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Adrian Ng Ho Yin, Stefan Roese, Frank Li
Replace the device tasklet with the per-channel BH helper so completion
handling runs via dmaengine_*_bh.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/altera-msgdma.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/altera-msgdma.c b/drivers/dma/altera-msgdma.c
index 567aea0fac38..9a0741642a7f 100644
--- a/drivers/dma/altera-msgdma.c
+++ b/drivers/dma/altera-msgdma.c
@@ -170,7 +170,6 @@ struct msgdma_sw_desc {
struct msgdma_device {
spinlock_t lock;
struct device *dev;
- struct tasklet_struct irq_tasklet;
struct list_head pending_list;
struct list_head free_list;
struct list_head active_list;
@@ -684,11 +683,12 @@ static int msgdma_alloc_chan_resources(struct dma_chan *dchan)
/**
* msgdma_tasklet - Schedule completion tasklet
- * @t: Pointer to the Altera sSGDMA channel structure
+ * @chan: Pointer to the Altera sSGDMA channel structure
*/
-static void msgdma_tasklet(struct tasklet_struct *t)
+static void msgdma_tasklet(struct dma_chan *chan)
{
- struct msgdma_device *mdev = from_tasklet(mdev, t, irq_tasklet);
+ struct msgdma_device *mdev = container_of(chan, struct msgdma_device,
+ dmachan);
u32 count;
u32 __maybe_unused size;
u32 __maybe_unused status;
@@ -748,7 +748,7 @@ static irqreturn_t msgdma_irq_handler(int irq, void *data)
spin_unlock(&mdev->lock);
}
- tasklet_schedule(&mdev->irq_tasklet);
+ dmaengine_schedule_bh(&mdev->dmachan);
/* Clear interrupt in mSGDMA controller */
iowrite32(MSGDMA_CSR_STAT_IRQ, mdev->csr + MSGDMA_CSR_STATUS);
@@ -766,7 +766,7 @@ static void msgdma_dev_remove(struct msgdma_device *mdev)
return;
devm_free_irq(mdev->dev, mdev->irq, mdev);
- tasklet_kill(&mdev->irq_tasklet);
+ dmaengine_kill_bh(&mdev->dmachan);
list_del(&mdev->dmachan.device_node);
}
@@ -847,7 +847,7 @@ static int msgdma_probe(struct platform_device *pdev)
if (mdev->irq < 0)
return -ENXIO;
- tasklet_setup(&mdev->irq_tasklet, msgdma_tasklet);
+ dmaengine_init_bh(&mdev->dmachan, msgdma_tasklet);
dma_cookie_init(&mdev->dmachan);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 31/33] dmaengine: hidma: defer callbacks via channel BH
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (29 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 30/33] dmaengine: altera-msgdma: use channel BH helpers Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 32/33] dmaengine: qcom-gpi: defer callbacks via vchan Allen Pais
2026-11-10 13:13 ` [PATCH v4 33/33] dmaengine: switchtec: use channel BH helpers Allen Pais
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Sinan Kaya, Frank Li, linux-arm-kernel, linux-arm-msm
Move descriptor callback processing out of the low-level completion
path and schedule it through per-channel BH work.
Since callbacks no longer run synchronously from hidma_callback(), drain
the BH wherever the driver previously relied on that ordering: provide a
device_synchronize() callback so dmaengine_terminate_sync() waits for
deferred callbacks, drain the BH in hidma_free_chan_resources() before
the descriptors are freed, and drain it in hidma_remove() before
hidma_ll_uninit() tears down the TRE pool that the completion path
reads. Kill the low-level completion tasklet before that drain, since
it is what schedules the BH.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
drivers/dma/qcom/hidma.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c
index 85844965f0d8..2cde0cd08b1f 100644
--- a/drivers/dma/qcom/hidma.c
+++ b/drivers/dma/qcom/hidma.c
@@ -155,6 +155,13 @@ static void hidma_process_completed(struct hidma_chan *mchan)
}
}
+static void hidma_chan_bh(struct dma_chan *chan)
+{
+ struct hidma_chan *mchan = to_hidma_chan(chan);
+
+ hidma_process_completed(mchan);
+}
+
/*
* Called once for each submitted descriptor.
* PM is locked once for each descriptor that is currently
@@ -181,7 +188,7 @@ static void hidma_callback(void *data)
}
spin_unlock_irqrestore(&mchan->lock, irqflags);
- hidma_process_completed(mchan);
+ dmaengine_schedule_bh(&mchan->chan);
if (queued) {
pm_runtime_mark_last_busy(dmadev->ddev.dev);
@@ -203,6 +210,7 @@ static int hidma_chan_init(struct hidma_dev *dmadev, u32 dma_sig)
mchan->dmadev = dmadev;
mchan->chan.device = ddev;
dma_cookie_init(&mchan->chan);
+ dmaengine_init_bh(&mchan->chan, hidma_chan_bh);
INIT_LIST_HEAD(&mchan->free);
INIT_LIST_HEAD(&mchan->prepared);
@@ -529,6 +537,11 @@ static int hidma_terminate_all(struct dma_chan *chan)
return rc;
}
+static void hidma_synchronize(struct dma_chan *dmach)
+{
+ dmaengine_kill_bh(dmach);
+}
+
static void hidma_free_chan_resources(struct dma_chan *dmach)
{
struct hidma_chan *mchan = to_hidma_chan(dmach);
@@ -540,6 +553,9 @@ static void hidma_free_chan_resources(struct dma_chan *dmach)
/* terminate running transactions and free descriptors */
hidma_terminate_channel(dmach);
+ /* drain completion work before the descriptors go away */
+ dmaengine_kill_bh(dmach);
+
spin_lock_irqsave(&mchan->lock, irqflags);
/* Move data */
@@ -813,6 +829,7 @@ static int hidma_probe(struct platform_device *pdev)
dmadev->ddev.device_pause = hidma_pause;
dmadev->ddev.device_resume = hidma_resume;
dmadev->ddev.device_terminate_all = hidma_terminate_all;
+ dmadev->ddev.device_synchronize = hidma_synchronize;
dmadev->ddev.copy_align = 8;
/*
@@ -913,6 +930,7 @@ static void hidma_shutdown(struct platform_device *pdev)
static void hidma_remove(struct platform_device *pdev)
{
struct hidma_dev *dmadev = platform_get_drvdata(pdev);
+ struct dma_chan *chan;
pm_runtime_get_sync(dmadev->ddev.dev);
dma_async_device_unregister(&dmadev->ddev);
@@ -922,6 +940,10 @@ static void hidma_remove(struct platform_device *pdev)
hidma_free_msis(dmadev);
tasklet_kill(&dmadev->task);
+ /* the low-level completion tasklet schedules the channel BH */
+ tasklet_kill(&dmadev->lldev->task);
+ list_for_each_entry(chan, &dmadev->ddev.channels, device_node)
+ dmaengine_kill_bh(chan);
hidma_sysfs_uninit(dmadev);
hidma_debug_uninit(dmadev);
hidma_ll_uninit(dmadev->lldev);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 32/33] dmaengine: qcom-gpi: defer callbacks via vchan
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (30 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 31/33] dmaengine: hidma: defer callbacks via channel BH Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 33/33] dmaengine: switchtec: use channel BH helpers Allen Pais
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Frank Li, Dmitry Baryshkov, Miaoqian Lin, Icenowy Zheng,
Bjorn Andersson, Kees Cook, linux-arm-msm
Complete descriptors through virt-dma instead of invoking callbacks
directly from event processing. This preserves the transfer result for
the shared vchan BH, which invokes the callback and frees the
descriptor.
Because callbacks are now deferred, synchronize the vchan before the
channel ring is freed in gpi_free_chan_resources(), so a late callback
cannot observe a torn-down ring, and provide a device_synchronize()
callback so dmaengine_terminate_sync() waits for pending callbacks.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
---
drivers/dma/qcom/gpi.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c
index d3c3d0f35afb..e125b3a83b1a 100644
--- a/drivers/dma/qcom/gpi.c
+++ b/drivers/dma/qcom/gpi.c
@@ -981,8 +981,12 @@ static void gpi_process_imed_data_event(struct gchan *gchan,
result.result = DMA_TRANS_NOERROR;
result.residue = gpi_desc->len - imed_event->length;
- dma_cookie_complete(&vd->tx);
- dmaengine_desc_get_callback_invoke(&vd->tx, &result);
+ spin_lock_irqsave(&gchan->vc.lock, flags);
+ list_del(&vd->node);
+ vd->tx_result = result;
+ vchan_cookie_complete(vd);
+ spin_unlock_irqrestore(&gchan->vc.lock, flags);
+ return;
gpi_free_desc:
spin_lock_irqsave(&gchan->vc.lock, flags);
@@ -1060,8 +1064,12 @@ static void gpi_process_xfer_compl_event(struct gchan *gchan,
result.residue = gpi_desc->len - compl_event->length;
dev_dbg(gpii->gpi_dev->dev, "Residue %d\n", result.residue);
- dma_cookie_complete(&vd->tx);
- dmaengine_desc_get_callback_invoke(&vd->tx, &result);
+ spin_lock_irqsave(&gchan->vc.lock, flags);
+ list_del(&vd->node);
+ vd->tx_result = result;
+ vchan_cookie_complete(vd);
+ spin_unlock_irqrestore(&gchan->vc.lock, flags);
+ return;
gpi_free_desc:
spin_lock_irqsave(&gchan->vc.lock, flags);
@@ -1983,6 +1991,13 @@ static int gpi_ch_init(struct gchan *gchan)
}
/* release all channel resources */
+static void gpi_synchronize(struct dma_chan *chan)
+{
+ struct gchan *gchan = to_gchan(chan);
+
+ vchan_synchronize(&gchan->vc);
+}
+
static void gpi_free_chan_resources(struct dma_chan *chan)
{
struct gchan *gchan = to_gchan(chan);
@@ -2010,6 +2025,9 @@ static void gpi_free_chan_resources(struct dma_chan *chan)
gpi_reset_chan(gchan, GPI_CH_CMD_DE_ALLOC);
}
+ /* drain deferred completions before the ring goes away */
+ vchan_synchronize(&gchan->vc);
+
/* free all allocated memory */
gpi_free_ring(&gchan->ch_ring, gpii);
vchan_free_chan_resources(&gchan->vc);
@@ -2279,6 +2297,7 @@ static int gpi_probe(struct platform_device *pdev)
gpi_dev->dma_device.device_prep_slave_sg = gpi_prep_slave_sg;
gpi_dev->dma_device.device_config = gpi_peripheral_config;
gpi_dev->dma_device.device_terminate_all = gpi_terminate_all;
+ gpi_dev->dma_device.device_synchronize = gpi_synchronize;
gpi_dev->dma_device.dev = gpi_dev->dev;
gpi_dev->dma_device.device_pause = gpi_pause;
gpi_dev->dma_device.device_resume = gpi_resume;
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v4 33/33] dmaengine: switchtec: use channel BH helpers
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
` (31 preceding siblings ...)
2026-11-10 13:13 ` [PATCH v4 32/33] dmaengine: qcom-gpi: defer callbacks via vchan Allen Pais
@ 2026-11-10 13:13 ` Allen Pais
32 siblings, 0 replies; 34+ messages in thread
From: Allen Pais @ 2026-11-10 13:13 UTC (permalink / raw)
To: Vinod Koul
Cc: Allen Pais, Frank Li, Arnd Bergmann, dmaengine, linux-kernel,
Logan Gunthorpe, Kelvin Cao, Frank Li
Replace the per-channel descriptor completion tasklet with the shared
dmaengine BH helpers. Client callbacks remain in softirq context while
using the common scheduling and teardown mechanism.
Signed-off-by: Allen Pais <allen.lkml@gmail.com>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/switchtec_dma.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c
index a386c3a284e6..21b281ea7cd7 100644
--- a/drivers/dma/switchtec_dma.c
+++ b/drivers/dma/switchtec_dma.c
@@ -133,8 +133,6 @@ struct switchtec_dma_chan {
/* Serialize hardware control register access */
spinlock_t hw_ctrl_lock;
- struct tasklet_struct desc_task;
-
/* Serialize descriptor preparation */
spinlock_t submit_lock;
bool ring_active;
@@ -584,6 +582,7 @@ static void switchtec_dma_synchronize(struct dma_chan *chan)
int rc;
+ dmaengine_kill_bh(chan);
switchtec_dma_abort_desc(swdma_chan, 1);
rc = enable_channel(swdma_chan);
@@ -820,9 +819,10 @@ static int switchtec_dma_resume(struct dma_chan *chan)
return ret;
}
-static void switchtec_dma_desc_task(unsigned long data)
+static void switchtec_dma_desc_task(struct dma_chan *chan)
{
- struct switchtec_dma_chan *swdma_chan = (void *)data;
+ struct switchtec_dma_chan *swdma_chan =
+ container_of(chan, struct switchtec_dma_chan, dma_chan);
switchtec_dma_cleanup_completed(swdma_chan);
}
@@ -832,7 +832,7 @@ static irqreturn_t switchtec_dma_isr(int irq, void *chan)
struct switchtec_dma_chan *swdma_chan = chan;
if (swdma_chan->comp_ring_active)
- tasklet_schedule(&swdma_chan->desc_task);
+ dmaengine_schedule_bh(&swdma_chan->dma_chan);
return IRQ_HANDLED;
}
@@ -1060,6 +1060,7 @@ static void switchtec_dma_free_chan_resources(struct dma_chan *chan)
spin_unlock_bh(&swdma_chan->complete_lock);
switchtec_dma_chan_stop(swdma_chan);
+ dmaengine_kill_bh(chan);
switchtec_dma_abort_desc(swdma_chan, 0);
switchtec_dma_free_desc(swdma_chan);
@@ -1086,8 +1087,6 @@ static int switchtec_dma_chan_init(struct switchtec_dma_dev *swdma_dev,
spin_lock_init(&swdma_chan->hw_ctrl_lock);
spin_lock_init(&swdma_chan->submit_lock);
spin_lock_init(&swdma_chan->complete_lock);
- tasklet_init(&swdma_chan->desc_task, switchtec_dma_desc_task,
- (unsigned long)swdma_chan);
swdma_chan->mmio_chan_fw =
swdma_dev->bar + SWITCHTEC_DMAC_CHAN_CFG_STS_OFFSET +
@@ -1135,6 +1134,7 @@ static int switchtec_dma_chan_init(struct switchtec_dma_dev *swdma_dev,
chan = &swdma_chan->dma_chan;
chan->device = dma;
dma_cookie_init(chan);
+ dmaengine_init_bh(chan, switchtec_dma_desc_task);
list_add_tail(&chan->device_node, &dma->channels);
@@ -1157,7 +1157,7 @@ static int switchtec_dma_chan_free(struct pci_dev *pdev,
spin_unlock_bh(&swdma_chan->complete_lock);
pci_free_irq(pdev, swdma_chan->irq, swdma_chan);
- tasklet_kill(&swdma_chan->desc_task);
+ dmaengine_kill_bh(&swdma_chan->dma_chan);
switchtec_dma_chan_stop(swdma_chan);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
end of thread, other threads:[~2026-11-10 13:17 UTC | newest]
Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 01/33] dmaengine: add tasklet-backed channel BH helpers Allen Pais
2026-11-10 13:13 ` [PATCH v4 02/33] dmaengine: back channel BH helpers with WQ_BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 03/33] dmaengine: apple-admac: use dmaengine BH callback Allen Pais
2026-11-10 13:13 ` [PATCH v4 04/33] dmaengine: at_xdmac: move irq bottom half to dmaengine BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 05/33] dmaengine: ep93xx: hook callbacks via " Allen Pais
2026-11-10 13:13 ` [PATCH v4 06/33] dmaengine: fsldma: migrate tasklet to " Allen Pais
2026-11-10 13:13 ` [PATCH v4 07/33] dmaengine: fsl_raid: run completions via " Allen Pais
2026-11-10 13:13 ` [PATCH v4 08/33] dmaengine: imx-dma: flip per-chan tasklet to " Allen Pais
2026-11-10 13:13 ` [PATCH v4 09/33] dmaengine: ioat: convert cleanup " Allen Pais
2026-11-10 13:13 ` [PATCH v4 10/33] dmaengine: mmp_pdma: replace per-chan tasklet with " Allen Pais
2026-11-10 13:13 ` [PATCH v4 11/33] dmaengine: mmp_tdma: hook completions to " Allen Pais
2026-11-10 13:13 ` [PATCH v4 12/33] dmaengine: mv_xor: convert irq tasklet " Allen Pais
2026-11-10 13:13 ` [PATCH v4 13/33] dmaengine: mxs-dma: use dmaengine BH scheduling Allen Pais
2026-11-10 13:13 ` [PATCH v4 14/33] dmaengine: nbpfaxi: switch callbacks to dmaengine BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 15/33] dmaengine: pch_dma: convert tasklet " Allen Pais
2026-11-10 13:13 ` [PATCH v4 16/33] dmaengine: ppc4xx: replace irq tasklet with " Allen Pais
2026-11-10 13:13 ` [PATCH v4 17/33] dmaengine: ste_dma40: convert per-channel tasklet to " Allen Pais
2026-11-10 13:13 ` [PATCH v4 18/33] dmaengine: xgene-dma: wire descriptor cleanup " Allen Pais
2026-11-10 13:13 ` [PATCH v4 19/33] dmaengine: xilinx-dma: use dmaengine BH instead of tasklets Allen Pais
2026-11-10 13:13 ` [PATCH v4 20/33] dmaengine: xilinx-dpdma: kill vchan BH on remove Allen Pais
2026-11-10 13:13 ` [PATCH v4 21/33] dmaengine: zynqmp-dma: switch completion tasklet to dmaengine BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 22/33] dmaengine: tegra20-apb: use channel BH helpers Allen Pais
2026-11-10 13:13 ` [PATCH v4 23/33] dmaengine: timb_dma: route callbacks via channel BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 24/33] dmaengine: txx9dmac: " Allen Pais
2026-11-10 13:13 ` [PATCH v4 25/33] dmaengine: mv_xor_v2: use channel BH helpers Allen Pais
2026-11-10 13:13 ` [PATCH v4 26/33] dmaengine: mpc512x: route callbacks via channel BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 27/33] dmaengine: plx_dma: use channel BH helpers Allen Pais
2026-11-10 13:13 ` [PATCH v4 28/33] dmaengine: sf-pdma: route error callbacks through channel BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 29/33] dmaengine: pl330: route callbacks via " Allen Pais
2026-11-10 13:13 ` [PATCH v4 30/33] dmaengine: altera-msgdma: use channel BH helpers Allen Pais
2026-11-10 13:13 ` [PATCH v4 31/33] dmaengine: hidma: defer callbacks via channel BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 32/33] dmaengine: qcom-gpi: defer callbacks via vchan Allen Pais
2026-11-10 13:13 ` [PATCH v4 33/33] dmaengine: switchtec: use channel BH helpers Allen Pais
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®