diff mbox series

[v19,08/20] io: add qio_channel_readv_full_all_eof & qio_channel_readv_full_all helpers

Message ID 02a82c80a35ab60b98028c85aa94f688a2843943.1610638428.git.jag.raman@oracle.com (mailing list archive)
State New, archived
Headers show
Series Initial support for multi-process Qemu | expand

Commit Message

Jag Raman Jan. 14, 2021, 3:40 p.m. UTC
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>
---
 include/io/channel.h | 51 +++++++++++++++++++++++++++++++
 io/channel.c         | 85 ++++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 120 insertions(+), 16 deletions(-)

Comments

Stefan Hajnoczi Jan. 14, 2021, 4:27 p.m. UTC | #1
On Thu, Jan 14, 2021 at 10:40:03AM -0500, Jagannathan Raman wrote:
> +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");

qio_channel_readv_full_all_eof() can read file descriptors but no data
and return 0.

Here that case is converted into an error and the file descriptors
aren't closed, freed, and fds/nfds isn't cleared.
Jag Raman Jan. 14, 2021, 5:55 p.m. UTC | #2
> On Jan 14, 2021, at 11:27 AM, Stefan Hajnoczi <stefanha@redhat.com> wrote:
> 
> On Thu, Jan 14, 2021 at 10:40:03AM -0500, Jagannathan Raman wrote:
>> +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");
> 
> qio_channel_readv_full_all_eof() can read file descriptors but no data
> and return 0.
> 
> Here that case is converted into an error and the file descriptors
> aren't closed, freed, and fds/nfds isn't cleared.

That’s a valid point. I’m wondering if the fix for this case should be in
qio_channel_readv_full_all_eof(), instead of here.

qio_channel_readv_full_all_eof() should probably return error (-1) if the
amount of data read does not match iov_size(). If the caller is only expecting
to read fds, and not any data, it would indicate that by setting iov to NULL
and/or setting niov=0. If the caller is setting these parameters, it means it is
expecting data.Does that sound good?

Thanks!
Daniel P. Berrangé Jan. 14, 2021, 6 p.m. UTC | #3
On Thu, Jan 14, 2021 at 12:55:58PM -0500, Jag Raman wrote:
> 
> 
> > On Jan 14, 2021, at 11:27 AM, Stefan Hajnoczi <stefanha@redhat.com> wrote:
> > 
> > On Thu, Jan 14, 2021 at 10:40:03AM -0500, Jagannathan Raman wrote:
> >> +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");
> > 
> > qio_channel_readv_full_all_eof() can read file descriptors but no data
> > and return 0.
> > 
> > Here that case is converted into an error and the file descriptors
> > aren't closed, freed, and fds/nfds isn't cleared.
> 
> That’s a valid point. I’m wondering if the fix for this case should be in
> qio_channel_readv_full_all_eof(), instead of here.
> 
> qio_channel_readv_full_all_eof() should probably return error (-1) if the
> amount of data read does not match iov_size(). If the caller is only expecting
> to read fds, and not any data, it would indicate that by setting iov to NULL
> and/or setting niov=0. If the caller is setting these parameters, it means it is
> expecting data.Does that sound good?

The API spec for the existing _eof() methods says:

 * The function will wait for all requested data
 * to be read, yielding from the current coroutine
 * if required.
 *
 * If end-of-file occurs before any data is read,
 * no error is reported; otherwise, if it occurs
 * before all requested data has been read, an error
 * will be reported.


IOW, return '0' is *only* valid if we've not read anything. I consider
file descriptors to be something.

IOW, qio_channel_readv_full_all_eof must only return 0, if it didn't
read any data and also didn't receive any file descriptors. So yeah,
we must return -1 in the scenario Stefan describes

