virtio-net: add tap_has_ufo flag to saved state

If we tell the guest we support UFO and then migrate to host which
doesn't support it, we will find ourselves in grave difficulties.

Prevent this scenario by adding a flag to virtio-net's savevm format
which indicates whether the device requires host UFO support.

[v2:
  - add has_ufo uint8_t field for ease of vmstate conversion
  - use qemu_error()
]

Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index bfe1366..4acdd12 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -16,7 +16,7 @@
 #include "qemu-timer.h"
 #include "virtio-net.h"
 
-#define VIRTIO_NET_VM_VERSION    10
+#define VIRTIO_NET_VM_VERSION    11
 
 #define MAC_TABLE_ENTRIES    64
 #define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
@@ -33,6 +33,7 @@
     QEMUTimer *tx_timer;
     int tx_timer_active;
     uint32_t has_vnet_hdr;
+    uint8_t has_ufo;
     struct {
         VirtQueueElement elem;
         ssize_t len;
@@ -134,6 +135,16 @@
     return n->has_vnet_hdr;
 }
 
+static int peer_has_ufo(VirtIONet *n)
+{
+    if (!peer_has_vnet_hdr(n))
+        return 0;
+
+    n->has_ufo = tap_has_ufo(n->vc->peer);
+
+    return n->has_ufo;
+}
+
 static uint32_t virtio_net_get_features(VirtIODevice *vdev)
 {
     VirtIONet *n = to_virtio_net(vdev);
@@ -158,7 +169,7 @@
         features |= (1 << VIRTIO_NET_F_GUEST_TSO6);
         features |= (1 << VIRTIO_NET_F_GUEST_ECN);
 
-        if (tap_has_ufo(n->vc->peer)) {
+        if (peer_has_ufo(n)) {
             features |= (1 << VIRTIO_NET_F_GUEST_UFO);
             features |= (1 << VIRTIO_NET_F_HOST_UFO);
         }
@@ -697,6 +708,7 @@
     qemu_put_byte(f, n->nomulti);
     qemu_put_byte(f, n->nouni);
     qemu_put_byte(f, n->nobcast);
+    qemu_put_byte(f, n->has_ufo);
 }
 
 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
@@ -771,6 +783,13 @@
         n->nobcast = qemu_get_byte(f);
     }
 
+    if (version_id >= 11) {
+        if (qemu_get_byte(f) && !peer_has_ufo(n)) {
+            qemu_error("virtio-net: saved image requires TUN_F_UFO support\n");
+            return -1;
+        }
+    }
+
     /* Find the first multicast entry in the saved MAC filter */
     for (i = 0; i < n->mac_table.in_use; i++) {
         if (n->mac_table.macs[i * ETH_ALEN] & 1) {