On Mon,  7 Feb 2022 12:59:28 +0000
Paul Cercueil <p...@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 <p...@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;
>  };
>  
>  /**

Reply via email to