[PATCH v2 02/64] dmaengine: back channel BH helpers with WQ_BH
Allen Pais <[email protected]> Mon, 27 Jul 2026 13:28:30 -0700
| Newsgroups | org.kernel.vger.dmaengine,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <dccbdfb0da3283455c72521e918ce1e98b77194b.1785183549.git.allen.lkml@gmail.com> |
Replace the tasklet implementation of the channel BH helpers with a dedicated WQ_BH | WQ_PERCPU workqueue. The public dma_chan_*_bh() API and its softirq execution context remain unchanged. Keep the workqueue operations internal to dmaengine. Drain scheduled work in dma_chan_kill_bh() to preserve the completion semantics of tasklet_kill(). Signed-off-by: Allen Pais <[email protected]> --- 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 f43aeecb47f9..8b1b17794956 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 @@ -1428,9 +1430,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); dma_chan_bh_work_fn fn = READ_ONCE(chan->bh_work_fn); if (fn) @@ -1446,7 +1473,7 @@ void dma_chan_init_bh(struct dma_chan *chan, dma_chan_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(dma_chan_init_bh); @@ -1456,8 +1483,7 @@ bool dma_chan_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(dma_chan_schedule_bh); @@ -1466,7 +1492,7 @@ void dma_chan_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(dma_chan_kill_bh); @@ -1666,15 +1692,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 c670781b3150..c87974d5048b 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> /** @@ -339,9 +339,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; @@ -367,7 +367,7 @@ struct dma_chan { void *route_data; void *private; - struct tasklet_struct bh_tasklet; + struct work_struct bh_work; dma_chan_bh_work_fn bh_work_fn; bool bh_work_initialized; }; -- 2.43.0