xref: /openbmc/qemu/hw/net/virtio-net.c (revision 590790297c0dd2c8e817c7b33daf66862b0ee8ef)
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 
80*59079029SYuri Benditovich #define VIRTIO_NET_RSS_SUPPORTED_HASHES (VIRTIO_NET_RSS_HASH_TYPE_IPv4 | \
81*59079029SYuri Benditovich                                          VIRTIO_NET_RSS_HASH_TYPE_TCPv4 | \
82*59079029SYuri Benditovich                                          VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | \
83*59079029SYuri Benditovich                                          VIRTIO_NET_RSS_HASH_TYPE_IPv6 | \
84*59079029SYuri Benditovich                                          VIRTIO_NET_RSS_HASH_TYPE_TCPv6 | \
85*59079029SYuri Benditovich                                          VIRTIO_NET_RSS_HASH_TYPE_UDPv6 | \
86*59079029SYuri Benditovich                                          VIRTIO_NET_RSS_HASH_TYPE_IP_EX | \
87*59079029SYuri Benditovich                                          VIRTIO_NET_RSS_HASH_TYPE_TCP_EX | \
88*59079029SYuri Benditovich                                          VIRTIO_NET_RSS_HASH_TYPE_UDP_EX)
89*59079029SYuri Benditovich 
902974e916SYuri Benditovich /* temporary until standard header include it */
912974e916SYuri Benditovich #if !defined(VIRTIO_NET_HDR_F_RSC_INFO)
922974e916SYuri Benditovich 
932974e916SYuri Benditovich #define VIRTIO_NET_HDR_F_RSC_INFO  4 /* rsc_ext data in csum_ fields */
94d47e5e31SYuri Benditovich #define VIRTIO_NET_F_RSC_EXT       61
952974e916SYuri Benditovich 
969904adfaSCornelia Huck #endif
979904adfaSCornelia Huck 
982974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_packets(
992974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
1002974e916SYuri Benditovich {
1012974e916SYuri Benditovich     return &hdr->csum_start;
1022974e916SYuri Benditovich }
1032974e916SYuri Benditovich 
1042974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
1052974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
1062974e916SYuri Benditovich {
1072974e916SYuri Benditovich     return &hdr->csum_offset;
1082974e916SYuri Benditovich }
1092974e916SYuri Benditovich 
1106e790746SPaolo Bonzini static VirtIOFeature feature_sizes[] = {
111127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MAC,
1125d5b33c0SMax Reitz      .end = endof(struct virtio_net_config, mac)},
113127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_STATUS,
1145d5b33c0SMax Reitz      .end = endof(struct virtio_net_config, status)},
115127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MQ,
1165d5b33c0SMax Reitz      .end = endof(struct virtio_net_config, max_virtqueue_pairs)},
117127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MTU,
1185d5b33c0SMax Reitz      .end = endof(struct virtio_net_config, mtu)},
1199473939eSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
1205d5b33c0SMax Reitz      .end = endof(struct virtio_net_config, duplex)},
121*59079029SYuri Benditovich     {.flags = 1ULL << VIRTIO_NET_F_RSS,
122*59079029SYuri Benditovich      .end = endof(struct virtio_net_config, supported_hash_types)},
1236e790746SPaolo Bonzini     {}
1246e790746SPaolo Bonzini };
1256e790746SPaolo Bonzini 
1266e790746SPaolo Bonzini static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc)
1276e790746SPaolo Bonzini {
1286e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
1296e790746SPaolo Bonzini 
1306e790746SPaolo Bonzini     return &n->vqs[nc->queue_index];
1316e790746SPaolo Bonzini }
1326e790746SPaolo Bonzini 
1336e790746SPaolo Bonzini static int vq2q(int queue_index)
1346e790746SPaolo Bonzini {
1356e790746SPaolo Bonzini     return queue_index / 2;
1366e790746SPaolo Bonzini }
1376e790746SPaolo Bonzini 
1386e790746SPaolo Bonzini /* TODO
1396e790746SPaolo Bonzini  * - we could suppress RX interrupt if we were so inclined.
1406e790746SPaolo Bonzini  */
1416e790746SPaolo Bonzini 
1426e790746SPaolo Bonzini static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
1436e790746SPaolo Bonzini {
14417a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1456e790746SPaolo Bonzini     struct virtio_net_config netcfg;
1466e790746SPaolo Bonzini 
1471399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.status, n->status);
1481399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues);
149a93e599dSMaxime Coquelin     virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu);
1506e790746SPaolo Bonzini     memcpy(netcfg.mac, n->mac, ETH_ALEN);
1519473939eSJason Baron     virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed);
1529473939eSJason Baron     netcfg.duplex = n->net_conf.duplex;
153*59079029SYuri Benditovich     netcfg.rss_max_key_size = VIRTIO_NET_RSS_MAX_KEY_SIZE;
154*59079029SYuri Benditovich     virtio_stw_p(vdev, &netcfg.rss_max_indirection_table_length,
155*59079029SYuri Benditovich                  VIRTIO_NET_RSS_MAX_TABLE_LEN);
156*59079029SYuri Benditovich     virtio_stl_p(vdev, &netcfg.supported_hash_types,
157*59079029SYuri Benditovich                  VIRTIO_NET_RSS_SUPPORTED_HASHES);
1586e790746SPaolo Bonzini     memcpy(config, &netcfg, n->config_size);
1596e790746SPaolo Bonzini }
1606e790746SPaolo Bonzini 
1616e790746SPaolo Bonzini static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
1626e790746SPaolo Bonzini {
16317a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1646e790746SPaolo Bonzini     struct virtio_net_config netcfg = {};
1656e790746SPaolo Bonzini 
1666e790746SPaolo Bonzini     memcpy(&netcfg, config, n->config_size);
1676e790746SPaolo Bonzini 
16895129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
16995129d6fSCornelia Huck         !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) &&
1706e790746SPaolo Bonzini         memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
1716e790746SPaolo Bonzini         memcpy(n->mac, netcfg.mac, ETH_ALEN);
1726e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
1736e790746SPaolo Bonzini     }
1746e790746SPaolo Bonzini }
1756e790746SPaolo Bonzini 
1766e790746SPaolo Bonzini static bool virtio_net_started(VirtIONet *n, uint8_t status)
1776e790746SPaolo Bonzini {
17817a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1796e790746SPaolo Bonzini     return (status & VIRTIO_CONFIG_S_DRIVER_OK) &&
18017a0ca55SKONRAD Frederic         (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running;
1816e790746SPaolo Bonzini }
1826e790746SPaolo Bonzini 
183b2c929f0SDr. David Alan Gilbert static void virtio_net_announce_notify(VirtIONet *net)
184b2c929f0SDr. David Alan Gilbert {
185b2c929f0SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(net);
186b2c929f0SDr. David Alan Gilbert     trace_virtio_net_announce_notify();
187b2c929f0SDr. David Alan Gilbert 
188b2c929f0SDr. David Alan Gilbert     net->status |= VIRTIO_NET_S_ANNOUNCE;
189b2c929f0SDr. David Alan Gilbert     virtio_notify_config(vdev);
190b2c929f0SDr. David Alan Gilbert }
191b2c929f0SDr. David Alan Gilbert 
192f57fcf70SJason Wang static void virtio_net_announce_timer(void *opaque)
193f57fcf70SJason Wang {
194f57fcf70SJason Wang     VirtIONet *n = opaque;
1959d8c6a25SDr. David Alan Gilbert     trace_virtio_net_announce_timer(n->announce_timer.round);
196f57fcf70SJason Wang 
1979d8c6a25SDr. David Alan Gilbert     n->announce_timer.round--;
198b2c929f0SDr. David Alan Gilbert     virtio_net_announce_notify(n);
199b2c929f0SDr. David Alan Gilbert }
200b2c929f0SDr. David Alan Gilbert 
201b2c929f0SDr. David Alan Gilbert static void virtio_net_announce(NetClientState *nc)
202b2c929f0SDr. David Alan Gilbert {
203b2c929f0SDr. David Alan Gilbert     VirtIONet *n = qemu_get_nic_opaque(nc);
204b2c929f0SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(n);
205b2c929f0SDr. David Alan Gilbert 
206b2c929f0SDr. David Alan Gilbert     /*
207b2c929f0SDr. David Alan Gilbert      * Make sure the virtio migration announcement timer isn't running
208b2c929f0SDr. David Alan Gilbert      * If it is, let it trigger announcement so that we do not cause
209b2c929f0SDr. David Alan Gilbert      * confusion.
210b2c929f0SDr. David Alan Gilbert      */
211b2c929f0SDr. David Alan Gilbert     if (n->announce_timer.round) {
212b2c929f0SDr. David Alan Gilbert         return;
213b2c929f0SDr. David Alan Gilbert     }
214b2c929f0SDr. David Alan Gilbert 
215b2c929f0SDr. David Alan Gilbert     if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
216b2c929f0SDr. David Alan Gilbert         virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
217b2c929f0SDr. David Alan Gilbert             virtio_net_announce_notify(n);
218b2c929f0SDr. David Alan Gilbert     }
219f57fcf70SJason Wang }
220f57fcf70SJason Wang 
2216e790746SPaolo Bonzini static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
2226e790746SPaolo Bonzini {
22317a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2246e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
2256e790746SPaolo Bonzini     int queues = n->multiqueue ? n->max_queues : 1;
2266e790746SPaolo Bonzini 
227ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
2286e790746SPaolo Bonzini         return;
2296e790746SPaolo Bonzini     }
2306e790746SPaolo Bonzini 
2318c1ac475SRadim Krčmář     if ((virtio_net_started(n, status) && !nc->peer->link_down) ==
2328c1ac475SRadim Krčmář         !!n->vhost_started) {
2336e790746SPaolo Bonzini         return;
2346e790746SPaolo Bonzini     }
2356e790746SPaolo Bonzini     if (!n->vhost_started) {
236086abc1cSMichael S. Tsirkin         int r, i;
237086abc1cSMichael S. Tsirkin 
2381bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
2391bfa316cSGreg Kurz             error_report("backend does not support %s vnet headers; "
2401bfa316cSGreg Kurz                          "falling back on userspace virtio",
2411bfa316cSGreg Kurz                          virtio_is_big_endian(vdev) ? "BE" : "LE");
2421bfa316cSGreg Kurz             return;
2431bfa316cSGreg Kurz         }
2441bfa316cSGreg Kurz 
245086abc1cSMichael S. Tsirkin         /* Any packets outstanding? Purge them to avoid touching rings
246086abc1cSMichael S. Tsirkin          * when vhost is running.
247086abc1cSMichael S. Tsirkin          */
248086abc1cSMichael S. Tsirkin         for (i = 0;  i < queues; i++) {
249086abc1cSMichael S. Tsirkin             NetClientState *qnc = qemu_get_subqueue(n->nic, i);
250086abc1cSMichael S. Tsirkin 
251086abc1cSMichael S. Tsirkin             /* Purge both directions: TX and RX. */
252086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->peer->incoming_queue, qnc);
253086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->incoming_queue, qnc->peer);
254086abc1cSMichael S. Tsirkin         }
255086abc1cSMichael S. Tsirkin 
256a93e599dSMaxime Coquelin         if (virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MTU)) {
257a93e599dSMaxime Coquelin             r = vhost_net_set_mtu(get_vhost_net(nc->peer), n->net_conf.mtu);
258a93e599dSMaxime Coquelin             if (r < 0) {
259a93e599dSMaxime Coquelin                 error_report("%uBytes MTU not supported by the backend",
260a93e599dSMaxime Coquelin                              n->net_conf.mtu);
261a93e599dSMaxime Coquelin 
262a93e599dSMaxime Coquelin                 return;
263a93e599dSMaxime Coquelin             }
264a93e599dSMaxime Coquelin         }
265a93e599dSMaxime Coquelin 
2666e790746SPaolo Bonzini         n->vhost_started = 1;
26717a0ca55SKONRAD Frederic         r = vhost_net_start(vdev, n->nic->ncs, queues);
2686e790746SPaolo Bonzini         if (r < 0) {
2696e790746SPaolo Bonzini             error_report("unable to start vhost net: %d: "
2706e790746SPaolo Bonzini                          "falling back on userspace virtio", -r);
2716e790746SPaolo Bonzini             n->vhost_started = 0;
2726e790746SPaolo Bonzini         }
2736e790746SPaolo Bonzini     } else {
27417a0ca55SKONRAD Frederic         vhost_net_stop(vdev, n->nic->ncs, queues);
2756e790746SPaolo Bonzini         n->vhost_started = 0;
2766e790746SPaolo Bonzini     }
2776e790746SPaolo Bonzini }
2786e790746SPaolo Bonzini 
2791bfa316cSGreg Kurz static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev,
2801bfa316cSGreg Kurz                                           NetClientState *peer,
2811bfa316cSGreg Kurz                                           bool enable)
2821bfa316cSGreg Kurz {
2831bfa316cSGreg Kurz     if (virtio_is_big_endian(vdev)) {
2841bfa316cSGreg Kurz         return qemu_set_vnet_be(peer, enable);
2851bfa316cSGreg Kurz     } else {
2861bfa316cSGreg Kurz         return qemu_set_vnet_le(peer, enable);
2871bfa316cSGreg Kurz     }
2881bfa316cSGreg Kurz }
2891bfa316cSGreg Kurz 
2901bfa316cSGreg Kurz static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs,
2911bfa316cSGreg Kurz                                        int queues, bool enable)
2921bfa316cSGreg Kurz {
2931bfa316cSGreg Kurz     int i;
2941bfa316cSGreg Kurz 
2951bfa316cSGreg Kurz     for (i = 0; i < queues; i++) {
2961bfa316cSGreg Kurz         if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 &&
2971bfa316cSGreg Kurz             enable) {
2981bfa316cSGreg Kurz             while (--i >= 0) {
2991bfa316cSGreg Kurz                 virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false);
3001bfa316cSGreg Kurz             }
3011bfa316cSGreg Kurz 
3021bfa316cSGreg Kurz             return true;
3031bfa316cSGreg Kurz         }
3041bfa316cSGreg Kurz     }
3051bfa316cSGreg Kurz 
3061bfa316cSGreg Kurz     return false;
3071bfa316cSGreg Kurz }
3081bfa316cSGreg Kurz 
3091bfa316cSGreg Kurz static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status)
3101bfa316cSGreg Kurz {
3111bfa316cSGreg Kurz     VirtIODevice *vdev = VIRTIO_DEVICE(n);
3121bfa316cSGreg Kurz     int queues = n->multiqueue ? n->max_queues : 1;
3131bfa316cSGreg Kurz 
3141bfa316cSGreg Kurz     if (virtio_net_started(n, status)) {
3151bfa316cSGreg Kurz         /* Before using the device, we tell the network backend about the
3161bfa316cSGreg Kurz          * endianness to use when parsing vnet headers. If the backend
3171bfa316cSGreg Kurz          * can't do it, we fallback onto fixing the headers in the core
3181bfa316cSGreg Kurz          * virtio-net code.
3191bfa316cSGreg Kurz          */
3201bfa316cSGreg Kurz         n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs,
3211bfa316cSGreg Kurz                                                             queues, true);
3221bfa316cSGreg Kurz     } else if (virtio_net_started(n, vdev->status)) {
3231bfa316cSGreg Kurz         /* After using the device, we need to reset the network backend to
3241bfa316cSGreg Kurz          * the default (guest native endianness), otherwise the guest may
3251bfa316cSGreg Kurz          * lose network connectivity if it is rebooted into a different
3261bfa316cSGreg Kurz          * endianness.
3271bfa316cSGreg Kurz          */
3281bfa316cSGreg Kurz         virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false);
3291bfa316cSGreg Kurz     }
3301bfa316cSGreg Kurz }
3311bfa316cSGreg Kurz 
332283e2c2aSYuri Benditovich static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq)
333283e2c2aSYuri Benditovich {
334283e2c2aSYuri Benditovich     unsigned int dropped = virtqueue_drop_all(vq);
335283e2c2aSYuri Benditovich     if (dropped) {
336283e2c2aSYuri Benditovich         virtio_notify(vdev, vq);
337283e2c2aSYuri Benditovich     }
338283e2c2aSYuri Benditovich }
339283e2c2aSYuri Benditovich 
3406e790746SPaolo Bonzini static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
3416e790746SPaolo Bonzini {
34217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
3436e790746SPaolo Bonzini     VirtIONetQueue *q;
3446e790746SPaolo Bonzini     int i;
3456e790746SPaolo Bonzini     uint8_t queue_status;
3466e790746SPaolo Bonzini 
3471bfa316cSGreg Kurz     virtio_net_vnet_endian_status(n, status);
3486e790746SPaolo Bonzini     virtio_net_vhost_status(n, status);
3496e790746SPaolo Bonzini 
3506e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
35138705bb5SFam Zheng         NetClientState *ncs = qemu_get_subqueue(n->nic, i);
35238705bb5SFam Zheng         bool queue_started;
3536e790746SPaolo Bonzini         q = &n->vqs[i];
3546e790746SPaolo Bonzini 
3556e790746SPaolo Bonzini         if ((!n->multiqueue && i != 0) || i >= n->curr_queues) {
3566e790746SPaolo Bonzini             queue_status = 0;
3576e790746SPaolo Bonzini         } else {
3586e790746SPaolo Bonzini             queue_status = status;
3596e790746SPaolo Bonzini         }
36038705bb5SFam Zheng         queue_started =
36138705bb5SFam Zheng             virtio_net_started(n, queue_status) && !n->vhost_started;
36238705bb5SFam Zheng 
36338705bb5SFam Zheng         if (queue_started) {
36438705bb5SFam Zheng             qemu_flush_queued_packets(ncs);
36538705bb5SFam Zheng         }
3666e790746SPaolo Bonzini 
3676e790746SPaolo Bonzini         if (!q->tx_waiting) {
3686e790746SPaolo Bonzini             continue;
3696e790746SPaolo Bonzini         }
3706e790746SPaolo Bonzini 
37138705bb5SFam Zheng         if (queue_started) {
3726e790746SPaolo Bonzini             if (q->tx_timer) {
373bc72ad67SAlex Bligh                 timer_mod(q->tx_timer,
374bc72ad67SAlex Bligh                                qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
3756e790746SPaolo Bonzini             } else {
3766e790746SPaolo Bonzini                 qemu_bh_schedule(q->tx_bh);
3776e790746SPaolo Bonzini             }
3786e790746SPaolo Bonzini         } else {
3796e790746SPaolo Bonzini             if (q->tx_timer) {
380bc72ad67SAlex Bligh                 timer_del(q->tx_timer);
3816e790746SPaolo Bonzini             } else {
3826e790746SPaolo Bonzini                 qemu_bh_cancel(q->tx_bh);
3836e790746SPaolo Bonzini             }
384283e2c2aSYuri Benditovich             if ((n->status & VIRTIO_NET_S_LINK_UP) == 0 &&
38570e53e6eSJason Wang                 (queue_status & VIRTIO_CONFIG_S_DRIVER_OK) &&
38670e53e6eSJason Wang                 vdev->vm_running) {
387283e2c2aSYuri Benditovich                 /* if tx is waiting we are likely have some packets in tx queue
388283e2c2aSYuri Benditovich                  * and disabled notification */
389283e2c2aSYuri Benditovich                 q->tx_waiting = 0;
390283e2c2aSYuri Benditovich                 virtio_queue_set_notification(q->tx_vq, 1);
391283e2c2aSYuri Benditovich                 virtio_net_drop_tx_queue_data(vdev, q->tx_vq);
392283e2c2aSYuri Benditovich             }
3936e790746SPaolo Bonzini         }
3946e790746SPaolo Bonzini     }
3956e790746SPaolo Bonzini }
3966e790746SPaolo Bonzini 
3976e790746SPaolo Bonzini static void virtio_net_set_link_status(NetClientState *nc)
3986e790746SPaolo Bonzini {
3996e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
40017a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
4016e790746SPaolo Bonzini     uint16_t old_status = n->status;
4026e790746SPaolo Bonzini 
4036e790746SPaolo Bonzini     if (nc->link_down)
4046e790746SPaolo Bonzini         n->status &= ~VIRTIO_NET_S_LINK_UP;
4056e790746SPaolo Bonzini     else
4066e790746SPaolo Bonzini         n->status |= VIRTIO_NET_S_LINK_UP;
4076e790746SPaolo Bonzini 
4086e790746SPaolo Bonzini     if (n->status != old_status)
40917a0ca55SKONRAD Frederic         virtio_notify_config(vdev);
4106e790746SPaolo Bonzini 
41117a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
4126e790746SPaolo Bonzini }
4136e790746SPaolo Bonzini 
414b1be4280SAmos Kong static void rxfilter_notify(NetClientState *nc)
415b1be4280SAmos Kong {
416b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
417b1be4280SAmos Kong 
418b1be4280SAmos Kong     if (nc->rxfilter_notify_enabled) {
419ddfb0baaSMarkus Armbruster         char *path = object_get_canonical_path(OBJECT(n->qdev));
42006150279SWenchao Xia         qapi_event_send_nic_rx_filter_changed(!!n->netclient_name,
4213ab72385SPeter Xu                                               n->netclient_name, path);
42296e35046SAmos Kong         g_free(path);
423b1be4280SAmos Kong 
424b1be4280SAmos Kong         /* disable event notification to avoid events flooding */
425b1be4280SAmos Kong         nc->rxfilter_notify_enabled = 0;
426b1be4280SAmos Kong     }
427b1be4280SAmos Kong }
428b1be4280SAmos Kong 
429f7bc8ef8SAmos Kong static intList *get_vlan_table(VirtIONet *n)
430f7bc8ef8SAmos Kong {
431f7bc8ef8SAmos Kong     intList *list, *entry;
432f7bc8ef8SAmos Kong     int i, j;
433f7bc8ef8SAmos Kong 
434f7bc8ef8SAmos Kong     list = NULL;
435f7bc8ef8SAmos Kong     for (i = 0; i < MAX_VLAN >> 5; i++) {
436f7bc8ef8SAmos Kong         for (j = 0; n->vlans[i] && j <= 0x1f; j++) {
437f7bc8ef8SAmos Kong             if (n->vlans[i] & (1U << j)) {
438f7bc8ef8SAmos Kong                 entry = g_malloc0(sizeof(*entry));
439f7bc8ef8SAmos Kong                 entry->value = (i << 5) + j;
440f7bc8ef8SAmos Kong                 entry->next = list;
441f7bc8ef8SAmos Kong                 list = entry;
442f7bc8ef8SAmos Kong             }
443f7bc8ef8SAmos Kong         }
444f7bc8ef8SAmos Kong     }
445f7bc8ef8SAmos Kong 
446f7bc8ef8SAmos Kong     return list;
447f7bc8ef8SAmos Kong }
448f7bc8ef8SAmos Kong 
449b1be4280SAmos Kong static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
450b1be4280SAmos Kong {
451b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
452f7bc8ef8SAmos Kong     VirtIODevice *vdev = VIRTIO_DEVICE(n);
453b1be4280SAmos Kong     RxFilterInfo *info;
454b1be4280SAmos Kong     strList *str_list, *entry;
455f7bc8ef8SAmos Kong     int i;
456b1be4280SAmos Kong 
457b1be4280SAmos Kong     info = g_malloc0(sizeof(*info));
458b1be4280SAmos Kong     info->name = g_strdup(nc->name);
459b1be4280SAmos Kong     info->promiscuous = n->promisc;
460b1be4280SAmos Kong 
461b1be4280SAmos Kong     if (n->nouni) {
462b1be4280SAmos Kong         info->unicast = RX_STATE_NONE;
463b1be4280SAmos Kong     } else if (n->alluni) {
464b1be4280SAmos Kong         info->unicast = RX_STATE_ALL;
465b1be4280SAmos Kong     } else {
466b1be4280SAmos Kong         info->unicast = RX_STATE_NORMAL;
467b1be4280SAmos Kong     }
468b1be4280SAmos Kong 
469b1be4280SAmos Kong     if (n->nomulti) {
470b1be4280SAmos Kong         info->multicast = RX_STATE_NONE;
471b1be4280SAmos Kong     } else if (n->allmulti) {
472b1be4280SAmos Kong         info->multicast = RX_STATE_ALL;
473b1be4280SAmos Kong     } else {
474b1be4280SAmos Kong         info->multicast = RX_STATE_NORMAL;
475b1be4280SAmos Kong     }
476b1be4280SAmos Kong 
477b1be4280SAmos Kong     info->broadcast_allowed = n->nobcast;
478b1be4280SAmos Kong     info->multicast_overflow = n->mac_table.multi_overflow;
479b1be4280SAmos Kong     info->unicast_overflow = n->mac_table.uni_overflow;
480b1be4280SAmos Kong 
481b0575ba4SScott Feldman     info->main_mac = qemu_mac_strdup_printf(n->mac);
482b1be4280SAmos Kong 
483b1be4280SAmos Kong     str_list = NULL;
484b1be4280SAmos Kong     for (i = 0; i < n->mac_table.first_multi; i++) {
485b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
486b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
487b1be4280SAmos Kong         entry->next = str_list;
488b1be4280SAmos Kong         str_list = entry;
489b1be4280SAmos Kong     }
490b1be4280SAmos Kong     info->unicast_table = str_list;
491b1be4280SAmos Kong 
492b1be4280SAmos Kong     str_list = NULL;
493b1be4280SAmos Kong     for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
494b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
495b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
496b1be4280SAmos Kong         entry->next = str_list;
497b1be4280SAmos Kong         str_list = entry;
498b1be4280SAmos Kong     }
499b1be4280SAmos Kong     info->multicast_table = str_list;
500f7bc8ef8SAmos Kong     info->vlan_table = get_vlan_table(n);
501b1be4280SAmos Kong 
50295129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) {
503f7bc8ef8SAmos Kong         info->vlan = RX_STATE_ALL;
504f7bc8ef8SAmos Kong     } else if (!info->vlan_table) {
505f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NONE;
506f7bc8ef8SAmos Kong     } else {
507f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NORMAL;
508b1be4280SAmos Kong     }
509b1be4280SAmos Kong 
510b1be4280SAmos Kong     /* enable event notification after query */
511b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
512b1be4280SAmos Kong 
513b1be4280SAmos Kong     return info;
514b1be4280SAmos Kong }
515b1be4280SAmos Kong 
5166e790746SPaolo Bonzini static void virtio_net_reset(VirtIODevice *vdev)
5176e790746SPaolo Bonzini {
51817a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
51994b52958SGreg Kurz     int i;
5206e790746SPaolo Bonzini 
5216e790746SPaolo Bonzini     /* Reset back to compatibility mode */
5226e790746SPaolo Bonzini     n->promisc = 1;
5236e790746SPaolo Bonzini     n->allmulti = 0;
5246e790746SPaolo Bonzini     n->alluni = 0;
5256e790746SPaolo Bonzini     n->nomulti = 0;
5266e790746SPaolo Bonzini     n->nouni = 0;
5276e790746SPaolo Bonzini     n->nobcast = 0;
5286e790746SPaolo Bonzini     /* multiqueue is disabled by default */
5296e790746SPaolo Bonzini     n->curr_queues = 1;
5309d8c6a25SDr. David Alan Gilbert     timer_del(n->announce_timer.tm);
5319d8c6a25SDr. David Alan Gilbert     n->announce_timer.round = 0;
532f57fcf70SJason Wang     n->status &= ~VIRTIO_NET_S_ANNOUNCE;
5336e790746SPaolo Bonzini 
5346e790746SPaolo Bonzini     /* Flush any MAC and VLAN filter table state */
5356e790746SPaolo Bonzini     n->mac_table.in_use = 0;
5366e790746SPaolo Bonzini     n->mac_table.first_multi = 0;
5376e790746SPaolo Bonzini     n->mac_table.multi_overflow = 0;
5386e790746SPaolo Bonzini     n->mac_table.uni_overflow = 0;
5396e790746SPaolo Bonzini     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
5406e790746SPaolo Bonzini     memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac));
541702d66a8SMichael S. Tsirkin     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
5426e790746SPaolo Bonzini     memset(n->vlans, 0, MAX_VLAN >> 3);
54394b52958SGreg Kurz 
54494b52958SGreg Kurz     /* Flush any async TX */
54594b52958SGreg Kurz     for (i = 0;  i < n->max_queues; i++) {
54694b52958SGreg Kurz         NetClientState *nc = qemu_get_subqueue(n->nic, i);
54794b52958SGreg Kurz 
54894b52958SGreg Kurz         if (nc->peer) {
54994b52958SGreg Kurz             qemu_flush_or_purge_queued_packets(nc->peer, true);
55094b52958SGreg Kurz             assert(!virtio_net_get_subqueue(nc)->async_tx.elem);
55194b52958SGreg Kurz         }
55294b52958SGreg Kurz     }
5536e790746SPaolo Bonzini }
5546e790746SPaolo Bonzini 
5556e790746SPaolo Bonzini static void peer_test_vnet_hdr(VirtIONet *n)
5566e790746SPaolo Bonzini {
5576e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
5586e790746SPaolo Bonzini     if (!nc->peer) {
5596e790746SPaolo Bonzini         return;
5606e790746SPaolo Bonzini     }
5616e790746SPaolo Bonzini 
562d6085e3aSStefan Hajnoczi     n->has_vnet_hdr = qemu_has_vnet_hdr(nc->peer);
5636e790746SPaolo Bonzini }
5646e790746SPaolo Bonzini 
5656e790746SPaolo Bonzini static int peer_has_vnet_hdr(VirtIONet *n)
5666e790746SPaolo Bonzini {
5676e790746SPaolo Bonzini     return n->has_vnet_hdr;
5686e790746SPaolo Bonzini }
5696e790746SPaolo Bonzini 
5706e790746SPaolo Bonzini static int peer_has_ufo(VirtIONet *n)
5716e790746SPaolo Bonzini {
5726e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n))
5736e790746SPaolo Bonzini         return 0;
5746e790746SPaolo Bonzini 
575d6085e3aSStefan Hajnoczi     n->has_ufo = qemu_has_ufo(qemu_get_queue(n->nic)->peer);
5766e790746SPaolo Bonzini 
5776e790746SPaolo Bonzini     return n->has_ufo;
5786e790746SPaolo Bonzini }
5796e790746SPaolo Bonzini 
580bb9d17f8SCornelia Huck static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
581bb9d17f8SCornelia Huck                                        int version_1)
5826e790746SPaolo Bonzini {
5836e790746SPaolo Bonzini     int i;
5846e790746SPaolo Bonzini     NetClientState *nc;
5856e790746SPaolo Bonzini 
5866e790746SPaolo Bonzini     n->mergeable_rx_bufs = mergeable_rx_bufs;
5876e790746SPaolo Bonzini 
588bb9d17f8SCornelia Huck     if (version_1) {
589bb9d17f8SCornelia Huck         n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
590bb9d17f8SCornelia Huck     } else {
5916e790746SPaolo Bonzini         n->guest_hdr_len = n->mergeable_rx_bufs ?
592bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr_mrg_rxbuf) :
593bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr);
594bb9d17f8SCornelia Huck     }
5956e790746SPaolo Bonzini 
5966e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
5976e790746SPaolo Bonzini         nc = qemu_get_subqueue(n->nic, i);
5986e790746SPaolo Bonzini 
5996e790746SPaolo Bonzini         if (peer_has_vnet_hdr(n) &&
600d6085e3aSStefan Hajnoczi             qemu_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) {
601d6085e3aSStefan Hajnoczi             qemu_set_vnet_hdr_len(nc->peer, n->guest_hdr_len);
6026e790746SPaolo Bonzini             n->host_hdr_len = n->guest_hdr_len;
6036e790746SPaolo Bonzini         }
6046e790746SPaolo Bonzini     }
6056e790746SPaolo Bonzini }
6066e790746SPaolo Bonzini 
6072eef278bSMichael S. Tsirkin static int virtio_net_max_tx_queue_size(VirtIONet *n)
6082eef278bSMichael S. Tsirkin {
6092eef278bSMichael S. Tsirkin     NetClientState *peer = n->nic_conf.peers.ncs[0];
6102eef278bSMichael S. Tsirkin 
6112eef278bSMichael S. Tsirkin     /*
6122eef278bSMichael S. Tsirkin      * Backends other than vhost-user don't support max queue size.
6132eef278bSMichael S. Tsirkin      */
6142eef278bSMichael S. Tsirkin     if (!peer) {
6152eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
6162eef278bSMichael S. Tsirkin     }
6172eef278bSMichael S. Tsirkin 
6182eef278bSMichael S. Tsirkin     if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) {
6192eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
6202eef278bSMichael S. Tsirkin     }
6212eef278bSMichael S. Tsirkin 
6222eef278bSMichael S. Tsirkin     return VIRTQUEUE_MAX_SIZE;
6232eef278bSMichael S. Tsirkin }
6242eef278bSMichael S. Tsirkin 
6256e790746SPaolo Bonzini static int peer_attach(VirtIONet *n, int index)
6266e790746SPaolo Bonzini {
6276e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
6286e790746SPaolo Bonzini 
6296e790746SPaolo Bonzini     if (!nc->peer) {
6306e790746SPaolo Bonzini         return 0;
6316e790746SPaolo Bonzini     }
6326e790746SPaolo Bonzini 
633f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
6347263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 1);
6357263a0adSChangchun Ouyang     }
6367263a0adSChangchun Ouyang 
637f394b2e2SEric Blake     if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
6386e790746SPaolo Bonzini         return 0;
6396e790746SPaolo Bonzini     }
6406e790746SPaolo Bonzini 
6411074b879SJason Wang     if (n->max_queues == 1) {
6421074b879SJason Wang         return 0;
6431074b879SJason Wang     }
6441074b879SJason Wang 
6456e790746SPaolo Bonzini     return tap_enable(nc->peer);
6466e790746SPaolo Bonzini }
6476e790746SPaolo Bonzini 
6486e790746SPaolo Bonzini static int peer_detach(VirtIONet *n, int index)
6496e790746SPaolo Bonzini {
6506e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
6516e790746SPaolo Bonzini 
6526e790746SPaolo Bonzini     if (!nc->peer) {
6536e790746SPaolo Bonzini         return 0;
6546e790746SPaolo Bonzini     }
6556e790746SPaolo Bonzini 
656f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
6577263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 0);
6587263a0adSChangchun Ouyang     }
6597263a0adSChangchun Ouyang 
660f394b2e2SEric Blake     if (nc->peer->info->type !=  NET_CLIENT_DRIVER_TAP) {
6616e790746SPaolo Bonzini         return 0;
6626e790746SPaolo Bonzini     }
6636e790746SPaolo Bonzini 
6646e790746SPaolo Bonzini     return tap_disable(nc->peer);
6656e790746SPaolo Bonzini }
6666e790746SPaolo Bonzini 
6676e790746SPaolo Bonzini static void virtio_net_set_queues(VirtIONet *n)
6686e790746SPaolo Bonzini {
6696e790746SPaolo Bonzini     int i;
670ddfa83eaSJoel Stanley     int r;
6716e790746SPaolo Bonzini 
67268b5f314SYuri Benditovich     if (n->nic->peer_deleted) {
67368b5f314SYuri Benditovich         return;
67468b5f314SYuri Benditovich     }
67568b5f314SYuri Benditovich 
6766e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
6776e790746SPaolo Bonzini         if (i < n->curr_queues) {
678ddfa83eaSJoel Stanley             r = peer_attach(n, i);
679ddfa83eaSJoel Stanley             assert(!r);
6806e790746SPaolo Bonzini         } else {
681ddfa83eaSJoel Stanley             r = peer_detach(n, i);
682ddfa83eaSJoel Stanley             assert(!r);
6836e790746SPaolo Bonzini         }
6846e790746SPaolo Bonzini     }
6856e790746SPaolo Bonzini }
6866e790746SPaolo Bonzini 
687ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue);
6886e790746SPaolo Bonzini 
6899d5b731dSJason Wang static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
6909d5b731dSJason Wang                                         Error **errp)
6916e790746SPaolo Bonzini {
69217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
6936e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
6946e790746SPaolo Bonzini 
695da3e8a23SShannon Zhao     /* Firstly sync all virtio-net possible supported features */
696da3e8a23SShannon Zhao     features |= n->host_features;
697da3e8a23SShannon Zhao 
6980cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
6996e790746SPaolo Bonzini 
7006e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n)) {
7010cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_CSUM);
7020cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4);
7030cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6);
7040cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN);
7056e790746SPaolo Bonzini 
7060cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM);
7070cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4);
7080cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6);
7090cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN);
7106e790746SPaolo Bonzini     }
7116e790746SPaolo Bonzini 
7126e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) {
7130cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO);
7140cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO);
7156e790746SPaolo Bonzini     }
7166e790746SPaolo Bonzini 
717ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
7186e790746SPaolo Bonzini         return features;
7196e790746SPaolo Bonzini     }
7202974e916SYuri Benditovich 
721*59079029SYuri Benditovich     virtio_clear_feature(&features, VIRTIO_NET_F_RSS);
72275ebec11SMaxime Coquelin     features = vhost_net_get_features(get_vhost_net(nc->peer), features);
72375ebec11SMaxime Coquelin     vdev->backend_features = features;
72475ebec11SMaxime Coquelin 
72575ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
72675ebec11SMaxime Coquelin             (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) {
72775ebec11SMaxime Coquelin         features |= (1ULL << VIRTIO_NET_F_MTU);
72875ebec11SMaxime Coquelin     }
72975ebec11SMaxime Coquelin 
73075ebec11SMaxime Coquelin     return features;
7316e790746SPaolo Bonzini }
7326e790746SPaolo Bonzini 
733019a3edbSGerd Hoffmann static uint64_t virtio_net_bad_features(VirtIODevice *vdev)
7346e790746SPaolo Bonzini {
735019a3edbSGerd Hoffmann     uint64_t features = 0;
7366e790746SPaolo Bonzini 
7376e790746SPaolo Bonzini     /* Linux kernel 2.6.25.  It understood MAC (as everyone must),
7386e790746SPaolo Bonzini      * but also these: */
7390cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
7400cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_CSUM);
7410cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO4);
7420cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO6);
7430cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_ECN);
7446e790746SPaolo Bonzini 
7456e790746SPaolo Bonzini     return features;
7466e790746SPaolo Bonzini }
7476e790746SPaolo Bonzini 
748644c9858SDmitry Fleytman static void virtio_net_apply_guest_offloads(VirtIONet *n)
749644c9858SDmitry Fleytman {
750ad37bb3bSStefan Hajnoczi     qemu_set_offload(qemu_get_queue(n->nic)->peer,
751644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)),
752644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)),
753644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)),
754644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)),
755644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)));
756644c9858SDmitry Fleytman }
757644c9858SDmitry Fleytman 
758644c9858SDmitry Fleytman static uint64_t virtio_net_guest_offloads_by_features(uint32_t features)
759644c9858SDmitry Fleytman {
760644c9858SDmitry Fleytman     static const uint64_t guest_offloads_mask =
761644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_CSUM) |
762644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO4) |
763644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO6) |
764644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_ECN)  |
765644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_UFO);
766644c9858SDmitry Fleytman 
767644c9858SDmitry Fleytman     return guest_offloads_mask & features;
768644c9858SDmitry Fleytman }
769644c9858SDmitry Fleytman 
770644c9858SDmitry Fleytman static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n)
771644c9858SDmitry Fleytman {
772644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
773644c9858SDmitry Fleytman     return virtio_net_guest_offloads_by_features(vdev->guest_features);
774644c9858SDmitry Fleytman }
775644c9858SDmitry Fleytman 
7769711cd0dSJens Freimann static void failover_add_primary(VirtIONet *n, Error **errp)
7779711cd0dSJens Freimann {
7789711cd0dSJens Freimann     Error *err = NULL;
7799711cd0dSJens Freimann 
780117378bfSJens Freimann     if (n->primary_dev) {
781117378bfSJens Freimann         return;
782117378bfSJens Freimann     }
783117378bfSJens Freimann 
7849711cd0dSJens Freimann     n->primary_device_opts = qemu_opts_find(qemu_find_opts("device"),
7859711cd0dSJens Freimann             n->primary_device_id);
7869711cd0dSJens Freimann     if (n->primary_device_opts) {
7879711cd0dSJens Freimann         n->primary_dev = qdev_device_add(n->primary_device_opts, &err);
7889711cd0dSJens Freimann         if (err) {
7899711cd0dSJens Freimann             qemu_opts_del(n->primary_device_opts);
7909711cd0dSJens Freimann         }
7919711cd0dSJens Freimann         if (n->primary_dev) {
7929711cd0dSJens Freimann             n->primary_bus = n->primary_dev->parent_bus;
7939711cd0dSJens Freimann             if (err) {
7949711cd0dSJens Freimann                 qdev_unplug(n->primary_dev, &err);
7959711cd0dSJens Freimann                 qdev_set_id(n->primary_dev, "");
7969711cd0dSJens Freimann 
7979711cd0dSJens Freimann             }
7989711cd0dSJens Freimann         }
7999711cd0dSJens Freimann     } else {
8009711cd0dSJens Freimann         error_setg(errp, "Primary device not found");
8019711cd0dSJens Freimann         error_append_hint(errp, "Virtio-net failover will not work. Make "
8029711cd0dSJens Freimann             "sure primary device has parameter"
8039711cd0dSJens Freimann             " failover_pair_id=<virtio-net-id>\n");
8049711cd0dSJens Freimann }
8059711cd0dSJens Freimann     if (err) {
8069711cd0dSJens Freimann         error_propagate(errp, err);
8079711cd0dSJens Freimann     }
8089711cd0dSJens Freimann }
8099711cd0dSJens Freimann 
8109711cd0dSJens Freimann static int is_my_primary(void *opaque, QemuOpts *opts, Error **errp)
8119711cd0dSJens Freimann {
8129711cd0dSJens Freimann     VirtIONet *n = opaque;
8139711cd0dSJens Freimann     int ret = 0;
8149711cd0dSJens Freimann 
8159711cd0dSJens Freimann     const char *standby_id = qemu_opt_get(opts, "failover_pair_id");
8169711cd0dSJens Freimann 
8179711cd0dSJens Freimann     if (standby_id != NULL && (g_strcmp0(standby_id, n->netclient_name) == 0)) {
8189711cd0dSJens Freimann         n->primary_device_id = g_strdup(opts->id);
8199711cd0dSJens Freimann         ret = 1;
8209711cd0dSJens Freimann     }
8219711cd0dSJens Freimann 
8229711cd0dSJens Freimann     return ret;
8239711cd0dSJens Freimann }
8249711cd0dSJens Freimann 
8259711cd0dSJens Freimann static DeviceState *virtio_net_find_primary(VirtIONet *n, Error **errp)
8269711cd0dSJens Freimann {
8279711cd0dSJens Freimann     DeviceState *dev = NULL;
8289711cd0dSJens Freimann     Error *err = NULL;
8299711cd0dSJens Freimann 
8309711cd0dSJens Freimann     if (qemu_opts_foreach(qemu_find_opts("device"),
8319711cd0dSJens Freimann                          is_my_primary, n, &err)) {
8329711cd0dSJens Freimann         if (err) {
8339711cd0dSJens Freimann             error_propagate(errp, err);
8349711cd0dSJens Freimann             return NULL;
8359711cd0dSJens Freimann         }
8369711cd0dSJens Freimann         if (n->primary_device_id) {
8379711cd0dSJens Freimann             dev = qdev_find_recursive(sysbus_get_default(),
8389711cd0dSJens Freimann                     n->primary_device_id);
8399711cd0dSJens Freimann         } else {
8409711cd0dSJens Freimann             error_setg(errp, "Primary device id not found");
8419711cd0dSJens Freimann             return NULL;
8429711cd0dSJens Freimann         }
8439711cd0dSJens Freimann     }
8449711cd0dSJens Freimann     return dev;
8459711cd0dSJens Freimann }
8469711cd0dSJens Freimann 
8479711cd0dSJens Freimann 
8489711cd0dSJens Freimann 
8499711cd0dSJens Freimann static DeviceState *virtio_connect_failover_devices(VirtIONet *n,
8509711cd0dSJens Freimann                                                     DeviceState *dev,
8519711cd0dSJens Freimann                                                     Error **errp)
8529711cd0dSJens Freimann {
8539711cd0dSJens Freimann     DeviceState *prim_dev = NULL;
8549711cd0dSJens Freimann     Error *err = NULL;
8559711cd0dSJens Freimann 
8569711cd0dSJens Freimann     prim_dev = virtio_net_find_primary(n, &err);
8579711cd0dSJens Freimann     if (prim_dev) {
8589711cd0dSJens Freimann         n->primary_device_id = g_strdup(prim_dev->id);
8599711cd0dSJens Freimann         n->primary_device_opts = prim_dev->opts;
8609711cd0dSJens Freimann     } else {
8619711cd0dSJens Freimann         if (err) {
8629711cd0dSJens Freimann             error_propagate(errp, err);
8639711cd0dSJens Freimann         }
8649711cd0dSJens Freimann     }
8659711cd0dSJens Freimann 
8669711cd0dSJens Freimann     return prim_dev;
8679711cd0dSJens Freimann }
8689711cd0dSJens Freimann 
869d5aaa1b0SGerd Hoffmann static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
8706e790746SPaolo Bonzini {
87117a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
8729711cd0dSJens Freimann     Error *err = NULL;
8736e790746SPaolo Bonzini     int i;
8746e790746SPaolo Bonzini 
87575ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
87675ebec11SMaxime Coquelin             !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) {
87775ebec11SMaxime Coquelin         features &= ~(1ULL << VIRTIO_NET_F_MTU);
87875ebec11SMaxime Coquelin     }
87975ebec11SMaxime Coquelin 
880ef546f12SCornelia Huck     virtio_net_set_multiqueue(n,
881*59079029SYuri Benditovich                               virtio_has_feature(features, VIRTIO_NET_F_RSS) ||
88295129d6fSCornelia Huck                               virtio_has_feature(features, VIRTIO_NET_F_MQ));
8836e790746SPaolo Bonzini 
884ef546f12SCornelia Huck     virtio_net_set_mrg_rx_bufs(n,
88595129d6fSCornelia Huck                                virtio_has_feature(features,
886bb9d17f8SCornelia Huck                                                   VIRTIO_NET_F_MRG_RXBUF),
88795129d6fSCornelia Huck                                virtio_has_feature(features,
888bb9d17f8SCornelia Huck                                                   VIRTIO_F_VERSION_1));
8896e790746SPaolo Bonzini 
8902974e916SYuri Benditovich     n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
8912974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4);
8922974e916SYuri Benditovich     n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
8932974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6);
8942974e916SYuri Benditovich 
8956e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
896644c9858SDmitry Fleytman         n->curr_guest_offloads =
897644c9858SDmitry Fleytman             virtio_net_guest_offloads_by_features(features);
898644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
8996e790746SPaolo Bonzini     }
9006e790746SPaolo Bonzini 
9016e790746SPaolo Bonzini     for (i = 0;  i < n->max_queues; i++) {
9026e790746SPaolo Bonzini         NetClientState *nc = qemu_get_subqueue(n->nic, i);
9036e790746SPaolo Bonzini 
904ed8b4afeSNikolay Nikolaev         if (!get_vhost_net(nc->peer)) {
9056e790746SPaolo Bonzini             continue;
9066e790746SPaolo Bonzini         }
907ed8b4afeSNikolay Nikolaev         vhost_net_ack_features(get_vhost_net(nc->peer), features);
9086e790746SPaolo Bonzini     }
9090b1eaa88SStefan Fritsch 
91095129d6fSCornelia Huck     if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
9110b1eaa88SStefan Fritsch         memset(n->vlans, 0, MAX_VLAN >> 3);
9120b1eaa88SStefan Fritsch     } else {
9130b1eaa88SStefan Fritsch         memset(n->vlans, 0xff, MAX_VLAN >> 3);
9140b1eaa88SStefan Fritsch     }
9159711cd0dSJens Freimann 
9169711cd0dSJens Freimann     if (virtio_has_feature(features, VIRTIO_NET_F_STANDBY)) {
9179711cd0dSJens Freimann         qapi_event_send_failover_negotiated(n->netclient_name);
9189711cd0dSJens Freimann         atomic_set(&n->primary_should_be_hidden, false);
9199711cd0dSJens Freimann         failover_add_primary(n, &err);
9209711cd0dSJens Freimann         if (err) {
9219711cd0dSJens Freimann             n->primary_dev = virtio_connect_failover_devices(n, n->qdev, &err);
9229711cd0dSJens Freimann             if (err) {
9239711cd0dSJens Freimann                 goto out_err;
9249711cd0dSJens Freimann             }
9259711cd0dSJens Freimann             failover_add_primary(n, &err);
9269711cd0dSJens Freimann             if (err) {
9279711cd0dSJens Freimann                 goto out_err;
9289711cd0dSJens Freimann             }
9299711cd0dSJens Freimann         }
9309711cd0dSJens Freimann     }
9319711cd0dSJens Freimann     return;
9329711cd0dSJens Freimann 
9339711cd0dSJens Freimann out_err:
9349711cd0dSJens Freimann     if (err) {
9359711cd0dSJens Freimann         warn_report_err(err);
9369711cd0dSJens Freimann     }
9376e790746SPaolo Bonzini }
9386e790746SPaolo Bonzini 
9396e790746SPaolo Bonzini static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
9406e790746SPaolo Bonzini                                      struct iovec *iov, unsigned int iov_cnt)
9416e790746SPaolo Bonzini {
9426e790746SPaolo Bonzini     uint8_t on;
9436e790746SPaolo Bonzini     size_t s;
944b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
9456e790746SPaolo Bonzini 
9466e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on));
9476e790746SPaolo Bonzini     if (s != sizeof(on)) {
9486e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9496e790746SPaolo Bonzini     }
9506e790746SPaolo Bonzini 
9516e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) {
9526e790746SPaolo Bonzini         n->promisc = on;
9536e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) {
9546e790746SPaolo Bonzini         n->allmulti = on;
9556e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) {
9566e790746SPaolo Bonzini         n->alluni = on;
9576e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) {
9586e790746SPaolo Bonzini         n->nomulti = on;
9596e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) {
9606e790746SPaolo Bonzini         n->nouni = on;
9616e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) {
9626e790746SPaolo Bonzini         n->nobcast = on;
9636e790746SPaolo Bonzini     } else {
9646e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9656e790746SPaolo Bonzini     }
9666e790746SPaolo Bonzini 
967b1be4280SAmos Kong     rxfilter_notify(nc);
968b1be4280SAmos Kong 
9696e790746SPaolo Bonzini     return VIRTIO_NET_OK;
9706e790746SPaolo Bonzini }
9716e790746SPaolo Bonzini 
972644c9858SDmitry Fleytman static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd,
973644c9858SDmitry Fleytman                                      struct iovec *iov, unsigned int iov_cnt)
974644c9858SDmitry Fleytman {
975644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
976644c9858SDmitry Fleytman     uint64_t offloads;
977644c9858SDmitry Fleytman     size_t s;
978644c9858SDmitry Fleytman 
97995129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
980644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
981644c9858SDmitry Fleytman     }
982644c9858SDmitry Fleytman 
983644c9858SDmitry Fleytman     s = iov_to_buf(iov, iov_cnt, 0, &offloads, sizeof(offloads));
984644c9858SDmitry Fleytman     if (s != sizeof(offloads)) {
985644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
986644c9858SDmitry Fleytman     }
987644c9858SDmitry Fleytman 
988644c9858SDmitry Fleytman     if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) {
989644c9858SDmitry Fleytman         uint64_t supported_offloads;
990644c9858SDmitry Fleytman 
991189ae6bbSJason Wang         offloads = virtio_ldq_p(vdev, &offloads);
992189ae6bbSJason Wang 
993644c9858SDmitry Fleytman         if (!n->has_vnet_hdr) {
994644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
995644c9858SDmitry Fleytman         }
996644c9858SDmitry Fleytman 
9972974e916SYuri Benditovich         n->rsc4_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
9982974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO4);
9992974e916SYuri Benditovich         n->rsc6_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
10002974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO6);
10012974e916SYuri Benditovich         virtio_clear_feature(&offloads, VIRTIO_NET_F_RSC_EXT);
10022974e916SYuri Benditovich 
1003644c9858SDmitry Fleytman         supported_offloads = virtio_net_supported_guest_offloads(n);
1004644c9858SDmitry Fleytman         if (offloads & ~supported_offloads) {
1005644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
1006644c9858SDmitry Fleytman         }
1007644c9858SDmitry Fleytman 
1008644c9858SDmitry Fleytman         n->curr_guest_offloads = offloads;
1009644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
1010644c9858SDmitry Fleytman 
1011644c9858SDmitry Fleytman         return VIRTIO_NET_OK;
1012644c9858SDmitry Fleytman     } else {
1013644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
1014644c9858SDmitry Fleytman     }
1015644c9858SDmitry Fleytman }
1016644c9858SDmitry Fleytman 
10176e790746SPaolo Bonzini static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
10186e790746SPaolo Bonzini                                  struct iovec *iov, unsigned int iov_cnt)
10196e790746SPaolo Bonzini {
10201399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
10216e790746SPaolo Bonzini     struct virtio_net_ctrl_mac mac_data;
10226e790746SPaolo Bonzini     size_t s;
1023b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
10246e790746SPaolo Bonzini 
10256e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {
10266e790746SPaolo Bonzini         if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {
10276e790746SPaolo Bonzini             return VIRTIO_NET_ERR;
10286e790746SPaolo Bonzini         }
10296e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
10306e790746SPaolo Bonzini         assert(s == sizeof(n->mac));
10316e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
1032b1be4280SAmos Kong         rxfilter_notify(nc);
1033b1be4280SAmos Kong 
10346e790746SPaolo Bonzini         return VIRTIO_NET_OK;
10356e790746SPaolo Bonzini     }
10366e790746SPaolo Bonzini 
10376e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) {
10386e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
10396e790746SPaolo Bonzini     }
10406e790746SPaolo Bonzini 
1041cae2e556SAmos Kong     int in_use = 0;
1042cae2e556SAmos Kong     int first_multi = 0;
1043cae2e556SAmos Kong     uint8_t uni_overflow = 0;
1044cae2e556SAmos Kong     uint8_t multi_overflow = 0;
1045cae2e556SAmos Kong     uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
10466e790746SPaolo Bonzini 
10476e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
10486e790746SPaolo Bonzini                    sizeof(mac_data.entries));
10491399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
10506e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
1051b1be4280SAmos Kong         goto error;
10526e790746SPaolo Bonzini     }
10536e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
10546e790746SPaolo Bonzini 
10556e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {
1056b1be4280SAmos Kong         goto error;
10576e790746SPaolo Bonzini     }
10586e790746SPaolo Bonzini 
10596e790746SPaolo Bonzini     if (mac_data.entries <= MAC_TABLE_ENTRIES) {
1060cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, macs,
10616e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
10626e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
1063b1be4280SAmos Kong             goto error;
10646e790746SPaolo Bonzini         }
1065cae2e556SAmos Kong         in_use += mac_data.entries;
10666e790746SPaolo Bonzini     } else {
1067cae2e556SAmos Kong         uni_overflow = 1;
10686e790746SPaolo Bonzini     }
10696e790746SPaolo Bonzini 
10706e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN);
10716e790746SPaolo Bonzini 
1072cae2e556SAmos Kong     first_multi = in_use;
10736e790746SPaolo Bonzini 
10746e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
10756e790746SPaolo Bonzini                    sizeof(mac_data.entries));
10761399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
10776e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
1078b1be4280SAmos Kong         goto error;
10796e790746SPaolo Bonzini     }
10806e790746SPaolo Bonzini 
10816e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
10826e790746SPaolo Bonzini 
10836e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {
1084b1be4280SAmos Kong         goto error;
10856e790746SPaolo Bonzini     }
10866e790746SPaolo Bonzini 
1087edc24385SMichael S. Tsirkin     if (mac_data.entries <= MAC_TABLE_ENTRIES - in_use) {
1088cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN],
10896e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
10906e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
1091b1be4280SAmos Kong             goto error;
10926e790746SPaolo Bonzini         }
1093cae2e556SAmos Kong         in_use += mac_data.entries;
10946e790746SPaolo Bonzini     } else {
1095cae2e556SAmos Kong         multi_overflow = 1;
10966e790746SPaolo Bonzini     }
10976e790746SPaolo Bonzini 
1098cae2e556SAmos Kong     n->mac_table.in_use = in_use;
1099cae2e556SAmos Kong     n->mac_table.first_multi = first_multi;
1100cae2e556SAmos Kong     n->mac_table.uni_overflow = uni_overflow;
1101cae2e556SAmos Kong     n->mac_table.multi_overflow = multi_overflow;
1102cae2e556SAmos Kong     memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN);
1103cae2e556SAmos Kong     g_free(macs);
1104b1be4280SAmos Kong     rxfilter_notify(nc);
1105b1be4280SAmos Kong 
11066e790746SPaolo Bonzini     return VIRTIO_NET_OK;
1107b1be4280SAmos Kong 
1108b1be4280SAmos Kong error:
1109cae2e556SAmos Kong     g_free(macs);
1110b1be4280SAmos Kong     return VIRTIO_NET_ERR;
11116e790746SPaolo Bonzini }
11126e790746SPaolo Bonzini 
11136e790746SPaolo Bonzini static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
11146e790746SPaolo Bonzini                                         struct iovec *iov, unsigned int iov_cnt)
11156e790746SPaolo Bonzini {
11161399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
11176e790746SPaolo Bonzini     uint16_t vid;
11186e790746SPaolo Bonzini     size_t s;
1119b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
11206e790746SPaolo Bonzini 
11216e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid));
11221399c60dSRusty Russell     vid = virtio_lduw_p(vdev, &vid);
11236e790746SPaolo Bonzini     if (s != sizeof(vid)) {
11246e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
11256e790746SPaolo Bonzini     }
11266e790746SPaolo Bonzini 
11276e790746SPaolo Bonzini     if (vid >= MAX_VLAN)
11286e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
11296e790746SPaolo Bonzini 
11306e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
11316e790746SPaolo Bonzini         n->vlans[vid >> 5] |= (1U << (vid & 0x1f));
11326e790746SPaolo Bonzini     else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL)
11336e790746SPaolo Bonzini         n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f));
11346e790746SPaolo Bonzini     else
11356e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
11366e790746SPaolo Bonzini 
1137b1be4280SAmos Kong     rxfilter_notify(nc);
1138b1be4280SAmos Kong 
11396e790746SPaolo Bonzini     return VIRTIO_NET_OK;
11406e790746SPaolo Bonzini }
11416e790746SPaolo Bonzini 
1142f57fcf70SJason Wang static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
1143f57fcf70SJason Wang                                       struct iovec *iov, unsigned int iov_cnt)
1144f57fcf70SJason Wang {
11459d8c6a25SDr. David Alan Gilbert     trace_virtio_net_handle_announce(n->announce_timer.round);
1146f57fcf70SJason Wang     if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK &&
1147f57fcf70SJason Wang         n->status & VIRTIO_NET_S_ANNOUNCE) {
1148f57fcf70SJason Wang         n->status &= ~VIRTIO_NET_S_ANNOUNCE;
11499d8c6a25SDr. David Alan Gilbert         if (n->announce_timer.round) {
11509d8c6a25SDr. David Alan Gilbert             qemu_announce_timer_step(&n->announce_timer);
1151f57fcf70SJason Wang         }
1152f57fcf70SJason Wang         return VIRTIO_NET_OK;
1153f57fcf70SJason Wang     } else {
1154f57fcf70SJason Wang         return VIRTIO_NET_ERR;
1155f57fcf70SJason Wang     }
1156f57fcf70SJason Wang }
1157f57fcf70SJason Wang 
1158*59079029SYuri Benditovich static void virtio_net_disable_rss(VirtIONet *n)
1159*59079029SYuri Benditovich {
1160*59079029SYuri Benditovich     if (n->rss_data.enabled) {
1161*59079029SYuri Benditovich         trace_virtio_net_rss_disable();
1162*59079029SYuri Benditovich     }
1163*59079029SYuri Benditovich     n->rss_data.enabled = false;
1164*59079029SYuri Benditovich }
1165*59079029SYuri Benditovich 
1166*59079029SYuri Benditovich static uint16_t virtio_net_handle_rss(VirtIONet *n,
1167*59079029SYuri Benditovich                                       struct iovec *iov, unsigned int iov_cnt)
1168*59079029SYuri Benditovich {
1169*59079029SYuri Benditovich     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1170*59079029SYuri Benditovich     struct virtio_net_rss_config cfg;
1171*59079029SYuri Benditovich     size_t s, offset = 0, size_get;
1172*59079029SYuri Benditovich     uint16_t queues, i;
1173*59079029SYuri Benditovich     struct {
1174*59079029SYuri Benditovich         uint16_t us;
1175*59079029SYuri Benditovich         uint8_t b;
1176*59079029SYuri Benditovich     } QEMU_PACKED temp;
1177*59079029SYuri Benditovich     const char *err_msg = "";
1178*59079029SYuri Benditovich     uint32_t err_value = 0;
1179*59079029SYuri Benditovich 
1180*59079029SYuri Benditovich     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_RSS)) {
1181*59079029SYuri Benditovich         err_msg = "RSS is not negotiated";
1182*59079029SYuri Benditovich         goto error;
1183*59079029SYuri Benditovich     }
1184*59079029SYuri Benditovich     size_get = offsetof(struct virtio_net_rss_config, indirection_table);
1185*59079029SYuri Benditovich     s = iov_to_buf(iov, iov_cnt, offset, &cfg, size_get);
1186*59079029SYuri Benditovich     if (s != size_get) {
1187*59079029SYuri Benditovich         err_msg = "Short command buffer";
1188*59079029SYuri Benditovich         err_value = (uint32_t)s;
1189*59079029SYuri Benditovich         goto error;
1190*59079029SYuri Benditovich     }
1191*59079029SYuri Benditovich     n->rss_data.hash_types = virtio_ldl_p(vdev, &cfg.hash_types);
1192*59079029SYuri Benditovich     n->rss_data.indirections_len =
1193*59079029SYuri Benditovich         virtio_lduw_p(vdev, &cfg.indirection_table_mask);
1194*59079029SYuri Benditovich     n->rss_data.indirections_len++;
1195*59079029SYuri Benditovich     if (!is_power_of_2(n->rss_data.indirections_len)) {
1196*59079029SYuri Benditovich         err_msg = "Invalid size of indirection table";
1197*59079029SYuri Benditovich         err_value = n->rss_data.indirections_len;
1198*59079029SYuri Benditovich         goto error;
1199*59079029SYuri Benditovich     }
1200*59079029SYuri Benditovich     if (n->rss_data.indirections_len > VIRTIO_NET_RSS_MAX_TABLE_LEN) {
1201*59079029SYuri Benditovich         err_msg = "Too large indirection table";
1202*59079029SYuri Benditovich         err_value = n->rss_data.indirections_len;
1203*59079029SYuri Benditovich         goto error;
1204*59079029SYuri Benditovich     }
1205*59079029SYuri Benditovich     n->rss_data.default_queue =
1206*59079029SYuri Benditovich         virtio_lduw_p(vdev, &cfg.unclassified_queue);
1207*59079029SYuri Benditovich     if (n->rss_data.default_queue >= n->max_queues) {
1208*59079029SYuri Benditovich         err_msg = "Invalid default queue";
1209*59079029SYuri Benditovich         err_value = n->rss_data.default_queue;
1210*59079029SYuri Benditovich         goto error;
1211*59079029SYuri Benditovich     }
1212*59079029SYuri Benditovich     offset += size_get;
1213*59079029SYuri Benditovich     size_get = sizeof(uint16_t) * n->rss_data.indirections_len;
1214*59079029SYuri Benditovich     g_free(n->rss_data.indirections_table);
1215*59079029SYuri Benditovich     n->rss_data.indirections_table = g_malloc(size_get);
1216*59079029SYuri Benditovich     if (!n->rss_data.indirections_table) {
1217*59079029SYuri Benditovich         err_msg = "Can't allocate indirections table";
1218*59079029SYuri Benditovich         err_value = n->rss_data.indirections_len;
1219*59079029SYuri Benditovich         goto error;
1220*59079029SYuri Benditovich     }
1221*59079029SYuri Benditovich     s = iov_to_buf(iov, iov_cnt, offset,
1222*59079029SYuri Benditovich                    n->rss_data.indirections_table, size_get);
1223*59079029SYuri Benditovich     if (s != size_get) {
1224*59079029SYuri Benditovich         err_msg = "Short indirection table buffer";
1225*59079029SYuri Benditovich         err_value = (uint32_t)s;
1226*59079029SYuri Benditovich         goto error;
1227*59079029SYuri Benditovich     }
1228*59079029SYuri Benditovich     for (i = 0; i < n->rss_data.indirections_len; ++i) {
1229*59079029SYuri Benditovich         uint16_t val = n->rss_data.indirections_table[i];
1230*59079029SYuri Benditovich         n->rss_data.indirections_table[i] = virtio_lduw_p(vdev, &val);
1231*59079029SYuri Benditovich     }
1232*59079029SYuri Benditovich     offset += size_get;
1233*59079029SYuri Benditovich     size_get = sizeof(temp);
1234*59079029SYuri Benditovich     s = iov_to_buf(iov, iov_cnt, offset, &temp, size_get);
1235*59079029SYuri Benditovich     if (s != size_get) {
1236*59079029SYuri Benditovich         err_msg = "Can't get queues";
1237*59079029SYuri Benditovich         err_value = (uint32_t)s;
1238*59079029SYuri Benditovich         goto error;
1239*59079029SYuri Benditovich     }
1240*59079029SYuri Benditovich     queues = virtio_lduw_p(vdev, &temp.us);
1241*59079029SYuri Benditovich     if (queues == 0 || queues > n->max_queues) {
1242*59079029SYuri Benditovich         err_msg = "Invalid number of queues";
1243*59079029SYuri Benditovich         err_value = queues;
1244*59079029SYuri Benditovich         goto error;
1245*59079029SYuri Benditovich     }
1246*59079029SYuri Benditovich     if (temp.b > VIRTIO_NET_RSS_MAX_KEY_SIZE) {
1247*59079029SYuri Benditovich         err_msg = "Invalid key size";
1248*59079029SYuri Benditovich         err_value = temp.b;
1249*59079029SYuri Benditovich         goto error;
1250*59079029SYuri Benditovich     }
1251*59079029SYuri Benditovich     if (!temp.b && n->rss_data.hash_types) {
1252*59079029SYuri Benditovich         err_msg = "No key provided";
1253*59079029SYuri Benditovich         err_value = 0;
1254*59079029SYuri Benditovich         goto error;
1255*59079029SYuri Benditovich     }
1256*59079029SYuri Benditovich     if (!temp.b && !n->rss_data.hash_types) {
1257*59079029SYuri Benditovich         virtio_net_disable_rss(n);
1258*59079029SYuri Benditovich         return queues;
1259*59079029SYuri Benditovich     }
1260*59079029SYuri Benditovich     offset += size_get;
1261*59079029SYuri Benditovich     size_get = temp.b;
1262*59079029SYuri Benditovich     s = iov_to_buf(iov, iov_cnt, offset, n->rss_data.key, size_get);
1263*59079029SYuri Benditovich     if (s != size_get) {
1264*59079029SYuri Benditovich         err_msg = "Can get key buffer";
1265*59079029SYuri Benditovich         err_value = (uint32_t)s;
1266*59079029SYuri Benditovich         goto error;
1267*59079029SYuri Benditovich     }
1268*59079029SYuri Benditovich     n->rss_data.enabled = true;
1269*59079029SYuri Benditovich     trace_virtio_net_rss_enable(n->rss_data.hash_types,
1270*59079029SYuri Benditovich                                 n->rss_data.indirections_len,
1271*59079029SYuri Benditovich                                 temp.b);
1272*59079029SYuri Benditovich     return queues;
1273*59079029SYuri Benditovich error:
1274*59079029SYuri Benditovich     trace_virtio_net_rss_error(err_msg, err_value);
1275*59079029SYuri Benditovich     virtio_net_disable_rss(n);
1276*59079029SYuri Benditovich     return 0;
1277*59079029SYuri Benditovich }
1278*59079029SYuri Benditovich 
12796e790746SPaolo Bonzini static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
12806e790746SPaolo Bonzini                                 struct iovec *iov, unsigned int iov_cnt)
12816e790746SPaolo Bonzini {
128217a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
12836e790746SPaolo Bonzini     uint16_t queues;
12846e790746SPaolo Bonzini 
1285*59079029SYuri Benditovich     virtio_net_disable_rss(n);
1286*59079029SYuri Benditovich     if (cmd == VIRTIO_NET_CTRL_MQ_RSS_CONFIG) {
1287*59079029SYuri Benditovich         queues = virtio_net_handle_rss(n, iov, iov_cnt);
1288*59079029SYuri Benditovich     } else if (cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
1289*59079029SYuri Benditovich         struct virtio_net_ctrl_mq mq;
1290*59079029SYuri Benditovich         size_t s;
1291*59079029SYuri Benditovich         if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_MQ)) {
1292*59079029SYuri Benditovich             return VIRTIO_NET_ERR;
1293*59079029SYuri Benditovich         }
12946e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq));
12956e790746SPaolo Bonzini         if (s != sizeof(mq)) {
12966e790746SPaolo Bonzini             return VIRTIO_NET_ERR;
12976e790746SPaolo Bonzini         }
1298*59079029SYuri Benditovich         queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs);
12996e790746SPaolo Bonzini 
1300*59079029SYuri Benditovich     } else {
13016e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
13026e790746SPaolo Bonzini     }
13036e790746SPaolo Bonzini 
13046e790746SPaolo Bonzini     if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
13056e790746SPaolo Bonzini         queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX ||
13066e790746SPaolo Bonzini         queues > n->max_queues ||
13076e790746SPaolo Bonzini         !n->multiqueue) {
13086e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
13096e790746SPaolo Bonzini     }
13106e790746SPaolo Bonzini 
13116e790746SPaolo Bonzini     n->curr_queues = queues;
13126e790746SPaolo Bonzini     /* stop the backend before changing the number of queues to avoid handling a
13136e790746SPaolo Bonzini      * disabled queue */
131417a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
13156e790746SPaolo Bonzini     virtio_net_set_queues(n);
13166e790746SPaolo Bonzini 
13176e790746SPaolo Bonzini     return VIRTIO_NET_OK;
13186e790746SPaolo Bonzini }
1319ba7eadb5SGreg Kurz 
13206e790746SPaolo Bonzini static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
13216e790746SPaolo Bonzini {
132217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
13236e790746SPaolo Bonzini     struct virtio_net_ctrl_hdr ctrl;
13246e790746SPaolo Bonzini     virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
132551b19ebeSPaolo Bonzini     VirtQueueElement *elem;
13266e790746SPaolo Bonzini     size_t s;
1327771b6ed3SJason Wang     struct iovec *iov, *iov2;
13286e790746SPaolo Bonzini     unsigned int iov_cnt;
13296e790746SPaolo Bonzini 
133051b19ebeSPaolo Bonzini     for (;;) {
133151b19ebeSPaolo Bonzini         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
133251b19ebeSPaolo Bonzini         if (!elem) {
133351b19ebeSPaolo Bonzini             break;
133451b19ebeSPaolo Bonzini         }
133551b19ebeSPaolo Bonzini         if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) ||
133651b19ebeSPaolo Bonzini             iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) {
1337ba7eadb5SGreg Kurz             virtio_error(vdev, "virtio-net ctrl missing headers");
1338ba7eadb5SGreg Kurz             virtqueue_detach_element(vq, elem, 0);
1339ba7eadb5SGreg Kurz             g_free(elem);
1340ba7eadb5SGreg Kurz             break;
13416e790746SPaolo Bonzini         }
13426e790746SPaolo Bonzini 
134351b19ebeSPaolo Bonzini         iov_cnt = elem->out_num;
134451b19ebeSPaolo Bonzini         iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num);
13456e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl));
13466e790746SPaolo Bonzini         iov_discard_front(&iov, &iov_cnt, sizeof(ctrl));
13476e790746SPaolo Bonzini         if (s != sizeof(ctrl)) {
13486e790746SPaolo Bonzini             status = VIRTIO_NET_ERR;
13496e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_RX) {
13506e790746SPaolo Bonzini             status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt);
13516e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) {
13526e790746SPaolo Bonzini             status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt);
13536e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) {
13546e790746SPaolo Bonzini             status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt);
1355f57fcf70SJason Wang         } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) {
1356f57fcf70SJason Wang             status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt);
13576e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) {
13586e790746SPaolo Bonzini             status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt);
1359644c9858SDmitry Fleytman         } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) {
1360644c9858SDmitry Fleytman             status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt);
13616e790746SPaolo Bonzini         }
13626e790746SPaolo Bonzini 
136351b19ebeSPaolo Bonzini         s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status));
13646e790746SPaolo Bonzini         assert(s == sizeof(status));
13656e790746SPaolo Bonzini 
136651b19ebeSPaolo Bonzini         virtqueue_push(vq, elem, sizeof(status));
13676e790746SPaolo Bonzini         virtio_notify(vdev, vq);
1368771b6ed3SJason Wang         g_free(iov2);
136951b19ebeSPaolo Bonzini         g_free(elem);
13706e790746SPaolo Bonzini     }
13716e790746SPaolo Bonzini }
13726e790746SPaolo Bonzini 
13736e790746SPaolo Bonzini /* RX */
13746e790746SPaolo Bonzini 
13756e790746SPaolo Bonzini static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
13766e790746SPaolo Bonzini {
137717a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
13786e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(vq));
13796e790746SPaolo Bonzini 
13806e790746SPaolo Bonzini     qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index));
13816e790746SPaolo Bonzini }
13826e790746SPaolo Bonzini 
1383b8c4b67eSPhilippe Mathieu-Daudé static bool virtio_net_can_receive(NetClientState *nc)
13846e790746SPaolo Bonzini {
13856e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
138617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
13876e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
13886e790746SPaolo Bonzini 
138917a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
1390b8c4b67eSPhilippe Mathieu-Daudé         return false;
13916e790746SPaolo Bonzini     }
13926e790746SPaolo Bonzini 
13936e790746SPaolo Bonzini     if (nc->queue_index >= n->curr_queues) {
1394b8c4b67eSPhilippe Mathieu-Daudé         return false;
13956e790746SPaolo Bonzini     }
13966e790746SPaolo Bonzini 
13976e790746SPaolo Bonzini     if (!virtio_queue_ready(q->rx_vq) ||
139817a0ca55SKONRAD Frederic         !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
1399b8c4b67eSPhilippe Mathieu-Daudé         return false;
14006e790746SPaolo Bonzini     }
14016e790746SPaolo Bonzini 
1402b8c4b67eSPhilippe Mathieu-Daudé     return true;
14036e790746SPaolo Bonzini }
14046e790746SPaolo Bonzini 
14056e790746SPaolo Bonzini static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize)
14066e790746SPaolo Bonzini {
14076e790746SPaolo Bonzini     VirtIONet *n = q->n;
14086e790746SPaolo Bonzini     if (virtio_queue_empty(q->rx_vq) ||
14096e790746SPaolo Bonzini         (n->mergeable_rx_bufs &&
14106e790746SPaolo Bonzini          !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
14116e790746SPaolo Bonzini         virtio_queue_set_notification(q->rx_vq, 1);
14126e790746SPaolo Bonzini 
14136e790746SPaolo Bonzini         /* To avoid a race condition where the guest has made some buffers
14146e790746SPaolo Bonzini          * available after the above check but before notification was
14156e790746SPaolo Bonzini          * enabled, check for available buffers again.
14166e790746SPaolo Bonzini          */
14176e790746SPaolo Bonzini         if (virtio_queue_empty(q->rx_vq) ||
14186e790746SPaolo Bonzini             (n->mergeable_rx_bufs &&
14196e790746SPaolo Bonzini              !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
14206e790746SPaolo Bonzini             return 0;
14216e790746SPaolo Bonzini         }
14226e790746SPaolo Bonzini     }
14236e790746SPaolo Bonzini 
14246e790746SPaolo Bonzini     virtio_queue_set_notification(q->rx_vq, 0);
14256e790746SPaolo Bonzini     return 1;
14266e790746SPaolo Bonzini }
14276e790746SPaolo Bonzini 
14281399c60dSRusty Russell static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr)
1429032a74a1SCédric Le Goater {
14301399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->hdr_len);
14311399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->gso_size);
14321399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_start);
14331399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_offset);
1434032a74a1SCédric Le Goater }
1435032a74a1SCédric Le Goater 
14366e790746SPaolo Bonzini /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so
14376e790746SPaolo Bonzini  * it never finds out that the packets don't have valid checksums.  This
14386e790746SPaolo Bonzini  * causes dhclient to get upset.  Fedora's carried a patch for ages to
14396e790746SPaolo Bonzini  * fix this with Xen but it hasn't appeared in an upstream release of
14406e790746SPaolo Bonzini  * dhclient yet.
14416e790746SPaolo Bonzini  *
14426e790746SPaolo Bonzini  * To avoid breaking existing guests, we catch udp packets and add
14436e790746SPaolo Bonzini  * checksums.  This is terrible but it's better than hacking the guest
14446e790746SPaolo Bonzini  * kernels.
14456e790746SPaolo Bonzini  *
14466e790746SPaolo Bonzini  * N.B. if we introduce a zero-copy API, this operation is no longer free so
14476e790746SPaolo Bonzini  * we should provide a mechanism to disable it to avoid polluting the host
14486e790746SPaolo Bonzini  * cache.
14496e790746SPaolo Bonzini  */
14506e790746SPaolo Bonzini static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
14516e790746SPaolo Bonzini                                         uint8_t *buf, size_t size)
14526e790746SPaolo Bonzini {
14536e790746SPaolo Bonzini     if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
14546e790746SPaolo Bonzini         (size > 27 && size < 1500) && /* normal sized MTU */
14556e790746SPaolo Bonzini         (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
14566e790746SPaolo Bonzini         (buf[23] == 17) && /* ip.protocol == UDP */
14576e790746SPaolo Bonzini         (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
14586e790746SPaolo Bonzini         net_checksum_calculate(buf, size);
14596e790746SPaolo Bonzini         hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
14606e790746SPaolo Bonzini     }
14616e790746SPaolo Bonzini }
14626e790746SPaolo Bonzini 
14636e790746SPaolo Bonzini static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt,
14646e790746SPaolo Bonzini                            const void *buf, size_t size)
14656e790746SPaolo Bonzini {
14666e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
14676e790746SPaolo Bonzini         /* FIXME this cast is evil */
14686e790746SPaolo Bonzini         void *wbuf = (void *)buf;
14696e790746SPaolo Bonzini         work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len,
14706e790746SPaolo Bonzini                                     size - n->host_hdr_len);
14711bfa316cSGreg Kurz 
14721bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
14731399c60dSRusty Russell             virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf);
14741bfa316cSGreg Kurz         }
14756e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr));
14766e790746SPaolo Bonzini     } else {
14776e790746SPaolo Bonzini         struct virtio_net_hdr hdr = {
14786e790746SPaolo Bonzini             .flags = 0,
14796e790746SPaolo Bonzini             .gso_type = VIRTIO_NET_HDR_GSO_NONE
14806e790746SPaolo Bonzini         };
14816e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr);
14826e790746SPaolo Bonzini     }
14836e790746SPaolo Bonzini }
14846e790746SPaolo Bonzini 
14856e790746SPaolo Bonzini static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
14866e790746SPaolo Bonzini {
14876e790746SPaolo Bonzini     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
14886e790746SPaolo Bonzini     static const uint8_t vlan[] = {0x81, 0x00};
14896e790746SPaolo Bonzini     uint8_t *ptr = (uint8_t *)buf;
14906e790746SPaolo Bonzini     int i;
14916e790746SPaolo Bonzini 
14926e790746SPaolo Bonzini     if (n->promisc)
14936e790746SPaolo Bonzini         return 1;
14946e790746SPaolo Bonzini 
14956e790746SPaolo Bonzini     ptr += n->host_hdr_len;
14966e790746SPaolo Bonzini 
14976e790746SPaolo Bonzini     if (!memcmp(&ptr[12], vlan, sizeof(vlan))) {
14987542d3e7SPeter Maydell         int vid = lduw_be_p(ptr + 14) & 0xfff;
14996e790746SPaolo Bonzini         if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f))))
15006e790746SPaolo Bonzini             return 0;
15016e790746SPaolo Bonzini     }
15026e790746SPaolo Bonzini 
15036e790746SPaolo Bonzini     if (ptr[0] & 1) { // multicast
15046e790746SPaolo Bonzini         if (!memcmp(ptr, bcast, sizeof(bcast))) {
15056e790746SPaolo Bonzini             return !n->nobcast;
15066e790746SPaolo Bonzini         } else if (n->nomulti) {
15076e790746SPaolo Bonzini             return 0;
15086e790746SPaolo Bonzini         } else if (n->allmulti || n->mac_table.multi_overflow) {
15096e790746SPaolo Bonzini             return 1;
15106e790746SPaolo Bonzini         }
15116e790746SPaolo Bonzini 
15126e790746SPaolo Bonzini         for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
15136e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
15146e790746SPaolo Bonzini                 return 1;
15156e790746SPaolo Bonzini             }
15166e790746SPaolo Bonzini         }
15176e790746SPaolo Bonzini     } else { // unicast
15186e790746SPaolo Bonzini         if (n->nouni) {
15196e790746SPaolo Bonzini             return 0;
15206e790746SPaolo Bonzini         } else if (n->alluni || n->mac_table.uni_overflow) {
15216e790746SPaolo Bonzini             return 1;
15226e790746SPaolo Bonzini         } else if (!memcmp(ptr, n->mac, ETH_ALEN)) {
15236e790746SPaolo Bonzini             return 1;
15246e790746SPaolo Bonzini         }
15256e790746SPaolo Bonzini 
15266e790746SPaolo Bonzini         for (i = 0; i < n->mac_table.first_multi; i++) {
15276e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
15286e790746SPaolo Bonzini                 return 1;
15296e790746SPaolo Bonzini             }
15306e790746SPaolo Bonzini         }
15316e790746SPaolo Bonzini     }
15326e790746SPaolo Bonzini 
15336e790746SPaolo Bonzini     return 0;
15346e790746SPaolo Bonzini }
15356e790746SPaolo Bonzini 
153697cd965cSPaolo Bonzini static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
153797cd965cSPaolo Bonzini                                       size_t size)
15386e790746SPaolo Bonzini {
15396e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
15406e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
154117a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
15426e790746SPaolo Bonzini     struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
15436e790746SPaolo Bonzini     struct virtio_net_hdr_mrg_rxbuf mhdr;
15446e790746SPaolo Bonzini     unsigned mhdr_cnt = 0;
15456e790746SPaolo Bonzini     size_t offset, i, guest_offset;
15466e790746SPaolo Bonzini 
15476e790746SPaolo Bonzini     if (!virtio_net_can_receive(nc)) {
15486e790746SPaolo Bonzini         return -1;
15496e790746SPaolo Bonzini     }
15506e790746SPaolo Bonzini 
15516e790746SPaolo Bonzini     /* hdr_len refers to the header we supply to the guest */
15526e790746SPaolo Bonzini     if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) {
15536e790746SPaolo Bonzini         return 0;
15546e790746SPaolo Bonzini     }
15556e790746SPaolo Bonzini 
15566e790746SPaolo Bonzini     if (!receive_filter(n, buf, size))
15576e790746SPaolo Bonzini         return size;
15586e790746SPaolo Bonzini 
15596e790746SPaolo Bonzini     offset = i = 0;
15606e790746SPaolo Bonzini 
15616e790746SPaolo Bonzini     while (offset < size) {
156251b19ebeSPaolo Bonzini         VirtQueueElement *elem;
15636e790746SPaolo Bonzini         int len, total;
156451b19ebeSPaolo Bonzini         const struct iovec *sg;
15656e790746SPaolo Bonzini 
15666e790746SPaolo Bonzini         total = 0;
15676e790746SPaolo Bonzini 
156851b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement));
156951b19ebeSPaolo Bonzini         if (!elem) {
1570ba10b9c0SGreg Kurz             if (i) {
1571ba10b9c0SGreg Kurz                 virtio_error(vdev, "virtio-net unexpected empty queue: "
15726e790746SPaolo Bonzini                              "i %zd mergeable %d offset %zd, size %zd, "
1573019a3edbSGerd Hoffmann                              "guest hdr len %zd, host hdr len %zd "
1574019a3edbSGerd Hoffmann                              "guest features 0x%" PRIx64,
15756e790746SPaolo Bonzini                              i, n->mergeable_rx_bufs, offset, size,
1576019a3edbSGerd Hoffmann                              n->guest_hdr_len, n->host_hdr_len,
1577019a3edbSGerd Hoffmann                              vdev->guest_features);
1578ba10b9c0SGreg Kurz             }
1579ba10b9c0SGreg Kurz             return -1;
15806e790746SPaolo Bonzini         }
15816e790746SPaolo Bonzini 
158251b19ebeSPaolo Bonzini         if (elem->in_num < 1) {
1583ba10b9c0SGreg Kurz             virtio_error(vdev,
1584ba10b9c0SGreg Kurz                          "virtio-net receive queue contains no in buffers");
1585ba10b9c0SGreg Kurz             virtqueue_detach_element(q->rx_vq, elem, 0);
1586ba10b9c0SGreg Kurz             g_free(elem);
1587ba10b9c0SGreg Kurz             return -1;
15886e790746SPaolo Bonzini         }
15896e790746SPaolo Bonzini 
159051b19ebeSPaolo Bonzini         sg = elem->in_sg;
15916e790746SPaolo Bonzini         if (i == 0) {
15926e790746SPaolo Bonzini             assert(offset == 0);
15936e790746SPaolo Bonzini             if (n->mergeable_rx_bufs) {
15946e790746SPaolo Bonzini                 mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg),
159551b19ebeSPaolo Bonzini                                     sg, elem->in_num,
15966e790746SPaolo Bonzini                                     offsetof(typeof(mhdr), num_buffers),
15976e790746SPaolo Bonzini                                     sizeof(mhdr.num_buffers));
15986e790746SPaolo Bonzini             }
15996e790746SPaolo Bonzini 
160051b19ebeSPaolo Bonzini             receive_header(n, sg, elem->in_num, buf, size);
16016e790746SPaolo Bonzini             offset = n->host_hdr_len;
16026e790746SPaolo Bonzini             total += n->guest_hdr_len;
16036e790746SPaolo Bonzini             guest_offset = n->guest_hdr_len;
16046e790746SPaolo Bonzini         } else {
16056e790746SPaolo Bonzini             guest_offset = 0;
16066e790746SPaolo Bonzini         }
16076e790746SPaolo Bonzini 
16086e790746SPaolo Bonzini         /* copy in packet.  ugh */
160951b19ebeSPaolo Bonzini         len = iov_from_buf(sg, elem->in_num, guest_offset,
16106e790746SPaolo Bonzini                            buf + offset, size - offset);
16116e790746SPaolo Bonzini         total += len;
16126e790746SPaolo Bonzini         offset += len;
16136e790746SPaolo Bonzini         /* If buffers can't be merged, at this point we
16146e790746SPaolo Bonzini          * must have consumed the complete packet.
16156e790746SPaolo Bonzini          * Otherwise, drop it. */
16166e790746SPaolo Bonzini         if (!n->mergeable_rx_bufs && offset < size) {
161727e57efeSLadi Prosek             virtqueue_unpop(q->rx_vq, elem, total);
161851b19ebeSPaolo Bonzini             g_free(elem);
16196e790746SPaolo Bonzini             return size;
16206e790746SPaolo Bonzini         }
16216e790746SPaolo Bonzini 
16226e790746SPaolo Bonzini         /* signal other side */
162351b19ebeSPaolo Bonzini         virtqueue_fill(q->rx_vq, elem, total, i++);
162451b19ebeSPaolo Bonzini         g_free(elem);
16256e790746SPaolo Bonzini     }
16266e790746SPaolo Bonzini 
16276e790746SPaolo Bonzini     if (mhdr_cnt) {
16281399c60dSRusty Russell         virtio_stw_p(vdev, &mhdr.num_buffers, i);
16296e790746SPaolo Bonzini         iov_from_buf(mhdr_sg, mhdr_cnt,
16306e790746SPaolo Bonzini                      0,
16316e790746SPaolo Bonzini                      &mhdr.num_buffers, sizeof mhdr.num_buffers);
16326e790746SPaolo Bonzini     }
16336e790746SPaolo Bonzini 
16346e790746SPaolo Bonzini     virtqueue_flush(q->rx_vq, i);
163517a0ca55SKONRAD Frederic     virtio_notify(vdev, q->rx_vq);
16366e790746SPaolo Bonzini 
16376e790746SPaolo Bonzini     return size;
16386e790746SPaolo Bonzini }
16396e790746SPaolo Bonzini 
16402974e916SYuri Benditovich static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf,
164197cd965cSPaolo Bonzini                                   size_t size)
164297cd965cSPaolo Bonzini {
1643068ddfa9SDr. David Alan Gilbert     RCU_READ_LOCK_GUARD();
164497cd965cSPaolo Bonzini 
1645068ddfa9SDr. David Alan Gilbert     return virtio_net_receive_rcu(nc, buf, size);
164697cd965cSPaolo Bonzini }
164797cd965cSPaolo Bonzini 
16482974e916SYuri Benditovich static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
16492974e916SYuri Benditovich                                          const uint8_t *buf,
16502974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
16512974e916SYuri Benditovich {
16522974e916SYuri Benditovich     uint16_t ip_hdrlen;
16532974e916SYuri Benditovich     struct ip_header *ip;
16542974e916SYuri Benditovich 
16552974e916SYuri Benditovich     ip = (struct ip_header *)(buf + chain->n->guest_hdr_len
16562974e916SYuri Benditovich                               + sizeof(struct eth_header));
16572974e916SYuri Benditovich     unit->ip = (void *)ip;
16582974e916SYuri Benditovich     ip_hdrlen = (ip->ip_ver_len & 0xF) << 2;
16592974e916SYuri Benditovich     unit->ip_plen = &ip->ip_len;
16602974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen);
16612974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
16622974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen;
16632974e916SYuri Benditovich }
16642974e916SYuri Benditovich 
16652974e916SYuri Benditovich static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
16662974e916SYuri Benditovich                                          const uint8_t *buf,
16672974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
16682974e916SYuri Benditovich {
16692974e916SYuri Benditovich     struct ip6_header *ip6;
16702974e916SYuri Benditovich 
16712974e916SYuri Benditovich     ip6 = (struct ip6_header *)(buf + chain->n->guest_hdr_len
16722974e916SYuri Benditovich                                  + sizeof(struct eth_header));
16732974e916SYuri Benditovich     unit->ip = ip6;
16742974e916SYuri Benditovich     unit->ip_plen = &(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
167578ee6bd0SPhilippe Mathieu-Daudé     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip)
16762974e916SYuri Benditovich                                         + sizeof(struct ip6_header));
16772974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
16782974e916SYuri Benditovich 
16792974e916SYuri Benditovich     /* There is a difference between payload lenght in ipv4 and v6,
16802974e916SYuri Benditovich        ip header is excluded in ipv6 */
16812974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen;
16822974e916SYuri Benditovich }
16832974e916SYuri Benditovich 
16842974e916SYuri Benditovich static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain,
16852974e916SYuri Benditovich                                        VirtioNetRscSeg *seg)
16862974e916SYuri Benditovich {
16872974e916SYuri Benditovich     int ret;
16882974e916SYuri Benditovich     struct virtio_net_hdr *h;
16892974e916SYuri Benditovich 
16902974e916SYuri Benditovich     h = (struct virtio_net_hdr *)seg->buf;
16912974e916SYuri Benditovich     h->flags = 0;
16922974e916SYuri Benditovich     h->gso_type = VIRTIO_NET_HDR_GSO_NONE;
16932974e916SYuri Benditovich 
16942974e916SYuri Benditovich     if (seg->is_coalesced) {
16952974e916SYuri Benditovich         *virtio_net_rsc_ext_num_packets(h) = seg->packets;
16962974e916SYuri Benditovich         *virtio_net_rsc_ext_num_dupacks(h) = seg->dup_ack;
16972974e916SYuri Benditovich         h->flags = VIRTIO_NET_HDR_F_RSC_INFO;
16982974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
16992974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
17002974e916SYuri Benditovich         } else {
17012974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
17022974e916SYuri Benditovich         }
17032974e916SYuri Benditovich     }
17042974e916SYuri Benditovich 
17052974e916SYuri Benditovich     ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size);
17062974e916SYuri Benditovich     QTAILQ_REMOVE(&chain->buffers, seg, next);
17072974e916SYuri Benditovich     g_free(seg->buf);
17082974e916SYuri Benditovich     g_free(seg);
17092974e916SYuri Benditovich 
17102974e916SYuri Benditovich     return ret;
17112974e916SYuri Benditovich }
17122974e916SYuri Benditovich 
17132974e916SYuri Benditovich static void virtio_net_rsc_purge(void *opq)
17142974e916SYuri Benditovich {
17152974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn;
17162974e916SYuri Benditovich     VirtioNetRscChain *chain = (VirtioNetRscChain *)opq;
17172974e916SYuri Benditovich 
17182974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn) {
17192974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
17202974e916SYuri Benditovich             chain->stat.purge_failed++;
17212974e916SYuri Benditovich             continue;
17222974e916SYuri Benditovich         }
17232974e916SYuri Benditovich     }
17242974e916SYuri Benditovich 
17252974e916SYuri Benditovich     chain->stat.timer++;
17262974e916SYuri Benditovich     if (!QTAILQ_EMPTY(&chain->buffers)) {
17272974e916SYuri Benditovich         timer_mod(chain->drain_timer,
17282974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
17292974e916SYuri Benditovich     }
17302974e916SYuri Benditovich }
17312974e916SYuri Benditovich 
17322974e916SYuri Benditovich static void virtio_net_rsc_cleanup(VirtIONet *n)
17332974e916SYuri Benditovich {
17342974e916SYuri Benditovich     VirtioNetRscChain *chain, *rn_chain;
17352974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn_seg;
17362974e916SYuri Benditovich 
17372974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(chain, &n->rsc_chains, next, rn_chain) {
17382974e916SYuri Benditovich         QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn_seg) {
17392974e916SYuri Benditovich             QTAILQ_REMOVE(&chain->buffers, seg, next);
17402974e916SYuri Benditovich             g_free(seg->buf);
17412974e916SYuri Benditovich             g_free(seg);
17422974e916SYuri Benditovich         }
17432974e916SYuri Benditovich 
17442974e916SYuri Benditovich         timer_del(chain->drain_timer);
17452974e916SYuri Benditovich         timer_free(chain->drain_timer);
17462974e916SYuri Benditovich         QTAILQ_REMOVE(&n->rsc_chains, chain, next);
17472974e916SYuri Benditovich         g_free(chain);
17482974e916SYuri Benditovich     }
17492974e916SYuri Benditovich }
17502974e916SYuri Benditovich 
17512974e916SYuri Benditovich static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain,
17522974e916SYuri Benditovich                                      NetClientState *nc,
17532974e916SYuri Benditovich                                      const uint8_t *buf, size_t size)
17542974e916SYuri Benditovich {
17552974e916SYuri Benditovich     uint16_t hdr_len;
17562974e916SYuri Benditovich     VirtioNetRscSeg *seg;
17572974e916SYuri Benditovich 
17582974e916SYuri Benditovich     hdr_len = chain->n->guest_hdr_len;
17592974e916SYuri Benditovich     seg = g_malloc(sizeof(VirtioNetRscSeg));
17602974e916SYuri Benditovich     seg->buf = g_malloc(hdr_len + sizeof(struct eth_header)
17612974e916SYuri Benditovich         + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD);
17622974e916SYuri Benditovich     memcpy(seg->buf, buf, size);
17632974e916SYuri Benditovich     seg->size = size;
17642974e916SYuri Benditovich     seg->packets = 1;
17652974e916SYuri Benditovich     seg->dup_ack = 0;
17662974e916SYuri Benditovich     seg->is_coalesced = 0;
17672974e916SYuri Benditovich     seg->nc = nc;
17682974e916SYuri Benditovich 
17692974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&chain->buffers, seg, next);
17702974e916SYuri Benditovich     chain->stat.cache++;
17712974e916SYuri Benditovich 
17722974e916SYuri Benditovich     switch (chain->proto) {
17732974e916SYuri Benditovich     case ETH_P_IP:
17742974e916SYuri Benditovich         virtio_net_rsc_extract_unit4(chain, seg->buf, &seg->unit);
17752974e916SYuri Benditovich         break;
17762974e916SYuri Benditovich     case ETH_P_IPV6:
17772974e916SYuri Benditovich         virtio_net_rsc_extract_unit6(chain, seg->buf, &seg->unit);
17782974e916SYuri Benditovich         break;
17792974e916SYuri Benditovich     default:
17802974e916SYuri Benditovich         g_assert_not_reached();
17812974e916SYuri Benditovich     }
17822974e916SYuri Benditovich }
17832974e916SYuri Benditovich 
17842974e916SYuri Benditovich static int32_t virtio_net_rsc_handle_ack(VirtioNetRscChain *chain,
17852974e916SYuri Benditovich                                          VirtioNetRscSeg *seg,
17862974e916SYuri Benditovich                                          const uint8_t *buf,
17872974e916SYuri Benditovich                                          struct tcp_header *n_tcp,
17882974e916SYuri Benditovich                                          struct tcp_header *o_tcp)
17892974e916SYuri Benditovich {
17902974e916SYuri Benditovich     uint32_t nack, oack;
17912974e916SYuri Benditovich     uint16_t nwin, owin;
17922974e916SYuri Benditovich 
17932974e916SYuri Benditovich     nack = htonl(n_tcp->th_ack);
17942974e916SYuri Benditovich     nwin = htons(n_tcp->th_win);
17952974e916SYuri Benditovich     oack = htonl(o_tcp->th_ack);
17962974e916SYuri Benditovich     owin = htons(o_tcp->th_win);
17972974e916SYuri Benditovich 
17982974e916SYuri Benditovich     if ((nack - oack) >= VIRTIO_NET_MAX_TCP_PAYLOAD) {
17992974e916SYuri Benditovich         chain->stat.ack_out_of_win++;
18002974e916SYuri Benditovich         return RSC_FINAL;
18012974e916SYuri Benditovich     } else if (nack == oack) {
18022974e916SYuri Benditovich         /* duplicated ack or window probe */
18032974e916SYuri Benditovich         if (nwin == owin) {
18042974e916SYuri Benditovich             /* duplicated ack, add dup ack count due to whql test up to 1 */
18052974e916SYuri Benditovich             chain->stat.dup_ack++;
18062974e916SYuri Benditovich             return RSC_FINAL;
18072974e916SYuri Benditovich         } else {
18082974e916SYuri Benditovich             /* Coalesce window update */
18092974e916SYuri Benditovich             o_tcp->th_win = n_tcp->th_win;
18102974e916SYuri Benditovich             chain->stat.win_update++;
18112974e916SYuri Benditovich             return RSC_COALESCE;
18122974e916SYuri Benditovich         }
18132974e916SYuri Benditovich     } else {
18142974e916SYuri Benditovich         /* pure ack, go to 'C', finalize*/
18152974e916SYuri Benditovich         chain->stat.pure_ack++;
18162974e916SYuri Benditovich         return RSC_FINAL;
18172974e916SYuri Benditovich     }
18182974e916SYuri Benditovich }
18192974e916SYuri Benditovich 
18202974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain,
18212974e916SYuri Benditovich                                             VirtioNetRscSeg *seg,
18222974e916SYuri Benditovich                                             const uint8_t *buf,
18232974e916SYuri Benditovich                                             VirtioNetRscUnit *n_unit)
18242974e916SYuri Benditovich {
18252974e916SYuri Benditovich     void *data;
18262974e916SYuri Benditovich     uint16_t o_ip_len;
18272974e916SYuri Benditovich     uint32_t nseq, oseq;
18282974e916SYuri Benditovich     VirtioNetRscUnit *o_unit;
18292974e916SYuri Benditovich 
18302974e916SYuri Benditovich     o_unit = &seg->unit;
18312974e916SYuri Benditovich     o_ip_len = htons(*o_unit->ip_plen);
18322974e916SYuri Benditovich     nseq = htonl(n_unit->tcp->th_seq);
18332974e916SYuri Benditovich     oseq = htonl(o_unit->tcp->th_seq);
18342974e916SYuri Benditovich 
18352974e916SYuri Benditovich     /* out of order or retransmitted. */
18362974e916SYuri Benditovich     if ((nseq - oseq) > VIRTIO_NET_MAX_TCP_PAYLOAD) {
18372974e916SYuri Benditovich         chain->stat.data_out_of_win++;
18382974e916SYuri Benditovich         return RSC_FINAL;
18392974e916SYuri Benditovich     }
18402974e916SYuri Benditovich 
18412974e916SYuri Benditovich     data = ((uint8_t *)n_unit->tcp) + n_unit->tcp_hdrlen;
18422974e916SYuri Benditovich     if (nseq == oseq) {
18432974e916SYuri Benditovich         if ((o_unit->payload == 0) && n_unit->payload) {
18442974e916SYuri Benditovich             /* From no payload to payload, normal case, not a dup ack or etc */
18452974e916SYuri Benditovich             chain->stat.data_after_pure_ack++;
18462974e916SYuri Benditovich             goto coalesce;
18472974e916SYuri Benditovich         } else {
18482974e916SYuri Benditovich             return virtio_net_rsc_handle_ack(chain, seg, buf,
18492974e916SYuri Benditovich                                              n_unit->tcp, o_unit->tcp);
18502974e916SYuri Benditovich         }
18512974e916SYuri Benditovich     } else if ((nseq - oseq) != o_unit->payload) {
18522974e916SYuri Benditovich         /* Not a consistent packet, out of order */
18532974e916SYuri Benditovich         chain->stat.data_out_of_order++;
18542974e916SYuri Benditovich         return RSC_FINAL;
18552974e916SYuri Benditovich     } else {
18562974e916SYuri Benditovich coalesce:
18572974e916SYuri Benditovich         if ((o_ip_len + n_unit->payload) > chain->max_payload) {
18582974e916SYuri Benditovich             chain->stat.over_size++;
18592974e916SYuri Benditovich             return RSC_FINAL;
18602974e916SYuri Benditovich         }
18612974e916SYuri Benditovich 
18622974e916SYuri Benditovich         /* Here comes the right data, the payload length in v4/v6 is different,
18632974e916SYuri Benditovich            so use the field value to update and record the new data len */
18642974e916SYuri Benditovich         o_unit->payload += n_unit->payload; /* update new data len */
18652974e916SYuri Benditovich 
18662974e916SYuri Benditovich         /* update field in ip header */
18672974e916SYuri Benditovich         *o_unit->ip_plen = htons(o_ip_len + n_unit->payload);
18682974e916SYuri Benditovich 
18692974e916SYuri Benditovich         /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced
18702974e916SYuri Benditovich            for windows guest, while this may change the behavior for linux
18712974e916SYuri Benditovich            guest (only if it uses RSC feature). */
18722974e916SYuri Benditovich         o_unit->tcp->th_offset_flags = n_unit->tcp->th_offset_flags;
18732974e916SYuri Benditovich 
18742974e916SYuri Benditovich         o_unit->tcp->th_ack = n_unit->tcp->th_ack;
18752974e916SYuri Benditovich         o_unit->tcp->th_win = n_unit->tcp->th_win;
18762974e916SYuri Benditovich 
18772974e916SYuri Benditovich         memmove(seg->buf + seg->size, data, n_unit->payload);
18782974e916SYuri Benditovich         seg->size += n_unit->payload;
18792974e916SYuri Benditovich         seg->packets++;
18802974e916SYuri Benditovich         chain->stat.coalesced++;
18812974e916SYuri Benditovich         return RSC_COALESCE;
18822974e916SYuri Benditovich     }
18832974e916SYuri Benditovich }
18842974e916SYuri Benditovich 
18852974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce4(VirtioNetRscChain *chain,
18862974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
18872974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
18882974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
18892974e916SYuri Benditovich {
18902974e916SYuri Benditovich     struct ip_header *ip1, *ip2;
18912974e916SYuri Benditovich 
18922974e916SYuri Benditovich     ip1 = (struct ip_header *)(unit->ip);
18932974e916SYuri Benditovich     ip2 = (struct ip_header *)(seg->unit.ip);
18942974e916SYuri Benditovich     if ((ip1->ip_src ^ ip2->ip_src) || (ip1->ip_dst ^ ip2->ip_dst)
18952974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
18962974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
18972974e916SYuri Benditovich         chain->stat.no_match++;
18982974e916SYuri Benditovich         return RSC_NO_MATCH;
18992974e916SYuri Benditovich     }
19002974e916SYuri Benditovich 
19012974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
19022974e916SYuri Benditovich }
19032974e916SYuri Benditovich 
19042974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce6(VirtioNetRscChain *chain,
19052974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
19062974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
19072974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
19082974e916SYuri Benditovich {
19092974e916SYuri Benditovich     struct ip6_header *ip1, *ip2;
19102974e916SYuri Benditovich 
19112974e916SYuri Benditovich     ip1 = (struct ip6_header *)(unit->ip);
19122974e916SYuri Benditovich     ip2 = (struct ip6_header *)(seg->unit.ip);
19132974e916SYuri Benditovich     if (memcmp(&ip1->ip6_src, &ip2->ip6_src, sizeof(struct in6_address))
19142974e916SYuri Benditovich         || memcmp(&ip1->ip6_dst, &ip2->ip6_dst, sizeof(struct in6_address))
19152974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
19162974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
19172974e916SYuri Benditovich             chain->stat.no_match++;
19182974e916SYuri Benditovich             return RSC_NO_MATCH;
19192974e916SYuri Benditovich     }
19202974e916SYuri Benditovich 
19212974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
19222974e916SYuri Benditovich }
19232974e916SYuri Benditovich 
19242974e916SYuri Benditovich /* Packets with 'SYN' should bypass, other flag should be sent after drain
19252974e916SYuri Benditovich  * to prevent out of order */
19262974e916SYuri Benditovich static int virtio_net_rsc_tcp_ctrl_check(VirtioNetRscChain *chain,
19272974e916SYuri Benditovich                                          struct tcp_header *tcp)
19282974e916SYuri Benditovich {
19292974e916SYuri Benditovich     uint16_t tcp_hdr;
19302974e916SYuri Benditovich     uint16_t tcp_flag;
19312974e916SYuri Benditovich 
19322974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags);
19332974e916SYuri Benditovich     tcp_hdr = (tcp_flag & VIRTIO_NET_TCP_HDR_LENGTH) >> 10;
19342974e916SYuri Benditovich     tcp_flag &= VIRTIO_NET_TCP_FLAG;
19352974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags) & 0x3F;
19362974e916SYuri Benditovich     if (tcp_flag & TH_SYN) {
19372974e916SYuri Benditovich         chain->stat.tcp_syn++;
19382974e916SYuri Benditovich         return RSC_BYPASS;
19392974e916SYuri Benditovich     }
19402974e916SYuri Benditovich 
19412974e916SYuri Benditovich     if (tcp_flag & (TH_FIN | TH_URG | TH_RST | TH_ECE | TH_CWR)) {
19422974e916SYuri Benditovich         chain->stat.tcp_ctrl_drain++;
19432974e916SYuri Benditovich         return RSC_FINAL;
19442974e916SYuri Benditovich     }
19452974e916SYuri Benditovich 
19462974e916SYuri Benditovich     if (tcp_hdr > sizeof(struct tcp_header)) {
19472974e916SYuri Benditovich         chain->stat.tcp_all_opt++;
19482974e916SYuri Benditovich         return RSC_FINAL;
19492974e916SYuri Benditovich     }
19502974e916SYuri Benditovich 
19512974e916SYuri Benditovich     return RSC_CANDIDATE;
19522974e916SYuri Benditovich }
19532974e916SYuri Benditovich 
19542974e916SYuri Benditovich static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain,
19552974e916SYuri Benditovich                                          NetClientState *nc,
19562974e916SYuri Benditovich                                          const uint8_t *buf, size_t size,
19572974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
19582974e916SYuri Benditovich {
19592974e916SYuri Benditovich     int ret;
19602974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
19612974e916SYuri Benditovich 
19622974e916SYuri Benditovich     if (QTAILQ_EMPTY(&chain->buffers)) {
19632974e916SYuri Benditovich         chain->stat.empty_cache++;
19642974e916SYuri Benditovich         virtio_net_rsc_cache_buf(chain, nc, buf, size);
19652974e916SYuri Benditovich         timer_mod(chain->drain_timer,
19662974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
19672974e916SYuri Benditovich         return size;
19682974e916SYuri Benditovich     }
19692974e916SYuri Benditovich 
19702974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
19712974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
19722974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce4(chain, seg, buf, size, unit);
19732974e916SYuri Benditovich         } else {
19742974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce6(chain, seg, buf, size, unit);
19752974e916SYuri Benditovich         }
19762974e916SYuri Benditovich 
19772974e916SYuri Benditovich         if (ret == RSC_FINAL) {
19782974e916SYuri Benditovich             if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
19792974e916SYuri Benditovich                 /* Send failed */
19802974e916SYuri Benditovich                 chain->stat.final_failed++;
19812974e916SYuri Benditovich                 return 0;
19822974e916SYuri Benditovich             }
19832974e916SYuri Benditovich 
19842974e916SYuri Benditovich             /* Send current packet */
19852974e916SYuri Benditovich             return virtio_net_do_receive(nc, buf, size);
19862974e916SYuri Benditovich         } else if (ret == RSC_NO_MATCH) {
19872974e916SYuri Benditovich             continue;
19882974e916SYuri Benditovich         } else {
19892974e916SYuri Benditovich             /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */
19902974e916SYuri Benditovich             seg->is_coalesced = 1;
19912974e916SYuri Benditovich             return size;
19922974e916SYuri Benditovich         }
19932974e916SYuri Benditovich     }
19942974e916SYuri Benditovich 
19952974e916SYuri Benditovich     chain->stat.no_match_cache++;
19962974e916SYuri Benditovich     virtio_net_rsc_cache_buf(chain, nc, buf, size);
19972974e916SYuri Benditovich     return size;
19982974e916SYuri Benditovich }
19992974e916SYuri Benditovich 
20002974e916SYuri Benditovich /* Drain a connection data, this is to avoid out of order segments */
20012974e916SYuri Benditovich static size_t virtio_net_rsc_drain_flow(VirtioNetRscChain *chain,
20022974e916SYuri Benditovich                                         NetClientState *nc,
20032974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
20042974e916SYuri Benditovich                                         uint16_t ip_start, uint16_t ip_size,
20052974e916SYuri Benditovich                                         uint16_t tcp_port)
20062974e916SYuri Benditovich {
20072974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
20082974e916SYuri Benditovich     uint32_t ppair1, ppair2;
20092974e916SYuri Benditovich 
20102974e916SYuri Benditovich     ppair1 = *(uint32_t *)(buf + tcp_port);
20112974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
20122974e916SYuri Benditovich         ppair2 = *(uint32_t *)(seg->buf + tcp_port);
20132974e916SYuri Benditovich         if (memcmp(buf + ip_start, seg->buf + ip_start, ip_size)
20142974e916SYuri Benditovich             || (ppair1 != ppair2)) {
20152974e916SYuri Benditovich             continue;
20162974e916SYuri Benditovich         }
20172974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
20182974e916SYuri Benditovich             chain->stat.drain_failed++;
20192974e916SYuri Benditovich         }
20202974e916SYuri Benditovich 
20212974e916SYuri Benditovich         break;
20222974e916SYuri Benditovich     }
20232974e916SYuri Benditovich 
20242974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
20252974e916SYuri Benditovich }
20262974e916SYuri Benditovich 
20272974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check4(VirtioNetRscChain *chain,
20282974e916SYuri Benditovich                                             struct ip_header *ip,
20292974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
20302974e916SYuri Benditovich {
20312974e916SYuri Benditovich     uint16_t ip_len;
20322974e916SYuri Benditovich 
20332974e916SYuri Benditovich     /* Not an ipv4 packet */
20342974e916SYuri Benditovich     if (((ip->ip_ver_len & 0xF0) >> 4) != IP_HEADER_VERSION_4) {
20352974e916SYuri Benditovich         chain->stat.ip_option++;
20362974e916SYuri Benditovich         return RSC_BYPASS;
20372974e916SYuri Benditovich     }
20382974e916SYuri Benditovich 
20392974e916SYuri Benditovich     /* Don't handle packets with ip option */
20402974e916SYuri Benditovich     if ((ip->ip_ver_len & 0xF) != VIRTIO_NET_IP4_HEADER_LENGTH) {
20412974e916SYuri Benditovich         chain->stat.ip_option++;
20422974e916SYuri Benditovich         return RSC_BYPASS;
20432974e916SYuri Benditovich     }
20442974e916SYuri Benditovich 
20452974e916SYuri Benditovich     if (ip->ip_p != IPPROTO_TCP) {
20462974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
20472974e916SYuri Benditovich         return RSC_BYPASS;
20482974e916SYuri Benditovich     }
20492974e916SYuri Benditovich 
20502974e916SYuri Benditovich     /* Don't handle packets with ip fragment */
20512974e916SYuri Benditovich     if (!(htons(ip->ip_off) & IP_DF)) {
20522974e916SYuri Benditovich         chain->stat.ip_frag++;
20532974e916SYuri Benditovich         return RSC_BYPASS;
20542974e916SYuri Benditovich     }
20552974e916SYuri Benditovich 
20562974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
20572974e916SYuri Benditovich     if (IPTOS_ECN(ip->ip_tos)) {
20582974e916SYuri Benditovich         chain->stat.ip_ecn++;
20592974e916SYuri Benditovich         return RSC_BYPASS;
20602974e916SYuri Benditovich     }
20612974e916SYuri Benditovich 
20622974e916SYuri Benditovich     ip_len = htons(ip->ip_len);
20632974e916SYuri Benditovich     if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header))
20642974e916SYuri Benditovich         || ip_len > (size - chain->n->guest_hdr_len -
20652974e916SYuri Benditovich                      sizeof(struct eth_header))) {
20662974e916SYuri Benditovich         chain->stat.ip_hacked++;
20672974e916SYuri Benditovich         return RSC_BYPASS;
20682974e916SYuri Benditovich     }
20692974e916SYuri Benditovich 
20702974e916SYuri Benditovich     return RSC_CANDIDATE;
20712974e916SYuri Benditovich }
20722974e916SYuri Benditovich 
20732974e916SYuri Benditovich static size_t virtio_net_rsc_receive4(VirtioNetRscChain *chain,
20742974e916SYuri Benditovich                                       NetClientState *nc,
20752974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
20762974e916SYuri Benditovich {
20772974e916SYuri Benditovich     int32_t ret;
20782974e916SYuri Benditovich     uint16_t hdr_len;
20792974e916SYuri Benditovich     VirtioNetRscUnit unit;
20802974e916SYuri Benditovich 
20812974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
20822974e916SYuri Benditovich 
20832974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header)
20842974e916SYuri Benditovich         + sizeof(struct tcp_header))) {
20852974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
20862974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
20872974e916SYuri Benditovich     }
20882974e916SYuri Benditovich 
20892974e916SYuri Benditovich     virtio_net_rsc_extract_unit4(chain, buf, &unit);
20902974e916SYuri Benditovich     if (virtio_net_rsc_sanity_check4(chain, unit.ip, buf, size)
20912974e916SYuri Benditovich         != RSC_CANDIDATE) {
20922974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
20932974e916SYuri Benditovich     }
20942974e916SYuri Benditovich 
20952974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
20962974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
20972974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
20982974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
20992974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
21002974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 12),
21012974e916SYuri Benditovich                 VIRTIO_NET_IP4_ADDR_SIZE,
21022974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header));
21032974e916SYuri Benditovich     }
21042974e916SYuri Benditovich 
21052974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
21062974e916SYuri Benditovich }
21072974e916SYuri Benditovich 
21082974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check6(VirtioNetRscChain *chain,
21092974e916SYuri Benditovich                                             struct ip6_header *ip6,
21102974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
21112974e916SYuri Benditovich {
21122974e916SYuri Benditovich     uint16_t ip_len;
21132974e916SYuri Benditovich 
21142974e916SYuri Benditovich     if (((ip6->ip6_ctlun.ip6_un1.ip6_un1_flow & 0xF0) >> 4)
21152974e916SYuri Benditovich         != IP_HEADER_VERSION_6) {
21162974e916SYuri Benditovich         return RSC_BYPASS;
21172974e916SYuri Benditovich     }
21182974e916SYuri Benditovich 
21192974e916SYuri Benditovich     /* Both option and protocol is checked in this */
21202974e916SYuri Benditovich     if (ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) {
21212974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
21222974e916SYuri Benditovich         return RSC_BYPASS;
21232974e916SYuri Benditovich     }
21242974e916SYuri Benditovich 
21252974e916SYuri Benditovich     ip_len = htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
21262974e916SYuri Benditovich     if (ip_len < sizeof(struct tcp_header) ||
21272974e916SYuri Benditovich         ip_len > (size - chain->n->guest_hdr_len - sizeof(struct eth_header)
21282974e916SYuri Benditovich                   - sizeof(struct ip6_header))) {
21292974e916SYuri Benditovich         chain->stat.ip_hacked++;
21302974e916SYuri Benditovich         return RSC_BYPASS;
21312974e916SYuri Benditovich     }
21322974e916SYuri Benditovich 
21332974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
21342974e916SYuri Benditovich     if (IP6_ECN(ip6->ip6_ctlun.ip6_un3.ip6_un3_ecn)) {
21352974e916SYuri Benditovich         chain->stat.ip_ecn++;
21362974e916SYuri Benditovich         return RSC_BYPASS;
21372974e916SYuri Benditovich     }
21382974e916SYuri Benditovich 
21392974e916SYuri Benditovich     return RSC_CANDIDATE;
21402974e916SYuri Benditovich }
21412974e916SYuri Benditovich 
21422974e916SYuri Benditovich static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc,
21432974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
21442974e916SYuri Benditovich {
21452974e916SYuri Benditovich     int32_t ret;
21462974e916SYuri Benditovich     uint16_t hdr_len;
21472974e916SYuri Benditovich     VirtioNetRscChain *chain;
21482974e916SYuri Benditovich     VirtioNetRscUnit unit;
21492974e916SYuri Benditovich 
21502974e916SYuri Benditovich     chain = (VirtioNetRscChain *)opq;
21512974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
21522974e916SYuri Benditovich 
21532974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header)
21542974e916SYuri Benditovich         + sizeof(tcp_header))) {
21552974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
21562974e916SYuri Benditovich     }
21572974e916SYuri Benditovich 
21582974e916SYuri Benditovich     virtio_net_rsc_extract_unit6(chain, buf, &unit);
21592974e916SYuri Benditovich     if (RSC_CANDIDATE != virtio_net_rsc_sanity_check6(chain,
21602974e916SYuri Benditovich                                                  unit.ip, buf, size)) {
21612974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
21622974e916SYuri Benditovich     }
21632974e916SYuri Benditovich 
21642974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
21652974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
21662974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
21672974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
21682974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
21692974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 8),
21702974e916SYuri Benditovich                 VIRTIO_NET_IP6_ADDR_SIZE,
21712974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header)
21722974e916SYuri Benditovich                 + sizeof(struct ip6_header));
21732974e916SYuri Benditovich     }
21742974e916SYuri Benditovich 
21752974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
21762974e916SYuri Benditovich }
21772974e916SYuri Benditovich 
21782974e916SYuri Benditovich static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n,
21792974e916SYuri Benditovich                                                       NetClientState *nc,
21802974e916SYuri Benditovich                                                       uint16_t proto)
21812974e916SYuri Benditovich {
21822974e916SYuri Benditovich     VirtioNetRscChain *chain;
21832974e916SYuri Benditovich 
21842974e916SYuri Benditovich     if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) {
21852974e916SYuri Benditovich         return NULL;
21862974e916SYuri Benditovich     }
21872974e916SYuri Benditovich 
21882974e916SYuri Benditovich     QTAILQ_FOREACH(chain, &n->rsc_chains, next) {
21892974e916SYuri Benditovich         if (chain->proto == proto) {
21902974e916SYuri Benditovich             return chain;
21912974e916SYuri Benditovich         }
21922974e916SYuri Benditovich     }
21932974e916SYuri Benditovich 
21942974e916SYuri Benditovich     chain = g_malloc(sizeof(*chain));
21952974e916SYuri Benditovich     chain->n = n;
21962974e916SYuri Benditovich     chain->proto = proto;
21972974e916SYuri Benditovich     if (proto == (uint16_t)ETH_P_IP) {
21982974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP4_PAYLOAD;
21992974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
22002974e916SYuri Benditovich     } else {
22012974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD;
22022974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
22032974e916SYuri Benditovich     }
22042974e916SYuri Benditovich     chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST,
22052974e916SYuri Benditovich                                       virtio_net_rsc_purge, chain);
22062974e916SYuri Benditovich     memset(&chain->stat, 0, sizeof(chain->stat));
22072974e916SYuri Benditovich 
22082974e916SYuri Benditovich     QTAILQ_INIT(&chain->buffers);
22092974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next);
22102974e916SYuri Benditovich 
22112974e916SYuri Benditovich     return chain;
22122974e916SYuri Benditovich }
22132974e916SYuri Benditovich 
22142974e916SYuri Benditovich static ssize_t virtio_net_rsc_receive(NetClientState *nc,
22152974e916SYuri Benditovich                                       const uint8_t *buf,
22162974e916SYuri Benditovich                                       size_t size)
22172974e916SYuri Benditovich {
22182974e916SYuri Benditovich     uint16_t proto;
22192974e916SYuri Benditovich     VirtioNetRscChain *chain;
22202974e916SYuri Benditovich     struct eth_header *eth;
22212974e916SYuri Benditovich     VirtIONet *n;
22222974e916SYuri Benditovich 
22232974e916SYuri Benditovich     n = qemu_get_nic_opaque(nc);
22242974e916SYuri Benditovich     if (size < (n->host_hdr_len + sizeof(struct eth_header))) {
22252974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
22262974e916SYuri Benditovich     }
22272974e916SYuri Benditovich 
22282974e916SYuri Benditovich     eth = (struct eth_header *)(buf + n->guest_hdr_len);
22292974e916SYuri Benditovich     proto = htons(eth->h_proto);
22302974e916SYuri Benditovich 
22312974e916SYuri Benditovich     chain = virtio_net_rsc_lookup_chain(n, nc, proto);
22322974e916SYuri Benditovich     if (chain) {
22332974e916SYuri Benditovich         chain->stat.received++;
22342974e916SYuri Benditovich         if (proto == (uint16_t)ETH_P_IP && n->rsc4_enabled) {
22352974e916SYuri Benditovich             return virtio_net_rsc_receive4(chain, nc, buf, size);
22362974e916SYuri Benditovich         } else if (proto == (uint16_t)ETH_P_IPV6 && n->rsc6_enabled) {
22372974e916SYuri Benditovich             return virtio_net_rsc_receive6(chain, nc, buf, size);
22382974e916SYuri Benditovich         }
22392974e916SYuri Benditovich     }
22402974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
22412974e916SYuri Benditovich }
22422974e916SYuri Benditovich 
22432974e916SYuri Benditovich static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf,
22442974e916SYuri Benditovich                                   size_t size)
22452974e916SYuri Benditovich {
22462974e916SYuri Benditovich     VirtIONet *n = qemu_get_nic_opaque(nc);
22472974e916SYuri Benditovich     if ((n->rsc4_enabled || n->rsc6_enabled)) {
22482974e916SYuri Benditovich         return virtio_net_rsc_receive(nc, buf, size);
22492974e916SYuri Benditovich     } else {
22502974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
22512974e916SYuri Benditovich     }
22522974e916SYuri Benditovich }
22532974e916SYuri Benditovich 
22546e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q);
22556e790746SPaolo Bonzini 
22566e790746SPaolo Bonzini static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
22576e790746SPaolo Bonzini {
22586e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
22596e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
226017a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
22616e790746SPaolo Bonzini 
226251b19ebeSPaolo Bonzini     virtqueue_push(q->tx_vq, q->async_tx.elem, 0);
226317a0ca55SKONRAD Frederic     virtio_notify(vdev, q->tx_vq);
22646e790746SPaolo Bonzini 
226551b19ebeSPaolo Bonzini     g_free(q->async_tx.elem);
226651b19ebeSPaolo Bonzini     q->async_tx.elem = NULL;
22676e790746SPaolo Bonzini 
22686e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
22696e790746SPaolo Bonzini     virtio_net_flush_tx(q);
22706e790746SPaolo Bonzini }
22716e790746SPaolo Bonzini 
22726e790746SPaolo Bonzini /* TX */
22736e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
22746e790746SPaolo Bonzini {
22756e790746SPaolo Bonzini     VirtIONet *n = q->n;
227617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
227751b19ebeSPaolo Bonzini     VirtQueueElement *elem;
22786e790746SPaolo Bonzini     int32_t num_packets = 0;
22796e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(q->tx_vq));
228017a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
22816e790746SPaolo Bonzini         return num_packets;
22826e790746SPaolo Bonzini     }
22836e790746SPaolo Bonzini 
228451b19ebeSPaolo Bonzini     if (q->async_tx.elem) {
22856e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
22866e790746SPaolo Bonzini         return num_packets;
22876e790746SPaolo Bonzini     }
22886e790746SPaolo Bonzini 
228951b19ebeSPaolo Bonzini     for (;;) {
2290bd89dd98SJason Wang         ssize_t ret;
229151b19ebeSPaolo Bonzini         unsigned int out_num;
229251b19ebeSPaolo Bonzini         struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
2293feb93f36SJason Wang         struct virtio_net_hdr_mrg_rxbuf mhdr;
22946e790746SPaolo Bonzini 
229551b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
229651b19ebeSPaolo Bonzini         if (!elem) {
229751b19ebeSPaolo Bonzini             break;
229851b19ebeSPaolo Bonzini         }
229951b19ebeSPaolo Bonzini 
230051b19ebeSPaolo Bonzini         out_num = elem->out_num;
230151b19ebeSPaolo Bonzini         out_sg = elem->out_sg;
23026e790746SPaolo Bonzini         if (out_num < 1) {
2303fa5e56c2SGreg Kurz             virtio_error(vdev, "virtio-net header not in first element");
2304fa5e56c2SGreg Kurz             virtqueue_detach_element(q->tx_vq, elem, 0);
2305fa5e56c2SGreg Kurz             g_free(elem);
2306fa5e56c2SGreg Kurz             return -EINVAL;
23076e790746SPaolo Bonzini         }
23086e790746SPaolo Bonzini 
2309032a74a1SCédric Le Goater         if (n->has_vnet_hdr) {
2310feb93f36SJason Wang             if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
2311feb93f36SJason Wang                 n->guest_hdr_len) {
2312fa5e56c2SGreg Kurz                 virtio_error(vdev, "virtio-net header incorrect");
2313fa5e56c2SGreg Kurz                 virtqueue_detach_element(q->tx_vq, elem, 0);
2314fa5e56c2SGreg Kurz                 g_free(elem);
2315fa5e56c2SGreg Kurz                 return -EINVAL;
2316032a74a1SCédric Le Goater             }
23171bfa316cSGreg Kurz             if (n->needs_vnet_hdr_swap) {
2318feb93f36SJason Wang                 virtio_net_hdr_swap(vdev, (void *) &mhdr);
2319feb93f36SJason Wang                 sg2[0].iov_base = &mhdr;
2320feb93f36SJason Wang                 sg2[0].iov_len = n->guest_hdr_len;
2321feb93f36SJason Wang                 out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
2322feb93f36SJason Wang                                    out_sg, out_num,
2323feb93f36SJason Wang                                    n->guest_hdr_len, -1);
2324feb93f36SJason Wang                 if (out_num == VIRTQUEUE_MAX_SIZE) {
2325feb93f36SJason Wang                     goto drop;
2326032a74a1SCédric Le Goater                 }
2327feb93f36SJason Wang                 out_num += 1;
2328feb93f36SJason Wang                 out_sg = sg2;
2329feb93f36SJason Wang             }
2330feb93f36SJason Wang         }
23316e790746SPaolo Bonzini         /*
23326e790746SPaolo Bonzini          * If host wants to see the guest header as is, we can
23336e790746SPaolo Bonzini          * pass it on unchanged. Otherwise, copy just the parts
23346e790746SPaolo Bonzini          * that host is interested in.
23356e790746SPaolo Bonzini          */
23366e790746SPaolo Bonzini         assert(n->host_hdr_len <= n->guest_hdr_len);
23376e790746SPaolo Bonzini         if (n->host_hdr_len != n->guest_hdr_len) {
23386e790746SPaolo Bonzini             unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg),
23396e790746SPaolo Bonzini                                        out_sg, out_num,
23406e790746SPaolo Bonzini                                        0, n->host_hdr_len);
23416e790746SPaolo Bonzini             sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num,
23426e790746SPaolo Bonzini                              out_sg, out_num,
23436e790746SPaolo Bonzini                              n->guest_hdr_len, -1);
23446e790746SPaolo Bonzini             out_num = sg_num;
23456e790746SPaolo Bonzini             out_sg = sg;
23466e790746SPaolo Bonzini         }
23476e790746SPaolo Bonzini 
23486e790746SPaolo Bonzini         ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
23496e790746SPaolo Bonzini                                       out_sg, out_num, virtio_net_tx_complete);
23506e790746SPaolo Bonzini         if (ret == 0) {
23516e790746SPaolo Bonzini             virtio_queue_set_notification(q->tx_vq, 0);
23526e790746SPaolo Bonzini             q->async_tx.elem = elem;
23536e790746SPaolo Bonzini             return -EBUSY;
23546e790746SPaolo Bonzini         }
23556e790746SPaolo Bonzini 
2356feb93f36SJason Wang drop:
235751b19ebeSPaolo Bonzini         virtqueue_push(q->tx_vq, elem, 0);
235817a0ca55SKONRAD Frederic         virtio_notify(vdev, q->tx_vq);
235951b19ebeSPaolo Bonzini         g_free(elem);
23606e790746SPaolo Bonzini 
23616e790746SPaolo Bonzini         if (++num_packets >= n->tx_burst) {
23626e790746SPaolo Bonzini             break;
23636e790746SPaolo Bonzini         }
23646e790746SPaolo Bonzini     }
23656e790746SPaolo Bonzini     return num_packets;
23666e790746SPaolo Bonzini }
23676e790746SPaolo Bonzini 
23686e790746SPaolo Bonzini static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
23696e790746SPaolo Bonzini {
237017a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
23716e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
23726e790746SPaolo Bonzini 
2373283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2374283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2375283e2c2aSYuri Benditovich         return;
2376283e2c2aSYuri Benditovich     }
2377283e2c2aSYuri Benditovich 
23786e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
237917a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
23806e790746SPaolo Bonzini         q->tx_waiting = 1;
23816e790746SPaolo Bonzini         return;
23826e790746SPaolo Bonzini     }
23836e790746SPaolo Bonzini 
23846e790746SPaolo Bonzini     if (q->tx_waiting) {
23856e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 1);
2386bc72ad67SAlex Bligh         timer_del(q->tx_timer);
23876e790746SPaolo Bonzini         q->tx_waiting = 0;
2388fa5e56c2SGreg Kurz         if (virtio_net_flush_tx(q) == -EINVAL) {
2389fa5e56c2SGreg Kurz             return;
2390fa5e56c2SGreg Kurz         }
23916e790746SPaolo Bonzini     } else {
2392bc72ad67SAlex Bligh         timer_mod(q->tx_timer,
2393bc72ad67SAlex Bligh                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
23946e790746SPaolo Bonzini         q->tx_waiting = 1;
23956e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 0);
23966e790746SPaolo Bonzini     }
23976e790746SPaolo Bonzini }
23986e790746SPaolo Bonzini 
23996e790746SPaolo Bonzini static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
24006e790746SPaolo Bonzini {
240117a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
24026e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
24036e790746SPaolo Bonzini 
2404283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2405283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2406283e2c2aSYuri Benditovich         return;
2407283e2c2aSYuri Benditovich     }
2408283e2c2aSYuri Benditovich 
24096e790746SPaolo Bonzini     if (unlikely(q->tx_waiting)) {
24106e790746SPaolo Bonzini         return;
24116e790746SPaolo Bonzini     }
24126e790746SPaolo Bonzini     q->tx_waiting = 1;
24136e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
241417a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
24156e790746SPaolo Bonzini         return;
24166e790746SPaolo Bonzini     }
24176e790746SPaolo Bonzini     virtio_queue_set_notification(vq, 0);
24186e790746SPaolo Bonzini     qemu_bh_schedule(q->tx_bh);
24196e790746SPaolo Bonzini }
24206e790746SPaolo Bonzini 
24216e790746SPaolo Bonzini static void virtio_net_tx_timer(void *opaque)
24226e790746SPaolo Bonzini {
24236e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
24246e790746SPaolo Bonzini     VirtIONet *n = q->n;
242517a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2426e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2427e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2428e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2429e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2430e8bcf842SMichael S. Tsirkin         return;
2431e8bcf842SMichael S. Tsirkin     }
24326e790746SPaolo Bonzini 
24336e790746SPaolo Bonzini     q->tx_waiting = 0;
24346e790746SPaolo Bonzini 
24356e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
243617a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
24376e790746SPaolo Bonzini         return;
243817a0ca55SKONRAD Frederic     }
24396e790746SPaolo Bonzini 
24406e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
24416e790746SPaolo Bonzini     virtio_net_flush_tx(q);
24426e790746SPaolo Bonzini }
24436e790746SPaolo Bonzini 
24446e790746SPaolo Bonzini static void virtio_net_tx_bh(void *opaque)
24456e790746SPaolo Bonzini {
24466e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
24476e790746SPaolo Bonzini     VirtIONet *n = q->n;
244817a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
24496e790746SPaolo Bonzini     int32_t ret;
24506e790746SPaolo Bonzini 
2451e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2452e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2453e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2454e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2455e8bcf842SMichael S. Tsirkin         return;
2456e8bcf842SMichael S. Tsirkin     }
24576e790746SPaolo Bonzini 
24586e790746SPaolo Bonzini     q->tx_waiting = 0;
24596e790746SPaolo Bonzini 
24606e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
246117a0ca55SKONRAD Frederic     if (unlikely(!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) {
24626e790746SPaolo Bonzini         return;
246317a0ca55SKONRAD Frederic     }
24646e790746SPaolo Bonzini 
24656e790746SPaolo Bonzini     ret = virtio_net_flush_tx(q);
2466fa5e56c2SGreg Kurz     if (ret == -EBUSY || ret == -EINVAL) {
2467fa5e56c2SGreg Kurz         return; /* Notification re-enable handled by tx_complete or device
2468fa5e56c2SGreg Kurz                  * broken */
24696e790746SPaolo Bonzini     }
24706e790746SPaolo Bonzini 
24716e790746SPaolo Bonzini     /* If we flush a full burst of packets, assume there are
24726e790746SPaolo Bonzini      * more coming and immediately reschedule */
24736e790746SPaolo Bonzini     if (ret >= n->tx_burst) {
24746e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
24756e790746SPaolo Bonzini         q->tx_waiting = 1;
24766e790746SPaolo Bonzini         return;
24776e790746SPaolo Bonzini     }
24786e790746SPaolo Bonzini 
24796e790746SPaolo Bonzini     /* If less than a full burst, re-enable notification and flush
24806e790746SPaolo Bonzini      * anything that may have come in while we weren't looking.  If
24816e790746SPaolo Bonzini      * we find something, assume the guest is still active and reschedule */
24826e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
2483fa5e56c2SGreg Kurz     ret = virtio_net_flush_tx(q);
2484fa5e56c2SGreg Kurz     if (ret == -EINVAL) {
2485fa5e56c2SGreg Kurz         return;
2486fa5e56c2SGreg Kurz     } else if (ret > 0) {
24876e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
24886e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
24896e790746SPaolo Bonzini         q->tx_waiting = 1;
24906e790746SPaolo Bonzini     }
24916e790746SPaolo Bonzini }
24926e790746SPaolo Bonzini 
2493f9d6dbf0SWen Congyang static void virtio_net_add_queue(VirtIONet *n, int index)
2494f9d6dbf0SWen Congyang {
2495f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2496f9d6dbf0SWen Congyang 
24971c0fbfa3SMichael S. Tsirkin     n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size,
24981c0fbfa3SMichael S. Tsirkin                                            virtio_net_handle_rx);
24999b02e161SWei Wang 
2500f9d6dbf0SWen Congyang     if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) {
2501f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
25029b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
25039b02e161SWei Wang                              virtio_net_handle_tx_timer);
2504f9d6dbf0SWen Congyang         n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
2505f9d6dbf0SWen Congyang                                               virtio_net_tx_timer,
2506f9d6dbf0SWen Congyang                                               &n->vqs[index]);
2507f9d6dbf0SWen Congyang     } else {
2508f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
25099b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
25109b02e161SWei Wang                              virtio_net_handle_tx_bh);
2511f9d6dbf0SWen Congyang         n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]);
2512f9d6dbf0SWen Congyang     }
2513f9d6dbf0SWen Congyang 
2514f9d6dbf0SWen Congyang     n->vqs[index].tx_waiting = 0;
2515f9d6dbf0SWen Congyang     n->vqs[index].n = n;
2516f9d6dbf0SWen Congyang }
2517f9d6dbf0SWen Congyang 
2518f9d6dbf0SWen Congyang static void virtio_net_del_queue(VirtIONet *n, int index)
2519f9d6dbf0SWen Congyang {
2520f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2521f9d6dbf0SWen Congyang     VirtIONetQueue *q = &n->vqs[index];
2522f9d6dbf0SWen Congyang     NetClientState *nc = qemu_get_subqueue(n->nic, index);
2523f9d6dbf0SWen Congyang 
2524f9d6dbf0SWen Congyang     qemu_purge_queued_packets(nc);
2525f9d6dbf0SWen Congyang 
2526f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2);
2527f9d6dbf0SWen Congyang     if (q->tx_timer) {
2528f9d6dbf0SWen Congyang         timer_del(q->tx_timer);
2529f9d6dbf0SWen Congyang         timer_free(q->tx_timer);
2530f989c30cSYunjian Wang         q->tx_timer = NULL;
2531f9d6dbf0SWen Congyang     } else {
2532f9d6dbf0SWen Congyang         qemu_bh_delete(q->tx_bh);
2533f989c30cSYunjian Wang         q->tx_bh = NULL;
2534f9d6dbf0SWen Congyang     }
2535f989c30cSYunjian Wang     q->tx_waiting = 0;
2536f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2 + 1);
2537f9d6dbf0SWen Congyang }
2538f9d6dbf0SWen Congyang 
2539f9d6dbf0SWen Congyang static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues)
2540f9d6dbf0SWen Congyang {
2541f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2542f9d6dbf0SWen Congyang     int old_num_queues = virtio_get_num_queues(vdev);
2543f9d6dbf0SWen Congyang     int new_num_queues = new_max_queues * 2 + 1;
2544f9d6dbf0SWen Congyang     int i;
2545f9d6dbf0SWen Congyang 
2546f9d6dbf0SWen Congyang     assert(old_num_queues >= 3);
2547f9d6dbf0SWen Congyang     assert(old_num_queues % 2 == 1);
2548f9d6dbf0SWen Congyang 
2549f9d6dbf0SWen Congyang     if (old_num_queues == new_num_queues) {
2550f9d6dbf0SWen Congyang         return;
2551f9d6dbf0SWen Congyang     }
2552f9d6dbf0SWen Congyang 
2553f9d6dbf0SWen Congyang     /*
2554f9d6dbf0SWen Congyang      * We always need to remove and add ctrl vq if
2555f9d6dbf0SWen Congyang      * old_num_queues != new_num_queues. Remove ctrl_vq first,
255620f86a75SYuval Shaia      * and then we only enter one of the following two loops.
2557f9d6dbf0SWen Congyang      */
2558f9d6dbf0SWen Congyang     virtio_del_queue(vdev, old_num_queues - 1);
2559f9d6dbf0SWen Congyang 
2560f9d6dbf0SWen Congyang     for (i = new_num_queues - 1; i < old_num_queues - 1; i += 2) {
2561f9d6dbf0SWen Congyang         /* new_num_queues < old_num_queues */
2562f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i / 2);
2563f9d6dbf0SWen Congyang     }
2564f9d6dbf0SWen Congyang 
2565f9d6dbf0SWen Congyang     for (i = old_num_queues - 1; i < new_num_queues - 1; i += 2) {
2566f9d6dbf0SWen Congyang         /* new_num_queues > old_num_queues */
2567f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i / 2);
2568f9d6dbf0SWen Congyang     }
2569f9d6dbf0SWen Congyang 
2570f9d6dbf0SWen Congyang     /* add ctrl_vq last */
2571f9d6dbf0SWen Congyang     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
2572f9d6dbf0SWen Congyang }
2573f9d6dbf0SWen Congyang 
2574ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
25756e790746SPaolo Bonzini {
2576f9d6dbf0SWen Congyang     int max = multiqueue ? n->max_queues : 1;
2577f9d6dbf0SWen Congyang 
25786e790746SPaolo Bonzini     n->multiqueue = multiqueue;
2579f9d6dbf0SWen Congyang     virtio_net_change_num_queues(n, max);
25806e790746SPaolo Bonzini 
25816e790746SPaolo Bonzini     virtio_net_set_queues(n);
25826e790746SPaolo Bonzini }
25836e790746SPaolo Bonzini 
2584982b78c5SDr. David Alan Gilbert static int virtio_net_post_load_device(void *opaque, int version_id)
2585037dab2fSGreg Kurz {
2586982b78c5SDr. David Alan Gilbert     VirtIONet *n = opaque;
2587982b78c5SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2588037dab2fSGreg Kurz     int i, link_down;
2589037dab2fSGreg Kurz 
25909d8c6a25SDr. David Alan Gilbert     trace_virtio_net_post_load_device();
2591982b78c5SDr. David Alan Gilbert     virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
259295129d6fSCornelia Huck                                virtio_vdev_has_feature(vdev,
259395129d6fSCornelia Huck                                                        VIRTIO_F_VERSION_1));
25946e790746SPaolo Bonzini 
25956e790746SPaolo Bonzini     /* MAC_TABLE_ENTRIES may be different from the saved image */
2596982b78c5SDr. David Alan Gilbert     if (n->mac_table.in_use > MAC_TABLE_ENTRIES) {
25976e790746SPaolo Bonzini         n->mac_table.in_use = 0;
25986e790746SPaolo Bonzini     }
25996e790746SPaolo Bonzini 
2600982b78c5SDr. David Alan Gilbert     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
26016c666823SMichael S. Tsirkin         n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
26026c666823SMichael S. Tsirkin     }
26036c666823SMichael S. Tsirkin 
26047788c3f2SMikhail Sennikovsky     /*
26057788c3f2SMikhail Sennikovsky      * curr_guest_offloads will be later overwritten by the
26067788c3f2SMikhail Sennikovsky      * virtio_set_features_nocheck call done from the virtio_load.
26077788c3f2SMikhail Sennikovsky      * Here we make sure it is preserved and restored accordingly
26087788c3f2SMikhail Sennikovsky      * in the virtio_net_post_load_virtio callback.
26097788c3f2SMikhail Sennikovsky      */
26107788c3f2SMikhail Sennikovsky     n->saved_guest_offloads = n->curr_guest_offloads;
26116c666823SMichael S. Tsirkin 
26126e790746SPaolo Bonzini     virtio_net_set_queues(n);
26136e790746SPaolo Bonzini 
26146e790746SPaolo Bonzini     /* Find the first multicast entry in the saved MAC filter */
26156e790746SPaolo Bonzini     for (i = 0; i < n->mac_table.in_use; i++) {
26166e790746SPaolo Bonzini         if (n->mac_table.macs[i * ETH_ALEN] & 1) {
26176e790746SPaolo Bonzini             break;
26186e790746SPaolo Bonzini         }
26196e790746SPaolo Bonzini     }
26206e790746SPaolo Bonzini     n->mac_table.first_multi = i;
26216e790746SPaolo Bonzini 
26226e790746SPaolo Bonzini     /* nc.link_down can't be migrated, so infer link_down according
26236e790746SPaolo Bonzini      * to link status bit in n->status */
26246e790746SPaolo Bonzini     link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0;
26256e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
26266e790746SPaolo Bonzini         qemu_get_subqueue(n->nic, i)->link_down = link_down;
26276e790746SPaolo Bonzini     }
26286e790746SPaolo Bonzini 
26296c666823SMichael S. Tsirkin     if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
26306c666823SMichael S. Tsirkin         virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
26319d8c6a25SDr. David Alan Gilbert         qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(),
26329d8c6a25SDr. David Alan Gilbert                                   QEMU_CLOCK_VIRTUAL,
26339d8c6a25SDr. David Alan Gilbert                                   virtio_net_announce_timer, n);
26349d8c6a25SDr. David Alan Gilbert         if (n->announce_timer.round) {
26359d8c6a25SDr. David Alan Gilbert             timer_mod(n->announce_timer.tm,
26369d8c6a25SDr. David Alan Gilbert                       qemu_clock_get_ms(n->announce_timer.type));
26379d8c6a25SDr. David Alan Gilbert         } else {
2638944458b6SDr. David Alan Gilbert             qemu_announce_timer_del(&n->announce_timer, false);
26399d8c6a25SDr. David Alan Gilbert         }
26406c666823SMichael S. Tsirkin     }
26416c666823SMichael S. Tsirkin 
26426e790746SPaolo Bonzini     return 0;
26436e790746SPaolo Bonzini }
26446e790746SPaolo Bonzini 
26457788c3f2SMikhail Sennikovsky static int virtio_net_post_load_virtio(VirtIODevice *vdev)
26467788c3f2SMikhail Sennikovsky {
26477788c3f2SMikhail Sennikovsky     VirtIONet *n = VIRTIO_NET(vdev);
26487788c3f2SMikhail Sennikovsky     /*
26497788c3f2SMikhail Sennikovsky      * The actual needed state is now in saved_guest_offloads,
26507788c3f2SMikhail Sennikovsky      * see virtio_net_post_load_device for detail.
26517788c3f2SMikhail Sennikovsky      * Restore it back and apply the desired offloads.
26527788c3f2SMikhail Sennikovsky      */
26537788c3f2SMikhail Sennikovsky     n->curr_guest_offloads = n->saved_guest_offloads;
26547788c3f2SMikhail Sennikovsky     if (peer_has_vnet_hdr(n)) {
26557788c3f2SMikhail Sennikovsky         virtio_net_apply_guest_offloads(n);
26567788c3f2SMikhail Sennikovsky     }
26577788c3f2SMikhail Sennikovsky 
26587788c3f2SMikhail Sennikovsky     return 0;
26597788c3f2SMikhail Sennikovsky }
26607788c3f2SMikhail Sennikovsky 
2661982b78c5SDr. David Alan Gilbert /* tx_waiting field of a VirtIONetQueue */
2662982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = {
2663982b78c5SDr. David Alan Gilbert     .name = "virtio-net-queue-tx_waiting",
2664982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2665982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(tx_waiting, VirtIONetQueue),
2666982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2667982b78c5SDr. David Alan Gilbert    },
2668982b78c5SDr. David Alan Gilbert };
2669982b78c5SDr. David Alan Gilbert 
2670982b78c5SDr. David Alan Gilbert static bool max_queues_gt_1(void *opaque, int version_id)
2671982b78c5SDr. David Alan Gilbert {
2672982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->max_queues > 1;
2673982b78c5SDr. David Alan Gilbert }
2674982b78c5SDr. David Alan Gilbert 
2675982b78c5SDr. David Alan Gilbert static bool has_ctrl_guest_offloads(void *opaque, int version_id)
2676982b78c5SDr. David Alan Gilbert {
2677982b78c5SDr. David Alan Gilbert     return virtio_vdev_has_feature(VIRTIO_DEVICE(opaque),
2678982b78c5SDr. David Alan Gilbert                                    VIRTIO_NET_F_CTRL_GUEST_OFFLOADS);
2679982b78c5SDr. David Alan Gilbert }
2680982b78c5SDr. David Alan Gilbert 
2681982b78c5SDr. David Alan Gilbert static bool mac_table_fits(void *opaque, int version_id)
2682982b78c5SDr. David Alan Gilbert {
2683982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->mac_table.in_use <= MAC_TABLE_ENTRIES;
2684982b78c5SDr. David Alan Gilbert }
2685982b78c5SDr. David Alan Gilbert 
2686982b78c5SDr. David Alan Gilbert static bool mac_table_doesnt_fit(void *opaque, int version_id)
2687982b78c5SDr. David Alan Gilbert {
2688982b78c5SDr. David Alan Gilbert     return !mac_table_fits(opaque, version_id);
2689982b78c5SDr. David Alan Gilbert }
2690982b78c5SDr. David Alan Gilbert 
2691982b78c5SDr. David Alan Gilbert /* This temporary type is shared by all the WITH_TMP methods
2692982b78c5SDr. David Alan Gilbert  * although only some fields are used by each.
2693982b78c5SDr. David Alan Gilbert  */
2694982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp {
2695982b78c5SDr. David Alan Gilbert     VirtIONet      *parent;
2696982b78c5SDr. David Alan Gilbert     VirtIONetQueue *vqs_1;
2697982b78c5SDr. David Alan Gilbert     uint16_t        curr_queues_1;
2698982b78c5SDr. David Alan Gilbert     uint8_t         has_ufo;
2699982b78c5SDr. David Alan Gilbert     uint32_t        has_vnet_hdr;
2700982b78c5SDr. David Alan Gilbert };
2701982b78c5SDr. David Alan Gilbert 
2702982b78c5SDr. David Alan Gilbert /* The 2nd and subsequent tx_waiting flags are loaded later than
2703982b78c5SDr. David Alan Gilbert  * the 1st entry in the queues and only if there's more than one
2704982b78c5SDr. David Alan Gilbert  * entry.  We use the tmp mechanism to calculate a temporary
2705982b78c5SDr. David Alan Gilbert  * pointer and count and also validate the count.
2706982b78c5SDr. David Alan Gilbert  */
2707982b78c5SDr. David Alan Gilbert 
270844b1ff31SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_save(void *opaque)
2709982b78c5SDr. David Alan Gilbert {
2710982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2711982b78c5SDr. David Alan Gilbert 
2712982b78c5SDr. David Alan Gilbert     tmp->vqs_1 = tmp->parent->vqs + 1;
2713982b78c5SDr. David Alan Gilbert     tmp->curr_queues_1 = tmp->parent->curr_queues - 1;
2714982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues == 0) {
2715982b78c5SDr. David Alan Gilbert         tmp->curr_queues_1 = 0;
2716982b78c5SDr. David Alan Gilbert     }
271744b1ff31SDr. David Alan Gilbert 
271844b1ff31SDr. David Alan Gilbert     return 0;
2719982b78c5SDr. David Alan Gilbert }
2720982b78c5SDr. David Alan Gilbert 
2721982b78c5SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_load(void *opaque)
2722982b78c5SDr. David Alan Gilbert {
2723982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2724982b78c5SDr. David Alan Gilbert 
2725982b78c5SDr. David Alan Gilbert     /* Reuse the pointer setup from save */
2726982b78c5SDr. David Alan Gilbert     virtio_net_tx_waiting_pre_save(opaque);
2727982b78c5SDr. David Alan Gilbert 
2728982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues > tmp->parent->max_queues) {
2729982b78c5SDr. David Alan Gilbert         error_report("virtio-net: curr_queues %x > max_queues %x",
2730982b78c5SDr. David Alan Gilbert             tmp->parent->curr_queues, tmp->parent->max_queues);
2731982b78c5SDr. David Alan Gilbert 
2732982b78c5SDr. David Alan Gilbert         return -EINVAL;
2733982b78c5SDr. David Alan Gilbert     }
2734982b78c5SDr. David Alan Gilbert 
2735982b78c5SDr. David Alan Gilbert     return 0; /* all good */
2736982b78c5SDr. David Alan Gilbert }
2737982b78c5SDr. David Alan Gilbert 
2738982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_tx_waiting = {
2739982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-tx_waiting",
2740982b78c5SDr. David Alan Gilbert     .pre_load  = virtio_net_tx_waiting_pre_load,
2741982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_tx_waiting_pre_save,
2742982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2743982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp,
2744982b78c5SDr. David Alan Gilbert                                      curr_queues_1,
2745982b78c5SDr. David Alan Gilbert                                      vmstate_virtio_net_queue_tx_waiting,
2746982b78c5SDr. David Alan Gilbert                                      struct VirtIONetQueue),
2747982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2748982b78c5SDr. David Alan Gilbert     },
2749982b78c5SDr. David Alan Gilbert };
2750982b78c5SDr. David Alan Gilbert 
2751982b78c5SDr. David Alan Gilbert /* the 'has_ufo' flag is just tested; if the incoming stream has the
2752982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2753982b78c5SDr. David Alan Gilbert  */
2754982b78c5SDr. David Alan Gilbert static int virtio_net_ufo_post_load(void *opaque, int version_id)
2755982b78c5SDr. David Alan Gilbert {
2756982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2757982b78c5SDr. David Alan Gilbert 
2758982b78c5SDr. David Alan Gilbert     if (tmp->has_ufo && !peer_has_ufo(tmp->parent)) {
2759982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires TUN_F_UFO support");
2760982b78c5SDr. David Alan Gilbert         return -EINVAL;
2761982b78c5SDr. David Alan Gilbert     }
2762982b78c5SDr. David Alan Gilbert 
2763982b78c5SDr. David Alan Gilbert     return 0;
2764982b78c5SDr. David Alan Gilbert }
2765982b78c5SDr. David Alan Gilbert 
276644b1ff31SDr. David Alan Gilbert static int virtio_net_ufo_pre_save(void *opaque)
2767982b78c5SDr. David Alan Gilbert {
2768982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2769982b78c5SDr. David Alan Gilbert 
2770982b78c5SDr. David Alan Gilbert     tmp->has_ufo = tmp->parent->has_ufo;
277144b1ff31SDr. David Alan Gilbert 
277244b1ff31SDr. David Alan Gilbert     return 0;
2773982b78c5SDr. David Alan Gilbert }
2774982b78c5SDr. David Alan Gilbert 
2775982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_ufo = {
2776982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-ufo",
2777982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_ufo_post_load,
2778982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_ufo_pre_save,
2779982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2780982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(has_ufo, struct VirtIONetMigTmp),
2781982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2782982b78c5SDr. David Alan Gilbert     },
2783982b78c5SDr. David Alan Gilbert };
2784982b78c5SDr. David Alan Gilbert 
2785982b78c5SDr. David Alan Gilbert /* the 'has_vnet_hdr' flag is just tested; if the incoming stream has the
2786982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2787982b78c5SDr. David Alan Gilbert  */
2788982b78c5SDr. David Alan Gilbert static int virtio_net_vnet_post_load(void *opaque, int version_id)
2789982b78c5SDr. David Alan Gilbert {
2790982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2791982b78c5SDr. David Alan Gilbert 
2792982b78c5SDr. David Alan Gilbert     if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) {
2793982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires vnet_hdr=on");
2794982b78c5SDr. David Alan Gilbert         return -EINVAL;
2795982b78c5SDr. David Alan Gilbert     }
2796982b78c5SDr. David Alan Gilbert 
2797982b78c5SDr. David Alan Gilbert     return 0;
2798982b78c5SDr. David Alan Gilbert }
2799982b78c5SDr. David Alan Gilbert 
280044b1ff31SDr. David Alan Gilbert static int virtio_net_vnet_pre_save(void *opaque)
2801982b78c5SDr. David Alan Gilbert {
2802982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2803982b78c5SDr. David Alan Gilbert 
2804982b78c5SDr. David Alan Gilbert     tmp->has_vnet_hdr = tmp->parent->has_vnet_hdr;
280544b1ff31SDr. David Alan Gilbert 
280644b1ff31SDr. David Alan Gilbert     return 0;
2807982b78c5SDr. David Alan Gilbert }
2808982b78c5SDr. David Alan Gilbert 
2809982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_vnet = {
2810982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-vnet",
2811982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_vnet_post_load,
2812982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_vnet_pre_save,
2813982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2814982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(has_vnet_hdr, struct VirtIONetMigTmp),
2815982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2816982b78c5SDr. David Alan Gilbert     },
2817982b78c5SDr. David Alan Gilbert };
2818982b78c5SDr. David Alan Gilbert 
2819982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_device = {
2820982b78c5SDr. David Alan Gilbert     .name = "virtio-net-device",
2821982b78c5SDr. David Alan Gilbert     .version_id = VIRTIO_NET_VM_VERSION,
2822982b78c5SDr. David Alan Gilbert     .minimum_version_id = VIRTIO_NET_VM_VERSION,
2823982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_post_load_device,
2824982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2825982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN),
2826982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_POINTER(vqs, VirtIONet,
2827982b78c5SDr. David Alan Gilbert                                vmstate_virtio_net_queue_tx_waiting,
2828982b78c5SDr. David Alan Gilbert                                VirtIONetQueue),
2829982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mergeable_rx_bufs, VirtIONet),
2830982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16(status, VirtIONet),
2831982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(promisc, VirtIONet),
2832982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(allmulti, VirtIONet),
2833982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mac_table.in_use, VirtIONet),
2834982b78c5SDr. David Alan Gilbert 
2835982b78c5SDr. David Alan Gilbert         /* Guarded pair: If it fits we load it, else we throw it away
2836982b78c5SDr. David Alan Gilbert          * - can happen if source has a larger MAC table.; post-load
2837982b78c5SDr. David Alan Gilbert          *  sets flags in this case.
2838982b78c5SDr. David Alan Gilbert          */
2839982b78c5SDr. David Alan Gilbert         VMSTATE_VBUFFER_MULTIPLY(mac_table.macs, VirtIONet,
2840982b78c5SDr. David Alan Gilbert                                 0, mac_table_fits, mac_table.in_use,
2841982b78c5SDr. David Alan Gilbert                                  ETH_ALEN),
2842982b78c5SDr. David Alan Gilbert         VMSTATE_UNUSED_VARRAY_UINT32(VirtIONet, mac_table_doesnt_fit, 0,
2843982b78c5SDr. David Alan Gilbert                                      mac_table.in_use, ETH_ALEN),
2844982b78c5SDr. David Alan Gilbert 
2845982b78c5SDr. David Alan Gilbert         /* Note: This is an array of uint32's that's always been saved as a
2846982b78c5SDr. David Alan Gilbert          * buffer; hold onto your endiannesses; it's actually used as a bitmap
2847982b78c5SDr. David Alan Gilbert          * but based on the uint.
2848982b78c5SDr. David Alan Gilbert          */
2849982b78c5SDr. David Alan Gilbert         VMSTATE_BUFFER_POINTER_UNSAFE(vlans, VirtIONet, 0, MAX_VLAN >> 3),
2850982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2851982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_vnet),
2852982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.multi_overflow, VirtIONet),
2853982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.uni_overflow, VirtIONet),
2854982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(alluni, VirtIONet),
2855982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nomulti, VirtIONet),
2856982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nouni, VirtIONet),
2857982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nobcast, VirtIONet),
2858982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2859982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_ufo),
2860982b78c5SDr. David Alan Gilbert         VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0,
2861982b78c5SDr. David Alan Gilbert                             vmstate_info_uint16_equal, uint16_t),
2862982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1),
2863982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2864982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_tx_waiting),
2865982b78c5SDr. David Alan Gilbert         VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet,
2866982b78c5SDr. David Alan Gilbert                             has_ctrl_guest_offloads),
2867982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2868982b78c5SDr. David Alan Gilbert    },
2869982b78c5SDr. David Alan Gilbert };
2870982b78c5SDr. David Alan Gilbert 
28716e790746SPaolo Bonzini static NetClientInfo net_virtio_info = {
2872f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
28736e790746SPaolo Bonzini     .size = sizeof(NICState),
28746e790746SPaolo Bonzini     .can_receive = virtio_net_can_receive,
28756e790746SPaolo Bonzini     .receive = virtio_net_receive,
28766e790746SPaolo Bonzini     .link_status_changed = virtio_net_set_link_status,
2877b1be4280SAmos Kong     .query_rx_filter = virtio_net_query_rxfilter,
2878b2c929f0SDr. David Alan Gilbert     .announce = virtio_net_announce,
28796e790746SPaolo Bonzini };
28806e790746SPaolo Bonzini 
28816e790746SPaolo Bonzini static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
28826e790746SPaolo Bonzini {
288317a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
28846e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
28856e790746SPaolo Bonzini     assert(n->vhost_started);
2886ed8b4afeSNikolay Nikolaev     return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx);
28876e790746SPaolo Bonzini }
28886e790746SPaolo Bonzini 
28896e790746SPaolo Bonzini static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
28906e790746SPaolo Bonzini                                            bool mask)
28916e790746SPaolo Bonzini {
289217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
28936e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
28946e790746SPaolo Bonzini     assert(n->vhost_started);
2895ed8b4afeSNikolay Nikolaev     vhost_net_virtqueue_mask(get_vhost_net(nc->peer),
28966e790746SPaolo Bonzini                              vdev, idx, mask);
28976e790746SPaolo Bonzini }
28986e790746SPaolo Bonzini 
2899019a3edbSGerd Hoffmann static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features)
29006e790746SPaolo Bonzini {
29010cd09c3aSCornelia Huck     virtio_add_feature(&host_features, VIRTIO_NET_F_MAC);
2902a93e599dSMaxime Coquelin 
2903ba550851SStefano Garzarella     n->config_size = virtio_feature_get_config_size(feature_sizes,
2904ba550851SStefano Garzarella                                                     host_features);
290517ec5a86SKONRAD Frederic }
29066e790746SPaolo Bonzini 
29078a253ec2SKONRAD Frederic void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
29088a253ec2SKONRAD Frederic                                    const char *type)
29098a253ec2SKONRAD Frederic {
29108a253ec2SKONRAD Frederic     /*
29118a253ec2SKONRAD Frederic      * The name can be NULL, the netclient name will be type.x.
29128a253ec2SKONRAD Frederic      */
29138a253ec2SKONRAD Frederic     assert(type != NULL);
29148a253ec2SKONRAD Frederic 
29158a253ec2SKONRAD Frederic     g_free(n->netclient_name);
29168a253ec2SKONRAD Frederic     g_free(n->netclient_type);
29178a253ec2SKONRAD Frederic     n->netclient_name = g_strdup(name);
29188a253ec2SKONRAD Frederic     n->netclient_type = g_strdup(type);
29198a253ec2SKONRAD Frederic }
29208a253ec2SKONRAD Frederic 
29219711cd0dSJens Freimann static bool failover_unplug_primary(VirtIONet *n)
29229711cd0dSJens Freimann {
29239711cd0dSJens Freimann     HotplugHandler *hotplug_ctrl;
29249711cd0dSJens Freimann     PCIDevice *pci_dev;
29259711cd0dSJens Freimann     Error *err = NULL;
29269711cd0dSJens Freimann 
29279711cd0dSJens Freimann     hotplug_ctrl = qdev_get_hotplug_handler(n->primary_dev);
29289711cd0dSJens Freimann     if (hotplug_ctrl) {
29299711cd0dSJens Freimann         pci_dev = PCI_DEVICE(n->primary_dev);
29309711cd0dSJens Freimann         pci_dev->partially_hotplugged = true;
29319711cd0dSJens Freimann         hotplug_handler_unplug_request(hotplug_ctrl, n->primary_dev, &err);
29329711cd0dSJens Freimann         if (err) {
29339711cd0dSJens Freimann             error_report_err(err);
29349711cd0dSJens Freimann             return false;
29359711cd0dSJens Freimann         }
29369711cd0dSJens Freimann     } else {
29379711cd0dSJens Freimann         return false;
29389711cd0dSJens Freimann     }
29399711cd0dSJens Freimann     return true;
29409711cd0dSJens Freimann }
29419711cd0dSJens Freimann 
29429711cd0dSJens Freimann static bool failover_replug_primary(VirtIONet *n, Error **errp)
29439711cd0dSJens Freimann {
29445a0948d3SMarkus Armbruster     Error *err = NULL;
29459711cd0dSJens Freimann     HotplugHandler *hotplug_ctrl;
29469711cd0dSJens Freimann     PCIDevice *pdev = PCI_DEVICE(n->primary_dev);
29479711cd0dSJens Freimann 
29489711cd0dSJens Freimann     if (!pdev->partially_hotplugged) {
29499711cd0dSJens Freimann         return true;
29509711cd0dSJens Freimann     }
29519711cd0dSJens Freimann     if (!n->primary_device_opts) {
29529711cd0dSJens Freimann         n->primary_device_opts = qemu_opts_from_qdict(
29539711cd0dSJens Freimann                 qemu_find_opts("device"),
29549711cd0dSJens Freimann                 n->primary_device_dict, errp);
2955150ab54aSJens Freimann         if (!n->primary_device_opts) {
29565a0948d3SMarkus Armbruster             return false;
29579711cd0dSJens Freimann         }
2958150ab54aSJens Freimann     }
29599711cd0dSJens Freimann     n->primary_bus = n->primary_dev->parent_bus;
2960150ab54aSJens Freimann     if (!n->primary_bus) {
2961150ab54aSJens Freimann         error_setg(errp, "virtio_net: couldn't find primary bus");
29625a0948d3SMarkus Armbruster         return false;
29639711cd0dSJens Freimann     }
29649711cd0dSJens Freimann     qdev_set_parent_bus(n->primary_dev, n->primary_bus);
29659711cd0dSJens Freimann     n->primary_should_be_hidden = false;
29669711cd0dSJens Freimann     qemu_opt_set_bool(n->primary_device_opts,
29675a0948d3SMarkus Armbruster                       "partially_hotplugged", true, &err);
29685a0948d3SMarkus Armbruster     if (err) {
29695a0948d3SMarkus Armbruster         goto out;
29705a0948d3SMarkus Armbruster     }
29719711cd0dSJens Freimann     hotplug_ctrl = qdev_get_hotplug_handler(n->primary_dev);
29729711cd0dSJens Freimann     if (hotplug_ctrl) {
29735a0948d3SMarkus Armbruster         hotplug_handler_pre_plug(hotplug_ctrl, n->primary_dev, &err);
29745a0948d3SMarkus Armbruster         if (err) {
29755a0948d3SMarkus Armbruster             goto out;
29765a0948d3SMarkus Armbruster         }
29779711cd0dSJens Freimann         hotplug_handler_plug(hotplug_ctrl, n->primary_dev, errp);
29789711cd0dSJens Freimann     }
2979150ab54aSJens Freimann 
2980150ab54aSJens Freimann out:
29815a0948d3SMarkus Armbruster     error_propagate(errp, err);
29825a0948d3SMarkus Armbruster     return !err;
29839711cd0dSJens Freimann }
29849711cd0dSJens Freimann 
29859711cd0dSJens Freimann static void virtio_net_handle_migration_primary(VirtIONet *n,
29869711cd0dSJens Freimann                                                 MigrationState *s)
29879711cd0dSJens Freimann {
29889711cd0dSJens Freimann     bool should_be_hidden;
29899711cd0dSJens Freimann     Error *err = NULL;
29909711cd0dSJens Freimann 
29919711cd0dSJens Freimann     should_be_hidden = atomic_read(&n->primary_should_be_hidden);
29929711cd0dSJens Freimann 
29939711cd0dSJens Freimann     if (!n->primary_dev) {
29949711cd0dSJens Freimann         n->primary_dev = virtio_connect_failover_devices(n, n->qdev, &err);
29959711cd0dSJens Freimann         if (!n->primary_dev) {
29969711cd0dSJens Freimann             return;
29979711cd0dSJens Freimann         }
29989711cd0dSJens Freimann     }
29999711cd0dSJens Freimann 
30004dbac1aeSMarkus Armbruster     if (migration_in_setup(s) && !should_be_hidden) {
30019711cd0dSJens Freimann         if (failover_unplug_primary(n)) {
30023cad405bSMarc-André Lureau             vmstate_unregister(VMSTATE_IF(n->primary_dev),
30033cad405bSMarc-André Lureau                     qdev_get_vmsd(n->primary_dev),
30049711cd0dSJens Freimann                     n->primary_dev);
30059711cd0dSJens Freimann             qapi_event_send_unplug_primary(n->primary_device_id);
30069711cd0dSJens Freimann             atomic_set(&n->primary_should_be_hidden, true);
30079711cd0dSJens Freimann         } else {
30089711cd0dSJens Freimann             warn_report("couldn't unplug primary device");
30099711cd0dSJens Freimann         }
30109711cd0dSJens Freimann     } else if (migration_has_failed(s)) {
3011150ab54aSJens Freimann         /* We already unplugged the device let's plug it back */
30129711cd0dSJens Freimann         if (!failover_replug_primary(n, &err)) {
30139711cd0dSJens Freimann             if (err) {
30149711cd0dSJens Freimann                 error_report_err(err);
30159711cd0dSJens Freimann             }
30169711cd0dSJens Freimann         }
30179711cd0dSJens Freimann     }
30189711cd0dSJens Freimann }
30199711cd0dSJens Freimann 
30209711cd0dSJens Freimann static void virtio_net_migration_state_notifier(Notifier *notifier, void *data)
30219711cd0dSJens Freimann {
30229711cd0dSJens Freimann     MigrationState *s = data;
30239711cd0dSJens Freimann     VirtIONet *n = container_of(notifier, VirtIONet, migration_state);
30249711cd0dSJens Freimann     virtio_net_handle_migration_primary(n, s);
30259711cd0dSJens Freimann }
30269711cd0dSJens Freimann 
30279711cd0dSJens Freimann static int virtio_net_primary_should_be_hidden(DeviceListener *listener,
30289711cd0dSJens Freimann             QemuOpts *device_opts)
30299711cd0dSJens Freimann {
30309711cd0dSJens Freimann     VirtIONet *n = container_of(listener, VirtIONet, primary_listener);
30314d0e59acSJens Freimann     bool match_found = false;
30324d0e59acSJens Freimann     bool hide = false;
30339711cd0dSJens Freimann 
30344d0e59acSJens Freimann     if (!device_opts) {
30354d0e59acSJens Freimann         return -1;
30364d0e59acSJens Freimann     }
30379711cd0dSJens Freimann     n->primary_device_dict = qemu_opts_to_qdict(device_opts,
30389711cd0dSJens Freimann             n->primary_device_dict);
30399711cd0dSJens Freimann     if (n->primary_device_dict) {
30409711cd0dSJens Freimann         g_free(n->standby_id);
30419711cd0dSJens Freimann         n->standby_id = g_strdup(qdict_get_try_str(n->primary_device_dict,
30429711cd0dSJens Freimann                     "failover_pair_id"));
30439711cd0dSJens Freimann     }
30444d0e59acSJens Freimann     if (g_strcmp0(n->standby_id, n->netclient_name) == 0) {
30459711cd0dSJens Freimann         match_found = true;
30469711cd0dSJens Freimann     } else {
30479711cd0dSJens Freimann         match_found = false;
30489711cd0dSJens Freimann         hide = false;
30499711cd0dSJens Freimann         g_free(n->standby_id);
30509711cd0dSJens Freimann         n->primary_device_dict = NULL;
30519711cd0dSJens Freimann         goto out;
30529711cd0dSJens Freimann     }
30539711cd0dSJens Freimann 
30549711cd0dSJens Freimann     n->primary_device_opts = device_opts;
30559711cd0dSJens Freimann 
30569711cd0dSJens Freimann     /* primary_should_be_hidden is set during feature negotiation */
30579711cd0dSJens Freimann     hide = atomic_read(&n->primary_should_be_hidden);
30589711cd0dSJens Freimann 
30599711cd0dSJens Freimann     if (n->primary_device_dict) {
30609711cd0dSJens Freimann         g_free(n->primary_device_id);
30619711cd0dSJens Freimann         n->primary_device_id = g_strdup(qdict_get_try_str(
30629711cd0dSJens Freimann                     n->primary_device_dict, "id"));
30639711cd0dSJens Freimann         if (!n->primary_device_id) {
30649711cd0dSJens Freimann             warn_report("primary_device_id not set");
30659711cd0dSJens Freimann         }
30669711cd0dSJens Freimann     }
30679711cd0dSJens Freimann 
30689711cd0dSJens Freimann out:
30699711cd0dSJens Freimann     if (match_found && hide) {
30709711cd0dSJens Freimann         return 1;
30719711cd0dSJens Freimann     } else if (match_found && !hide) {
30729711cd0dSJens Freimann         return 0;
30739711cd0dSJens Freimann     } else {
30749711cd0dSJens Freimann         return -1;
30759711cd0dSJens Freimann     }
30769711cd0dSJens Freimann }
30779711cd0dSJens Freimann 
3078e6f746b3SAndreas Färber static void virtio_net_device_realize(DeviceState *dev, Error **errp)
307917ec5a86SKONRAD Frederic {
3080e6f746b3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
3081284a32f0SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
3082284a32f0SAndreas Färber     NetClientState *nc;
30831773d9eeSKONRAD Frederic     int i;
308417ec5a86SKONRAD Frederic 
3085a93e599dSMaxime Coquelin     if (n->net_conf.mtu) {
3086127833eeSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_MTU);
3087a93e599dSMaxime Coquelin     }
3088a93e599dSMaxime Coquelin 
30899473939eSJason Baron     if (n->net_conf.duplex_str) {
30909473939eSJason Baron         if (strncmp(n->net_conf.duplex_str, "half", 5) == 0) {
30919473939eSJason Baron             n->net_conf.duplex = DUPLEX_HALF;
30929473939eSJason Baron         } else if (strncmp(n->net_conf.duplex_str, "full", 5) == 0) {
30939473939eSJason Baron             n->net_conf.duplex = DUPLEX_FULL;
30949473939eSJason Baron         } else {
30959473939eSJason Baron             error_setg(errp, "'duplex' must be 'half' or 'full'");
3096843c4cfcSMarkus Armbruster             return;
30979473939eSJason Baron         }
30989473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
30999473939eSJason Baron     } else {
31009473939eSJason Baron         n->net_conf.duplex = DUPLEX_UNKNOWN;
31019473939eSJason Baron     }
31029473939eSJason Baron 
31039473939eSJason Baron     if (n->net_conf.speed < SPEED_UNKNOWN) {
31049473939eSJason Baron         error_setg(errp, "'speed' must be between 0 and INT_MAX");
3105843c4cfcSMarkus Armbruster         return;
3106843c4cfcSMarkus Armbruster     }
3107843c4cfcSMarkus Armbruster     if (n->net_conf.speed >= 0) {
31089473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
31099473939eSJason Baron     }
31109473939eSJason Baron 
31119711cd0dSJens Freimann     if (n->failover) {
31129711cd0dSJens Freimann         n->primary_listener.should_be_hidden =
31139711cd0dSJens Freimann             virtio_net_primary_should_be_hidden;
31149711cd0dSJens Freimann         atomic_set(&n->primary_should_be_hidden, true);
31159711cd0dSJens Freimann         device_listener_register(&n->primary_listener);
31169711cd0dSJens Freimann         n->migration_state.notify = virtio_net_migration_state_notifier;
31179711cd0dSJens Freimann         add_migration_state_change_notifier(&n->migration_state);
31189711cd0dSJens Freimann         n->host_features |= (1ULL << VIRTIO_NET_F_STANDBY);
31199711cd0dSJens Freimann     }
31209711cd0dSJens Freimann 
3121da3e8a23SShannon Zhao     virtio_net_set_config_size(n, n->host_features);
3122284a32f0SAndreas Färber     virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size);
312317ec5a86SKONRAD Frederic 
31241c0fbfa3SMichael S. Tsirkin     /*
31251c0fbfa3SMichael S. Tsirkin      * We set a lower limit on RX queue size to what it always was.
31261c0fbfa3SMichael S. Tsirkin      * Guests that want a smaller ring can always resize it without
31271c0fbfa3SMichael S. Tsirkin      * help from us (using virtio 1 and up).
31281c0fbfa3SMichael S. Tsirkin      */
31291c0fbfa3SMichael S. Tsirkin     if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE ||
31301c0fbfa3SMichael S. Tsirkin         n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE ||
31315f997fd1SMichal Privoznik         !is_power_of_2(n->net_conf.rx_queue_size)) {
31321c0fbfa3SMichael S. Tsirkin         error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), "
31331c0fbfa3SMichael S. Tsirkin                    "must be a power of 2 between %d and %d.",
31341c0fbfa3SMichael S. Tsirkin                    n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE,
31351c0fbfa3SMichael S. Tsirkin                    VIRTQUEUE_MAX_SIZE);
31361c0fbfa3SMichael S. Tsirkin         virtio_cleanup(vdev);
31371c0fbfa3SMichael S. Tsirkin         return;
31381c0fbfa3SMichael S. Tsirkin     }
31391c0fbfa3SMichael S. Tsirkin 
31409b02e161SWei Wang     if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE ||
31419b02e161SWei Wang         n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE ||
31429b02e161SWei Wang         !is_power_of_2(n->net_conf.tx_queue_size)) {
31439b02e161SWei Wang         error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), "
31449b02e161SWei Wang                    "must be a power of 2 between %d and %d",
31459b02e161SWei Wang                    n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE,
31469b02e161SWei Wang                    VIRTQUEUE_MAX_SIZE);
31479b02e161SWei Wang         virtio_cleanup(vdev);
31489b02e161SWei Wang         return;
31499b02e161SWei Wang     }
31509b02e161SWei Wang 
3151575a1c0eSJiri Pirko     n->max_queues = MAX(n->nic_conf.peers.queues, 1);
315287b3bd1cSJason Wang     if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) {
31537e0e736eSJason Wang         error_setg(errp, "Invalid number of queues (= %" PRIu32 "), "
3154631b22eaSStefan Weil                    "must be a positive integer less than %d.",
315587b3bd1cSJason Wang                    n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2);
31567e0e736eSJason Wang         virtio_cleanup(vdev);
31577e0e736eSJason Wang         return;
31587e0e736eSJason Wang     }
31596e790746SPaolo Bonzini     n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues);
31606e790746SPaolo Bonzini     n->curr_queues = 1;
31611773d9eeSKONRAD Frederic     n->tx_timeout = n->net_conf.txtimer;
31626e790746SPaolo Bonzini 
31631773d9eeSKONRAD Frederic     if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer")
31641773d9eeSKONRAD Frederic                        && strcmp(n->net_conf.tx, "bh")) {
31650765691eSMarkus Armbruster         warn_report("virtio-net: "
31666e790746SPaolo Bonzini                     "Unknown option tx=%s, valid options: \"timer\" \"bh\"",
31671773d9eeSKONRAD Frederic                     n->net_conf.tx);
31680765691eSMarkus Armbruster         error_printf("Defaulting to \"bh\"");
31696e790746SPaolo Bonzini     }
31706e790746SPaolo Bonzini 
31712eef278bSMichael S. Tsirkin     n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n),
31722eef278bSMichael S. Tsirkin                                     n->net_conf.tx_queue_size);
31739b02e161SWei Wang 
3174da51a335SJason Wang     for (i = 0; i < n->max_queues; i++) {
3175f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i);
3176da51a335SJason Wang     }
3177da51a335SJason Wang 
317817a0ca55SKONRAD Frederic     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
31791773d9eeSKONRAD Frederic     qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);
31801773d9eeSKONRAD Frederic     memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac));
31816e790746SPaolo Bonzini     n->status = VIRTIO_NET_S_LINK_UP;
31829d8c6a25SDr. David Alan Gilbert     qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(),
31839d8c6a25SDr. David Alan Gilbert                               QEMU_CLOCK_VIRTUAL,
3184f57fcf70SJason Wang                               virtio_net_announce_timer, n);
3185b2c929f0SDr. David Alan Gilbert     n->announce_timer.round = 0;
31866e790746SPaolo Bonzini 
31878a253ec2SKONRAD Frederic     if (n->netclient_type) {
31888a253ec2SKONRAD Frederic         /*
31898a253ec2SKONRAD Frederic          * Happen when virtio_net_set_netclient_name has been called.
31908a253ec2SKONRAD Frederic          */
31918a253ec2SKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
31928a253ec2SKONRAD Frederic                               n->netclient_type, n->netclient_name, n);
31938a253ec2SKONRAD Frederic     } else {
31941773d9eeSKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
3195284a32f0SAndreas Färber                               object_get_typename(OBJECT(dev)), dev->id, n);
31968a253ec2SKONRAD Frederic     }
31978a253ec2SKONRAD Frederic 
31986e790746SPaolo Bonzini     peer_test_vnet_hdr(n);
31996e790746SPaolo Bonzini     if (peer_has_vnet_hdr(n)) {
32006e790746SPaolo Bonzini         for (i = 0; i < n->max_queues; i++) {
3201d6085e3aSStefan Hajnoczi             qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true);
32026e790746SPaolo Bonzini         }
32036e790746SPaolo Bonzini         n->host_hdr_len = sizeof(struct virtio_net_hdr);
32046e790746SPaolo Bonzini     } else {
32056e790746SPaolo Bonzini         n->host_hdr_len = 0;
32066e790746SPaolo Bonzini     }
32076e790746SPaolo Bonzini 
32081773d9eeSKONRAD Frederic     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->nic_conf.macaddr.a);
32096e790746SPaolo Bonzini 
32106e790746SPaolo Bonzini     n->vqs[0].tx_waiting = 0;
32111773d9eeSKONRAD Frederic     n->tx_burst = n->net_conf.txburst;
3212bb9d17f8SCornelia Huck     virtio_net_set_mrg_rx_bufs(n, 0, 0);
32136e790746SPaolo Bonzini     n->promisc = 1; /* for compatibility */
32146e790746SPaolo Bonzini 
32156e790746SPaolo Bonzini     n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
32166e790746SPaolo Bonzini 
32176e790746SPaolo Bonzini     n->vlans = g_malloc0(MAX_VLAN >> 3);
32186e790746SPaolo Bonzini 
3219b1be4280SAmos Kong     nc = qemu_get_queue(n->nic);
3220b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
3221b1be4280SAmos Kong 
32222974e916SYuri Benditovich     QTAILQ_INIT(&n->rsc_chains);
3223284a32f0SAndreas Färber     n->qdev = dev;
322417ec5a86SKONRAD Frederic }
322517ec5a86SKONRAD Frederic 
3226b69c3c21SMarkus Armbruster static void virtio_net_device_unrealize(DeviceState *dev)
322717ec5a86SKONRAD Frederic {
3228306ec6c3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
3229306ec6c3SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
3230f9d6dbf0SWen Congyang     int i, max_queues;
323117ec5a86SKONRAD Frederic 
323217ec5a86SKONRAD Frederic     /* This will stop vhost backend if appropriate. */
323317ec5a86SKONRAD Frederic     virtio_net_set_status(vdev, 0);
323417ec5a86SKONRAD Frederic 
32358a253ec2SKONRAD Frederic     g_free(n->netclient_name);
32368a253ec2SKONRAD Frederic     n->netclient_name = NULL;
32378a253ec2SKONRAD Frederic     g_free(n->netclient_type);
32388a253ec2SKONRAD Frederic     n->netclient_type = NULL;
32398a253ec2SKONRAD Frederic 
324017ec5a86SKONRAD Frederic     g_free(n->mac_table.macs);
324117ec5a86SKONRAD Frederic     g_free(n->vlans);
324217ec5a86SKONRAD Frederic 
32439711cd0dSJens Freimann     if (n->failover) {
32449711cd0dSJens Freimann         g_free(n->primary_device_id);
32459711cd0dSJens Freimann         g_free(n->standby_id);
32469711cd0dSJens Freimann         qobject_unref(n->primary_device_dict);
32479711cd0dSJens Freimann         n->primary_device_dict = NULL;
32489711cd0dSJens Freimann     }
32499711cd0dSJens Freimann 
3250f9d6dbf0SWen Congyang     max_queues = n->multiqueue ? n->max_queues : 1;
3251f9d6dbf0SWen Congyang     for (i = 0; i < max_queues; i++) {
3252f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i);
325317ec5a86SKONRAD Frederic     }
3254d945d9f1SYuri Benditovich     /* delete also control vq */
3255d945d9f1SYuri Benditovich     virtio_del_queue(vdev, max_queues * 2);
3256944458b6SDr. David Alan Gilbert     qemu_announce_timer_del(&n->announce_timer, false);
325717ec5a86SKONRAD Frederic     g_free(n->vqs);
325817ec5a86SKONRAD Frederic     qemu_del_nic(n->nic);
32592974e916SYuri Benditovich     virtio_net_rsc_cleanup(n);
3260*59079029SYuri Benditovich     g_free(n->rss_data.indirections_table);
32616a1a8cc7SKONRAD Frederic     virtio_cleanup(vdev);
326217ec5a86SKONRAD Frederic }
326317ec5a86SKONRAD Frederic 
326417ec5a86SKONRAD Frederic static void virtio_net_instance_init(Object *obj)
326517ec5a86SKONRAD Frederic {
326617ec5a86SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(obj);
326717ec5a86SKONRAD Frederic 
326817ec5a86SKONRAD Frederic     /*
326917ec5a86SKONRAD Frederic      * The default config_size is sizeof(struct virtio_net_config).
327017ec5a86SKONRAD Frederic      * Can be overriden with virtio_net_set_config_size.
327117ec5a86SKONRAD Frederic      */
327217ec5a86SKONRAD Frederic     n->config_size = sizeof(struct virtio_net_config);
3273aa4197c3SGonglei     device_add_bootindex_property(obj, &n->nic_conf.bootindex,
3274aa4197c3SGonglei                                   "bootindex", "/ethernet-phy@0",
327540c2281cSMarkus Armbruster                                   DEVICE(n));
327617ec5a86SKONRAD Frederic }
327717ec5a86SKONRAD Frederic 
327844b1ff31SDr. David Alan Gilbert static int virtio_net_pre_save(void *opaque)
32794d45dcfbSHalil Pasic {
32804d45dcfbSHalil Pasic     VirtIONet *n = opaque;
32814d45dcfbSHalil Pasic 
32824d45dcfbSHalil Pasic     /* At this point, backend must be stopped, otherwise
32834d45dcfbSHalil Pasic      * it might keep writing to memory. */
32844d45dcfbSHalil Pasic     assert(!n->vhost_started);
328544b1ff31SDr. David Alan Gilbert 
328644b1ff31SDr. David Alan Gilbert     return 0;
32874d45dcfbSHalil Pasic }
32884d45dcfbSHalil Pasic 
32899711cd0dSJens Freimann static bool primary_unplug_pending(void *opaque)
32909711cd0dSJens Freimann {
32919711cd0dSJens Freimann     DeviceState *dev = opaque;
32929711cd0dSJens Freimann     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
32939711cd0dSJens Freimann     VirtIONet *n = VIRTIO_NET(vdev);
32949711cd0dSJens Freimann 
3295284f42a5SJens Freimann     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_STANDBY)) {
3296284f42a5SJens Freimann         return false;
3297284f42a5SJens Freimann     }
32989711cd0dSJens Freimann     return n->primary_dev ? n->primary_dev->pending_deleted_event : false;
32999711cd0dSJens Freimann }
33009711cd0dSJens Freimann 
33019711cd0dSJens Freimann static bool dev_unplug_pending(void *opaque)
33029711cd0dSJens Freimann {
33039711cd0dSJens Freimann     DeviceState *dev = opaque;
33049711cd0dSJens Freimann     VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev);
33059711cd0dSJens Freimann 
33069711cd0dSJens Freimann     return vdc->primary_unplug_pending(dev);
33079711cd0dSJens Freimann }
33089711cd0dSJens Freimann 
33094d45dcfbSHalil Pasic static const VMStateDescription vmstate_virtio_net = {
33104d45dcfbSHalil Pasic     .name = "virtio-net",
33114d45dcfbSHalil Pasic     .minimum_version_id = VIRTIO_NET_VM_VERSION,
33124d45dcfbSHalil Pasic     .version_id = VIRTIO_NET_VM_VERSION,
33134d45dcfbSHalil Pasic     .fields = (VMStateField[]) {
33144d45dcfbSHalil Pasic         VMSTATE_VIRTIO_DEVICE,
33154d45dcfbSHalil Pasic         VMSTATE_END_OF_LIST()
33164d45dcfbSHalil Pasic     },
33174d45dcfbSHalil Pasic     .pre_save = virtio_net_pre_save,
33189711cd0dSJens Freimann     .dev_unplug_pending = dev_unplug_pending,
33194d45dcfbSHalil Pasic };
3320290c2428SDr. David Alan Gilbert 
332117ec5a86SKONRAD Frederic static Property virtio_net_properties[] = {
3322127833eeSJason Baron     DEFINE_PROP_BIT64("csum", VirtIONet, host_features,
3323127833eeSJason Baron                     VIRTIO_NET_F_CSUM, true),
3324127833eeSJason Baron     DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features,
332587108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_CSUM, true),
3326127833eeSJason Baron     DEFINE_PROP_BIT64("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true),
3327127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso4", VirtIONet, host_features,
332887108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO4, true),
3329127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso6", VirtIONet, host_features,
333087108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO6, true),
3331127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ecn", VirtIONet, host_features,
333287108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ECN, true),
3333127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ufo", VirtIONet, host_features,
333487108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_UFO, true),
3335127833eeSJason Baron     DEFINE_PROP_BIT64("guest_announce", VirtIONet, host_features,
333687108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ANNOUNCE, true),
3337127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso4", VirtIONet, host_features,
333887108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO4, true),
3339127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso6", VirtIONet, host_features,
334087108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO6, true),
3341127833eeSJason Baron     DEFINE_PROP_BIT64("host_ecn", VirtIONet, host_features,
334287108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_ECN, true),
3343127833eeSJason Baron     DEFINE_PROP_BIT64("host_ufo", VirtIONet, host_features,
334487108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_UFO, true),
3345127833eeSJason Baron     DEFINE_PROP_BIT64("mrg_rxbuf", VirtIONet, host_features,
334687108bb2SShannon Zhao                     VIRTIO_NET_F_MRG_RXBUF, true),
3347127833eeSJason Baron     DEFINE_PROP_BIT64("status", VirtIONet, host_features,
334887108bb2SShannon Zhao                     VIRTIO_NET_F_STATUS, true),
3349127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vq", VirtIONet, host_features,
335087108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VQ, true),
3351127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx", VirtIONet, host_features,
335287108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX, true),
3353127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vlan", VirtIONet, host_features,
335487108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VLAN, true),
3355127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx_extra", VirtIONet, host_features,
335687108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX_EXTRA, true),
3357127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_mac_addr", VirtIONet, host_features,
335887108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_MAC_ADDR, true),
3359127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features,
336087108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
3361127833eeSJason Baron     DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
3362*59079029SYuri Benditovich     DEFINE_PROP_BIT64("rss", VirtIONet, host_features,
3363*59079029SYuri Benditovich                     VIRTIO_NET_F_RSS, false),
33642974e916SYuri Benditovich     DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features,
33652974e916SYuri Benditovich                     VIRTIO_NET_F_RSC_EXT, false),
33662974e916SYuri Benditovich     DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout,
33672974e916SYuri Benditovich                        VIRTIO_NET_RSC_DEFAULT_INTERVAL),
336817ec5a86SKONRAD Frederic     DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf),
336917ec5a86SKONRAD Frederic     DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer,
337017ec5a86SKONRAD Frederic                        TX_TIMER_INTERVAL),
337117ec5a86SKONRAD Frederic     DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST),
337217ec5a86SKONRAD Frederic     DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx),
33731c0fbfa3SMichael S. Tsirkin     DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size,
33741c0fbfa3SMichael S. Tsirkin                        VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE),
33759b02e161SWei Wang     DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size,
33769b02e161SWei Wang                        VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE),
3377a93e599dSMaxime Coquelin     DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0),
337875ebec11SMaxime Coquelin     DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend,
337975ebec11SMaxime Coquelin                      true),
33809473939eSJason Baron     DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN),
33819473939eSJason Baron     DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str),
33829711cd0dSJens Freimann     DEFINE_PROP_BOOL("failover", VirtIONet, failover, false),
338317ec5a86SKONRAD Frederic     DEFINE_PROP_END_OF_LIST(),
338417ec5a86SKONRAD Frederic };
338517ec5a86SKONRAD Frederic 
338617ec5a86SKONRAD Frederic static void virtio_net_class_init(ObjectClass *klass, void *data)
338717ec5a86SKONRAD Frederic {
338817ec5a86SKONRAD Frederic     DeviceClass *dc = DEVICE_CLASS(klass);
338917ec5a86SKONRAD Frederic     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
3390e6f746b3SAndreas Färber 
33914f67d30bSMarc-André Lureau     device_class_set_props(dc, virtio_net_properties);
3392290c2428SDr. David Alan Gilbert     dc->vmsd = &vmstate_virtio_net;
3393125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
3394e6f746b3SAndreas Färber     vdc->realize = virtio_net_device_realize;
3395306ec6c3SAndreas Färber     vdc->unrealize = virtio_net_device_unrealize;
339617ec5a86SKONRAD Frederic     vdc->get_config = virtio_net_get_config;
339717ec5a86SKONRAD Frederic     vdc->set_config = virtio_net_set_config;
339817ec5a86SKONRAD Frederic     vdc->get_features = virtio_net_get_features;
339917ec5a86SKONRAD Frederic     vdc->set_features = virtio_net_set_features;
340017ec5a86SKONRAD Frederic     vdc->bad_features = virtio_net_bad_features;
340117ec5a86SKONRAD Frederic     vdc->reset = virtio_net_reset;
340217ec5a86SKONRAD Frederic     vdc->set_status = virtio_net_set_status;
340317ec5a86SKONRAD Frederic     vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
340417ec5a86SKONRAD Frederic     vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
34052a083ffdSMichael S. Tsirkin     vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO);
34067788c3f2SMikhail Sennikovsky     vdc->post_load = virtio_net_post_load_virtio;
3407982b78c5SDr. David Alan Gilbert     vdc->vmsd = &vmstate_virtio_net_device;
34089711cd0dSJens Freimann     vdc->primary_unplug_pending = primary_unplug_pending;
340917ec5a86SKONRAD Frederic }
341017ec5a86SKONRAD Frederic 
341117ec5a86SKONRAD Frederic static const TypeInfo virtio_net_info = {
341217ec5a86SKONRAD Frederic     .name = TYPE_VIRTIO_NET,
341317ec5a86SKONRAD Frederic     .parent = TYPE_VIRTIO_DEVICE,
341417ec5a86SKONRAD Frederic     .instance_size = sizeof(VirtIONet),
341517ec5a86SKONRAD Frederic     .instance_init = virtio_net_instance_init,
341617ec5a86SKONRAD Frederic     .class_init = virtio_net_class_init,
341717ec5a86SKONRAD Frederic };
341817ec5a86SKONRAD Frederic 
341917ec5a86SKONRAD Frederic static void virtio_register_types(void)
342017ec5a86SKONRAD Frederic {
342117ec5a86SKONRAD Frederic     type_register_static(&virtio_net_info);
342217ec5a86SKONRAD Frederic }
342317ec5a86SKONRAD Frederic 
342417ec5a86SKONRAD Frederic type_init(virtio_register_types)
3425