An importer that lists more than one mapping type cannot know which
type-specific map function to call, because the exporter selects the
type at map time. Create a generic entry point to determine the correct
type.

dma_buf_mapping_map() selects via dma_buf_mapping_select() and dispatches
to a map op on struct dma_buf_mapping_type. That returns a struct
dma_buf_mapping carrying the type, embedded in the type's own
result struct. dma_buf_mapping_unmap() hands it back through the type's
unmap op.

To support existing SGT importers the SGT type implements both. Its
result is struct dma_buf_sgt_mapping with the sg_table reachable through
dma_buf_sgt_mapping_table(). The sgt map body is shared with the SGT
type's map function which remains the entry point for importers listing
only SGT.

Assisted-by: LLM
Signed-off-by: Alistair Popple <[email protected]>
---
 drivers/dma-buf/dma-buf-mapping.c | 75 +++++++++++++++++++++++++++++++
 drivers/dma-buf/dma-buf.c         | 33 ++++++++++++++
 include/linux/dma-buf-mapping.h   | 63 ++++++++++++++++++++++++++
 3 files changed, 171 insertions(+)

diff --git a/drivers/dma-buf/dma-buf-mapping.c 
b/drivers/dma-buf/dma-buf-mapping.c
index dbcd83b6dcff..7a5a379a372e 100644
--- a/drivers/dma-buf/dma-buf-mapping.c
+++ b/drivers/dma-buf/dma-buf-mapping.c
@@ -366,6 +366,79 @@ int dma_buf_mapping_select(struct dma_buf_attachment 
*attach)
        return 0;
 }
 
+/**
+ * dma_buf_mapping_map - Map an attachment with whichever type the exporter
+ *                       selects
+ * @attach: the attachment
+ * @dir: DMA direction
+ *
+ * For importers that listed more than one mapping type. The exporter picks the
+ * type from the possible matches and the result's &dma_buf_mapping.type 
records
+ * which was used. Must be called with the dma_resv lock held, and the lock 
must
+ * be held until the result has been acted on. Importers that listed a single
+ * type keep using that type's map function.
+ *
+ * Returns: a &struct dma_buf_mapping to pass to dma_buf_mapping_unmap(), or
+ * an ERR_PTR.
+ */
+struct dma_buf_mapping *dma_buf_mapping_map(struct dma_buf_attachment *attach,
+                                           enum dma_data_direction dir)
+{
+       struct dma_buf_mapping *mapping;
+       int ret;
+
+       might_sleep();
+
+       if (WARN_ON(!attach || !attach->dmabuf))
+               return ERR_PTR(-EINVAL);
+
+       dma_resv_assert_held(attach->dmabuf->resv);
+
+       ret = dma_buf_mapping_select(attach);
+       if (ret)
+               return ERR_PTR(ret);
+
+       if (!attach->map_type.type->map) {
+               ret = -EOPNOTSUPP;
+               goto err_release;
+       }
+
+       mapping = attach->map_type.type->map(attach, dir);
+       if (IS_ERR(mapping)) {
+               ret = PTR_ERR(mapping);
+               goto err_release;
+       }
+
+       return mapping;
+
+err_release:
+       /* Nothing was established, so the choice may be made afresh */
+       attach->selected = false;
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_NS_GPL(dma_buf_mapping_map, "DMA_BUF");
+
+/**
+ * dma_buf_mapping_unmap - Release a mapping from dma_buf_mapping_map()
+ * @attach: the attachment
+ * @mapping: what dma_buf_mapping_map() returned
+ * @dir: DMA direction used for the map
+ */
+void dma_buf_mapping_unmap(struct dma_buf_attachment *attach,
+                          struct dma_buf_mapping *mapping,
+                          enum dma_data_direction dir)
+{
+       might_sleep();
+
+       if (WARN_ON(!attach || !attach->dmabuf || !mapping))
+               return;
+
+       dma_resv_assert_held(attach->dmabuf->resv);
+
+       mapping->type->unmap(attach, mapping, dir);
+}
+EXPORT_SYMBOL_NS_GPL(dma_buf_mapping_unmap, "DMA_BUF");
+
 static int dma_buf_sgt_match(struct dma_buf *dmabuf,
                             const struct dma_buf_mapping_match *exp,
                             const struct dma_buf_mapping_match *imp)
@@ -427,6 +500,8 @@ struct dma_buf_mapping_type dma_buf_mapping_sgt_type = {
        .match = dma_buf_sgt_match,
        .finish_match = dma_buf_sgt_finish_match,
        .debugfs_dump = dma_buf_sgt_debugfs_dump,
+       .map = dma_buf_sgt_type_map,
+       .unmap = dma_buf_sgt_type_unmap,
 };
 EXPORT_SYMBOL_NS_GPL(dma_buf_mapping_sgt_type, "DMA_BUF");
 
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 03f06c4b957c..90699e85e4c8 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -1353,6 +1353,39 @@ struct sg_table *dma_buf_map_attachment(struct 
dma_buf_attachment *attach,
 }
 EXPORT_SYMBOL_NS_GPL(dma_buf_map_attachment, "DMA_BUF");
 
+/* &dma_buf_mapping_type.map / .unmap for the SGT type */
+struct dma_buf_mapping *dma_buf_sgt_type_map(struct dma_buf_attachment *attach,
+                                            enum dma_data_direction dir)
+{
+       struct dma_buf_sgt_mapping *mapping;
+       int ret;
+
+       mapping = kzalloc_obj(*mapping);
+       if (!mapping)
+               return ERR_PTR(-ENOMEM);
+
+       mapping->base.type = &dma_buf_mapping_sgt_type;
+       mapping->sgt = __dma_buf_sgt_map_attachment(attach, dir);
+       if (IS_ERR(mapping->sgt)) {
+               ret = PTR_ERR(mapping->sgt);
+               kfree(mapping);
+               return ERR_PTR(ret);
+       }
+
+       return &mapping->base;
+}
+
+void dma_buf_sgt_type_unmap(struct dma_buf_attachment *attach,
+                           struct dma_buf_mapping *base,
+                           enum dma_data_direction dir)
+{
+       struct dma_buf_sgt_mapping *mapping =
+               container_of(base, struct dma_buf_sgt_mapping, base);
+
+       dma_buf_unmap_attachment(attach, mapping->sgt, dir);
+       kfree(mapping);
+}
+
 /**
  * dma_buf_map_attachment_unlocked - Returns the scatterlist table of the 
attachment;
  * mapped into _device_ address space. Is a wrapper for map_dma_buf() of the
diff --git a/include/linux/dma-buf-mapping.h b/include/linux/dma-buf-mapping.h
index 494bc29910f4..885e0e54b5eb 100644
--- a/include/linux/dma-buf-mapping.h
+++ b/include/linux/dma-buf-mapping.h
@@ -21,6 +21,16 @@ enum dma_sgt_requires_p2p {
 /* Type tag for all mapping operations */
 struct dma_buf_mapping_exp_ops {};
 
+/**
+ * struct dma_buf_mapping - header of a mapping made with dma_buf_mapping_map()
+ * @type: The mapping type the exporter selected. Importers that listed more
+ *        than one type switch on this and use the type's accessor to get at
+ *        the type-specific result embedding this header.
+ */
+struct dma_buf_mapping {
+       const struct dma_buf_mapping_type *type;
+};
+
 /*
  * Internal struct to pass arguments from the attach function to the matching
  * function
@@ -82,8 +92,35 @@ struct dma_buf_mapping_type {
         */
        void (*debugfs_dump)(struct seq_file *s,
                             struct dma_buf_attachment *attach);
+
+       /**
+        * @map:
+        *
+        * Produce a mapping of this type for @attach, which has already been
+        * selected into @attach->map_type. Called by dma_buf_mapping_map()
+        * with the dma_resv lock held. Responsible for pin-on-map of static
+        * importers if the type supports them. Optional; a type without it
+        * can only be used through its own map functions.
+        */
+       struct dma_buf_mapping *(*map)(struct dma_buf_attachment *attach,
+                                      enum dma_data_direction dir);
+
+       /**
+        * @unmap:
+        *
+        * Release a mapping returned by @map.
+        */
+       void (*unmap)(struct dma_buf_attachment *attach,
+                     struct dma_buf_mapping *mapping,
+                     enum dma_data_direction dir);
 };
 
