xref: /openbmc/qemu/hw/net/virtio-net.c (revision d47e5e31c37be43dc11a7a29d168f535589dbebd)
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"
166e790746SPaolo Bonzini #include "hw/virtio/virtio.h"
176e790746SPaolo Bonzini #include "net/net.h"
186e790746SPaolo Bonzini #include "net/checksum.h"
196e790746SPaolo Bonzini #include "net/tap.h"
206e790746SPaolo Bonzini #include "qemu/error-report.h"
216e790746SPaolo Bonzini #include "qemu/timer.h"
226e790746SPaolo Bonzini #include "hw/virtio/virtio-net.h"
236e790746SPaolo Bonzini #include "net/vhost_net.h"
2417ec5a86SKONRAD Frederic #include "hw/virtio/virtio-bus.h"
25e688df6bSMarkus Armbruster #include "qapi/error.h"
269af23989SMarkus Armbruster #include "qapi/qapi-events-net.h"
271399c60dSRusty Russell #include "hw/virtio/virtio-access.h"
28f8d806c9SJuan Quintela #include "migration/misc.h"
299473939eSJason Baron #include "standard-headers/linux/ethtool.h"
306e790746SPaolo Bonzini 
316e790746SPaolo Bonzini #define VIRTIO_NET_VM_VERSION    11
326e790746SPaolo Bonzini 
336e790746SPaolo Bonzini #define MAC_TABLE_ENTRIES    64
346e790746SPaolo Bonzini #define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
356e790746SPaolo Bonzini 
361c0fbfa3SMichael S. Tsirkin /* previously fixed value */
371c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256
389b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256
399b02e161SWei Wang 
401c0fbfa3SMichael S. Tsirkin /* for now, only allow larger queues; with virtio-1, guest can downsize */
411c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE
429b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE
431c0fbfa3SMichael S. Tsirkin 
442974e916SYuri Benditovich #define VIRTIO_NET_IP4_ADDR_SIZE   8        /* ipv4 saddr + daddr */
452974e916SYuri Benditovich 
462974e916SYuri Benditovich #define VIRTIO_NET_TCP_FLAG         0x3F
472974e916SYuri Benditovich #define VIRTIO_NET_TCP_HDR_LENGTH   0xF000
482974e916SYuri Benditovich 
492974e916SYuri Benditovich /* IPv4 max payload, 16 bits in the header */
502974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP4_PAYLOAD (65535 - sizeof(struct ip_header))
512974e916SYuri Benditovich #define VIRTIO_NET_MAX_TCP_PAYLOAD 65535
522974e916SYuri Benditovich 
532974e916SYuri Benditovich /* header length value in ip header without option */
542974e916SYuri Benditovich #define VIRTIO_NET_IP4_HEADER_LENGTH 5
552974e916SYuri Benditovich 
562974e916SYuri Benditovich #define VIRTIO_NET_IP6_ADDR_SIZE   32      /* ipv6 saddr + daddr */
572974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP6_PAYLOAD VIRTIO_NET_MAX_TCP_PAYLOAD
582974e916SYuri Benditovich 
592974e916SYuri Benditovich /* Purge coalesced packets timer interval, This value affects the performance
602974e916SYuri Benditovich    a lot, and should be tuned carefully, '300000'(300us) is the recommended
612974e916SYuri Benditovich    value to pass the WHQL test, '50000' can gain 2x netperf throughput with
622974e916SYuri Benditovich    tso/gso/gro 'off'. */
632974e916SYuri Benditovich #define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000
642974e916SYuri Benditovich 
652974e916SYuri Benditovich /* temporary until standard header include it */
662974e916SYuri Benditovich #if !defined(VIRTIO_NET_HDR_F_RSC_INFO)
672974e916SYuri Benditovich 
682974e916SYuri Benditovich #define VIRTIO_NET_HDR_F_RSC_INFO  4 /* rsc_ext data in csum_ fields */
69*d47e5e31SYuri Benditovich #define VIRTIO_NET_F_RSC_EXT       61
702974e916SYuri Benditovich 
712974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_packets(
722974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
732974e916SYuri Benditovich {
742974e916SYuri Benditovich     return &hdr->csum_start;
752974e916SYuri Benditovich }
762974e916SYuri Benditovich 
772974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
782974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
792974e916SYuri Benditovich {
802974e916SYuri Benditovich     return &hdr->csum_offset;
812974e916SYuri Benditovich }
822974e916SYuri Benditovich 
832974e916SYuri Benditovich #endif
842974e916SYuri Benditovich 
856e790746SPaolo Bonzini /*
866e790746SPaolo Bonzini  * Calculate the number of bytes up to and including the given 'field' of
876e790746SPaolo Bonzini  * 'container'.
886e790746SPaolo Bonzini  */
896e790746SPaolo Bonzini #define endof(container, field) \
90f18793b0SStefan Hajnoczi     (offsetof(container, field) + sizeof_field(container, field))
916e790746SPaolo Bonzini 
926e790746SPaolo Bonzini typedef struct VirtIOFeature {
93127833eeSJason Baron     uint64_t flags;
946e790746SPaolo Bonzini     size_t end;
956e790746SPaolo Bonzini } VirtIOFeature;
966e790746SPaolo Bonzini 
976e790746SPaolo Bonzini static VirtIOFeature feature_sizes[] = {
98127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MAC,
996e790746SPaolo Bonzini      .end = endof(struct virtio_net_config, mac)},
100127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_STATUS,
1016e790746SPaolo Bonzini      .end = endof(struct virtio_net_config, status)},
102127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MQ,
1036e790746SPaolo Bonzini      .end = endof(struct virtio_net_config, max_virtqueue_pairs)},
104127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MTU,
105a93e599dSMaxime Coquelin      .end = endof(struct virtio_net_config, mtu)},
1069473939eSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
1079473939eSJason Baron      .end = endof(struct virtio_net_config, duplex)},
1086e790746SPaolo Bonzini     {}
1096e790746SPaolo Bonzini };
1106e790746SPaolo Bonzini 
1116e790746SPaolo Bonzini static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc)
1126e790746SPaolo Bonzini {
1136e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
1146e790746SPaolo Bonzini 
1156e790746SPaolo Bonzini     return &n->vqs[nc->queue_index];
1166e790746SPaolo Bonzini }
1176e790746SPaolo Bonzini 
1186e790746SPaolo Bonzini static int vq2q(int queue_index)
1196e790746SPaolo Bonzini {
1206e790746SPaolo Bonzini     return queue_index / 2;
1216e790746SPaolo Bonzini }
1226e790746SPaolo Bonzini 
1236e790746SPaolo Bonzini /* TODO
1246e790746SPaolo Bonzini  * - we could suppress RX interrupt if we were so inclined.
1256e790746SPaolo Bonzini  */
1266e790746SPaolo Bonzini 
1276e790746SPaolo Bonzini static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
1286e790746SPaolo Bonzini {
12917a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1306e790746SPaolo Bonzini     struct virtio_net_config netcfg;
1316e790746SPaolo Bonzini 
1321399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.status, n->status);
1331399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues);
134a93e599dSMaxime Coquelin     virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu);
1356e790746SPaolo Bonzini     memcpy(netcfg.mac, n->mac, ETH_ALEN);
1369473939eSJason Baron     virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed);
1379473939eSJason Baron     netcfg.duplex = n->net_conf.duplex;
1386e790746SPaolo Bonzini     memcpy(config, &netcfg, n->config_size);
1396e790746SPaolo Bonzini }
1406e790746SPaolo Bonzini 
1416e790746SPaolo Bonzini static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
1426e790746SPaolo Bonzini {
14317a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1446e790746SPaolo Bonzini     struct virtio_net_config netcfg = {};
1456e790746SPaolo Bonzini 
1466e790746SPaolo Bonzini     memcpy(&netcfg, config, n->config_size);
1476e790746SPaolo Bonzini 
14895129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
14995129d6fSCornelia Huck         !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) &&
1506e790746SPaolo Bonzini         memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
1516e790746SPaolo Bonzini         memcpy(n->mac, netcfg.mac, ETH_ALEN);
1526e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
1536e790746SPaolo Bonzini     }
1546e790746SPaolo Bonzini }
1556e790746SPaolo Bonzini 
1566e790746SPaolo Bonzini static bool virtio_net_started(VirtIONet *n, uint8_t status)
1576e790746SPaolo Bonzini {
15817a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1596e790746SPaolo Bonzini     return (status & VIRTIO_CONFIG_S_DRIVER_OK) &&
16017a0ca55SKONRAD Frederic         (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running;
1616e790746SPaolo Bonzini }
1626e790746SPaolo Bonzini 
163f57fcf70SJason Wang static void virtio_net_announce_timer(void *opaque)
164f57fcf70SJason Wang {
165f57fcf70SJason Wang     VirtIONet *n = opaque;
166f57fcf70SJason Wang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
167f57fcf70SJason Wang 
168f57fcf70SJason Wang     n->announce_counter--;
169f57fcf70SJason Wang     n->status |= VIRTIO_NET_S_ANNOUNCE;
170f57fcf70SJason Wang     virtio_notify_config(vdev);
171f57fcf70SJason Wang }
172f57fcf70SJason Wang 
1736e790746SPaolo Bonzini static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
1746e790746SPaolo Bonzini {
17517a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1766e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
1776e790746SPaolo Bonzini     int queues = n->multiqueue ? n->max_queues : 1;
1786e790746SPaolo Bonzini 
179ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
1806e790746SPaolo Bonzini         return;
1816e790746SPaolo Bonzini     }
1826e790746SPaolo Bonzini 
1838c1ac475SRadim Krčmář     if ((virtio_net_started(n, status) && !nc->peer->link_down) ==
1848c1ac475SRadim Krčmář         !!n->vhost_started) {
1856e790746SPaolo Bonzini         return;
1866e790746SPaolo Bonzini     }
1876e790746SPaolo Bonzini     if (!n->vhost_started) {
188086abc1cSMichael S. Tsirkin         int r, i;
189086abc1cSMichael S. Tsirkin 
1901bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
1911bfa316cSGreg Kurz             error_report("backend does not support %s vnet headers; "
1921bfa316cSGreg Kurz                          "falling back on userspace virtio",
1931bfa316cSGreg Kurz                          virtio_is_big_endian(vdev) ? "BE" : "LE");
1941bfa316cSGreg Kurz             return;
1951bfa316cSGreg Kurz         }
1961bfa316cSGreg Kurz 
197086abc1cSMichael S. Tsirkin         /* Any packets outstanding? Purge them to avoid touching rings
198086abc1cSMichael S. Tsirkin          * when vhost is running.
199086abc1cSMichael S. Tsirkin          */
200086abc1cSMichael S. Tsirkin         for (i = 0;  i < queues; i++) {
201086abc1cSMichael S. Tsirkin             NetClientState *qnc = qemu_get_subqueue(n->nic, i);
202086abc1cSMichael S. Tsirkin 
203086abc1cSMichael S. Tsirkin             /* Purge both directions: TX and RX. */
204086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->peer->incoming_queue, qnc);
205086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->incoming_queue, qnc->peer);
206086abc1cSMichael S. Tsirkin         }
207086abc1cSMichael S. Tsirkin 
208a93e599dSMaxime Coquelin         if (virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MTU)) {
209a93e599dSMaxime Coquelin             r = vhost_net_set_mtu(get_vhost_net(nc->peer), n->net_conf.mtu);
210a93e599dSMaxime Coquelin             if (r < 0) {
211a93e599dSMaxime Coquelin                 error_report("%uBytes MTU not supported by the backend",
212a93e599dSMaxime Coquelin                              n->net_conf.mtu);
213a93e599dSMaxime Coquelin 
214a93e599dSMaxime Coquelin                 return;
215a93e599dSMaxime Coquelin             }
216a93e599dSMaxime Coquelin         }
217a93e599dSMaxime Coquelin 
2186e790746SPaolo Bonzini         n->vhost_started = 1;
21917a0ca55SKONRAD Frederic         r = vhost_net_start(vdev, n->nic->ncs, queues);
2206e790746SPaolo Bonzini         if (r < 0) {
2216e790746SPaolo Bonzini             error_report("unable to start vhost net: %d: "
2226e790746SPaolo Bonzini                          "falling back on userspace virtio", -r);
2236e790746SPaolo Bonzini             n->vhost_started = 0;
2246e790746SPaolo Bonzini         }
2256e790746SPaolo Bonzini     } else {
22617a0ca55SKONRAD Frederic         vhost_net_stop(vdev, n->nic->ncs, queues);
2276e790746SPaolo Bonzini         n->vhost_started = 0;
2286e790746SPaolo Bonzini     }
2296e790746SPaolo Bonzini }
2306e790746SPaolo Bonzini 
2311bfa316cSGreg Kurz static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev,
2321bfa316cSGreg Kurz                                           NetClientState *peer,
2331bfa316cSGreg Kurz                                           bool enable)
2341bfa316cSGreg Kurz {
2351bfa316cSGreg Kurz     if (virtio_is_big_endian(vdev)) {
2361bfa316cSGreg Kurz         return qemu_set_vnet_be(peer, enable);
2371bfa316cSGreg Kurz     } else {
2381bfa316cSGreg Kurz         return qemu_set_vnet_le(peer, enable);
2391bfa316cSGreg Kurz     }
2401bfa316cSGreg Kurz }
2411bfa316cSGreg Kurz 
2421bfa316cSGreg Kurz static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs,
2431bfa316cSGreg Kurz                                        int queues, bool enable)
2441bfa316cSGreg Kurz {
2451bfa316cSGreg Kurz     int i;
2461bfa316cSGreg Kurz 
2471bfa316cSGreg Kurz     for (i = 0; i < queues; i++) {
2481bfa316cSGreg Kurz         if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 &&
2491bfa316cSGreg Kurz             enable) {
2501bfa316cSGreg Kurz             while (--i >= 0) {
2511bfa316cSGreg Kurz                 virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false);
2521bfa316cSGreg Kurz             }
2531bfa316cSGreg Kurz 
2541bfa316cSGreg Kurz             return true;
2551bfa316cSGreg Kurz         }
2561bfa316cSGreg Kurz     }
2571bfa316cSGreg Kurz 
2581bfa316cSGreg Kurz     return false;
2591bfa316cSGreg Kurz }
2601bfa316cSGreg Kurz 
2611bfa316cSGreg Kurz static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status)
2621bfa316cSGreg Kurz {
2631bfa316cSGreg Kurz     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2641bfa316cSGreg Kurz     int queues = n->multiqueue ? n->max_queues : 1;
2651bfa316cSGreg Kurz 
2661bfa316cSGreg Kurz     if (virtio_net_started(n, status)) {
2671bfa316cSGreg Kurz         /* Before using the device, we tell the network backend about the
2681bfa316cSGreg Kurz          * endianness to use when parsing vnet headers. If the backend
2691bfa316cSGreg Kurz          * can't do it, we fallback onto fixing the headers in the core
2701bfa316cSGreg Kurz          * virtio-net code.
2711bfa316cSGreg Kurz          */
2721bfa316cSGreg Kurz         n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs,
2731bfa316cSGreg Kurz                                                             queues, true);
2741bfa316cSGreg Kurz     } else if (virtio_net_started(n, vdev->status)) {
2751bfa316cSGreg Kurz         /* After using the device, we need to reset the network backend to
2761bfa316cSGreg Kurz          * the default (guest native endianness), otherwise the guest may
2771bfa316cSGreg Kurz          * lose network connectivity if it is rebooted into a different
2781bfa316cSGreg Kurz          * endianness.
2791bfa316cSGreg Kurz          */
2801bfa316cSGreg Kurz         virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false);
2811bfa316cSGreg Kurz     }
2821bfa316cSGreg Kurz }
2831bfa316cSGreg Kurz 
284283e2c2aSYuri Benditovich static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq)
285283e2c2aSYuri Benditovich {
286283e2c2aSYuri Benditovich     unsigned int dropped = virtqueue_drop_all(vq);
287283e2c2aSYuri Benditovich     if (dropped) {
288283e2c2aSYuri Benditovich         virtio_notify(vdev, vq);
289283e2c2aSYuri Benditovich     }
290283e2c2aSYuri Benditovich }
291283e2c2aSYuri Benditovich 
2926e790746SPaolo Bonzini static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
2936e790746SPaolo Bonzini {
29417a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
2956e790746SPaolo Bonzini     VirtIONetQueue *q;
2966e790746SPaolo Bonzini     int i;
2976e790746SPaolo Bonzini     uint8_t queue_status;
2986e790746SPaolo Bonzini 
2991bfa316cSGreg Kurz     virtio_net_vnet_endian_status(n, status);
3006e790746SPaolo Bonzini     virtio_net_vhost_status(n, status);
3016e790746SPaolo Bonzini 
3026e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
30338705bb5SFam Zheng         NetClientState *ncs = qemu_get_subqueue(n->nic, i);
30438705bb5SFam Zheng         bool queue_started;
3056e790746SPaolo Bonzini         q = &n->vqs[i];
3066e790746SPaolo Bonzini 
3076e790746SPaolo Bonzini         if ((!n->multiqueue && i != 0) || i >= n->curr_queues) {
3086e790746SPaolo Bonzini             queue_status = 0;
3096e790746SPaolo Bonzini         } else {
3106e790746SPaolo Bonzini             queue_status = status;
3116e790746SPaolo Bonzini         }
31238705bb5SFam Zheng         queue_started =
31338705bb5SFam Zheng             virtio_net_started(n, queue_status) && !n->vhost_started;
31438705bb5SFam Zheng 
31538705bb5SFam Zheng         if (queue_started) {
31638705bb5SFam Zheng             qemu_flush_queued_packets(ncs);
31738705bb5SFam Zheng         }
3186e790746SPaolo Bonzini 
3196e790746SPaolo Bonzini         if (!q->tx_waiting) {
3206e790746SPaolo Bonzini             continue;
3216e790746SPaolo Bonzini         }
3226e790746SPaolo Bonzini 
32338705bb5SFam Zheng         if (queue_started) {
3246e790746SPaolo Bonzini             if (q->tx_timer) {
325bc72ad67SAlex Bligh                 timer_mod(q->tx_timer,
326bc72ad67SAlex Bligh                                qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
3276e790746SPaolo Bonzini             } else {
3286e790746SPaolo Bonzini                 qemu_bh_schedule(q->tx_bh);
3296e790746SPaolo Bonzini             }
3306e790746SPaolo Bonzini         } else {
3316e790746SPaolo Bonzini             if (q->tx_timer) {
332bc72ad67SAlex Bligh                 timer_del(q->tx_timer);
3336e790746SPaolo Bonzini             } else {
3346e790746SPaolo Bonzini                 qemu_bh_cancel(q->tx_bh);
3356e790746SPaolo Bonzini             }
336283e2c2aSYuri Benditovich             if ((n->status & VIRTIO_NET_S_LINK_UP) == 0 &&
33770e53e6eSJason Wang                 (queue_status & VIRTIO_CONFIG_S_DRIVER_OK) &&
33870e53e6eSJason Wang                 vdev->vm_running) {
339283e2c2aSYuri Benditovich                 /* if tx is waiting we are likely have some packets in tx queue
340283e2c2aSYuri Benditovich                  * and disabled notification */
341283e2c2aSYuri Benditovich                 q->tx_waiting = 0;
342283e2c2aSYuri Benditovich                 virtio_queue_set_notification(q->tx_vq, 1);
343283e2c2aSYuri Benditovich                 virtio_net_drop_tx_queue_data(vdev, q->tx_vq);
344283e2c2aSYuri Benditovich             }
3456e790746SPaolo Bonzini         }
3466e790746SPaolo Bonzini     }
3476e790746SPaolo Bonzini }
3486e790746SPaolo Bonzini 
3496e790746SPaolo Bonzini static void virtio_net_set_link_status(NetClientState *nc)
3506e790746SPaolo Bonzini {
3516e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
35217a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
3536e790746SPaolo Bonzini     uint16_t old_status = n->status;
3546e790746SPaolo Bonzini 
3556e790746SPaolo Bonzini     if (nc->link_down)
3566e790746SPaolo Bonzini         n->status &= ~VIRTIO_NET_S_LINK_UP;
3576e790746SPaolo Bonzini     else
3586e790746SPaolo Bonzini         n->status |= VIRTIO_NET_S_LINK_UP;
3596e790746SPaolo Bonzini 
3606e790746SPaolo Bonzini     if (n->status != old_status)
36117a0ca55SKONRAD Frederic         virtio_notify_config(vdev);
3626e790746SPaolo Bonzini 
36317a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
3646e790746SPaolo Bonzini }
3656e790746SPaolo Bonzini 
366b1be4280SAmos Kong static void rxfilter_notify(NetClientState *nc)
367b1be4280SAmos Kong {
368b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
369b1be4280SAmos Kong 
370b1be4280SAmos Kong     if (nc->rxfilter_notify_enabled) {
37196e35046SAmos Kong         gchar *path = object_get_canonical_path(OBJECT(n->qdev));
37206150279SWenchao Xia         qapi_event_send_nic_rx_filter_changed(!!n->netclient_name,
3733ab72385SPeter Xu                                               n->netclient_name, path);
37496e35046SAmos Kong         g_free(path);
375b1be4280SAmos Kong 
376b1be4280SAmos Kong         /* disable event notification to avoid events flooding */
377b1be4280SAmos Kong         nc->rxfilter_notify_enabled = 0;
378b1be4280SAmos Kong     }
379b1be4280SAmos Kong }
380b1be4280SAmos Kong 
381f7bc8ef8SAmos Kong static intList *get_vlan_table(VirtIONet *n)
382f7bc8ef8SAmos Kong {
383f7bc8ef8SAmos Kong     intList *list, *entry;
384f7bc8ef8SAmos Kong     int i, j;
385f7bc8ef8SAmos Kong 
386f7bc8ef8SAmos Kong     list = NULL;
387f7bc8ef8SAmos Kong     for (i = 0; i < MAX_VLAN >> 5; i++) {
388f7bc8ef8SAmos Kong         for (j = 0; n->vlans[i] && j <= 0x1f; j++) {
389f7bc8ef8SAmos Kong             if (n->vlans[i] & (1U << j)) {
390f7bc8ef8SAmos Kong                 entry = g_malloc0(sizeof(*entry));
391f7bc8ef8SAmos Kong                 entry->value = (i << 5) + j;
392f7bc8ef8SAmos Kong                 entry->next = list;
393f7bc8ef8SAmos Kong                 list = entry;
394f7bc8ef8SAmos Kong             }
395f7bc8ef8SAmos Kong         }
396f7bc8ef8SAmos Kong     }
397f7bc8ef8SAmos Kong 
398f7bc8ef8SAmos Kong     return list;
399f7bc8ef8SAmos Kong }
400f7bc8ef8SAmos Kong 
401b1be4280SAmos Kong static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
402b1be4280SAmos Kong {
403b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
404f7bc8ef8SAmos Kong     VirtIODevice *vdev = VIRTIO_DEVICE(n);
405b1be4280SAmos Kong     RxFilterInfo *info;
406b1be4280SAmos Kong     strList *str_list, *entry;
407f7bc8ef8SAmos Kong     int i;
408b1be4280SAmos Kong 
409b1be4280SAmos Kong     info = g_malloc0(sizeof(*info));
410b1be4280SAmos Kong     info->name = g_strdup(nc->name);
411b1be4280SAmos Kong     info->promiscuous = n->promisc;
412b1be4280SAmos Kong 
413b1be4280SAmos Kong     if (n->nouni) {
414b1be4280SAmos Kong         info->unicast = RX_STATE_NONE;
415b1be4280SAmos Kong     } else if (n->alluni) {
416b1be4280SAmos Kong         info->unicast = RX_STATE_ALL;
417b1be4280SAmos Kong     } else {
418b1be4280SAmos Kong         info->unicast = RX_STATE_NORMAL;
419b1be4280SAmos Kong     }
420b1be4280SAmos Kong 
421b1be4280SAmos Kong     if (n->nomulti) {
422b1be4280SAmos Kong         info->multicast = RX_STATE_NONE;
423b1be4280SAmos Kong     } else if (n->allmulti) {
424b1be4280SAmos Kong         info->multicast = RX_STATE_ALL;
425b1be4280SAmos Kong     } else {
426b1be4280SAmos Kong         info->multicast = RX_STATE_NORMAL;
427b1be4280SAmos Kong     }
428b1be4280SAmos Kong 
429b1be4280SAmos Kong     info->broadcast_allowed = n->nobcast;
430b1be4280SAmos Kong     info->multicast_overflow = n->mac_table.multi_overflow;
431b1be4280SAmos Kong     info->unicast_overflow = n->mac_table.uni_overflow;
432b1be4280SAmos Kong 
433b0575ba4SScott Feldman     info->main_mac = qemu_mac_strdup_printf(n->mac);
434b1be4280SAmos Kong 
435b1be4280SAmos Kong     str_list = NULL;
436b1be4280SAmos Kong     for (i = 0; i < n->mac_table.first_multi; i++) {
437b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
438b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
439b1be4280SAmos Kong         entry->next = str_list;
440b1be4280SAmos Kong         str_list = entry;
441b1be4280SAmos Kong     }
442b1be4280SAmos Kong     info->unicast_table = str_list;
443b1be4280SAmos Kong 
444b1be4280SAmos Kong     str_list = NULL;
445b1be4280SAmos Kong     for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
446b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
447b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
448b1be4280SAmos Kong         entry->next = str_list;
449b1be4280SAmos Kong         str_list = entry;
450b1be4280SAmos Kong     }
451b1be4280SAmos Kong     info->multicast_table = str_list;
452f7bc8ef8SAmos Kong     info->vlan_table = get_vlan_table(n);
453b1be4280SAmos Kong 
45495129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) {
455f7bc8ef8SAmos Kong         info->vlan = RX_STATE_ALL;
456f7bc8ef8SAmos Kong     } else if (!info->vlan_table) {
457f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NONE;
458f7bc8ef8SAmos Kong     } else {
459f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NORMAL;
460b1be4280SAmos Kong     }
461b1be4280SAmos Kong 
462b1be4280SAmos Kong     /* enable event notification after query */
463b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
464b1be4280SAmos Kong 
465b1be4280SAmos Kong     return info;
466b1be4280SAmos Kong }
467b1be4280SAmos Kong 
4686e790746SPaolo Bonzini static void virtio_net_reset(VirtIODevice *vdev)
4696e790746SPaolo Bonzini {
47017a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
47194b52958SGreg Kurz     int i;
4726e790746SPaolo Bonzini 
4736e790746SPaolo Bonzini     /* Reset back to compatibility mode */
4746e790746SPaolo Bonzini     n->promisc = 1;
4756e790746SPaolo Bonzini     n->allmulti = 0;
4766e790746SPaolo Bonzini     n->alluni = 0;
4776e790746SPaolo Bonzini     n->nomulti = 0;
4786e790746SPaolo Bonzini     n->nouni = 0;
4796e790746SPaolo Bonzini     n->nobcast = 0;
4806e790746SPaolo Bonzini     /* multiqueue is disabled by default */
4816e790746SPaolo Bonzini     n->curr_queues = 1;
482f57fcf70SJason Wang     timer_del(n->announce_timer);
483f57fcf70SJason Wang     n->announce_counter = 0;
484f57fcf70SJason Wang     n->status &= ~VIRTIO_NET_S_ANNOUNCE;
4856e790746SPaolo Bonzini 
4866e790746SPaolo Bonzini     /* Flush any MAC and VLAN filter table state */
4876e790746SPaolo Bonzini     n->mac_table.in_use = 0;
4886e790746SPaolo Bonzini     n->mac_table.first_multi = 0;
4896e790746SPaolo Bonzini     n->mac_table.multi_overflow = 0;
4906e790746SPaolo Bonzini     n->mac_table.uni_overflow = 0;
4916e790746SPaolo Bonzini     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
4926e790746SPaolo Bonzini     memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac));
493702d66a8SMichael S. Tsirkin     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
4946e790746SPaolo Bonzini     memset(n->vlans, 0, MAX_VLAN >> 3);
49594b52958SGreg Kurz 
49694b52958SGreg Kurz     /* Flush any async TX */
49794b52958SGreg Kurz     for (i = 0;  i < n->max_queues; i++) {
49894b52958SGreg Kurz         NetClientState *nc = qemu_get_subqueue(n->nic, i);
49994b52958SGreg Kurz 
50094b52958SGreg Kurz         if (nc->peer) {
50194b52958SGreg Kurz             qemu_flush_or_purge_queued_packets(nc->peer, true);
50294b52958SGreg Kurz             assert(!virtio_net_get_subqueue(nc)->async_tx.elem);
50394b52958SGreg Kurz         }
50494b52958SGreg Kurz     }
5056e790746SPaolo Bonzini }
5066e790746SPaolo Bonzini 
5076e790746SPaolo Bonzini static void peer_test_vnet_hdr(VirtIONet *n)
5086e790746SPaolo Bonzini {
5096e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
5106e790746SPaolo Bonzini     if (!nc->peer) {
5116e790746SPaolo Bonzini         return;
5126e790746SPaolo Bonzini     }
5136e790746SPaolo Bonzini 
514d6085e3aSStefan Hajnoczi     n->has_vnet_hdr = qemu_has_vnet_hdr(nc->peer);
5156e790746SPaolo Bonzini }
5166e790746SPaolo Bonzini 
5176e790746SPaolo Bonzini static int peer_has_vnet_hdr(VirtIONet *n)
5186e790746SPaolo Bonzini {
5196e790746SPaolo Bonzini     return n->has_vnet_hdr;
5206e790746SPaolo Bonzini }
5216e790746SPaolo Bonzini 
5226e790746SPaolo Bonzini static int peer_has_ufo(VirtIONet *n)
5236e790746SPaolo Bonzini {
5246e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n))
5256e790746SPaolo Bonzini         return 0;
5266e790746SPaolo Bonzini 
527d6085e3aSStefan Hajnoczi     n->has_ufo = qemu_has_ufo(qemu_get_queue(n->nic)->peer);
5286e790746SPaolo Bonzini 
5296e790746SPaolo Bonzini     return n->has_ufo;
5306e790746SPaolo Bonzini }
5316e790746SPaolo Bonzini 
532bb9d17f8SCornelia Huck static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
533bb9d17f8SCornelia Huck                                        int version_1)
5346e790746SPaolo Bonzini {
5356e790746SPaolo Bonzini     int i;
5366e790746SPaolo Bonzini     NetClientState *nc;
5376e790746SPaolo Bonzini 
5386e790746SPaolo Bonzini     n->mergeable_rx_bufs = mergeable_rx_bufs;
5396e790746SPaolo Bonzini 
540bb9d17f8SCornelia Huck     if (version_1) {
541bb9d17f8SCornelia Huck         n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
542bb9d17f8SCornelia Huck     } else {
5436e790746SPaolo Bonzini         n->guest_hdr_len = n->mergeable_rx_bufs ?
544bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr_mrg_rxbuf) :
545bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr);
546bb9d17f8SCornelia Huck     }
5476e790746SPaolo Bonzini 
5486e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
5496e790746SPaolo Bonzini         nc = qemu_get_subqueue(n->nic, i);
5506e790746SPaolo Bonzini 
5516e790746SPaolo Bonzini         if (peer_has_vnet_hdr(n) &&
552d6085e3aSStefan Hajnoczi             qemu_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) {
553d6085e3aSStefan Hajnoczi             qemu_set_vnet_hdr_len(nc->peer, n->guest_hdr_len);
5546e790746SPaolo Bonzini             n->host_hdr_len = n->guest_hdr_len;
5556e790746SPaolo Bonzini         }
5566e790746SPaolo Bonzini     }
5576e790746SPaolo Bonzini }
5586e790746SPaolo Bonzini 
5592eef278bSMichael S. Tsirkin static int virtio_net_max_tx_queue_size(VirtIONet *n)
5602eef278bSMichael S. Tsirkin {
5612eef278bSMichael S. Tsirkin     NetClientState *peer = n->nic_conf.peers.ncs[0];
5622eef278bSMichael S. Tsirkin 
5632eef278bSMichael S. Tsirkin     /*
5642eef278bSMichael S. Tsirkin      * Backends other than vhost-user don't support max queue size.
5652eef278bSMichael S. Tsirkin      */
5662eef278bSMichael S. Tsirkin     if (!peer) {
5672eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
5682eef278bSMichael S. Tsirkin     }
5692eef278bSMichael S. Tsirkin 
5702eef278bSMichael S. Tsirkin     if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) {
5712eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
5722eef278bSMichael S. Tsirkin     }
5732eef278bSMichael S. Tsirkin 
5742eef278bSMichael S. Tsirkin     return VIRTQUEUE_MAX_SIZE;
5752eef278bSMichael S. Tsirkin }
5762eef278bSMichael S. Tsirkin 
5776e790746SPaolo Bonzini static int peer_attach(VirtIONet *n, int index)
5786e790746SPaolo Bonzini {
5796e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
5806e790746SPaolo Bonzini 
5816e790746SPaolo Bonzini     if (!nc->peer) {
5826e790746SPaolo Bonzini         return 0;
5836e790746SPaolo Bonzini     }
5846e790746SPaolo Bonzini 
585f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
5867263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 1);
5877263a0adSChangchun Ouyang     }
5887263a0adSChangchun Ouyang 
589f394b2e2SEric Blake     if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
5906e790746SPaolo Bonzini         return 0;
5916e790746SPaolo Bonzini     }
5926e790746SPaolo Bonzini 
5931074b879SJason Wang     if (n->max_queues == 1) {
5941074b879SJason Wang         return 0;
5951074b879SJason Wang     }
5961074b879SJason Wang 
5976e790746SPaolo Bonzini     return tap_enable(nc->peer);
5986e790746SPaolo Bonzini }
5996e790746SPaolo Bonzini 
6006e790746SPaolo Bonzini static int peer_detach(VirtIONet *n, int index)
6016e790746SPaolo Bonzini {
6026e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
6036e790746SPaolo Bonzini 
6046e790746SPaolo Bonzini     if (!nc->peer) {
6056e790746SPaolo Bonzini         return 0;
6066e790746SPaolo Bonzini     }
6076e790746SPaolo Bonzini 
608f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
6097263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 0);
6107263a0adSChangchun Ouyang     }
6117263a0adSChangchun Ouyang 
612f394b2e2SEric Blake     if (nc->peer->info->type !=  NET_CLIENT_DRIVER_TAP) {
6136e790746SPaolo Bonzini         return 0;
6146e790746SPaolo Bonzini     }
6156e790746SPaolo Bonzini 
6166e790746SPaolo Bonzini     return tap_disable(nc->peer);
6176e790746SPaolo Bonzini }
6186e790746SPaolo Bonzini 
6196e790746SPaolo Bonzini static void virtio_net_set_queues(VirtIONet *n)
6206e790746SPaolo Bonzini {
6216e790746SPaolo Bonzini     int i;
622ddfa83eaSJoel Stanley     int r;
6236e790746SPaolo Bonzini 
62468b5f314SYuri Benditovich     if (n->nic->peer_deleted) {
62568b5f314SYuri Benditovich         return;
62668b5f314SYuri Benditovich     }
62768b5f314SYuri Benditovich 
6286e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
6296e790746SPaolo Bonzini         if (i < n->curr_queues) {
630ddfa83eaSJoel Stanley             r = peer_attach(n, i);
631ddfa83eaSJoel Stanley             assert(!r);
6326e790746SPaolo Bonzini         } else {
633ddfa83eaSJoel Stanley             r = peer_detach(n, i);
634ddfa83eaSJoel Stanley             assert(!r);
6356e790746SPaolo Bonzini         }
6366e790746SPaolo Bonzini     }
6376e790746SPaolo Bonzini }
6386e790746SPaolo Bonzini 
639ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue);
6406e790746SPaolo Bonzini 
6419d5b731dSJason Wang static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
6429d5b731dSJason Wang                                         Error **errp)
6436e790746SPaolo Bonzini {
64417a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
6456e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
6466e790746SPaolo Bonzini 
647da3e8a23SShannon Zhao     /* Firstly sync all virtio-net possible supported features */
648da3e8a23SShannon Zhao     features |= n->host_features;
649da3e8a23SShannon Zhao 
6500cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
6516e790746SPaolo Bonzini 
6526e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n)) {
6530cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_CSUM);
6540cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4);
6550cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6);
6560cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN);
6576e790746SPaolo Bonzini 
6580cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM);
6590cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4);
6600cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6);
6610cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN);
6626e790746SPaolo Bonzini     }
6636e790746SPaolo Bonzini 
6646e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) {
6650cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO);
6660cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO);
6676e790746SPaolo Bonzini     }
6686e790746SPaolo Bonzini 
669ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
6706e790746SPaolo Bonzini         return features;
6716e790746SPaolo Bonzini     }
6722974e916SYuri Benditovich 
67375ebec11SMaxime Coquelin     features = vhost_net_get_features(get_vhost_net(nc->peer), features);
67475ebec11SMaxime Coquelin     vdev->backend_features = features;
67575ebec11SMaxime Coquelin 
67675ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
67775ebec11SMaxime Coquelin             (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) {
67875ebec11SMaxime Coquelin         features |= (1ULL << VIRTIO_NET_F_MTU);
67975ebec11SMaxime Coquelin     }
68075ebec11SMaxime Coquelin 
68175ebec11SMaxime Coquelin     return features;
6826e790746SPaolo Bonzini }
6836e790746SPaolo Bonzini 
684019a3edbSGerd Hoffmann static uint64_t virtio_net_bad_features(VirtIODevice *vdev)
6856e790746SPaolo Bonzini {
686019a3edbSGerd Hoffmann     uint64_t features = 0;
6876e790746SPaolo Bonzini 
6886e790746SPaolo Bonzini     /* Linux kernel 2.6.25.  It understood MAC (as everyone must),
6896e790746SPaolo Bonzini      * but also these: */
6900cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
6910cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_CSUM);
6920cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO4);
6930cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO6);
6940cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_ECN);
6956e790746SPaolo Bonzini 
6966e790746SPaolo Bonzini     return features;
6976e790746SPaolo Bonzini }
6986e790746SPaolo Bonzini 
699644c9858SDmitry Fleytman static void virtio_net_apply_guest_offloads(VirtIONet *n)
700644c9858SDmitry Fleytman {
701ad37bb3bSStefan Hajnoczi     qemu_set_offload(qemu_get_queue(n->nic)->peer,
702644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)),
703644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)),
704644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)),
705644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)),
706644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)));
707644c9858SDmitry Fleytman }
708644c9858SDmitry Fleytman 
709644c9858SDmitry Fleytman static uint64_t virtio_net_guest_offloads_by_features(uint32_t features)
710644c9858SDmitry Fleytman {
711644c9858SDmitry Fleytman     static const uint64_t guest_offloads_mask =
712644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_CSUM) |
713644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO4) |
714644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO6) |
715644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_ECN)  |
716644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_UFO);
717644c9858SDmitry Fleytman 
718644c9858SDmitry Fleytman     return guest_offloads_mask & features;
719644c9858SDmitry Fleytman }
720644c9858SDmitry Fleytman 
721644c9858SDmitry Fleytman static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n)
722644c9858SDmitry Fleytman {
723644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
724644c9858SDmitry Fleytman     return virtio_net_guest_offloads_by_features(vdev->guest_features);
725644c9858SDmitry Fleytman }
726644c9858SDmitry Fleytman 
727d5aaa1b0SGerd Hoffmann static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
7286e790746SPaolo Bonzini {
72917a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
7306e790746SPaolo Bonzini     int i;
7316e790746SPaolo Bonzini 
73275ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
73375ebec11SMaxime Coquelin             !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) {
73475ebec11SMaxime Coquelin         features &= ~(1ULL << VIRTIO_NET_F_MTU);
73575ebec11SMaxime Coquelin     }
73675ebec11SMaxime Coquelin 
737ef546f12SCornelia Huck     virtio_net_set_multiqueue(n,
73895129d6fSCornelia Huck                               virtio_has_feature(features, VIRTIO_NET_F_MQ));
7396e790746SPaolo Bonzini 
740ef546f12SCornelia Huck     virtio_net_set_mrg_rx_bufs(n,
74195129d6fSCornelia Huck                                virtio_has_feature(features,
742bb9d17f8SCornelia Huck                                                   VIRTIO_NET_F_MRG_RXBUF),
74395129d6fSCornelia Huck                                virtio_has_feature(features,
744bb9d17f8SCornelia Huck                                                   VIRTIO_F_VERSION_1));
7456e790746SPaolo Bonzini 
7462974e916SYuri Benditovich     n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
7472974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4);
7482974e916SYuri Benditovich     n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
7492974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6);
7502974e916SYuri Benditovich 
7516e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
752644c9858SDmitry Fleytman         n->curr_guest_offloads =
753644c9858SDmitry Fleytman             virtio_net_guest_offloads_by_features(features);
754644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
7556e790746SPaolo Bonzini     }
7566e790746SPaolo Bonzini 
7576e790746SPaolo Bonzini     for (i = 0;  i < n->max_queues; i++) {
7586e790746SPaolo Bonzini         NetClientState *nc = qemu_get_subqueue(n->nic, i);
7596e790746SPaolo Bonzini 
760ed8b4afeSNikolay Nikolaev         if (!get_vhost_net(nc->peer)) {
7616e790746SPaolo Bonzini             continue;
7626e790746SPaolo Bonzini         }
763ed8b4afeSNikolay Nikolaev         vhost_net_ack_features(get_vhost_net(nc->peer), features);
7646e790746SPaolo Bonzini     }
7650b1eaa88SStefan Fritsch 
76695129d6fSCornelia Huck     if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
7670b1eaa88SStefan Fritsch         memset(n->vlans, 0, MAX_VLAN >> 3);
7680b1eaa88SStefan Fritsch     } else {
7690b1eaa88SStefan Fritsch         memset(n->vlans, 0xff, MAX_VLAN >> 3);
7700b1eaa88SStefan Fritsch     }
7716e790746SPaolo Bonzini }
7726e790746SPaolo Bonzini 
7736e790746SPaolo Bonzini static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
7746e790746SPaolo Bonzini                                      struct iovec *iov, unsigned int iov_cnt)
7756e790746SPaolo Bonzini {
7766e790746SPaolo Bonzini     uint8_t on;
7776e790746SPaolo Bonzini     size_t s;
778b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
7796e790746SPaolo Bonzini 
7806e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on));
7816e790746SPaolo Bonzini     if (s != sizeof(on)) {
7826e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
7836e790746SPaolo Bonzini     }
7846e790746SPaolo Bonzini 
7856e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) {
7866e790746SPaolo Bonzini         n->promisc = on;
7876e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) {
7886e790746SPaolo Bonzini         n->allmulti = on;
7896e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) {
7906e790746SPaolo Bonzini         n->alluni = on;
7916e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) {
7926e790746SPaolo Bonzini         n->nomulti = on;
7936e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) {
7946e790746SPaolo Bonzini         n->nouni = on;
7956e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) {
7966e790746SPaolo Bonzini         n->nobcast = on;
7976e790746SPaolo Bonzini     } else {
7986e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
7996e790746SPaolo Bonzini     }
8006e790746SPaolo Bonzini 
801b1be4280SAmos Kong     rxfilter_notify(nc);
802b1be4280SAmos Kong 
8036e790746SPaolo Bonzini     return VIRTIO_NET_OK;
8046e790746SPaolo Bonzini }
8056e790746SPaolo Bonzini 
806644c9858SDmitry Fleytman static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd,
807644c9858SDmitry Fleytman                                      struct iovec *iov, unsigned int iov_cnt)
808644c9858SDmitry Fleytman {
809644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
810644c9858SDmitry Fleytman     uint64_t offloads;
811644c9858SDmitry Fleytman     size_t s;
812644c9858SDmitry Fleytman 
81395129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
814644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
815644c9858SDmitry Fleytman     }
816644c9858SDmitry Fleytman 
817644c9858SDmitry Fleytman     s = iov_to_buf(iov, iov_cnt, 0, &offloads, sizeof(offloads));
818644c9858SDmitry Fleytman     if (s != sizeof(offloads)) {
819644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
820644c9858SDmitry Fleytman     }
821644c9858SDmitry Fleytman 
822644c9858SDmitry Fleytman     if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) {
823644c9858SDmitry Fleytman         uint64_t supported_offloads;
824644c9858SDmitry Fleytman 
825189ae6bbSJason Wang         offloads = virtio_ldq_p(vdev, &offloads);
826189ae6bbSJason Wang 
827644c9858SDmitry Fleytman         if (!n->has_vnet_hdr) {
828644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
829644c9858SDmitry Fleytman         }
830644c9858SDmitry Fleytman 
8312974e916SYuri Benditovich         n->rsc4_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
8322974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO4);
8332974e916SYuri Benditovich         n->rsc6_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
8342974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO6);
8352974e916SYuri Benditovich         virtio_clear_feature(&offloads, VIRTIO_NET_F_RSC_EXT);
8362974e916SYuri Benditovich 
837644c9858SDmitry Fleytman         supported_offloads = virtio_net_supported_guest_offloads(n);
838644c9858SDmitry Fleytman         if (offloads & ~supported_offloads) {
839644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
840644c9858SDmitry Fleytman         }
841644c9858SDmitry Fleytman 
842644c9858SDmitry Fleytman         n->curr_guest_offloads = offloads;
843644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
844644c9858SDmitry Fleytman 
845644c9858SDmitry Fleytman         return VIRTIO_NET_OK;
846644c9858SDmitry Fleytman     } else {
847644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
848644c9858SDmitry Fleytman     }
849644c9858SDmitry Fleytman }
850644c9858SDmitry Fleytman 
8516e790746SPaolo Bonzini static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
8526e790746SPaolo Bonzini                                  struct iovec *iov, unsigned int iov_cnt)
8536e790746SPaolo Bonzini {
8541399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
8556e790746SPaolo Bonzini     struct virtio_net_ctrl_mac mac_data;
8566e790746SPaolo Bonzini     size_t s;
857b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
8586e790746SPaolo Bonzini 
8596e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {
8606e790746SPaolo Bonzini         if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {
8616e790746SPaolo Bonzini             return VIRTIO_NET_ERR;
8626e790746SPaolo Bonzini         }
8636e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
8646e790746SPaolo Bonzini         assert(s == sizeof(n->mac));
8656e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
866b1be4280SAmos Kong         rxfilter_notify(nc);
867b1be4280SAmos Kong 
8686e790746SPaolo Bonzini         return VIRTIO_NET_OK;
8696e790746SPaolo Bonzini     }
8706e790746SPaolo Bonzini 
8716e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) {
8726e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
8736e790746SPaolo Bonzini     }
8746e790746SPaolo Bonzini 
875cae2e556SAmos Kong     int in_use = 0;
876cae2e556SAmos Kong     int first_multi = 0;
877cae2e556SAmos Kong     uint8_t uni_overflow = 0;
878cae2e556SAmos Kong     uint8_t multi_overflow = 0;
879cae2e556SAmos Kong     uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
8806e790746SPaolo Bonzini 
8816e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
8826e790746SPaolo Bonzini                    sizeof(mac_data.entries));
8831399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
8846e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
885b1be4280SAmos Kong         goto error;
8866e790746SPaolo Bonzini     }
8876e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
8886e790746SPaolo Bonzini 
8896e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {
890b1be4280SAmos Kong         goto error;
8916e790746SPaolo Bonzini     }
8926e790746SPaolo Bonzini 
8936e790746SPaolo Bonzini     if (mac_data.entries <= MAC_TABLE_ENTRIES) {
894cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, macs,
8956e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
8966e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
897b1be4280SAmos Kong             goto error;
8986e790746SPaolo Bonzini         }
899cae2e556SAmos Kong         in_use += mac_data.entries;
9006e790746SPaolo Bonzini     } else {
901cae2e556SAmos Kong         uni_overflow = 1;
9026e790746SPaolo Bonzini     }
9036e790746SPaolo Bonzini 
9046e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN);
9056e790746SPaolo Bonzini 
906cae2e556SAmos Kong     first_multi = in_use;
9076e790746SPaolo Bonzini 
9086e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
9096e790746SPaolo Bonzini                    sizeof(mac_data.entries));
9101399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
9116e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
912b1be4280SAmos Kong         goto error;
9136e790746SPaolo Bonzini     }
9146e790746SPaolo Bonzini 
9156e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
9166e790746SPaolo Bonzini 
9176e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {
918b1be4280SAmos Kong         goto error;
9196e790746SPaolo Bonzini     }
9206e790746SPaolo Bonzini 
921edc24385SMichael S. Tsirkin     if (mac_data.entries <= MAC_TABLE_ENTRIES - in_use) {
922cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN],
9236e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
9246e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
925b1be4280SAmos Kong             goto error;
9266e790746SPaolo Bonzini         }
927cae2e556SAmos Kong         in_use += mac_data.entries;
9286e790746SPaolo Bonzini     } else {
929cae2e556SAmos Kong         multi_overflow = 1;
9306e790746SPaolo Bonzini     }
9316e790746SPaolo Bonzini 
932cae2e556SAmos Kong     n->mac_table.in_use = in_use;
933cae2e556SAmos Kong     n->mac_table.first_multi = first_multi;
934cae2e556SAmos Kong     n->mac_table.uni_overflow = uni_overflow;
935cae2e556SAmos Kong     n->mac_table.multi_overflow = multi_overflow;
936cae2e556SAmos Kong     memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN);
937cae2e556SAmos Kong     g_free(macs);
938b1be4280SAmos Kong     rxfilter_notify(nc);
939b1be4280SAmos Kong 
9406e790746SPaolo Bonzini     return VIRTIO_NET_OK;
941b1be4280SAmos Kong 
942b1be4280SAmos Kong error:
943cae2e556SAmos Kong     g_free(macs);
944b1be4280SAmos Kong     return VIRTIO_NET_ERR;
9456e790746SPaolo Bonzini }
9466e790746SPaolo Bonzini 
9476e790746SPaolo Bonzini static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
9486e790746SPaolo Bonzini                                         struct iovec *iov, unsigned int iov_cnt)
9496e790746SPaolo Bonzini {
9501399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
9516e790746SPaolo Bonzini     uint16_t vid;
9526e790746SPaolo Bonzini     size_t s;
953b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
9546e790746SPaolo Bonzini 
9556e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid));
9561399c60dSRusty Russell     vid = virtio_lduw_p(vdev, &vid);
9576e790746SPaolo Bonzini     if (s != sizeof(vid)) {
9586e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9596e790746SPaolo Bonzini     }
9606e790746SPaolo Bonzini 
9616e790746SPaolo Bonzini     if (vid >= MAX_VLAN)
9626e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9636e790746SPaolo Bonzini 
9646e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
9656e790746SPaolo Bonzini         n->vlans[vid >> 5] |= (1U << (vid & 0x1f));
9666e790746SPaolo Bonzini     else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL)
9676e790746SPaolo Bonzini         n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f));
9686e790746SPaolo Bonzini     else
9696e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9706e790746SPaolo Bonzini 
971b1be4280SAmos Kong     rxfilter_notify(nc);
972b1be4280SAmos Kong 
9736e790746SPaolo Bonzini     return VIRTIO_NET_OK;
9746e790746SPaolo Bonzini }
9756e790746SPaolo Bonzini 
976f57fcf70SJason Wang static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
977f57fcf70SJason Wang                                       struct iovec *iov, unsigned int iov_cnt)
978f57fcf70SJason Wang {
979f57fcf70SJason Wang     if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK &&
980f57fcf70SJason Wang         n->status & VIRTIO_NET_S_ANNOUNCE) {
981f57fcf70SJason Wang         n->status &= ~VIRTIO_NET_S_ANNOUNCE;
982f57fcf70SJason Wang         if (n->announce_counter) {
983f57fcf70SJason Wang             timer_mod(n->announce_timer,
984f57fcf70SJason Wang                       qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
985f57fcf70SJason Wang                       self_announce_delay(n->announce_counter));
986f57fcf70SJason Wang         }
987f57fcf70SJason Wang         return VIRTIO_NET_OK;
988f57fcf70SJason Wang     } else {
989f57fcf70SJason Wang         return VIRTIO_NET_ERR;
990f57fcf70SJason Wang     }
991f57fcf70SJason Wang }
992f57fcf70SJason Wang 
9936e790746SPaolo Bonzini static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
9946e790746SPaolo Bonzini                                 struct iovec *iov, unsigned int iov_cnt)
9956e790746SPaolo Bonzini {
99617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
9976e790746SPaolo Bonzini     struct virtio_net_ctrl_mq mq;
9986e790746SPaolo Bonzini     size_t s;
9996e790746SPaolo Bonzini     uint16_t queues;
10006e790746SPaolo Bonzini 
10016e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq));
10026e790746SPaolo Bonzini     if (s != sizeof(mq)) {
10036e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
10046e790746SPaolo Bonzini     }
10056e790746SPaolo Bonzini 
10066e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
10076e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
10086e790746SPaolo Bonzini     }
10096e790746SPaolo Bonzini 
10101399c60dSRusty Russell     queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs);
10116e790746SPaolo Bonzini 
10126e790746SPaolo Bonzini     if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
10136e790746SPaolo Bonzini         queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX ||
10146e790746SPaolo Bonzini         queues > n->max_queues ||
10156e790746SPaolo Bonzini         !n->multiqueue) {
10166e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
10176e790746SPaolo Bonzini     }
10186e790746SPaolo Bonzini 
10196e790746SPaolo Bonzini     n->curr_queues = queues;
10206e790746SPaolo Bonzini     /* stop the backend before changing the number of queues to avoid handling a
10216e790746SPaolo Bonzini      * disabled queue */
102217a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
10236e790746SPaolo Bonzini     virtio_net_set_queues(n);
10246e790746SPaolo Bonzini 
10256e790746SPaolo Bonzini     return VIRTIO_NET_OK;
10266e790746SPaolo Bonzini }
1027ba7eadb5SGreg Kurz 
10286e790746SPaolo Bonzini static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
10296e790746SPaolo Bonzini {
103017a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
10316e790746SPaolo Bonzini     struct virtio_net_ctrl_hdr ctrl;
10326e790746SPaolo Bonzini     virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
103351b19ebeSPaolo Bonzini     VirtQueueElement *elem;
10346e790746SPaolo Bonzini     size_t s;
1035771b6ed3SJason Wang     struct iovec *iov, *iov2;
10366e790746SPaolo Bonzini     unsigned int iov_cnt;
10376e790746SPaolo Bonzini 
103851b19ebeSPaolo Bonzini     for (;;) {
103951b19ebeSPaolo Bonzini         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
104051b19ebeSPaolo Bonzini         if (!elem) {
104151b19ebeSPaolo Bonzini             break;
104251b19ebeSPaolo Bonzini         }
104351b19ebeSPaolo Bonzini         if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) ||
104451b19ebeSPaolo Bonzini             iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) {
1045ba7eadb5SGreg Kurz             virtio_error(vdev, "virtio-net ctrl missing headers");
1046ba7eadb5SGreg Kurz             virtqueue_detach_element(vq, elem, 0);
1047ba7eadb5SGreg Kurz             g_free(elem);
1048ba7eadb5SGreg Kurz             break;
10496e790746SPaolo Bonzini         }
10506e790746SPaolo Bonzini 
105151b19ebeSPaolo Bonzini         iov_cnt = elem->out_num;
105251b19ebeSPaolo Bonzini         iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num);
10536e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl));
10546e790746SPaolo Bonzini         iov_discard_front(&iov, &iov_cnt, sizeof(ctrl));
10556e790746SPaolo Bonzini         if (s != sizeof(ctrl)) {
10566e790746SPaolo Bonzini             status = VIRTIO_NET_ERR;
10576e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_RX) {
10586e790746SPaolo Bonzini             status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt);
10596e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) {
10606e790746SPaolo Bonzini             status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt);
10616e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) {
10626e790746SPaolo Bonzini             status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt);
1063f57fcf70SJason Wang         } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) {
1064f57fcf70SJason Wang             status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt);
10656e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) {
10666e790746SPaolo Bonzini             status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt);
1067644c9858SDmitry Fleytman         } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) {
1068644c9858SDmitry Fleytman             status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt);
10696e790746SPaolo Bonzini         }
10706e790746SPaolo Bonzini 
107151b19ebeSPaolo Bonzini         s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status));
10726e790746SPaolo Bonzini         assert(s == sizeof(status));
10736e790746SPaolo Bonzini 
107451b19ebeSPaolo Bonzini         virtqueue_push(vq, elem, sizeof(status));
10756e790746SPaolo Bonzini         virtio_notify(vdev, vq);
1076771b6ed3SJason Wang         g_free(iov2);
107751b19ebeSPaolo Bonzini         g_free(elem);
10786e790746SPaolo Bonzini     }
10796e790746SPaolo Bonzini }
10806e790746SPaolo Bonzini 
10816e790746SPaolo Bonzini /* RX */
10826e790746SPaolo Bonzini 
10836e790746SPaolo Bonzini static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
10846e790746SPaolo Bonzini {
108517a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
10866e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(vq));
10876e790746SPaolo Bonzini 
10886e790746SPaolo Bonzini     qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index));
10896e790746SPaolo Bonzini }
10906e790746SPaolo Bonzini 
10916e790746SPaolo Bonzini static int virtio_net_can_receive(NetClientState *nc)
10926e790746SPaolo Bonzini {
10936e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
109417a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
10956e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
10966e790746SPaolo Bonzini 
109717a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
10986e790746SPaolo Bonzini         return 0;
10996e790746SPaolo Bonzini     }
11006e790746SPaolo Bonzini 
11016e790746SPaolo Bonzini     if (nc->queue_index >= n->curr_queues) {
11026e790746SPaolo Bonzini         return 0;
11036e790746SPaolo Bonzini     }
11046e790746SPaolo Bonzini 
11056e790746SPaolo Bonzini     if (!virtio_queue_ready(q->rx_vq) ||
110617a0ca55SKONRAD Frederic         !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
11076e790746SPaolo Bonzini         return 0;
11086e790746SPaolo Bonzini     }
11096e790746SPaolo Bonzini 
11106e790746SPaolo Bonzini     return 1;
11116e790746SPaolo Bonzini }
11126e790746SPaolo Bonzini 
11136e790746SPaolo Bonzini static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize)
11146e790746SPaolo Bonzini {
11156e790746SPaolo Bonzini     VirtIONet *n = q->n;
11166e790746SPaolo Bonzini     if (virtio_queue_empty(q->rx_vq) ||
11176e790746SPaolo Bonzini         (n->mergeable_rx_bufs &&
11186e790746SPaolo Bonzini          !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
11196e790746SPaolo Bonzini         virtio_queue_set_notification(q->rx_vq, 1);
11206e790746SPaolo Bonzini 
11216e790746SPaolo Bonzini         /* To avoid a race condition where the guest has made some buffers
11226e790746SPaolo Bonzini          * available after the above check but before notification was
11236e790746SPaolo Bonzini          * enabled, check for available buffers again.
11246e790746SPaolo Bonzini          */
11256e790746SPaolo Bonzini         if (virtio_queue_empty(q->rx_vq) ||
11266e790746SPaolo Bonzini             (n->mergeable_rx_bufs &&
11276e790746SPaolo Bonzini              !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
11286e790746SPaolo Bonzini             return 0;
11296e790746SPaolo Bonzini         }
11306e790746SPaolo Bonzini     }
11316e790746SPaolo Bonzini 
11326e790746SPaolo Bonzini     virtio_queue_set_notification(q->rx_vq, 0);
11336e790746SPaolo Bonzini     return 1;
11346e790746SPaolo Bonzini }
11356e790746SPaolo Bonzini 
11361399c60dSRusty Russell static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr)
1137032a74a1SCédric Le Goater {
11381399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->hdr_len);
11391399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->gso_size);
11401399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_start);
11411399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_offset);
1142032a74a1SCédric Le Goater }
1143032a74a1SCédric Le Goater 
11446e790746SPaolo Bonzini /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so
11456e790746SPaolo Bonzini  * it never finds out that the packets don't have valid checksums.  This
11466e790746SPaolo Bonzini  * causes dhclient to get upset.  Fedora's carried a patch for ages to
11476e790746SPaolo Bonzini  * fix this with Xen but it hasn't appeared in an upstream release of
11486e790746SPaolo Bonzini  * dhclient yet.
11496e790746SPaolo Bonzini  *
11506e790746SPaolo Bonzini  * To avoid breaking existing guests, we catch udp packets and add
11516e790746SPaolo Bonzini  * checksums.  This is terrible but it's better than hacking the guest
11526e790746SPaolo Bonzini  * kernels.
11536e790746SPaolo Bonzini  *
11546e790746SPaolo Bonzini  * N.B. if we introduce a zero-copy API, this operation is no longer free so
11556e790746SPaolo Bonzini  * we should provide a mechanism to disable it to avoid polluting the host
11566e790746SPaolo Bonzini  * cache.
11576e790746SPaolo Bonzini  */
11586e790746SPaolo Bonzini static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
11596e790746SPaolo Bonzini                                         uint8_t *buf, size_t size)
11606e790746SPaolo Bonzini {
11616e790746SPaolo Bonzini     if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
11626e790746SPaolo Bonzini         (size > 27 && size < 1500) && /* normal sized MTU */
11636e790746SPaolo Bonzini         (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
11646e790746SPaolo Bonzini         (buf[23] == 17) && /* ip.protocol == UDP */
11656e790746SPaolo Bonzini         (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
11666e790746SPaolo Bonzini         net_checksum_calculate(buf, size);
11676e790746SPaolo Bonzini         hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
11686e790746SPaolo Bonzini     }
11696e790746SPaolo Bonzini }
11706e790746SPaolo Bonzini 
11716e790746SPaolo Bonzini static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt,
11726e790746SPaolo Bonzini                            const void *buf, size_t size)
11736e790746SPaolo Bonzini {
11746e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
11756e790746SPaolo Bonzini         /* FIXME this cast is evil */
11766e790746SPaolo Bonzini         void *wbuf = (void *)buf;
11776e790746SPaolo Bonzini         work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len,
11786e790746SPaolo Bonzini                                     size - n->host_hdr_len);
11791bfa316cSGreg Kurz 
11801bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
11811399c60dSRusty Russell             virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf);
11821bfa316cSGreg Kurz         }
11836e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr));
11846e790746SPaolo Bonzini     } else {
11856e790746SPaolo Bonzini         struct virtio_net_hdr hdr = {
11866e790746SPaolo Bonzini             .flags = 0,
11876e790746SPaolo Bonzini             .gso_type = VIRTIO_NET_HDR_GSO_NONE
11886e790746SPaolo Bonzini         };
11896e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr);
11906e790746SPaolo Bonzini     }
11916e790746SPaolo Bonzini }
11926e790746SPaolo Bonzini 
11936e790746SPaolo Bonzini static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
11946e790746SPaolo Bonzini {
11956e790746SPaolo Bonzini     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
11966e790746SPaolo Bonzini     static const uint8_t vlan[] = {0x81, 0x00};
11976e790746SPaolo Bonzini     uint8_t *ptr = (uint8_t *)buf;
11986e790746SPaolo Bonzini     int i;
11996e790746SPaolo Bonzini 
12006e790746SPaolo Bonzini     if (n->promisc)
12016e790746SPaolo Bonzini         return 1;
12026e790746SPaolo Bonzini 
12036e790746SPaolo Bonzini     ptr += n->host_hdr_len;
12046e790746SPaolo Bonzini 
12056e790746SPaolo Bonzini     if (!memcmp(&ptr[12], vlan, sizeof(vlan))) {
12067542d3e7SPeter Maydell         int vid = lduw_be_p(ptr + 14) & 0xfff;
12076e790746SPaolo Bonzini         if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f))))
12086e790746SPaolo Bonzini             return 0;
12096e790746SPaolo Bonzini     }
12106e790746SPaolo Bonzini 
12116e790746SPaolo Bonzini     if (ptr[0] & 1) { // multicast
12126e790746SPaolo Bonzini         if (!memcmp(ptr, bcast, sizeof(bcast))) {
12136e790746SPaolo Bonzini             return !n->nobcast;
12146e790746SPaolo Bonzini         } else if (n->nomulti) {
12156e790746SPaolo Bonzini             return 0;
12166e790746SPaolo Bonzini         } else if (n->allmulti || n->mac_table.multi_overflow) {
12176e790746SPaolo Bonzini             return 1;
12186e790746SPaolo Bonzini         }
12196e790746SPaolo Bonzini 
12206e790746SPaolo Bonzini         for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
12216e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
12226e790746SPaolo Bonzini                 return 1;
12236e790746SPaolo Bonzini             }
12246e790746SPaolo Bonzini         }
12256e790746SPaolo Bonzini     } else { // unicast
12266e790746SPaolo Bonzini         if (n->nouni) {
12276e790746SPaolo Bonzini             return 0;
12286e790746SPaolo Bonzini         } else if (n->alluni || n->mac_table.uni_overflow) {
12296e790746SPaolo Bonzini             return 1;
12306e790746SPaolo Bonzini         } else if (!memcmp(ptr, n->mac, ETH_ALEN)) {
12316e790746SPaolo Bonzini             return 1;
12326e790746SPaolo Bonzini         }
12336e790746SPaolo Bonzini 
12346e790746SPaolo Bonzini         for (i = 0; i < n->mac_table.first_multi; i++) {
12356e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
12366e790746SPaolo Bonzini                 return 1;
12376e790746SPaolo Bonzini             }
12386e790746SPaolo Bonzini         }
12396e790746SPaolo Bonzini     }
12406e790746SPaolo Bonzini 
12416e790746SPaolo Bonzini     return 0;
12426e790746SPaolo Bonzini }
12436e790746SPaolo Bonzini 
124497cd965cSPaolo Bonzini static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
124597cd965cSPaolo Bonzini                                       size_t size)
12466e790746SPaolo Bonzini {
12476e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
12486e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
124917a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
12506e790746SPaolo Bonzini     struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
12516e790746SPaolo Bonzini     struct virtio_net_hdr_mrg_rxbuf mhdr;
12526e790746SPaolo Bonzini     unsigned mhdr_cnt = 0;
12536e790746SPaolo Bonzini     size_t offset, i, guest_offset;
12546e790746SPaolo Bonzini 
12556e790746SPaolo Bonzini     if (!virtio_net_can_receive(nc)) {
12566e790746SPaolo Bonzini         return -1;
12576e790746SPaolo Bonzini     }
12586e790746SPaolo Bonzini 
12596e790746SPaolo Bonzini     /* hdr_len refers to the header we supply to the guest */
12606e790746SPaolo Bonzini     if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) {
12616e790746SPaolo Bonzini         return 0;
12626e790746SPaolo Bonzini     }
12636e790746SPaolo Bonzini 
12646e790746SPaolo Bonzini     if (!receive_filter(n, buf, size))
12656e790746SPaolo Bonzini         return size;
12666e790746SPaolo Bonzini 
12676e790746SPaolo Bonzini     offset = i = 0;
12686e790746SPaolo Bonzini 
12696e790746SPaolo Bonzini     while (offset < size) {
127051b19ebeSPaolo Bonzini         VirtQueueElement *elem;
12716e790746SPaolo Bonzini         int len, total;
127251b19ebeSPaolo Bonzini         const struct iovec *sg;
12736e790746SPaolo Bonzini 
12746e790746SPaolo Bonzini         total = 0;
12756e790746SPaolo Bonzini 
127651b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement));
127751b19ebeSPaolo Bonzini         if (!elem) {
1278ba10b9c0SGreg Kurz             if (i) {
1279ba10b9c0SGreg Kurz                 virtio_error(vdev, "virtio-net unexpected empty queue: "
12806e790746SPaolo Bonzini                              "i %zd mergeable %d offset %zd, size %zd, "
1281019a3edbSGerd Hoffmann                              "guest hdr len %zd, host hdr len %zd "
1282019a3edbSGerd Hoffmann                              "guest features 0x%" PRIx64,
12836e790746SPaolo Bonzini                              i, n->mergeable_rx_bufs, offset, size,
1284019a3edbSGerd Hoffmann                              n->guest_hdr_len, n->host_hdr_len,
1285019a3edbSGerd Hoffmann                              vdev->guest_features);
1286ba10b9c0SGreg Kurz             }
1287ba10b9c0SGreg Kurz             return -1;
12886e790746SPaolo Bonzini         }
12896e790746SPaolo Bonzini 
129051b19ebeSPaolo Bonzini         if (elem->in_num < 1) {
1291ba10b9c0SGreg Kurz             virtio_error(vdev,
1292ba10b9c0SGreg Kurz                          "virtio-net receive queue contains no in buffers");
1293ba10b9c0SGreg Kurz             virtqueue_detach_element(q->rx_vq, elem, 0);
1294ba10b9c0SGreg Kurz             g_free(elem);
1295ba10b9c0SGreg Kurz             return -1;
12966e790746SPaolo Bonzini         }
12976e790746SPaolo Bonzini 
129851b19ebeSPaolo Bonzini         sg = elem->in_sg;
12996e790746SPaolo Bonzini         if (i == 0) {
13006e790746SPaolo Bonzini             assert(offset == 0);
13016e790746SPaolo Bonzini             if (n->mergeable_rx_bufs) {
13026e790746SPaolo Bonzini                 mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg),
130351b19ebeSPaolo Bonzini                                     sg, elem->in_num,
13046e790746SPaolo Bonzini                                     offsetof(typeof(mhdr), num_buffers),
13056e790746SPaolo Bonzini                                     sizeof(mhdr.num_buffers));
13066e790746SPaolo Bonzini             }
13076e790746SPaolo Bonzini 
130851b19ebeSPaolo Bonzini             receive_header(n, sg, elem->in_num, buf, size);
13096e790746SPaolo Bonzini             offset = n->host_hdr_len;
13106e790746SPaolo Bonzini             total += n->guest_hdr_len;
13116e790746SPaolo Bonzini             guest_offset = n->guest_hdr_len;
13126e790746SPaolo Bonzini         } else {
13136e790746SPaolo Bonzini             guest_offset = 0;
13146e790746SPaolo Bonzini         }
13156e790746SPaolo Bonzini 
13166e790746SPaolo Bonzini         /* copy in packet.  ugh */
131751b19ebeSPaolo Bonzini         len = iov_from_buf(sg, elem->in_num, guest_offset,
13186e790746SPaolo Bonzini                            buf + offset, size - offset);
13196e790746SPaolo Bonzini         total += len;
13206e790746SPaolo Bonzini         offset += len;
13216e790746SPaolo Bonzini         /* If buffers can't be merged, at this point we
13226e790746SPaolo Bonzini          * must have consumed the complete packet.
13236e790746SPaolo Bonzini          * Otherwise, drop it. */
13246e790746SPaolo Bonzini         if (!n->mergeable_rx_bufs && offset < size) {
132527e57efeSLadi Prosek             virtqueue_unpop(q->rx_vq, elem, total);
132651b19ebeSPaolo Bonzini             g_free(elem);
13276e790746SPaolo Bonzini             return size;
13286e790746SPaolo Bonzini         }
13296e790746SPaolo Bonzini 
13306e790746SPaolo Bonzini         /* signal other side */
133151b19ebeSPaolo Bonzini         virtqueue_fill(q->rx_vq, elem, total, i++);
133251b19ebeSPaolo Bonzini         g_free(elem);
13336e790746SPaolo Bonzini     }
13346e790746SPaolo Bonzini 
13356e790746SPaolo Bonzini     if (mhdr_cnt) {
13361399c60dSRusty Russell         virtio_stw_p(vdev, &mhdr.num_buffers, i);
13376e790746SPaolo Bonzini         iov_from_buf(mhdr_sg, mhdr_cnt,
13386e790746SPaolo Bonzini                      0,
13396e790746SPaolo Bonzini                      &mhdr.num_buffers, sizeof mhdr.num_buffers);
13406e790746SPaolo Bonzini     }
13416e790746SPaolo Bonzini 
13426e790746SPaolo Bonzini     virtqueue_flush(q->rx_vq, i);
134317a0ca55SKONRAD Frederic     virtio_notify(vdev, q->rx_vq);
13446e790746SPaolo Bonzini 
13456e790746SPaolo Bonzini     return size;
13466e790746SPaolo Bonzini }
13476e790746SPaolo Bonzini 
13482974e916SYuri Benditovich static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf,
134997cd965cSPaolo Bonzini                                   size_t size)
135097cd965cSPaolo Bonzini {
135197cd965cSPaolo Bonzini     ssize_t r;
135297cd965cSPaolo Bonzini 
135397cd965cSPaolo Bonzini     rcu_read_lock();
135497cd965cSPaolo Bonzini     r = virtio_net_receive_rcu(nc, buf, size);
135597cd965cSPaolo Bonzini     rcu_read_unlock();
135697cd965cSPaolo Bonzini     return r;
135797cd965cSPaolo Bonzini }
135897cd965cSPaolo Bonzini 
13592974e916SYuri Benditovich static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
13602974e916SYuri Benditovich                                          const uint8_t *buf,
13612974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
13622974e916SYuri Benditovich {
13632974e916SYuri Benditovich     uint16_t ip_hdrlen;
13642974e916SYuri Benditovich     struct ip_header *ip;
13652974e916SYuri Benditovich 
13662974e916SYuri Benditovich     ip = (struct ip_header *)(buf + chain->n->guest_hdr_len
13672974e916SYuri Benditovich                               + sizeof(struct eth_header));
13682974e916SYuri Benditovich     unit->ip = (void *)ip;
13692974e916SYuri Benditovich     ip_hdrlen = (ip->ip_ver_len & 0xF) << 2;
13702974e916SYuri Benditovich     unit->ip_plen = &ip->ip_len;
13712974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen);
13722974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
13732974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen;
13742974e916SYuri Benditovich }
13752974e916SYuri Benditovich 
13762974e916SYuri Benditovich static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
13772974e916SYuri Benditovich                                          const uint8_t *buf,
13782974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
13792974e916SYuri Benditovich {
13802974e916SYuri Benditovich     struct ip6_header *ip6;
13812974e916SYuri Benditovich 
13822974e916SYuri Benditovich     ip6 = (struct ip6_header *)(buf + chain->n->guest_hdr_len
13832974e916SYuri Benditovich                                  + sizeof(struct eth_header));
13842974e916SYuri Benditovich     unit->ip = ip6;
13852974e916SYuri Benditovich     unit->ip_plen = &(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
13862974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip)\
13872974e916SYuri Benditovich                                         + sizeof(struct ip6_header));
13882974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
13892974e916SYuri Benditovich 
13902974e916SYuri Benditovich     /* There is a difference between payload lenght in ipv4 and v6,
13912974e916SYuri Benditovich        ip header is excluded in ipv6 */
13922974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen;
13932974e916SYuri Benditovich }
13942974e916SYuri Benditovich 
13952974e916SYuri Benditovich static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain,
13962974e916SYuri Benditovich                                        VirtioNetRscSeg *seg)
13972974e916SYuri Benditovich {
13982974e916SYuri Benditovich     int ret;
13992974e916SYuri Benditovich     struct virtio_net_hdr *h;
14002974e916SYuri Benditovich 
14012974e916SYuri Benditovich     h = (struct virtio_net_hdr *)seg->buf;
14022974e916SYuri Benditovich     h->flags = 0;
14032974e916SYuri Benditovich     h->gso_type = VIRTIO_NET_HDR_GSO_NONE;
14042974e916SYuri Benditovich 
14052974e916SYuri Benditovich     if (seg->is_coalesced) {
14062974e916SYuri Benditovich         *virtio_net_rsc_ext_num_packets(h) = seg->packets;
14072974e916SYuri Benditovich         *virtio_net_rsc_ext_num_dupacks(h) = seg->dup_ack;
14082974e916SYuri Benditovich         h->flags = VIRTIO_NET_HDR_F_RSC_INFO;
14092974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
14102974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
14112974e916SYuri Benditovich         } else {
14122974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
14132974e916SYuri Benditovich         }
14142974e916SYuri Benditovich     }
14152974e916SYuri Benditovich 
14162974e916SYuri Benditovich     ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size);
14172974e916SYuri Benditovich     QTAILQ_REMOVE(&chain->buffers, seg, next);
14182974e916SYuri Benditovich     g_free(seg->buf);
14192974e916SYuri Benditovich     g_free(seg);
14202974e916SYuri Benditovich 
14212974e916SYuri Benditovich     return ret;
14222974e916SYuri Benditovich }
14232974e916SYuri Benditovich 
14242974e916SYuri Benditovich static void virtio_net_rsc_purge(void *opq)
14252974e916SYuri Benditovich {
14262974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn;
14272974e916SYuri Benditovich     VirtioNetRscChain *chain = (VirtioNetRscChain *)opq;
14282974e916SYuri Benditovich 
14292974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn) {
14302974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
14312974e916SYuri Benditovich             chain->stat.purge_failed++;
14322974e916SYuri Benditovich             continue;
14332974e916SYuri Benditovich         }
14342974e916SYuri Benditovich     }
14352974e916SYuri Benditovich 
14362974e916SYuri Benditovich     chain->stat.timer++;
14372974e916SYuri Benditovich     if (!QTAILQ_EMPTY(&chain->buffers)) {
14382974e916SYuri Benditovich         timer_mod(chain->drain_timer,
14392974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
14402974e916SYuri Benditovich     }
14412974e916SYuri Benditovich }
14422974e916SYuri Benditovich 
14432974e916SYuri Benditovich static void virtio_net_rsc_cleanup(VirtIONet *n)
14442974e916SYuri Benditovich {
14452974e916SYuri Benditovich     VirtioNetRscChain *chain, *rn_chain;
14462974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn_seg;
14472974e916SYuri Benditovich 
14482974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(chain, &n->rsc_chains, next, rn_chain) {
14492974e916SYuri Benditovich         QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn_seg) {
14502974e916SYuri Benditovich             QTAILQ_REMOVE(&chain->buffers, seg, next);
14512974e916SYuri Benditovich             g_free(seg->buf);
14522974e916SYuri Benditovich             g_free(seg);
14532974e916SYuri Benditovich         }
14542974e916SYuri Benditovich 
14552974e916SYuri Benditovich         timer_del(chain->drain_timer);
14562974e916SYuri Benditovich         timer_free(chain->drain_timer);
14572974e916SYuri Benditovich         QTAILQ_REMOVE(&n->rsc_chains, chain, next);
14582974e916SYuri Benditovich         g_free(chain);
14592974e916SYuri Benditovich     }
14602974e916SYuri Benditovich }
14612974e916SYuri Benditovich 
14622974e916SYuri Benditovich static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain,
14632974e916SYuri Benditovich                                      NetClientState *nc,
14642974e916SYuri Benditovich                                      const uint8_t *buf, size_t size)
14652974e916SYuri Benditovich {
14662974e916SYuri Benditovich     uint16_t hdr_len;
14672974e916SYuri Benditovich     VirtioNetRscSeg *seg;
14682974e916SYuri Benditovich 
14692974e916SYuri Benditovich     hdr_len = chain->n->guest_hdr_len;
14702974e916SYuri Benditovich     seg = g_malloc(sizeof(VirtioNetRscSeg));
14712974e916SYuri Benditovich     seg->buf = g_malloc(hdr_len + sizeof(struct eth_header)
14722974e916SYuri Benditovich         + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD);
14732974e916SYuri Benditovich     memcpy(seg->buf, buf, size);
14742974e916SYuri Benditovich     seg->size = size;
14752974e916SYuri Benditovich     seg->packets = 1;
14762974e916SYuri Benditovich     seg->dup_ack = 0;
14772974e916SYuri Benditovich     seg->is_coalesced = 0;
14782974e916SYuri Benditovich     seg->nc = nc;
14792974e916SYuri Benditovich 
14802974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&chain->buffers, seg, next);
14812974e916SYuri Benditovich     chain->stat.cache++;
14822974e916SYuri Benditovich 
14832974e916SYuri Benditovich     switch (chain->proto) {
14842974e916SYuri Benditovich     case ETH_P_IP:
14852974e916SYuri Benditovich         virtio_net_rsc_extract_unit4(chain, seg->buf, &seg->unit);
14862974e916SYuri Benditovich         break;
14872974e916SYuri Benditovich     case ETH_P_IPV6:
14882974e916SYuri Benditovich         virtio_net_rsc_extract_unit6(chain, seg->buf, &seg->unit);
14892974e916SYuri Benditovich         break;
14902974e916SYuri Benditovich     default:
14912974e916SYuri Benditovich         g_assert_not_reached();
14922974e916SYuri Benditovich     }
14932974e916SYuri Benditovich }
14942974e916SYuri Benditovich 
14952974e916SYuri Benditovich static int32_t virtio_net_rsc_handle_ack(VirtioNetRscChain *chain,
14962974e916SYuri Benditovich                                          VirtioNetRscSeg *seg,
14972974e916SYuri Benditovich                                          const uint8_t *buf,
14982974e916SYuri Benditovich                                          struct tcp_header *n_tcp,
14992974e916SYuri Benditovich                                          struct tcp_header *o_tcp)
15002974e916SYuri Benditovich {
15012974e916SYuri Benditovich     uint32_t nack, oack;
15022974e916SYuri Benditovich     uint16_t nwin, owin;
15032974e916SYuri Benditovich 
15042974e916SYuri Benditovich     nack = htonl(n_tcp->th_ack);
15052974e916SYuri Benditovich     nwin = htons(n_tcp->th_win);
15062974e916SYuri Benditovich     oack = htonl(o_tcp->th_ack);
15072974e916SYuri Benditovich     owin = htons(o_tcp->th_win);
15082974e916SYuri Benditovich 
15092974e916SYuri Benditovich     if ((nack - oack) >= VIRTIO_NET_MAX_TCP_PAYLOAD) {
15102974e916SYuri Benditovich         chain->stat.ack_out_of_win++;
15112974e916SYuri Benditovich         return RSC_FINAL;
15122974e916SYuri Benditovich     } else if (nack == oack) {
15132974e916SYuri Benditovich         /* duplicated ack or window probe */
15142974e916SYuri Benditovich         if (nwin == owin) {
15152974e916SYuri Benditovich             /* duplicated ack, add dup ack count due to whql test up to 1 */
15162974e916SYuri Benditovich             chain->stat.dup_ack++;
15172974e916SYuri Benditovich             return RSC_FINAL;
15182974e916SYuri Benditovich         } else {
15192974e916SYuri Benditovich             /* Coalesce window update */
15202974e916SYuri Benditovich             o_tcp->th_win = n_tcp->th_win;
15212974e916SYuri Benditovich             chain->stat.win_update++;
15222974e916SYuri Benditovich             return RSC_COALESCE;
15232974e916SYuri Benditovich         }
15242974e916SYuri Benditovich     } else {
15252974e916SYuri Benditovich         /* pure ack, go to 'C', finalize*/
15262974e916SYuri Benditovich         chain->stat.pure_ack++;
15272974e916SYuri Benditovich         return RSC_FINAL;
15282974e916SYuri Benditovich     }
15292974e916SYuri Benditovich }
15302974e916SYuri Benditovich 
15312974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain,
15322974e916SYuri Benditovich                                             VirtioNetRscSeg *seg,
15332974e916SYuri Benditovich                                             const uint8_t *buf,
15342974e916SYuri Benditovich                                             VirtioNetRscUnit *n_unit)
15352974e916SYuri Benditovich {
15362974e916SYuri Benditovich     void *data;
15372974e916SYuri Benditovich     uint16_t o_ip_len;
15382974e916SYuri Benditovich     uint32_t nseq, oseq;
15392974e916SYuri Benditovich     VirtioNetRscUnit *o_unit;
15402974e916SYuri Benditovich 
15412974e916SYuri Benditovich     o_unit = &seg->unit;
15422974e916SYuri Benditovich     o_ip_len = htons(*o_unit->ip_plen);
15432974e916SYuri Benditovich     nseq = htonl(n_unit->tcp->th_seq);
15442974e916SYuri Benditovich     oseq = htonl(o_unit->tcp->th_seq);
15452974e916SYuri Benditovich 
15462974e916SYuri Benditovich     /* out of order or retransmitted. */
15472974e916SYuri Benditovich     if ((nseq - oseq) > VIRTIO_NET_MAX_TCP_PAYLOAD) {
15482974e916SYuri Benditovich         chain->stat.data_out_of_win++;
15492974e916SYuri Benditovich         return RSC_FINAL;
15502974e916SYuri Benditovich     }
15512974e916SYuri Benditovich 
15522974e916SYuri Benditovich     data = ((uint8_t *)n_unit->tcp) + n_unit->tcp_hdrlen;
15532974e916SYuri Benditovich     if (nseq == oseq) {
15542974e916SYuri Benditovich         if ((o_unit->payload == 0) && n_unit->payload) {
15552974e916SYuri Benditovich             /* From no payload to payload, normal case, not a dup ack or etc */
15562974e916SYuri Benditovich             chain->stat.data_after_pure_ack++;
15572974e916SYuri Benditovich             goto coalesce;
15582974e916SYuri Benditovich         } else {
15592974e916SYuri Benditovich             return virtio_net_rsc_handle_ack(chain, seg, buf,
15602974e916SYuri Benditovich                                              n_unit->tcp, o_unit->tcp);
15612974e916SYuri Benditovich         }
15622974e916SYuri Benditovich     } else if ((nseq - oseq) != o_unit->payload) {
15632974e916SYuri Benditovich         /* Not a consistent packet, out of order */
15642974e916SYuri Benditovich         chain->stat.data_out_of_order++;
15652974e916SYuri Benditovich         return RSC_FINAL;
15662974e916SYuri Benditovich     } else {
15672974e916SYuri Benditovich coalesce:
15682974e916SYuri Benditovich         if ((o_ip_len + n_unit->payload) > chain->max_payload) {
15692974e916SYuri Benditovich             chain->stat.over_size++;
15702974e916SYuri Benditovich             return RSC_FINAL;
15712974e916SYuri Benditovich         }
15722974e916SYuri Benditovich 
15732974e916SYuri Benditovich         /* Here comes the right data, the payload length in v4/v6 is different,
15742974e916SYuri Benditovich            so use the field value to update and record the new data len */
15752974e916SYuri Benditovich         o_unit->payload += n_unit->payload; /* update new data len */
15762974e916SYuri Benditovich 
15772974e916SYuri Benditovich         /* update field in ip header */
15782974e916SYuri Benditovich         *o_unit->ip_plen = htons(o_ip_len + n_unit->payload);
15792974e916SYuri Benditovich 
15802974e916SYuri Benditovich         /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced
15812974e916SYuri Benditovich            for windows guest, while this may change the behavior for linux
15822974e916SYuri Benditovich            guest (only if it uses RSC feature). */
15832974e916SYuri Benditovich         o_unit->tcp->th_offset_flags = n_unit->tcp->th_offset_flags;
15842974e916SYuri Benditovich 
15852974e916SYuri Benditovich         o_unit->tcp->th_ack = n_unit->tcp->th_ack;
15862974e916SYuri Benditovich         o_unit->tcp->th_win = n_unit->tcp->th_win;
15872974e916SYuri Benditovich 
15882974e916SYuri Benditovich         memmove(seg->buf + seg->size, data, n_unit->payload);
15892974e916SYuri Benditovich         seg->size += n_unit->payload;
15902974e916SYuri Benditovich         seg->packets++;
15912974e916SYuri Benditovich         chain->stat.coalesced++;
15922974e916SYuri Benditovich         return RSC_COALESCE;
15932974e916SYuri Benditovich     }
15942974e916SYuri Benditovich }
15952974e916SYuri Benditovich 
15962974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce4(VirtioNetRscChain *chain,
15972974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
15982974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
15992974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
16002974e916SYuri Benditovich {
16012974e916SYuri Benditovich     struct ip_header *ip1, *ip2;
16022974e916SYuri Benditovich 
16032974e916SYuri Benditovich     ip1 = (struct ip_header *)(unit->ip);
16042974e916SYuri Benditovich     ip2 = (struct ip_header *)(seg->unit.ip);
16052974e916SYuri Benditovich     if ((ip1->ip_src ^ ip2->ip_src) || (ip1->ip_dst ^ ip2->ip_dst)
16062974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
16072974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
16082974e916SYuri Benditovich         chain->stat.no_match++;
16092974e916SYuri Benditovich         return RSC_NO_MATCH;
16102974e916SYuri Benditovich     }
16112974e916SYuri Benditovich 
16122974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
16132974e916SYuri Benditovich }
16142974e916SYuri Benditovich 
16152974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce6(VirtioNetRscChain *chain,
16162974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
16172974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
16182974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
16192974e916SYuri Benditovich {
16202974e916SYuri Benditovich     struct ip6_header *ip1, *ip2;
16212974e916SYuri Benditovich 
16222974e916SYuri Benditovich     ip1 = (struct ip6_header *)(unit->ip);
16232974e916SYuri Benditovich     ip2 = (struct ip6_header *)(seg->unit.ip);
16242974e916SYuri Benditovich     if (memcmp(&ip1->ip6_src, &ip2->ip6_src, sizeof(struct in6_address))
16252974e916SYuri Benditovich         || memcmp(&ip1->ip6_dst, &ip2->ip6_dst, sizeof(struct in6_address))
16262974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
16272974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
16282974e916SYuri Benditovich             chain->stat.no_match++;
16292974e916SYuri Benditovich             return RSC_NO_MATCH;
16302974e916SYuri Benditovich     }
16312974e916SYuri Benditovich 
16322974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
16332974e916SYuri Benditovich }
16342974e916SYuri Benditovich 
16352974e916SYuri Benditovich /* Packets with 'SYN' should bypass, other flag should be sent after drain
16362974e916SYuri Benditovich  * to prevent out of order */
16372974e916SYuri Benditovich static int virtio_net_rsc_tcp_ctrl_check(VirtioNetRscChain *chain,
16382974e916SYuri Benditovich                                          struct tcp_header *tcp)
16392974e916SYuri Benditovich {
16402974e916SYuri Benditovich     uint16_t tcp_hdr;
16412974e916SYuri Benditovich     uint16_t tcp_flag;
16422974e916SYuri Benditovich 
16432974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags);
16442974e916SYuri Benditovich     tcp_hdr = (tcp_flag & VIRTIO_NET_TCP_HDR_LENGTH) >> 10;
16452974e916SYuri Benditovich     tcp_flag &= VIRTIO_NET_TCP_FLAG;
16462974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags) & 0x3F;
16472974e916SYuri Benditovich     if (tcp_flag & TH_SYN) {
16482974e916SYuri Benditovich         chain->stat.tcp_syn++;
16492974e916SYuri Benditovich         return RSC_BYPASS;
16502974e916SYuri Benditovich     }
16512974e916SYuri Benditovich 
16522974e916SYuri Benditovich     if (tcp_flag & (TH_FIN | TH_URG | TH_RST | TH_ECE | TH_CWR)) {
16532974e916SYuri Benditovich         chain->stat.tcp_ctrl_drain++;
16542974e916SYuri Benditovich         return RSC_FINAL;
16552974e916SYuri Benditovich     }
16562974e916SYuri Benditovich 
16572974e916SYuri Benditovich     if (tcp_hdr > sizeof(struct tcp_header)) {
16582974e916SYuri Benditovich         chain->stat.tcp_all_opt++;
16592974e916SYuri Benditovich         return RSC_FINAL;
16602974e916SYuri Benditovich     }
16612974e916SYuri Benditovich 
16622974e916SYuri Benditovich     return RSC_CANDIDATE;
16632974e916SYuri Benditovich }
16642974e916SYuri Benditovich 
16652974e916SYuri Benditovich static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain,
16662974e916SYuri Benditovich                                          NetClientState *nc,
16672974e916SYuri Benditovich                                          const uint8_t *buf, size_t size,
16682974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
16692974e916SYuri Benditovich {
16702974e916SYuri Benditovich     int ret;
16712974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
16722974e916SYuri Benditovich 
16732974e916SYuri Benditovich     if (QTAILQ_EMPTY(&chain->buffers)) {
16742974e916SYuri Benditovich         chain->stat.empty_cache++;
16752974e916SYuri Benditovich         virtio_net_rsc_cache_buf(chain, nc, buf, size);
16762974e916SYuri Benditovich         timer_mod(chain->drain_timer,
16772974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
16782974e916SYuri Benditovich         return size;
16792974e916SYuri Benditovich     }
16802974e916SYuri Benditovich 
16812974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
16822974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
16832974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce4(chain, seg, buf, size, unit);
16842974e916SYuri Benditovich         } else {
16852974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce6(chain, seg, buf, size, unit);
16862974e916SYuri Benditovich         }
16872974e916SYuri Benditovich 
16882974e916SYuri Benditovich         if (ret == RSC_FINAL) {
16892974e916SYuri Benditovich             if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
16902974e916SYuri Benditovich                 /* Send failed */
16912974e916SYuri Benditovich                 chain->stat.final_failed++;
16922974e916SYuri Benditovich                 return 0;
16932974e916SYuri Benditovich             }
16942974e916SYuri Benditovich 
16952974e916SYuri Benditovich             /* Send current packet */
16962974e916SYuri Benditovich             return virtio_net_do_receive(nc, buf, size);
16972974e916SYuri Benditovich         } else if (ret == RSC_NO_MATCH) {
16982974e916SYuri Benditovich             continue;
16992974e916SYuri Benditovich         } else {
17002974e916SYuri Benditovich             /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */
17012974e916SYuri Benditovich             seg->is_coalesced = 1;
17022974e916SYuri Benditovich             return size;
17032974e916SYuri Benditovich         }
17042974e916SYuri Benditovich     }
17052974e916SYuri Benditovich 
17062974e916SYuri Benditovich     chain->stat.no_match_cache++;
17072974e916SYuri Benditovich     virtio_net_rsc_cache_buf(chain, nc, buf, size);
17082974e916SYuri Benditovich     return size;
17092974e916SYuri Benditovich }
17102974e916SYuri Benditovich 
17112974e916SYuri Benditovich /* Drain a connection data, this is to avoid out of order segments */
17122974e916SYuri Benditovich static size_t virtio_net_rsc_drain_flow(VirtioNetRscChain *chain,
17132974e916SYuri Benditovich                                         NetClientState *nc,
17142974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
17152974e916SYuri Benditovich                                         uint16_t ip_start, uint16_t ip_size,
17162974e916SYuri Benditovich                                         uint16_t tcp_port)
17172974e916SYuri Benditovich {
17182974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
17192974e916SYuri Benditovich     uint32_t ppair1, ppair2;
17202974e916SYuri Benditovich 
17212974e916SYuri Benditovich     ppair1 = *(uint32_t *)(buf + tcp_port);
17222974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
17232974e916SYuri Benditovich         ppair2 = *(uint32_t *)(seg->buf + tcp_port);
17242974e916SYuri Benditovich         if (memcmp(buf + ip_start, seg->buf + ip_start, ip_size)
17252974e916SYuri Benditovich             || (ppair1 != ppair2)) {
17262974e916SYuri Benditovich             continue;
17272974e916SYuri Benditovich         }
17282974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
17292974e916SYuri Benditovich             chain->stat.drain_failed++;
17302974e916SYuri Benditovich         }
17312974e916SYuri Benditovich 
17322974e916SYuri Benditovich         break;
17332974e916SYuri Benditovich     }
17342974e916SYuri Benditovich 
17352974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
17362974e916SYuri Benditovich }
17372974e916SYuri Benditovich 
17382974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check4(VirtioNetRscChain *chain,
17392974e916SYuri Benditovich                                             struct ip_header *ip,
17402974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
17412974e916SYuri Benditovich {
17422974e916SYuri Benditovich     uint16_t ip_len;
17432974e916SYuri Benditovich 
17442974e916SYuri Benditovich     /* Not an ipv4 packet */
17452974e916SYuri Benditovich     if (((ip->ip_ver_len & 0xF0) >> 4) != IP_HEADER_VERSION_4) {
17462974e916SYuri Benditovich         chain->stat.ip_option++;
17472974e916SYuri Benditovich         return RSC_BYPASS;
17482974e916SYuri Benditovich     }
17492974e916SYuri Benditovich 
17502974e916SYuri Benditovich     /* Don't handle packets with ip option */
17512974e916SYuri Benditovich     if ((ip->ip_ver_len & 0xF) != VIRTIO_NET_IP4_HEADER_LENGTH) {
17522974e916SYuri Benditovich         chain->stat.ip_option++;
17532974e916SYuri Benditovich         return RSC_BYPASS;
17542974e916SYuri Benditovich     }
17552974e916SYuri Benditovich 
17562974e916SYuri Benditovich     if (ip->ip_p != IPPROTO_TCP) {
17572974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
17582974e916SYuri Benditovich         return RSC_BYPASS;
17592974e916SYuri Benditovich     }
17602974e916SYuri Benditovich 
17612974e916SYuri Benditovich     /* Don't handle packets with ip fragment */
17622974e916SYuri Benditovich     if (!(htons(ip->ip_off) & IP_DF)) {
17632974e916SYuri Benditovich         chain->stat.ip_frag++;
17642974e916SYuri Benditovich         return RSC_BYPASS;
17652974e916SYuri Benditovich     }
17662974e916SYuri Benditovich 
17672974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
17682974e916SYuri Benditovich     if (IPTOS_ECN(ip->ip_tos)) {
17692974e916SYuri Benditovich         chain->stat.ip_ecn++;
17702974e916SYuri Benditovich         return RSC_BYPASS;
17712974e916SYuri Benditovich     }
17722974e916SYuri Benditovich 
17732974e916SYuri Benditovich     ip_len = htons(ip->ip_len);
17742974e916SYuri Benditovich     if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header))
17752974e916SYuri Benditovich         || ip_len > (size - chain->n->guest_hdr_len -
17762974e916SYuri Benditovich                      sizeof(struct eth_header))) {
17772974e916SYuri Benditovich         chain->stat.ip_hacked++;
17782974e916SYuri Benditovich         return RSC_BYPASS;
17792974e916SYuri Benditovich     }
17802974e916SYuri Benditovich 
17812974e916SYuri Benditovich     return RSC_CANDIDATE;
17822974e916SYuri Benditovich }
17832974e916SYuri Benditovich 
17842974e916SYuri Benditovich static size_t virtio_net_rsc_receive4(VirtioNetRscChain *chain,
17852974e916SYuri Benditovich                                       NetClientState *nc,
17862974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
17872974e916SYuri Benditovich {
17882974e916SYuri Benditovich     int32_t ret;
17892974e916SYuri Benditovich     uint16_t hdr_len;
17902974e916SYuri Benditovich     VirtioNetRscUnit unit;
17912974e916SYuri Benditovich 
17922974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
17932974e916SYuri Benditovich 
17942974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header)
17952974e916SYuri Benditovich         + sizeof(struct tcp_header))) {
17962974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
17972974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
17982974e916SYuri Benditovich     }
17992974e916SYuri Benditovich 
18002974e916SYuri Benditovich     virtio_net_rsc_extract_unit4(chain, buf, &unit);
18012974e916SYuri Benditovich     if (virtio_net_rsc_sanity_check4(chain, unit.ip, buf, size)
18022974e916SYuri Benditovich         != RSC_CANDIDATE) {
18032974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18042974e916SYuri Benditovich     }
18052974e916SYuri Benditovich 
18062974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
18072974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
18082974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18092974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
18102974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
18112974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 12),
18122974e916SYuri Benditovich                 VIRTIO_NET_IP4_ADDR_SIZE,
18132974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header));
18142974e916SYuri Benditovich     }
18152974e916SYuri Benditovich 
18162974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
18172974e916SYuri Benditovich }
18182974e916SYuri Benditovich 
18192974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check6(VirtioNetRscChain *chain,
18202974e916SYuri Benditovich                                             struct ip6_header *ip6,
18212974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
18222974e916SYuri Benditovich {
18232974e916SYuri Benditovich     uint16_t ip_len;
18242974e916SYuri Benditovich 
18252974e916SYuri Benditovich     if (((ip6->ip6_ctlun.ip6_un1.ip6_un1_flow & 0xF0) >> 4)
18262974e916SYuri Benditovich         != IP_HEADER_VERSION_6) {
18272974e916SYuri Benditovich         return RSC_BYPASS;
18282974e916SYuri Benditovich     }
18292974e916SYuri Benditovich 
18302974e916SYuri Benditovich     /* Both option and protocol is checked in this */
18312974e916SYuri Benditovich     if (ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) {
18322974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
18332974e916SYuri Benditovich         return RSC_BYPASS;
18342974e916SYuri Benditovich     }
18352974e916SYuri Benditovich 
18362974e916SYuri Benditovich     ip_len = htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
18372974e916SYuri Benditovich     if (ip_len < sizeof(struct tcp_header) ||
18382974e916SYuri Benditovich         ip_len > (size - chain->n->guest_hdr_len - sizeof(struct eth_header)
18392974e916SYuri Benditovich                   - sizeof(struct ip6_header))) {
18402974e916SYuri Benditovich         chain->stat.ip_hacked++;
18412974e916SYuri Benditovich         return RSC_BYPASS;
18422974e916SYuri Benditovich     }
18432974e916SYuri Benditovich 
18442974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
18452974e916SYuri Benditovich     if (IP6_ECN(ip6->ip6_ctlun.ip6_un3.ip6_un3_ecn)) {
18462974e916SYuri Benditovich         chain->stat.ip_ecn++;
18472974e916SYuri Benditovich         return RSC_BYPASS;
18482974e916SYuri Benditovich     }
18492974e916SYuri Benditovich 
18502974e916SYuri Benditovich     return RSC_CANDIDATE;
18512974e916SYuri Benditovich }
18522974e916SYuri Benditovich 
18532974e916SYuri Benditovich static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc,
18542974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
18552974e916SYuri Benditovich {
18562974e916SYuri Benditovich     int32_t ret;
18572974e916SYuri Benditovich     uint16_t hdr_len;
18582974e916SYuri Benditovich     VirtioNetRscChain *chain;
18592974e916SYuri Benditovich     VirtioNetRscUnit unit;
18602974e916SYuri Benditovich 
18612974e916SYuri Benditovich     chain = (VirtioNetRscChain *)opq;
18622974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
18632974e916SYuri Benditovich 
18642974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header)
18652974e916SYuri Benditovich         + sizeof(tcp_header))) {
18662974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18672974e916SYuri Benditovich     }
18682974e916SYuri Benditovich 
18692974e916SYuri Benditovich     virtio_net_rsc_extract_unit6(chain, buf, &unit);
18702974e916SYuri Benditovich     if (RSC_CANDIDATE != virtio_net_rsc_sanity_check6(chain,
18712974e916SYuri Benditovich                                                  unit.ip, buf, size)) {
18722974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18732974e916SYuri Benditovich     }
18742974e916SYuri Benditovich 
18752974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
18762974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
18772974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18782974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
18792974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
18802974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 8),
18812974e916SYuri Benditovich                 VIRTIO_NET_IP6_ADDR_SIZE,
18822974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header)
18832974e916SYuri Benditovich                 + sizeof(struct ip6_header));
18842974e916SYuri Benditovich     }
18852974e916SYuri Benditovich 
18862974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
18872974e916SYuri Benditovich }
18882974e916SYuri Benditovich 
18892974e916SYuri Benditovich static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n,
18902974e916SYuri Benditovich                                                       NetClientState *nc,
18912974e916SYuri Benditovich                                                       uint16_t proto)
18922974e916SYuri Benditovich {
18932974e916SYuri Benditovich     VirtioNetRscChain *chain;
18942974e916SYuri Benditovich 
18952974e916SYuri Benditovich     if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) {
18962974e916SYuri Benditovich         return NULL;
18972974e916SYuri Benditovich     }
18982974e916SYuri Benditovich 
18992974e916SYuri Benditovich     QTAILQ_FOREACH(chain, &n->rsc_chains, next) {
19002974e916SYuri Benditovich         if (chain->proto == proto) {
19012974e916SYuri Benditovich             return chain;
19022974e916SYuri Benditovich         }
19032974e916SYuri Benditovich     }
19042974e916SYuri Benditovich 
19052974e916SYuri Benditovich     chain = g_malloc(sizeof(*chain));
19062974e916SYuri Benditovich     chain->n = n;
19072974e916SYuri Benditovich     chain->proto = proto;
19082974e916SYuri Benditovich     if (proto == (uint16_t)ETH_P_IP) {
19092974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP4_PAYLOAD;
19102974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
19112974e916SYuri Benditovich     } else {
19122974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD;
19132974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
19142974e916SYuri Benditovich     }
19152974e916SYuri Benditovich     chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST,
19162974e916SYuri Benditovich                                       virtio_net_rsc_purge, chain);
19172974e916SYuri Benditovich     memset(&chain->stat, 0, sizeof(chain->stat));
19182974e916SYuri Benditovich 
19192974e916SYuri Benditovich     QTAILQ_INIT(&chain->buffers);
19202974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next);
19212974e916SYuri Benditovich 
19222974e916SYuri Benditovich     return chain;
19232974e916SYuri Benditovich }
19242974e916SYuri Benditovich 
19252974e916SYuri Benditovich static ssize_t virtio_net_rsc_receive(NetClientState *nc,
19262974e916SYuri Benditovich                                       const uint8_t *buf,
19272974e916SYuri Benditovich                                       size_t size)
19282974e916SYuri Benditovich {
19292974e916SYuri Benditovich     uint16_t proto;
19302974e916SYuri Benditovich     VirtioNetRscChain *chain;
19312974e916SYuri Benditovich     struct eth_header *eth;
19322974e916SYuri Benditovich     VirtIONet *n;
19332974e916SYuri Benditovich 
19342974e916SYuri Benditovich     n = qemu_get_nic_opaque(nc);
19352974e916SYuri Benditovich     if (size < (n->host_hdr_len + sizeof(struct eth_header))) {
19362974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19372974e916SYuri Benditovich     }
19382974e916SYuri Benditovich 
19392974e916SYuri Benditovich     eth = (struct eth_header *)(buf + n->guest_hdr_len);
19402974e916SYuri Benditovich     proto = htons(eth->h_proto);
19412974e916SYuri Benditovich 
19422974e916SYuri Benditovich     chain = virtio_net_rsc_lookup_chain(n, nc, proto);
19432974e916SYuri Benditovich     if (chain) {
19442974e916SYuri Benditovich         chain->stat.received++;
19452974e916SYuri Benditovich         if (proto == (uint16_t)ETH_P_IP && n->rsc4_enabled) {
19462974e916SYuri Benditovich             return virtio_net_rsc_receive4(chain, nc, buf, size);
19472974e916SYuri Benditovich         } else if (proto == (uint16_t)ETH_P_IPV6 && n->rsc6_enabled) {
19482974e916SYuri Benditovich             return virtio_net_rsc_receive6(chain, nc, buf, size);
19492974e916SYuri Benditovich         }
19502974e916SYuri Benditovich     }
19512974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
19522974e916SYuri Benditovich }
19532974e916SYuri Benditovich 
19542974e916SYuri Benditovich static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf,
19552974e916SYuri Benditovich                                   size_t size)
19562974e916SYuri Benditovich {
19572974e916SYuri Benditovich     VirtIONet *n = qemu_get_nic_opaque(nc);
19582974e916SYuri Benditovich     if ((n->rsc4_enabled || n->rsc6_enabled)) {
19592974e916SYuri Benditovich         return virtio_net_rsc_receive(nc, buf, size);
19602974e916SYuri Benditovich     } else {
19612974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19622974e916SYuri Benditovich     }
19632974e916SYuri Benditovich }
19642974e916SYuri Benditovich 
19656e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q);
19666e790746SPaolo Bonzini 
19676e790746SPaolo Bonzini static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
19686e790746SPaolo Bonzini {
19696e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
19706e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
197117a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
19726e790746SPaolo Bonzini 
197351b19ebeSPaolo Bonzini     virtqueue_push(q->tx_vq, q->async_tx.elem, 0);
197417a0ca55SKONRAD Frederic     virtio_notify(vdev, q->tx_vq);
19756e790746SPaolo Bonzini 
197651b19ebeSPaolo Bonzini     g_free(q->async_tx.elem);
197751b19ebeSPaolo Bonzini     q->async_tx.elem = NULL;
19786e790746SPaolo Bonzini 
19796e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
19806e790746SPaolo Bonzini     virtio_net_flush_tx(q);
19816e790746SPaolo Bonzini }
19826e790746SPaolo Bonzini 
19836e790746SPaolo Bonzini /* TX */
19846e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
19856e790746SPaolo Bonzini {
19866e790746SPaolo Bonzini     VirtIONet *n = q->n;
198717a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
198851b19ebeSPaolo Bonzini     VirtQueueElement *elem;
19896e790746SPaolo Bonzini     int32_t num_packets = 0;
19906e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(q->tx_vq));
199117a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
19926e790746SPaolo Bonzini         return num_packets;
19936e790746SPaolo Bonzini     }
19946e790746SPaolo Bonzini 
199551b19ebeSPaolo Bonzini     if (q->async_tx.elem) {
19966e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
19976e790746SPaolo Bonzini         return num_packets;
19986e790746SPaolo Bonzini     }
19996e790746SPaolo Bonzini 
200051b19ebeSPaolo Bonzini     for (;;) {
2001bd89dd98SJason Wang         ssize_t ret;
200251b19ebeSPaolo Bonzini         unsigned int out_num;
200351b19ebeSPaolo Bonzini         struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
2004feb93f36SJason Wang         struct virtio_net_hdr_mrg_rxbuf mhdr;
20056e790746SPaolo Bonzini 
200651b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
200751b19ebeSPaolo Bonzini         if (!elem) {
200851b19ebeSPaolo Bonzini             break;
200951b19ebeSPaolo Bonzini         }
201051b19ebeSPaolo Bonzini 
201151b19ebeSPaolo Bonzini         out_num = elem->out_num;
201251b19ebeSPaolo Bonzini         out_sg = elem->out_sg;
20136e790746SPaolo Bonzini         if (out_num < 1) {
2014fa5e56c2SGreg Kurz             virtio_error(vdev, "virtio-net header not in first element");
2015fa5e56c2SGreg Kurz             virtqueue_detach_element(q->tx_vq, elem, 0);
2016fa5e56c2SGreg Kurz             g_free(elem);
2017fa5e56c2SGreg Kurz             return -EINVAL;
20186e790746SPaolo Bonzini         }
20196e790746SPaolo Bonzini 
2020032a74a1SCédric Le Goater         if (n->has_vnet_hdr) {
2021feb93f36SJason Wang             if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
2022feb93f36SJason Wang                 n->guest_hdr_len) {
2023fa5e56c2SGreg Kurz                 virtio_error(vdev, "virtio-net header incorrect");
2024fa5e56c2SGreg Kurz                 virtqueue_detach_element(q->tx_vq, elem, 0);
2025fa5e56c2SGreg Kurz                 g_free(elem);
2026fa5e56c2SGreg Kurz                 return -EINVAL;
2027032a74a1SCédric Le Goater             }
20281bfa316cSGreg Kurz             if (n->needs_vnet_hdr_swap) {
2029feb93f36SJason Wang                 virtio_net_hdr_swap(vdev, (void *) &mhdr);
2030feb93f36SJason Wang                 sg2[0].iov_base = &mhdr;
2031feb93f36SJason Wang                 sg2[0].iov_len = n->guest_hdr_len;
2032feb93f36SJason Wang                 out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
2033feb93f36SJason Wang                                    out_sg, out_num,
2034feb93f36SJason Wang                                    n->guest_hdr_len, -1);
2035feb93f36SJason Wang                 if (out_num == VIRTQUEUE_MAX_SIZE) {
2036feb93f36SJason Wang                     goto drop;
2037032a74a1SCédric Le Goater                 }
2038feb93f36SJason Wang                 out_num += 1;
2039feb93f36SJason Wang                 out_sg = sg2;
2040feb93f36SJason Wang             }
2041feb93f36SJason Wang         }
20426e790746SPaolo Bonzini         /*
20436e790746SPaolo Bonzini          * If host wants to see the guest header as is, we can
20446e790746SPaolo Bonzini          * pass it on unchanged. Otherwise, copy just the parts
20456e790746SPaolo Bonzini          * that host is interested in.
20466e790746SPaolo Bonzini          */
20476e790746SPaolo Bonzini         assert(n->host_hdr_len <= n->guest_hdr_len);
20486e790746SPaolo Bonzini         if (n->host_hdr_len != n->guest_hdr_len) {
20496e790746SPaolo Bonzini             unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg),
20506e790746SPaolo Bonzini                                        out_sg, out_num,
20516e790746SPaolo Bonzini                                        0, n->host_hdr_len);
20526e790746SPaolo Bonzini             sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num,
20536e790746SPaolo Bonzini                              out_sg, out_num,
20546e790746SPaolo Bonzini                              n->guest_hdr_len, -1);
20556e790746SPaolo Bonzini             out_num = sg_num;
20566e790746SPaolo Bonzini             out_sg = sg;
20576e790746SPaolo Bonzini         }
20586e790746SPaolo Bonzini 
20596e790746SPaolo Bonzini         ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
20606e790746SPaolo Bonzini                                       out_sg, out_num, virtio_net_tx_complete);
20616e790746SPaolo Bonzini         if (ret == 0) {
20626e790746SPaolo Bonzini             virtio_queue_set_notification(q->tx_vq, 0);
20636e790746SPaolo Bonzini             q->async_tx.elem = elem;
20646e790746SPaolo Bonzini             return -EBUSY;
20656e790746SPaolo Bonzini         }
20666e790746SPaolo Bonzini 
2067feb93f36SJason Wang drop:
206851b19ebeSPaolo Bonzini         virtqueue_push(q->tx_vq, elem, 0);
206917a0ca55SKONRAD Frederic         virtio_notify(vdev, q->tx_vq);
207051b19ebeSPaolo Bonzini         g_free(elem);
20716e790746SPaolo Bonzini 
20726e790746SPaolo Bonzini         if (++num_packets >= n->tx_burst) {
20736e790746SPaolo Bonzini             break;
20746e790746SPaolo Bonzini         }
20756e790746SPaolo Bonzini     }
20766e790746SPaolo Bonzini     return num_packets;
20776e790746SPaolo Bonzini }
20786e790746SPaolo Bonzini 
20796e790746SPaolo Bonzini static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
20806e790746SPaolo Bonzini {
208117a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
20826e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
20836e790746SPaolo Bonzini 
2084283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2085283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2086283e2c2aSYuri Benditovich         return;
2087283e2c2aSYuri Benditovich     }
2088283e2c2aSYuri Benditovich 
20896e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
209017a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
20916e790746SPaolo Bonzini         q->tx_waiting = 1;
20926e790746SPaolo Bonzini         return;
20936e790746SPaolo Bonzini     }
20946e790746SPaolo Bonzini 
20956e790746SPaolo Bonzini     if (q->tx_waiting) {
20966e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 1);
2097bc72ad67SAlex Bligh         timer_del(q->tx_timer);
20986e790746SPaolo Bonzini         q->tx_waiting = 0;
2099fa5e56c2SGreg Kurz         if (virtio_net_flush_tx(q) == -EINVAL) {
2100fa5e56c2SGreg Kurz             return;
2101fa5e56c2SGreg Kurz         }
21026e790746SPaolo Bonzini     } else {
2103bc72ad67SAlex Bligh         timer_mod(q->tx_timer,
2104bc72ad67SAlex Bligh                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
21056e790746SPaolo Bonzini         q->tx_waiting = 1;
21066e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 0);
21076e790746SPaolo Bonzini     }
21086e790746SPaolo Bonzini }
21096e790746SPaolo Bonzini 
21106e790746SPaolo Bonzini static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
21116e790746SPaolo Bonzini {
211217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
21136e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
21146e790746SPaolo Bonzini 
2115283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2116283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2117283e2c2aSYuri Benditovich         return;
2118283e2c2aSYuri Benditovich     }
2119283e2c2aSYuri Benditovich 
21206e790746SPaolo Bonzini     if (unlikely(q->tx_waiting)) {
21216e790746SPaolo Bonzini         return;
21226e790746SPaolo Bonzini     }
21236e790746SPaolo Bonzini     q->tx_waiting = 1;
21246e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
212517a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
21266e790746SPaolo Bonzini         return;
21276e790746SPaolo Bonzini     }
21286e790746SPaolo Bonzini     virtio_queue_set_notification(vq, 0);
21296e790746SPaolo Bonzini     qemu_bh_schedule(q->tx_bh);
21306e790746SPaolo Bonzini }
21316e790746SPaolo Bonzini 
21326e790746SPaolo Bonzini static void virtio_net_tx_timer(void *opaque)
21336e790746SPaolo Bonzini {
21346e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
21356e790746SPaolo Bonzini     VirtIONet *n = q->n;
213617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2137e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2138e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2139e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2140e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2141e8bcf842SMichael S. Tsirkin         return;
2142e8bcf842SMichael S. Tsirkin     }
21436e790746SPaolo Bonzini 
21446e790746SPaolo Bonzini     q->tx_waiting = 0;
21456e790746SPaolo Bonzini 
21466e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
214717a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
21486e790746SPaolo Bonzini         return;
214917a0ca55SKONRAD Frederic     }
21506e790746SPaolo Bonzini 
21516e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
21526e790746SPaolo Bonzini     virtio_net_flush_tx(q);
21536e790746SPaolo Bonzini }
21546e790746SPaolo Bonzini 
21556e790746SPaolo Bonzini static void virtio_net_tx_bh(void *opaque)
21566e790746SPaolo Bonzini {
21576e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
21586e790746SPaolo Bonzini     VirtIONet *n = q->n;
215917a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
21606e790746SPaolo Bonzini     int32_t ret;
21616e790746SPaolo Bonzini 
2162e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2163e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2164e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2165e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2166e8bcf842SMichael S. Tsirkin         return;
2167e8bcf842SMichael S. Tsirkin     }
21686e790746SPaolo Bonzini 
21696e790746SPaolo Bonzini     q->tx_waiting = 0;
21706e790746SPaolo Bonzini 
21716e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
217217a0ca55SKONRAD Frederic     if (unlikely(!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) {
21736e790746SPaolo Bonzini         return;
217417a0ca55SKONRAD Frederic     }
21756e790746SPaolo Bonzini 
21766e790746SPaolo Bonzini     ret = virtio_net_flush_tx(q);
2177fa5e56c2SGreg Kurz     if (ret == -EBUSY || ret == -EINVAL) {
2178fa5e56c2SGreg Kurz         return; /* Notification re-enable handled by tx_complete or device
2179fa5e56c2SGreg Kurz                  * broken */
21806e790746SPaolo Bonzini     }
21816e790746SPaolo Bonzini 
21826e790746SPaolo Bonzini     /* If we flush a full burst of packets, assume there are
21836e790746SPaolo Bonzini      * more coming and immediately reschedule */
21846e790746SPaolo Bonzini     if (ret >= n->tx_burst) {
21856e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
21866e790746SPaolo Bonzini         q->tx_waiting = 1;
21876e790746SPaolo Bonzini         return;
21886e790746SPaolo Bonzini     }
21896e790746SPaolo Bonzini 
21906e790746SPaolo Bonzini     /* If less than a full burst, re-enable notification and flush
21916e790746SPaolo Bonzini      * anything that may have come in while we weren't looking.  If
21926e790746SPaolo Bonzini      * we find something, assume the guest is still active and reschedule */
21936e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
2194fa5e56c2SGreg Kurz     ret = virtio_net_flush_tx(q);
2195fa5e56c2SGreg Kurz     if (ret == -EINVAL) {
2196fa5e56c2SGreg Kurz         return;
2197fa5e56c2SGreg Kurz     } else if (ret > 0) {
21986e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
21996e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
22006e790746SPaolo Bonzini         q->tx_waiting = 1;
22016e790746SPaolo Bonzini     }
22026e790746SPaolo Bonzini }
22036e790746SPaolo Bonzini 
2204f9d6dbf0SWen Congyang static void virtio_net_add_queue(VirtIONet *n, int index)
2205f9d6dbf0SWen Congyang {
2206f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2207f9d6dbf0SWen Congyang 
22081c0fbfa3SMichael S. Tsirkin     n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size,
22091c0fbfa3SMichael S. Tsirkin                                            virtio_net_handle_rx);
22109b02e161SWei Wang 
2211f9d6dbf0SWen Congyang     if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) {
2212f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
22139b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
22149b02e161SWei Wang                              virtio_net_handle_tx_timer);
2215f9d6dbf0SWen Congyang         n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
2216f9d6dbf0SWen Congyang                                               virtio_net_tx_timer,
2217f9d6dbf0SWen Congyang                                               &n->vqs[index]);
2218f9d6dbf0SWen Congyang     } else {
2219f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
22209b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
22219b02e161SWei Wang                              virtio_net_handle_tx_bh);
2222f9d6dbf0SWen Congyang         n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]);
2223f9d6dbf0SWen Congyang     }
2224f9d6dbf0SWen Congyang 
2225f9d6dbf0SWen Congyang     n->vqs[index].tx_waiting = 0;
2226f9d6dbf0SWen Congyang     n->vqs[index].n = n;
2227f9d6dbf0SWen Congyang }
2228f9d6dbf0SWen Congyang 
2229f9d6dbf0SWen Congyang static void virtio_net_del_queue(VirtIONet *n, int index)
2230f9d6dbf0SWen Congyang {
2231f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2232f9d6dbf0SWen Congyang     VirtIONetQueue *q = &n->vqs[index];
2233f9d6dbf0SWen Congyang     NetClientState *nc = qemu_get_subqueue(n->nic, index);
2234f9d6dbf0SWen Congyang 
2235f9d6dbf0SWen Congyang     qemu_purge_queued_packets(nc);
2236f9d6dbf0SWen Congyang 
2237f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2);
2238f9d6dbf0SWen Congyang     if (q->tx_timer) {
2239f9d6dbf0SWen Congyang         timer_del(q->tx_timer);
2240f9d6dbf0SWen Congyang         timer_free(q->tx_timer);
2241f989c30cSYunjian Wang         q->tx_timer = NULL;
2242f9d6dbf0SWen Congyang     } else {
2243f9d6dbf0SWen Congyang         qemu_bh_delete(q->tx_bh);
2244f989c30cSYunjian Wang         q->tx_bh = NULL;
2245f9d6dbf0SWen Congyang     }
2246f989c30cSYunjian Wang     q->tx_waiting = 0;
2247f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2 + 1);
2248f9d6dbf0SWen Congyang }
2249f9d6dbf0SWen Congyang 
2250f9d6dbf0SWen Congyang static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues)
2251f9d6dbf0SWen Congyang {
2252f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2253f9d6dbf0SWen Congyang     int old_num_queues = virtio_get_num_queues(vdev);
2254f9d6dbf0SWen Congyang     int new_num_queues = new_max_queues * 2 + 1;
2255f9d6dbf0SWen Congyang     int i;
2256f9d6dbf0SWen Congyang 
2257f9d6dbf0SWen Congyang     assert(old_num_queues >= 3);
2258f9d6dbf0SWen Congyang     assert(old_num_queues % 2 == 1);
2259f9d6dbf0SWen Congyang 
2260f9d6dbf0SWen Congyang     if (old_num_queues == new_num_queues) {
2261f9d6dbf0SWen Congyang         return;
2262f9d6dbf0SWen Congyang     }
2263f9d6dbf0SWen Congyang 
2264f9d6dbf0SWen Congyang     /*
2265f9d6dbf0SWen Congyang      * We always need to remove and add ctrl vq if
2266f9d6dbf0SWen Congyang      * old_num_queues != new_num_queues. Remove ctrl_vq first,
2267f9d6dbf0SWen Congyang      * and then we only enter one of the following too loops.
2268f9d6dbf0SWen Congyang      */
2269f9d6dbf0SWen Congyang     virtio_del_queue(vdev, old_num_queues - 1);
2270f9d6dbf0SWen Congyang 
2271f9d6dbf0SWen Congyang     for (i = new_num_queues - 1; i < old_num_queues - 1; i += 2) {
2272f9d6dbf0SWen Congyang         /* new_num_queues < old_num_queues */
2273f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i / 2);
2274f9d6dbf0SWen Congyang     }
2275f9d6dbf0SWen Congyang 
2276f9d6dbf0SWen Congyang     for (i = old_num_queues - 1; i < new_num_queues - 1; i += 2) {
2277f9d6dbf0SWen Congyang         /* new_num_queues > old_num_queues */
2278f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i / 2);
2279f9d6dbf0SWen Congyang     }
2280f9d6dbf0SWen Congyang 
2281f9d6dbf0SWen Congyang     /* add ctrl_vq last */
2282f9d6dbf0SWen Congyang     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
2283f9d6dbf0SWen Congyang }
2284f9d6dbf0SWen Congyang 
2285ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
22866e790746SPaolo Bonzini {
2287f9d6dbf0SWen Congyang     int max = multiqueue ? n->max_queues : 1;
2288f9d6dbf0SWen Congyang 
22896e790746SPaolo Bonzini     n->multiqueue = multiqueue;
2290f9d6dbf0SWen Congyang     virtio_net_change_num_queues(n, max);
22916e790746SPaolo Bonzini 
22926e790746SPaolo Bonzini     virtio_net_set_queues(n);
22936e790746SPaolo Bonzini }
22946e790746SPaolo Bonzini 
2295982b78c5SDr. David Alan Gilbert static int virtio_net_post_load_device(void *opaque, int version_id)
2296037dab2fSGreg Kurz {
2297982b78c5SDr. David Alan Gilbert     VirtIONet *n = opaque;
2298982b78c5SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2299037dab2fSGreg Kurz     int i, link_down;
2300037dab2fSGreg Kurz 
2301982b78c5SDr. David Alan Gilbert     virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
230295129d6fSCornelia Huck                                virtio_vdev_has_feature(vdev,
230395129d6fSCornelia Huck                                                        VIRTIO_F_VERSION_1));
23046e790746SPaolo Bonzini 
23056e790746SPaolo Bonzini     /* MAC_TABLE_ENTRIES may be different from the saved image */
2306982b78c5SDr. David Alan Gilbert     if (n->mac_table.in_use > MAC_TABLE_ENTRIES) {
23076e790746SPaolo Bonzini         n->mac_table.in_use = 0;
23086e790746SPaolo Bonzini     }
23096e790746SPaolo Bonzini 
2310982b78c5SDr. David Alan Gilbert     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
23116c666823SMichael S. Tsirkin         n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
23126c666823SMichael S. Tsirkin     }
23136c666823SMichael S. Tsirkin 
23146c666823SMichael S. Tsirkin     if (peer_has_vnet_hdr(n)) {
23156c666823SMichael S. Tsirkin         virtio_net_apply_guest_offloads(n);
23166c666823SMichael S. Tsirkin     }
23176c666823SMichael S. Tsirkin 
23186e790746SPaolo Bonzini     virtio_net_set_queues(n);
23196e790746SPaolo Bonzini 
23206e790746SPaolo Bonzini     /* Find the first multicast entry in the saved MAC filter */
23216e790746SPaolo Bonzini     for (i = 0; i < n->mac_table.in_use; i++) {
23226e790746SPaolo Bonzini         if (n->mac_table.macs[i * ETH_ALEN] & 1) {
23236e790746SPaolo Bonzini             break;
23246e790746SPaolo Bonzini         }
23256e790746SPaolo Bonzini     }
23266e790746SPaolo Bonzini     n->mac_table.first_multi = i;
23276e790746SPaolo Bonzini 
23286e790746SPaolo Bonzini     /* nc.link_down can't be migrated, so infer link_down according
23296e790746SPaolo Bonzini      * to link status bit in n->status */
23306e790746SPaolo Bonzini     link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0;
23316e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
23326e790746SPaolo Bonzini         qemu_get_subqueue(n->nic, i)->link_down = link_down;
23336e790746SPaolo Bonzini     }
23346e790746SPaolo Bonzini 
23356c666823SMichael S. Tsirkin     if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
23366c666823SMichael S. Tsirkin         virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
23376c666823SMichael S. Tsirkin         n->announce_counter = SELF_ANNOUNCE_ROUNDS;
23386c666823SMichael S. Tsirkin         timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL));
23396c666823SMichael S. Tsirkin     }
23406c666823SMichael S. Tsirkin 
23416e790746SPaolo Bonzini     return 0;
23426e790746SPaolo Bonzini }
23436e790746SPaolo Bonzini 
2344982b78c5SDr. David Alan Gilbert /* tx_waiting field of a VirtIONetQueue */
2345982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = {
2346982b78c5SDr. David Alan Gilbert     .name = "virtio-net-queue-tx_waiting",
2347982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2348982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(tx_waiting, VirtIONetQueue),
2349982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2350982b78c5SDr. David Alan Gilbert    },
2351982b78c5SDr. David Alan Gilbert };
2352982b78c5SDr. David Alan Gilbert 
2353982b78c5SDr. David Alan Gilbert static bool max_queues_gt_1(void *opaque, int version_id)
2354982b78c5SDr. David Alan Gilbert {
2355982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->max_queues > 1;
2356982b78c5SDr. David Alan Gilbert }
2357982b78c5SDr. David Alan Gilbert 
2358982b78c5SDr. David Alan Gilbert static bool has_ctrl_guest_offloads(void *opaque, int version_id)
2359982b78c5SDr. David Alan Gilbert {
2360982b78c5SDr. David Alan Gilbert     return virtio_vdev_has_feature(VIRTIO_DEVICE(opaque),
2361982b78c5SDr. David Alan Gilbert                                    VIRTIO_NET_F_CTRL_GUEST_OFFLOADS);
2362982b78c5SDr. David Alan Gilbert }
2363982b78c5SDr. David Alan Gilbert 
2364982b78c5SDr. David Alan Gilbert static bool mac_table_fits(void *opaque, int version_id)
2365982b78c5SDr. David Alan Gilbert {
2366982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->mac_table.in_use <= MAC_TABLE_ENTRIES;
2367982b78c5SDr. David Alan Gilbert }
2368982b78c5SDr. David Alan Gilbert 
2369982b78c5SDr. David Alan Gilbert static bool mac_table_doesnt_fit(void *opaque, int version_id)
2370982b78c5SDr. David Alan Gilbert {
2371982b78c5SDr. David Alan Gilbert     return !mac_table_fits(opaque, version_id);
2372982b78c5SDr. David Alan Gilbert }
2373982b78c5SDr. David Alan Gilbert 
2374982b78c5SDr. David Alan Gilbert /* This temporary type is shared by all the WITH_TMP methods
2375982b78c5SDr. David Alan Gilbert  * although only some fields are used by each.
2376982b78c5SDr. David Alan Gilbert  */
2377982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp {
2378982b78c5SDr. David Alan Gilbert     VirtIONet      *parent;
2379982b78c5SDr. David Alan Gilbert     VirtIONetQueue *vqs_1;
2380982b78c5SDr. David Alan Gilbert     uint16_t        curr_queues_1;
2381982b78c5SDr. David Alan Gilbert     uint8_t         has_ufo;
2382982b78c5SDr. David Alan Gilbert     uint32_t        has_vnet_hdr;
2383982b78c5SDr. David Alan Gilbert };
2384982b78c5SDr. David Alan Gilbert 
2385982b78c5SDr. David Alan Gilbert /* The 2nd and subsequent tx_waiting flags are loaded later than
2386982b78c5SDr. David Alan Gilbert  * the 1st entry in the queues and only if there's more than one
2387982b78c5SDr. David Alan Gilbert  * entry.  We use the tmp mechanism to calculate a temporary
2388982b78c5SDr. David Alan Gilbert  * pointer and count and also validate the count.
2389982b78c5SDr. David Alan Gilbert  */
2390982b78c5SDr. David Alan Gilbert 
239144b1ff31SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_save(void *opaque)
2392982b78c5SDr. David Alan Gilbert {
2393982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2394982b78c5SDr. David Alan Gilbert 
2395982b78c5SDr. David Alan Gilbert     tmp->vqs_1 = tmp->parent->vqs + 1;
2396982b78c5SDr. David Alan Gilbert     tmp->curr_queues_1 = tmp->parent->curr_queues - 1;
2397982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues == 0) {
2398982b78c5SDr. David Alan Gilbert         tmp->curr_queues_1 = 0;
2399982b78c5SDr. David Alan Gilbert     }
240044b1ff31SDr. David Alan Gilbert 
240144b1ff31SDr. David Alan Gilbert     return 0;
2402982b78c5SDr. David Alan Gilbert }
2403982b78c5SDr. David Alan Gilbert 
2404982b78c5SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_load(void *opaque)
2405982b78c5SDr. David Alan Gilbert {
2406982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2407982b78c5SDr. David Alan Gilbert 
2408982b78c5SDr. David Alan Gilbert     /* Reuse the pointer setup from save */
2409982b78c5SDr. David Alan Gilbert     virtio_net_tx_waiting_pre_save(opaque);
2410982b78c5SDr. David Alan Gilbert 
2411982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues > tmp->parent->max_queues) {
2412982b78c5SDr. David Alan Gilbert         error_report("virtio-net: curr_queues %x > max_queues %x",
2413982b78c5SDr. David Alan Gilbert             tmp->parent->curr_queues, tmp->parent->max_queues);
2414982b78c5SDr. David Alan Gilbert 
2415982b78c5SDr. David Alan Gilbert         return -EINVAL;
2416982b78c5SDr. David Alan Gilbert     }
2417982b78c5SDr. David Alan Gilbert 
2418982b78c5SDr. David Alan Gilbert     return 0; /* all good */
2419982b78c5SDr. David Alan Gilbert }
2420982b78c5SDr. David Alan Gilbert 
2421982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_tx_waiting = {
2422982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-tx_waiting",
2423982b78c5SDr. David Alan Gilbert     .pre_load  = virtio_net_tx_waiting_pre_load,
2424982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_tx_waiting_pre_save,
2425982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2426982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp,
2427982b78c5SDr. David Alan Gilbert                                      curr_queues_1,
2428982b78c5SDr. David Alan Gilbert                                      vmstate_virtio_net_queue_tx_waiting,
2429982b78c5SDr. David Alan Gilbert                                      struct VirtIONetQueue),
2430982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2431982b78c5SDr. David Alan Gilbert     },
2432982b78c5SDr. David Alan Gilbert };
2433982b78c5SDr. David Alan Gilbert 
2434982b78c5SDr. David Alan Gilbert /* the 'has_ufo' flag is just tested; if the incoming stream has the
2435982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2436982b78c5SDr. David Alan Gilbert  */
2437982b78c5SDr. David Alan Gilbert static int virtio_net_ufo_post_load(void *opaque, int version_id)
2438982b78c5SDr. David Alan Gilbert {
2439982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2440982b78c5SDr. David Alan Gilbert 
2441982b78c5SDr. David Alan Gilbert     if (tmp->has_ufo && !peer_has_ufo(tmp->parent)) {
2442982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires TUN_F_UFO support");
2443982b78c5SDr. David Alan Gilbert         return -EINVAL;
2444982b78c5SDr. David Alan Gilbert     }
2445982b78c5SDr. David Alan Gilbert 
2446982b78c5SDr. David Alan Gilbert     return 0;
2447982b78c5SDr. David Alan Gilbert }
2448982b78c5SDr. David Alan Gilbert 
244944b1ff31SDr. David Alan Gilbert static int virtio_net_ufo_pre_save(void *opaque)
2450982b78c5SDr. David Alan Gilbert {
2451982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2452982b78c5SDr. David Alan Gilbert 
2453982b78c5SDr. David Alan Gilbert     tmp->has_ufo = tmp->parent->has_ufo;
245444b1ff31SDr. David Alan Gilbert 
245544b1ff31SDr. David Alan Gilbert     return 0;
2456982b78c5SDr. David Alan Gilbert }
2457982b78c5SDr. David Alan Gilbert 
2458982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_ufo = {
2459982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-ufo",
2460982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_ufo_post_load,
2461982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_ufo_pre_save,
2462982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2463982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(has_ufo, struct VirtIONetMigTmp),
2464982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2465982b78c5SDr. David Alan Gilbert     },
2466982b78c5SDr. David Alan Gilbert };
2467982b78c5SDr. David Alan Gilbert 
2468982b78c5SDr. David Alan Gilbert /* the 'has_vnet_hdr' flag is just tested; if the incoming stream has the
2469982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2470982b78c5SDr. David Alan Gilbert  */
2471982b78c5SDr. David Alan Gilbert static int virtio_net_vnet_post_load(void *opaque, int version_id)
2472982b78c5SDr. David Alan Gilbert {
2473982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2474982b78c5SDr. David Alan Gilbert 
2475982b78c5SDr. David Alan Gilbert     if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) {
2476982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires vnet_hdr=on");
2477982b78c5SDr. David Alan Gilbert         return -EINVAL;
2478982b78c5SDr. David Alan Gilbert     }
2479982b78c5SDr. David Alan Gilbert 
2480982b78c5SDr. David Alan Gilbert     return 0;
2481982b78c5SDr. David Alan Gilbert }
2482982b78c5SDr. David Alan Gilbert 
248344b1ff31SDr. David Alan Gilbert static int virtio_net_vnet_pre_save(void *opaque)
2484982b78c5SDr. David Alan Gilbert {
2485982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2486982b78c5SDr. David Alan Gilbert 
2487982b78c5SDr. David Alan Gilbert     tmp->has_vnet_hdr = tmp->parent->has_vnet_hdr;
248844b1ff31SDr. David Alan Gilbert 
248944b1ff31SDr. David Alan Gilbert     return 0;
2490982b78c5SDr. David Alan Gilbert }
2491982b78c5SDr. David Alan Gilbert 
2492982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_vnet = {
2493982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-vnet",
2494982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_vnet_post_load,
2495982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_vnet_pre_save,
2496982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2497982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(has_vnet_hdr, struct VirtIONetMigTmp),
2498982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2499982b78c5SDr. David Alan Gilbert     },
2500982b78c5SDr. David Alan Gilbert };
2501982b78c5SDr. David Alan Gilbert 
2502982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_device = {
2503982b78c5SDr. David Alan Gilbert     .name = "virtio-net-device",
2504982b78c5SDr. David Alan Gilbert     .version_id = VIRTIO_NET_VM_VERSION,
2505982b78c5SDr. David Alan Gilbert     .minimum_version_id = VIRTIO_NET_VM_VERSION,
2506982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_post_load_device,
2507982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2508982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN),
2509982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_POINTER(vqs, VirtIONet,
2510982b78c5SDr. David Alan Gilbert                                vmstate_virtio_net_queue_tx_waiting,
2511982b78c5SDr. David Alan Gilbert                                VirtIONetQueue),
2512982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mergeable_rx_bufs, VirtIONet),
2513982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16(status, VirtIONet),
2514982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(promisc, VirtIONet),
2515982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(allmulti, VirtIONet),
2516982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mac_table.in_use, VirtIONet),
2517982b78c5SDr. David Alan Gilbert 
2518982b78c5SDr. David Alan Gilbert         /* Guarded pair: If it fits we load it, else we throw it away
2519982b78c5SDr. David Alan Gilbert          * - can happen if source has a larger MAC table.; post-load
2520982b78c5SDr. David Alan Gilbert          *  sets flags in this case.
2521982b78c5SDr. David Alan Gilbert          */
2522982b78c5SDr. David Alan Gilbert         VMSTATE_VBUFFER_MULTIPLY(mac_table.macs, VirtIONet,
2523982b78c5SDr. David Alan Gilbert                                 0, mac_table_fits, mac_table.in_use,
2524982b78c5SDr. David Alan Gilbert                                  ETH_ALEN),
2525982b78c5SDr. David Alan Gilbert         VMSTATE_UNUSED_VARRAY_UINT32(VirtIONet, mac_table_doesnt_fit, 0,
2526982b78c5SDr. David Alan Gilbert                                      mac_table.in_use, ETH_ALEN),
2527982b78c5SDr. David Alan Gilbert 
2528982b78c5SDr. David Alan Gilbert         /* Note: This is an array of uint32's that's always been saved as a
2529982b78c5SDr. David Alan Gilbert          * buffer; hold onto your endiannesses; it's actually used as a bitmap
2530982b78c5SDr. David Alan Gilbert          * but based on the uint.
2531982b78c5SDr. David Alan Gilbert          */
2532982b78c5SDr. David Alan Gilbert         VMSTATE_BUFFER_POINTER_UNSAFE(vlans, VirtIONet, 0, MAX_VLAN >> 3),
2533982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2534982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_vnet),
2535982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.multi_overflow, VirtIONet),
2536982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.uni_overflow, VirtIONet),
2537982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(alluni, VirtIONet),
2538982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nomulti, VirtIONet),
2539982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nouni, VirtIONet),
2540982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nobcast, VirtIONet),
2541982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2542982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_ufo),
2543982b78c5SDr. David Alan Gilbert         VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0,
2544982b78c5SDr. David Alan Gilbert                             vmstate_info_uint16_equal, uint16_t),
2545982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1),
2546982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2547982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_tx_waiting),
2548982b78c5SDr. David Alan Gilbert         VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet,
2549982b78c5SDr. David Alan Gilbert                             has_ctrl_guest_offloads),
2550982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2551982b78c5SDr. David Alan Gilbert    },
2552982b78c5SDr. David Alan Gilbert };
2553982b78c5SDr. David Alan Gilbert 
25546e790746SPaolo Bonzini static NetClientInfo net_virtio_info = {
2555f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
25566e790746SPaolo Bonzini     .size = sizeof(NICState),
25576e790746SPaolo Bonzini     .can_receive = virtio_net_can_receive,
25586e790746SPaolo Bonzini     .receive = virtio_net_receive,
25596e790746SPaolo Bonzini     .link_status_changed = virtio_net_set_link_status,
2560b1be4280SAmos Kong     .query_rx_filter = virtio_net_query_rxfilter,
25616e790746SPaolo Bonzini };
25626e790746SPaolo Bonzini 
25636e790746SPaolo Bonzini static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
25646e790746SPaolo Bonzini {
256517a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
25666e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
25676e790746SPaolo Bonzini     assert(n->vhost_started);
2568ed8b4afeSNikolay Nikolaev     return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx);
25696e790746SPaolo Bonzini }
25706e790746SPaolo Bonzini 
25716e790746SPaolo Bonzini static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
25726e790746SPaolo Bonzini                                            bool mask)
25736e790746SPaolo Bonzini {
257417a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
25756e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
25766e790746SPaolo Bonzini     assert(n->vhost_started);
2577ed8b4afeSNikolay Nikolaev     vhost_net_virtqueue_mask(get_vhost_net(nc->peer),
25786e790746SPaolo Bonzini                              vdev, idx, mask);
25796e790746SPaolo Bonzini }
25806e790746SPaolo Bonzini 
2581019a3edbSGerd Hoffmann static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features)
25826e790746SPaolo Bonzini {
25836e790746SPaolo Bonzini     int i, config_size = 0;
25840cd09c3aSCornelia Huck     virtio_add_feature(&host_features, VIRTIO_NET_F_MAC);
2585a93e599dSMaxime Coquelin 
25866e790746SPaolo Bonzini     for (i = 0; feature_sizes[i].flags != 0; i++) {
25876e790746SPaolo Bonzini         if (host_features & feature_sizes[i].flags) {
25886e790746SPaolo Bonzini             config_size = MAX(feature_sizes[i].end, config_size);
25896e790746SPaolo Bonzini         }
25906e790746SPaolo Bonzini     }
259117ec5a86SKONRAD Frederic     n->config_size = config_size;
259217ec5a86SKONRAD Frederic }
25936e790746SPaolo Bonzini 
25948a253ec2SKONRAD Frederic void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
25958a253ec2SKONRAD Frederic                                    const char *type)
25968a253ec2SKONRAD Frederic {
25978a253ec2SKONRAD Frederic     /*
25988a253ec2SKONRAD Frederic      * The name can be NULL, the netclient name will be type.x.
25998a253ec2SKONRAD Frederic      */
26008a253ec2SKONRAD Frederic     assert(type != NULL);
26018a253ec2SKONRAD Frederic 
26028a253ec2SKONRAD Frederic     g_free(n->netclient_name);
26038a253ec2SKONRAD Frederic     g_free(n->netclient_type);
26048a253ec2SKONRAD Frederic     n->netclient_name = g_strdup(name);
26058a253ec2SKONRAD Frederic     n->netclient_type = g_strdup(type);
26068a253ec2SKONRAD Frederic }
26078a253ec2SKONRAD Frederic 
2608e6f746b3SAndreas Färber static void virtio_net_device_realize(DeviceState *dev, Error **errp)
260917ec5a86SKONRAD Frederic {
2610e6f746b3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
2611284a32f0SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
2612284a32f0SAndreas Färber     NetClientState *nc;
26131773d9eeSKONRAD Frederic     int i;
261417ec5a86SKONRAD Frederic 
2615a93e599dSMaxime Coquelin     if (n->net_conf.mtu) {
2616127833eeSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_MTU);
2617a93e599dSMaxime Coquelin     }
2618a93e599dSMaxime Coquelin 
26199473939eSJason Baron     if (n->net_conf.duplex_str) {
26209473939eSJason Baron         if (strncmp(n->net_conf.duplex_str, "half", 5) == 0) {
26219473939eSJason Baron             n->net_conf.duplex = DUPLEX_HALF;
26229473939eSJason Baron         } else if (strncmp(n->net_conf.duplex_str, "full", 5) == 0) {
26239473939eSJason Baron             n->net_conf.duplex = DUPLEX_FULL;
26249473939eSJason Baron         } else {
26259473939eSJason Baron             error_setg(errp, "'duplex' must be 'half' or 'full'");
26269473939eSJason Baron         }
26279473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
26289473939eSJason Baron     } else {
26299473939eSJason Baron         n->net_conf.duplex = DUPLEX_UNKNOWN;
26309473939eSJason Baron     }
26319473939eSJason Baron 
26329473939eSJason Baron     if (n->net_conf.speed < SPEED_UNKNOWN) {
26339473939eSJason Baron         error_setg(errp, "'speed' must be between 0 and INT_MAX");
26349473939eSJason Baron     } else if (n->net_conf.speed >= 0) {
26359473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
26369473939eSJason Baron     }
26379473939eSJason Baron 
2638da3e8a23SShannon Zhao     virtio_net_set_config_size(n, n->host_features);
2639284a32f0SAndreas Färber     virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size);
264017ec5a86SKONRAD Frederic 
26411c0fbfa3SMichael S. Tsirkin     /*
26421c0fbfa3SMichael S. Tsirkin      * We set a lower limit on RX queue size to what it always was.
26431c0fbfa3SMichael S. Tsirkin      * Guests that want a smaller ring can always resize it without
26441c0fbfa3SMichael S. Tsirkin      * help from us (using virtio 1 and up).
26451c0fbfa3SMichael S. Tsirkin      */
26461c0fbfa3SMichael S. Tsirkin     if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE ||
26471c0fbfa3SMichael S. Tsirkin         n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE ||
26485f997fd1SMichal Privoznik         !is_power_of_2(n->net_conf.rx_queue_size)) {
26491c0fbfa3SMichael S. Tsirkin         error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), "
26501c0fbfa3SMichael S. Tsirkin                    "must be a power of 2 between %d and %d.",
26511c0fbfa3SMichael S. Tsirkin                    n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE,
26521c0fbfa3SMichael S. Tsirkin                    VIRTQUEUE_MAX_SIZE);
26531c0fbfa3SMichael S. Tsirkin         virtio_cleanup(vdev);
26541c0fbfa3SMichael S. Tsirkin         return;
26551c0fbfa3SMichael S. Tsirkin     }
26561c0fbfa3SMichael S. Tsirkin 
26579b02e161SWei Wang     if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE ||
26589b02e161SWei Wang         n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE ||
26599b02e161SWei Wang         !is_power_of_2(n->net_conf.tx_queue_size)) {
26609b02e161SWei Wang         error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), "
26619b02e161SWei Wang                    "must be a power of 2 between %d and %d",
26629b02e161SWei Wang                    n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE,
26639b02e161SWei Wang                    VIRTQUEUE_MAX_SIZE);
26649b02e161SWei Wang         virtio_cleanup(vdev);
26659b02e161SWei Wang         return;
26669b02e161SWei Wang     }
26679b02e161SWei Wang 
2668575a1c0eSJiri Pirko     n->max_queues = MAX(n->nic_conf.peers.queues, 1);
266987b3bd1cSJason Wang     if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) {
26707e0e736eSJason Wang         error_setg(errp, "Invalid number of queues (= %" PRIu32 "), "
2671631b22eaSStefan Weil                    "must be a positive integer less than %d.",
267287b3bd1cSJason Wang                    n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2);
26737e0e736eSJason Wang         virtio_cleanup(vdev);
26747e0e736eSJason Wang         return;
26757e0e736eSJason Wang     }
26766e790746SPaolo Bonzini     n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues);
26776e790746SPaolo Bonzini     n->curr_queues = 1;
26781773d9eeSKONRAD Frederic     n->tx_timeout = n->net_conf.txtimer;
26796e790746SPaolo Bonzini 
26801773d9eeSKONRAD Frederic     if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer")
26811773d9eeSKONRAD Frederic                        && strcmp(n->net_conf.tx, "bh")) {
26820765691eSMarkus Armbruster         warn_report("virtio-net: "
26836e790746SPaolo Bonzini                     "Unknown option tx=%s, valid options: \"timer\" \"bh\"",
26841773d9eeSKONRAD Frederic                     n->net_conf.tx);
26850765691eSMarkus Armbruster         error_printf("Defaulting to \"bh\"");
26866e790746SPaolo Bonzini     }
26876e790746SPaolo Bonzini 
26882eef278bSMichael S. Tsirkin     n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n),
26892eef278bSMichael S. Tsirkin                                     n->net_conf.tx_queue_size);
26909b02e161SWei Wang 
2691da51a335SJason Wang     for (i = 0; i < n->max_queues; i++) {
2692f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i);
2693da51a335SJason Wang     }
2694da51a335SJason Wang 
269517a0ca55SKONRAD Frederic     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
26961773d9eeSKONRAD Frederic     qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);
26971773d9eeSKONRAD Frederic     memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac));
26986e790746SPaolo Bonzini     n->status = VIRTIO_NET_S_LINK_UP;
2699f57fcf70SJason Wang     n->announce_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
2700f57fcf70SJason Wang                                      virtio_net_announce_timer, n);
27016e790746SPaolo Bonzini 
27028a253ec2SKONRAD Frederic     if (n->netclient_type) {
27038a253ec2SKONRAD Frederic         /*
27048a253ec2SKONRAD Frederic          * Happen when virtio_net_set_netclient_name has been called.
27058a253ec2SKONRAD Frederic          */
27068a253ec2SKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
27078a253ec2SKONRAD Frederic                               n->netclient_type, n->netclient_name, n);
27088a253ec2SKONRAD Frederic     } else {
27091773d9eeSKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
2710284a32f0SAndreas Färber                               object_get_typename(OBJECT(dev)), dev->id, n);
27118a253ec2SKONRAD Frederic     }
27128a253ec2SKONRAD Frederic 
27136e790746SPaolo Bonzini     peer_test_vnet_hdr(n);
27146e790746SPaolo Bonzini     if (peer_has_vnet_hdr(n)) {
27156e790746SPaolo Bonzini         for (i = 0; i < n->max_queues; i++) {
2716d6085e3aSStefan Hajnoczi             qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true);
27176e790746SPaolo Bonzini         }
27186e790746SPaolo Bonzini         n->host_hdr_len = sizeof(struct virtio_net_hdr);
27196e790746SPaolo Bonzini     } else {
27206e790746SPaolo Bonzini         n->host_hdr_len = 0;
27216e790746SPaolo Bonzini     }
27226e790746SPaolo Bonzini 
27231773d9eeSKONRAD Frederic     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->nic_conf.macaddr.a);
27246e790746SPaolo Bonzini 
27256e790746SPaolo Bonzini     n->vqs[0].tx_waiting = 0;
27261773d9eeSKONRAD Frederic     n->tx_burst = n->net_conf.txburst;
2727bb9d17f8SCornelia Huck     virtio_net_set_mrg_rx_bufs(n, 0, 0);
27286e790746SPaolo Bonzini     n->promisc = 1; /* for compatibility */
27296e790746SPaolo Bonzini 
27306e790746SPaolo Bonzini     n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
27316e790746SPaolo Bonzini 
27326e790746SPaolo Bonzini     n->vlans = g_malloc0(MAX_VLAN >> 3);
27336e790746SPaolo Bonzini 
2734b1be4280SAmos Kong     nc = qemu_get_queue(n->nic);
2735b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
2736b1be4280SAmos Kong 
27372974e916SYuri Benditovich     QTAILQ_INIT(&n->rsc_chains);
2738284a32f0SAndreas Färber     n->qdev = dev;
273917ec5a86SKONRAD Frederic }
274017ec5a86SKONRAD Frederic 
2741306ec6c3SAndreas Färber static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
274217ec5a86SKONRAD Frederic {
2743306ec6c3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
2744306ec6c3SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
2745f9d6dbf0SWen Congyang     int i, max_queues;
274617ec5a86SKONRAD Frederic 
274717ec5a86SKONRAD Frederic     /* This will stop vhost backend if appropriate. */
274817ec5a86SKONRAD Frederic     virtio_net_set_status(vdev, 0);
274917ec5a86SKONRAD Frederic 
27508a253ec2SKONRAD Frederic     g_free(n->netclient_name);
27518a253ec2SKONRAD Frederic     n->netclient_name = NULL;
27528a253ec2SKONRAD Frederic     g_free(n->netclient_type);
27538a253ec2SKONRAD Frederic     n->netclient_type = NULL;
27548a253ec2SKONRAD Frederic 
275517ec5a86SKONRAD Frederic     g_free(n->mac_table.macs);
275617ec5a86SKONRAD Frederic     g_free(n->vlans);
275717ec5a86SKONRAD Frederic 
2758f9d6dbf0SWen Congyang     max_queues = n->multiqueue ? n->max_queues : 1;
2759f9d6dbf0SWen Congyang     for (i = 0; i < max_queues; i++) {
2760f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i);
276117ec5a86SKONRAD Frederic     }
276217ec5a86SKONRAD Frederic 
2763f57fcf70SJason Wang     timer_del(n->announce_timer);
2764f57fcf70SJason Wang     timer_free(n->announce_timer);
276517ec5a86SKONRAD Frederic     g_free(n->vqs);
276617ec5a86SKONRAD Frederic     qemu_del_nic(n->nic);
27672974e916SYuri Benditovich     virtio_net_rsc_cleanup(n);
27686a1a8cc7SKONRAD Frederic     virtio_cleanup(vdev);
276917ec5a86SKONRAD Frederic }
277017ec5a86SKONRAD Frederic 
277117ec5a86SKONRAD Frederic static void virtio_net_instance_init(Object *obj)
277217ec5a86SKONRAD Frederic {
277317ec5a86SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(obj);
277417ec5a86SKONRAD Frederic 
277517ec5a86SKONRAD Frederic     /*
277617ec5a86SKONRAD Frederic      * The default config_size is sizeof(struct virtio_net_config).
277717ec5a86SKONRAD Frederic      * Can be overriden with virtio_net_set_config_size.
277817ec5a86SKONRAD Frederic      */
277917ec5a86SKONRAD Frederic     n->config_size = sizeof(struct virtio_net_config);
2780aa4197c3SGonglei     device_add_bootindex_property(obj, &n->nic_conf.bootindex,
2781aa4197c3SGonglei                                   "bootindex", "/ethernet-phy@0",
2782aa4197c3SGonglei                                   DEVICE(n), NULL);
278317ec5a86SKONRAD Frederic }
278417ec5a86SKONRAD Frederic 
278544b1ff31SDr. David Alan Gilbert static int virtio_net_pre_save(void *opaque)
27864d45dcfbSHalil Pasic {
27874d45dcfbSHalil Pasic     VirtIONet *n = opaque;
27884d45dcfbSHalil Pasic 
27894d45dcfbSHalil Pasic     /* At this point, backend must be stopped, otherwise
27904d45dcfbSHalil Pasic      * it might keep writing to memory. */
27914d45dcfbSHalil Pasic     assert(!n->vhost_started);
279244b1ff31SDr. David Alan Gilbert 
279344b1ff31SDr. David Alan Gilbert     return 0;
27944d45dcfbSHalil Pasic }
27954d45dcfbSHalil Pasic 
27964d45dcfbSHalil Pasic static const VMStateDescription vmstate_virtio_net = {
27974d45dcfbSHalil Pasic     .name = "virtio-net",
27984d45dcfbSHalil Pasic     .minimum_version_id = VIRTIO_NET_VM_VERSION,
27994d45dcfbSHalil Pasic     .version_id = VIRTIO_NET_VM_VERSION,
28004d45dcfbSHalil Pasic     .fields = (VMStateField[]) {
28014d45dcfbSHalil Pasic         VMSTATE_VIRTIO_DEVICE,
28024d45dcfbSHalil Pasic         VMSTATE_END_OF_LIST()
28034d45dcfbSHalil Pasic     },
28044d45dcfbSHalil Pasic     .pre_save = virtio_net_pre_save,
28054d45dcfbSHalil Pasic };
2806290c2428SDr. David Alan Gilbert 
280717ec5a86SKONRAD Frederic static Property virtio_net_properties[] = {
2808127833eeSJason Baron     DEFINE_PROP_BIT64("csum", VirtIONet, host_features,
2809127833eeSJason Baron                     VIRTIO_NET_F_CSUM, true),
2810127833eeSJason Baron     DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features,
281187108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_CSUM, true),
2812127833eeSJason Baron     DEFINE_PROP_BIT64("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true),
2813127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso4", VirtIONet, host_features,
281487108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO4, true),
2815127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso6", VirtIONet, host_features,
281687108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO6, true),
2817127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ecn", VirtIONet, host_features,
281887108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ECN, true),
2819127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ufo", VirtIONet, host_features,
282087108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_UFO, true),
2821127833eeSJason Baron     DEFINE_PROP_BIT64("guest_announce", VirtIONet, host_features,
282287108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ANNOUNCE, true),
2823127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso4", VirtIONet, host_features,
282487108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO4, true),
2825127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso6", VirtIONet, host_features,
282687108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO6, true),
2827127833eeSJason Baron     DEFINE_PROP_BIT64("host_ecn", VirtIONet, host_features,
282887108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_ECN, true),
2829127833eeSJason Baron     DEFINE_PROP_BIT64("host_ufo", VirtIONet, host_features,
283087108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_UFO, true),
2831127833eeSJason Baron     DEFINE_PROP_BIT64("mrg_rxbuf", VirtIONet, host_features,
283287108bb2SShannon Zhao                     VIRTIO_NET_F_MRG_RXBUF, true),
2833127833eeSJason Baron     DEFINE_PROP_BIT64("status", VirtIONet, host_features,
283487108bb2SShannon Zhao                     VIRTIO_NET_F_STATUS, true),
2835127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vq", VirtIONet, host_features,
283687108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VQ, true),
2837127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx", VirtIONet, host_features,
283887108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX, true),
2839127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vlan", VirtIONet, host_features,
284087108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VLAN, true),
2841127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx_extra", VirtIONet, host_features,
284287108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX_EXTRA, true),
2843127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_mac_addr", VirtIONet, host_features,
284487108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_MAC_ADDR, true),
2845127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features,
284687108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
2847127833eeSJason Baron     DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
28482974e916SYuri Benditovich     DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features,
28492974e916SYuri Benditovich                     VIRTIO_NET_F_RSC_EXT, false),
28502974e916SYuri Benditovich     DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout,
28512974e916SYuri Benditovich                        VIRTIO_NET_RSC_DEFAULT_INTERVAL),
285217ec5a86SKONRAD Frederic     DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf),
285317ec5a86SKONRAD Frederic     DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer,
285417ec5a86SKONRAD Frederic                        TX_TIMER_INTERVAL),
285517ec5a86SKONRAD Frederic     DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST),
285617ec5a86SKONRAD Frederic     DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx),
28571c0fbfa3SMichael S. Tsirkin     DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size,
28581c0fbfa3SMichael S. Tsirkin                        VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE),
28599b02e161SWei Wang     DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size,
28609b02e161SWei Wang                        VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE),
2861a93e599dSMaxime Coquelin     DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0),
286275ebec11SMaxime Coquelin     DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend,
286375ebec11SMaxime Coquelin                      true),
28649473939eSJason Baron     DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN),
28659473939eSJason Baron     DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str),
286617ec5a86SKONRAD Frederic     DEFINE_PROP_END_OF_LIST(),
286717ec5a86SKONRAD Frederic };
286817ec5a86SKONRAD Frederic 
286917ec5a86SKONRAD Frederic static void virtio_net_class_init(ObjectClass *klass, void *data)
287017ec5a86SKONRAD Frederic {
287117ec5a86SKONRAD Frederic     DeviceClass *dc = DEVICE_CLASS(klass);
287217ec5a86SKONRAD Frederic     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
2873e6f746b3SAndreas Färber 
287417ec5a86SKONRAD Frederic     dc->props = virtio_net_properties;
2875290c2428SDr. David Alan Gilbert     dc->vmsd = &vmstate_virtio_net;
2876125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
2877e6f746b3SAndreas Färber     vdc->realize = virtio_net_device_realize;
2878306ec6c3SAndreas Färber     vdc->unrealize = virtio_net_device_unrealize;
287917ec5a86SKONRAD Frederic     vdc->get_config = virtio_net_get_config;
288017ec5a86SKONRAD Frederic     vdc->set_config = virtio_net_set_config;
288117ec5a86SKONRAD Frederic     vdc->get_features = virtio_net_get_features;
288217ec5a86SKONRAD Frederic     vdc->set_features = virtio_net_set_features;
288317ec5a86SKONRAD Frederic     vdc->bad_features = virtio_net_bad_features;
288417ec5a86SKONRAD Frederic     vdc->reset = virtio_net_reset;
288517ec5a86SKONRAD Frederic     vdc->set_status = virtio_net_set_status;
288617ec5a86SKONRAD Frederic     vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
288717ec5a86SKONRAD Frederic     vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
28882a083ffdSMichael S. Tsirkin     vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO);
2889982b78c5SDr. David Alan Gilbert     vdc->vmsd = &vmstate_virtio_net_device;
289017ec5a86SKONRAD Frederic }
289117ec5a86SKONRAD Frederic 
289217ec5a86SKONRAD Frederic static const TypeInfo virtio_net_info = {
289317ec5a86SKONRAD Frederic     .name = TYPE_VIRTIO_NET,
289417ec5a86SKONRAD Frederic     .parent = TYPE_VIRTIO_DEVICE,
289517ec5a86SKONRAD Frederic     .instance_size = sizeof(VirtIONet),
289617ec5a86SKONRAD Frederic     .instance_init = virtio_net_instance_init,
289717ec5a86SKONRAD Frederic     .class_init = virtio_net_class_init,
289817ec5a86SKONRAD Frederic };
289917ec5a86SKONRAD Frederic 
290017ec5a86SKONRAD Frederic static void virtio_register_types(void)
290117ec5a86SKONRAD Frederic {
290217ec5a86SKONRAD Frederic     type_register_static(&virtio_net_info);
290317ec5a86SKONRAD Frederic }
290417ec5a86SKONRAD Frederic 
290517ec5a86SKONRAD Frederic type_init(virtio_register_types)
2906