At present, struct device lacks a mechanism of exposing memory
access constraints for the device.

Consequently, there is also no mechanism to share these constraints
while sharing buffers using dma-buf.

If we add support for sharing such constraints, we could use that
to try to collect requirements of different buffer-sharing devices
to allocate buffers from a pool that satisfies requirements of all
such devices.

This is an attempt to add this support; at the moment, only a bitmask
is added, but if post discussion, we realise we need more information,
we could always extend the definition of constraint.

A new dma-buf op is also added, to allow exporters to interpret or decide
on constraint-masks on their own. A default implementation is provided to
just AND (&) all the constraint-masks.

What constitutes a constraint-mask could be left for interpretation on a
per-platform basis, while defining some common masks.

Signed-off-by: Sumit Semwal <sumit.semwal at linaro.org>
Cc: linux-kernel at vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
Cc: linux-media at vger.kernel.org
Cc: dri-devel at lists.freedesktop.org
Cc: linaro-mm-sig at lists.linaro.org
---
 drivers/dma-buf/dma-buf.c | 50 ++++++++++++++++++++++++++++++++++++++++++-----
 include/linux/device.h    |  7 ++++++-
 include/linux/dma-buf.h   | 14 +++++++++++++
 3 files changed, 65 insertions(+), 6 deletions(-)

diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index f3014c4..33bdb6a 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -264,6 +264,30 @@ static inline int is_dma_buf_file(struct file *file)
        return file->f_op == &dma_buf_fops;
 }

+/*
+ * def_calc_access_constraints - default implementation of constraint checking
+ */
+static int def_calc_access_constraints(struct dma_buf *dmabuf,
+               struct dma_buf_attachment *attach)
+{
+       unsigned long access_mask;
+
+       access_mask = attach->dev->dma_parms->access_constraints_mask;
+
+       if (!access_mask) {
+               pr_warn("%s dev has no access_constraints_mask; using 
default\n",
+                               dev_name(attach->dev));
+                access_mask = DMA_BUF_ALL_MEMORY_ACCESS_MASK;
+       }
+
+       dmabuf->access_constraints_mask &= access_mask;
+
+       if (!dmabuf->access_constraints_mask)
+               return -EINVAL;
+
+       return 0;
+}
+
 /**
  * dma_buf_export_named - Creates a new dma_buf, and associates an anon file
  * with this buffer, so it can be exported.
@@ -313,6 +337,8 @@ struct dma_buf *dma_buf_export_named(void *priv, const 
struct dma_buf_ops *ops,
        dmabuf->ops = ops;
        dmabuf->size = size;
        dmabuf->exp_name = exp_name;
+       dmabuf->access_constraints_mask = DMA_BUF_ALL_MEMORY_ACCESS_MASK;
+
        init_waitqueue_head(&dmabuf->poll);
        dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll;
        dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0;
@@ -410,8 +436,10 @@ void dma_buf_put(struct dma_buf *dmabuf)
 EXPORT_SYMBOL_GPL(dma_buf_put);

 /**
- * dma_buf_attach - Add the device to dma_buf's attachments list; optionally,
- * calls attach() of dma_buf_ops to allow device-specific attach functionality
+ * dma_buf_attach - Add the device to dma_buf's attachments list;
+ *     calculates access_constraints and throws error if constraints aren't
+ *     satisfied. Optionally, calls attach() of dma_buf_ops to allow
+ *     device-specific attach functionality.
  * @dmabuf:    [in]    buffer to attach device to.
  * @dev:       [in]    device to be attached.
  *
@@ -436,11 +464,20 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf 
*dmabuf,

        mutex_lock(&dmabuf->lock);

+       if (!dmabuf->ops->calc_access_constraints)
+               ret = def_calc_access_constraints(dmabuf, attach);
+       else
+               ret = dmabuf->ops->calc_access_constraints(dmabuf, attach);
+
+       if (ret)
+               goto err_attach;
+
        if (dmabuf->ops->attach) {
                ret = dmabuf->ops->attach(dmabuf, dev, attach);
                if (ret)
                        goto err_attach;
        }
+
        list_add(&attach->node, &dmabuf->attachments);

        mutex_unlock(&dmabuf->lock);
@@ -785,7 +822,7 @@ static int dma_buf_describe(struct seq_file *s)
                return ret;

        seq_puts(s, "\nDma-buf Objects:\n");
-       seq_puts(s, "size\tflags\tmode\tcount\texp_name\n");
+       seq_puts(s, "size\tflags\tmode\tcount\tconstraints\texp_name\n");

        list_for_each_entry(buf_obj, &db_list.head, list_node) {
                ret = mutex_lock_interruptible(&buf_obj->lock);
@@ -796,10 +833,11 @@ static int dma_buf_describe(struct seq_file *s)
                        continue;
                }

-               seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\n",
+               seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%08lx\t%s\n",
                                buf_obj->size,
                                buf_obj->file->f_flags, buf_obj->file->f_mode,
                                (long)(buf_obj->file->f_count.counter),
+                               buf_obj->access_constraints_mask,
                                buf_obj->exp_name);

                seq_puts(s, "\tAttached Devices:\n");
@@ -808,7 +846,9 @@ static int dma_buf_describe(struct seq_file *s)
                list_for_each_entry(attach_obj, &buf_obj->attachments, node) {
                        seq_puts(s, "\t");

-                       seq_printf(s, "%s\n", dev_name(attach_obj->dev));
+                       seq_printf(s, "%s\t:%lx\n",
+                                       dev_name(attach_obj->dev),
+                         attach_obj->dev->dma_parms->access_constraints_mask);
                        attach_count++;
                }

diff --git a/include/linux/device.h b/include/linux/device.h
index a608e23..f9aefa2 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -647,6 +647,11 @@ struct device_dma_parameters {
         */
        unsigned int max_segment_size;
        unsigned long segment_boundary_mask;
