From: Paul Cercueil <paul@crapouillou.net>
To: Jonathan Cameron <jic23@kernel.org>
Cc: "Lars-Peter Clausen" <lars@metafoo.de>,
"Sumit Semwal" <sumit.semwal@linaro.org>,
"Christian König" <christian.koenig@amd.com>,
"Vinod Koul" <vkoul@kernel.org>,
"Jonathan Corbet" <corbet@lwn.net>,
linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
dmaengine@vger.kernel.org, linux-iio@vger.kernel.org,
linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org,
linaro-mm-sig@lists.linaro.org, "Nuno Sá" <noname.nuno@gmail.com>,
"Michael Hennerich" <Michael.Hennerich@analog.com>
Subject: Re: [PATCH v5 5/8] iio: core: Add new DMABUF interface infrastructure
Date: Thu, 21 Dec 2023 18:21:51 +0100 [thread overview]
Message-ID: <baee615936893ab5e28e6fff1014211286579476.camel@crapouillou.net> (raw)
In-Reply-To: <20231221120624.7bcdc302@jic23-huawei>
Hi Jonathan,
Le jeudi 21 décembre 2023 à 12:06 +0000, Jonathan Cameron a écrit :
> On Tue, 19 Dec 2023 18:50:06 +0100
> Paul Cercueil <paul@crapouillou.net> 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.
> >
>
> Fair enough - so they don't apply to the 'legacy' buffer which
> simplifies
> things but in one place you assume that logic is used (given error
> return
> values).
>
> > Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> >
> This is big and complex and I'm out of time for now, so I've made
> some
> comments but should revisit it.
> I'm also looking for review from those more familiar with dmabuf side
> of things than I am!
>
> Jonathan
>
>
> >
> > +static int iio_dma_resv_lock(struct dma_buf *dmabuf, bool
> > nonblock)
> > +{
> > + int ret;
> > +
> > + ret = dma_resv_lock_interruptible(dmabuf->resv, NULL);
> > + if (ret) {
> > + if (ret != -EDEADLK)
> > + goto out;
> > + if (nonblock) {
> > + ret = -EBUSY;
> > + goto out;
> > + }
> > +
> > + ret = dma_resv_lock_slow_interruptible(dmabuf-
> > >resv, NULL);
> > + }
> > +
> > +out:
> > + return ret;
>
> I'm not a fan gotos that do nothing. Just return in appropriate
> places above.
>
> > +}
> >
> > +static int iio_buffer_detach_dmabuf(struct iio_dev_buffer_pair
> > *ib, int *user_req)
> > +{
> > + struct dma_buf_attachment *attach;
> > + struct iio_dmabuf_priv *priv;
> > + struct dma_buf *dmabuf;
> > + int dmabuf_fd, ret = 0;
> > +
> > + 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);
> > +
> > + attach = iio_buffer_find_attachment(ib->indio_dev,
> > dmabuf);
> > + if (IS_ERR(attach)) {
> > + ret = PTR_ERR(attach);
> > + goto out_dmabuf_put;
> > + }
> > +
> > + priv = attach->importer_priv;
> > + list_del_init(&priv->entry);
> > +
> > + /*
> > + * Unref twice to release the reference obtained with
> > + * iio_buffer_find_attachment() above, and the one
> > obtained in
> > + * iio_buffer_attach_dmabuf().
> > + */
> > + iio_buffer_dmabuf_put(attach);
> > + iio_buffer_dmabuf_put(attach);
> > +
> > +out_dmabuf_put:
> > + dma_buf_put(dmabuf);
> As below. Feels like a __free(dma_buf_put) bit of magic would be a
> nice to have.
Whoa, never heard about this. That looks great!
>
> > +
> > + 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_dev *indio_dev = ib->indio_dev;
> > + 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(indio_dev, dmabuf);
> > + if (IS_ERR(attach)) {
> > + ret = PTR_ERR(attach);
> > + goto err_dmabuf_put;
>
> Might be worth some cleanup.h magic given this put happens in all
> exit paths.
>
> > + }
> > +
> > + 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);
> > + dev_err(&indio_dev->dev, "Unable to map
> > attachment: %d\n", ret);
> > + goto err_attachment_put;
> > + }
> > +
> > + fence = kmalloc(sizeof(*fence), GFP_KERNEL);
> > + if (!fence) {
> > + ret = -ENOMEM;
> > + goto err_unmap_attachment;
> > + }
> > +
> > + fence->priv = priv;
> > + fence->sgt = sgt;
> > + fence->dir = dir;
> > + priv->fence = fence;
> > +
> > + dma_fence_init(&fence->base, &iio_buffer_dma_fence_ops,
> > + &priv->lock, priv->context, 0);
> > +
> > + 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 */
> > + ret = (int) dma_resv_wait_timeout(dmabuf->resv,
> > DMA_RESV_USAGE_WRITE,
> > + true, timeout);
>
> I'd handle this and similar cases as long rather than adding the odd
> looking cast and making
> me think too much about it.
>
> > + if (ret == 0)
> > + ret = -EBUSY;
> > + if (ret < 0)
> > + goto err_resv_unlock;
> > +
> > + if (dma_to_ram) {
> > + /*
> > + * If we're writing to the DMABUF, make sure we
> > don't have
> > + * readers
> > + */
> > + ret = (int) dma_resv_wait_timeout(dmabuf->resv,
> > +
> > DMA_RESV_USAGE_READ, true,
> > + timeout);
> > + if (ret == 0)
> > + ret = -EBUSY;
> > + if (ret < 0)
> > + goto err_resv_unlock;
> > + }
> > +
> > + ret = dma_resv_reserve_fences(dmabuf->resv, 1);
> > + if (ret)
> > + goto err_resv_unlock;
> > +
> > + dma_resv_add_fence(dmabuf->resv, &fence->base,
> > + dma_resv_usage_rw(dma_to_ram));
> > + dma_resv_unlock(dmabuf->resv);
> > +
> > + ret = buffer->access->enqueue_dmabuf(buffer, priv->block,
> > sgt,
> > +
> > iio_dmabuf.bytes_used, cyclic);
> > + if (ret)
> > + iio_buffer_signal_dmabuf_done(attach, ret);
>
> I'd like a comment on why using the 'successful' path cleanup makes
> sense in this
> error case. It's possible to figure it out, but reviewers are lazy
> and generally
> we like the cleanup to be obvious and local on error paths.
>
> > +
> > + dma_buf_put(dmabuf);
> > +
> > + return ret;
> > +
> > +err_resv_unlock:
> > + dma_resv_unlock(dmabuf->resv);
> > +err_fence_put:
> > + dma_fence_put(&fence->base);
> > +err_unmap_attachment:
> > + dma_buf_unmap_attachment(attach, sgt, dir);
> > +err_attachment_put:
> > + iio_buffer_dmabuf_put(attach);
> > +err_dmabuf_put:
> > + dma_buf_put(dmabuf);
> > +
> > + return ret;
> > +}
> > +
> > +void iio_buffer_signal_dmabuf_done(struct dma_buf_attachment
> > *attach, int ret)
> > +{
> > + struct iio_dmabuf_priv *priv = attach->importer_priv;
> > + struct iio_dma_fence *fence = priv->fence;
> > + enum dma_data_direction dir = fence->dir;
> > + struct sg_table *sgt = fence->sgt;
> > +
> > + dma_fence_get(&fence->base);
>
> I don't know much about dma_fence, but is it valid to access
> contents of it (sgt, etc) before getting a reference?
> Ultimately dma_fence_put() can result in a kfree_rcu() so seems
> unlikely to be safe and definitely fails the 'obviously correct'
> test. Given those are I assume trivial accesses just do them
> down here perhaps?
It is valid to access the fence before getting a reference - the fence
won't be freed before it is signaled down below. It would be illegal to
access it after the dma_fence_put() though, which I'm not doing here.
>
>
> > + fence->base.error = ret;
> > + dma_fence_signal(&fence->base);
> > + dma_fence_put(&fence->base);
> > +
> > + dma_buf_unmap_attachment(attach, sgt, dir);
> > + iio_buffer_dmabuf_put(attach);
> > +}
> > +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;
> > +
> > + switch (cmd) {
> > + case IIO_BUFFER_DMABUF_ATTACH_IOCTL:
> > + return iio_buffer_attach_dmabuf(ib, _arg);
> > + case IIO_BUFFER_DMABUF_DETACH_IOCTL:
> > + return iio_buffer_detach_dmabuf(ib, _arg);
> > + case IIO_BUFFER_DMABUF_ENQUEUE_IOCTL:
> > + return iio_buffer_enqueue_dmabuf(ib, _arg,
> > + filp->f_flags &
> > O_NONBLOCK);
> > + default:
> > + return IIO_IOCTL_UNHANDLED;
>
> Given you aren't using the ioctl handling on the legacy buffer, I
> think this
> should simply return an error code, not the magic value we use to
> indicate
> 'all fine, but it's not mine'.
> Probably -EINVAL or similar. Note that the wrapper around the legacy
> buffer
> ioctls translates this to -ENODEV; rather than returning from the
> ioctl.
ACK for this and the other comments.
Thanks for reviewing!
Cheers,
-Paul
> > + }
> > +}
> > +
> > 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,
> > };
>
next prev parent reply other threads:[~2023-12-21 17:21 UTC|newest]
Thread overview: 41+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-12-19 17:50 [PATCH v5 0/8] iio: new DMABUF based API, v5 Paul Cercueil
2023-12-19 17:50 ` [PATCH v5 1/8] iio: buffer-dma: Get rid of outgoing queue Paul Cercueil
2023-12-21 11:28 ` Jonathan Cameron
2023-12-19 17:50 ` [PATCH v5 2/8] iio: buffer-dma: split iio_dma_buffer_fileio_free() function Paul Cercueil
2023-12-21 11:31 ` Jonathan Cameron
2023-12-19 17:50 ` [PATCH v5 3/8] dmaengine: Add API function dmaengine_prep_slave_dma_vec() Paul Cercueil
2023-12-21 11:40 ` Jonathan Cameron
2023-12-21 15:14 ` Vinod Koul
2023-12-21 15:29 ` Paul Cercueil
2024-01-08 12:20 ` Paul Cercueil
2024-01-22 11:06 ` [Linaro-mm-sig] " Vinod Koul
2023-12-19 17:50 ` [PATCH v5 4/8] dmaengine: dma-axi-dmac: Implement device_prep_slave_dma_vec Paul Cercueil
2023-12-19 17:50 ` [PATCH v5 5/8] iio: core: Add new DMABUF interface infrastructure Paul Cercueil
2023-12-21 12:06 ` Jonathan Cameron
2023-12-21 17:21 ` Paul Cercueil [this message]
2024-01-25 13:47 ` Paul Cercueil
2024-01-27 16:50 ` Jonathan Cameron
2024-01-29 12:52 ` Christian König
2024-01-29 13:06 ` Paul Cercueil
2024-01-29 13:17 ` Christian König
2024-01-29 13:32 ` Paul Cercueil
2024-01-29 14:15 ` Paul Cercueil
2024-01-08 13:20 ` Daniel Vetter
2023-12-19 17:50 ` [PATCH v5 6/8] iio: buffer-dma: Enable support for DMABUFs Paul Cercueil
2023-12-21 16:04 ` Jonathan Cameron
2023-12-22 8:56 ` Nuno Sá
2023-12-26 15:30 ` Jonathan Cameron
2023-12-19 17:50 ` [PATCH v5 7/8] iio: buffer-dmaengine: Support new DMABUF based userspace API Paul Cercueil
2023-12-21 16:12 ` Jonathan Cameron
2023-12-21 17:30 ` Paul Cercueil
2023-12-22 8:58 ` Nuno Sá
2023-12-26 15:31 ` Jonathan Cameron
2023-12-19 17:50 ` [PATCH v5 8/8] Documentation: iio: Document high-speed DMABUF based API Paul Cercueil
2023-12-21 16:15 ` Jonathan Cameron
2023-12-21 16:30 ` [PATCH v5 0/8] iio: new DMABUF based API, v5 Jonathan Cameron
2023-12-21 17:56 ` Paul Cercueil
2023-12-26 15:37 ` Jonathan Cameron
2024-01-08 21:12 ` Andrew Davis
2024-01-11 9:20 ` Paul Cercueil
2024-01-11 17:30 ` Andrew Davis
2024-01-12 11:33 ` Paul Cercueil
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=baee615936893ab5e28e6fff1014211286579476.camel@crapouillou.net \
--to=paul@crapouillou.net \
--cc=Michael.Hennerich@analog.com \
--cc=christian.koenig@amd.com \
--cc=corbet@lwn.net \
--cc=dmaengine@vger.kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=jic23@kernel.org \
--cc=lars@metafoo.de \
--cc=linaro-mm-sig@lists.linaro.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=noname.nuno@gmail.com \
--cc=sumit.semwal@linaro.org \
--cc=vkoul@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox