From de4c57eb11a670ebdce1a1b3b1e099d377cdb228 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Fri, 13 Apr 2018 20:39:04 +0900 Subject: [PATCH] dma-buf: add test driver for dma-buf container Change-Id: Ic7a9015dd828ca66560555a44955d625a301e715 Signed-off-by: Cho KyongHo --- drivers/dma-buf/Kconfig | 8 + drivers/dma-buf/dma-buf-container.c | 239 ++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index 8f069e4d0a4a..72fe41df191c 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig @@ -39,4 +39,12 @@ config DMA_BUF_CONTAINER dma-buf objects. It is useful when delivering a lot of dma-buf objects between processes or user and kernel to reduce communication overhead. +config DMA_BUF_CONTAINER_TEST + bool "test driver for dma-buf container" + default n + depends on DMA_BUF_CONTAINER + ---help--- + Test driver for dma-buf container to verify the elements in the + dma-buf container are the same as the given list of dma-buf objects. + endmenu diff --git a/drivers/dma-buf/dma-buf-container.c b/drivers/dma-buf/dma-buf-container.c index 7313102c2c65..f76500240739 100644 --- a/drivers/dma-buf/dma-buf-container.c +++ b/drivers/dma-buf/dma-buf-container.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "dma-buf-container.h" @@ -347,3 +348,241 @@ struct dma_buf *dma_buf_get_any(int fd) return anybuf; } EXPORT_SYMBOL_GPL(dma_buf_get_any); + +#ifdef CONFIG_DMA_BUF_CONTAINER_TEST + +static int dmabuf_container_verify_one(struct dma_buf_container *container, + struct dma_buf *dmabuf, int idx) +{ + struct dma_buf_container *subset; + int subidx; + + if (!is_dmabuf_container(dmabuf)) { + if (container->dmabufs[idx] == dmabuf) + return 1; + return -ENOENT; + } + + subset = get_container(dmabuf); + + for (subidx = 0; subidx < subset->count; subidx++) + if (subset->dmabufs[subidx] != container->dmabufs[idx++]) + return -ENOENT; + + return subset->count; +} + +static long dmabuf_container_verify(struct dma_buf_container *container, + struct dma_buf *dmabufs[], int count) +{ + int idx = 0, i = 0; + + while ((i < count) && (idx < container->count)) { + int ret = dmabuf_container_verify_one(container, + dmabufs[i], idx); + + if (ret < 0) { + pr_err("%s: verify failed @ container[%d]/dmabuf[%d]\n", + __func__, idx, i); + return ret; + } + + idx += ret; + i++; + } + + return 0; +} + +struct bufcon_test_data { + int *buf_fds; + __s32 num_fd; + __s32 bufcon_fd; + __u32 total_size; + __s32 reserved; +}; + +static long dmabuf_container_test(struct bufcon_test_data *data) +{ + struct dma_buf *container = dma_buf_get(data->bufcon_fd); + struct dma_buf **dmabufs; + int i; + long ret = -EINVAL; + + if (IS_ERR(container)) { + pr_err("%s: fd %d is not a dmabuf\n", + __func__, data->bufcon_fd); + return PTR_ERR(container); + } + + if (!is_dmabuf_container(container)) { + pr_err("%s: fd %d is not a dmabuf container\n", + __func__, data->bufcon_fd); + goto err_bufcon; + } + + if (container->size != data->total_size) { + pr_err("%s: the size of dmabuf container %zu is not %u\n", + __func__, container->size, data->total_size); + goto err_bufcon; + } + + dmabufs = kmalloc_array(data->num_fd, sizeof(dmabufs[0]), GFP_KERNEL); + if (!dmabufs) { + ret = -ENOMEM; + goto err_bufcon; + } + + for (i = 0; i < data->num_fd; i++) { + dmabufs[i] = dma_buf_get(data->buf_fds[i]); + if (IS_ERR(dmabufs[i])) { + ret = PTR_ERR(dmabufs[i]); + goto err; + } + } + + ret = dmabuf_container_verify(container->priv, dmabufs, data->num_fd); +err: + while (i--) + dma_buf_put(dmabufs[i]); + kfree(dmabufs); +err_bufcon: + dma_buf_put(container); + + return ret; +} + +#define TEST_IOC_TEST _IOR('T', 0, struct bufcon_test_data) + +static long dmabuf_container_test_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct bufcon_test_data __user *udata = (void __user *)arg; + struct bufcon_test_data data; + int __user *buf_fds; + long ret = 0; + + if (cmd != TEST_IOC_TEST) { + pr_err("%s: unknown cmd %#x\n", __func__, cmd); + return -ENOTTY; + } + + if (copy_from_user(&data, udata, sizeof(data))) { + pr_err("%s: failed to read user data\n", __func__); + return -EFAULT; + } + + if ((data.num_fd < 1) || (data.num_fd > MAX_BUFCON_BUFS)) { + pr_err("%s: invalid number of dma-buf fds %d\n", + __func__, data.num_fd); + return -EINVAL; + } + + buf_fds = data.buf_fds; + + data.buf_fds = kcalloc(data.num_fd, sizeof(*data.buf_fds), GFP_KERNEL); + if (!data.buf_fds) + return -ENOMEM; + + if (copy_from_user(data.buf_fds, buf_fds, + sizeof(data.buf_fds[0]) * data.num_fd)) { + pr_err("%s: failed to read fd list\n", __func__); + ret = -EFAULT; + goto err; + } + + ret = dmabuf_container_test(&data); +err: + kfree(data.buf_fds); + + return ret; +} + +#ifdef CONFIG_COMPAT +struct compat_bufcon_test_data { + compat_uptr_t buf_fds; + __s32 num_fd; + __s32 bufcon_fd; + __u32 total_size; + __s32 reserved; +}; + +#define COMPAT_TEST_IOC_TEST _IOR('T', 0, struct compat_bufcon_test_data) + +static long dmabuf_container_test_compat_ioctl(struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + struct compat_bufcon_test_data __user *udata = compat_ptr(arg); + struct bufcon_test_data data; + compat_uptr_t buf_fds; + long ret = 0; + + if (cmd != COMPAT_TEST_IOC_TEST) { + pr_err("%s: unknown cmd %#x\n", __func__, cmd); + return -ENOTTY; + } + + ret = get_user(buf_fds, &udata->buf_fds); + ret |= get_user(data.num_fd, &udata->num_fd); + ret |= get_user(data.bufcon_fd, &udata->bufcon_fd); + ret |= get_user(data.total_size, &udata->total_size); + if (ret) { + pr_err("%s: failed to read user data\n", __func__); + return -EFAULT; + } + + if ((data.num_fd < 1) || (data.num_fd > MAX_BUFCON_BUFS)) { + pr_err("%s: invalid number of dma-buf fds %d\n", + __func__, data.num_fd); + return -EINVAL; + } + + data.buf_fds = kcalloc(data.num_fd, sizeof(*data.buf_fds), GFP_KERNEL); + if (!data.buf_fds) + return -ENOMEM; + + if (copy_from_user(data.buf_fds, compat_ptr(buf_fds), + sizeof(data.buf_fds[0]) * data.num_fd)) { + pr_err("%s: failed to read fd list\n", __func__); + ret = -EFAULT; + goto err; + } + + ret = dmabuf_container_test(&data); +err: + kfree(data.buf_fds); + + return 0; +} +#endif + +static const struct file_operations dmabuf_container_test_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = dmabuf_container_test_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = dmabuf_container_test_compat_ioctl, +#endif +}; + +static struct miscdevice dmabuf_container_test_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "bufcon-test", + .fops = &dmabuf_container_test_fops, +}; + +static int __init dmabuf_container_test_init(void) +{ + int ret = misc_register(&dmabuf_container_test_dev); + + if (ret) { + pr_err("%s: failed to register %s\n", __func__, + dmabuf_container_test_dev.name); + return ret; + } + + return 0; +} +device_initcall(dmabuf_container_test_init); + +#endif /* CONFIG_DMA_BUF_CONTAINER_TEST */ -- 2.20.1