[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-block] [Qemu-devel] [PATCH 1/2] block: enhance QEMUIOVector st

From: Eric Blake
Subject: Re: [Qemu-block] [Qemu-devel] [PATCH 1/2] block: enhance QEMUIOVector structure
Date: Wed, 30 Jan 2019 08:55:18 -0600
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.4.0

On 1/30/19 3:14 AM, Vladimir Sementsov-Ogievskiy wrote:
> 30.01.2019 0:35, Eric Blake wrote:
>> On 1/29/19 8:37 AM, Vladimir Sementsov-Ogievskiy wrote:
>>> Add a possibility of embedded iovec, for cases when we need only one
>>> local iov.
>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <address@hidden>
>>> ---
>>>   include/qemu/iov.h | 41 ++++++++++++++++++++++++++++++++++++++++-
>>>   1 file changed, 40 insertions(+), 1 deletion(-)

>> POSIX does not guarantee that iov_base occurs prior to iov_len in struct
>> iovec; your code is therefore rather fragile and would fail to compile
>> if we encounter a libc where iov_len is positioned differently than in
>> glibc, tripping the first assertion.  For an offset other than 0, you
>> could declare:
>>    char [offsetof(struct iovec, iov_len)] __pad;
>> to be more robust; but since C doesn't like 0-length array declarations,
>> I'm not sure how you would cater to a libc where iov_len is at offset 0,
> Hmm, tested a bit, and the only complain I can get is a warning about 0-sized
> array when option -pedantic is set. So, is it a real problem?

Even if we require the use of gcc extensions elsewhere, it doesn't hurt
to avoid them where it is easy.

> And we already have a lot of zero-sized arrays in Qemu, just look at
>   git grep '\w\+\s\+\w\+\[0\];' | grep -v return

And how many of those are the last entry in a struct?  A 0-size array at
the end is a common idiom for a flex-sized struct (without relying on
C99 VLA); a 0-size array in the middle of a struct is unusual.

> hm, or we can do like this:
> typedef struct QEMUIOVector {
>      struct iovec *iov;
>      int niov;
>      union {
>          struct {
>              int nalloc;
>              struct iovec local_iov;
>          };
>          struct {
>              char __pad[sizeof(int) + offsetof(struct iovec, iov_len)];
>              size_t size;
>          };
>      };
> } QEMUIOVector;

Yes, that's a reasonable way to do it.

>> short of a configure probe and #ifdeffery, which feels like a bit much
>> to manage (really, the point of the assert is that we don't have to
>> worry about an unusual libc having a different offset UNLESS the build
>> fails, so no need to address a problem we aren't hitting yet).
> However, char __pad[offsetof(struct iovec, iov_len)]; seems useful and even
> more self-documented, so I'll use it in v2, if nobody minds.

We'll see how it looks, then.

>>> +        .iov_base = (void *)(buf),          \
>> Why is the cast necessary?  Are we casting away const?

Answered in 2/2 - yes. But a comment about /* cast away const */ is useful.

>>  Since C already
>> allows assignment of any other (non-const) pointer to void* without a
>> cast, it looks superfluous.
>>> +        .iov_len = len                      \
>> Missing () around len.
> For style? What the thing len should be to break it without ()?

Although we are unlikely to hit this given our coding conventions,
remember that '=' has higher precedence than ',', for this contorted
example (and yes, it's contorted because you can't pass the comma
operator through to a macro without either supplying your own (), which
defeats the demonstration, or relying on a helper macro such as
raw_pair() here):

$ cat foo.c
struct s {
    int i;
#define raw_pair(a, b) a, b
#define A(arg) { .i = (arg) }
#define B(arg) { .i = arg }

main(int argc, char **argv)
    struct s s1 = A(raw_pair(1, 2));
    struct s s2 = B(raw_pair(4, 8));
    return s1.i + s2.i;
$ gcc -o foo foo.c
foo.c: In function ‘main’:
foo.c:12:33: warning: excess elements in struct initializer
     struct s s2 = B(raw_pair(4, 8));
foo.c:6:23: note: in definition of macro ‘B’
 #define B(arg) { .i = arg }
foo.c:12:21: note: in expansion of macro ‘raw_pair’
     struct s s2 = B(raw_pair(4, 8));
foo.c:12:33: note: (near initialization for ‘s2’)
     struct s s2 = B(raw_pair(4, 8));
foo.c:6:23: note: in definition of macro ‘B’
 #define B(arg) { .i = arg }
foo.c:12:21: note: in expansion of macro ‘raw_pair’
     struct s s2 = B(raw_pair(4, 8));
$ ./foo; echo $?

which says that for A() using the (), there were no warnings and the
second value was assigned (-Wall changes that, complaining about unused
value of the left side of the comma operator - but that's okay); but for
B() without (), there was a diagnostic even without -Wall about too many
initializers, and the first value was assigned.

>> Why both a macro and a function that uses the macro? 

Also answered in 2/2 - the macro is for variable declarations; the
function is for runtime code such as for-loops that start with an
uninitialized declaration and have to reinitialize things. They also
differ in that one takes a struct, the other takes a pointer to a struct.

Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org

Attachment: signature.asc
Description: OpenPGP digital signature

reply via email to

[Prev in Thread] Current Thread [Next in Thread]