[PATCH 07/15] PCI: endpoint: Add EPC DMA channel delegation hooks
Koichiro Den <[email protected]> Fri, 13 Mar 2026 01:49:57 +0900
| Newsgroups | dev.linux.lists.ntb,org.kernel.vger.dmaengine,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci |
|---|---|
| Message-ID | <[email protected]> |
Add EPC ops and core wrappers to delegate and undelegate controller-owned DMA channels. The exported DMA helper needs more than a passive "delegated" bitmap: it must be able to reserve channels away from local users, let the backend perform controller-specific setup (e.g. prevent the EP from racing to ack the completion interrupt for delegated channels), and later hand the channels back as a matched lifetime operation. Signed-off-by: Koichiro Den <[email protected]> --- drivers/pci/endpoint/pci-epc-core.c | 84 +++++++++++++++++++++++++++++ include/linux/pci-epc.h | 19 +++++++ 2 files changed, 103 insertions(+) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index dc6d6ab4ea1e..892f7ccbd236 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -197,6 +197,90 @@ 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_channels() - reserve EPC-owned DMA channels + * @epc: EPC device + * @func_no: function number + * @vfunc_no: virtual function number + * @dir: DMA channel direction + * @req_chans: number of channels requested + * @chan_ids: output array of delegated channel IDs + * @max_chans: capacity of @chan_ids in entries + * + * Return: + * * > 0: number of channels delegated + * * -EOPNOTSUPP: backend does not support DMA delegation + * * other -errno on failure + */ +int pci_epc_delegate_dma_channels(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + enum pci_epc_aux_dma_dir dir, + u32 req_chans, int *chan_ids, u32 max_chans) +{ + int ret; + + if (!epc || !epc->ops) + return -EINVAL; + + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) + return -EINVAL; + + if (!req_chans || !chan_ids || !max_chans) + return -EINVAL; + + if (!epc->ops->delegate_dma_channels) + return -EOPNOTSUPP; + + mutex_lock(&epc->lock); + ret = epc->ops->delegate_dma_channels(epc, func_no, vfunc_no, dir, + req_chans, chan_ids, max_chans); + mutex_unlock(&epc->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_epc_delegate_dma_channels); + +/** + * pci_epc_undelegate_dma_channels() - release previously delegated channels + * @epc: EPC device + * @func_no: function number + * @vfunc_no: virtual function number + * @dir: DMA channel direction + * @chan_ids: array of delegated channel IDs + * @num_chans: number of entries in @chan_ids + * + * Return: 0 on success, negative errno otherwise. + */ +int pci_epc_undelegate_dma_channels(struct pci_epc *epc, u8 func_no, + u8 vfunc_no, + enum pci_epc_aux_dma_dir dir, + const int *chan_ids, u32 num_chans) +{ + int ret; + + if (!epc || !epc->ops) + return -EINVAL; + + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) + return -EINVAL; + + if (!num_chans) + return 0; + + if (!chan_ids) + return -EINVAL; + + if (!epc->ops->undelegate_dma_channels) + return -EOPNOTSUPP; + + mutex_lock(&epc->lock); + ret = epc->ops->undelegate_dma_channels(epc, func_no, vfunc_no, dir, + chan_ids, num_chans); + mutex_unlock(&epc->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_epc_undelegate_dma_channels); + /** * 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 7dd2e4d5d952..db8623b84c56 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -142,6 +142,8 @@ struct pci_epc_aux_resource { * @stop: ops to stop the PCI link * @get_features: ops to get the features supported by the EPC * @get_aux_resources: ops to retrieve controller-owned auxiliary resources + * @delegate_dma_channels: reserve controller-owned DMA channels for peer use + * @undelegate_dma_channels: release previously delegated DMA channels * @owner: the module owner containing the ops */ struct pci_epc_ops { @@ -176,6 +178,16 @@ 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_channels)(struct pci_epc *epc, u8 func_no, + u8 vfunc_no, + enum pci_epc_aux_dma_dir dir, + u32 req_chans, int *chan_ids, + u32 max_chans); + int (*undelegate_dma_channels)(struct pci_epc *epc, u8 func_no, + u8 vfunc_no, + enum pci_epc_aux_dma_dir dir, + const int *chan_ids, + u32 num_chans); struct module *owner; }; @@ -403,6 +415,13 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, 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_channels(struct pci_epc *epc, u8 func_no, + u8 vfunc_no, enum pci_epc_aux_dma_dir dir, + u32 req_chans, int *chan_ids, u32 max_chans); +int pci_epc_undelegate_dma_channels(struct pci_epc *epc, u8 func_no, + u8 vfunc_no, + enum pci_epc_aux_dma_dir dir, + const int *chan_ids, u32 num_chans); 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