Re: [PATCH v5 2/5] dma-buf: heaps: Add heap helpers
From: Brian Starkey
Date: Fri Jun 07 2019 - 07:22:23 EST
Hi John,
On Fri, Jun 07, 2019 at 03:07:16AM +0000, John Stultz wrote:
> Add generic helper dmabuf ops for dma heaps, so we can reduce
> the amount of duplicative code for the exported dmabufs.
>
> This code is an evolution of the Android ION implementation, so
> thanks to its original authors and maintainters:
> Rebecca Schultz Zavin, Colin Cross, Laura Abbott, and others!
>
> Cc: Laura Abbott <labbott@xxxxxxxxxx>
> Cc: Benjamin Gaignard <benjamin.gaignard@xxxxxxxxxx>
> Cc: Sumit Semwal <sumit.semwal@xxxxxxxxxx>
> Cc: Liam Mark <lmark@xxxxxxxxxxxxxx>
> Cc: Pratik Patel <pratikp@xxxxxxxxxxxxxx>
> Cc: Brian Starkey <Brian.Starkey@xxxxxxx>
> Cc: Vincent Donnefort <Vincent.Donnefort@xxxxxxx>
> Cc: Sudipto Paul <Sudipto.Paul@xxxxxxx>
> Cc: Andrew F. Davis <afd@xxxxxx>
> Cc: Christoph Hellwig <hch@xxxxxxxxxxxxx>
> Cc: Chenbo Feng <fengc@xxxxxxxxxx>
> Cc: Alistair Strachan <astrachan@xxxxxxxxxx>
> Cc: dri-devel@xxxxxxxxxxxxxxxxxxxxx
> Reviewed-by: Benjamin Gaignard <benjamin.gaignard@xxxxxxxxxx>
> Signed-off-by: John Stultz <john.stultz@xxxxxxxxxx>
> Change-Id: I48d43656e7783f266d877e363116b5187639f996
> ---
> v2:
> * Removed cache management performance hack that I had
> accidentally folded in.
> * Removed stats code that was in helpers
> * Lots of checkpatch cleanups
> v3:
> * Uninline INIT_HEAP_HELPER_BUFFER (suggested by Christoph)
> * Switch to WARN on buffer destroy failure (suggested by Brian)
> * buffer->kmap_cnt decrementing cleanup (suggested by Christoph)
> * Extra buffer->vaddr checking in dma_heap_dma_buf_kmap
> (suggested by Brian)
> * Switch to_helper_buffer from macro to inline function
> (suggested by Benjamin)
> * Rename kmap->vmap (folded in from Andrew)
> * Use vmap for vmapping - not begin_cpu_access (folded in from
> Andrew)
> * Drop kmap for now, as its optional (folded in from Andrew)
> * Fold dma_heap_map_user into the single caller (foled in from
> Andrew)
> * Folded in patch from Andrew to track page list per heap not
> sglist, which simplifies the tracking logic
> v4:
> * Moved dma-heap.h change out to previous patch
> ---
> drivers/dma-buf/Makefile | 1 +
> drivers/dma-buf/heaps/Makefile | 2 +
> drivers/dma-buf/heaps/heap-helpers.c | 261 +++++++++++++++++++++++++++
> drivers/dma-buf/heaps/heap-helpers.h | 55 ++++++
> 4 files changed, 319 insertions(+)
> create mode 100644 drivers/dma-buf/heaps/Makefile
> create mode 100644 drivers/dma-buf/heaps/heap-helpers.c
> create mode 100644 drivers/dma-buf/heaps/heap-helpers.h
>
> diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
> index 1cb3dd104825..e3e3dca29e46 100644
> --- a/drivers/dma-buf/Makefile
> +++ b/drivers/dma-buf/Makefile
> @@ -1,6 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0-only
> obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
> reservation.o seqno-fence.o
> +obj-$(CONFIG_DMABUF_HEAPS) += heaps/
> obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
> obj-$(CONFIG_SYNC_FILE) += sync_file.o
> obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
> diff --git a/drivers/dma-buf/heaps/Makefile b/drivers/dma-buf/heaps/Makefile
> new file mode 100644
> index 000000000000..de49898112db
> --- /dev/null
> +++ b/drivers/dma-buf/heaps/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-y += heap-helpers.o
> diff --git a/drivers/dma-buf/heaps/heap-helpers.c b/drivers/dma-buf/heaps/heap-helpers.c
> new file mode 100644
> index 000000000000..00cbdbbb97e5
> --- /dev/null
> +++ b/drivers/dma-buf/heaps/heap-helpers.c
> @@ -0,0 +1,261 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/device.h>
> +#include <linux/dma-buf.h>
> +#include <linux/err.h>
> +#include <linux/idr.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <uapi/linux/dma-heap.h>
> +
> +#include "heap-helpers.h"
> +
> +void INIT_HEAP_HELPER_BUFFER(struct heap_helper_buffer *buffer,
> + void (*free)(struct heap_helper_buffer *))
> +{
> + buffer->private_flags = 0;
> + buffer->priv_virt = NULL;
> + mutex_init(&buffer->lock);
> + buffer->vmap_cnt = 0;
> + buffer->vaddr = NULL;
> + INIT_LIST_HEAD(&buffer->attachments);
> + buffer->free = free;
Should pagecount and pages be initialised here too? I'm not sure what
the metric for picking which members are/aren't initialised is.
> +}
> +
extra newline
> +
> +static void *dma_heap_map_kernel(struct heap_helper_buffer *buffer)
> +{
> + void *vaddr;
> +
> + vaddr = vmap(buffer->pages, buffer->pagecount, VM_MAP, PAGE_KERNEL);
> + if (!vaddr)
> + return ERR_PTR(-ENOMEM);
> +
> + return vaddr;
> +}
> +
> +void dma_heap_buffer_destroy(struct dma_heap_buffer *heap_buffer)
can be static
> +{
> + struct heap_helper_buffer *buffer = to_helper_buffer(heap_buffer);
> +
> + if (buffer->vmap_cnt > 0) {
> + WARN("%s: buffer still mapped in the kernel\n",
> + __func__);
> + vunmap(buffer->vaddr);
> + }
> +
> + buffer->free(buffer);
> +}
> +
> +static void *dma_heap_buffer_vmap_get(struct dma_heap_buffer *heap_buffer)
> +{
> + struct heap_helper_buffer *buffer = to_helper_buffer(heap_buffer);
> + void *vaddr;
> +
> + if (buffer->vmap_cnt) {
> + buffer->vmap_cnt++;
> + return buffer->vaddr;
> + }
> + vaddr = dma_heap_map_kernel(buffer);
> + if (WARN_ONCE(!vaddr,
> + "heap->ops->map_kernel should return ERR_PTR on error"))
> + return ERR_PTR(-EINVAL);
> + if (IS_ERR(vaddr))
> + return vaddr;
> + buffer->vaddr = vaddr;
> + buffer->vmap_cnt++;
> + return vaddr;
> +}
> +
> +static void dma_heap_buffer_vmap_put(struct dma_heap_buffer *heap_buffer)
> +{
> + struct heap_helper_buffer *buffer = to_helper_buffer(heap_buffer);
> +
> + if (!--buffer->vmap_cnt) {
> + vunmap(buffer->vaddr);
> + buffer->vaddr = NULL;
> + }
> +}
> +
> +struct dma_heaps_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 dma_heaps_attachment *a;
> + struct dma_heap_buffer *heap_buffer = dmabuf->priv;
> + struct heap_helper_buffer *buffer = to_helper_buffer(heap_buffer);
> + int ret;
> +
> + a = kzalloc(sizeof(*a), GFP_KERNEL);
> + if (!a)
> + return -ENOMEM;
> +
> + ret = sg_alloc_table_from_pages(&a->table, buffer->pages,
> + buffer->pagecount, 0,
> + buffer->pagecount << PAGE_SHIFT,
> + GFP_KERNEL);
> + if (ret) {
> + kfree(a);
> + return ret;
> + }
> +
> + a->dev = attachment->dev;
> + INIT_LIST_HEAD(&a->list);
> +
> + attachment->priv = a;
> +
> + mutex_lock(&buffer->lock);
> + list_add(&a->list, &buffer->attachments);
> + mutex_unlock(&buffer->lock);
> +
> + return 0;
> +}
> +
> +static void dma_heap_detatch(struct dma_buf *dmabuf,
> + struct dma_buf_attachment *attachment)
s/detatch/detach/
> +{
> + struct dma_heaps_attachment *a = attachment->priv;
> + struct dma_heap_buffer *heap_buffer = dmabuf->priv;
> + struct heap_helper_buffer *buffer = to_helper_buffer(heap_buffer);
> +
> + mutex_lock(&buffer->lock);
> + list_del(&a->list);
> + mutex_unlock(&buffer->lock);
> +
> + sg_free_table(&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_heaps_attachment *a = attachment->priv;
> + struct sg_table *table;
> +
> + table = &a->table;
> +
> + if (!dma_map_sg(attachment->dev, table->sgl, table->nents,
> + direction))
> + table = 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(attachment->dev, table->sgl, table->nents, direction);
> +}
> +
> +static vm_fault_t dma_heap_vm_fault(struct vm_fault *vmf)
> +{
> + struct vm_area_struct *vma = vmf->vma;
> + struct heap_helper_buffer *buffer = vma->vm_private_data;
> +
> + vmf->page = buffer->pages[vmf->pgoff];
> + get_page(vmf->page);
> +
> + return 0;
> +}
> +
> +static const struct vm_operations_struct dma_heap_vm_ops = {
> + .fault = dma_heap_vm_fault,
> +};
> +
> +static int dma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
> +{
> + struct dma_heap_buffer *heap_buffer = dmabuf->priv;
> + struct heap_helper_buffer *buffer = to_helper_buffer(heap_buffer);
> +
> + if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
> + return -EINVAL;
> +
> + vma->vm_ops = &dma_heap_vm_ops;
> + vma->vm_private_data = buffer;
> +
> + return 0;
> +}
> +
> +static void dma_heap_dma_buf_release(struct dma_buf *dmabuf)
> +{
> + struct dma_heap_buffer *buffer = dmabuf->priv;
> +
> + dma_heap_buffer_destroy(buffer);
> +}
> +
> +static int dma_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
> + enum dma_data_direction direction)
> +{
> + struct dma_heap_buffer *heap_buffer = dmabuf->priv;
> + struct heap_helper_buffer *buffer = to_helper_buffer(heap_buffer);
> + struct dma_heaps_attachment *a;
> + int ret = 0;
> +
> + mutex_lock(&buffer->lock);
> + list_for_each_entry(a, &buffer->attachments, list) {
> + dma_sync_sg_for_cpu(a->dev, a->table.sgl, a->table.nents,
> + direction);
> + }
> + mutex_unlock(&buffer->lock);
> +
> + return ret;
> +}
> +
> +static int dma_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
> + enum dma_data_direction direction)
> +{
> + struct dma_heap_buffer *heap_buffer = dmabuf->priv;
> + struct heap_helper_buffer *buffer = to_helper_buffer(heap_buffer);
> + struct dma_heaps_attachment *a;
> +
> + mutex_lock(&buffer->lock);
> + list_for_each_entry(a, &buffer->attachments, list) {
> + dma_sync_sg_for_device(a->dev, a->table.sgl, a->table.nents,
> + direction);
> + }
> + mutex_unlock(&buffer->lock);
> +
> + return 0;
> +}
> +
> +void *dma_heap_dma_buf_vmap(struct dma_buf *dmabuf)
can be static
> +{
> + struct dma_heap_buffer *heap_buffer = dmabuf->priv;
> + struct heap_helper_buffer *buffer = to_helper_buffer(heap_buffer);
> + void *vaddr;
> +
> + mutex_lock(&buffer->lock);
> + vaddr = dma_heap_buffer_vmap_get(heap_buffer);
> + mutex_unlock(&buffer->lock);
> +
> + return vaddr;
> +}
> +
> +void dma_heap_dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
can be static
> +{
> + struct dma_heap_buffer *heap_buffer = dmabuf->priv;
> + struct heap_helper_buffer *buffer = to_helper_buffer(heap_buffer);
> +
> + mutex_lock(&buffer->lock);
> + dma_heap_buffer_vmap_put(heap_buffer);
> + mutex_unlock(&buffer->lock);
> +}
> +
> +const struct dma_buf_ops heap_helper_ops = {
> + .map_dma_buf = dma_heap_map_dma_buf,
> + .unmap_dma_buf = dma_heap_unmap_dma_buf,
> + .mmap = dma_heap_mmap,
> + .release = dma_heap_dma_buf_release,
> + .attach = dma_heap_attach,
> + .detach = dma_heap_detatch,
> + .begin_cpu_access = dma_heap_dma_buf_begin_cpu_access,
> + .end_cpu_access = dma_heap_dma_buf_end_cpu_access,
> + .vmap = dma_heap_dma_buf_vmap,
> + .vunmap = dma_heap_dma_buf_vunmap,
> +};
> diff --git a/drivers/dma-buf/heaps/heap-helpers.h b/drivers/dma-buf/heaps/heap-helpers.h
> new file mode 100644
> index 000000000000..a17502dc22e3
> --- /dev/null
> +++ b/drivers/dma-buf/heaps/heap-helpers.h
> @@ -0,0 +1,55 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * DMABUF Heaps helper code
> + *
> + * Copyright (C) 2011 Google, Inc.
> + * Copyright (C) 2019 Linaro Ltd.
> + */
> +
> +#ifndef _HEAP_HELPERS_H
> +#define _HEAP_HELPERS_H
> +
> +#include <linux/dma-heap.h>
> +#include <linux/list.h>
> +
> +/**
> + * struct dma_heap_buffer - metadata for a particular buffer
> + * @heap: back pointer to the heap the buffer came from
> + * @dmabuf: backing dma-buf for this buffer
> + * @size: size of the buffer
> + * @flags: buffer specific flags
> + */
> +struct dma_heap_buffer {
> + struct dma_heap *heap;
> + struct dma_buf *dmabuf;
> + size_t size;
> + unsigned long flags;
> +};
> +
> +struct heap_helper_buffer {
> + struct dma_heap_buffer heap_buffer;
> +
> + unsigned long private_flags;
> + void *priv_virt;
> + struct mutex lock;
> + int vmap_cnt;
> + void *vaddr;
> + pgoff_t pagecount;
> + struct page **pages;
> + struct list_head attachments;
> +
> + void (*free)(struct heap_helper_buffer *buffer);
> +
extra newline
With the statics and the detatch typo fixed:
Reviewed-by: Brian Starkey <brian.starkey@xxxxxxx>
> +};
> +
> +static inline struct heap_helper_buffer *to_helper_buffer(
> + struct dma_heap_buffer *h)
> +{
> + return container_of(h, struct heap_helper_buffer, heap_buffer);
> +}
> +
> +void INIT_HEAP_HELPER_BUFFER(struct heap_helper_buffer *buffer,
> + void (*free)(struct heap_helper_buffer *));
> +extern const struct dma_buf_ops heap_helper_ops;
> +
> +#endif /* _HEAP_HELPERS_H */
> --
> 2.17.1
>