From 987a9b4800003567b1a47a379255e886a77d57ea Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Aug 2012 16:45:55 +0200 Subject: net: notify iothread after flushing queue virtio-net has code to flush the queue and notify the iothread whenever new receive buffers are added by the guest. That is fine, and indeed we need to do the same in all other drivers. However, notifying the iothread should be work for the network subsystem. And since we are at it we can add a little smartness: if some of the queued packets already could not be delivered, there is no need to notify the iothread. Reported-by: Luigi Rizzo Cc: Stefan Hajnoczi Cc: Jan Kiszka Signed-off-by: Paolo Bonzini Reviewed-by: Amos Kong Signed-off-by: Stefan Hajnoczi --- hw/virtio-net.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index b1998b2..6490743 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -447,10 +447,6 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) VirtIONet *n = to_virtio_net(vdev); qemu_flush_queued_packets(&n->nic->nc); - - /* We now have RX buffers, signal to the IO thread to break out of the - * select to re-poll the tap file descriptor */ - qemu_notify_event(); } static int virtio_net_can_receive(NetClientState *nc) -- cgit v1.1 From 40bad8f3deba15e2074ff34cfe923c12916b1cc5 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 15:15:43 +0200 Subject: virtio-net: fix used len for tx There is no out sg for TX, so used buf length for tx should always be 0. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 6490743..247d7be 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -690,7 +690,7 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; - virtqueue_push(n->tx_vq, &n->async_tx.elem, n->async_tx.len); + virtqueue_push(n->tx_vq, &n->async_tx.elem, 0); virtio_notify(&n->vdev, n->tx_vq); n->async_tx.elem.out_num = n->async_tx.len = 0; @@ -754,7 +754,7 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) len += ret; - virtqueue_push(vq, &elem, len); + virtqueue_push(vq, &elem, 0); virtio_notify(&n->vdev, vq); if (++num_packets >= n->tx_burst) { -- cgit v1.1 From 9899148110627da388f82c02997bca6d678d1aa1 Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Fri, 28 Sep 2012 10:06:02 +0800 Subject: virtio-net: update nc.link_down in virtio_net_load() nc.link_down could not be migrated, this patch updates link_down in virtio_post_load() to keep it coincident with real link status. Signed-off-by: Amos Kong Signed-off-by: Stefan Hajnoczi --- hw/virtio-net.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 247d7be..8342391 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -973,6 +973,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } } n->mac_table.first_multi = i; + + /* nc.link_down can't be migrated, so infer link_down according + * to link status bit in n->status */ + n->nic->nc.link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0; + return 0; } -- cgit v1.1 From e398d61b4777125f32a99fa49519c5edbb00809b Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 29 Aug 2012 19:03:09 +0200 Subject: virtio-net: use qemu_get_buffer() in a temp buffer qemu_fseek() is known to be wrong. Would be removed on the next commit. This code should never been used (value has been MAC_TABLE_ENTRIES since 2009). Signed-off-by: Juan Quintela Reviewed-by: Paolo Bonzini --- hw/virtio-net.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 8342391..50ba728 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -921,7 +921,9 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) qemu_get_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN); } else if (n->mac_table.in_use) { - qemu_fseek(f, n->mac_table.in_use * ETH_ALEN, SEEK_CUR); + uint8_t *buf = g_malloc0(n->mac_table.in_use); + qemu_get_buffer(f, buf, n->mac_table.in_use * ETH_ALEN); + g_free(buf); n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1; n->mac_table.in_use = 0; } -- cgit v1.1 From e35e23f655fb120a2b4d0695bdee86fafd9caabf Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 12:12:25 +0200 Subject: virtio-net: track host/guest header length Tracking these in device state instead of re-calculating on each packet. No functional changes. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 50ba728..a0c79e1 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -41,6 +41,8 @@ typedef struct VirtIONet int32_t tx_burst; int tx_waiting; uint32_t has_vnet_hdr; + size_t host_hdr_len; + size_t guest_hdr_len; uint8_t has_ufo; struct { VirtQueueElement elem; @@ -231,6 +233,7 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) if (peer_has_vnet_hdr(n)) { tap_using_vnet_hdr(n->nic->nc.peer, 1); + n->host_hdr_len = sizeof(struct virtio_net_hdr); } else { features &= ~(0x1 << VIRTIO_NET_F_CSUM); features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO4); @@ -278,6 +281,8 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) VirtIONet *n = to_virtio_net(vdev); n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF)); + n->guest_hdr_len = n->mergeable_rx_bufs ? + sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); if (n->has_vnet_hdr) { tap_set_offload(n->nic->nc.peer, @@ -593,18 +598,13 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; - size_t guest_hdr_len, offset, i, host_hdr_len; + size_t offset, i; if (!virtio_net_can_receive(&n->nic->nc)) return -1; /* hdr_len refers to the header we supply to the guest */ - guest_hdr_len = n->mergeable_rx_bufs ? - sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); - - - host_hdr_len = n->has_vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; - if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len)) + if (!virtio_net_has_buffers(n, size + n->guest_hdr_len - n->host_hdr_len)) return 0; if (!receive_filter(n, buf, size)) @@ -626,7 +626,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t "i %zd mergeable %d offset %zd, size %zd, " "guest hdr len %zd, host hdr len %zd guest features 0x%x", i, n->mergeable_rx_bufs, offset, size, - guest_hdr_len, host_hdr_len, n->vdev.guest_features); + n->guest_hdr_len, n->host_hdr_len, n->vdev.guest_features); exit(1); } @@ -635,7 +635,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t exit(1); } - if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != guest_hdr_len) { + if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != n->guest_hdr_len) { error_report("virtio-net header not in first element"); exit(1); } @@ -647,8 +647,9 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base; offset += receive_header(n, sg, elem.in_num, - buf + offset, size - offset, guest_hdr_len); - total += guest_hdr_len; + buf + offset, size - offset, + n->guest_hdr_len); + total += n->guest_hdr_len; } /* copy in packet. ugh */ @@ -665,7 +666,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t "i %zd mergeable %d offset %zd, size %zd, " "guest hdr len %zd, host hdr len %zd", i, n->mergeable_rx_bufs, - offset, size, guest_hdr_len, host_hdr_len); + offset, size, n->guest_hdr_len, n->host_hdr_len); #endif return size; } @@ -900,6 +901,8 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) qemu_get_buffer(f, n->mac, ETH_ALEN); n->tx_waiting = qemu_get_be32(f); n->mergeable_rx_bufs = qemu_get_be32(f); + n->guest_hdr_len = n->mergeable_rx_bufs ? + sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); if (version_id >= 3) n->status = qemu_get_be16(f); @@ -940,6 +943,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) if (n->has_vnet_hdr) { tap_using_vnet_hdr(n->nic->nc.peer, 1); + n->host_hdr_len = sizeof(struct virtio_net_hdr); tap_set_offload(n->nic->nc.peer, (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1, (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1, @@ -1044,6 +1048,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, n->tx_waiting = 0; n->tx_burst = net->txburst; n->mergeable_rx_bufs = 0; + n->guest_hdr_len = sizeof(struct virtio_net_hdr); n->promisc = 1; /* for compatibility */ n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); -- cgit v1.1 From 22cc84db6e42bef8646b8cd671f4c999e9c0a38f Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 13:14:16 +0200 Subject: virtio-net: avoid sg copy Avoid tweaking iovec during receive. This removes the need to copy the vector. Note: we currently have an evil cast in work_around_broken_dhclient and unfortunately this patch does not fix it - just pushes the evil cast to another place. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index a0c79e1..8461586 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -504,40 +504,37 @@ static int virtio_net_has_buffers(VirtIONet *n, int bufsize) * cache. */ static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, - const uint8_t *buf, size_t size) + uint8_t *buf, size_t size) { if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */ (size > 27 && size < 1500) && /* normal sized MTU */ (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */ (buf[23] == 17) && /* ip.protocol == UDP */ (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */ - /* FIXME this cast is evil */ - net_checksum_calculate((uint8_t *)buf, size); + net_checksum_calculate(buf, size); hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; } } -static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt, - const void *buf, size_t size, size_t hdr_len) +static int receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, + const void *buf, size_t size) { - struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)iov[0].iov_base; int offset = 0; - hdr->flags = 0; - hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; - if (n->has_vnet_hdr) { - memcpy(hdr, buf, sizeof(*hdr)); - offset = sizeof(*hdr); - work_around_broken_dhclient(hdr, buf + offset, size - offset); + /* FIXME this cast is evil */ + void *wbuf = (void *)buf; + work_around_broken_dhclient(wbuf, wbuf + offset, size - offset); + offset = sizeof(struct virtio_net_hdr); + iov_from_buf(iov, iov_cnt, 0, buf, offset); + } else { + struct virtio_net_hdr hdr = { + .flags = 0, + .gso_type = VIRTIO_NET_HDR_GSO_NONE + }; + iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr); } - /* We only ever receive a struct virtio_net_hdr from the tapfd, - * but we may be passing along a larger header to the guest. - */ - iov[0].iov_base += hdr_len; - iov[0].iov_len -= hdr_len; - return offset; } @@ -598,7 +595,8 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; - size_t offset, i; + const struct iovec *sg = elem.in_sg; + size_t offset, i, guest_offset; if (!virtio_net_can_receive(&n->nic->nc)) return -1; @@ -615,7 +613,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t while (offset < size) { VirtQueueElement elem; int len, total; - struct iovec sg[VIRTQUEUE_MAX_SIZE]; + const struct iovec *sg = elem.in_sg; total = 0; @@ -640,20 +638,20 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t exit(1); } - memcpy(&sg, &elem.in_sg[0], sizeof(sg[0]) * elem.in_num); - if (i == 0) { if (n->mergeable_rx_bufs) mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base; offset += receive_header(n, sg, elem.in_num, - buf + offset, size - offset, - n->guest_hdr_len); + buf + offset, size - offset); total += n->guest_hdr_len; + guest_offset = n->guest_hdr_len; + } else { + guest_offset = 0; } /* copy in packet. ugh */ - len = iov_from_buf(sg, elem.in_num, 0, + len = iov_from_buf(sg, elem.in_num, guest_offset, buf + offset, size - offset); total += len; offset += len; -- cgit v1.1 From 63c5872873de8d7d994a589eed7bfe6a70cc8e06 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 13:17:13 +0200 Subject: virtio-net: use safe iov operations for rx Avoid magling iov manually: use safe iov operations for processing packets incoming to guest. This also removes the requirement for virtio header to fit the first s/g entry exactly. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 8461586..d315fdf 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -594,8 +594,9 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t size) { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; - struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; - const struct iovec *sg = elem.in_sg; + struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; + struct virtio_net_hdr_mrg_rxbuf mhdr; + unsigned mhdr_cnt = 0; size_t offset, i, guest_offset; if (!virtio_net_can_receive(&n->nic->nc)) @@ -633,14 +634,13 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t exit(1); } - if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != n->guest_hdr_len) { - error_report("virtio-net header not in first element"); - exit(1); - } - if (i == 0) { - if (n->mergeable_rx_bufs) - mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base; + if (n->mergeable_rx_bufs) { + mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg), + sg, elem.in_num, + offsetof(typeof(mhdr), num_buffers), + sizeof(mhdr.num_buffers)); + } offset += receive_header(n, sg, elem.in_num, buf + offset, size - offset); @@ -673,8 +673,11 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t virtqueue_fill(n->rx_vq, &elem, total, i++); } - if (mhdr) { - stw_p(&mhdr->num_buffers, i); + if (mhdr_cnt) { + stw_p(&mhdr.num_buffers, i); + iov_from_buf(mhdr_sg, mhdr_cnt, + 0, + &mhdr.num_buffers, sizeof mhdr.num_buffers); } virtqueue_flush(n->rx_vq, i); -- cgit v1.1 From 280598b7a5fdf96fb79d87a2129750bad5dbf24b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 13:24:17 +0200 Subject: virtio-net: refactor receive_hdr Now that we know host hdr length, we don't need to duplicate the logic in receive_hdr: caller can figure out the offset itself. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index d315fdf..5128a2a 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -516,17 +516,15 @@ static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, } } -static int receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, - const void *buf, size_t size) +static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, + const void *buf, size_t size) { - int offset = 0; - if (n->has_vnet_hdr) { /* FIXME this cast is evil */ void *wbuf = (void *)buf; - work_around_broken_dhclient(wbuf, wbuf + offset, size - offset); - offset = sizeof(struct virtio_net_hdr); - iov_from_buf(iov, iov_cnt, 0, buf, offset); + work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, + size - n->host_hdr_len); + iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); } else { struct virtio_net_hdr hdr = { .flags = 0, @@ -534,8 +532,6 @@ static int receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, }; iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr); } - - return offset; } static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) @@ -642,8 +638,8 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t sizeof(mhdr.num_buffers)); } - offset += receive_header(n, sg, elem.in_num, - buf + offset, size - offset); + receive_header(n, sg, elem.in_num, buf + offset, size - offset); + offset += n->host_hdr_len; total += n->guest_hdr_len; guest_offset = n->guest_hdr_len; } else { -- cgit v1.1 From c8d28e7e336869524d166d88f08ad476eadedccb Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 13:26:55 +0200 Subject: virtio-net: first s/g is always at start of buf We know offset is 0, assert that. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 5128a2a..59ab674 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -631,6 +631,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t } if (i == 0) { + assert(offset == 0); if (n->mergeable_rx_bufs) { mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg), sg, elem.in_num, @@ -638,8 +639,8 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t sizeof(mhdr.num_buffers)); } - receive_header(n, sg, elem.in_num, buf + offset, size - offset); - offset += n->host_hdr_len; + receive_header(n, sg, elem.in_num, buf, size); + offset = n->host_hdr_len; total += n->guest_hdr_len; guest_offset = n->guest_hdr_len; } else { -- cgit v1.1 From 14761f9cf7fbc6d058c1e51c313a139066eab256 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 14:52:28 +0200 Subject: virtio-net: switch tx to safe iov functions Avoid mangling iovec manually: use safe iov_* functions. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 59ab674..5206648 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -715,10 +715,10 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) } while (virtqueue_pop(vq, &elem)) { - ssize_t ret, len = 0; + ssize_t ret, len; unsigned int out_num = elem.out_num; struct iovec *out_sg = &elem.out_sg[0]; - unsigned hdr_len; + struct iovec sg[VIRTQUEUE_MAX_SIZE]; /* hdr_len refers to the header received from the guest */ hdr_len = n->mergeable_rx_bufs ? @@ -730,18 +730,25 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) exit(1); } - /* ignore the header if GSO is not supported */ - if (!n->has_vnet_hdr) { - out_num--; - out_sg++; - len += hdr_len; - } else if (n->mergeable_rx_bufs) { - /* tapfd expects a struct virtio_net_hdr */ - hdr_len -= sizeof(struct virtio_net_hdr); - out_sg->iov_len -= hdr_len; - len += hdr_len; + /* + * If host wants to see the guest header as is, we can + * pass it on unchanged. Otherwise, copy just the parts + * that host is interested in. + */ + assert(n->host_hdr_len <= n->guest_hdr_len); + if (n->host_hdr_len != n->guest_hdr_len) { + unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg), + out_sg, out_num, + 0, n->host_hdr_len); + sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num, + out_sg, out_num, + n->guest_hdr_len, -1); + out_num = sg_num; + out_sg = sg; } + len = hdr_len; + ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num, virtio_net_tx_complete); if (ret == 0) { -- cgit v1.1 From 7b80d08efc36fd6c7881e98302f00148b5fd908a Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 14:54:44 +0200 Subject: virtio-net: simplify rx code Remove code duplication using guest header length that we track. Drop specific layout requirement for rx buffers: things work using generic iovec functions in any case. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 5206648..dc4a26c 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -720,12 +720,7 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) struct iovec *out_sg = &elem.out_sg[0]; struct iovec sg[VIRTQUEUE_MAX_SIZE]; - /* hdr_len refers to the header received from the guest */ - hdr_len = n->mergeable_rx_bufs ? - sizeof(struct virtio_net_hdr_mrg_rxbuf) : - sizeof(struct virtio_net_hdr); - - if (out_num < 1 || out_sg->iov_len != hdr_len) { + if (out_num < 1) { error_report("virtio-net header not in first element"); exit(1); } @@ -747,7 +742,7 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) out_sg = sg; } - len = hdr_len; + len = n->guest_hdr_len; ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num, virtio_net_tx_complete); -- cgit v1.1 From e043ebc6f9a093c1fd1b677191ad9fbeefe22d1e Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 16:27:27 +0200 Subject: virtio-net: minor code simplification During packet filtering, we can now use host hdr len to offset incoming buffer unconditionally. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index dc4a26c..c94521e 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -544,9 +544,7 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) if (n->promisc) return 1; - if (n->has_vnet_hdr) { - ptr += sizeof(struct virtio_net_hdr); - } + ptr += n->host_hdr_len; if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { int vid = be16_to_cpup((uint16_t *)(ptr + 14)) & 0xfff; -- cgit v1.1 From 6e371ab867d48c16e6a9ee32cefcea48692a4deb Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 17:04:21 +0200 Subject: virtio-net: test peer header support at init time There's no reason to query header support at random times: at load or feature query. Driver also might not query functions. Cleaner to do it at device init. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index c94521e..1180b51 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -202,16 +202,19 @@ static void virtio_net_reset(VirtIODevice *vdev) memset(n->vlans, 0, MAX_VLAN >> 3); } -static int peer_has_vnet_hdr(VirtIONet *n) +static void peer_test_vnet_hdr(VirtIONet *n) { if (!n->nic->nc.peer) - return 0; + return; if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) - return 0; + return; n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer); +} +static int peer_has_vnet_hdr(VirtIONet *n) +{ return n->has_vnet_hdr; } @@ -231,10 +234,7 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) features |= (1 << VIRTIO_NET_F_MAC); - if (peer_has_vnet_hdr(n)) { - tap_using_vnet_hdr(n->nic->nc.peer, 1); - n->host_hdr_len = sizeof(struct virtio_net_hdr); - } else { + if (!peer_has_vnet_hdr(n)) { features &= ~(0x1 << VIRTIO_NET_F_CSUM); features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO4); features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO6); @@ -940,8 +940,6 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } if (n->has_vnet_hdr) { - tap_using_vnet_hdr(n->nic->nc.peer, 1); - n->host_hdr_len = sizeof(struct virtio_net_hdr); tap_set_offload(n->nic->nc.peer, (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1, (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1, @@ -1040,6 +1038,13 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, n->status = VIRTIO_NET_S_LINK_UP; n->nic = qemu_new_nic(&net_virtio_info, conf, object_get_typename(OBJECT(dev)), dev->id, n); + peer_test_vnet_hdr(n); + if (peer_has_vnet_hdr(n)) { + tap_using_vnet_hdr(n->nic->nc.peer, 1); + n->host_hdr_len = sizeof(struct virtio_net_hdr); + } else { + n->host_hdr_len = 0; + } qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a); -- cgit v1.1 From ff3a8066e651230d255e6eea340e2d48e7da4aeb Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Sep 2012 21:05:03 +0200 Subject: virtio-net: enable mrg buf header in tap on linux Modern linux supports arbitrary header size, which makes it possible to pass mrg buf header to tap directly without iovec mangling. Use this capability when it is there. This removes the need to deal with it in vhost-net as we do now. Signed-off-by: Michael S. Tsirkin --- hw/virtio-net.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 1180b51..108ce07 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -228,6 +228,20 @@ static int peer_has_ufo(VirtIONet *n) return n->has_ufo; } +static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs) +{ + n->mergeable_rx_bufs = mergeable_rx_bufs; + + n->guest_hdr_len = n->mergeable_rx_bufs ? + sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); + + if (peer_has_vnet_hdr(n) && + tap_has_vnet_hdr_len(n->nic->nc.peer, n->guest_hdr_len)) { + tap_set_vnet_hdr_len(n->nic->nc.peer, n->guest_hdr_len); + n->host_hdr_len = n->guest_hdr_len; + } +} + static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) { VirtIONet *n = to_virtio_net(vdev); @@ -280,9 +294,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) { VirtIONet *n = to_virtio_net(vdev); - n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF)); - n->guest_hdr_len = n->mergeable_rx_bufs ? - sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); + virtio_net_set_mrg_rx_bufs(n, !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF))); if (n->has_vnet_hdr) { tap_set_offload(n->nic->nc.peer, @@ -898,9 +910,8 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) qemu_get_buffer(f, n->mac, ETH_ALEN); n->tx_waiting = qemu_get_be32(f); - n->mergeable_rx_bufs = qemu_get_be32(f); - n->guest_hdr_len = n->mergeable_rx_bufs ? - sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); + + virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f)); if (version_id >= 3) n->status = qemu_get_be16(f); @@ -1050,8 +1061,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, n->tx_waiting = 0; n->tx_burst = net->txburst; - n->mergeable_rx_bufs = 0; - n->guest_hdr_len = sizeof(struct virtio_net_hdr); + virtio_net_set_mrg_rx_bufs(n, 0); n->promisc = 1; /* for compatibility */ n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); -- cgit v1.1 From 1422e32db51ff2b1194fb24a6201c4310be5667d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 24 Oct 2012 08:43:34 +0200 Subject: net: reorganize headers Move public headers to include/net, and leave private headers in net/. Put the virtio headers in include/net/tap.h, removing the multiple copies that existed. Leave include/net/tap.h as the interface for NICs, and net/tap_int.h as the interface for OS-specific parts of the tap backend. Signed-off-by: Paolo Bonzini --- hw/virtio-net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 108ce07..dc7c6d6 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -13,7 +13,7 @@ #include "iov.h" #include "virtio.h" -#include "net.h" +#include "net/net.h" #include "net/checksum.h" #include "net/tap.h" #include "qemu-error.h" -- cgit v1.1 From 1de7afc984b49af164e2619e6850b9732b173b34 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 17 Dec 2012 18:20:00 +0100 Subject: misc: move include files to include/qemu/ Signed-off-by: Paolo Bonzini --- hw/virtio-net.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'hw/virtio-net.c') diff --git a/hw/virtio-net.c b/hw/virtio-net.c index dc7c6d6..5d03b31 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -11,13 +11,13 @@ * */ -#include "iov.h" +#include "qemu/iov.h" #include "virtio.h" #include "net/net.h" #include "net/checksum.h" #include "net/tap.h" -#include "qemu-error.h" -#include "qemu-timer.h" +#include "qemu/error-report.h" +#include "qemu/timer.h" #include "virtio-net.h" #include "vhost_net.h" -- cgit v1.1