Re: [PATCH v2 07/12] iio: buffer-dma: Use DMABUFs instead of custom solution

Jonathan Cameron
2022-03-28T17:46:57Z

On Mon, 7 Feb 2022 12:59:28 +0000
Paul Cercueil <paul@crapouillou.net> wrote:

> Enhance the current fileio code by using DMABUF objects instead of
> custom buffers.
>
> This adds more code than it removes, but:
> - a lot of the complexity can be dropped, e.g. custom kref and
> iio_buffer_block_put_atomic() are not needed anymore;
> - it will be much easier to introduce an API to export these DMABUF
> objects to userspace in a following patch.
>
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Hi Paul,

I'm a bit rusty on dma mappings, but you seem to have
a mixture of streaming and coherent mappings going on in here.

Is it the case that the current code is using the coherent mappings
and a potential 'other user' of the dma buffer might need
streaming mappings?

Jonathan

> ---
> drivers/iio/buffer/industrialio-buffer-dma.c | 192 ++++++++++++-------
> include/linux/iio/buffer-dma.h | 8 +-
> 2 files changed, 122 insertions(+), 78 deletions(-)
>
> diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
> index 15ea7bc3ac08..54e6000cd2ee 100644
> --- a/drivers/iio/buffer/industrialio-buffer-dma.c
> +++ b/drivers/iio/buffer/industrialio-buffer-dma.c
> @@ -14,6 +14,7 @@
> #include <linux/poll.h>
> #include <linux/iio/buffer_impl.h>
> #include <linux/iio/buffer-dma.h>
> +#include <linux/dma-buf.h>
> #include <linux/dma-mapping.h>
> #include <linux/sizes.h>
>
> @@ -90,103 +91,145 @@
> * callback is called from within the custom callback.
> */
>
> -static void iio_buffer_block_release(struct kref *kref)
> -{
> - struct iio_dma_buffer_block *block = container_of(kref,
> - struct iio_dma_buffer_block, kref);
> -
> - WARN_ON(block->state != IIO_BLOCK_STATE_DEAD);
> -
> - dma_free_coherent(block->queue->dev, PAGE_ALIGN(block->size),
> - block->vaddr, block->phys_addr);
> -
> - iio_buffer_put(&block->queue->buffer);
> - kfree(block);
> -}
> -
> -static void iio_buffer_block_get(struct iio_dma_buffer_block *block)
> -{
> - kref_get(&block->kref);
> -}
> -
> -static void iio_buffer_block_put(struct iio_dma_buffer_block *block)
> -{
> - kref_put(&block->kref, iio_buffer_block_release);
> -}
> -
> -/*
> - * dma_free_coherent can sleep, hence we need to take some special care to be
> - * able to drop a reference from an atomic context.
> - */
> -static LIST_HEAD(iio_dma_buffer_dead_blocks);
> -static DEFINE_SPINLOCK(iio_dma_buffer_dead_blocks_lock);
> -
> -static void iio_dma_buffer_cleanup_worker(struct work_struct *work)
> -{
> - struct iio_dma_buffer_block *block, *_block;
> - LIST_HEAD(block_list);
> -
> - spin_lock_irq(&iio_dma_buffer_dead_blocks_lock);
> - list_splice_tail_init(&iio_dma_buffer_dead_blocks, &block_list);
> - spin_unlock_irq(&iio_dma_buffer_dead_blocks_lock);
> -
> - list_for_each_entry_safe(block, _block, &block_list, head)
> - iio_buffer_block_release(&block->kref);
> -}
> -static DECLARE_WORK(iio_dma_buffer_cleanup_work, iio_dma_buffer_cleanup_worker);
> -
> -static void iio_buffer_block_release_atomic(struct kref *kref)
> -{
> +struct iio_buffer_dma_buf_attachment {
> + struct scatterlist sgl;
> + struct sg_table sg_table;
> struct iio_dma_buffer_block *block;
> - unsigned long flags;
> -
> - block = container_of(kref, struct iio_dma_buffer_block, kref);
> -
> - spin_lock_irqsave(&iio_dma_buffer_dead_blocks_lock, flags);
> - list_add_tail(&block->head, &iio_dma_buffer_dead_blocks);
> - spin_unlock_irqrestore(&iio_dma_buffer_dead_blocks_lock, flags);
> -
> - schedule_work(&iio_dma_buffer_cleanup_work);
> -}
> -
> -/*
> - * Version of iio_buffer_block_put() that can be called from atomic context
> - */
> -static void iio_buffer_block_put_atomic(struct iio_dma_buffer_block *block)
> -{
> - kref_put(&block->kref, iio_buffer_block_release_atomic);
> -}
> +};
>
> static struct iio_dma_buffer_queue *iio_buffer_to_queue(struct iio_buffer *buf)
> {
> return container_of(buf, struct iio_dma_buffer_queue, buffer);
> }
>
> +static struct iio_buffer_dma_buf_attachment *
> +to_iio_buffer_dma_buf_attachment(struct sg_table *table)
> +{
> + return container_of(table, struct iio_buffer_dma_buf_attachment, sg_table);
> +}
> +
> +static void iio_buffer_block_get(struct iio_dma_buffer_block *block)
> +{
> + get_dma_buf(block->dmabuf);
> +}
> +
> +static void iio_buffer_block_put(struct iio_dma_buffer_block *block)
> +{
> + dma_buf_put(block->dmabuf);
> +}
> +
> +static int iio_buffer_dma_buf_attach(struct dma_buf *dbuf,
> + struct dma_buf_attachment *at)
> +{
> + at->priv = dbuf->priv;
> +
> + return 0;
> +}
> +
> +static struct sg_table *iio_buffer_dma_buf_map(struct dma_buf_attachment *at,
> + enum dma_data_direction dma_dir)
> +{
> + struct iio_dma_buffer_block *block = at->priv;
> + struct iio_buffer_dma_buf_attachment *dba;
> + int ret;
> +
> + dba = kzalloc(sizeof(*dba), GFP_KERNEL);
> + if (!dba)
> + return ERR_PTR(-ENOMEM);
> +
> + sg_init_one(&dba->sgl, block->vaddr, PAGE_ALIGN(block->size));
> + dba->sg_table.sgl = &dba->sgl;
> + dba->sg_table.nents = 1;
> + dba->block = block;
> +
> + ret = dma_map_sgtable(at->dev, &dba->sg_table, dma_dir, 0);
> + if (ret) {
> + kfree(dba);
> + return ERR_PTR(ret);
> + }
> +
> + return &dba->sg_table;
> +}
> +
> +static void iio_buffer_dma_buf_unmap(struct dma_buf_attachment *at,
> + struct sg_table *sg_table,
> + enum dma_data_direction dma_dir)
> +{
> + struct iio_buffer_dma_buf_attachment *dba =
> + to_iio_buffer_dma_buf_attachment(sg_table);
> +
> + dma_unmap_sgtable(at->dev, &dba->sg_table, dma_dir, 0);
> + kfree(dba);
> +}
> +
> +static void iio_buffer_dma_buf_release(struct dma_buf *dbuf)
> +{
> + struct iio_dma_buffer_block *block = dbuf->priv;
> + struct iio_dma_buffer_queue *queue = block->queue;
> +
> + WARN_ON(block->state != IIO_BLOCK_STATE_DEAD);
> +
> + mutex_lock(&queue->lock);
> +
> + dma_free_coherent(queue->dev, PAGE_ALIGN(block->size),
> + block->vaddr, block->phys_addr);
> + kfree(block);
> +
> + mutex_unlock(&queue->lock);
> + iio_buffer_put(&queue->buffer);
> +}
> +
> +static const struct dma_buf_ops iio_dma_buffer_dmabuf_ops = {
> + .attach = iio_buffer_dma_buf_attach,
> + .map_dma_buf = iio_buffer_dma_buf_map,
> + .unmap_dma_buf = iio_buffer_dma_buf_unmap,
> + .release = iio_buffer_dma_buf_release,
> +};
> +
> static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block(
> struct iio_dma_buffer_queue *queue, size_t size)
> {
> struct iio_dma_buffer_block *block;
> + DEFINE_DMA_BUF_EXPORT_INFO(einfo);
> + struct dma_buf *dmabuf;
> + int err = -ENOMEM;
>
> block = kzalloc(sizeof(*block), GFP_KERNEL);
> if (!block)
> - return NULL;
> + return ERR_PTR(err);
>
> block->vaddr = dma_alloc_coherent(queue->dev, PAGE_ALIGN(size),
> &block->phys_addr, GFP_KERNEL);
> - if (!block->vaddr) {
> - kfree(block);
> - return NULL;
> + if (!block->vaddr)
> + goto err_free_block;
> +
> + einfo.ops = &iio_dma_buffer_dmabuf_ops;
> + einfo.size = PAGE_ALIGN(size);
> + einfo.priv = block;
> + einfo.flags = O_RDWR;
> +
> + dmabuf = dma_buf_export(&einfo);
> + if (IS_ERR(dmabuf)) {
> + err = PTR_ERR(dmabuf);
> + goto err_free_dma;
> }
>
> + block->dmabuf = dmabuf;
> block->size = size;
> block->state = IIO_BLOCK_STATE_DONE;
> block->queue = queue;
> INIT_LIST_HEAD(&block->head);
> - kref_init(&block->kref);
>
> iio_buffer_get(&queue->buffer);
>
> return block;
> +
> +err_free_dma:
> + dma_free_coherent(queue->dev, PAGE_ALIGN(size),
> + block->vaddr, block->phys_addr);
> +err_free_block:
> + kfree(block);
> + return ERR_PTR(err);
> }
>
> static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
> @@ -223,7 +266,7 @@ void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
> _iio_dma_buffer_block_done(block);
> spin_unlock_irqrestore(&queue->list_lock, flags);
>
> - iio_buffer_block_put_atomic(block);
> + iio_buffer_block_put(block);
> iio_dma_buffer_queue_wake(queue);
> }
> EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done);
> @@ -249,7 +292,8 @@ void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
> list_del(&block->head);
> block->bytes_used = 0;
> _iio_dma_buffer_block_done(block);
> - iio_buffer_block_put_atomic(block);
> +
> + iio_buffer_block_put(block);
> }
> spin_unlock_irqrestore(&queue->list_lock, flags);
>
> @@ -340,8 +384,8 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
>
> if (!block) {
> block = iio_dma_buffer_alloc_block(queue, size);
> - if (!block) {
> - ret = -ENOMEM;
> + if (IS_ERR(block)) {
> + ret = PTR_ERR(block);
> goto out_unlock;
> }
> queue->fileio.blocks[i] = block;
> diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h
> index 490b93f76fa8..6b3fa7d2124b 100644
> --- a/include/linux/iio/buffer-dma.h
> +++ b/include/linux/iio/buffer-dma.h
> @@ -8,7 +8,6 @@
> #define __INDUSTRIALIO_DMA_BUFFER_H__
>
> #include <linux/list.h>
> -#include <linux/kref.h>
> #include <linux/spinlock.h>
> #include <linux/mutex.h>
> #include <linux/iio/buffer_impl.h>
> @@ -16,6 +15,7 @@
> struct iio_dma_buffer_queue;
> struct iio_dma_buffer_ops;
> struct device;
> +struct dma_buf;
>
> /**
> * enum iio_block_state - State of a struct iio_dma_buffer_block
> @@ -39,8 +39,8 @@ enum iio_block_state {
> * @vaddr: Virutal address of the blocks memory
> * @phys_addr: Physical address of the blocks memory
> * @queue: Parent DMA buffer queue
> - * @kref: kref used to manage the lifetime of block
> * @state: Current state of the block
> + * @dmabuf: Underlying DMABUF object
> */
> struct iio_dma_buffer_block {
> /* May only be accessed by the owner of the block */
> @@ -56,13 +56,13 @@ struct iio_dma_buffer_block {
> size_t size;
> struct iio_dma_buffer_queue *queue;
>
> - /* Must not be accessed outside the core. */
> - struct kref kref;
> /*
> * Must not be accessed outside the core. Access needs to hold
> * queue->list_lock if the block is not owned by the core.
> */
> enum iio_block_state state;
> +
> + struct dma_buf *dmabuf;
> };
>
> /**