Message ID | 20170104133442.4534-4-noralf@tronnes.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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 --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
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