If the importer provides a callback for supports_interconnects(), the exporter starts the matching (or negotiation) process (during attach) by invoking the supports_interconnects() callback which would then call this helper to identify the first common interconnect supported by both exporter and importer.
Note that whether an interconnect is supported between an exporter/importer is ultimately determined by the exporter via the match callback it is expected to provide. Cc: Jason Gunthorpe <[email protected]> Cc: Christian Koenig <[email protected]> Cc: Sumit Semwal <[email protected]> Cc: Thomas Hellström <[email protected]> Cc: Simona Vetter <[email protected]> Signed-off-by: Vivek Kasireddy <[email protected]> --- drivers/dma-buf/dma-buf-interconnect.c | 65 ++++++++++++++++++++++++++ drivers/dma-buf/dma-buf.c | 6 ++- include/linux/dma-buf-interconnect.h | 36 ++++++++++++++ include/linux/dma-buf.h | 14 ++++++ 4 files changed, 120 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/dma-buf-interconnect.c b/drivers/dma-buf/dma-buf-interconnect.c index 690423b6682f..12db77e6b9f1 100644 --- a/drivers/dma-buf/dma-buf-interconnect.c +++ b/drivers/dma-buf/dma-buf-interconnect.c @@ -94,3 +94,68 @@ void dma_buf_unmap_interconnect(struct dma_buf_attachment *attach, kfree(ranges); } EXPORT_SYMBOL_NS_GPL(dma_buf_unmap_interconnect, "DMA_BUF"); + +/** + * dma_buf_match_interconnects - determine if there is a specific interconnect + * that is supported by both exporter and importer. + * @attach: [in] attachment to populate ic_match field + * @exp: [in] array of interconnects supported by exporter + * @exp_ics: [in] number of interconnects supported by exporter + * @imp: [in] array of interconnects supported by importer + * @imp_ics: [in] number of interconnects supported by importer + * + * This helper function iterates through the list interconnects supported by + * both exporter and importer to find a match. A successful match means that + * a common interconnect type is supported by both parties and the exporter's + * match_interconnect() callback also confirms that the importer is compatible + * with the exporter for that interconnect type. + * + * If a match is found, the attach->ic_match field is populated with a copy + * of the exporter's match data. + * Return: true if a match is found, false otherwise. + */ +bool dma_buf_match_interconnects(struct dma_buf_attachment *attach, + const struct dma_buf_interconnect_match *exp, + unsigned int exp_ics, + const struct dma_buf_interconnect_match *imp, + unsigned int imp_ics) +{ + const struct dma_buf_interconnect_ops *ic_ops; + struct dma_buf_interconnect_match *ic_match; + struct dma_buf *dmabuf = attach->dmabuf; + unsigned int i, j; + + if (!exp || !imp) + return false; + + if (!attach->allow_ic) + return false; + + ic_ops = dmabuf->ops->interconnect_ops; + if (!ic_ops || !ic_ops->match_interconnect) + return false; + + ic_match = kzalloc(sizeof(*ic_match), GFP_KERNEL); + if (!ic_match) + return false; + + for (i = 0; i < exp_ics; i++) { + for (j = 0; j < imp_ics; j++) { + if (exp[i].type == imp[j].type) { + if (ic_ops->match_interconnect(&exp[i], + &imp[j])) { + memcpy(ic_match, &exp[i], + sizeof(*ic_match)); + + attach->ic_match = ic_match; + return true; + } + } + } + } + + attach->allow_ic = false; + kfree(ic_match); + return false; +} +EXPORT_SYMBOL_NS_GPL(dma_buf_match_interconnects, "DMA_BUF"); diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index daa993503052..a6977375f11e 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -959,8 +959,11 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev, attach->dev = dev; attach->dmabuf = dmabuf; - if (importer_ops) + if (importer_ops) { attach->peer2peer = importer_ops->allow_peer2peer; + if (importer_ops->supports_interconnects) + attach->allow_ic = true; + } attach->importer_ops = importer_ops; attach->importer_priv = importer_priv; @@ -1017,6 +1020,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) if (dmabuf->ops->detach) dmabuf->ops->detach(dmabuf, attach); + kfree(attach->ic_match); kfree(attach); } EXPORT_SYMBOL_NS_GPL(dma_buf_detach, "DMA_BUF"); diff --git a/include/linux/dma-buf-interconnect.h b/include/linux/dma-buf-interconnect.h index 50fc7a8272ce..efe3ca1c354a 100644 --- a/include/linux/dma-buf-interconnect.h +++ b/include/linux/dma-buf-interconnect.h @@ -3,8 +3,14 @@ #ifndef __DMA_BUF_INTERCONNECT_H__ #define __DMA_BUF_INTERCONNECT_H__ +#include <linux/device.h> #include <linux/xarray.h> +#define MATCH_INTERCONNECT(interconnect, ...) \ + ((const struct dma_buf_interconnect_match) { \ + .type = interconnect __VA_OPT__(, __VA_ARGS__) \ + }) \ + #define CREATE_INTERCONNECT(type) \ static const struct dma_buf_interconnect __##type##_interconnect = { \ .name = #type"_interconnect", \ @@ -25,6 +31,22 @@ struct dma_buf_interconnect { const char *name; }; +/** + * struct dma_buf_interconnect_match - holds data used to match interconnects + * @type: pointer to the interconnect instance + * @dev: the device associated with a given exporter or importer + * @bar: the BAR index associated with the device + * + * The exporter and importer are expected to populate this structure with + * their respective device and BAR information for each interconnect type they + * support. This data is used to determine if a match exists between them. + */ +struct dma_buf_interconnect_match { + const struct dma_buf_interconnect *type; + struct device *dev; + unsigned int bar; +}; + /** * struct dma_buf_ranges - holds info about interconnect address ranges * @ranges: xarray that contains the address ranges @@ -71,9 +93,23 @@ struct dma_buf_interconnect_ops { */ void (*unmap_interconnect)(struct dma_buf_attachment *attach, struct dma_buf_ranges *ranges); + /** + * @match_interconnect: + * + * This is called by dma_buf_match_interconnects() and is used by + * the exporter to determine if the importer is compatible for a + * given interconnect type. + */ + bool (*match_interconnect)(const struct dma_buf_interconnect_match *, + const struct dma_buf_interconnect_match *); }; struct dma_buf_ranges *dma_buf_map_interconnect(struct dma_buf_attachment *); void dma_buf_unmap_interconnect(struct dma_buf_attachment *, struct dma_buf_ranges *); +bool dma_buf_match_interconnects(struct dma_buf_attachment *attach, + const struct dma_buf_interconnect_match *, + unsigned int exp_ics, + const struct dma_buf_interconnect_match *, + unsigned int imp_ics); #endif diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index a675bc89a69c..f7d0b0dbcb24 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -487,6 +487,18 @@ struct dma_buf_attach_ops { * point to the new location of the DMA-buf. */ void (*move_notify)(struct dma_buf_attachment *attach); + + /** + * @supports_interconnects: [optional] indicate interconnect support + * + * If this callback is provided, it means that the importer would + * provide a list of interconnects that it supports and would + * invoke dma_buf_match_interconnects() to identify a match with the + * exporter's interconnects. + */ + bool (*supports_interconnects)(struct dma_buf_attachment *attach, + const struct dma_buf_interconnect_match *, + unsigned int num_ics); }; /** @@ -498,6 +510,7 @@ struct dma_buf_attach_ops { * @allow_ic: true if the importer is allowed to use interconnect ops. * @priv: exporter specific attachment data. * @importer_ops: importer operations for this attachment, if provided + * @ic_match: copy of exporter's interconnect match data. * dma_buf_map/unmap_attachment() must be called with the dma_resv lock held. * @importer_priv: importer specific attachment data. * @@ -517,6 +530,7 @@ struct dma_buf_attachment { bool peer2peer; bool allow_ic; const struct dma_buf_attach_ops *importer_ops; + struct dma_buf_interconnect_match *ic_match; void *importer_priv; void *priv; }; -- 2.50.1
