xref: /openbmc/qemu/hw/net/virtio-net.c (revision 2f780b6a91fe99652266004bf78191ceddfae09c)
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"
156e790746SPaolo Bonzini #include "qemu/iov.h"
16db725815SMarkus Armbruster #include "qemu/main-loop.h"
170b8fa32fSMarkus Armbruster #include "qemu/module.h"
186e790746SPaolo Bonzini #include "hw/virtio/virtio.h"
196e790746SPaolo Bonzini #include "net/net.h"
206e790746SPaolo Bonzini #include "net/checksum.h"
216e790746SPaolo Bonzini #include "net/tap.h"
226e790746SPaolo Bonzini #include "qemu/error-report.h"
236e790746SPaolo Bonzini #include "qemu/timer.h"
246e790746SPaolo Bonzini #include "hw/virtio/virtio-net.h"
256e790746SPaolo Bonzini #include "net/vhost_net.h"
269d8c6a25SDr. David Alan Gilbert #include "net/announce.h"
2717ec5a86SKONRAD Frederic #include "hw/virtio/virtio-bus.h"
28e688df6bSMarkus Armbruster #include "qapi/error.h"
299af23989SMarkus Armbruster #include "qapi/qapi-events-net.h"
30a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
311399c60dSRusty Russell #include "hw/virtio/virtio-access.h"
32f8d806c9SJuan Quintela #include "migration/misc.h"
339473939eSJason Baron #include "standard-headers/linux/ethtool.h"
34*2f780b6aSMarkus Armbruster #include "sysemu/sysemu.h"
359d8c6a25SDr. David Alan Gilbert #include "trace.h"
366e790746SPaolo Bonzini 
376e790746SPaolo Bonzini #define VIRTIO_NET_VM_VERSION    11
386e790746SPaolo Bonzini 
396e790746SPaolo Bonzini #define MAC_TABLE_ENTRIES    64
406e790746SPaolo Bonzini #define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
416e790746SPaolo Bonzini 
421c0fbfa3SMichael S. Tsirkin /* previously fixed value */
431c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256
449b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256
459b02e161SWei Wang 
461c0fbfa3SMichael S. Tsirkin /* for now, only allow larger queues; with virtio-1, guest can downsize */
471c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE
489b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE
491c0fbfa3SMichael S. Tsirkin 
502974e916SYuri Benditovich #define VIRTIO_NET_IP4_ADDR_SIZE   8        /* ipv4 saddr + daddr */
512974e916SYuri Benditovich 
522974e916SYuri Benditovich #define VIRTIO_NET_TCP_FLAG         0x3F
532974e916SYuri Benditovich #define VIRTIO_NET_TCP_HDR_LENGTH   0xF000
542974e916SYuri Benditovich 
552974e916SYuri Benditovich /* IPv4 max payload, 16 bits in the header */
562974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP4_PAYLOAD (65535 - sizeof(struct ip_header))
572974e916SYuri Benditovich #define VIRTIO_NET_MAX_TCP_PAYLOAD 65535
582974e916SYuri Benditovich 
592974e916SYuri Benditovich /* header length value in ip header without option */
602974e916SYuri Benditovich #define VIRTIO_NET_IP4_HEADER_LENGTH 5
612974e916SYuri Benditovich 
622974e916SYuri Benditovich #define VIRTIO_NET_IP6_ADDR_SIZE   32      /* ipv6 saddr + daddr */
632974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP6_PAYLOAD VIRTIO_NET_MAX_TCP_PAYLOAD
642974e916SYuri Benditovich 
652974e916SYuri Benditovich /* Purge coalesced packets timer interval, This value affects the performance
662974e916SYuri Benditovich    a lot, and should be tuned carefully, '300000'(300us) is the recommended
672974e916SYuri Benditovich    value to pass the WHQL test, '50000' can gain 2x netperf throughput with
682974e916SYuri Benditovich    tso/gso/gro 'off'. */
692974e916SYuri Benditovich #define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000
702974e916SYuri Benditovich 
712974e916SYuri Benditovich /* temporary until standard header include it */
722974e916SYuri Benditovich #if !defined(VIRTIO_NET_HDR_F_RSC_INFO)
732974e916SYuri Benditovich 
742974e916SYuri Benditovich #define VIRTIO_NET_HDR_F_RSC_INFO  4 /* rsc_ext data in csum_ fields */
75d47e5e31SYuri Benditovich #define VIRTIO_NET_F_RSC_EXT       61
762974e916SYuri Benditovich 
772974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_packets(
782974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
792974e916SYuri Benditovich {
802974e916SYuri Benditovich     return &hdr->csum_start;
812974e916SYuri Benditovich }
822974e916SYuri Benditovich 
832974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
842974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
852974e916SYuri Benditovich {
862974e916SYuri Benditovich     return &hdr->csum_offset;
872974e916SYuri Benditovich }
882974e916SYuri Benditovich 
892974e916SYuri Benditovich #endif
902974e916SYuri Benditovich 
916e790746SPaolo Bonzini static VirtIOFeature feature_sizes[] = {
92127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MAC,
93ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, mac)},
94127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_STATUS,
95ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, status)},
96127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MQ,
97ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, max_virtqueue_pairs)},
98127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MTU,
99ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, mtu)},
1009473939eSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
101ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, duplex)},
1026e790746SPaolo Bonzini     {}
1036e790746SPaolo Bonzini };
1046e790746SPaolo Bonzini 
1056e790746SPaolo Bonzini static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc)
1066e790746SPaolo Bonzini {
1076e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
1086e790746SPaolo Bonzini 
1096e790746SPaolo Bonzini     return &n->vqs[nc->queue_index];
1106e790746SPaolo Bonzini }
1116e790746SPaolo Bonzini 
1126e790746SPaolo Bonzini static int vq2q(int queue_index)
1136e790746SPaolo Bonzini {
1146e790746SPaolo Bonzini     return queue_index / 2;
1156e790746SPaolo Bonzini }
1166e790746SPaolo Bonzini 
1176e790746SPaolo Bonzini /* TODO
1186e790746SPaolo Bonzini  * - we could suppress RX interrupt if we were so inclined.
1196e790746SPaolo Bonzini  */
1206e790746SPaolo Bonzini 
1216e790746SPaolo Bonzini static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
1226e790746SPaolo Bonzini {
12317a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1246e790746SPaolo Bonzini     struct virtio_net_config netcfg;
1256e790746SPaolo Bonzini 
1261399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.status, n->status);
1271399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues);
128a93e599dSMaxime Coquelin     virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu);
1296e790746SPaolo Bonzini     memcpy(netcfg.mac, n->mac, ETH_ALEN);
1309473939eSJason Baron     virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed);
1319473939eSJason Baron     netcfg.duplex = n->net_conf.duplex;
1326e790746SPaolo Bonzini     memcpy(config, &netcfg, n->config_size);
1336e790746SPaolo Bonzini }
1346e790746SPaolo Bonzini 
1356e790746SPaolo Bonzini static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
1366e790746SPaolo Bonzini {
13717a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1386e790746SPaolo Bonzini     struct virtio_net_config netcfg = {};
1396e790746SPaolo Bonzini 
1406e790746SPaolo Bonzini     memcpy(&netcfg, config, n->config_size);
1416e790746SPaolo Bonzini 
14295129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
14395129d6fSCornelia Huck         !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) &&
1446e790746SPaolo Bonzini         memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
1456e790746SPaolo Bonzini         memcpy(n->mac, netcfg.mac, ETH_ALEN);
1466e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
1476e790746SPaolo Bonzini     }
1486e790746SPaolo Bonzini }
1496e790746SPaolo Bonzini 
1506e790746SPaolo Bonzini static bool virtio_net_started(VirtIONet *n, uint8_t status)
1516e790746SPaolo Bonzini {
15217a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1536e790746SPaolo Bonzini     return (status & VIRTIO_CONFIG_S_DRIVER_OK) &&
15417a0ca55SKONRAD Frederic         (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running;
1556e790746SPaolo Bonzini }
1566e790746SPaolo Bonzini 
157b2c929f0SDr. David Alan Gilbert static void virtio_net_announce_notify(VirtIONet *net)
158b2c929f0SDr. David Alan Gilbert {
159b2c929f0SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(net);
160b2c929f0SDr. David Alan Gilbert     trace_virtio_net_announce_notify();
161b2c929f0SDr. David Alan Gilbert 
162b2c929f0SDr. David Alan Gilbert     net->status |= VIRTIO_NET_S_ANNOUNCE;
163b2c929f0SDr. David Alan Gilbert     virtio_notify_config(vdev);
164b2c929f0SDr. David Alan Gilbert }
165b2c929f0SDr. David Alan Gilbert 
166f57fcf70SJason Wang static void virtio_net_announce_timer(void *opaque)
167f57fcf70SJason Wang {
168f57fcf70SJason Wang     VirtIONet *n = opaque;
1699d8c6a25SDr. David Alan Gilbert     trace_virtio_net_announce_timer(n->announce_timer.round);
170f57fcf70SJason Wang 
1719d8c6a25SDr. David Alan Gilbert     n->announce_timer.round--;
172b2c929f0SDr. David Alan Gilbert     virtio_net_announce_notify(n);
173b2c929f0SDr. David Alan Gilbert }
174b2c929f0SDr. David Alan Gilbert 
175b2c929f0SDr. David Alan Gilbert static void virtio_net_announce(NetClientState *nc)
176b2c929f0SDr. David Alan Gilbert {
177b2c929f0SDr. David Alan Gilbert     VirtIONet *n = qemu_get_nic_opaque(nc);
178b2c929f0SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(n);
179b2c929f0SDr. David Alan Gilbert 
180b2c929f0SDr. David Alan Gilbert     /*
181b2c929f0SDr. David Alan Gilbert      * Make sure the virtio migration announcement timer isn't running
182b2c929f0SDr. David Alan Gilbert      * If it is, let it trigger announcement so that we do not cause
183b2c929f0SDr. David Alan Gilbert      * confusion.
184b2c929f0SDr. David Alan Gilbert      */
185b2c929f0SDr. David Alan Gilbert     if (n->announce_timer.round) {
186b2c929f0SDr. David Alan Gilbert         return;
187b2c929f0SDr. David Alan Gilbert     }
188b2c929f0SDr. David Alan Gilbert 
189b2c929f0SDr. David Alan Gilbert     if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
190b2c929f0SDr. David Alan Gilbert         virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
191b2c929f0SDr. David Alan Gilbert             virtio_net_announce_notify(n);
192b2c929f0SDr. David Alan Gilbert     }
193f57fcf70SJason Wang }
194f57fcf70SJason Wang 
1956e790746SPaolo Bonzini static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
1966e790746SPaolo Bonzini {
19717a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1986e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
1996e790746SPaolo Bonzini     int queues = n->multiqueue ? n->max_queues : 1;
2006e790746SPaolo Bonzini 
201ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
2026e790746SPaolo Bonzini         return;
2036e790746SPaolo Bonzini     }
2046e790746SPaolo Bonzini 
2058c1ac475SRadim Krčmář     if ((virtio_net_started(n, status) && !nc->peer->link_down) ==
2068c1ac475SRadim Krčmář         !!n->vhost_started) {
2076e790746SPaolo Bonzini         return;
2086e790746SPaolo Bonzini     }
2096e790746SPaolo Bonzini     if (!n->vhost_started) {
210086abc1cSMichael S. Tsirkin         int r, i;
211086abc1cSMichael S. Tsirkin 
2121bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
2131bfa316cSGreg Kurz             error_report("backend does not support %s vnet headers; "
2141bfa316cSGreg Kurz                          "falling back on userspace virtio",
2151bfa316cSGreg Kurz                          virtio_is_big_endian(vdev) ? "BE" : "LE");
2161bfa316cSGreg Kurz             return;
2171bfa316cSGreg Kurz         }
2181bfa316cSGreg Kurz 
219086abc1cSMichael S. Tsirkin         /* Any packets outstanding? Purge them to avoid touching rings
220086abc1cSMichael S. Tsirkin          * when vhost is running.
221086abc1cSMichael S. Tsirkin          */
222086abc1cSMichael S. Tsirkin         for (i = 0;  i < queues; i++) {
223086abc1cSMichael S. Tsirkin             NetClientState *qnc = qemu_get_subqueue(n->nic, i);
224086abc1cSMichael S. Tsirkin 
225086abc1cSMichael S. Tsirkin             /* Purge both directions: TX and RX. */
226086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->peer->incoming_queue, qnc);
227086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->incoming_queue, qnc->peer);
228086abc1cSMichael S. Tsirkin         }
229086abc1cSMichael S. Tsirkin 
230a93e599dSMaxime Coquelin         if (virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MTU)) {
231a93e599dSMaxime Coquelin             r = vhost_net_set_mtu(get_vhost_net(nc->peer), n->net_conf.mtu);
232a93e599dSMaxime Coquelin             if (r < 0) {
233a93e599dSMaxime Coquelin                 error_report("%uBytes MTU not supported by the backend",
234a93e599dSMaxime Coquelin                              n->net_conf.mtu);
235a93e599dSMaxime Coquelin 
236a93e599dSMaxime Coquelin                 return;
237a93e599dSMaxime Coquelin             }
238a93e599dSMaxime Coquelin         }
239a93e599dSMaxime Coquelin 
2406e790746SPaolo Bonzini         n->vhost_started = 1;
24117a0ca55SKONRAD Frederic         r = vhost_net_start(vdev, n->nic->ncs, queues);
2426e790746SPaolo Bonzini         if (r < 0) {
2436e790746SPaolo Bonzini             error_report("unable to start vhost net: %d: "
2446e790746SPaolo Bonzini                          "falling back on userspace virtio", -r);
2456e790746SPaolo Bonzini             n->vhost_started = 0;
2466e790746SPaolo Bonzini         }
2476e790746SPaolo Bonzini     } else {
24817a0ca55SKONRAD Frederic         vhost_net_stop(vdev, n->nic->ncs, queues);
2496e790746SPaolo Bonzini         n->vhost_started = 0;
2506e790746SPaolo Bonzini     }
2516e790746SPaolo Bonzini }
2526e790746SPaolo Bonzini 
2531bfa316cSGreg Kurz static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev,
2541bfa316cSGreg Kurz                                           NetClientState *peer,
2551bfa316cSGreg Kurz                                           bool enable)
2561bfa316cSGreg Kurz {
2571bfa316cSGreg Kurz     if (virtio_is_big_endian(vdev)) {
2581bfa316cSGreg Kurz         return qemu_set_vnet_be(peer, enable);
2591bfa316cSGreg Kurz     } else {
2601bfa316cSGreg Kurz         return qemu_set_vnet_le(peer, enable);
2611bfa316cSGreg Kurz     }
2621bfa316cSGreg Kurz }
2631bfa316cSGreg Kurz 
2641bfa316cSGreg Kurz static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs,
2651bfa316cSGreg Kurz                                        int queues, bool enable)
2661bfa316cSGreg Kurz {
2671bfa316cSGreg Kurz     int i;
2681bfa316cSGreg Kurz 
2691bfa316cSGreg Kurz     for (i = 0; i < queues; i++) {
2701bfa316cSGreg Kurz         if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 &&
2711bfa316cSGreg Kurz             enable) {
2721bfa316cSGreg Kurz             while (--i >= 0) {
2731bfa316cSGreg Kurz                 virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false);
2741bfa316cSGreg Kurz             }
2751bfa316cSGreg Kurz 
2761bfa316cSGreg Kurz             return true;
2771bfa316cSGreg Kurz         }
2781bfa316cSGreg Kurz     }
2791bfa316cSGreg Kurz 
2801bfa316cSGreg Kurz     return false;
2811bfa316cSGreg Kurz }
2821bfa316cSGreg Kurz 
2831bfa316cSGreg Kurz static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status)
2841bfa316cSGreg Kurz {
2851bfa316cSGreg Kurz     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2861bfa316cSGreg Kurz     int queues = n->multiqueue ? n->max_queues : 1;
2871bfa316cSGreg Kurz 
2881bfa316cSGreg Kurz     if (virtio_net_started(n, status)) {
2891bfa316cSGreg Kurz         /* Before using the device, we tell the network backend about the
2901bfa316cSGreg Kurz          * endianness to use when parsing vnet headers. If the backend
2911bfa316cSGreg Kurz          * can't do it, we fallback onto fixing the headers in the core
2921bfa316cSGreg Kurz          * virtio-net code.
2931bfa316cSGreg Kurz          */
2941bfa316cSGreg Kurz         n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs,
2951bfa316cSGreg Kurz                                                             queues, true);
2961bfa316cSGreg Kurz     } else if (virtio_net_started(n, vdev->status)) {
2971bfa316cSGreg Kurz         /* After using the device, we need to reset the network backend to
2981bfa316cSGreg Kurz          * the default (guest native endianness), otherwise the guest may
2991bfa316cSGreg Kurz          * lose network connectivity if it is rebooted into a different
3001bfa316cSGreg Kurz          * endianness.
3011bfa316cSGreg Kurz          */
3021bfa316cSGreg Kurz         virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false);
3031bfa316cSGreg Kurz     }
3041bfa316cSGreg Kurz }
3051bfa316cSGreg Kurz 
306283e2c2aSYuri Benditovich static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq)
307283e2c2aSYuri Benditovich {
308283e2c2aSYuri Benditovich     unsigned int dropped = virtqueue_drop_all(vq);
309283e2c2aSYuri Benditovich     if (dropped) {
310283e2c2aSYuri Benditovich         virtio_notify(vdev, vq);
311283e2c2aSYuri Benditovich     }
312283e2c2aSYuri Benditovich }
313283e2c2aSYuri Benditovich 
3146e790746SPaolo Bonzini static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
3156e790746SPaolo Bonzini {
31617a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
3176e790746SPaolo Bonzini     VirtIONetQueue *q;
3186e790746SPaolo Bonzini     int i;
3196e790746SPaolo Bonzini     uint8_t queue_status;
3206e790746SPaolo Bonzini 
3211bfa316cSGreg Kurz     virtio_net_vnet_endian_status(n, status);
3226e790746SPaolo Bonzini     virtio_net_vhost_status(n, status);
3236e790746SPaolo Bonzini 
3246e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
32538705bb5SFam Zheng         NetClientState *ncs = qemu_get_subqueue(n->nic, i);
32638705bb5SFam Zheng         bool queue_started;
3276e790746SPaolo Bonzini         q = &n->vqs[i];
3286e790746SPaolo Bonzini 
3296e790746SPaolo Bonzini         if ((!n->multiqueue && i != 0) || i >= n->curr_queues) {
3306e790746SPaolo Bonzini             queue_status = 0;
3316e790746SPaolo Bonzini         } else {
3326e790746SPaolo Bonzini             queue_status = status;
3336e790746SPaolo Bonzini         }
33438705bb5SFam Zheng         queue_started =
33538705bb5SFam Zheng             virtio_net_started(n, queue_status) && !n->vhost_started;
33638705bb5SFam Zheng 
33738705bb5SFam Zheng         if (queue_started) {
33838705bb5SFam Zheng             qemu_flush_queued_packets(ncs);
33938705bb5SFam Zheng         }
3406e790746SPaolo Bonzini 
3416e790746SPaolo Bonzini         if (!q->tx_waiting) {
3426e790746SPaolo Bonzini             continue;
3436e790746SPaolo Bonzini         }
3446e790746SPaolo Bonzini 
34538705bb5SFam Zheng         if (queue_started) {
3466e790746SPaolo Bonzini             if (q->tx_timer) {
347bc72ad67SAlex Bligh                 timer_mod(q->tx_timer,
348bc72ad67SAlex Bligh                                qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
3496e790746SPaolo Bonzini             } else {
3506e790746SPaolo Bonzini                 qemu_bh_schedule(q->tx_bh);
3516e790746SPaolo Bonzini             }
3526e790746SPaolo Bonzini         } else {
3536e790746SPaolo Bonzini             if (q->tx_timer) {
354bc72ad67SAlex Bligh                 timer_del(q->tx_timer);
3556e790746SPaolo Bonzini             } else {
3566e790746SPaolo Bonzini                 qemu_bh_cancel(q->tx_bh);
3576e790746SPaolo Bonzini             }
358283e2c2aSYuri Benditovich             if ((n->status & VIRTIO_NET_S_LINK_UP) == 0 &&
35970e53e6eSJason Wang                 (queue_status & VIRTIO_CONFIG_S_DRIVER_OK) &&
36070e53e6eSJason Wang                 vdev->vm_running) {
361283e2c2aSYuri Benditovich                 /* if tx is waiting we are likely have some packets in tx queue
362283e2c2aSYuri Benditovich                  * and disabled notification */
363283e2c2aSYuri Benditovich                 q->tx_waiting = 0;
364283e2c2aSYuri Benditovich                 virtio_queue_set_notification(q->tx_vq, 1);
365283e2c2aSYuri Benditovich                 virtio_net_drop_tx_queue_data(vdev, q->tx_vq);
366283e2c2aSYuri Benditovich             }
3676e790746SPaolo Bonzini         }
3686e790746SPaolo Bonzini     }
3696e790746SPaolo Bonzini }
3706e790746SPaolo Bonzini 
3716e790746SPaolo Bonzini static void virtio_net_set_link_status(NetClientState *nc)
3726e790746SPaolo Bonzini {
3736e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
37417a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
3756e790746SPaolo Bonzini     uint16_t old_status = n->status;
3766e790746SPaolo Bonzini 
3776e790746SPaolo Bonzini     if (nc->link_down)
3786e790746SPaolo Bonzini         n->status &= ~VIRTIO_NET_S_LINK_UP;
3796e790746SPaolo Bonzini     else
3806e790746SPaolo Bonzini         n->status |= VIRTIO_NET_S_LINK_UP;
3816e790746SPaolo Bonzini 
3826e790746SPaolo Bonzini     if (n->status != old_status)
38317a0ca55SKONRAD Frederic         virtio_notify_config(vdev);
3846e790746SPaolo Bonzini 
38517a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
3866e790746SPaolo Bonzini }
3876e790746SPaolo Bonzini 
388b1be4280SAmos Kong static void rxfilter_notify(NetClientState *nc)
389b1be4280SAmos Kong {
390b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
391b1be4280SAmos Kong 
392b1be4280SAmos Kong     if (nc->rxfilter_notify_enabled) {
39396e35046SAmos Kong         gchar *path = object_get_canonical_path(OBJECT(n->qdev));
39406150279SWenchao Xia         qapi_event_send_nic_rx_filter_changed(!!n->netclient_name,
3953ab72385SPeter Xu                                               n->netclient_name, path);
39696e35046SAmos Kong         g_free(path);
397b1be4280SAmos Kong 
398b1be4280SAmos Kong         /* disable event notification to avoid events flooding */
399b1be4280SAmos Kong         nc->rxfilter_notify_enabled = 0;
400b1be4280SAmos Kong     }
401b1be4280SAmos Kong }
402b1be4280SAmos Kong 
403f7bc8ef8SAmos Kong static intList *get_vlan_table(VirtIONet *n)
404f7bc8ef8SAmos Kong {
405f7bc8ef8SAmos Kong     intList *list, *entry;
406f7bc8ef8SAmos Kong     int i, j;
407f7bc8ef8SAmos Kong 
408f7bc8ef8SAmos Kong     list = NULL;
409f7bc8ef8SAmos Kong     for (i = 0; i < MAX_VLAN >> 5; i++) {
410f7bc8ef8SAmos Kong         for (j = 0; n->vlans[i] && j <= 0x1f; j++) {
411f7bc8ef8SAmos Kong             if (n->vlans[i] & (1U << j)) {
412f7bc8ef8SAmos Kong                 entry = g_malloc0(sizeof(*entry));
413f7bc8ef8SAmos Kong                 entry->value = (i << 5) + j;
414f7bc8ef8SAmos Kong                 entry->next = list;
415f7bc8ef8SAmos Kong                 list = entry;
416f7bc8ef8SAmos Kong             }
417f7bc8ef8SAmos Kong         }
418f7bc8ef8SAmos Kong     }
419f7bc8ef8SAmos Kong 
420f7bc8ef8SAmos Kong     return list;
421f7bc8ef8SAmos Kong }
422f7bc8ef8SAmos Kong 
423b1be4280SAmos Kong static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
424b1be4280SAmos Kong {
425b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
426f7bc8ef8SAmos Kong     VirtIODevice *vdev = VIRTIO_DEVICE(n);
427b1be4280SAmos Kong     RxFilterInfo *info;
428b1be4280SAmos Kong     strList *str_list, *entry;
429f7bc8ef8SAmos Kong     int i;
430b1be4280SAmos Kong 
431b1be4280SAmos Kong     info = g_malloc0(sizeof(*info));
432b1be4280SAmos Kong     info->name = g_strdup(nc->name);
433b1be4280SAmos Kong     info->promiscuous = n->promisc;
434b1be4280SAmos Kong 
435b1be4280SAmos Kong     if (n->nouni) {
436b1be4280SAmos Kong         info->unicast = RX_STATE_NONE;
437b1be4280SAmos Kong     } else if (n->alluni) {
438b1be4280SAmos Kong         info->unicast = RX_STATE_ALL;
439b1be4280SAmos Kong     } else {
440b1be4280SAmos Kong         info->unicast = RX_STATE_NORMAL;
441b1be4280SAmos Kong     }
442b1be4280SAmos Kong 
443b1be4280SAmos Kong     if (n->nomulti) {
444b1be4280SAmos Kong         info->multicast = RX_STATE_NONE;
445b1be4280SAmos Kong     } else if (n->allmulti) {
446b1be4280SAmos Kong         info->multicast = RX_STATE_ALL;
447b1be4280SAmos Kong     } else {
448b1be4280SAmos Kong         info->multicast = RX_STATE_NORMAL;
449b1be4280SAmos Kong     }
450b1be4280SAmos Kong 
451b1be4280SAmos Kong     info->broadcast_allowed = n->nobcast;
452b1be4280SAmos Kong     info->multicast_overflow = n->mac_table.multi_overflow;
453b1be4280SAmos Kong     info->unicast_overflow = n->mac_table.uni_overflow;
454b1be4280SAmos Kong 
455b0575ba4SScott Feldman     info->main_mac = qemu_mac_strdup_printf(n->mac);
456b1be4280SAmos Kong 
457b1be4280SAmos Kong     str_list = NULL;
458b1be4280SAmos Kong     for (i = 0; i < n->mac_table.first_multi; i++) {
459b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
460b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
461b1be4280SAmos Kong         entry->next = str_list;
462b1be4280SAmos Kong         str_list = entry;
463b1be4280SAmos Kong     }
464b1be4280SAmos Kong     info->unicast_table = str_list;
465b1be4280SAmos Kong 
466b1be4280SAmos Kong     str_list = NULL;
467b1be4280SAmos Kong     for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
468b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
469b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
470b1be4280SAmos Kong         entry->next = str_list;
471b1be4280SAmos Kong         str_list = entry;
472b1be4280SAmos Kong     }
473b1be4280SAmos Kong     info->multicast_table = str_list;
474f7bc8ef8SAmos Kong     info->vlan_table = get_vlan_table(n);
475b1be4280SAmos Kong 
47695129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) {
477f7bc8ef8SAmos Kong         info->vlan = RX_STATE_ALL;
478f7bc8ef8SAmos Kong     } else if (!info->vlan_table) {
479f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NONE;
480f7bc8ef8SAmos Kong     } else {
481f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NORMAL;
482b1be4280SAmos Kong     }
483b1be4280SAmos Kong 
484b1be4280SAmos Kong     /* enable event notification after query */
485b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
486b1be4280SAmos Kong 
487b1be4280SAmos Kong     return info;
488b1be4280SAmos Kong }
489b1be4280SAmos Kong 
4906e790746SPaolo Bonzini static void virtio_net_reset(VirtIODevice *vdev)
4916e790746SPaolo Bonzini {
49217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
49394b52958SGreg Kurz     int i;
4946e790746SPaolo Bonzini 
4956e790746SPaolo Bonzini     /* Reset back to compatibility mode */
4966e790746SPaolo Bonzini     n->promisc = 1;
4976e790746SPaolo Bonzini     n->allmulti = 0;
4986e790746SPaolo Bonzini     n->alluni = 0;
4996e790746SPaolo Bonzini     n->nomulti = 0;
5006e790746SPaolo Bonzini     n->nouni = 0;
5016e790746SPaolo Bonzini     n->nobcast = 0;
5026e790746SPaolo Bonzini     /* multiqueue is disabled by default */
5036e790746SPaolo Bonzini     n->curr_queues = 1;
5049d8c6a25SDr. David Alan Gilbert     timer_del(n->announce_timer.tm);
5059d8c6a25SDr. David Alan Gilbert     n->announce_timer.round = 0;
506f57fcf70SJason Wang     n->status &= ~VIRTIO_NET_S_ANNOUNCE;
5076e790746SPaolo Bonzini 
5086e790746SPaolo Bonzini     /* Flush any MAC and VLAN filter table state */
5096e790746SPaolo Bonzini     n->mac_table.in_use = 0;
5106e790746SPaolo Bonzini     n->mac_table.first_multi = 0;
5116e790746SPaolo Bonzini     n->mac_table.multi_overflow = 0;
5126e790746SPaolo Bonzini     n->mac_table.uni_overflow = 0;
5136e790746SPaolo Bonzini     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
5146e790746SPaolo Bonzini     memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac));
515702d66a8SMichael S. Tsirkin     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
5166e790746SPaolo Bonzini     memset(n->vlans, 0, MAX_VLAN >> 3);
51794b52958SGreg Kurz 
51894b52958SGreg Kurz     /* Flush any async TX */
51994b52958SGreg Kurz     for (i = 0;  i < n->max_queues; i++) {
52094b52958SGreg Kurz         NetClientState *nc = qemu_get_subqueue(n->nic, i);
52194b52958SGreg Kurz 
52294b52958SGreg Kurz         if (nc->peer) {
52394b52958SGreg Kurz             qemu_flush_or_purge_queued_packets(nc->peer, true);
52494b52958SGreg Kurz             assert(!virtio_net_get_subqueue(nc)->async_tx.elem);
52594b52958SGreg Kurz         }
52694b52958SGreg Kurz     }
5276e790746SPaolo Bonzini }
5286e790746SPaolo Bonzini 
5296e790746SPaolo Bonzini static void peer_test_vnet_hdr(VirtIONet *n)
5306e790746SPaolo Bonzini {
5316e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
5326e790746SPaolo Bonzini     if (!nc->peer) {
5336e790746SPaolo Bonzini         return;
5346e790746SPaolo Bonzini     }
5356e790746SPaolo Bonzini 
536d6085e3aSStefan Hajnoczi     n->has_vnet_hdr = qemu_has_vnet_hdr(nc->peer);
5376e790746SPaolo Bonzini }
5386e790746SPaolo Bonzini 
5396e790746SPaolo Bonzini static int peer_has_vnet_hdr(VirtIONet *n)
5406e790746SPaolo Bonzini {
5416e790746SPaolo Bonzini     return n->has_vnet_hdr;
5426e790746SPaolo Bonzini }
5436e790746SPaolo Bonzini 
5446e790746SPaolo Bonzini static int peer_has_ufo(VirtIONet *n)
5456e790746SPaolo Bonzini {
5466e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n))
5476e790746SPaolo Bonzini         return 0;
5486e790746SPaolo Bonzini 
549d6085e3aSStefan Hajnoczi     n->has_ufo = qemu_has_ufo(qemu_get_queue(n->nic)->peer);
5506e790746SPaolo Bonzini 
5516e790746SPaolo Bonzini     return n->has_ufo;
5526e790746SPaolo Bonzini }
5536e790746SPaolo Bonzini 
554bb9d17f8SCornelia Huck static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
555bb9d17f8SCornelia Huck                                        int version_1)
5566e790746SPaolo Bonzini {
5576e790746SPaolo Bonzini     int i;
5586e790746SPaolo Bonzini     NetClientState *nc;
5596e790746SPaolo Bonzini 
5606e790746SPaolo Bonzini     n->mergeable_rx_bufs = mergeable_rx_bufs;
5616e790746SPaolo Bonzini 
562bb9d17f8SCornelia Huck     if (version_1) {
563bb9d17f8SCornelia Huck         n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
564bb9d17f8SCornelia Huck     } else {
5656e790746SPaolo Bonzini         n->guest_hdr_len = n->mergeable_rx_bufs ?
566bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr_mrg_rxbuf) :
567bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr);
568bb9d17f8SCornelia Huck     }
5696e790746SPaolo Bonzini 
5706e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
5716e790746SPaolo Bonzini         nc = qemu_get_subqueue(n->nic, i);
5726e790746SPaolo Bonzini 
5736e790746SPaolo Bonzini         if (peer_has_vnet_hdr(n) &&
574d6085e3aSStefan Hajnoczi             qemu_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) {
575d6085e3aSStefan Hajnoczi             qemu_set_vnet_hdr_len(nc->peer, n->guest_hdr_len);
5766e790746SPaolo Bonzini             n->host_hdr_len = n->guest_hdr_len;
5776e790746SPaolo Bonzini         }
5786e790746SPaolo Bonzini     }
5796e790746SPaolo Bonzini }
5806e790746SPaolo Bonzini 
5812eef278bSMichael S. Tsirkin static int virtio_net_max_tx_queue_size(VirtIONet *n)
5822eef278bSMichael S. Tsirkin {
5832eef278bSMichael S. Tsirkin     NetClientState *peer = n->nic_conf.peers.ncs[0];
5842eef278bSMichael S. Tsirkin 
5852eef278bSMichael S. Tsirkin     /*
5862eef278bSMichael S. Tsirkin      * Backends other than vhost-user don't support max queue size.
5872eef278bSMichael S. Tsirkin      */
5882eef278bSMichael S. Tsirkin     if (!peer) {
5892eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
5902eef278bSMichael S. Tsirkin     }
5912eef278bSMichael S. Tsirkin 
5922eef278bSMichael S. Tsirkin     if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) {
5932eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
5942eef278bSMichael S. Tsirkin     }
5952eef278bSMichael S. Tsirkin 
5962eef278bSMichael S. Tsirkin     return VIRTQUEUE_MAX_SIZE;
5972eef278bSMichael S. Tsirkin }
5982eef278bSMichael S. Tsirkin 
5996e790746SPaolo Bonzini static int peer_attach(VirtIONet *n, int index)
6006e790746SPaolo Bonzini {
6016e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
6026e790746SPaolo Bonzini 
6036e790746SPaolo Bonzini     if (!nc->peer) {
6046e790746SPaolo Bonzini         return 0;
6056e790746SPaolo Bonzini     }
6066e790746SPaolo Bonzini 
607f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
6087263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 1);
6097263a0adSChangchun Ouyang     }
6107263a0adSChangchun Ouyang 
611f394b2e2SEric Blake     if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
6126e790746SPaolo Bonzini         return 0;
6136e790746SPaolo Bonzini     }
6146e790746SPaolo Bonzini 
6151074b879SJason Wang     if (n->max_queues == 1) {
6161074b879SJason Wang         return 0;
6171074b879SJason Wang     }
6181074b879SJason Wang 
6196e790746SPaolo Bonzini     return tap_enable(nc->peer);
6206e790746SPaolo Bonzini }
6216e790746SPaolo Bonzini 
6226e790746SPaolo Bonzini static int peer_detach(VirtIONet *n, int index)
6236e790746SPaolo Bonzini {
6246e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
6256e790746SPaolo Bonzini 
6266e790746SPaolo Bonzini     if (!nc->peer) {
6276e790746SPaolo Bonzini         return 0;
6286e790746SPaolo Bonzini     }
6296e790746SPaolo Bonzini 
630f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
6317263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 0);
6327263a0adSChangchun Ouyang     }
6337263a0adSChangchun Ouyang 
634f394b2e2SEric Blake     if (nc->peer->info->type !=  NET_CLIENT_DRIVER_TAP) {
6356e790746SPaolo Bonzini         return 0;
6366e790746SPaolo Bonzini     }
6376e790746SPaolo Bonzini 
6386e790746SPaolo Bonzini     return tap_disable(nc->peer);
6396e790746SPaolo Bonzini }
6406e790746SPaolo Bonzini 
6416e790746SPaolo Bonzini static void virtio_net_set_queues(VirtIONet *n)
6426e790746SPaolo Bonzini {
6436e790746SPaolo Bonzini     int i;
644ddfa83eaSJoel Stanley     int r;
6456e790746SPaolo Bonzini 
64668b5f314SYuri Benditovich     if (n->nic->peer_deleted) {
64768b5f314SYuri Benditovich         return;
64868b5f314SYuri Benditovich     }
64968b5f314SYuri Benditovich 
6506e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
6516e790746SPaolo Bonzini         if (i < n->curr_queues) {
652ddfa83eaSJoel Stanley             r = peer_attach(n, i);
653ddfa83eaSJoel Stanley             assert(!r);
6546e790746SPaolo Bonzini         } else {
655ddfa83eaSJoel Stanley             r = peer_detach(n, i);
656ddfa83eaSJoel Stanley             assert(!r);
6576e790746SPaolo Bonzini         }
6586e790746SPaolo Bonzini     }
6596e790746SPaolo Bonzini }
6606e790746SPaolo Bonzini 
661ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue);
6626e790746SPaolo Bonzini 
6639d5b731dSJason Wang static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
6649d5b731dSJason Wang                                         Error **errp)
6656e790746SPaolo Bonzini {
66617a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
6676e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
6686e790746SPaolo Bonzini 
669da3e8a23SShannon Zhao     /* Firstly sync all virtio-net possible supported features */
670da3e8a23SShannon Zhao     features |= n->host_features;
671da3e8a23SShannon Zhao 
6720cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
6736e790746SPaolo Bonzini 
6746e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n)) {
6750cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_CSUM);
6760cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4);
6770cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6);
6780cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN);
6796e790746SPaolo Bonzini 
6800cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM);
6810cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4);
6820cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6);
6830cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN);
6846e790746SPaolo Bonzini     }
6856e790746SPaolo Bonzini 
6866e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) {
6870cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO);
6880cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO);
6896e790746SPaolo Bonzini     }
6906e790746SPaolo Bonzini 
691ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
6926e790746SPaolo Bonzini         return features;
6936e790746SPaolo Bonzini     }
6942974e916SYuri Benditovich 
69575ebec11SMaxime Coquelin     features = vhost_net_get_features(get_vhost_net(nc->peer), features);
69675ebec11SMaxime Coquelin     vdev->backend_features = features;
69775ebec11SMaxime Coquelin 
69875ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
69975ebec11SMaxime Coquelin             (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) {
70075ebec11SMaxime Coquelin         features |= (1ULL << VIRTIO_NET_F_MTU);
70175ebec11SMaxime Coquelin     }
70275ebec11SMaxime Coquelin 
70375ebec11SMaxime Coquelin     return features;
7046e790746SPaolo Bonzini }
7056e790746SPaolo Bonzini 
706019a3edbSGerd Hoffmann static uint64_t virtio_net_bad_features(VirtIODevice *vdev)
7076e790746SPaolo Bonzini {
708019a3edbSGerd Hoffmann     uint64_t features = 0;
7096e790746SPaolo Bonzini 
7106e790746SPaolo Bonzini     /* Linux kernel 2.6.25.  It understood MAC (as everyone must),
7116e790746SPaolo Bonzini      * but also these: */
7120cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
7130cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_CSUM);
7140cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO4);
7150cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO6);
7160cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_ECN);
7176e790746SPaolo Bonzini 
7186e790746SPaolo Bonzini     return features;
7196e790746SPaolo Bonzini }
7206e790746SPaolo Bonzini 
721644c9858SDmitry Fleytman static void virtio_net_apply_guest_offloads(VirtIONet *n)
722644c9858SDmitry Fleytman {
723ad37bb3bSStefan Hajnoczi     qemu_set_offload(qemu_get_queue(n->nic)->peer,
724644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)),
725644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)),
726644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)),
727644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)),
728644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)));
729644c9858SDmitry Fleytman }
730644c9858SDmitry Fleytman 
731644c9858SDmitry Fleytman static uint64_t virtio_net_guest_offloads_by_features(uint32_t features)
732644c9858SDmitry Fleytman {
733644c9858SDmitry Fleytman     static const uint64_t guest_offloads_mask =
734644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_CSUM) |
735644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO4) |
736644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO6) |
737644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_ECN)  |
738644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_UFO);
739644c9858SDmitry Fleytman 
740644c9858SDmitry Fleytman     return guest_offloads_mask & features;
741644c9858SDmitry Fleytman }
742644c9858SDmitry Fleytman 
743644c9858SDmitry Fleytman static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n)
744644c9858SDmitry Fleytman {
745644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
746644c9858SDmitry Fleytman     return virtio_net_guest_offloads_by_features(vdev->guest_features);
747644c9858SDmitry Fleytman }
748644c9858SDmitry Fleytman 
749d5aaa1b0SGerd Hoffmann static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
7506e790746SPaolo Bonzini {
75117a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
7526e790746SPaolo Bonzini     int i;
7536e790746SPaolo Bonzini 
75475ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
75575ebec11SMaxime Coquelin             !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) {
75675ebec11SMaxime Coquelin         features &= ~(1ULL << VIRTIO_NET_F_MTU);
75775ebec11SMaxime Coquelin     }
75875ebec11SMaxime Coquelin 
759ef546f12SCornelia Huck     virtio_net_set_multiqueue(n,
76095129d6fSCornelia Huck                               virtio_has_feature(features, VIRTIO_NET_F_MQ));
7616e790746SPaolo Bonzini 
762ef546f12SCornelia Huck     virtio_net_set_mrg_rx_bufs(n,
76395129d6fSCornelia Huck                                virtio_has_feature(features,
764bb9d17f8SCornelia Huck                                                   VIRTIO_NET_F_MRG_RXBUF),
76595129d6fSCornelia Huck                                virtio_has_feature(features,
766bb9d17f8SCornelia Huck                                                   VIRTIO_F_VERSION_1));
7676e790746SPaolo Bonzini 
7682974e916SYuri Benditovich     n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
7692974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4);
7702974e916SYuri Benditovich     n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
7712974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6);
7722974e916SYuri Benditovich 
7736e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
774644c9858SDmitry Fleytman         n->curr_guest_offloads =
775644c9858SDmitry Fleytman             virtio_net_guest_offloads_by_features(features);
776644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
7776e790746SPaolo Bonzini     }
7786e790746SPaolo Bonzini 
7796e790746SPaolo Bonzini     for (i = 0;  i < n->max_queues; i++) {
7806e790746SPaolo Bonzini         NetClientState *nc = qemu_get_subqueue(n->nic, i);
7816e790746SPaolo Bonzini 
782ed8b4afeSNikolay Nikolaev         if (!get_vhost_net(nc->peer)) {
7836e790746SPaolo Bonzini             continue;
7846e790746SPaolo Bonzini         }
785ed8b4afeSNikolay Nikolaev         vhost_net_ack_features(get_vhost_net(nc->peer), features);
7866e790746SPaolo Bonzini     }
7870b1eaa88SStefan Fritsch 
78895129d6fSCornelia Huck     if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
7890b1eaa88SStefan Fritsch         memset(n->vlans, 0, MAX_VLAN >> 3);
7900b1eaa88SStefan Fritsch     } else {
7910b1eaa88SStefan Fritsch         memset(n->vlans, 0xff, MAX_VLAN >> 3);
7920b1eaa88SStefan Fritsch     }
7936e790746SPaolo Bonzini }
7946e790746SPaolo Bonzini 
7956e790746SPaolo Bonzini static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
7966e790746SPaolo Bonzini                                      struct iovec *iov, unsigned int iov_cnt)
7976e790746SPaolo Bonzini {
7986e790746SPaolo Bonzini     uint8_t on;
7996e790746SPaolo Bonzini     size_t s;
800b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
8016e790746SPaolo Bonzini 
8026e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on));
8036e790746SPaolo Bonzini     if (s != sizeof(on)) {
8046e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
8056e790746SPaolo Bonzini     }
8066e790746SPaolo Bonzini 
8076e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) {
8086e790746SPaolo Bonzini         n->promisc = on;
8096e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) {
8106e790746SPaolo Bonzini         n->allmulti = on;
8116e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) {
8126e790746SPaolo Bonzini         n->alluni = on;
8136e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) {
8146e790746SPaolo Bonzini         n->nomulti = on;
8156e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) {
8166e790746SPaolo Bonzini         n->nouni = on;
8176e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) {
8186e790746SPaolo Bonzini         n->nobcast = on;
8196e790746SPaolo Bonzini     } else {
8206e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
8216e790746SPaolo Bonzini     }
8226e790746SPaolo Bonzini 
823b1be4280SAmos Kong     rxfilter_notify(nc);
824b1be4280SAmos Kong 
8256e790746SPaolo Bonzini     return VIRTIO_NET_OK;
8266e790746SPaolo Bonzini }
8276e790746SPaolo Bonzini 
828644c9858SDmitry Fleytman static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd,
829644c9858SDmitry Fleytman                                      struct iovec *iov, unsigned int iov_cnt)
830644c9858SDmitry Fleytman {
831644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
832644c9858SDmitry Fleytman     uint64_t offloads;
833644c9858SDmitry Fleytman     size_t s;
834644c9858SDmitry Fleytman 
83595129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
836644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
837644c9858SDmitry Fleytman     }
838644c9858SDmitry Fleytman 
839644c9858SDmitry Fleytman     s = iov_to_buf(iov, iov_cnt, 0, &offloads, sizeof(offloads));
840644c9858SDmitry Fleytman     if (s != sizeof(offloads)) {
841644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
842644c9858SDmitry Fleytman     }
843644c9858SDmitry Fleytman 
844644c9858SDmitry Fleytman     if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) {
845644c9858SDmitry Fleytman         uint64_t supported_offloads;
846644c9858SDmitry Fleytman 
847189ae6bbSJason Wang         offloads = virtio_ldq_p(vdev, &offloads);
848189ae6bbSJason Wang 
849644c9858SDmitry Fleytman         if (!n->has_vnet_hdr) {
850644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
851644c9858SDmitry Fleytman         }
852644c9858SDmitry Fleytman 
8532974e916SYuri Benditovich         n->rsc4_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
8542974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO4);
8552974e916SYuri Benditovich         n->rsc6_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
8562974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO6);
8572974e916SYuri Benditovich         virtio_clear_feature(&offloads, VIRTIO_NET_F_RSC_EXT);
8582974e916SYuri Benditovich 
859644c9858SDmitry Fleytman         supported_offloads = virtio_net_supported_guest_offloads(n);
860644c9858SDmitry Fleytman         if (offloads & ~supported_offloads) {
861644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
862644c9858SDmitry Fleytman         }
863644c9858SDmitry Fleytman 
864644c9858SDmitry Fleytman         n->curr_guest_offloads = offloads;
865644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
866644c9858SDmitry Fleytman 
867644c9858SDmitry Fleytman         return VIRTIO_NET_OK;
868644c9858SDmitry Fleytman     } else {
869644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
870644c9858SDmitry Fleytman     }
871644c9858SDmitry Fleytman }
872644c9858SDmitry Fleytman 
8736e790746SPaolo Bonzini static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
8746e790746SPaolo Bonzini                                  struct iovec *iov, unsigned int iov_cnt)
8756e790746SPaolo Bonzini {
8761399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
8776e790746SPaolo Bonzini     struct virtio_net_ctrl_mac mac_data;
8786e790746SPaolo Bonzini     size_t s;
879b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
8806e790746SPaolo Bonzini 
8816e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {
8826e790746SPaolo Bonzini         if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {
8836e790746SPaolo Bonzini             return VIRTIO_NET_ERR;
8846e790746SPaolo Bonzini         }
8856e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
8866e790746SPaolo Bonzini         assert(s == sizeof(n->mac));
8876e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
888b1be4280SAmos Kong         rxfilter_notify(nc);
889b1be4280SAmos Kong 
8906e790746SPaolo Bonzini         return VIRTIO_NET_OK;
8916e790746SPaolo Bonzini     }
8926e790746SPaolo Bonzini 
8936e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) {
8946e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
8956e790746SPaolo Bonzini     }
8966e790746SPaolo Bonzini 
897cae2e556SAmos Kong     int in_use = 0;
898cae2e556SAmos Kong     int first_multi = 0;
899cae2e556SAmos Kong     uint8_t uni_overflow = 0;
900cae2e556SAmos Kong     uint8_t multi_overflow = 0;
901cae2e556SAmos Kong     uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
9026e790746SPaolo Bonzini 
9036e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
9046e790746SPaolo Bonzini                    sizeof(mac_data.entries));
9051399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
9066e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
907b1be4280SAmos Kong         goto error;
9086e790746SPaolo Bonzini     }
9096e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
9106e790746SPaolo Bonzini 
9116e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {
912b1be4280SAmos Kong         goto error;
9136e790746SPaolo Bonzini     }
9146e790746SPaolo Bonzini 
9156e790746SPaolo Bonzini     if (mac_data.entries <= MAC_TABLE_ENTRIES) {
916cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, macs,
9176e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
9186e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
919b1be4280SAmos Kong             goto error;
9206e790746SPaolo Bonzini         }
921cae2e556SAmos Kong         in_use += mac_data.entries;
9226e790746SPaolo Bonzini     } else {
923cae2e556SAmos Kong         uni_overflow = 1;
9246e790746SPaolo Bonzini     }
9256e790746SPaolo Bonzini 
9266e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN);
9276e790746SPaolo Bonzini 
928cae2e556SAmos Kong     first_multi = in_use;
9296e790746SPaolo Bonzini 
9306e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
9316e790746SPaolo Bonzini                    sizeof(mac_data.entries));
9321399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
9336e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
934b1be4280SAmos Kong         goto error;
9356e790746SPaolo Bonzini     }
9366e790746SPaolo Bonzini 
9376e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
9386e790746SPaolo Bonzini 
9396e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {
940b1be4280SAmos Kong         goto error;
9416e790746SPaolo Bonzini     }
9426e790746SPaolo Bonzini 
943edc24385SMichael S. Tsirkin     if (mac_data.entries <= MAC_TABLE_ENTRIES - in_use) {
944cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN],
9456e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
9466e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
947b1be4280SAmos Kong             goto error;
9486e790746SPaolo Bonzini         }
949cae2e556SAmos Kong         in_use += mac_data.entries;
9506e790746SPaolo Bonzini     } else {
951cae2e556SAmos Kong         multi_overflow = 1;
9526e790746SPaolo Bonzini     }
9536e790746SPaolo Bonzini 
954cae2e556SAmos Kong     n->mac_table.in_use = in_use;
955cae2e556SAmos Kong     n->mac_table.first_multi = first_multi;
956cae2e556SAmos Kong     n->mac_table.uni_overflow = uni_overflow;
957cae2e556SAmos Kong     n->mac_table.multi_overflow = multi_overflow;
958cae2e556SAmos Kong     memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN);
959cae2e556SAmos Kong     g_free(macs);
960b1be4280SAmos Kong     rxfilter_notify(nc);
961b1be4280SAmos Kong 
9626e790746SPaolo Bonzini     return VIRTIO_NET_OK;
963b1be4280SAmos Kong 
964b1be4280SAmos Kong error:
965cae2e556SAmos Kong     g_free(macs);
966b1be4280SAmos Kong     return VIRTIO_NET_ERR;
9676e790746SPaolo Bonzini }
9686e790746SPaolo Bonzini 
9696e790746SPaolo Bonzini static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
9706e790746SPaolo Bonzini                                         struct iovec *iov, unsigned int iov_cnt)
9716e790746SPaolo Bonzini {
9721399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
9736e790746SPaolo Bonzini     uint16_t vid;
9746e790746SPaolo Bonzini     size_t s;
975b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
9766e790746SPaolo Bonzini 
9776e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid));
9781399c60dSRusty Russell     vid = virtio_lduw_p(vdev, &vid);
9796e790746SPaolo Bonzini     if (s != sizeof(vid)) {
9806e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9816e790746SPaolo Bonzini     }
9826e790746SPaolo Bonzini 
9836e790746SPaolo Bonzini     if (vid >= MAX_VLAN)
9846e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9856e790746SPaolo Bonzini 
9866e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
9876e790746SPaolo Bonzini         n->vlans[vid >> 5] |= (1U << (vid & 0x1f));
9886e790746SPaolo Bonzini     else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL)
9896e790746SPaolo Bonzini         n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f));
9906e790746SPaolo Bonzini     else
9916e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9926e790746SPaolo Bonzini 
993b1be4280SAmos Kong     rxfilter_notify(nc);
994b1be4280SAmos Kong 
9956e790746SPaolo Bonzini     return VIRTIO_NET_OK;
9966e790746SPaolo Bonzini }
9976e790746SPaolo Bonzini 
998f57fcf70SJason Wang static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
999f57fcf70SJason Wang                                       struct iovec *iov, unsigned int iov_cnt)
1000f57fcf70SJason Wang {
10019d8c6a25SDr. David Alan Gilbert     trace_virtio_net_handle_announce(n->announce_timer.round);
1002f57fcf70SJason Wang     if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK &&
1003f57fcf70SJason Wang         n->status & VIRTIO_NET_S_ANNOUNCE) {
1004f57fcf70SJason Wang         n->status &= ~VIRTIO_NET_S_ANNOUNCE;
10059d8c6a25SDr. David Alan Gilbert         if (n->announce_timer.round) {
10069d8c6a25SDr. David Alan Gilbert             qemu_announce_timer_step(&n->announce_timer);
1007f57fcf70SJason Wang         }
1008f57fcf70SJason Wang         return VIRTIO_NET_OK;
1009f57fcf70SJason Wang     } else {
1010f57fcf70SJason Wang         return VIRTIO_NET_ERR;
1011f57fcf70SJason Wang     }
1012f57fcf70SJason Wang }
1013f57fcf70SJason Wang 
10146e790746SPaolo Bonzini static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
10156e790746SPaolo Bonzini                                 struct iovec *iov, unsigned int iov_cnt)
10166e790746SPaolo Bonzini {
101717a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
10186e790746SPaolo Bonzini     struct virtio_net_ctrl_mq mq;
10196e790746SPaolo Bonzini     size_t s;
10206e790746SPaolo Bonzini     uint16_t queues;
10216e790746SPaolo Bonzini 
10226e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq));
10236e790746SPaolo Bonzini     if (s != sizeof(mq)) {
10246e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
10256e790746SPaolo Bonzini     }
10266e790746SPaolo Bonzini 
10276e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
10286e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
10296e790746SPaolo Bonzini     }
10306e790746SPaolo Bonzini 
10311399c60dSRusty Russell     queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs);
10326e790746SPaolo Bonzini 
10336e790746SPaolo Bonzini     if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
10346e790746SPaolo Bonzini         queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX ||
10356e790746SPaolo Bonzini         queues > n->max_queues ||
10366e790746SPaolo Bonzini         !n->multiqueue) {
10376e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
10386e790746SPaolo Bonzini     }
10396e790746SPaolo Bonzini 
10406e790746SPaolo Bonzini     n->curr_queues = queues;
10416e790746SPaolo Bonzini     /* stop the backend before changing the number of queues to avoid handling a
10426e790746SPaolo Bonzini      * disabled queue */
104317a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
10446e790746SPaolo Bonzini     virtio_net_set_queues(n);
10456e790746SPaolo Bonzini 
10466e790746SPaolo Bonzini     return VIRTIO_NET_OK;
10476e790746SPaolo Bonzini }
1048ba7eadb5SGreg Kurz 
10496e790746SPaolo Bonzini static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
10506e790746SPaolo Bonzini {
105117a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
10526e790746SPaolo Bonzini     struct virtio_net_ctrl_hdr ctrl;
10536e790746SPaolo Bonzini     virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
105451b19ebeSPaolo Bonzini     VirtQueueElement *elem;
10556e790746SPaolo Bonzini     size_t s;
1056771b6ed3SJason Wang     struct iovec *iov, *iov2;
10576e790746SPaolo Bonzini     unsigned int iov_cnt;
10586e790746SPaolo Bonzini 
105951b19ebeSPaolo Bonzini     for (;;) {
106051b19ebeSPaolo Bonzini         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
106151b19ebeSPaolo Bonzini         if (!elem) {
106251b19ebeSPaolo Bonzini             break;
106351b19ebeSPaolo Bonzini         }
106451b19ebeSPaolo Bonzini         if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) ||
106551b19ebeSPaolo Bonzini             iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) {
1066ba7eadb5SGreg Kurz             virtio_error(vdev, "virtio-net ctrl missing headers");
1067ba7eadb5SGreg Kurz             virtqueue_detach_element(vq, elem, 0);
1068ba7eadb5SGreg Kurz             g_free(elem);
1069ba7eadb5SGreg Kurz             break;
10706e790746SPaolo Bonzini         }
10716e790746SPaolo Bonzini 
107251b19ebeSPaolo Bonzini         iov_cnt = elem->out_num;
107351b19ebeSPaolo Bonzini         iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num);
10746e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl));
10756e790746SPaolo Bonzini         iov_discard_front(&iov, &iov_cnt, sizeof(ctrl));
10766e790746SPaolo Bonzini         if (s != sizeof(ctrl)) {
10776e790746SPaolo Bonzini             status = VIRTIO_NET_ERR;
10786e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_RX) {
10796e790746SPaolo Bonzini             status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt);
10806e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) {
10816e790746SPaolo Bonzini             status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt);
10826e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) {
10836e790746SPaolo Bonzini             status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt);
1084f57fcf70SJason Wang         } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) {
1085f57fcf70SJason Wang             status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt);
10866e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) {
10876e790746SPaolo Bonzini             status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt);
1088644c9858SDmitry Fleytman         } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) {
1089644c9858SDmitry Fleytman             status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt);
10906e790746SPaolo Bonzini         }
10916e790746SPaolo Bonzini 
109251b19ebeSPaolo Bonzini         s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status));
10936e790746SPaolo Bonzini         assert(s == sizeof(status));
10946e790746SPaolo Bonzini 
109551b19ebeSPaolo Bonzini         virtqueue_push(vq, elem, sizeof(status));
10966e790746SPaolo Bonzini         virtio_notify(vdev, vq);
1097771b6ed3SJason Wang         g_free(iov2);
109851b19ebeSPaolo Bonzini         g_free(elem);
10996e790746SPaolo Bonzini     }
11006e790746SPaolo Bonzini }
11016e790746SPaolo Bonzini 
11026e790746SPaolo Bonzini /* RX */
11036e790746SPaolo Bonzini 
11046e790746SPaolo Bonzini static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
11056e790746SPaolo Bonzini {
110617a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
11076e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(vq));
11086e790746SPaolo Bonzini 
11096e790746SPaolo Bonzini     qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index));
11106e790746SPaolo Bonzini }
11116e790746SPaolo Bonzini 
11126e790746SPaolo Bonzini static int virtio_net_can_receive(NetClientState *nc)
11136e790746SPaolo Bonzini {
11146e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
111517a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
11166e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
11176e790746SPaolo Bonzini 
111817a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
11196e790746SPaolo Bonzini         return 0;
11206e790746SPaolo Bonzini     }
11216e790746SPaolo Bonzini 
11226e790746SPaolo Bonzini     if (nc->queue_index >= n->curr_queues) {
11236e790746SPaolo Bonzini         return 0;
11246e790746SPaolo Bonzini     }
11256e790746SPaolo Bonzini 
11266e790746SPaolo Bonzini     if (!virtio_queue_ready(q->rx_vq) ||
112717a0ca55SKONRAD Frederic         !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
11286e790746SPaolo Bonzini         return 0;
11296e790746SPaolo Bonzini     }
11306e790746SPaolo Bonzini 
11316e790746SPaolo Bonzini     return 1;
11326e790746SPaolo Bonzini }
11336e790746SPaolo Bonzini 
11346e790746SPaolo Bonzini static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize)
11356e790746SPaolo Bonzini {
11366e790746SPaolo Bonzini     VirtIONet *n = q->n;
11376e790746SPaolo Bonzini     if (virtio_queue_empty(q->rx_vq) ||
11386e790746SPaolo Bonzini         (n->mergeable_rx_bufs &&
11396e790746SPaolo Bonzini          !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
11406e790746SPaolo Bonzini         virtio_queue_set_notification(q->rx_vq, 1);
11416e790746SPaolo Bonzini 
11426e790746SPaolo Bonzini         /* To avoid a race condition where the guest has made some buffers
11436e790746SPaolo Bonzini          * available after the above check but before notification was
11446e790746SPaolo Bonzini          * enabled, check for available buffers again.
11456e790746SPaolo Bonzini          */
11466e790746SPaolo Bonzini         if (virtio_queue_empty(q->rx_vq) ||
11476e790746SPaolo Bonzini             (n->mergeable_rx_bufs &&
11486e790746SPaolo Bonzini              !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
11496e790746SPaolo Bonzini             return 0;
11506e790746SPaolo Bonzini         }
11516e790746SPaolo Bonzini     }
11526e790746SPaolo Bonzini 
11536e790746SPaolo Bonzini     virtio_queue_set_notification(q->rx_vq, 0);
11546e790746SPaolo Bonzini     return 1;
11556e790746SPaolo Bonzini }
11566e790746SPaolo Bonzini 
11571399c60dSRusty Russell static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr)
1158032a74a1SCédric Le Goater {
11591399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->hdr_len);
11601399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->gso_size);
11611399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_start);
11621399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_offset);
1163032a74a1SCédric Le Goater }
1164032a74a1SCédric Le Goater 
11656e790746SPaolo Bonzini /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so
11666e790746SPaolo Bonzini  * it never finds out that the packets don't have valid checksums.  This
11676e790746SPaolo Bonzini  * causes dhclient to get upset.  Fedora's carried a patch for ages to
11686e790746SPaolo Bonzini  * fix this with Xen but it hasn't appeared in an upstream release of
11696e790746SPaolo Bonzini  * dhclient yet.
11706e790746SPaolo Bonzini  *
11716e790746SPaolo Bonzini  * To avoid breaking existing guests, we catch udp packets and add
11726e790746SPaolo Bonzini  * checksums.  This is terrible but it's better than hacking the guest
11736e790746SPaolo Bonzini  * kernels.
11746e790746SPaolo Bonzini  *
11756e790746SPaolo Bonzini  * N.B. if we introduce a zero-copy API, this operation is no longer free so
11766e790746SPaolo Bonzini  * we should provide a mechanism to disable it to avoid polluting the host
11776e790746SPaolo Bonzini  * cache.
11786e790746SPaolo Bonzini  */
11796e790746SPaolo Bonzini static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
11806e790746SPaolo Bonzini                                         uint8_t *buf, size_t size)
11816e790746SPaolo Bonzini {
11826e790746SPaolo Bonzini     if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
11836e790746SPaolo Bonzini         (size > 27 && size < 1500) && /* normal sized MTU */
11846e790746SPaolo Bonzini         (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
11856e790746SPaolo Bonzini         (buf[23] == 17) && /* ip.protocol == UDP */
11866e790746SPaolo Bonzini         (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
11876e790746SPaolo Bonzini         net_checksum_calculate(buf, size);
11886e790746SPaolo Bonzini         hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
11896e790746SPaolo Bonzini     }
11906e790746SPaolo Bonzini }
11916e790746SPaolo Bonzini 
11926e790746SPaolo Bonzini static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt,
11936e790746SPaolo Bonzini                            const void *buf, size_t size)
11946e790746SPaolo Bonzini {
11956e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
11966e790746SPaolo Bonzini         /* FIXME this cast is evil */
11976e790746SPaolo Bonzini         void *wbuf = (void *)buf;
11986e790746SPaolo Bonzini         work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len,
11996e790746SPaolo Bonzini                                     size - n->host_hdr_len);
12001bfa316cSGreg Kurz 
12011bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
12021399c60dSRusty Russell             virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf);
12031bfa316cSGreg Kurz         }
12046e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr));
12056e790746SPaolo Bonzini     } else {
12066e790746SPaolo Bonzini         struct virtio_net_hdr hdr = {
12076e790746SPaolo Bonzini             .flags = 0,
12086e790746SPaolo Bonzini             .gso_type = VIRTIO_NET_HDR_GSO_NONE
12096e790746SPaolo Bonzini         };
12106e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr);
12116e790746SPaolo Bonzini     }
12126e790746SPaolo Bonzini }
12136e790746SPaolo Bonzini 
12146e790746SPaolo Bonzini static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
12156e790746SPaolo Bonzini {
12166e790746SPaolo Bonzini     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
12176e790746SPaolo Bonzini     static const uint8_t vlan[] = {0x81, 0x00};
12186e790746SPaolo Bonzini     uint8_t *ptr = (uint8_t *)buf;
12196e790746SPaolo Bonzini     int i;
12206e790746SPaolo Bonzini 
12216e790746SPaolo Bonzini     if (n->promisc)
12226e790746SPaolo Bonzini         return 1;
12236e790746SPaolo Bonzini 
12246e790746SPaolo Bonzini     ptr += n->host_hdr_len;
12256e790746SPaolo Bonzini 
12266e790746SPaolo Bonzini     if (!memcmp(&ptr[12], vlan, sizeof(vlan))) {
12277542d3e7SPeter Maydell         int vid = lduw_be_p(ptr + 14) & 0xfff;
12286e790746SPaolo Bonzini         if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f))))
12296e790746SPaolo Bonzini             return 0;
12306e790746SPaolo Bonzini     }
12316e790746SPaolo Bonzini 
12326e790746SPaolo Bonzini     if (ptr[0] & 1) { // multicast
12336e790746SPaolo Bonzini         if (!memcmp(ptr, bcast, sizeof(bcast))) {
12346e790746SPaolo Bonzini             return !n->nobcast;
12356e790746SPaolo Bonzini         } else if (n->nomulti) {
12366e790746SPaolo Bonzini             return 0;
12376e790746SPaolo Bonzini         } else if (n->allmulti || n->mac_table.multi_overflow) {
12386e790746SPaolo Bonzini             return 1;
12396e790746SPaolo Bonzini         }
12406e790746SPaolo Bonzini 
12416e790746SPaolo Bonzini         for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
12426e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
12436e790746SPaolo Bonzini                 return 1;
12446e790746SPaolo Bonzini             }
12456e790746SPaolo Bonzini         }
12466e790746SPaolo Bonzini     } else { // unicast
12476e790746SPaolo Bonzini         if (n->nouni) {
12486e790746SPaolo Bonzini             return 0;
12496e790746SPaolo Bonzini         } else if (n->alluni || n->mac_table.uni_overflow) {
12506e790746SPaolo Bonzini             return 1;
12516e790746SPaolo Bonzini         } else if (!memcmp(ptr, n->mac, ETH_ALEN)) {
12526e790746SPaolo Bonzini             return 1;
12536e790746SPaolo Bonzini         }
12546e790746SPaolo Bonzini 
12556e790746SPaolo Bonzini         for (i = 0; i < n->mac_table.first_multi; i++) {
12566e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
12576e790746SPaolo Bonzini                 return 1;
12586e790746SPaolo Bonzini             }
12596e790746SPaolo Bonzini         }
12606e790746SPaolo Bonzini     }
12616e790746SPaolo Bonzini 
12626e790746SPaolo Bonzini     return 0;
12636e790746SPaolo Bonzini }
12646e790746SPaolo Bonzini 
126597cd965cSPaolo Bonzini static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
126697cd965cSPaolo Bonzini                                       size_t size)
12676e790746SPaolo Bonzini {
12686e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
12696e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
127017a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
12716e790746SPaolo Bonzini     struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
12726e790746SPaolo Bonzini     struct virtio_net_hdr_mrg_rxbuf mhdr;
12736e790746SPaolo Bonzini     unsigned mhdr_cnt = 0;
12746e790746SPaolo Bonzini     size_t offset, i, guest_offset;
12756e790746SPaolo Bonzini 
12766e790746SPaolo Bonzini     if (!virtio_net_can_receive(nc)) {
12776e790746SPaolo Bonzini         return -1;
12786e790746SPaolo Bonzini     }
12796e790746SPaolo Bonzini 
12806e790746SPaolo Bonzini     /* hdr_len refers to the header we supply to the guest */
12816e790746SPaolo Bonzini     if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) {
12826e790746SPaolo Bonzini         return 0;
12836e790746SPaolo Bonzini     }
12846e790746SPaolo Bonzini 
12856e790746SPaolo Bonzini     if (!receive_filter(n, buf, size))
12866e790746SPaolo Bonzini         return size;
12876e790746SPaolo Bonzini 
12886e790746SPaolo Bonzini     offset = i = 0;
12896e790746SPaolo Bonzini 
12906e790746SPaolo Bonzini     while (offset < size) {
129151b19ebeSPaolo Bonzini         VirtQueueElement *elem;
12926e790746SPaolo Bonzini         int len, total;
129351b19ebeSPaolo Bonzini         const struct iovec *sg;
12946e790746SPaolo Bonzini 
12956e790746SPaolo Bonzini         total = 0;
12966e790746SPaolo Bonzini 
129751b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement));
129851b19ebeSPaolo Bonzini         if (!elem) {
1299ba10b9c0SGreg Kurz             if (i) {
1300ba10b9c0SGreg Kurz                 virtio_error(vdev, "virtio-net unexpected empty queue: "
13016e790746SPaolo Bonzini                              "i %zd mergeable %d offset %zd, size %zd, "
1302019a3edbSGerd Hoffmann                              "guest hdr len %zd, host hdr len %zd "
1303019a3edbSGerd Hoffmann                              "guest features 0x%" PRIx64,
13046e790746SPaolo Bonzini                              i, n->mergeable_rx_bufs, offset, size,
1305019a3edbSGerd Hoffmann                              n->guest_hdr_len, n->host_hdr_len,
1306019a3edbSGerd Hoffmann                              vdev->guest_features);
1307ba10b9c0SGreg Kurz             }
1308ba10b9c0SGreg Kurz             return -1;
13096e790746SPaolo Bonzini         }
13106e790746SPaolo Bonzini 
131151b19ebeSPaolo Bonzini         if (elem->in_num < 1) {
1312ba10b9c0SGreg Kurz             virtio_error(vdev,
1313ba10b9c0SGreg Kurz                          "virtio-net receive queue contains no in buffers");
1314ba10b9c0SGreg Kurz             virtqueue_detach_element(q->rx_vq, elem, 0);
1315ba10b9c0SGreg Kurz             g_free(elem);
1316ba10b9c0SGreg Kurz             return -1;
13176e790746SPaolo Bonzini         }
13186e790746SPaolo Bonzini 
131951b19ebeSPaolo Bonzini         sg = elem->in_sg;
13206e790746SPaolo Bonzini         if (i == 0) {
13216e790746SPaolo Bonzini             assert(offset == 0);
13226e790746SPaolo Bonzini             if (n->mergeable_rx_bufs) {
13236e790746SPaolo Bonzini                 mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg),
132451b19ebeSPaolo Bonzini                                     sg, elem->in_num,
13256e790746SPaolo Bonzini                                     offsetof(typeof(mhdr), num_buffers),
13266e790746SPaolo Bonzini                                     sizeof(mhdr.num_buffers));
13276e790746SPaolo Bonzini             }
13286e790746SPaolo Bonzini 
132951b19ebeSPaolo Bonzini             receive_header(n, sg, elem->in_num, buf, size);
13306e790746SPaolo Bonzini             offset = n->host_hdr_len;
13316e790746SPaolo Bonzini             total += n->guest_hdr_len;
13326e790746SPaolo Bonzini             guest_offset = n->guest_hdr_len;
13336e790746SPaolo Bonzini         } else {
13346e790746SPaolo Bonzini             guest_offset = 0;
13356e790746SPaolo Bonzini         }
13366e790746SPaolo Bonzini 
13376e790746SPaolo Bonzini         /* copy in packet.  ugh */
133851b19ebeSPaolo Bonzini         len = iov_from_buf(sg, elem->in_num, guest_offset,
13396e790746SPaolo Bonzini                            buf + offset, size - offset);
13406e790746SPaolo Bonzini         total += len;
13416e790746SPaolo Bonzini         offset += len;
13426e790746SPaolo Bonzini         /* If buffers can't be merged, at this point we
13436e790746SPaolo Bonzini          * must have consumed the complete packet.
13446e790746SPaolo Bonzini          * Otherwise, drop it. */
13456e790746SPaolo Bonzini         if (!n->mergeable_rx_bufs && offset < size) {
134627e57efeSLadi Prosek             virtqueue_unpop(q->rx_vq, elem, total);
134751b19ebeSPaolo Bonzini             g_free(elem);
13486e790746SPaolo Bonzini             return size;
13496e790746SPaolo Bonzini         }
13506e790746SPaolo Bonzini 
13516e790746SPaolo Bonzini         /* signal other side */
135251b19ebeSPaolo Bonzini         virtqueue_fill(q->rx_vq, elem, total, i++);
135351b19ebeSPaolo Bonzini         g_free(elem);
13546e790746SPaolo Bonzini     }
13556e790746SPaolo Bonzini 
13566e790746SPaolo Bonzini     if (mhdr_cnt) {
13571399c60dSRusty Russell         virtio_stw_p(vdev, &mhdr.num_buffers, i);
13586e790746SPaolo Bonzini         iov_from_buf(mhdr_sg, mhdr_cnt,
13596e790746SPaolo Bonzini                      0,
13606e790746SPaolo Bonzini                      &mhdr.num_buffers, sizeof mhdr.num_buffers);
13616e790746SPaolo Bonzini     }
13626e790746SPaolo Bonzini 
13636e790746SPaolo Bonzini     virtqueue_flush(q->rx_vq, i);
136417a0ca55SKONRAD Frederic     virtio_notify(vdev, q->rx_vq);
13656e790746SPaolo Bonzini 
13666e790746SPaolo Bonzini     return size;
13676e790746SPaolo Bonzini }
13686e790746SPaolo Bonzini 
13692974e916SYuri Benditovich static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf,
137097cd965cSPaolo Bonzini                                   size_t size)
137197cd965cSPaolo Bonzini {
137297cd965cSPaolo Bonzini     ssize_t r;
137397cd965cSPaolo Bonzini 
137497cd965cSPaolo Bonzini     rcu_read_lock();
137597cd965cSPaolo Bonzini     r = virtio_net_receive_rcu(nc, buf, size);
137697cd965cSPaolo Bonzini     rcu_read_unlock();
137797cd965cSPaolo Bonzini     return r;
137897cd965cSPaolo Bonzini }
137997cd965cSPaolo Bonzini 
13802974e916SYuri Benditovich static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
13812974e916SYuri Benditovich                                          const uint8_t *buf,
13822974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
13832974e916SYuri Benditovich {
13842974e916SYuri Benditovich     uint16_t ip_hdrlen;
13852974e916SYuri Benditovich     struct ip_header *ip;
13862974e916SYuri Benditovich 
13872974e916SYuri Benditovich     ip = (struct ip_header *)(buf + chain->n->guest_hdr_len
13882974e916SYuri Benditovich                               + sizeof(struct eth_header));
13892974e916SYuri Benditovich     unit->ip = (void *)ip;
13902974e916SYuri Benditovich     ip_hdrlen = (ip->ip_ver_len & 0xF) << 2;
13912974e916SYuri Benditovich     unit->ip_plen = &ip->ip_len;
13922974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen);
13932974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
13942974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen;
13952974e916SYuri Benditovich }
13962974e916SYuri Benditovich 
13972974e916SYuri Benditovich static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
13982974e916SYuri Benditovich                                          const uint8_t *buf,
13992974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
14002974e916SYuri Benditovich {
14012974e916SYuri Benditovich     struct ip6_header *ip6;
14022974e916SYuri Benditovich 
14032974e916SYuri Benditovich     ip6 = (struct ip6_header *)(buf + chain->n->guest_hdr_len
14042974e916SYuri Benditovich                                  + sizeof(struct eth_header));
14052974e916SYuri Benditovich     unit->ip = ip6;
14062974e916SYuri Benditovich     unit->ip_plen = &(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
14072974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip)\
14082974e916SYuri Benditovich                                         + sizeof(struct ip6_header));
14092974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
14102974e916SYuri Benditovich 
14112974e916SYuri Benditovich     /* There is a difference between payload lenght in ipv4 and v6,
14122974e916SYuri Benditovich        ip header is excluded in ipv6 */
14132974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen;
14142974e916SYuri Benditovich }
14152974e916SYuri Benditovich 
14162974e916SYuri Benditovich static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain,
14172974e916SYuri Benditovich                                        VirtioNetRscSeg *seg)
14182974e916SYuri Benditovich {
14192974e916SYuri Benditovich     int ret;
14202974e916SYuri Benditovich     struct virtio_net_hdr *h;
14212974e916SYuri Benditovich 
14222974e916SYuri Benditovich     h = (struct virtio_net_hdr *)seg->buf;
14232974e916SYuri Benditovich     h->flags = 0;
14242974e916SYuri Benditovich     h->gso_type = VIRTIO_NET_HDR_GSO_NONE;
14252974e916SYuri Benditovich 
14262974e916SYuri Benditovich     if (seg->is_coalesced) {
14272974e916SYuri Benditovich         *virtio_net_rsc_ext_num_packets(h) = seg->packets;
14282974e916SYuri Benditovich         *virtio_net_rsc_ext_num_dupacks(h) = seg->dup_ack;
14292974e916SYuri Benditovich         h->flags = VIRTIO_NET_HDR_F_RSC_INFO;
14302974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
14312974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
14322974e916SYuri Benditovich         } else {
14332974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
14342974e916SYuri Benditovich         }
14352974e916SYuri Benditovich     }
14362974e916SYuri Benditovich 
14372974e916SYuri Benditovich     ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size);
14382974e916SYuri Benditovich     QTAILQ_REMOVE(&chain->buffers, seg, next);
14392974e916SYuri Benditovich     g_free(seg->buf);
14402974e916SYuri Benditovich     g_free(seg);
14412974e916SYuri Benditovich 
14422974e916SYuri Benditovich     return ret;
14432974e916SYuri Benditovich }
14442974e916SYuri Benditovich 
14452974e916SYuri Benditovich static void virtio_net_rsc_purge(void *opq)
14462974e916SYuri Benditovich {
14472974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn;
14482974e916SYuri Benditovich     VirtioNetRscChain *chain = (VirtioNetRscChain *)opq;
14492974e916SYuri Benditovich 
14502974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn) {
14512974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
14522974e916SYuri Benditovich             chain->stat.purge_failed++;
14532974e916SYuri Benditovich             continue;
14542974e916SYuri Benditovich         }
14552974e916SYuri Benditovich     }
14562974e916SYuri Benditovich 
14572974e916SYuri Benditovich     chain->stat.timer++;
14582974e916SYuri Benditovich     if (!QTAILQ_EMPTY(&chain->buffers)) {
14592974e916SYuri Benditovich         timer_mod(chain->drain_timer,
14602974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
14612974e916SYuri Benditovich     }
14622974e916SYuri Benditovich }
14632974e916SYuri Benditovich 
14642974e916SYuri Benditovich static void virtio_net_rsc_cleanup(VirtIONet *n)
14652974e916SYuri Benditovich {
14662974e916SYuri Benditovich     VirtioNetRscChain *chain, *rn_chain;
14672974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn_seg;
14682974e916SYuri Benditovich 
14692974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(chain, &n->rsc_chains, next, rn_chain) {
14702974e916SYuri Benditovich         QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn_seg) {
14712974e916SYuri Benditovich             QTAILQ_REMOVE(&chain->buffers, seg, next);
14722974e916SYuri Benditovich             g_free(seg->buf);
14732974e916SYuri Benditovich             g_free(seg);
14742974e916SYuri Benditovich         }
14752974e916SYuri Benditovich 
14762974e916SYuri Benditovich         timer_del(chain->drain_timer);
14772974e916SYuri Benditovich         timer_free(chain->drain_timer);
14782974e916SYuri Benditovich         QTAILQ_REMOVE(&n->rsc_chains, chain, next);
14792974e916SYuri Benditovich         g_free(chain);
14802974e916SYuri Benditovich     }
14812974e916SYuri Benditovich }
14822974e916SYuri Benditovich 
14832974e916SYuri Benditovich static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain,
14842974e916SYuri Benditovich                                      NetClientState *nc,
14852974e916SYuri Benditovich                                      const uint8_t *buf, size_t size)
14862974e916SYuri Benditovich {
14872974e916SYuri Benditovich     uint16_t hdr_len;
14882974e916SYuri Benditovich     VirtioNetRscSeg *seg;
14892974e916SYuri Benditovich 
14902974e916SYuri Benditovich     hdr_len = chain->n->guest_hdr_len;
14912974e916SYuri Benditovich     seg = g_malloc(sizeof(VirtioNetRscSeg));
14922974e916SYuri Benditovich     seg->buf = g_malloc(hdr_len + sizeof(struct eth_header)
14932974e916SYuri Benditovich         + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD);
14942974e916SYuri Benditovich     memcpy(seg->buf, buf, size);
14952974e916SYuri Benditovich     seg->size = size;
14962974e916SYuri Benditovich     seg->packets = 1;
14972974e916SYuri Benditovich     seg->dup_ack = 0;
14982974e916SYuri Benditovich     seg->is_coalesced = 0;
14992974e916SYuri Benditovich     seg->nc = nc;
15002974e916SYuri Benditovich 
15012974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&chain->buffers, seg, next);
15022974e916SYuri Benditovich     chain->stat.cache++;
15032974e916SYuri Benditovich 
15042974e916SYuri Benditovich     switch (chain->proto) {
15052974e916SYuri Benditovich     case ETH_P_IP:
15062974e916SYuri Benditovich         virtio_net_rsc_extract_unit4(chain, seg->buf, &seg->unit);
15072974e916SYuri Benditovich         break;
15082974e916SYuri Benditovich     case ETH_P_IPV6:
15092974e916SYuri Benditovich         virtio_net_rsc_extract_unit6(chain, seg->buf, &seg->unit);
15102974e916SYuri Benditovich         break;
15112974e916SYuri Benditovich     default:
15122974e916SYuri Benditovich         g_assert_not_reached();
15132974e916SYuri Benditovich     }
15142974e916SYuri Benditovich }
15152974e916SYuri Benditovich 
15162974e916SYuri Benditovich static int32_t virtio_net_rsc_handle_ack(VirtioNetRscChain *chain,
15172974e916SYuri Benditovich                                          VirtioNetRscSeg *seg,
15182974e916SYuri Benditovich                                          const uint8_t *buf,
15192974e916SYuri Benditovich                                          struct tcp_header *n_tcp,
15202974e916SYuri Benditovich                                          struct tcp_header *o_tcp)
15212974e916SYuri Benditovich {
15222974e916SYuri Benditovich     uint32_t nack, oack;
15232974e916SYuri Benditovich     uint16_t nwin, owin;
15242974e916SYuri Benditovich 
15252974e916SYuri Benditovich     nack = htonl(n_tcp->th_ack);
15262974e916SYuri Benditovich     nwin = htons(n_tcp->th_win);
15272974e916SYuri Benditovich     oack = htonl(o_tcp->th_ack);
15282974e916SYuri Benditovich     owin = htons(o_tcp->th_win);
15292974e916SYuri Benditovich 
15302974e916SYuri Benditovich     if ((nack - oack) >= VIRTIO_NET_MAX_TCP_PAYLOAD) {
15312974e916SYuri Benditovich         chain->stat.ack_out_of_win++;
15322974e916SYuri Benditovich         return RSC_FINAL;
15332974e916SYuri Benditovich     } else if (nack == oack) {
15342974e916SYuri Benditovich         /* duplicated ack or window probe */
15352974e916SYuri Benditovich         if (nwin == owin) {
15362974e916SYuri Benditovich             /* duplicated ack, add dup ack count due to whql test up to 1 */
15372974e916SYuri Benditovich             chain->stat.dup_ack++;
15382974e916SYuri Benditovich             return RSC_FINAL;
15392974e916SYuri Benditovich         } else {
15402974e916SYuri Benditovich             /* Coalesce window update */
15412974e916SYuri Benditovich             o_tcp->th_win = n_tcp->th_win;
15422974e916SYuri Benditovich             chain->stat.win_update++;
15432974e916SYuri Benditovich             return RSC_COALESCE;
15442974e916SYuri Benditovich         }
15452974e916SYuri Benditovich     } else {
15462974e916SYuri Benditovich         /* pure ack, go to 'C', finalize*/
15472974e916SYuri Benditovich         chain->stat.pure_ack++;
15482974e916SYuri Benditovich         return RSC_FINAL;
15492974e916SYuri Benditovich     }
15502974e916SYuri Benditovich }
15512974e916SYuri Benditovich 
15522974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain,
15532974e916SYuri Benditovich                                             VirtioNetRscSeg *seg,
15542974e916SYuri Benditovich                                             const uint8_t *buf,
15552974e916SYuri Benditovich                                             VirtioNetRscUnit *n_unit)
15562974e916SYuri Benditovich {
15572974e916SYuri Benditovich     void *data;
15582974e916SYuri Benditovich     uint16_t o_ip_len;
15592974e916SYuri Benditovich     uint32_t nseq, oseq;
15602974e916SYuri Benditovich     VirtioNetRscUnit *o_unit;
15612974e916SYuri Benditovich 
15622974e916SYuri Benditovich     o_unit = &seg->unit;
15632974e916SYuri Benditovich     o_ip_len = htons(*o_unit->ip_plen);
15642974e916SYuri Benditovich     nseq = htonl(n_unit->tcp->th_seq);
15652974e916SYuri Benditovich     oseq = htonl(o_unit->tcp->th_seq);
15662974e916SYuri Benditovich 
15672974e916SYuri Benditovich     /* out of order or retransmitted. */
15682974e916SYuri Benditovich     if ((nseq - oseq) > VIRTIO_NET_MAX_TCP_PAYLOAD) {
15692974e916SYuri Benditovich         chain->stat.data_out_of_win++;
15702974e916SYuri Benditovich         return RSC_FINAL;
15712974e916SYuri Benditovich     }
15722974e916SYuri Benditovich 
15732974e916SYuri Benditovich     data = ((uint8_t *)n_unit->tcp) + n_unit->tcp_hdrlen;
15742974e916SYuri Benditovich     if (nseq == oseq) {
15752974e916SYuri Benditovich         if ((o_unit->payload == 0) && n_unit->payload) {
15762974e916SYuri Benditovich             /* From no payload to payload, normal case, not a dup ack or etc */
15772974e916SYuri Benditovich             chain->stat.data_after_pure_ack++;
15782974e916SYuri Benditovich             goto coalesce;
15792974e916SYuri Benditovich         } else {
15802974e916SYuri Benditovich             return virtio_net_rsc_handle_ack(chain, seg, buf,
15812974e916SYuri Benditovich                                              n_unit->tcp, o_unit->tcp);
15822974e916SYuri Benditovich         }
15832974e916SYuri Benditovich     } else if ((nseq - oseq) != o_unit->payload) {
15842974e916SYuri Benditovich         /* Not a consistent packet, out of order */
15852974e916SYuri Benditovich         chain->stat.data_out_of_order++;
15862974e916SYuri Benditovich         return RSC_FINAL;
15872974e916SYuri Benditovich     } else {
15882974e916SYuri Benditovich coalesce:
15892974e916SYuri Benditovich         if ((o_ip_len + n_unit->payload) > chain->max_payload) {
15902974e916SYuri Benditovich             chain->stat.over_size++;
15912974e916SYuri Benditovich             return RSC_FINAL;
15922974e916SYuri Benditovich         }
15932974e916SYuri Benditovich 
15942974e916SYuri Benditovich         /* Here comes the right data, the payload length in v4/v6 is different,
15952974e916SYuri Benditovich            so use the field value to update and record the new data len */
15962974e916SYuri Benditovich         o_unit->payload += n_unit->payload; /* update new data len */
15972974e916SYuri Benditovich 
15982974e916SYuri Benditovich         /* update field in ip header */
15992974e916SYuri Benditovich         *o_unit->ip_plen = htons(o_ip_len + n_unit->payload);
16002974e916SYuri Benditovich 
16012974e916SYuri Benditovich         /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced
16022974e916SYuri Benditovich            for windows guest, while this may change the behavior for linux
16032974e916SYuri Benditovich            guest (only if it uses RSC feature). */
16042974e916SYuri Benditovich         o_unit->tcp->th_offset_flags = n_unit->tcp->th_offset_flags;
16052974e916SYuri Benditovich 
16062974e916SYuri Benditovich         o_unit->tcp->th_ack = n_unit->tcp->th_ack;
16072974e916SYuri Benditovich         o_unit->tcp->th_win = n_unit->tcp->th_win;
16082974e916SYuri Benditovich 
16092974e916SYuri Benditovich         memmove(seg->buf + seg->size, data, n_unit->payload);
16102974e916SYuri Benditovich         seg->size += n_unit->payload;
16112974e916SYuri Benditovich         seg->packets++;
16122974e916SYuri Benditovich         chain->stat.coalesced++;
16132974e916SYuri Benditovich         return RSC_COALESCE;
16142974e916SYuri Benditovich     }
16152974e916SYuri Benditovich }
16162974e916SYuri Benditovich 
16172974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce4(VirtioNetRscChain *chain,
16182974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
16192974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
16202974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
16212974e916SYuri Benditovich {
16222974e916SYuri Benditovich     struct ip_header *ip1, *ip2;
16232974e916SYuri Benditovich 
16242974e916SYuri Benditovich     ip1 = (struct ip_header *)(unit->ip);
16252974e916SYuri Benditovich     ip2 = (struct ip_header *)(seg->unit.ip);
16262974e916SYuri Benditovich     if ((ip1->ip_src ^ ip2->ip_src) || (ip1->ip_dst ^ ip2->ip_dst)
16272974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
16282974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
16292974e916SYuri Benditovich         chain->stat.no_match++;
16302974e916SYuri Benditovich         return RSC_NO_MATCH;
16312974e916SYuri Benditovich     }
16322974e916SYuri Benditovich 
16332974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
16342974e916SYuri Benditovich }
16352974e916SYuri Benditovich 
16362974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce6(VirtioNetRscChain *chain,
16372974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
16382974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
16392974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
16402974e916SYuri Benditovich {
16412974e916SYuri Benditovich     struct ip6_header *ip1, *ip2;
16422974e916SYuri Benditovich 
16432974e916SYuri Benditovich     ip1 = (struct ip6_header *)(unit->ip);
16442974e916SYuri Benditovich     ip2 = (struct ip6_header *)(seg->unit.ip);
16452974e916SYuri Benditovich     if (memcmp(&ip1->ip6_src, &ip2->ip6_src, sizeof(struct in6_address))
16462974e916SYuri Benditovich         || memcmp(&ip1->ip6_dst, &ip2->ip6_dst, sizeof(struct in6_address))
16472974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
16482974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
16492974e916SYuri Benditovich             chain->stat.no_match++;
16502974e916SYuri Benditovich             return RSC_NO_MATCH;
16512974e916SYuri Benditovich     }
16522974e916SYuri Benditovich 
16532974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
16542974e916SYuri Benditovich }
16552974e916SYuri Benditovich 
16562974e916SYuri Benditovich /* Packets with 'SYN' should bypass, other flag should be sent after drain
16572974e916SYuri Benditovich  * to prevent out of order */
16582974e916SYuri Benditovich static int virtio_net_rsc_tcp_ctrl_check(VirtioNetRscChain *chain,
16592974e916SYuri Benditovich                                          struct tcp_header *tcp)
16602974e916SYuri Benditovich {
16612974e916SYuri Benditovich     uint16_t tcp_hdr;
16622974e916SYuri Benditovich     uint16_t tcp_flag;
16632974e916SYuri Benditovich 
16642974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags);
16652974e916SYuri Benditovich     tcp_hdr = (tcp_flag & VIRTIO_NET_TCP_HDR_LENGTH) >> 10;
16662974e916SYuri Benditovich     tcp_flag &= VIRTIO_NET_TCP_FLAG;
16672974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags) & 0x3F;
16682974e916SYuri Benditovich     if (tcp_flag & TH_SYN) {
16692974e916SYuri Benditovich         chain->stat.tcp_syn++;
16702974e916SYuri Benditovich         return RSC_BYPASS;
16712974e916SYuri Benditovich     }
16722974e916SYuri Benditovich 
16732974e916SYuri Benditovich     if (tcp_flag & (TH_FIN | TH_URG | TH_RST | TH_ECE | TH_CWR)) {
16742974e916SYuri Benditovich         chain->stat.tcp_ctrl_drain++;
16752974e916SYuri Benditovich         return RSC_FINAL;
16762974e916SYuri Benditovich     }
16772974e916SYuri Benditovich 
16782974e916SYuri Benditovich     if (tcp_hdr > sizeof(struct tcp_header)) {
16792974e916SYuri Benditovich         chain->stat.tcp_all_opt++;
16802974e916SYuri Benditovich         return RSC_FINAL;
16812974e916SYuri Benditovich     }
16822974e916SYuri Benditovich 
16832974e916SYuri Benditovich     return RSC_CANDIDATE;
16842974e916SYuri Benditovich }
16852974e916SYuri Benditovich 
16862974e916SYuri Benditovich static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain,
16872974e916SYuri Benditovich                                          NetClientState *nc,
16882974e916SYuri Benditovich                                          const uint8_t *buf, size_t size,
16892974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
16902974e916SYuri Benditovich {
16912974e916SYuri Benditovich     int ret;
16922974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
16932974e916SYuri Benditovich 
16942974e916SYuri Benditovich     if (QTAILQ_EMPTY(&chain->buffers)) {
16952974e916SYuri Benditovich         chain->stat.empty_cache++;
16962974e916SYuri Benditovich         virtio_net_rsc_cache_buf(chain, nc, buf, size);
16972974e916SYuri Benditovich         timer_mod(chain->drain_timer,
16982974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
16992974e916SYuri Benditovich         return size;
17002974e916SYuri Benditovich     }
17012974e916SYuri Benditovich 
17022974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
17032974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
17042974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce4(chain, seg, buf, size, unit);
17052974e916SYuri Benditovich         } else {
17062974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce6(chain, seg, buf, size, unit);
17072974e916SYuri Benditovich         }
17082974e916SYuri Benditovich 
17092974e916SYuri Benditovich         if (ret == RSC_FINAL) {
17102974e916SYuri Benditovich             if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
17112974e916SYuri Benditovich                 /* Send failed */
17122974e916SYuri Benditovich                 chain->stat.final_failed++;
17132974e916SYuri Benditovich                 return 0;
17142974e916SYuri Benditovich             }
17152974e916SYuri Benditovich 
17162974e916SYuri Benditovich             /* Send current packet */
17172974e916SYuri Benditovich             return virtio_net_do_receive(nc, buf, size);
17182974e916SYuri Benditovich         } else if (ret == RSC_NO_MATCH) {
17192974e916SYuri Benditovich             continue;
17202974e916SYuri Benditovich         } else {
17212974e916SYuri Benditovich             /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */
17222974e916SYuri Benditovich             seg->is_coalesced = 1;
17232974e916SYuri Benditovich             return size;
17242974e916SYuri Benditovich         }
17252974e916SYuri Benditovich     }
17262974e916SYuri Benditovich 
17272974e916SYuri Benditovich     chain->stat.no_match_cache++;
17282974e916SYuri Benditovich     virtio_net_rsc_cache_buf(chain, nc, buf, size);
17292974e916SYuri Benditovich     return size;
17302974e916SYuri Benditovich }
17312974e916SYuri Benditovich 
17322974e916SYuri Benditovich /* Drain a connection data, this is to avoid out of order segments */
17332974e916SYuri Benditovich static size_t virtio_net_rsc_drain_flow(VirtioNetRscChain *chain,
17342974e916SYuri Benditovich                                         NetClientState *nc,
17352974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
17362974e916SYuri Benditovich                                         uint16_t ip_start, uint16_t ip_size,
17372974e916SYuri Benditovich                                         uint16_t tcp_port)
17382974e916SYuri Benditovich {
17392974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
17402974e916SYuri Benditovich     uint32_t ppair1, ppair2;
17412974e916SYuri Benditovich 
17422974e916SYuri Benditovich     ppair1 = *(uint32_t *)(buf + tcp_port);
17432974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
17442974e916SYuri Benditovich         ppair2 = *(uint32_t *)(seg->buf + tcp_port);
17452974e916SYuri Benditovich         if (memcmp(buf + ip_start, seg->buf + ip_start, ip_size)
17462974e916SYuri Benditovich             || (ppair1 != ppair2)) {
17472974e916SYuri Benditovich             continue;
17482974e916SYuri Benditovich         }
17492974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
17502974e916SYuri Benditovich             chain->stat.drain_failed++;
17512974e916SYuri Benditovich         }
17522974e916SYuri Benditovich 
17532974e916SYuri Benditovich         break;
17542974e916SYuri Benditovich     }
17552974e916SYuri Benditovich 
17562974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
17572974e916SYuri Benditovich }
17582974e916SYuri Benditovich 
17592974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check4(VirtioNetRscChain *chain,
17602974e916SYuri Benditovich                                             struct ip_header *ip,
17612974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
17622974e916SYuri Benditovich {
17632974e916SYuri Benditovich     uint16_t ip_len;
17642974e916SYuri Benditovich 
17652974e916SYuri Benditovich     /* Not an ipv4 packet */
17662974e916SYuri Benditovich     if (((ip->ip_ver_len & 0xF0) >> 4) != IP_HEADER_VERSION_4) {
17672974e916SYuri Benditovich         chain->stat.ip_option++;
17682974e916SYuri Benditovich         return RSC_BYPASS;
17692974e916SYuri Benditovich     }
17702974e916SYuri Benditovich 
17712974e916SYuri Benditovich     /* Don't handle packets with ip option */
17722974e916SYuri Benditovich     if ((ip->ip_ver_len & 0xF) != VIRTIO_NET_IP4_HEADER_LENGTH) {
17732974e916SYuri Benditovich         chain->stat.ip_option++;
17742974e916SYuri Benditovich         return RSC_BYPASS;
17752974e916SYuri Benditovich     }
17762974e916SYuri Benditovich 
17772974e916SYuri Benditovich     if (ip->ip_p != IPPROTO_TCP) {
17782974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
17792974e916SYuri Benditovich         return RSC_BYPASS;
17802974e916SYuri Benditovich     }
17812974e916SYuri Benditovich 
17822974e916SYuri Benditovich     /* Don't handle packets with ip fragment */
17832974e916SYuri Benditovich     if (!(htons(ip->ip_off) & IP_DF)) {
17842974e916SYuri Benditovich         chain->stat.ip_frag++;
17852974e916SYuri Benditovich         return RSC_BYPASS;
17862974e916SYuri Benditovich     }
17872974e916SYuri Benditovich 
17882974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
17892974e916SYuri Benditovich     if (IPTOS_ECN(ip->ip_tos)) {
17902974e916SYuri Benditovich         chain->stat.ip_ecn++;
17912974e916SYuri Benditovich         return RSC_BYPASS;
17922974e916SYuri Benditovich     }
17932974e916SYuri Benditovich 
17942974e916SYuri Benditovich     ip_len = htons(ip->ip_len);
17952974e916SYuri Benditovich     if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header))
17962974e916SYuri Benditovich         || ip_len > (size - chain->n->guest_hdr_len -
17972974e916SYuri Benditovich                      sizeof(struct eth_header))) {
17982974e916SYuri Benditovich         chain->stat.ip_hacked++;
17992974e916SYuri Benditovich         return RSC_BYPASS;
18002974e916SYuri Benditovich     }
18012974e916SYuri Benditovich 
18022974e916SYuri Benditovich     return RSC_CANDIDATE;
18032974e916SYuri Benditovich }
18042974e916SYuri Benditovich 
18052974e916SYuri Benditovich static size_t virtio_net_rsc_receive4(VirtioNetRscChain *chain,
18062974e916SYuri Benditovich                                       NetClientState *nc,
18072974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
18082974e916SYuri Benditovich {
18092974e916SYuri Benditovich     int32_t ret;
18102974e916SYuri Benditovich     uint16_t hdr_len;
18112974e916SYuri Benditovich     VirtioNetRscUnit unit;
18122974e916SYuri Benditovich 
18132974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
18142974e916SYuri Benditovich 
18152974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header)
18162974e916SYuri Benditovich         + sizeof(struct tcp_header))) {
18172974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
18182974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18192974e916SYuri Benditovich     }
18202974e916SYuri Benditovich 
18212974e916SYuri Benditovich     virtio_net_rsc_extract_unit4(chain, buf, &unit);
18222974e916SYuri Benditovich     if (virtio_net_rsc_sanity_check4(chain, unit.ip, buf, size)
18232974e916SYuri Benditovich         != RSC_CANDIDATE) {
18242974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18252974e916SYuri Benditovich     }
18262974e916SYuri Benditovich 
18272974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
18282974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
18292974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18302974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
18312974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
18322974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 12),
18332974e916SYuri Benditovich                 VIRTIO_NET_IP4_ADDR_SIZE,
18342974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header));
18352974e916SYuri Benditovich     }
18362974e916SYuri Benditovich 
18372974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
18382974e916SYuri Benditovich }
18392974e916SYuri Benditovich 
18402974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check6(VirtioNetRscChain *chain,
18412974e916SYuri Benditovich                                             struct ip6_header *ip6,
18422974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
18432974e916SYuri Benditovich {
18442974e916SYuri Benditovich     uint16_t ip_len;
18452974e916SYuri Benditovich 
18462974e916SYuri Benditovich     if (((ip6->ip6_ctlun.ip6_un1.ip6_un1_flow & 0xF0) >> 4)
18472974e916SYuri Benditovich         != IP_HEADER_VERSION_6) {
18482974e916SYuri Benditovich         return RSC_BYPASS;
18492974e916SYuri Benditovich     }
18502974e916SYuri Benditovich 
18512974e916SYuri Benditovich     /* Both option and protocol is checked in this */
18522974e916SYuri Benditovich     if (ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) {
18532974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
18542974e916SYuri Benditovich         return RSC_BYPASS;
18552974e916SYuri Benditovich     }
18562974e916SYuri Benditovich 
18572974e916SYuri Benditovich     ip_len = htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
18582974e916SYuri Benditovich     if (ip_len < sizeof(struct tcp_header) ||
18592974e916SYuri Benditovich         ip_len > (size - chain->n->guest_hdr_len - sizeof(struct eth_header)
18602974e916SYuri Benditovich                   - sizeof(struct ip6_header))) {
18612974e916SYuri Benditovich         chain->stat.ip_hacked++;
18622974e916SYuri Benditovich         return RSC_BYPASS;
18632974e916SYuri Benditovich     }
18642974e916SYuri Benditovich 
18652974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
18662974e916SYuri Benditovich     if (IP6_ECN(ip6->ip6_ctlun.ip6_un3.ip6_un3_ecn)) {
18672974e916SYuri Benditovich         chain->stat.ip_ecn++;
18682974e916SYuri Benditovich         return RSC_BYPASS;
18692974e916SYuri Benditovich     }
18702974e916SYuri Benditovich 
18712974e916SYuri Benditovich     return RSC_CANDIDATE;
18722974e916SYuri Benditovich }
18732974e916SYuri Benditovich 
18742974e916SYuri Benditovich static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc,
18752974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
18762974e916SYuri Benditovich {
18772974e916SYuri Benditovich     int32_t ret;
18782974e916SYuri Benditovich     uint16_t hdr_len;
18792974e916SYuri Benditovich     VirtioNetRscChain *chain;
18802974e916SYuri Benditovich     VirtioNetRscUnit unit;
18812974e916SYuri Benditovich 
18822974e916SYuri Benditovich     chain = (VirtioNetRscChain *)opq;
18832974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
18842974e916SYuri Benditovich 
18852974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header)
18862974e916SYuri Benditovich         + sizeof(tcp_header))) {
18872974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18882974e916SYuri Benditovich     }
18892974e916SYuri Benditovich 
18902974e916SYuri Benditovich     virtio_net_rsc_extract_unit6(chain, buf, &unit);
18912974e916SYuri Benditovich     if (RSC_CANDIDATE != virtio_net_rsc_sanity_check6(chain,
18922974e916SYuri Benditovich                                                  unit.ip, buf, size)) {
18932974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18942974e916SYuri Benditovich     }
18952974e916SYuri Benditovich 
18962974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
18972974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
18982974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18992974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
19002974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
19012974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 8),
19022974e916SYuri Benditovich                 VIRTIO_NET_IP6_ADDR_SIZE,
19032974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header)
19042974e916SYuri Benditovich                 + sizeof(struct ip6_header));
19052974e916SYuri Benditovich     }
19062974e916SYuri Benditovich 
19072974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
19082974e916SYuri Benditovich }
19092974e916SYuri Benditovich 
19102974e916SYuri Benditovich static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n,
19112974e916SYuri Benditovich                                                       NetClientState *nc,
19122974e916SYuri Benditovich                                                       uint16_t proto)
19132974e916SYuri Benditovich {
19142974e916SYuri Benditovich     VirtioNetRscChain *chain;
19152974e916SYuri Benditovich 
19162974e916SYuri Benditovich     if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) {
19172974e916SYuri Benditovich         return NULL;
19182974e916SYuri Benditovich     }
19192974e916SYuri Benditovich 
19202974e916SYuri Benditovich     QTAILQ_FOREACH(chain, &n->rsc_chains, next) {
19212974e916SYuri Benditovich         if (chain->proto == proto) {
19222974e916SYuri Benditovich             return chain;
19232974e916SYuri Benditovich         }
19242974e916SYuri Benditovich     }
19252974e916SYuri Benditovich 
19262974e916SYuri Benditovich     chain = g_malloc(sizeof(*chain));
19272974e916SYuri Benditovich     chain->n = n;
19282974e916SYuri Benditovich     chain->proto = proto;
19292974e916SYuri Benditovich     if (proto == (uint16_t)ETH_P_IP) {
19302974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP4_PAYLOAD;
19312974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
19322974e916SYuri Benditovich     } else {
19332974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD;
19342974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
19352974e916SYuri Benditovich     }
19362974e916SYuri Benditovich     chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST,
19372974e916SYuri Benditovich                                       virtio_net_rsc_purge, chain);
19382974e916SYuri Benditovich     memset(&chain->stat, 0, sizeof(chain->stat));
19392974e916SYuri Benditovich 
19402974e916SYuri Benditovich     QTAILQ_INIT(&chain->buffers);
19412974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next);
19422974e916SYuri Benditovich 
19432974e916SYuri Benditovich     return chain;
19442974e916SYuri Benditovich }
19452974e916SYuri Benditovich 
19462974e916SYuri Benditovich static ssize_t virtio_net_rsc_receive(NetClientState *nc,
19472974e916SYuri Benditovich                                       const uint8_t *buf,
19482974e916SYuri Benditovich                                       size_t size)
19492974e916SYuri Benditovich {
19502974e916SYuri Benditovich     uint16_t proto;
19512974e916SYuri Benditovich     VirtioNetRscChain *chain;
19522974e916SYuri Benditovich     struct eth_header *eth;
19532974e916SYuri Benditovich     VirtIONet *n;
19542974e916SYuri Benditovich 
19552974e916SYuri Benditovich     n = qemu_get_nic_opaque(nc);
19562974e916SYuri Benditovich     if (size < (n->host_hdr_len + sizeof(struct eth_header))) {
19572974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19582974e916SYuri Benditovich     }
19592974e916SYuri Benditovich 
19602974e916SYuri Benditovich     eth = (struct eth_header *)(buf + n->guest_hdr_len);
19612974e916SYuri Benditovich     proto = htons(eth->h_proto);
19622974e916SYuri Benditovich 
19632974e916SYuri Benditovich     chain = virtio_net_rsc_lookup_chain(n, nc, proto);
19642974e916SYuri Benditovich     if (chain) {
19652974e916SYuri Benditovich         chain->stat.received++;
19662974e916SYuri Benditovich         if (proto == (uint16_t)ETH_P_IP && n->rsc4_enabled) {
19672974e916SYuri Benditovich             return virtio_net_rsc_receive4(chain, nc, buf, size);
19682974e916SYuri Benditovich         } else if (proto == (uint16_t)ETH_P_IPV6 && n->rsc6_enabled) {
19692974e916SYuri Benditovich             return virtio_net_rsc_receive6(chain, nc, buf, size);
19702974e916SYuri Benditovich         }
19712974e916SYuri Benditovich     }
19722974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
19732974e916SYuri Benditovich }
19742974e916SYuri Benditovich 
19752974e916SYuri Benditovich static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf,
19762974e916SYuri Benditovich                                   size_t size)
19772974e916SYuri Benditovich {
19782974e916SYuri Benditovich     VirtIONet *n = qemu_get_nic_opaque(nc);
19792974e916SYuri Benditovich     if ((n->rsc4_enabled || n->rsc6_enabled)) {
19802974e916SYuri Benditovich         return virtio_net_rsc_receive(nc, buf, size);
19812974e916SYuri Benditovich     } else {
19822974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19832974e916SYuri Benditovich     }
19842974e916SYuri Benditovich }
19852974e916SYuri Benditovich 
19866e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q);
19876e790746SPaolo Bonzini 
19886e790746SPaolo Bonzini static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
19896e790746SPaolo Bonzini {
19906e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
19916e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
199217a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
19936e790746SPaolo Bonzini 
199451b19ebeSPaolo Bonzini     virtqueue_push(q->tx_vq, q->async_tx.elem, 0);
199517a0ca55SKONRAD Frederic     virtio_notify(vdev, q->tx_vq);
19966e790746SPaolo Bonzini 
199751b19ebeSPaolo Bonzini     g_free(q->async_tx.elem);
199851b19ebeSPaolo Bonzini     q->async_tx.elem = NULL;
19996e790746SPaolo Bonzini 
20006e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
20016e790746SPaolo Bonzini     virtio_net_flush_tx(q);
20026e790746SPaolo Bonzini }
20036e790746SPaolo Bonzini 
20046e790746SPaolo Bonzini /* TX */
20056e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
20066e790746SPaolo Bonzini {
20076e790746SPaolo Bonzini     VirtIONet *n = q->n;
200817a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
200951b19ebeSPaolo Bonzini     VirtQueueElement *elem;
20106e790746SPaolo Bonzini     int32_t num_packets = 0;
20116e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(q->tx_vq));
201217a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
20136e790746SPaolo Bonzini         return num_packets;
20146e790746SPaolo Bonzini     }
20156e790746SPaolo Bonzini 
201651b19ebeSPaolo Bonzini     if (q->async_tx.elem) {
20176e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
20186e790746SPaolo Bonzini         return num_packets;
20196e790746SPaolo Bonzini     }
20206e790746SPaolo Bonzini 
202151b19ebeSPaolo Bonzini     for (;;) {
2022bd89dd98SJason Wang         ssize_t ret;
202351b19ebeSPaolo Bonzini         unsigned int out_num;
202451b19ebeSPaolo Bonzini         struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
2025feb93f36SJason Wang         struct virtio_net_hdr_mrg_rxbuf mhdr;
20266e790746SPaolo Bonzini 
202751b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
202851b19ebeSPaolo Bonzini         if (!elem) {
202951b19ebeSPaolo Bonzini             break;
203051b19ebeSPaolo Bonzini         }
203151b19ebeSPaolo Bonzini 
203251b19ebeSPaolo Bonzini         out_num = elem->out_num;
203351b19ebeSPaolo Bonzini         out_sg = elem->out_sg;
20346e790746SPaolo Bonzini         if (out_num < 1) {
2035fa5e56c2SGreg Kurz             virtio_error(vdev, "virtio-net header not in first element");
2036fa5e56c2SGreg Kurz             virtqueue_detach_element(q->tx_vq, elem, 0);
2037fa5e56c2SGreg Kurz             g_free(elem);
2038fa5e56c2SGreg Kurz             return -EINVAL;
20396e790746SPaolo Bonzini         }
20406e790746SPaolo Bonzini 
2041032a74a1SCédric Le Goater         if (n->has_vnet_hdr) {
2042feb93f36SJason Wang             if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
2043feb93f36SJason Wang                 n->guest_hdr_len) {
2044fa5e56c2SGreg Kurz                 virtio_error(vdev, "virtio-net header incorrect");
2045fa5e56c2SGreg Kurz                 virtqueue_detach_element(q->tx_vq, elem, 0);
2046fa5e56c2SGreg Kurz                 g_free(elem);
2047fa5e56c2SGreg Kurz                 return -EINVAL;
2048032a74a1SCédric Le Goater             }
20491bfa316cSGreg Kurz             if (n->needs_vnet_hdr_swap) {
2050feb93f36SJason Wang                 virtio_net_hdr_swap(vdev, (void *) &mhdr);
2051feb93f36SJason Wang                 sg2[0].iov_base = &mhdr;
2052feb93f36SJason Wang                 sg2[0].iov_len = n->guest_hdr_len;
2053feb93f36SJason Wang                 out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
2054feb93f36SJason Wang                                    out_sg, out_num,
2055feb93f36SJason Wang                                    n->guest_hdr_len, -1);
2056feb93f36SJason Wang                 if (out_num == VIRTQUEUE_MAX_SIZE) {
2057feb93f36SJason Wang                     goto drop;
2058032a74a1SCédric Le Goater                 }
2059feb93f36SJason Wang                 out_num += 1;
2060feb93f36SJason Wang                 out_sg = sg2;
2061feb93f36SJason Wang             }
2062feb93f36SJason Wang         }
20636e790746SPaolo Bonzini         /*
20646e790746SPaolo Bonzini          * If host wants to see the guest header as is, we can
20656e790746SPaolo Bonzini          * pass it on unchanged. Otherwise, copy just the parts
20666e790746SPaolo Bonzini          * that host is interested in.
20676e790746SPaolo Bonzini          */
20686e790746SPaolo Bonzini         assert(n->host_hdr_len <= n->guest_hdr_len);
20696e790746SPaolo Bonzini         if (n->host_hdr_len != n->guest_hdr_len) {
20706e790746SPaolo Bonzini             unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg),
20716e790746SPaolo Bonzini                                        out_sg, out_num,
20726e790746SPaolo Bonzini                                        0, n->host_hdr_len);
20736e790746SPaolo Bonzini             sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num,
20746e790746SPaolo Bonzini                              out_sg, out_num,
20756e790746SPaolo Bonzini                              n->guest_hdr_len, -1);
20766e790746SPaolo Bonzini             out_num = sg_num;
20776e790746SPaolo Bonzini             out_sg = sg;
20786e790746SPaolo Bonzini         }
20796e790746SPaolo Bonzini 
20806e790746SPaolo Bonzini         ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
20816e790746SPaolo Bonzini                                       out_sg, out_num, virtio_net_tx_complete);
20826e790746SPaolo Bonzini         if (ret == 0) {
20836e790746SPaolo Bonzini             virtio_queue_set_notification(q->tx_vq, 0);
20846e790746SPaolo Bonzini             q->async_tx.elem = elem;
20856e790746SPaolo Bonzini             return -EBUSY;
20866e790746SPaolo Bonzini         }
20876e790746SPaolo Bonzini 
2088feb93f36SJason Wang drop:
208951b19ebeSPaolo Bonzini         virtqueue_push(q->tx_vq, elem, 0);
209017a0ca55SKONRAD Frederic         virtio_notify(vdev, q->tx_vq);
209151b19ebeSPaolo Bonzini         g_free(elem);
20926e790746SPaolo Bonzini 
20936e790746SPaolo Bonzini         if (++num_packets >= n->tx_burst) {
20946e790746SPaolo Bonzini             break;
20956e790746SPaolo Bonzini         }
20966e790746SPaolo Bonzini     }
20976e790746SPaolo Bonzini     return num_packets;
20986e790746SPaolo Bonzini }
20996e790746SPaolo Bonzini 
21006e790746SPaolo Bonzini static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
21016e790746SPaolo Bonzini {
210217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
21036e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
21046e790746SPaolo Bonzini 
2105283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2106283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2107283e2c2aSYuri Benditovich         return;
2108283e2c2aSYuri Benditovich     }
2109283e2c2aSYuri Benditovich 
21106e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
211117a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
21126e790746SPaolo Bonzini         q->tx_waiting = 1;
21136e790746SPaolo Bonzini         return;
21146e790746SPaolo Bonzini     }
21156e790746SPaolo Bonzini 
21166e790746SPaolo Bonzini     if (q->tx_waiting) {
21176e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 1);
2118bc72ad67SAlex Bligh         timer_del(q->tx_timer);
21196e790746SPaolo Bonzini         q->tx_waiting = 0;
2120fa5e56c2SGreg Kurz         if (virtio_net_flush_tx(q) == -EINVAL) {
2121fa5e56c2SGreg Kurz             return;
2122fa5e56c2SGreg Kurz         }
21236e790746SPaolo Bonzini     } else {
2124bc72ad67SAlex Bligh         timer_mod(q->tx_timer,
2125bc72ad67SAlex Bligh                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
21266e790746SPaolo Bonzini         q->tx_waiting = 1;
21276e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 0);
21286e790746SPaolo Bonzini     }
21296e790746SPaolo Bonzini }
21306e790746SPaolo Bonzini 
21316e790746SPaolo Bonzini static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
21326e790746SPaolo Bonzini {
213317a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
21346e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
21356e790746SPaolo Bonzini 
2136283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2137283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2138283e2c2aSYuri Benditovich         return;
2139283e2c2aSYuri Benditovich     }
2140283e2c2aSYuri Benditovich 
21416e790746SPaolo Bonzini     if (unlikely(q->tx_waiting)) {
21426e790746SPaolo Bonzini         return;
21436e790746SPaolo Bonzini     }
21446e790746SPaolo Bonzini     q->tx_waiting = 1;
21456e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
214617a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
21476e790746SPaolo Bonzini         return;
21486e790746SPaolo Bonzini     }
21496e790746SPaolo Bonzini     virtio_queue_set_notification(vq, 0);
21506e790746SPaolo Bonzini     qemu_bh_schedule(q->tx_bh);
21516e790746SPaolo Bonzini }
21526e790746SPaolo Bonzini 
21536e790746SPaolo Bonzini static void virtio_net_tx_timer(void *opaque)
21546e790746SPaolo Bonzini {
21556e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
21566e790746SPaolo Bonzini     VirtIONet *n = q->n;
215717a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2158e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2159e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2160e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2161e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2162e8bcf842SMichael S. Tsirkin         return;
2163e8bcf842SMichael S. Tsirkin     }
21646e790746SPaolo Bonzini 
21656e790746SPaolo Bonzini     q->tx_waiting = 0;
21666e790746SPaolo Bonzini 
21676e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
216817a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
21696e790746SPaolo Bonzini         return;
217017a0ca55SKONRAD Frederic     }
21716e790746SPaolo Bonzini 
21726e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
21736e790746SPaolo Bonzini     virtio_net_flush_tx(q);
21746e790746SPaolo Bonzini }
21756e790746SPaolo Bonzini 
21766e790746SPaolo Bonzini static void virtio_net_tx_bh(void *opaque)
21776e790746SPaolo Bonzini {
21786e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
21796e790746SPaolo Bonzini     VirtIONet *n = q->n;
218017a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
21816e790746SPaolo Bonzini     int32_t ret;
21826e790746SPaolo Bonzini 
2183e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2184e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2185e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2186e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2187e8bcf842SMichael S. Tsirkin         return;
2188e8bcf842SMichael S. Tsirkin     }
21896e790746SPaolo Bonzini 
21906e790746SPaolo Bonzini     q->tx_waiting = 0;
21916e790746SPaolo Bonzini 
21926e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
219317a0ca55SKONRAD Frederic     if (unlikely(!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) {
21946e790746SPaolo Bonzini         return;
219517a0ca55SKONRAD Frederic     }
21966e790746SPaolo Bonzini 
21976e790746SPaolo Bonzini     ret = virtio_net_flush_tx(q);
2198fa5e56c2SGreg Kurz     if (ret == -EBUSY || ret == -EINVAL) {
2199fa5e56c2SGreg Kurz         return; /* Notification re-enable handled by tx_complete or device
2200fa5e56c2SGreg Kurz                  * broken */
22016e790746SPaolo Bonzini     }
22026e790746SPaolo Bonzini 
22036e790746SPaolo Bonzini     /* If we flush a full burst of packets, assume there are
22046e790746SPaolo Bonzini      * more coming and immediately reschedule */
22056e790746SPaolo Bonzini     if (ret >= n->tx_burst) {
22066e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
22076e790746SPaolo Bonzini         q->tx_waiting = 1;
22086e790746SPaolo Bonzini         return;
22096e790746SPaolo Bonzini     }
22106e790746SPaolo Bonzini 
22116e790746SPaolo Bonzini     /* If less than a full burst, re-enable notification and flush
22126e790746SPaolo Bonzini      * anything that may have come in while we weren't looking.  If
22136e790746SPaolo Bonzini      * we find something, assume the guest is still active and reschedule */
22146e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
2215fa5e56c2SGreg Kurz     ret = virtio_net_flush_tx(q);
2216fa5e56c2SGreg Kurz     if (ret == -EINVAL) {
2217fa5e56c2SGreg Kurz         return;
2218fa5e56c2SGreg Kurz     } else if (ret > 0) {
22196e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
22206e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
22216e790746SPaolo Bonzini         q->tx_waiting = 1;
22226e790746SPaolo Bonzini     }
22236e790746SPaolo Bonzini }
22246e790746SPaolo Bonzini 
2225f9d6dbf0SWen Congyang static void virtio_net_add_queue(VirtIONet *n, int index)
2226f9d6dbf0SWen Congyang {
2227f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2228f9d6dbf0SWen Congyang 
22291c0fbfa3SMichael S. Tsirkin     n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size,
22301c0fbfa3SMichael S. Tsirkin                                            virtio_net_handle_rx);
22319b02e161SWei Wang 
2232f9d6dbf0SWen Congyang     if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) {
2233f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
22349b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
22359b02e161SWei Wang                              virtio_net_handle_tx_timer);
2236f9d6dbf0SWen Congyang         n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
2237f9d6dbf0SWen Congyang                                               virtio_net_tx_timer,
2238f9d6dbf0SWen Congyang                                               &n->vqs[index]);
2239f9d6dbf0SWen Congyang     } else {
2240f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
22419b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
22429b02e161SWei Wang                              virtio_net_handle_tx_bh);
2243f9d6dbf0SWen Congyang         n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]);
2244f9d6dbf0SWen Congyang     }
2245f9d6dbf0SWen Congyang 
2246f9d6dbf0SWen Congyang     n->vqs[index].tx_waiting = 0;
2247f9d6dbf0SWen Congyang     n->vqs[index].n = n;
2248f9d6dbf0SWen Congyang }
2249f9d6dbf0SWen Congyang 
2250f9d6dbf0SWen Congyang static void virtio_net_del_queue(VirtIONet *n, int index)
2251f9d6dbf0SWen Congyang {
2252f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2253f9d6dbf0SWen Congyang     VirtIONetQueue *q = &n->vqs[index];
2254f9d6dbf0SWen Congyang     NetClientState *nc = qemu_get_subqueue(n->nic, index);
2255f9d6dbf0SWen Congyang 
2256f9d6dbf0SWen Congyang     qemu_purge_queued_packets(nc);
2257f9d6dbf0SWen Congyang 
2258f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2);
2259f9d6dbf0SWen Congyang     if (q->tx_timer) {
2260f9d6dbf0SWen Congyang         timer_del(q->tx_timer);
2261f9d6dbf0SWen Congyang         timer_free(q->tx_timer);
2262f989c30cSYunjian Wang         q->tx_timer = NULL;
2263f9d6dbf0SWen Congyang     } else {
2264f9d6dbf0SWen Congyang         qemu_bh_delete(q->tx_bh);
2265f989c30cSYunjian Wang         q->tx_bh = NULL;
2266f9d6dbf0SWen Congyang     }
2267f989c30cSYunjian Wang     q->tx_waiting = 0;
2268f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2 + 1);
2269f9d6dbf0SWen Congyang }
2270f9d6dbf0SWen Congyang 
2271f9d6dbf0SWen Congyang static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues)
2272f9d6dbf0SWen Congyang {
2273f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2274f9d6dbf0SWen Congyang     int old_num_queues = virtio_get_num_queues(vdev);
2275f9d6dbf0SWen Congyang     int new_num_queues = new_max_queues * 2 + 1;
2276f9d6dbf0SWen Congyang     int i;
2277f9d6dbf0SWen Congyang 
2278f9d6dbf0SWen Congyang     assert(old_num_queues >= 3);
2279f9d6dbf0SWen Congyang     assert(old_num_queues % 2 == 1);
2280f9d6dbf0SWen Congyang 
2281f9d6dbf0SWen Congyang     if (old_num_queues == new_num_queues) {
2282f9d6dbf0SWen Congyang         return;
2283f9d6dbf0SWen Congyang     }
2284f9d6dbf0SWen Congyang 
2285f9d6dbf0SWen Congyang     /*
2286f9d6dbf0SWen Congyang      * We always need to remove and add ctrl vq if
2287f9d6dbf0SWen Congyang      * old_num_queues != new_num_queues. Remove ctrl_vq first,
228820f86a75SYuval Shaia      * and then we only enter one of the following two loops.
2289f9d6dbf0SWen Congyang      */
2290f9d6dbf0SWen Congyang     virtio_del_queue(vdev, old_num_queues - 1);
2291f9d6dbf0SWen Congyang 
2292f9d6dbf0SWen Congyang     for (i = new_num_queues - 1; i < old_num_queues - 1; i += 2) {
2293f9d6dbf0SWen Congyang         /* new_num_queues < old_num_queues */
2294f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i / 2);
2295f9d6dbf0SWen Congyang     }
2296f9d6dbf0SWen Congyang 
2297f9d6dbf0SWen Congyang     for (i = old_num_queues - 1; i < new_num_queues - 1; i += 2) {
2298f9d6dbf0SWen Congyang         /* new_num_queues > old_num_queues */
2299f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i / 2);
2300f9d6dbf0SWen Congyang     }
2301f9d6dbf0SWen Congyang 
2302f9d6dbf0SWen Congyang     /* add ctrl_vq last */
2303f9d6dbf0SWen Congyang     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
2304f9d6dbf0SWen Congyang }
2305f9d6dbf0SWen Congyang 
2306ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
23076e790746SPaolo Bonzini {
2308f9d6dbf0SWen Congyang     int max = multiqueue ? n->max_queues : 1;
2309f9d6dbf0SWen Congyang 
23106e790746SPaolo Bonzini     n->multiqueue = multiqueue;
2311f9d6dbf0SWen Congyang     virtio_net_change_num_queues(n, max);
23126e790746SPaolo Bonzini 
23136e790746SPaolo Bonzini     virtio_net_set_queues(n);
23146e790746SPaolo Bonzini }
23156e790746SPaolo Bonzini 
2316982b78c5SDr. David Alan Gilbert static int virtio_net_post_load_device(void *opaque, int version_id)
2317037dab2fSGreg Kurz {
2318982b78c5SDr. David Alan Gilbert     VirtIONet *n = opaque;
2319982b78c5SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2320037dab2fSGreg Kurz     int i, link_down;
2321037dab2fSGreg Kurz 
23229d8c6a25SDr. David Alan Gilbert     trace_virtio_net_post_load_device();
2323982b78c5SDr. David Alan Gilbert     virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
232495129d6fSCornelia Huck                                virtio_vdev_has_feature(vdev,
232595129d6fSCornelia Huck                                                        VIRTIO_F_VERSION_1));
23266e790746SPaolo Bonzini 
23276e790746SPaolo Bonzini     /* MAC_TABLE_ENTRIES may be different from the saved image */
2328982b78c5SDr. David Alan Gilbert     if (n->mac_table.in_use > MAC_TABLE_ENTRIES) {
23296e790746SPaolo Bonzini         n->mac_table.in_use = 0;
23306e790746SPaolo Bonzini     }
23316e790746SPaolo Bonzini 
2332982b78c5SDr. David Alan Gilbert     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
23336c666823SMichael S. Tsirkin         n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
23346c666823SMichael S. Tsirkin     }
23356c666823SMichael S. Tsirkin 
23366c666823SMichael S. Tsirkin     if (peer_has_vnet_hdr(n)) {
23376c666823SMichael S. Tsirkin         virtio_net_apply_guest_offloads(n);
23386c666823SMichael S. Tsirkin     }
23396c666823SMichael S. Tsirkin 
23406e790746SPaolo Bonzini     virtio_net_set_queues(n);
23416e790746SPaolo Bonzini 
23426e790746SPaolo Bonzini     /* Find the first multicast entry in the saved MAC filter */
23436e790746SPaolo Bonzini     for (i = 0; i < n->mac_table.in_use; i++) {
23446e790746SPaolo Bonzini         if (n->mac_table.macs[i * ETH_ALEN] & 1) {
23456e790746SPaolo Bonzini             break;
23466e790746SPaolo Bonzini         }
23476e790746SPaolo Bonzini     }
23486e790746SPaolo Bonzini     n->mac_table.first_multi = i;
23496e790746SPaolo Bonzini 
23506e790746SPaolo Bonzini     /* nc.link_down can't be migrated, so infer link_down according
23516e790746SPaolo Bonzini      * to link status bit in n->status */
23526e790746SPaolo Bonzini     link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0;
23536e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
23546e790746SPaolo Bonzini         qemu_get_subqueue(n->nic, i)->link_down = link_down;
23556e790746SPaolo Bonzini     }
23566e790746SPaolo Bonzini 
23576c666823SMichael S. Tsirkin     if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
23586c666823SMichael S. Tsirkin         virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
23599d8c6a25SDr. David Alan Gilbert         qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(),
23609d8c6a25SDr. David Alan Gilbert                                   QEMU_CLOCK_VIRTUAL,
23619d8c6a25SDr. David Alan Gilbert                                   virtio_net_announce_timer, n);
23629d8c6a25SDr. David Alan Gilbert         if (n->announce_timer.round) {
23639d8c6a25SDr. David Alan Gilbert             timer_mod(n->announce_timer.tm,
23649d8c6a25SDr. David Alan Gilbert                       qemu_clock_get_ms(n->announce_timer.type));
23659d8c6a25SDr. David Alan Gilbert         } else {
2366944458b6SDr. David Alan Gilbert             qemu_announce_timer_del(&n->announce_timer, false);
23679d8c6a25SDr. David Alan Gilbert         }
23686c666823SMichael S. Tsirkin     }
23696c666823SMichael S. Tsirkin 
23706e790746SPaolo Bonzini     return 0;
23716e790746SPaolo Bonzini }
23726e790746SPaolo Bonzini 
2373982b78c5SDr. David Alan Gilbert /* tx_waiting field of a VirtIONetQueue */
2374982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = {
2375982b78c5SDr. David Alan Gilbert     .name = "virtio-net-queue-tx_waiting",
2376982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2377982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(tx_waiting, VirtIONetQueue),
2378982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2379982b78c5SDr. David Alan Gilbert    },
2380982b78c5SDr. David Alan Gilbert };
2381982b78c5SDr. David Alan Gilbert 
2382982b78c5SDr. David Alan Gilbert static bool max_queues_gt_1(void *opaque, int version_id)
2383982b78c5SDr. David Alan Gilbert {
2384982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->max_queues > 1;
2385982b78c5SDr. David Alan Gilbert }
2386982b78c5SDr. David Alan Gilbert 
2387982b78c5SDr. David Alan Gilbert static bool has_ctrl_guest_offloads(void *opaque, int version_id)
2388982b78c5SDr. David Alan Gilbert {
2389982b78c5SDr. David Alan Gilbert     return virtio_vdev_has_feature(VIRTIO_DEVICE(opaque),
2390982b78c5SDr. David Alan Gilbert                                    VIRTIO_NET_F_CTRL_GUEST_OFFLOADS);
2391982b78c5SDr. David Alan Gilbert }
2392982b78c5SDr. David Alan Gilbert 
2393982b78c5SDr. David Alan Gilbert static bool mac_table_fits(void *opaque, int version_id)
2394982b78c5SDr. David Alan Gilbert {
2395982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->mac_table.in_use <= MAC_TABLE_ENTRIES;
2396982b78c5SDr. David Alan Gilbert }
2397982b78c5SDr. David Alan Gilbert 
2398982b78c5SDr. David Alan Gilbert static bool mac_table_doesnt_fit(void *opaque, int version_id)
2399982b78c5SDr. David Alan Gilbert {
2400982b78c5SDr. David Alan Gilbert     return !mac_table_fits(opaque, version_id);
2401982b78c5SDr. David Alan Gilbert }
2402982b78c5SDr. David Alan Gilbert 
2403982b78c5SDr. David Alan Gilbert /* This temporary type is shared by all the WITH_TMP methods
2404982b78c5SDr. David Alan Gilbert  * although only some fields are used by each.
2405982b78c5SDr. David Alan Gilbert  */
2406982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp {
2407982b78c5SDr. David Alan Gilbert     VirtIONet      *parent;
2408982b78c5SDr. David Alan Gilbert     VirtIONetQueue *vqs_1;
2409982b78c5SDr. David Alan Gilbert     uint16_t        curr_queues_1;
2410982b78c5SDr. David Alan Gilbert     uint8_t         has_ufo;
2411982b78c5SDr. David Alan Gilbert     uint32_t        has_vnet_hdr;
2412982b78c5SDr. David Alan Gilbert };
2413982b78c5SDr. David Alan Gilbert 
2414982b78c5SDr. David Alan Gilbert /* The 2nd and subsequent tx_waiting flags are loaded later than
2415982b78c5SDr. David Alan Gilbert  * the 1st entry in the queues and only if there's more than one
2416982b78c5SDr. David Alan Gilbert  * entry.  We use the tmp mechanism to calculate a temporary
2417982b78c5SDr. David Alan Gilbert  * pointer and count and also validate the count.
2418982b78c5SDr. David Alan Gilbert  */
2419982b78c5SDr. David Alan Gilbert 
242044b1ff31SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_save(void *opaque)
2421982b78c5SDr. David Alan Gilbert {
2422982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2423982b78c5SDr. David Alan Gilbert 
2424982b78c5SDr. David Alan Gilbert     tmp->vqs_1 = tmp->parent->vqs + 1;
2425982b78c5SDr. David Alan Gilbert     tmp->curr_queues_1 = tmp->parent->curr_queues - 1;
2426982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues == 0) {
2427982b78c5SDr. David Alan Gilbert         tmp->curr_queues_1 = 0;
2428982b78c5SDr. David Alan Gilbert     }
242944b1ff31SDr. David Alan Gilbert 
243044b1ff31SDr. David Alan Gilbert     return 0;
2431982b78c5SDr. David Alan Gilbert }
2432982b78c5SDr. David Alan Gilbert 
2433982b78c5SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_load(void *opaque)
2434982b78c5SDr. David Alan Gilbert {
2435982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2436982b78c5SDr. David Alan Gilbert 
2437982b78c5SDr. David Alan Gilbert     /* Reuse the pointer setup from save */
2438982b78c5SDr. David Alan Gilbert     virtio_net_tx_waiting_pre_save(opaque);
2439982b78c5SDr. David Alan Gilbert 
2440982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues > tmp->parent->max_queues) {
2441982b78c5SDr. David Alan Gilbert         error_report("virtio-net: curr_queues %x > max_queues %x",
2442982b78c5SDr. David Alan Gilbert             tmp->parent->curr_queues, tmp->parent->max_queues);
2443982b78c5SDr. David Alan Gilbert 
2444982b78c5SDr. David Alan Gilbert         return -EINVAL;
2445982b78c5SDr. David Alan Gilbert     }
2446982b78c5SDr. David Alan Gilbert 
2447982b78c5SDr. David Alan Gilbert     return 0; /* all good */
2448982b78c5SDr. David Alan Gilbert }
2449982b78c5SDr. David Alan Gilbert 
2450982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_tx_waiting = {
2451982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-tx_waiting",
2452982b78c5SDr. David Alan Gilbert     .pre_load  = virtio_net_tx_waiting_pre_load,
2453982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_tx_waiting_pre_save,
2454982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2455982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp,
2456982b78c5SDr. David Alan Gilbert                                      curr_queues_1,
2457982b78c5SDr. David Alan Gilbert                                      vmstate_virtio_net_queue_tx_waiting,
2458982b78c5SDr. David Alan Gilbert                                      struct VirtIONetQueue),
2459982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2460982b78c5SDr. David Alan Gilbert     },
2461982b78c5SDr. David Alan Gilbert };
2462982b78c5SDr. David Alan Gilbert 
2463982b78c5SDr. David Alan Gilbert /* the 'has_ufo' flag is just tested; if the incoming stream has the
2464982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2465982b78c5SDr. David Alan Gilbert  */
2466982b78c5SDr. David Alan Gilbert static int virtio_net_ufo_post_load(void *opaque, int version_id)
2467982b78c5SDr. David Alan Gilbert {
2468982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2469982b78c5SDr. David Alan Gilbert 
2470982b78c5SDr. David Alan Gilbert     if (tmp->has_ufo && !peer_has_ufo(tmp->parent)) {
2471982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires TUN_F_UFO support");
2472982b78c5SDr. David Alan Gilbert         return -EINVAL;
2473982b78c5SDr. David Alan Gilbert     }
2474982b78c5SDr. David Alan Gilbert 
2475982b78c5SDr. David Alan Gilbert     return 0;
2476982b78c5SDr. David Alan Gilbert }
2477982b78c5SDr. David Alan Gilbert 
247844b1ff31SDr. David Alan Gilbert static int virtio_net_ufo_pre_save(void *opaque)
2479982b78c5SDr. David Alan Gilbert {
2480982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2481982b78c5SDr. David Alan Gilbert 
2482982b78c5SDr. David Alan Gilbert     tmp->has_ufo = tmp->parent->has_ufo;
248344b1ff31SDr. David Alan Gilbert 
248444b1ff31SDr. David Alan Gilbert     return 0;
2485982b78c5SDr. David Alan Gilbert }
2486982b78c5SDr. David Alan Gilbert 
2487982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_ufo = {
2488982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-ufo",
2489982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_ufo_post_load,
2490982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_ufo_pre_save,
2491982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2492982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(has_ufo, struct VirtIONetMigTmp),
2493982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2494982b78c5SDr. David Alan Gilbert     },
2495982b78c5SDr. David Alan Gilbert };
2496982b78c5SDr. David Alan Gilbert 
2497982b78c5SDr. David Alan Gilbert /* the 'has_vnet_hdr' flag is just tested; if the incoming stream has the
2498982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2499982b78c5SDr. David Alan Gilbert  */
2500982b78c5SDr. David Alan Gilbert static int virtio_net_vnet_post_load(void *opaque, int version_id)
2501982b78c5SDr. David Alan Gilbert {
2502982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2503982b78c5SDr. David Alan Gilbert 
2504982b78c5SDr. David Alan Gilbert     if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) {
2505982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires vnet_hdr=on");
2506982b78c5SDr. David Alan Gilbert         return -EINVAL;
2507982b78c5SDr. David Alan Gilbert     }
2508982b78c5SDr. David Alan Gilbert 
2509982b78c5SDr. David Alan Gilbert     return 0;
2510982b78c5SDr. David Alan Gilbert }
2511982b78c5SDr. David Alan Gilbert 
251244b1ff31SDr. David Alan Gilbert static int virtio_net_vnet_pre_save(void *opaque)
2513982b78c5SDr. David Alan Gilbert {
2514982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2515982b78c5SDr. David Alan Gilbert 
2516982b78c5SDr. David Alan Gilbert     tmp->has_vnet_hdr = tmp->parent->has_vnet_hdr;
251744b1ff31SDr. David Alan Gilbert 
251844b1ff31SDr. David Alan Gilbert     return 0;
2519982b78c5SDr. David Alan Gilbert }
2520982b78c5SDr. David Alan Gilbert 
2521982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_vnet = {
2522982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-vnet",
2523982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_vnet_post_load,
2524982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_vnet_pre_save,
2525982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2526982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(has_vnet_hdr, struct VirtIONetMigTmp),
2527982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2528982b78c5SDr. David Alan Gilbert     },
2529982b78c5SDr. David Alan Gilbert };
2530982b78c5SDr. David Alan Gilbert 
2531982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_device = {
2532982b78c5SDr. David Alan Gilbert     .name = "virtio-net-device",
2533982b78c5SDr. David Alan Gilbert     .version_id = VIRTIO_NET_VM_VERSION,
2534982b78c5SDr. David Alan Gilbert     .minimum_version_id = VIRTIO_NET_VM_VERSION,
2535982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_post_load_device,
2536982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2537982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN),
2538982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_POINTER(vqs, VirtIONet,
2539982b78c5SDr. David Alan Gilbert                                vmstate_virtio_net_queue_tx_waiting,
2540982b78c5SDr. David Alan Gilbert                                VirtIONetQueue),
2541982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mergeable_rx_bufs, VirtIONet),
2542982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16(status, VirtIONet),
2543982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(promisc, VirtIONet),
2544982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(allmulti, VirtIONet),
2545982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mac_table.in_use, VirtIONet),
2546982b78c5SDr. David Alan Gilbert 
2547982b78c5SDr. David Alan Gilbert         /* Guarded pair: If it fits we load it, else we throw it away
2548982b78c5SDr. David Alan Gilbert          * - can happen if source has a larger MAC table.; post-load
2549982b78c5SDr. David Alan Gilbert          *  sets flags in this case.
2550982b78c5SDr. David Alan Gilbert          */
2551982b78c5SDr. David Alan Gilbert         VMSTATE_VBUFFER_MULTIPLY(mac_table.macs, VirtIONet,
2552982b78c5SDr. David Alan Gilbert                                 0, mac_table_fits, mac_table.in_use,
2553982b78c5SDr. David Alan Gilbert                                  ETH_ALEN),
2554982b78c5SDr. David Alan Gilbert         VMSTATE_UNUSED_VARRAY_UINT32(VirtIONet, mac_table_doesnt_fit, 0,
2555982b78c5SDr. David Alan Gilbert                                      mac_table.in_use, ETH_ALEN),
2556982b78c5SDr. David Alan Gilbert 
2557982b78c5SDr. David Alan Gilbert         /* Note: This is an array of uint32's that's always been saved as a
2558982b78c5SDr. David Alan Gilbert          * buffer; hold onto your endiannesses; it's actually used as a bitmap
2559982b78c5SDr. David Alan Gilbert          * but based on the uint.
2560982b78c5SDr. David Alan Gilbert          */
2561982b78c5SDr. David Alan Gilbert         VMSTATE_BUFFER_POINTER_UNSAFE(vlans, VirtIONet, 0, MAX_VLAN >> 3),
2562982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2563982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_vnet),
2564982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.multi_overflow, VirtIONet),
2565982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.uni_overflow, VirtIONet),
2566982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(alluni, VirtIONet),
2567982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nomulti, VirtIONet),
2568982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nouni, VirtIONet),
2569982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nobcast, VirtIONet),
2570982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2571982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_ufo),
2572982b78c5SDr. David Alan Gilbert         VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0,
2573982b78c5SDr. David Alan Gilbert                             vmstate_info_uint16_equal, uint16_t),
2574982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1),
2575982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2576982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_tx_waiting),
2577982b78c5SDr. David Alan Gilbert         VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet,
2578982b78c5SDr. David Alan Gilbert                             has_ctrl_guest_offloads),
2579982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2580982b78c5SDr. David Alan Gilbert    },
2581982b78c5SDr. David Alan Gilbert };
2582982b78c5SDr. David Alan Gilbert 
25836e790746SPaolo Bonzini static NetClientInfo net_virtio_info = {
2584f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
25856e790746SPaolo Bonzini     .size = sizeof(NICState),
25866e790746SPaolo Bonzini     .can_receive = virtio_net_can_receive,
25876e790746SPaolo Bonzini     .receive = virtio_net_receive,
25886e790746SPaolo Bonzini     .link_status_changed = virtio_net_set_link_status,
2589b1be4280SAmos Kong     .query_rx_filter = virtio_net_query_rxfilter,
2590b2c929f0SDr. David Alan Gilbert     .announce = virtio_net_announce,
25916e790746SPaolo Bonzini };
25926e790746SPaolo Bonzini 
25936e790746SPaolo Bonzini static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
25946e790746SPaolo Bonzini {
259517a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
25966e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
25976e790746SPaolo Bonzini     assert(n->vhost_started);
2598ed8b4afeSNikolay Nikolaev     return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx);
25996e790746SPaolo Bonzini }
26006e790746SPaolo Bonzini 
26016e790746SPaolo Bonzini static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
26026e790746SPaolo Bonzini                                            bool mask)
26036e790746SPaolo Bonzini {
260417a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
26056e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
26066e790746SPaolo Bonzini     assert(n->vhost_started);
2607ed8b4afeSNikolay Nikolaev     vhost_net_virtqueue_mask(get_vhost_net(nc->peer),
26086e790746SPaolo Bonzini                              vdev, idx, mask);
26096e790746SPaolo Bonzini }
26106e790746SPaolo Bonzini 
2611019a3edbSGerd Hoffmann static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features)
26126e790746SPaolo Bonzini {
26130cd09c3aSCornelia Huck     virtio_add_feature(&host_features, VIRTIO_NET_F_MAC);
2614a93e599dSMaxime Coquelin 
2615ba550851SStefano Garzarella     n->config_size = virtio_feature_get_config_size(feature_sizes,
2616ba550851SStefano Garzarella                                                     host_features);
261717ec5a86SKONRAD Frederic }
26186e790746SPaolo Bonzini 
26198a253ec2SKONRAD Frederic void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
26208a253ec2SKONRAD Frederic                                    const char *type)
26218a253ec2SKONRAD Frederic {
26228a253ec2SKONRAD Frederic     /*
26238a253ec2SKONRAD Frederic      * The name can be NULL, the netclient name will be type.x.
26248a253ec2SKONRAD Frederic      */
26258a253ec2SKONRAD Frederic     assert(type != NULL);
26268a253ec2SKONRAD Frederic 
26278a253ec2SKONRAD Frederic     g_free(n->netclient_name);
26288a253ec2SKONRAD Frederic     g_free(n->netclient_type);
26298a253ec2SKONRAD Frederic     n->netclient_name = g_strdup(name);
26308a253ec2SKONRAD Frederic     n->netclient_type = g_strdup(type);
26318a253ec2SKONRAD Frederic }
26328a253ec2SKONRAD Frederic 
2633e6f746b3SAndreas Färber static void virtio_net_device_realize(DeviceState *dev, Error **errp)
263417ec5a86SKONRAD Frederic {
2635e6f746b3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
2636284a32f0SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
2637284a32f0SAndreas Färber     NetClientState *nc;
26381773d9eeSKONRAD Frederic     int i;
263917ec5a86SKONRAD Frederic 
2640a93e599dSMaxime Coquelin     if (n->net_conf.mtu) {
2641127833eeSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_MTU);
2642a93e599dSMaxime Coquelin     }
2643a93e599dSMaxime Coquelin 
26449473939eSJason Baron     if (n->net_conf.duplex_str) {
26459473939eSJason Baron         if (strncmp(n->net_conf.duplex_str, "half", 5) == 0) {
26469473939eSJason Baron             n->net_conf.duplex = DUPLEX_HALF;
26479473939eSJason Baron         } else if (strncmp(n->net_conf.duplex_str, "full", 5) == 0) {
26489473939eSJason Baron             n->net_conf.duplex = DUPLEX_FULL;
26499473939eSJason Baron         } else {
26509473939eSJason Baron             error_setg(errp, "'duplex' must be 'half' or 'full'");
26519473939eSJason Baron         }
26529473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
26539473939eSJason Baron     } else {
26549473939eSJason Baron         n->net_conf.duplex = DUPLEX_UNKNOWN;
26559473939eSJason Baron     }
26569473939eSJason Baron 
26579473939eSJason Baron     if (n->net_conf.speed < SPEED_UNKNOWN) {
26589473939eSJason Baron         error_setg(errp, "'speed' must be between 0 and INT_MAX");
26599473939eSJason Baron     } else if (n->net_conf.speed >= 0) {
26609473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
26619473939eSJason Baron     }
26629473939eSJason Baron 
2663da3e8a23SShannon Zhao     virtio_net_set_config_size(n, n->host_features);
2664284a32f0SAndreas Färber     virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size);
266517ec5a86SKONRAD Frederic 
26661c0fbfa3SMichael S. Tsirkin     /*
26671c0fbfa3SMichael S. Tsirkin      * We set a lower limit on RX queue size to what it always was.
26681c0fbfa3SMichael S. Tsirkin      * Guests that want a smaller ring can always resize it without
26691c0fbfa3SMichael S. Tsirkin      * help from us (using virtio 1 and up).
26701c0fbfa3SMichael S. Tsirkin      */
26711c0fbfa3SMichael S. Tsirkin     if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE ||
26721c0fbfa3SMichael S. Tsirkin         n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE ||
26735f997fd1SMichal Privoznik         !is_power_of_2(n->net_conf.rx_queue_size)) {
26741c0fbfa3SMichael S. Tsirkin         error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), "
26751c0fbfa3SMichael S. Tsirkin                    "must be a power of 2 between %d and %d.",
26761c0fbfa3SMichael S. Tsirkin                    n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE,
26771c0fbfa3SMichael S. Tsirkin                    VIRTQUEUE_MAX_SIZE);
26781c0fbfa3SMichael S. Tsirkin         virtio_cleanup(vdev);
26791c0fbfa3SMichael S. Tsirkin         return;
26801c0fbfa3SMichael S. Tsirkin     }
26811c0fbfa3SMichael S. Tsirkin 
26829b02e161SWei Wang     if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE ||
26839b02e161SWei Wang         n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE ||
26849b02e161SWei Wang         !is_power_of_2(n->net_conf.tx_queue_size)) {
26859b02e161SWei Wang         error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), "
26869b02e161SWei Wang                    "must be a power of 2 between %d and %d",
26879b02e161SWei Wang                    n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE,
26889b02e161SWei Wang                    VIRTQUEUE_MAX_SIZE);
26899b02e161SWei Wang         virtio_cleanup(vdev);
26909b02e161SWei Wang         return;
26919b02e161SWei Wang     }
26929b02e161SWei Wang 
2693575a1c0eSJiri Pirko     n->max_queues = MAX(n->nic_conf.peers.queues, 1);
269487b3bd1cSJason Wang     if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) {
26957e0e736eSJason Wang         error_setg(errp, "Invalid number of queues (= %" PRIu32 "), "
2696631b22eaSStefan Weil                    "must be a positive integer less than %d.",
269787b3bd1cSJason Wang                    n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2);
26987e0e736eSJason Wang         virtio_cleanup(vdev);
26997e0e736eSJason Wang         return;
27007e0e736eSJason Wang     }
27016e790746SPaolo Bonzini     n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues);
27026e790746SPaolo Bonzini     n->curr_queues = 1;
27031773d9eeSKONRAD Frederic     n->tx_timeout = n->net_conf.txtimer;
27046e790746SPaolo Bonzini 
27051773d9eeSKONRAD Frederic     if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer")
27061773d9eeSKONRAD Frederic                        && strcmp(n->net_conf.tx, "bh")) {
27070765691eSMarkus Armbruster         warn_report("virtio-net: "
27086e790746SPaolo Bonzini                     "Unknown option tx=%s, valid options: \"timer\" \"bh\"",
27091773d9eeSKONRAD Frederic                     n->net_conf.tx);
27100765691eSMarkus Armbruster         error_printf("Defaulting to \"bh\"");
27116e790746SPaolo Bonzini     }
27126e790746SPaolo Bonzini 
27132eef278bSMichael S. Tsirkin     n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n),
27142eef278bSMichael S. Tsirkin                                     n->net_conf.tx_queue_size);
27159b02e161SWei Wang 
2716da51a335SJason Wang     for (i = 0; i < n->max_queues; i++) {
2717f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i);
2718da51a335SJason Wang     }
2719da51a335SJason Wang 
272017a0ca55SKONRAD Frederic     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
27211773d9eeSKONRAD Frederic     qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);
27221773d9eeSKONRAD Frederic     memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac));
27236e790746SPaolo Bonzini     n->status = VIRTIO_NET_S_LINK_UP;
27249d8c6a25SDr. David Alan Gilbert     qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(),
27259d8c6a25SDr. David Alan Gilbert                               QEMU_CLOCK_VIRTUAL,
2726f57fcf70SJason Wang                               virtio_net_announce_timer, n);
2727b2c929f0SDr. David Alan Gilbert     n->announce_timer.round = 0;
27286e790746SPaolo Bonzini 
27298a253ec2SKONRAD Frederic     if (n->netclient_type) {
27308a253ec2SKONRAD Frederic         /*
27318a253ec2SKONRAD Frederic          * Happen when virtio_net_set_netclient_name has been called.
27328a253ec2SKONRAD Frederic          */
27338a253ec2SKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
27348a253ec2SKONRAD Frederic                               n->netclient_type, n->netclient_name, n);
27358a253ec2SKONRAD Frederic     } else {
27361773d9eeSKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
2737284a32f0SAndreas Färber                               object_get_typename(OBJECT(dev)), dev->id, n);
27388a253ec2SKONRAD Frederic     }
27398a253ec2SKONRAD Frederic 
27406e790746SPaolo Bonzini     peer_test_vnet_hdr(n);
27416e790746SPaolo Bonzini     if (peer_has_vnet_hdr(n)) {
27426e790746SPaolo Bonzini         for (i = 0; i < n->max_queues; i++) {
2743d6085e3aSStefan Hajnoczi             qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true);
27446e790746SPaolo Bonzini         }
27456e790746SPaolo Bonzini         n->host_hdr_len = sizeof(struct virtio_net_hdr);
27466e790746SPaolo Bonzini     } else {
27476e790746SPaolo Bonzini         n->host_hdr_len = 0;
27486e790746SPaolo Bonzini     }
27496e790746SPaolo Bonzini 
27501773d9eeSKONRAD Frederic     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->nic_conf.macaddr.a);
27516e790746SPaolo Bonzini 
27526e790746SPaolo Bonzini     n->vqs[0].tx_waiting = 0;
27531773d9eeSKONRAD Frederic     n->tx_burst = n->net_conf.txburst;
2754bb9d17f8SCornelia Huck     virtio_net_set_mrg_rx_bufs(n, 0, 0);
27556e790746SPaolo Bonzini     n->promisc = 1; /* for compatibility */
27566e790746SPaolo Bonzini 
27576e790746SPaolo Bonzini     n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
27586e790746SPaolo Bonzini 
27596e790746SPaolo Bonzini     n->vlans = g_malloc0(MAX_VLAN >> 3);
27606e790746SPaolo Bonzini 
2761b1be4280SAmos Kong     nc = qemu_get_queue(n->nic);
2762b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
2763b1be4280SAmos Kong 
27642974e916SYuri Benditovich     QTAILQ_INIT(&n->rsc_chains);
2765284a32f0SAndreas Färber     n->qdev = dev;
276617ec5a86SKONRAD Frederic }
276717ec5a86SKONRAD Frederic 
2768306ec6c3SAndreas Färber static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
276917ec5a86SKONRAD Frederic {
2770306ec6c3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
2771306ec6c3SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
2772f9d6dbf0SWen Congyang     int i, max_queues;
277317ec5a86SKONRAD Frederic 
277417ec5a86SKONRAD Frederic     /* This will stop vhost backend if appropriate. */
277517ec5a86SKONRAD Frederic     virtio_net_set_status(vdev, 0);
277617ec5a86SKONRAD Frederic 
27778a253ec2SKONRAD Frederic     g_free(n->netclient_name);
27788a253ec2SKONRAD Frederic     n->netclient_name = NULL;
27798a253ec2SKONRAD Frederic     g_free(n->netclient_type);
27808a253ec2SKONRAD Frederic     n->netclient_type = NULL;
27818a253ec2SKONRAD Frederic 
278217ec5a86SKONRAD Frederic     g_free(n->mac_table.macs);
278317ec5a86SKONRAD Frederic     g_free(n->vlans);
278417ec5a86SKONRAD Frederic 
2785f9d6dbf0SWen Congyang     max_queues = n->multiqueue ? n->max_queues : 1;
2786f9d6dbf0SWen Congyang     for (i = 0; i < max_queues; i++) {
2787f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i);
278817ec5a86SKONRAD Frederic     }
278917ec5a86SKONRAD Frederic 
2790944458b6SDr. David Alan Gilbert     qemu_announce_timer_del(&n->announce_timer, false);
279117ec5a86SKONRAD Frederic     g_free(n->vqs);
279217ec5a86SKONRAD Frederic     qemu_del_nic(n->nic);
27932974e916SYuri Benditovich     virtio_net_rsc_cleanup(n);
27946a1a8cc7SKONRAD Frederic     virtio_cleanup(vdev);
279517ec5a86SKONRAD Frederic }
279617ec5a86SKONRAD Frederic 
279717ec5a86SKONRAD Frederic static void virtio_net_instance_init(Object *obj)
279817ec5a86SKONRAD Frederic {
279917ec5a86SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(obj);
280017ec5a86SKONRAD Frederic 
280117ec5a86SKONRAD Frederic     /*
280217ec5a86SKONRAD Frederic      * The default config_size is sizeof(struct virtio_net_config).
280317ec5a86SKONRAD Frederic      * Can be overriden with virtio_net_set_config_size.
280417ec5a86SKONRAD Frederic      */
280517ec5a86SKONRAD Frederic     n->config_size = sizeof(struct virtio_net_config);
2806aa4197c3SGonglei     device_add_bootindex_property(obj, &n->nic_conf.bootindex,
2807aa4197c3SGonglei                                   "bootindex", "/ethernet-phy@0",
2808aa4197c3SGonglei                                   DEVICE(n), NULL);
280917ec5a86SKONRAD Frederic }
281017ec5a86SKONRAD Frederic 
281144b1ff31SDr. David Alan Gilbert static int virtio_net_pre_save(void *opaque)
28124d45dcfbSHalil Pasic {
28134d45dcfbSHalil Pasic     VirtIONet *n = opaque;
28144d45dcfbSHalil Pasic 
28154d45dcfbSHalil Pasic     /* At this point, backend must be stopped, otherwise
28164d45dcfbSHalil Pasic      * it might keep writing to memory. */
28174d45dcfbSHalil Pasic     assert(!n->vhost_started);
281844b1ff31SDr. David Alan Gilbert 
281944b1ff31SDr. David Alan Gilbert     return 0;
28204d45dcfbSHalil Pasic }
28214d45dcfbSHalil Pasic 
28224d45dcfbSHalil Pasic static const VMStateDescription vmstate_virtio_net = {
28234d45dcfbSHalil Pasic     .name = "virtio-net",
28244d45dcfbSHalil Pasic     .minimum_version_id = VIRTIO_NET_VM_VERSION,
28254d45dcfbSHalil Pasic     .version_id = VIRTIO_NET_VM_VERSION,
28264d45dcfbSHalil Pasic     .fields = (VMStateField[]) {
28274d45dcfbSHalil Pasic         VMSTATE_VIRTIO_DEVICE,
28284d45dcfbSHalil Pasic         VMSTATE_END_OF_LIST()
28294d45dcfbSHalil Pasic     },
28304d45dcfbSHalil Pasic     .pre_save = virtio_net_pre_save,
28314d45dcfbSHalil Pasic };
2832290c2428SDr. David Alan Gilbert 
283317ec5a86SKONRAD Frederic static Property virtio_net_properties[] = {
2834127833eeSJason Baron     DEFINE_PROP_BIT64("csum", VirtIONet, host_features,
2835127833eeSJason Baron                     VIRTIO_NET_F_CSUM, true),
2836127833eeSJason Baron     DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features,
283787108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_CSUM, true),
2838127833eeSJason Baron     DEFINE_PROP_BIT64("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true),
2839127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso4", VirtIONet, host_features,
284087108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO4, true),
2841127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso6", VirtIONet, host_features,
284287108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO6, true),
2843127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ecn", VirtIONet, host_features,
284487108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ECN, true),
2845127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ufo", VirtIONet, host_features,
284687108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_UFO, true),
2847127833eeSJason Baron     DEFINE_PROP_BIT64("guest_announce", VirtIONet, host_features,
284887108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ANNOUNCE, true),
2849127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso4", VirtIONet, host_features,
285087108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO4, true),
2851127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso6", VirtIONet, host_features,
285287108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO6, true),
2853127833eeSJason Baron     DEFINE_PROP_BIT64("host_ecn", VirtIONet, host_features,
285487108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_ECN, true),
2855127833eeSJason Baron     DEFINE_PROP_BIT64("host_ufo", VirtIONet, host_features,
285687108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_UFO, true),
2857127833eeSJason Baron     DEFINE_PROP_BIT64("mrg_rxbuf", VirtIONet, host_features,
285887108bb2SShannon Zhao                     VIRTIO_NET_F_MRG_RXBUF, true),
2859127833eeSJason Baron     DEFINE_PROP_BIT64("status", VirtIONet, host_features,
286087108bb2SShannon Zhao                     VIRTIO_NET_F_STATUS, true),
2861127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vq", VirtIONet, host_features,
286287108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VQ, true),
2863127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx", VirtIONet, host_features,
286487108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX, true),
2865127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vlan", VirtIONet, host_features,
286687108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VLAN, true),
2867127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx_extra", VirtIONet, host_features,
286887108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX_EXTRA, true),
2869127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_mac_addr", VirtIONet, host_features,
287087108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_MAC_ADDR, true),
2871127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features,
287287108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
2873127833eeSJason Baron     DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
28742974e916SYuri Benditovich     DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features,
28752974e916SYuri Benditovich                     VIRTIO_NET_F_RSC_EXT, false),
28762974e916SYuri Benditovich     DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout,
28772974e916SYuri Benditovich                        VIRTIO_NET_RSC_DEFAULT_INTERVAL),
287817ec5a86SKONRAD Frederic     DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf),
287917ec5a86SKONRAD Frederic     DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer,
288017ec5a86SKONRAD Frederic                        TX_TIMER_INTERVAL),
288117ec5a86SKONRAD Frederic     DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST),
288217ec5a86SKONRAD Frederic     DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx),
28831c0fbfa3SMichael S. Tsirkin     DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size,
28841c0fbfa3SMichael S. Tsirkin                        VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE),
28859b02e161SWei Wang     DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size,
28869b02e161SWei Wang                        VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE),
2887a93e599dSMaxime Coquelin     DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0),
288875ebec11SMaxime Coquelin     DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend,
288975ebec11SMaxime Coquelin                      true),
28909473939eSJason Baron     DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN),
28919473939eSJason Baron     DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str),
289217ec5a86SKONRAD Frederic     DEFINE_PROP_END_OF_LIST(),
289317ec5a86SKONRAD Frederic };
289417ec5a86SKONRAD Frederic 
289517ec5a86SKONRAD Frederic static void virtio_net_class_init(ObjectClass *klass, void *data)
289617ec5a86SKONRAD Frederic {
289717ec5a86SKONRAD Frederic     DeviceClass *dc = DEVICE_CLASS(klass);
289817ec5a86SKONRAD Frederic     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
2899e6f746b3SAndreas Färber 
290017ec5a86SKONRAD Frederic     dc->props = virtio_net_properties;
2901290c2428SDr. David Alan Gilbert     dc->vmsd = &vmstate_virtio_net;
2902125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
2903e6f746b3SAndreas Färber     vdc->realize = virtio_net_device_realize;
2904306ec6c3SAndreas Färber     vdc->unrealize = virtio_net_device_unrealize;
290517ec5a86SKONRAD Frederic     vdc->get_config = virtio_net_get_config;
290617ec5a86SKONRAD Frederic     vdc->set_config = virtio_net_set_config;
290717ec5a86SKONRAD Frederic     vdc->get_features = virtio_net_get_features;
290817ec5a86SKONRAD Frederic     vdc->set_features = virtio_net_set_features;
290917ec5a86SKONRAD Frederic     vdc->bad_features = virtio_net_bad_features;
291017ec5a86SKONRAD Frederic     vdc->reset = virtio_net_reset;
291117ec5a86SKONRAD Frederic     vdc->set_status = virtio_net_set_status;
291217ec5a86SKONRAD Frederic     vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
291317ec5a86SKONRAD Frederic     vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
29142a083ffdSMichael S. Tsirkin     vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO);
2915982b78c5SDr. David Alan Gilbert     vdc->vmsd = &vmstate_virtio_net_device;
291617ec5a86SKONRAD Frederic }
291717ec5a86SKONRAD Frederic 
291817ec5a86SKONRAD Frederic static const TypeInfo virtio_net_info = {
291917ec5a86SKONRAD Frederic     .name = TYPE_VIRTIO_NET,
292017ec5a86SKONRAD Frederic     .parent = TYPE_VIRTIO_DEVICE,
292117ec5a86SKONRAD Frederic     .instance_size = sizeof(VirtIONet),
292217ec5a86SKONRAD Frederic     .instance_init = virtio_net_instance_init,
292317ec5a86SKONRAD Frederic     .class_init = virtio_net_class_init,
292417ec5a86SKONRAD Frederic };
292517ec5a86SKONRAD Frederic 
292617ec5a86SKONRAD Frederic static void virtio_register_types(void)
292717ec5a86SKONRAD Frederic {
292817ec5a86SKONRAD Frederic     type_register_static(&virtio_net_info);
292917ec5a86SKONRAD Frederic }
293017ec5a86SKONRAD Frederic 
293117ec5a86SKONRAD Frederic type_init(virtio_register_types)
2932