From nobody Sun Feb 8 04:31:06 2026 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B680739341E for ; Thu, 8 Jan 2026 08:03:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767859460; cv=none; b=X0dF2Z4E/VI6UHgyaR8MkhWOrYDTi1MnRTWP4qvN8MjtZqC070h1gsWVDKluOgc377ZnxvGNUinkfObiiQemUMGozGrse6LlNXDshIdWwVgnQYiw0SlMoMPwiWys6NsdckWXoBcpfoQtYypZlrK32H7XmbnjwBhFiKhmwRCnNm8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767859460; c=relaxed/simple; bh=zpUZc3T435msSu1SJ1BG3K14/hJMKlYtWpIP176EsMM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CfV8Bq1Nehlw877rmGECnj6s3Q0W1K98P/ITBaMrNKvNWNr8vtWKrnb+/rqNOFMEpiCySNtn9GW3w5p5gLyKjPdHPHEjwvY8IQfflHMlEB9K2C5GMPJgHKI3MTZSmTZtEgjbiSTlw0wv2yWugn09D7bNQdYRQw0RdMC967idIDs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=achkHf65; arc=none smtp.client-ip=209.85.214.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="achkHf65" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-2a1022dda33so19109565ad.2 for ; Thu, 08 Jan 2026 00:03:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1767859421; x=1768464221; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=2/tzbOA+1IHkoRIw/lhz1F5yQGhPM9qE3QnUiAF+STg=; b=achkHf65Xu5EcX0K9gbCRrPsQDpuHEF8bhGboZTg7DqfH7VmfabS/frkTsWeBf0Dqh iypsGMKkWPhUQ8CYMasa/5RwP7DOBYn1/TM1M4hFer4OJ6MYpJ1gA/RT6m00UXKpmJDu VnvifyJhyQn5Ckgkeo2C6oYwZUQ/CiATZ5aWe2bPcqhM5pKcuw0z0Gp5bQcol3J046aF uwSxLSkYjm4wil1BEuIRsWkC66Wzx5vp7JMX5PMIXPHwIdtvxmh1AKy21GABzHhaGDXj z9hlYQe6qMNz7fAfzebnCSIaoS27kRRtDHl0hKUpNEw+i3mDfkArz4qduWBe05G0Qipq p30A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767859421; x=1768464221; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=2/tzbOA+1IHkoRIw/lhz1F5yQGhPM9qE3QnUiAF+STg=; b=kh3XaM2+A5t4XNAvlQRU6a60AfzzKWyXWq2f+bcnaDnZnXwqVtbR+jf7JkaLcaDsQ+ zS64yRvwCwz8P0xK0EJysHrhjXPrb7L5TcadKQ34pnT3lI0+e03zS8LGhIE0tE40h9Bn Hmu6pHz7/LqMBGadNT5WDIDeW5OThpWhrkjntjOg+fq9VMHgi2Z31DMfHC+X2P2aU8eE nhh910W3O0k0iM7168NfvAWDR5T2LWRmvpuhDplbtMin7J1F28NKBjNYKy3yd9a/asrq 8B6xU1tORKI+kfSIWDk0uUnmsssJ6lD6wcYHgF2R6L/D7axZQFsWDBzt+zHxHR66o2eS VLTQ== X-Forwarded-Encrypted: i=1; AJvYcCWuzy1nlfW9txMv29SFBIEbAJDSrRMdXRZx/bcU7VVlR58apsZzvGYczkbK1AKOpdP3tWFHR6o8TzT2mhY=@vger.kernel.org X-Gm-Message-State: AOJu0Yz1POhlRhyE/qTk3Y2S4PfWkqvvOMA6ZhoUEtfYrJ4iFbhq3BeA Lz328dw540PBhkEUIjUqM4CeXAM26J4fIRm/Z/ATMqoCiGXpKMtY2+8t X-Gm-Gg: AY/fxX5y7+AFeD6feQSkI6FVbQI35wDrnKI0Etf1i9FUwNQSDFv3+pUx6/Z7QTzjOoc kek5bhBeHZ/lvloK0Zw9Hxg/VbQL4lT8M040LYgyqwiAHeUnd2elcYgvnzvLktrvpbSzbz/z715 gm+vw67rmW6+UVh5BdqVg8K2r/On7BXdNdAY6s6QL1Dq3w7lQ58HcdsC1aeklUCHpjMqAd2GgeY qlQ412LBa6+Cb50kXDhiwFXWRz+o9jG96nfIM8aVrm7mP4LraA990v/NnSOOIyUe0xVoCc478c2 /FH4C0Vo6fKXvKXLGrxrveydguN+0Ly52sri+8oIcR4q6YTxG4W0FJ9J1rVSGxrWeJpaQVVBNAZ gSmFCC+B9X7mYu49hE/6YzZmkwUllX0uX6/fyOGI4EijsjTeR0Z8bNd1Oh1dgfAVe6CD3Dh9ydI Xa2z3eP5SkuMf1JEPD3P9Zzxc= X-Google-Smtp-Source: AGHT+IHZ6BlQf1lnXJWcoLZR2Hea1KpPth6/85Sk8DpSOVG+0om7ExNZg57MjmwKzD3J7N+NTbRKbQ== X-Received: by 2002:a17:903:244d:b0:297:f8d9:aad7 with SMTP id d9443c01a7336-2a3ee4a1f19mr47039785ad.50.1767859417384; Thu, 08 Jan 2026 00:03:37 -0800 (PST) Received: from cryptic.lan ([2001:569:7e17:d700:9e5a:c54:de19:a242]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2a3e3cb2fe3sm71065775ad.59.2026.01.08.00.03.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 00:03:35 -0800 (PST) From: Allen Pais To: vkoul@kernel.org Cc: arnd@arndb.de, kees@kernel.org, dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org, Allen Pais Subject: [RFC PATCH 1/1] dmaengine: introduce dmaengine_bh_wq and bh helpers Date: Thu, 8 Jan 2026 00:03:31 -0800 Message-ID: <20260108080332.2341725-2-allen.lkml@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260108080332.2341725-1-allen.lkml@gmail.com> References: <20260108080332.2341725-1-allen.lkml@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Create a dedicated dmaengine bottom-half workqueue (WQ_BH | WQ_PERCPU) and provide helper APIs for queue/flush/cancel of BH work items. Add per-channel BH helpers in dma_chan so drivers can schedule a BH callback without maintaining their own tasklets. Convert virt-dma to use the new per-channel BH helpers and remove the per-channel tasklet. Update existing drivers that only need tasklet teardown to use dma_chan_kill_bh(). This provides a common BH execution path for dmaengine and establishes the base for converting remaining DMA tasklets to workqueue-based BHs. Signed-off-by: Allen Pais --- drivers/dma/amd/qdma/qdma.c | 1 + drivers/dma/bcm2835-dma.c | 2 +- drivers/dma/dmaengine.c | 109 +++++++++++++++++- .../dma/dw-axi-dmac/dw-axi-dmac-platform.c | 2 +- drivers/dma/dw-edma/dw-edma-core.c | 2 +- drivers/dma/hisi_dma.c | 2 +- drivers/dma/img-mdc-dma.c | 2 +- drivers/dma/imx-sdma.c | 2 +- drivers/dma/k3dma.c | 2 +- drivers/dma/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 | 2 +- drivers/dma/sa11x0-dma.c | 2 +- drivers/dma/sprd-dma.c | 2 +- drivers/dma/sun6i-dma.c | 2 +- drivers/dma/tegra186-gpc-dma.c | 2 +- drivers/dma/tegra210-adma.c | 2 +- drivers/dma/ti/k3-udma.c | 8 +- drivers/dma/ti/omap-dma.c | 2 +- drivers/dma/virt-dma.c | 6 +- drivers/dma/virt-dma.h | 8 +- include/linux/dmaengine.h | 50 ++++++++ 27 files changed, 189 insertions(+), 39 deletions(-) diff --git a/drivers/dma/amd/qdma/qdma.c b/drivers/dma/amd/qdma/qdma.c index 8fb2d5e1df20..b57f8ebf2446 100644 --- a/drivers/dma/amd/qdma/qdma.c +++ b/drivers/dma/amd/qdma/qdma.c @@ -13,6 +13,7 @@ #include #include #include +#include =20 #include "qdma.h" =20 diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 321748e2983e..08a206cfbb76 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -846,7 +846,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); + dma_chan_kill_bh(&c->vc.chan); } =20 dma_unmap_page_attrs(od->ddev.dev, od->zero_page, PAGE_SIZE, diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index ca13cd39330b..d3df4e943d37 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -54,6 +54,7 @@ #include #include #include +#include =20 #include "dmaengine.h" =20 @@ -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; =20 /* --- debugfs implementation --- */ #ifdef CONFIG_DEBUG_FS @@ -1425,6 +1427,92 @@ static void dmaengine_destroy_unmap_pool(void) } } =20 +static void dmaengine_destroy_bh_wq(void) +{ + if (!dmaengine_bh_wq) + return; + + destroy_workqueue(dmaengine_bh_wq); + dmaengine_bh_wq =3D NULL; +} + +bool dmaengine_queue_bh_work(struct work_struct *work) +{ + if (WARN_ON(!dmaengine_bh_wq)) + return false; + + return queue_work(dmaengine_bh_wq, work); +} +EXPORT_SYMBOL_GPL(dmaengine_queue_bh_work); + +void dmaengine_flush_bh_work(struct work_struct *work) +{ + if (!work) + return; + + flush_work(work); +} +EXPORT_SYMBOL_GPL(dmaengine_flush_bh_work); + +void dmaengine_cancel_bh_work_sync(struct work_struct *work) +{ + if (!work) + return; + + cancel_work_sync(work); +} +EXPORT_SYMBOL_GPL(dmaengine_cancel_bh_work_sync); + +static void dma_chan_bh_entry(struct work_struct *work) +{ + struct dma_chan *chan =3D container_of(work, struct dma_chan, bh_work); + dma_chan_bh_work_fn fn =3D READ_ONCE(chan->bh_work_fn); + + if (fn) + fn(chan); +} + +void dma_chan_init_bh(struct dma_chan *chan, dma_chan_bh_work_fn fn) +{ + if (WARN_ON(!fn)) + return; + + if (WARN_ON(chan->bh_work_initialized)) + return; + + chan->bh_work_fn =3D fn; + INIT_WORK(&chan->bh_work, dma_chan_bh_entry); + chan->bh_work_initialized =3D true; +} +EXPORT_SYMBOL_GPL(dma_chan_init_bh); + +bool dma_chan_schedule_bh(struct dma_chan *chan) +{ + if (WARN_ON(!chan->bh_work_initialized)) + return false; + + return dmaengine_queue_bh_work(&chan->bh_work); +} +EXPORT_SYMBOL_GPL(dma_chan_schedule_bh); + +void dma_chan_flush_bh(struct dma_chan *chan) +{ + if (!chan->bh_work_initialized) + return; + + dmaengine_flush_bh_work(&chan->bh_work); +} +EXPORT_SYMBOL_GPL(dma_chan_flush_bh); + +void dma_chan_kill_bh(struct dma_chan *chan) +{ + if (!chan->bh_work_initialized) + return; + + dmaengine_cancel_bh_work_sync(&chan->bh_work); +} +EXPORT_SYMBOL_GPL(dma_chan_kill_bh); + static int __init dmaengine_init_unmap_pool(void) { int i; @@ -1621,15 +1709,28 @@ EXPORT_SYMBOL_GPL(dma_run_dependencies); =20 static int __init dma_bus_init(void) { - int err =3D dmaengine_init_unmap_pool(); + int err; =20 + dmaengine_bh_wq =3D alloc_workqueue("dmaengine_bh", + WQ_BH | WQ_PERCPU, 0); + if (!dmaengine_bh_wq) + return -ENOMEM; + + err =3D dmaengine_init_unmap_pool(); if (err) - return err; + goto err_destroy_wq; =20 err =3D class_register(&dma_devclass); - if (!err) - dmaengine_debugfs_init(); + if (err) + goto err_destroy_pool; =20 + 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/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/d= w-axi-dmac/dw-axi-dmac-platform.c index b23536645ff7..42018b21d2ab 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -1649,7 +1649,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); + dma_chan_kill_bh(&chan->vc.chan); } } =20 diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-ed= ma-core.c index 8e5f7defa6b6..378650bcc430 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -1015,7 +1015,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); + dma_chan_kill_bh(&chan->vc.chan); list_del(&chan->vc.chan.device_node); } =20 diff --git a/drivers/dma/hisi_dma.c b/drivers/dma/hisi_dma.c index 25a4134be36b..a15491945329 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 *h= dma_dev) =20 for (i =3D 0; i < hdma_dev->chan_num; i++) { hisi_dma_disable_qp(hdma_dev, i); - tasklet_kill(&hdma_dev->chan[i].vc.task); + dma_chan_kill_bh(&hdma_dev->chan[i].vc.chan); } } =20 diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c index fd55bcd060ab..d4f6d839acca 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 *pd= ev) =20 devm_free_irq(&pdev->dev, mchan->irq, mchan); =20 - tasklet_kill(&mchan->vc.task); + dma_chan_kill_bh(&mchan->vc.chan); } =20 pm_runtime_disable(&pdev->dev); diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index ed9e56de5a9b..9cbc95fbb4ee 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -2427,7 +2427,7 @@ static void sdma_remove(struct platform_device *pdev) for (i =3D 0; i < MAX_DMA_CHANNELS; i++) { struct sdma_channel *sdmac =3D &sdma->channel[i]; =20 - tasklet_kill(&sdmac->vc.task); + dma_chan_kill_bh(&sdmac->vc.chan); sdma_free_chan_resources(&sdmac->vc.chan); } =20 diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index 0f9cd7815f88..36d5df545b57 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -981,7 +981,7 @@ static void k3_dma_remove(struct platform_device *op) =20 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); + dma_chan_kill_bh(&c->vc.chan); } tasklet_kill(&d->task); clk_disable_unprepare(d->clk); diff --git a/drivers/dma/loongson1-apb-dma.c b/drivers/dma/loongson1-apb-dm= a.c index 255fe7eca212..49a78ea1514f 100644 --- a/drivers/dma/loongson1-apb-dma.c +++ b/drivers/dma/loongson1-apb-dma.c @@ -552,7 +552,7 @@ static void ls1x_dma_chan_remove(struct ls1x_dma *dma) =20 if (chan->vc.chan.device =3D=3D &dma->ddev) { list_del(&chan->vc.chan.device_node); - tasklet_kill(&chan->vc.task); + dma_chan_kill_bh(&chan->vc.chan); } } } diff --git a/drivers/dma/mediatek/mtk-cqdma.c b/drivers/dma/mediatek/mtk-cq= dma.c index 9f0c41ca7770..3ba54df12bae 100644 --- a/drivers/dma/mediatek/mtk-cqdma.c +++ b/drivers/dma/mediatek/mtk-cqdma.c @@ -895,7 +895,7 @@ static void mtk_cqdma_remove(struct platform_device *pd= ev) vc =3D &cqdma->vc[i]; =20 list_del(&vc->vc.chan.device_node); - tasklet_kill(&vc->vc.task); + dma_chan_kill_bh(&vc->vc.chan); } =20 /* disable interrupt */ diff --git a/drivers/dma/mediatek/mtk-hsdma.c b/drivers/dma/mediatek/mtk-hs= dma.c index fa77bb24a430..0bbf865de24b 100644 --- a/drivers/dma/mediatek/mtk-hsdma.c +++ b/drivers/dma/mediatek/mtk-hsdma.c @@ -1020,7 +1020,7 @@ static void mtk_hsdma_remove(struct platform_device *= pdev) vc =3D &hsdma->vc[i]; =20 list_del(&vc->vc.chan.device_node); - tasklet_kill(&vc->vc.task); + dma_chan_kill_bh(&vc->vc.chan); } =20 /* Disable DMA interrupt */ diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/m= tk-uart-apdma.c index 08e15177427b..257b9b77cc57 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 d= ma_chan *chan) =20 free_irq(c->irq, chan); =20 - tasklet_kill(&c->vc.task); + dma_chan_kill_bh(&c->vc.chan); =20 vchan_free_chan_resources(&c->vc); =20 @@ -463,7 +463,7 @@ static void mtk_uart_apdma_free(struct mtk_uart_apdmade= v *mtkd) struct mtk_chan, vc.chan.device_node); =20 list_del(&c->vc.chan.device_node); - tasklet_kill(&c->vc.task); + dma_chan_kill_bh(&c->vc.chan); } } =20 diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c index 57cec757d8f5..4e6ebf990688 100644 --- a/drivers/dma/owl-dma.c +++ b/drivers/dma/owl-dma.c @@ -1055,7 +1055,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); + dma_chan_kill_bh(&vchan->vc.chan); } } =20 diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c index 249296389771..634b723e4891 100644 --- a/drivers/dma/pxa_dma.c +++ b/drivers/dma/pxa_dma.c @@ -1218,7 +1218,7 @@ static void pxad_free_channels(struct dma_device *dma= dev) 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); + dma_chan_kill_bh(&c->vc.chan); } } =20 diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c index 2cf060174795..6ec82adb89ce 100644 --- a/drivers/dma/qcom/bam_dma.c +++ b/drivers/dma/qcom/bam_dma.c @@ -1377,7 +1377,7 @@ static int bam_dma_probe(struct platform_device *pdev) dma_async_device_unregister(&bdev->common); err_bam_channel_exit: for (i =3D 0; i < bdev->num_channels; i++) - tasklet_kill(&bdev->channels[i].vc.task); + dma_chan_kill_bh(&bdev->channels[i].vc.chan); err_tasklet_kill: tasklet_kill(&bdev->task); err_disable_clk: @@ -1403,7 +1403,7 @@ static void bam_dma_remove(struct platform_device *pd= ev) =20 for (i =3D 0; i < bdev->num_channels; i++) { bam_dma_terminate_all(&bdev->channels[i].vc.chan); - tasklet_kill(&bdev->channels[i].vc.task); + dma_chan_kill_bh(&bdev->channels[i].vc.chan); =20 if (!bdev->channels[i].fifo_virt) continue; diff --git a/drivers/dma/qcom/qcom_adm.c b/drivers/dma/qcom/qcom_adm.c index 6be54fddcee1..0f9e906bd463 100644 --- a/drivers/dma/qcom/qcom_adm.c +++ b/drivers/dma/qcom/qcom_adm.c @@ -919,7 +919,7 @@ 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)); =20 - tasklet_kill(&adev->channels[i].vc.task); + dma_chan_kill_bh(&adev->channels[i].vc.chan); adm_terminate_all(&adev->channels[i].vc.chan); } =20 diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index dc1a9a05252e..42940079efbf 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -893,7 +893,7 @@ static void sa11x0_dma_free_channels(struct dma_device = *dmadev) =20 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); + dma_chan_kill_bh(&c->vc.chan); kfree(c); } } diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 6207e0b185e1..5124fe0a93bf 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -1253,7 +1253,7 @@ static void sprd_dma_remove(struct platform_device *p= dev) 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); + dma_chan_kill_bh(&c->vc.chan); } =20 of_dma_controller_free(pdev->dev.of_node); diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 2215ff877bf7..6b3ab99272a5 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -1073,7 +1073,7 @@ static inline void sun6i_dma_free(struct sun6i_dma_de= v *sdev) struct sun6i_vchan *vchan =3D &sdev->vchans[i]; =20 list_del(&vchan->vc.chan.device_node); - tasklet_kill(&vchan->vc.task); + dma_chan_kill_bh(&vchan->vc.chan); } } =20 diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index 4d6fe0efa76e..3b9dc64eb635 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -1279,7 +1279,7 @@ static void tegra_dma_free_chan_resources(struct dma_= chan *dc) tegra_dma_terminate_all(dc); synchronize_irq(tdc->irq); =20 - tasklet_kill(&tdc->vc.task); + dma_chan_kill_bh(&tdc->vc.chan); tdc->config_init =3D false; tdc->slave_id =3D -1; tdc->sid_dir =3D DMA_TRANS_NONE; diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index d0e8bb27a03b..67ccb1d48361 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -793,7 +793,7 @@ static void tegra_adma_free_chan_resources(struct dma_c= han *dc) =20 tegra_adma_terminate_all(dc); vchan_free_chan_resources(&tdc->vc); - tasklet_kill(&tdc->vc.task); + dma_chan_kill_bh(&tdc->vc.chan); free_irq(tdc->irq, tdc); pm_runtime_put(tdc2dev(tdc)); =20 diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index aa2dc762140f..89dd7926705d 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -4045,9 +4045,9 @@ static void udma_desc_pre_callback(struct virt_dma_ch= an *vc, * This tasklet 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 =3D from_tasklet(vc, t, task); + struct virt_dma_chan *vc =3D 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) } =20 vchan_free_chan_resources(&uc->vc); - tasklet_kill(&uc->vc.task); + dma_chan_kill_bh(&uc->vc.chan); =20 bcdma_free_bchan_resources(uc); udma_free_tx_resources(uc); @@ -5628,7 +5628,7 @@ static int udma_probe(struct platform_device *pdev) return -ENOMEM; vchan_init(&uc->vc, &ud->ddev); /* Use custom vchan completion handling */ - tasklet_setup(&uc->vc.task, udma_vchan_complete); + dma_chan_init_bh(&uc->vc.chan, 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 8c023c6e623a..f6aee92071e2 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); =20 list_del(&c->vc.chan.device_node); - tasklet_kill(&c->vc.task); + dma_chan_kill_bh(&c->vc.chan); kfree(c); } } diff --git a/drivers/dma/virt-dma.c b/drivers/dma/virt-dma.c index 7961172a780d..c311397f3bd7 100644 --- a/drivers/dma/virt-dma.c +++ b/drivers/dma/virt-dma.c @@ -80,9 +80,9 @@ EXPORT_SYMBOL_GPL(vchan_find_desc); * This tasklet handles the completion of a DMA descriptor by * calling 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 =3D from_tasklet(vc, t, task); + struct virt_dma_chan *vc =3D to_virt_chan(chan); struct virt_dma_desc *vd, *_vd; struct dmaengine_desc_callback cb; LIST_HEAD(head); @@ -131,7 +131,7 @@ void vchan_init(struct virt_dma_chan *vc, struct dma_de= vice *dmadev) INIT_LIST_HEAD(&vc->desc_completed); INIT_LIST_HEAD(&vc->desc_terminated); =20 - tasklet_setup(&vc->task, vchan_complete); + dma_chan_init_bh(&vc->chan, vchan_complete); =20 vc->chan.device =3D 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 59d9eabc8b67..5299fb7367ca 100644 --- a/drivers/dma/virt-dma.h +++ b/drivers/dma/virt-dma.h @@ -8,7 +8,6 @@ #define VIRT_DMA_H =20 #include -#include =20 #include "dmaengine.h" =20 @@ -21,7 +20,6 @@ struct virt_dma_desc { =20 struct virt_dma_chan { struct dma_chan chan; - struct tasklet_struct task; void (*desc_free)(struct virt_dma_desc *); =20 spinlock_t lock; @@ -106,7 +104,7 @@ static inline void vchan_cookie_complete(struct virt_dm= a_desc *vd) vd, cookie); list_add_tail(&vd->node, &vc->desc_completed); =20 - tasklet_schedule(&vc->task); + dma_chan_schedule_bh(&vc->chan); } =20 /** @@ -137,7 +135,7 @@ static inline void vchan_cyclic_callback(struct virt_dm= a_desc *vd) struct virt_dma_chan *vc =3D to_virt_chan(vd->tx.chan); =20 vc->cyclic =3D vd; - tasklet_schedule(&vc->task); + dma_chan_schedule_bh(&vc->chan); } =20 /** @@ -223,7 +221,7 @@ static inline void vchan_synchronize(struct virt_dma_ch= an *vc) LIST_HEAD(head); unsigned long flags; =20 - tasklet_kill(&vc->task); + dma_chan_kill_bh(&vc->chan); =20 spin_lock_irqsave(&vc->lock, flags); =20 diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 99efe2b9b4ea..00ad92a73c3b 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -12,6 +12,7 @@ #include #include #include +#include #include =20 /** @@ -295,6 +296,10 @@ enum dma_desc_metadata_mode { DESC_METADATA_ENGINE =3D BIT(1), }; =20 +struct dma_chan; + +typedef void (*dma_chan_bh_work_fn)(struct dma_chan *chan); + /** * struct dma_chan_percpu - the per-CPU part of struct dma_chan * @memcpy_count: transaction counter @@ -334,6 +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_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; @@ -359,6 +367,9 @@ struct dma_chan { void *route_data; =20 void *private; + struct work_struct bh_work; + dma_chan_bh_work_fn bh_work_fn; + bool bh_work_initialized; }; =20 /** @@ -1528,6 +1539,14 @@ struct dma_chan *devm_dma_request_chan(struct device= *dev, const char *name); =20 void dma_release_channel(struct dma_chan *chan); int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps); +bool dmaengine_queue_bh_work(struct work_struct *work); +void dmaengine_flush_bh_work(struct work_struct *work); +void dmaengine_cancel_bh_work_sync(struct work_struct *work); + +void dma_chan_init_bh(struct dma_chan *chan, dma_chan_bh_work_fn fn); +bool dma_chan_schedule_bh(struct dma_chan *chan); +void dma_chan_flush_bh(struct dma_chan *chan); +void dma_chan_kill_bh(struct dma_chan *chan); #else static inline struct dma_chan *dma_find_channel(enum dma_transaction_type = tx_type) { @@ -1575,6 +1594,37 @@ static inline int dma_get_slave_caps(struct dma_chan= *chan, { return -ENXIO; } + +static inline bool dmaengine_queue_bh_work(struct work_struct *work) +{ + return false; +} + +static inline void dmaengine_flush_bh_work(struct work_struct *work) +{ +} + +static inline void dmaengine_cancel_bh_work_sync(struct work_struct *work) +{ +} + +static inline void dma_chan_init_bh(struct dma_chan *chan, + dma_chan_bh_work_fn fn) +{ +} + +static inline bool dma_chan_schedule_bh(struct dma_chan *chan) +{ + return false; +} + +static inline void dma_chan_flush_bh(struct dma_chan *chan) +{ +} + +static inline void dma_chan_kill_bh(struct dma_chan *chan) +{ +} #endif =20 static inline int dmaengine_desc_set_reuse(struct dma_async_tx_descriptor = *tx) --=20 2.43.0