[PATCH v2 07/12] iio: buffer-dma: Use DMABUFs instead of custom solution
Paul Cercueil
paul at crapouillou.net
Mon Mar 28 19:16:13 UTC 2022
Hi Jonathan,
Le lun., mars 28 2022 at 18:54:25 +0100, Jonathan Cameron
<jic23 at kernel.org> a écrit :
> On Mon, 7 Feb 2022 12:59:28 +0000
> Paul Cercueil <paul at 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 at 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.
That's OK, so am I. What do you call "streaming mappings"?
> 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?
Something like that. There are two different things; on both cases,
userspace needs to create a DMABUF with IIO_BUFFER_DMABUF_ALLOC_IOCTL,
and the backing memory is allocated with dma_alloc_coherent().
- For the userspace interface, you then have a "cpu access" IOCTL
(DMA_BUF_IOCTL_SYNC), that allows userspace to inform when it will
start/finish to process the buffer in user-space (which will
sync/invalidate the data cache if needed). A buffer can then be
enqueued for DMA processing (TX or RX) with the new
IIO_BUFFER_DMABUF_ENQUEUE_IOCTL.
- When the DMABUF created via the IIO core is sent to another driver
through the driver's custom DMABUF import function, this driver will
call dma_buf_attach(), which will call iio_buffer_dma_buf_map(). Since
it has to return a "struct sg_table *", this function then simply
creates a sgtable with one entry that points to the backing memory.
Note that I added the iio_buffer_dma_buf_map() / _unmap() functions
because the dma-buf core would WARN() if these were not provided. But
since this code doesn't yet support importing/exporting DMABUFs to
other drivers, these are never called, and I should probably just make
them return a ERR_PTR() unconditionally.
Cheers,
-Paul
> 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;
>> };
>>
>> /**
>
More information about the dri-devel
mailing list