On 02/07/13 09:55, Michael S. Tsirkin wrote:
> On Tue, Feb 05, 2013 at 05:47:16PM -0600, Jesse Larrew wrote:
>> Currently, the config size for virtio devices is hard coded. When a new
>> feature is added that changes the config size, drivers that assume a static
>> config size will break. For purposes of backward compatibility, there needs
>> to be a way to inform drivers of the config size needed to accommodate the
>> set of features enabled.
>>
>> Signed-off-by: Jesse Larrew <jlar...@linux.vnet.ibm.com>
>> ---
>>  hw/virtio-net.c | 44 ++++++++++++++++++++++++++++++++++++--------
>>  1 file changed, 36 insertions(+), 8 deletions(-)
>>
>> diff --git a/hw/virtio-net.c b/hw/virtio-net.c
>> index f1c2884..8f521b3 100644
>> --- a/hw/virtio-net.c
>> +++ b/hw/virtio-net.c
>> @@ -73,8 +73,31 @@ typedef struct VirtIONet
>>      int multiqueue;
>>      uint16_t max_queues;
>>      uint16_t curr_queues;
>> +    int config_size;
>>  } VirtIONet;
>>  
>> +/*
>> + * Calculate the number of bytes up to and including the given 'field' of
>> + * 'container'.
>> + */
>> +#define endof(container, field) \
>> +    ((intptr_t)(&(((container *)0)->field)+1))
> 
> Hmm I'm worried whether it's legal to take a pointer to a
> field in a packed structure.

Yes, it is.

(a) Padding inside structures is in the compiler's jurisdiction (except
before the first member, where it must be 0 -- C99 6.7.2.1p13). If you
tell the compiler to pack members, then it's your responsibility to make
sure no problems will come from accesses to non-naturally aligned fields.

(b) If the member is not a bitfield, you can take its address (C99
6.5.3.2p1). Since these unaligned fields are accessed *anyway* without
problems in practice, it's safe to dereference their addresses in practice.

IOW, purely by these fields being unaligned and already well-accessible
in practice, you don't commit a greater crime by taking their addresses.


Furthermore, taking the pointer to "one past" &field, ie. (&field + 1),
is safe, provided &field itself is well-defined. This comes from two
bits in the ISO C standard, sloppily paraphrased:

- Given an array with N elements, it's allowed to form a pointer to one
past the last element in it. IOW, &array[N], or (array + N), or
(&array[0] + N). You can't dereference it, but the pointer itself is
valid. (C99 6.5.6p8-9.)

- For the purposes of pointer arithmetic, (&x) works like (&x_array[0]),
where x is an object of type X, outside of an array, and x_array has
element type X and N=1 element. (C99 6.5.6p7.)

Since you're allowed to evaluate ((&x_array[0]) + 1), you're also
allowed to eval ((&x) + 1).


Anyway the above expression could be reworked for more portability with
offsetof and sizeof. As-is, it contains a pointer-to-non-void to
intptr_t conversion; what's more, with the resultant integer value meant
to be used numerically later on (ie. not for converting it back to
(void*), C99 7.18.1.4p). (The use of the null pointer is valid, the
dereference is not evaluated because it's the operand of &, C99
6.5.2.3p4 and 6.5.3.2p3.)

Instead, what about

#define endof(container, field) \
    (offsetof(container, field) + sizeof ((container *)0)->field)

Laszlo

Reply via email to