Regards,
Daniel
Jag Raman Jan. 14, 2021, 6:24 p.m. UTC | #4
> On Jan 14, 2021, at 1:00 PM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> 
> On Thu, Jan 14, 2021 at 12:55:58PM -0500, Jag Raman wrote:
>> 
>> 
>>> On Jan 14, 2021, at 11:27 AM, Stefan Hajnoczi <stefanha@redhat.com> wrote:
>>> 
>>> On Thu, Jan 14, 2021 at 10:40:03AM -0500, Jagannathan Raman wrote:
>>>> +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");
>>> 
>>> qio_channel_readv_full_all_eof() can read file descriptors but no data
>>> and return 0.
>>> 
>>> Here that case is converted into an error and the file descriptors
>>> aren't closed, freed, and fds/nfds isn't cleared.
>> 
>> That’s a valid point. I’m wondering if the fix for this case should be in
>> qio_channel_readv_full_all_eof(), instead of here.
>> 
>> qio_channel_readv_full_all_eof() should probably return error (-1) if the
>> amount of data read does not match iov_size(). If the caller is only expecting
>> to read fds, and not any data, it would indicate that by setting iov to NULL
>> and/or setting niov=0. If the caller is setting these parameters, it means it is
>> expecting data.Does that sound good?
> 
> The API spec for the existing _eof() methods says:
> 
> * The function will wait for all requested data
> * to be read, yielding from the current coroutine
> * if required.
> *
> * If end-of-file occurs before any data is read,
> * no error is reported; otherwise, if it occurs
> * before all requested data has been read, an error
> * will be reported.
> 
> 
> IOW, return '0' is *only* valid if we've not read anything. I consider
> file descriptors to be something.
> 
> IOW, qio_channel_readv_full_all_eof must only return 0, if it didn't
> read any data and also didn't receive any file descriptors. So yeah,
> we must return -1 in the scenario Stefan describes

That makes sense to me. Reading “fds" is something, which is different
from our previous understanding. I thought data only meant iov, and not fds.

So the return values for qio_channel_readv_full_all_eof() would be:
  - ‘0’ only if EOF is reached without reading any fds and data.
  - ‘1’ if all data that the caller expects are read (even if the caller reads
    fds exclusively, without any iovs)
  - ‘-1’ otherwise, considered as error

qio_channel_readv_full_all() would return:
  - ‘0’ if all the data that caller expects are read
  - ‘-1’ otherwise, considered as error

Hey Stefan,

    Does this sound good to you?

Thank you!
--
Jag

> 
> 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 :|
>
Stefan Hajnoczi Jan. 15, 2021, 9:20 a.m. UTC | #5
On Thu, Jan 14, 2021 at 01:24:37PM -0500, Jag Raman wrote:
> 
> 
> > On Jan 14, 2021, at 1:00 PM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> > 
> > On Thu, Jan 14, 2021 at 12:55:58PM -0500, Jag Raman wrote:
> >> 
> >> 
> >>> On Jan 14, 2021, at 11:27 AM, Stefan Hajnoczi <stefanha@redhat.com> wrote:
> >>> 
> >>> On Thu, Jan 14, 2021 at 10:40:03AM -0500, Jagannathan Raman wrote:
> >>>> +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");
> >>> 
> >>> qio_channel_readv_full_all_eof() can read file descriptors but no data
> >>> and return 0.
> >>> 
> >>> Here that case is converted into an error and the file descriptors
> >>> aren't closed, freed, and fds/nfds isn't cleared.
> >> 
> >> That’s a valid point. I’m wondering if the fix for this case should be in
> >> qio_channel_readv_full_all_eof(), instead of here.
> >> 
> >> qio_channel_readv_full_all_eof() should probably return error (-1) if the
> >> amount of data read does not match iov_size(). If the caller is only expecting
> >> to read fds, and not any data, it would indicate that by setting iov to NULL
> >> and/or setting niov=0. If the caller is setting these parameters, it means it is
> >> expecting data.Does that sound good?
> > 
> > The API spec for the existing _eof() methods says:
> > 
> > * The function will wait for all requested data
> > * to be read, yielding from the current coroutine
> > * if required.
> > *
> > * If end-of-file occurs before any data is read,
> > * no error is reported; otherwise, if it occurs
> > * before all requested data has been read, an error
> > * will be reported.
> > 
> > 
> > IOW, return '0' is *only* valid if we've not read anything. I consider
> > file descriptors to be something.
> > 
> > IOW, qio_channel_readv_full_all_eof must only return 0, if it didn't
> > read any data and also didn't receive any file descriptors. So yeah,
> > we must return -1 in the scenario Stefan describes
> 
> That makes sense to me. Reading “fds" is something, which is different
> from our previous understanding. I thought data only meant iov, and not fds.
> 
> So the return values for qio_channel_readv_full_all_eof() would be:
>   - ‘0’ only if EOF is reached without reading any fds and data.
>   - ‘1’ if all data that the caller expects are read (even if the caller reads
>     fds exclusively, without any iovs)
>   - ‘-1’ otherwise, considered as error
> 
> qio_channel_readv_full_all() would return:
>   - ‘0’ if all the data that caller expects are read
>   - ‘-1’ otherwise, considered as error
> 
> Hey Stefan,
> 
>     Does this sound good to you?

