dma-buf: use reservation objects
authorMaarten Lankhorst <maarten.lankhorst@canonical.com>
Tue, 1 Jul 2014 10:57:26 +0000 (12:57 +0200)
committerChanho Park <chanho61.park@samsung.com>
Fri, 21 Nov 2014 10:12:25 +0000 (19:12 +0900)
This allows reservation objects to be used in dma-buf. it's required
for implementing polling support on the fences that belong to a dma-buf.

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Acked-by: Mauro Carvalho Chehab <m.chehab@samsung.com> #drivers/media/v4l2-core/
Acked-by: Thomas Hellstrom <thellstrom@vmware.com> #drivers/gpu/drm/ttm
Acked-by: Sumit Semwal <sumit.semwal@linaro.org>
Acked-by: Daniel Vetter <daniel@ffwll.ch>
Signed-off-by: Vincent Stehlé <vincent.stehle@laposte.net> #drivers/gpu/drm/armada/
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Chanho Park <chanho61.park@samsung.com>
Conflicts:
drivers/gpu/drm/armada/armada_gem.c
drivers/gpu/drm/drm_prime.c
drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
drivers/gpu/drm/i915/i915_gem_dmabuf.c
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_gem.h
drivers/gpu/drm/nouveau/nouveau_prime.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/tegra/gem.c
drivers/gpu/drm/ttm/ttm_object.c
drivers/staging/android/ion/ion.c

Change-Id: I44fbb1f41500deaf9067eb5d7e1c6ed758231d69

drivers/dma-buf/dma-buf.c
drivers/gpu/drm/drm_prime.c
drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
drivers/gpu/drm/ttm/ttm_object.c
drivers/media/v4l2-core/videobuf2-dma-contig.c
include/drm/drmP.h
include/linux/dma-buf.h

index 7b9904e..68789e1 100644 (file)
 #include <linux/fs.h>
 #include <linux/slab.h>
 #include <linux/dma-buf.h>
+#include <linux/fence.h>
 #include <linux/anon_inodes.h>
 #include <linux/export.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
+#include <linux/reservation.h>
 
 static inline int is_dma_buf_file(struct file *);
 
@@ -56,6 +58,9 @@ static int dma_buf_release(struct inode *inode, struct file *file)
        list_del(&dmabuf->list_node);
        mutex_unlock(&db_list.lock);
 
+       if (dmabuf->resv == (struct reservation_object *)&dmabuf[1])
+               reservation_object_fini(dmabuf->resv);
+
        kfree(dmabuf);
        return 0;
 }
@@ -101,6 +106,7 @@ static inline int is_dma_buf_file(struct file *file)
  * @size:      [in]    Size of the buffer
  * @flags:     [in]    mode flags for the file.
  * @exp_name:  [in]    name of the exporting module - useful for debugging.
+ * @resv:      [in]    reservation-object, NULL to allocate default one.
  *
  * Returns, on success, a newly created dma_buf object, which wraps the
  * supplied private data and operations for dma_buf_ops. On either missing
@@ -108,10 +114,17 @@ static inline int is_dma_buf_file(struct file *file)
  *
  */
 struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
-                               size_t size, int flags, const char *exp_name)
+                               size_t size, int flags, const char *exp_name,
+                               struct reservation_object *resv)
 {
        struct dma_buf *dmabuf;
        struct file *file;
+       size_t alloc_size = sizeof(struct dma_buf);
+       if (!resv)
+               alloc_size += sizeof(struct reservation_object);
+       else
+               /* prevent &dma_buf[1] == dma_buf->resv */
+               alloc_size += 1;
 
        if (WARN_ON(!priv || !ops
                          || !ops->map_dma_buf
@@ -123,7 +136,7 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
                return ERR_PTR(-EINVAL);
        }
 
-       dmabuf = kzalloc(sizeof(struct dma_buf), GFP_KERNEL);
+       dmabuf = kzalloc(alloc_size, GFP_KERNEL);
        if (dmabuf == NULL)
                return ERR_PTR(-ENOMEM);
 
@@ -131,6 +144,11 @@ 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;
+       if (!resv) {
+               resv = (struct reservation_object *)&dmabuf[1];
+               reservation_object_init(resv);
+       }
+       dmabuf->resv = resv;
 
        file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags);
 
index 9ac664e..3c7db7d 100644 (file)
@@ -317,8 +317,13 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops =  {
 struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
                                     struct drm_gem_object *obj, int flags)
 {
+       struct reservation_object *robj = NULL;
+
+       if (dev->driver->gem_prime_res_obj)
+               robj = dev->driver->gem_prime_res_obj(obj);
+
        return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size,
-                            flags);
+                             flags, robj);
 }
 EXPORT_SYMBOL(drm_gem_prime_export);
 
