Patchwork qemu-kvm-0.11 regression, crashes on older guests with virtio network

login
register
mail settings
Submitter Scott Tsai
Date Oct. 29, 2009, 3:12 a.m.
Message ID <1256785636-sup-215@xpc65.scottt>
Download mbox | patch
Permalink /patch/37166/
State New
Headers show

Comments

Scott Tsai - Oct. 29, 2009, 3:12 a.m.
Excerpts from Dustin Kirkland's message of Thu Oct 29 03:22:43 +0800 2009:
> We're tracking this issue at:
>  * https://bugs.launchpad.net/ubuntu/+source/qemu-kvm/+bug/458521
> 
> I'll gladly review and test patches, or take pointers on where I might
> look to solve this issue.

Try the following patch against the stable-0.11 branch.
I've only just started learning about the virtio-net code but hopefully this patch points you
to the right direction.

Note that this patch just drops the packets that would have caused virtio-net 
to call exit(1).

From d48af0377f359983bff67eb9296ba040def401ec Mon Sep 17 00:00:00 2001
From: Scott Tsai <scottt.tw@gmail.com>
Date: Thu, 29 Oct 2009 10:56:12 +0800
Subject: [PATCH] virtio-net: drop large packets when no mergable_rx_bufs

see: https://bugs.launchpad.net/ubuntu/+source/qemu-kvm/+bug/458521
---
 hw/virtio-net.c |    8 +++++++-
 hw/virtio.c     |   22 ++++++++++++++++++++++
 2 files changed, 29 insertions(+), 1 deletions(-)

Patch

diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index ce8e6cb..2e6725b 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -502,6 +502,8 @@  static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
     return 0;
 }
 
+int buffer_fits_in_virtqueue_top(VirtQueue *vq, int size);
+
 static ssize_t virtio_net_receive2(VLANClientState *vc, const uint8_t *buf, size_t size, int raw)
 {
     VirtIONet *n = vc->opaque;
@@ -518,6 +520,10 @@  static ssize_t virtio_net_receive2(VLANClientState *vc, const uint8_t *buf, size
     hdr_len = n->mergeable_rx_bufs ?
         sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
 
+    /* drop packet instead of truncating it */
+    if (!n->mergeable_rx_bufs && !buffer_fits_in_virtqueue_top(n->rx_vq, hdr_len + size))
+        return;
+
     offset = i = 0;
 
     while (offset < size) {
@@ -531,7 +537,7 @@  static ssize_t virtio_net_receive2(VLANClientState *vc, const uint8_t *buf, size
             virtqueue_pop(n->rx_vq, &elem) == 0) {
             if (i == 0)
                 return -1;
-            fprintf(stderr, "virtio-net truncating packet\n");
+            fprintf(stderr, "virtio-net truncating packet: mergable_rx_bufs: %d\n", n->mergeable_rx_bufs);
             exit(1);
         }
 
diff --git a/hw/virtio.c b/hw/virtio.c
index 41e7ca2..d6f5a12 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -356,6 +356,28 @@  int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes)
     return 0;
 }
 
+/* buffer_fits_in_virtqueue_top: returns true if a 'size' byte buffer could fit in the
+ * input descriptors that virtqueue_pop() would have returned
+ */
+int buffer_fits_in_virtqueue_top(VirtQueue *vq, int size);
+
+int buffer_fits_in_virtqueue_top(VirtQueue *vq, int size)
+{
+    unsigned int i;
+    int input_iov_len_sum;
+
+    if (!virtqueue_num_heads(vq, vq->last_avail_idx))
+        return 0;
+
+    input_iov_len_sum = 0;
+    i = virtqueue_get_head(vq, vq->last_avail_idx);
+    do {
+        if (vring_desc_flags(vq, i) & VRING_DESC_F_WRITE)
+            input_iov_len_sum += vring_desc_len(vq, i);
+    } while ((i = virtqueue_next_desc(vq, i)) != vq->vring.num);
+    return input_iov_len_sum >= size;
+}
+
 int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
 {
     unsigned int i, head, max;