[PULL v4 14/27] io: add qio_channel_readv_full_all_eof & qio_channel_readv_full_all helpers

[PULL v4 14/27] io: add qio_channel_readv_full_all_eof & qio_channel_readv_full_all helpers
Posted by Stefan Hajnoczi 4 years, 2 months ago
From: Elena Ufimtseva <elena.ufimtseva@oracle.com>

Adds qio_channel_readv_full_all_eof() and qio_channel_readv_full_all()
to read both data and FDs. Refactors existing code to use these helpers.

Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
Acked-by: Daniel P. Berrangé <berrange@redhat.com>
Message-id: b059c4cc0fb741e794d644c144cc21372cad877d.1611938319.git.jag.raman@oracle.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 include/io/channel.h |  53 +++++++++++++++++++++++
 io/channel.c         | 101 ++++++++++++++++++++++++++++++++++---------
 2 files changed, 134 insertions(+), 20 deletions(-)

diff --git a/include/io/channel.h b/include/io/channel.h
index 19e76fc32f..88988979f8 100644
--- a/include/io/channel.h
+++ b/include/io/channel.h
@@ -777,6 +777,59 @@ void qio_channel_set_aio_fd_handler(QIOChannel *ioc,
                                     IOHandler *io_write,
                                     void *opaque);
 
