Re: [PATCH 09/15] PCI: endpoint: Add pci-ep-dma helper for exported DMA ABI v1

From: Koichiro Den

Date: Fri Aug 21 2026 - 11:44:25 EST


On Fri, Mar 13, 2026 at 01:49:59AM +0900, Koichiro Den wrote:
> Add a generic helper that packages controller-owned DMA resources into a
> peer-visible BAR slice described by exported DMA ABI v1.
>
> pci_epf_alloc_dma() queries EPC auxiliary resources, delegates the
> requested DMA read channels, builds an ABI header in coherent memory,
> and assembles the BAR region list covering the header, controller
> register window, and per-channel descriptor windows. If the controller
> control window is not already BAR-backed, map it into the exported slice
> so the peer still sees a self-contained layout.
>
> The first ABI is designed based on the DesignWare unrolled eDMA model,
> but it is intended to be vendor-neutral. It exports delegated READ
> channels only, which are the channels the host uses to send data from
> host memory into the endpoint.
>
> Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
> ---
> drivers/pci/endpoint/Makefile | 2 +-
> drivers/pci/endpoint/pci-ep-dma.c | 342 ++++++++++++++++++++++++++++++
> include/linux/pci-ep-dma.h | 130 ++++++++++++
> 3 files changed, 473 insertions(+), 1 deletion(-)
> create mode 100644 drivers/pci/endpoint/pci-ep-dma.c
> create mode 100644 include/linux/pci-ep-dma.h
>
> diff --git a/drivers/pci/endpoint/Makefile b/drivers/pci/endpoint/Makefile
> index b4869d52053a..94824f3ed5a1 100644
> --- a/drivers/pci/endpoint/Makefile
> +++ b/drivers/pci/endpoint/Makefile
> @@ -5,5 +5,5 @@
>
> obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS) += pci-ep-cfs.o
> obj-$(CONFIG_PCI_ENDPOINT) += pci-epc-core.o pci-epf-core.o\
> - pci-epc-mem.o functions/
> + pci-epc-mem.o pci-ep-dma.o functions/
> obj-$(CONFIG_PCI_ENDPOINT_MSI_DOORBELL) += pci-ep-msi.o
> diff --git a/drivers/pci/endpoint/pci-ep-dma.c b/drivers/pci/endpoint/pci-ep-dma.c
> new file mode 100644
> index 000000000000..2a996f9b1424
> --- /dev/null
> +++ b/drivers/pci/endpoint/pci-ep-dma.c

---[snip]---

> diff --git a/include/linux/pci-ep-dma.h b/include/linux/pci-ep-dma.h
> new file mode 100644
> index 000000000000..0ef6f9eb8593
> --- /dev/null
> +++ b/include/linux/pci-ep-dma.h

Hi Mani, (Cc: Frank, Niklas)

Perhaps it's time to reincarnate this series, with much of it reworked.

Following the discussion here:
https://lore.kernel.org/r/fg7cma2ccvdgjgcref7qa4q7ocvvume5dteekedikszebkq7rd@uhrjfp3nmmwh/
I am now thinking of dropping the generic pci-ep-dma helper and
include/linux/pci-ep-dma.h altogether, as I understand the direction is to keep
the DMA export within the vNTB/ntb_hw_epf path *as much as possible*.

The old wire layout was consumed by pci-epf-vntb.c, ntb_hw_epf.c, and
dw-edma-aux.c (see patch 13). That is why I put pci-ep-dma.h under
include/linux/. For the rework, I am thinking ntb_hw_epf should decode it and
pass only the resulting resources to dw-edma-aux. This keeps the wire format
between pci-epf-vntb and ntb_hw_epf. At that point, defining the same small
layout in both files might even be preferable to adding another shared header in
include/linux/.

Mani, does this direction look acceptable to you? I would also appreciate your
view on the linked discussion when you have a chance.

Best regards,
Koichiro

