[PATCH v7 04/10] PCI: endpoint: Add API to delegate EPC DMA channels to the host
From: Koichiro Den
Date: Thu Aug 13 2026 - 02:41:53 EST
Some endpoint functions expose an EPC-integrated DMA controller to the
host. The endpoint function reserves the local DMA engine channel, but
should not need to know the backend-specific mechanism used to hand its
programming interface to the host.
Add pci_epc_delegate_dma_chan() and pci_epc_reclaim_dma_chan().
Add matching EPC operations. They pass an already reserved dma_chan to
the backend. This lets generic endpoint functions delegate channels
without depending on a specific DMA engine driver. The caller retains
the dmaengine reservation until reclaim completes.
Let reclaim callers tell the backend whether hardware exposed to host
programming needs to be quiesced before local ownership is restored.
The quiesce may cover a provider-defined sharing group, so callers must
hold every delegated member and stop peer programming before reclaim.
Bind failure paths that only unwind local reservations can skip quiesce.
Reclaim is best-effort because it runs from teardown paths that cannot be
aborted. The backend always consumes the delegation and reports any
quiesce failure itself.
Suggested-by: Frank Li <Frank.Li@xxxxxxx>
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v7:
- Take an already reserved DMA engine channel and leave its lifetime to
the endpoint function, per discussion with Frank on v6 patch 4.
- Remove the opaque pci_epc_dma_chan handle and pass the EPC and function
identity explicitly on reclaim.
@Frank, I haven't picked up your R-b tag because the API has changed.
Please take another look, thanks.
drivers/pci/endpoint/pci-epc-core.c | 70 +++++++++++++++++++++++++++++
include/linux/pci-epc.h | 15 +++++++
2 files changed, 85 insertions(+)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 831b40458dcd..d0bcf3dc7039 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -236,6 +236,76 @@ int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
}
EXPORT_SYMBOL_GPL(pci_epc_get_aux_resources);
+/**
+ * pci_epc_delegate_dma_chan() - delegate an EPC-owned DMA channel to the host
+ * @epc: EPC device
+ * @func_no: function number
+ * @vfunc_no: virtual function number
+ * @chan: DMA engine channel reserved by the endpoint function
+ *
+ * Some EPC backends integrate DMA channels that can be exposed to the host.
+ * This helper asks the backend to hand a channel already reserved through
+ * dmaengine to the host and place it in a state where the host driver may
+ * program it through the exposed register window.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if the backend does not support DMA channel
+ * delegation, or another -errno on failure.
+ */
+int pci_epc_delegate_dma_chan(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct dma_chan *chan)
+{
+ int ret;
+
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
+ return -EINVAL;
+
+ if (!chan)
+ return -EINVAL;
+
+ if (!epc->ops->delegate_dma_chan || !epc->ops->reclaim_dma_chan)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&epc->lock);
+ ret = epc->ops->delegate_dma_chan(epc, func_no, vfunc_no, chan);
+ mutex_unlock(&epc->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pci_epc_delegate_dma_chan);
+
+/**
+ * pci_epc_reclaim_dma_chan() - reclaim a delegated EPC-owned DMA channel
+ * @epc: EPC device
+ * @func_no: function number used when delegating @chan
+ * @vfunc_no: virtual function number used when delegating @chan
+ * @chan: delegated DMA engine channel
+ * @quiesce: quiesce affected hardware before reclaiming the channel
+ *
+ * Reclaim a channel previously delegated to the host. Set @quiesce for channels
+ * that may have been exposed to host programming. Bind failure paths that are
+ * unwinding local reservations before exposure may leave it clear.
+ *
+ * Some providers share enable and interrupt controls among channels. The
+ * caller must retain every delegated member of that sharing group and prevent
+ * further peer programming before requesting reclaim.
+ *
+ * Reclaim is best-effort because it runs from teardown paths that cannot be
+ * aborted. The backend always consumes the delegation and reports any quiesce
+ * failure itself. The caller retains the DMA engine channel reservation and
+ * releases it after this function returns.
+ */
+void pci_epc_reclaim_dma_chan(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct dma_chan *chan, bool quiesce)
+{
+ if (!epc || !chan || !epc->ops->reclaim_dma_chan)
+ return;
+
+ mutex_lock(&epc->lock);
+ epc->ops->reclaim_dma_chan(epc, func_no, vfunc_no, chan, quiesce);
+ mutex_unlock(&epc->lock);
+}
+EXPORT_SYMBOL_GPL(pci_epc_reclaim_dma_chan);
+
/**
* pci_epc_stop() - stop the PCI link
* @epc: the link of the EPC device that has to be stopped
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 3b60cf6628e2..3b0aaaa9a259 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -11,6 +11,8 @@
#include <linux/pci-epf.h>
+struct device;
+struct dma_chan;
struct pci_epc;
enum pci_epc_interface_type {
@@ -176,6 +178,11 @@ struct pci_epc_aux_resource {
* @get_aux_resources_count: ops to get the number of controller-owned
* auxiliary resources
* @get_aux_resources: ops to retrieve controller-owned auxiliary resources
+ * @delegate_dma_chan: ops to delegate a controller-owned DMA channel to the
+ * host
+ * @reclaim_dma_chan: ops to reclaim a previously delegated DMA channel.
+ * The callback quiesces the channel or its provider-defined
+ * sharing group when requested.
* @owner: the module owner containing the ops
*/
struct pci_epc_ops {
@@ -212,6 +219,10 @@ struct pci_epc_ops {
int (*get_aux_resources)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epc_aux_resource *resources,
int num_resources);
+ int (*delegate_dma_chan)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct dma_chan *chan);
+ void (*reclaim_dma_chan)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct dma_chan *chan, bool quiesce);
struct module *owner;
};
@@ -445,6 +456,10 @@ int pci_epc_get_aux_resources_count(struct pci_epc *epc, u8 func_no,
int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epc_aux_resource *resources,
int num_resources);
+int pci_epc_delegate_dma_chan(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct dma_chan *chan);
+void pci_epc_reclaim_dma_chan(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct dma_chan *chan, bool quiesce);
enum pci_barno
pci_epc_get_first_free_bar(const struct pci_epc_features *epc_features);
enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features
--
2.51.0