92 lines
3.0 KiB
Diff
92 lines
3.0 KiB
Diff
|
|
From 5651eb5cfd3a49506be4be97f8def3fed713c641 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Alexey Dobriyan <adobriyan@yandex-team.ru>
|
||
|
|
Date: Tue, 30 Apr 2024 13:53:33 +0300
|
||
|
|
Subject: [PATCH] virtio-net: drop too short packets early
|
||
|
|
|
||
|
|
Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451
|
||
|
|
creates small packet (1 segment, len = 10 == n->guest_hdr_len),
|
||
|
|
then destroys queue.
|
||
|
|
|
||
|
|
"if (n->host_hdr_len != n->guest_hdr_len)" is triggered, if body creates
|
||
|
|
zero length/zero segment packet as there is nothing after guest header.
|
||
|
|
|
||
|
|
qemu_sendv_packet_async() tries to send it.
|
||
|
|
|
||
|
|
slirp discards it because it is smaller than Ethernet header,
|
||
|
|
but returns 0 because tx hooks are supposed to return total length of data.
|
||
|
|
|
||
|
|
0 is propagated upwards and is interpreted as "packet has been sent"
|
||
|
|
which is terrible because queue is being destroyed, nobody is waiting for TX
|
||
|
|
to complete and assert it triggered.
|
||
|
|
|
||
|
|
Fix is discard such empty packets instead of sending them.
|
||
|
|
|
||
|
|
Length 1 packets will go via different codepath:
|
||
|
|
|
||
|
|
virtqueue_push(q->tx_vq, elem, 0);
|
||
|
|
virtio_notify(vdev, q->tx_vq);
|
||
|
|
g_free(elem);
|
||
|
|
|
||
|
|
and aren't problematic.
|
||
|
|
|
||
|
|
Signed-off-by: Alexey Dobriyan <adobriyan@yandex-team.ru>
|
||
|
|
Signed-off-by: Jason Wang <jasowang@redhat.com>
|
||
|
|
(cherry picked from commit 2c3e4e2de699cd4d9f6c71f30a22d8f125cd6164)
|
||
|
|
Signed-off-by: zhujun2 <zhujun2_yewu@cmss.chinamobile.com>
|
||
|
|
---
|
||
|
|
hw/net/virtio-net.c | 18 ++++++++++++------
|
||
|
|
1 file changed, 12 insertions(+), 6 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
|
||
|
|
index 432c433540..b17137a686 100644
|
||
|
|
--- a/hw/net/virtio-net.c
|
||
|
|
+++ b/hw/net/virtio-net.c
|
||
|
|
@@ -2732,18 +2732,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
|
||
|
|
out_sg = elem->out_sg;
|
||
|
|
if (out_num < 1) {
|
||
|
|
virtio_error(vdev, "virtio-net header not in first element");
|
||
|
|
- virtqueue_detach_element(q->tx_vq, elem, 0);
|
||
|
|
- g_free(elem);
|
||
|
|
- return -EINVAL;
|
||
|
|
+ goto detach;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (n->has_vnet_hdr) {
|
||
|
|
if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
|
||
|
|
n->guest_hdr_len) {
|
||
|
|
virtio_error(vdev, "virtio-net header incorrect");
|
||
|
|
- virtqueue_detach_element(q->tx_vq, elem, 0);
|
||
|
|
- g_free(elem);
|
||
|
|
- return -EINVAL;
|
||
|
|
+ goto detach;
|
||
|
|
}
|
||
|
|
if (n->needs_vnet_hdr_swap) {
|
||
|
|
virtio_net_hdr_swap(vdev, (void *) &vhdr);
|
||
|
|
@@ -2774,6 +2770,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
|
||
|
|
n->guest_hdr_len, -1);
|
||
|
|
out_num = sg_num;
|
||
|
|
out_sg = sg;
|
||
|
|
+
|
||
|
|
+ if (out_num < 1) {
|
||
|
|
+ virtio_error(vdev, "virtio-net nothing to send");
|
||
|
|
+ goto detach;
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
|
||
|
|
@@ -2794,6 +2795,11 @@ drop:
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return num_packets;
|
||
|
|
+
|
||
|
|
+detach:
|
||
|
|
+ virtqueue_detach_element(q->tx_vq, elem, 0);
|
||
|
|
+ g_free(elem);
|
||
|
|
+ return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void virtio_net_tx_timer(void *opaque);
|
||
|
|
--
|
||
|
|
2.41.0.windows.1
|
||
|
|
|