> @@ -0,0 +1,130 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Generic exported DMA helper for PCI endpoint functions
> + */
> +
> +#ifndef __LINUX_PCI_EP_DMA_H
> +#define __LINUX_PCI_EP_DMA_H
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/pci-epf.h>
> +
> +#define PCI_EP_DMA_MAGIC 0x4d445045 /* "EPDM" */
> +#define PCI_EP_DMA_MAX_CHANS 8
> +#define PCI_EP_DMA_MAX_REGIONS (2 + PCI_EP_DMA_MAX_CHANS)
> +
> +enum pci_ep_dma_abi {
> + PCI_EP_DMA_ABI_NONE = 0,
> + PCI_EP_DMA_ABI_V1 = 1,
> +};
> +
> +/**
> + * struct pci_ep_dma_locator - peer-visible location of an exported DMA slice
> + * @abi: exported-DMA ABI identifier from &enum pci_ep_dma_abi
> + * @bar: BAR number that carries the exported slice
> + * @flags: ABI-specific locator flags, reserved for future use in v1
> + * @offset: BAR-relative start offset of the exported slice
> + * @size: total size of the exported slice in bytes
> + */
> +struct pci_ep_dma_locator {
> + u8 abi;
> + u8 bar;
> + u16 flags;
> + u32 offset;
> + u32 size;
> +};
> +
> +/**
> + * struct pci_ep_dma_region - one physical region mapped into the exported slice
> + * @offset: BAR-relative start offset of the region within the exported slice
> + * @phys_addr: DMA address to program into the EPC BAR mapping
> + * @size: mapped size in bytes
> + */
> +struct pci_ep_dma_region {
> + u32 offset;
> + dma_addr_t phys_addr;
> + size_t size;
> +};
> +
> +/**
> + * struct pci_ep_dma_chan_info - per-channel descriptor metadata in ABI v1
> + * @desc_bar: BAR number that exposes the descriptor window
> + * @desc_offset: BAR-relative start offset of the descriptor window
> + * @desc_size: descriptor window size in bytes
> + * @desc_phys_addr: physical/DMA address used for the EPC-side BAR mapping
> + */
> +struct pci_ep_dma_chan_info {
> + __le32 desc_bar;
> + __le32 desc_offset;
> + __le32 desc_size;
> + __le64 desc_phys_addr;
> +};
> +
> +/**
> + * struct pci_ep_dma_hdr_v1 - exported DMA header format, version 1
> + * @magic: fixed signature, must be %PCI_EP_DMA_MAGIC
> + * @version: header version, must be 1 for this structure
> + * @hdr_size: size of the populated header structure in bytes
> + * @total_size: total exported-slice size starting at &struct pci_ep_dma_locator.offset
> + * @ctrl_bar: BAR that exposes the live DMA control registers
> + * @ctrl_offset: BAR-relative start offset of the control-register window
> + * @ctrl_size: size of the control-register window in bytes
> + * @irq_count: number of IRQ vectors reserved for the exported DMA provider
> + * @num_chans: number of valid entries in @chans
> + * @chans: per-channel descriptor metadata
> + *
> + * Exported DMA ABI v1 lays out the peer-visible slice as:
> + *
> + * [header][controller window?][descriptor window 0]...[descriptor window N]
> + *
> + * The controller window is optional in that slice. When the live register
> + * block is already exposed through another BAR, @ctrl_bar/@ctrl_offset point at
> + * that BAR directly and no controller subrange is embedded in the exported
> + * slice.
> + *
> + * @chans[] describes a dense prefix of the remote hardware READ-channel
> + * space, ordered by remote hardware READ-channel index starting at 0. A
> + * consumer may map @chans[i] directly to remote READ channel i.
> + */
> +struct pci_ep_dma_hdr_v1 {
> + __le32 magic;
> + __le16 version;
> + __le16 hdr_size;
> + __le32 total_size;
> + __le32 ctrl_bar;
> + __le32 ctrl_offset;
> + __le32 ctrl_size;
> + __le32 irq_count;
> + __le32 num_chans;
> + struct pci_ep_dma_chan_info chans[PCI_EP_DMA_MAX_CHANS];
> +};
> +
> +struct pci_ep_dma {
> + struct pci_epf *epf;
> + enum pci_barno bar;
> + void *hdr_virt;
> + dma_addr_t hdr_phys;
> + size_t hdr_alloc_size;
> + struct pci_ep_dma_locator loc;
> + unsigned int num_regions;
> + u32 num_chans;
> + struct pci_ep_dma_region regions[PCI_EP_DMA_MAX_REGIONS];
> + dma_addr_t ctrl_map_addr;
> + size_t ctrl_map_size;
> + int delegated_chan_ids[PCI_EP_DMA_MAX_CHANS];
> + u8 delegated_num_chans;
> +};
> +
> +struct pci_ep_dma *pci_epf_alloc_dma(struct pci_epf *epf, enum pci_barno bar,
> + u32 offset, u32 req_chans);
> +void pci_epf_free_dma(struct pci_ep_dma *dma);
> +
> +const struct pci_ep_dma_locator *
> +pci_epf_get_dma_locator(const struct pci_ep_dma *dma);
> +
> +unsigned int pci_epf_get_dma_region_count(const struct pci_ep_dma *dma);
> +
> +const struct pci_ep_dma_region *
> +pci_epf_get_dma_regions(const struct pci_ep_dma *dma);
> +
> +#endif /* __LINUX_PCI_EP_DMA_H */
> --
> 2.51.0
>