From mboxrd@z Thu Jan 1 00:00:00 1970 From: ckoenig.leichtzumerken at gmail.com (=?UTF-8?Q?Christian_K=c3=b6nig?=) Date: Fri, 16 Mar 2018 13:30:43 +0100 Subject: [PATCH v2] Add udmabuf misc device In-Reply-To: <20180316074650.5415-1-kraxel@redhat.com> References: <20180316074650.5415-1-kraxel@redhat.com> Message-ID: <7547e99b-0e3c-264e-e52b-40ad5d52b49a@gmail.com> Am 16.03.2018 um 08:46 schrieb Gerd Hoffmann: > A driver to let userspace turn iovecs into dma-bufs. > > Use case: Allows qemu create dmabufs for the vga framebuffer or > virtio-gpu ressources. Then they can be passed around to display > those guest things on the host. To spice client for classic full > framebuffer display, and hopefully some day to wayland server for > seamless guest window display. > > Those dma-bufs are accounted against user's shm mlock bucket as the > pages are effectively locked in memory. Sorry to disappoint you, but we have investigated the same idea for userptr use quite extensively and found that whole approach doesn't work. The pages backing a DMA-buf are not allowed to move (at least not without a patch set I'm currently working on), but for certain MM operations to work correctly you must be able to modify the page tables entries and move the pages backing them around. For example try to use fork() with some copy on write pages with this approach. You will find that you have only two options to correctly handle this. Either you access memory which could no longer belong to your process, which in turn is major security no-go. Or you use a MM notifier, but without being able to unmap DMA-bufs that won't work and you will certainly create a deadlock. Even with the patch set I'm currently working on the MM notifier approach is a real beast to get right. Regards, Christian. > > Cc: David Airlie > Cc: Tomeu Vizoso > Cc: Daniel Vetter > Signed-off-by: Gerd Hoffmann > --- > include/uapi/linux/udmabuf.h | 23 ++ > drivers/dma-buf/udmabuf.c | 261 ++++++++++++++++++++++ > tools/testing/selftests/drivers/dma-buf/udmabuf.c | 69 ++++++ > drivers/dma-buf/Kconfig | 7 + > drivers/dma-buf/Makefile | 1 + > tools/testing/selftests/drivers/dma-buf/Makefile | 5 + > 6 files changed, 366 insertions(+) > create mode 100644 include/uapi/linux/udmabuf.h > create mode 100644 drivers/dma-buf/udmabuf.c > create mode 100644 tools/testing/selftests/drivers/dma-buf/udmabuf.c > create mode 100644 tools/testing/selftests/drivers/dma-buf/Makefile > > diff --git a/include/uapi/linux/udmabuf.h b/include/uapi/linux/udmabuf.h > new file mode 100644 > index 0000000000..54ceba203a > --- /dev/null > +++ b/include/uapi/linux/udmabuf.h > @@ -0,0 +1,23 @@ > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > +#ifndef _UAPI_LINUX_UDMABUF_H > +#define _UAPI_LINUX_UDMABUF_H > + > +#include > +#include > + > +struct udmabuf_iovec { > + __u64 base; > + __u64 len; > +}; > + > +#define UDMABUF_FLAGS_CLOEXEC 0x01 > + > +struct udmabuf_create { > + __u32 flags; > + __u32 niov; > + struct udmabuf_iovec iovs[]; > +}; > + > +#define UDMABUF_CREATE _IOW(0x42, 0x23, struct udmabuf_create) > + > +#endif /* _UAPI_LINUX_UDMABUF_H */ > diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c > new file mode 100644 > index 0000000000..664ab4ee4e > --- /dev/null > +++ b/drivers/dma-buf/udmabuf.c > @@ -0,0 +1,261 @@ > +/* > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +struct udmabuf { > + u32 pagecount; > + struct page **pages; > + struct user_struct *owner; > +}; > + > +static int udmabuf_vm_fault(struct vm_fault *vmf) > +{ > + struct vm_area_struct *vma = vmf->vma; > + struct udmabuf *ubuf = vma->vm_private_data; > + > + if (WARN_ON(vmf->pgoff >= ubuf->pagecount)) > + return VM_FAULT_SIGBUS; > + > + vmf->page = ubuf->pages[vmf->pgoff]; > + get_page(vmf->page); > + return 0; > +} > + > +static const struct vm_operations_struct udmabuf_vm_ops = { > + .fault = udmabuf_vm_fault, > +}; > + > +static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma) > +{ > + struct udmabuf *ubuf = buf->priv; > + > + if ((vma->vm_flags & VM_SHARED) == 0) > + return -EINVAL; > + > + vma->vm_ops = &udmabuf_vm_ops; > + vma->vm_private_data = ubuf; > + return 0; > +} > + > +static struct sg_table *map_udmabuf(struct dma_buf_attachment *at, > + enum dma_data_direction direction) > +{ > + struct udmabuf *ubuf = at->dmabuf->priv; > + struct sg_table *sg; > + > + sg = kzalloc(sizeof(*sg), GFP_KERNEL); > + if (!sg) > + goto err1; > + if (sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->pagecount, > + 0, ubuf->pagecount << PAGE_SHIFT, > + GFP_KERNEL) < 0) > + goto err2; > + if (!dma_map_sg(at->dev, sg->sgl, sg->nents, direction)) > + goto err3; > + > + return sg; > + > +err3: > + sg_free_table(sg); > +err2: > + kfree(sg); > +err1: > + return ERR_PTR(-ENOMEM); > +} > + > +static void unmap_udmabuf(struct dma_buf_attachment *at, > + struct sg_table *sg, > + enum dma_data_direction direction) > +{ > + sg_free_table(sg); > + kfree(sg); > +} > + > +static void release_udmabuf(struct dma_buf *buf) > +{ > + struct udmabuf *ubuf = buf->priv; > + pgoff_t pg; > + > + for (pg = 0; pg < ubuf->pagecount; pg++) > + put_page(ubuf->pages[pg]); > + user_shm_unlock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner); > + free_uid(ubuf->owner); > + kfree(ubuf->pages); > + kfree(ubuf); > +} > + > +static void *kmap_atomic_udmabuf(struct dma_buf *buf, unsigned long page_num) > +{ > + struct udmabuf *ubuf = buf->priv; > + struct page *page = ubuf->pages[page_num]; > + > + return kmap_atomic(page); > +} > + > +static void *kmap_udmabuf(struct dma_buf *buf, unsigned long page_num) > +{ > + struct udmabuf *ubuf = buf->priv; > + struct page *page = ubuf->pages[page_num]; > + > + return kmap(page); > +} > + > +static struct dma_buf_ops udmabuf_ops = { > + .map_dma_buf = map_udmabuf, > + .unmap_dma_buf = unmap_udmabuf, > + .release = release_udmabuf, > + .map_atomic = kmap_atomic_udmabuf, > + .map = kmap_udmabuf, > + .mmap = mmap_udmabuf, > +}; > + > +static long udmabuf_ioctl_create(struct file *filp, unsigned long arg) > +{ > + struct udmabuf_create create; > + struct udmabuf_iovec *iovs; > + struct udmabuf *ubuf; > + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); > + struct dma_buf *buf; > + pgoff_t pgoff, pgcnt; > + u32 iov, flags; > + int ret; > + > + if (copy_from_user(&create, (void __user *)arg, > + sizeof(struct udmabuf_create))) > + return -EFAULT; > + > + iovs = kmalloc_array(create.niov, sizeof(struct udmabuf_iovec), > + GFP_KERNEL); > + if (!iovs) > + return -ENOMEM; > + > + arg += offsetof(struct udmabuf_create, iovs); > + ret = -EFAULT; > + if (copy_from_user(iovs, (void __user *)arg, > + create.niov * sizeof(struct udmabuf_iovec))) > + goto err_free_iovs; > + > + ubuf = kzalloc(sizeof(struct udmabuf), GFP_KERNEL); > + if (!ubuf) > + goto err_free_iovs; > + > + ret = -EINVAL; > + for (iov = 0; iov < create.niov; iov++) { > + if (!IS_ALIGNED(iovs[iov].base, PAGE_SIZE)) > + goto err_free_buf; > + if (!IS_ALIGNED(iovs[iov].len, PAGE_SIZE)) > + goto err_free_buf; > + ubuf->pagecount += iovs[iov].len >> PAGE_SHIFT; > + } > + > + /* this effectively mlocks the pages so account it accordingly */ > + ret = -ENOMEM; > + ubuf->owner = current_user(); > + if (!user_shm_lock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner)) > + goto err_free_buf; > + > + ubuf->pages = kmalloc_array(ubuf->pagecount, sizeof(struct page*), > + GFP_KERNEL); > + if (!ubuf->pages) > + goto err_shm_unlock; > + > + pgoff = 0; > + for (iov = 0; iov < create.niov; iov++) { > + pgcnt = iovs[iov].len >> PAGE_SHIFT; > + while (pgcnt > 0) { > + ret = get_user_pages_fast(iovs[iov].base, pgcnt, > + true, /* write */ > + ubuf->pages + pgoff); > + if (ret < 0) > + goto err_put_pages; > + pgoff += ret; > + pgcnt -= ret; > + } > + } > + > + exp_info.ops = &udmabuf_ops; > + exp_info.size = ubuf->pagecount << PAGE_SHIFT; > + exp_info.priv = ubuf; > + > + buf = dma_buf_export(&exp_info); > + if (IS_ERR(buf)) { > + ret = PTR_ERR(buf); > + goto err_put_pages; > + } > + > + flags = 0; > + if (create.flags & UDMABUF_FLAGS_CLOEXEC) > + flags |= O_CLOEXEC; > + > + kfree(iovs); > + return dma_buf_fd(buf, flags); > + > +err_put_pages: > + while (pgoff > 0) > + put_page(ubuf->pages[--pgoff]); > +err_shm_unlock: > + user_shm_unlock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner); > +err_free_buf: > + free_uid(ubuf->owner); > + kfree(ubuf->pages); > + kfree(ubuf); > +err_free_iovs: > + kfree(iovs); > + return ret; > +} > + > +static long udmabuf_ioctl(struct file *filp, unsigned int ioctl, > + unsigned long arg) > +{ > + long ret; > + > + switch (ioctl) { > + case UDMABUF_CREATE: > + ret = udmabuf_ioctl_create(filp, arg); > + break; > + default: > + ret = -EINVAL; > + break; > + } > + return ret; > +} > + > +static const struct file_operations udmabuf_fops = { > + .owner = THIS_MODULE, > + .unlocked_ioctl = udmabuf_ioctl, > +}; > + > +static struct miscdevice udmabuf_misc = { > + .minor = MISC_DYNAMIC_MINOR, > + .name = "udmabuf", > + .fops = &udmabuf_fops, > +}; > + > +static int __init udmabuf_dev_init(void) > +{ > + return misc_register(&udmabuf_misc); > +} > + > +static void __exit udmabuf_dev_exit(void) > +{ > + misc_deregister(&udmabuf_misc); > +} > + > +module_init(udmabuf_dev_init) > +module_exit(udmabuf_dev_exit) > + > +MODULE_AUTHOR("Gerd Hoffmann "); > +MODULE_LICENSE("GPL v2"); > diff --git a/tools/testing/selftests/drivers/dma-buf/udmabuf.c b/tools/testing/selftests/drivers/dma-buf/udmabuf.c > new file mode 100644 > index 0000000000..3472c8ee49 > --- /dev/null > +++ b/tools/testing/selftests/drivers/dma-buf/udmabuf.c > @@ -0,0 +1,69 @@ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#define TEST_PREFIX "drivers/dma-buf/udmabuf" > +#define NUM_PAGES 4 > + > +int main(int argc, char *argv[]) > +{ > + struct udmabuf_create *create; > + void *mem; > + int dev, fd; > + > + dev = open("/dev/udmabuf", O_RDWR); > + if (dev < 0) { > + printf("%s: [skip]\n", TEST_PREFIX); > + exit(77); > + } > + > + mem = memalign(getpagesize(), getpagesize() * NUM_PAGES); > + if (mem == NULL) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit (1); > + } > + > + create = malloc(sizeof(struct udmabuf_create) + > + sizeof(struct udmabuf_iovec)); > + create->flags = 0; > + create->niov = 1; > + > + /* should fail (base not page aligned) */ > + create->iovs[0].base = (intptr_t)mem + getpagesize()/2; > + create->iovs[0].len = getpagesize(); > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd >= 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + > + /* should fail (size not multiple of page) */ > + create->iovs[0].base = (intptr_t)mem; > + create->iovs[0].len = getpagesize()/2; > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd >= 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + > + /* should work */ > + create->iovs[0].base = (intptr_t)mem; > + create->iovs[0].len = getpagesize() * NUM_PAGES; > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd < 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + close(fd); > + > + fprintf(stderr, "%s: ok\n", TEST_PREFIX); > + close(dev); > + return 0; > +} > diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig > index ed3b785bae..19be3ec62d 100644 > --- a/drivers/dma-buf/Kconfig > +++ b/drivers/dma-buf/Kconfig > @@ -30,4 +30,11 @@ config SW_SYNC > WARNING: improper use of this can result in deadlocking kernel > drivers from userspace. Intended for test and debug only. > > +config UDMABUF > + bool "userspace dmabuf misc driver" > + default n > + depends on DMA_SHARED_BUFFER > + ---help--- > + A driver to let userspace turn iovs into dma-bufs. > + > endmenu > diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile > index c33bf88631..0913a6ccab 100644 > --- a/drivers/dma-buf/Makefile > +++ b/drivers/dma-buf/Makefile > @@ -1,3 +1,4 @@ > obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o > obj-$(CONFIG_SYNC_FILE) += sync_file.o > obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o > +obj-$(CONFIG_UDMABUF) += udmabuf.o > diff --git a/tools/testing/selftests/drivers/dma-buf/Makefile b/tools/testing/selftests/drivers/dma-buf/Makefile > new file mode 100644 > index 0000000000..4154c3d7aa > --- /dev/null > +++ b/tools/testing/selftests/drivers/dma-buf/Makefile > @@ -0,0 +1,5 @@ > +CFLAGS += -I../../../../../usr/include/ > + > +TEST_GEN_PROGS := udmabuf > + > +include ../../lib.mk -- To unsubscribe from this list: send the line "unsubscribe linux-kselftest" in the body of a message to majordomo at vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html From mboxrd@z Thu Jan 1 00:00:00 1970 From: ckoenig.leichtzumerken@gmail.com (=?UTF-8?Q?Christian_K=c3=b6nig?=) Date: Fri, 16 Mar 2018 13:30:43 +0100 Subject: [PATCH v2] Add udmabuf misc device In-Reply-To: <20180316074650.5415-1-kraxel@redhat.com> References: <20180316074650.5415-1-kraxel@redhat.com> Message-ID: <7547e99b-0e3c-264e-e52b-40ad5d52b49a@gmail.com> Content-Type: text/plain; charset="UTF-8" Message-ID: <20180316123043.FV0taQfoLN3LtNLdKivxNQ0feEa3JCEMTu4gMLqt334@z> Am 16.03.2018 um 08:46 schrieb Gerd Hoffmann: > A driver to let userspace turn iovecs into dma-bufs. > > Use case: Allows qemu create dmabufs for the vga framebuffer or > virtio-gpu ressources. Then they can be passed around to display > those guest things on the host. To spice client for classic full > framebuffer display, and hopefully some day to wayland server for > seamless guest window display. > > Those dma-bufs are accounted against user's shm mlock bucket as the > pages are effectively locked in memory. Sorry to disappoint you, but we have investigated the same idea for userptr use quite extensively and found that whole approach doesn't work. The pages backing a DMA-buf are not allowed to move (at least not without a patch set I'm currently working on), but for certain MM operations to work correctly you must be able to modify the page tables entries and move the pages backing them around. For example try to use fork() with some copy on write pages with this approach. You will find that you have only two options to correctly handle this. Either you access memory which could no longer belong to your process, which in turn is major security no-go. Or you use a MM notifier, but without being able to unmap DMA-bufs that won't work and you will certainly create a deadlock. Even with the patch set I'm currently working on the MM notifier approach is a real beast to get right. Regards, Christian. > > Cc: David Airlie > Cc: Tomeu Vizoso > Cc: Daniel Vetter > Signed-off-by: Gerd Hoffmann > --- > include/uapi/linux/udmabuf.h | 23 ++ > drivers/dma-buf/udmabuf.c | 261 ++++++++++++++++++++++ > tools/testing/selftests/drivers/dma-buf/udmabuf.c | 69 ++++++ > drivers/dma-buf/Kconfig | 7 + > drivers/dma-buf/Makefile | 1 + > tools/testing/selftests/drivers/dma-buf/Makefile | 5 + > 6 files changed, 366 insertions(+) > create mode 100644 include/uapi/linux/udmabuf.h > create mode 100644 drivers/dma-buf/udmabuf.c > create mode 100644 tools/testing/selftests/drivers/dma-buf/udmabuf.c > create mode 100644 tools/testing/selftests/drivers/dma-buf/Makefile > > diff --git a/include/uapi/linux/udmabuf.h b/include/uapi/linux/udmabuf.h > new file mode 100644 > index 0000000000..54ceba203a > --- /dev/null > +++ b/include/uapi/linux/udmabuf.h > @@ -0,0 +1,23 @@ > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > +#ifndef _UAPI_LINUX_UDMABUF_H > +#define _UAPI_LINUX_UDMABUF_H > + > +#include > +#include > + > +struct udmabuf_iovec { > + __u64 base; > + __u64 len; > +}; > + > +#define UDMABUF_FLAGS_CLOEXEC 0x01 > + > +struct udmabuf_create { > + __u32 flags; > + __u32 niov; > + struct udmabuf_iovec iovs[]; > +}; > + > +#define UDMABUF_CREATE _IOW(0x42, 0x23, struct udmabuf_create) > + > +#endif /* _UAPI_LINUX_UDMABUF_H */ > diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c > new file mode 100644 > index 0000000000..664ab4ee4e > --- /dev/null > +++ b/drivers/dma-buf/udmabuf.c > @@ -0,0 +1,261 @@ > +/* > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +struct udmabuf { > + u32 pagecount; > + struct page **pages; > + struct user_struct *owner; > +}; > + > +static int udmabuf_vm_fault(struct vm_fault *vmf) > +{ > + struct vm_area_struct *vma = vmf->vma; > + struct udmabuf *ubuf = vma->vm_private_data; > + > + if (WARN_ON(vmf->pgoff >= ubuf->pagecount)) > + return VM_FAULT_SIGBUS; > + > + vmf->page = ubuf->pages[vmf->pgoff]; > + get_page(vmf->page); > + return 0; > +} > + > +static const struct vm_operations_struct udmabuf_vm_ops = { > + .fault = udmabuf_vm_fault, > +}; > + > +static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma) > +{ > + struct udmabuf *ubuf = buf->priv; > + > + if ((vma->vm_flags & VM_SHARED) == 0) > + return -EINVAL; > + > + vma->vm_ops = &udmabuf_vm_ops; > + vma->vm_private_data = ubuf; > + return 0; > +} > + > +static struct sg_table *map_udmabuf(struct dma_buf_attachment *at, > + enum dma_data_direction direction) > +{ > + struct udmabuf *ubuf = at->dmabuf->priv; > + struct sg_table *sg; > + > + sg = kzalloc(sizeof(*sg), GFP_KERNEL); > + if (!sg) > + goto err1; > + if (sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->pagecount, > + 0, ubuf->pagecount << PAGE_SHIFT, > + GFP_KERNEL) < 0) > + goto err2; > + if (!dma_map_sg(at->dev, sg->sgl, sg->nents, direction)) > + goto err3; > + > + return sg; > + > +err3: > + sg_free_table(sg); > +err2: > + kfree(sg); > +err1: > + return ERR_PTR(-ENOMEM); > +} > + > +static void unmap_udmabuf(struct dma_buf_attachment *at, > + struct sg_table *sg, > + enum dma_data_direction direction) > +{ > + sg_free_table(sg); > + kfree(sg); > +} > + > +static void release_udmabuf(struct dma_buf *buf) > +{ > + struct udmabuf *ubuf = buf->priv; > + pgoff_t pg; > + > + for (pg = 0; pg < ubuf->pagecount; pg++) > + put_page(ubuf->pages[pg]); > + user_shm_unlock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner); > + free_uid(ubuf->owner); > + kfree(ubuf->pages); > + kfree(ubuf); > +} > + > +static void *kmap_atomic_udmabuf(struct dma_buf *buf, unsigned long page_num) > +{ > + struct udmabuf *ubuf = buf->priv; > + struct page *page = ubuf->pages[page_num]; > + > + return kmap_atomic(page); > +} > + > +static void *kmap_udmabuf(struct dma_buf *buf, unsigned long page_num) > +{ > + struct udmabuf *ubuf = buf->priv; > + struct page *page = ubuf->pages[page_num]; > + > + return kmap(page); > +} > + > +static struct dma_buf_ops udmabuf_ops = { > + .map_dma_buf = map_udmabuf, > + .unmap_dma_buf = unmap_udmabuf, > + .release = release_udmabuf, > + .map_atomic = kmap_atomic_udmabuf, > + .map = kmap_udmabuf, > + .mmap = mmap_udmabuf, > +}; > + > +static long udmabuf_ioctl_create(struct file *filp, unsigned long arg) > +{ > + struct udmabuf_create create; > + struct udmabuf_iovec *iovs; > + struct udmabuf *ubuf; > + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); > + struct dma_buf *buf; > + pgoff_t pgoff, pgcnt; > + u32 iov, flags; > + int ret; > + > + if (copy_from_user(&create, (void __user *)arg, > + sizeof(struct udmabuf_create))) > + return -EFAULT; > + > + iovs = kmalloc_array(create.niov, sizeof(struct udmabuf_iovec), > + GFP_KERNEL); > + if (!iovs) > + return -ENOMEM; > + > + arg += offsetof(struct udmabuf_create, iovs); > + ret = -EFAULT; > + if (copy_from_user(iovs, (void __user *)arg, > + create.niov * sizeof(struct udmabuf_iovec))) > + goto err_free_iovs; > + > + ubuf = kzalloc(sizeof(struct udmabuf), GFP_KERNEL); > + if (!ubuf) > + goto err_free_iovs; > + > + ret = -EINVAL; > + for (iov = 0; iov < create.niov; iov++) { > + if (!IS_ALIGNED(iovs[iov].base, PAGE_SIZE)) > + goto err_free_buf; > + if (!IS_ALIGNED(iovs[iov].len, PAGE_SIZE)) > + goto err_free_buf; > + ubuf->pagecount += iovs[iov].len >> PAGE_SHIFT; > + } > + > + /* this effectively mlocks the pages so account it accordingly */ > + ret = -ENOMEM; > + ubuf->owner = current_user(); > + if (!user_shm_lock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner)) > + goto err_free_buf; > + > + ubuf->pages = kmalloc_array(ubuf->pagecount, sizeof(struct page*), > + GFP_KERNEL); > + if (!ubuf->pages) > + goto err_shm_unlock; > + > + pgoff = 0; > + for (iov = 0; iov < create.niov; iov++) { > + pgcnt = iovs[iov].len >> PAGE_SHIFT; > + while (pgcnt > 0) { > + ret = get_user_pages_fast(iovs[iov].base, pgcnt, > + true, /* write */ > + ubuf->pages + pgoff); > + if (ret < 0) > + goto err_put_pages; > + pgoff += ret; > + pgcnt -= ret; > + } > + } > + > + exp_info.ops = &udmabuf_ops; > + exp_info.size = ubuf->pagecount << PAGE_SHIFT; > + exp_info.priv = ubuf; > + > + buf = dma_buf_export(&exp_info); > + if (IS_ERR(buf)) { > + ret = PTR_ERR(buf); > + goto err_put_pages; > + } > + > + flags = 0; > + if (create.flags & UDMABUF_FLAGS_CLOEXEC) > + flags |= O_CLOEXEC; > + > + kfree(iovs); > + return dma_buf_fd(buf, flags); > + > +err_put_pages: > + while (pgoff > 0) > + put_page(ubuf->pages[--pgoff]); > +err_shm_unlock: > + user_shm_unlock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner); > +err_free_buf: > + free_uid(ubuf->owner); > + kfree(ubuf->pages); > + kfree(ubuf); > +err_free_iovs: > + kfree(iovs); > + return ret; > +} > + > +static long udmabuf_ioctl(struct file *filp, unsigned int ioctl, > + unsigned long arg) > +{ > + long ret; > + > + switch (ioctl) { > + case UDMABUF_CREATE: > + ret = udmabuf_ioctl_create(filp, arg); > + break; > + default: > + ret = -EINVAL; > + break; > + } > + return ret; > +} > + > +static const struct file_operations udmabuf_fops = { > + .owner = THIS_MODULE, > + .unlocked_ioctl = udmabuf_ioctl, > +}; > + > +static struct miscdevice udmabuf_misc = { > + .minor = MISC_DYNAMIC_MINOR, > + .name = "udmabuf", > + .fops = &udmabuf_fops, > +}; > + > +static int __init udmabuf_dev_init(void) > +{ > + return misc_register(&udmabuf_misc); > +} > + > +static void __exit udmabuf_dev_exit(void) > +{ > + misc_deregister(&udmabuf_misc); > +} > + > +module_init(udmabuf_dev_init) > +module_exit(udmabuf_dev_exit) > + > +MODULE_AUTHOR("Gerd Hoffmann "); > +MODULE_LICENSE("GPL v2"); > diff --git a/tools/testing/selftests/drivers/dma-buf/udmabuf.c b/tools/testing/selftests/drivers/dma-buf/udmabuf.c > new file mode 100644 > index 0000000000..3472c8ee49 > --- /dev/null > +++ b/tools/testing/selftests/drivers/dma-buf/udmabuf.c > @@ -0,0 +1,69 @@ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#define TEST_PREFIX "drivers/dma-buf/udmabuf" > +#define NUM_PAGES 4 > + > +int main(int argc, char *argv[]) > +{ > + struct udmabuf_create *create; > + void *mem; > + int dev, fd; > + > + dev = open("/dev/udmabuf", O_RDWR); > + if (dev < 0) { > + printf("%s: [skip]\n", TEST_PREFIX); > + exit(77); > + } > + > + mem = memalign(getpagesize(), getpagesize() * NUM_PAGES); > + if (mem == NULL) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit (1); > + } > + > + create = malloc(sizeof(struct udmabuf_create) + > + sizeof(struct udmabuf_iovec)); > + create->flags = 0; > + create->niov = 1; > + > + /* should fail (base not page aligned) */ > + create->iovs[0].base = (intptr_t)mem + getpagesize()/2; > + create->iovs[0].len = getpagesize(); > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd >= 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + > + /* should fail (size not multiple of page) */ > + create->iovs[0].base = (intptr_t)mem; > + create->iovs[0].len = getpagesize()/2; > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd >= 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + > + /* should work */ > + create->iovs[0].base = (intptr_t)mem; > + create->iovs[0].len = getpagesize() * NUM_PAGES; > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd < 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + close(fd); > + > + fprintf(stderr, "%s: ok\n", TEST_PREFIX); > + close(dev); > + return 0; > +} > diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig > index ed3b785bae..19be3ec62d 100644 > --- a/drivers/dma-buf/Kconfig > +++ b/drivers/dma-buf/Kconfig > @@ -30,4 +30,11 @@ config SW_SYNC > WARNING: improper use of this can result in deadlocking kernel > drivers from userspace. Intended for test and debug only. > > +config UDMABUF > + bool "userspace dmabuf misc driver" > + default n > + depends on DMA_SHARED_BUFFER > + ---help--- > + A driver to let userspace turn iovs into dma-bufs. > + > endmenu > diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile > index c33bf88631..0913a6ccab 100644 > --- a/drivers/dma-buf/Makefile > +++ b/drivers/dma-buf/Makefile > @@ -1,3 +1,4 @@ > obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o > obj-$(CONFIG_SYNC_FILE) += sync_file.o > obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o > +obj-$(CONFIG_UDMABUF) += udmabuf.o > diff --git a/tools/testing/selftests/drivers/dma-buf/Makefile b/tools/testing/selftests/drivers/dma-buf/Makefile > new file mode 100644 > index 0000000000..4154c3d7aa > --- /dev/null > +++ b/tools/testing/selftests/drivers/dma-buf/Makefile > @@ -0,0 +1,5 @@ > +CFLAGS += -I../../../../../usr/include/ > + > +TEST_GEN_PROGS := udmabuf > + > +include ../../lib.mk -- To unsubscribe from this list: send the line "unsubscribe linux-kselftest" in the body of a message to majordomo at vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html From mboxrd@z Thu Jan 1 00:00:00 1970 From: =?UTF-8?Q?Christian_K=c3=b6nig?= Subject: Re: [PATCH v2] Add udmabuf misc device Date: Fri, 16 Mar 2018 13:30:43 +0100 Message-ID: <7547e99b-0e3c-264e-e52b-40ad5d52b49a@gmail.com> References: <20180316074650.5415-1-kraxel@redhat.com> Reply-To: christian.koenig@amd.com Mime-Version: 1.0 Content-Type: text/plain; charset="utf-8"; Format="flowed" Content-Transfer-Encoding: base64 Return-path: Received: from mail-wr0-x241.google.com (mail-wr0-x241.google.com [IPv6:2a00:1450:400c:c0c::241]) by gabe.freedesktop.org (Postfix) with ESMTPS id 55B4E6EB35 for ; Fri, 16 Mar 2018 12:30:46 +0000 (UTC) Received: by mail-wr0-x241.google.com with SMTP id s18so11490717wrg.9 for ; Fri, 16 Mar 2018 05:30:46 -0700 (PDT) In-Reply-To: <20180316074650.5415-1-kraxel@redhat.com> Content-Language: en-US List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" To: Gerd Hoffmann , dri-devel@lists.freedesktop.org, Daniel Vetter Cc: Tomeu Vizoso , David Airlie , open list , qemu-devel@nongnu.org, "moderated list:DMA BUFFER SHARING FRAMEWORK" , "open list:KERNEL SELFTEST FRAMEWORK" , Shuah Khan , "open list:DMA BUFFER SHARING FRAMEWORK" List-Id: dri-devel@lists.freedesktop.org QW0gMTYuMDMuMjAxOCB1bSAwODo0NiBzY2hyaWViIEdlcmQgSG9mZm1hbm46Cj4gQSBkcml2ZXIg dG8gbGV0IHVzZXJzcGFjZSB0dXJuIGlvdmVjcyBpbnRvIGRtYS1idWZzLgo+Cj4gVXNlIGNhc2U6 ICBBbGxvd3MgcWVtdSBjcmVhdGUgZG1hYnVmcyBmb3IgdGhlIHZnYSBmcmFtZWJ1ZmZlciBvcgo+ IHZpcnRpby1ncHUgcmVzc291cmNlcy4gIFRoZW4gdGhleSBjYW4gYmUgcGFzc2VkIGFyb3VuZCB0 byBkaXNwbGF5Cj4gdGhvc2UgZ3Vlc3QgdGhpbmdzIG9uIHRoZSBob3N0LiAgVG8gc3BpY2UgY2xp ZW50IGZvciBjbGFzc2ljIGZ1bGwKPiBmcmFtZWJ1ZmZlciBkaXNwbGF5LCBhbmQgaG9wZWZ1bGx5 IHNvbWUgZGF5IHRvIHdheWxhbmQgc2VydmVyIGZvcgo+IHNlYW1sZXNzIGd1ZXN0IHdpbmRvdyBk aXNwbGF5Lgo+Cj4gVGhvc2UgZG1hLWJ1ZnMgYXJlIGFjY291bnRlZCBhZ2FpbnN0IHVzZXIncyBz aG0gbWxvY2sgYnVja2V0IGFzIHRoZQo+IHBhZ2VzIGFyZSBlZmZlY3RpdmVseSBsb2NrZWQgaW4g bWVtb3J5LgoKU29ycnkgdG8gZGlzYXBwb2ludCB5b3UsIGJ1dCB3ZSBoYXZlIGludmVzdGlnYXRl ZCB0aGUgc2FtZSBpZGVhIGZvciAKdXNlcnB0ciB1c2UgcXVpdGUgZXh0ZW5zaXZlbHkgYW5kIGZv dW5kIHRoYXQgd2hvbGUgYXBwcm9hY2ggZG9lc24ndCB3b3JrLgoKVGhlIHBhZ2VzIGJhY2tpbmcg YSBETUEtYnVmIGFyZSBub3QgYWxsb3dlZCB0byBtb3ZlIChhdCBsZWFzdCBub3QgCndpdGhvdXQg YSBwYXRjaCBzZXQgSSdtIGN1cnJlbnRseSB3b3JraW5nIG9uKSwgYnV0IGZvciBjZXJ0YWluIE1N IApvcGVyYXRpb25zIHRvIHdvcmsgY29ycmVjdGx5IHlvdSBtdXN0IGJlIGFibGUgdG8gbW9kaWZ5 IHRoZSBwYWdlIHRhYmxlcyAKZW50cmllcyBhbmQgbW92ZSB0aGUgcGFnZXMgYmFja2luZyB0aGVt IGFyb3VuZC4KCkZvciBleGFtcGxlIHRyeSB0byB1c2UgZm9yaygpIHdpdGggc29tZSBjb3B5IG9u IHdyaXRlIHBhZ2VzIHdpdGggdGhpcyAKYXBwcm9hY2guIFlvdSB3aWxsIGZpbmQgdGhhdCB5b3Ug aGF2ZSBvbmx5IHR3byBvcHRpb25zIHRvIGNvcnJlY3RseSAKaGFuZGxlIHRoaXMuCgpFaXRoZXIg eW91IGFjY2VzcyBtZW1vcnkgd2hpY2ggY291bGQgbm8gbG9uZ2VyIGJlbG9uZyB0byB5b3VyIHBy b2Nlc3MsIAp3aGljaCBpbiB0dXJuIGlzIG1ham9yIHNlY3VyaXR5IG5vLWdvLgoKT3IgeW91IHVz ZSBhIE1NIG5vdGlmaWVyLCBidXQgd2l0aG91dCBiZWluZyBhYmxlIHRvIHVubWFwIERNQS1idWZz IHRoYXQgCndvbid0IHdvcmsgYW5kIHlvdSB3aWxsIGNlcnRhaW5seSBjcmVhdGUgYSBkZWFkbG9j ay4KCkV2ZW4gd2l0aCB0aGUgcGF0Y2ggc2V0IEknbSBjdXJyZW50bHkgd29ya2luZyBvbiB0aGUg TU0gbm90aWZpZXIgCmFwcHJvYWNoIGlzIGEgcmVhbCBiZWFzdCB0byBnZXQgcmlnaHQuCgpSZWdh cmRzLApDaHJpc3RpYW4uCgo+Cj4gQ2M6IERhdmlkIEFpcmxpZSA8YWlybGllZEBsaW51eC5pZT4K PiBDYzogVG9tZXUgVml6b3NvIDx0b21ldS52aXpvc29AY29sbGFib3JhLmNvbT4KPiBDYzogRGFu aWVsIFZldHRlciA8ZGFuaWVsQGZmd2xsLmNoPgo+IFNpZ25lZC1vZmYtYnk6IEdlcmQgSG9mZm1h bm4gPGtyYXhlbEByZWRoYXQuY29tPgo+IC0tLQo+ICAgaW5jbHVkZS91YXBpL2xpbnV4L3VkbWFi dWYuaCAgICAgICAgICAgICAgICAgICAgICB8ICAyMyArKwo+ICAgZHJpdmVycy9kbWEtYnVmL3Vk bWFidWYuYyAgICAgICAgICAgICAgICAgICAgICAgICB8IDI2MSArKysrKysrKysrKysrKysrKysr KysrCj4gICB0b29scy90ZXN0aW5nL3NlbGZ0ZXN0cy9kcml2ZXJzL2RtYS1idWYvdWRtYWJ1Zi5j IHwgIDY5ICsrKysrKwo+ICAgZHJpdmVycy9kbWEtYnVmL0tjb25maWcgICAgICAgICAgICAgICAg ICAgICAgICAgICB8ICAgNyArCj4gICBkcml2ZXJzL2RtYS1idWYvTWFrZWZpbGUgICAgICAgICAg ICAgICAgICAgICAgICAgIHwgICAxICsKPiAgIHRvb2xzL3Rlc3Rpbmcvc2VsZnRlc3RzL2RyaXZl cnMvZG1hLWJ1Zi9NYWtlZmlsZSAgfCAgIDUgKwo+ICAgNiBmaWxlcyBjaGFuZ2VkLCAzNjYgaW5z ZXJ0aW9ucygrKQo+ICAgY3JlYXRlIG1vZGUgMTAwNjQ0IGluY2x1ZGUvdWFwaS9saW51eC91ZG1h YnVmLmgKPiAgIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL2RtYS1idWYvdWRtYWJ1Zi5jCj4g ICBjcmVhdGUgbW9kZSAxMDA2NDQgdG9vbHMvdGVzdGluZy9zZWxmdGVzdHMvZHJpdmVycy9kbWEt YnVmL3VkbWFidWYuYwo+ICAgY3JlYXRlIG1vZGUgMTAwNjQ0IHRvb2xzL3Rlc3Rpbmcvc2VsZnRl c3RzL2RyaXZlcnMvZG1hLWJ1Zi9NYWtlZmlsZQo+Cj4gZGlmZiAtLWdpdCBhL2luY2x1ZGUvdWFw aS9saW51eC91ZG1hYnVmLmggYi9pbmNsdWRlL3VhcGkvbGludXgvdWRtYWJ1Zi5oCj4gbmV3IGZp bGUgbW9kZSAxMDA2NDQKPiBpbmRleCAwMDAwMDAwMDAwLi41NGNlYmEyMDNhCj4gLS0tIC9kZXYv bnVsbAo+ICsrKyBiL2luY2x1ZGUvdWFwaS9saW51eC91ZG1hYnVmLmgKPiBAQCAtMCwwICsxLDIz IEBACj4gKy8qIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBHUEwtMi4wIFdJVEggTGludXgtc3lz Y2FsbC1ub3RlICovCj4gKyNpZm5kZWYgX1VBUElfTElOVVhfVURNQUJVRl9ICj4gKyNkZWZpbmUg X1VBUElfTElOVVhfVURNQUJVRl9ICj4gKwo+ICsjaW5jbHVkZSA8bGludXgvdHlwZXMuaD4KPiAr I2luY2x1ZGUgPGxpbnV4L2lvY3RsLmg+Cj4gKwo+ICtzdHJ1Y3QgdWRtYWJ1Zl9pb3ZlYyB7Cj4g KwlfX3U2NCBiYXNlOwo+ICsJX191NjQgbGVuOwo+ICt9Owo+ICsKPiArI2RlZmluZSBVRE1BQlVG X0ZMQUdTX0NMT0VYRUMJMHgwMQo+ICsKPiArc3RydWN0IHVkbWFidWZfY3JlYXRlIHsKPiArCV9f dTMyIGZsYWdzOwo+ICsJX191MzIgbmlvdjsKPiArCXN0cnVjdCB1ZG1hYnVmX2lvdmVjIGlvdnNb XTsKPiArfTsKPiArCj4gKyNkZWZpbmUgVURNQUJVRl9DUkVBVEUgX0lPVygweDQyLCAweDIzLCBz dHJ1Y3QgdWRtYWJ1Zl9jcmVhdGUpCj4gKwo+ICsjZW5kaWYgLyogX1VBUElfTElOVVhfVURNQUJV Rl9IICovCj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvZG1hLWJ1Zi91ZG1hYnVmLmMgYi9kcml2ZXJz L2RtYS1idWYvdWRtYWJ1Zi5jCj4gbmV3IGZpbGUgbW9kZSAxMDA2NDQKPiBpbmRleCAwMDAwMDAw MDAwLi42NjRhYjRlZTRlCj4gLS0tIC9kZXYvbnVsbAo+ICsrKyBiL2RyaXZlcnMvZG1hLWJ1Zi91 ZG1hYnVmLmMKPiBAQCAtMCwwICsxLDI2MSBAQAo+ICsvKgo+ICsgKiBUaGlzIHByb2dyYW0gaXMg ZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeQo+ICsg KiBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIHZl cnNpb24gMiBhcwo+ICsgKiBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlv bi4KPiArICovCj4gKyNpbmNsdWRlIDxsaW51eC9pbml0Lmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9t b2R1bGUuaD4KPiArI2luY2x1ZGUgPGxpbnV4L2RldmljZS5oPgo+ICsjaW5jbHVkZSA8bGludXgv a2VybmVsLmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9zbGFiLmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9t aXNjZGV2aWNlLmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9kbWEtYnVmLmg+Cj4gKyNpbmNsdWRlIDxs aW51eC9oaWdobWVtLmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9jcmVkLmg+Cj4gKwo+ICsjaW5jbHVk ZSA8dWFwaS9saW51eC91ZG1hYnVmLmg+Cj4gKwo+ICtzdHJ1Y3QgdWRtYWJ1ZiB7Cj4gKwl1MzIg cGFnZWNvdW50Owo+ICsJc3RydWN0IHBhZ2UgKipwYWdlczsKPiArCXN0cnVjdCB1c2VyX3N0cnVj dCAqb3duZXI7Cj4gK307Cj4gKwo+ICtzdGF0aWMgaW50IHVkbWFidWZfdm1fZmF1bHQoc3RydWN0 IHZtX2ZhdWx0ICp2bWYpCj4gK3sKPiArCXN0cnVjdCB2bV9hcmVhX3N0cnVjdCAqdm1hID0gdm1m LT52bWE7Cj4gKwlzdHJ1Y3QgdWRtYWJ1ZiAqdWJ1ZiA9IHZtYS0+dm1fcHJpdmF0ZV9kYXRhOwo+ ICsKPiArCWlmIChXQVJOX09OKHZtZi0+cGdvZmYgPj0gdWJ1Zi0+cGFnZWNvdW50KSkKPiArCQly ZXR1cm4gVk1fRkFVTFRfU0lHQlVTOwo+ICsKPiArCXZtZi0+cGFnZSA9IHVidWYtPnBhZ2VzW3Zt Zi0+cGdvZmZdOwo+ICsJZ2V0X3BhZ2Uodm1mLT5wYWdlKTsKPiArCXJldHVybiAwOwo+ICt9Cj4g Kwo+ICtzdGF0aWMgY29uc3Qgc3RydWN0IHZtX29wZXJhdGlvbnNfc3RydWN0IHVkbWFidWZfdm1f b3BzID0gewo+ICsJLmZhdWx0ID0gdWRtYWJ1Zl92bV9mYXVsdCwKPiArfTsKPiArCj4gK3N0YXRp YyBpbnQgbW1hcF91ZG1hYnVmKHN0cnVjdCBkbWFfYnVmICpidWYsIHN0cnVjdCB2bV9hcmVhX3N0 cnVjdCAqdm1hKQo+ICt7Cj4gKwlzdHJ1Y3QgdWRtYWJ1ZiAqdWJ1ZiA9IGJ1Zi0+cHJpdjsKPiAr Cj4gKwlpZiAoKHZtYS0+dm1fZmxhZ3MgJiBWTV9TSEFSRUQpID09IDApCj4gKwkJcmV0dXJuIC1F SU5WQUw7Cj4gKwo+ICsJdm1hLT52bV9vcHMgPSAmdWRtYWJ1Zl92bV9vcHM7Cj4gKwl2bWEtPnZt X3ByaXZhdGVfZGF0YSA9IHVidWY7Cj4gKwlyZXR1cm4gMDsKPiArfQo+ICsKPiArc3RhdGljIHN0 cnVjdCBzZ190YWJsZSAqbWFwX3VkbWFidWYoc3RydWN0IGRtYV9idWZfYXR0YWNobWVudCAqYXQs Cj4gKwkJCQkgICAgZW51bSBkbWFfZGF0YV9kaXJlY3Rpb24gZGlyZWN0aW9uKQo+ICt7Cj4gKwlz dHJ1Y3QgdWRtYWJ1ZiAqdWJ1ZiA9IGF0LT5kbWFidWYtPnByaXY7Cj4gKwlzdHJ1Y3Qgc2dfdGFi bGUgKnNnOwo+ICsKPiArCXNnID0ga3phbGxvYyhzaXplb2YoKnNnKSwgR0ZQX0tFUk5FTCk7Cj4g KwlpZiAoIXNnKQo+ICsJCWdvdG8gZXJyMTsKPiArCWlmIChzZ19hbGxvY190YWJsZV9mcm9tX3Bh Z2VzKHNnLCB1YnVmLT5wYWdlcywgdWJ1Zi0+cGFnZWNvdW50LAo+ICsJCQkJICAgICAgMCwgdWJ1 Zi0+cGFnZWNvdW50IDw8IFBBR0VfU0hJRlQsCj4gKwkJCQkgICAgICBHRlBfS0VSTkVMKSA8IDAp Cj4gKwkJZ290byBlcnIyOwo+ICsJaWYgKCFkbWFfbWFwX3NnKGF0LT5kZXYsIHNnLT5zZ2wsIHNn LT5uZW50cywgZGlyZWN0aW9uKSkKPiArCQlnb3RvIGVycjM7Cj4gKwo+ICsJcmV0dXJuIHNnOwo+ ICsKPiArZXJyMzoKPiArCXNnX2ZyZWVfdGFibGUoc2cpOwo+ICtlcnIyOgo+ICsJa2ZyZWUoc2cp Owo+ICtlcnIxOgo+ICsJcmV0dXJuIEVSUl9QVFIoLUVOT01FTSk7Cj4gK30KPiArCj4gK3N0YXRp YyB2b2lkIHVubWFwX3VkbWFidWYoc3RydWN0IGRtYV9idWZfYXR0YWNobWVudCAqYXQsCj4gKwkJ CSAgc3RydWN0IHNnX3RhYmxlICpzZywKPiArCQkJICBlbnVtIGRtYV9kYXRhX2RpcmVjdGlvbiBk aXJlY3Rpb24pCj4gK3sKPiArCXNnX2ZyZWVfdGFibGUoc2cpOwo+ICsJa2ZyZWUoc2cpOwo+ICt9 Cj4gKwo+ICtzdGF0aWMgdm9pZCByZWxlYXNlX3VkbWFidWYoc3RydWN0IGRtYV9idWYgKmJ1ZikK PiArewo+ICsJc3RydWN0IHVkbWFidWYgKnVidWYgPSBidWYtPnByaXY7Cj4gKwlwZ29mZl90IHBn Owo+ICsKPiArCWZvciAocGcgPSAwOyBwZyA8IHVidWYtPnBhZ2Vjb3VudDsgcGcrKykKPiArCQlw dXRfcGFnZSh1YnVmLT5wYWdlc1twZ10pOwo+ICsJdXNlcl9zaG1fdW5sb2NrKHVidWYtPnBhZ2Vj b3VudCA8PCBQQUdFX1NISUZULCB1YnVmLT5vd25lcik7Cj4gKwlmcmVlX3VpZCh1YnVmLT5vd25l cik7Cj4gKwlrZnJlZSh1YnVmLT5wYWdlcyk7Cj4gKwlrZnJlZSh1YnVmKTsKPiArfQo+ICsKPiAr c3RhdGljIHZvaWQgKmttYXBfYXRvbWljX3VkbWFidWYoc3RydWN0IGRtYV9idWYgKmJ1ZiwgdW5z aWduZWQgbG9uZyBwYWdlX251bSkKPiArewo+ICsJc3RydWN0IHVkbWFidWYgKnVidWYgPSBidWYt PnByaXY7Cj4gKwlzdHJ1Y3QgcGFnZSAqcGFnZSA9IHVidWYtPnBhZ2VzW3BhZ2VfbnVtXTsKPiAr Cj4gKwlyZXR1cm4ga21hcF9hdG9taWMocGFnZSk7Cj4gK30KPiArCj4gK3N0YXRpYyB2b2lkICpr bWFwX3VkbWFidWYoc3RydWN0IGRtYV9idWYgKmJ1ZiwgdW5zaWduZWQgbG9uZyBwYWdlX251bSkK PiArewo+ICsJc3RydWN0IHVkbWFidWYgKnVidWYgPSBidWYtPnByaXY7Cj4gKwlzdHJ1Y3QgcGFn ZSAqcGFnZSA9IHVidWYtPnBhZ2VzW3BhZ2VfbnVtXTsKPiArCj4gKwlyZXR1cm4ga21hcChwYWdl KTsKPiArfQo+ICsKPiArc3RhdGljIHN0cnVjdCBkbWFfYnVmX29wcyB1ZG1hYnVmX29wcyA9IHsK PiArCS5tYXBfZG1hX2J1ZgkgID0gbWFwX3VkbWFidWYsCj4gKwkudW5tYXBfZG1hX2J1ZgkgID0g dW5tYXBfdWRtYWJ1ZiwKPiArCS5yZWxlYXNlCSAgPSByZWxlYXNlX3VkbWFidWYsCj4gKwkubWFw X2F0b21pYwkgID0ga21hcF9hdG9taWNfdWRtYWJ1ZiwKPiArCS5tYXAJCSAgPSBrbWFwX3VkbWFi dWYsCj4gKwkubW1hcAkJICA9IG1tYXBfdWRtYWJ1ZiwKPiArfTsKPiArCj4gK3N0YXRpYyBsb25n IHVkbWFidWZfaW9jdGxfY3JlYXRlKHN0cnVjdCBmaWxlICpmaWxwLCB1bnNpZ25lZCBsb25nIGFy ZykKPiArewo+ICsJc3RydWN0IHVkbWFidWZfY3JlYXRlIGNyZWF0ZTsKPiArCXN0cnVjdCB1ZG1h YnVmX2lvdmVjICppb3ZzOwo+ICsJc3RydWN0IHVkbWFidWYgKnVidWY7Cj4gKwlERUZJTkVfRE1B X0JVRl9FWFBPUlRfSU5GTyhleHBfaW5mbyk7Cj4gKwlzdHJ1Y3QgZG1hX2J1ZiAqYnVmOwo+ICsJ cGdvZmZfdCBwZ29mZiwgcGdjbnQ7Cj4gKwl1MzIgaW92LCBmbGFnczsKPiArCWludCByZXQ7Cj4g Kwo+ICsJaWYgKGNvcHlfZnJvbV91c2VyKCZjcmVhdGUsICh2b2lkIF9fdXNlciAqKWFyZywKPiAr CQkJICAgc2l6ZW9mKHN0cnVjdCB1ZG1hYnVmX2NyZWF0ZSkpKQo+ICsJCXJldHVybiAtRUZBVUxU Owo+ICsKPiArCWlvdnMgPSBrbWFsbG9jX2FycmF5KGNyZWF0ZS5uaW92LCBzaXplb2Yoc3RydWN0 IHVkbWFidWZfaW92ZWMpLAo+ICsJCQkgICAgIEdGUF9LRVJORUwpOwo+ICsJaWYgKCFpb3ZzKQo+ ICsJCXJldHVybiAtRU5PTUVNOwo+ICsKPiArCWFyZyArPSBvZmZzZXRvZihzdHJ1Y3QgdWRtYWJ1 Zl9jcmVhdGUsIGlvdnMpOwo+ICsJcmV0ID0gLUVGQVVMVDsKPiArCWlmIChjb3B5X2Zyb21fdXNl cihpb3ZzLCAodm9pZCBfX3VzZXIgKilhcmcsCj4gKwkJCSAgIGNyZWF0ZS5uaW92ICogc2l6ZW9m KHN0cnVjdCB1ZG1hYnVmX2lvdmVjKSkpCj4gKwkJZ290byBlcnJfZnJlZV9pb3ZzOwo+ICsKPiAr CXVidWYgPSBremFsbG9jKHNpemVvZihzdHJ1Y3QgdWRtYWJ1ZiksIEdGUF9LRVJORUwpOwo+ICsJ aWYgKCF1YnVmKQo+ICsJCWdvdG8gZXJyX2ZyZWVfaW92czsKPiArCj4gKwlyZXQgPSAtRUlOVkFM Owo+ICsJZm9yIChpb3YgPSAwOyBpb3YgPCBjcmVhdGUubmlvdjsgaW92KyspIHsKPiArCQlpZiAo IUlTX0FMSUdORUQoaW92c1tpb3ZdLmJhc2UsIFBBR0VfU0laRSkpCj4gKwkJCWdvdG8gZXJyX2Zy ZWVfYnVmOwo+ICsJCWlmICghSVNfQUxJR05FRChpb3ZzW2lvdl0ubGVuLCBQQUdFX1NJWkUpKQo+ ICsJCQlnb3RvIGVycl9mcmVlX2J1ZjsKPiArCQl1YnVmLT5wYWdlY291bnQgKz0gaW92c1tpb3Zd LmxlbiA+PiBQQUdFX1NISUZUOwo+ICsJfQo+ICsKPiArCS8qIHRoaXMgZWZmZWN0aXZlbHkgbWxv Y2tzIHRoZSBwYWdlcyBzbyBhY2NvdW50IGl0IGFjY29yZGluZ2x5ICovCj4gKwlyZXQgPSAtRU5P TUVNOwo+ICsJdWJ1Zi0+b3duZXIgPSBjdXJyZW50X3VzZXIoKTsKPiArCWlmICghdXNlcl9zaG1f bG9jayh1YnVmLT5wYWdlY291bnQgPDwgUEFHRV9TSElGVCwgdWJ1Zi0+b3duZXIpKQo+ICsJCWdv dG8gZXJyX2ZyZWVfYnVmOwo+ICsKPiArCXVidWYtPnBhZ2VzID0ga21hbGxvY19hcnJheSh1YnVm LT5wYWdlY291bnQsIHNpemVvZihzdHJ1Y3QgcGFnZSopLAo+ICsJCQkJICAgIEdGUF9LRVJORUwp Owo+ICsJaWYgKCF1YnVmLT5wYWdlcykKPiArCQlnb3RvIGVycl9zaG1fdW5sb2NrOwo+ICsKPiAr CXBnb2ZmID0gMDsKPiArCWZvciAoaW92ID0gMDsgaW92IDwgY3JlYXRlLm5pb3Y7IGlvdisrKSB7 Cj4gKwkJcGdjbnQgPSBpb3ZzW2lvdl0ubGVuID4+IFBBR0VfU0hJRlQ7Cj4gKwkJd2hpbGUgKHBn Y250ID4gMCkgewo+ICsJCQlyZXQgPSBnZXRfdXNlcl9wYWdlc19mYXN0KGlvdnNbaW92XS5iYXNl LCBwZ2NudCwKPiArCQkJCQkJICB0cnVlLCAvKiB3cml0ZSAqLwo+ICsJCQkJCQkgIHVidWYtPnBh Z2VzICsgcGdvZmYpOwo+ICsJCQlpZiAocmV0IDwgMCkKPiArCQkJCWdvdG8gZXJyX3B1dF9wYWdl czsKPiArCQkJcGdvZmYgKz0gcmV0Owo+ICsJCQlwZ2NudCAtPSByZXQ7Cj4gKwkJfQo+ICsJfQo+ ICsKPiArCWV4cF9pbmZvLm9wcyAgPSAmdWRtYWJ1Zl9vcHM7Cj4gKwlleHBfaW5mby5zaXplID0g dWJ1Zi0+cGFnZWNvdW50IDw8IFBBR0VfU0hJRlQ7Cj4gKwlleHBfaW5mby5wcml2ID0gdWJ1ZjsK PiArCj4gKwlidWYgPSBkbWFfYnVmX2V4cG9ydCgmZXhwX2luZm8pOwo+ICsJaWYgKElTX0VSUihi dWYpKSB7Cj4gKwkJcmV0ID0gUFRSX0VSUihidWYpOwo+ICsJCWdvdG8gZXJyX3B1dF9wYWdlczsK PiArCX0KPiArCj4gKwlmbGFncyA9IDA7Cj4gKwlpZiAoY3JlYXRlLmZsYWdzICYgVURNQUJVRl9G TEFHU19DTE9FWEVDKQo+ICsJCWZsYWdzIHw9IE9fQ0xPRVhFQzsKPiArCj4gKwlrZnJlZShpb3Zz KTsKPiArCXJldHVybiBkbWFfYnVmX2ZkKGJ1ZiwgZmxhZ3MpOwo+ICsKPiArZXJyX3B1dF9wYWdl czoKPiArCXdoaWxlIChwZ29mZiA+IDApCj4gKwkJcHV0X3BhZ2UodWJ1Zi0+cGFnZXNbLS1wZ29m Zl0pOwo+ICtlcnJfc2htX3VubG9jazoKPiArCXVzZXJfc2htX3VubG9jayh1YnVmLT5wYWdlY291 bnQgPDwgUEFHRV9TSElGVCwgdWJ1Zi0+b3duZXIpOwo+ICtlcnJfZnJlZV9idWY6Cj4gKwlmcmVl X3VpZCh1YnVmLT5vd25lcik7Cj4gKwlrZnJlZSh1YnVmLT5wYWdlcyk7Cj4gKwlrZnJlZSh1YnVm KTsKPiArZXJyX2ZyZWVfaW92czoKPiArCWtmcmVlKGlvdnMpOwo+ICsJcmV0dXJuIHJldDsKPiAr fQo+ICsKPiArc3RhdGljIGxvbmcgdWRtYWJ1Zl9pb2N0bChzdHJ1Y3QgZmlsZSAqZmlscCwgdW5z aWduZWQgaW50IGlvY3RsLAo+ICsJCQkJICB1bnNpZ25lZCBsb25nIGFyZykKPiArewo+ICsJbG9u ZyByZXQ7Cj4gKwo+ICsJc3dpdGNoIChpb2N0bCkgewo+ICsJY2FzZSBVRE1BQlVGX0NSRUFURToK PiArCQlyZXQgPSB1ZG1hYnVmX2lvY3RsX2NyZWF0ZShmaWxwLCBhcmcpOwo+ICsJCWJyZWFrOwo+ ICsJZGVmYXVsdDoKPiArCQlyZXQgPSAtRUlOVkFMOwo+ICsJCWJyZWFrOwo+ICsJfQo+ICsJcmV0 dXJuIHJldDsKPiArfQo+ICsKPiArc3RhdGljIGNvbnN0IHN0cnVjdCBmaWxlX29wZXJhdGlvbnMg dWRtYWJ1Zl9mb3BzID0gewo+ICsJLm93bmVyCQk9IFRISVNfTU9EVUxFLAo+ICsJLnVubG9ja2Vk X2lvY3RsID0gdWRtYWJ1Zl9pb2N0bCwKPiArfTsKPiArCj4gK3N0YXRpYyBzdHJ1Y3QgbWlzY2Rl dmljZSB1ZG1hYnVmX21pc2MgPSB7Cj4gKwkubWlub3IgICAgICAgICAgPSBNSVNDX0RZTkFNSUNf TUlOT1IsCj4gKwkubmFtZSAgICAgICAgICAgPSAidWRtYWJ1ZiIsCj4gKwkuZm9wcyAgICAgICAg ICAgPSAmdWRtYWJ1Zl9mb3BzLAo+ICt9Owo+ICsKPiArc3RhdGljIGludCBfX2luaXQgdWRtYWJ1 Zl9kZXZfaW5pdCh2b2lkKQo+ICt7Cj4gKwlyZXR1cm4gbWlzY19yZWdpc3RlcigmdWRtYWJ1Zl9t aXNjKTsKPiArfQo+ICsKPiArc3RhdGljIHZvaWQgX19leGl0IHVkbWFidWZfZGV2X2V4aXQodm9p ZCkKPiArewo+ICsJbWlzY19kZXJlZ2lzdGVyKCZ1ZG1hYnVmX21pc2MpOwo+ICt9Cj4gKwo+ICtt b2R1bGVfaW5pdCh1ZG1hYnVmX2Rldl9pbml0KQo+ICttb2R1bGVfZXhpdCh1ZG1hYnVmX2Rldl9l eGl0KQo+ICsKPiArTU9EVUxFX0FVVEhPUigiR2VyZCBIb2ZmbWFubiA8a3JheGVsQHJlZGhhdC5j b20+Iik7Cj4gK01PRFVMRV9MSUNFTlNFKCJHUEwgdjIiKTsKPiBkaWZmIC0tZ2l0IGEvdG9vbHMv dGVzdGluZy9zZWxmdGVzdHMvZHJpdmVycy9kbWEtYnVmL3VkbWFidWYuYyBiL3Rvb2xzL3Rlc3Rp bmcvc2VsZnRlc3RzL2RyaXZlcnMvZG1hLWJ1Zi91ZG1hYnVmLmMKPiBuZXcgZmlsZSBtb2RlIDEw MDY0NAo+IGluZGV4IDAwMDAwMDAwMDAuLjM0NzJjOGVlNDkKPiAtLS0gL2Rldi9udWxsCj4gKysr IGIvdG9vbHMvdGVzdGluZy9zZWxmdGVzdHMvZHJpdmVycy9kbWEtYnVmL3VkbWFidWYuYwo+IEBA IC0wLDAgKzEsNjkgQEAKPiArI2luY2x1ZGUgPHN0ZGlvLmg+Cj4gKyNpbmNsdWRlIDxzdGRsaWIu aD4KPiArI2luY2x1ZGUgPHVuaXN0ZC5oPgo+ICsjaW5jbHVkZSA8c3RyaW5nLmg+Cj4gKyNpbmNs dWRlIDxlcnJuby5oPgo+ICsjaW5jbHVkZSA8ZmNudGwuaD4KPiArI2luY2x1ZGUgPG1hbGxvYy5o Pgo+ICsKPiArI2luY2x1ZGUgPHN5cy9pb2N0bC5oPgo+ICsjaW5jbHVkZSA8bGludXgvdWRtYWJ1 Zi5oPgo+ICsKPiArI2RlZmluZSBURVNUX1BSRUZJWAkiZHJpdmVycy9kbWEtYnVmL3VkbWFidWYi Cj4gKyNkZWZpbmUgTlVNX1BBR0VTICAgICAgIDQKPiArCj4gK2ludCBtYWluKGludCBhcmdjLCBj aGFyICphcmd2W10pCj4gK3sKPiArCXN0cnVjdCB1ZG1hYnVmX2NyZWF0ZSAqY3JlYXRlOwo+ICsJ dm9pZCAqbWVtOwo+ICsJaW50IGRldiwgZmQ7Cj4gKwo+ICsJZGV2ID0gb3BlbigiL2Rldi91ZG1h YnVmIiwgT19SRFdSKTsKPiArCWlmIChkZXYgPCAwKSB7Cj4gKwkJcHJpbnRmKCIlczogW3NraXBd XG4iLCBURVNUX1BSRUZJWCk7Cj4gKwkJZXhpdCg3Nyk7Cj4gKwl9Cj4gKwo+ICsJbWVtID0gbWVt YWxpZ24oZ2V0cGFnZXNpemUoKSwgZ2V0cGFnZXNpemUoKSAqIE5VTV9QQUdFUyk7Cj4gKwlpZiAo bWVtID09IE5VTEwpIHsKPiArCQlwcmludGYoIiVzOiBbRkFJTF1cbiIsIFRFU1RfUFJFRklYKTsK PiArCQlleGl0ICgxKTsKPiArCX0KPiArCj4gKwljcmVhdGUgPSBtYWxsb2Moc2l6ZW9mKHN0cnVj dCB1ZG1hYnVmX2NyZWF0ZSkgKwo+ICsJCQlzaXplb2Yoc3RydWN0IHVkbWFidWZfaW92ZWMpKTsK PiArCWNyZWF0ZS0+ZmxhZ3MgPSAwOwo+ICsJY3JlYXRlLT5uaW92ICA9IDE7Cj4gKwo+ICsJLyog c2hvdWxkIGZhaWwgKGJhc2Ugbm90IHBhZ2UgYWxpZ25lZCkgKi8KPiArCWNyZWF0ZS0+aW92c1sw XS5iYXNlID0gKGludHB0cl90KW1lbSArIGdldHBhZ2VzaXplKCkvMjsKPiArCWNyZWF0ZS0+aW92 c1swXS5sZW4gID0gZ2V0cGFnZXNpemUoKTsKPiArCWZkID0gaW9jdGwoZGV2LCBVRE1BQlVGX0NS RUFURSwgY3JlYXRlKTsKPiArCWlmIChmZCA+PSAwKSB7Cj4gKwkJcHJpbnRmKCIlczogW0ZBSUxd XG4iLCBURVNUX1BSRUZJWCk7Cj4gKwkJZXhpdCgxKTsKPiArCX0KPiArCj4gKwkvKiBzaG91bGQg ZmFpbCAoc2l6ZSBub3QgbXVsdGlwbGUgb2YgcGFnZSkgKi8KPiArCWNyZWF0ZS0+aW92c1swXS5i YXNlID0gKGludHB0cl90KW1lbTsKPiArCWNyZWF0ZS0+aW92c1swXS5sZW4gID0gZ2V0cGFnZXNp emUoKS8yOwo+ICsJZmQgPSBpb2N0bChkZXYsIFVETUFCVUZfQ1JFQVRFLCBjcmVhdGUpOwo+ICsJ aWYgKGZkID49IDApIHsKPiArCQlwcmludGYoIiVzOiBbRkFJTF1cbiIsIFRFU1RfUFJFRklYKTsK PiArCQlleGl0KDEpOwo+ICsJfQo+ICsKPiArCS8qIHNob3VsZCB3b3JrICovCj4gKwljcmVhdGUt PmlvdnNbMF0uYmFzZSA9IChpbnRwdHJfdCltZW07Cj4gKwljcmVhdGUtPmlvdnNbMF0ubGVuICA9 IGdldHBhZ2VzaXplKCkgKiBOVU1fUEFHRVM7Cj4gKwlmZCA9IGlvY3RsKGRldiwgVURNQUJVRl9D UkVBVEUsIGNyZWF0ZSk7Cj4gKwlpZiAoZmQgPCAwKSB7Cj4gKwkJcHJpbnRmKCIlczogW0ZBSUxd XG4iLCBURVNUX1BSRUZJWCk7Cj4gKwkJZXhpdCgxKTsKPiArCX0KPiArCWNsb3NlKGZkKTsKPiAr Cj4gKwlmcHJpbnRmKHN0ZGVyciwgIiVzOiBva1xuIiwgVEVTVF9QUkVGSVgpOwo+ICsJY2xvc2Uo ZGV2KTsKPiArCXJldHVybiAwOwo+ICt9Cj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvZG1hLWJ1Zi9L Y29uZmlnIGIvZHJpdmVycy9kbWEtYnVmL0tjb25maWcKPiBpbmRleCBlZDNiNzg1YmFlLi4xOWJl M2VjNjJkIDEwMDY0NAo+IC0tLSBhL2RyaXZlcnMvZG1hLWJ1Zi9LY29uZmlnCj4gKysrIGIvZHJp dmVycy9kbWEtYnVmL0tjb25maWcKPiBAQCAtMzAsNCArMzAsMTEgQEAgY29uZmlnIFNXX1NZTkMK PiAgIAkgIFdBUk5JTkc6IGltcHJvcGVyIHVzZSBvZiB0aGlzIGNhbiByZXN1bHQgaW4gZGVhZGxv Y2tpbmcga2VybmVsCj4gICAJICBkcml2ZXJzIGZyb20gdXNlcnNwYWNlLiBJbnRlbmRlZCBmb3Ig dGVzdCBhbmQgZGVidWcgb25seS4KPiAgIAo+ICtjb25maWcgVURNQUJVRgo+ICsJYm9vbCAidXNl cnNwYWNlIGRtYWJ1ZiBtaXNjIGRyaXZlciIKPiArCWRlZmF1bHQgbgo+ICsJZGVwZW5kcyBvbiBE TUFfU0hBUkVEX0JVRkZFUgo+ICsJLS0taGVscC0tLQo+ICsJICBBIGRyaXZlciB0byBsZXQgdXNl cnNwYWNlIHR1cm4gaW92cyBpbnRvIGRtYS1idWZzLgo+ICsKPiAgIGVuZG1lbnUKPiBkaWZmIC0t Z2l0IGEvZHJpdmVycy9kbWEtYnVmL01ha2VmaWxlIGIvZHJpdmVycy9kbWEtYnVmL01ha2VmaWxl Cj4gaW5kZXggYzMzYmY4ODYzMS4uMDkxM2E2Y2NhYiAxMDA2NDQKPiAtLS0gYS9kcml2ZXJzL2Rt YS1idWYvTWFrZWZpbGUKPiArKysgYi9kcml2ZXJzL2RtYS1idWYvTWFrZWZpbGUKPiBAQCAtMSwz ICsxLDQgQEAKPiAgIG9iai15IDo9IGRtYS1idWYubyBkbWEtZmVuY2UubyBkbWEtZmVuY2UtYXJy YXkubyByZXNlcnZhdGlvbi5vIHNlcW5vLWZlbmNlLm8KPiAgIG9iai0kKENPTkZJR19TWU5DX0ZJ TEUpCQkrPSBzeW5jX2ZpbGUubwo+ICAgb2JqLSQoQ09ORklHX1NXX1NZTkMpCQkrPSBzd19zeW5j Lm8gc3luY19kZWJ1Zy5vCj4gK29iai0kKENPTkZJR19VRE1BQlVGKQkJKz0gdWRtYWJ1Zi5vCj4g ZGlmZiAtLWdpdCBhL3Rvb2xzL3Rlc3Rpbmcvc2VsZnRlc3RzL2RyaXZlcnMvZG1hLWJ1Zi9NYWtl ZmlsZSBiL3Rvb2xzL3Rlc3Rpbmcvc2VsZnRlc3RzL2RyaXZlcnMvZG1hLWJ1Zi9NYWtlZmlsZQo+ IG5ldyBmaWxlIG1vZGUgMTAwNjQ0Cj4gaW5kZXggMDAwMDAwMDAwMC4uNDE1NGMzZDdhYQo+IC0t LSAvZGV2L251bGwKPiArKysgYi90b29scy90ZXN0aW5nL3NlbGZ0ZXN0cy9kcml2ZXJzL2RtYS1i dWYvTWFrZWZpbGUKPiBAQCAtMCwwICsxLDUgQEAKPiArQ0ZMQUdTICs9IC1JLi4vLi4vLi4vLi4v Li4vdXNyL2luY2x1ZGUvCj4gKwo+ICtURVNUX0dFTl9QUk9HUyA6PSB1ZG1hYnVmCj4gKwo+ICtp bmNsdWRlIC4uLy4uL2xpYi5tawoKX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX19fX18KZHJpLWRldmVsIG1haWxpbmcgbGlzdApkcmktZGV2ZWxAbGlzdHMuZnJlZWRl c2t0b3Aub3JnCmh0dHBzOi8vbGlzdHMuZnJlZWRlc2t0b3Aub3JnL21haWxtYW4vbGlzdGluZm8v ZHJpLWRldmVsCg== From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from mail-wr0-f196.google.com ([209.85.128.196]:37866 "EHLO mail-wr0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750749AbeCPMaq (ORCPT ); Fri, 16 Mar 2018 08:30:46 -0400 Reply-To: christian.koenig@amd.com Subject: Re: [PATCH v2] Add udmabuf misc device To: Gerd Hoffmann , dri-devel@lists.freedesktop.org, Daniel Vetter Cc: "open list:KERNEL SELFTEST FRAMEWORK" , Tomeu Vizoso , David Airlie , qemu-devel@nongnu.org, open list , "moderated list:DMA BUFFER SHARING FRAMEWORK" , Shuah Khan , "open list:DMA BUFFER SHARING FRAMEWORK" References: <20180316074650.5415-1-kraxel@redhat.com> From: =?UTF-8?Q?Christian_K=c3=b6nig?= Message-ID: <7547e99b-0e3c-264e-e52b-40ad5d52b49a@gmail.com> Date: Fri, 16 Mar 2018 13:30:43 +0100 MIME-Version: 1.0 In-Reply-To: <20180316074650.5415-1-kraxel@redhat.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-Language: en-US Sender: linux-media-owner@vger.kernel.org List-ID: Am 16.03.2018 um 08:46 schrieb Gerd Hoffmann: > A driver to let userspace turn iovecs into dma-bufs. > > Use case: Allows qemu create dmabufs for the vga framebuffer or > virtio-gpu ressources. Then they can be passed around to display > those guest things on the host. To spice client for classic full > framebuffer display, and hopefully some day to wayland server for > seamless guest window display. > > Those dma-bufs are accounted against user's shm mlock bucket as the > pages are effectively locked in memory. Sorry to disappoint you, but we have investigated the same idea for userptr use quite extensively and found that whole approach doesn't work. The pages backing a DMA-buf are not allowed to move (at least not without a patch set I'm currently working on), but for certain MM operations to work correctly you must be able to modify the page tables entries and move the pages backing them around. For example try to use fork() with some copy on write pages with this approach. You will find that you have only two options to correctly handle this. Either you access memory which could no longer belong to your process, which in turn is major security no-go. Or you use a MM notifier, but without being able to unmap DMA-bufs that won't work and you will certainly create a deadlock. Even with the patch set I'm currently working on the MM notifier approach is a real beast to get right. Regards, Christian. > > Cc: David Airlie > Cc: Tomeu Vizoso > Cc: Daniel Vetter > Signed-off-by: Gerd Hoffmann > --- > include/uapi/linux/udmabuf.h | 23 ++ > drivers/dma-buf/udmabuf.c | 261 ++++++++++++++++++++++ > tools/testing/selftests/drivers/dma-buf/udmabuf.c | 69 ++++++ > drivers/dma-buf/Kconfig | 7 + > drivers/dma-buf/Makefile | 1 + > tools/testing/selftests/drivers/dma-buf/Makefile | 5 + > 6 files changed, 366 insertions(+) > create mode 100644 include/uapi/linux/udmabuf.h > create mode 100644 drivers/dma-buf/udmabuf.c > create mode 100644 tools/testing/selftests/drivers/dma-buf/udmabuf.c > create mode 100644 tools/testing/selftests/drivers/dma-buf/Makefile > > diff --git a/include/uapi/linux/udmabuf.h b/include/uapi/linux/udmabuf.h > new file mode 100644 > index 0000000000..54ceba203a > --- /dev/null > +++ b/include/uapi/linux/udmabuf.h > @@ -0,0 +1,23 @@ > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > +#ifndef _UAPI_LINUX_UDMABUF_H > +#define _UAPI_LINUX_UDMABUF_H > + > +#include > +#include > + > +struct udmabuf_iovec { > + __u64 base; > + __u64 len; > +}; > + > +#define UDMABUF_FLAGS_CLOEXEC 0x01 > + > +struct udmabuf_create { > + __u32 flags; > + __u32 niov; > + struct udmabuf_iovec iovs[]; > +}; > + > +#define UDMABUF_CREATE _IOW(0x42, 0x23, struct udmabuf_create) > + > +#endif /* _UAPI_LINUX_UDMABUF_H */ > diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c > new file mode 100644 > index 0000000000..664ab4ee4e > --- /dev/null > +++ b/drivers/dma-buf/udmabuf.c > @@ -0,0 +1,261 @@ > +/* > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +struct udmabuf { > + u32 pagecount; > + struct page **pages; > + struct user_struct *owner; > +}; > + > +static int udmabuf_vm_fault(struct vm_fault *vmf) > +{ > + struct vm_area_struct *vma = vmf->vma; > + struct udmabuf *ubuf = vma->vm_private_data; > + > + if (WARN_ON(vmf->pgoff >= ubuf->pagecount)) > + return VM_FAULT_SIGBUS; > + > + vmf->page = ubuf->pages[vmf->pgoff]; > + get_page(vmf->page); > + return 0; > +} > + > +static const struct vm_operations_struct udmabuf_vm_ops = { > + .fault = udmabuf_vm_fault, > +}; > + > +static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma) > +{ > + struct udmabuf *ubuf = buf->priv; > + > + if ((vma->vm_flags & VM_SHARED) == 0) > + return -EINVAL; > + > + vma->vm_ops = &udmabuf_vm_ops; > + vma->vm_private_data = ubuf; > + return 0; > +} > + > +static struct sg_table *map_udmabuf(struct dma_buf_attachment *at, > + enum dma_data_direction direction) > +{ > + struct udmabuf *ubuf = at->dmabuf->priv; > + struct sg_table *sg; > + > + sg = kzalloc(sizeof(*sg), GFP_KERNEL); > + if (!sg) > + goto err1; > + if (sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->pagecount, > + 0, ubuf->pagecount << PAGE_SHIFT, > + GFP_KERNEL) < 0) > + goto err2; > + if (!dma_map_sg(at->dev, sg->sgl, sg->nents, direction)) > + goto err3; > + > + return sg; > + > +err3: > + sg_free_table(sg); > +err2: > + kfree(sg); > +err1: > + return ERR_PTR(-ENOMEM); > +} > + > +static void unmap_udmabuf(struct dma_buf_attachment *at, > + struct sg_table *sg, > + enum dma_data_direction direction) > +{ > + sg_free_table(sg); > + kfree(sg); > +} > + > +static void release_udmabuf(struct dma_buf *buf) > +{ > + struct udmabuf *ubuf = buf->priv; > + pgoff_t pg; > + > + for (pg = 0; pg < ubuf->pagecount; pg++) > + put_page(ubuf->pages[pg]); > + user_shm_unlock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner); > + free_uid(ubuf->owner); > + kfree(ubuf->pages); > + kfree(ubuf); > +} > + > +static void *kmap_atomic_udmabuf(struct dma_buf *buf, unsigned long page_num) > +{ > + struct udmabuf *ubuf = buf->priv; > + struct page *page = ubuf->pages[page_num]; > + > + return kmap_atomic(page); > +} > + > +static void *kmap_udmabuf(struct dma_buf *buf, unsigned long page_num) > +{ > + struct udmabuf *ubuf = buf->priv; > + struct page *page = ubuf->pages[page_num]; > + > + return kmap(page); > +} > + > +static struct dma_buf_ops udmabuf_ops = { > + .map_dma_buf = map_udmabuf, > + .unmap_dma_buf = unmap_udmabuf, > + .release = release_udmabuf, > + .map_atomic = kmap_atomic_udmabuf, > + .map = kmap_udmabuf, > + .mmap = mmap_udmabuf, > +}; > + > +static long udmabuf_ioctl_create(struct file *filp, unsigned long arg) > +{ > + struct udmabuf_create create; > + struct udmabuf_iovec *iovs; > + struct udmabuf *ubuf; > + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); > + struct dma_buf *buf; > + pgoff_t pgoff, pgcnt; > + u32 iov, flags; > + int ret; > + > + if (copy_from_user(&create, (void __user *)arg, > + sizeof(struct udmabuf_create))) > + return -EFAULT; > + > + iovs = kmalloc_array(create.niov, sizeof(struct udmabuf_iovec), > + GFP_KERNEL); > + if (!iovs) > + return -ENOMEM; > + > + arg += offsetof(struct udmabuf_create, iovs); > + ret = -EFAULT; > + if (copy_from_user(iovs, (void __user *)arg, > + create.niov * sizeof(struct udmabuf_iovec))) > + goto err_free_iovs; > + > + ubuf = kzalloc(sizeof(struct udmabuf), GFP_KERNEL); > + if (!ubuf) > + goto err_free_iovs; > + > + ret = -EINVAL; > + for (iov = 0; iov < create.niov; iov++) { > + if (!IS_ALIGNED(iovs[iov].base, PAGE_SIZE)) > + goto err_free_buf; > + if (!IS_ALIGNED(iovs[iov].len, PAGE_SIZE)) > + goto err_free_buf; > + ubuf->pagecount += iovs[iov].len >> PAGE_SHIFT; > + } > + > + /* this effectively mlocks the pages so account it accordingly */ > + ret = -ENOMEM; > + ubuf->owner = current_user(); > + if (!user_shm_lock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner)) > + goto err_free_buf; > + > + ubuf->pages = kmalloc_array(ubuf->pagecount, sizeof(struct page*), > + GFP_KERNEL); > + if (!ubuf->pages) > + goto err_shm_unlock; > + > + pgoff = 0; > + for (iov = 0; iov < create.niov; iov++) { > + pgcnt = iovs[iov].len >> PAGE_SHIFT; > + while (pgcnt > 0) { > + ret = get_user_pages_fast(iovs[iov].base, pgcnt, > + true, /* write */ > + ubuf->pages + pgoff); > + if (ret < 0) > + goto err_put_pages; > + pgoff += ret; > + pgcnt -= ret; > + } > + } > + > + exp_info.ops = &udmabuf_ops; > + exp_info.size = ubuf->pagecount << PAGE_SHIFT; > + exp_info.priv = ubuf; > + > + buf = dma_buf_export(&exp_info); > + if (IS_ERR(buf)) { > + ret = PTR_ERR(buf); > + goto err_put_pages; > + } > + > + flags = 0; > + if (create.flags & UDMABUF_FLAGS_CLOEXEC) > + flags |= O_CLOEXEC; > + > + kfree(iovs); > + return dma_buf_fd(buf, flags); > + > +err_put_pages: > + while (pgoff > 0) > + put_page(ubuf->pages[--pgoff]); > +err_shm_unlock: > + user_shm_unlock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner); > +err_free_buf: > + free_uid(ubuf->owner); > + kfree(ubuf->pages); > + kfree(ubuf); > +err_free_iovs: > + kfree(iovs); > + return ret; > +} > + > +static long udmabuf_ioctl(struct file *filp, unsigned int ioctl, > + unsigned long arg) > +{ > + long ret; > + > + switch (ioctl) { > + case UDMABUF_CREATE: > + ret = udmabuf_ioctl_create(filp, arg); > + break; > + default: > + ret = -EINVAL; > + break; > + } > + return ret; > +} > + > +static const struct file_operations udmabuf_fops = { > + .owner = THIS_MODULE, > + .unlocked_ioctl = udmabuf_ioctl, > +}; > + > +static struct miscdevice udmabuf_misc = { > + .minor = MISC_DYNAMIC_MINOR, > + .name = "udmabuf", > + .fops = &udmabuf_fops, > +}; > + > +static int __init udmabuf_dev_init(void) > +{ > + return misc_register(&udmabuf_misc); > +} > + > +static void __exit udmabuf_dev_exit(void) > +{ > + misc_deregister(&udmabuf_misc); > +} > + > +module_init(udmabuf_dev_init) > +module_exit(udmabuf_dev_exit) > + > +MODULE_AUTHOR("Gerd Hoffmann "); > +MODULE_LICENSE("GPL v2"); > diff --git a/tools/testing/selftests/drivers/dma-buf/udmabuf.c b/tools/testing/selftests/drivers/dma-buf/udmabuf.c > new file mode 100644 > index 0000000000..3472c8ee49 > --- /dev/null > +++ b/tools/testing/selftests/drivers/dma-buf/udmabuf.c > @@ -0,0 +1,69 @@ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#define TEST_PREFIX "drivers/dma-buf/udmabuf" > +#define NUM_PAGES 4 > + > +int main(int argc, char *argv[]) > +{ > + struct udmabuf_create *create; > + void *mem; > + int dev, fd; > + > + dev = open("/dev/udmabuf", O_RDWR); > + if (dev < 0) { > + printf("%s: [skip]\n", TEST_PREFIX); > + exit(77); > + } > + > + mem = memalign(getpagesize(), getpagesize() * NUM_PAGES); > + if (mem == NULL) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit (1); > + } > + > + create = malloc(sizeof(struct udmabuf_create) + > + sizeof(struct udmabuf_iovec)); > + create->flags = 0; > + create->niov = 1; > + > + /* should fail (base not page aligned) */ > + create->iovs[0].base = (intptr_t)mem + getpagesize()/2; > + create->iovs[0].len = getpagesize(); > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd >= 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + > + /* should fail (size not multiple of page) */ > + create->iovs[0].base = (intptr_t)mem; > + create->iovs[0].len = getpagesize()/2; > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd >= 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + > + /* should work */ > + create->iovs[0].base = (intptr_t)mem; > + create->iovs[0].len = getpagesize() * NUM_PAGES; > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd < 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + close(fd); > + > + fprintf(stderr, "%s: ok\n", TEST_PREFIX); > + close(dev); > + return 0; > +} > diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig > index ed3b785bae..19be3ec62d 100644 > --- a/drivers/dma-buf/Kconfig > +++ b/drivers/dma-buf/Kconfig > @@ -30,4 +30,11 @@ config SW_SYNC > WARNING: improper use of this can result in deadlocking kernel > drivers from userspace. Intended for test and debug only. > > +config UDMABUF > + bool "userspace dmabuf misc driver" > + default n > + depends on DMA_SHARED_BUFFER > + ---help--- > + A driver to let userspace turn iovs into dma-bufs. > + > endmenu > diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile > index c33bf88631..0913a6ccab 100644 > --- a/drivers/dma-buf/Makefile > +++ b/drivers/dma-buf/Makefile > @@ -1,3 +1,4 @@ > obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o > obj-$(CONFIG_SYNC_FILE) += sync_file.o > obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o > +obj-$(CONFIG_UDMABUF) += udmabuf.o > diff --git a/tools/testing/selftests/drivers/dma-buf/Makefile b/tools/testing/selftests/drivers/dma-buf/Makefile > new file mode 100644 > index 0000000000..4154c3d7aa > --- /dev/null > +++ b/tools/testing/selftests/drivers/dma-buf/Makefile > @@ -0,0 +1,5 @@ > +CFLAGS += -I../../../../../usr/include/ > + > +TEST_GEN_PROGS := udmabuf > + > +include ../../lib.mk From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39073) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ewoVa-0001LQ-Vd for qemu-devel@nongnu.org; Fri, 16 Mar 2018 08:30:57 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ewoVY-00024Z-No for qemu-devel@nongnu.org; Fri, 16 Mar 2018 08:30:55 -0400 Received: from mail-wr0-x243.google.com ([2a00:1450:400c:c0c::243]:39752) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ewoVY-0001o7-Cn for qemu-devel@nongnu.org; Fri, 16 Mar 2018 08:30:52 -0400 Received: by mail-wr0-x243.google.com with SMTP id k3so11534717wrg.6 for ; Fri, 16 Mar 2018 05:30:50 -0700 (PDT) Reply-To: christian.koenig@amd.com References: <20180316074650.5415-1-kraxel@redhat.com> From: =?UTF-8?Q?Christian_K=c3=b6nig?= Message-ID: <7547e99b-0e3c-264e-e52b-40ad5d52b49a@gmail.com> Date: Fri, 16 Mar 2018 13:30:43 +0100 MIME-Version: 1.0 In-Reply-To: <20180316074650.5415-1-kraxel@redhat.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-Language: en-US Subject: Re: [Qemu-devel] [PATCH v2] Add udmabuf misc device List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Gerd Hoffmann , dri-devel@lists.freedesktop.org, Daniel Vetter Cc: "open list:KERNEL SELFTEST FRAMEWORK" , Tomeu Vizoso , David Airlie , qemu-devel@nongnu.org, open list , "moderated list:DMA BUFFER SHARING FRAMEWORK" , Shuah Khan , "open list:DMA BUFFER SHARING FRAMEWORK" Am 16.03.2018 um 08:46 schrieb Gerd Hoffmann: > A driver to let userspace turn iovecs into dma-bufs. > > Use case: Allows qemu create dmabufs for the vga framebuffer or > virtio-gpu ressources. Then they can be passed around to display > those guest things on the host. To spice client for classic full > framebuffer display, and hopefully some day to wayland server for > seamless guest window display. > > Those dma-bufs are accounted against user's shm mlock bucket as the > pages are effectively locked in memory. Sorry to disappoint you, but we have investigated the same idea for userptr use quite extensively and found that whole approach doesn't work. The pages backing a DMA-buf are not allowed to move (at least not without a patch set I'm currently working on), but for certain MM operations to work correctly you must be able to modify the page tables entries and move the pages backing them around. For example try to use fork() with some copy on write pages with this approach. You will find that you have only two options to correctly handle this. Either you access memory which could no longer belong to your process, which in turn is major security no-go. Or you use a MM notifier, but without being able to unmap DMA-bufs that won't work and you will certainly create a deadlock. Even with the patch set I'm currently working on the MM notifier approach is a real beast to get right. Regards, Christian. > > Cc: David Airlie > Cc: Tomeu Vizoso > Cc: Daniel Vetter > Signed-off-by: Gerd Hoffmann > --- > include/uapi/linux/udmabuf.h | 23 ++ > drivers/dma-buf/udmabuf.c | 261 ++++++++++++++++++++++ > tools/testing/selftests/drivers/dma-buf/udmabuf.c | 69 ++++++ > drivers/dma-buf/Kconfig | 7 + > drivers/dma-buf/Makefile | 1 + > tools/testing/selftests/drivers/dma-buf/Makefile | 5 + > 6 files changed, 366 insertions(+) > create mode 100644 include/uapi/linux/udmabuf.h > create mode 100644 drivers/dma-buf/udmabuf.c > create mode 100644 tools/testing/selftests/drivers/dma-buf/udmabuf.c > create mode 100644 tools/testing/selftests/drivers/dma-buf/Makefile > > diff --git a/include/uapi/linux/udmabuf.h b/include/uapi/linux/udmabuf.h > new file mode 100644 > index 0000000000..54ceba203a > --- /dev/null > +++ b/include/uapi/linux/udmabuf.h > @@ -0,0 +1,23 @@ > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > +#ifndef _UAPI_LINUX_UDMABUF_H > +#define _UAPI_LINUX_UDMABUF_H > + > +#include > +#include > + > +struct udmabuf_iovec { > + __u64 base; > + __u64 len; > +}; > + > +#define UDMABUF_FLAGS_CLOEXEC 0x01 > + > +struct udmabuf_create { > + __u32 flags; > + __u32 niov; > + struct udmabuf_iovec iovs[]; > +}; > + > +#define UDMABUF_CREATE _IOW(0x42, 0x23, struct udmabuf_create) > + > +#endif /* _UAPI_LINUX_UDMABUF_H */ > diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c > new file mode 100644 > index 0000000000..664ab4ee4e > --- /dev/null > +++ b/drivers/dma-buf/udmabuf.c > @@ -0,0 +1,261 @@ > +/* > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +struct udmabuf { > + u32 pagecount; > + struct page **pages; > + struct user_struct *owner; > +}; > + > +static int udmabuf_vm_fault(struct vm_fault *vmf) > +{ > + struct vm_area_struct *vma = vmf->vma; > + struct udmabuf *ubuf = vma->vm_private_data; > + > + if (WARN_ON(vmf->pgoff >= ubuf->pagecount)) > + return VM_FAULT_SIGBUS; > + > + vmf->page = ubuf->pages[vmf->pgoff]; > + get_page(vmf->page); > + return 0; > +} > + > +static const struct vm_operations_struct udmabuf_vm_ops = { > + .fault = udmabuf_vm_fault, > +}; > + > +static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma) > +{ > + struct udmabuf *ubuf = buf->priv; > + > + if ((vma->vm_flags & VM_SHARED) == 0) > + return -EINVAL; > + > + vma->vm_ops = &udmabuf_vm_ops; > + vma->vm_private_data = ubuf; > + return 0; > +} > + > +static struct sg_table *map_udmabuf(struct dma_buf_attachment *at, > + enum dma_data_direction direction) > +{ > + struct udmabuf *ubuf = at->dmabuf->priv; > + struct sg_table *sg; > + > + sg = kzalloc(sizeof(*sg), GFP_KERNEL); > + if (!sg) > + goto err1; > + if (sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->pagecount, > + 0, ubuf->pagecount << PAGE_SHIFT, > + GFP_KERNEL) < 0) > + goto err2; > + if (!dma_map_sg(at->dev, sg->sgl, sg->nents, direction)) > + goto err3; > + > + return sg; > + > +err3: > + sg_free_table(sg); > +err2: > + kfree(sg); > +err1: > + return ERR_PTR(-ENOMEM); > +} > + > +static void unmap_udmabuf(struct dma_buf_attachment *at, > + struct sg_table *sg, > + enum dma_data_direction direction) > +{ > + sg_free_table(sg); > + kfree(sg); > +} > + > +static void release_udmabuf(struct dma_buf *buf) > +{ > + struct udmabuf *ubuf = buf->priv; > + pgoff_t pg; > + > + for (pg = 0; pg < ubuf->pagecount; pg++) > + put_page(ubuf->pages[pg]); > + user_shm_unlock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner); > + free_uid(ubuf->owner); > + kfree(ubuf->pages); > + kfree(ubuf); > +} > + > +static void *kmap_atomic_udmabuf(struct dma_buf *buf, unsigned long page_num) > +{ > + struct udmabuf *ubuf = buf->priv; > + struct page *page = ubuf->pages[page_num]; > + > + return kmap_atomic(page); > +} > + > +static void *kmap_udmabuf(struct dma_buf *buf, unsigned long page_num) > +{ > + struct udmabuf *ubuf = buf->priv; > + struct page *page = ubuf->pages[page_num]; > + > + return kmap(page); > +} > + > +static struct dma_buf_ops udmabuf_ops = { > + .map_dma_buf = map_udmabuf, > + .unmap_dma_buf = unmap_udmabuf, > + .release = release_udmabuf, > + .map_atomic = kmap_atomic_udmabuf, > + .map = kmap_udmabuf, > + .mmap = mmap_udmabuf, > +}; > + > +static long udmabuf_ioctl_create(struct file *filp, unsigned long arg) > +{ > + struct udmabuf_create create; > + struct udmabuf_iovec *iovs; > + struct udmabuf *ubuf; > + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); > + struct dma_buf *buf; > + pgoff_t pgoff, pgcnt; > + u32 iov, flags; > + int ret; > + > + if (copy_from_user(&create, (void __user *)arg, > + sizeof(struct udmabuf_create))) > + return -EFAULT; > + > + iovs = kmalloc_array(create.niov, sizeof(struct udmabuf_iovec), > + GFP_KERNEL); > + if (!iovs) > + return -ENOMEM; > + > + arg += offsetof(struct udmabuf_create, iovs); > + ret = -EFAULT; > + if (copy_from_user(iovs, (void __user *)arg, > + create.niov * sizeof(struct udmabuf_iovec))) > + goto err_free_iovs; > + > + ubuf = kzalloc(sizeof(struct udmabuf), GFP_KERNEL); > + if (!ubuf) > + goto err_free_iovs; > + > + ret = -EINVAL; > + for (iov = 0; iov < create.niov; iov++) { > + if (!IS_ALIGNED(iovs[iov].base, PAGE_SIZE)) > + goto err_free_buf; > + if (!IS_ALIGNED(iovs[iov].len, PAGE_SIZE)) > + goto err_free_buf; > + ubuf->pagecount += iovs[iov].len >> PAGE_SHIFT; > + } > + > + /* this effectively mlocks the pages so account it accordingly */ > + ret = -ENOMEM; > + ubuf->owner = current_user(); > + if (!user_shm_lock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner)) > + goto err_free_buf; > + > + ubuf->pages = kmalloc_array(ubuf->pagecount, sizeof(struct page*), > + GFP_KERNEL); > + if (!ubuf->pages) > + goto err_shm_unlock; > + > + pgoff = 0; > + for (iov = 0; iov < create.niov; iov++) { > + pgcnt = iovs[iov].len >> PAGE_SHIFT; > + while (pgcnt > 0) { > + ret = get_user_pages_fast(iovs[iov].base, pgcnt, > + true, /* write */ > + ubuf->pages + pgoff); > + if (ret < 0) > + goto err_put_pages; > + pgoff += ret; > + pgcnt -= ret; > + } > + } > + > + exp_info.ops = &udmabuf_ops; > + exp_info.size = ubuf->pagecount << PAGE_SHIFT; > + exp_info.priv = ubuf; > + > + buf = dma_buf_export(&exp_info); > + if (IS_ERR(buf)) { > + ret = PTR_ERR(buf); > + goto err_put_pages; > + } > + > + flags = 0; > + if (create.flags & UDMABUF_FLAGS_CLOEXEC) > + flags |= O_CLOEXEC; > + > + kfree(iovs); > + return dma_buf_fd(buf, flags); > + > +err_put_pages: > + while (pgoff > 0) > + put_page(ubuf->pages[--pgoff]); > +err_shm_unlock: > + user_shm_unlock(ubuf->pagecount << PAGE_SHIFT, ubuf->owner); > +err_free_buf: > + free_uid(ubuf->owner); > + kfree(ubuf->pages); > + kfree(ubuf); > +err_free_iovs: > + kfree(iovs); > + return ret; > +} > + > +static long udmabuf_ioctl(struct file *filp, unsigned int ioctl, > + unsigned long arg) > +{ > + long ret; > + > + switch (ioctl) { > + case UDMABUF_CREATE: > + ret = udmabuf_ioctl_create(filp, arg); > + break; > + default: > + ret = -EINVAL; > + break; > + } > + return ret; > +} > + > +static const struct file_operations udmabuf_fops = { > + .owner = THIS_MODULE, > + .unlocked_ioctl = udmabuf_ioctl, > +}; > + > +static struct miscdevice udmabuf_misc = { > + .minor = MISC_DYNAMIC_MINOR, > + .name = "udmabuf", > + .fops = &udmabuf_fops, > +}; > + > +static int __init udmabuf_dev_init(void) > +{ > + return misc_register(&udmabuf_misc); > +} > + > +static void __exit udmabuf_dev_exit(void) > +{ > + misc_deregister(&udmabuf_misc); > +} > + > +module_init(udmabuf_dev_init) > +module_exit(udmabuf_dev_exit) > + > +MODULE_AUTHOR("Gerd Hoffmann "); > +MODULE_LICENSE("GPL v2"); > diff --git a/tools/testing/selftests/drivers/dma-buf/udmabuf.c b/tools/testing/selftests/drivers/dma-buf/udmabuf.c > new file mode 100644 > index 0000000000..3472c8ee49 > --- /dev/null > +++ b/tools/testing/selftests/drivers/dma-buf/udmabuf.c > @@ -0,0 +1,69 @@ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#define TEST_PREFIX "drivers/dma-buf/udmabuf" > +#define NUM_PAGES 4 > + > +int main(int argc, char *argv[]) > +{ > + struct udmabuf_create *create; > + void *mem; > + int dev, fd; > + > + dev = open("/dev/udmabuf", O_RDWR); > + if (dev < 0) { > + printf("%s: [skip]\n", TEST_PREFIX); > + exit(77); > + } > + > + mem = memalign(getpagesize(), getpagesize() * NUM_PAGES); > + if (mem == NULL) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit (1); > + } > + > + create = malloc(sizeof(struct udmabuf_create) + > + sizeof(struct udmabuf_iovec)); > + create->flags = 0; > + create->niov = 1; > + > + /* should fail (base not page aligned) */ > + create->iovs[0].base = (intptr_t)mem + getpagesize()/2; > + create->iovs[0].len = getpagesize(); > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd >= 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + > + /* should fail (size not multiple of page) */ > + create->iovs[0].base = (intptr_t)mem; > + create->iovs[0].len = getpagesize()/2; > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd >= 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + > + /* should work */ > + create->iovs[0].base = (intptr_t)mem; > + create->iovs[0].len = getpagesize() * NUM_PAGES; > + fd = ioctl(dev, UDMABUF_CREATE, create); > + if (fd < 0) { > + printf("%s: [FAIL]\n", TEST_PREFIX); > + exit(1); > + } > + close(fd); > + > + fprintf(stderr, "%s: ok\n", TEST_PREFIX); > + close(dev); > + return 0; > +} > diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig > index ed3b785bae..19be3ec62d 100644 > --- a/drivers/dma-buf/Kconfig > +++ b/drivers/dma-buf/Kconfig > @@ -30,4 +30,11 @@ config SW_SYNC > WARNING: improper use of this can result in deadlocking kernel > drivers from userspace. Intended for test and debug only. > > +config UDMABUF > + bool "userspace dmabuf misc driver" > + default n > + depends on DMA_SHARED_BUFFER > + ---help--- > + A driver to let userspace turn iovs into dma-bufs. > + > endmenu > diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile > index c33bf88631..0913a6ccab 100644 > --- a/drivers/dma-buf/Makefile > +++ b/drivers/dma-buf/Makefile > @@ -1,3 +1,4 @@ > obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o > obj-$(CONFIG_SYNC_FILE) += sync_file.o > obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o > +obj-$(CONFIG_UDMABUF) += udmabuf.o > diff --git a/tools/testing/selftests/drivers/dma-buf/Makefile b/tools/testing/selftests/drivers/dma-buf/Makefile > new file mode 100644 > index 0000000000..4154c3d7aa > --- /dev/null > +++ b/tools/testing/selftests/drivers/dma-buf/Makefile > @@ -0,0 +1,5 @@ > +CFLAGS += -I../../../../../usr/include/ > + > +TEST_GEN_PROGS := udmabuf > + > +include ../../lib.mk