[PATCH v6 0/9] dmaengine: add DMA channel bus and fsl-edma per-channel IOMMU support
From: Peng Fan (OSS)
Date: Thu Nov 12 2026 - 02:50:11 EST
I used iommu-map for eDMA, but iommu experts rejected it:
"Using iommu-map to describe synthetic dma_chan_dev devices that the
kernel creates is the same kind of DT abuse from over here:"
More info: https://lore.kernel.org/all/20260917144804.GI3196566@xxxxxxxx/
So I followed here [2] as Krzysztof wrote for QCOM VPU:
"Thanks Jason for context and detailed arguments. Robin did not chime in
yet, but from what I understood the DT-child-node approach will be the
way we should go here.
I accept above point of view and I am fine with this, thus Vishnu and
Vikash - please go ahead with DT-child-node solution we had some time ago."
[2] https://lore.kernel.org/linux-iommu/c7b956a9-d3e8-4e18-b780-5d08f5cd2ca1@xxxxxxxxxx/
This series adds per-channel DT child node support to fsl-edma and, as a
prerequisite, introduces a proper bus for DMA channel devices.
The fsl-edma change allows each channel to carry its own reg, interrupts
and iommus properties. Power domains remain attached at the parent level.
Parsing the child nodes is straightforward, but per-channel IOMMU
configuration is not: the DMA engine currently creates struct dma_chan_dev
as a class device without any bus, so of_dma_configure() has no bus to probe
the IOMMU with, and the IOMMU core does not have a bus notifier for DMA
channel devices.
Drivers that need per-channel IOMMU support currently work around this by
assigning dev->bus after dmaenginem_async_device_register(). That is unsafe
because bus_add_device() already ran with bus == NULL and did not take a bus
subsystem reference. When the channel device is later unregistered,
bus_remove_device() drops a reference that was never acquired, leading to a
reference count underflow and a potential use-after-free. Using
platform_bus_type is also wrong: platform_match() and platform_uevent()
expect the device to be embedded in a struct platform_device.
A dedicated bus for DMA channel devices solves this. The channel devices are
converted from class devices to bus devices:
- the bus is registered early, before iommu_subsys_init() consumes
iommu_buses[];
- the match callback always reports no match, so no driver binds to a
channel device;
- channel devices are registered on the bus before device_register(), so
bus_add_device() and bus_remove_device() are balanced;
- the bus is added to iommu_buses[], so the existing IOMMU notifier
handles per-channel IOMMU probe and release.
The existing /sys/class/dma interface is preserved with class_compat, the
mechanism provided by the driver core for converting a class device family to
bus devices. A compatibility class named "dma" keeps
/sys/class/dma/dmaNchanM available, while the devices themselves live on the
DMA bus. The bus is named "dmaengine-chan" to avoid colliding with the SuperH DMA
bus, which is also named "dma". The uevent SUBSYSTEM and "subsystem" link
therefore use "dmaengine-chan", while /sys/class/dma is unchanged. dma_dev_groups
moves from the old class to the bus, and chan_dev_release is installed
directly as the device release callback.
While testing controller unbind, a pre-existing fsl-edma ordering bug was
found: fsl_edma_remove() removed all channels from dma_dev.channels before the
managed dma_async_device_unregister() action ran, so the channel devices and
their IOMMU groups were never released. Patch 1 fixes that ordering, which is
needed to make the bind/unbind lifecycle test meaningful.
Runtime-tested on i.MX95: repeated controller bind/unbind cycles with a
local DT based on the child-node conversion removed and recreated the
channel devices as expected, with no refcount, BUG, WARNING or suspicious
kobject diagnostics. The per-channel IOMMU path was exercised with a local
DT adding iommus; that DT update is not part of this series.
The binding and driver support per-channel iommus, but patch 9 does not add
them to the i.MX95 DTS yet. That DT update will be sent separately when the
shared stream ID support are finalized.
I put all the patches in one patchset to give people a full picture.
If need to separate patches into multiple patchsets, I could do that
in next version.
Signed-off-by: Peng Fan <peng.fan@xxxxxxx>
---
Changes in v6:
- Patch 2:
Update commit message include more info regarding bus introduction. (Frank)
name the DMA channel bus "dmaengine-chan" instead of "dma-chan" (Frank).
- Patch 6: new prep patch converting chan_masked tests from BIT() to
BIT_ULL() for channels 32-63 (Frank).
- Patch 7: new prep patch converting fsl-edma to dmaengine_get_dma_device() (Frank).
- Patch 8: Use scoped api, avoid scan channel twice. (Frank)
- Link to v5: https://patch.msgid.link/20261111-edma-iommu-v3-v5-0-2537cebe6ca2@xxxxxxx
Changes in v5:
- Patch 2: Rename bus name to dma-chan per report from Sashiko.
- Patch 6: Address the issue reported by kernel bot:
symbol '__udivdi3' undefined, using div_u64_rem
- Rebased to next-20261110
- Link to v4: https://lore.kernel.org/all/20261103-edma-iommu-v3-v4-0-470415d418c5@xxxxxxxxxxx/
Changes in v4:
- Patch 1, patch 4 and patch 5 are unchanged.
- Patch 2:
Register the DMA channel bus from core_initcall so it exists before any
IOMMU driver can call iommu_device_register(), which iterates
iommu_buses[].
Clean up the DMA unmap pools if dmaengine initialisation fails.
Clarify that the bus match callback always reports no match.
- Patch 3:
Update the ordering rationale for IOMMU drivers that register before
arch_initcall. No functional change beyond patch 2's initcall move.
- Patch 6:
- Validate child register resources: require them to be contained in the
controller resource and aligned to chreg_space_sz.
- Use div_u64() for the channel index calculation and reject values that
do not fit in an int, avoiding 32-bit __aeabi_uldivmod/__udivdi3 issues.
- Ignore child nodes beyond dma-channels, so imx95-navq.dts with
dma-channels = <30> still probes.
- Use BIT_ULL() for the 64-bit chan_masked bitmap, including channels
32-63 on 32-bit builds.
- Set chdev->dma_parms to the controller dma_parms so the per-channel
device keeps the 0x7fff segment size limit.
- Use dev_err_probe() for of_dma_configure() failures.
- Reject channels for which iommus did not result in an IOMMU mapping
instead of setting chan_dma_dev = true silently.
- If child nodes are used, require every unmasked channel to have an
available child node with a valid interrupt; otherwise fail probe with
an error asking for dma-channel-mask.
- Do not fall back to the parent flat interrupt array in child-node mode.
- Keep the shared error IRQ on the parent node.
- Clarify that child power-domains are not handled by this patch.
- Patch 7:
- No functional change. The out-of-range child nodes used by
imx95-navq.dts are handled by patch 6.
- The i.MX95 DTS still does not add per-channel iommus; that update will be
submitted separately.
- Board test overlays are kept out of the upstream series.
- Link to v3: https://patch.msgid.link/20261102-edma-iommu-v3-v3-0-9d0e62a954eb@xxxxxxxxxxx
Changes in V3:
- Patch [1-4] are new patches
- Add R-b for patch 5
- Patch 6: handle of_irq_get() -EPROBE_DEFER, drop the explicit bus
assignment to "chdev->bus" which is resolved by patch [2-4].
- Rebased to linus/master v7.4-rc1
Changes in V2:
- Switch to add child node for dma channel.
- Link to V1: https://lore.kernel.org/dmaengine/20260916-edma-iommu-v1-0-e1731968081e@xxxxxxx/
---
Peng Fan (9):
dmaengine: fsl-edma: unregister channels before vchan cleanup
dmaengine: add a bus for DMA channel devices
iommu: register the DMA channel bus for IOMMU notifications
dmaengine: move channel devices to the DMA channel bus
dt-bindings: dma: fsl,edma: add per-channel child node support
dmaengine: fsl-edma: use BIT_ULL for 64-bit channel mask
dmaengine: fsl-edma: use dmaengine_get_dma_device()
dmaengine: fsl-edma: support per-channel DT child nodes
arm64: dts: imx95: convert eDMA to per-channel child nodes
.../devicetree/bindings/dma/fsl,edma.yaml | 70 +-
arch/arm64/boot/dts/freescale/imx95.dtsi | 774 +++++++++++++++++----
drivers/dma/dmaengine.c | 49 +-
drivers/dma/fsl-edma-common.c | 10 +-
drivers/dma/fsl-edma-common.h | 1 +
drivers/dma/fsl-edma-main.c | 225 +++++-
drivers/dma/tegra186-gpc-dma.c | 1 -
drivers/iommu/iommu.c | 4 +
include/linux/dmaengine.h | 4 +
9 files changed, 979 insertions(+), 159 deletions(-)
---
base-commit: aaa2f954c21b205a0bcd0961e5b2be4e82e58455
change-id: 20261102-edma-iommu-v3-22b673717cf4
Best regards,
--
Peng Fan <peng.fan@xxxxxxx>