The while (nlocal_iov > 0) loop only runs if the caller has requested to
read at least some data, so the fds-only case doesn't work yet.

This suggests that no current QEMU code relies on the fds-only case.
Therefore you could change the doc comment to clarify this instead of
adding support for this case to the code.

But if you would to fully support the fds-only case that would be even
better.

Stefan
Jag Raman Jan. 15, 2021, 1:46 p.m. UTC | #6
> On Jan 15, 2021, at 4:20 AM, Stefan Hajnoczi <stefanha@redhat.com> wrote:
> 
> On Thu, Jan 14, 2021 at 01:24:37PM -0500, Jag Raman wrote:
>> 
>> 
>>> On Jan 14, 2021, at 1:00 PM, Daniel P. Berrangé <berrange@redhat.com> wrote:
>>> 
>>> On Thu, Jan 14, 2021 at 12:55:58PM -0500, Jag Raman wrote:
>>>> 
>>>> 
>>>>> On Jan 14, 2021, at 11:27 AM, Stefan Hajnoczi <stefanha@redhat.com> wrote:
>>>>> 
>>>>> On Thu, Jan 14, 2021 at 10:40:03AM -0500, Jagannathan Raman wrote:
>>>>>> +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");
>>>>> 
>>>>> qio_channel_readv_full_all_eof() can read file descriptors but no data
>>>>> and return 0.
>>>>> 
>>>>> Here that case is converted into an error and the file descriptors
>>>>> aren't closed, freed, and fds/nfds isn't cleared.
>>>> 
>>>> That’s a valid point. I’m wondering if the fix for this case should be in
>>>> qio_channel_readv_full_all_eof(), instead of here.
>>>> 
>>>> qio_channel_readv_full_all_eof() should probably return error (-1) if the
>>>> amount of data read does not match iov_size(). If the caller is only expecting
>>>> to read fds, and not any data, it would indicate that by setting iov to NULL
>>>> and/or setting niov=0. If the caller is setting these parameters, it means it is
>>>> expecting data.Does that sound good?
>>> 
>>> The API spec for the existing _eof() methods says:
>>> 
>>> * The function will wait for all requested data
>>> * to be read, yielding from the current coroutine
>>> * if required.
>>> *
>>> * If end-of-file occurs before any data is read,
>>> * no error is reported; otherwise, if it occurs
>>> * before all requested data has been read, an error
>>> * will be reported.
>>> 
>>> 
>>> IOW, return '0' is *only* valid if we've not read anything. I consider
>>> file descriptors to be something.
>>> 
>>> IOW, qio_channel_readv_full_all_eof must only return 0, if it didn't
>>> read any data and also didn't receive any file descriptors. So yeah,
>>> we must return -1 in the scenario Stefan describes
>> 
>> That makes sense to me. Reading “fds" is something, which is different
>> from our previous understanding. I thought data only meant iov, and not fds.
>> 
>> So the return values for qio_channel_readv_full_all_eof() would be:
>>  - ‘0’ only if EOF is reached without reading any fds and data.
>>  - ‘1’ if all data that the caller expects are read (even if the caller reads
>>    fds exclusively, without any iovs)
>>  - ‘-1’ otherwise, considered as error
>> 
>> qio_channel_readv_full_all() would return:
>>  - ‘0’ if all the data that caller expects are read
>>  - ‘-1’ otherwise, considered as error
>> 
>> Hey Stefan,
>> 
>>    Does this sound good to you?
> 
> The while (nlocal_iov > 0) loop only runs if the caller has requested to
> read at least some data, so the fds-only case doesn't work yet.
> 
> This suggests that no current QEMU code relies on the fds-only case.
> Therefore you could change the doc comment to clarify this instead of
> adding support for this case to the code.
> 
> But if you would to fully support the fds-only case that would be even
> better.

