]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
dma-buf: implement vmap refcounting in the interface logic
authorDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 20 Dec 2012 13:14:23 +0000 (14:14 +0100)
committerSumit Semwal <sumit.semwal@linaro.org>
Tue, 19 Feb 2013 10:24:46 +0000 (15:54 +0530)
All drivers which implement this need to have some sort of refcount to
allow concurrent vmap usage. Hence implement this in the dma-buf core.

To protect against concurrent calls we need a lock, which potentially
causes new funny locking inversions. But this shouldn't be a problem
for exporters with statically allocated backing storage, and more
dynamic drivers have decent issues already anyway.

Inspired by some refactoring patches from Aaron Plattner, who
implemented the same idea, but only for drm/prime drivers.

v2: Check in dma_buf_release that no dangling vmaps are left.
Suggested by Aaron Plattner. We might want to do similar checks for
attachments, but that's for another patch. Also fix up ERR_PTR return
for vmap.

v3: Check whether the passed-in vmap address matches with the cached
one for vunmap. Eventually we might want to remove that parameter -
compared to the kmap functions there's no need for the vaddr for
unmapping.  Suggested by Chris Wilson.

v4: Fix a brown-paper-bag bug spotted by Aaron Plattner.

Cc: Aaron Plattner <aplattner@nvidia.com>
Reviewed-by: Aaron Plattner <aplattner@nvidia.com>
Tested-by: Aaron Plattner <aplattner@nvidia.com>
Reviewed-by: Rob Clark <rob@ti.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Documentation/dma-buf-sharing.txt
drivers/base/dma-buf.c
include/linux/dma-buf.h

index 0188903bc9e1ede2c5668d5d78c632d2c4f923b3..4966b1be42ac09d17ecfa0ded944c13cf808ea37 100644 (file)
@@ -302,7 +302,11 @@ Access to a dma_buf from the kernel context involves three steps:
       void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
 
    The vmap call can fail if there is no vmap support in the exporter, or if it
-   runs out of vmalloc space. Fallback to kmap should be implemented.
+   runs out of vmalloc space. Fallback to kmap should be implemented. Note that
+   the dma-buf layer keeps a reference count for all vmap access and calls down
+   into the exporter's vmap function only when no vmapping exists, and only
+   unmaps it once. Protection against concurrent vmap/vunmap calls is provided
+   by taking the dma_buf->lock mutex.
 
 3. Finish access
 
index ff5b745c470525c5e1a19560cb81be188a76100c..394523807a6db8a9ad183268f1c7f3962ec85418 100644 (file)
@@ -39,6 +39,8 @@ static int dma_buf_release(struct inode *inode, struct file *file)
 
        dmabuf = file->private_data;
 
+       BUG_ON(dmabuf->vmapping_counter);
+
        dmabuf->ops->release(dmabuf);
        kfree(dmabuf);
        return 0;
@@ -481,12 +483,34 @@ EXPORT_SYMBOL_GPL(dma_buf_mmap);
  */
 void *dma_buf_vmap(struct dma_buf *dmabuf)
 {
+       void *ptr;
+
        if (WARN_ON(!dmabuf))
                return NULL;
 
-       if (dmabuf->ops->vmap)
-               return dmabuf->ops->vmap(dmabuf);
-       return NULL;
+       if (!dmabuf->ops->vmap)
+               return NULL;
+
+       mutex_lock(&dmabuf->lock);
+       if (dmabuf->vmapping_counter) {
+               dmabuf->vmapping_counter++;
+               BUG_ON(!dmabuf->vmap_ptr);
+               ptr = dmabuf->vmap_ptr;
+               goto out_unlock;
+       }
+
+       BUG_ON(dmabuf->vmap_ptr);
+
+       ptr = dmabuf->ops->vmap(dmabuf);
+       if (IS_ERR_OR_NULL(ptr))
+               goto out_unlock;
+
+       dmabuf->vmap_ptr = ptr;
+       dmabuf->vmapping_counter = 1;
+
+out_unlock:
+       mutex_unlock(&dmabuf->lock);
+       return ptr;
 }
 EXPORT_SYMBOL_GPL(dma_buf_vmap);
 
@@ -500,7 +524,16 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
        if (WARN_ON(!dmabuf))
                return;
 
-       if (dmabuf->ops->vunmap)
-               dmabuf->ops->vunmap(dmabuf, vaddr);
+       BUG_ON(!dmabuf->vmap_ptr);
+       BUG_ON(dmabuf->vmapping_counter == 0);
+       BUG_ON(dmabuf->vmap_ptr != vaddr);
+
+       mutex_lock(&dmabuf->lock);
+       if (--dmabuf->vmapping_counter == 0) {
+               if (dmabuf->ops->vunmap)
+                       dmabuf->ops->vunmap(dmabuf, vaddr);
+               dmabuf->vmap_ptr = NULL;
+       }
+       mutex_unlock(&dmabuf->lock);
 }
 EXPORT_SYMBOL_GPL(dma_buf_vunmap);
index bd2e52ccc4f24a7345c5eceaa698f3b2dc03ea82..e3bf2f67178c0367b0b151f748f44c54258e1cae 100644 (file)
@@ -119,8 +119,10 @@ struct dma_buf {
        struct file *file;
        struct list_head attachments;
        const struct dma_buf_ops *ops;
-       /* mutex to serialize list manipulation and attach/detach */
+       /* mutex to serialize list manipulation, attach/detach and vmap/unmap */
        struct mutex lock;
+       unsigned vmapping_counter;
+       void *vmap_ptr;
        void *priv;
 };