Re: [PATCH v3 07/11] iio: core: Add new DMABUF interface infrastructure
From: Nuno Sá
Date: Tue Apr 04 2023 - 03:31:06 EST
On Mon, 2023-04-03 at 17:47 +0200, Paul Cercueil wrote:
> 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.
>
> Signed-off-by: Paul Cercueil <paul@xxxxxxxxxxxxxxx>
>
> ---
> v2: Only allow the new IOCTLs on the buffer FD created with
> IIO_BUFFER_GET_FD_IOCTL().
>
> v3: - Get rid of the old IOCTLs. The IIO subsystem does not create or
> manage DMABUFs anymore, and only attaches/detaches externally
> created DMABUFs.
> - Add IIO_BUFFER_DMABUF_CYCLIC to the supported flags.
> ---
> drivers/iio/industrialio-buffer.c | 402
> ++++++++++++++++++++++++++++++
> include/linux/iio/buffer_impl.h | 22 ++
> include/uapi/linux/iio/buffer.h | 22 ++
> 3 files changed, 446 insertions(+)
>
> diff --git a/drivers/iio/industrialio-buffer.c
> b/drivers/iio/industrialio-buffer.c
> index 80c78bd6bbef..5d88e098b3e7 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -13,10 +13,14 @@
> #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,11 +32,41 @@
> #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 iio_dma_fence *fence;
> +};
> +
> +struct iio_dma_fence {
> + struct dma_fence base;
> + struct iio_dmabuf_priv *priv;
> + struct sg_table *sgt;
> + enum dma_data_direction dir;
> +};
> +
> static const char * const iio_endian_prefix[] = {
> [IIO_BE] = "be",
> [IIO_LE] = "le",
> };
>
> +static inline struct iio_dma_fence *to_iio_dma_fence(struct
> dma_fence *fence)
> +{
> + return container_of(fence, struct iio_dma_fence, base);
> +}
> +
Kind of a nitpick but I only see this being used once so I would maybe
use plain 'container_of()' as you are already doing for:
... = container_of(ref, struct iio_dmabuf_priv, ref);
So I would at least advocate for consistency. I would also probably
ditch the inline but I guess that is more a matter of style/preference.
> static bool iio_buffer_is_active(struct iio_buffer *buf)
> {
> return !list_empty(&buf->buffer_list);
> @@ -329,6 +363,7 @@ void iio_buffer_init(struct iio_buffer *buffer)
> {
>
...
> + priv = attach->importer_priv;
> + list_del_init(&priv->entry);
> +
> + iio_buffer_dmabuf_put(attach);
> + iio_buffer_dmabuf_put(attach);
> +
Is this intended? Looks suspicious...
> +out_dmabuf_put:
> + 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 = to_iio_dma_fence(fence);
> +
> + 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;
> + enum dma_data_direction dir;
> + struct iio_dma_fence *fence;
> + struct dma_buf *dmabuf;
> + struct sg_table *sgt;
> + unsigned long timeout;
> + bool dma_to_ram;
> + bool cyclic;
> + 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->indio_dev, dmabuf);
> + if (IS_ERR(attach)) {
> + ret = PTR_ERR(attach);
> + goto err_dmabuf_put;
> + }
> +
> + priv = attach->importer_priv;
> +
> + dma_to_ram = buffer->direction == IIO_BUFFER_DIRECTION_IN;
> + dir = dma_to_ram ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> + sgt = dma_buf_map_attachment(attach, dir);
> + if (IS_ERR(sgt)) {
> + ret = PTR_ERR(sgt);
> + pr_err("Unable to map attachment: %d\n", ret);
dev_err()? We should be able to reach the iio_dev
> + goto err_attachment_put;
> + }
> +
> + fence = kmalloc(sizeof(*fence), GFP_KERNEL);
> + if (!fence) {
> + ret = -ENOMEM;
> + goto err_unmap_attachment;
> + }
> +
>
...
> 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,
> };
Hmmm, what about the legacy buffer? We should also support this
interface using it, right? Otherwise, using one of the new IOCTL in
iio_device_buffer_ioctl() (or /dev/iio:device0) will error out.
- Nuno Sá