[PATCH v4 12/12] block/io_uring: use non-vectored read/write when possible

Stefan Hajnoczi posted 12 patches 5 months ago
Maintainers: Kevin Wolf <kwolf@redhat.com>, Hanna Reitz <hreitz@redhat.com>, Aarushi Mehta <mehta.aaru20@gmail.com>, Julia Suvorova <jusual@redhat.com>, Stefan Hajnoczi <stefanha@redhat.com>, Stefano Garzarella <sgarzare@redhat.com>, Fam Zheng <fam@euphon.net>, Paolo Bonzini <pbonzini@redhat.com>, Stefan Weil <sw@weilnetz.de>
There is a newer version of this series
[PATCH v4 12/12] block/io_uring: use non-vectored read/write when possible
Posted by Stefan Hajnoczi 5 months ago
The io_uring_prep_readv2/writev2() man pages recommend using the
non-vectored read/write operations when possible for performance
reasons.

I didn't measure a significant difference but it doesn't hurt to have
this optimization in place.

Suggested-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 block/io_uring.c | 29 ++++++++++++++++++++++++-----
 1 file changed, 24 insertions(+), 5 deletions(-)

diff --git a/block/io_uring.c b/block/io_uring.c
index dd930ee57e..bbefbddcc0 100644
--- a/block/io_uring.c
+++ b/block/io_uring.c
@@ -49,12 +49,24 @@ static void luring_prep_sqe(struct io_uring_sqe *sqe, void *opaque)
 #ifdef HAVE_IO_URING_PREP_WRITEV2
     {
         int luring_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
-        io_uring_prep_writev2(sqe, fd, qiov->iov,
-                              qiov->niov, offset, luring_flags);
+        if (luring_flags != 0 || qiov->niov > 1) {
+            io_uring_prep_writev2(sqe, fd, qiov->iov,
+                                  qiov->niov, offset, luring_flags);
+        } else {
+            /* The man page says non-vectored is faster than vectored */
+            struct iovec *iov = qiov->iov;
+            io_uring_prep_write(sqe, fd, iov->iov_base, iov->iov_len, offset);
+        }
     }
 #else
         assert(flags == 0);
-        io_uring_prep_writev(sqe, fd, qiov->iov, qiov->niov, offset);
+        if (qiov->niov > 1) {
+            io_uring_prep_writev(sqe, fd, qiov->iov, qiov->niov, offset);
+        } else {
+            /* The man page says non-vectored is faster than vectored */
+            struct iovec *iov = qiov->iov;
+            io_uring_prep_write(sqe, fd, iov->iov_base, iov->iov_len, offset);
+        }
 #endif
         break;
     case QEMU_AIO_ZONE_APPEND:
@@ -65,8 +77,15 @@ static void luring_prep_sqe(struct io_uring_sqe *sqe, void *opaque)
         if (req->resubmit_qiov.iov != NULL) {
             qiov = &req->resubmit_qiov;
         }
-        io_uring_prep_readv(sqe, fd, qiov->iov, qiov->niov,
-                            offset + req->total_read);
+        if (qiov->niov > 1) {
+            io_uring_prep_readv(sqe, fd, qiov->iov, qiov->niov,
+                                offset + req->total_read);
+        } else {
+            /* The man page says non-vectored is faster than vectored */
+            struct iovec *iov = qiov->iov;
+            io_uring_prep_read(sqe, fd, iov->iov_base, iov->iov_len,
+                               offset + req->total_read);
+        }
         break;
     }
     case QEMU_AIO_FLUSH:
-- 
2.51.0
Re: [PATCH v4 12/12] block/io_uring: use non-vectored read/write when possible
Posted by Kevin Wolf 4 months ago
Am 10.09.2025 um 19:57 hat Stefan Hajnoczi geschrieben:
> The io_uring_prep_readv2/writev2() man pages recommend using the
> non-vectored read/write operations when possible for performance
> reasons.
> 
> I didn't measure a significant difference but it doesn't hurt to have
> this optimization in place.
> 
> Suggested-by: Eric Blake <eblake@redhat.com>
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> ---
>  block/io_uring.c | 29 ++++++++++++++++++++++++-----
>  1 file changed, 24 insertions(+), 5 deletions(-)
> 
> diff --git a/block/io_uring.c b/block/io_uring.c
> index dd930ee57e..bbefbddcc0 100644
> --- a/block/io_uring.c
> +++ b/block/io_uring.c
> @@ -49,12 +49,24 @@ static void luring_prep_sqe(struct io_uring_sqe *sqe, void *opaque)
>  #ifdef HAVE_IO_URING_PREP_WRITEV2
>      {
>          int luring_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
> -        io_uring_prep_writev2(sqe, fd, qiov->iov,
> -                              qiov->niov, offset, luring_flags);
> +        if (luring_flags != 0 || qiov->niov > 1) {
> +            io_uring_prep_writev2(sqe, fd, qiov->iov,
> +                                  qiov->niov, offset, luring_flags);
> +        } else {
> +            /* The man page says non-vectored is faster than vectored */
> +            struct iovec *iov = qiov->iov;
> +            io_uring_prep_write(sqe, fd, iov->iov_base, iov->iov_len, offset);
> +        }
>      }
>  #else
>          assert(flags == 0);
> -        io_uring_prep_writev(sqe, fd, qiov->iov, qiov->niov, offset);
> +        if (qiov->niov > 1) {
> +            io_uring_prep_writev(sqe, fd, qiov->iov, qiov->niov, offset);
> +        } else {
> +            /* The man page says non-vectored is faster than vectored */
> +            struct iovec *iov = qiov->iov;
> +            io_uring_prep_write(sqe, fd, iov->iov_base, iov->iov_len, offset);
> +        }
>  #endif