index a77cad3..4dcb100 100644 (file)
@@ -238,8 +238,8 @@ struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev,
 
        flags |= O_RDWR;
 
-       return dma_buf_export(exynos_gem_obj, &exynos_dmabuf_ops,
-                               exynos_gem_obj->base.size, flags);
+       return dma_buf_export(obj, &exynos_dmabuf_ops,
+                               exynos_gem_obj->base.size, flags, NULL);
 }
 
 struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
index 58a5f32..ddc3b33 100644 (file)
@@ -452,3 +452,225 @@ void ttm_object_device_release(struct ttm_object_device **p_tdev)
        kfree(tdev);
 }
 EXPORT_SYMBOL(ttm_object_device_release);
+
+/**
+ * get_dma_buf_unless_doomed - get a dma_buf reference if possible.
+ *
+ * @dma_buf: Non-refcounted pointer to a struct dma-buf.
+ *
+ * Obtain a file reference from a lookup structure that doesn't refcount
+ * the file, but synchronizes with its release method to make sure it has
+ * not been freed yet. See for example kref_get_unless_zero documentation.
+ * Returns true if refcounting succeeds, false otherwise.
+ *
+ * Nobody really wants this as a public API yet, so let it mature here
+ * for some time...
+ */
+static bool __must_check get_dma_buf_unless_doomed(struct dma_buf *dmabuf)
+{
+       return atomic_long_inc_not_zero(&dmabuf->file->f_count) != 0L;
+}
+
+/**
+ * ttm_prime_refcount_release - refcount release method for a prime object.
+ *
+ * @p_base: Pointer to ttm_base_object pointer.
+ *
+ * This is a wrapper that calls the refcount_release founction of the
+ * underlying object. At the same time it cleans up the prime object.
+ * This function is called when all references to the base object we
+ * derive from are gone.
+ */
+static void ttm_prime_refcount_release(struct ttm_base_object **p_base)
+{
+       struct ttm_base_object *base = *p_base;
+       struct ttm_prime_object *prime;
+
+       *p_base = NULL;
+       prime = container_of(base, struct ttm_prime_object, base);
+       BUG_ON(prime->dma_buf != NULL);
+       mutex_destroy(&prime->mutex);
+       if (prime->refcount_release)
+               prime->refcount_release(&base);
+}
+
+/**
+ * ttm_prime_dmabuf_release - Release method for the dma-bufs we export
+ *
+ * @dma_buf:
+ *
+ * This function first calls the dma_buf release method the driver
+ * provides. Then it cleans up our dma_buf pointer used for lookup,
+ * and finally releases the reference the dma_buf has on our base
+ * object.
+ */
+static void ttm_prime_dmabuf_release(struct dma_buf *dma_buf)
+{
+       struct ttm_prime_object *prime =
+               (struct ttm_prime_object *) dma_buf->priv;
+       struct ttm_base_object *base = &prime->base;
+       struct ttm_object_device *tdev = base->tfile->tdev;
+
+       if (tdev->dmabuf_release)
+               tdev->dmabuf_release(dma_buf);
+       mutex_lock(&prime->mutex);
+       if (prime->dma_buf == dma_buf)
+               prime->dma_buf = NULL;
+       mutex_unlock(&prime->mutex);
+       ttm_mem_global_free(tdev->mem_glob, tdev->dma_buf_size);
+       ttm_base_object_unref(&base);
+}
+
+/**
+ * ttm_prime_fd_to_handle - Get a base object handle from a prime fd
+ *
+ * @tfile: A struct ttm_object_file identifying the caller.
+ * @fd: The prime / dmabuf fd.
+ * @handle: The returned handle.
+ *
+ * This function returns a handle to an object that previously exported
+ * a dma-buf. Note that we don't handle imports yet, because we simply
+ * have no consumers of that implementation.
+ */
+int ttm_prime_fd_to_handle(struct ttm_object_file *tfile,
+                          int fd, u32 *handle)
+{
+       struct ttm_object_device *tdev = tfile->tdev;
+       struct dma_buf *dma_buf;
+       struct ttm_prime_object *prime;
+       struct ttm_base_object *base;
+       int ret;
+
+       dma_buf = dma_buf_get(fd);
+       if (IS_ERR(dma_buf))
+               return PTR_ERR(dma_buf);
+
+       if (dma_buf->ops != &tdev->ops)
+               return -ENOSYS;
+
+       prime = (struct ttm_prime_object *) dma_buf->priv;
+       base = &prime->base;
+       *handle = base->hash.key;
+       ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+
+       dma_buf_put(dma_buf);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ttm_prime_fd_to_handle);
+
+/**
+ * ttm_prime_handle_to_fd - Return a dma_buf fd from a ttm prime object
+ *
+ * @tfile: Struct ttm_object_file identifying the caller.
+ * @handle: Handle to the object we're exporting from.
+ * @flags: flags for dma-buf creation. We just pass them on.
+ * @prime_fd: The returned file descriptor.
+ *
+ */
+int ttm_prime_handle_to_fd(struct ttm_object_file *tfile,
+                          uint32_t handle, uint32_t flags,
+                          int *prime_fd)
+{
+       struct ttm_object_device *tdev = tfile->tdev;
+       struct ttm_base_object *base;
+       struct dma_buf *dma_buf;
+       struct ttm_prime_object *prime;
+       int ret;
+
+       base = ttm_base_object_lookup(tfile, handle);
+       if (unlikely(base == NULL ||
+                    base->object_type != ttm_prime_type)) {
+               ret = -ENOENT;
+               goto out_unref;
+       }
+
+       prime = container_of(base, struct ttm_prime_object, base);
+       if (unlikely(!base->shareable)) {
+               ret = -EPERM;
+               goto out_unref;
+       }
+
+       ret = mutex_lock_interruptible(&prime->mutex);
+       if (unlikely(ret != 0)) {
+               ret = -ERESTARTSYS;
+               goto out_unref;
+       }
+
+       dma_buf = prime->dma_buf;
+       if (!dma_buf || !get_dma_buf_unless_doomed(dma_buf)) {
+
+               /*
+                * Need to create a new dma_buf, with memory accounting.
+                */
+               ret = ttm_mem_global_alloc(tdev->mem_glob, tdev->dma_buf_size,
+                                          false, true);
+               if (unlikely(ret != 0)) {
+                       mutex_unlock(&prime->mutex);
+                       goto out_unref;
+               }
+
+               dma_buf = dma_buf_export(prime, &tdev->ops,
+                                        prime->size, flags, NULL);
+               if (IS_ERR(dma_buf)) {
+                       ret = PTR_ERR(dma_buf);
+                       ttm_mem_global_free(tdev->mem_glob,
+                                           tdev->dma_buf_size);
+                       mutex_unlock(&prime->mutex);
+                       goto out_unref;
+               }
+
+               /*
+                * dma_buf has taken the base object reference
+                */
+               base = NULL;
+               prime->dma_buf = dma_buf;
+       }
+       mutex_unlock(&prime->mutex);
+
+       ret = dma_buf_fd(dma_buf, flags);
+       if (ret >= 0) {
+               *prime_fd = ret;
+               ret = 0;
+       } else
+               dma_buf_put(dma_buf);
+
+out_unref:
+       if (base)
+               ttm_base_object_unref(&base);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ttm_prime_handle_to_fd);
+
+/**
+ * ttm_prime_object_init - Initialize a ttm_prime_object
+ *
+ * @tfile: struct ttm_object_file identifying the caller
+ * @size: The size of the dma_bufs we export.
+ * @prime: The object to be initialized.
+ * @shareable: See ttm_base_object_init
+ * @type: See ttm_base_object_init
+ * @refcount_release: See ttm_base_object_init
+ * @ref_obj_release: See ttm_base_object_init
+ *
+ * Initializes an object which is compatible with the drm_prime model
+ * for data sharing between processes and devices.
+ */
+int ttm_prime_object_init(struct ttm_object_file *tfile, size_t size,
+                         struct ttm_prime_object *prime, bool shareable,
+                         enum ttm_object_type type,
+                         void (*refcount_release) (struct ttm_base_object **),
+                         void (*ref_obj_release) (struct ttm_base_object *,
+                                                  enum ttm_ref_type ref_type))
+{
+       mutex_init(&prime->mutex);
+       prime->size = PAGE_ALIGN(size);
+       prime->real_type = type;
+       prime->dma_buf = NULL;
+       prime->refcount_release = refcount_release;
+       return ttm_base_object_init(tfile, &prime->base, shareable,
+                                   ttm_prime_type,
+                                   ttm_prime_refcount_release,
+                                   ref_obj_release);
+}
+EXPORT_SYMBOL(ttm_prime_object_init);
index 4ca73df..c16f1c1 100644 (file)
@@ -405,7 +405,7 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags)
        if (WARN_ON(!buf->sgt_base))
                return NULL;
 
