diff mbox series

[v3,2/2] misc: sram: Add DMA-BUF Heap exporting of SRAM areas

Message ID 20240409120605.4138472-3-Pascal.FONTAIN@bachmann.info (mailing list archive)
State New, archived
Headers show
Series None | expand

Commit Message

Pascal FONTAIN April 9, 2024, 12:06 p.m. UTC
From: Andrew Davis <afd@ti.com>

This new export type exposes to userspace the SRAM area as a DMA-BUF
Heap,
this allows for allocations of DMA-BUFs that can be consumed by various
DMA-BUF supporting devices.

Signed-off-by: Andrew Davis <afd@ti.com>
Tested-by: Pascal Fontain <pascal.fontain@bachmann.info>
---
 drivers/misc/Kconfig         |   7 +
 drivers/misc/Makefile        |   1 +
 drivers/misc/sram-dma-heap.c | 246 +++++++++++++++++++++++++++++++++++
 drivers/misc/sram.c          |   6 +
 drivers/misc/sram.h          |  16 +++
 5 files changed, 276 insertions(+)
 create mode 100644 drivers/misc/sram-dma-heap.c

Comments

Christian Gmeiner April 19, 2024, 4:57 p.m. UTC | #1
Am Di., 9. Apr. 2024 um 14:14 Uhr schrieb Greg Kroah-Hartman
<gregkh@linuxfoundation.org>:
>
> On Tue, Apr 09, 2024 at 02:06:05PM +0200, Pascal FONTAIN wrote:
> > From: Andrew Davis <afd@ti.com>
> >
> > This new export type exposes to userspace the SRAM area as a DMA-BUF
> > Heap,
> > this allows for allocations of DMA-BUFs that can be consumed by various
> > DMA-BUF supporting devices.
> >
> > Signed-off-by: Andrew Davis <afd@ti.com>
> > Tested-by: Pascal Fontain <pascal.fontain@bachmann.info>
>
> When sending on a patch from someone else, you too must sign off on it
> as per our documenation.  Please read it and understand what you are
> agreeing to when you do that for a new version please.
>
> > ---
> >  drivers/misc/Kconfig         |   7 +
> >  drivers/misc/Makefile        |   1 +
> >  drivers/misc/sram-dma-heap.c | 246 +++++++++++++++++++++++++++++++++++
> >  drivers/misc/sram.c          |   6 +
> >  drivers/misc/sram.h          |  16 +++
> >  5 files changed, 276 insertions(+)
> >  create mode 100644 drivers/misc/sram-dma-heap.c
> >
> > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > index 9e4ad4d61f06..e6674e913168 100644
> > --- a/drivers/misc/Kconfig
> > +++ b/drivers/misc/Kconfig
> > @@ -448,6 +448,13 @@ config SRAM
> >  config SRAM_EXEC
> >       bool
> >
> > +config SRAM_DMA_HEAP
> > +     bool "Export on-chip SRAM pools using DMA-Heaps"
> > +     depends on DMABUF_HEAPS && SRAM
> > +     help
> > +       This driver allows the export of on-chip SRAM marked as both pool
> > +       and exportable to userspace using the DMA-Heaps interface.
>
> What will use this in userspace?
>

I could imagine a way it might be used.

