[PATCH v6 3/6] PCI: endpoint: Add API to delegate EPC DMA channels to the host
Koichiro Den <[email protected]> Tue, 4 Aug 2026 12:38:52 +0900
| Newsgroups | org.kernel.vger.linux-pci,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- Changes in v6: - No changes. 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