+/**
+ * qio_channel_readv_full_all_eof:
+ * @ioc: the channel object
+ * @iov: the array of memory regions to read data to
+ * @niov: the length of the @iov array
+ * @fds: an array of file handles to read
+ * @nfds: number of file handles in @fds
+ * @errp: pointer to a NULL-initialized error object
+ *
+ *
+ * Performs same function as qio_channel_readv_all_eof.
+ * Additionally, attempts to read file descriptors shared
+ * over the channel. The function will wait for all
+ * requested data to be read, yielding from the current
+ * coroutine if required. data refers to both file
+ * descriptors and the iovs.
+ *
+ * Returns: 1 if all bytes were read, 0 if end-of-file
+ *          occurs without data, or -1 on error
+ */
+
+int qio_channel_readv_full_all_eof(QIOChannel *ioc,
+                                   const struct iovec *iov,
+                                   size_t niov,
+                                   int **fds, size_t *nfds,
+                                   Error **errp);
+
+/**
+ * qio_channel_readv_full_all:
+ * @ioc: the channel object
+ * @iov: the array of memory regions to read data to
+ * @niov: the length of the @iov array
+ * @fds: an array of file handles to read
+ * @nfds: number of file handles in @fds
+ * @errp: pointer to a NULL-initialized error object
+ *
+ *
+ * Performs same function as qio_channel_readv_all_eof.
+ * Additionally, attempts to read file descriptors shared
+ * over the channel. The function will wait for all
+ * requested data to be read, yielding from the current
+ * coroutine if required. data refers to both file
+ * descriptors and the iovs.
+ *
+ * Returns: 0 if all bytes were read, or -1 on error
+ */
+
+int qio_channel_readv_full_all(QIOChannel *ioc,
+                               const struct iovec *iov,
+                               size_t niov,
+                               int **fds, size_t *nfds,
+                               Error **errp);
+
 /**
  * qio_channel_writev_full_all:
  * @ioc: the channel object
diff --git a/io/channel.c b/io/channel.c
index 0d4b8b5160..4555021b62 100644
--- a/io/channel.c
+++ b/io/channel.c
@@ -91,20 +91,48 @@ int qio_channel_readv_all_eof(QIOChannel *ioc,
                               const struct iovec *iov,
                               size_t niov,
                               Error **errp)
+{
+    return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, errp);
+}
+
+int qio_channel_readv_all(QIOChannel *ioc,
+                          const struct iovec *iov,
+                          size_t niov,
+                          Error **errp)
+{
+    return qio_channel_readv_full_all(ioc, iov, niov, NULL, NULL, errp);
+}
+
+int qio_channel_readv_full_all_eof(QIOChannel *ioc,
+                                   const struct iovec *iov,
+                                   size_t niov,
+                                   int **fds, size_t *nfds,
+                                   Error **errp)
 {
     int ret = -1;
     struct iovec *local_iov = g_new(struct iovec, niov);
     struct iovec *local_iov_head = local_iov;
     unsigned int nlocal_iov = niov;
+    int **local_fds = fds;
+    size_t *local_nfds = nfds;
     bool partial = false;
 
+    if (nfds) {
+        *nfds = 0;
+    }
+
+    if (fds) {
+        *fds = NULL;
+    }
+
     nlocal_iov = iov_copy(local_iov, nlocal_iov,
                           iov, niov,
                           0, iov_size(iov, niov));
 
-    while (nlocal_iov > 0) {
+    while ((nlocal_iov > 0) || local_fds) {
         ssize_t len;
-        len = qio_channel_readv(ioc, local_iov, nlocal_iov, errp);
+        len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds,
+                                     local_nfds, errp);
         if (len == QIO_CHANNEL_ERR_BLOCK) {
             if (qemu_in_coroutine()) {
                 qio_channel_yield(ioc, G_IO_IN);
@@ -112,20 +140,50 @@ int qio_channel_readv_all_eof(QIOChannel *ioc,
                 qio_channel_wait(ioc, G_IO_IN);
             }
             continue;
-        } else if (len < 0) {
-            goto cleanup;
-        } else if (len == 0) {
-            if (partial) {
-                error_setg(errp,
-                           "Unexpected end-of-file before all bytes were read");
-            } else {
+        }
+
+        if (len == 0) {
+            if (local_nfds && *local_nfds) {
+                /*
+                 * Got some FDs, but no data yet. This isn't an EOF
+                 * scenario (yet), so carry on to try to read data
+                 * on next loop iteration
+                 */
+                goto next_iter;
+            } else if (!partial) {
+                /* No fds and no data - EOF before any data read */
                 ret = 0;
+                goto cleanup;
+            } else {
+                len = -1;
+                error_setg(errp,
+                           "Unexpected end-of-file before all data were read");
+                /* Fallthrough into len < 0 handling */
+            }
+        }
+
+        if (len < 0) {
+            /* Close any FDs we previously received */
+            if (nfds && fds) {
+                size_t i;
+                for (i = 0; i < (*nfds); i++) {
+                    close((*fds)[i]);
+                }
+                g_free(*fds);
+                *fds = NULL;
+                *nfds = 0;
             }
             goto cleanup;
         }
 
+        if (nlocal_iov) {
+            iov_discard_front(&local_iov, &nlocal_iov, len);
+        }
+
+next_iter:
         partial = true;
-        iov_discard_front(&local_iov, &nlocal_iov, len);
+        local_fds = NULL;
+        local_nfds = NULL;
     }
 
     ret = 1;
@@ -135,20 +193,23 @@ int qio_channel_readv_all_eof(QIOChannel *ioc,
     return ret;
 }
 
-int qio_channel_readv_all(QIOChannel *ioc,
-                          const struct iovec *iov,
-                          size_t niov,
-                          Error **errp)
+int qio_channel_readv_full_all(QIOChannel *ioc,
+                               const struct iovec *iov,
+                               size_t niov,
+                               int **fds, size_t *nfds,
+                               Error **errp)
 {
-    int ret = qio_channel_readv_all_eof(ioc, iov, niov, errp);
+    int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp);
 
     if (ret == 0) {
-        ret = -1;
-        error_setg(errp,
-                   "Unexpected end-of-file before all bytes were read");
-    } else if (ret == 1) {
-        ret = 0;
+        error_prepend(errp,
+                      "Unexpected end-of-file before all data were read.");
+        return -1;
     }
