[RFC,5/6,v3] dma-buf: Add Dummy Importer Test Device

Message ID 1553818562-2516-6-git-send-email-john.stultz@linaro.org
State New
Headers show
Series
  • DMA-BUF Heaps (destaging ION)
Related show

Commit Message

John Stultz March 29, 2019, 12:16 a.m.
From: "Andrew F. Davis" <afd@ti.com>


This dummy test driver lets us do some very basic testing of
importing dma-bufs.

It is based originally on TI's out of tree "DMA-BUF physical
address user-space exporter" originally by
Andrew F. Davis <afd@ti.com>

Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: Liam Mark <lmark@codeaurora.org>
Cc: Pratik Patel <pratikp@codeaurora.org>
Cc: Brian Starkey <Brian.Starkey@arm.com>
Cc: Vincent Donnefort <Vincent.Donnefort@arm.com>
Cc: Sudipto Paul <Sudipto.Paul@arm.com>
Cc: Andrew F. Davis <afd@ti.com>
Cc: Xu YiPing <xuyiping@hisilicon.com>
Cc: "Chenfeng (puck)" <puck.chen@hisilicon.com>
Cc: butao <butao@hisilicon.com>
Cc: "Xiaqing (A)" <saberlily.xia@hisilicon.com>
Cc: Yudongbin <yudongbin@hisilicon.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Chenbo Feng <fengc@google.com>
Cc: Alistair Strachan <astrachan@google.com>
Cc: dri-devel@lists.freedesktop.org
Signed-off-by: Andrew F. Davis <afd@ti.com>

[Renamed and refactored dma_buf_phys driver, rewote commitlog]
Signed-off-by: John Stultz <john.stultz@linaro.org>

---
 drivers/dma-buf/Kconfig              |   6 +
 drivers/dma-buf/Makefile             |   1 +
 drivers/dma-buf/dma-buf-testdev.c    | 239 +++++++++++++++++++++++++++++++++++
 include/uapi/linux/dma-buf-testdev.h |  37 ++++++
 4 files changed, 283 insertions(+)
 create mode 100644 drivers/dma-buf/dma-buf-testdev.c
 create mode 100644 include/uapi/linux/dma-buf-testdev.h

-- 
2.7.4

Patch

diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig
index 63c139d..3cbcbe0 100644
--- a/drivers/dma-buf/Kconfig
+++ b/drivers/dma-buf/Kconfig
@@ -49,4 +49,10 @@  menuconfig DMABUF_HEAPS
 
 source "drivers/dma-buf/heaps/Kconfig"
 
+config DMABUF_TESTDEV
+	bool "DMA-BUF Dummy Test Device"
+	depends on DMA_SHARED_BUFFER
+	help
+	  This provides a dummy test device that can be used to test
+	  importing dma-bufs.
 endmenu
diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index 09c2f2d..69bf45d 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -4,3 +4,4 @@  obj-$(CONFIG_DMABUF_HEAPS)	+= dma-heap.o
 obj-$(CONFIG_SYNC_FILE)		+= sync_file.o
 obj-$(CONFIG_SW_SYNC)		+= sw_sync.o sync_debug.o
 obj-$(CONFIG_UDMABUF)		+= udmabuf.o
