Re: [PATCH] dma: apple-admac: Convert from tasklet to BH workqueue
From: Frank Li <Frank.li@nxp.com>
Date: 2026-02-06 19:31:08
Also in:
asahi, dmaengine, lkml
On Fri, Feb 06, 2026 at 04:01:37AM -0500, Pat Somaru wrote:
The only generic interface to execute asynchronously in the BH context is tasklet; however, it's marked deprecated and has some design flaws such as the execution code accessing the tasklet item after the execution is complete which can lead to subtle use-after-free in certain usage scenarios and less-developed flush and cancel mechanisms. To replace tasklets, BH workqueue support was recently added. A BH workqueue behaves similarly to regular workqueues except that the queued work items are executed in the BH context. This patch converts drivers/dma/apple-admac.c from tasklet to BH workqueue.
Avoid use words "This patch", Convert apple-admac.c from tasklet to BH workqueue
The Apple ADMAC driver uses a per-channel tasklet to invoke DMA completion callbacks for cyclic transactions. This conversion maintains the same execution semantics while using the modern BH workqueue infrastructure.
...
This patch was tested by:
- Building with allmodconfig: no new warnings (compared to v6.18)
- Building with allyesconfig: no new warnings (compared to v6.18)
- Booting defconfig kernel via vng and running `uname -a`:
Linux virtme-ng 6.18.0-virtme #1 SMP PREEMPT_DYNAMIC 0 x86_64 GNU/LinuxShould move after ---,
Semantically, this is an equivalent conversion and there shouldn't be any user-visible behavior changes. The BH workqueue implementation uses the same softirq infrastructure, and performance-critical networking conversions have shown no measurable performance impact.
Maintainers can apply this directly to the DMA subsystem tree or ack it for the workqueue tree to carry.
This private talk to maintainer, put after --- Sorry, the same comments for https://lore.kernel.org/dmaengine/aYYm2pA6l-ksXvkk@lizhi-Precision-Tower-5810/T/#t (local) Frank
quoted hunk ↗ jump to hunk
Signed-off-by: Pat Somaru <redacted> --- drivers/dma/apple-admac.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)diff --git a/drivers/dma/apple-admac.c b/drivers/dma/apple-admac.c index bd49f0374291..8a0e100d5aaf 100644 --- a/drivers/dma/apple-admac.c +++ b/drivers/dma/apple-admac.c@@ -16,6 +16,7 @@ #include <linux/reset.h> #include <linux/spinlock.h> #include <linux/interrupt.h> +#include <linux/workqueue.h> #include "dmaengine.h"@@ -89,7 +90,7 @@ struct admac_chan { unsigned int no; struct admac_data *host; struct dma_chan chan; - struct tasklet_struct tasklet; + struct work_struct work; u32 carveout;@@ -522,8 +523,8 @@ static int admac_terminate_all(struct dma_chan *chan) adchan->current_tx = NULL; } /* - * Descriptors can only be freed after the tasklet - * has been killed (in admac_synchronize). + * Descriptors can only be freed after the work + * has been cancelled (in admac_synchronize). */ list_splice_tail_init(&adchan->submitted, &adchan->to_free); list_splice_tail_init(&adchan->issued, &adchan->to_free);@@ -543,7 +544,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); + cancel_work_sync(&adchan->work); list_for_each_entry_safe(adtx, _adtx, &head, node) { list_del(&adtx->node);@@ -662,7 +663,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); + queue_work(system_bh_wq, &adchan->work); } spin_unlock_irqrestore(&adchan->lock, flags); }@@ -712,9 +713,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_work(struct work_struct *work) { - struct admac_chan *adchan = from_tasklet(adchan, t, tasklet); + struct admac_chan *adchan = from_work(adchan, work, work); struct admac_tx *adtx; struct dmaengine_desc_callback cb; struct dmaengine_result tx_result;@@ -886,7 +887,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); + INIT_WORK(&adchan->work, admac_chan_work); } err = reset_control_reset(ad->rstc); --2.52.0