+    if (ret == 1) {
+        return 0;
+    }
+
     return ret;
 }
 
-- 
2.29.2

Re: [PULL v4 14/27] io: add qio_channel_readv_full_all_eof & qio_channel_readv_full_all helpers
Posted by Max Reitz 4 years, 2 months ago
On 10.02.21 10:26, Stefan Hajnoczi wrote:
> From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> 
> Adds qio_channel_readv_full_all_eof() and qio_channel_readv_full_all()
> to read both data and FDs. Refactors existing code to use these helpers.
> 
> Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> Acked-by: Daniel P. Berrangé <berrange@redhat.com>
> Message-id: b059c4cc0fb741e794d644c144cc21372cad877d.1611938319.git.jag.raman@oracle.com
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> ---
>   include/io/channel.h |  53 +++++++++++++++++++++++
>   io/channel.c         | 101 ++++++++++++++++++++++++++++++++++---------
>   2 files changed, 134 insertions(+), 20 deletions(-)

[...]

> diff --git a/io/channel.c b/io/channel.c
> index 0d4b8b5160..4555021b62 100644
> --- a/io/channel.c
> +++ b/io/channel.c

[...]

> @@ -135,20 +193,23 @@ int qio_channel_readv_all_eof(QIOChannel *ioc,
>       return ret;
>   }
>   
> -int qio_channel_readv_all(QIOChannel *ioc,
> -                          const struct iovec *iov,
> -                          size_t niov,
> -                          Error **errp)
> +int qio_channel_readv_full_all(QIOChannel *ioc,
> +                               const struct iovec *iov,
> +                               size_t niov,
> +                               int **fds, size_t *nfds,
> +                               Error **errp)
>   {
> -    int ret = qio_channel_readv_all_eof(ioc, iov, niov, errp);
> +    int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp);
>   
>       if (ret == 0) {
> -        ret = -1;
> -        error_setg(errp,
> -                   "Unexpected end-of-file before all bytes were read");
> -    } else if (ret == 1) {
> -        ret = 0;
> +        error_prepend(errp,
> +                      "Unexpected end-of-file before all data were read.");
> +        return -1;

This change breaks iotest 083 (i.e., it segfaults), because 
qio_channel_readv_full_all_eof() doesn’t set *errp when it returns 0, so 
there is no error to prepend.

Also, I think usually we don’t let error messages end in full stops.

Max


Re: [PULL v4 14/27] io: add qio_channel_readv_full_all_eof & qio_channel_readv_full_all helpers
Posted by Daniel P. Berrangé 4 years, 2 months ago
On Thu, Feb 11, 2021 at 04:34:40PM +0100, Max Reitz wrote:
> On 10.02.21 10:26, Stefan Hajnoczi wrote:
> > From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > 
> > Adds qio_channel_readv_full_all_eof() and qio_channel_readv_full_all()
> > to read both data and FDs. Refactors existing code to use these helpers.
> > 
> > Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> > Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> > Acked-by: Daniel P. Berrangé <berrange@redhat.com>
> > Message-id: b059c4cc0fb741e794d644c144cc21372cad877d.1611938319.git.jag.raman@oracle.com
> > Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> > ---
> >   include/io/channel.h |  53 +++++++++++++++++++++++
> >   io/channel.c         | 101 ++++++++++++++++++++++++++++++++++---------
> >   2 files changed, 134 insertions(+), 20 deletions(-)
> 
> [...]
> 
> > diff --git a/io/channel.c b/io/channel.c
> > index 0d4b8b5160..4555021b62 100644
> > --- a/io/channel.c
> > +++ b/io/channel.c
> 
> [...]
> 
> > @@ -135,20 +193,23 @@ int qio_channel_readv_all_eof(QIOChannel *ioc,
> >       return ret;
> >   }
> > -int qio_channel_readv_all(QIOChannel *ioc,
> > -                          const struct iovec *iov,
> > -                          size_t niov,
> > -                          Error **errp)
> > +int qio_channel_readv_full_all(QIOChannel *ioc,
> > +                               const struct iovec *iov,
> > +                               size_t niov,
> > +                               int **fds, size_t *nfds,
> > +                               Error **errp)
> >   {
> > -    int ret = qio_channel_readv_all_eof(ioc, iov, niov, errp);
> > +    int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp);
> >       if (ret == 0) {
> > -        ret = -1;
> > -        error_setg(errp,
> > -                   "Unexpected end-of-file before all bytes were read");
> > -    } else if (ret == 1) {
> > -        ret = 0;
> > +        error_prepend(errp,
> > +                      "Unexpected end-of-file before all data were read.");
> > +        return -1;
> 
> This change breaks iotest 083 (i.e., it segfaults), because
> qio_channel_readv_full_all_eof() doesn’t set *errp when it returns 0, so
> there is no error to prepend.

Opps, yes, this needs to be error_setg() not error_prepend()


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


Re: [PULL v4 14/27] io: add qio_channel_readv_full_all_eof & qio_channel_readv_full_all helpers
Posted by Jag Raman 4 years, 2 months ago
> On Feb 11, 2021, at 10:46 AM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> 
> On Thu, Feb 11, 2021 at 04:34:40PM +0100, Max Reitz wrote:
>> On 10.02.21 10:26, Stefan Hajnoczi wrote:
>>> From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
>>> 
>>> Adds qio_channel_readv_full_all_eof() and qio_channel_readv_full_all()
>>> to read both data and FDs. Refactors existing code to use these helpers.
>>> 
>>> Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
>>> Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
>>> Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
>>> Acked-by: Daniel P. Berrangé <berrange@redhat.com>
>>> Message-id: b059c4cc0fb741e794d644c144cc21372cad877d.1611938319.git.jag.raman@oracle.com
>>> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
>>> ---
>>>  include/io/channel.h |  53 +++++++++++++++++++++++
>>>  io/channel.c         | 101 ++++++++++++++++++++++++++++++++++---------
>>>  2 files changed, 134 insertions(+), 20 deletions(-)
>> 
>> [...]
>> 
>>> diff --git a/io/channel.c b/io/channel.c
>>> index 0d4b8b5160..4555021b62 100644
>>> --- a/io/channel.c
>>> +++ b/io/channel.c
>> 
>> [...]
>> 
>>> @@ -135,20 +193,23 @@ int qio_channel_readv_all_eof(QIOChannel *ioc,
>>>      return ret;
>>>  }
>>> -int qio_channel_readv_all(QIOChannel *ioc,
>>> -                          const struct iovec *iov,
>>> -                          size_t niov,
>>> -                          Error **errp)
>>> +int qio_channel_readv_full_all(QIOChannel *ioc,
>>> +                               const struct iovec *iov,
>>> +                               size_t niov,
>>> +                               int **fds, size_t *nfds,
>>> +                               Error **errp)
>>>  {
>>> -    int ret = qio_channel_readv_all_eof(ioc, iov, niov, errp);
>>> +    int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp);
>>>      if (ret == 0) {
>>> -        ret = -1;
>>> -        error_setg(errp,
>>> -                   "Unexpected end-of-file before all bytes were read");
>>> -    } else if (ret == 1) {
>>> -        ret = 0;
>>> +        error_prepend(errp,
>>> +                      "Unexpected end-of-file before all data were read.");
>>> +        return -1;
>> 
>> This change breaks iotest 083 (i.e., it segfaults), because
>> qio_channel_readv_full_all_eof() doesn’t set *errp when it returns 0, so
>> there is no error to prepend.
> 
> Opps, yes, this needs to be error_setg() not error_prepend()

Hi Max & Daniel,

We will send a patch shortly to address this issue.

Thank you very much!

> 
> 
> Regards,
> Daniel
> -- 
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
>