[PATCH v7 3/6] iio: core: Add new DMABUF interface infrastructure
Paul Cercueil
paul at crapouillou.net
Mon Mar 4 13:59:44 UTC 2024
Hi Christian,
Le lundi 04 mars 2024 à 13:44 +0100, Christian König a écrit :
> Am 23.02.24 um 13:14 schrieb Nuno Sa:
> > From: Paul Cercueil <paul at crapouillou.net>
> >
> > Add the necessary infrastructure to the IIO core to support a new
> > optional DMABUF based interface.
> >
> > With this new interface, DMABUF objects (externally created) can be
> > attached to a IIO buffer, and subsequently used for data transfer.
> >
> > A userspace application can then use this interface to share DMABUF
> > objects between several interfaces, allowing it to transfer data in
> > a
> > zero-copy fashion, for instance between IIO and the USB stack.
> >
> > The userspace application can also memory-map the DMABUF objects,
> > and
> > access the sample data directly. The advantage of doing this vs.
> > the
> > read() interface is that it avoids an extra copy of the data
> > between the
> > kernel and userspace. This is particularly userful for high-speed
> > devices which produce several megabytes or even gigabytes of data
> > per
> > second.
> >
> > As part of the interface, 3 new IOCTLs have been added:
> >
> > IIO_BUFFER_DMABUF_ATTACH_IOCTL(int fd):
> > Attach the DMABUF object identified by the given file descriptor
> > to the
> > buffer.
> >
> > IIO_BUFFER_DMABUF_DETACH_IOCTL(int fd):
> > Detach the DMABUF object identified by the given file descriptor
> > from
> > the buffer. Note that closing the IIO buffer's file descriptor
> > will
> > automatically detach all previously attached DMABUF objects.
> >
> > IIO_BUFFER_DMABUF_ENQUEUE_IOCTL(struct iio_dmabuf *):
> > Request a data transfer to/from the given DMABUF object. Its file
> > descriptor, as well as the transfer size and flags are provided
> > in the
> > "iio_dmabuf" structure.
> >
> > These three IOCTLs have to be performed on the IIO buffer's file
> > descriptor, obtained using the IIO_BUFFER_GET_FD_IOCTL() ioctl.
>
> A few nit picks and one bug below, apart from that looks good to me.
>
> >
> > Signed-off-by: Paul Cercueil <paul at crapouillou.net>
> > Signed-off-by: Nuno Sa <nuno.sa at analog.com>
> > ---
> > drivers/iio/industrialio-buffer.c | 462
> > ++++++++++++++++++++++++++++++++++++++
> > include/linux/iio/buffer_impl.h | 33 +++
> > include/uapi/linux/iio/buffer.h | 22 ++
> > 3 files changed, 517 insertions(+)
> >
> > diff --git a/drivers/iio/industrialio-buffer.c
> > b/drivers/iio/industrialio-buffer.c
> > index b581a7e80566..0e63a09fa90a 100644
> > --- a/drivers/iio/industrialio-buffer.c
> > +++ b/drivers/iio/industrialio-buffer.c
> > @@ -9,14 +9,19 @@
> > * - Better memory allocation techniques?
> > * - Alternative access techniques?
> > */
> > +#include <linux/atomic.h>
> > #include <linux/anon_inodes.h>
> > #include <linux/kernel.h>
> > #include <linux/export.h>
> > #include <linux/device.h>
> > +#include <linux/dma-buf.h>
> > +#include <linux/dma-fence.h>
> > +#include <linux/dma-resv.h>
> > #include <linux/file.h>
> > #include <linux/fs.h>
> > #include <linux/cdev.h>
> > #include <linux/slab.h>
> > +#include <linux/mm.h>
> > #include <linux/poll.h>
> > #include <linux/sched/signal.h>
> >
> > @@ -28,6 +33,32 @@
> > #include <linux/iio/buffer.h>
> > #include <linux/iio/buffer_impl.h>
> >
> > +#define DMABUF_ENQUEUE_TIMEOUT_MS 5000
> > +
> > +struct iio_dma_fence;
> > +
> > +struct iio_dmabuf_priv {
> > + struct list_head entry;
> > + struct kref ref;
> > +
> > + struct iio_buffer *buffer;
> > + struct iio_dma_buffer_block *block;
> > +
> > + u64 context;
> > + spinlock_t lock;
> > +
> > + struct dma_buf_attachment *attach;
> > + struct sg_table *sgt;
> > + enum dma_data_direction dir;
> > + atomic_t seqno;
> > +};
> > +
> > +struct iio_dma_fence {
> > + struct dma_fence base;
> > + struct iio_dmabuf_priv *priv;
> > + struct work_struct work;
> > +};
> > +
> > static const char * const iio_endian_prefix[] = {
> > [IIO_BE] = "be",
> > [IIO_LE] = "le",
> > @@ -332,6 +363,8 @@ void iio_buffer_init(struct iio_buffer *buffer)
> > {
> > INIT_LIST_HEAD(&buffer->demux_list);
> > INIT_LIST_HEAD(&buffer->buffer_list);
> > + INIT_LIST_HEAD(&buffer->dmabufs);
> > + mutex_init(&buffer->dmabufs_mutex);
> > init_waitqueue_head(&buffer->pollq);
> > kref_init(&buffer->ref);
> > if (!buffer->watermark)
> > @@ -1519,14 +1552,62 @@ static void
> > iio_buffer_unregister_legacy_sysfs_groups(struct iio_dev
> > *indio_dev)
> > kfree(iio_dev_opaque->legacy_scan_el_group.attrs);
> > }
> >
> > +static void iio_buffer_dmabuf_release(struct kref *ref)
> > +{
> > + struct iio_dmabuf_priv *priv = container_of(ref, struct
> > iio_dmabuf_priv, ref);
> > + struct dma_buf_attachment *attach = priv->attach;
> > + struct iio_buffer *buffer = priv->buffer;
> > + struct dma_buf *dmabuf = attach->dmabuf;
> > +
> > + dma_resv_lock(dmabuf->resv, NULL);
> > + dma_buf_unmap_attachment(attach, priv->sgt, priv->dir);
> > + dma_resv_unlock(dmabuf->resv);
> > +
> > + buffer->access->detach_dmabuf(buffer, priv->block);
> > +
> > + dma_buf_detach(attach->dmabuf, attach);
> > + dma_buf_put(dmabuf);
> > + kfree(priv);
> > +}
> > +
> > +void iio_buffer_dmabuf_get(struct dma_buf_attachment *attach)
> > +{
> > + struct iio_dmabuf_priv *priv = attach->importer_priv;
> > +
> > + kref_get(&priv->ref);
> > +}
> > +EXPORT_SYMBOL_GPL(iio_buffer_dmabuf_get);
> > +
> > +void iio_buffer_dmabuf_put(struct dma_buf_attachment *attach)
> > +{
> > + struct iio_dmabuf_priv *priv = attach->importer_priv;
> > +
> > + kref_put(&priv->ref, iio_buffer_dmabuf_release);
> > +}
> > +EXPORT_SYMBOL_GPL(iio_buffer_dmabuf_put);
> > +
> > static int iio_buffer_chrdev_release(struct inode *inode, struct
> > file *filep)
> > {
> > struct iio_dev_buffer_pair *ib = filep->private_data;
> > struct iio_dev *indio_dev = ib->indio_dev;
> > struct iio_buffer *buffer = ib->buffer;
> > + struct iio_dmabuf_priv *priv, *tmp;
> >
> > wake_up(&buffer->pollq);
> >
> > + mutex_lock(&buffer->dmabufs_mutex);
> > +
> > + /* Close all attached DMABUFs */
> > + list_for_each_entry_safe(priv, tmp, &buffer->dmabufs,
> > entry) {
> > + list_del_init(&priv->entry);
> > + iio_buffer_dmabuf_put(priv->attach);
> > + }
> > +
> > + if (!list_empty(&buffer->dmabufs))
> > + dev_warn(&indio_dev->dev, "Buffer FD closed with
> > active transfers\n");
>
> That check here smells fishy.
>
> Either the list_for_each_entry_safe() above has removed all entries
> and
> then it can never happen or the buffer list is not always properly
> protected by the mutex.
>
> Do you really need that? If yes then please justify.
The dev_warn() can be removed, the buffers->dmabufs list will always be
empty at that point because of the loop above.
> BTW: When the transfers are async it's perfectly possible that there
> are
> active transfers on close (when the application is just killed for
> example).
>
> > +
> > + mutex_unlock(&buffer->dmabufs_mutex);
> > +
> > kfree(ib);
> > clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > iio_device_put(indio_dev);
> > @@ -1534,11 +1615,391 @@ static int
> > iio_buffer_chrdev_release(struct inode *inode, struct file *filep)
> > return 0;
> > }
> >
> > +static int iio_dma_resv_lock(struct dma_buf *dmabuf, bool
> > nonblock)
> > +{
> > + if (!nonblock)
> > + return dma_resv_lock_interruptible(dmabuf->resv,
> > NULL);
> > +
> > + if (!dma_resv_trylock(dmabuf->resv))
> > + return -EBUSY;
> > +
> > + return 0;
> > +}
> > +
> > +static struct dma_buf_attachment *
> > +iio_buffer_find_attachment(struct iio_dev_buffer_pair *ib,
> > + struct dma_buf *dmabuf, bool nonblock)
> > +{
> > + struct device *dev = ib->indio_dev->dev.parent;
> > + struct iio_buffer *buffer = ib->buffer;
> > + struct dma_buf_attachment *attach = NULL;
> > + struct iio_dmabuf_priv *priv;
> > +
> > + mutex_lock(&buffer->dmabufs_mutex);
> > +
> > + list_for_each_entry(priv, &buffer->dmabufs, entry) {
> > + if (priv->attach->dev == dev
> > + && priv->attach->dmabuf == dmabuf) {
> > + attach = priv->attach;
> > + break;
> > + }
> > + }
> > +
> > + if (attach)
> > + iio_buffer_dmabuf_get(attach);
> > +
> > + mutex_unlock(&buffer->dmabufs_mutex);
> > +
> > + return attach ?: ERR_PTR(-EPERM);
> > +}
> > +
> > +static int iio_buffer_attach_dmabuf(struct iio_dev_buffer_pair
> > *ib,
> > + int __user *user_fd, bool
> > nonblock)
> > +{
> > + struct iio_dev *indio_dev = ib->indio_dev;
> > + struct iio_buffer *buffer = ib->buffer;
> > + struct dma_buf_attachment *attach;
> > + struct iio_dmabuf_priv *priv;
> > + struct dma_buf *dmabuf;
> > + int err, fd;
> > +
> > + if (!buffer->access->attach_dmabuf
> > + || !buffer->access->detach_dmabuf
> > + || !buffer->access->enqueue_dmabuf)
> > + return -EPERM;
> > +
> > + if (copy_from_user(&fd, user_fd, sizeof(fd)))
> > + return -EFAULT;
> > +
> > + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> > + if (!priv)
> > + return -ENOMEM;
> > +
> > + spin_lock_init(&priv->lock);
> > + priv->context = dma_fence_context_alloc(1);
> > +
> > + dmabuf = dma_buf_get(fd);
> > + if (IS_ERR(dmabuf)) {
> > + err = PTR_ERR(dmabuf);
> > + goto err_free_priv;
> > + }
> > +
> > + attach = dma_buf_attach(dmabuf, indio_dev->dev.parent);
> > + if (IS_ERR(attach)) {
> > + err = PTR_ERR(attach);
> > + goto err_dmabuf_put;
> > + }
> > +
> > + err = iio_dma_resv_lock(dmabuf, nonblock);
> > + if (err)
> > + goto err_dmabuf_detach;
> > +
> > + priv->dir = buffer->direction == IIO_BUFFER_DIRECTION_IN
> > + ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> > +
> > + priv->sgt = dma_buf_map_attachment(attach, priv->dir);
> > + if (IS_ERR(priv->sgt)) {
> > + err = PTR_ERR(priv->sgt);
> > + dev_err(&indio_dev->dev, "Unable to map
> > attachment: %d\n", err);
> > + goto err_resv_unlock;
> > + }
> > +
> > + kref_init(&priv->ref);
> > + priv->buffer = buffer;
> > + priv->attach = attach;
> > + attach->importer_priv = priv;
> > +
> > + priv->block = buffer->access->attach_dmabuf(buffer,
> > attach);
> > + if (IS_ERR(priv->block)) {
> > + err = PTR_ERR(priv->block);
> > + goto err_dmabuf_unmap_attachment;
> > + }
> > +
> > + dma_resv_unlock(dmabuf->resv);
> > +
>
> > + mutex_lock(&buffer->dmabufs_mutex);
> > + list_add(&priv->entry, &buffer->dmabufs);
> > + mutex_unlock(&buffer->dmabufs_mutex);
>
> Is it valid to attach the same dma_buf multiple times?
>
> If not that code here should probably check that.
Hmm, I remember that dma_buf_attach() wouldn't allow attaching multiple
times to the same device, but looking at the code, I can't find where
it was checked. Maybe it changed (or I just remember wrong).
I can add code to check that.
>
> > +
> > + return 0;
> > +
> > +err_dmabuf_unmap_attachment:
> > + dma_buf_unmap_attachment(attach, priv->sgt, priv->dir);
> > +err_resv_unlock:
> > + dma_resv_unlock(dmabuf->resv);
> > +err_dmabuf_detach:
> > + dma_buf_detach(dmabuf, attach);
> > +err_dmabuf_put:
> > + dma_buf_put(dmabuf);
> > +err_free_priv:
> > + kfree(priv);
> > +
> > + return err;
> > +}
> > +
> > +static int iio_buffer_detach_dmabuf(struct iio_dev_buffer_pair
> > *ib,
> > + int *user_req, bool nonblock)
> > +{
> > + struct iio_buffer *buffer = ib->buffer;
> > + struct iio_dev *indio_dev = ib->indio_dev;
> > + struct iio_dmabuf_priv *priv;
> > + struct dma_buf *dmabuf;
> > + int dmabuf_fd, ret = -EPERM;
>
> ENOENT is more common to use when something can't be found I think,
> but
> perfectly up to you what you prefer.
The way I see it, the detach is an "operation not permitted" if the
DMABUF hasn't been attached before.
>
> > +
> > + if (copy_from_user(&dmabuf_fd, user_req,
> > sizeof(dmabuf_fd)))
> > + return -EFAULT;
> > +
> > + dmabuf = dma_buf_get(dmabuf_fd);
> > + if (IS_ERR(dmabuf))
> > + return PTR_ERR(dmabuf);
> > +
> > + mutex_lock(&buffer->dmabufs_mutex);
> > +
> > + list_for_each_entry(priv, &buffer->dmabufs, entry) {
> > + if (priv->attach->dev == indio_dev->dev.parent
> > + && priv->attach->dmabuf == dmabuf) {
> > + list_del(&priv->entry);
> > +
> > + /* Unref the reference from
> > iio_buffer_attach_dmabuf() */
> > + iio_buffer_dmabuf_put(priv->attach);
> > + ret = 0;
> > + break;
> > + }
> > + }
> > +
> > + mutex_unlock(&buffer->dmabufs_mutex);
> > + dma_buf_put(dmabuf);
> > +
> > + return ret;
> > +}
> > +
> > +static const char *
> > +iio_buffer_dma_fence_get_driver_name(struct dma_fence *fence)
> > +{
> > + return "iio";
> > +}
> > +
> > +static void iio_buffer_dma_fence_release(struct dma_fence *fence)
> > +{
> > + struct iio_dma_fence *iio_fence =
> > + container_of(fence, struct iio_dma_fence, base);
> > +
> > + kfree(iio_fence);
> > +}
> > +
> > +static const struct dma_fence_ops iio_buffer_dma_fence_ops = {
> > + .get_driver_name =
> > iio_buffer_dma_fence_get_driver_name,
> > + .get_timeline_name =
> > iio_buffer_dma_fence_get_driver_name,
> > + .release = iio_buffer_dma_fence_release,
> > +};
> > +
> > +static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair
> > *ib,
> > + struct iio_dmabuf __user
> > *iio_dmabuf_req,
> > + bool nonblock)
> > +{
> > + struct iio_buffer *buffer = ib->buffer;
> > + struct iio_dmabuf iio_dmabuf;
> > + struct dma_buf_attachment *attach;
> > + struct iio_dmabuf_priv *priv;
> > + struct iio_dma_fence *fence;
> > + struct dma_buf *dmabuf;
> > + unsigned long timeout;
> > + bool cookie, cyclic, dma_to_ram;
> > + long retl;
> > + u32 seqno;
> > + int ret;
> > +
> > + if (copy_from_user(&iio_dmabuf, iio_dmabuf_req,
> > sizeof(iio_dmabuf)))
> > + return -EFAULT;
> > +
> > + if (iio_dmabuf.flags & ~IIO_BUFFER_DMABUF_SUPPORTED_FLAGS)
> > + return -EINVAL;
> > +
> > + cyclic = iio_dmabuf.flags & IIO_BUFFER_DMABUF_CYCLIC;
> > +
> > + /* Cyclic flag is only supported on output buffers */
> > + if (cyclic && buffer->direction !=
> > IIO_BUFFER_DIRECTION_OUT)
> > + return -EINVAL;
> > +
> > + dmabuf = dma_buf_get(iio_dmabuf.fd);
> > + if (IS_ERR(dmabuf))
> > + return PTR_ERR(dmabuf);
> > +
> > + if (!iio_dmabuf.bytes_used || iio_dmabuf.bytes_used >
> > dmabuf->size) {
> > + ret = -EINVAL;
> > + goto err_dmabuf_put;
> > + }
> > +
> > + attach = iio_buffer_find_attachment(ib, dmabuf, nonblock);
> > + if (IS_ERR(attach)) {
> > + ret = PTR_ERR(attach);
> > + goto err_dmabuf_put;
> > + }
> > +
> > + priv = attach->importer_priv;
> > +
> > + fence = kmalloc(sizeof(*fence), GFP_KERNEL);
> > + if (!fence) {
> > + ret = -ENOMEM;
> > + goto err_attachment_put;
> > + }
> > +
> > + fence->priv = priv;
> > +
> > + seqno = atomic_add_return(1, &priv->seqno);
> > +
> > + /*
> > + * The transfers are guaranteed to be processed in the
> > order they are
> > + * enqueued, so we can use a simple incrementing sequence
> > number for
> > + * the dma_fence.
> > + */
> > + dma_fence_init(&fence->base, &iio_buffer_dma_fence_ops,
> > + &priv->lock, priv->context, seqno);
> > +
> > + ret = iio_dma_resv_lock(dmabuf, nonblock);
> > + if (ret)
> > + goto err_fence_put;
> > +
> > + timeout = nonblock ? 0 :
> > msecs_to_jiffies(DMABUF_ENQUEUE_TIMEOUT_MS);
> > +
> > + /* Make sure we don't have writers */
> > + retl = dma_resv_wait_timeout(dmabuf->resv,
> > DMA_RESV_USAGE_WRITE,
> > + true, timeout);
> > + if (retl == 0)
> > + retl = -EBUSY;
> > + if (retl < 0) {
> > + ret = (int)retl;
> > + goto err_resv_unlock;
> > + }
> > +
> > + dma_to_ram = buffer->direction == IIO_BUFFER_DIRECTION_IN;
> > +
> > + if (dma_to_ram) {
> > + /*
> > + * If we're writing to the DMABUF, make sure we
> > don't have
> > + * readers
> > + */
> > + retl = dma_resv_wait_timeout(dmabuf->resv,
> > + DMA_RESV_USAGE_READ,
> > true,
> > + timeout);
> > + if (retl == 0)
> > + retl = -EBUSY;
> > + if (retl < 0) {
> > + ret = (int)retl;
> > + goto err_resv_unlock;
> > + }
> > + }
> > +
> > + if (buffer->access->lock_queue)
> > + buffer->access->lock_queue(buffer);
> > +
> > + ret = dma_resv_reserve_fences(dmabuf->resv, 1);
> > + if (ret)
> > + goto err_queue_unlock;
> > +
> > + dma_resv_add_fence(dmabuf->resv, &fence->base,
> > + dma_resv_usage_rw(dma_to_ram));
>
> That is incorrect use of the function dma_resv_usage_rw(). That
> function
> is for retrieving fences and not adding them.
>
> See the function implementation and comments, when you use it like
> this
> you get exactly what you don't want.
No, I get exactly what I want. If "dma_to_ram", I get
DMA_RESV_USAGE_READ, otherwise I get DMA_RESV_USAGE_WRITE.
If you really don't like the macro, I can inline things here.
Cheers,
-Paul
>
> Regards,
> Christian.
>
> > + dma_resv_unlock(dmabuf->resv);
> > +
> > + cookie = dma_fence_begin_signalling();
> > +
> > + ret = buffer->access->enqueue_dmabuf(buffer, priv->block,
> > &fence->base,
> > + priv->sgt,
> > iio_dmabuf.bytes_used,
> > + cyclic);
> > + if (ret) {
> > + /*
> > + * DMABUF enqueue failed, but we already added the
> > fence.
> > + * Signal the error through the fence completion
> > mechanism.
> > + */
> > + iio_buffer_signal_dmabuf_done(&fence->base, ret);
> > + }
> > +
> > + if (buffer->access->unlock_queue)
> > + buffer->access->unlock_queue(buffer);
> > +
> > + dma_fence_end_signalling(cookie);
> > + dma_buf_put(dmabuf);
> > +
> > + return ret;
> > +
> > +err_queue_unlock:
> > + if (buffer->access->unlock_queue)
> > + buffer->access->unlock_queue(buffer);
> > +err_resv_unlock:
> > + dma_resv_unlock(dmabuf->resv);
> > +err_fence_put:
> > + dma_fence_put(&fence->base);
> > +err_attachment_put:
> > + iio_buffer_dmabuf_put(attach);
> > +err_dmabuf_put:
> > + dma_buf_put(dmabuf);
> > +
> > + return ret;
> > +}
> > +
> > +static void iio_buffer_cleanup(struct work_struct *work)
> > +{
> > + struct iio_dma_fence *fence =
> > + container_of(work, struct iio_dma_fence, work);
> > + struct iio_dmabuf_priv *priv = fence->priv;
> > + struct dma_buf_attachment *attach = priv->attach;
> > +
> > + dma_fence_put(&fence->base);
> > + iio_buffer_dmabuf_put(attach);
> > +}
> > +
> > +void iio_buffer_signal_dmabuf_done(struct dma_fence *fence, int
> > ret)
> > +{
> > + struct iio_dma_fence *iio_fence =
> > + container_of(fence, struct iio_dma_fence, base);
> > + bool cookie = dma_fence_begin_signalling();
> > +
> > + /*
> > + * Get a reference to the fence, so that it's not freed as
> > soon as
> > + * it's signaled.
> > + */
> > + dma_fence_get(fence);
> > +
> > + fence->error = ret;
> > + dma_fence_signal(fence);
> > + dma_fence_end_signalling(cookie);
> > +
> > + /*
> > + * The fence will be unref'd in iio_buffer_cleanup.
> > + * It can't be done here, as the unref functions might try
> > to lock the
> > + * resv object, which can deadlock.
> > + */
> > + INIT_WORK(&iio_fence->work, iio_buffer_cleanup);
> > + schedule_work(&iio_fence->work);
> > +}
> > +EXPORT_SYMBOL_GPL(iio_buffer_signal_dmabuf_done);
> > +
> > +static long iio_buffer_chrdev_ioctl(struct file *filp,
> > + unsigned int cmd, unsigned
> > long arg)
> > +{
> > + struct iio_dev_buffer_pair *ib = filp->private_data;
> > + void __user *_arg = (void __user *)arg;
> > + bool nonblock = filp->f_flags & O_NONBLOCK;
> > +
> > + switch (cmd) {
> > + case IIO_BUFFER_DMABUF_ATTACH_IOCTL:
> > + return iio_buffer_attach_dmabuf(ib, _arg,
> > nonblock);
> > + case IIO_BUFFER_DMABUF_DETACH_IOCTL:
> > + return iio_buffer_detach_dmabuf(ib, _arg,
> > nonblock);
> > + case IIO_BUFFER_DMABUF_ENQUEUE_IOCTL:
> > + return iio_buffer_enqueue_dmabuf(ib, _arg,
> > nonblock);
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > static const struct file_operations iio_buffer_chrdev_fileops = {
> > .owner = THIS_MODULE,
> > .llseek = noop_llseek,
> > .read = iio_buffer_read,
> > .write = iio_buffer_write,
> > + .unlocked_ioctl = iio_buffer_chrdev_ioctl,
> > + .compat_ioctl = compat_ptr_ioctl,
> > .poll = iio_buffer_poll,
> > .release = iio_buffer_chrdev_release,
> > };
> > @@ -1953,6 +2414,7 @@ static void iio_buffer_release(struct kref
> > *ref)
> > {
> > struct iio_buffer *buffer = container_of(ref, struct
> > iio_buffer, ref);
> >
> > + mutex_destroy(&buffer->dmabufs_mutex);
> > buffer->access->release(buffer);
> > }
> >
> > diff --git a/include/linux/iio/buffer_impl.h
> > b/include/linux/iio/buffer_impl.h
> > index 89c3fd7c29ca..f4b1147291e5 100644
> > --- a/include/linux/iio/buffer_impl.h
> > +++ b/include/linux/iio/buffer_impl.h
> > @@ -9,8 +9,12 @@
> > #include <uapi/linux/iio/buffer.h>
> > #include <linux/iio/buffer.h>
> >
> > +struct dma_buf_attachment;
> > +struct dma_fence;
> > struct iio_dev;
> > +struct iio_dma_buffer_block;
> > struct iio_buffer;
> > +struct sg_table;
> >
> > /**
> > * INDIO_BUFFER_FLAG_FIXED_WATERMARK - Watermark level of the
> > buffer can not be
> > @@ -39,6 +43,13 @@ struct iio_buffer;
> > * device stops sampling. Calles are
> > balanced with @enable.
> > * @release: called when the last reference to the
> > buffer is dropped,
> > * should free all resources allocated by the
> > buffer.
> > + * @attach_dmabuf: called from userspace via ioctl to attach
> > one external
> > + * DMABUF.
> > + * @detach_dmabuf: called from userspace via ioctl to detach
> > one previously
> > + * attached DMABUF.
> > + * @enqueue_dmabuf: called from userspace via ioctl to queue
> > this DMABUF
> > + * object to this buffer. Requires a valid
> > DMABUF fd, that
> > + * was previouly attached to this buffer.
> > * @modes: Supported operating modes by this buffer
> > type
> > * @flags: A bitmask combination of
> > INDIO_BUFFER_FLAG_*
> > *
> > @@ -68,6 +79,17 @@ struct iio_buffer_access_funcs {
> >
> > void (*release)(struct iio_buffer *buffer);
> >
> > + struct iio_dma_buffer_block * (*attach_dmabuf)(struct
> > iio_buffer *buffer,
> > + struct
> > dma_buf_attachment *attach);
> > + void (*detach_dmabuf)(struct iio_buffer *buffer,
> > + struct iio_dma_buffer_block *block);
> > + int (*enqueue_dmabuf)(struct iio_buffer *buffer,
> > + struct iio_dma_buffer_block *block,
> > + struct dma_fence *fence, struct
> > sg_table *sgt,
> > + size_t size, bool cyclic);
> > + void (*lock_queue)(struct iio_buffer *buffer);
> > + void (*unlock_queue)(struct iio_buffer *buffer);
> > +
> > unsigned int modes;
> > unsigned int flags;
> > };
> > @@ -136,6 +158,12 @@ struct iio_buffer {
> >
> > /* @ref: Reference count of the buffer. */
> > struct kref ref;
> > +
> > + /* @dmabufs: List of DMABUF attachments */
> > + struct list_head dmabufs; /* P: dmabufs_mutex */
> > +
> > + /* @dmabufs_mutex: Protects dmabufs */
> > + struct mutex dmabufs_mutex;
> > };
> >
> > /**
> > @@ -156,9 +184,14 @@ int iio_update_buffers(struct iio_dev
> > *indio_dev,
> > **/
> > void iio_buffer_init(struct iio_buffer *buffer);
> >
> > +void iio_buffer_dmabuf_get(struct dma_buf_attachment *attach);
> > +void iio_buffer_dmabuf_put(struct dma_buf_attachment *attach);
> > +
> > struct iio_buffer *iio_buffer_get(struct iio_buffer *buffer);
> > void iio_buffer_put(struct iio_buffer *buffer);
> >
> > +void iio_buffer_signal_dmabuf_done(struct dma_fence *fence, int
> > ret);
> > +
> > #else /* CONFIG_IIO_BUFFER */
> >
> > static inline void iio_buffer_get(struct iio_buffer *buffer) {}
> > diff --git a/include/uapi/linux/iio/buffer.h
> > b/include/uapi/linux/iio/buffer.h
> > index 13939032b3f6..c666aa95e532 100644
> > --- a/include/uapi/linux/iio/buffer.h
> > +++ b/include/uapi/linux/iio/buffer.h
> > @@ -5,6 +5,28 @@
> > #ifndef _UAPI_IIO_BUFFER_H_
> > #define _UAPI_IIO_BUFFER_H_
> >
> > +#include <linux/types.h>
> > +
> > +/* Flags for iio_dmabuf.flags */
> > +#define IIO_BUFFER_DMABUF_CYCLIC (1 << 0)
> > +#define IIO_BUFFER_DMABUF_SUPPORTED_FLAGS 0x00000001
> > +
> > +/**
> > + * struct iio_dmabuf - Descriptor for a single IIO DMABUF object
> > + * @fd: file descriptor of the DMABUF object
> > + * @flags: one or more IIO_BUFFER_DMABUF_* flags
> > + * @bytes_used: number of bytes used in this DMABUF for
> > the data transfer.
> > + * Should generally be set to the DMABUF's size.
> > + */
> > +struct iio_dmabuf {
> > + __u32 fd;
> > + __u32 flags;
> > + __u64 bytes_used;
> > +};
> > +
> > #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i',
> > 0x91, int)
> > +#define IIO_BUFFER_DMABUF_ATTACH_IOCTL _IOW('i', 0x92,
> > int)
> > +#define IIO_BUFFER_DMABUF_DETACH_IOCTL _IOW('i', 0x93,
> > int)
> > +#define IIO_BUFFER_DMABUF_ENQUEUE_IOCTL _IOW('i',
> > 0x94, struct iio_dmabuf)
> >
> > #endif /* _UAPI_IIO_BUFFER_H_ */
> >
>
More information about the dri-devel
mailing list