video: tegra: nvmap: Introduce dma-buf exporter
Hiroshi Doyu [Tue, 4 Sep 2012 11:26:51 +0000 (14:26 +0300)]
Based on Terje's host nvmap exporter.

Change-Id: I9627ac9c4fc838f2e2c5c95b6972aa6185be7d6b
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
Cc: Terje Bergstrom <tbergstrom@nvidia.com>
Reviewed-on: http://git-master/r/133500
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>

drivers/video/tegra/nvmap/Makefile
drivers/video/tegra/nvmap/nvmap.h
drivers/video/tegra/nvmap/nvmap_dev.c
drivers/video/tegra/nvmap/nvmap_dmabuf.c [new file with mode: 0644]
drivers/video/tegra/nvmap/nvmap_ioctl.h
include/linux/nvmap.h

index 3da03af..6047f16 100644 (file)
@@ -5,4 +5,5 @@ obj-y += nvmap_handle.o
 obj-y += nvmap_heap.o
 obj-y += nvmap_ioctl.o
 obj-${CONFIG_IOMMU_API}        += nvmap_iommu.o
-obj-${CONFIG_NVMAP_RECLAIM_UNPINNED_VM} += nvmap_mru.o
\ No newline at end of file
+obj-${CONFIG_DMA_SHARED_BUFFER} += nvmap_dmabuf.o
+obj-${CONFIG_NVMAP_RECLAIM_UNPINNED_VM} += nvmap_mru.o
index 715a39f..a852ce3 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/sched.h>
 #include <linux/wait.h>
 #include <linux/atomic.h>
+#include <linux/dma-buf.h>
 #include <linux/nvmap.h>
 #include "nvmap_heap.h"
 
index 1a7e36f..78b01e0 100644 (file)
@@ -871,6 +871,10 @@ static long nvmap_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                err = nvmap_ioctl_cache_maint(filp, uarg);
                break;
 
+       case NVMAP_IOC_SHARE:
+               err = nvmap_ioctl_share_dmabuf(filp, uarg);
+               break;
+
        default:
                return -ENOTTY;
        }