+       /*
+        * access_constraints_mask: this would be used to share constraints
+        * about memories that this device can access.
+        */
+       unsigned long access_constraints_mask;
 };

 struct acpi_device;
@@ -696,7 +701,7 @@ struct acpi_dev_node {
  *             such descriptors.
  * @dma_pfn_offset: offset of DMA memory range relatively of RAM
  * @dma_parms: A low level driver may set these to teach IOMMU code about
- *             segment limitations.
+ *             segment limitations, and access constraints.
  * @dma_pools: Dma pools (if dma'ble device).
  * @dma_mem:   Internal for coherent mem override.
  * @cma_area:  Contiguous memory area for dma allocations
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index 694e1fe..8429a38 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -37,6 +37,8 @@ struct device;
 struct dma_buf;
 struct dma_buf_attachment;

+#define DMA_BUF_ALL_MEMORY_ACCESS_MASK ((unsigned long)-1)
+
 /**
  * struct dma_buf_ops - operations possible on struct dma_buf
  * @attach: [optional] allows different devices to 'attach' themselves to the
@@ -44,6 +46,12 @@ struct dma_buf_attachment;
  *         is already allocated and incompatible with the requirements
  *         of requesting device.
  * @detach: [optional] detach a given device from this buffer.
+ * @calc_access_constraints(): [optional] will be called at the end of each
+ *             attach - to calculate and set the constraints for this dma_buf
+ *             according to this attachment's access_constraint_mask in
+ *             dev->dma_parms.
+ *             A default implementation is provided, but exporters are free to
+ *             provide custom version if needed.
  * @map_dma_buf: returns list of scatter pages allocated, increases usecount
  *              of the buffer. Requires atleast one attach to be called
  *              before. Returned sg list should already be mapped into
@@ -77,6 +85,9 @@ struct dma_buf_ops {

        void (*detach)(struct dma_buf *, struct dma_buf_attachment *);

+       int (*calc_access_constraints)(struct dma_buf *,
+                       struct dma_buf_attachment *);
+
        /* For {map,unmap}_dma_buf below, any specific buffer attributes
         * required should get added to device_dma_parameters accessible
         * via dev->dma_params.
@@ -86,6 +97,7 @@ struct dma_buf_ops {
        void (*unmap_dma_buf)(struct dma_buf_attachment *,
                                                struct sg_table *,
                                                enum dma_data_direction);
+
        /* TODO: Add try_map_dma_buf version, to return immed with -EBUSY
         * if the call would block.
         */
@@ -116,6 +128,7 @@ struct dma_buf_ops {
  * @ops: dma_buf_ops associated with this buffer object.
  * @exp_name: name of the exporter; useful for debugging.
  * @list_node: node for dma_buf accounting and debugging.
+ * @access_constraints_mask: mask to share access constraints.
  * @priv: exporter specific private data for this buffer object.
  * @resv: reservation object linked to this dma-buf
  */
@@ -130,6 +143,7 @@ struct dma_buf {
        void *vmap_ptr;
        const char *exp_name;
        struct list_head list_node;
+       unsigned long access_constraints_mask;
        void *priv;
        struct reservation_object *resv;

-- 
1.9.1

Reply via email to