#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"
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 */