Thanks for confirming, Stefan!

Since we are adding fds support in this patch, I suppose we could add
the fds-only case since you feel that’s a better approach.

I believe it’s reasonable to assume that the caller wants to read file
descriptors if it sets the fds double-pointer. I think it would be reasonable
to wait for data if the caller expects to read either data or fds, as follows:
while ((nlocal_iov > 0) || local_fds)

Thank you! 

> 
> Stefan
Jag Raman Jan. 15, 2021, 6:19 p.m. UTC | #7
> On Jan 15, 2021, at 4:20 AM, Stefan Hajnoczi <stefanha@redhat.com> wrote:
> 
> On Thu, Jan 14, 2021 at 01:24:37PM -0500, Jag Raman wrote:
>> 
>> 
>>> On Jan 14, 2021, at 1:00 PM, Daniel P. Berrangé <berrange@redhat.com> wrote:
>>> 
>>> On Thu, Jan 14, 2021 at 12:55:58PM -0500, Jag Raman wrote:
>>>> 
>>>> 
>>>>> On Jan 14, 2021, at 11:27 AM, Stefan Hajnoczi <stefanha@redhat.com> wrote:
>>>>> 
>>>>> On Thu, Jan 14, 2021 at 10:40:03AM -0500, Jagannathan Raman wrote:
>>>>>> +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");
>>>>> 
>>>>> qio_channel_readv_full_all_eof() can read file descriptors but no data
>>>>> and return 0.
>>>>> 
>>>>> Here that case is converted into an error and the file descriptors
>>>>> aren't closed, freed, and fds/nfds isn't cleared.
>>>> 
>>>> That’s a valid point. I’m wondering if the fix for this case should be in
>>>> qio_channel_readv_full_all_eof(), instead of here.
>>>> 
>>>> qio_channel_readv_full_all_eof() should probably return error (-1) if the
>>>> amount of data read does not match iov_size(). If the caller is only expecting
>>>> to read fds, and not any data, it would indicate that by setting iov to NULL
>>>> and/or setting niov=0. If the caller is setting these parameters, it means it is
>>>> expecting data.Does that sound good?
>>> 
>>> The API spec for the existing _eof() methods says:
>>> 
>>> * The function will wait for all requested data
>>> * to be read, yielding from the current coroutine
>>> * if required.
>>> *
>>> * If end-of-file occurs before any data is read,
>>> * no error is reported; otherwise, if it occurs
>>> * before all requested data has been read, an error
>>> * will be reported.
>>> 
>>> 
>>> IOW, return '0' is *only* valid if we've not read anything. I consider
>>> file descriptors to be something.
>>> 
>>> IOW, qio_channel_readv_full_all_eof must only return 0, if it didn't
>>> read any data and also didn't receive any file descriptors. So yeah,
>>> we must return -1 in the scenario Stefan describes
>> 
>> That makes sense to me. Reading “fds" is something, which is different
>> from our previous understanding. I thought data only meant iov, and not fds.
>> 
>> So the return values for qio_channel_readv_full_all_eof() would be:
>>  - ‘0’ only if EOF is reached without reading any fds and data.
>>  - ‘1’ if all data that the caller expects are read (even if the caller reads
>>    fds exclusively, without any iovs)
>>  - ‘-1’ otherwise, considered as error
>> 
>> qio_channel_readv_full_all() would return:
>>  - ‘0’ if all the data that caller expects are read
>>  - ‘-1’ otherwise, considered as error
>> 
>> Hey Stefan,
>> 
>>    Does this sound good to you?
> 
> The while (nlocal_iov > 0) loop only runs if the caller has requested to
> read at least some data, so the fds-only case doesn't work yet.
> 
> This suggests that no current QEMU code relies on the fds-only case.
> Therefore you could change the doc comment to clarify this instead of
> adding support for this case to the code.
> 
> But if you would to fully support the fds-only case that would be even
> better.
> 
> Stefan

We are working on sending the next revision out. We could handle the
fds-only case by altering the while loop condition to be:
((nlocal_iov > 0) || local_fds)

For reference, we would need to handle the following cases:
len < 0; !partial, !*nfds       => ret = -1;
len = 0; !partial, !*nfds       => ret = 0;
len < 0; partial, !*nfds        => ret = -1; errmsg;
len = 0; partial, !*nfds        => ret = -1; errmsg;
len < 0; partial, *nfds         => ret = -1; errmsg, clearfds
len < 0; !partial, *nfds        => ret = -1; errmsg, clearfds
len = 0; partial, *nfds         => ret = -1; errmsg, clearfds
len = 0; !partial, *nfds        => ret = -1; errmsg, clearfds
len = 0; !niov; (nfds && *nfds) => ret = 1 /* fds-only */
len > 0                         => ret 1

Thank you!
--
Jag
Stefan Hajnoczi Jan. 18, 2021, 4:35 p.m. UTC | #8
On Fri, Jan 15, 2021 at 01:19:01PM -0500, Jag Raman wrote:
> 
> 
> > On Jan 15, 2021, at 4:20 AM, Stefan Hajnoczi <stefanha@redhat.com> wrote:
> > 
> > On Thu, Jan 14, 2021 at 01:24:37PM -0500, Jag Raman wrote:
> >> 
> >> 
> >>> On Jan 14, 2021, at 1:00 PM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> >>> 
> >>> On Thu, Jan 14, 2021 at 12:55:58PM -0500, Jag Raman wrote:
> >>>> 
> >>>> 
> >>>>> On Jan 14, 2021, at 11:27 AM, Stefan Hajnoczi <stefanha@redhat.com> wrote:
> >>>>> 
> >>>>> On Thu, Jan 14, 2021 at 10:40:03AM -0500, Jagannathan Raman wrote:
> >>>>>> +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");
> >>>>> 
> >>>>> qio_channel_readv_full_all_eof() can read file descriptors but no data
> >>>>> and return 0.
> >>>>> 
> >>>>> Here that case is converted into an error and the file descriptors
> >>>>> aren't closed, freed, and fds/nfds isn't cleared.
> >>>> 
> >>>> That’s a valid point. I’m wondering if the fix for this case should be in
> >>>> qio_channel_readv_full_all_eof(), instead of here.
> >>>> 
> >>>> qio_channel_readv_full_all_eof() should probably return error (-1) if the
> >>>> amount of data read does not match iov_size(). If the caller is only expecting
> >>>> to read fds, and not any data, it would indicate that by setting iov to NULL
> >>>> and/or setting niov=0. If the caller is setting these parameters, it means it is
> >>>> expecting data.Does that sound good?
> >>> 
> >>> The API spec for the existing _eof() methods says:
> >>> 
> >>> * The function will wait for all requested data
> >>> * to be read, yielding from the current coroutine
> >>> * if required.
> >>> *
> >>> * If end-of-file occurs before any data is read,
> >>> * no error is reported; otherwise, if it occurs
> >>> * before all requested data has been read, an error
> >>> * will be reported.
> >>> 
> >>> 
> >>> IOW, return '0' is *only* valid if we've not read anything. I consider
> >>> file descriptors to be something.
> >>> 
> >>> IOW, qio_channel_readv_full_all_eof must only return 0, if it didn't
> >>> read any data and also didn't receive any file descriptors. So yeah,
> >>> we must return -1 in the scenario Stefan describes
> >> 
> >> That makes sense to me. Reading “fds" is something, which is different
> >> from our previous understanding. I thought data only meant iov, and not fds.
> >> 
> >> So the return values for qio_channel_readv_full_all_eof() would be:
> >>  - ‘0’ only if EOF is reached without reading any fds and data.
> >>  - ‘1’ if all data that the caller expects are read (even if the caller reads
> >>    fds exclusively, without any iovs)
> >>  - ‘-1’ otherwise, considered as error
> >> 
> >> qio_channel_readv_full_all() would return:
> >>  - ‘0’ if all the data that caller expects are read
> >>  - ‘-1’ otherwise, considered as error
> >> 
> >> Hey Stefan,
> >> 
> >>    Does this sound good to you?
> > 
> > The while (nlocal_iov > 0) loop only runs if the caller has requested to
> > read at least some data, so the fds-only case doesn't work yet.
> > 
> > This suggests that no current QEMU code relies on the fds-only case.
> > Therefore you could change the doc comment to clarify this instead of
> > adding support for this case to the code.
> > 
> > But if you would to fully support the fds-only case that would be even
> > better.
> > 
> > Stefan
> 
> We are working on sending the next revision out. We could handle the
> fds-only case by altering the while loop condition to be:
> ((nlocal_iov > 0) || local_fds)
> 
> For reference, we would need to handle the following cases:
> len < 0; !partial, !*nfds       => ret = -1;
> len = 0; !partial, !*nfds       => ret = 0;
> len < 0; partial, !*nfds        => ret = -1; errmsg;
> len = 0; partial, !*nfds        => ret = -1; errmsg;
> len < 0; partial, *nfds         => ret = -1; errmsg, clearfds
> len < 0; !partial, *nfds        => ret = -1; errmsg, clearfds
> len = 0; partial, *nfds         => ret = -1; errmsg, clearfds
> len = 0; !partial, *nfds        => ret = -1; errmsg, clearfds
> len = 0; !niov; (nfds && *nfds) => ret = 1 /* fds-only */
> len > 0                         => ret 1

