[RFC 3/6] dma-buf: Support generic userspace allocations

2017-01-04 Thread Daniel Vetter
On Wed, Jan 04, 2017 at 02:34:39PM +0100, Noralf Trønnes wrote:
> Add a generic way for userspace to allocate dma-buf's for SPI transfers.
> 
> Signed-off-by: Noralf Trønnes 

Having a central dma-buf allocator is a common thing, there's already ION
in drivers/staging/android. If we need one I think it's better to
accelarate ION destaging than creating yet another one.
> ---
>  drivers/dma-buf/Makefile |   2 +-
>  drivers/dma-buf/dev.c| 211 
> +++
>  include/uapi/linux/dma-buf-dev.h |  35 +++
>  3 files changed, 247 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/dma-buf/dev.c
>  create mode 100644 include/uapi/linux/dma-buf-dev.h
> 
> diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
> index 210a10b..ec867f7 100644
> --- a/drivers/dma-buf/Makefile
> +++ b/drivers/dma-buf/Makefile
> @@ -1,3 +1,3 @@
> -obj-y := dma-buf.o fence.o reservation.o seqno-fence.o fence-array.o
> +obj-y := dma-buf.o fence.o reservation.o seqno-fence.o fence-array.o dev.o
>  obj-$(CONFIG_SYNC_FILE)  += sync_file.o
>  obj-$(CONFIG_SW_SYNC)+= sw_sync.o sync_debug.o
> diff --git a/drivers/dma-buf/dev.c b/drivers/dma-buf/dev.c
> new file mode 100644
> index 000..536d9bf
> --- /dev/null
> +++ b/drivers/dma-buf/dev.c
> @@ -0,0 +1,211 @@
> +/*
> + * Copyright (C) 2016 Noralf Trønnes
> + *
> + * 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
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include 
> +
> +struct dma_buf_dev_object {
> + struct device *dev;
> + unsigned long attrs;
> + dma_addr_t dma_addr;
> + void *vaddr;
> + size_t size;
> +};
> +
> +static struct sg_table *
> +dma_buf_dev_map_dma_buf(struct dma_buf_attachment *attach,
> + enum dma_data_direction dir)
> +{
> + struct dma_buf_dev_object *obj = attach->dmabuf->priv;
> + struct sg_table *sgt;
> + int ret;
> +
> + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
> + if (!sgt)
> + return ERR_PTR(-ENOMEM);
> +
> + ret = dma_get_sgtable(obj->dev, sgt, obj->vaddr,
> +   obj->dma_addr, obj->size);
> + if (ret < 0)
> + goto err_free;
> +
> + if (!dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir)) {
> + ret = -ENOMEM;
> + goto err_free_table;
> + }
> +
> + return sgt;
> +
> +err_free_table:
> + sg_free_table(sgt);
> +err_free:
> + kfree(sgt);
> +
> + return ERR_PTR(ret);
> +}
> +
> +static void dma_buf_dev_unmap_dma_buf(struct dma_buf_attachment *attach,
> +   struct sg_table *sgt,
> +   enum dma_data_direction dir)
> +{
> + dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
> + sg_free_table(sgt);
> + kfree(sgt);
> +}
> +
> +static void dma_buf_dev_release(struct dma_buf *dma_buf)
> +{
> + struct dma_buf_dev_object *obj = dma_buf->priv;
> +
> +/* FIXME remove */
> +pr_info("%s()\n", __func__);
> + dma_free_attrs(obj->dev, obj->size, obj->vaddr, obj->dma_addr,
> +obj->attrs);
> + kfree(obj);
> +}
> +
> +static void *dma_buf_dev_kmap(struct dma_buf *dma_buf, unsigned long 
> page_num)
> +{
> + struct dma_buf_dev_object *obj = dma_buf->priv;
> +
> + return obj->vaddr + page_num * PAGE_SIZE;
> +}
> +
> +static void *dma_buf_dev_vmap(struct dma_buf *dma_buf)
> +{
> + struct dma_buf_dev_object *obj = dma_buf->priv;
> +
> + return obj->vaddr;
> +}
> +
> +static int dma_buf_dev_mmap(struct dma_buf *dma_buf,
> + struct vm_area_struct *vma)
> +{
> + struct dma_buf_dev_object *obj = dma_buf->priv;
> + int ret;
> +
> + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
> +
> + ret = dma_mmap_attrs(obj->dev, vma, obj->vaddr, obj->dma_addr,
> +  vma->vm_end - vma->vm_start, obj->attrs);
> +
> + return ret;
> +}
> +
> +static const struct dma_buf_ops dma_buf_dev_ops =  {
> + .map_dma_buf = dma_buf_dev_map_dma_buf,
> + .unmap_dma_buf = dma_buf_dev_unmap_dma_buf,
> + .release = dma_buf_dev_release,
> + .kmap_atomic = dma_buf_dev_kmap,
> + .kmap = dma_buf_dev_kmap,
> + .vmap = dma_buf_dev_vmap,
> + .mmap = dma_buf_dev_mmap,
> +};
> +
> +struct dma_buf *dma_buf_dev_alloc_attrs(struct device *dev, size_t size,
> + unsigned long attrs, int flags)
> +{
> + DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
> + struct dma_buf_dev_object *obj;
> + struct dma_buf *dmabuf;
> + int ret;
> +
> + if (flags & ~(O_CLOEXEC | O_ACCMODE))
> + return ERR_PTR(-EINVAL);
> +
> + obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> +

[RFC 3/6] dma-buf: Support generic userspace allocations

2017-01-04 Thread Noralf Trønnes
Add a generic way for userspace to allocate dma-buf's for SPI transfers.

Signed-off-by: Noralf Trønnes 
---
 drivers/dma-buf/Makefile |   2 +-
 drivers/dma-buf/dev.c| 211 +++
 include/uapi/linux/dma-buf-dev.h |  35 +++
 3 files changed, 247 insertions(+), 1 deletion(-)
 create mode 100644 drivers/dma-buf/dev.c
 create mode 100644 include/uapi/linux/dma-buf-dev.h

diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index 210a10b..ec867f7 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -1,3 +1,3 @@
-obj-y := dma-buf.o fence.o reservation.o seqno-fence.o fence-array.o
+obj-y := dma-buf.o fence.o reservation.o seqno-fence.o fence-array.o dev.o
 obj-$(CONFIG_SYNC_FILE)+= sync_file.o
 obj-$(CONFIG_SW_SYNC)  += sw_sync.o sync_debug.o
diff --git a/drivers/dma-buf/dev.c b/drivers/dma-buf/dev.c
new file mode 100644
index 000..536d9bf
--- /dev/null
+++ b/drivers/dma-buf/dev.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2016 Noralf Trønnes
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+struct dma_buf_dev_object {
+   struct device *dev;
+   unsigned long attrs;
+   dma_addr_t dma_addr;
+   void *vaddr;
+   size_t size;
+};
+
+static struct sg_table *
+dma_buf_dev_map_dma_buf(struct dma_buf_attachment *attach,
+   enum dma_data_direction dir)
+{
+   struct dma_buf_dev_object *obj = attach->dmabuf->priv;
+   struct sg_table *sgt;
+   int ret;
+
+   sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+   if (!sgt)
+   return ERR_PTR(-ENOMEM);
+
+   ret = dma_get_sgtable(obj->dev, sgt, obj->vaddr,
+ obj->dma_addr, obj->size);
+   if (ret < 0)
+   goto err_free;
+
+   if (!dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir)) {
+   ret = -ENOMEM;
+   goto err_free_table;
+   }
+
+   return sgt;
+
+err_free_table:
+   sg_free_table(sgt);
+err_free:
+   kfree(sgt);
+
+   return ERR_PTR(ret);
+}
+
+static void dma_buf_dev_unmap_dma_buf(struct dma_buf_attachment *attach,
+ struct sg_table *sgt,
+ enum dma_data_direction dir)
+{
+   dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
+   sg_free_table(sgt);
+   kfree(sgt);
+}
+
+static void dma_buf_dev_release(struct dma_buf *dma_buf)
+{
+   struct dma_buf_dev_object *obj = dma_buf->priv;
+
+/* FIXME remove */
+pr_info("%s()\n", __func__);
+   dma_free_attrs(obj->dev, obj->size, obj->vaddr, obj->dma_addr,
+  obj->attrs);
+   kfree(obj);
+}
+
+static void *dma_buf_dev_kmap(struct dma_buf *dma_buf, unsigned long page_num)
+{
+   struct dma_buf_dev_object *obj = dma_buf->priv;
+
+   return obj->vaddr + page_num * PAGE_SIZE;
+}
+
+static void *dma_buf_dev_vmap(struct dma_buf *dma_buf)
+{
+   struct dma_buf_dev_object *obj = dma_buf->priv;
+
+   return obj->vaddr;
+}
+
+static int dma_buf_dev_mmap(struct dma_buf *dma_buf,
+   struct vm_area_struct *vma)
+{
+   struct dma_buf_dev_object *obj = dma_buf->priv;
+   int ret;
+
+   vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+
+   ret = dma_mmap_attrs(obj->dev, vma, obj->vaddr, obj->dma_addr,
+vma->vm_end - vma->vm_start, obj->attrs);
+
+   return ret;
+}
+
+static const struct dma_buf_ops dma_buf_dev_ops =  {
+   .map_dma_buf = dma_buf_dev_map_dma_buf,
+   .unmap_dma_buf = dma_buf_dev_unmap_dma_buf,
+   .release = dma_buf_dev_release,
+   .kmap_atomic = dma_buf_dev_kmap,
+   .kmap = dma_buf_dev_kmap,
+   .vmap = dma_buf_dev_vmap,
+   .mmap = dma_buf_dev_mmap,
+};
+
+struct dma_buf *dma_buf_dev_alloc_attrs(struct device *dev, size_t size,
+   unsigned long attrs, int flags)
+{
+   DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+   struct dma_buf_dev_object *obj;
+   struct dma_buf *dmabuf;
+   int ret;
+
+   if (flags & ~(O_CLOEXEC | O_ACCMODE))
+   return ERR_PTR(-EINVAL);
+
+   obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+   if (!obj)
+   return ERR_PTR(-ENOMEM);
+
+   obj->dev = dev;
+   obj->size = size;
+   obj->attrs = attrs;
+
+   obj->vaddr = dma_alloc_attrs(dev, size, &obj->dma_addr, GFP_KERNEL, 
attrs);
+   if (!obj->vaddr) {
+   ret = -ENOMEM;
+   goto err_free_obj;
+   }
+
+   exp_info.ops = &dma_buf_dev_ops;
+   exp_info.size = obj->size;
+   exp_info.flags = flags;
+   exp_info.priv = obj;