[PATCH v5 3/6] PCI: endpoint: Add API to delegate EPC DMA channels to the host
From: Koichiro Den
Date: Fri Jul 17 2026 - 01:07:11 EST
Some endpoint functions expose an EPC-integrated DMA controller to the
host. The endpoint function should not need to know the backend-specific
mechanism used to reserve a channel locally and hand its programming
interface to the host.
Add pci_epc_delegate_dma_chan() and pci_epc_reclaim_dma_chan().
Add matching EPC operations. The public API returns an opaque handle,
while the EPC backend keeps any private channel state. This lets generic
endpoint functions delegate channels without depending on a specific
DMAengine provider.
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. The opaque handle is always freed.
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v5:
- Document provider-defined sharing groups and the best-effort reclaim
contract.
drivers/pci/endpoint/pci-epc-core.c | 105 ++++++++++++++++++++++++++++
include/linux/pci-epc.h | 16 +++++
2 files changed, 121 insertions(+)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 831b40458dcd..344e54677bfa 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -18,6 +18,13 @@ static const struct class pci_epc_class = {
.name = "pci_epc",
};
+struct pci_epc_dma_chan {
+ struct pci_epc *epc;
+ u8 func_no;
+ u8 vfunc_no;
+ void *data;
+};
+
static void devm_pci_epc_release(struct device *dev, void *res)
{
struct pci_epc *epc = *(struct pci_epc **)res;
@@ -236,6 +243,104 @@ 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
+ * @dir: DMA channel direction relative to the endpoint
+ * @hw_ch: hardware channel number
+ * @chan: output delegated-channel handle
+ *
+ * Some EPC backends integrate DMA channels that can be exposed to the host.
+ * This helper asks the backend to reserve the specified channel locally 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,
+ enum pci_epc_aux_dma_dir dir, u16 hw_ch,
+ struct pci_epc_dma_chan **chan)
+{
+ struct pci_epc_dma_chan *epc_chan;
+ void *data = NULL;
+ int ret;
+
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
+ return -EINVAL;
+
+ if (!chan)
+ return -EINVAL;
+ *chan = NULL;
+
+ if (dir != PCI_EPC_AUX_DMA_EP_TO_RC &&
+ dir != PCI_EPC_AUX_DMA_RC_TO_EP)
+ return -EINVAL;
+
+ if (!epc->ops->delegate_dma_chan || !epc->ops->reclaim_dma_chan)
+ return -EOPNOTSUPP;
+
+ epc_chan = kzalloc_obj(*epc_chan, GFP_KERNEL);
+ if (!epc_chan)
+ return -ENOMEM;
+
+ mutex_lock(&epc->lock);
+ ret = epc->ops->delegate_dma_chan(epc, func_no, vfunc_no, dir, hw_ch,
+ &data);
+ mutex_unlock(&epc->lock);
+ if (ret) {
+ kfree(epc_chan);
+ return ret;
+ }
+
+ epc_chan->epc = epc;
+ epc_chan->func_no = func_no;
+ epc_chan->vfunc_no = vfunc_no;
+ epc_chan->data = data;
+ *chan = epc_chan;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_epc_delegate_dma_chan);
+
+/**
+ * pci_epc_reclaim_dma_chan() - reclaim a delegated EPC-owned DMA channel
+ * @chan: delegated-channel handle returned by pci_epc_delegate_dma_chan()
+ * @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.
+ */
+void pci_epc_reclaim_dma_chan(struct pci_epc_dma_chan *chan, bool quiesce)
+{
+ struct pci_epc *epc;
+
+ if (!chan)
+ return;
+
+ epc = chan->epc;
+ if (epc && epc->ops && epc->ops->reclaim_dma_chan) {
+ mutex_lock(&epc->lock);
+ epc->ops->reclaim_dma_chan(epc, chan->func_no, chan->vfunc_no,
+ chan->data, quiesce);
+ mutex_unlock(&epc->lock);
+ }
+
+ kfree(chan);
+}
+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 8c89cb6d6733..2d15194a126b 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -11,7 +11,9 @@
#include <linux/pci-epf.h>
+struct device;
struct pci_epc;
+struct pci_epc_dma_chan;
enum pci_epc_interface_type {
UNKNOWN_INTERFACE = -1,
@@ -174,6 +176,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 {
@@ -210,6 +217,11 @@ 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,
+ enum pci_epc_aux_dma_dir dir, u16 hw_ch,
+ void **data);
+ void (*reclaim_dma_chan)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ void *data, bool quiesce);
struct module *owner;
};
@@ -443,6 +455,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,
+ enum pci_epc_aux_dma_dir dir, u16 hw_ch,
+ struct pci_epc_dma_chan **chan);
+void pci_epc_reclaim_dma_chan(struct pci_epc_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