+struct dma_buf_mapping *dma_buf_mapping_map(struct dma_buf_attachment *attach,
+                                           enum dma_data_direction dir);
+void dma_buf_mapping_unmap(struct dma_buf_attachment *attach,
+                          struct dma_buf_mapping *mapping,
+                          enum dma_data_direction dir);
+
 struct sg_table *dma_buf_phys_vec_to_sgt(struct dma_buf_attachment *attach,
                                         struct p2pdma_provider *provider,
                                         struct phys_vec *phys_vec,
@@ -122,6 +159,13 @@ dma_buf_attach_accepts(const struct dma_buf_attachment 
*attach,
        return false;
 }
 
+/* Internal: &dma_buf_mapping_sgt_type.map / .unmap, implemented in dma-buf.c 
*/
+struct dma_buf_mapping *dma_buf_sgt_type_map(struct dma_buf_attachment *attach,
+                                            enum dma_data_direction dir);
+void dma_buf_sgt_type_unmap(struct dma_buf_attachment *attach,
+                           struct dma_buf_mapping *mapping,
+                           enum dma_data_direction dir);
+
 /*
  * DMA Mapped Scatterlist Type
  *
@@ -137,6 +181,25 @@ dma_buf_attach_accepts(const struct dma_buf_attachment 
*attach,
  */
 extern struct dma_buf_mapping_type dma_buf_mapping_sgt_type;
 
+/**
+ * struct dma_buf_sgt_mapping - dma_buf_mapping_map() result for the SGT type
+ * @base: common header, @base.type == &dma_buf_mapping_sgt_type
+ * @sgt: the DMA mapped scatterlist, as the SGT type's own map function
+ *       returns it
+ */
+struct dma_buf_sgt_mapping {
+       struct dma_buf_mapping base;
+       struct sg_table *sgt;
+};
+
+static inline struct sg_table *
+dma_buf_sgt_mapping_table(struct dma_buf_mapping *mapping)
+{
+       if (WARN_ON(mapping->type != &dma_buf_mapping_sgt_type))
+               return NULL;
+       return container_of(mapping, struct dma_buf_sgt_mapping, base)->sgt;
+}
+
 struct dma_buf_mapping_sgt_exp_ops {
        struct dma_buf_mapping_exp_ops ops;
        struct sg_table *(*map_dma_buf)(struct dma_buf_attachment *attach,
-- 
2.54.0

Reply via email to