[PATCH] dma: plx_dma: Convert from tasklet to BH workqueue

Pat Somaru posted 1 patch 1 day, 8 hours ago
drivers/dma/plx_dma.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
[PATCH] dma: plx_dma: Convert from tasklet to BH workqueue
Posted by Pat Somaru 1 day, 8 hours ago
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/plx_dma.c from tasklet to BH workqueue.

The PLX DMA driver uses a single tasklet to process completed DMA
descriptors in BH context after an interrupt signals descriptor
completion. 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/Linux

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.

Signed-off-by: Pat Somaru <patso@likewhatevs.io>
---
 drivers/dma/plx_dma.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/dma/plx_dma.c b/drivers/dma/plx_dma.c
index 34b6416c3287..be13a7fa5763 100644
--- a/drivers/dma/plx_dma.c
+++ b/drivers/dma/plx_dma.c
@@ -13,6 +13,7 @@
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/workqueue.h>
 
 MODULE_DESCRIPTION("PLX ExpressLane PEX PCI Switch DMA Engine");
 MODULE_VERSION("0.1");
@@ -105,7 +106,7 @@ struct plx_dma_dev {
 	struct dma_chan dma_chan;
 	struct pci_dev __rcu *pdev;
 	void __iomem *bar;
-	struct tasklet_struct desc_task;
+	struct work_struct desc_task;
 
 	spinlock_t ring_lock;
 	bool ring_active;
@@ -241,9 +242,9 @@ 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 work_struct *work)
 {
-	struct plx_dma_dev *plxdev = from_tasklet(plxdev, t, desc_task);
+	struct plx_dma_dev *plxdev = from_work(plxdev, work, desc_task);
 
 	plx_dma_process_desc(plxdev);
 }
@@ -366,7 +367,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);
+		queue_work(system_bh_wq, &plxdev->desc_task);
 
 	writew(status, plxdev->bar + PLX_REG_INTR_STATUS);
 
@@ -472,7 +473,7 @@ static void plx_dma_free_chan_resources(struct dma_chan *chan)
 	if (irq > 0)
 		synchronize_irq(irq);
 
-	tasklet_kill(&plxdev->desc_task);
+	cancel_work_sync(&plxdev->desc_task);
 
 	plx_dma_abort_desc(plxdev);
 
@@ -511,7 +512,7 @@ static int plx_dma_create(struct pci_dev *pdev)
 		goto free_plx;
 
 	spin_lock_init(&plxdev->ring_lock);
-	tasklet_setup(&plxdev->desc_task, plx_dma_desc_task);
+	INIT_WORK(&plxdev->desc_task, plx_dma_desc_task);
 
 	RCU_INIT_POINTER(plxdev->pdev, pdev);
 	plxdev->bar = pcim_iomap_table(pdev)[0];
-- 
2.52.0
Re: [PATCH] dma: plx_dma: Convert from tasklet to BH workqueue
Posted by Logan Gunthorpe 23 hours ago

On 2026-02-06 2:00 a.m., 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/plx_dma.c from tasklet to BH workqueue.
> 
> The PLX DMA driver uses a single tasklet to process completed DMA
> descriptors in BH context after an interrupt signals descriptor
> completion. 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/Linux
> 
> 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.
> 
> Signed-off-by: Pat Somaru <patso@likewhatevs.io>

Looks good to me, thanks!

Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
Re: [PATCH] dma: plx_dma: Convert from tasklet to BH workqueue
Posted by Tejun Heo an hour ago
On Fri, Feb 06, 2026 at 11:43:59AM -0700, Logan Gunthorpe wrote:
> > Signed-off-by: Pat Somaru <patso@likewhatevs.io>
> 
> Looks good to me, thanks!
> 
> Reviewed-by: Logan Gunthorpe <logang@deltatee.com>

How do you want to route the patch? I can take it through the wq tree if
that's preferable.

Thanks.

-- 
tejun
Re: [PATCH] dma: plx_dma: Convert from tasklet to BH workqueue
Posted by Frank Li 1 day ago
On Fri, Feb 06, 2026 at 04:00:58AM -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/plx_dma.c from tasklet to BH workqueue.
>
> The PLX DMA driver uses a single tasklet to process completed DMA
> descriptors in BH context after an interrupt signals descriptor
> completion. 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/Linux
>
> 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.
>
> Signed-off-by: Pat Somaru <patso@likewhatevs.io>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>  drivers/dma/plx_dma.c | 13 +++++++------
>  1 file changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/dma/plx_dma.c b/drivers/dma/plx_dma.c
> index 34b6416c3287..be13a7fa5763 100644
> --- a/drivers/dma/plx_dma.c
> +++ b/drivers/dma/plx_dma.c
> @@ -13,6 +13,7 @@
>  #include <linux/list.h>
>  #include <linux/module.h>
>  #include <linux/pci.h>
> +#include <linux/workqueue.h>
>
>  MODULE_DESCRIPTION("PLX ExpressLane PEX PCI Switch DMA Engine");
>  MODULE_VERSION("0.1");
> @@ -105,7 +106,7 @@ struct plx_dma_dev {
>  	struct dma_chan dma_chan;
>  	struct pci_dev __rcu *pdev;
>  	void __iomem *bar;
> -	struct tasklet_struct desc_task;
> +	struct work_struct desc_task;
>
>  	spinlock_t ring_lock;
>  	bool ring_active;
> @@ -241,9 +242,9 @@ 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 work_struct *work)
>  {
> -	struct plx_dma_dev *plxdev = from_tasklet(plxdev, t, desc_task);
> +	struct plx_dma_dev *plxdev = from_work(plxdev, work, desc_task);
>
>  	plx_dma_process_desc(plxdev);
>  }
> @@ -366,7 +367,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);
> +		queue_work(system_bh_wq, &plxdev->desc_task);
>
>  	writew(status, plxdev->bar + PLX_REG_INTR_STATUS);
>
> @@ -472,7 +473,7 @@ static void plx_dma_free_chan_resources(struct dma_chan *chan)
>  	if (irq > 0)
>  		synchronize_irq(irq);
>
> -	tasklet_kill(&plxdev->desc_task);
> +	cancel_work_sync(&plxdev->desc_task);
>
>  	plx_dma_abort_desc(plxdev);
>
> @@ -511,7 +512,7 @@ static int plx_dma_create(struct pci_dev *pdev)
>  		goto free_plx;
>
>  	spin_lock_init(&plxdev->ring_lock);
> -	tasklet_setup(&plxdev->desc_task, plx_dma_desc_task);
> +	INIT_WORK(&plxdev->desc_task, plx_dma_desc_task);
>
>  	RCU_INIT_POINTER(plxdev->pdev, pdev);
>  	plxdev->bar = pcim_iomap_table(pdev)[0];
> --
> 2.52.0
>