Yes, I think that looks right.

Stefan
diff mbox series

Patch

diff --git a/include/io/channel.h b/include/io/channel.h
index 19e76fc..f725665 100644
--- a/include/io/channel.h
+++ b/include/io/channel.h
@@ -778,6 +778,57 @@  void qio_channel_set_aio_fd_handler(QIOChannel *ioc,
                                     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.
+ *
+ * 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.
+ *
+ * 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
  * @iov: the array of memory regions to write data from
diff --git a/io/channel.c b/io/channel.c
index 0d4b8b5..8d3341f 100644
--- a/io/channel.c
+++ b/io/channel.c
@@ -92,19 +92,47 @@  int qio_channel_readv_all_eof(QIOChannel *ioc,
                               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) {
         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,42 @@  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) {
+            size_t fd_idx = 0;
+
+            if ((len == 0) && !partial) {
                 ret = 0;
+                goto cleanup;
+            } else if (len == 0) {
+                error_prepend(errp,
+                              "Unexpected end-of-file before all bytes were read.");
             }
+
+            if (nfds) {
+                fd_idx = *nfds;
+                *nfds = 0;
+            }
+
+            while (fds && fd_idx) {
+                close((*fds)[fd_idx - 1]);
+                fd_idx--;
+            }
+
+            if (fds) {
+                g_free(*fds);
+                *fds = NULL;
+            }
+
             goto cleanup;
         }
 
         partial = true;
         iov_discard_front(&local_iov, &nlocal_iov, len);
+
+        local_fds = NULL;
+        local_nfds = 0;
     }
 
     ret = 1;
@@ -135,20 +185,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;
+        return -1;
     }
+    if (ret == 1) {
+        return 0;
+    }
+
     return ret;
 }