+obj-$(CONFIG_DMABUF_TESTDEV)	+= dma-buf-testdev.o
diff --git a/drivers/dma-buf/dma-buf-testdev.c b/drivers/dma-buf/dma-buf-testdev.c
new file mode 100644
index 0000000..dc3ed93
--- /dev/null
+++ b/drivers/dma-buf/dma-buf-testdev.c
@@ -0,0 +1,239 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DMA-BUF Dummy Importer Test Device
+ *
+ * Originally from TI DMA BUF contiguous buffer physical address
+ * user-space exporter
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *	Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/device.h>
+#include <linux/dma-buf.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <uapi/linux/dma-buf-testdev.h>
+
+#define DEVICE_NAME "dma-buf-testdev"
+
+struct dma_buf_testdev_priv {
+	struct miscdevice miscdev;
+};
+
+struct dma_buf_testdev_file {
+	struct device *dev;
+	struct dma_buf *dma_buf;
+	struct dma_buf_attachment *attachment;
+	struct sg_table *sgt;
+};
+
+static int dma_buf_testdev_open(struct inode *inode, struct file *file)
+{
+	struct miscdevice *miscdev = file->private_data;
+	struct device *dev = miscdev->this_device;
+	struct dma_buf_testdev_file *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	priv->dev = dev;
+	file->private_data = (void *)priv;
+
+	return 0;
+}
+
+static int dma_buf_testdev_release(struct inode *inode, struct file *file)
+{
+	struct dma_buf_testdev_file *priv = file->private_data;
+
+	if (priv->attachment && priv->sgt)
+		dma_buf_unmap_attachment(priv->attachment, priv->sgt,
+					 DMA_BIDIRECTIONAL);
+	if (priv->dma_buf && priv->attachment)
+		dma_buf_detach(priv->dma_buf, priv->attachment);
+	if (priv->dma_buf)
+		dma_buf_put(priv->dma_buf);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static int dma_buf_testdev_convert(struct dma_buf_testdev_file *priv, int fd,
+				   phys_addr_t *phys)
+{
+	struct device *dev = priv->dev;
+	struct dma_buf *dma_buf;
+	struct dma_buf_attachment *attachment;
+	struct sg_table *sgt;
+	dma_addr_t dma_addr;
+	int ret;
+
+	dma_buf = dma_buf_get(fd);
+	if (IS_ERR(dma_buf))
+		return PTR_ERR(dma_buf);
+
+	/* Attach as the parent device as it will have the correct DMA ops */
+	attachment = dma_buf_attach(dma_buf, dev->parent);
+	if (IS_ERR(attachment)) {
+		ret = PTR_ERR(attachment);
+		goto fail_put;
+	}
+
+	sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL);
+	if (IS_ERR(sgt)) {
+		ret = PTR_ERR(sgt);
+		goto fail_detach;
+	}
+
+	/* Without PAT only physically contiguous buffers can be supported */
+	if (sgt->orig_nents != 1) {
+		dev_err(dev, "DMA-BUF not contiguous\n");
+		ret = -EINVAL;
+		goto fail_unmap;
+	}
+
+	dma_addr = sg_dma_address(sgt->sgl);
+
+	*phys = dma_addr;
+
+	priv->dma_buf = dma_buf;
+	priv->attachment = attachment;
+	priv->sgt = sgt;
+
+	return 0;
+
+fail_unmap:
+	dma_buf_unmap_attachment(attachment, sgt, DMA_BIDIRECTIONAL);
+fail_detach:
+	dma_buf_detach(dma_buf, attachment);
+fail_put:
+	dma_buf_put(dma_buf);
+
+	return ret;
+}
+
+static long dma_buf_testdev_ioctl(struct file *file, unsigned int cmd,
+				  unsigned long arg)
+{
+	struct dma_buf_testdev_file *priv = file->private_data;
+
+	switch (cmd) {
+	case DMA_BUF_TESTDEV_IOC_CONVERT:
+	{
+		struct dma_buf_testdev_data data;
+		int ret;
+
+		/*
+		 * TODO: this should likely be properly serialized, but I
+		 * see no reason this file would ever need to be shared.
+		 */
+		/* one attachment per file */
+		if (priv->dma_buf)
+			return -EFAULT;
+
+		if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
+			return -EFAULT;
+
+		ret = dma_buf_testdev_convert(priv, data.fd, &data.phys);
+		if (ret)
+			return ret;
+
+		if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd)))
+			return -EFAULT;
+
+		break;
+	}
+	default:
+		return -ENOTTY;
+	}
+
+	return 0;
+}
+
+static const struct file_operations dma_buf_testdev_fops = {
+	.owner = THIS_MODULE,
+	.open = dma_buf_testdev_open,
+	.release = dma_buf_testdev_release,
+	.unlocked_ioctl = dma_buf_testdev_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= dma_buf_testdev_ioctl,
+#endif
+};
+
+static int dma_buf_testdev_probe(struct platform_device *pdev)
+{
+	struct dma_buf_testdev_priv *priv;
+	struct device *dev = &pdev->dev;
+	int err;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	dev_set_drvdata(dev, priv);
+
+	dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+
+	if (!dev->dma_parms) {
+		dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
+					      GFP_KERNEL);
+		if (!dev->dma_parms)
+			return -ENOMEM;
+	}
+	dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
+
+	priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+	priv->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s",
+					    DEVICE_NAME);
+	priv->miscdev.fops = &dma_buf_testdev_fops;
+	priv->miscdev.parent = dev;
+	err = misc_register(&priv->miscdev);
+	if (err) {
+		dev_err(dev,
+			"unable to register DMA-BUF to Phys misc device\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static int dma_buf_testdev_remove(struct platform_device *pdev)
+{
+	struct dma_buf_testdev_priv *priv = dev_get_drvdata(&pdev->dev);
+
+	misc_deregister(&priv->miscdev);
+
+	return 0;
+}
+
+static struct platform_driver dma_buf_testdev_driver = {
+	.probe = dma_buf_testdev_probe,
+	.remove = dma_buf_testdev_remove,
+	.driver = {
+		.name = "dma_buf_testdev",
+	}
+};
+module_platform_driver(dma_buf_testdev_driver);
+
+static int __init dma_buf_testdev_init(void)
+{
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_simple("dma_buf_testdev", -1, NULL, 0);
+
+	return PTR_ERR_OR_ZERO(pdev);
+}
+
+postcore_initcall(dma_buf_testdev_init);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("DMA-BUF Dummy Importer Test Device");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/dma-buf-testdev.h b/include/uapi/linux/dma-buf-testdev.h
new file mode 100644
index 0000000..b9706b8
--- /dev/null
+++ b/include/uapi/linux/dma-buf-testdev.h
@@ -0,0 +1,37 @@ 
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * DMA-BUF Dummy Importer Test Device
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *	Andrew F. Davis <afd@ti.com>
+ */
+
+#ifndef DMA_BUF_TESTDEV_H
+#define DMA_BUF_TESTDEV_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * struct dma_buf_testdev_data - metadata passed from userspace for conversion
+ * @fd:		DMA-BUF fd for conversion
+ * @phys:	populated with CPU physical address of DMA-BUF
+ */
+struct dma_buf_testdev_data {
+	__u32 fd;
+	__u64 phys;
+};
+
+#define DMA_BUF_TESTDEV_IOC_MAGIC 'D'
+
+/**
+ * DOC: DMA_BUF_TESTDEV_IOC_CONVERT - Convert DMA-BUF to physical address
+ *
+ * Takes a dma_buf_testdev_data struct containing a fd for a
+ * physicaly contigous buffer. Pins this buffer and populates
+ * phys field with the CPU physical address.
+ */
+#define DMA_BUF_TESTDEV_IOC_CONVERT _IOWR(DMA_BUF_TESTDEV_IOC_MAGIC, 0, \
+					  struct dma_buf_testdev_data)
+
+#endif /* DMA_BUF_PHYS_H */