xref: /openbmc/qemu/hw/net/virtio-net.c (revision 068ddfa970063730c5a61b06bae336d68de114e1)
16e790746SPaolo Bonzini /*
26e790746SPaolo Bonzini  * Virtio Network Device
36e790746SPaolo Bonzini  *
46e790746SPaolo Bonzini  * Copyright IBM, Corp. 2007
56e790746SPaolo Bonzini  *
66e790746SPaolo Bonzini  * Authors:
76e790746SPaolo Bonzini  *  Anthony Liguori   <aliguori@us.ibm.com>
86e790746SPaolo Bonzini  *
96e790746SPaolo Bonzini  * This work is licensed under the terms of the GNU GPL, version 2.  See
106e790746SPaolo Bonzini  * the COPYING file in the top-level directory.
116e790746SPaolo Bonzini  *
126e790746SPaolo Bonzini  */
136e790746SPaolo Bonzini 
149b8bfe21SPeter Maydell #include "qemu/osdep.h"
159711cd0dSJens Freimann #include "qemu/atomic.h"
166e790746SPaolo Bonzini #include "qemu/iov.h"
17db725815SMarkus Armbruster #include "qemu/main-loop.h"
180b8fa32fSMarkus Armbruster #include "qemu/module.h"
196e790746SPaolo Bonzini #include "hw/virtio/virtio.h"
206e790746SPaolo Bonzini #include "net/net.h"
216e790746SPaolo Bonzini #include "net/checksum.h"
226e790746SPaolo Bonzini #include "net/tap.h"
236e790746SPaolo Bonzini #include "qemu/error-report.h"
246e790746SPaolo Bonzini #include "qemu/timer.h"
259711cd0dSJens Freimann #include "qemu/option.h"
269711cd0dSJens Freimann #include "qemu/option_int.h"
279711cd0dSJens Freimann #include "qemu/config-file.h"
289711cd0dSJens Freimann #include "qapi/qmp/qdict.h"
296e790746SPaolo Bonzini #include "hw/virtio/virtio-net.h"
306e790746SPaolo Bonzini #include "net/vhost_net.h"
319d8c6a25SDr. David Alan Gilbert #include "net/announce.h"
3217ec5a86SKONRAD Frederic #include "hw/virtio/virtio-bus.h"
33e688df6bSMarkus Armbruster #include "qapi/error.h"
349af23989SMarkus Armbruster #include "qapi/qapi-events-net.h"
35a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
369711cd0dSJens Freimann #include "qapi/qapi-types-migration.h"
379711cd0dSJens Freimann #include "qapi/qapi-events-migration.h"
381399c60dSRusty Russell #include "hw/virtio/virtio-access.h"
39f8d806c9SJuan Quintela #include "migration/misc.h"
409473939eSJason Baron #include "standard-headers/linux/ethtool.h"
412f780b6aSMarkus Armbruster #include "sysemu/sysemu.h"
429d8c6a25SDr. David Alan Gilbert #include "trace.h"
439711cd0dSJens Freimann #include "monitor/qdev.h"
449711cd0dSJens Freimann #include "hw/pci/pci.h"
456e790746SPaolo Bonzini 
466e790746SPaolo Bonzini #define VIRTIO_NET_VM_VERSION    11
476e790746SPaolo Bonzini 
486e790746SPaolo Bonzini #define MAC_TABLE_ENTRIES    64
496e790746SPaolo Bonzini #define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
506e790746SPaolo Bonzini 
511c0fbfa3SMichael S. Tsirkin /* previously fixed value */
521c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256
539b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256
549b02e161SWei Wang 
551c0fbfa3SMichael S. Tsirkin /* for now, only allow larger queues; with virtio-1, guest can downsize */
561c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE
579b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE
581c0fbfa3SMichael S. Tsirkin 
592974e916SYuri Benditovich #define VIRTIO_NET_IP4_ADDR_SIZE   8        /* ipv4 saddr + daddr */
602974e916SYuri Benditovich 
612974e916SYuri Benditovich #define VIRTIO_NET_TCP_FLAG         0x3F
622974e916SYuri Benditovich #define VIRTIO_NET_TCP_HDR_LENGTH   0xF000
632974e916SYuri Benditovich 
642974e916SYuri Benditovich /* IPv4 max payload, 16 bits in the header */
652974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP4_PAYLOAD (65535 - sizeof(struct ip_header))
662974e916SYuri Benditovich #define VIRTIO_NET_MAX_TCP_PAYLOAD 65535
672974e916SYuri Benditovich 
682974e916SYuri Benditovich /* header length value in ip header without option */
692974e916SYuri Benditovich #define VIRTIO_NET_IP4_HEADER_LENGTH 5
702974e916SYuri Benditovich 
712974e916SYuri Benditovich #define VIRTIO_NET_IP6_ADDR_SIZE   32      /* ipv6 saddr + daddr */
722974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP6_PAYLOAD VIRTIO_NET_MAX_TCP_PAYLOAD
732974e916SYuri Benditovich 
742974e916SYuri Benditovich /* Purge coalesced packets timer interval, This value affects the performance
752974e916SYuri Benditovich    a lot, and should be tuned carefully, '300000'(300us) is the recommended
762974e916SYuri Benditovich    value to pass the WHQL test, '50000' can gain 2x netperf throughput with
772974e916SYuri Benditovich    tso/gso/gro 'off'. */
782974e916SYuri Benditovich #define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000
792974e916SYuri Benditovich 
802974e916SYuri Benditovich /* temporary until standard header include it */
812974e916SYuri Benditovich #if !defined(VIRTIO_NET_HDR_F_RSC_INFO)
822974e916SYuri Benditovich 
832974e916SYuri Benditovich #define VIRTIO_NET_HDR_F_RSC_INFO  4 /* rsc_ext data in csum_ fields */
84d47e5e31SYuri Benditovich #define VIRTIO_NET_F_RSC_EXT       61
852974e916SYuri Benditovich 
862974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_packets(
872974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
882974e916SYuri Benditovich {
892974e916SYuri Benditovich     return &hdr->csum_start;
902974e916SYuri Benditovich }
912974e916SYuri Benditovich 
922974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
932974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
942974e916SYuri Benditovich {
952974e916SYuri Benditovich     return &hdr->csum_offset;
962974e916SYuri Benditovich }
972974e916SYuri Benditovich 
982974e916SYuri Benditovich #endif
992974e916SYuri Benditovich 
1006e790746SPaolo Bonzini static VirtIOFeature feature_sizes[] = {
101127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MAC,
1025d5b33c0SMax Reitz      .end = endof(struct virtio_net_config, mac)},
103127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_STATUS,
1045d5b33c0SMax Reitz      .end = endof(struct virtio_net_config, status)},
105127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MQ,
1065d5b33c0SMax Reitz      .end = endof(struct virtio_net_config, max_virtqueue_pairs)},
107127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MTU,
1085d5b33c0SMax Reitz      .end = endof(struct virtio_net_config, mtu)},
1099473939eSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
1105d5b33c0SMax Reitz      .end = endof(struct virtio_net_config, duplex)},
1116e790746SPaolo Bonzini     {}
1126e790746SPaolo Bonzini };
1136e790746SPaolo Bonzini 
1146e790746SPaolo Bonzini static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc)
1156e790746SPaolo Bonzini {
1166e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
1176e790746SPaolo Bonzini 
1186e790746SPaolo Bonzini     return &n->vqs[nc->queue_index];
1196e790746SPaolo Bonzini }
1206e790746SPaolo Bonzini 
1216e790746SPaolo Bonzini static int vq2q(int queue_index)
1226e790746SPaolo Bonzini {
1236e790746SPaolo Bonzini     return queue_index / 2;
1246e790746SPaolo Bonzini }
1256e790746SPaolo Bonzini 
1266e790746SPaolo Bonzini /* TODO
1276e790746SPaolo Bonzini  * - we could suppress RX interrupt if we were so inclined.
1286e790746SPaolo Bonzini  */
1296e790746SPaolo Bonzini 
1306e790746SPaolo Bonzini static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
1316e790746SPaolo Bonzini {
13217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1336e790746SPaolo Bonzini     struct virtio_net_config netcfg;
1346e790746SPaolo Bonzini 
1351399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.status, n->status);
1361399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues);
137a93e599dSMaxime Coquelin     virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu);
1386e790746SPaolo Bonzini     memcpy(netcfg.mac, n->mac, ETH_ALEN);
1399473939eSJason Baron     virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed);
1409473939eSJason Baron     netcfg.duplex = n->net_conf.duplex;
1416e790746SPaolo Bonzini     memcpy(config, &netcfg, n->config_size);
1426e790746SPaolo Bonzini }
1436e790746SPaolo Bonzini 
1446e790746SPaolo Bonzini static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
1456e790746SPaolo Bonzini {
14617a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1476e790746SPaolo Bonzini     struct virtio_net_config netcfg = {};
1486e790746SPaolo Bonzini 
1496e790746SPaolo Bonzini     memcpy(&netcfg, config, n->config_size);
1506e790746SPaolo Bonzini 
15195129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
15295129d6fSCornelia Huck         !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) &&
1536e790746SPaolo Bonzini         memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
1546e790746SPaolo Bonzini         memcpy(n->mac, netcfg.mac, ETH_ALEN);
1556e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
1566e790746SPaolo Bonzini     }
1576e790746SPaolo Bonzini }
1586e790746SPaolo Bonzini 
1596e790746SPaolo Bonzini static bool virtio_net_started(VirtIONet *n, uint8_t status)
1606e790746SPaolo Bonzini {
16117a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1626e790746SPaolo Bonzini     return (status & VIRTIO_CONFIG_S_DRIVER_OK) &&
16317a0ca55SKONRAD Frederic         (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running;
1646e790746SPaolo Bonzini }
1656e790746SPaolo Bonzini 
166b2c929f0SDr. David Alan Gilbert static void virtio_net_announce_notify(VirtIONet *net)
167b2c929f0SDr. David Alan Gilbert {
168b2c929f0SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(net);
169b2c929f0SDr. David Alan Gilbert     trace_virtio_net_announce_notify();
170b2c929f0SDr. David Alan Gilbert 
171b2c929f0SDr. David Alan Gilbert     net->status |= VIRTIO_NET_S_ANNOUNCE;
172b2c929f0SDr. David Alan Gilbert     virtio_notify_config(vdev);
173b2c929f0SDr. David Alan Gilbert }
174b2c929f0SDr. David Alan Gilbert 
175f57fcf70SJason Wang static void virtio_net_announce_timer(void *opaque)
176f57fcf70SJason Wang {
177f57fcf70SJason Wang     VirtIONet *n = opaque;
1789d8c6a25SDr. David Alan Gilbert     trace_virtio_net_announce_timer(n->announce_timer.round);
179f57fcf70SJason Wang 
1809d8c6a25SDr. David Alan Gilbert     n->announce_timer.round--;
181b2c929f0SDr. David Alan Gilbert     virtio_net_announce_notify(n);
182b2c929f0SDr. David Alan Gilbert }
183b2c929f0SDr. David Alan Gilbert 
184b2c929f0SDr. David Alan Gilbert static void virtio_net_announce(NetClientState *nc)
185b2c929f0SDr. David Alan Gilbert {
186b2c929f0SDr. David Alan Gilbert     VirtIONet *n = qemu_get_nic_opaque(nc);
187b2c929f0SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(n);
188b2c929f0SDr. David Alan Gilbert 
189b2c929f0SDr. David Alan Gilbert     /*
190b2c929f0SDr. David Alan Gilbert      * Make sure the virtio migration announcement timer isn't running
191b2c929f0SDr. David Alan Gilbert      * If it is, let it trigger announcement so that we do not cause
192b2c929f0SDr. David Alan Gilbert      * confusion.
193b2c929f0SDr. David Alan Gilbert      */
194b2c929f0SDr. David Alan Gilbert     if (n->announce_timer.round) {
195b2c929f0SDr. David Alan Gilbert         return;
196b2c929f0SDr. David Alan Gilbert     }
197b2c929f0SDr. David Alan Gilbert 
198b2c929f0SDr. David Alan Gilbert     if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
199b2c929f0SDr. David Alan Gilbert         virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
200b2c929f0SDr. David Alan Gilbert             virtio_net_announce_notify(n);
201b2c929f0SDr. David Alan Gilbert     }
202f57fcf70SJason Wang }
203f57fcf70SJason Wang 
2046e790746SPaolo Bonzini static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
2056e790746SPaolo Bonzini {
20617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2076e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
2086e790746SPaolo Bonzini     int queues = n->multiqueue ? n->max_queues : 1;
2096e790746SPaolo Bonzini 
210ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
2116e790746SPaolo Bonzini         return;
2126e790746SPaolo Bonzini     }
2136e790746SPaolo Bonzini 
2148c1ac475SRadim Krčmář     if ((virtio_net_started(n, status) && !nc->peer->link_down) ==
2158c1ac475SRadim Krčmář         !!n->vhost_started) {
2166e790746SPaolo Bonzini         return;
2176e790746SPaolo Bonzini     }
2186e790746SPaolo Bonzini     if (!n->vhost_started) {
219086abc1cSMichael S. Tsirkin         int r, i;
220086abc1cSMichael S. Tsirkin 
2211bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
2221bfa316cSGreg Kurz             error_report("backend does not support %s vnet headers; "
2231bfa316cSGreg Kurz                          "falling back on userspace virtio",
2241bfa316cSGreg Kurz                          virtio_is_big_endian(vdev) ? "BE" : "LE");
2251bfa316cSGreg Kurz             return;
2261bfa316cSGreg Kurz         }
2271bfa316cSGreg Kurz 
228086abc1cSMichael S. Tsirkin         /* Any packets outstanding? Purge them to avoid touching rings
229086abc1cSMichael S. Tsirkin          * when vhost is running.
230086abc1cSMichael S. Tsirkin          */
231086abc1cSMichael S. Tsirkin         for (i = 0;  i < queues; i++) {
232086abc1cSMichael S. Tsirkin             NetClientState *qnc = qemu_get_subqueue(n->nic, i);
233086abc1cSMichael S. Tsirkin 
234086abc1cSMichael S. Tsirkin             /* Purge both directions: TX and RX. */
235086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->peer->incoming_queue, qnc);
236086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->incoming_queue, qnc->peer);
237086abc1cSMichael S. Tsirkin         }
238086abc1cSMichael S. Tsirkin 
239a93e599dSMaxime Coquelin         if (virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MTU)) {
240a93e599dSMaxime Coquelin             r = vhost_net_set_mtu(get_vhost_net(nc->peer), n->net_conf.mtu);
241a93e599dSMaxime Coquelin             if (r < 0) {
242a93e599dSMaxime Coquelin                 error_report("%uBytes MTU not supported by the backend",
243a93e599dSMaxime Coquelin                              n->net_conf.mtu);
244a93e599dSMaxime Coquelin 
245a93e599dSMaxime Coquelin                 return;
246a93e599dSMaxime Coquelin             }
247a93e599dSMaxime Coquelin         }
248a93e599dSMaxime Coquelin 
2496e790746SPaolo Bonzini         n->vhost_started = 1;
25017a0ca55SKONRAD Frederic         r = vhost_net_start(vdev, n->nic->ncs, queues);
2516e790746SPaolo Bonzini         if (r < 0) {
2526e790746SPaolo Bonzini             error_report("unable to start vhost net: %d: "
2536e790746SPaolo Bonzini                          "falling back on userspace virtio", -r);
2546e790746SPaolo Bonzini             n->vhost_started = 0;
2556e790746SPaolo Bonzini         }
2566e790746SPaolo Bonzini     } else {
25717a0ca55SKONRAD Frederic         vhost_net_stop(vdev, n->nic->ncs, queues);
2586e790746SPaolo Bonzini         n->vhost_started = 0;
2596e790746SPaolo Bonzini     }
2606e790746SPaolo Bonzini }
2616e790746SPaolo Bonzini 
2621bfa316cSGreg Kurz static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev,
2631bfa316cSGreg Kurz                                           NetClientState *peer,
2641bfa316cSGreg Kurz                                           bool enable)
2651bfa316cSGreg Kurz {
2661bfa316cSGreg Kurz     if (virtio_is_big_endian(vdev)) {
2671bfa316cSGreg Kurz         return qemu_set_vnet_be(peer, enable);
2681bfa316cSGreg Kurz     } else {
2691bfa316cSGreg Kurz         return qemu_set_vnet_le(peer, enable);
2701bfa316cSGreg Kurz     }
2711bfa316cSGreg Kurz }
2721bfa316cSGreg Kurz 
2731bfa316cSGreg Kurz static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs,
2741bfa316cSGreg Kurz                                        int queues, bool enable)
2751bfa316cSGreg Kurz {
2761bfa316cSGreg Kurz     int i;
2771bfa316cSGreg Kurz 
2781bfa316cSGreg Kurz     for (i = 0; i < queues; i++) {
2791bfa316cSGreg Kurz         if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 &&
2801bfa316cSGreg Kurz             enable) {
2811bfa316cSGreg Kurz             while (--i >= 0) {
2821bfa316cSGreg Kurz                 virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false);
2831bfa316cSGreg Kurz             }
2841bfa316cSGreg Kurz 
2851bfa316cSGreg Kurz             return true;
2861bfa316cSGreg Kurz         }
2871bfa316cSGreg Kurz     }
2881bfa316cSGreg Kurz 
2891bfa316cSGreg Kurz     return false;
2901bfa316cSGreg Kurz }
2911bfa316cSGreg Kurz 
2921bfa316cSGreg Kurz static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status)
2931bfa316cSGreg Kurz {
2941bfa316cSGreg Kurz     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2951bfa316cSGreg Kurz     int queues = n->multiqueue ? n->max_queues : 1;
2961bfa316cSGreg Kurz 
2971bfa316cSGreg Kurz     if (virtio_net_started(n, status)) {
2981bfa316cSGreg Kurz         /* Before using the device, we tell the network backend about the
2991bfa316cSGreg Kurz          * endianness to use when parsing vnet headers. If the backend
3001bfa316cSGreg Kurz          * can't do it, we fallback onto fixing the headers in the core
3011bfa316cSGreg Kurz          * virtio-net code.
3021bfa316cSGreg Kurz          */
3031bfa316cSGreg Kurz         n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs,
3041bfa316cSGreg Kurz                                                             queues, true);
3051bfa316cSGreg Kurz     } else if (virtio_net_started(n, vdev->status)) {
3061bfa316cSGreg Kurz         /* After using the device, we need to reset the network backend to
3071bfa316cSGreg Kurz          * the default (guest native endianness), otherwise the guest may
3081bfa316cSGreg Kurz          * lose network connectivity if it is rebooted into a different
3091bfa316cSGreg Kurz          * endianness.
3101bfa316cSGreg Kurz          */
3111bfa316cSGreg Kurz         virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false);
3121bfa316cSGreg Kurz     }
3131bfa316cSGreg Kurz }
3141bfa316cSGreg Kurz 
315283e2c2aSYuri Benditovich static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq)
316283e2c2aSYuri Benditovich {
317283e2c2aSYuri Benditovich     unsigned int dropped = virtqueue_drop_all(vq);
318283e2c2aSYuri Benditovich     if (dropped) {
319283e2c2aSYuri Benditovich         virtio_notify(vdev, vq);
320283e2c2aSYuri Benditovich     }
321283e2c2aSYuri Benditovich }
322283e2c2aSYuri Benditovich 
3236e790746SPaolo Bonzini static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
3246e790746SPaolo Bonzini {
32517a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
3266e790746SPaolo Bonzini     VirtIONetQueue *q;
3276e790746SPaolo Bonzini     int i;
3286e790746SPaolo Bonzini     uint8_t queue_status;
3296e790746SPaolo Bonzini 
3301bfa316cSGreg Kurz     virtio_net_vnet_endian_status(n, status);
3316e790746SPaolo Bonzini     virtio_net_vhost_status(n, status);
3326e790746SPaolo Bonzini 
3336e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
33438705bb5SFam Zheng         NetClientState *ncs = qemu_get_subqueue(n->nic, i);
33538705bb5SFam Zheng         bool queue_started;
3366e790746SPaolo Bonzini         q = &n->vqs[i];
3376e790746SPaolo Bonzini 
3386e790746SPaolo Bonzini         if ((!n->multiqueue && i != 0) || i >= n->curr_queues) {
3396e790746SPaolo Bonzini             queue_status = 0;
3406e790746SPaolo Bonzini         } else {
3416e790746SPaolo Bonzini             queue_status = status;
3426e790746SPaolo Bonzini         }
34338705bb5SFam Zheng         queue_started =
34438705bb5SFam Zheng             virtio_net_started(n, queue_status) && !n->vhost_started;
34538705bb5SFam Zheng 
34638705bb5SFam Zheng         if (queue_started) {
34738705bb5SFam Zheng             qemu_flush_queued_packets(ncs);
34838705bb5SFam Zheng         }
3496e790746SPaolo Bonzini 
3506e790746SPaolo Bonzini         if (!q->tx_waiting) {
3516e790746SPaolo Bonzini             continue;
3526e790746SPaolo Bonzini         }
3536e790746SPaolo Bonzini 
35438705bb5SFam Zheng         if (queue_started) {
3556e790746SPaolo Bonzini             if (q->tx_timer) {
356bc72ad67SAlex Bligh                 timer_mod(q->tx_timer,
357bc72ad67SAlex Bligh                                qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
3586e790746SPaolo Bonzini             } else {
3596e790746SPaolo Bonzini                 qemu_bh_schedule(q->tx_bh);
3606e790746SPaolo Bonzini             }
3616e790746SPaolo Bonzini         } else {
3626e790746SPaolo Bonzini             if (q->tx_timer) {
363bc72ad67SAlex Bligh                 timer_del(q->tx_timer);
3646e790746SPaolo Bonzini             } else {
3656e790746SPaolo Bonzini                 qemu_bh_cancel(q->tx_bh);
3666e790746SPaolo Bonzini             }
367283e2c2aSYuri Benditovich             if ((n->status & VIRTIO_NET_S_LINK_UP) == 0 &&
36870e53e6eSJason Wang                 (queue_status & VIRTIO_CONFIG_S_DRIVER_OK) &&
36970e53e6eSJason Wang                 vdev->vm_running) {
370283e2c2aSYuri Benditovich                 /* if tx is waiting we are likely have some packets in tx queue
371283e2c2aSYuri Benditovich                  * and disabled notification */
372283e2c2aSYuri Benditovich                 q->tx_waiting = 0;
373283e2c2aSYuri Benditovich                 virtio_queue_set_notification(q->tx_vq, 1);
374283e2c2aSYuri Benditovich                 virtio_net_drop_tx_queue_data(vdev, q->tx_vq);
375283e2c2aSYuri Benditovich             }
3766e790746SPaolo Bonzini         }
3776e790746SPaolo Bonzini     }
3786e790746SPaolo Bonzini }
3796e790746SPaolo Bonzini 
3806e790746SPaolo Bonzini static void virtio_net_set_link_status(NetClientState *nc)
3816e790746SPaolo Bonzini {
3826e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
38317a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
3846e790746SPaolo Bonzini     uint16_t old_status = n->status;
3856e790746SPaolo Bonzini 
3866e790746SPaolo Bonzini     if (nc->link_down)
3876e790746SPaolo Bonzini         n->status &= ~VIRTIO_NET_S_LINK_UP;
3886e790746SPaolo Bonzini     else
3896e790746SPaolo Bonzini         n->status |= VIRTIO_NET_S_LINK_UP;
3906e790746SPaolo Bonzini 
3916e790746SPaolo Bonzini     if (n->status != old_status)
39217a0ca55SKONRAD Frederic         virtio_notify_config(vdev);
3936e790746SPaolo Bonzini 
39417a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
3956e790746SPaolo Bonzini }
3966e790746SPaolo Bonzini 
397b1be4280SAmos Kong static void rxfilter_notify(NetClientState *nc)
398b1be4280SAmos Kong {
399b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
400b1be4280SAmos Kong 
401b1be4280SAmos Kong     if (nc->rxfilter_notify_enabled) {
40296e35046SAmos Kong         gchar *path = object_get_canonical_path(OBJECT(n->qdev));
40306150279SWenchao Xia         qapi_event_send_nic_rx_filter_changed(!!n->netclient_name,
4043ab72385SPeter Xu                                               n->netclient_name, path);
40596e35046SAmos Kong         g_free(path);
406b1be4280SAmos Kong 
407b1be4280SAmos Kong         /* disable event notification to avoid events flooding */
408b1be4280SAmos Kong         nc->rxfilter_notify_enabled = 0;
409b1be4280SAmos Kong     }
410b1be4280SAmos Kong }
411b1be4280SAmos Kong 
412f7bc8ef8SAmos Kong static intList *get_vlan_table(VirtIONet *n)
413f7bc8ef8SAmos Kong {
414f7bc8ef8SAmos Kong     intList *list, *entry;
415f7bc8ef8SAmos Kong     int i, j;
416f7bc8ef8SAmos Kong 
417f7bc8ef8SAmos Kong     list = NULL;
418f7bc8ef8SAmos Kong     for (i = 0; i < MAX_VLAN >> 5; i++) {
419f7bc8ef8SAmos Kong         for (j = 0; n->vlans[i] && j <= 0x1f; j++) {
420f7bc8ef8SAmos Kong             if (n->vlans[i] & (1U << j)) {
421f7bc8ef8SAmos Kong                 entry = g_malloc0(sizeof(*entry));
422f7bc8ef8SAmos Kong                 entry->value = (i << 5) + j;
423f7bc8ef8SAmos Kong                 entry->next = list;
424f7bc8ef8SAmos Kong                 list = entry;
425f7bc8ef8SAmos Kong             }
426f7bc8ef8SAmos Kong         }
427f7bc8ef8SAmos Kong     }
428f7bc8ef8SAmos Kong 
429f7bc8ef8SAmos Kong     return list;
430f7bc8ef8SAmos Kong }
431f7bc8ef8SAmos Kong 
432b1be4280SAmos Kong static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
433b1be4280SAmos Kong {
434b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
435f7bc8ef8SAmos Kong     VirtIODevice *vdev = VIRTIO_DEVICE(n);
436b1be4280SAmos Kong     RxFilterInfo *info;
437b1be4280SAmos Kong     strList *str_list, *entry;
438f7bc8ef8SAmos Kong     int i;
439b1be4280SAmos Kong 
440b1be4280SAmos Kong     info = g_malloc0(sizeof(*info));
441b1be4280SAmos Kong     info->name = g_strdup(nc->name);
442b1be4280SAmos Kong     info->promiscuous = n->promisc;
443b1be4280SAmos Kong 
444b1be4280SAmos Kong     if (n->nouni) {
445b1be4280SAmos Kong         info->unicast = RX_STATE_NONE;
446b1be4280SAmos Kong     } else if (n->alluni) {
447b1be4280SAmos Kong         info->unicast = RX_STATE_ALL;
448b1be4280SAmos Kong     } else {
449b1be4280SAmos Kong         info->unicast = RX_STATE_NORMAL;
450b1be4280SAmos Kong     }
451b1be4280SAmos Kong 
452b1be4280SAmos Kong     if (n->nomulti) {
453b1be4280SAmos Kong         info->multicast = RX_STATE_NONE;
454b1be4280SAmos Kong     } else if (n->allmulti) {
455b1be4280SAmos Kong         info->multicast = RX_STATE_ALL;
456b1be4280SAmos Kong     } else {
457b1be4280SAmos Kong         info->multicast = RX_STATE_NORMAL;
458b1be4280SAmos Kong     }
459b1be4280SAmos Kong 
460b1be4280SAmos Kong     info->broadcast_allowed = n->nobcast;
461b1be4280SAmos Kong     info->multicast_overflow = n->mac_table.multi_overflow;
462b1be4280SAmos Kong     info->unicast_overflow = n->mac_table.uni_overflow;
463b1be4280SAmos Kong 
464b0575ba4SScott Feldman     info->main_mac = qemu_mac_strdup_printf(n->mac);
465b1be4280SAmos Kong 
466b1be4280SAmos Kong     str_list = NULL;
467b1be4280SAmos Kong     for (i = 0; i < n->mac_table.first_multi; i++) {
468b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
469b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
470b1be4280SAmos Kong         entry->next = str_list;
471b1be4280SAmos Kong         str_list = entry;
472b1be4280SAmos Kong     }
473b1be4280SAmos Kong     info->unicast_table = str_list;
474b1be4280SAmos Kong 
475b1be4280SAmos Kong     str_list = NULL;
476b1be4280SAmos Kong     for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
477b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
478b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
479b1be4280SAmos Kong         entry->next = str_list;
480b1be4280SAmos Kong         str_list = entry;
481b1be4280SAmos Kong     }
482b1be4280SAmos Kong     info->multicast_table = str_list;
483f7bc8ef8SAmos Kong     info->vlan_table = get_vlan_table(n);
484b1be4280SAmos Kong 
48595129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) {
486f7bc8ef8SAmos Kong         info->vlan = RX_STATE_ALL;
487f7bc8ef8SAmos Kong     } else if (!info->vlan_table) {
488f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NONE;
489f7bc8ef8SAmos Kong     } else {
490f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NORMAL;
491b1be4280SAmos Kong     }
492b1be4280SAmos Kong 
493b1be4280SAmos Kong     /* enable event notification after query */
494b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
495b1be4280SAmos Kong 
496b1be4280SAmos Kong     return info;
497b1be4280SAmos Kong }
498b1be4280SAmos Kong 
4996e790746SPaolo Bonzini static void virtio_net_reset(VirtIODevice *vdev)
5006e790746SPaolo Bonzini {
50117a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
50294b52958SGreg Kurz     int i;
5036e790746SPaolo Bonzini 
5046e790746SPaolo Bonzini     /* Reset back to compatibility mode */
5056e790746SPaolo Bonzini     n->promisc = 1;
5066e790746SPaolo Bonzini     n->allmulti = 0;
5076e790746SPaolo Bonzini     n->alluni = 0;
5086e790746SPaolo Bonzini     n->nomulti = 0;
5096e790746SPaolo Bonzini     n->nouni = 0;
5106e790746SPaolo Bonzini     n->nobcast = 0;
5116e790746SPaolo Bonzini     /* multiqueue is disabled by default */
5126e790746SPaolo Bonzini     n->curr_queues = 1;
5139d8c6a25SDr. David Alan Gilbert     timer_del(n->announce_timer.tm);
5149d8c6a25SDr. David Alan Gilbert     n->announce_timer.round = 0;
515f57fcf70SJason Wang     n->status &= ~VIRTIO_NET_S_ANNOUNCE;
5166e790746SPaolo Bonzini 
5176e790746SPaolo Bonzini     /* Flush any MAC and VLAN filter table state */
5186e790746SPaolo Bonzini     n->mac_table.in_use = 0;
5196e790746SPaolo Bonzini     n->mac_table.first_multi = 0;
5206e790746SPaolo Bonzini     n->mac_table.multi_overflow = 0;
5216e790746SPaolo Bonzini     n->mac_table.uni_overflow = 0;
5226e790746SPaolo Bonzini     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
5236e790746SPaolo Bonzini     memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac));
524702d66a8SMichael S. Tsirkin     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
5256e790746SPaolo Bonzini     memset(n->vlans, 0, MAX_VLAN >> 3);
52694b52958SGreg Kurz 
52794b52958SGreg Kurz     /* Flush any async TX */
52894b52958SGreg Kurz     for (i = 0;  i < n->max_queues; i++) {
52994b52958SGreg Kurz         NetClientState *nc = qemu_get_subqueue(n->nic, i);
53094b52958SGreg Kurz 
53194b52958SGreg Kurz         if (nc->peer) {
53294b52958SGreg Kurz             qemu_flush_or_purge_queued_packets(nc->peer, true);
53394b52958SGreg Kurz             assert(!virtio_net_get_subqueue(nc)->async_tx.elem);
53494b52958SGreg Kurz         }
53594b52958SGreg Kurz     }
5366e790746SPaolo Bonzini }
5376e790746SPaolo Bonzini 
5386e790746SPaolo Bonzini static void peer_test_vnet_hdr(VirtIONet *n)
5396e790746SPaolo Bonzini {
5406e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
5416e790746SPaolo Bonzini     if (!nc->peer) {
5426e790746SPaolo Bonzini         return;
5436e790746SPaolo Bonzini     }
5446e790746SPaolo Bonzini 
545d6085e3aSStefan Hajnoczi     n->has_vnet_hdr = qemu_has_vnet_hdr(nc->peer);
5466e790746SPaolo Bonzini }
5476e790746SPaolo Bonzini 
5486e790746SPaolo Bonzini static int peer_has_vnet_hdr(VirtIONet *n)
5496e790746SPaolo Bonzini {
5506e790746SPaolo Bonzini     return n->has_vnet_hdr;
5516e790746SPaolo Bonzini }
5526e790746SPaolo Bonzini 
5536e790746SPaolo Bonzini static int peer_has_ufo(VirtIONet *n)
5546e790746SPaolo Bonzini {
5556e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n))
5566e790746SPaolo Bonzini         return 0;
5576e790746SPaolo Bonzini 
558d6085e3aSStefan Hajnoczi     n->has_ufo = qemu_has_ufo(qemu_get_queue(n->nic)->peer);
5596e790746SPaolo Bonzini 
5606e790746SPaolo Bonzini     return n->has_ufo;
5616e790746SPaolo Bonzini }
5626e790746SPaolo Bonzini 
563bb9d17f8SCornelia Huck static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
564bb9d17f8SCornelia Huck                                        int version_1)
5656e790746SPaolo Bonzini {
5666e790746SPaolo Bonzini     int i;
5676e790746SPaolo Bonzini     NetClientState *nc;
5686e790746SPaolo Bonzini 
5696e790746SPaolo Bonzini     n->mergeable_rx_bufs = mergeable_rx_bufs;
5706e790746SPaolo Bonzini 
571bb9d17f8SCornelia Huck     if (version_1) {
572bb9d17f8SCornelia Huck         n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
573bb9d17f8SCornelia Huck     } else {
5746e790746SPaolo Bonzini         n->guest_hdr_len = n->mergeable_rx_bufs ?
575bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr_mrg_rxbuf) :
576bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr);
577bb9d17f8SCornelia Huck     }
5786e790746SPaolo Bonzini 
5796e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
5806e790746SPaolo Bonzini         nc = qemu_get_subqueue(n->nic, i);
5816e790746SPaolo Bonzini 
5826e790746SPaolo Bonzini         if (peer_has_vnet_hdr(n) &&
583d6085e3aSStefan Hajnoczi             qemu_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) {
584d6085e3aSStefan Hajnoczi             qemu_set_vnet_hdr_len(nc->peer, n->guest_hdr_len);
5856e790746SPaolo Bonzini             n->host_hdr_len = n->guest_hdr_len;
5866e790746SPaolo Bonzini         }
5876e790746SPaolo Bonzini     }
5886e790746SPaolo Bonzini }
5896e790746SPaolo Bonzini 
5902eef278bSMichael S. Tsirkin static int virtio_net_max_tx_queue_size(VirtIONet *n)
5912eef278bSMichael S. Tsirkin {
5922eef278bSMichael S. Tsirkin     NetClientState *peer = n->nic_conf.peers.ncs[0];
5932eef278bSMichael S. Tsirkin 
5942eef278bSMichael S. Tsirkin     /*
5952eef278bSMichael S. Tsirkin      * Backends other than vhost-user don't support max queue size.
5962eef278bSMichael S. Tsirkin      */
5972eef278bSMichael S. Tsirkin     if (!peer) {
5982eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
5992eef278bSMichael S. Tsirkin     }
6002eef278bSMichael S. Tsirkin 
6012eef278bSMichael S. Tsirkin     if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) {
6022eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
6032eef278bSMichael S. Tsirkin     }
6042eef278bSMichael S. Tsirkin 
6052eef278bSMichael S. Tsirkin     return VIRTQUEUE_MAX_SIZE;
6062eef278bSMichael S. Tsirkin }
6072eef278bSMichael S. Tsirkin 
6086e790746SPaolo Bonzini static int peer_attach(VirtIONet *n, int index)
6096e790746SPaolo Bonzini {
6106e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
6116e790746SPaolo Bonzini 
6126e790746SPaolo Bonzini     if (!nc->peer) {
6136e790746SPaolo Bonzini         return 0;
6146e790746SPaolo Bonzini     }
6156e790746SPaolo Bonzini 
616f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
6177263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 1);
6187263a0adSChangchun Ouyang     }
6197263a0adSChangchun Ouyang 
620f394b2e2SEric Blake     if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
6216e790746SPaolo Bonzini         return 0;
6226e790746SPaolo Bonzini     }
6236e790746SPaolo Bonzini 
6241074b879SJason Wang     if (n->max_queues == 1) {
6251074b879SJason Wang         return 0;
6261074b879SJason Wang     }
6271074b879SJason Wang 
6286e790746SPaolo Bonzini     return tap_enable(nc->peer);
6296e790746SPaolo Bonzini }
6306e790746SPaolo Bonzini 
6316e790746SPaolo Bonzini static int peer_detach(VirtIONet *n, int index)
6326e790746SPaolo Bonzini {
6336e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
6346e790746SPaolo Bonzini 
6356e790746SPaolo Bonzini     if (!nc->peer) {
6366e790746SPaolo Bonzini         return 0;
6376e790746SPaolo Bonzini     }
6386e790746SPaolo Bonzini 
639f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
6407263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 0);
6417263a0adSChangchun Ouyang     }
6427263a0adSChangchun Ouyang 
643f394b2e2SEric Blake     if (nc->peer->info->type !=  NET_CLIENT_DRIVER_TAP) {
6446e790746SPaolo Bonzini         return 0;
6456e790746SPaolo Bonzini     }
6466e790746SPaolo Bonzini 
6476e790746SPaolo Bonzini     return tap_disable(nc->peer);
6486e790746SPaolo Bonzini }
6496e790746SPaolo Bonzini 
6506e790746SPaolo Bonzini static void virtio_net_set_queues(VirtIONet *n)
6516e790746SPaolo Bonzini {
6526e790746SPaolo Bonzini     int i;
653ddfa83eaSJoel Stanley     int r;
6546e790746SPaolo Bonzini 
65568b5f314SYuri Benditovich     if (n->nic->peer_deleted) {
65668b5f314SYuri Benditovich         return;
65768b5f314SYuri Benditovich     }
65868b5f314SYuri Benditovich 
6596e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
6606e790746SPaolo Bonzini         if (i < n->curr_queues) {
661ddfa83eaSJoel Stanley             r = peer_attach(n, i);
662ddfa83eaSJoel Stanley             assert(!r);
6636e790746SPaolo Bonzini         } else {
664ddfa83eaSJoel Stanley             r = peer_detach(n, i);
665ddfa83eaSJoel Stanley             assert(!r);
6666e790746SPaolo Bonzini         }
6676e790746SPaolo Bonzini     }
6686e790746SPaolo Bonzini }
6696e790746SPaolo Bonzini 
670ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue);
6716e790746SPaolo Bonzini 
6729d5b731dSJason Wang static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
6739d5b731dSJason Wang                                         Error **errp)
6746e790746SPaolo Bonzini {
67517a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
6766e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
6776e790746SPaolo Bonzini 
678da3e8a23SShannon Zhao     /* Firstly sync all virtio-net possible supported features */
679da3e8a23SShannon Zhao     features |= n->host_features;
680da3e8a23SShannon Zhao 
6810cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
6826e790746SPaolo Bonzini 
6836e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n)) {
6840cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_CSUM);
6850cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4);
6860cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6);
6870cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN);
6886e790746SPaolo Bonzini 
6890cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM);
6900cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4);
6910cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6);
6920cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN);
6936e790746SPaolo Bonzini     }
6946e790746SPaolo Bonzini 
6956e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) {
6960cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO);
6970cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO);
6986e790746SPaolo Bonzini     }
6996e790746SPaolo Bonzini 
700ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
7016e790746SPaolo Bonzini         return features;
7026e790746SPaolo Bonzini     }
7032974e916SYuri Benditovich 
70475ebec11SMaxime Coquelin     features = vhost_net_get_features(get_vhost_net(nc->peer), features);
70575ebec11SMaxime Coquelin     vdev->backend_features = features;
70675ebec11SMaxime Coquelin 
70775ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
70875ebec11SMaxime Coquelin             (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) {
70975ebec11SMaxime Coquelin         features |= (1ULL << VIRTIO_NET_F_MTU);
71075ebec11SMaxime Coquelin     }
71175ebec11SMaxime Coquelin 
71275ebec11SMaxime Coquelin     return features;
7136e790746SPaolo Bonzini }
7146e790746SPaolo Bonzini 
715019a3edbSGerd Hoffmann static uint64_t virtio_net_bad_features(VirtIODevice *vdev)
7166e790746SPaolo Bonzini {
717019a3edbSGerd Hoffmann     uint64_t features = 0;
7186e790746SPaolo Bonzini 
7196e790746SPaolo Bonzini     /* Linux kernel 2.6.25.  It understood MAC (as everyone must),
7206e790746SPaolo Bonzini      * but also these: */
7210cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
7220cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_CSUM);
7230cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO4);
7240cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO6);
7250cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_ECN);
7266e790746SPaolo Bonzini 
7276e790746SPaolo Bonzini     return features;
7286e790746SPaolo Bonzini }
7296e790746SPaolo Bonzini 
730644c9858SDmitry Fleytman static void virtio_net_apply_guest_offloads(VirtIONet *n)
731644c9858SDmitry Fleytman {
732ad37bb3bSStefan Hajnoczi     qemu_set_offload(qemu_get_queue(n->nic)->peer,
733644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)),
734644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)),
735644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)),
736644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)),
737644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)));
738644c9858SDmitry Fleytman }
739644c9858SDmitry Fleytman 
740644c9858SDmitry Fleytman static uint64_t virtio_net_guest_offloads_by_features(uint32_t features)
741644c9858SDmitry Fleytman {
742644c9858SDmitry Fleytman     static const uint64_t guest_offloads_mask =
743644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_CSUM) |
744644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO4) |
745644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO6) |
746644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_ECN)  |
747644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_UFO);
748644c9858SDmitry Fleytman 
749644c9858SDmitry Fleytman     return guest_offloads_mask & features;
750644c9858SDmitry Fleytman }
751644c9858SDmitry Fleytman 
752644c9858SDmitry Fleytman static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n)
753644c9858SDmitry Fleytman {
754644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
755644c9858SDmitry Fleytman     return virtio_net_guest_offloads_by_features(vdev->guest_features);
756644c9858SDmitry Fleytman }
757644c9858SDmitry Fleytman 
7589711cd0dSJens Freimann static void failover_add_primary(VirtIONet *n, Error **errp)
7599711cd0dSJens Freimann {
7609711cd0dSJens Freimann     Error *err = NULL;
7619711cd0dSJens Freimann 
7629711cd0dSJens Freimann     n->primary_device_opts = qemu_opts_find(qemu_find_opts("device"),
7639711cd0dSJens Freimann             n->primary_device_id);
7649711cd0dSJens Freimann     if (n->primary_device_opts) {
7659711cd0dSJens Freimann         n->primary_dev = qdev_device_add(n->primary_device_opts, &err);
7669711cd0dSJens Freimann         if (err) {
7679711cd0dSJens Freimann             qemu_opts_del(n->primary_device_opts);
7689711cd0dSJens Freimann         }
7699711cd0dSJens Freimann         if (n->primary_dev) {
7709711cd0dSJens Freimann             n->primary_bus = n->primary_dev->parent_bus;
7719711cd0dSJens Freimann             if (err) {
7729711cd0dSJens Freimann                 qdev_unplug(n->primary_dev, &err);
7739711cd0dSJens Freimann                 qdev_set_id(n->primary_dev, "");
7749711cd0dSJens Freimann 
7759711cd0dSJens Freimann             }
7769711cd0dSJens Freimann         }
7779711cd0dSJens Freimann     } else {
7789711cd0dSJens Freimann         error_setg(errp, "Primary device not found");
7799711cd0dSJens Freimann         error_append_hint(errp, "Virtio-net failover will not work. Make "
7809711cd0dSJens Freimann             "sure primary device has parameter"
7819711cd0dSJens Freimann             " failover_pair_id=<virtio-net-id>\n");
7829711cd0dSJens Freimann }
7839711cd0dSJens Freimann     if (err) {
7849711cd0dSJens Freimann         error_propagate(errp, err);
7859711cd0dSJens Freimann     }
7869711cd0dSJens Freimann }
7879711cd0dSJens Freimann 
7889711cd0dSJens Freimann static int is_my_primary(void *opaque, QemuOpts *opts, Error **errp)
7899711cd0dSJens Freimann {
7909711cd0dSJens Freimann     VirtIONet *n = opaque;
7919711cd0dSJens Freimann     int ret = 0;
7929711cd0dSJens Freimann 
7939711cd0dSJens Freimann     const char *standby_id = qemu_opt_get(opts, "failover_pair_id");
7949711cd0dSJens Freimann 
7959711cd0dSJens Freimann     if (standby_id != NULL && (g_strcmp0(standby_id, n->netclient_name) == 0)) {
7969711cd0dSJens Freimann         n->primary_device_id = g_strdup(opts->id);
7979711cd0dSJens Freimann         ret = 1;
7989711cd0dSJens Freimann     }
7999711cd0dSJens Freimann 
8009711cd0dSJens Freimann     return ret;
8019711cd0dSJens Freimann }
8029711cd0dSJens Freimann 
8039711cd0dSJens Freimann static DeviceState *virtio_net_find_primary(VirtIONet *n, Error **errp)
8049711cd0dSJens Freimann {
8059711cd0dSJens Freimann     DeviceState *dev = NULL;
8069711cd0dSJens Freimann     Error *err = NULL;
8079711cd0dSJens Freimann 
8089711cd0dSJens Freimann     if (qemu_opts_foreach(qemu_find_opts("device"),
8099711cd0dSJens Freimann                          is_my_primary, n, &err)) {
8109711cd0dSJens Freimann         if (err) {
8119711cd0dSJens Freimann             error_propagate(errp, err);
8129711cd0dSJens Freimann             return NULL;
8139711cd0dSJens Freimann         }
8149711cd0dSJens Freimann         if (n->primary_device_id) {
8159711cd0dSJens Freimann             dev = qdev_find_recursive(sysbus_get_default(),
8169711cd0dSJens Freimann                     n->primary_device_id);
8179711cd0dSJens Freimann         } else {
8189711cd0dSJens Freimann             error_setg(errp, "Primary device id not found");
8199711cd0dSJens Freimann             return NULL;
8209711cd0dSJens Freimann         }
8219711cd0dSJens Freimann     }
8229711cd0dSJens Freimann     return dev;
8239711cd0dSJens Freimann }
8249711cd0dSJens Freimann 
8259711cd0dSJens Freimann 
8269711cd0dSJens Freimann 
8279711cd0dSJens Freimann static DeviceState *virtio_connect_failover_devices(VirtIONet *n,
8289711cd0dSJens Freimann                                                     DeviceState *dev,
8299711cd0dSJens Freimann                                                     Error **errp)
8309711cd0dSJens Freimann {
8319711cd0dSJens Freimann     DeviceState *prim_dev = NULL;
8329711cd0dSJens Freimann     Error *err = NULL;
8339711cd0dSJens Freimann 
8349711cd0dSJens Freimann     prim_dev = virtio_net_find_primary(n, &err);
8359711cd0dSJens Freimann     if (prim_dev) {
8369711cd0dSJens Freimann         n->primary_device_id = g_strdup(prim_dev->id);
8379711cd0dSJens Freimann         n->primary_device_opts = prim_dev->opts;
8389711cd0dSJens Freimann     } else {
8399711cd0dSJens Freimann         if (err) {
8409711cd0dSJens Freimann             error_propagate(errp, err);
8419711cd0dSJens Freimann         }
8429711cd0dSJens Freimann     }
8439711cd0dSJens Freimann 
8449711cd0dSJens Freimann     return prim_dev;
8459711cd0dSJens Freimann }
8469711cd0dSJens Freimann 
847d5aaa1b0SGerd Hoffmann static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
8486e790746SPaolo Bonzini {
84917a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
8509711cd0dSJens Freimann     Error *err = NULL;
8516e790746SPaolo Bonzini     int i;
8526e790746SPaolo Bonzini 
85375ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
85475ebec11SMaxime Coquelin             !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) {
85575ebec11SMaxime Coquelin         features &= ~(1ULL << VIRTIO_NET_F_MTU);
85675ebec11SMaxime Coquelin     }
85775ebec11SMaxime Coquelin 
858ef546f12SCornelia Huck     virtio_net_set_multiqueue(n,
85995129d6fSCornelia Huck                               virtio_has_feature(features, VIRTIO_NET_F_MQ));
8606e790746SPaolo Bonzini 
861ef546f12SCornelia Huck     virtio_net_set_mrg_rx_bufs(n,
86295129d6fSCornelia Huck                                virtio_has_feature(features,
863bb9d17f8SCornelia Huck                                                   VIRTIO_NET_F_MRG_RXBUF),
86495129d6fSCornelia Huck                                virtio_has_feature(features,
865bb9d17f8SCornelia Huck                                                   VIRTIO_F_VERSION_1));
8666e790746SPaolo Bonzini 
8672974e916SYuri Benditovich     n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
8682974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4);
8692974e916SYuri Benditovich     n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
8702974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6);
8712974e916SYuri Benditovich 
8726e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
873644c9858SDmitry Fleytman         n->curr_guest_offloads =
874644c9858SDmitry Fleytman             virtio_net_guest_offloads_by_features(features);
875644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
8766e790746SPaolo Bonzini     }
8776e790746SPaolo Bonzini 
8786e790746SPaolo Bonzini     for (i = 0;  i < n->max_queues; i++) {
8796e790746SPaolo Bonzini         NetClientState *nc = qemu_get_subqueue(n->nic, i);
8806e790746SPaolo Bonzini 
881ed8b4afeSNikolay Nikolaev         if (!get_vhost_net(nc->peer)) {
8826e790746SPaolo Bonzini             continue;
8836e790746SPaolo Bonzini         }
884ed8b4afeSNikolay Nikolaev         vhost_net_ack_features(get_vhost_net(nc->peer), features);
8856e790746SPaolo Bonzini     }
8860b1eaa88SStefan Fritsch 
88795129d6fSCornelia Huck     if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
8880b1eaa88SStefan Fritsch         memset(n->vlans, 0, MAX_VLAN >> 3);
8890b1eaa88SStefan Fritsch     } else {
8900b1eaa88SStefan Fritsch         memset(n->vlans, 0xff, MAX_VLAN >> 3);
8910b1eaa88SStefan Fritsch     }
8929711cd0dSJens Freimann 
8939711cd0dSJens Freimann     if (virtio_has_feature(features, VIRTIO_NET_F_STANDBY)) {
8949711cd0dSJens Freimann         qapi_event_send_failover_negotiated(n->netclient_name);
8959711cd0dSJens Freimann         atomic_set(&n->primary_should_be_hidden, false);
8969711cd0dSJens Freimann         failover_add_primary(n, &err);
8979711cd0dSJens Freimann         if (err) {
8989711cd0dSJens Freimann             n->primary_dev = virtio_connect_failover_devices(n, n->qdev, &err);
8999711cd0dSJens Freimann             if (err) {
9009711cd0dSJens Freimann                 goto out_err;
9019711cd0dSJens Freimann             }
9029711cd0dSJens Freimann             failover_add_primary(n, &err);
9039711cd0dSJens Freimann             if (err) {
9049711cd0dSJens Freimann                 goto out_err;
9059711cd0dSJens Freimann             }
9069711cd0dSJens Freimann         }
9079711cd0dSJens Freimann     }
9089711cd0dSJens Freimann     return;
9099711cd0dSJens Freimann 
9109711cd0dSJens Freimann out_err:
9119711cd0dSJens Freimann     if (err) {
9129711cd0dSJens Freimann         warn_report_err(err);
9139711cd0dSJens Freimann     }
9146e790746SPaolo Bonzini }
9156e790746SPaolo Bonzini 
9166e790746SPaolo Bonzini static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
9176e790746SPaolo Bonzini                                      struct iovec *iov, unsigned int iov_cnt)
9186e790746SPaolo Bonzini {
9196e790746SPaolo Bonzini     uint8_t on;
9206e790746SPaolo Bonzini     size_t s;
921b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
9226e790746SPaolo Bonzini 
9236e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on));
9246e790746SPaolo Bonzini     if (s != sizeof(on)) {
9256e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9266e790746SPaolo Bonzini     }
9276e790746SPaolo Bonzini 
9286e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) {
9296e790746SPaolo Bonzini         n->promisc = on;
9306e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) {
9316e790746SPaolo Bonzini         n->allmulti = on;
9326e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) {
9336e790746SPaolo Bonzini         n->alluni = on;
9346e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) {
9356e790746SPaolo Bonzini         n->nomulti = on;
9366e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) {
9376e790746SPaolo Bonzini         n->nouni = on;
9386e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) {
9396e790746SPaolo Bonzini         n->nobcast = on;
9406e790746SPaolo Bonzini     } else {
9416e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9426e790746SPaolo Bonzini     }
9436e790746SPaolo Bonzini 
944b1be4280SAmos Kong     rxfilter_notify(nc);
945b1be4280SAmos Kong 
9466e790746SPaolo Bonzini     return VIRTIO_NET_OK;
9476e790746SPaolo Bonzini }
9486e790746SPaolo Bonzini 
949644c9858SDmitry Fleytman static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd,
950644c9858SDmitry Fleytman                                      struct iovec *iov, unsigned int iov_cnt)
951644c9858SDmitry Fleytman {
952644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
953644c9858SDmitry Fleytman     uint64_t offloads;
954644c9858SDmitry Fleytman     size_t s;
955644c9858SDmitry Fleytman 
95695129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
957644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
958644c9858SDmitry Fleytman     }
959644c9858SDmitry Fleytman 
960644c9858SDmitry Fleytman     s = iov_to_buf(iov, iov_cnt, 0, &offloads, sizeof(offloads));
961644c9858SDmitry Fleytman     if (s != sizeof(offloads)) {
962644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
963644c9858SDmitry Fleytman     }
964644c9858SDmitry Fleytman 
965644c9858SDmitry Fleytman     if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) {
966644c9858SDmitry Fleytman         uint64_t supported_offloads;
967644c9858SDmitry Fleytman 
968189ae6bbSJason Wang         offloads = virtio_ldq_p(vdev, &offloads);
969189ae6bbSJason Wang 
970644c9858SDmitry Fleytman         if (!n->has_vnet_hdr) {
971644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
972644c9858SDmitry Fleytman         }
973644c9858SDmitry Fleytman 
9742974e916SYuri Benditovich         n->rsc4_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
9752974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO4);
9762974e916SYuri Benditovich         n->rsc6_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
9772974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO6);
9782974e916SYuri Benditovich         virtio_clear_feature(&offloads, VIRTIO_NET_F_RSC_EXT);
9792974e916SYuri Benditovich 
980644c9858SDmitry Fleytman         supported_offloads = virtio_net_supported_guest_offloads(n);
981644c9858SDmitry Fleytman         if (offloads & ~supported_offloads) {
982644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
983644c9858SDmitry Fleytman         }
984644c9858SDmitry Fleytman 
985644c9858SDmitry Fleytman         n->curr_guest_offloads = offloads;
986644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
987644c9858SDmitry Fleytman 
988644c9858SDmitry Fleytman         return VIRTIO_NET_OK;
989644c9858SDmitry Fleytman     } else {
990644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
991644c9858SDmitry Fleytman     }
992644c9858SDmitry Fleytman }
993644c9858SDmitry Fleytman 
9946e790746SPaolo Bonzini static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
9956e790746SPaolo Bonzini                                  struct iovec *iov, unsigned int iov_cnt)
9966e790746SPaolo Bonzini {
9971399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
9986e790746SPaolo Bonzini     struct virtio_net_ctrl_mac mac_data;
9996e790746SPaolo Bonzini     size_t s;
1000b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
10016e790746SPaolo Bonzini 
10026e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {
10036e790746SPaolo Bonzini         if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {
10046e790746SPaolo Bonzini             return VIRTIO_NET_ERR;
10056e790746SPaolo Bonzini         }
10066e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
10076e790746SPaolo Bonzini         assert(s == sizeof(n->mac));
10086e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
1009b1be4280SAmos Kong         rxfilter_notify(nc);
1010b1be4280SAmos Kong 
10116e790746SPaolo Bonzini         return VIRTIO_NET_OK;
10126e790746SPaolo Bonzini     }
10136e790746SPaolo Bonzini 
10146e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) {
10156e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
10166e790746SPaolo Bonzini     }
10176e790746SPaolo Bonzini 
1018cae2e556SAmos Kong     int in_use = 0;
1019cae2e556SAmos Kong     int first_multi = 0;
1020cae2e556SAmos Kong     uint8_t uni_overflow = 0;
1021cae2e556SAmos Kong     uint8_t multi_overflow = 0;
1022cae2e556SAmos Kong     uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
10236e790746SPaolo Bonzini 
10246e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
10256e790746SPaolo Bonzini                    sizeof(mac_data.entries));
10261399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
10276e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
1028b1be4280SAmos Kong         goto error;
10296e790746SPaolo Bonzini     }
10306e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
10316e790746SPaolo Bonzini 
10326e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {
1033b1be4280SAmos Kong         goto error;
10346e790746SPaolo Bonzini     }
10356e790746SPaolo Bonzini 
10366e790746SPaolo Bonzini     if (mac_data.entries <= MAC_TABLE_ENTRIES) {
1037cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, macs,
10386e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
10396e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
1040b1be4280SAmos Kong             goto error;
10416e790746SPaolo Bonzini         }
1042cae2e556SAmos Kong         in_use += mac_data.entries;
10436e790746SPaolo Bonzini     } else {
1044cae2e556SAmos Kong         uni_overflow = 1;
10456e790746SPaolo Bonzini     }
10466e790746SPaolo Bonzini 
10476e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN);
10486e790746SPaolo Bonzini 
1049cae2e556SAmos Kong     first_multi = in_use;
10506e790746SPaolo Bonzini 
10516e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
10526e790746SPaolo Bonzini                    sizeof(mac_data.entries));
10531399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
10546e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
1055b1be4280SAmos Kong         goto error;
10566e790746SPaolo Bonzini     }
10576e790746SPaolo Bonzini 
10586e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
10596e790746SPaolo Bonzini 
10606e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {
1061b1be4280SAmos Kong         goto error;
10626e790746SPaolo Bonzini     }
10636e790746SPaolo Bonzini 
1064edc24385SMichael S. Tsirkin     if (mac_data.entries <= MAC_TABLE_ENTRIES - in_use) {
1065cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN],
10666e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
10676e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
1068b1be4280SAmos Kong             goto error;
10696e790746SPaolo Bonzini         }
1070cae2e556SAmos Kong         in_use += mac_data.entries;
10716e790746SPaolo Bonzini     } else {
1072cae2e556SAmos Kong         multi_overflow = 1;
10736e790746SPaolo Bonzini     }
10746e790746SPaolo Bonzini 
1075cae2e556SAmos Kong     n->mac_table.in_use = in_use;
1076cae2e556SAmos Kong     n->mac_table.first_multi = first_multi;
1077cae2e556SAmos Kong     n->mac_table.uni_overflow = uni_overflow;
1078cae2e556SAmos Kong     n->mac_table.multi_overflow = multi_overflow;
1079cae2e556SAmos Kong     memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN);
1080cae2e556SAmos Kong     g_free(macs);
1081b1be4280SAmos Kong     rxfilter_notify(nc);
1082b1be4280SAmos Kong 
10836e790746SPaolo Bonzini     return VIRTIO_NET_OK;
1084b1be4280SAmos Kong 
1085b1be4280SAmos Kong error:
1086cae2e556SAmos Kong     g_free(macs);
1087b1be4280SAmos Kong     return VIRTIO_NET_ERR;
10886e790746SPaolo Bonzini }
10896e790746SPaolo Bonzini 
10906e790746SPaolo Bonzini static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
10916e790746SPaolo Bonzini                                         struct iovec *iov, unsigned int iov_cnt)
10926e790746SPaolo Bonzini {
10931399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
10946e790746SPaolo Bonzini     uint16_t vid;
10956e790746SPaolo Bonzini     size_t s;
1096b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
10976e790746SPaolo Bonzini 
10986e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid));
10991399c60dSRusty Russell     vid = virtio_lduw_p(vdev, &vid);
11006e790746SPaolo Bonzini     if (s != sizeof(vid)) {
11016e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
11026e790746SPaolo Bonzini     }
11036e790746SPaolo Bonzini 
11046e790746SPaolo Bonzini     if (vid >= MAX_VLAN)
11056e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
11066e790746SPaolo Bonzini 
11076e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
11086e790746SPaolo Bonzini         n->vlans[vid >> 5] |= (1U << (vid & 0x1f));
11096e790746SPaolo Bonzini     else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL)
11106e790746SPaolo Bonzini         n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f));
11116e790746SPaolo Bonzini     else
11126e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
11136e790746SPaolo Bonzini 
1114b1be4280SAmos Kong     rxfilter_notify(nc);
1115b1be4280SAmos Kong 
11166e790746SPaolo Bonzini     return VIRTIO_NET_OK;
11176e790746SPaolo Bonzini }
11186e790746SPaolo Bonzini 
1119f57fcf70SJason Wang static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
1120f57fcf70SJason Wang                                       struct iovec *iov, unsigned int iov_cnt)
1121f57fcf70SJason Wang {
11229d8c6a25SDr. David Alan Gilbert     trace_virtio_net_handle_announce(n->announce_timer.round);
1123f57fcf70SJason Wang     if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK &&
1124f57fcf70SJason Wang         n->status & VIRTIO_NET_S_ANNOUNCE) {
1125f57fcf70SJason Wang         n->status &= ~VIRTIO_NET_S_ANNOUNCE;
11269d8c6a25SDr. David Alan Gilbert         if (n->announce_timer.round) {
11279d8c6a25SDr. David Alan Gilbert             qemu_announce_timer_step(&n->announce_timer);
1128f57fcf70SJason Wang         }
1129f57fcf70SJason Wang         return VIRTIO_NET_OK;
1130f57fcf70SJason Wang     } else {
1131f57fcf70SJason Wang         return VIRTIO_NET_ERR;
1132f57fcf70SJason Wang     }
1133f57fcf70SJason Wang }
1134f57fcf70SJason Wang 
11356e790746SPaolo Bonzini static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
11366e790746SPaolo Bonzini                                 struct iovec *iov, unsigned int iov_cnt)
11376e790746SPaolo Bonzini {
113817a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
11396e790746SPaolo Bonzini     struct virtio_net_ctrl_mq mq;
11406e790746SPaolo Bonzini     size_t s;
11416e790746SPaolo Bonzini     uint16_t queues;
11426e790746SPaolo Bonzini 
11436e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq));
11446e790746SPaolo Bonzini     if (s != sizeof(mq)) {
11456e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
11466e790746SPaolo Bonzini     }
11476e790746SPaolo Bonzini 
11486e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
11496e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
11506e790746SPaolo Bonzini     }
11516e790746SPaolo Bonzini 
11521399c60dSRusty Russell     queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs);
11536e790746SPaolo Bonzini 
11546e790746SPaolo Bonzini     if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
11556e790746SPaolo Bonzini         queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX ||
11566e790746SPaolo Bonzini         queues > n->max_queues ||
11576e790746SPaolo Bonzini         !n->multiqueue) {
11586e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
11596e790746SPaolo Bonzini     }
11606e790746SPaolo Bonzini 
11616e790746SPaolo Bonzini     n->curr_queues = queues;
11626e790746SPaolo Bonzini     /* stop the backend before changing the number of queues to avoid handling a
11636e790746SPaolo Bonzini      * disabled queue */
116417a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
11656e790746SPaolo Bonzini     virtio_net_set_queues(n);
11666e790746SPaolo Bonzini 
11676e790746SPaolo Bonzini     return VIRTIO_NET_OK;
11686e790746SPaolo Bonzini }
1169ba7eadb5SGreg Kurz 
11706e790746SPaolo Bonzini static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
11716e790746SPaolo Bonzini {
117217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
11736e790746SPaolo Bonzini     struct virtio_net_ctrl_hdr ctrl;
11746e790746SPaolo Bonzini     virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
117551b19ebeSPaolo Bonzini     VirtQueueElement *elem;
11766e790746SPaolo Bonzini     size_t s;
1177771b6ed3SJason Wang     struct iovec *iov, *iov2;
11786e790746SPaolo Bonzini     unsigned int iov_cnt;
11796e790746SPaolo Bonzini 
118051b19ebeSPaolo Bonzini     for (;;) {
118151b19ebeSPaolo Bonzini         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
118251b19ebeSPaolo Bonzini         if (!elem) {
118351b19ebeSPaolo Bonzini             break;
118451b19ebeSPaolo Bonzini         }
118551b19ebeSPaolo Bonzini         if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) ||
118651b19ebeSPaolo Bonzini             iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) {
1187ba7eadb5SGreg Kurz             virtio_error(vdev, "virtio-net ctrl missing headers");
1188ba7eadb5SGreg Kurz             virtqueue_detach_element(vq, elem, 0);
1189ba7eadb5SGreg Kurz             g_free(elem);
1190ba7eadb5SGreg Kurz             break;
11916e790746SPaolo Bonzini         }
11926e790746SPaolo Bonzini 
119351b19ebeSPaolo Bonzini         iov_cnt = elem->out_num;
119451b19ebeSPaolo Bonzini         iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num);
11956e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl));
11966e790746SPaolo Bonzini         iov_discard_front(&iov, &iov_cnt, sizeof(ctrl));
11976e790746SPaolo Bonzini         if (s != sizeof(ctrl)) {
11986e790746SPaolo Bonzini             status = VIRTIO_NET_ERR;
11996e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_RX) {
12006e790746SPaolo Bonzini             status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt);
12016e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) {
12026e790746SPaolo Bonzini             status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt);
12036e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) {
12046e790746SPaolo Bonzini             status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt);
1205f57fcf70SJason Wang         } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) {
1206f57fcf70SJason Wang             status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt);
12076e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) {
12086e790746SPaolo Bonzini             status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt);
1209644c9858SDmitry Fleytman         } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) {
1210644c9858SDmitry Fleytman             status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt);
12116e790746SPaolo Bonzini         }
12126e790746SPaolo Bonzini 
121351b19ebeSPaolo Bonzini         s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status));
12146e790746SPaolo Bonzini         assert(s == sizeof(status));
12156e790746SPaolo Bonzini 
121651b19ebeSPaolo Bonzini         virtqueue_push(vq, elem, sizeof(status));
12176e790746SPaolo Bonzini         virtio_notify(vdev, vq);
1218771b6ed3SJason Wang         g_free(iov2);
121951b19ebeSPaolo Bonzini         g_free(elem);
12206e790746SPaolo Bonzini     }
12216e790746SPaolo Bonzini }
12226e790746SPaolo Bonzini 
12236e790746SPaolo Bonzini /* RX */
12246e790746SPaolo Bonzini 
12256e790746SPaolo Bonzini static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
12266e790746SPaolo Bonzini {
122717a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
12286e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(vq));
12296e790746SPaolo Bonzini 
12306e790746SPaolo Bonzini     qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index));
12316e790746SPaolo Bonzini }
12326e790746SPaolo Bonzini 
12336e790746SPaolo Bonzini static int virtio_net_can_receive(NetClientState *nc)
12346e790746SPaolo Bonzini {
12356e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
123617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
12376e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
12386e790746SPaolo Bonzini 
123917a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
12406e790746SPaolo Bonzini         return 0;
12416e790746SPaolo Bonzini     }
12426e790746SPaolo Bonzini 
12436e790746SPaolo Bonzini     if (nc->queue_index >= n->curr_queues) {
12446e790746SPaolo Bonzini         return 0;
12456e790746SPaolo Bonzini     }
12466e790746SPaolo Bonzini 
12476e790746SPaolo Bonzini     if (!virtio_queue_ready(q->rx_vq) ||
124817a0ca55SKONRAD Frederic         !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
12496e790746SPaolo Bonzini         return 0;
12506e790746SPaolo Bonzini     }
12516e790746SPaolo Bonzini 
12526e790746SPaolo Bonzini     return 1;
12536e790746SPaolo Bonzini }
12546e790746SPaolo Bonzini 
12556e790746SPaolo Bonzini static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize)
12566e790746SPaolo Bonzini {
12576e790746SPaolo Bonzini     VirtIONet *n = q->n;
12586e790746SPaolo Bonzini     if (virtio_queue_empty(q->rx_vq) ||
12596e790746SPaolo Bonzini         (n->mergeable_rx_bufs &&
12606e790746SPaolo Bonzini          !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
12616e790746SPaolo Bonzini         virtio_queue_set_notification(q->rx_vq, 1);
12626e790746SPaolo Bonzini 
12636e790746SPaolo Bonzini         /* To avoid a race condition where the guest has made some buffers
12646e790746SPaolo Bonzini          * available after the above check but before notification was
12656e790746SPaolo Bonzini          * enabled, check for available buffers again.
12666e790746SPaolo Bonzini          */
12676e790746SPaolo Bonzini         if (virtio_queue_empty(q->rx_vq) ||
12686e790746SPaolo Bonzini             (n->mergeable_rx_bufs &&
12696e790746SPaolo Bonzini              !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
12706e790746SPaolo Bonzini             return 0;
12716e790746SPaolo Bonzini         }
12726e790746SPaolo Bonzini     }
12736e790746SPaolo Bonzini 
12746e790746SPaolo Bonzini     virtio_queue_set_notification(q->rx_vq, 0);
12756e790746SPaolo Bonzini     return 1;
12766e790746SPaolo Bonzini }
12776e790746SPaolo Bonzini 
12781399c60dSRusty Russell static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr)
1279032a74a1SCédric Le Goater {
12801399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->hdr_len);
12811399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->gso_size);
12821399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_start);
12831399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_offset);
1284032a74a1SCédric Le Goater }
1285032a74a1SCédric Le Goater 
12866e790746SPaolo Bonzini /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so
12876e790746SPaolo Bonzini  * it never finds out that the packets don't have valid checksums.  This
12886e790746SPaolo Bonzini  * causes dhclient to get upset.  Fedora's carried a patch for ages to
12896e790746SPaolo Bonzini  * fix this with Xen but it hasn't appeared in an upstream release of
12906e790746SPaolo Bonzini  * dhclient yet.
12916e790746SPaolo Bonzini  *
12926e790746SPaolo Bonzini  * To avoid breaking existing guests, we catch udp packets and add
12936e790746SPaolo Bonzini  * checksums.  This is terrible but it's better than hacking the guest
12946e790746SPaolo Bonzini  * kernels.
12956e790746SPaolo Bonzini  *
12966e790746SPaolo Bonzini  * N.B. if we introduce a zero-copy API, this operation is no longer free so
12976e790746SPaolo Bonzini  * we should provide a mechanism to disable it to avoid polluting the host
12986e790746SPaolo Bonzini  * cache.
12996e790746SPaolo Bonzini  */
13006e790746SPaolo Bonzini static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
13016e790746SPaolo Bonzini                                         uint8_t *buf, size_t size)
13026e790746SPaolo Bonzini {
13036e790746SPaolo Bonzini     if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
13046e790746SPaolo Bonzini         (size > 27 && size < 1500) && /* normal sized MTU */
13056e790746SPaolo Bonzini         (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
13066e790746SPaolo Bonzini         (buf[23] == 17) && /* ip.protocol == UDP */
13076e790746SPaolo Bonzini         (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
13086e790746SPaolo Bonzini         net_checksum_calculate(buf, size);
13096e790746SPaolo Bonzini         hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
13106e790746SPaolo Bonzini     }
13116e790746SPaolo Bonzini }
13126e790746SPaolo Bonzini 
13136e790746SPaolo Bonzini static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt,
13146e790746SPaolo Bonzini                            const void *buf, size_t size)
13156e790746SPaolo Bonzini {
13166e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
13176e790746SPaolo Bonzini         /* FIXME this cast is evil */
13186e790746SPaolo Bonzini         void *wbuf = (void *)buf;
13196e790746SPaolo Bonzini         work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len,
13206e790746SPaolo Bonzini                                     size - n->host_hdr_len);
13211bfa316cSGreg Kurz 
13221bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
13231399c60dSRusty Russell             virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf);
13241bfa316cSGreg Kurz         }
13256e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr));
13266e790746SPaolo Bonzini     } else {
13276e790746SPaolo Bonzini         struct virtio_net_hdr hdr = {
13286e790746SPaolo Bonzini             .flags = 0,
13296e790746SPaolo Bonzini             .gso_type = VIRTIO_NET_HDR_GSO_NONE
13306e790746SPaolo Bonzini         };
13316e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr);
13326e790746SPaolo Bonzini     }
13336e790746SPaolo Bonzini }
13346e790746SPaolo Bonzini 
13356e790746SPaolo Bonzini static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
13366e790746SPaolo Bonzini {
13376e790746SPaolo Bonzini     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
13386e790746SPaolo Bonzini     static const uint8_t vlan[] = {0x81, 0x00};
13396e790746SPaolo Bonzini     uint8_t *ptr = (uint8_t *)buf;
13406e790746SPaolo Bonzini     int i;
13416e790746SPaolo Bonzini 
13426e790746SPaolo Bonzini     if (n->promisc)
13436e790746SPaolo Bonzini         return 1;
13446e790746SPaolo Bonzini 
13456e790746SPaolo Bonzini     ptr += n->host_hdr_len;
13466e790746SPaolo Bonzini 
13476e790746SPaolo Bonzini     if (!memcmp(&ptr[12], vlan, sizeof(vlan))) {
13487542d3e7SPeter Maydell         int vid = lduw_be_p(ptr + 14) & 0xfff;
13496e790746SPaolo Bonzini         if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f))))
13506e790746SPaolo Bonzini             return 0;
13516e790746SPaolo Bonzini     }
13526e790746SPaolo Bonzini 
13536e790746SPaolo Bonzini     if (ptr[0] & 1) { // multicast
13546e790746SPaolo Bonzini         if (!memcmp(ptr, bcast, sizeof(bcast))) {
13556e790746SPaolo Bonzini             return !n->nobcast;
13566e790746SPaolo Bonzini         } else if (n->nomulti) {
13576e790746SPaolo Bonzini             return 0;
13586e790746SPaolo Bonzini         } else if (n->allmulti || n->mac_table.multi_overflow) {
13596e790746SPaolo Bonzini             return 1;
13606e790746SPaolo Bonzini         }
13616e790746SPaolo Bonzini 
13626e790746SPaolo Bonzini         for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
13636e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
13646e790746SPaolo Bonzini                 return 1;
13656e790746SPaolo Bonzini             }
13666e790746SPaolo Bonzini         }
13676e790746SPaolo Bonzini     } else { // unicast
13686e790746SPaolo Bonzini         if (n->nouni) {
13696e790746SPaolo Bonzini             return 0;
13706e790746SPaolo Bonzini         } else if (n->alluni || n->mac_table.uni_overflow) {
13716e790746SPaolo Bonzini             return 1;
13726e790746SPaolo Bonzini         } else if (!memcmp(ptr, n->mac, ETH_ALEN)) {
13736e790746SPaolo Bonzini             return 1;
13746e790746SPaolo Bonzini         }
13756e790746SPaolo Bonzini 
13766e790746SPaolo Bonzini         for (i = 0; i < n->mac_table.first_multi; i++) {
13776e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
13786e790746SPaolo Bonzini                 return 1;
13796e790746SPaolo Bonzini             }
13806e790746SPaolo Bonzini         }
13816e790746SPaolo Bonzini     }
13826e790746SPaolo Bonzini 
13836e790746SPaolo Bonzini     return 0;
13846e790746SPaolo Bonzini }
13856e790746SPaolo Bonzini 
138697cd965cSPaolo Bonzini static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
138797cd965cSPaolo Bonzini                                       size_t size)
13886e790746SPaolo Bonzini {
13896e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
13906e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
139117a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
13926e790746SPaolo Bonzini     struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
13936e790746SPaolo Bonzini     struct virtio_net_hdr_mrg_rxbuf mhdr;
13946e790746SPaolo Bonzini     unsigned mhdr_cnt = 0;
13956e790746SPaolo Bonzini     size_t offset, i, guest_offset;
13966e790746SPaolo Bonzini 
13976e790746SPaolo Bonzini     if (!virtio_net_can_receive(nc)) {
13986e790746SPaolo Bonzini         return -1;
13996e790746SPaolo Bonzini     }
14006e790746SPaolo Bonzini 
14016e790746SPaolo Bonzini     /* hdr_len refers to the header we supply to the guest */
14026e790746SPaolo Bonzini     if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) {
14036e790746SPaolo Bonzini         return 0;
14046e790746SPaolo Bonzini     }
14056e790746SPaolo Bonzini 
14066e790746SPaolo Bonzini     if (!receive_filter(n, buf, size))
14076e790746SPaolo Bonzini         return size;
14086e790746SPaolo Bonzini 
14096e790746SPaolo Bonzini     offset = i = 0;
14106e790746SPaolo Bonzini 
14116e790746SPaolo Bonzini     while (offset < size) {
141251b19ebeSPaolo Bonzini         VirtQueueElement *elem;
14136e790746SPaolo Bonzini         int len, total;
141451b19ebeSPaolo Bonzini         const struct iovec *sg;
14156e790746SPaolo Bonzini 
14166e790746SPaolo Bonzini         total = 0;
14176e790746SPaolo Bonzini 
141851b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement));
141951b19ebeSPaolo Bonzini         if (!elem) {
1420ba10b9c0SGreg Kurz             if (i) {
1421ba10b9c0SGreg Kurz                 virtio_error(vdev, "virtio-net unexpected empty queue: "
14226e790746SPaolo Bonzini                              "i %zd mergeable %d offset %zd, size %zd, "
1423019a3edbSGerd Hoffmann                              "guest hdr len %zd, host hdr len %zd "
1424019a3edbSGerd Hoffmann                              "guest features 0x%" PRIx64,
14256e790746SPaolo Bonzini                              i, n->mergeable_rx_bufs, offset, size,
1426019a3edbSGerd Hoffmann                              n->guest_hdr_len, n->host_hdr_len,
1427019a3edbSGerd Hoffmann                              vdev->guest_features);
1428ba10b9c0SGreg Kurz             }
1429ba10b9c0SGreg Kurz             return -1;
14306e790746SPaolo Bonzini         }
14316e790746SPaolo Bonzini 
143251b19ebeSPaolo Bonzini         if (elem->in_num < 1) {
1433ba10b9c0SGreg Kurz             virtio_error(vdev,
1434ba10b9c0SGreg Kurz                          "virtio-net receive queue contains no in buffers");
1435ba10b9c0SGreg Kurz             virtqueue_detach_element(q->rx_vq, elem, 0);
1436ba10b9c0SGreg Kurz             g_free(elem);
1437ba10b9c0SGreg Kurz             return -1;
14386e790746SPaolo Bonzini         }
14396e790746SPaolo Bonzini 
144051b19ebeSPaolo Bonzini         sg = elem->in_sg;
14416e790746SPaolo Bonzini         if (i == 0) {
14426e790746SPaolo Bonzini             assert(offset == 0);
14436e790746SPaolo Bonzini             if (n->mergeable_rx_bufs) {
14446e790746SPaolo Bonzini                 mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg),
144551b19ebeSPaolo Bonzini                                     sg, elem->in_num,
14466e790746SPaolo Bonzini                                     offsetof(typeof(mhdr), num_buffers),
14476e790746SPaolo Bonzini                                     sizeof(mhdr.num_buffers));
14486e790746SPaolo Bonzini             }
14496e790746SPaolo Bonzini 
145051b19ebeSPaolo Bonzini             receive_header(n, sg, elem->in_num, buf, size);
14516e790746SPaolo Bonzini             offset = n->host_hdr_len;
14526e790746SPaolo Bonzini             total += n->guest_hdr_len;
14536e790746SPaolo Bonzini             guest_offset = n->guest_hdr_len;
14546e790746SPaolo Bonzini         } else {
14556e790746SPaolo Bonzini             guest_offset = 0;
14566e790746SPaolo Bonzini         }
14576e790746SPaolo Bonzini 
14586e790746SPaolo Bonzini         /* copy in packet.  ugh */
145951b19ebeSPaolo Bonzini         len = iov_from_buf(sg, elem->in_num, guest_offset,
14606e790746SPaolo Bonzini                            buf + offset, size - offset);
14616e790746SPaolo Bonzini         total += len;
14626e790746SPaolo Bonzini         offset += len;
14636e790746SPaolo Bonzini         /* If buffers can't be merged, at this point we
14646e790746SPaolo Bonzini          * must have consumed the complete packet.
14656e790746SPaolo Bonzini          * Otherwise, drop it. */
14666e790746SPaolo Bonzini         if (!n->mergeable_rx_bufs && offset < size) {
146727e57efeSLadi Prosek             virtqueue_unpop(q->rx_vq, elem, total);
146851b19ebeSPaolo Bonzini             g_free(elem);
14696e790746SPaolo Bonzini             return size;
14706e790746SPaolo Bonzini         }
14716e790746SPaolo Bonzini 
14726e790746SPaolo Bonzini         /* signal other side */
147351b19ebeSPaolo Bonzini         virtqueue_fill(q->rx_vq, elem, total, i++);
147451b19ebeSPaolo Bonzini         g_free(elem);
14756e790746SPaolo Bonzini     }
14766e790746SPaolo Bonzini 
14776e790746SPaolo Bonzini     if (mhdr_cnt) {
14781399c60dSRusty Russell         virtio_stw_p(vdev, &mhdr.num_buffers, i);
14796e790746SPaolo Bonzini         iov_from_buf(mhdr_sg, mhdr_cnt,
14806e790746SPaolo Bonzini                      0,
14816e790746SPaolo Bonzini                      &mhdr.num_buffers, sizeof mhdr.num_buffers);
14826e790746SPaolo Bonzini     }
14836e790746SPaolo Bonzini 
14846e790746SPaolo Bonzini     virtqueue_flush(q->rx_vq, i);
148517a0ca55SKONRAD Frederic     virtio_notify(vdev, q->rx_vq);
14866e790746SPaolo Bonzini 
14876e790746SPaolo Bonzini     return size;
14886e790746SPaolo Bonzini }
14896e790746SPaolo Bonzini 
14902974e916SYuri Benditovich static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf,
149197cd965cSPaolo Bonzini                                   size_t size)
149297cd965cSPaolo Bonzini {
1493*068ddfa9SDr. David Alan Gilbert     RCU_READ_LOCK_GUARD();
149497cd965cSPaolo Bonzini 
1495*068ddfa9SDr. David Alan Gilbert     return virtio_net_receive_rcu(nc, buf, size);
149697cd965cSPaolo Bonzini }
149797cd965cSPaolo Bonzini 
14982974e916SYuri Benditovich static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
14992974e916SYuri Benditovich                                          const uint8_t *buf,
15002974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
15012974e916SYuri Benditovich {
15022974e916SYuri Benditovich     uint16_t ip_hdrlen;
15032974e916SYuri Benditovich     struct ip_header *ip;
15042974e916SYuri Benditovich 
15052974e916SYuri Benditovich     ip = (struct ip_header *)(buf + chain->n->guest_hdr_len
15062974e916SYuri Benditovich                               + sizeof(struct eth_header));
15072974e916SYuri Benditovich     unit->ip = (void *)ip;
15082974e916SYuri Benditovich     ip_hdrlen = (ip->ip_ver_len & 0xF) << 2;
15092974e916SYuri Benditovich     unit->ip_plen = &ip->ip_len;
15102974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen);
15112974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
15122974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen;
15132974e916SYuri Benditovich }
15142974e916SYuri Benditovich 
15152974e916SYuri Benditovich static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
15162974e916SYuri Benditovich                                          const uint8_t *buf,
15172974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
15182974e916SYuri Benditovich {
15192974e916SYuri Benditovich     struct ip6_header *ip6;
15202974e916SYuri Benditovich 
15212974e916SYuri Benditovich     ip6 = (struct ip6_header *)(buf + chain->n->guest_hdr_len
15222974e916SYuri Benditovich                                  + sizeof(struct eth_header));
15232974e916SYuri Benditovich     unit->ip = ip6;
15242974e916SYuri Benditovich     unit->ip_plen = &(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
15252974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip)\
15262974e916SYuri Benditovich                                         + sizeof(struct ip6_header));
15272974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
15282974e916SYuri Benditovich 
15292974e916SYuri Benditovich     /* There is a difference between payload lenght in ipv4 and v6,
15302974e916SYuri Benditovich        ip header is excluded in ipv6 */
15312974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen;
15322974e916SYuri Benditovich }
15332974e916SYuri Benditovich 
15342974e916SYuri Benditovich static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain,
15352974e916SYuri Benditovich                                        VirtioNetRscSeg *seg)
15362974e916SYuri Benditovich {
15372974e916SYuri Benditovich     int ret;
15382974e916SYuri Benditovich     struct virtio_net_hdr *h;
15392974e916SYuri Benditovich 
15402974e916SYuri Benditovich     h = (struct virtio_net_hdr *)seg->buf;
15412974e916SYuri Benditovich     h->flags = 0;
15422974e916SYuri Benditovich     h->gso_type = VIRTIO_NET_HDR_GSO_NONE;
15432974e916SYuri Benditovich 
15442974e916SYuri Benditovich     if (seg->is_coalesced) {
15452974e916SYuri Benditovich         *virtio_net_rsc_ext_num_packets(h) = seg->packets;
15462974e916SYuri Benditovich         *virtio_net_rsc_ext_num_dupacks(h) = seg->dup_ack;
15472974e916SYuri Benditovich         h->flags = VIRTIO_NET_HDR_F_RSC_INFO;
15482974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
15492974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
15502974e916SYuri Benditovich         } else {
15512974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
15522974e916SYuri Benditovich         }
15532974e916SYuri Benditovich     }
15542974e916SYuri Benditovich 
15552974e916SYuri Benditovich     ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size);
15562974e916SYuri Benditovich     QTAILQ_REMOVE(&chain->buffers, seg, next);
15572974e916SYuri Benditovich     g_free(seg->buf);
15582974e916SYuri Benditovich     g_free(seg);
15592974e916SYuri Benditovich 
15602974e916SYuri Benditovich     return ret;
15612974e916SYuri Benditovich }
15622974e916SYuri Benditovich 
15632974e916SYuri Benditovich static void virtio_net_rsc_purge(void *opq)
15642974e916SYuri Benditovich {
15652974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn;
15662974e916SYuri Benditovich     VirtioNetRscChain *chain = (VirtioNetRscChain *)opq;
15672974e916SYuri Benditovich 
15682974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn) {
15692974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
15702974e916SYuri Benditovich             chain->stat.purge_failed++;
15712974e916SYuri Benditovich             continue;
15722974e916SYuri Benditovich         }
15732974e916SYuri Benditovich     }
15742974e916SYuri Benditovich 
15752974e916SYuri Benditovich     chain->stat.timer++;
15762974e916SYuri Benditovich     if (!QTAILQ_EMPTY(&chain->buffers)) {
15772974e916SYuri Benditovich         timer_mod(chain->drain_timer,
15782974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
15792974e916SYuri Benditovich     }
15802974e916SYuri Benditovich }
15812974e916SYuri Benditovich 
15822974e916SYuri Benditovich static void virtio_net_rsc_cleanup(VirtIONet *n)
15832974e916SYuri Benditovich {
15842974e916SYuri Benditovich     VirtioNetRscChain *chain, *rn_chain;
15852974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn_seg;
15862974e916SYuri Benditovich 
15872974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(chain, &n->rsc_chains, next, rn_chain) {
15882974e916SYuri Benditovich         QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn_seg) {
15892974e916SYuri Benditovich             QTAILQ_REMOVE(&chain->buffers, seg, next);
15902974e916SYuri Benditovich             g_free(seg->buf);
15912974e916SYuri Benditovich             g_free(seg);
15922974e916SYuri Benditovich         }
15932974e916SYuri Benditovich 
15942974e916SYuri Benditovich         timer_del(chain->drain_timer);
15952974e916SYuri Benditovich         timer_free(chain->drain_timer);
15962974e916SYuri Benditovich         QTAILQ_REMOVE(&n->rsc_chains, chain, next);
15972974e916SYuri Benditovich         g_free(chain);
15982974e916SYuri Benditovich     }
15992974e916SYuri Benditovich }
16002974e916SYuri Benditovich 
16012974e916SYuri Benditovich static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain,
16022974e916SYuri Benditovich                                      NetClientState *nc,
16032974e916SYuri Benditovich                                      const uint8_t *buf, size_t size)
16042974e916SYuri Benditovich {
16052974e916SYuri Benditovich     uint16_t hdr_len;
16062974e916SYuri Benditovich     VirtioNetRscSeg *seg;
16072974e916SYuri Benditovich 
16082974e916SYuri Benditovich     hdr_len = chain->n->guest_hdr_len;
16092974e916SYuri Benditovich     seg = g_malloc(sizeof(VirtioNetRscSeg));
16102974e916SYuri Benditovich     seg->buf = g_malloc(hdr_len + sizeof(struct eth_header)
16112974e916SYuri Benditovich         + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD);
16122974e916SYuri Benditovich     memcpy(seg->buf, buf, size);
16132974e916SYuri Benditovich     seg->size = size;
16142974e916SYuri Benditovich     seg->packets = 1;
16152974e916SYuri Benditovich     seg->dup_ack = 0;
16162974e916SYuri Benditovich     seg->is_coalesced = 0;
16172974e916SYuri Benditovich     seg->nc = nc;
16182974e916SYuri Benditovich 
16192974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&chain->buffers, seg, next);
16202974e916SYuri Benditovich     chain->stat.cache++;
16212974e916SYuri Benditovich 
16222974e916SYuri Benditovich     switch (chain->proto) {
16232974e916SYuri Benditovich     case ETH_P_IP:
16242974e916SYuri Benditovich         virtio_net_rsc_extract_unit4(chain, seg->buf, &seg->unit);
16252974e916SYuri Benditovich         break;
16262974e916SYuri Benditovich     case ETH_P_IPV6:
16272974e916SYuri Benditovich         virtio_net_rsc_extract_unit6(chain, seg->buf, &seg->unit);
16282974e916SYuri Benditovich         break;
16292974e916SYuri Benditovich     default:
16302974e916SYuri Benditovich         g_assert_not_reached();
16312974e916SYuri Benditovich     }
16322974e916SYuri Benditovich }
16332974e916SYuri Benditovich 
16342974e916SYuri Benditovich static int32_t virtio_net_rsc_handle_ack(VirtioNetRscChain *chain,
16352974e916SYuri Benditovich                                          VirtioNetRscSeg *seg,
16362974e916SYuri Benditovich                                          const uint8_t *buf,
16372974e916SYuri Benditovich                                          struct tcp_header *n_tcp,
16382974e916SYuri Benditovich                                          struct tcp_header *o_tcp)
16392974e916SYuri Benditovich {
16402974e916SYuri Benditovich     uint32_t nack, oack;
16412974e916SYuri Benditovich     uint16_t nwin, owin;
16422974e916SYuri Benditovich 
16432974e916SYuri Benditovich     nack = htonl(n_tcp->th_ack);
16442974e916SYuri Benditovich     nwin = htons(n_tcp->th_win);
16452974e916SYuri Benditovich     oack = htonl(o_tcp->th_ack);
16462974e916SYuri Benditovich     owin = htons(o_tcp->th_win);
16472974e916SYuri Benditovich 
16482974e916SYuri Benditovich     if ((nack - oack) >= VIRTIO_NET_MAX_TCP_PAYLOAD) {
16492974e916SYuri Benditovich         chain->stat.ack_out_of_win++;
16502974e916SYuri Benditovich         return RSC_FINAL;
16512974e916SYuri Benditovich     } else if (nack == oack) {
16522974e916SYuri Benditovich         /* duplicated ack or window probe */
16532974e916SYuri Benditovich         if (nwin == owin) {
16542974e916SYuri Benditovich             /* duplicated ack, add dup ack count due to whql test up to 1 */
16552974e916SYuri Benditovich             chain->stat.dup_ack++;
16562974e916SYuri Benditovich             return RSC_FINAL;
16572974e916SYuri Benditovich         } else {
16582974e916SYuri Benditovich             /* Coalesce window update */
16592974e916SYuri Benditovich             o_tcp->th_win = n_tcp->th_win;
16602974e916SYuri Benditovich             chain->stat.win_update++;
16612974e916SYuri Benditovich             return RSC_COALESCE;
16622974e916SYuri Benditovich         }
16632974e916SYuri Benditovich     } else {
16642974e916SYuri Benditovich         /* pure ack, go to 'C', finalize*/
16652974e916SYuri Benditovich         chain->stat.pure_ack++;
16662974e916SYuri Benditovich         return RSC_FINAL;
16672974e916SYuri Benditovich     }
16682974e916SYuri Benditovich }
16692974e916SYuri Benditovich 
16702974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain,
16712974e916SYuri Benditovich                                             VirtioNetRscSeg *seg,
16722974e916SYuri Benditovich                                             const uint8_t *buf,
16732974e916SYuri Benditovich                                             VirtioNetRscUnit *n_unit)
16742974e916SYuri Benditovich {
16752974e916SYuri Benditovich     void *data;
16762974e916SYuri Benditovich     uint16_t o_ip_len;
16772974e916SYuri Benditovich     uint32_t nseq, oseq;
16782974e916SYuri Benditovich     VirtioNetRscUnit *o_unit;
16792974e916SYuri Benditovich 
16802974e916SYuri Benditovich     o_unit = &seg->unit;
16812974e916SYuri Benditovich     o_ip_len = htons(*o_unit->ip_plen);
16822974e916SYuri Benditovich     nseq = htonl(n_unit->tcp->th_seq);
16832974e916SYuri Benditovich     oseq = htonl(o_unit->tcp->th_seq);
16842974e916SYuri Benditovich 
16852974e916SYuri Benditovich     /* out of order or retransmitted. */
16862974e916SYuri Benditovich     if ((nseq - oseq) > VIRTIO_NET_MAX_TCP_PAYLOAD) {
16872974e916SYuri Benditovich         chain->stat.data_out_of_win++;
16882974e916SYuri Benditovich         return RSC_FINAL;
16892974e916SYuri Benditovich     }
16902974e916SYuri Benditovich 
16912974e916SYuri Benditovich     data = ((uint8_t *)n_unit->tcp) + n_unit->tcp_hdrlen;
16922974e916SYuri Benditovich     if (nseq == oseq) {
16932974e916SYuri Benditovich         if ((o_unit->payload == 0) && n_unit->payload) {
16942974e916SYuri Benditovich             /* From no payload to payload, normal case, not a dup ack or etc */
16952974e916SYuri Benditovich             chain->stat.data_after_pure_ack++;
16962974e916SYuri Benditovich             goto coalesce;
16972974e916SYuri Benditovich         } else {
16982974e916SYuri Benditovich             return virtio_net_rsc_handle_ack(chain, seg, buf,
16992974e916SYuri Benditovich                                              n_unit->tcp, o_unit->tcp);
17002974e916SYuri Benditovich         }
17012974e916SYuri Benditovich     } else if ((nseq - oseq) != o_unit->payload) {
17022974e916SYuri Benditovich         /* Not a consistent packet, out of order */
17032974e916SYuri Benditovich         chain->stat.data_out_of_order++;
17042974e916SYuri Benditovich         return RSC_FINAL;
17052974e916SYuri Benditovich     } else {
17062974e916SYuri Benditovich coalesce:
17072974e916SYuri Benditovich         if ((o_ip_len + n_unit->payload) > chain->max_payload) {
17082974e916SYuri Benditovich             chain->stat.over_size++;
17092974e916SYuri Benditovich             return RSC_FINAL;
17102974e916SYuri Benditovich         }
17112974e916SYuri Benditovich 
17122974e916SYuri Benditovich         /* Here comes the right data, the payload length in v4/v6 is different,
17132974e916SYuri Benditovich            so use the field value to update and record the new data len */
17142974e916SYuri Benditovich         o_unit->payload += n_unit->payload; /* update new data len */
17152974e916SYuri Benditovich 
17162974e916SYuri Benditovich         /* update field in ip header */
17172974e916SYuri Benditovich         *o_unit->ip_plen = htons(o_ip_len + n_unit->payload);
17182974e916SYuri Benditovich 
17192974e916SYuri Benditovich         /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced
17202974e916SYuri Benditovich            for windows guest, while this may change the behavior for linux
17212974e916SYuri Benditovich            guest (only if it uses RSC feature). */
17222974e916SYuri Benditovich         o_unit->tcp->th_offset_flags = n_unit->tcp->th_offset_flags;
17232974e916SYuri Benditovich 
17242974e916SYuri Benditovich         o_unit->tcp->th_ack = n_unit->tcp->th_ack;
17252974e916SYuri Benditovich         o_unit->tcp->th_win = n_unit->tcp->th_win;
17262974e916SYuri Benditovich 
17272974e916SYuri Benditovich         memmove(seg->buf + seg->size, data, n_unit->payload);
17282974e916SYuri Benditovich         seg->size += n_unit->payload;
17292974e916SYuri Benditovich         seg->packets++;
17302974e916SYuri Benditovich         chain->stat.coalesced++;
17312974e916SYuri Benditovich         return RSC_COALESCE;
17322974e916SYuri Benditovich     }
17332974e916SYuri Benditovich }
17342974e916SYuri Benditovich 
17352974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce4(VirtioNetRscChain *chain,
17362974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
17372974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
17382974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
17392974e916SYuri Benditovich {
17402974e916SYuri Benditovich     struct ip_header *ip1, *ip2;
17412974e916SYuri Benditovich 
17422974e916SYuri Benditovich     ip1 = (struct ip_header *)(unit->ip);
17432974e916SYuri Benditovich     ip2 = (struct ip_header *)(seg->unit.ip);
17442974e916SYuri Benditovich     if ((ip1->ip_src ^ ip2->ip_src) || (ip1->ip_dst ^ ip2->ip_dst)
17452974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
17462974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
17472974e916SYuri Benditovich         chain->stat.no_match++;
17482974e916SYuri Benditovich         return RSC_NO_MATCH;
17492974e916SYuri Benditovich     }
17502974e916SYuri Benditovich 
17512974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
17522974e916SYuri Benditovich }
17532974e916SYuri Benditovich 
17542974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce6(VirtioNetRscChain *chain,
17552974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
17562974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
17572974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
17582974e916SYuri Benditovich {
17592974e916SYuri Benditovich     struct ip6_header *ip1, *ip2;
17602974e916SYuri Benditovich 
17612974e916SYuri Benditovich     ip1 = (struct ip6_header *)(unit->ip);
17622974e916SYuri Benditovich     ip2 = (struct ip6_header *)(seg->unit.ip);
17632974e916SYuri Benditovich     if (memcmp(&ip1->ip6_src, &ip2->ip6_src, sizeof(struct in6_address))
17642974e916SYuri Benditovich         || memcmp(&ip1->ip6_dst, &ip2->ip6_dst, sizeof(struct in6_address))
17652974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
17662974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
17672974e916SYuri Benditovich             chain->stat.no_match++;
17682974e916SYuri Benditovich             return RSC_NO_MATCH;
17692974e916SYuri Benditovich     }
17702974e916SYuri Benditovich 
17712974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
17722974e916SYuri Benditovich }
17732974e916SYuri Benditovich 
17742974e916SYuri Benditovich /* Packets with 'SYN' should bypass, other flag should be sent after drain
17752974e916SYuri Benditovich  * to prevent out of order */
17762974e916SYuri Benditovich static int virtio_net_rsc_tcp_ctrl_check(VirtioNetRscChain *chain,
17772974e916SYuri Benditovich                                          struct tcp_header *tcp)
17782974e916SYuri Benditovich {
17792974e916SYuri Benditovich     uint16_t tcp_hdr;
17802974e916SYuri Benditovich     uint16_t tcp_flag;
17812974e916SYuri Benditovich 
17822974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags);
17832974e916SYuri Benditovich     tcp_hdr = (tcp_flag & VIRTIO_NET_TCP_HDR_LENGTH) >> 10;
17842974e916SYuri Benditovich     tcp_flag &= VIRTIO_NET_TCP_FLAG;
17852974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags) & 0x3F;
17862974e916SYuri Benditovich     if (tcp_flag & TH_SYN) {
17872974e916SYuri Benditovich         chain->stat.tcp_syn++;
17882974e916SYuri Benditovich         return RSC_BYPASS;
17892974e916SYuri Benditovich     }
17902974e916SYuri Benditovich 
17912974e916SYuri Benditovich     if (tcp_flag & (TH_FIN | TH_URG | TH_RST | TH_ECE | TH_CWR)) {
17922974e916SYuri Benditovich         chain->stat.tcp_ctrl_drain++;
17932974e916SYuri Benditovich         return RSC_FINAL;
17942974e916SYuri Benditovich     }
17952974e916SYuri Benditovich 
17962974e916SYuri Benditovich     if (tcp_hdr > sizeof(struct tcp_header)) {
17972974e916SYuri Benditovich         chain->stat.tcp_all_opt++;
17982974e916SYuri Benditovich         return RSC_FINAL;
17992974e916SYuri Benditovich     }
18002974e916SYuri Benditovich 
18012974e916SYuri Benditovich     return RSC_CANDIDATE;
18022974e916SYuri Benditovich }
18032974e916SYuri Benditovich 
18042974e916SYuri Benditovich static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain,
18052974e916SYuri Benditovich                                          NetClientState *nc,
18062974e916SYuri Benditovich                                          const uint8_t *buf, size_t size,
18072974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
18082974e916SYuri Benditovich {
18092974e916SYuri Benditovich     int ret;
18102974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
18112974e916SYuri Benditovich 
18122974e916SYuri Benditovich     if (QTAILQ_EMPTY(&chain->buffers)) {
18132974e916SYuri Benditovich         chain->stat.empty_cache++;
18142974e916SYuri Benditovich         virtio_net_rsc_cache_buf(chain, nc, buf, size);
18152974e916SYuri Benditovich         timer_mod(chain->drain_timer,
18162974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
18172974e916SYuri Benditovich         return size;
18182974e916SYuri Benditovich     }
18192974e916SYuri Benditovich 
18202974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
18212974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
18222974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce4(chain, seg, buf, size, unit);
18232974e916SYuri Benditovich         } else {
18242974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce6(chain, seg, buf, size, unit);
18252974e916SYuri Benditovich         }
18262974e916SYuri Benditovich 
18272974e916SYuri Benditovich         if (ret == RSC_FINAL) {
18282974e916SYuri Benditovich             if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
18292974e916SYuri Benditovich                 /* Send failed */
18302974e916SYuri Benditovich                 chain->stat.final_failed++;
18312974e916SYuri Benditovich                 return 0;
18322974e916SYuri Benditovich             }
18332974e916SYuri Benditovich 
18342974e916SYuri Benditovich             /* Send current packet */
18352974e916SYuri Benditovich             return virtio_net_do_receive(nc, buf, size);
18362974e916SYuri Benditovich         } else if (ret == RSC_NO_MATCH) {
18372974e916SYuri Benditovich             continue;
18382974e916SYuri Benditovich         } else {
18392974e916SYuri Benditovich             /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */
18402974e916SYuri Benditovich             seg->is_coalesced = 1;
18412974e916SYuri Benditovich             return size;
18422974e916SYuri Benditovich         }
18432974e916SYuri Benditovich     }
18442974e916SYuri Benditovich 
18452974e916SYuri Benditovich     chain->stat.no_match_cache++;
18462974e916SYuri Benditovich     virtio_net_rsc_cache_buf(chain, nc, buf, size);
18472974e916SYuri Benditovich     return size;
18482974e916SYuri Benditovich }
18492974e916SYuri Benditovich 
18502974e916SYuri Benditovich /* Drain a connection data, this is to avoid out of order segments */
18512974e916SYuri Benditovich static size_t virtio_net_rsc_drain_flow(VirtioNetRscChain *chain,
18522974e916SYuri Benditovich                                         NetClientState *nc,
18532974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
18542974e916SYuri Benditovich                                         uint16_t ip_start, uint16_t ip_size,
18552974e916SYuri Benditovich                                         uint16_t tcp_port)
18562974e916SYuri Benditovich {
18572974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
18582974e916SYuri Benditovich     uint32_t ppair1, ppair2;
18592974e916SYuri Benditovich 
18602974e916SYuri Benditovich     ppair1 = *(uint32_t *)(buf + tcp_port);
18612974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
18622974e916SYuri Benditovich         ppair2 = *(uint32_t *)(seg->buf + tcp_port);
18632974e916SYuri Benditovich         if (memcmp(buf + ip_start, seg->buf + ip_start, ip_size)
18642974e916SYuri Benditovich             || (ppair1 != ppair2)) {
18652974e916SYuri Benditovich             continue;
18662974e916SYuri Benditovich         }
18672974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
18682974e916SYuri Benditovich             chain->stat.drain_failed++;
18692974e916SYuri Benditovich         }
18702974e916SYuri Benditovich 
18712974e916SYuri Benditovich         break;
18722974e916SYuri Benditovich     }
18732974e916SYuri Benditovich 
18742974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
18752974e916SYuri Benditovich }
18762974e916SYuri Benditovich 
18772974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check4(VirtioNetRscChain *chain,
18782974e916SYuri Benditovich                                             struct ip_header *ip,
18792974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
18802974e916SYuri Benditovich {
18812974e916SYuri Benditovich     uint16_t ip_len;
18822974e916SYuri Benditovich 
18832974e916SYuri Benditovich     /* Not an ipv4 packet */
18842974e916SYuri Benditovich     if (((ip->ip_ver_len & 0xF0) >> 4) != IP_HEADER_VERSION_4) {
18852974e916SYuri Benditovich         chain->stat.ip_option++;
18862974e916SYuri Benditovich         return RSC_BYPASS;
18872974e916SYuri Benditovich     }
18882974e916SYuri Benditovich 
18892974e916SYuri Benditovich     /* Don't handle packets with ip option */
18902974e916SYuri Benditovich     if ((ip->ip_ver_len & 0xF) != VIRTIO_NET_IP4_HEADER_LENGTH) {
18912974e916SYuri Benditovich         chain->stat.ip_option++;
18922974e916SYuri Benditovich         return RSC_BYPASS;
18932974e916SYuri Benditovich     }
18942974e916SYuri Benditovich 
18952974e916SYuri Benditovich     if (ip->ip_p != IPPROTO_TCP) {
18962974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
18972974e916SYuri Benditovich         return RSC_BYPASS;
18982974e916SYuri Benditovich     }
18992974e916SYuri Benditovich 
19002974e916SYuri Benditovich     /* Don't handle packets with ip fragment */
19012974e916SYuri Benditovich     if (!(htons(ip->ip_off) & IP_DF)) {
19022974e916SYuri Benditovich         chain->stat.ip_frag++;
19032974e916SYuri Benditovich         return RSC_BYPASS;
19042974e916SYuri Benditovich     }
19052974e916SYuri Benditovich 
19062974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
19072974e916SYuri Benditovich     if (IPTOS_ECN(ip->ip_tos)) {
19082974e916SYuri Benditovich         chain->stat.ip_ecn++;
19092974e916SYuri Benditovich         return RSC_BYPASS;
19102974e916SYuri Benditovich     }
19112974e916SYuri Benditovich 
19122974e916SYuri Benditovich     ip_len = htons(ip->ip_len);
19132974e916SYuri Benditovich     if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header))
19142974e916SYuri Benditovich         || ip_len > (size - chain->n->guest_hdr_len -
19152974e916SYuri Benditovich                      sizeof(struct eth_header))) {
19162974e916SYuri Benditovich         chain->stat.ip_hacked++;
19172974e916SYuri Benditovich         return RSC_BYPASS;
19182974e916SYuri Benditovich     }
19192974e916SYuri Benditovich 
19202974e916SYuri Benditovich     return RSC_CANDIDATE;
19212974e916SYuri Benditovich }
19222974e916SYuri Benditovich 
19232974e916SYuri Benditovich static size_t virtio_net_rsc_receive4(VirtioNetRscChain *chain,
19242974e916SYuri Benditovich                                       NetClientState *nc,
19252974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
19262974e916SYuri Benditovich {
19272974e916SYuri Benditovich     int32_t ret;
19282974e916SYuri Benditovich     uint16_t hdr_len;
19292974e916SYuri Benditovich     VirtioNetRscUnit unit;
19302974e916SYuri Benditovich 
19312974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
19322974e916SYuri Benditovich 
19332974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header)
19342974e916SYuri Benditovich         + sizeof(struct tcp_header))) {
19352974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
19362974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19372974e916SYuri Benditovich     }
19382974e916SYuri Benditovich 
19392974e916SYuri Benditovich     virtio_net_rsc_extract_unit4(chain, buf, &unit);
19402974e916SYuri Benditovich     if (virtio_net_rsc_sanity_check4(chain, unit.ip, buf, size)
19412974e916SYuri Benditovich         != RSC_CANDIDATE) {
19422974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19432974e916SYuri Benditovich     }
19442974e916SYuri Benditovich 
19452974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
19462974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
19472974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19482974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
19492974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
19502974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 12),
19512974e916SYuri Benditovich                 VIRTIO_NET_IP4_ADDR_SIZE,
19522974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header));
19532974e916SYuri Benditovich     }
19542974e916SYuri Benditovich 
19552974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
19562974e916SYuri Benditovich }
19572974e916SYuri Benditovich 
19582974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check6(VirtioNetRscChain *chain,
19592974e916SYuri Benditovich                                             struct ip6_header *ip6,
19602974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
19612974e916SYuri Benditovich {
19622974e916SYuri Benditovich     uint16_t ip_len;
19632974e916SYuri Benditovich 
19642974e916SYuri Benditovich     if (((ip6->ip6_ctlun.ip6_un1.ip6_un1_flow & 0xF0) >> 4)
19652974e916SYuri Benditovich         != IP_HEADER_VERSION_6) {
19662974e916SYuri Benditovich         return RSC_BYPASS;
19672974e916SYuri Benditovich     }
19682974e916SYuri Benditovich 
19692974e916SYuri Benditovich     /* Both option and protocol is checked in this */
19702974e916SYuri Benditovich     if (ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) {
19712974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
19722974e916SYuri Benditovich         return RSC_BYPASS;
19732974e916SYuri Benditovich     }
19742974e916SYuri Benditovich 
19752974e916SYuri Benditovich     ip_len = htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
19762974e916SYuri Benditovich     if (ip_len < sizeof(struct tcp_header) ||
19772974e916SYuri Benditovich         ip_len > (size - chain->n->guest_hdr_len - sizeof(struct eth_header)
19782974e916SYuri Benditovich                   - sizeof(struct ip6_header))) {
19792974e916SYuri Benditovich         chain->stat.ip_hacked++;
19802974e916SYuri Benditovich         return RSC_BYPASS;
19812974e916SYuri Benditovich     }
19822974e916SYuri Benditovich 
19832974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
19842974e916SYuri Benditovich     if (IP6_ECN(ip6->ip6_ctlun.ip6_un3.ip6_un3_ecn)) {
19852974e916SYuri Benditovich         chain->stat.ip_ecn++;
19862974e916SYuri Benditovich         return RSC_BYPASS;
19872974e916SYuri Benditovich     }
19882974e916SYuri Benditovich 
19892974e916SYuri Benditovich     return RSC_CANDIDATE;
19902974e916SYuri Benditovich }
19912974e916SYuri Benditovich 
19922974e916SYuri Benditovich static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc,
19932974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
19942974e916SYuri Benditovich {
19952974e916SYuri Benditovich     int32_t ret;
19962974e916SYuri Benditovich     uint16_t hdr_len;
19972974e916SYuri Benditovich     VirtioNetRscChain *chain;
19982974e916SYuri Benditovich     VirtioNetRscUnit unit;
19992974e916SYuri Benditovich 
20002974e916SYuri Benditovich     chain = (VirtioNetRscChain *)opq;
20012974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
20022974e916SYuri Benditovich 
20032974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header)
20042974e916SYuri Benditovich         + sizeof(tcp_header))) {
20052974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
20062974e916SYuri Benditovich     }
20072974e916SYuri Benditovich 
20082974e916SYuri Benditovich     virtio_net_rsc_extract_unit6(chain, buf, &unit);
20092974e916SYuri Benditovich     if (RSC_CANDIDATE != virtio_net_rsc_sanity_check6(chain,
20102974e916SYuri Benditovich                                                  unit.ip, buf, size)) {
20112974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
20122974e916SYuri Benditovich     }
20132974e916SYuri Benditovich 
20142974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
20152974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
20162974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
20172974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
20182974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
20192974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 8),
20202974e916SYuri Benditovich                 VIRTIO_NET_IP6_ADDR_SIZE,
20212974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header)
20222974e916SYuri Benditovich                 + sizeof(struct ip6_header));
20232974e916SYuri Benditovich     }
20242974e916SYuri Benditovich 
20252974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
20262974e916SYuri Benditovich }
20272974e916SYuri Benditovich 
20282974e916SYuri Benditovich static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n,
20292974e916SYuri Benditovich                                                       NetClientState *nc,
20302974e916SYuri Benditovich                                                       uint16_t proto)
20312974e916SYuri Benditovich {
20322974e916SYuri Benditovich     VirtioNetRscChain *chain;
20332974e916SYuri Benditovich 
20342974e916SYuri Benditovich     if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) {
20352974e916SYuri Benditovich         return NULL;
20362974e916SYuri Benditovich     }
20372974e916SYuri Benditovich 
20382974e916SYuri Benditovich     QTAILQ_FOREACH(chain, &n->rsc_chains, next) {
20392974e916SYuri Benditovich         if (chain->proto == proto) {
20402974e916SYuri Benditovich             return chain;
20412974e916SYuri Benditovich         }
20422974e916SYuri Benditovich     }
20432974e916SYuri Benditovich 
20442974e916SYuri Benditovich     chain = g_malloc(sizeof(*chain));
20452974e916SYuri Benditovich     chain->n = n;
20462974e916SYuri Benditovich     chain->proto = proto;
20472974e916SYuri Benditovich     if (proto == (uint16_t)ETH_P_IP) {
20482974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP4_PAYLOAD;
20492974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
20502974e916SYuri Benditovich     } else {
20512974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD;
20522974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
20532974e916SYuri Benditovich     }
20542974e916SYuri Benditovich     chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST,
20552974e916SYuri Benditovich                                       virtio_net_rsc_purge, chain);
20562974e916SYuri Benditovich     memset(&chain->stat, 0, sizeof(chain->stat));
20572974e916SYuri Benditovich 
20582974e916SYuri Benditovich     QTAILQ_INIT(&chain->buffers);
20592974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next);
20602974e916SYuri Benditovich 
20612974e916SYuri Benditovich     return chain;
20622974e916SYuri Benditovich }
20632974e916SYuri Benditovich 
20642974e916SYuri Benditovich static ssize_t virtio_net_rsc_receive(NetClientState *nc,
20652974e916SYuri Benditovich                                       const uint8_t *buf,
20662974e916SYuri Benditovich                                       size_t size)
20672974e916SYuri Benditovich {
20682974e916SYuri Benditovich     uint16_t proto;
20692974e916SYuri Benditovich     VirtioNetRscChain *chain;
20702974e916SYuri Benditovich     struct eth_header *eth;
20712974e916SYuri Benditovich     VirtIONet *n;
20722974e916SYuri Benditovich 
20732974e916SYuri Benditovich     n = qemu_get_nic_opaque(nc);
20742974e916SYuri Benditovich     if (size < (n->host_hdr_len + sizeof(struct eth_header))) {
20752974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
20762974e916SYuri Benditovich     }
20772974e916SYuri Benditovich 
20782974e916SYuri Benditovich     eth = (struct eth_header *)(buf + n->guest_hdr_len);
20792974e916SYuri Benditovich     proto = htons(eth->h_proto);
20802974e916SYuri Benditovich 
20812974e916SYuri Benditovich     chain = virtio_net_rsc_lookup_chain(n, nc, proto);
20822974e916SYuri Benditovich     if (chain) {
20832974e916SYuri Benditovich         chain->stat.received++;
20842974e916SYuri Benditovich         if (proto == (uint16_t)ETH_P_IP && n->rsc4_enabled) {
20852974e916SYuri Benditovich             return virtio_net_rsc_receive4(chain, nc, buf, size);
20862974e916SYuri Benditovich         } else if (proto == (uint16_t)ETH_P_IPV6 && n->rsc6_enabled) {
20872974e916SYuri Benditovich             return virtio_net_rsc_receive6(chain, nc, buf, size);
20882974e916SYuri Benditovich         }
20892974e916SYuri Benditovich     }
20902974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
20912974e916SYuri Benditovich }
20922974e916SYuri Benditovich 
20932974e916SYuri Benditovich static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf,
20942974e916SYuri Benditovich                                   size_t size)
20952974e916SYuri Benditovich {
20962974e916SYuri Benditovich     VirtIONet *n = qemu_get_nic_opaque(nc);
20972974e916SYuri Benditovich     if ((n->rsc4_enabled || n->rsc6_enabled)) {
20982974e916SYuri Benditovich         return virtio_net_rsc_receive(nc, buf, size);
20992974e916SYuri Benditovich     } else {
21002974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
21012974e916SYuri Benditovich     }
21022974e916SYuri Benditovich }
21032974e916SYuri Benditovich 
21046e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q);
21056e790746SPaolo Bonzini 
21066e790746SPaolo Bonzini static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
21076e790746SPaolo Bonzini {
21086e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
21096e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
211017a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
21116e790746SPaolo Bonzini 
211251b19ebeSPaolo Bonzini     virtqueue_push(q->tx_vq, q->async_tx.elem, 0);
211317a0ca55SKONRAD Frederic     virtio_notify(vdev, q->tx_vq);
21146e790746SPaolo Bonzini 
211551b19ebeSPaolo Bonzini     g_free(q->async_tx.elem);
211651b19ebeSPaolo Bonzini     q->async_tx.elem = NULL;
21176e790746SPaolo Bonzini 
21186e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
21196e790746SPaolo Bonzini     virtio_net_flush_tx(q);
21206e790746SPaolo Bonzini }
21216e790746SPaolo Bonzini 
21226e790746SPaolo Bonzini /* TX */
21236e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
21246e790746SPaolo Bonzini {
21256e790746SPaolo Bonzini     VirtIONet *n = q->n;
212617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
212751b19ebeSPaolo Bonzini     VirtQueueElement *elem;
21286e790746SPaolo Bonzini     int32_t num_packets = 0;
21296e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(q->tx_vq));
213017a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
21316e790746SPaolo Bonzini         return num_packets;
21326e790746SPaolo Bonzini     }
21336e790746SPaolo Bonzini 
213451b19ebeSPaolo Bonzini     if (q->async_tx.elem) {
21356e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
21366e790746SPaolo Bonzini         return num_packets;
21376e790746SPaolo Bonzini     }
21386e790746SPaolo Bonzini 
213951b19ebeSPaolo Bonzini     for (;;) {
2140bd89dd98SJason Wang         ssize_t ret;
214151b19ebeSPaolo Bonzini         unsigned int out_num;
214251b19ebeSPaolo Bonzini         struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
2143feb93f36SJason Wang         struct virtio_net_hdr_mrg_rxbuf mhdr;
21446e790746SPaolo Bonzini 
214551b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
214651b19ebeSPaolo Bonzini         if (!elem) {
214751b19ebeSPaolo Bonzini             break;
214851b19ebeSPaolo Bonzini         }
214951b19ebeSPaolo Bonzini 
215051b19ebeSPaolo Bonzini         out_num = elem->out_num;
215151b19ebeSPaolo Bonzini         out_sg = elem->out_sg;
21526e790746SPaolo Bonzini         if (out_num < 1) {
2153fa5e56c2SGreg Kurz             virtio_error(vdev, "virtio-net header not in first element");
2154fa5e56c2SGreg Kurz             virtqueue_detach_element(q->tx_vq, elem, 0);
2155fa5e56c2SGreg Kurz             g_free(elem);
2156fa5e56c2SGreg Kurz             return -EINVAL;
21576e790746SPaolo Bonzini         }
21586e790746SPaolo Bonzini 
2159032a74a1SCédric Le Goater         if (n->has_vnet_hdr) {
2160feb93f36SJason Wang             if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
2161feb93f36SJason Wang                 n->guest_hdr_len) {
2162fa5e56c2SGreg Kurz                 virtio_error(vdev, "virtio-net header incorrect");
2163fa5e56c2SGreg Kurz                 virtqueue_detach_element(q->tx_vq, elem, 0);
2164fa5e56c2SGreg Kurz                 g_free(elem);
2165fa5e56c2SGreg Kurz                 return -EINVAL;
2166032a74a1SCédric Le Goater             }
21671bfa316cSGreg Kurz             if (n->needs_vnet_hdr_swap) {
2168feb93f36SJason Wang                 virtio_net_hdr_swap(vdev, (void *) &mhdr);
2169feb93f36SJason Wang                 sg2[0].iov_base = &mhdr;
2170feb93f36SJason Wang                 sg2[0].iov_len = n->guest_hdr_len;
2171feb93f36SJason Wang                 out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
2172feb93f36SJason Wang                                    out_sg, out_num,
2173feb93f36SJason Wang                                    n->guest_hdr_len, -1);
2174feb93f36SJason Wang                 if (out_num == VIRTQUEUE_MAX_SIZE) {
2175feb93f36SJason Wang                     goto drop;
2176032a74a1SCédric Le Goater                 }
2177feb93f36SJason Wang                 out_num += 1;
2178feb93f36SJason Wang                 out_sg = sg2;
2179feb93f36SJason Wang             }
2180feb93f36SJason Wang         }
21816e790746SPaolo Bonzini         /*
21826e790746SPaolo Bonzini          * If host wants to see the guest header as is, we can
21836e790746SPaolo Bonzini          * pass it on unchanged. Otherwise, copy just the parts
21846e790746SPaolo Bonzini          * that host is interested in.
21856e790746SPaolo Bonzini          */
21866e790746SPaolo Bonzini         assert(n->host_hdr_len <= n->guest_hdr_len);
21876e790746SPaolo Bonzini         if (n->host_hdr_len != n->guest_hdr_len) {
21886e790746SPaolo Bonzini             unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg),
21896e790746SPaolo Bonzini                                        out_sg, out_num,
21906e790746SPaolo Bonzini                                        0, n->host_hdr_len);
21916e790746SPaolo Bonzini             sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num,
21926e790746SPaolo Bonzini                              out_sg, out_num,
21936e790746SPaolo Bonzini                              n->guest_hdr_len, -1);
21946e790746SPaolo Bonzini             out_num = sg_num;
21956e790746SPaolo Bonzini             out_sg = sg;
21966e790746SPaolo Bonzini         }
21976e790746SPaolo Bonzini 
21986e790746SPaolo Bonzini         ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
21996e790746SPaolo Bonzini                                       out_sg, out_num, virtio_net_tx_complete);
22006e790746SPaolo Bonzini         if (ret == 0) {
22016e790746SPaolo Bonzini             virtio_queue_set_notification(q->tx_vq, 0);
22026e790746SPaolo Bonzini             q->async_tx.elem = elem;
22036e790746SPaolo Bonzini             return -EBUSY;
22046e790746SPaolo Bonzini         }
22056e790746SPaolo Bonzini 
2206feb93f36SJason Wang drop:
220751b19ebeSPaolo Bonzini         virtqueue_push(q->tx_vq, elem, 0);
220817a0ca55SKONRAD Frederic         virtio_notify(vdev, q->tx_vq);
220951b19ebeSPaolo Bonzini         g_free(elem);
22106e790746SPaolo Bonzini 
22116e790746SPaolo Bonzini         if (++num_packets >= n->tx_burst) {
22126e790746SPaolo Bonzini             break;
22136e790746SPaolo Bonzini         }
22146e790746SPaolo Bonzini     }
22156e790746SPaolo Bonzini     return num_packets;
22166e790746SPaolo Bonzini }
22176e790746SPaolo Bonzini 
22186e790746SPaolo Bonzini static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
22196e790746SPaolo Bonzini {
222017a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
22216e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
22226e790746SPaolo Bonzini 
2223283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2224283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2225283e2c2aSYuri Benditovich         return;
2226283e2c2aSYuri Benditovich     }
2227283e2c2aSYuri Benditovich 
22286e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
222917a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
22306e790746SPaolo Bonzini         q->tx_waiting = 1;
22316e790746SPaolo Bonzini         return;
22326e790746SPaolo Bonzini     }
22336e790746SPaolo Bonzini 
22346e790746SPaolo Bonzini     if (q->tx_waiting) {
22356e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 1);
2236bc72ad67SAlex Bligh         timer_del(q->tx_timer);
22376e790746SPaolo Bonzini         q->tx_waiting = 0;
2238fa5e56c2SGreg Kurz         if (virtio_net_flush_tx(q) == -EINVAL) {
2239fa5e56c2SGreg Kurz             return;
2240fa5e56c2SGreg Kurz         }
22416e790746SPaolo Bonzini     } else {
2242bc72ad67SAlex Bligh         timer_mod(q->tx_timer,
2243bc72ad67SAlex Bligh                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
22446e790746SPaolo Bonzini         q->tx_waiting = 1;
22456e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 0);
22466e790746SPaolo Bonzini     }
22476e790746SPaolo Bonzini }
22486e790746SPaolo Bonzini 
22496e790746SPaolo Bonzini static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
22506e790746SPaolo Bonzini {
225117a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
22526e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
22536e790746SPaolo Bonzini 
2254283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2255283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2256283e2c2aSYuri Benditovich         return;
2257283e2c2aSYuri Benditovich     }
2258283e2c2aSYuri Benditovich 
22596e790746SPaolo Bonzini     if (unlikely(q->tx_waiting)) {
22606e790746SPaolo Bonzini         return;
22616e790746SPaolo Bonzini     }
22626e790746SPaolo Bonzini     q->tx_waiting = 1;
22636e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
226417a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
22656e790746SPaolo Bonzini         return;
22666e790746SPaolo Bonzini     }
22676e790746SPaolo Bonzini     virtio_queue_set_notification(vq, 0);
22686e790746SPaolo Bonzini     qemu_bh_schedule(q->tx_bh);
22696e790746SPaolo Bonzini }
22706e790746SPaolo Bonzini 
22716e790746SPaolo Bonzini static void virtio_net_tx_timer(void *opaque)
22726e790746SPaolo Bonzini {
22736e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
22746e790746SPaolo Bonzini     VirtIONet *n = q->n;
227517a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2276e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2277e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2278e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2279e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2280e8bcf842SMichael S. Tsirkin         return;
2281e8bcf842SMichael S. Tsirkin     }
22826e790746SPaolo Bonzini 
22836e790746SPaolo Bonzini     q->tx_waiting = 0;
22846e790746SPaolo Bonzini 
22856e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
228617a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
22876e790746SPaolo Bonzini         return;
228817a0ca55SKONRAD Frederic     }
22896e790746SPaolo Bonzini 
22906e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
22916e790746SPaolo Bonzini     virtio_net_flush_tx(q);
22926e790746SPaolo Bonzini }
22936e790746SPaolo Bonzini 
22946e790746SPaolo Bonzini static void virtio_net_tx_bh(void *opaque)
22956e790746SPaolo Bonzini {
22966e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
22976e790746SPaolo Bonzini     VirtIONet *n = q->n;
229817a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
22996e790746SPaolo Bonzini     int32_t ret;
23006e790746SPaolo Bonzini 
2301e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2302e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2303e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2304e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2305e8bcf842SMichael S. Tsirkin         return;
2306e8bcf842SMichael S. Tsirkin     }
23076e790746SPaolo Bonzini 
23086e790746SPaolo Bonzini     q->tx_waiting = 0;
23096e790746SPaolo Bonzini 
23106e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
231117a0ca55SKONRAD Frederic     if (unlikely(!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) {
23126e790746SPaolo Bonzini         return;
231317a0ca55SKONRAD Frederic     }
23146e790746SPaolo Bonzini 
23156e790746SPaolo Bonzini     ret = virtio_net_flush_tx(q);
2316fa5e56c2SGreg Kurz     if (ret == -EBUSY || ret == -EINVAL) {
2317fa5e56c2SGreg Kurz         return; /* Notification re-enable handled by tx_complete or device
2318fa5e56c2SGreg Kurz                  * broken */
23196e790746SPaolo Bonzini     }
23206e790746SPaolo Bonzini 
23216e790746SPaolo Bonzini     /* If we flush a full burst of packets, assume there are
23226e790746SPaolo Bonzini      * more coming and immediately reschedule */
23236e790746SPaolo Bonzini     if (ret >= n->tx_burst) {
23246e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
23256e790746SPaolo Bonzini         q->tx_waiting = 1;
23266e790746SPaolo Bonzini         return;
23276e790746SPaolo Bonzini     }
23286e790746SPaolo Bonzini 
23296e790746SPaolo Bonzini     /* If less than a full burst, re-enable notification and flush
23306e790746SPaolo Bonzini      * anything that may have come in while we weren't looking.  If
23316e790746SPaolo Bonzini      * we find something, assume the guest is still active and reschedule */
23326e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
2333fa5e56c2SGreg Kurz     ret = virtio_net_flush_tx(q);
2334fa5e56c2SGreg Kurz     if (ret == -EINVAL) {
2335fa5e56c2SGreg Kurz         return;
2336fa5e56c2SGreg Kurz     } else if (ret > 0) {
23376e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
23386e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
23396e790746SPaolo Bonzini         q->tx_waiting = 1;
23406e790746SPaolo Bonzini     }
23416e790746SPaolo Bonzini }
23426e790746SPaolo Bonzini 
2343f9d6dbf0SWen Congyang static void virtio_net_add_queue(VirtIONet *n, int index)
2344f9d6dbf0SWen Congyang {
2345f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2346f9d6dbf0SWen Congyang 
23471c0fbfa3SMichael S. Tsirkin     n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size,
23481c0fbfa3SMichael S. Tsirkin                                            virtio_net_handle_rx);
23499b02e161SWei Wang 
2350f9d6dbf0SWen Congyang     if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) {
2351f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
23529b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
23539b02e161SWei Wang                              virtio_net_handle_tx_timer);
2354f9d6dbf0SWen Congyang         n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
2355f9d6dbf0SWen Congyang                                               virtio_net_tx_timer,
2356f9d6dbf0SWen Congyang                                               &n->vqs[index]);
2357f9d6dbf0SWen Congyang     } else {
2358f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
23599b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
23609b02e161SWei Wang                              virtio_net_handle_tx_bh);
2361f9d6dbf0SWen Congyang         n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]);
2362f9d6dbf0SWen Congyang     }
2363f9d6dbf0SWen Congyang 
2364f9d6dbf0SWen Congyang     n->vqs[index].tx_waiting = 0;
2365f9d6dbf0SWen Congyang     n->vqs[index].n = n;
2366f9d6dbf0SWen Congyang }
2367f9d6dbf0SWen Congyang 
2368f9d6dbf0SWen Congyang static void virtio_net_del_queue(VirtIONet *n, int index)
2369f9d6dbf0SWen Congyang {
2370f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2371f9d6dbf0SWen Congyang     VirtIONetQueue *q = &n->vqs[index];
2372f9d6dbf0SWen Congyang     NetClientState *nc = qemu_get_subqueue(n->nic, index);
2373f9d6dbf0SWen Congyang 
2374f9d6dbf0SWen Congyang     qemu_purge_queued_packets(nc);
2375f9d6dbf0SWen Congyang 
2376f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2);
2377f9d6dbf0SWen Congyang     if (q->tx_timer) {
2378f9d6dbf0SWen Congyang         timer_del(q->tx_timer);
2379f9d6dbf0SWen Congyang         timer_free(q->tx_timer);
2380f989c30cSYunjian Wang         q->tx_timer = NULL;
2381f9d6dbf0SWen Congyang     } else {
2382f9d6dbf0SWen Congyang         qemu_bh_delete(q->tx_bh);
2383f989c30cSYunjian Wang         q->tx_bh = NULL;
2384f9d6dbf0SWen Congyang     }
2385f989c30cSYunjian Wang     q->tx_waiting = 0;
2386f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2 + 1);
2387f9d6dbf0SWen Congyang }
2388f9d6dbf0SWen Congyang 
2389f9d6dbf0SWen Congyang static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues)
2390f9d6dbf0SWen Congyang {
2391f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2392f9d6dbf0SWen Congyang     int old_num_queues = virtio_get_num_queues(vdev);
2393f9d6dbf0SWen Congyang     int new_num_queues = new_max_queues * 2 + 1;
2394f9d6dbf0SWen Congyang     int i;
2395f9d6dbf0SWen Congyang 
2396f9d6dbf0SWen Congyang     assert(old_num_queues >= 3);
2397f9d6dbf0SWen Congyang     assert(old_num_queues % 2 == 1);
2398f9d6dbf0SWen Congyang 
2399f9d6dbf0SWen Congyang     if (old_num_queues == new_num_queues) {
2400f9d6dbf0SWen Congyang         return;
2401f9d6dbf0SWen Congyang     }
2402f9d6dbf0SWen Congyang 
2403f9d6dbf0SWen Congyang     /*
2404f9d6dbf0SWen Congyang      * We always need to remove and add ctrl vq if
2405f9d6dbf0SWen Congyang      * old_num_queues != new_num_queues. Remove ctrl_vq first,
240620f86a75SYuval Shaia      * and then we only enter one of the following two loops.
2407f9d6dbf0SWen Congyang      */
2408f9d6dbf0SWen Congyang     virtio_del_queue(vdev, old_num_queues - 1);
2409f9d6dbf0SWen Congyang 
2410f9d6dbf0SWen Congyang     for (i = new_num_queues - 1; i < old_num_queues - 1; i += 2) {
2411f9d6dbf0SWen Congyang         /* new_num_queues < old_num_queues */
2412f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i / 2);
2413f9d6dbf0SWen Congyang     }
2414f9d6dbf0SWen Congyang 
2415f9d6dbf0SWen Congyang     for (i = old_num_queues - 1; i < new_num_queues - 1; i += 2) {
2416f9d6dbf0SWen Congyang         /* new_num_queues > old_num_queues */
2417f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i / 2);
2418f9d6dbf0SWen Congyang     }
2419f9d6dbf0SWen Congyang 
2420f9d6dbf0SWen Congyang     /* add ctrl_vq last */
2421f9d6dbf0SWen Congyang     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
2422f9d6dbf0SWen Congyang }
2423f9d6dbf0SWen Congyang 
2424ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
24256e790746SPaolo Bonzini {
2426f9d6dbf0SWen Congyang     int max = multiqueue ? n->max_queues : 1;
2427f9d6dbf0SWen Congyang 
24286e790746SPaolo Bonzini     n->multiqueue = multiqueue;
2429f9d6dbf0SWen Congyang     virtio_net_change_num_queues(n, max);
24306e790746SPaolo Bonzini 
24316e790746SPaolo Bonzini     virtio_net_set_queues(n);
24326e790746SPaolo Bonzini }
24336e790746SPaolo Bonzini 
2434982b78c5SDr. David Alan Gilbert static int virtio_net_post_load_device(void *opaque, int version_id)
2435037dab2fSGreg Kurz {
2436982b78c5SDr. David Alan Gilbert     VirtIONet *n = opaque;
2437982b78c5SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2438037dab2fSGreg Kurz     int i, link_down;
2439037dab2fSGreg Kurz 
24409d8c6a25SDr. David Alan Gilbert     trace_virtio_net_post_load_device();
2441982b78c5SDr. David Alan Gilbert     virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
244295129d6fSCornelia Huck                                virtio_vdev_has_feature(vdev,
244395129d6fSCornelia Huck                                                        VIRTIO_F_VERSION_1));
24446e790746SPaolo Bonzini 
24456e790746SPaolo Bonzini     /* MAC_TABLE_ENTRIES may be different from the saved image */
2446982b78c5SDr. David Alan Gilbert     if (n->mac_table.in_use > MAC_TABLE_ENTRIES) {
24476e790746SPaolo Bonzini         n->mac_table.in_use = 0;
24486e790746SPaolo Bonzini     }
24496e790746SPaolo Bonzini 
2450982b78c5SDr. David Alan Gilbert     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
24516c666823SMichael S. Tsirkin         n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
24526c666823SMichael S. Tsirkin     }
24536c666823SMichael S. Tsirkin 
24547788c3f2SMikhail Sennikovsky     /*
24557788c3f2SMikhail Sennikovsky      * curr_guest_offloads will be later overwritten by the
24567788c3f2SMikhail Sennikovsky      * virtio_set_features_nocheck call done from the virtio_load.
24577788c3f2SMikhail Sennikovsky      * Here we make sure it is preserved and restored accordingly
24587788c3f2SMikhail Sennikovsky      * in the virtio_net_post_load_virtio callback.
24597788c3f2SMikhail Sennikovsky      */
24607788c3f2SMikhail Sennikovsky     n->saved_guest_offloads = n->curr_guest_offloads;
24616c666823SMichael S. Tsirkin 
24626e790746SPaolo Bonzini     virtio_net_set_queues(n);
24636e790746SPaolo Bonzini 
24646e790746SPaolo Bonzini     /* Find the first multicast entry in the saved MAC filter */
24656e790746SPaolo Bonzini     for (i = 0; i < n->mac_table.in_use; i++) {
24666e790746SPaolo Bonzini         if (n->mac_table.macs[i * ETH_ALEN] & 1) {
24676e790746SPaolo Bonzini             break;
24686e790746SPaolo Bonzini         }
24696e790746SPaolo Bonzini     }
24706e790746SPaolo Bonzini     n->mac_table.first_multi = i;
24716e790746SPaolo Bonzini 
24726e790746SPaolo Bonzini     /* nc.link_down can't be migrated, so infer link_down according
24736e790746SPaolo Bonzini      * to link status bit in n->status */
24746e790746SPaolo Bonzini     link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0;
24756e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
24766e790746SPaolo Bonzini         qemu_get_subqueue(n->nic, i)->link_down = link_down;
24776e790746SPaolo Bonzini     }
24786e790746SPaolo Bonzini 
24796c666823SMichael S. Tsirkin     if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
24806c666823SMichael S. Tsirkin         virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
24819d8c6a25SDr. David Alan Gilbert         qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(),
24829d8c6a25SDr. David Alan Gilbert                                   QEMU_CLOCK_VIRTUAL,
24839d8c6a25SDr. David Alan Gilbert                                   virtio_net_announce_timer, n);
24849d8c6a25SDr. David Alan Gilbert         if (n->announce_timer.round) {
24859d8c6a25SDr. David Alan Gilbert             timer_mod(n->announce_timer.tm,
24869d8c6a25SDr. David Alan Gilbert                       qemu_clock_get_ms(n->announce_timer.type));
24879d8c6a25SDr. David Alan Gilbert         } else {
2488944458b6SDr. David Alan Gilbert             qemu_announce_timer_del(&n->announce_timer, false);
24899d8c6a25SDr. David Alan Gilbert         }
24906c666823SMichael S. Tsirkin     }
24916c666823SMichael S. Tsirkin 
24926e790746SPaolo Bonzini     return 0;
24936e790746SPaolo Bonzini }
24946e790746SPaolo Bonzini 
24957788c3f2SMikhail Sennikovsky static int virtio_net_post_load_virtio(VirtIODevice *vdev)
24967788c3f2SMikhail Sennikovsky {
24977788c3f2SMikhail Sennikovsky     VirtIONet *n = VIRTIO_NET(vdev);
24987788c3f2SMikhail Sennikovsky     /*
24997788c3f2SMikhail Sennikovsky      * The actual needed state is now in saved_guest_offloads,
25007788c3f2SMikhail Sennikovsky      * see virtio_net_post_load_device for detail.
25017788c3f2SMikhail Sennikovsky      * Restore it back and apply the desired offloads.
25027788c3f2SMikhail Sennikovsky      */
25037788c3f2SMikhail Sennikovsky     n->curr_guest_offloads = n->saved_guest_offloads;
25047788c3f2SMikhail Sennikovsky     if (peer_has_vnet_hdr(n)) {
25057788c3f2SMikhail Sennikovsky         virtio_net_apply_guest_offloads(n);
25067788c3f2SMikhail Sennikovsky     }
25077788c3f2SMikhail Sennikovsky 
25087788c3f2SMikhail Sennikovsky     return 0;
25097788c3f2SMikhail Sennikovsky }
25107788c3f2SMikhail Sennikovsky 
2511982b78c5SDr. David Alan Gilbert /* tx_waiting field of a VirtIONetQueue */
2512982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = {
2513982b78c5SDr. David Alan Gilbert     .name = "virtio-net-queue-tx_waiting",
2514982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2515982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(tx_waiting, VirtIONetQueue),
2516982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2517982b78c5SDr. David Alan Gilbert    },
2518982b78c5SDr. David Alan Gilbert };
2519982b78c5SDr. David Alan Gilbert 
2520982b78c5SDr. David Alan Gilbert static bool max_queues_gt_1(void *opaque, int version_id)
2521982b78c5SDr. David Alan Gilbert {
2522982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->max_queues > 1;
2523982b78c5SDr. David Alan Gilbert }
2524982b78c5SDr. David Alan Gilbert 
2525982b78c5SDr. David Alan Gilbert static bool has_ctrl_guest_offloads(void *opaque, int version_id)
2526982b78c5SDr. David Alan Gilbert {
2527982b78c5SDr. David Alan Gilbert     return virtio_vdev_has_feature(VIRTIO_DEVICE(opaque),
2528982b78c5SDr. David Alan Gilbert                                    VIRTIO_NET_F_CTRL_GUEST_OFFLOADS);
2529982b78c5SDr. David Alan Gilbert }
2530982b78c5SDr. David Alan Gilbert 
2531982b78c5SDr. David Alan Gilbert static bool mac_table_fits(void *opaque, int version_id)
2532982b78c5SDr. David Alan Gilbert {
2533982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->mac_table.in_use <= MAC_TABLE_ENTRIES;
2534982b78c5SDr. David Alan Gilbert }
2535982b78c5SDr. David Alan Gilbert 
2536982b78c5SDr. David Alan Gilbert static bool mac_table_doesnt_fit(void *opaque, int version_id)
2537982b78c5SDr. David Alan Gilbert {
2538982b78c5SDr. David Alan Gilbert     return !mac_table_fits(opaque, version_id);
2539982b78c5SDr. David Alan Gilbert }
2540982b78c5SDr. David Alan Gilbert 
2541982b78c5SDr. David Alan Gilbert /* This temporary type is shared by all the WITH_TMP methods
2542982b78c5SDr. David Alan Gilbert  * although only some fields are used by each.
2543982b78c5SDr. David Alan Gilbert  */
2544982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp {
2545982b78c5SDr. David Alan Gilbert     VirtIONet      *parent;
2546982b78c5SDr. David Alan Gilbert     VirtIONetQueue *vqs_1;
2547982b78c5SDr. David Alan Gilbert     uint16_t        curr_queues_1;
2548982b78c5SDr. David Alan Gilbert     uint8_t         has_ufo;
2549982b78c5SDr. David Alan Gilbert     uint32_t        has_vnet_hdr;
2550982b78c5SDr. David Alan Gilbert };
2551982b78c5SDr. David Alan Gilbert 
2552982b78c5SDr. David Alan Gilbert /* The 2nd and subsequent tx_waiting flags are loaded later than
2553982b78c5SDr. David Alan Gilbert  * the 1st entry in the queues and only if there's more than one
2554982b78c5SDr. David Alan Gilbert  * entry.  We use the tmp mechanism to calculate a temporary
2555982b78c5SDr. David Alan Gilbert  * pointer and count and also validate the count.
2556982b78c5SDr. David Alan Gilbert  */
2557982b78c5SDr. David Alan Gilbert 
255844b1ff31SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_save(void *opaque)
2559982b78c5SDr. David Alan Gilbert {
2560982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2561982b78c5SDr. David Alan Gilbert 
2562982b78c5SDr. David Alan Gilbert     tmp->vqs_1 = tmp->parent->vqs + 1;
2563982b78c5SDr. David Alan Gilbert     tmp->curr_queues_1 = tmp->parent->curr_queues - 1;
2564982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues == 0) {
2565982b78c5SDr. David Alan Gilbert         tmp->curr_queues_1 = 0;
2566982b78c5SDr. David Alan Gilbert     }
256744b1ff31SDr. David Alan Gilbert 
256844b1ff31SDr. David Alan Gilbert     return 0;
2569982b78c5SDr. David Alan Gilbert }
2570982b78c5SDr. David Alan Gilbert 
2571982b78c5SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_load(void *opaque)
2572982b78c5SDr. David Alan Gilbert {
2573982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2574982b78c5SDr. David Alan Gilbert 
2575982b78c5SDr. David Alan Gilbert     /* Reuse the pointer setup from save */
2576982b78c5SDr. David Alan Gilbert     virtio_net_tx_waiting_pre_save(opaque);
2577982b78c5SDr. David Alan Gilbert 
2578982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues > tmp->parent->max_queues) {
2579982b78c5SDr. David Alan Gilbert         error_report("virtio-net: curr_queues %x > max_queues %x",
2580982b78c5SDr. David Alan Gilbert             tmp->parent->curr_queues, tmp->parent->max_queues);
2581982b78c5SDr. David Alan Gilbert 
2582982b78c5SDr. David Alan Gilbert         return -EINVAL;
2583982b78c5SDr. David Alan Gilbert     }
2584982b78c5SDr. David Alan Gilbert 
2585982b78c5SDr. David Alan Gilbert     return 0; /* all good */
2586982b78c5SDr. David Alan Gilbert }
2587982b78c5SDr. David Alan Gilbert 
2588982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_tx_waiting = {
2589982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-tx_waiting",
2590982b78c5SDr. David Alan Gilbert     .pre_load  = virtio_net_tx_waiting_pre_load,
2591982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_tx_waiting_pre_save,
2592982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2593982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp,
2594982b78c5SDr. David Alan Gilbert                                      curr_queues_1,
2595982b78c5SDr. David Alan Gilbert                                      vmstate_virtio_net_queue_tx_waiting,
2596982b78c5SDr. David Alan Gilbert                                      struct VirtIONetQueue),
2597982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2598982b78c5SDr. David Alan Gilbert     },
2599982b78c5SDr. David Alan Gilbert };
2600982b78c5SDr. David Alan Gilbert 
2601982b78c5SDr. David Alan Gilbert /* the 'has_ufo' flag is just tested; if the incoming stream has the
2602982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2603982b78c5SDr. David Alan Gilbert  */
2604982b78c5SDr. David Alan Gilbert static int virtio_net_ufo_post_load(void *opaque, int version_id)
2605982b78c5SDr. David Alan Gilbert {
2606982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2607982b78c5SDr. David Alan Gilbert 
2608982b78c5SDr. David Alan Gilbert     if (tmp->has_ufo && !peer_has_ufo(tmp->parent)) {
2609982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires TUN_F_UFO support");
2610982b78c5SDr. David Alan Gilbert         return -EINVAL;
2611982b78c5SDr. David Alan Gilbert     }
2612982b78c5SDr. David Alan Gilbert 
2613982b78c5SDr. David Alan Gilbert     return 0;
2614982b78c5SDr. David Alan Gilbert }
2615982b78c5SDr. David Alan Gilbert 
261644b1ff31SDr. David Alan Gilbert static int virtio_net_ufo_pre_save(void *opaque)
2617982b78c5SDr. David Alan Gilbert {
2618982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2619982b78c5SDr. David Alan Gilbert 
2620982b78c5SDr. David Alan Gilbert     tmp->has_ufo = tmp->parent->has_ufo;
262144b1ff31SDr. David Alan Gilbert 
262244b1ff31SDr. David Alan Gilbert     return 0;
2623982b78c5SDr. David Alan Gilbert }
2624982b78c5SDr. David Alan Gilbert 
2625982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_ufo = {
2626982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-ufo",
2627982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_ufo_post_load,
2628982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_ufo_pre_save,
2629982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2630982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(has_ufo, struct VirtIONetMigTmp),
2631982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2632982b78c5SDr. David Alan Gilbert     },
2633982b78c5SDr. David Alan Gilbert };
2634982b78c5SDr. David Alan Gilbert 
2635982b78c5SDr. David Alan Gilbert /* the 'has_vnet_hdr' flag is just tested; if the incoming stream has the
2636982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2637982b78c5SDr. David Alan Gilbert  */
2638982b78c5SDr. David Alan Gilbert static int virtio_net_vnet_post_load(void *opaque, int version_id)
2639982b78c5SDr. David Alan Gilbert {
2640982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2641982b78c5SDr. David Alan Gilbert 
2642982b78c5SDr. David Alan Gilbert     if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) {
2643982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires vnet_hdr=on");
2644982b78c5SDr. David Alan Gilbert         return -EINVAL;
2645982b78c5SDr. David Alan Gilbert     }
2646982b78c5SDr. David Alan Gilbert 
2647982b78c5SDr. David Alan Gilbert     return 0;
2648982b78c5SDr. David Alan Gilbert }
2649982b78c5SDr. David Alan Gilbert 
265044b1ff31SDr. David Alan Gilbert static int virtio_net_vnet_pre_save(void *opaque)
2651982b78c5SDr. David Alan Gilbert {
2652982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2653982b78c5SDr. David Alan Gilbert 
2654982b78c5SDr. David Alan Gilbert     tmp->has_vnet_hdr = tmp->parent->has_vnet_hdr;
265544b1ff31SDr. David Alan Gilbert 
265644b1ff31SDr. David Alan Gilbert     return 0;
2657982b78c5SDr. David Alan Gilbert }
2658982b78c5SDr. David Alan Gilbert 
2659982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_vnet = {
2660982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-vnet",
2661982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_vnet_post_load,
2662982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_vnet_pre_save,
2663982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2664982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(has_vnet_hdr, struct VirtIONetMigTmp),
2665982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2666982b78c5SDr. David Alan Gilbert     },
2667982b78c5SDr. David Alan Gilbert };
2668982b78c5SDr. David Alan Gilbert 
2669982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_device = {
2670982b78c5SDr. David Alan Gilbert     .name = "virtio-net-device",
2671982b78c5SDr. David Alan Gilbert     .version_id = VIRTIO_NET_VM_VERSION,
2672982b78c5SDr. David Alan Gilbert     .minimum_version_id = VIRTIO_NET_VM_VERSION,
2673982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_post_load_device,
2674982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2675982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN),
2676982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_POINTER(vqs, VirtIONet,
2677982b78c5SDr. David Alan Gilbert                                vmstate_virtio_net_queue_tx_waiting,
2678982b78c5SDr. David Alan Gilbert                                VirtIONetQueue),
2679982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mergeable_rx_bufs, VirtIONet),
2680982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16(status, VirtIONet),
2681982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(promisc, VirtIONet),
2682982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(allmulti, VirtIONet),
2683982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mac_table.in_use, VirtIONet),
2684982b78c5SDr. David Alan Gilbert 
2685982b78c5SDr. David Alan Gilbert         /* Guarded pair: If it fits we load it, else we throw it away
2686982b78c5SDr. David Alan Gilbert          * - can happen if source has a larger MAC table.; post-load
2687982b78c5SDr. David Alan Gilbert          *  sets flags in this case.
2688982b78c5SDr. David Alan Gilbert          */
2689982b78c5SDr. David Alan Gilbert         VMSTATE_VBUFFER_MULTIPLY(mac_table.macs, VirtIONet,
2690982b78c5SDr. David Alan Gilbert                                 0, mac_table_fits, mac_table.in_use,
2691982b78c5SDr. David Alan Gilbert                                  ETH_ALEN),
2692982b78c5SDr. David Alan Gilbert         VMSTATE_UNUSED_VARRAY_UINT32(VirtIONet, mac_table_doesnt_fit, 0,
2693982b78c5SDr. David Alan Gilbert                                      mac_table.in_use, ETH_ALEN),
2694982b78c5SDr. David Alan Gilbert 
2695982b78c5SDr. David Alan Gilbert         /* Note: This is an array of uint32's that's always been saved as a
2696982b78c5SDr. David Alan Gilbert          * buffer; hold onto your endiannesses; it's actually used as a bitmap
2697982b78c5SDr. David Alan Gilbert          * but based on the uint.
2698982b78c5SDr. David Alan Gilbert          */
2699982b78c5SDr. David Alan Gilbert         VMSTATE_BUFFER_POINTER_UNSAFE(vlans, VirtIONet, 0, MAX_VLAN >> 3),
2700982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2701982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_vnet),
2702982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.multi_overflow, VirtIONet),
2703982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.uni_overflow, VirtIONet),
2704982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(alluni, VirtIONet),
2705982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nomulti, VirtIONet),
2706982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nouni, VirtIONet),
2707982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nobcast, VirtIONet),
2708982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2709982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_ufo),
2710982b78c5SDr. David Alan Gilbert         VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0,
2711982b78c5SDr. David Alan Gilbert                             vmstate_info_uint16_equal, uint16_t),
2712982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1),
2713982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2714982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_tx_waiting),
2715982b78c5SDr. David Alan Gilbert         VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet,
2716982b78c5SDr. David Alan Gilbert                             has_ctrl_guest_offloads),
2717982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2718982b78c5SDr. David Alan Gilbert    },
2719982b78c5SDr. David Alan Gilbert };
2720982b78c5SDr. David Alan Gilbert 
27216e790746SPaolo Bonzini static NetClientInfo net_virtio_info = {
2722f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
27236e790746SPaolo Bonzini     .size = sizeof(NICState),
27246e790746SPaolo Bonzini     .can_receive = virtio_net_can_receive,
27256e790746SPaolo Bonzini     .receive = virtio_net_receive,
27266e790746SPaolo Bonzini     .link_status_changed = virtio_net_set_link_status,
2727b1be4280SAmos Kong     .query_rx_filter = virtio_net_query_rxfilter,
2728b2c929f0SDr. David Alan Gilbert     .announce = virtio_net_announce,
27296e790746SPaolo Bonzini };
27306e790746SPaolo Bonzini 
27316e790746SPaolo Bonzini static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
27326e790746SPaolo Bonzini {
273317a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
27346e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
27356e790746SPaolo Bonzini     assert(n->vhost_started);
2736ed8b4afeSNikolay Nikolaev     return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx);
27376e790746SPaolo Bonzini }
27386e790746SPaolo Bonzini 
27396e790746SPaolo Bonzini static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
27406e790746SPaolo Bonzini                                            bool mask)
27416e790746SPaolo Bonzini {
274217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
27436e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
27446e790746SPaolo Bonzini     assert(n->vhost_started);
2745ed8b4afeSNikolay Nikolaev     vhost_net_virtqueue_mask(get_vhost_net(nc->peer),
27466e790746SPaolo Bonzini                              vdev, idx, mask);
27476e790746SPaolo Bonzini }
27486e790746SPaolo Bonzini 
2749019a3edbSGerd Hoffmann static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features)
27506e790746SPaolo Bonzini {
27510cd09c3aSCornelia Huck     virtio_add_feature(&host_features, VIRTIO_NET_F_MAC);
2752a93e599dSMaxime Coquelin 
2753ba550851SStefano Garzarella     n->config_size = virtio_feature_get_config_size(feature_sizes,
2754ba550851SStefano Garzarella                                                     host_features);
275517ec5a86SKONRAD Frederic }
27566e790746SPaolo Bonzini 
27578a253ec2SKONRAD Frederic void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
27588a253ec2SKONRAD Frederic                                    const char *type)
27598a253ec2SKONRAD Frederic {
27608a253ec2SKONRAD Frederic     /*
27618a253ec2SKONRAD Frederic      * The name can be NULL, the netclient name will be type.x.
27628a253ec2SKONRAD Frederic      */
27638a253ec2SKONRAD Frederic     assert(type != NULL);
27648a253ec2SKONRAD Frederic 
27658a253ec2SKONRAD Frederic     g_free(n->netclient_name);
27668a253ec2SKONRAD Frederic     g_free(n->netclient_type);
27678a253ec2SKONRAD Frederic     n->netclient_name = g_strdup(name);
27688a253ec2SKONRAD Frederic     n->netclient_type = g_strdup(type);
27698a253ec2SKONRAD Frederic }
27708a253ec2SKONRAD Frederic 
27719711cd0dSJens Freimann static bool failover_unplug_primary(VirtIONet *n)
27729711cd0dSJens Freimann {
27739711cd0dSJens Freimann     HotplugHandler *hotplug_ctrl;
27749711cd0dSJens Freimann     PCIDevice *pci_dev;
27759711cd0dSJens Freimann     Error *err = NULL;
27769711cd0dSJens Freimann 
27779711cd0dSJens Freimann     hotplug_ctrl = qdev_get_hotplug_handler(n->primary_dev);
27789711cd0dSJens Freimann     if (hotplug_ctrl) {
27799711cd0dSJens Freimann         pci_dev = PCI_DEVICE(n->primary_dev);
27809711cd0dSJens Freimann         pci_dev->partially_hotplugged = true;
27819711cd0dSJens Freimann         hotplug_handler_unplug_request(hotplug_ctrl, n->primary_dev, &err);
27829711cd0dSJens Freimann         if (err) {
27839711cd0dSJens Freimann             error_report_err(err);
27849711cd0dSJens Freimann             return false;
27859711cd0dSJens Freimann         }
27869711cd0dSJens Freimann     } else {
27879711cd0dSJens Freimann         return false;
27889711cd0dSJens Freimann     }
27899711cd0dSJens Freimann     return true;
27909711cd0dSJens Freimann }
27919711cd0dSJens Freimann 
27929711cd0dSJens Freimann static bool failover_replug_primary(VirtIONet *n, Error **errp)
27939711cd0dSJens Freimann {
27949711cd0dSJens Freimann     HotplugHandler *hotplug_ctrl;
27959711cd0dSJens Freimann     PCIDevice *pdev = PCI_DEVICE(n->primary_dev);
27969711cd0dSJens Freimann 
27979711cd0dSJens Freimann     if (!pdev->partially_hotplugged) {
27989711cd0dSJens Freimann         return true;
27999711cd0dSJens Freimann     }
28009711cd0dSJens Freimann     if (!n->primary_device_opts) {
28019711cd0dSJens Freimann         n->primary_device_opts = qemu_opts_from_qdict(
28029711cd0dSJens Freimann                 qemu_find_opts("device"),
28039711cd0dSJens Freimann                 n->primary_device_dict, errp);
28049711cd0dSJens Freimann     }
28059711cd0dSJens Freimann     if (n->primary_device_opts) {
28069711cd0dSJens Freimann         if (n->primary_dev) {
28079711cd0dSJens Freimann             n->primary_bus = n->primary_dev->parent_bus;
28089711cd0dSJens Freimann         }
28099711cd0dSJens Freimann         qdev_set_parent_bus(n->primary_dev, n->primary_bus);
28109711cd0dSJens Freimann         n->primary_should_be_hidden = false;
28119711cd0dSJens Freimann         qemu_opt_set_bool(n->primary_device_opts,
28129711cd0dSJens Freimann                 "partially_hotplugged", true, errp);
28139711cd0dSJens Freimann         hotplug_ctrl = qdev_get_hotplug_handler(n->primary_dev);
28149711cd0dSJens Freimann         if (hotplug_ctrl) {
28159711cd0dSJens Freimann             hotplug_handler_pre_plug(hotplug_ctrl, n->primary_dev, errp);
28169711cd0dSJens Freimann             hotplug_handler_plug(hotplug_ctrl, n->primary_dev, errp);
28179711cd0dSJens Freimann         }
28189711cd0dSJens Freimann         if (!n->primary_dev) {
28199711cd0dSJens Freimann             error_setg(errp, "virtio_net: couldn't find primary device");
28209711cd0dSJens Freimann         }
28219711cd0dSJens Freimann     }
28229711cd0dSJens Freimann     return *errp != NULL;
28239711cd0dSJens Freimann }
28249711cd0dSJens Freimann 
28259711cd0dSJens Freimann static void virtio_net_handle_migration_primary(VirtIONet *n,
28269711cd0dSJens Freimann                                                 MigrationState *s)
28279711cd0dSJens Freimann {
28289711cd0dSJens Freimann     bool should_be_hidden;
28299711cd0dSJens Freimann     Error *err = NULL;
28309711cd0dSJens Freimann 
28319711cd0dSJens Freimann     should_be_hidden = atomic_read(&n->primary_should_be_hidden);
28329711cd0dSJens Freimann 
28339711cd0dSJens Freimann     if (!n->primary_dev) {
28349711cd0dSJens Freimann         n->primary_dev = virtio_connect_failover_devices(n, n->qdev, &err);
28359711cd0dSJens Freimann         if (!n->primary_dev) {
28369711cd0dSJens Freimann             return;
28379711cd0dSJens Freimann         }
28389711cd0dSJens Freimann     }
28399711cd0dSJens Freimann 
28409711cd0dSJens Freimann     if (migration_in_setup(s) && !should_be_hidden &&
28419711cd0dSJens Freimann         n->primary_dev) {
28429711cd0dSJens Freimann         if (failover_unplug_primary(n)) {
28439711cd0dSJens Freimann             vmstate_unregister(n->primary_dev, qdev_get_vmsd(n->primary_dev),
28449711cd0dSJens Freimann                     n->primary_dev);
28459711cd0dSJens Freimann             qapi_event_send_unplug_primary(n->primary_device_id);
28469711cd0dSJens Freimann             atomic_set(&n->primary_should_be_hidden, true);
28479711cd0dSJens Freimann         } else {
28489711cd0dSJens Freimann             warn_report("couldn't unplug primary device");
28499711cd0dSJens Freimann         }
28509711cd0dSJens Freimann     } else if (migration_has_failed(s)) {
28519711cd0dSJens Freimann         /* We already unplugged the device let's plugged it back */
28529711cd0dSJens Freimann         if (!failover_replug_primary(n, &err)) {
28539711cd0dSJens Freimann             if (err) {
28549711cd0dSJens Freimann                 error_report_err(err);
28559711cd0dSJens Freimann             }
28569711cd0dSJens Freimann         }
28579711cd0dSJens Freimann     }
28589711cd0dSJens Freimann }
28599711cd0dSJens Freimann 
28609711cd0dSJens Freimann static void virtio_net_migration_state_notifier(Notifier *notifier, void *data)
28619711cd0dSJens Freimann {
28629711cd0dSJens Freimann     MigrationState *s = data;
28639711cd0dSJens Freimann     VirtIONet *n = container_of(notifier, VirtIONet, migration_state);
28649711cd0dSJens Freimann     virtio_net_handle_migration_primary(n, s);
28659711cd0dSJens Freimann }
28669711cd0dSJens Freimann 
28679711cd0dSJens Freimann static int virtio_net_primary_should_be_hidden(DeviceListener *listener,
28689711cd0dSJens Freimann             QemuOpts *device_opts)
28699711cd0dSJens Freimann {
28709711cd0dSJens Freimann     VirtIONet *n = container_of(listener, VirtIONet, primary_listener);
28719711cd0dSJens Freimann     bool match_found;
28729711cd0dSJens Freimann     bool hide;
28739711cd0dSJens Freimann 
28749711cd0dSJens Freimann     n->primary_device_dict = qemu_opts_to_qdict(device_opts,
28759711cd0dSJens Freimann             n->primary_device_dict);
28769711cd0dSJens Freimann     if (n->primary_device_dict) {
28779711cd0dSJens Freimann         g_free(n->standby_id);
28789711cd0dSJens Freimann         n->standby_id = g_strdup(qdict_get_try_str(n->primary_device_dict,
28799711cd0dSJens Freimann                     "failover_pair_id"));
28809711cd0dSJens Freimann     }
28819711cd0dSJens Freimann     if (device_opts && g_strcmp0(n->standby_id, n->netclient_name) == 0) {
28829711cd0dSJens Freimann         match_found = true;
28839711cd0dSJens Freimann     } else {
28849711cd0dSJens Freimann         match_found = false;
28859711cd0dSJens Freimann         hide = false;
28869711cd0dSJens Freimann         g_free(n->standby_id);
28879711cd0dSJens Freimann         n->primary_device_dict = NULL;
28889711cd0dSJens Freimann         goto out;
28899711cd0dSJens Freimann     }
28909711cd0dSJens Freimann 
28919711cd0dSJens Freimann     n->primary_device_opts = device_opts;
28929711cd0dSJens Freimann 
28939711cd0dSJens Freimann     /* primary_should_be_hidden is set during feature negotiation */
28949711cd0dSJens Freimann     hide = atomic_read(&n->primary_should_be_hidden);
28959711cd0dSJens Freimann 
28969711cd0dSJens Freimann     if (n->primary_device_dict) {
28979711cd0dSJens Freimann         g_free(n->primary_device_id);
28989711cd0dSJens Freimann         n->primary_device_id = g_strdup(qdict_get_try_str(
28999711cd0dSJens Freimann                     n->primary_device_dict, "id"));
29009711cd0dSJens Freimann         if (!n->primary_device_id) {
29019711cd0dSJens Freimann             warn_report("primary_device_id not set");
29029711cd0dSJens Freimann         }
29039711cd0dSJens Freimann     }
29049711cd0dSJens Freimann 
29059711cd0dSJens Freimann out:
29069711cd0dSJens Freimann     if (match_found && hide) {
29079711cd0dSJens Freimann         return 1;
29089711cd0dSJens Freimann     } else if (match_found && !hide) {
29099711cd0dSJens Freimann         return 0;
29109711cd0dSJens Freimann     } else {
29119711cd0dSJens Freimann         return -1;
29129711cd0dSJens Freimann     }
29139711cd0dSJens Freimann }
29149711cd0dSJens Freimann 
2915e6f746b3SAndreas Färber static void virtio_net_device_realize(DeviceState *dev, Error **errp)
291617ec5a86SKONRAD Frederic {
2917e6f746b3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
2918284a32f0SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
2919284a32f0SAndreas Färber     NetClientState *nc;
29201773d9eeSKONRAD Frederic     int i;
292117ec5a86SKONRAD Frederic 
2922a93e599dSMaxime Coquelin     if (n->net_conf.mtu) {
2923127833eeSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_MTU);
2924a93e599dSMaxime Coquelin     }
2925a93e599dSMaxime Coquelin 
29269473939eSJason Baron     if (n->net_conf.duplex_str) {
29279473939eSJason Baron         if (strncmp(n->net_conf.duplex_str, "half", 5) == 0) {
29289473939eSJason Baron             n->net_conf.duplex = DUPLEX_HALF;
29299473939eSJason Baron         } else if (strncmp(n->net_conf.duplex_str, "full", 5) == 0) {
29309473939eSJason Baron             n->net_conf.duplex = DUPLEX_FULL;
29319473939eSJason Baron         } else {
29329473939eSJason Baron             error_setg(errp, "'duplex' must be 'half' or 'full'");
29339473939eSJason Baron         }
29349473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
29359473939eSJason Baron     } else {
29369473939eSJason Baron         n->net_conf.duplex = DUPLEX_UNKNOWN;
29379473939eSJason Baron     }
29389473939eSJason Baron 
29399473939eSJason Baron     if (n->net_conf.speed < SPEED_UNKNOWN) {
29409473939eSJason Baron         error_setg(errp, "'speed' must be between 0 and INT_MAX");
29419473939eSJason Baron     } else if (n->net_conf.speed >= 0) {
29429473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
29439473939eSJason Baron     }
29449473939eSJason Baron 
29459711cd0dSJens Freimann     if (n->failover) {
29469711cd0dSJens Freimann         n->primary_listener.should_be_hidden =
29479711cd0dSJens Freimann             virtio_net_primary_should_be_hidden;
29489711cd0dSJens Freimann         atomic_set(&n->primary_should_be_hidden, true);
29499711cd0dSJens Freimann         device_listener_register(&n->primary_listener);
29509711cd0dSJens Freimann         n->migration_state.notify = virtio_net_migration_state_notifier;
29519711cd0dSJens Freimann         add_migration_state_change_notifier(&n->migration_state);
29529711cd0dSJens Freimann         n->host_features |= (1ULL << VIRTIO_NET_F_STANDBY);
29539711cd0dSJens Freimann     }
29549711cd0dSJens Freimann 
2955da3e8a23SShannon Zhao     virtio_net_set_config_size(n, n->host_features);
2956284a32f0SAndreas Färber     virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size);
295717ec5a86SKONRAD Frederic 
29581c0fbfa3SMichael S. Tsirkin     /*
29591c0fbfa3SMichael S. Tsirkin      * We set a lower limit on RX queue size to what it always was.
29601c0fbfa3SMichael S. Tsirkin      * Guests that want a smaller ring can always resize it without
29611c0fbfa3SMichael S. Tsirkin      * help from us (using virtio 1 and up).
29621c0fbfa3SMichael S. Tsirkin      */
29631c0fbfa3SMichael S. Tsirkin     if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE ||
29641c0fbfa3SMichael S. Tsirkin         n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE ||
29655f997fd1SMichal Privoznik         !is_power_of_2(n->net_conf.rx_queue_size)) {
29661c0fbfa3SMichael S. Tsirkin         error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), "
29671c0fbfa3SMichael S. Tsirkin                    "must be a power of 2 between %d and %d.",
29681c0fbfa3SMichael S. Tsirkin                    n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE,
29691c0fbfa3SMichael S. Tsirkin                    VIRTQUEUE_MAX_SIZE);
29701c0fbfa3SMichael S. Tsirkin         virtio_cleanup(vdev);
29711c0fbfa3SMichael S. Tsirkin         return;
29721c0fbfa3SMichael S. Tsirkin     }
29731c0fbfa3SMichael S. Tsirkin 
29749b02e161SWei Wang     if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE ||
29759b02e161SWei Wang         n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE ||
29769b02e161SWei Wang         !is_power_of_2(n->net_conf.tx_queue_size)) {
29779b02e161SWei Wang         error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), "
29789b02e161SWei Wang                    "must be a power of 2 between %d and %d",
29799b02e161SWei Wang                    n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE,
29809b02e161SWei Wang                    VIRTQUEUE_MAX_SIZE);
29819b02e161SWei Wang         virtio_cleanup(vdev);
29829b02e161SWei Wang         return;
29839b02e161SWei Wang     }
29849b02e161SWei Wang 
2985575a1c0eSJiri Pirko     n->max_queues = MAX(n->nic_conf.peers.queues, 1);
298687b3bd1cSJason Wang     if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) {
29877e0e736eSJason Wang         error_setg(errp, "Invalid number of queues (= %" PRIu32 "), "
2988631b22eaSStefan Weil                    "must be a positive integer less than %d.",
298987b3bd1cSJason Wang                    n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2);
29907e0e736eSJason Wang         virtio_cleanup(vdev);
29917e0e736eSJason Wang         return;
29927e0e736eSJason Wang     }
29936e790746SPaolo Bonzini     n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues);
29946e790746SPaolo Bonzini     n->curr_queues = 1;
29951773d9eeSKONRAD Frederic     n->tx_timeout = n->net_conf.txtimer;
29966e790746SPaolo Bonzini 
29971773d9eeSKONRAD Frederic     if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer")
29981773d9eeSKONRAD Frederic                        && strcmp(n->net_conf.tx, "bh")) {
29990765691eSMarkus Armbruster         warn_report("virtio-net: "
30006e790746SPaolo Bonzini                     "Unknown option tx=%s, valid options: \"timer\" \"bh\"",
30011773d9eeSKONRAD Frederic                     n->net_conf.tx);
30020765691eSMarkus Armbruster         error_printf("Defaulting to \"bh\"");
30036e790746SPaolo Bonzini     }
30046e790746SPaolo Bonzini 
30052eef278bSMichael S. Tsirkin     n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n),
30062eef278bSMichael S. Tsirkin                                     n->net_conf.tx_queue_size);
30079b02e161SWei Wang 
3008da51a335SJason Wang     for (i = 0; i < n->max_queues; i++) {
3009f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i);
3010da51a335SJason Wang     }
3011da51a335SJason Wang 
301217a0ca55SKONRAD Frederic     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
30131773d9eeSKONRAD Frederic     qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);
30141773d9eeSKONRAD Frederic     memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac));
30156e790746SPaolo Bonzini     n->status = VIRTIO_NET_S_LINK_UP;
30169d8c6a25SDr. David Alan Gilbert     qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(),
30179d8c6a25SDr. David Alan Gilbert                               QEMU_CLOCK_VIRTUAL,
3018f57fcf70SJason Wang                               virtio_net_announce_timer, n);
3019b2c929f0SDr. David Alan Gilbert     n->announce_timer.round = 0;
30206e790746SPaolo Bonzini 
30218a253ec2SKONRAD Frederic     if (n->netclient_type) {
30228a253ec2SKONRAD Frederic         /*
30238a253ec2SKONRAD Frederic          * Happen when virtio_net_set_netclient_name has been called.
30248a253ec2SKONRAD Frederic          */
30258a253ec2SKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
30268a253ec2SKONRAD Frederic                               n->netclient_type, n->netclient_name, n);
30278a253ec2SKONRAD Frederic     } else {
30281773d9eeSKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
3029284a32f0SAndreas Färber                               object_get_typename(OBJECT(dev)), dev->id, n);
30308a253ec2SKONRAD Frederic     }
30318a253ec2SKONRAD Frederic 
30326e790746SPaolo Bonzini     peer_test_vnet_hdr(n);
30336e790746SPaolo Bonzini     if (peer_has_vnet_hdr(n)) {
30346e790746SPaolo Bonzini         for (i = 0; i < n->max_queues; i++) {
3035d6085e3aSStefan Hajnoczi             qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true);
30366e790746SPaolo Bonzini         }
30376e790746SPaolo Bonzini         n->host_hdr_len = sizeof(struct virtio_net_hdr);
30386e790746SPaolo Bonzini     } else {
30396e790746SPaolo Bonzini         n->host_hdr_len = 0;
30406e790746SPaolo Bonzini     }
30416e790746SPaolo Bonzini 
30421773d9eeSKONRAD Frederic     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->nic_conf.macaddr.a);
30436e790746SPaolo Bonzini 
30446e790746SPaolo Bonzini     n->vqs[0].tx_waiting = 0;
30451773d9eeSKONRAD Frederic     n->tx_burst = n->net_conf.txburst;
3046bb9d17f8SCornelia Huck     virtio_net_set_mrg_rx_bufs(n, 0, 0);
30476e790746SPaolo Bonzini     n->promisc = 1; /* for compatibility */
30486e790746SPaolo Bonzini 
30496e790746SPaolo Bonzini     n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
30506e790746SPaolo Bonzini 
30516e790746SPaolo Bonzini     n->vlans = g_malloc0(MAX_VLAN >> 3);
30526e790746SPaolo Bonzini 
3053b1be4280SAmos Kong     nc = qemu_get_queue(n->nic);
3054b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
3055b1be4280SAmos Kong 
30562974e916SYuri Benditovich     QTAILQ_INIT(&n->rsc_chains);
3057284a32f0SAndreas Färber     n->qdev = dev;
305817ec5a86SKONRAD Frederic }
305917ec5a86SKONRAD Frederic 
3060306ec6c3SAndreas Färber static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
306117ec5a86SKONRAD Frederic {
3062306ec6c3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
3063306ec6c3SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
3064f9d6dbf0SWen Congyang     int i, max_queues;
306517ec5a86SKONRAD Frederic 
306617ec5a86SKONRAD Frederic     /* This will stop vhost backend if appropriate. */
306717ec5a86SKONRAD Frederic     virtio_net_set_status(vdev, 0);
306817ec5a86SKONRAD Frederic 
30698a253ec2SKONRAD Frederic     g_free(n->netclient_name);
30708a253ec2SKONRAD Frederic     n->netclient_name = NULL;
30718a253ec2SKONRAD Frederic     g_free(n->netclient_type);
30728a253ec2SKONRAD Frederic     n->netclient_type = NULL;
30738a253ec2SKONRAD Frederic 
307417ec5a86SKONRAD Frederic     g_free(n->mac_table.macs);
307517ec5a86SKONRAD Frederic     g_free(n->vlans);
307617ec5a86SKONRAD Frederic 
30779711cd0dSJens Freimann     if (n->failover) {
30789711cd0dSJens Freimann         g_free(n->primary_device_id);
30799711cd0dSJens Freimann         g_free(n->standby_id);
30809711cd0dSJens Freimann         qobject_unref(n->primary_device_dict);
30819711cd0dSJens Freimann         n->primary_device_dict = NULL;
30829711cd0dSJens Freimann     }
30839711cd0dSJens Freimann 
3084f9d6dbf0SWen Congyang     max_queues = n->multiqueue ? n->max_queues : 1;
3085f9d6dbf0SWen Congyang     for (i = 0; i < max_queues; i++) {
3086f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i);
308717ec5a86SKONRAD Frederic     }
308817ec5a86SKONRAD Frederic 
3089944458b6SDr. David Alan Gilbert     qemu_announce_timer_del(&n->announce_timer, false);
309017ec5a86SKONRAD Frederic     g_free(n->vqs);
309117ec5a86SKONRAD Frederic     qemu_del_nic(n->nic);
30922974e916SYuri Benditovich     virtio_net_rsc_cleanup(n);
30936a1a8cc7SKONRAD Frederic     virtio_cleanup(vdev);
309417ec5a86SKONRAD Frederic }
309517ec5a86SKONRAD Frederic 
309617ec5a86SKONRAD Frederic static void virtio_net_instance_init(Object *obj)
309717ec5a86SKONRAD Frederic {
309817ec5a86SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(obj);
309917ec5a86SKONRAD Frederic 
310017ec5a86SKONRAD Frederic     /*
310117ec5a86SKONRAD Frederic      * The default config_size is sizeof(struct virtio_net_config).
310217ec5a86SKONRAD Frederic      * Can be overriden with virtio_net_set_config_size.
310317ec5a86SKONRAD Frederic      */
310417ec5a86SKONRAD Frederic     n->config_size = sizeof(struct virtio_net_config);
3105aa4197c3SGonglei     device_add_bootindex_property(obj, &n->nic_conf.bootindex,
3106aa4197c3SGonglei                                   "bootindex", "/ethernet-phy@0",
3107aa4197c3SGonglei                                   DEVICE(n), NULL);
310817ec5a86SKONRAD Frederic }
310917ec5a86SKONRAD Frederic 
311044b1ff31SDr. David Alan Gilbert static int virtio_net_pre_save(void *opaque)
31114d45dcfbSHalil Pasic {
31124d45dcfbSHalil Pasic     VirtIONet *n = opaque;
31134d45dcfbSHalil Pasic 
31144d45dcfbSHalil Pasic     /* At this point, backend must be stopped, otherwise
31154d45dcfbSHalil Pasic      * it might keep writing to memory. */
31164d45dcfbSHalil Pasic     assert(!n->vhost_started);
311744b1ff31SDr. David Alan Gilbert 
311844b1ff31SDr. David Alan Gilbert     return 0;
31194d45dcfbSHalil Pasic }
31204d45dcfbSHalil Pasic 
31219711cd0dSJens Freimann static bool primary_unplug_pending(void *opaque)
31229711cd0dSJens Freimann {
31239711cd0dSJens Freimann     DeviceState *dev = opaque;
31249711cd0dSJens Freimann     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
31259711cd0dSJens Freimann     VirtIONet *n = VIRTIO_NET(vdev);
31269711cd0dSJens Freimann 
31279711cd0dSJens Freimann     return n->primary_dev ? n->primary_dev->pending_deleted_event : false;
31289711cd0dSJens Freimann }
31299711cd0dSJens Freimann 
31309711cd0dSJens Freimann static bool dev_unplug_pending(void *opaque)
31319711cd0dSJens Freimann {
31329711cd0dSJens Freimann     DeviceState *dev = opaque;
31339711cd0dSJens Freimann     VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev);
31349711cd0dSJens Freimann 
31359711cd0dSJens Freimann     return vdc->primary_unplug_pending(dev);
31369711cd0dSJens Freimann }
31379711cd0dSJens Freimann 
31384d45dcfbSHalil Pasic static const VMStateDescription vmstate_virtio_net = {
31394d45dcfbSHalil Pasic     .name = "virtio-net",
31404d45dcfbSHalil Pasic     .minimum_version_id = VIRTIO_NET_VM_VERSION,
31414d45dcfbSHalil Pasic     .version_id = VIRTIO_NET_VM_VERSION,
31424d45dcfbSHalil Pasic     .fields = (VMStateField[]) {
31434d45dcfbSHalil Pasic         VMSTATE_VIRTIO_DEVICE,
31444d45dcfbSHalil Pasic         VMSTATE_END_OF_LIST()
31454d45dcfbSHalil Pasic     },
31464d45dcfbSHalil Pasic     .pre_save = virtio_net_pre_save,
31479711cd0dSJens Freimann     .dev_unplug_pending = dev_unplug_pending,
31484d45dcfbSHalil Pasic };
3149290c2428SDr. David Alan Gilbert 
315017ec5a86SKONRAD Frederic static Property virtio_net_properties[] = {
3151127833eeSJason Baron     DEFINE_PROP_BIT64("csum", VirtIONet, host_features,
3152127833eeSJason Baron                     VIRTIO_NET_F_CSUM, true),
3153127833eeSJason Baron     DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features,
315487108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_CSUM, true),
3155127833eeSJason Baron     DEFINE_PROP_BIT64("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true),
3156127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso4", VirtIONet, host_features,
315787108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO4, true),
3158127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso6", VirtIONet, host_features,
315987108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO6, true),
3160127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ecn", VirtIONet, host_features,
316187108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ECN, true),
3162127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ufo", VirtIONet, host_features,
316387108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_UFO, true),
3164127833eeSJason Baron     DEFINE_PROP_BIT64("guest_announce", VirtIONet, host_features,
316587108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ANNOUNCE, true),
3166127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso4", VirtIONet, host_features,
316787108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO4, true),
3168127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso6", VirtIONet, host_features,
316987108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO6, true),
3170127833eeSJason Baron     DEFINE_PROP_BIT64("host_ecn", VirtIONet, host_features,
317187108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_ECN, true),
3172127833eeSJason Baron     DEFINE_PROP_BIT64("host_ufo", VirtIONet, host_features,
317387108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_UFO, true),
3174127833eeSJason Baron     DEFINE_PROP_BIT64("mrg_rxbuf", VirtIONet, host_features,
317587108bb2SShannon Zhao                     VIRTIO_NET_F_MRG_RXBUF, true),
3176127833eeSJason Baron     DEFINE_PROP_BIT64("status", VirtIONet, host_features,
317787108bb2SShannon Zhao                     VIRTIO_NET_F_STATUS, true),
3178127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vq", VirtIONet, host_features,
317987108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VQ, true),
3180127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx", VirtIONet, host_features,
318187108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX, true),
3182127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vlan", VirtIONet, host_features,
318387108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VLAN, true),
3184127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx_extra", VirtIONet, host_features,
318587108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX_EXTRA, true),
3186127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_mac_addr", VirtIONet, host_features,
318787108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_MAC_ADDR, true),
3188127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features,
318987108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
3190127833eeSJason Baron     DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
31912974e916SYuri Benditovich     DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features,
31922974e916SYuri Benditovich                     VIRTIO_NET_F_RSC_EXT, false),
31932974e916SYuri Benditovich     DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout,
31942974e916SYuri Benditovich                        VIRTIO_NET_RSC_DEFAULT_INTERVAL),
319517ec5a86SKONRAD Frederic     DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf),
319617ec5a86SKONRAD Frederic     DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer,
319717ec5a86SKONRAD Frederic                        TX_TIMER_INTERVAL),
319817ec5a86SKONRAD Frederic     DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST),
319917ec5a86SKONRAD Frederic     DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx),
32001c0fbfa3SMichael S. Tsirkin     DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size,
32011c0fbfa3SMichael S. Tsirkin                        VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE),
32029b02e161SWei Wang     DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size,
32039b02e161SWei Wang                        VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE),
3204a93e599dSMaxime Coquelin     DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0),
320575ebec11SMaxime Coquelin     DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend,
320675ebec11SMaxime Coquelin                      true),
32079473939eSJason Baron     DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN),
32089473939eSJason Baron     DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str),
32099711cd0dSJens Freimann     DEFINE_PROP_BOOL("failover", VirtIONet, failover, false),
321017ec5a86SKONRAD Frederic     DEFINE_PROP_END_OF_LIST(),
321117ec5a86SKONRAD Frederic };
321217ec5a86SKONRAD Frederic 
321317ec5a86SKONRAD Frederic static void virtio_net_class_init(ObjectClass *klass, void *data)
321417ec5a86SKONRAD Frederic {
321517ec5a86SKONRAD Frederic     DeviceClass *dc = DEVICE_CLASS(klass);
321617ec5a86SKONRAD Frederic     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
3217e6f746b3SAndreas Färber 
321817ec5a86SKONRAD Frederic     dc->props = virtio_net_properties;
3219290c2428SDr. David Alan Gilbert     dc->vmsd = &vmstate_virtio_net;
3220125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
3221e6f746b3SAndreas Färber     vdc->realize = virtio_net_device_realize;
3222306ec6c3SAndreas Färber     vdc->unrealize = virtio_net_device_unrealize;
322317ec5a86SKONRAD Frederic     vdc->get_config = virtio_net_get_config;
322417ec5a86SKONRAD Frederic     vdc->set_config = virtio_net_set_config;
322517ec5a86SKONRAD Frederic     vdc->get_features = virtio_net_get_features;
322617ec5a86SKONRAD Frederic     vdc->set_features = virtio_net_set_features;
322717ec5a86SKONRAD Frederic     vdc->bad_features = virtio_net_bad_features;
322817ec5a86SKONRAD Frederic     vdc->reset = virtio_net_reset;
322917ec5a86SKONRAD Frederic     vdc->set_status = virtio_net_set_status;
323017ec5a86SKONRAD Frederic     vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
323117ec5a86SKONRAD Frederic     vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
32322a083ffdSMichael S. Tsirkin     vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO);
32337788c3f2SMikhail Sennikovsky     vdc->post_load = virtio_net_post_load_virtio;
3234982b78c5SDr. David Alan Gilbert     vdc->vmsd = &vmstate_virtio_net_device;
32359711cd0dSJens Freimann     vdc->primary_unplug_pending = primary_unplug_pending;
323617ec5a86SKONRAD Frederic }
323717ec5a86SKONRAD Frederic 
323817ec5a86SKONRAD Frederic static const TypeInfo virtio_net_info = {
323917ec5a86SKONRAD Frederic     .name = TYPE_VIRTIO_NET,
324017ec5a86SKONRAD Frederic     .parent = TYPE_VIRTIO_DEVICE,
324117ec5a86SKONRAD Frederic     .instance_size = sizeof(VirtIONet),
324217ec5a86SKONRAD Frederic     .instance_init = virtio_net_instance_init,
324317ec5a86SKONRAD Frederic     .class_init = virtio_net_class_init,
324417ec5a86SKONRAD Frederic };
324517ec5a86SKONRAD Frederic 
324617ec5a86SKONRAD Frederic static void virtio_register_types(void)
324717ec5a86SKONRAD Frederic {
324817ec5a86SKONRAD Frederic     type_register_static(&virtio_net_info);
324917ec5a86SKONRAD Frederic }
325017ec5a86SKONRAD Frederic 
325117ec5a86SKONRAD Frederic type_init(virtio_register_types)
3252