diff mbox

[V2] virtio-net: unbreak any layout

Message ID 1436946967-26349-1-git-send-email-jasowang@redhat.com
State New
Headers show

Commit Message

Jason Wang July 15, 2015, 7:56 a.m. UTC
Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
("virtio-net: byteswap virtio-net header") breaks any layout by
requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
copying header to temporary buffer if swap is needed, and then use
this buffer as part of out_sg.

Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
("virtio-net: byteswap virtio-net header")
Cc: qemu-stable@nongnu.org
Cc: clg@fr.ibm.com
Signed-off-by: Jason Wang <jasowang@redhat.com>
---
Changes from V1:
- avoid header copying if there's no need to do header swap
- don't write the header back
---
 hw/net/virtio-net.c               | 17 ++++++++++++++---
 include/hw/virtio/virtio-access.h |  9 +++++++++
 2 files changed, 23 insertions(+), 3 deletions(-)

Comments

Paolo Bonzini July 15, 2015, 8 a.m. UTC | #1
On 15/07/2015 09:56, Jason Wang wrote:
> Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
> ("virtio-net: byteswap virtio-net header") breaks any layout by
> requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
> copying header to temporary buffer if swap is needed, and then use
> this buffer as part of out_sg.
> 
> Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
> ("virtio-net: byteswap virtio-net header")
> Cc: qemu-stable@nongnu.org
> Cc: clg@fr.ibm.com
> Signed-off-by: Jason Wang <jasowang@redhat.com>
> ---
> Changes from V1:
> - avoid header copying if there's no need to do header swap
> - don't write the header back
> ---
>  hw/net/virtio-net.c               | 17 ++++++++++++++---
>  include/hw/virtio/virtio-access.h |  9 +++++++++
>  2 files changed, 23 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> index e3c2db3..12322bd 100644
> --- a/hw/net/virtio-net.c
> +++ b/hw/net/virtio-net.c
> @@ -1142,7 +1142,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>          ssize_t ret, len;
>          unsigned int out_num = elem.out_num;
>          struct iovec *out_sg = &elem.out_sg[0];
> -        struct iovec sg[VIRTQUEUE_MAX_SIZE];
> +        struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE];
> +        struct virtio_net_hdr hdr;
>  
>          if (out_num < 1) {
>              error_report("virtio-net header not in first element");
> @@ -1150,11 +1151,21 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>          }
>  
>          if (n->has_vnet_hdr) {
> -            if (out_sg[0].iov_len < n->guest_hdr_len) {
> +            if (iov_size(out_sg, out_num) < n->guest_hdr_len) {
>                  error_report("virtio-net header incorrect");
>                  exit(1);
>              }
> -            virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base);
> +            if (virtio_needs_swap(vdev)) {
> +                iov_to_buf(out_sg, out_num, 0, &hdr, sizeof(hdr));
> +                virtio_net_hdr_swap(vdev, (void *) &hdr);
> +                sg2[0].iov_base = &hdr;
> +                sg2[0].iov_len = sizeof(hdr);
> +                out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
> +                                   out_sg, out_num,
> +                                   sizeof(hdr), -1);
> +                out_num += 1;
> +                out_sg = sg2;
> +            }
>          }
>  
>          /*
> diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h
> index cee5dd7..1ec1dfd 100644
> --- a/include/hw/virtio/virtio-access.h
> +++ b/include/hw/virtio/virtio-access.h
> @@ -143,6 +143,15 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr)
>      }
>  }
>  
> +static inline bool virtio_needs_swap(VirtIODevice *vdev)
> +{
> +#ifdef HOST_WORDS_BIGENDIAN
> +    return virtio_access_is_big_endian(vdev) ? false : true;
> +#else
> +    return virtio_access_is_big_endian(vdev) ? true : false;
> +#endif
> +}
> +
>  static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s)
>  {
>  #ifdef HOST_WORDS_BIGENDIAN
> 

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Michael S. Tsirkin July 15, 2015, 11:23 a.m. UTC | #2
On Wed, Jul 15, 2015 at 03:56:07PM +0800, Jason Wang wrote:
> Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
> ("virtio-net: byteswap virtio-net header") breaks any layout by
> requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
> copying header to temporary buffer if swap is needed, and then use
> this buffer as part of out_sg.
> 
> Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
> ("virtio-net: byteswap virtio-net header")
> Cc: qemu-stable@nongnu.org
> Cc: clg@fr.ibm.com
> Signed-off-by: Jason Wang <jasowang@redhat.com>
> ---
> Changes from V1:
> - avoid header copying if there's no need to do header swap
> - don't write the header back
> ---
>  hw/net/virtio-net.c               | 17 ++++++++++++++---
>  include/hw/virtio/virtio-access.h |  9 +++++++++
>  2 files changed, 23 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> index e3c2db3..12322bd 100644
> --- a/hw/net/virtio-net.c
> +++ b/hw/net/virtio-net.c
> @@ -1142,7 +1142,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>          ssize_t ret, len;
>          unsigned int out_num = elem.out_num;
>          struct iovec *out_sg = &elem.out_sg[0];
> -        struct iovec sg[VIRTQUEUE_MAX_SIZE];
> +        struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE];
> +        struct virtio_net_hdr hdr;
>  
>          if (out_num < 1) {
>              error_report("virtio-net header not in first element");
> @@ -1150,11 +1151,21 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>          }
>  
>          if (n->has_vnet_hdr) {
> -            if (out_sg[0].iov_len < n->guest_hdr_len) {
> +            if (iov_size(out_sg, out_num) < n->guest_hdr_len) {
>                  error_report("virtio-net header incorrect");
>                  exit(1);
>              }
> -            virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base);
> +            if (virtio_needs_swap(vdev)) {
> +                iov_to_buf(out_sg, out_num, 0, &hdr, sizeof(hdr));
> +                virtio_net_hdr_swap(vdev, (void *) &hdr);
> +                sg2[0].iov_base = &hdr;
> +                sg2[0].iov_len = sizeof(hdr);
> +                out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
> +                                   out_sg, out_num,
> +                                   sizeof(hdr), -1);
> +                out_num += 1;
> +                out_sg = sg2;
> +            }
>          }
>  
>          /*

I guess this works but iov cpy is pretty expensive.

- When using the tun backend, we can just set VNETBE/VNETLE
  correctly and avoid swaps in userspace.
  It's easy to probe for - why don't we do this?

- OTOH when not using the tun backend, offloads are
  generally disabled, so the swap isn't needed
  since header is going to be all 0s.

- what's left is old kernels when using tun.
  how about probing for that explicitly?
  In other words, virtio_net_needs_swap as opposed to
  the generic virtio_needs_swap.


> diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h
> index cee5dd7..1ec1dfd 100644
> --- a/include/hw/virtio/virtio-access.h
> +++ b/include/hw/virtio/virtio-access.h
> @@ -143,6 +143,15 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr)
>      }
>  }
>  
> +static inline bool virtio_needs_swap(VirtIODevice *vdev)
> +{
> +#ifdef HOST_WORDS_BIGENDIAN
> +    return virtio_access_is_big_endian(vdev) ? false : true;
> +#else
> +    return virtio_access_is_big_endian(vdev) ? true : false;
> +#endif
> +}
> +
>  static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s)
>  {
>  #ifdef HOST_WORDS_BIGENDIAN
> -- 
> 2.1.4
Jason Wang July 16, 2015, 5:54 a.m. UTC | #3
On 07/15/2015 07:23 PM, Michael S. Tsirkin wrote:
> On Wed, Jul 15, 2015 at 03:56:07PM +0800, Jason Wang wrote:
>> Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
>> ("virtio-net: byteswap virtio-net header") breaks any layout by
>> requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
>> copying header to temporary buffer if swap is needed, and then use
>> this buffer as part of out_sg.
>>
>> Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
>> ("virtio-net: byteswap virtio-net header")
>> Cc: qemu-stable@nongnu.org
>> Cc: clg@fr.ibm.com
>> Signed-off-by: Jason Wang <jasowang@redhat.com>
>> ---
>> Changes from V1:
>> - avoid header copying if there's no need to do header swap
>> - don't write the header back
>> ---
>>  hw/net/virtio-net.c               | 17 ++++++++++++++---
>>  include/hw/virtio/virtio-access.h |  9 +++++++++
>>  2 files changed, 23 insertions(+), 3 deletions(-)
>>
>> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
>> index e3c2db3..12322bd 100644
>> --- a/hw/net/virtio-net.c
>> +++ b/hw/net/virtio-net.c
>> @@ -1142,7 +1142,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>>          ssize_t ret, len;
>>          unsigned int out_num = elem.out_num;
>>          struct iovec *out_sg = &elem.out_sg[0];
>> -        struct iovec sg[VIRTQUEUE_MAX_SIZE];
>> +        struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE];
>> +        struct virtio_net_hdr hdr;
>>  
>>          if (out_num < 1) {
>>              error_report("virtio-net header not in first element");
>> @@ -1150,11 +1151,21 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>>          }
>>  
>>          if (n->has_vnet_hdr) {
>> -            if (out_sg[0].iov_len < n->guest_hdr_len) {
>> +            if (iov_size(out_sg, out_num) < n->guest_hdr_len) {
>>                  error_report("virtio-net header incorrect");
>>                  exit(1);
>>              }
>> -            virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base);
>> +            if (virtio_needs_swap(vdev)) {
>> +                iov_to_buf(out_sg, out_num, 0, &hdr, sizeof(hdr));
>> +                virtio_net_hdr_swap(vdev, (void *) &hdr);
>> +                sg2[0].iov_base = &hdr;
>> +                sg2[0].iov_len = sizeof(hdr);
>> +                out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
>> +                                   out_sg, out_num,
>> +                                   sizeof(hdr), -1);
>> +                out_num += 1;
>> +                out_sg = sg2;
>> +            }
>>          }
>>  
>>          /*
> I guess this works but iov cpy is pretty expensive.
>
> - When using the tun backend, we can just set VNETBE/VNETLE
>   correctly and avoid swaps in userspace.
>   It's easy to probe for - why don't we do this?

We need to fix stable branches first. And if we do this for 2.4, if
running on a older kernel which does not support VNETBE/VNETLE, do we
want to fail the initialization? If yes, it breaks backward
compatibility. If no, we still need the swap here.

>
> - OTOH when not using the tun backend, offloads are
>   generally disabled, so the swap isn't needed
>   since header is going to be all 0s.

I agree it's better to check backend (e.g host_hdr_len) before trying to
do the swap.

>
> - what's left is old kernels when using tun.
>   how about probing for that explicitly?
>   In other words, virtio_net_needs_swap as opposed to
>   the generic virtio_needs_swap.

So we still need swap anyway. How about apply this patch first for
stable and 2.4 and probe the VNETBE/VNETLE explicitly on top for 2.5?

>
>
>> diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h
>> index cee5dd7..1ec1dfd 100644
>> --- a/include/hw/virtio/virtio-access.h
>> +++ b/include/hw/virtio/virtio-access.h
>> @@ -143,6 +143,15 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr)
>>      }
>>  }
>>  
>> +static inline bool virtio_needs_swap(VirtIODevice *vdev)
>> +{
>> +#ifdef HOST_WORDS_BIGENDIAN
>> +    return virtio_access_is_big_endian(vdev) ? false : true;
>> +#else
>> +    return virtio_access_is_big_endian(vdev) ? true : false;
>> +#endif
>> +}
>> +
>>  static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s)
>>  {
>>  #ifdef HOST_WORDS_BIGENDIAN
>> -- 
>> 2.1.4
Michael S. Tsirkin July 16, 2015, 6:19 a.m. UTC | #4
On Thu, Jul 16, 2015 at 01:54:38PM +0800, Jason Wang wrote:
> 
> 
> On 07/15/2015 07:23 PM, Michael S. Tsirkin wrote:
> > On Wed, Jul 15, 2015 at 03:56:07PM +0800, Jason Wang wrote:
> >> Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
> >> ("virtio-net: byteswap virtio-net header") breaks any layout by
> >> requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
> >> copying header to temporary buffer if swap is needed, and then use
> >> this buffer as part of out_sg.
> >>
> >> Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
> >> ("virtio-net: byteswap virtio-net header")
> >> Cc: qemu-stable@nongnu.org
> >> Cc: clg@fr.ibm.com
> >> Signed-off-by: Jason Wang <jasowang@redhat.com>
> >> ---
> >> Changes from V1:
> >> - avoid header copying if there's no need to do header swap
> >> - don't write the header back
> >> ---
> >>  hw/net/virtio-net.c               | 17 ++++++++++++++---
> >>  include/hw/virtio/virtio-access.h |  9 +++++++++
> >>  2 files changed, 23 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> >> index e3c2db3..12322bd 100644
> >> --- a/hw/net/virtio-net.c
> >> +++ b/hw/net/virtio-net.c
> >> @@ -1142,7 +1142,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> >>          ssize_t ret, len;
> >>          unsigned int out_num = elem.out_num;
> >>          struct iovec *out_sg = &elem.out_sg[0];
> >> -        struct iovec sg[VIRTQUEUE_MAX_SIZE];
> >> +        struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE];
> >> +        struct virtio_net_hdr hdr;
> >>  
> >>          if (out_num < 1) {
> >>              error_report("virtio-net header not in first element");
> >> @@ -1150,11 +1151,21 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> >>          }
> >>  
> >>          if (n->has_vnet_hdr) {
> >> -            if (out_sg[0].iov_len < n->guest_hdr_len) {
> >> +            if (iov_size(out_sg, out_num) < n->guest_hdr_len) {
> >>                  error_report("virtio-net header incorrect");
> >>                  exit(1);
> >>              }
> >> -            virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base);
> >> +            if (virtio_needs_swap(vdev)) {
> >> +                iov_to_buf(out_sg, out_num, 0, &hdr, sizeof(hdr));
> >> +                virtio_net_hdr_swap(vdev, (void *) &hdr);
> >> +                sg2[0].iov_base = &hdr;
> >> +                sg2[0].iov_len = sizeof(hdr);
> >> +                out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
> >> +                                   out_sg, out_num,
> >> +                                   sizeof(hdr), -1);
> >> +                out_num += 1;
> >> +                out_sg = sg2;
> >> +            }
> >>          }
> >>  
> >>          /*
> > I guess this works but iov cpy is pretty expensive.
> >
> > - When using the tun backend, we can just set VNETBE/VNETLE
> >   correctly and avoid swaps in userspace.
> >   It's easy to probe for - why don't we do this?
> 
> We need to fix stable branches first. And if we do this for 2.4, if
> running on a older kernel which does not support VNETBE/VNETLE, do we
> want to fail the initialization? If yes, it breaks backward
> compatibility. If no, we still need the swap here.
> 
> >
> > - OTOH when not using the tun backend, offloads are
> >   generally disabled, so the swap isn't needed
> >   since header is going to be all 0s.
> 
> I agree it's better to check backend (e.g host_hdr_len) before trying to
> do the swap.
> 
> >
> > - what's left is old kernels when using tun.
> >   how about probing for that explicitly?
> >   In other words, virtio_net_needs_swap as opposed to
> >   the generic virtio_needs_swap.
> 
> So we still need swap anyway. How about apply this patch first for
> stable and 2.4 and probe the VNETBE/VNETLE explicitly on top for 2.5?

I'm fine with it being a patch on top, but I think it's preferable
to use VNETBE/VNETLE even in 2.4 if that's available.

> >
> >
> >> diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h
> >> index cee5dd7..1ec1dfd 100644
> >> --- a/include/hw/virtio/virtio-access.h
> >> +++ b/include/hw/virtio/virtio-access.h
> >> @@ -143,6 +143,15 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr)
> >>      }
> >>  }
> >>  
> >> +static inline bool virtio_needs_swap(VirtIODevice *vdev)
> >> +{
> >> +#ifdef HOST_WORDS_BIGENDIAN
> >> +    return virtio_access_is_big_endian(vdev) ? false : true;
> >> +#else
> >> +    return virtio_access_is_big_endian(vdev) ? true : false;
> >> +#endif
> >> +}
> >> +
> >>  static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s)
> >>  {
> >>  #ifdef HOST_WORDS_BIGENDIAN
> >> -- 
> >> 2.1.4
Michael S. Tsirkin July 16, 2015, 6:42 a.m. UTC | #5
On Wed, Jul 15, 2015 at 03:56:07PM +0800, Jason Wang wrote:
> Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
> ("virtio-net: byteswap virtio-net header") breaks any layout by
> requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
> copying header to temporary buffer if swap is needed, and then use
> this buffer as part of out_sg.
> 
> Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
> ("virtio-net: byteswap virtio-net header")
> Cc: qemu-stable@nongnu.org
> Cc: clg@fr.ibm.com
> Signed-off-by: Jason Wang <jasowang@redhat.com>
> ---
> Changes from V1:
> - avoid header copying if there's no need to do header swap
> - don't write the header back
> ---
>  hw/net/virtio-net.c               | 17 ++++++++++++++---
>  include/hw/virtio/virtio-access.h |  9 +++++++++
>  2 files changed, 23 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> index e3c2db3..12322bd 100644
> --- a/hw/net/virtio-net.c
> +++ b/hw/net/virtio-net.c
> @@ -1142,7 +1142,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>          ssize_t ret, len;
>          unsigned int out_num = elem.out_num;
>          struct iovec *out_sg = &elem.out_sg[0];
> -        struct iovec sg[VIRTQUEUE_MAX_SIZE];
> +        struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE];
> +        struct virtio_net_hdr hdr;
>  
>          if (out_num < 1) {
>              error_report("virtio-net header not in first element");
> @@ -1150,11 +1151,21 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>          }
>  
>          if (n->has_vnet_hdr) {
> -            if (out_sg[0].iov_len < n->guest_hdr_len) {
> +            if (iov_size(out_sg, out_num) < n->guest_hdr_len) {
>                  error_report("virtio-net header incorrect");
>                  exit(1);
>              }

this scans the iov unnecessarily. How about checking return
code from iov_to_buf instead?

> -            virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base);
> +            if (virtio_needs_swap(vdev)) {
> +                iov_to_buf(out_sg, out_num, 0, &hdr, sizeof(hdr));
> +                virtio_net_hdr_swap(vdev, (void *) &hdr);
> +                sg2[0].iov_base = &hdr;
> +                sg2[0].iov_len = sizeof(hdr);
> +                out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
> +                                   out_sg, out_num,
> +                                   sizeof(hdr), -1);


This might truncate packet if it does not fit in 1024 anymore.
It might be better to just drop it.

> +                out_num += 1;
> +                out_sg = sg2;
> +            }
>          }
>  
>          /*
> diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h
> index cee5dd7..1ec1dfd 100644
> --- a/include/hw/virtio/virtio-access.h
> +++ b/include/hw/virtio/virtio-access.h
> @@ -143,6 +143,15 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr)
>      }
>  }
>  
> +static inline bool virtio_needs_swap(VirtIODevice *vdev)
> +{
> +#ifdef HOST_WORDS_BIGENDIAN
> +    return virtio_access_is_big_endian(vdev) ? false : true;
> +#else
> +    return virtio_access_is_big_endian(vdev) ? true : false;
> +#endif
> +}
> +
>  static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s)
>  {
>  #ifdef HOST_WORDS_BIGENDIAN
> -- 
> 2.1.4
Jason Wang July 16, 2015, 6:49 a.m. UTC | #6
On 07/16/2015 02:42 PM, Michael S. Tsirkin wrote:
> On Wed, Jul 15, 2015 at 03:56:07PM +0800, Jason Wang wrote:
>> Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
>> ("virtio-net: byteswap virtio-net header") breaks any layout by
>> requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
>> copying header to temporary buffer if swap is needed, and then use
>> this buffer as part of out_sg.
>>
>> Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
>> ("virtio-net: byteswap virtio-net header")
>> Cc: qemu-stable@nongnu.org
>> Cc: clg@fr.ibm.com
>> Signed-off-by: Jason Wang <jasowang@redhat.com>
>> ---
>> Changes from V1:
>> - avoid header copying if there's no need to do header swap
>> - don't write the header back
>> ---
>>  hw/net/virtio-net.c               | 17 ++++++++++++++---
>>  include/hw/virtio/virtio-access.h |  9 +++++++++
>>  2 files changed, 23 insertions(+), 3 deletions(-)
>>
>> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
>> index e3c2db3..12322bd 100644
>> --- a/hw/net/virtio-net.c
>> +++ b/hw/net/virtio-net.c
>> @@ -1142,7 +1142,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>>          ssize_t ret, len;
>>          unsigned int out_num = elem.out_num;
>>          struct iovec *out_sg = &elem.out_sg[0];
>> -        struct iovec sg[VIRTQUEUE_MAX_SIZE];
>> +        struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE];
>> +        struct virtio_net_hdr hdr;
>>  
>>          if (out_num < 1) {
>>              error_report("virtio-net header not in first element");
>> @@ -1150,11 +1151,21 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>>          }
>>  
>>          if (n->has_vnet_hdr) {
>> -            if (out_sg[0].iov_len < n->guest_hdr_len) {
>> +            if (iov_size(out_sg, out_num) < n->guest_hdr_len) {
>>                  error_report("virtio-net header incorrect");
>>                  exit(1);
>>              }
> this scans the iov unnecessarily. How about checking return
> code from iov_to_buf instead?

Looks ok since hdr is very short anyway.

>
>> -            virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base);
>> +            if (virtio_needs_swap(vdev)) {
>> +                iov_to_buf(out_sg, out_num, 0, &hdr, sizeof(hdr));
>> +                virtio_net_hdr_swap(vdev, (void *) &hdr);
>> +                sg2[0].iov_base = &hdr;
>> +                sg2[0].iov_len = sizeof(hdr);
>> +                out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
>> +                                   out_sg, out_num,
>> +                                   sizeof(hdr), -1);
>
> This might truncate packet if it does not fit in 1024 anymore.
> It might be better to just drop it.

Ok, will do this in V3.

>
>> +                out_num += 1;
>> +                out_sg = sg2;
>> +            }
>>          }
>>  
>>          /*
>> diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h
>> index cee5dd7..1ec1dfd 100644
>> --- a/include/hw/virtio/virtio-access.h
>> +++ b/include/hw/virtio/virtio-access.h
>> @@ -143,6 +143,15 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr)
>>      }
>>  }
>>  
>> +static inline bool virtio_needs_swap(VirtIODevice *vdev)
>> +{
>> +#ifdef HOST_WORDS_BIGENDIAN
>> +    return virtio_access_is_big_endian(vdev) ? false : true;
>> +#else
>> +    return virtio_access_is_big_endian(vdev) ? true : false;
>> +#endif
>> +}
>> +
>>  static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s)
>>  {
>>  #ifdef HOST_WORDS_BIGENDIAN
>> -- 
>> 2.1.4
Michael S. Tsirkin July 16, 2015, 7:29 a.m. UTC | #7
On Thu, Jul 16, 2015 at 02:49:48PM +0800, Jason Wang wrote:
> 
> 
> On 07/16/2015 02:42 PM, Michael S. Tsirkin wrote:
> > On Wed, Jul 15, 2015 at 03:56:07PM +0800, Jason Wang wrote:
> >> Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
> >> ("virtio-net: byteswap virtio-net header") breaks any layout by
> >> requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
> >> copying header to temporary buffer if swap is needed, and then use
> >> this buffer as part of out_sg.
> >>
> >> Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
> >> ("virtio-net: byteswap virtio-net header")
> >> Cc: qemu-stable@nongnu.org
> >> Cc: clg@fr.ibm.com
> >> Signed-off-by: Jason Wang <jasowang@redhat.com>
> >> ---
> >> Changes from V1:
> >> - avoid header copying if there's no need to do header swap
> >> - don't write the header back
> >> ---
> >>  hw/net/virtio-net.c               | 17 ++++++++++++++---
> >>  include/hw/virtio/virtio-access.h |  9 +++++++++
> >>  2 files changed, 23 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> >> index e3c2db3..12322bd 100644
> >> --- a/hw/net/virtio-net.c
> >> +++ b/hw/net/virtio-net.c
> >> @@ -1142,7 +1142,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> >>          ssize_t ret, len;
> >>          unsigned int out_num = elem.out_num;
> >>          struct iovec *out_sg = &elem.out_sg[0];
> >> -        struct iovec sg[VIRTQUEUE_MAX_SIZE];
> >> +        struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE];
> >> +        struct virtio_net_hdr hdr;
> >>  
> >>          if (out_num < 1) {
> >>              error_report("virtio-net header not in first element");
> >> @@ -1150,11 +1151,21 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> >>          }
> >>  
> >>          if (n->has_vnet_hdr) {
> >> -            if (out_sg[0].iov_len < n->guest_hdr_len) {
> >> +            if (iov_size(out_sg, out_num) < n->guest_hdr_len) {
> >>                  error_report("virtio-net header incorrect");
> >>                  exit(1);
> >>              }
> > this scans the iov unnecessarily. How about checking return
> > code from iov_to_buf instead?
> 
> Looks ok since hdr is very short anyway.

but iov_size scans the whole iov, not just the header.

> >
> >> -            virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base);
> >> +            if (virtio_needs_swap(vdev)) {
> >> +                iov_to_buf(out_sg, out_num, 0, &hdr, sizeof(hdr));
> >> +                virtio_net_hdr_swap(vdev, (void *) &hdr);
> >> +                sg2[0].iov_base = &hdr;
> >> +                sg2[0].iov_len = sizeof(hdr);
> >> +                out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
> >> +                                   out_sg, out_num,
> >> +                                   sizeof(hdr), -1);
> >
> > This might truncate packet if it does not fit in 1024 anymore.
> > It might be better to just drop it.
> 
> Ok, will do this in V3.
> 
> >
> >> +                out_num += 1;
> >> +                out_sg = sg2;
> >> +            }
> >>          }
> >>  
> >>          /*
> >> diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h
> >> index cee5dd7..1ec1dfd 100644
> >> --- a/include/hw/virtio/virtio-access.h
> >> +++ b/include/hw/virtio/virtio-access.h
> >> @@ -143,6 +143,15 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr)
> >>      }
> >>  }
> >>  
> >> +static inline bool virtio_needs_swap(VirtIODevice *vdev)
> >> +{
> >> +#ifdef HOST_WORDS_BIGENDIAN
> >> +    return virtio_access_is_big_endian(vdev) ? false : true;
> >> +#else
> >> +    return virtio_access_is_big_endian(vdev) ? true : false;
> >> +#endif
> >> +}
> >> +
> >>  static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s)
> >>  {
> >>  #ifdef HOST_WORDS_BIGENDIAN
> >> -- 
> >> 2.1.4
Jason Wang July 16, 2015, 8 a.m. UTC | #8
On 07/16/2015 03:29 PM, Michael S. Tsirkin wrote:
> On Thu, Jul 16, 2015 at 02:49:48PM +0800, Jason Wang wrote:
>> > 
>> > 
>> > On 07/16/2015 02:42 PM, Michael S. Tsirkin wrote:
>>> > > On Wed, Jul 15, 2015 at 03:56:07PM +0800, Jason Wang wrote:
>>>> > >> Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
>>>> > >> ("virtio-net: byteswap virtio-net header") breaks any layout by
>>>> > >> requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
>>>> > >> copying header to temporary buffer if swap is needed, and then use
>>>> > >> this buffer as part of out_sg.
>>>> > >>
>>>> > >> Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
>>>> > >> ("virtio-net: byteswap virtio-net header")
>>>> > >> Cc: qemu-stable@nongnu.org
>>>> > >> Cc: clg@fr.ibm.com
>>>> > >> Signed-off-by: Jason Wang <jasowang@redhat.com>
>>>> > >> ---
>>>> > >> Changes from V1:
>>>> > >> - avoid header copying if there's no need to do header swap
>>>> > >> - don't write the header back
>>>> > >> ---
>>>> > >>  hw/net/virtio-net.c               | 17 ++++++++++++++---
>>>> > >>  include/hw/virtio/virtio-access.h |  9 +++++++++
>>>> > >>  2 files changed, 23 insertions(+), 3 deletions(-)
>>>> > >>
>>>> > >> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
>>>> > >> index e3c2db3..12322bd 100644
>>>> > >> --- a/hw/net/virtio-net.c
>>>> > >> +++ b/hw/net/virtio-net.c
>>>> > >> @@ -1142,7 +1142,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>>>> > >>          ssize_t ret, len;
>>>> > >>          unsigned int out_num = elem.out_num;
>>>> > >>          struct iovec *out_sg = &elem.out_sg[0];
>>>> > >> -        struct iovec sg[VIRTQUEUE_MAX_SIZE];
>>>> > >> +        struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE];
>>>> > >> +        struct virtio_net_hdr hdr;
>>>> > >>  
>>>> > >>          if (out_num < 1) {
>>>> > >>              error_report("virtio-net header not in first element");
>>>> > >> @@ -1150,11 +1151,21 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>>>> > >>          }
>>>> > >>  
>>>> > >>          if (n->has_vnet_hdr) {
>>>> > >> -            if (out_sg[0].iov_len < n->guest_hdr_len) {
>>>> > >> +            if (iov_size(out_sg, out_num) < n->guest_hdr_len) {
>>>> > >>                  error_report("virtio-net header incorrect");
>>>> > >>                  exit(1);
>>>> > >>              }
>>> > > this scans the iov unnecessarily. How about checking return
>>> > > code from iov_to_buf instead?
>> > 
>> > Looks ok since hdr is very short anyway.
> but iov_size scans the whole iov, not just the header.
>

Right.
diff mbox

Patch

diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index e3c2db3..12322bd 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -1142,7 +1142,8 @@  static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
         ssize_t ret, len;
         unsigned int out_num = elem.out_num;
         struct iovec *out_sg = &elem.out_sg[0];
-        struct iovec sg[VIRTQUEUE_MAX_SIZE];
+        struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE];
+        struct virtio_net_hdr hdr;
 
         if (out_num < 1) {
             error_report("virtio-net header not in first element");
@@ -1150,11 +1151,21 @@  static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
         }
 
         if (n->has_vnet_hdr) {
-            if (out_sg[0].iov_len < n->guest_hdr_len) {
+            if (iov_size(out_sg, out_num) < n->guest_hdr_len) {
                 error_report("virtio-net header incorrect");
                 exit(1);
             }
-            virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base);
+            if (virtio_needs_swap(vdev)) {
+                iov_to_buf(out_sg, out_num, 0, &hdr, sizeof(hdr));
+                virtio_net_hdr_swap(vdev, (void *) &hdr);
+                sg2[0].iov_base = &hdr;
+                sg2[0].iov_len = sizeof(hdr);
+                out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
+                                   out_sg, out_num,
+                                   sizeof(hdr), -1);
+                out_num += 1;
+                out_sg = sg2;
+            }
         }
 
         /*
diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h
index cee5dd7..1ec1dfd 100644
--- a/include/hw/virtio/virtio-access.h
+++ b/include/hw/virtio/virtio-access.h
@@ -143,6 +143,15 @@  static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr)
     }
 }
 
+static inline bool virtio_needs_swap(VirtIODevice *vdev)
+{
+#ifdef HOST_WORDS_BIGENDIAN
+    return virtio_access_is_big_endian(vdev) ? false : true;
+#else
+    return virtio_access_is_big_endian(vdev) ? true : false;
+#endif
+}
+
 static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s)
 {
 #ifdef HOST_WORDS_BIGENDIAN