Imagine a SoC like TI's AM64x series, where some cores (A53) run Linux
and others (R5F) are managed by remoteproc to
execute a RTOS. When it comes to efficiently exchanging large data
sets, the conventional method involves using rpmsg,
which has limitations due to message size and could potentially slow
down data transfer. However, an alternative
approach could be to allocate a sizable chunk of SRAM memory in
userspace. By utilizing memcpy() to copy data into
this memory, followed by a single rpmsg signal to notify the RTOS that
the data is ready, we can leverage the faster access
speed of SRAM compared to DDR from the remoteproc.
Greg Kroah-Hartman April 20, 2024, 6:36 a.m. UTC | #2
On Fri, Apr 19, 2024 at 06:57:47PM +0200, Christian Gmeiner wrote:
> Am Di., 9. Apr. 2024 um 14:14 Uhr schrieb Greg Kroah-Hartman
> <gregkh@linuxfoundation.org>:
> >
> > On Tue, Apr 09, 2024 at 02:06:05PM +0200, Pascal FONTAIN wrote:
> > > From: Andrew Davis <afd@ti.com>
> > >
> > > This new export type exposes to userspace the SRAM area as a DMA-BUF
> > > Heap,
> > > this allows for allocations of DMA-BUFs that can be consumed by various
> > > DMA-BUF supporting devices.
> > >
> > > Signed-off-by: Andrew Davis <afd@ti.com>
> > > Tested-by: Pascal Fontain <pascal.fontain@bachmann.info>
> >
> > When sending on a patch from someone else, you too must sign off on it
> > as per our documenation.  Please read it and understand what you are
> > agreeing to when you do that for a new version please.
> >
> > > ---
> > >  drivers/misc/Kconfig         |   7 +
> > >  drivers/misc/Makefile        |   1 +
> > >  drivers/misc/sram-dma-heap.c | 246 +++++++++++++++++++++++++++++++++++
> > >  drivers/misc/sram.c          |   6 +
> > >  drivers/misc/sram.h          |  16 +++
> > >  5 files changed, 276 insertions(+)
> > >  create mode 100644 drivers/misc/sram-dma-heap.c
> > >
> > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > > index 9e4ad4d61f06..e6674e913168 100644
> > > --- a/drivers/misc/Kconfig
> > > +++ b/drivers/misc/Kconfig
> > > @@ -448,6 +448,13 @@ config SRAM
> > >  config SRAM_EXEC
> > >       bool
> > >
> > > +config SRAM_DMA_HEAP
> > > +     bool "Export on-chip SRAM pools using DMA-Heaps"
> > > +     depends on DMABUF_HEAPS && SRAM
> > > +     help
> > > +       This driver allows the export of on-chip SRAM marked as both pool
> > > +       and exportable to userspace using the DMA-Heaps interface.
> >
> > What will use this in userspace?
> >
> 
> I could imagine a way it might be used.

This implies it is not needed because no one actually has actually used
it for anything, so why make this change?

> Imagine a SoC like TI's AM64x series, where some cores (A53) run Linux
> and others (R5F) are managed by remoteproc to
> execute a RTOS. When it comes to efficiently exchanging large data
> sets, the conventional method involves using rpmsg,
> which has limitations due to message size and could potentially slow
> down data transfer. However, an alternative
> approach could be to allocate a sizable chunk of SRAM memory in
> userspace. By utilizing memcpy() to copy data into
> this memory, followed by a single rpmsg signal to notify the RTOS that
> the data is ready, we can leverage the faster access
> speed of SRAM compared to DDR from the remoteproc.

Why is virtio not used instead?

thanks,

greg k-h
diff mbox series

Patch

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 9e4ad4d61f06..e6674e913168 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -448,6 +448,13 @@  config SRAM
 config SRAM_EXEC
 	bool
 
+config SRAM_DMA_HEAP
+	bool "Export on-chip SRAM pools using DMA-Heaps"
+	depends on DMABUF_HEAPS && SRAM
+	help
+	  This driver allows the export of on-chip SRAM marked as both pool
+	  and exportable to userspace using the DMA-Heaps interface.
+
 config DW_XDATA_PCIE
 	depends on PCI
 	tristate "Synopsys DesignWare xData PCIe driver"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index cdc6405584e3..63effdde5163 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -47,6 +47,7 @@  obj-$(CONFIG_VMWARE_VMCI)	+= vmw_vmci/
 obj-$(CONFIG_LATTICE_ECP3_CONFIG)	+= lattice-ecp3-config.o
 obj-$(CONFIG_SRAM)		+= sram.o
 obj-$(CONFIG_SRAM_EXEC)		+= sram-exec.o
+obj-$(CONFIG_SRAM_DMA_HEAP)	+= sram-dma-heap.o
 obj-$(CONFIG_GENWQE)		+= genwqe/
 obj-$(CONFIG_ECHO)		+= echo/
 obj-$(CONFIG_CXL_BASE)		+= cxl/