diff --git a/drivers/video/tegra/nvmap/nvmap_dmabuf.c b/drivers/video/tegra/nvmap/nvmap_dmabuf.c
new file mode 100644 (file)
index 0000000..fcfb718
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * dma_buf exporter for nvmap
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/export.h>
+#include <linux/nvmap.h>
+#include <linux/dma-buf.h>
+
+#include "nvmap.h"
+#include "nvmap_ioctl.h"
+
+struct nvmap_handle_info {
+       struct nvmap_client *client;
+       u32 id;
+       struct nvmap_handle_ref *ref;
+       struct nvmap_handle *handle;
+};
+
+static int nvmap_dmabuf_attach(struct dma_buf *dmabuf, struct device *dev,
+                              struct dma_buf_attachment *attach)
+{
+       struct nvmap_handle_info *info = dmabuf->priv;
+       struct nvmap_handle_ref *ref;
+
+       ref = nvmap_duplicate_handle_id(info->client, info->id);
+       if (IS_ERR(ref))
+               return PTR_ERR(ref);
+
+       info->ref = ref;
+       attach->priv = info;
+
+       dev_dbg(dev, "%s(%08x)\n", __func__, info->id);
+       return 0;
+}
+
+static void nvmap_dmabuf_detach(struct dma_buf *dmabuf,
+                               struct dma_buf_attachment *attach)
+{
+       struct nvmap_handle_info *info = dmabuf->priv;
+
+       nvmap_free(info->client, info->ref);
+
+       dev_dbg(attach->dev, "%s(%08x)\n", __func__, info->id);
+}
+
+static struct sg_table *nvmap_dmabuf_map_dma_buf(
+       struct dma_buf_attachment *attach, enum dma_data_direction dir)
+{
+       struct nvmap_handle_info *info = attach->dmabuf->priv;
+       struct nvmap_handle *handle = info->ref->handle;
+       int err, npages = PAGE_ALIGN(handle->size) >> PAGE_SHIFT;
+       struct sg_table *sgt;
+       dma_addr_t addr;
+
+       if (WARN_ON(!handle->heap_pgalloc))
+               return ERR_PTR(-EINVAL);
+
+       sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+       if (!sgt)
+               return ERR_PTR(-ENOMEM);
+
+       addr = nvmap_pin(info->client, info->ref);
+       if (IS_ERR_VALUE(addr)) {
+               err = addr;
+               goto err_pin;
+       }
+
+       if (info->ref->handle->pgalloc.contig) {
+               err = sg_alloc_table(sgt, 1, GFP_KERNEL);
+               if (err)
+                       goto err_sgalloc;
+               sg_set_page(sgt->sgl, *handle->pgalloc.pages, handle->size, 0);
+       } else {
+               err = sg_alloc_table_from_pages(sgt, handle->pgalloc.pages,
+                                               npages, 0, handle->size,
+                                               GFP_KERNEL);
+               if (err)
+                       goto err_sgalloc;
+       }
+       sg_dma_len(sgt->sgl) = handle->size;
+       sg_dma_address(sgt->sgl) = addr;
+
+       dev_dbg(attach->dev, "%s(%08x)\n", __func__, info->id);
+       return sgt;
+
+err_sgalloc:
+       nvmap_unpin(info->client, info->ref);
+err_pin:
+       kfree(sgt);
+       return ERR_PTR(err);
+}
+
+static void nvmap_dmabuf_unmap_dma_buf(struct dma_buf_attachment *attach,
+                                      struct sg_table *sgt,
+                                      enum dma_data_direction dir)
+{
+       struct nvmap_handle_info *info = attach->dmabuf->priv;
+
+       nvmap_unpin(info->client, info->ref);
+       sg_free_table(sgt);
+       kfree(sgt);
+
+       dev_dbg(attach->dev, "%s(%08x)\n", __func__, info->id);
+}
+
+static void nvmap_dmabuf_release(struct dma_buf *dmabuf)
+{
+       struct nvmap_handle_info *info = dmabuf->priv;
+
+       pr_debug("%s(%08x)\n", __func__, info->id);
+
+       nvmap_handle_put(info->handle);
+       nvmap_client_put(info->client);
+       kfree(info);
+}
+
+static void *nvmap_dmabuf_kmap(struct dma_buf *dmabuf, unsigned long page_num)
+{
+       struct nvmap_handle_info *info = dmabuf->priv;
+       unsigned long allpages = PAGE_ALIGN(dmabuf->size) >> PAGE_SHIFT;
+
+       if (page_num != allpages) {
+               pr_err("%s() doesn't support partial kmap\n", __func__);
+               return NULL;
+       }
+
+       pr_debug("%s(%08x)\n", __func__, info->id);
+       return nvmap_mmap(info->ref);
+}
+
+static void *nvmap_dmabuf_kmap_atomic(struct dma_buf *dmabuf,
+                                     unsigned long page_num)
+{
+       WARN(1, "%s() can't be called from atomic\n", __func__);
+       return NULL;
+}
+
+static int nvmap_dmabuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+       WARN(1, "%s() not implemented yet\n", __func__);
+       return -1;
+}
+
+static void *nvmap_dmabuf_vmap(struct dma_buf *dmabuf)
+{
+       struct nvmap_handle_info *info = dmabuf->priv;
+
+       pr_debug("%s(%08x)\n", __func__, info->id);
+       return nvmap_mmap(info->ref);
+}
+
+static void nvmap_dmabuf_vunmap(struct dma_buf *dmabuf, void *vaddr)
+{
+       struct nvmap_handle_info *info = dmabuf->priv;
+
+       pr_debug("%s(%08x)\n", __func__, info->id);
+       nvmap_munmap(info->ref, vaddr);
+}
+
+static struct dma_buf_ops nvmap_dma_buf_ops = {
+       .attach         = nvmap_dmabuf_attach,
+       .detach         = nvmap_dmabuf_detach,
+       .map_dma_buf    = nvmap_dmabuf_map_dma_buf,
+       .unmap_dma_buf  = nvmap_dmabuf_unmap_dma_buf,
+       .release        = nvmap_dmabuf_release,
+       .kmap_atomic    = nvmap_dmabuf_kmap_atomic,
+       .kmap           = nvmap_dmabuf_kmap,
+       .mmap           = nvmap_dmabuf_mmap,
+       .vmap           = nvmap_dmabuf_vmap,
+       .vunmap         = nvmap_dmabuf_vunmap,
+};
+
+struct dma_buf *nvmap_share_dmabuf(struct nvmap_client *client, u32 id)
+{
+       struct dma_buf *dmabuf;
+       struct nvmap_handle_info *info;
+       int err;
+       struct nvmap_handle *handle;
+
+       if (!nvmap_client_get(client))
+               return ERR_PTR(-EINVAL);
+
+       handle = nvmap_validate_get(client, id);
+       if (!handle) {
+               err = -EINVAL;
+               goto err_nvmap_validate_get;
+       }
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               err = -ENOMEM;
+               goto err_nomem;
+       }
+       info->id = id;
+       info->handle = handle;
+       info->client = client;
+
+       dmabuf = dma_buf_export(info, &nvmap_dma_buf_ops, handle->size,
+                               O_RDWR);
+       if (IS_ERR(dmabuf)) {
+               err = PTR_ERR(dmabuf);
+               goto err_export;
+       }
+       pr_debug("%s(%08x) %p\n", __func__, info->id, dmabuf);
+       return dmabuf;
+
+err_export:
+       kfree(info);
+err_nomem:
+       nvmap_handle_put(handle);
+err_nvmap_validate_get:
+       nvmap_client_put(client);
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(nvmap_share_dmabuf);
+
+int nvmap_ioctl_share_dmabuf(struct file *filp, void __user *arg)
+{
+       int err;
+       struct nvmap_create_handle op;
+       struct nvmap_client *client = filp->private_data;
+       struct dma_buf *dmabuf;
+
+       BUG_ON(!client);
+
+       if (copy_from_user(&op, (void __user *)arg, sizeof(op)))
+               return -EFAULT;
+
+       dmabuf = nvmap_share_dmabuf(client, op.id);
+       if (IS_ERR(dmabuf))
+               return -EINVAL;
+
+       op.fd = dma_buf_fd(dmabuf, O_CLOEXEC);
+       if (op.fd < 0) {
+               err = op.fd;
+               goto err_out;
+       }
+
+       if (copy_to_user((void __user *)arg, &op, sizeof(op))) {
+               err = -EFAULT;
+               goto err_out;
+       }
+       return 0;
+
+err_out:
+       nvmap_dmabuf_release(dmabuf);
+       return err;
+}
index 300ce9b..e98c304 100644 (file)
@@ -3,7 +3,7 @@
  *
  * ioctl declarations for nvmap
  *