-       dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, flags);
+       dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, flags, NULL);
        if (IS_ERR(dbuf))
                return NULL;
 
index c5fae78..cf0646d 100644 (file)
@@ -87,6 +87,7 @@ struct drm_device;
 
 struct device_node;
 struct videomode;
+struct reservation_object;
 
 #include <drm/drm_os_linux.h>
 #include <drm/drm_hashtab.h>
@@ -942,6 +943,8 @@ struct drm_driver {
        /* low-level interface used by drm_gem_prime_{import,export} */
        int (*gem_prime_pin)(struct drm_gem_object *obj);
        void (*gem_prime_unpin)(struct drm_gem_object *obj);
+       struct reservation_object * (*gem_prime_res_obj)(
+                               struct drm_gem_object *obj);
        struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj);
        struct drm_gem_object *(*gem_prime_import_sg_table)(
                                struct drm_device *dev, size_t size,
index f886985..fd7def2 100644 (file)
@@ -115,6 +115,7 @@ struct dma_buf_ops {
  * @exp_name: name of the exporter; useful for debugging.
  * @list_node: node for dma_buf accounting and debugging.
  * @priv: exporter specific private data for this buffer object.
+ * @resv: reservation object linked to this dma-buf
  */
 struct dma_buf {
        size_t size;
@@ -128,6 +129,7 @@ struct dma_buf {
        const char *exp_name;
        struct list_head list_node;
        void *priv;
+       struct reservation_object *resv;
 };
 
 /**
@@ -168,10 +170,11 @@ void dma_buf_detach(struct dma_buf *dmabuf,
                                struct dma_buf_attachment *dmabuf_attach);
 
 struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
-                              size_t size, int flags, const char *);
+                              size_t size, int flags, const char *,
+                              struct reservation_object *);
 
-#define dma_buf_export(priv, ops, size, flags) \
-       dma_buf_export_named(priv, ops, size, flags, KBUILD_MODNAME)
+#define dma_buf_export(priv, ops, size, flags, resv)   \
+       dma_buf_export_named(priv, ops, size, flags, KBUILD_MODNAME, resv)
 
 int dma_buf_fd(struct dma_buf *dmabuf, int flags);
 struct dma_buf *dma_buf_get(int fd);