diff mbox

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

Message ID 20170104133442.4534-4-noralf@tronnes.org (mailing list archive)
State New, archived
Headers show

Commit Message

Noralf Trønnes Jan. 4, 2017, 1:34 p.m. UTC
Add a generic way for userspace to allocate dma-buf's for SPI transfers.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 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

Comments

Daniel Vetter Jan. 4, 2017, 3:08 p.m. UTC | #1
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 <noralf@tronnes.org>

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 0000000..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 <linux/dma-buf.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +#include <uapi/linux/dma-buf-dev.h>
> +
> +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);

Hm, does dma_alloc reall always work with dev == NULL? I had no idea this
was possible ...

Cheers, Daniel

> +	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;
> +
> +	dmabuf = dma_buf_export(&exp_info);
> +	if (IS_ERR(dmabuf)) {
> +		ret = PTR_ERR(dmabuf);
> +		goto err_free_buf;
> +	}
> +
> +	return dmabuf;
> +
> +err_free_buf:
> +	dma_free_attrs(dev, size, obj->vaddr, obj->dma_addr, attrs);
> +err_free_obj:
> +	kfree(obj);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static long dma_buf_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> +	struct dma_buf_dev_create create;
> +	struct dma_buf *dmabuf;
> +
> +	switch (cmd) {
> +	case DMA_BUF_DEV_IOCTL_CREATE:
> +
> +		if (copy_from_user(&create, (void __user *)arg, sizeof(create)))
> +			return -EFAULT;
> +
> +		if (!create.size)
> +			return -EINVAL;
> +
> +		dmabuf = dma_buf_dev_alloc_attrs(NULL, create.size,
> +						 create.attrs, create.flags);
> +		if (IS_ERR(dmabuf))
> +			return PTR_ERR(dmabuf);
> +
> +		create.fd = dma_buf_fd(dmabuf, create.flags);
> +		if (create.fd < 0) {
> +			dma_buf_put(dmabuf);
> +			return create.fd;
> +		}
> +
> +		if (copy_to_user((void __user *)arg, &create, sizeof(create)))
> +			return -EFAULT;
> +
> +		return 0;
> +	default:
> +		return -ENOTTY;
> +	}
> +}
> +
> +static const struct file_operations dma_buf_dev_fops = {
> +	.owner		= THIS_MODULE,
> +	.unlocked_ioctl	= dma_buf_dev_ioctl,
> +	.compat_ioctl	= dma_buf_dev_ioctl,
> +};
> +
> +static struct miscdevice dma_buf_dev_misc = {
> +	.fops		= &dma_buf_dev_fops,
> +	.minor		= MISC_DYNAMIC_MINOR,
> +	.name		= "dma-buf",
> +};
> +module_misc_device(dma_buf_dev_misc);
> +
> +MODULE_AUTHOR("Noralf Trønnes");
> +MODULE_DESCRIPTION("User mode dma-buf creation");
> +MODULE_LICENSE("GPL");
> diff --git a/include/uapi/linux/dma-buf-dev.h b/include/uapi/linux/dma-buf-dev.h
> new file mode 100644
> index 0000000..fddbe04
> --- /dev/null
> +++ b/include/uapi/linux/dma-buf-dev.h
> @@ -0,0 +1,35 @@
> +/*
> + * 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.
> + */
> +
> +#ifndef _DMA_BUF_DEV_UAPI_H_
> +#define _DMA_BUF_DEV_UAPI_H_
> +
> +#include <linux/types.h>
> +
> +/**
> + * struct dma_buf_dev_create -
> + * @attrs: Attributes of mapping properties requested in dma_alloc_attrs
> + * @size: Buffer size
> + * @flags: Mode flags for the dma-buf file
> + * @fd: Returned dma-buf file descriptor
> + */
> +struct dma_buf_dev_create {
> +	__u64 attrs;
> +#define DMA_BUF_DEV_ATTR_WRITE_COMBINE          BIT(2)
> +	__u64 size;
> +	__u64 flags;
> +
> +	__s64 fd;
> +};
> +
> +/* FIXME: Update Documentation/ioctl/ioctl-number.txt */
> +#define DMA_BUF_DEV_BASE		0xB6
> +#define DMA_BUF_DEV_IOCTL_CREATE	_IOWR(DMA_BUF_DEV_BASE, 0, struct dma_buf_dev_create)
> +
> +#endif
> -- 
> 2.10.2
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
diff mbox

Patch

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 0000000..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 <linux/dma-buf.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <uapi/linux/dma-buf-dev.h>
+
+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;
+
+	dmabuf = dma_buf_export(&exp_info);
+	if (IS_ERR(dmabuf)) {
+		ret = PTR_ERR(dmabuf);
+		goto err_free_buf;
+	}
+
+	return dmabuf;
+
+err_free_buf:
+	dma_free_attrs(dev, size, obj->vaddr, obj->dma_addr, attrs);
+err_free_obj:
+	kfree(obj);
+
+	return ERR_PTR(ret);
+}
+
+static long dma_buf_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct dma_buf_dev_create create;
+	struct dma_buf *dmabuf;
+
+	switch (cmd) {
+	case DMA_BUF_DEV_IOCTL_CREATE:
+
+		if (copy_from_user(&create, (void __user *)arg, sizeof(create)))
+			return -EFAULT;
+
+		if (!create.size)
+			return -EINVAL;
+
+		dmabuf = dma_buf_dev_alloc_attrs(NULL, create.size,
+						 create.attrs, create.flags);
+		if (IS_ERR(dmabuf))
+			return PTR_ERR(dmabuf);
+
+		create.fd = dma_buf_fd(dmabuf, create.flags);
+		if (create.fd < 0) {
+			dma_buf_put(dmabuf);
+			return create.fd;
+		}
+
+		if (copy_to_user((void __user *)arg, &create, sizeof(create)))
+			return -EFAULT;
+
+		return 0;
+	default:
+		return -ENOTTY;
+	}
+}
+
+static const struct file_operations dma_buf_dev_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl	= dma_buf_dev_ioctl,
+	.compat_ioctl	= dma_buf_dev_ioctl,
+};
+
+static struct miscdevice dma_buf_dev_misc = {
+	.fops		= &dma_buf_dev_fops,
+	.minor		= MISC_DYNAMIC_MINOR,
+	.name		= "dma-buf",
+};
+module_misc_device(dma_buf_dev_misc);
+
+MODULE_AUTHOR("Noralf Trønnes");
+MODULE_DESCRIPTION("User mode dma-buf creation");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/dma-buf-dev.h b/include/uapi/linux/dma-buf-dev.h
new file mode 100644
index 0000000..fddbe04
--- /dev/null
+++ b/include/uapi/linux/dma-buf-dev.h
@@ -0,0 +1,35 @@ 
+/*
+ * 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.
+ */
+
+#ifndef _DMA_BUF_DEV_UAPI_H_
+#define _DMA_BUF_DEV_UAPI_H_
+
+#include <linux/types.h>
+
+/**
+ * struct dma_buf_dev_create -
+ * @attrs: Attributes of mapping properties requested in dma_alloc_attrs
+ * @size: Buffer size
+ * @flags: Mode flags for the dma-buf file
+ * @fd: Returned dma-buf file descriptor
+ */
+struct dma_buf_dev_create {
+	__u64 attrs;
+#define DMA_BUF_DEV_ATTR_WRITE_COMBINE          BIT(2)
+	__u64 size;
+	__u64 flags;
+
+	__s64 fd;
+};
+
+/* FIXME: Update Documentation/ioctl/ioctl-number.txt */
+#define DMA_BUF_DEV_BASE		0xB6
+#define DMA_BUF_DEV_IOCTL_CREATE	_IOWR(DMA_BUF_DEV_BASE, 0, struct dma_buf_dev_create)
+
+#endif