We have a lot of duplication in this now. Let's use the #ifdef a little
more locally:

    {
        int luring_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
        if (luring_flags != 0 || qiov->niov > 1) {
#ifdef HAVE_IO_URING_PREP_WRITEV2
            io_uring_prep_writev2(sqe, fd, qiov->iov,
                                  qiov->niov, offset, luring_flags);
#else
            assert(luring_flags == 0);
            io_uring_prep_writev(sqe, fd, qiov->iov, qiov->niov, offset);
#endif
        } else {
            /* The man page says non-vectored is faster than vectored */
            struct iovec *iov = qiov->iov;
            io_uring_prep_write(sqe, fd, iov->iov_base, iov->iov_len, offset);
        }
    }

Kevin
Re: [PATCH v4 12/12] block/io_uring: use non-vectored read/write when possible
Posted by Stefan Hajnoczi 3 months, 3 weeks ago
On Fri, Oct 10, 2025 at 06:33:40PM +0200, Kevin Wolf wrote:
> Am 10.09.2025 um 19:57 hat Stefan Hajnoczi geschrieben:
> > The io_uring_prep_readv2/writev2() man pages recommend using the
> > non-vectored read/write operations when possible for performance
> > reasons.
> > 
> > I didn't measure a significant difference but it doesn't hurt to have
> > this optimization in place.
> > 
> > Suggested-by: Eric Blake <eblake@redhat.com>
> > Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> > ---
> >  block/io_uring.c | 29 ++++++++++++++++++++++++-----
> >  1 file changed, 24 insertions(+), 5 deletions(-)
> > 
> > diff --git a/block/io_uring.c b/block/io_uring.c
> > index dd930ee57e..bbefbddcc0 100644
> > --- a/block/io_uring.c
> > +++ b/block/io_uring.c
> > @@ -49,12 +49,24 @@ static void luring_prep_sqe(struct io_uring_sqe *sqe, void *opaque)
> >  #ifdef HAVE_IO_URING_PREP_WRITEV2
> >      {
> >          int luring_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
> > -        io_uring_prep_writev2(sqe, fd, qiov->iov,
> > -                              qiov->niov, offset, luring_flags);
> > +        if (luring_flags != 0 || qiov->niov > 1) {
> > +            io_uring_prep_writev2(sqe, fd, qiov->iov,
> > +                                  qiov->niov, offset, luring_flags);
> > +        } else {
> > +            /* The man page says non-vectored is faster than vectored */
> > +            struct iovec *iov = qiov->iov;
> > +            io_uring_prep_write(sqe, fd, iov->iov_base, iov->iov_len, offset);
> > +        }
> >      }
> >  #else
> >          assert(flags == 0);
> > -        io_uring_prep_writev(sqe, fd, qiov->iov, qiov->niov, offset);
> > +        if (qiov->niov > 1) {
> > +            io_uring_prep_writev(sqe, fd, qiov->iov, qiov->niov, offset);
> > +        } else {
> > +            /* The man page says non-vectored is faster than vectored */
> > +            struct iovec *iov = qiov->iov;
> > +            io_uring_prep_write(sqe, fd, iov->iov_base, iov->iov_len, offset);
> > +        }
> >  #endif
> 
> We have a lot of duplication in this now. Let's use the #ifdef a little
> more locally:
> 
>     {
>         int luring_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
>         if (luring_flags != 0 || qiov->niov > 1) {
> #ifdef HAVE_IO_URING_PREP_WRITEV2
>             io_uring_prep_writev2(sqe, fd, qiov->iov,
>                                   qiov->niov, offset, luring_flags);
> #else
>             assert(luring_flags == 0);
>             io_uring_prep_writev(sqe, fd, qiov->iov, qiov->niov, offset);
> #endif
>         } else {
>             /* The man page says non-vectored is faster than vectored */
>             struct iovec *iov = qiov->iov;
>             io_uring_prep_write(sqe, fd, iov->iov_base, iov->iov_len, offset);
>         }
>     }

Will fix in v5. Thanks!

Stefan