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

Reply via email to