dma-buf: add test driver for dma-buf container
authorCho KyongHo <pullip.cho@samsung.com>
Fri, 13 Apr 2018 11:39:04 +0000 (20:39 +0900)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:22:20 +0000 (20:22 +0300)
Change-Id: Ic7a9015dd828ca66560555a44955d625a301e715
Signed-off-by: Cho KyongHo <pullip.cho@samsung.com>
drivers/dma-buf/Kconfig
drivers/dma-buf/dma-buf-container.c

index 8f069e4d0a4afada9d064efcde4e4dc3bf67731d..72fe41df191c3422ae5eefd37a2324705628b949 100644 (file)
@@ -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
index 7313102c2c650a501c3271960cfa264c162c19b5..f76500240739ed181f64ed751eb8d6001f6a3062 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 #include <linux/dma-buf.h>
+#include <linux/miscdevice.h>
 #include <linux/dma-buf-container.h>
 
 #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 */