diff --git a/drivers/misc/sram-dma-heap.c b/drivers/misc/sram-dma-heap.c
new file mode 100644
index 000000000000..e5a0bafe8cb9
--- /dev/null
+++ b/drivers/misc/sram-dma-heap.c
@@ -0,0 +1,246 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SRAM DMA-Heap userspace exporter
+ *
+ * Copyright (C) 2019-2022 Texas Instruments Incorporated - https://www.ti.com/
+ *	Andrew Davis <afd@ti.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-heap.h>
+
+#include "sram.h"
+
+struct sram_dma_heap {
+	struct dma_heap *heap;
+	struct gen_pool *pool;
+};
+
+struct sram_dma_heap_buffer {
+	struct gen_pool *pool;
+	struct list_head attachments;
+	struct mutex attachments_lock;
+	unsigned long len;
+	void *vaddr;
+	phys_addr_t paddr;
+};
+
+struct dma_heap_attachment {
+	struct device *dev;
+	struct sg_table *table;
+	struct list_head list;
+};
+
+static int dma_heap_attach(struct dma_buf *dmabuf,
+			   struct dma_buf_attachment *attachment)
+{
+	struct sram_dma_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a;
+	struct sg_table *table;
+
+	a = kzalloc(sizeof(*a), GFP_KERNEL);
+	if (!a)
+		return -ENOMEM;
+
+	table = kmalloc(sizeof(*table), GFP_KERNEL);
+	if (!table) {
+		kfree(a);
+		return -ENOMEM;
+	}
+	if (sg_alloc_table(table, 1, GFP_KERNEL)) {
+		kfree(table);
+		kfree(a);
+		return -ENOMEM;
+	}
+	sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(buffer->paddr)), buffer->len, 0);
+
+	a->table = table;
+	a->dev = attachment->dev;
+	INIT_LIST_HEAD(&a->list);
+
+	attachment->priv = a;
+
+	mutex_lock(&buffer->attachments_lock);
+	list_add(&a->list, &buffer->attachments);
+	mutex_unlock(&buffer->attachments_lock);
+
+	return 0;
+}
+
+static void dma_heap_detatch(struct dma_buf *dmabuf,
+			     struct dma_buf_attachment *attachment)
+{
+	struct sram_dma_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a = attachment->priv;
+
+	mutex_lock(&buffer->attachments_lock);
+	list_del(&a->list);
+	mutex_unlock(&buffer->attachments_lock);
+
+	sg_free_table(a->table);
+	kfree(a->table);
+	kfree(a);
+}
+
+static struct sg_table *dma_heap_map_dma_buf(struct dma_buf_attachment *attachment,
+					     enum dma_data_direction direction)
+{
+	struct dma_heap_attachment *a = attachment->priv;
+	struct sg_table *table = a->table;
+
+	/*
+	 * As this heap is backed by uncached SRAM memory we do not need to
+	 * perform any sync operations on the buffer before allowing device
+	 * domain access. For this reason we use SKIP_CPU_SYNC and also do
+	 * not use or provide begin/end_cpu_access() dma-buf functions.
+	 */
+	if (!dma_map_sg_attrs(attachment->dev, table->sgl, table->nents,
+			      direction, DMA_ATTR_SKIP_CPU_SYNC))
+		return ERR_PTR(-ENOMEM);
+
+	return table;
+}
+
+static void dma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
+				   struct sg_table *table,
+				   enum dma_data_direction direction)
+{
+	dma_unmap_sg_attrs(attachment->dev, table->sgl, table->nents,
+			   direction, DMA_ATTR_SKIP_CPU_SYNC);
+}
+
+static void dma_heap_dma_buf_release(struct dma_buf *dmabuf)
+{
+	struct sram_dma_heap_buffer *buffer = dmabuf->priv;
+
+	gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len);
+	kfree(buffer);
+}
+
+static int dma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+	struct sram_dma_heap_buffer *buffer = dmabuf->priv;
+	int ret;
+
+	/* SRAM mappings are not cached */
+	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+	ret = vm_iomap_memory(vma, buffer->paddr, buffer->len);
+	if (ret)
+		pr_err("Could not map buffer to userspace\n");
+
+	return ret;
+}
+
+static int dma_heap_vmap(struct dma_buf *dmabuf, struct iosys_map *map)
+{
+	struct sram_dma_heap_buffer *buffer = dmabuf->priv;
+
+	iosys_map_set_vaddr(map, buffer->vaddr);
+
+	return 0;
+}
+
+static const struct dma_buf_ops sram_dma_heap_buf_ops = {
+	.attach = dma_heap_attach,
+	.detach = dma_heap_detatch,
+	.map_dma_buf = dma_heap_map_dma_buf,
+	.unmap_dma_buf = dma_heap_unmap_dma_buf,
+	.release = dma_heap_dma_buf_release,
+	.mmap = dma_heap_mmap,
+	.vmap = dma_heap_vmap,
+};
+
+static struct dma_buf *sram_dma_heap_allocate(struct dma_heap *heap,
+					      unsigned long len,
+					      unsigned long fd_flags,
+					      unsigned long heap_flags)
+{
+	struct sram_dma_heap *sram_dma_heap = dma_heap_get_drvdata(heap);
+	struct sram_dma_heap_buffer *buffer;
+
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+	struct dma_buf *dmabuf;
+	int ret;
+
+	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+	if (!buffer)
+		return ERR_PTR(-ENOMEM);
+	buffer->pool = sram_dma_heap->pool;
+	INIT_LIST_HEAD(&buffer->attachments);
+	mutex_init(&buffer->attachments_lock);
+	buffer->len = len;
+
+	buffer->vaddr = (void *)gen_pool_alloc(buffer->pool, buffer->len);
+	if (!buffer->vaddr) {
+		ret = -ENOMEM;
+		goto free_buffer;
+	}
+
+	buffer->paddr = gen_pool_virt_to_phys(buffer->pool, (unsigned long)buffer->vaddr);
+	if (buffer->paddr == -1) {
+		ret = -ENOMEM;
+		goto free_pool;
+	}
+
+	/* create the dmabuf */
+	exp_info.exp_name = dma_heap_get_name(heap);
+	exp_info.ops = &sram_dma_heap_buf_ops;
+	exp_info.size = buffer->len;
+	exp_info.flags = fd_flags;
+	exp_info.priv = buffer;
+	dmabuf = dma_buf_export(&exp_info);
+	if (IS_ERR(dmabuf)) {
+		ret = PTR_ERR(dmabuf);
+		goto free_pool;
+	}
+
+	return dmabuf;
+
+free_pool:
+	gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len);
+free_buffer:
+	kfree(buffer);
+
+	return ERR_PTR(ret);
+}
+
+static struct dma_heap_ops sram_dma_heap_ops = {
+	.allocate = sram_dma_heap_allocate,
+};
+
+int sram_add_dma_heap(struct sram_dev *sram,
+		      struct sram_reserve *block,
+		      phys_addr_t start,
+		      struct sram_partition *part)
+{
+	struct sram_dma_heap *sram_dma_heap;
+	struct dma_heap_export_info exp_info;
+
+	dev_info(sram->dev, "Exporting SRAM Heap '%s'\n", block->label);
+
+	sram_dma_heap = kzalloc(sizeof(*sram_dma_heap), GFP_KERNEL);
+	if (!sram_dma_heap)
+		return -ENOMEM;
+	sram_dma_heap->pool = part->pool;
+
+	exp_info.name = kasprintf(GFP_KERNEL, "sram_%s", block->label);
+	exp_info.ops = &sram_dma_heap_ops;
+	exp_info.priv = sram_dma_heap;
+	sram_dma_heap->heap = dma_heap_add(&exp_info);
+	if (IS_ERR(sram_dma_heap->heap)) {
+		int ret = PTR_ERR(sram_dma_heap->heap);
+
+		kfree(sram_dma_heap);
+		return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c
index 632f90d9bcea..643c77598beb 100644
--- a/drivers/misc/sram.c
+++ b/drivers/misc/sram.c
@@ -120,6 +120,12 @@  static int sram_add_partition(struct sram_dev *sram, struct sram_reserve *block,
 		ret = sram_add_pool(sram, block, start, part);
 		if (ret)
 			return ret;
+
+		if (block->export) {
+			ret = sram_add_dma_heap(sram, block, start, part);
+			if (ret)
+				return ret;
+		}
 	}
 	if (block->export) {
 		ret = sram_add_export(sram, block, start, part);
diff --git a/drivers/misc/sram.h b/drivers/misc/sram.h
index 397205b8bf6f..062bdd25fa06 100644
--- a/drivers/misc/sram.h
+++ b/drivers/misc/sram.h
@@ -60,4 +60,20 @@  static inline int sram_add_protect_exec(struct sram_partition *part)
 	return -ENODEV;
 }
 #endif /* CONFIG_SRAM_EXEC */
+
+#ifdef CONFIG_SRAM_DMA_HEAP
+int sram_add_dma_heap(struct sram_dev *sram,
+		      struct sram_reserve *block,
+		      phys_addr_t start,
+		      struct sram_partition *part);
+#else
+static inline int sram_add_dma_heap(struct sram_dev *sram,
+				    struct sram_reserve *block,
+				    phys_addr_t start,
+				    struct sram_partition *part)
+{
+	return 0;
+}
+#endif /* CONFIG_SRAM_DMA_HEAP */
+
 #endif /* __SRAM_H */