- * Copyright (c) 2010, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -49,6 +49,7 @@ struct nvmap_create_handle {
                __u32 key;      /* ClaimPreservedHandle */
                __u32 id;       /* FromId */
                __u32 size;     /* CreateHandle */
+               int fd;         /* dmabuf fd */
        };
        __u32 handle;
 };
@@ -136,7 +137,11 @@ struct nvmap_cache_op {
  * reference to the same handle */
 #define NVMAP_IOC_GET_ID  _IOWR(NVMAP_IOC_MAGIC, 13, struct nvmap_create_handle)
 
-#define NVMAP_IOC_MAXNR (_IOC_NR(NVMAP_IOC_GET_ID))
+/* Returns a dma-buf fd usable to allow a remote process to create a handle
+ * reference to the same handle */
+#define NVMAP_IOC_SHARE  _IOWR(NVMAP_IOC_MAGIC, 14, struct nvmap_create_handle)
+
+#define NVMAP_IOC_MAXNR (_IOC_NR(NVMAP_IOC_SHARE))
 
 #ifdef  __KERNEL__
 int nvmap_ioctl_pinop(struct file *filp, bool is_pin, void __user *arg);
@@ -156,8 +161,16 @@ int nvmap_map_into_caller_ptr(struct file *filp, void __user *arg);
 int nvmap_ioctl_cache_maint(struct file *filp, void __user *arg);
 
 int nvmap_ioctl_rw_handle(struct file *filp, int is_read, void __user* arg);
-#endif
 
+#ifdef CONFIG_DMA_SHARED_BUFFER
+int nvmap_ioctl_share_dmabuf(struct file *filp, void __user *arg);
+#else
+static inline int nvmap_ioctl_share_dmabuf(struct file *filp, void __user *arg)
+{
+       return -EINVAL;
+}
+#endif /* !CONFIG_DMA_SHARED_BUFFER */
 
+#endif /* __KERNEL__ */
 
-#endif
+#endif /*  __VIDEO_TEGRA_NVMAP_IOCTL_H */
index b9dca8a..795a7fc 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/ioctl.h>
 #include <linux/file.h>
 #include <linux/rbtree.h>
+#include <linux/dma-buf.h>
 
 #if !defined(__KERNEL__)
 #define __user
@@ -140,6 +141,17 @@ struct nvmap_platform_data {
 
 extern struct nvmap_device *nvmap_dev;
 
+#ifdef CONFIG_DMA_SHARED_BUFFER
+/* dma-buf exporter */
+struct dma_buf *nvmap_share_dmabuf(struct nvmap_client *client, u32 id);
+#else
+static inline struct dma_buf *nvmap_share_dmabuf(struct nvmap_client *client,
+                                                u32 id)
+{
+       return NULL;
+}
+#endif /* !CONFIG_DMA_SHARED_BUFFER */
+
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_NVMAP_H */