xref: /openbmc/qemu/hw/net/virtio-net.c (revision 9d8c6a258c70d8ff494489140a4fcb3a965909b2)
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"
24*9d8c6a25SDr. David Alan Gilbert #include "net/announce.h"
2517ec5a86SKONRAD Frederic #include "hw/virtio/virtio-bus.h"
26e688df6bSMarkus Armbruster #include "qapi/error.h"
279af23989SMarkus Armbruster #include "qapi/qapi-events-net.h"
281399c60dSRusty Russell #include "hw/virtio/virtio-access.h"
29f8d806c9SJuan Quintela #include "migration/misc.h"
309473939eSJason Baron #include "standard-headers/linux/ethtool.h"
31*9d8c6a25SDr. David Alan Gilbert #include "trace.h"
326e790746SPaolo Bonzini 
336e790746SPaolo Bonzini #define VIRTIO_NET_VM_VERSION    11
346e790746SPaolo Bonzini 
356e790746SPaolo Bonzini #define MAC_TABLE_ENTRIES    64
366e790746SPaolo Bonzini #define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
376e790746SPaolo Bonzini 
381c0fbfa3SMichael S. Tsirkin /* previously fixed value */
391c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256
409b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256
419b02e161SWei Wang 
421c0fbfa3SMichael S. Tsirkin /* for now, only allow larger queues; with virtio-1, guest can downsize */
431c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE
449b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE
451c0fbfa3SMichael S. Tsirkin 
462974e916SYuri Benditovich #define VIRTIO_NET_IP4_ADDR_SIZE   8        /* ipv4 saddr + daddr */
472974e916SYuri Benditovich 
482974e916SYuri Benditovich #define VIRTIO_NET_TCP_FLAG         0x3F
492974e916SYuri Benditovich #define VIRTIO_NET_TCP_HDR_LENGTH   0xF000
502974e916SYuri Benditovich 
512974e916SYuri Benditovich /* IPv4 max payload, 16 bits in the header */
522974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP4_PAYLOAD (65535 - sizeof(struct ip_header))
532974e916SYuri Benditovich #define VIRTIO_NET_MAX_TCP_PAYLOAD 65535
542974e916SYuri Benditovich 
552974e916SYuri Benditovich /* header length value in ip header without option */
562974e916SYuri Benditovich #define VIRTIO_NET_IP4_HEADER_LENGTH 5
572974e916SYuri Benditovich 
582974e916SYuri Benditovich #define VIRTIO_NET_IP6_ADDR_SIZE   32      /* ipv6 saddr + daddr */
592974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP6_PAYLOAD VIRTIO_NET_MAX_TCP_PAYLOAD
602974e916SYuri Benditovich 
612974e916SYuri Benditovich /* Purge coalesced packets timer interval, This value affects the performance
622974e916SYuri Benditovich    a lot, and should be tuned carefully, '300000'(300us) is the recommended
632974e916SYuri Benditovich    value to pass the WHQL test, '50000' can gain 2x netperf throughput with
642974e916SYuri Benditovich    tso/gso/gro 'off'. */
652974e916SYuri Benditovich #define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000
662974e916SYuri Benditovich 
672974e916SYuri Benditovich /* temporary until standard header include it */
682974e916SYuri Benditovich #if !defined(VIRTIO_NET_HDR_F_RSC_INFO)
692974e916SYuri Benditovich 
702974e916SYuri Benditovich #define VIRTIO_NET_HDR_F_RSC_INFO  4 /* rsc_ext data in csum_ fields */
71d47e5e31SYuri Benditovich #define VIRTIO_NET_F_RSC_EXT       61
722974e916SYuri Benditovich 
732974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_packets(
742974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
752974e916SYuri Benditovich {
762974e916SYuri Benditovich     return &hdr->csum_start;
772974e916SYuri Benditovich }
782974e916SYuri Benditovich 
792974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
802974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
812974e916SYuri Benditovich {
822974e916SYuri Benditovich     return &hdr->csum_offset;
832974e916SYuri Benditovich }
842974e916SYuri Benditovich 
852974e916SYuri Benditovich #endif
862974e916SYuri Benditovich 
876e790746SPaolo Bonzini static VirtIOFeature feature_sizes[] = {
88127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MAC,
89ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, mac)},
90127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_STATUS,
91ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, status)},
92127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MQ,
93ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, max_virtqueue_pairs)},
94127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MTU,
95ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, mtu)},
969473939eSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
97ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, duplex)},
986e790746SPaolo Bonzini     {}
996e790746SPaolo Bonzini };
1006e790746SPaolo Bonzini 
1016e790746SPaolo Bonzini static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc)
1026e790746SPaolo Bonzini {
1036e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
1046e790746SPaolo Bonzini 
1056e790746SPaolo Bonzini     return &n->vqs[nc->queue_index];
1066e790746SPaolo Bonzini }
1076e790746SPaolo Bonzini 
1086e790746SPaolo Bonzini static int vq2q(int queue_index)
1096e790746SPaolo Bonzini {
1106e790746SPaolo Bonzini     return queue_index / 2;
1116e790746SPaolo Bonzini }
1126e790746SPaolo Bonzini 
1136e790746SPaolo Bonzini /* TODO
1146e790746SPaolo Bonzini  * - we could suppress RX interrupt if we were so inclined.
1156e790746SPaolo Bonzini  */
1166e790746SPaolo Bonzini 
1176e790746SPaolo Bonzini static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
1186e790746SPaolo Bonzini {
11917a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1206e790746SPaolo Bonzini     struct virtio_net_config netcfg;
1216e790746SPaolo Bonzini 
1221399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.status, n->status);
1231399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues);
124a93e599dSMaxime Coquelin     virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu);
1256e790746SPaolo Bonzini     memcpy(netcfg.mac, n->mac, ETH_ALEN);
1269473939eSJason Baron     virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed);
1279473939eSJason Baron     netcfg.duplex = n->net_conf.duplex;
1286e790746SPaolo Bonzini     memcpy(config, &netcfg, n->config_size);
1296e790746SPaolo Bonzini }
1306e790746SPaolo Bonzini 
1316e790746SPaolo Bonzini static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
1326e790746SPaolo Bonzini {
13317a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1346e790746SPaolo Bonzini     struct virtio_net_config netcfg = {};
1356e790746SPaolo Bonzini 
1366e790746SPaolo Bonzini     memcpy(&netcfg, config, n->config_size);
1376e790746SPaolo Bonzini 
13895129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
13995129d6fSCornelia Huck         !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) &&
1406e790746SPaolo Bonzini         memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
1416e790746SPaolo Bonzini         memcpy(n->mac, netcfg.mac, ETH_ALEN);
1426e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
1436e790746SPaolo Bonzini     }
1446e790746SPaolo Bonzini }
1456e790746SPaolo Bonzini 
1466e790746SPaolo Bonzini static bool virtio_net_started(VirtIONet *n, uint8_t status)
1476e790746SPaolo Bonzini {
14817a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1496e790746SPaolo Bonzini     return (status & VIRTIO_CONFIG_S_DRIVER_OK) &&
15017a0ca55SKONRAD Frederic         (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running;
1516e790746SPaolo Bonzini }
1526e790746SPaolo Bonzini 
153f57fcf70SJason Wang static void virtio_net_announce_timer(void *opaque)
154f57fcf70SJason Wang {
155f57fcf70SJason Wang     VirtIONet *n = opaque;
156f57fcf70SJason Wang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
157*9d8c6a25SDr. David Alan Gilbert     trace_virtio_net_announce_timer(n->announce_timer.round);
158f57fcf70SJason Wang 
159*9d8c6a25SDr. David Alan Gilbert     n->announce_timer.round--;
160f57fcf70SJason Wang     n->status |= VIRTIO_NET_S_ANNOUNCE;
161f57fcf70SJason Wang     virtio_notify_config(vdev);
162f57fcf70SJason Wang }
163f57fcf70SJason Wang 
1646e790746SPaolo Bonzini static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
1656e790746SPaolo Bonzini {
16617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1676e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
1686e790746SPaolo Bonzini     int queues = n->multiqueue ? n->max_queues : 1;
1696e790746SPaolo Bonzini 
170ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
1716e790746SPaolo Bonzini         return;
1726e790746SPaolo Bonzini     }
1736e790746SPaolo Bonzini 
1748c1ac475SRadim Krčmář     if ((virtio_net_started(n, status) && !nc->peer->link_down) ==
1758c1ac475SRadim Krčmář         !!n->vhost_started) {
1766e790746SPaolo Bonzini         return;
1776e790746SPaolo Bonzini     }
1786e790746SPaolo Bonzini     if (!n->vhost_started) {
179086abc1cSMichael S. Tsirkin         int r, i;
180086abc1cSMichael S. Tsirkin 
1811bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
1821bfa316cSGreg Kurz             error_report("backend does not support %s vnet headers; "
1831bfa316cSGreg Kurz                          "falling back on userspace virtio",
1841bfa316cSGreg Kurz                          virtio_is_big_endian(vdev) ? "BE" : "LE");
1851bfa316cSGreg Kurz             return;
1861bfa316cSGreg Kurz         }
1871bfa316cSGreg Kurz 
188086abc1cSMichael S. Tsirkin         /* Any packets outstanding? Purge them to avoid touching rings
189086abc1cSMichael S. Tsirkin          * when vhost is running.
190086abc1cSMichael S. Tsirkin          */
191086abc1cSMichael S. Tsirkin         for (i = 0;  i < queues; i++) {
192086abc1cSMichael S. Tsirkin             NetClientState *qnc = qemu_get_subqueue(n->nic, i);
193086abc1cSMichael S. Tsirkin 
194086abc1cSMichael S. Tsirkin             /* Purge both directions: TX and RX. */
195086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->peer->incoming_queue, qnc);
196086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->incoming_queue, qnc->peer);
197086abc1cSMichael S. Tsirkin         }
198086abc1cSMichael S. Tsirkin 
199a93e599dSMaxime Coquelin         if (virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MTU)) {
200a93e599dSMaxime Coquelin             r = vhost_net_set_mtu(get_vhost_net(nc->peer), n->net_conf.mtu);
201a93e599dSMaxime Coquelin             if (r < 0) {
202a93e599dSMaxime Coquelin                 error_report("%uBytes MTU not supported by the backend",
203a93e599dSMaxime Coquelin                              n->net_conf.mtu);
204a93e599dSMaxime Coquelin 
205a93e599dSMaxime Coquelin                 return;
206a93e599dSMaxime Coquelin             }
207a93e599dSMaxime Coquelin         }
208a93e599dSMaxime Coquelin 
2096e790746SPaolo Bonzini         n->vhost_started = 1;
21017a0ca55SKONRAD Frederic         r = vhost_net_start(vdev, n->nic->ncs, queues);
2116e790746SPaolo Bonzini         if (r < 0) {
2126e790746SPaolo Bonzini             error_report("unable to start vhost net: %d: "
2136e790746SPaolo Bonzini                          "falling back on userspace virtio", -r);
2146e790746SPaolo Bonzini             n->vhost_started = 0;
2156e790746SPaolo Bonzini         }
2166e790746SPaolo Bonzini     } else {
21717a0ca55SKONRAD Frederic         vhost_net_stop(vdev, n->nic->ncs, queues);
2186e790746SPaolo Bonzini         n->vhost_started = 0;
2196e790746SPaolo Bonzini     }
2206e790746SPaolo Bonzini }
2216e790746SPaolo Bonzini 
2221bfa316cSGreg Kurz static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev,
2231bfa316cSGreg Kurz                                           NetClientState *peer,
2241bfa316cSGreg Kurz                                           bool enable)
2251bfa316cSGreg Kurz {
2261bfa316cSGreg Kurz     if (virtio_is_big_endian(vdev)) {
2271bfa316cSGreg Kurz         return qemu_set_vnet_be(peer, enable);
2281bfa316cSGreg Kurz     } else {
2291bfa316cSGreg Kurz         return qemu_set_vnet_le(peer, enable);
2301bfa316cSGreg Kurz     }
2311bfa316cSGreg Kurz }
2321bfa316cSGreg Kurz 
2331bfa316cSGreg Kurz static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs,
2341bfa316cSGreg Kurz                                        int queues, bool enable)
2351bfa316cSGreg Kurz {
2361bfa316cSGreg Kurz     int i;
2371bfa316cSGreg Kurz 
2381bfa316cSGreg Kurz     for (i = 0; i < queues; i++) {
2391bfa316cSGreg Kurz         if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 &&
2401bfa316cSGreg Kurz             enable) {
2411bfa316cSGreg Kurz             while (--i >= 0) {
2421bfa316cSGreg Kurz                 virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false);
2431bfa316cSGreg Kurz             }
2441bfa316cSGreg Kurz 
2451bfa316cSGreg Kurz             return true;
2461bfa316cSGreg Kurz         }
2471bfa316cSGreg Kurz     }
2481bfa316cSGreg Kurz 
2491bfa316cSGreg Kurz     return false;
2501bfa316cSGreg Kurz }
2511bfa316cSGreg Kurz 
2521bfa316cSGreg Kurz static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status)
2531bfa316cSGreg Kurz {
2541bfa316cSGreg Kurz     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2551bfa316cSGreg Kurz     int queues = n->multiqueue ? n->max_queues : 1;
2561bfa316cSGreg Kurz 
2571bfa316cSGreg Kurz     if (virtio_net_started(n, status)) {
2581bfa316cSGreg Kurz         /* Before using the device, we tell the network backend about the
2591bfa316cSGreg Kurz          * endianness to use when parsing vnet headers. If the backend
2601bfa316cSGreg Kurz          * can't do it, we fallback onto fixing the headers in the core
2611bfa316cSGreg Kurz          * virtio-net code.
2621bfa316cSGreg Kurz          */
2631bfa316cSGreg Kurz         n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs,
2641bfa316cSGreg Kurz                                                             queues, true);
2651bfa316cSGreg Kurz     } else if (virtio_net_started(n, vdev->status)) {
2661bfa316cSGreg Kurz         /* After using the device, we need to reset the network backend to
2671bfa316cSGreg Kurz          * the default (guest native endianness), otherwise the guest may
2681bfa316cSGreg Kurz          * lose network connectivity if it is rebooted into a different
2691bfa316cSGreg Kurz          * endianness.
2701bfa316cSGreg Kurz          */
2711bfa316cSGreg Kurz         virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false);
2721bfa316cSGreg Kurz     }
2731bfa316cSGreg Kurz }
2741bfa316cSGreg Kurz 
275283e2c2aSYuri Benditovich static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq)
276283e2c2aSYuri Benditovich {
277283e2c2aSYuri Benditovich     unsigned int dropped = virtqueue_drop_all(vq);
278283e2c2aSYuri Benditovich     if (dropped) {
279283e2c2aSYuri Benditovich         virtio_notify(vdev, vq);
280283e2c2aSYuri Benditovich     }
281283e2c2aSYuri Benditovich }
282283e2c2aSYuri Benditovich 
2836e790746SPaolo Bonzini static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
2846e790746SPaolo Bonzini {
28517a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
2866e790746SPaolo Bonzini     VirtIONetQueue *q;
2876e790746SPaolo Bonzini     int i;
2886e790746SPaolo Bonzini     uint8_t queue_status;
2896e790746SPaolo Bonzini 
2901bfa316cSGreg Kurz     virtio_net_vnet_endian_status(n, status);
2916e790746SPaolo Bonzini     virtio_net_vhost_status(n, status);
2926e790746SPaolo Bonzini 
2936e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
29438705bb5SFam Zheng         NetClientState *ncs = qemu_get_subqueue(n->nic, i);
29538705bb5SFam Zheng         bool queue_started;
2966e790746SPaolo Bonzini         q = &n->vqs[i];
2976e790746SPaolo Bonzini 
2986e790746SPaolo Bonzini         if ((!n->multiqueue && i != 0) || i >= n->curr_queues) {
2996e790746SPaolo Bonzini             queue_status = 0;
3006e790746SPaolo Bonzini         } else {
3016e790746SPaolo Bonzini             queue_status = status;
3026e790746SPaolo Bonzini         }
30338705bb5SFam Zheng         queue_started =
30438705bb5SFam Zheng             virtio_net_started(n, queue_status) && !n->vhost_started;
30538705bb5SFam Zheng 
30638705bb5SFam Zheng         if (queue_started) {
30738705bb5SFam Zheng             qemu_flush_queued_packets(ncs);
30838705bb5SFam Zheng         }
3096e790746SPaolo Bonzini 
3106e790746SPaolo Bonzini         if (!q->tx_waiting) {
3116e790746SPaolo Bonzini             continue;
3126e790746SPaolo Bonzini         }
3136e790746SPaolo Bonzini 
31438705bb5SFam Zheng         if (queue_started) {
3156e790746SPaolo Bonzini             if (q->tx_timer) {
316bc72ad67SAlex Bligh                 timer_mod(q->tx_timer,
317bc72ad67SAlex Bligh                                qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
3186e790746SPaolo Bonzini             } else {
3196e790746SPaolo Bonzini                 qemu_bh_schedule(q->tx_bh);
3206e790746SPaolo Bonzini             }
3216e790746SPaolo Bonzini         } else {
3226e790746SPaolo Bonzini             if (q->tx_timer) {
323bc72ad67SAlex Bligh                 timer_del(q->tx_timer);
3246e790746SPaolo Bonzini             } else {
3256e790746SPaolo Bonzini                 qemu_bh_cancel(q->tx_bh);
3266e790746SPaolo Bonzini             }
327283e2c2aSYuri Benditovich             if ((n->status & VIRTIO_NET_S_LINK_UP) == 0 &&
32870e53e6eSJason Wang                 (queue_status & VIRTIO_CONFIG_S_DRIVER_OK) &&
32970e53e6eSJason Wang                 vdev->vm_running) {
330283e2c2aSYuri Benditovich                 /* if tx is waiting we are likely have some packets in tx queue
331283e2c2aSYuri Benditovich                  * and disabled notification */
332283e2c2aSYuri Benditovich                 q->tx_waiting = 0;
333283e2c2aSYuri Benditovich                 virtio_queue_set_notification(q->tx_vq, 1);
334283e2c2aSYuri Benditovich                 virtio_net_drop_tx_queue_data(vdev, q->tx_vq);
335283e2c2aSYuri Benditovich             }
3366e790746SPaolo Bonzini         }
3376e790746SPaolo Bonzini     }
3386e790746SPaolo Bonzini }
3396e790746SPaolo Bonzini 
3406e790746SPaolo Bonzini static void virtio_net_set_link_status(NetClientState *nc)
3416e790746SPaolo Bonzini {
3426e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
34317a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
3446e790746SPaolo Bonzini     uint16_t old_status = n->status;
3456e790746SPaolo Bonzini 
3466e790746SPaolo Bonzini     if (nc->link_down)
3476e790746SPaolo Bonzini         n->status &= ~VIRTIO_NET_S_LINK_UP;
3486e790746SPaolo Bonzini     else
3496e790746SPaolo Bonzini         n->status |= VIRTIO_NET_S_LINK_UP;
3506e790746SPaolo Bonzini 
3516e790746SPaolo Bonzini     if (n->status != old_status)
35217a0ca55SKONRAD Frederic         virtio_notify_config(vdev);
3536e790746SPaolo Bonzini 
35417a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
3556e790746SPaolo Bonzini }
3566e790746SPaolo Bonzini 
357b1be4280SAmos Kong static void rxfilter_notify(NetClientState *nc)
358b1be4280SAmos Kong {
359b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
360b1be4280SAmos Kong 
361b1be4280SAmos Kong     if (nc->rxfilter_notify_enabled) {
36296e35046SAmos Kong         gchar *path = object_get_canonical_path(OBJECT(n->qdev));
36306150279SWenchao Xia         qapi_event_send_nic_rx_filter_changed(!!n->netclient_name,
3643ab72385SPeter Xu                                               n->netclient_name, path);
36596e35046SAmos Kong         g_free(path);
366b1be4280SAmos Kong 
367b1be4280SAmos Kong         /* disable event notification to avoid events flooding */
368b1be4280SAmos Kong         nc->rxfilter_notify_enabled = 0;
369b1be4280SAmos Kong     }
370b1be4280SAmos Kong }
371b1be4280SAmos Kong 
372f7bc8ef8SAmos Kong static intList *get_vlan_table(VirtIONet *n)
373f7bc8ef8SAmos Kong {
374f7bc8ef8SAmos Kong     intList *list, *entry;
375f7bc8ef8SAmos Kong     int i, j;
376f7bc8ef8SAmos Kong 
377f7bc8ef8SAmos Kong     list = NULL;
378f7bc8ef8SAmos Kong     for (i = 0; i < MAX_VLAN >> 5; i++) {
379f7bc8ef8SAmos Kong         for (j = 0; n->vlans[i] && j <= 0x1f; j++) {
380f7bc8ef8SAmos Kong             if (n->vlans[i] & (1U << j)) {
381f7bc8ef8SAmos Kong                 entry = g_malloc0(sizeof(*entry));
382f7bc8ef8SAmos Kong                 entry->value = (i << 5) + j;
383f7bc8ef8SAmos Kong                 entry->next = list;
384f7bc8ef8SAmos Kong                 list = entry;
385f7bc8ef8SAmos Kong             }
386f7bc8ef8SAmos Kong         }
387f7bc8ef8SAmos Kong     }
388f7bc8ef8SAmos Kong 
389f7bc8ef8SAmos Kong     return list;
390f7bc8ef8SAmos Kong }
391f7bc8ef8SAmos Kong 
392b1be4280SAmos Kong static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
393b1be4280SAmos Kong {
394b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
395f7bc8ef8SAmos Kong     VirtIODevice *vdev = VIRTIO_DEVICE(n);
396b1be4280SAmos Kong     RxFilterInfo *info;
397b1be4280SAmos Kong     strList *str_list, *entry;
398f7bc8ef8SAmos Kong     int i;
399b1be4280SAmos Kong 
400b1be4280SAmos Kong     info = g_malloc0(sizeof(*info));
401b1be4280SAmos Kong     info->name = g_strdup(nc->name);
402b1be4280SAmos Kong     info->promiscuous = n->promisc;
403b1be4280SAmos Kong 
404b1be4280SAmos Kong     if (n->nouni) {
405b1be4280SAmos Kong         info->unicast = RX_STATE_NONE;
406b1be4280SAmos Kong     } else if (n->alluni) {
407b1be4280SAmos Kong         info->unicast = RX_STATE_ALL;
408b1be4280SAmos Kong     } else {
409b1be4280SAmos Kong         info->unicast = RX_STATE_NORMAL;
410b1be4280SAmos Kong     }
411b1be4280SAmos Kong 
412b1be4280SAmos Kong     if (n->nomulti) {
413b1be4280SAmos Kong         info->multicast = RX_STATE_NONE;
414b1be4280SAmos Kong     } else if (n->allmulti) {
415b1be4280SAmos Kong         info->multicast = RX_STATE_ALL;
416b1be4280SAmos Kong     } else {
417b1be4280SAmos Kong         info->multicast = RX_STATE_NORMAL;
418b1be4280SAmos Kong     }
419b1be4280SAmos Kong 
420b1be4280SAmos Kong     info->broadcast_allowed = n->nobcast;
421b1be4280SAmos Kong     info->multicast_overflow = n->mac_table.multi_overflow;
422b1be4280SAmos Kong     info->unicast_overflow = n->mac_table.uni_overflow;
423b1be4280SAmos Kong 
424b0575ba4SScott Feldman     info->main_mac = qemu_mac_strdup_printf(n->mac);
425b1be4280SAmos Kong 
426b1be4280SAmos Kong     str_list = NULL;
427b1be4280SAmos Kong     for (i = 0; i < n->mac_table.first_multi; i++) {
428b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
429b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
430b1be4280SAmos Kong         entry->next = str_list;
431b1be4280SAmos Kong         str_list = entry;
432b1be4280SAmos Kong     }
433b1be4280SAmos Kong     info->unicast_table = str_list;
434b1be4280SAmos Kong 
435b1be4280SAmos Kong     str_list = NULL;
436b1be4280SAmos Kong     for (i = n->mac_table.first_multi; i < n->mac_table.in_use; 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->multicast_table = str_list;
443f7bc8ef8SAmos Kong     info->vlan_table = get_vlan_table(n);
444b1be4280SAmos Kong 
44595129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) {
446f7bc8ef8SAmos Kong         info->vlan = RX_STATE_ALL;
447f7bc8ef8SAmos Kong     } else if (!info->vlan_table) {
448f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NONE;
449f7bc8ef8SAmos Kong     } else {
450f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NORMAL;
451b1be4280SAmos Kong     }
452b1be4280SAmos Kong 
453b1be4280SAmos Kong     /* enable event notification after query */
454b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
455b1be4280SAmos Kong 
456b1be4280SAmos Kong     return info;
457b1be4280SAmos Kong }
458b1be4280SAmos Kong 
4596e790746SPaolo Bonzini static void virtio_net_reset(VirtIODevice *vdev)
4606e790746SPaolo Bonzini {
46117a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
46294b52958SGreg Kurz     int i;
4636e790746SPaolo Bonzini 
4646e790746SPaolo Bonzini     /* Reset back to compatibility mode */
4656e790746SPaolo Bonzini     n->promisc = 1;
4666e790746SPaolo Bonzini     n->allmulti = 0;
4676e790746SPaolo Bonzini     n->alluni = 0;
4686e790746SPaolo Bonzini     n->nomulti = 0;
4696e790746SPaolo Bonzini     n->nouni = 0;
4706e790746SPaolo Bonzini     n->nobcast = 0;
4716e790746SPaolo Bonzini     /* multiqueue is disabled by default */
4726e790746SPaolo Bonzini     n->curr_queues = 1;
473*9d8c6a25SDr. David Alan Gilbert     timer_del(n->announce_timer.tm);
474*9d8c6a25SDr. David Alan Gilbert     n->announce_timer.round = 0;
475f57fcf70SJason Wang     n->status &= ~VIRTIO_NET_S_ANNOUNCE;
4766e790746SPaolo Bonzini 
4776e790746SPaolo Bonzini     /* Flush any MAC and VLAN filter table state */
4786e790746SPaolo Bonzini     n->mac_table.in_use = 0;
4796e790746SPaolo Bonzini     n->mac_table.first_multi = 0;
4806e790746SPaolo Bonzini     n->mac_table.multi_overflow = 0;
4816e790746SPaolo Bonzini     n->mac_table.uni_overflow = 0;
4826e790746SPaolo Bonzini     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
4836e790746SPaolo Bonzini     memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac));
484702d66a8SMichael S. Tsirkin     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
4856e790746SPaolo Bonzini     memset(n->vlans, 0, MAX_VLAN >> 3);
48694b52958SGreg Kurz 
48794b52958SGreg Kurz     /* Flush any async TX */
48894b52958SGreg Kurz     for (i = 0;  i < n->max_queues; i++) {
48994b52958SGreg Kurz         NetClientState *nc = qemu_get_subqueue(n->nic, i);
49094b52958SGreg Kurz 
49194b52958SGreg Kurz         if (nc->peer) {
49294b52958SGreg Kurz             qemu_flush_or_purge_queued_packets(nc->peer, true);
49394b52958SGreg Kurz             assert(!virtio_net_get_subqueue(nc)->async_tx.elem);
49494b52958SGreg Kurz         }
49594b52958SGreg Kurz     }
4966e790746SPaolo Bonzini }
4976e790746SPaolo Bonzini 
4986e790746SPaolo Bonzini static void peer_test_vnet_hdr(VirtIONet *n)
4996e790746SPaolo Bonzini {
5006e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
5016e790746SPaolo Bonzini     if (!nc->peer) {
5026e790746SPaolo Bonzini         return;
5036e790746SPaolo Bonzini     }
5046e790746SPaolo Bonzini 
505d6085e3aSStefan Hajnoczi     n->has_vnet_hdr = qemu_has_vnet_hdr(nc->peer);
5066e790746SPaolo Bonzini }
5076e790746SPaolo Bonzini 
5086e790746SPaolo Bonzini static int peer_has_vnet_hdr(VirtIONet *n)
5096e790746SPaolo Bonzini {
5106e790746SPaolo Bonzini     return n->has_vnet_hdr;
5116e790746SPaolo Bonzini }
5126e790746SPaolo Bonzini 
5136e790746SPaolo Bonzini static int peer_has_ufo(VirtIONet *n)
5146e790746SPaolo Bonzini {
5156e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n))
5166e790746SPaolo Bonzini         return 0;
5176e790746SPaolo Bonzini 
518d6085e3aSStefan Hajnoczi     n->has_ufo = qemu_has_ufo(qemu_get_queue(n->nic)->peer);
5196e790746SPaolo Bonzini 
5206e790746SPaolo Bonzini     return n->has_ufo;
5216e790746SPaolo Bonzini }
5226e790746SPaolo Bonzini 
523bb9d17f8SCornelia Huck static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
524bb9d17f8SCornelia Huck                                        int version_1)
5256e790746SPaolo Bonzini {
5266e790746SPaolo Bonzini     int i;
5276e790746SPaolo Bonzini     NetClientState *nc;
5286e790746SPaolo Bonzini 
5296e790746SPaolo Bonzini     n->mergeable_rx_bufs = mergeable_rx_bufs;
5306e790746SPaolo Bonzini 
531bb9d17f8SCornelia Huck     if (version_1) {
532bb9d17f8SCornelia Huck         n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
533bb9d17f8SCornelia Huck     } else {
5346e790746SPaolo Bonzini         n->guest_hdr_len = n->mergeable_rx_bufs ?
535bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr_mrg_rxbuf) :
536bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr);
537bb9d17f8SCornelia Huck     }
5386e790746SPaolo Bonzini 
5396e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
5406e790746SPaolo Bonzini         nc = qemu_get_subqueue(n->nic, i);
5416e790746SPaolo Bonzini 
5426e790746SPaolo Bonzini         if (peer_has_vnet_hdr(n) &&
543d6085e3aSStefan Hajnoczi             qemu_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) {
544d6085e3aSStefan Hajnoczi             qemu_set_vnet_hdr_len(nc->peer, n->guest_hdr_len);
5456e790746SPaolo Bonzini             n->host_hdr_len = n->guest_hdr_len;
5466e790746SPaolo Bonzini         }
5476e790746SPaolo Bonzini     }
5486e790746SPaolo Bonzini }
5496e790746SPaolo Bonzini 
5502eef278bSMichael S. Tsirkin static int virtio_net_max_tx_queue_size(VirtIONet *n)
5512eef278bSMichael S. Tsirkin {
5522eef278bSMichael S. Tsirkin     NetClientState *peer = n->nic_conf.peers.ncs[0];
5532eef278bSMichael S. Tsirkin 
5542eef278bSMichael S. Tsirkin     /*
5552eef278bSMichael S. Tsirkin      * Backends other than vhost-user don't support max queue size.
5562eef278bSMichael S. Tsirkin      */
5572eef278bSMichael S. Tsirkin     if (!peer) {
5582eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
5592eef278bSMichael S. Tsirkin     }
5602eef278bSMichael S. Tsirkin 
5612eef278bSMichael S. Tsirkin     if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) {
5622eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
5632eef278bSMichael S. Tsirkin     }
5642eef278bSMichael S. Tsirkin 
5652eef278bSMichael S. Tsirkin     return VIRTQUEUE_MAX_SIZE;
5662eef278bSMichael S. Tsirkin }
5672eef278bSMichael S. Tsirkin 
5686e790746SPaolo Bonzini static int peer_attach(VirtIONet *n, int index)
5696e790746SPaolo Bonzini {
5706e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
5716e790746SPaolo Bonzini 
5726e790746SPaolo Bonzini     if (!nc->peer) {
5736e790746SPaolo Bonzini         return 0;
5746e790746SPaolo Bonzini     }
5756e790746SPaolo Bonzini 
576f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
5777263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 1);
5787263a0adSChangchun Ouyang     }
5797263a0adSChangchun Ouyang 
580f394b2e2SEric Blake     if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
5816e790746SPaolo Bonzini         return 0;
5826e790746SPaolo Bonzini     }
5836e790746SPaolo Bonzini 
5841074b879SJason Wang     if (n->max_queues == 1) {
5851074b879SJason Wang         return 0;
5861074b879SJason Wang     }
5871074b879SJason Wang 
5886e790746SPaolo Bonzini     return tap_enable(nc->peer);
5896e790746SPaolo Bonzini }
5906e790746SPaolo Bonzini 
5916e790746SPaolo Bonzini static int peer_detach(VirtIONet *n, int index)
5926e790746SPaolo Bonzini {
5936e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
5946e790746SPaolo Bonzini 
5956e790746SPaolo Bonzini     if (!nc->peer) {
5966e790746SPaolo Bonzini         return 0;
5976e790746SPaolo Bonzini     }
5986e790746SPaolo Bonzini 
599f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
6007263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 0);
6017263a0adSChangchun Ouyang     }
6027263a0adSChangchun Ouyang 
603f394b2e2SEric Blake     if (nc->peer->info->type !=  NET_CLIENT_DRIVER_TAP) {
6046e790746SPaolo Bonzini         return 0;
6056e790746SPaolo Bonzini     }
6066e790746SPaolo Bonzini 
6076e790746SPaolo Bonzini     return tap_disable(nc->peer);
6086e790746SPaolo Bonzini }
6096e790746SPaolo Bonzini 
6106e790746SPaolo Bonzini static void virtio_net_set_queues(VirtIONet *n)
6116e790746SPaolo Bonzini {
6126e790746SPaolo Bonzini     int i;
613ddfa83eaSJoel Stanley     int r;
6146e790746SPaolo Bonzini 
61568b5f314SYuri Benditovich     if (n->nic->peer_deleted) {
61668b5f314SYuri Benditovich         return;
61768b5f314SYuri Benditovich     }
61868b5f314SYuri Benditovich 
6196e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
6206e790746SPaolo Bonzini         if (i < n->curr_queues) {
621ddfa83eaSJoel Stanley             r = peer_attach(n, i);
622ddfa83eaSJoel Stanley             assert(!r);
6236e790746SPaolo Bonzini         } else {
624ddfa83eaSJoel Stanley             r = peer_detach(n, i);
625ddfa83eaSJoel Stanley             assert(!r);
6266e790746SPaolo Bonzini         }
6276e790746SPaolo Bonzini     }
6286e790746SPaolo Bonzini }
6296e790746SPaolo Bonzini 
630ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue);
6316e790746SPaolo Bonzini 
6329d5b731dSJason Wang static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
6339d5b731dSJason Wang                                         Error **errp)
6346e790746SPaolo Bonzini {
63517a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
6366e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
6376e790746SPaolo Bonzini 
638da3e8a23SShannon Zhao     /* Firstly sync all virtio-net possible supported features */
639da3e8a23SShannon Zhao     features |= n->host_features;
640da3e8a23SShannon Zhao 
6410cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
6426e790746SPaolo Bonzini 
6436e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n)) {
6440cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_CSUM);
6450cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4);
6460cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6);
6470cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN);
6486e790746SPaolo Bonzini 
6490cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM);
6500cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4);
6510cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6);
6520cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN);
6536e790746SPaolo Bonzini     }
6546e790746SPaolo Bonzini 
6556e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) {
6560cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO);
6570cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO);
6586e790746SPaolo Bonzini     }
6596e790746SPaolo Bonzini 
660ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
6616e790746SPaolo Bonzini         return features;
6626e790746SPaolo Bonzini     }
6632974e916SYuri Benditovich 
66475ebec11SMaxime Coquelin     features = vhost_net_get_features(get_vhost_net(nc->peer), features);
66575ebec11SMaxime Coquelin     vdev->backend_features = features;
66675ebec11SMaxime Coquelin 
66775ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
66875ebec11SMaxime Coquelin             (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) {
66975ebec11SMaxime Coquelin         features |= (1ULL << VIRTIO_NET_F_MTU);
67075ebec11SMaxime Coquelin     }
67175ebec11SMaxime Coquelin 
67275ebec11SMaxime Coquelin     return features;
6736e790746SPaolo Bonzini }
6746e790746SPaolo Bonzini 
675019a3edbSGerd Hoffmann static uint64_t virtio_net_bad_features(VirtIODevice *vdev)
6766e790746SPaolo Bonzini {
677019a3edbSGerd Hoffmann     uint64_t features = 0;
6786e790746SPaolo Bonzini 
6796e790746SPaolo Bonzini     /* Linux kernel 2.6.25.  It understood MAC (as everyone must),
6806e790746SPaolo Bonzini      * but also these: */
6810cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
6820cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_CSUM);
6830cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO4);
6840cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO6);
6850cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_ECN);
6866e790746SPaolo Bonzini 
6876e790746SPaolo Bonzini     return features;
6886e790746SPaolo Bonzini }
6896e790746SPaolo Bonzini 
690644c9858SDmitry Fleytman static void virtio_net_apply_guest_offloads(VirtIONet *n)
691644c9858SDmitry Fleytman {
692ad37bb3bSStefan Hajnoczi     qemu_set_offload(qemu_get_queue(n->nic)->peer,
693644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)),
694644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)),
695644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)),
696644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)),
697644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)));
698644c9858SDmitry Fleytman }
699644c9858SDmitry Fleytman 
700644c9858SDmitry Fleytman static uint64_t virtio_net_guest_offloads_by_features(uint32_t features)
701644c9858SDmitry Fleytman {
702644c9858SDmitry Fleytman     static const uint64_t guest_offloads_mask =
703644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_CSUM) |
704644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO4) |
705644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO6) |
706644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_ECN)  |
707644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_UFO);
708644c9858SDmitry Fleytman 
709644c9858SDmitry Fleytman     return guest_offloads_mask & features;
710644c9858SDmitry Fleytman }
711644c9858SDmitry Fleytman 
712644c9858SDmitry Fleytman static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n)
713644c9858SDmitry Fleytman {
714644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
715644c9858SDmitry Fleytman     return virtio_net_guest_offloads_by_features(vdev->guest_features);
716644c9858SDmitry Fleytman }
717644c9858SDmitry Fleytman 
718d5aaa1b0SGerd Hoffmann static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
7196e790746SPaolo Bonzini {
72017a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
7216e790746SPaolo Bonzini     int i;
7226e790746SPaolo Bonzini 
72375ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
72475ebec11SMaxime Coquelin             !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) {
72575ebec11SMaxime Coquelin         features &= ~(1ULL << VIRTIO_NET_F_MTU);
72675ebec11SMaxime Coquelin     }
72775ebec11SMaxime Coquelin 
728ef546f12SCornelia Huck     virtio_net_set_multiqueue(n,
72995129d6fSCornelia Huck                               virtio_has_feature(features, VIRTIO_NET_F_MQ));
7306e790746SPaolo Bonzini 
731ef546f12SCornelia Huck     virtio_net_set_mrg_rx_bufs(n,
73295129d6fSCornelia Huck                                virtio_has_feature(features,
733bb9d17f8SCornelia Huck                                                   VIRTIO_NET_F_MRG_RXBUF),
73495129d6fSCornelia Huck                                virtio_has_feature(features,
735bb9d17f8SCornelia Huck                                                   VIRTIO_F_VERSION_1));
7366e790746SPaolo Bonzini 
7372974e916SYuri Benditovich     n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
7382974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4);
7392974e916SYuri Benditovich     n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
7402974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6);
7412974e916SYuri Benditovich 
7426e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
743644c9858SDmitry Fleytman         n->curr_guest_offloads =
744644c9858SDmitry Fleytman             virtio_net_guest_offloads_by_features(features);
745644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
7466e790746SPaolo Bonzini     }
7476e790746SPaolo Bonzini 
7486e790746SPaolo Bonzini     for (i = 0;  i < n->max_queues; i++) {
7496e790746SPaolo Bonzini         NetClientState *nc = qemu_get_subqueue(n->nic, i);
7506e790746SPaolo Bonzini 
751ed8b4afeSNikolay Nikolaev         if (!get_vhost_net(nc->peer)) {
7526e790746SPaolo Bonzini             continue;
7536e790746SPaolo Bonzini         }
754ed8b4afeSNikolay Nikolaev         vhost_net_ack_features(get_vhost_net(nc->peer), features);
7556e790746SPaolo Bonzini     }
7560b1eaa88SStefan Fritsch 
75795129d6fSCornelia Huck     if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
7580b1eaa88SStefan Fritsch         memset(n->vlans, 0, MAX_VLAN >> 3);
7590b1eaa88SStefan Fritsch     } else {
7600b1eaa88SStefan Fritsch         memset(n->vlans, 0xff, MAX_VLAN >> 3);
7610b1eaa88SStefan Fritsch     }
7626e790746SPaolo Bonzini }
7636e790746SPaolo Bonzini 
7646e790746SPaolo Bonzini static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
7656e790746SPaolo Bonzini                                      struct iovec *iov, unsigned int iov_cnt)
7666e790746SPaolo Bonzini {
7676e790746SPaolo Bonzini     uint8_t on;
7686e790746SPaolo Bonzini     size_t s;
769b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
7706e790746SPaolo Bonzini 
7716e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on));
7726e790746SPaolo Bonzini     if (s != sizeof(on)) {
7736e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
7746e790746SPaolo Bonzini     }
7756e790746SPaolo Bonzini 
7766e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) {
7776e790746SPaolo Bonzini         n->promisc = on;
7786e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) {
7796e790746SPaolo Bonzini         n->allmulti = on;
7806e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) {
7816e790746SPaolo Bonzini         n->alluni = on;
7826e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) {
7836e790746SPaolo Bonzini         n->nomulti = on;
7846e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) {
7856e790746SPaolo Bonzini         n->nouni = on;
7866e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) {
7876e790746SPaolo Bonzini         n->nobcast = on;
7886e790746SPaolo Bonzini     } else {
7896e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
7906e790746SPaolo Bonzini     }
7916e790746SPaolo Bonzini 
792b1be4280SAmos Kong     rxfilter_notify(nc);
793b1be4280SAmos Kong 
7946e790746SPaolo Bonzini     return VIRTIO_NET_OK;
7956e790746SPaolo Bonzini }
7966e790746SPaolo Bonzini 
797644c9858SDmitry Fleytman static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd,
798644c9858SDmitry Fleytman                                      struct iovec *iov, unsigned int iov_cnt)
799644c9858SDmitry Fleytman {
800644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
801644c9858SDmitry Fleytman     uint64_t offloads;
802644c9858SDmitry Fleytman     size_t s;
803644c9858SDmitry Fleytman 
80495129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
805644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
806644c9858SDmitry Fleytman     }
807644c9858SDmitry Fleytman 
808644c9858SDmitry Fleytman     s = iov_to_buf(iov, iov_cnt, 0, &offloads, sizeof(offloads));
809644c9858SDmitry Fleytman     if (s != sizeof(offloads)) {
810644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
811644c9858SDmitry Fleytman     }
812644c9858SDmitry Fleytman 
813644c9858SDmitry Fleytman     if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) {
814644c9858SDmitry Fleytman         uint64_t supported_offloads;
815644c9858SDmitry Fleytman 
816189ae6bbSJason Wang         offloads = virtio_ldq_p(vdev, &offloads);
817189ae6bbSJason Wang 
818644c9858SDmitry Fleytman         if (!n->has_vnet_hdr) {
819644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
820644c9858SDmitry Fleytman         }
821644c9858SDmitry Fleytman 
8222974e916SYuri Benditovich         n->rsc4_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
8232974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO4);
8242974e916SYuri Benditovich         n->rsc6_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
8252974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO6);
8262974e916SYuri Benditovich         virtio_clear_feature(&offloads, VIRTIO_NET_F_RSC_EXT);
8272974e916SYuri Benditovich 
828644c9858SDmitry Fleytman         supported_offloads = virtio_net_supported_guest_offloads(n);
829644c9858SDmitry Fleytman         if (offloads & ~supported_offloads) {
830644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
831644c9858SDmitry Fleytman         }
832644c9858SDmitry Fleytman 
833644c9858SDmitry Fleytman         n->curr_guest_offloads = offloads;
834644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
835644c9858SDmitry Fleytman 
836644c9858SDmitry Fleytman         return VIRTIO_NET_OK;
837644c9858SDmitry Fleytman     } else {
838644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
839644c9858SDmitry Fleytman     }
840644c9858SDmitry Fleytman }
841644c9858SDmitry Fleytman 
8426e790746SPaolo Bonzini static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
8436e790746SPaolo Bonzini                                  struct iovec *iov, unsigned int iov_cnt)
8446e790746SPaolo Bonzini {
8451399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
8466e790746SPaolo Bonzini     struct virtio_net_ctrl_mac mac_data;
8476e790746SPaolo Bonzini     size_t s;
848b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
8496e790746SPaolo Bonzini 
8506e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {
8516e790746SPaolo Bonzini         if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {
8526e790746SPaolo Bonzini             return VIRTIO_NET_ERR;
8536e790746SPaolo Bonzini         }
8546e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
8556e790746SPaolo Bonzini         assert(s == sizeof(n->mac));
8566e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
857b1be4280SAmos Kong         rxfilter_notify(nc);
858b1be4280SAmos Kong 
8596e790746SPaolo Bonzini         return VIRTIO_NET_OK;
8606e790746SPaolo Bonzini     }
8616e790746SPaolo Bonzini 
8626e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) {
8636e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
8646e790746SPaolo Bonzini     }
8656e790746SPaolo Bonzini 
866cae2e556SAmos Kong     int in_use = 0;
867cae2e556SAmos Kong     int first_multi = 0;
868cae2e556SAmos Kong     uint8_t uni_overflow = 0;
869cae2e556SAmos Kong     uint8_t multi_overflow = 0;
870cae2e556SAmos Kong     uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
8716e790746SPaolo Bonzini 
8726e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
8736e790746SPaolo Bonzini                    sizeof(mac_data.entries));
8741399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
8756e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
876b1be4280SAmos Kong         goto error;
8776e790746SPaolo Bonzini     }
8786e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
8796e790746SPaolo Bonzini 
8806e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {
881b1be4280SAmos Kong         goto error;
8826e790746SPaolo Bonzini     }
8836e790746SPaolo Bonzini 
8846e790746SPaolo Bonzini     if (mac_data.entries <= MAC_TABLE_ENTRIES) {
885cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, macs,
8866e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
8876e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
888b1be4280SAmos Kong             goto error;
8896e790746SPaolo Bonzini         }
890cae2e556SAmos Kong         in_use += mac_data.entries;
8916e790746SPaolo Bonzini     } else {
892cae2e556SAmos Kong         uni_overflow = 1;
8936e790746SPaolo Bonzini     }
8946e790746SPaolo Bonzini 
8956e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN);
8966e790746SPaolo Bonzini 
897cae2e556SAmos Kong     first_multi = in_use;
8986e790746SPaolo Bonzini 
8996e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
9006e790746SPaolo Bonzini                    sizeof(mac_data.entries));
9011399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
9026e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
903b1be4280SAmos Kong         goto error;
9046e790746SPaolo Bonzini     }
9056e790746SPaolo Bonzini 
9066e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
9076e790746SPaolo Bonzini 
9086e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {
909b1be4280SAmos Kong         goto error;
9106e790746SPaolo Bonzini     }
9116e790746SPaolo Bonzini 
912edc24385SMichael S. Tsirkin     if (mac_data.entries <= MAC_TABLE_ENTRIES - in_use) {
913cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN],
9146e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
9156e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
916b1be4280SAmos Kong             goto error;
9176e790746SPaolo Bonzini         }
918cae2e556SAmos Kong         in_use += mac_data.entries;
9196e790746SPaolo Bonzini     } else {
920cae2e556SAmos Kong         multi_overflow = 1;
9216e790746SPaolo Bonzini     }
9226e790746SPaolo Bonzini 
923cae2e556SAmos Kong     n->mac_table.in_use = in_use;
924cae2e556SAmos Kong     n->mac_table.first_multi = first_multi;
925cae2e556SAmos Kong     n->mac_table.uni_overflow = uni_overflow;
926cae2e556SAmos Kong     n->mac_table.multi_overflow = multi_overflow;
927cae2e556SAmos Kong     memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN);
928cae2e556SAmos Kong     g_free(macs);
929b1be4280SAmos Kong     rxfilter_notify(nc);
930b1be4280SAmos Kong 
9316e790746SPaolo Bonzini     return VIRTIO_NET_OK;
932b1be4280SAmos Kong 
933b1be4280SAmos Kong error:
934cae2e556SAmos Kong     g_free(macs);
935b1be4280SAmos Kong     return VIRTIO_NET_ERR;
9366e790746SPaolo Bonzini }
9376e790746SPaolo Bonzini 
9386e790746SPaolo Bonzini static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
9396e790746SPaolo Bonzini                                         struct iovec *iov, unsigned int iov_cnt)
9406e790746SPaolo Bonzini {
9411399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
9426e790746SPaolo Bonzini     uint16_t vid;
9436e790746SPaolo Bonzini     size_t s;
944b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
9456e790746SPaolo Bonzini 
9466e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid));
9471399c60dSRusty Russell     vid = virtio_lduw_p(vdev, &vid);
9486e790746SPaolo Bonzini     if (s != sizeof(vid)) {
9496e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9506e790746SPaolo Bonzini     }
9516e790746SPaolo Bonzini 
9526e790746SPaolo Bonzini     if (vid >= MAX_VLAN)
9536e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9546e790746SPaolo Bonzini 
9556e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
9566e790746SPaolo Bonzini         n->vlans[vid >> 5] |= (1U << (vid & 0x1f));
9576e790746SPaolo Bonzini     else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL)
9586e790746SPaolo Bonzini         n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f));
9596e790746SPaolo Bonzini     else
9606e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9616e790746SPaolo Bonzini 
962b1be4280SAmos Kong     rxfilter_notify(nc);
963b1be4280SAmos Kong 
9646e790746SPaolo Bonzini     return VIRTIO_NET_OK;
9656e790746SPaolo Bonzini }
9666e790746SPaolo Bonzini 
967f57fcf70SJason Wang static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
968f57fcf70SJason Wang                                       struct iovec *iov, unsigned int iov_cnt)
969f57fcf70SJason Wang {
970*9d8c6a25SDr. David Alan Gilbert     trace_virtio_net_handle_announce(n->announce_timer.round);
971f57fcf70SJason Wang     if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK &&
972f57fcf70SJason Wang         n->status & VIRTIO_NET_S_ANNOUNCE) {
973f57fcf70SJason Wang         n->status &= ~VIRTIO_NET_S_ANNOUNCE;
974*9d8c6a25SDr. David Alan Gilbert         if (n->announce_timer.round) {
975*9d8c6a25SDr. David Alan Gilbert             qemu_announce_timer_step(&n->announce_timer);
976f57fcf70SJason Wang         }
977f57fcf70SJason Wang         return VIRTIO_NET_OK;
978f57fcf70SJason Wang     } else {
979f57fcf70SJason Wang         return VIRTIO_NET_ERR;
980f57fcf70SJason Wang     }
981f57fcf70SJason Wang }
982f57fcf70SJason Wang 
9836e790746SPaolo Bonzini static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
9846e790746SPaolo Bonzini                                 struct iovec *iov, unsigned int iov_cnt)
9856e790746SPaolo Bonzini {
98617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
9876e790746SPaolo Bonzini     struct virtio_net_ctrl_mq mq;
9886e790746SPaolo Bonzini     size_t s;
9896e790746SPaolo Bonzini     uint16_t queues;
9906e790746SPaolo Bonzini 
9916e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq));
9926e790746SPaolo Bonzini     if (s != sizeof(mq)) {
9936e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9946e790746SPaolo Bonzini     }
9956e790746SPaolo Bonzini 
9966e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
9976e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9986e790746SPaolo Bonzini     }
9996e790746SPaolo Bonzini 
10001399c60dSRusty Russell     queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs);
10016e790746SPaolo Bonzini 
10026e790746SPaolo Bonzini     if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
10036e790746SPaolo Bonzini         queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX ||
10046e790746SPaolo Bonzini         queues > n->max_queues ||
10056e790746SPaolo Bonzini         !n->multiqueue) {
10066e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
10076e790746SPaolo Bonzini     }
10086e790746SPaolo Bonzini 
10096e790746SPaolo Bonzini     n->curr_queues = queues;
10106e790746SPaolo Bonzini     /* stop the backend before changing the number of queues to avoid handling a
10116e790746SPaolo Bonzini      * disabled queue */
101217a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
10136e790746SPaolo Bonzini     virtio_net_set_queues(n);
10146e790746SPaolo Bonzini 
10156e790746SPaolo Bonzini     return VIRTIO_NET_OK;
10166e790746SPaolo Bonzini }
1017ba7eadb5SGreg Kurz 
10186e790746SPaolo Bonzini static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
10196e790746SPaolo Bonzini {
102017a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
10216e790746SPaolo Bonzini     struct virtio_net_ctrl_hdr ctrl;
10226e790746SPaolo Bonzini     virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
102351b19ebeSPaolo Bonzini     VirtQueueElement *elem;
10246e790746SPaolo Bonzini     size_t s;
1025771b6ed3SJason Wang     struct iovec *iov, *iov2;
10266e790746SPaolo Bonzini     unsigned int iov_cnt;
10276e790746SPaolo Bonzini 
102851b19ebeSPaolo Bonzini     for (;;) {
102951b19ebeSPaolo Bonzini         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
103051b19ebeSPaolo Bonzini         if (!elem) {
103151b19ebeSPaolo Bonzini             break;
103251b19ebeSPaolo Bonzini         }
103351b19ebeSPaolo Bonzini         if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) ||
103451b19ebeSPaolo Bonzini             iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) {
1035ba7eadb5SGreg Kurz             virtio_error(vdev, "virtio-net ctrl missing headers");
1036ba7eadb5SGreg Kurz             virtqueue_detach_element(vq, elem, 0);
1037ba7eadb5SGreg Kurz             g_free(elem);
1038ba7eadb5SGreg Kurz             break;
10396e790746SPaolo Bonzini         }
10406e790746SPaolo Bonzini 
104151b19ebeSPaolo Bonzini         iov_cnt = elem->out_num;
104251b19ebeSPaolo Bonzini         iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num);
10436e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl));
10446e790746SPaolo Bonzini         iov_discard_front(&iov, &iov_cnt, sizeof(ctrl));
10456e790746SPaolo Bonzini         if (s != sizeof(ctrl)) {
10466e790746SPaolo Bonzini             status = VIRTIO_NET_ERR;
10476e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_RX) {
10486e790746SPaolo Bonzini             status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt);
10496e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) {
10506e790746SPaolo Bonzini             status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt);
10516e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) {
10526e790746SPaolo Bonzini             status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt);
1053f57fcf70SJason Wang         } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) {
1054f57fcf70SJason Wang             status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt);
10556e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) {
10566e790746SPaolo Bonzini             status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt);
1057644c9858SDmitry Fleytman         } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) {
1058644c9858SDmitry Fleytman             status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt);
10596e790746SPaolo Bonzini         }
10606e790746SPaolo Bonzini 
106151b19ebeSPaolo Bonzini         s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status));
10626e790746SPaolo Bonzini         assert(s == sizeof(status));
10636e790746SPaolo Bonzini 
106451b19ebeSPaolo Bonzini         virtqueue_push(vq, elem, sizeof(status));
10656e790746SPaolo Bonzini         virtio_notify(vdev, vq);
1066771b6ed3SJason Wang         g_free(iov2);
106751b19ebeSPaolo Bonzini         g_free(elem);
10686e790746SPaolo Bonzini     }
10696e790746SPaolo Bonzini }
10706e790746SPaolo Bonzini 
10716e790746SPaolo Bonzini /* RX */
10726e790746SPaolo Bonzini 
10736e790746SPaolo Bonzini static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
10746e790746SPaolo Bonzini {
107517a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
10766e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(vq));
10776e790746SPaolo Bonzini 
10786e790746SPaolo Bonzini     qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index));
10796e790746SPaolo Bonzini }
10806e790746SPaolo Bonzini 
10816e790746SPaolo Bonzini static int virtio_net_can_receive(NetClientState *nc)
10826e790746SPaolo Bonzini {
10836e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
108417a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
10856e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
10866e790746SPaolo Bonzini 
108717a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
10886e790746SPaolo Bonzini         return 0;
10896e790746SPaolo Bonzini     }
10906e790746SPaolo Bonzini 
10916e790746SPaolo Bonzini     if (nc->queue_index >= n->curr_queues) {
10926e790746SPaolo Bonzini         return 0;
10936e790746SPaolo Bonzini     }
10946e790746SPaolo Bonzini 
10956e790746SPaolo Bonzini     if (!virtio_queue_ready(q->rx_vq) ||
109617a0ca55SKONRAD Frederic         !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
10976e790746SPaolo Bonzini         return 0;
10986e790746SPaolo Bonzini     }
10996e790746SPaolo Bonzini 
11006e790746SPaolo Bonzini     return 1;
11016e790746SPaolo Bonzini }
11026e790746SPaolo Bonzini 
11036e790746SPaolo Bonzini static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize)
11046e790746SPaolo Bonzini {
11056e790746SPaolo Bonzini     VirtIONet *n = q->n;
11066e790746SPaolo Bonzini     if (virtio_queue_empty(q->rx_vq) ||
11076e790746SPaolo Bonzini         (n->mergeable_rx_bufs &&
11086e790746SPaolo Bonzini          !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
11096e790746SPaolo Bonzini         virtio_queue_set_notification(q->rx_vq, 1);
11106e790746SPaolo Bonzini 
11116e790746SPaolo Bonzini         /* To avoid a race condition where the guest has made some buffers
11126e790746SPaolo Bonzini          * available after the above check but before notification was
11136e790746SPaolo Bonzini          * enabled, check for available buffers again.
11146e790746SPaolo Bonzini          */
11156e790746SPaolo Bonzini         if (virtio_queue_empty(q->rx_vq) ||
11166e790746SPaolo Bonzini             (n->mergeable_rx_bufs &&
11176e790746SPaolo Bonzini              !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
11186e790746SPaolo Bonzini             return 0;
11196e790746SPaolo Bonzini         }
11206e790746SPaolo Bonzini     }
11216e790746SPaolo Bonzini 
11226e790746SPaolo Bonzini     virtio_queue_set_notification(q->rx_vq, 0);
11236e790746SPaolo Bonzini     return 1;
11246e790746SPaolo Bonzini }
11256e790746SPaolo Bonzini 
11261399c60dSRusty Russell static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr)
1127032a74a1SCédric Le Goater {
11281399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->hdr_len);
11291399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->gso_size);
11301399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_start);
11311399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_offset);
1132032a74a1SCédric Le Goater }
1133032a74a1SCédric Le Goater 
11346e790746SPaolo Bonzini /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so
11356e790746SPaolo Bonzini  * it never finds out that the packets don't have valid checksums.  This
11366e790746SPaolo Bonzini  * causes dhclient to get upset.  Fedora's carried a patch for ages to
11376e790746SPaolo Bonzini  * fix this with Xen but it hasn't appeared in an upstream release of
11386e790746SPaolo Bonzini  * dhclient yet.
11396e790746SPaolo Bonzini  *
11406e790746SPaolo Bonzini  * To avoid breaking existing guests, we catch udp packets and add
11416e790746SPaolo Bonzini  * checksums.  This is terrible but it's better than hacking the guest
11426e790746SPaolo Bonzini  * kernels.
11436e790746SPaolo Bonzini  *
11446e790746SPaolo Bonzini  * N.B. if we introduce a zero-copy API, this operation is no longer free so
11456e790746SPaolo Bonzini  * we should provide a mechanism to disable it to avoid polluting the host
11466e790746SPaolo Bonzini  * cache.
11476e790746SPaolo Bonzini  */
11486e790746SPaolo Bonzini static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
11496e790746SPaolo Bonzini                                         uint8_t *buf, size_t size)
11506e790746SPaolo Bonzini {
11516e790746SPaolo Bonzini     if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
11526e790746SPaolo Bonzini         (size > 27 && size < 1500) && /* normal sized MTU */
11536e790746SPaolo Bonzini         (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
11546e790746SPaolo Bonzini         (buf[23] == 17) && /* ip.protocol == UDP */
11556e790746SPaolo Bonzini         (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
11566e790746SPaolo Bonzini         net_checksum_calculate(buf, size);
11576e790746SPaolo Bonzini         hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
11586e790746SPaolo Bonzini     }
11596e790746SPaolo Bonzini }
11606e790746SPaolo Bonzini 
11616e790746SPaolo Bonzini static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt,
11626e790746SPaolo Bonzini                            const void *buf, size_t size)
11636e790746SPaolo Bonzini {
11646e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
11656e790746SPaolo Bonzini         /* FIXME this cast is evil */
11666e790746SPaolo Bonzini         void *wbuf = (void *)buf;
11676e790746SPaolo Bonzini         work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len,
11686e790746SPaolo Bonzini                                     size - n->host_hdr_len);
11691bfa316cSGreg Kurz 
11701bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
11711399c60dSRusty Russell             virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf);
11721bfa316cSGreg Kurz         }
11736e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr));
11746e790746SPaolo Bonzini     } else {
11756e790746SPaolo Bonzini         struct virtio_net_hdr hdr = {
11766e790746SPaolo Bonzini             .flags = 0,
11776e790746SPaolo Bonzini             .gso_type = VIRTIO_NET_HDR_GSO_NONE
11786e790746SPaolo Bonzini         };
11796e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr);
11806e790746SPaolo Bonzini     }
11816e790746SPaolo Bonzini }
11826e790746SPaolo Bonzini 
11836e790746SPaolo Bonzini static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
11846e790746SPaolo Bonzini {
11856e790746SPaolo Bonzini     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
11866e790746SPaolo Bonzini     static const uint8_t vlan[] = {0x81, 0x00};
11876e790746SPaolo Bonzini     uint8_t *ptr = (uint8_t *)buf;
11886e790746SPaolo Bonzini     int i;
11896e790746SPaolo Bonzini 
11906e790746SPaolo Bonzini     if (n->promisc)
11916e790746SPaolo Bonzini         return 1;
11926e790746SPaolo Bonzini 
11936e790746SPaolo Bonzini     ptr += n->host_hdr_len;
11946e790746SPaolo Bonzini 
11956e790746SPaolo Bonzini     if (!memcmp(&ptr[12], vlan, sizeof(vlan))) {
11967542d3e7SPeter Maydell         int vid = lduw_be_p(ptr + 14) & 0xfff;
11976e790746SPaolo Bonzini         if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f))))
11986e790746SPaolo Bonzini             return 0;
11996e790746SPaolo Bonzini     }
12006e790746SPaolo Bonzini 
12016e790746SPaolo Bonzini     if (ptr[0] & 1) { // multicast
12026e790746SPaolo Bonzini         if (!memcmp(ptr, bcast, sizeof(bcast))) {
12036e790746SPaolo Bonzini             return !n->nobcast;
12046e790746SPaolo Bonzini         } else if (n->nomulti) {
12056e790746SPaolo Bonzini             return 0;
12066e790746SPaolo Bonzini         } else if (n->allmulti || n->mac_table.multi_overflow) {
12076e790746SPaolo Bonzini             return 1;
12086e790746SPaolo Bonzini         }
12096e790746SPaolo Bonzini 
12106e790746SPaolo Bonzini         for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
12116e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
12126e790746SPaolo Bonzini                 return 1;
12136e790746SPaolo Bonzini             }
12146e790746SPaolo Bonzini         }
12156e790746SPaolo Bonzini     } else { // unicast
12166e790746SPaolo Bonzini         if (n->nouni) {
12176e790746SPaolo Bonzini             return 0;
12186e790746SPaolo Bonzini         } else if (n->alluni || n->mac_table.uni_overflow) {
12196e790746SPaolo Bonzini             return 1;
12206e790746SPaolo Bonzini         } else if (!memcmp(ptr, n->mac, ETH_ALEN)) {
12216e790746SPaolo Bonzini             return 1;
12226e790746SPaolo Bonzini         }
12236e790746SPaolo Bonzini 
12246e790746SPaolo Bonzini         for (i = 0; i < n->mac_table.first_multi; i++) {
12256e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
12266e790746SPaolo Bonzini                 return 1;
12276e790746SPaolo Bonzini             }
12286e790746SPaolo Bonzini         }
12296e790746SPaolo Bonzini     }
12306e790746SPaolo Bonzini 
12316e790746SPaolo Bonzini     return 0;
12326e790746SPaolo Bonzini }
12336e790746SPaolo Bonzini 
123497cd965cSPaolo Bonzini static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
123597cd965cSPaolo Bonzini                                       size_t size)
12366e790746SPaolo Bonzini {
12376e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
12386e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
123917a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
12406e790746SPaolo Bonzini     struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
12416e790746SPaolo Bonzini     struct virtio_net_hdr_mrg_rxbuf mhdr;
12426e790746SPaolo Bonzini     unsigned mhdr_cnt = 0;
12436e790746SPaolo Bonzini     size_t offset, i, guest_offset;
12446e790746SPaolo Bonzini 
12456e790746SPaolo Bonzini     if (!virtio_net_can_receive(nc)) {
12466e790746SPaolo Bonzini         return -1;
12476e790746SPaolo Bonzini     }
12486e790746SPaolo Bonzini 
12496e790746SPaolo Bonzini     /* hdr_len refers to the header we supply to the guest */
12506e790746SPaolo Bonzini     if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) {
12516e790746SPaolo Bonzini         return 0;
12526e790746SPaolo Bonzini     }
12536e790746SPaolo Bonzini 
12546e790746SPaolo Bonzini     if (!receive_filter(n, buf, size))
12556e790746SPaolo Bonzini         return size;
12566e790746SPaolo Bonzini 
12576e790746SPaolo Bonzini     offset = i = 0;
12586e790746SPaolo Bonzini 
12596e790746SPaolo Bonzini     while (offset < size) {
126051b19ebeSPaolo Bonzini         VirtQueueElement *elem;
12616e790746SPaolo Bonzini         int len, total;
126251b19ebeSPaolo Bonzini         const struct iovec *sg;
12636e790746SPaolo Bonzini 
12646e790746SPaolo Bonzini         total = 0;
12656e790746SPaolo Bonzini 
126651b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement));
126751b19ebeSPaolo Bonzini         if (!elem) {
1268ba10b9c0SGreg Kurz             if (i) {
1269ba10b9c0SGreg Kurz                 virtio_error(vdev, "virtio-net unexpected empty queue: "
12706e790746SPaolo Bonzini                              "i %zd mergeable %d offset %zd, size %zd, "
1271019a3edbSGerd Hoffmann                              "guest hdr len %zd, host hdr len %zd "
1272019a3edbSGerd Hoffmann                              "guest features 0x%" PRIx64,
12736e790746SPaolo Bonzini                              i, n->mergeable_rx_bufs, offset, size,
1274019a3edbSGerd Hoffmann                              n->guest_hdr_len, n->host_hdr_len,
1275019a3edbSGerd Hoffmann                              vdev->guest_features);
1276ba10b9c0SGreg Kurz             }
1277ba10b9c0SGreg Kurz             return -1;
12786e790746SPaolo Bonzini         }
12796e790746SPaolo Bonzini 
128051b19ebeSPaolo Bonzini         if (elem->in_num < 1) {
1281ba10b9c0SGreg Kurz             virtio_error(vdev,
1282ba10b9c0SGreg Kurz                          "virtio-net receive queue contains no in buffers");
1283ba10b9c0SGreg Kurz             virtqueue_detach_element(q->rx_vq, elem, 0);
1284ba10b9c0SGreg Kurz             g_free(elem);
1285ba10b9c0SGreg Kurz             return -1;
12866e790746SPaolo Bonzini         }
12876e790746SPaolo Bonzini 
128851b19ebeSPaolo Bonzini         sg = elem->in_sg;
12896e790746SPaolo Bonzini         if (i == 0) {
12906e790746SPaolo Bonzini             assert(offset == 0);
12916e790746SPaolo Bonzini             if (n->mergeable_rx_bufs) {
12926e790746SPaolo Bonzini                 mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg),
129351b19ebeSPaolo Bonzini                                     sg, elem->in_num,
12946e790746SPaolo Bonzini                                     offsetof(typeof(mhdr), num_buffers),
12956e790746SPaolo Bonzini                                     sizeof(mhdr.num_buffers));
12966e790746SPaolo Bonzini             }
12976e790746SPaolo Bonzini 
129851b19ebeSPaolo Bonzini             receive_header(n, sg, elem->in_num, buf, size);
12996e790746SPaolo Bonzini             offset = n->host_hdr_len;
13006e790746SPaolo Bonzini             total += n->guest_hdr_len;
13016e790746SPaolo Bonzini             guest_offset = n->guest_hdr_len;
13026e790746SPaolo Bonzini         } else {
13036e790746SPaolo Bonzini             guest_offset = 0;
13046e790746SPaolo Bonzini         }
13056e790746SPaolo Bonzini 
13066e790746SPaolo Bonzini         /* copy in packet.  ugh */
130751b19ebeSPaolo Bonzini         len = iov_from_buf(sg, elem->in_num, guest_offset,
13086e790746SPaolo Bonzini                            buf + offset, size - offset);
13096e790746SPaolo Bonzini         total += len;
13106e790746SPaolo Bonzini         offset += len;
13116e790746SPaolo Bonzini         /* If buffers can't be merged, at this point we
13126e790746SPaolo Bonzini          * must have consumed the complete packet.
13136e790746SPaolo Bonzini          * Otherwise, drop it. */
13146e790746SPaolo Bonzini         if (!n->mergeable_rx_bufs && offset < size) {
131527e57efeSLadi Prosek             virtqueue_unpop(q->rx_vq, elem, total);
131651b19ebeSPaolo Bonzini             g_free(elem);
13176e790746SPaolo Bonzini             return size;
13186e790746SPaolo Bonzini         }
13196e790746SPaolo Bonzini 
13206e790746SPaolo Bonzini         /* signal other side */
132151b19ebeSPaolo Bonzini         virtqueue_fill(q->rx_vq, elem, total, i++);
132251b19ebeSPaolo Bonzini         g_free(elem);
13236e790746SPaolo Bonzini     }
13246e790746SPaolo Bonzini 
13256e790746SPaolo Bonzini     if (mhdr_cnt) {
13261399c60dSRusty Russell         virtio_stw_p(vdev, &mhdr.num_buffers, i);
13276e790746SPaolo Bonzini         iov_from_buf(mhdr_sg, mhdr_cnt,
13286e790746SPaolo Bonzini                      0,
13296e790746SPaolo Bonzini                      &mhdr.num_buffers, sizeof mhdr.num_buffers);
13306e790746SPaolo Bonzini     }
13316e790746SPaolo Bonzini 
13326e790746SPaolo Bonzini     virtqueue_flush(q->rx_vq, i);
133317a0ca55SKONRAD Frederic     virtio_notify(vdev, q->rx_vq);
13346e790746SPaolo Bonzini 
13356e790746SPaolo Bonzini     return size;
13366e790746SPaolo Bonzini }
13376e790746SPaolo Bonzini 
13382974e916SYuri Benditovich static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf,
133997cd965cSPaolo Bonzini                                   size_t size)
134097cd965cSPaolo Bonzini {
134197cd965cSPaolo Bonzini     ssize_t r;
134297cd965cSPaolo Bonzini 
134397cd965cSPaolo Bonzini     rcu_read_lock();
134497cd965cSPaolo Bonzini     r = virtio_net_receive_rcu(nc, buf, size);
134597cd965cSPaolo Bonzini     rcu_read_unlock();
134697cd965cSPaolo Bonzini     return r;
134797cd965cSPaolo Bonzini }
134897cd965cSPaolo Bonzini 
13492974e916SYuri Benditovich static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
13502974e916SYuri Benditovich                                          const uint8_t *buf,
13512974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
13522974e916SYuri Benditovich {
13532974e916SYuri Benditovich     uint16_t ip_hdrlen;
13542974e916SYuri Benditovich     struct ip_header *ip;
13552974e916SYuri Benditovich 
13562974e916SYuri Benditovich     ip = (struct ip_header *)(buf + chain->n->guest_hdr_len
13572974e916SYuri Benditovich                               + sizeof(struct eth_header));
13582974e916SYuri Benditovich     unit->ip = (void *)ip;
13592974e916SYuri Benditovich     ip_hdrlen = (ip->ip_ver_len & 0xF) << 2;
13602974e916SYuri Benditovich     unit->ip_plen = &ip->ip_len;
13612974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen);
13622974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
13632974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen;
13642974e916SYuri Benditovich }
13652974e916SYuri Benditovich 
13662974e916SYuri Benditovich static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
13672974e916SYuri Benditovich                                          const uint8_t *buf,
13682974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
13692974e916SYuri Benditovich {
13702974e916SYuri Benditovich     struct ip6_header *ip6;
13712974e916SYuri Benditovich 
13722974e916SYuri Benditovich     ip6 = (struct ip6_header *)(buf + chain->n->guest_hdr_len
13732974e916SYuri Benditovich                                  + sizeof(struct eth_header));
13742974e916SYuri Benditovich     unit->ip = ip6;
13752974e916SYuri Benditovich     unit->ip_plen = &(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
13762974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip)\
13772974e916SYuri Benditovich                                         + sizeof(struct ip6_header));
13782974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
13792974e916SYuri Benditovich 
13802974e916SYuri Benditovich     /* There is a difference between payload lenght in ipv4 and v6,
13812974e916SYuri Benditovich        ip header is excluded in ipv6 */
13822974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen;
13832974e916SYuri Benditovich }
13842974e916SYuri Benditovich 
13852974e916SYuri Benditovich static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain,
13862974e916SYuri Benditovich                                        VirtioNetRscSeg *seg)
13872974e916SYuri Benditovich {
13882974e916SYuri Benditovich     int ret;
13892974e916SYuri Benditovich     struct virtio_net_hdr *h;
13902974e916SYuri Benditovich 
13912974e916SYuri Benditovich     h = (struct virtio_net_hdr *)seg->buf;
13922974e916SYuri Benditovich     h->flags = 0;
13932974e916SYuri Benditovich     h->gso_type = VIRTIO_NET_HDR_GSO_NONE;
13942974e916SYuri Benditovich 
13952974e916SYuri Benditovich     if (seg->is_coalesced) {
13962974e916SYuri Benditovich         *virtio_net_rsc_ext_num_packets(h) = seg->packets;
13972974e916SYuri Benditovich         *virtio_net_rsc_ext_num_dupacks(h) = seg->dup_ack;
13982974e916SYuri Benditovich         h->flags = VIRTIO_NET_HDR_F_RSC_INFO;
13992974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
14002974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
14012974e916SYuri Benditovich         } else {
14022974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
14032974e916SYuri Benditovich         }
14042974e916SYuri Benditovich     }
14052974e916SYuri Benditovich 
14062974e916SYuri Benditovich     ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size);
14072974e916SYuri Benditovich     QTAILQ_REMOVE(&chain->buffers, seg, next);
14082974e916SYuri Benditovich     g_free(seg->buf);
14092974e916SYuri Benditovich     g_free(seg);
14102974e916SYuri Benditovich 
14112974e916SYuri Benditovich     return ret;
14122974e916SYuri Benditovich }
14132974e916SYuri Benditovich 
14142974e916SYuri Benditovich static void virtio_net_rsc_purge(void *opq)
14152974e916SYuri Benditovich {
14162974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn;
14172974e916SYuri Benditovich     VirtioNetRscChain *chain = (VirtioNetRscChain *)opq;
14182974e916SYuri Benditovich 
14192974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn) {
14202974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
14212974e916SYuri Benditovich             chain->stat.purge_failed++;
14222974e916SYuri Benditovich             continue;
14232974e916SYuri Benditovich         }
14242974e916SYuri Benditovich     }
14252974e916SYuri Benditovich 
14262974e916SYuri Benditovich     chain->stat.timer++;
14272974e916SYuri Benditovich     if (!QTAILQ_EMPTY(&chain->buffers)) {
14282974e916SYuri Benditovich         timer_mod(chain->drain_timer,
14292974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
14302974e916SYuri Benditovich     }
14312974e916SYuri Benditovich }
14322974e916SYuri Benditovich 
14332974e916SYuri Benditovich static void virtio_net_rsc_cleanup(VirtIONet *n)
14342974e916SYuri Benditovich {
14352974e916SYuri Benditovich     VirtioNetRscChain *chain, *rn_chain;
14362974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn_seg;
14372974e916SYuri Benditovich 
14382974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(chain, &n->rsc_chains, next, rn_chain) {
14392974e916SYuri Benditovich         QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn_seg) {
14402974e916SYuri Benditovich             QTAILQ_REMOVE(&chain->buffers, seg, next);
14412974e916SYuri Benditovich             g_free(seg->buf);
14422974e916SYuri Benditovich             g_free(seg);
14432974e916SYuri Benditovich         }
14442974e916SYuri Benditovich 
14452974e916SYuri Benditovich         timer_del(chain->drain_timer);
14462974e916SYuri Benditovich         timer_free(chain->drain_timer);
14472974e916SYuri Benditovich         QTAILQ_REMOVE(&n->rsc_chains, chain, next);
14482974e916SYuri Benditovich         g_free(chain);
14492974e916SYuri Benditovich     }
14502974e916SYuri Benditovich }
14512974e916SYuri Benditovich 
14522974e916SYuri Benditovich static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain,
14532974e916SYuri Benditovich                                      NetClientState *nc,
14542974e916SYuri Benditovich                                      const uint8_t *buf, size_t size)
14552974e916SYuri Benditovich {
14562974e916SYuri Benditovich     uint16_t hdr_len;
14572974e916SYuri Benditovich     VirtioNetRscSeg *seg;
14582974e916SYuri Benditovich 
14592974e916SYuri Benditovich     hdr_len = chain->n->guest_hdr_len;
14602974e916SYuri Benditovich     seg = g_malloc(sizeof(VirtioNetRscSeg));
14612974e916SYuri Benditovich     seg->buf = g_malloc(hdr_len + sizeof(struct eth_header)
14622974e916SYuri Benditovich         + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD);
14632974e916SYuri Benditovich     memcpy(seg->buf, buf, size);
14642974e916SYuri Benditovich     seg->size = size;
14652974e916SYuri Benditovich     seg->packets = 1;
14662974e916SYuri Benditovich     seg->dup_ack = 0;
14672974e916SYuri Benditovich     seg->is_coalesced = 0;
14682974e916SYuri Benditovich     seg->nc = nc;
14692974e916SYuri Benditovich 
14702974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&chain->buffers, seg, next);
14712974e916SYuri Benditovich     chain->stat.cache++;
14722974e916SYuri Benditovich 
14732974e916SYuri Benditovich     switch (chain->proto) {
14742974e916SYuri Benditovich     case ETH_P_IP:
14752974e916SYuri Benditovich         virtio_net_rsc_extract_unit4(chain, seg->buf, &seg->unit);
14762974e916SYuri Benditovich         break;
14772974e916SYuri Benditovich     case ETH_P_IPV6:
14782974e916SYuri Benditovich         virtio_net_rsc_extract_unit6(chain, seg->buf, &seg->unit);
14792974e916SYuri Benditovich         break;
14802974e916SYuri Benditovich     default:
14812974e916SYuri Benditovich         g_assert_not_reached();
14822974e916SYuri Benditovich     }
14832974e916SYuri Benditovich }
14842974e916SYuri Benditovich 
14852974e916SYuri Benditovich static int32_t virtio_net_rsc_handle_ack(VirtioNetRscChain *chain,
14862974e916SYuri Benditovich                                          VirtioNetRscSeg *seg,
14872974e916SYuri Benditovich                                          const uint8_t *buf,
14882974e916SYuri Benditovich                                          struct tcp_header *n_tcp,
14892974e916SYuri Benditovich                                          struct tcp_header *o_tcp)
14902974e916SYuri Benditovich {
14912974e916SYuri Benditovich     uint32_t nack, oack;
14922974e916SYuri Benditovich     uint16_t nwin, owin;
14932974e916SYuri Benditovich 
14942974e916SYuri Benditovich     nack = htonl(n_tcp->th_ack);
14952974e916SYuri Benditovich     nwin = htons(n_tcp->th_win);
14962974e916SYuri Benditovich     oack = htonl(o_tcp->th_ack);
14972974e916SYuri Benditovich     owin = htons(o_tcp->th_win);
14982974e916SYuri Benditovich 
14992974e916SYuri Benditovich     if ((nack - oack) >= VIRTIO_NET_MAX_TCP_PAYLOAD) {
15002974e916SYuri Benditovich         chain->stat.ack_out_of_win++;
15012974e916SYuri Benditovich         return RSC_FINAL;
15022974e916SYuri Benditovich     } else if (nack == oack) {
15032974e916SYuri Benditovich         /* duplicated ack or window probe */
15042974e916SYuri Benditovich         if (nwin == owin) {
15052974e916SYuri Benditovich             /* duplicated ack, add dup ack count due to whql test up to 1 */
15062974e916SYuri Benditovich             chain->stat.dup_ack++;
15072974e916SYuri Benditovich             return RSC_FINAL;
15082974e916SYuri Benditovich         } else {
15092974e916SYuri Benditovich             /* Coalesce window update */
15102974e916SYuri Benditovich             o_tcp->th_win = n_tcp->th_win;
15112974e916SYuri Benditovich             chain->stat.win_update++;
15122974e916SYuri Benditovich             return RSC_COALESCE;
15132974e916SYuri Benditovich         }
15142974e916SYuri Benditovich     } else {
15152974e916SYuri Benditovich         /* pure ack, go to 'C', finalize*/
15162974e916SYuri Benditovich         chain->stat.pure_ack++;
15172974e916SYuri Benditovich         return RSC_FINAL;
15182974e916SYuri Benditovich     }
15192974e916SYuri Benditovich }
15202974e916SYuri Benditovich 
15212974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain,
15222974e916SYuri Benditovich                                             VirtioNetRscSeg *seg,
15232974e916SYuri Benditovich                                             const uint8_t *buf,
15242974e916SYuri Benditovich                                             VirtioNetRscUnit *n_unit)
15252974e916SYuri Benditovich {
15262974e916SYuri Benditovich     void *data;
15272974e916SYuri Benditovich     uint16_t o_ip_len;
15282974e916SYuri Benditovich     uint32_t nseq, oseq;
15292974e916SYuri Benditovich     VirtioNetRscUnit *o_unit;
15302974e916SYuri Benditovich 
15312974e916SYuri Benditovich     o_unit = &seg->unit;
15322974e916SYuri Benditovich     o_ip_len = htons(*o_unit->ip_plen);
15332974e916SYuri Benditovich     nseq = htonl(n_unit->tcp->th_seq);
15342974e916SYuri Benditovich     oseq = htonl(o_unit->tcp->th_seq);
15352974e916SYuri Benditovich 
15362974e916SYuri Benditovich     /* out of order or retransmitted. */
15372974e916SYuri Benditovich     if ((nseq - oseq) > VIRTIO_NET_MAX_TCP_PAYLOAD) {
15382974e916SYuri Benditovich         chain->stat.data_out_of_win++;
15392974e916SYuri Benditovich         return RSC_FINAL;
15402974e916SYuri Benditovich     }
15412974e916SYuri Benditovich 
15422974e916SYuri Benditovich     data = ((uint8_t *)n_unit->tcp) + n_unit->tcp_hdrlen;
15432974e916SYuri Benditovich     if (nseq == oseq) {
15442974e916SYuri Benditovich         if ((o_unit->payload == 0) && n_unit->payload) {
15452974e916SYuri Benditovich             /* From no payload to payload, normal case, not a dup ack or etc */
15462974e916SYuri Benditovich             chain->stat.data_after_pure_ack++;
15472974e916SYuri Benditovich             goto coalesce;
15482974e916SYuri Benditovich         } else {
15492974e916SYuri Benditovich             return virtio_net_rsc_handle_ack(chain, seg, buf,
15502974e916SYuri Benditovich                                              n_unit->tcp, o_unit->tcp);
15512974e916SYuri Benditovich         }
15522974e916SYuri Benditovich     } else if ((nseq - oseq) != o_unit->payload) {
15532974e916SYuri Benditovich         /* Not a consistent packet, out of order */
15542974e916SYuri Benditovich         chain->stat.data_out_of_order++;
15552974e916SYuri Benditovich         return RSC_FINAL;
15562974e916SYuri Benditovich     } else {
15572974e916SYuri Benditovich coalesce:
15582974e916SYuri Benditovich         if ((o_ip_len + n_unit->payload) > chain->max_payload) {
15592974e916SYuri Benditovich             chain->stat.over_size++;
15602974e916SYuri Benditovich             return RSC_FINAL;
15612974e916SYuri Benditovich         }
15622974e916SYuri Benditovich 
15632974e916SYuri Benditovich         /* Here comes the right data, the payload length in v4/v6 is different,
15642974e916SYuri Benditovich            so use the field value to update and record the new data len */
15652974e916SYuri Benditovich         o_unit->payload += n_unit->payload; /* update new data len */
15662974e916SYuri Benditovich 
15672974e916SYuri Benditovich         /* update field in ip header */
15682974e916SYuri Benditovich         *o_unit->ip_plen = htons(o_ip_len + n_unit->payload);
15692974e916SYuri Benditovich 
15702974e916SYuri Benditovich         /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced
15712974e916SYuri Benditovich            for windows guest, while this may change the behavior for linux
15722974e916SYuri Benditovich            guest (only if it uses RSC feature). */
15732974e916SYuri Benditovich         o_unit->tcp->th_offset_flags = n_unit->tcp->th_offset_flags;
15742974e916SYuri Benditovich 
15752974e916SYuri Benditovich         o_unit->tcp->th_ack = n_unit->tcp->th_ack;
15762974e916SYuri Benditovich         o_unit->tcp->th_win = n_unit->tcp->th_win;
15772974e916SYuri Benditovich 
15782974e916SYuri Benditovich         memmove(seg->buf + seg->size, data, n_unit->payload);
15792974e916SYuri Benditovich         seg->size += n_unit->payload;
15802974e916SYuri Benditovich         seg->packets++;
15812974e916SYuri Benditovich         chain->stat.coalesced++;
15822974e916SYuri Benditovich         return RSC_COALESCE;
15832974e916SYuri Benditovich     }
15842974e916SYuri Benditovich }
15852974e916SYuri Benditovich 
15862974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce4(VirtioNetRscChain *chain,
15872974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
15882974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
15892974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
15902974e916SYuri Benditovich {
15912974e916SYuri Benditovich     struct ip_header *ip1, *ip2;
15922974e916SYuri Benditovich 
15932974e916SYuri Benditovich     ip1 = (struct ip_header *)(unit->ip);
15942974e916SYuri Benditovich     ip2 = (struct ip_header *)(seg->unit.ip);
15952974e916SYuri Benditovich     if ((ip1->ip_src ^ ip2->ip_src) || (ip1->ip_dst ^ ip2->ip_dst)
15962974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
15972974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
15982974e916SYuri Benditovich         chain->stat.no_match++;
15992974e916SYuri Benditovich         return RSC_NO_MATCH;
16002974e916SYuri Benditovich     }
16012974e916SYuri Benditovich 
16022974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
16032974e916SYuri Benditovich }
16042974e916SYuri Benditovich 
16052974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce6(VirtioNetRscChain *chain,
16062974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
16072974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
16082974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
16092974e916SYuri Benditovich {
16102974e916SYuri Benditovich     struct ip6_header *ip1, *ip2;
16112974e916SYuri Benditovich 
16122974e916SYuri Benditovich     ip1 = (struct ip6_header *)(unit->ip);
16132974e916SYuri Benditovich     ip2 = (struct ip6_header *)(seg->unit.ip);
16142974e916SYuri Benditovich     if (memcmp(&ip1->ip6_src, &ip2->ip6_src, sizeof(struct in6_address))
16152974e916SYuri Benditovich         || memcmp(&ip1->ip6_dst, &ip2->ip6_dst, sizeof(struct in6_address))
16162974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
16172974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
16182974e916SYuri Benditovich             chain->stat.no_match++;
16192974e916SYuri Benditovich             return RSC_NO_MATCH;
16202974e916SYuri Benditovich     }
16212974e916SYuri Benditovich 
16222974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
16232974e916SYuri Benditovich }
16242974e916SYuri Benditovich 
16252974e916SYuri Benditovich /* Packets with 'SYN' should bypass, other flag should be sent after drain
16262974e916SYuri Benditovich  * to prevent out of order */
16272974e916SYuri Benditovich static int virtio_net_rsc_tcp_ctrl_check(VirtioNetRscChain *chain,
16282974e916SYuri Benditovich                                          struct tcp_header *tcp)
16292974e916SYuri Benditovich {
16302974e916SYuri Benditovich     uint16_t tcp_hdr;
16312974e916SYuri Benditovich     uint16_t tcp_flag;
16322974e916SYuri Benditovich 
16332974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags);
16342974e916SYuri Benditovich     tcp_hdr = (tcp_flag & VIRTIO_NET_TCP_HDR_LENGTH) >> 10;
16352974e916SYuri Benditovich     tcp_flag &= VIRTIO_NET_TCP_FLAG;
16362974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags) & 0x3F;
16372974e916SYuri Benditovich     if (tcp_flag & TH_SYN) {
16382974e916SYuri Benditovich         chain->stat.tcp_syn++;
16392974e916SYuri Benditovich         return RSC_BYPASS;
16402974e916SYuri Benditovich     }
16412974e916SYuri Benditovich 
16422974e916SYuri Benditovich     if (tcp_flag & (TH_FIN | TH_URG | TH_RST | TH_ECE | TH_CWR)) {
16432974e916SYuri Benditovich         chain->stat.tcp_ctrl_drain++;
16442974e916SYuri Benditovich         return RSC_FINAL;
16452974e916SYuri Benditovich     }
16462974e916SYuri Benditovich 
16472974e916SYuri Benditovich     if (tcp_hdr > sizeof(struct tcp_header)) {
16482974e916SYuri Benditovich         chain->stat.tcp_all_opt++;
16492974e916SYuri Benditovich         return RSC_FINAL;
16502974e916SYuri Benditovich     }
16512974e916SYuri Benditovich 
16522974e916SYuri Benditovich     return RSC_CANDIDATE;
16532974e916SYuri Benditovich }
16542974e916SYuri Benditovich 
16552974e916SYuri Benditovich static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain,
16562974e916SYuri Benditovich                                          NetClientState *nc,
16572974e916SYuri Benditovich                                          const uint8_t *buf, size_t size,
16582974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
16592974e916SYuri Benditovich {
16602974e916SYuri Benditovich     int ret;
16612974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
16622974e916SYuri Benditovich 
16632974e916SYuri Benditovich     if (QTAILQ_EMPTY(&chain->buffers)) {
16642974e916SYuri Benditovich         chain->stat.empty_cache++;
16652974e916SYuri Benditovich         virtio_net_rsc_cache_buf(chain, nc, buf, size);
16662974e916SYuri Benditovich         timer_mod(chain->drain_timer,
16672974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
16682974e916SYuri Benditovich         return size;
16692974e916SYuri Benditovich     }
16702974e916SYuri Benditovich 
16712974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
16722974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
16732974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce4(chain, seg, buf, size, unit);
16742974e916SYuri Benditovich         } else {
16752974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce6(chain, seg, buf, size, unit);
16762974e916SYuri Benditovich         }
16772974e916SYuri Benditovich 
16782974e916SYuri Benditovich         if (ret == RSC_FINAL) {
16792974e916SYuri Benditovich             if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
16802974e916SYuri Benditovich                 /* Send failed */
16812974e916SYuri Benditovich                 chain->stat.final_failed++;
16822974e916SYuri Benditovich                 return 0;
16832974e916SYuri Benditovich             }
16842974e916SYuri Benditovich 
16852974e916SYuri Benditovich             /* Send current packet */
16862974e916SYuri Benditovich             return virtio_net_do_receive(nc, buf, size);
16872974e916SYuri Benditovich         } else if (ret == RSC_NO_MATCH) {
16882974e916SYuri Benditovich             continue;
16892974e916SYuri Benditovich         } else {
16902974e916SYuri Benditovich             /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */
16912974e916SYuri Benditovich             seg->is_coalesced = 1;
16922974e916SYuri Benditovich             return size;
16932974e916SYuri Benditovich         }
16942974e916SYuri Benditovich     }
16952974e916SYuri Benditovich 
16962974e916SYuri Benditovich     chain->stat.no_match_cache++;
16972974e916SYuri Benditovich     virtio_net_rsc_cache_buf(chain, nc, buf, size);
16982974e916SYuri Benditovich     return size;
16992974e916SYuri Benditovich }
17002974e916SYuri Benditovich 
17012974e916SYuri Benditovich /* Drain a connection data, this is to avoid out of order segments */
17022974e916SYuri Benditovich static size_t virtio_net_rsc_drain_flow(VirtioNetRscChain *chain,
17032974e916SYuri Benditovich                                         NetClientState *nc,
17042974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
17052974e916SYuri Benditovich                                         uint16_t ip_start, uint16_t ip_size,
17062974e916SYuri Benditovich                                         uint16_t tcp_port)
17072974e916SYuri Benditovich {
17082974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
17092974e916SYuri Benditovich     uint32_t ppair1, ppair2;
17102974e916SYuri Benditovich 
17112974e916SYuri Benditovich     ppair1 = *(uint32_t *)(buf + tcp_port);
17122974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
17132974e916SYuri Benditovich         ppair2 = *(uint32_t *)(seg->buf + tcp_port);
17142974e916SYuri Benditovich         if (memcmp(buf + ip_start, seg->buf + ip_start, ip_size)
17152974e916SYuri Benditovich             || (ppair1 != ppair2)) {
17162974e916SYuri Benditovich             continue;
17172974e916SYuri Benditovich         }
17182974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
17192974e916SYuri Benditovich             chain->stat.drain_failed++;
17202974e916SYuri Benditovich         }
17212974e916SYuri Benditovich 
17222974e916SYuri Benditovich         break;
17232974e916SYuri Benditovich     }
17242974e916SYuri Benditovich 
17252974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
17262974e916SYuri Benditovich }
17272974e916SYuri Benditovich 
17282974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check4(VirtioNetRscChain *chain,
17292974e916SYuri Benditovich                                             struct ip_header *ip,
17302974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
17312974e916SYuri Benditovich {
17322974e916SYuri Benditovich     uint16_t ip_len;
17332974e916SYuri Benditovich 
17342974e916SYuri Benditovich     /* Not an ipv4 packet */
17352974e916SYuri Benditovich     if (((ip->ip_ver_len & 0xF0) >> 4) != IP_HEADER_VERSION_4) {
17362974e916SYuri Benditovich         chain->stat.ip_option++;
17372974e916SYuri Benditovich         return RSC_BYPASS;
17382974e916SYuri Benditovich     }
17392974e916SYuri Benditovich 
17402974e916SYuri Benditovich     /* Don't handle packets with ip option */
17412974e916SYuri Benditovich     if ((ip->ip_ver_len & 0xF) != VIRTIO_NET_IP4_HEADER_LENGTH) {
17422974e916SYuri Benditovich         chain->stat.ip_option++;
17432974e916SYuri Benditovich         return RSC_BYPASS;
17442974e916SYuri Benditovich     }
17452974e916SYuri Benditovich 
17462974e916SYuri Benditovich     if (ip->ip_p != IPPROTO_TCP) {
17472974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
17482974e916SYuri Benditovich         return RSC_BYPASS;
17492974e916SYuri Benditovich     }
17502974e916SYuri Benditovich 
17512974e916SYuri Benditovich     /* Don't handle packets with ip fragment */
17522974e916SYuri Benditovich     if (!(htons(ip->ip_off) & IP_DF)) {
17532974e916SYuri Benditovich         chain->stat.ip_frag++;
17542974e916SYuri Benditovich         return RSC_BYPASS;
17552974e916SYuri Benditovich     }
17562974e916SYuri Benditovich 
17572974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
17582974e916SYuri Benditovich     if (IPTOS_ECN(ip->ip_tos)) {
17592974e916SYuri Benditovich         chain->stat.ip_ecn++;
17602974e916SYuri Benditovich         return RSC_BYPASS;
17612974e916SYuri Benditovich     }
17622974e916SYuri Benditovich 
17632974e916SYuri Benditovich     ip_len = htons(ip->ip_len);
17642974e916SYuri Benditovich     if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header))
17652974e916SYuri Benditovich         || ip_len > (size - chain->n->guest_hdr_len -
17662974e916SYuri Benditovich                      sizeof(struct eth_header))) {
17672974e916SYuri Benditovich         chain->stat.ip_hacked++;
17682974e916SYuri Benditovich         return RSC_BYPASS;
17692974e916SYuri Benditovich     }
17702974e916SYuri Benditovich 
17712974e916SYuri Benditovich     return RSC_CANDIDATE;
17722974e916SYuri Benditovich }
17732974e916SYuri Benditovich 
17742974e916SYuri Benditovich static size_t virtio_net_rsc_receive4(VirtioNetRscChain *chain,
17752974e916SYuri Benditovich                                       NetClientState *nc,
17762974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
17772974e916SYuri Benditovich {
17782974e916SYuri Benditovich     int32_t ret;
17792974e916SYuri Benditovich     uint16_t hdr_len;
17802974e916SYuri Benditovich     VirtioNetRscUnit unit;
17812974e916SYuri Benditovich 
17822974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
17832974e916SYuri Benditovich 
17842974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header)
17852974e916SYuri Benditovich         + sizeof(struct tcp_header))) {
17862974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
17872974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
17882974e916SYuri Benditovich     }
17892974e916SYuri Benditovich 
17902974e916SYuri Benditovich     virtio_net_rsc_extract_unit4(chain, buf, &unit);
17912974e916SYuri Benditovich     if (virtio_net_rsc_sanity_check4(chain, unit.ip, buf, size)
17922974e916SYuri Benditovich         != RSC_CANDIDATE) {
17932974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
17942974e916SYuri Benditovich     }
17952974e916SYuri Benditovich 
17962974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
17972974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
17982974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
17992974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
18002974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
18012974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 12),
18022974e916SYuri Benditovich                 VIRTIO_NET_IP4_ADDR_SIZE,
18032974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header));
18042974e916SYuri Benditovich     }
18052974e916SYuri Benditovich 
18062974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
18072974e916SYuri Benditovich }
18082974e916SYuri Benditovich 
18092974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check6(VirtioNetRscChain *chain,
18102974e916SYuri Benditovich                                             struct ip6_header *ip6,
18112974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
18122974e916SYuri Benditovich {
18132974e916SYuri Benditovich     uint16_t ip_len;
18142974e916SYuri Benditovich 
18152974e916SYuri Benditovich     if (((ip6->ip6_ctlun.ip6_un1.ip6_un1_flow & 0xF0) >> 4)
18162974e916SYuri Benditovich         != IP_HEADER_VERSION_6) {
18172974e916SYuri Benditovich         return RSC_BYPASS;
18182974e916SYuri Benditovich     }
18192974e916SYuri Benditovich 
18202974e916SYuri Benditovich     /* Both option and protocol is checked in this */
18212974e916SYuri Benditovich     if (ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) {
18222974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
18232974e916SYuri Benditovich         return RSC_BYPASS;
18242974e916SYuri Benditovich     }
18252974e916SYuri Benditovich 
18262974e916SYuri Benditovich     ip_len = htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
18272974e916SYuri Benditovich     if (ip_len < sizeof(struct tcp_header) ||
18282974e916SYuri Benditovich         ip_len > (size - chain->n->guest_hdr_len - sizeof(struct eth_header)
18292974e916SYuri Benditovich                   - sizeof(struct ip6_header))) {
18302974e916SYuri Benditovich         chain->stat.ip_hacked++;
18312974e916SYuri Benditovich         return RSC_BYPASS;
18322974e916SYuri Benditovich     }
18332974e916SYuri Benditovich 
18342974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
18352974e916SYuri Benditovich     if (IP6_ECN(ip6->ip6_ctlun.ip6_un3.ip6_un3_ecn)) {
18362974e916SYuri Benditovich         chain->stat.ip_ecn++;
18372974e916SYuri Benditovich         return RSC_BYPASS;
18382974e916SYuri Benditovich     }
18392974e916SYuri Benditovich 
18402974e916SYuri Benditovich     return RSC_CANDIDATE;
18412974e916SYuri Benditovich }
18422974e916SYuri Benditovich 
18432974e916SYuri Benditovich static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc,
18442974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
18452974e916SYuri Benditovich {
18462974e916SYuri Benditovich     int32_t ret;
18472974e916SYuri Benditovich     uint16_t hdr_len;
18482974e916SYuri Benditovich     VirtioNetRscChain *chain;
18492974e916SYuri Benditovich     VirtioNetRscUnit unit;
18502974e916SYuri Benditovich 
18512974e916SYuri Benditovich     chain = (VirtioNetRscChain *)opq;
18522974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
18532974e916SYuri Benditovich 
18542974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header)
18552974e916SYuri Benditovich         + sizeof(tcp_header))) {
18562974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18572974e916SYuri Benditovich     }
18582974e916SYuri Benditovich 
18592974e916SYuri Benditovich     virtio_net_rsc_extract_unit6(chain, buf, &unit);
18602974e916SYuri Benditovich     if (RSC_CANDIDATE != virtio_net_rsc_sanity_check6(chain,
18612974e916SYuri Benditovich                                                  unit.ip, buf, size)) {
18622974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18632974e916SYuri Benditovich     }
18642974e916SYuri Benditovich 
18652974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
18662974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
18672974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18682974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
18692974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
18702974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 8),
18712974e916SYuri Benditovich                 VIRTIO_NET_IP6_ADDR_SIZE,
18722974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header)
18732974e916SYuri Benditovich                 + sizeof(struct ip6_header));
18742974e916SYuri Benditovich     }
18752974e916SYuri Benditovich 
18762974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
18772974e916SYuri Benditovich }
18782974e916SYuri Benditovich 
18792974e916SYuri Benditovich static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n,
18802974e916SYuri Benditovich                                                       NetClientState *nc,
18812974e916SYuri Benditovich                                                       uint16_t proto)
18822974e916SYuri Benditovich {
18832974e916SYuri Benditovich     VirtioNetRscChain *chain;
18842974e916SYuri Benditovich 
18852974e916SYuri Benditovich     if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) {
18862974e916SYuri Benditovich         return NULL;
18872974e916SYuri Benditovich     }
18882974e916SYuri Benditovich 
18892974e916SYuri Benditovich     QTAILQ_FOREACH(chain, &n->rsc_chains, next) {
18902974e916SYuri Benditovich         if (chain->proto == proto) {
18912974e916SYuri Benditovich             return chain;
18922974e916SYuri Benditovich         }
18932974e916SYuri Benditovich     }
18942974e916SYuri Benditovich 
18952974e916SYuri Benditovich     chain = g_malloc(sizeof(*chain));
18962974e916SYuri Benditovich     chain->n = n;
18972974e916SYuri Benditovich     chain->proto = proto;
18982974e916SYuri Benditovich     if (proto == (uint16_t)ETH_P_IP) {
18992974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP4_PAYLOAD;
19002974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
19012974e916SYuri Benditovich     } else {
19022974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD;
19032974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
19042974e916SYuri Benditovich     }
19052974e916SYuri Benditovich     chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST,
19062974e916SYuri Benditovich                                       virtio_net_rsc_purge, chain);
19072974e916SYuri Benditovich     memset(&chain->stat, 0, sizeof(chain->stat));
19082974e916SYuri Benditovich 
19092974e916SYuri Benditovich     QTAILQ_INIT(&chain->buffers);
19102974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next);
19112974e916SYuri Benditovich 
19122974e916SYuri Benditovich     return chain;
19132974e916SYuri Benditovich }
19142974e916SYuri Benditovich 
19152974e916SYuri Benditovich static ssize_t virtio_net_rsc_receive(NetClientState *nc,
19162974e916SYuri Benditovich                                       const uint8_t *buf,
19172974e916SYuri Benditovich                                       size_t size)
19182974e916SYuri Benditovich {
19192974e916SYuri Benditovich     uint16_t proto;
19202974e916SYuri Benditovich     VirtioNetRscChain *chain;
19212974e916SYuri Benditovich     struct eth_header *eth;
19222974e916SYuri Benditovich     VirtIONet *n;
19232974e916SYuri Benditovich 
19242974e916SYuri Benditovich     n = qemu_get_nic_opaque(nc);
19252974e916SYuri Benditovich     if (size < (n->host_hdr_len + sizeof(struct eth_header))) {
19262974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19272974e916SYuri Benditovich     }
19282974e916SYuri Benditovich 
19292974e916SYuri Benditovich     eth = (struct eth_header *)(buf + n->guest_hdr_len);
19302974e916SYuri Benditovich     proto = htons(eth->h_proto);
19312974e916SYuri Benditovich 
19322974e916SYuri Benditovich     chain = virtio_net_rsc_lookup_chain(n, nc, proto);
19332974e916SYuri Benditovich     if (chain) {
19342974e916SYuri Benditovich         chain->stat.received++;
19352974e916SYuri Benditovich         if (proto == (uint16_t)ETH_P_IP && n->rsc4_enabled) {
19362974e916SYuri Benditovich             return virtio_net_rsc_receive4(chain, nc, buf, size);
19372974e916SYuri Benditovich         } else if (proto == (uint16_t)ETH_P_IPV6 && n->rsc6_enabled) {
19382974e916SYuri Benditovich             return virtio_net_rsc_receive6(chain, nc, buf, size);
19392974e916SYuri Benditovich         }
19402974e916SYuri Benditovich     }
19412974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
19422974e916SYuri Benditovich }
19432974e916SYuri Benditovich 
19442974e916SYuri Benditovich static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf,
19452974e916SYuri Benditovich                                   size_t size)
19462974e916SYuri Benditovich {
19472974e916SYuri Benditovich     VirtIONet *n = qemu_get_nic_opaque(nc);
19482974e916SYuri Benditovich     if ((n->rsc4_enabled || n->rsc6_enabled)) {
19492974e916SYuri Benditovich         return virtio_net_rsc_receive(nc, buf, size);
19502974e916SYuri Benditovich     } else {
19512974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19522974e916SYuri Benditovich     }
19532974e916SYuri Benditovich }
19542974e916SYuri Benditovich 
19556e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q);
19566e790746SPaolo Bonzini 
19576e790746SPaolo Bonzini static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
19586e790746SPaolo Bonzini {
19596e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
19606e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
196117a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
19626e790746SPaolo Bonzini 
196351b19ebeSPaolo Bonzini     virtqueue_push(q->tx_vq, q->async_tx.elem, 0);
196417a0ca55SKONRAD Frederic     virtio_notify(vdev, q->tx_vq);
19656e790746SPaolo Bonzini 
196651b19ebeSPaolo Bonzini     g_free(q->async_tx.elem);
196751b19ebeSPaolo Bonzini     q->async_tx.elem = NULL;
19686e790746SPaolo Bonzini 
19696e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
19706e790746SPaolo Bonzini     virtio_net_flush_tx(q);
19716e790746SPaolo Bonzini }
19726e790746SPaolo Bonzini 
19736e790746SPaolo Bonzini /* TX */
19746e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
19756e790746SPaolo Bonzini {
19766e790746SPaolo Bonzini     VirtIONet *n = q->n;
197717a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
197851b19ebeSPaolo Bonzini     VirtQueueElement *elem;
19796e790746SPaolo Bonzini     int32_t num_packets = 0;
19806e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(q->tx_vq));
198117a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
19826e790746SPaolo Bonzini         return num_packets;
19836e790746SPaolo Bonzini     }
19846e790746SPaolo Bonzini 
198551b19ebeSPaolo Bonzini     if (q->async_tx.elem) {
19866e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
19876e790746SPaolo Bonzini         return num_packets;
19886e790746SPaolo Bonzini     }
19896e790746SPaolo Bonzini 
199051b19ebeSPaolo Bonzini     for (;;) {
1991bd89dd98SJason Wang         ssize_t ret;
199251b19ebeSPaolo Bonzini         unsigned int out_num;
199351b19ebeSPaolo Bonzini         struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
1994feb93f36SJason Wang         struct virtio_net_hdr_mrg_rxbuf mhdr;
19956e790746SPaolo Bonzini 
199651b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
199751b19ebeSPaolo Bonzini         if (!elem) {
199851b19ebeSPaolo Bonzini             break;
199951b19ebeSPaolo Bonzini         }
200051b19ebeSPaolo Bonzini 
200151b19ebeSPaolo Bonzini         out_num = elem->out_num;
200251b19ebeSPaolo Bonzini         out_sg = elem->out_sg;
20036e790746SPaolo Bonzini         if (out_num < 1) {
2004fa5e56c2SGreg Kurz             virtio_error(vdev, "virtio-net header not in first element");
2005fa5e56c2SGreg Kurz             virtqueue_detach_element(q->tx_vq, elem, 0);
2006fa5e56c2SGreg Kurz             g_free(elem);
2007fa5e56c2SGreg Kurz             return -EINVAL;
20086e790746SPaolo Bonzini         }
20096e790746SPaolo Bonzini 
2010032a74a1SCédric Le Goater         if (n->has_vnet_hdr) {
2011feb93f36SJason Wang             if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
2012feb93f36SJason Wang                 n->guest_hdr_len) {
2013fa5e56c2SGreg Kurz                 virtio_error(vdev, "virtio-net header incorrect");
2014fa5e56c2SGreg Kurz                 virtqueue_detach_element(q->tx_vq, elem, 0);
2015fa5e56c2SGreg Kurz                 g_free(elem);
2016fa5e56c2SGreg Kurz                 return -EINVAL;
2017032a74a1SCédric Le Goater             }
20181bfa316cSGreg Kurz             if (n->needs_vnet_hdr_swap) {
2019feb93f36SJason Wang                 virtio_net_hdr_swap(vdev, (void *) &mhdr);
2020feb93f36SJason Wang                 sg2[0].iov_base = &mhdr;
2021feb93f36SJason Wang                 sg2[0].iov_len = n->guest_hdr_len;
2022feb93f36SJason Wang                 out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
2023feb93f36SJason Wang                                    out_sg, out_num,
2024feb93f36SJason Wang                                    n->guest_hdr_len, -1);
2025feb93f36SJason Wang                 if (out_num == VIRTQUEUE_MAX_SIZE) {
2026feb93f36SJason Wang                     goto drop;
2027032a74a1SCédric Le Goater                 }
2028feb93f36SJason Wang                 out_num += 1;
2029feb93f36SJason Wang                 out_sg = sg2;
2030feb93f36SJason Wang             }
2031feb93f36SJason Wang         }
20326e790746SPaolo Bonzini         /*
20336e790746SPaolo Bonzini          * If host wants to see the guest header as is, we can
20346e790746SPaolo Bonzini          * pass it on unchanged. Otherwise, copy just the parts
20356e790746SPaolo Bonzini          * that host is interested in.
20366e790746SPaolo Bonzini          */
20376e790746SPaolo Bonzini         assert(n->host_hdr_len <= n->guest_hdr_len);
20386e790746SPaolo Bonzini         if (n->host_hdr_len != n->guest_hdr_len) {
20396e790746SPaolo Bonzini             unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg),
20406e790746SPaolo Bonzini                                        out_sg, out_num,
20416e790746SPaolo Bonzini                                        0, n->host_hdr_len);
20426e790746SPaolo Bonzini             sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num,
20436e790746SPaolo Bonzini                              out_sg, out_num,
20446e790746SPaolo Bonzini                              n->guest_hdr_len, -1);
20456e790746SPaolo Bonzini             out_num = sg_num;
20466e790746SPaolo Bonzini             out_sg = sg;
20476e790746SPaolo Bonzini         }
20486e790746SPaolo Bonzini 
20496e790746SPaolo Bonzini         ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
20506e790746SPaolo Bonzini                                       out_sg, out_num, virtio_net_tx_complete);
20516e790746SPaolo Bonzini         if (ret == 0) {
20526e790746SPaolo Bonzini             virtio_queue_set_notification(q->tx_vq, 0);
20536e790746SPaolo Bonzini             q->async_tx.elem = elem;
20546e790746SPaolo Bonzini             return -EBUSY;
20556e790746SPaolo Bonzini         }
20566e790746SPaolo Bonzini 
2057feb93f36SJason Wang drop:
205851b19ebeSPaolo Bonzini         virtqueue_push(q->tx_vq, elem, 0);
205917a0ca55SKONRAD Frederic         virtio_notify(vdev, q->tx_vq);
206051b19ebeSPaolo Bonzini         g_free(elem);
20616e790746SPaolo Bonzini 
20626e790746SPaolo Bonzini         if (++num_packets >= n->tx_burst) {
20636e790746SPaolo Bonzini             break;
20646e790746SPaolo Bonzini         }
20656e790746SPaolo Bonzini     }
20666e790746SPaolo Bonzini     return num_packets;
20676e790746SPaolo Bonzini }
20686e790746SPaolo Bonzini 
20696e790746SPaolo Bonzini static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
20706e790746SPaolo Bonzini {
207117a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
20726e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
20736e790746SPaolo Bonzini 
2074283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2075283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2076283e2c2aSYuri Benditovich         return;
2077283e2c2aSYuri Benditovich     }
2078283e2c2aSYuri Benditovich 
20796e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
208017a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
20816e790746SPaolo Bonzini         q->tx_waiting = 1;
20826e790746SPaolo Bonzini         return;
20836e790746SPaolo Bonzini     }
20846e790746SPaolo Bonzini 
20856e790746SPaolo Bonzini     if (q->tx_waiting) {
20866e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 1);
2087bc72ad67SAlex Bligh         timer_del(q->tx_timer);
20886e790746SPaolo Bonzini         q->tx_waiting = 0;
2089fa5e56c2SGreg Kurz         if (virtio_net_flush_tx(q) == -EINVAL) {
2090fa5e56c2SGreg Kurz             return;
2091fa5e56c2SGreg Kurz         }
20926e790746SPaolo Bonzini     } else {
2093bc72ad67SAlex Bligh         timer_mod(q->tx_timer,
2094bc72ad67SAlex Bligh                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
20956e790746SPaolo Bonzini         q->tx_waiting = 1;
20966e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 0);
20976e790746SPaolo Bonzini     }
20986e790746SPaolo Bonzini }
20996e790746SPaolo Bonzini 
21006e790746SPaolo Bonzini static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
21016e790746SPaolo Bonzini {
210217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
21036e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
21046e790746SPaolo Bonzini 
2105283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2106283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2107283e2c2aSYuri Benditovich         return;
2108283e2c2aSYuri Benditovich     }
2109283e2c2aSYuri Benditovich 
21106e790746SPaolo Bonzini     if (unlikely(q->tx_waiting)) {
21116e790746SPaolo Bonzini         return;
21126e790746SPaolo Bonzini     }
21136e790746SPaolo Bonzini     q->tx_waiting = 1;
21146e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
211517a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
21166e790746SPaolo Bonzini         return;
21176e790746SPaolo Bonzini     }
21186e790746SPaolo Bonzini     virtio_queue_set_notification(vq, 0);
21196e790746SPaolo Bonzini     qemu_bh_schedule(q->tx_bh);
21206e790746SPaolo Bonzini }
21216e790746SPaolo Bonzini 
21226e790746SPaolo Bonzini static void virtio_net_tx_timer(void *opaque)
21236e790746SPaolo Bonzini {
21246e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
21256e790746SPaolo Bonzini     VirtIONet *n = q->n;
212617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2127e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2128e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2129e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2130e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2131e8bcf842SMichael S. Tsirkin         return;
2132e8bcf842SMichael S. Tsirkin     }
21336e790746SPaolo Bonzini 
21346e790746SPaolo Bonzini     q->tx_waiting = 0;
21356e790746SPaolo Bonzini 
21366e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
213717a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
21386e790746SPaolo Bonzini         return;
213917a0ca55SKONRAD Frederic     }
21406e790746SPaolo Bonzini 
21416e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
21426e790746SPaolo Bonzini     virtio_net_flush_tx(q);
21436e790746SPaolo Bonzini }
21446e790746SPaolo Bonzini 
21456e790746SPaolo Bonzini static void virtio_net_tx_bh(void *opaque)
21466e790746SPaolo Bonzini {
21476e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
21486e790746SPaolo Bonzini     VirtIONet *n = q->n;
214917a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
21506e790746SPaolo Bonzini     int32_t ret;
21516e790746SPaolo Bonzini 
2152e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2153e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2154e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2155e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2156e8bcf842SMichael S. Tsirkin         return;
2157e8bcf842SMichael S. Tsirkin     }
21586e790746SPaolo Bonzini 
21596e790746SPaolo Bonzini     q->tx_waiting = 0;
21606e790746SPaolo Bonzini 
21616e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
216217a0ca55SKONRAD Frederic     if (unlikely(!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) {
21636e790746SPaolo Bonzini         return;
216417a0ca55SKONRAD Frederic     }
21656e790746SPaolo Bonzini 
21666e790746SPaolo Bonzini     ret = virtio_net_flush_tx(q);
2167fa5e56c2SGreg Kurz     if (ret == -EBUSY || ret == -EINVAL) {
2168fa5e56c2SGreg Kurz         return; /* Notification re-enable handled by tx_complete or device
2169fa5e56c2SGreg Kurz                  * broken */
21706e790746SPaolo Bonzini     }
21716e790746SPaolo Bonzini 
21726e790746SPaolo Bonzini     /* If we flush a full burst of packets, assume there are
21736e790746SPaolo Bonzini      * more coming and immediately reschedule */
21746e790746SPaolo Bonzini     if (ret >= n->tx_burst) {
21756e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
21766e790746SPaolo Bonzini         q->tx_waiting = 1;
21776e790746SPaolo Bonzini         return;
21786e790746SPaolo Bonzini     }
21796e790746SPaolo Bonzini 
21806e790746SPaolo Bonzini     /* If less than a full burst, re-enable notification and flush
21816e790746SPaolo Bonzini      * anything that may have come in while we weren't looking.  If
21826e790746SPaolo Bonzini      * we find something, assume the guest is still active and reschedule */
21836e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
2184fa5e56c2SGreg Kurz     ret = virtio_net_flush_tx(q);
2185fa5e56c2SGreg Kurz     if (ret == -EINVAL) {
2186fa5e56c2SGreg Kurz         return;
2187fa5e56c2SGreg Kurz     } else if (ret > 0) {
21886e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
21896e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
21906e790746SPaolo Bonzini         q->tx_waiting = 1;
21916e790746SPaolo Bonzini     }
21926e790746SPaolo Bonzini }
21936e790746SPaolo Bonzini 
2194f9d6dbf0SWen Congyang static void virtio_net_add_queue(VirtIONet *n, int index)
2195f9d6dbf0SWen Congyang {
2196f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2197f9d6dbf0SWen Congyang 
21981c0fbfa3SMichael S. Tsirkin     n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size,
21991c0fbfa3SMichael S. Tsirkin                                            virtio_net_handle_rx);
22009b02e161SWei Wang 
2201f9d6dbf0SWen Congyang     if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) {
2202f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
22039b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
22049b02e161SWei Wang                              virtio_net_handle_tx_timer);
2205f9d6dbf0SWen Congyang         n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
2206f9d6dbf0SWen Congyang                                               virtio_net_tx_timer,
2207f9d6dbf0SWen Congyang                                               &n->vqs[index]);
2208f9d6dbf0SWen Congyang     } else {
2209f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
22109b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
22119b02e161SWei Wang                              virtio_net_handle_tx_bh);
2212f9d6dbf0SWen Congyang         n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]);
2213f9d6dbf0SWen Congyang     }
2214f9d6dbf0SWen Congyang 
2215f9d6dbf0SWen Congyang     n->vqs[index].tx_waiting = 0;
2216f9d6dbf0SWen Congyang     n->vqs[index].n = n;
2217f9d6dbf0SWen Congyang }
2218f9d6dbf0SWen Congyang 
2219f9d6dbf0SWen Congyang static void virtio_net_del_queue(VirtIONet *n, int index)
2220f9d6dbf0SWen Congyang {
2221f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2222f9d6dbf0SWen Congyang     VirtIONetQueue *q = &n->vqs[index];
2223f9d6dbf0SWen Congyang     NetClientState *nc = qemu_get_subqueue(n->nic, index);
2224f9d6dbf0SWen Congyang 
2225f9d6dbf0SWen Congyang     qemu_purge_queued_packets(nc);
2226f9d6dbf0SWen Congyang 
2227f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2);
2228f9d6dbf0SWen Congyang     if (q->tx_timer) {
2229f9d6dbf0SWen Congyang         timer_del(q->tx_timer);
2230f9d6dbf0SWen Congyang         timer_free(q->tx_timer);
2231f989c30cSYunjian Wang         q->tx_timer = NULL;
2232f9d6dbf0SWen Congyang     } else {
2233f9d6dbf0SWen Congyang         qemu_bh_delete(q->tx_bh);
2234f989c30cSYunjian Wang         q->tx_bh = NULL;
2235f9d6dbf0SWen Congyang     }
2236f989c30cSYunjian Wang     q->tx_waiting = 0;
2237f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2 + 1);
2238f9d6dbf0SWen Congyang }
2239f9d6dbf0SWen Congyang 
2240f9d6dbf0SWen Congyang static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues)
2241f9d6dbf0SWen Congyang {
2242f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2243f9d6dbf0SWen Congyang     int old_num_queues = virtio_get_num_queues(vdev);
2244f9d6dbf0SWen Congyang     int new_num_queues = new_max_queues * 2 + 1;
2245f9d6dbf0SWen Congyang     int i;
2246f9d6dbf0SWen Congyang 
2247f9d6dbf0SWen Congyang     assert(old_num_queues >= 3);
2248f9d6dbf0SWen Congyang     assert(old_num_queues % 2 == 1);
2249f9d6dbf0SWen Congyang 
2250f9d6dbf0SWen Congyang     if (old_num_queues == new_num_queues) {
2251f9d6dbf0SWen Congyang         return;
2252f9d6dbf0SWen Congyang     }
2253f9d6dbf0SWen Congyang 
2254f9d6dbf0SWen Congyang     /*
2255f9d6dbf0SWen Congyang      * We always need to remove and add ctrl vq if
2256f9d6dbf0SWen Congyang      * old_num_queues != new_num_queues. Remove ctrl_vq first,
2257f9d6dbf0SWen Congyang      * and then we only enter one of the following too loops.
2258f9d6dbf0SWen Congyang      */
2259f9d6dbf0SWen Congyang     virtio_del_queue(vdev, old_num_queues - 1);
2260f9d6dbf0SWen Congyang 
2261f9d6dbf0SWen Congyang     for (i = new_num_queues - 1; i < old_num_queues - 1; i += 2) {
2262f9d6dbf0SWen Congyang         /* new_num_queues < old_num_queues */
2263f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i / 2);
2264f9d6dbf0SWen Congyang     }
2265f9d6dbf0SWen Congyang 
2266f9d6dbf0SWen Congyang     for (i = old_num_queues - 1; i < new_num_queues - 1; i += 2) {
2267f9d6dbf0SWen Congyang         /* new_num_queues > old_num_queues */
2268f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i / 2);
2269f9d6dbf0SWen Congyang     }
2270f9d6dbf0SWen Congyang 
2271f9d6dbf0SWen Congyang     /* add ctrl_vq last */
2272f9d6dbf0SWen Congyang     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
2273f9d6dbf0SWen Congyang }
2274f9d6dbf0SWen Congyang 
2275ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
22766e790746SPaolo Bonzini {
2277f9d6dbf0SWen Congyang     int max = multiqueue ? n->max_queues : 1;
2278f9d6dbf0SWen Congyang 
22796e790746SPaolo Bonzini     n->multiqueue = multiqueue;
2280f9d6dbf0SWen Congyang     virtio_net_change_num_queues(n, max);
22816e790746SPaolo Bonzini 
22826e790746SPaolo Bonzini     virtio_net_set_queues(n);
22836e790746SPaolo Bonzini }
22846e790746SPaolo Bonzini 
2285982b78c5SDr. David Alan Gilbert static int virtio_net_post_load_device(void *opaque, int version_id)
2286037dab2fSGreg Kurz {
2287982b78c5SDr. David Alan Gilbert     VirtIONet *n = opaque;
2288982b78c5SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2289037dab2fSGreg Kurz     int i, link_down;
2290037dab2fSGreg Kurz 
2291*9d8c6a25SDr. David Alan Gilbert     trace_virtio_net_post_load_device();
2292982b78c5SDr. David Alan Gilbert     virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
229395129d6fSCornelia Huck                                virtio_vdev_has_feature(vdev,
229495129d6fSCornelia Huck                                                        VIRTIO_F_VERSION_1));
22956e790746SPaolo Bonzini 
22966e790746SPaolo Bonzini     /* MAC_TABLE_ENTRIES may be different from the saved image */
2297982b78c5SDr. David Alan Gilbert     if (n->mac_table.in_use > MAC_TABLE_ENTRIES) {
22986e790746SPaolo Bonzini         n->mac_table.in_use = 0;
22996e790746SPaolo Bonzini     }
23006e790746SPaolo Bonzini 
2301982b78c5SDr. David Alan Gilbert     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
23026c666823SMichael S. Tsirkin         n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
23036c666823SMichael S. Tsirkin     }
23046c666823SMichael S. Tsirkin 
23056c666823SMichael S. Tsirkin     if (peer_has_vnet_hdr(n)) {
23066c666823SMichael S. Tsirkin         virtio_net_apply_guest_offloads(n);
23076c666823SMichael S. Tsirkin     }
23086c666823SMichael S. Tsirkin 
23096e790746SPaolo Bonzini     virtio_net_set_queues(n);
23106e790746SPaolo Bonzini 
23116e790746SPaolo Bonzini     /* Find the first multicast entry in the saved MAC filter */
23126e790746SPaolo Bonzini     for (i = 0; i < n->mac_table.in_use; i++) {
23136e790746SPaolo Bonzini         if (n->mac_table.macs[i * ETH_ALEN] & 1) {
23146e790746SPaolo Bonzini             break;
23156e790746SPaolo Bonzini         }
23166e790746SPaolo Bonzini     }
23176e790746SPaolo Bonzini     n->mac_table.first_multi = i;
23186e790746SPaolo Bonzini 
23196e790746SPaolo Bonzini     /* nc.link_down can't be migrated, so infer link_down according
23206e790746SPaolo Bonzini      * to link status bit in n->status */
23216e790746SPaolo Bonzini     link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0;
23226e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
23236e790746SPaolo Bonzini         qemu_get_subqueue(n->nic, i)->link_down = link_down;
23246e790746SPaolo Bonzini     }
23256e790746SPaolo Bonzini 
23266c666823SMichael S. Tsirkin     if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
23276c666823SMichael S. Tsirkin         virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
2328*9d8c6a25SDr. David Alan Gilbert         qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(),
2329*9d8c6a25SDr. David Alan Gilbert                                   QEMU_CLOCK_VIRTUAL,
2330*9d8c6a25SDr. David Alan Gilbert                                   virtio_net_announce_timer, n);
2331*9d8c6a25SDr. David Alan Gilbert         if (n->announce_timer.round) {
2332*9d8c6a25SDr. David Alan Gilbert             timer_mod(n->announce_timer.tm,
2333*9d8c6a25SDr. David Alan Gilbert                       qemu_clock_get_ms(n->announce_timer.type));
2334*9d8c6a25SDr. David Alan Gilbert         } else {
2335*9d8c6a25SDr. David Alan Gilbert             qemu_announce_timer_del(&n->announce_timer);
2336*9d8c6a25SDr. David Alan Gilbert         }
23376c666823SMichael S. Tsirkin     }
23386c666823SMichael S. Tsirkin 
23396e790746SPaolo Bonzini     return 0;
23406e790746SPaolo Bonzini }
23416e790746SPaolo Bonzini 
2342982b78c5SDr. David Alan Gilbert /* tx_waiting field of a VirtIONetQueue */
2343982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = {
2344982b78c5SDr. David Alan Gilbert     .name = "virtio-net-queue-tx_waiting",
2345982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2346982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(tx_waiting, VirtIONetQueue),
2347982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2348982b78c5SDr. David Alan Gilbert    },
2349982b78c5SDr. David Alan Gilbert };
2350982b78c5SDr. David Alan Gilbert 
2351982b78c5SDr. David Alan Gilbert static bool max_queues_gt_1(void *opaque, int version_id)
2352982b78c5SDr. David Alan Gilbert {
2353982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->max_queues > 1;
2354982b78c5SDr. David Alan Gilbert }
2355982b78c5SDr. David Alan Gilbert 
2356982b78c5SDr. David Alan Gilbert static bool has_ctrl_guest_offloads(void *opaque, int version_id)
2357982b78c5SDr. David Alan Gilbert {
2358982b78c5SDr. David Alan Gilbert     return virtio_vdev_has_feature(VIRTIO_DEVICE(opaque),
2359982b78c5SDr. David Alan Gilbert                                    VIRTIO_NET_F_CTRL_GUEST_OFFLOADS);
2360982b78c5SDr. David Alan Gilbert }
2361982b78c5SDr. David Alan Gilbert 
2362982b78c5SDr. David Alan Gilbert static bool mac_table_fits(void *opaque, int version_id)
2363982b78c5SDr. David Alan Gilbert {
2364982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->mac_table.in_use <= MAC_TABLE_ENTRIES;
2365982b78c5SDr. David Alan Gilbert }
2366982b78c5SDr. David Alan Gilbert 
2367982b78c5SDr. David Alan Gilbert static bool mac_table_doesnt_fit(void *opaque, int version_id)
2368982b78c5SDr. David Alan Gilbert {
2369982b78c5SDr. David Alan Gilbert     return !mac_table_fits(opaque, version_id);
2370982b78c5SDr. David Alan Gilbert }
2371982b78c5SDr. David Alan Gilbert 
2372982b78c5SDr. David Alan Gilbert /* This temporary type is shared by all the WITH_TMP methods
2373982b78c5SDr. David Alan Gilbert  * although only some fields are used by each.
2374982b78c5SDr. David Alan Gilbert  */
2375982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp {
2376982b78c5SDr. David Alan Gilbert     VirtIONet      *parent;
2377982b78c5SDr. David Alan Gilbert     VirtIONetQueue *vqs_1;
2378982b78c5SDr. David Alan Gilbert     uint16_t        curr_queues_1;
2379982b78c5SDr. David Alan Gilbert     uint8_t         has_ufo;
2380982b78c5SDr. David Alan Gilbert     uint32_t        has_vnet_hdr;
2381982b78c5SDr. David Alan Gilbert };
2382982b78c5SDr. David Alan Gilbert 
2383982b78c5SDr. David Alan Gilbert /* The 2nd and subsequent tx_waiting flags are loaded later than
2384982b78c5SDr. David Alan Gilbert  * the 1st entry in the queues and only if there's more than one
2385982b78c5SDr. David Alan Gilbert  * entry.  We use the tmp mechanism to calculate a temporary
2386982b78c5SDr. David Alan Gilbert  * pointer and count and also validate the count.
2387982b78c5SDr. David Alan Gilbert  */
2388982b78c5SDr. David Alan Gilbert 
238944b1ff31SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_save(void *opaque)
2390982b78c5SDr. David Alan Gilbert {
2391982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2392982b78c5SDr. David Alan Gilbert 
2393982b78c5SDr. David Alan Gilbert     tmp->vqs_1 = tmp->parent->vqs + 1;
2394982b78c5SDr. David Alan Gilbert     tmp->curr_queues_1 = tmp->parent->curr_queues - 1;
2395982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues == 0) {
2396982b78c5SDr. David Alan Gilbert         tmp->curr_queues_1 = 0;
2397982b78c5SDr. David Alan Gilbert     }
239844b1ff31SDr. David Alan Gilbert 
239944b1ff31SDr. David Alan Gilbert     return 0;
2400982b78c5SDr. David Alan Gilbert }
2401982b78c5SDr. David Alan Gilbert 
2402982b78c5SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_load(void *opaque)
2403982b78c5SDr. David Alan Gilbert {
2404982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2405982b78c5SDr. David Alan Gilbert 
2406982b78c5SDr. David Alan Gilbert     /* Reuse the pointer setup from save */
2407982b78c5SDr. David Alan Gilbert     virtio_net_tx_waiting_pre_save(opaque);
2408982b78c5SDr. David Alan Gilbert 
2409982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues > tmp->parent->max_queues) {
2410982b78c5SDr. David Alan Gilbert         error_report("virtio-net: curr_queues %x > max_queues %x",
2411982b78c5SDr. David Alan Gilbert             tmp->parent->curr_queues, tmp->parent->max_queues);
2412982b78c5SDr. David Alan Gilbert 
2413982b78c5SDr. David Alan Gilbert         return -EINVAL;
2414982b78c5SDr. David Alan Gilbert     }
2415982b78c5SDr. David Alan Gilbert 
2416982b78c5SDr. David Alan Gilbert     return 0; /* all good */
2417982b78c5SDr. David Alan Gilbert }
2418982b78c5SDr. David Alan Gilbert 
2419982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_tx_waiting = {
2420982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-tx_waiting",
2421982b78c5SDr. David Alan Gilbert     .pre_load  = virtio_net_tx_waiting_pre_load,
2422982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_tx_waiting_pre_save,
2423982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2424982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp,
2425982b78c5SDr. David Alan Gilbert                                      curr_queues_1,
2426982b78c5SDr. David Alan Gilbert                                      vmstate_virtio_net_queue_tx_waiting,
2427982b78c5SDr. David Alan Gilbert                                      struct VirtIONetQueue),
2428982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2429982b78c5SDr. David Alan Gilbert     },
2430982b78c5SDr. David Alan Gilbert };
2431982b78c5SDr. David Alan Gilbert 
2432982b78c5SDr. David Alan Gilbert /* the 'has_ufo' flag is just tested; if the incoming stream has the
2433982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2434982b78c5SDr. David Alan Gilbert  */
2435982b78c5SDr. David Alan Gilbert static int virtio_net_ufo_post_load(void *opaque, int version_id)
2436982b78c5SDr. David Alan Gilbert {
2437982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2438982b78c5SDr. David Alan Gilbert 
2439982b78c5SDr. David Alan Gilbert     if (tmp->has_ufo && !peer_has_ufo(tmp->parent)) {
2440982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires TUN_F_UFO support");
2441982b78c5SDr. David Alan Gilbert         return -EINVAL;
2442982b78c5SDr. David Alan Gilbert     }
2443982b78c5SDr. David Alan Gilbert 
2444982b78c5SDr. David Alan Gilbert     return 0;
2445982b78c5SDr. David Alan Gilbert }
2446982b78c5SDr. David Alan Gilbert 
244744b1ff31SDr. David Alan Gilbert static int virtio_net_ufo_pre_save(void *opaque)
2448982b78c5SDr. David Alan Gilbert {
2449982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2450982b78c5SDr. David Alan Gilbert 
2451982b78c5SDr. David Alan Gilbert     tmp->has_ufo = tmp->parent->has_ufo;
245244b1ff31SDr. David Alan Gilbert 
245344b1ff31SDr. David Alan Gilbert     return 0;
2454982b78c5SDr. David Alan Gilbert }
2455982b78c5SDr. David Alan Gilbert 
2456982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_ufo = {
2457982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-ufo",
2458982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_ufo_post_load,
2459982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_ufo_pre_save,
2460982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2461982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(has_ufo, struct VirtIONetMigTmp),
2462982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2463982b78c5SDr. David Alan Gilbert     },
2464982b78c5SDr. David Alan Gilbert };
2465982b78c5SDr. David Alan Gilbert 
2466982b78c5SDr. David Alan Gilbert /* the 'has_vnet_hdr' flag is just tested; if the incoming stream has the
2467982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2468982b78c5SDr. David Alan Gilbert  */
2469982b78c5SDr. David Alan Gilbert static int virtio_net_vnet_post_load(void *opaque, int version_id)
2470982b78c5SDr. David Alan Gilbert {
2471982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2472982b78c5SDr. David Alan Gilbert 
2473982b78c5SDr. David Alan Gilbert     if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) {
2474982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires vnet_hdr=on");
2475982b78c5SDr. David Alan Gilbert         return -EINVAL;
2476982b78c5SDr. David Alan Gilbert     }
2477982b78c5SDr. David Alan Gilbert 
2478982b78c5SDr. David Alan Gilbert     return 0;
2479982b78c5SDr. David Alan Gilbert }
2480982b78c5SDr. David Alan Gilbert 
248144b1ff31SDr. David Alan Gilbert static int virtio_net_vnet_pre_save(void *opaque)
2482982b78c5SDr. David Alan Gilbert {
2483982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2484982b78c5SDr. David Alan Gilbert 
2485982b78c5SDr. David Alan Gilbert     tmp->has_vnet_hdr = tmp->parent->has_vnet_hdr;
248644b1ff31SDr. David Alan Gilbert 
248744b1ff31SDr. David Alan Gilbert     return 0;
2488982b78c5SDr. David Alan Gilbert }
2489982b78c5SDr. David Alan Gilbert 
2490982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_vnet = {
2491982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-vnet",
2492982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_vnet_post_load,
2493982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_vnet_pre_save,
2494982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2495982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(has_vnet_hdr, struct VirtIONetMigTmp),
2496982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2497982b78c5SDr. David Alan Gilbert     },
2498982b78c5SDr. David Alan Gilbert };
2499982b78c5SDr. David Alan Gilbert 
2500982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_device = {
2501982b78c5SDr. David Alan Gilbert     .name = "virtio-net-device",
2502982b78c5SDr. David Alan Gilbert     .version_id = VIRTIO_NET_VM_VERSION,
2503982b78c5SDr. David Alan Gilbert     .minimum_version_id = VIRTIO_NET_VM_VERSION,
2504982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_post_load_device,
2505982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2506982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN),
2507982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_POINTER(vqs, VirtIONet,
2508982b78c5SDr. David Alan Gilbert                                vmstate_virtio_net_queue_tx_waiting,
2509982b78c5SDr. David Alan Gilbert                                VirtIONetQueue),
2510982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mergeable_rx_bufs, VirtIONet),
2511982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16(status, VirtIONet),
2512982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(promisc, VirtIONet),
2513982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(allmulti, VirtIONet),
2514982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mac_table.in_use, VirtIONet),
2515982b78c5SDr. David Alan Gilbert 
2516982b78c5SDr. David Alan Gilbert         /* Guarded pair: If it fits we load it, else we throw it away
2517982b78c5SDr. David Alan Gilbert          * - can happen if source has a larger MAC table.; post-load
2518982b78c5SDr. David Alan Gilbert          *  sets flags in this case.
2519982b78c5SDr. David Alan Gilbert          */
2520982b78c5SDr. David Alan Gilbert         VMSTATE_VBUFFER_MULTIPLY(mac_table.macs, VirtIONet,
2521982b78c5SDr. David Alan Gilbert                                 0, mac_table_fits, mac_table.in_use,
2522982b78c5SDr. David Alan Gilbert                                  ETH_ALEN),
2523982b78c5SDr. David Alan Gilbert         VMSTATE_UNUSED_VARRAY_UINT32(VirtIONet, mac_table_doesnt_fit, 0,
2524982b78c5SDr. David Alan Gilbert                                      mac_table.in_use, ETH_ALEN),
2525982b78c5SDr. David Alan Gilbert 
2526982b78c5SDr. David Alan Gilbert         /* Note: This is an array of uint32's that's always been saved as a
2527982b78c5SDr. David Alan Gilbert          * buffer; hold onto your endiannesses; it's actually used as a bitmap
2528982b78c5SDr. David Alan Gilbert          * but based on the uint.
2529982b78c5SDr. David Alan Gilbert          */
2530982b78c5SDr. David Alan Gilbert         VMSTATE_BUFFER_POINTER_UNSAFE(vlans, VirtIONet, 0, MAX_VLAN >> 3),
2531982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2532982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_vnet),
2533982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.multi_overflow, VirtIONet),
2534982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.uni_overflow, VirtIONet),
2535982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(alluni, VirtIONet),
2536982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nomulti, VirtIONet),
2537982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nouni, VirtIONet),
2538982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nobcast, VirtIONet),
2539982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2540982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_ufo),
2541982b78c5SDr. David Alan Gilbert         VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0,
2542982b78c5SDr. David Alan Gilbert                             vmstate_info_uint16_equal, uint16_t),
2543982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1),
2544982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2545982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_tx_waiting),
2546982b78c5SDr. David Alan Gilbert         VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet,
2547982b78c5SDr. David Alan Gilbert                             has_ctrl_guest_offloads),
2548982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2549982b78c5SDr. David Alan Gilbert    },
2550982b78c5SDr. David Alan Gilbert };
2551982b78c5SDr. David Alan Gilbert 
25526e790746SPaolo Bonzini static NetClientInfo net_virtio_info = {
2553f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
25546e790746SPaolo Bonzini     .size = sizeof(NICState),
25556e790746SPaolo Bonzini     .can_receive = virtio_net_can_receive,
25566e790746SPaolo Bonzini     .receive = virtio_net_receive,
25576e790746SPaolo Bonzini     .link_status_changed = virtio_net_set_link_status,
2558b1be4280SAmos Kong     .query_rx_filter = virtio_net_query_rxfilter,
25596e790746SPaolo Bonzini };
25606e790746SPaolo Bonzini 
25616e790746SPaolo Bonzini static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
25626e790746SPaolo Bonzini {
256317a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
25646e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
25656e790746SPaolo Bonzini     assert(n->vhost_started);
2566ed8b4afeSNikolay Nikolaev     return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx);
25676e790746SPaolo Bonzini }
25686e790746SPaolo Bonzini 
25696e790746SPaolo Bonzini static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
25706e790746SPaolo Bonzini                                            bool mask)
25716e790746SPaolo Bonzini {
257217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
25736e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
25746e790746SPaolo Bonzini     assert(n->vhost_started);
2575ed8b4afeSNikolay Nikolaev     vhost_net_virtqueue_mask(get_vhost_net(nc->peer),
25766e790746SPaolo Bonzini                              vdev, idx, mask);
25776e790746SPaolo Bonzini }
25786e790746SPaolo Bonzini 
2579019a3edbSGerd Hoffmann static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features)
25806e790746SPaolo Bonzini {
25810cd09c3aSCornelia Huck     virtio_add_feature(&host_features, VIRTIO_NET_F_MAC);
2582a93e599dSMaxime Coquelin 
2583ba550851SStefano Garzarella     n->config_size = virtio_feature_get_config_size(feature_sizes,
2584ba550851SStefano Garzarella                                                     host_features);
258517ec5a86SKONRAD Frederic }
25866e790746SPaolo Bonzini 
25878a253ec2SKONRAD Frederic void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
25888a253ec2SKONRAD Frederic                                    const char *type)
25898a253ec2SKONRAD Frederic {
25908a253ec2SKONRAD Frederic     /*
25918a253ec2SKONRAD Frederic      * The name can be NULL, the netclient name will be type.x.
25928a253ec2SKONRAD Frederic      */
25938a253ec2SKONRAD Frederic     assert(type != NULL);
25948a253ec2SKONRAD Frederic 
25958a253ec2SKONRAD Frederic     g_free(n->netclient_name);
25968a253ec2SKONRAD Frederic     g_free(n->netclient_type);
25978a253ec2SKONRAD Frederic     n->netclient_name = g_strdup(name);
25988a253ec2SKONRAD Frederic     n->netclient_type = g_strdup(type);
25998a253ec2SKONRAD Frederic }
26008a253ec2SKONRAD Frederic 
2601e6f746b3SAndreas Färber static void virtio_net_device_realize(DeviceState *dev, Error **errp)
260217ec5a86SKONRAD Frederic {
2603e6f746b3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
2604284a32f0SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
2605284a32f0SAndreas Färber     NetClientState *nc;
26061773d9eeSKONRAD Frederic     int i;
260717ec5a86SKONRAD Frederic 
2608a93e599dSMaxime Coquelin     if (n->net_conf.mtu) {
2609127833eeSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_MTU);
2610a93e599dSMaxime Coquelin     }
2611a93e599dSMaxime Coquelin 
26129473939eSJason Baron     if (n->net_conf.duplex_str) {
26139473939eSJason Baron         if (strncmp(n->net_conf.duplex_str, "half", 5) == 0) {
26149473939eSJason Baron             n->net_conf.duplex = DUPLEX_HALF;
26159473939eSJason Baron         } else if (strncmp(n->net_conf.duplex_str, "full", 5) == 0) {
26169473939eSJason Baron             n->net_conf.duplex = DUPLEX_FULL;
26179473939eSJason Baron         } else {
26189473939eSJason Baron             error_setg(errp, "'duplex' must be 'half' or 'full'");
26199473939eSJason Baron         }
26209473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
26219473939eSJason Baron     } else {
26229473939eSJason Baron         n->net_conf.duplex = DUPLEX_UNKNOWN;
26239473939eSJason Baron     }
26249473939eSJason Baron 
26259473939eSJason Baron     if (n->net_conf.speed < SPEED_UNKNOWN) {
26269473939eSJason Baron         error_setg(errp, "'speed' must be between 0 and INT_MAX");
26279473939eSJason Baron     } else if (n->net_conf.speed >= 0) {
26289473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
26299473939eSJason Baron     }
26309473939eSJason Baron 
2631da3e8a23SShannon Zhao     virtio_net_set_config_size(n, n->host_features);
2632284a32f0SAndreas Färber     virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size);
263317ec5a86SKONRAD Frederic 
26341c0fbfa3SMichael S. Tsirkin     /*
26351c0fbfa3SMichael S. Tsirkin      * We set a lower limit on RX queue size to what it always was.
26361c0fbfa3SMichael S. Tsirkin      * Guests that want a smaller ring can always resize it without
26371c0fbfa3SMichael S. Tsirkin      * help from us (using virtio 1 and up).
26381c0fbfa3SMichael S. Tsirkin      */
26391c0fbfa3SMichael S. Tsirkin     if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE ||
26401c0fbfa3SMichael S. Tsirkin         n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE ||
26415f997fd1SMichal Privoznik         !is_power_of_2(n->net_conf.rx_queue_size)) {
26421c0fbfa3SMichael S. Tsirkin         error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), "
26431c0fbfa3SMichael S. Tsirkin                    "must be a power of 2 between %d and %d.",
26441c0fbfa3SMichael S. Tsirkin                    n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE,
26451c0fbfa3SMichael S. Tsirkin                    VIRTQUEUE_MAX_SIZE);
26461c0fbfa3SMichael S. Tsirkin         virtio_cleanup(vdev);
26471c0fbfa3SMichael S. Tsirkin         return;
26481c0fbfa3SMichael S. Tsirkin     }
26491c0fbfa3SMichael S. Tsirkin 
26509b02e161SWei Wang     if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE ||
26519b02e161SWei Wang         n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE ||
26529b02e161SWei Wang         !is_power_of_2(n->net_conf.tx_queue_size)) {
26539b02e161SWei Wang         error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), "
26549b02e161SWei Wang                    "must be a power of 2 between %d and %d",
26559b02e161SWei Wang                    n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE,
26569b02e161SWei Wang                    VIRTQUEUE_MAX_SIZE);
26579b02e161SWei Wang         virtio_cleanup(vdev);
26589b02e161SWei Wang         return;
26599b02e161SWei Wang     }
26609b02e161SWei Wang 
2661575a1c0eSJiri Pirko     n->max_queues = MAX(n->nic_conf.peers.queues, 1);
266287b3bd1cSJason Wang     if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) {
26637e0e736eSJason Wang         error_setg(errp, "Invalid number of queues (= %" PRIu32 "), "
2664631b22eaSStefan Weil                    "must be a positive integer less than %d.",
266587b3bd1cSJason Wang                    n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2);
26667e0e736eSJason Wang         virtio_cleanup(vdev);
26677e0e736eSJason Wang         return;
26687e0e736eSJason Wang     }
26696e790746SPaolo Bonzini     n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues);
26706e790746SPaolo Bonzini     n->curr_queues = 1;
26711773d9eeSKONRAD Frederic     n->tx_timeout = n->net_conf.txtimer;
26726e790746SPaolo Bonzini 
26731773d9eeSKONRAD Frederic     if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer")
26741773d9eeSKONRAD Frederic                        && strcmp(n->net_conf.tx, "bh")) {
26750765691eSMarkus Armbruster         warn_report("virtio-net: "
26766e790746SPaolo Bonzini                     "Unknown option tx=%s, valid options: \"timer\" \"bh\"",
26771773d9eeSKONRAD Frederic                     n->net_conf.tx);
26780765691eSMarkus Armbruster         error_printf("Defaulting to \"bh\"");
26796e790746SPaolo Bonzini     }
26806e790746SPaolo Bonzini 
26812eef278bSMichael S. Tsirkin     n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n),
26822eef278bSMichael S. Tsirkin                                     n->net_conf.tx_queue_size);
26839b02e161SWei Wang 
2684da51a335SJason Wang     for (i = 0; i < n->max_queues; i++) {
2685f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i);
2686da51a335SJason Wang     }
2687da51a335SJason Wang 
268817a0ca55SKONRAD Frederic     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
26891773d9eeSKONRAD Frederic     qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);
26901773d9eeSKONRAD Frederic     memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac));
26916e790746SPaolo Bonzini     n->status = VIRTIO_NET_S_LINK_UP;
2692*9d8c6a25SDr. David Alan Gilbert     qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(),
2693*9d8c6a25SDr. David Alan Gilbert                               QEMU_CLOCK_VIRTUAL,
2694f57fcf70SJason Wang                               virtio_net_announce_timer, n);
26956e790746SPaolo Bonzini 
26968a253ec2SKONRAD Frederic     if (n->netclient_type) {
26978a253ec2SKONRAD Frederic         /*
26988a253ec2SKONRAD Frederic          * Happen when virtio_net_set_netclient_name has been called.
26998a253ec2SKONRAD Frederic          */
27008a253ec2SKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
27018a253ec2SKONRAD Frederic                               n->netclient_type, n->netclient_name, n);
27028a253ec2SKONRAD Frederic     } else {
27031773d9eeSKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
2704284a32f0SAndreas Färber                               object_get_typename(OBJECT(dev)), dev->id, n);
27058a253ec2SKONRAD Frederic     }
27068a253ec2SKONRAD Frederic 
27076e790746SPaolo Bonzini     peer_test_vnet_hdr(n);
27086e790746SPaolo Bonzini     if (peer_has_vnet_hdr(n)) {
27096e790746SPaolo Bonzini         for (i = 0; i < n->max_queues; i++) {
2710d6085e3aSStefan Hajnoczi             qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true);
27116e790746SPaolo Bonzini         }
27126e790746SPaolo Bonzini         n->host_hdr_len = sizeof(struct virtio_net_hdr);
27136e790746SPaolo Bonzini     } else {
27146e790746SPaolo Bonzini         n->host_hdr_len = 0;
27156e790746SPaolo Bonzini     }
27166e790746SPaolo Bonzini 
27171773d9eeSKONRAD Frederic     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->nic_conf.macaddr.a);
27186e790746SPaolo Bonzini 
27196e790746SPaolo Bonzini     n->vqs[0].tx_waiting = 0;
27201773d9eeSKONRAD Frederic     n->tx_burst = n->net_conf.txburst;
2721bb9d17f8SCornelia Huck     virtio_net_set_mrg_rx_bufs(n, 0, 0);
27226e790746SPaolo Bonzini     n->promisc = 1; /* for compatibility */
27236e790746SPaolo Bonzini 
27246e790746SPaolo Bonzini     n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
27256e790746SPaolo Bonzini 
27266e790746SPaolo Bonzini     n->vlans = g_malloc0(MAX_VLAN >> 3);
27276e790746SPaolo Bonzini 
2728b1be4280SAmos Kong     nc = qemu_get_queue(n->nic);
2729b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
2730b1be4280SAmos Kong 
27312974e916SYuri Benditovich     QTAILQ_INIT(&n->rsc_chains);
2732284a32f0SAndreas Färber     n->qdev = dev;
273317ec5a86SKONRAD Frederic }
273417ec5a86SKONRAD Frederic 
2735306ec6c3SAndreas Färber static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
273617ec5a86SKONRAD Frederic {
2737306ec6c3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
2738306ec6c3SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
2739f9d6dbf0SWen Congyang     int i, max_queues;
274017ec5a86SKONRAD Frederic 
274117ec5a86SKONRAD Frederic     /* This will stop vhost backend if appropriate. */
274217ec5a86SKONRAD Frederic     virtio_net_set_status(vdev, 0);
274317ec5a86SKONRAD Frederic 
27448a253ec2SKONRAD Frederic     g_free(n->netclient_name);
27458a253ec2SKONRAD Frederic     n->netclient_name = NULL;
27468a253ec2SKONRAD Frederic     g_free(n->netclient_type);
27478a253ec2SKONRAD Frederic     n->netclient_type = NULL;
27488a253ec2SKONRAD Frederic 
274917ec5a86SKONRAD Frederic     g_free(n->mac_table.macs);
275017ec5a86SKONRAD Frederic     g_free(n->vlans);
275117ec5a86SKONRAD Frederic 
2752f9d6dbf0SWen Congyang     max_queues = n->multiqueue ? n->max_queues : 1;
2753f9d6dbf0SWen Congyang     for (i = 0; i < max_queues; i++) {
2754f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i);
275517ec5a86SKONRAD Frederic     }
275617ec5a86SKONRAD Frederic 
2757*9d8c6a25SDr. David Alan Gilbert     qemu_announce_timer_del(&n->announce_timer);
275817ec5a86SKONRAD Frederic     g_free(n->vqs);
275917ec5a86SKONRAD Frederic     qemu_del_nic(n->nic);
27602974e916SYuri Benditovich     virtio_net_rsc_cleanup(n);
27616a1a8cc7SKONRAD Frederic     virtio_cleanup(vdev);
276217ec5a86SKONRAD Frederic }
276317ec5a86SKONRAD Frederic 
276417ec5a86SKONRAD Frederic static void virtio_net_instance_init(Object *obj)
276517ec5a86SKONRAD Frederic {
276617ec5a86SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(obj);
276717ec5a86SKONRAD Frederic 
276817ec5a86SKONRAD Frederic     /*
276917ec5a86SKONRAD Frederic      * The default config_size is sizeof(struct virtio_net_config).
277017ec5a86SKONRAD Frederic      * Can be overriden with virtio_net_set_config_size.
277117ec5a86SKONRAD Frederic      */
277217ec5a86SKONRAD Frederic     n->config_size = sizeof(struct virtio_net_config);
2773aa4197c3SGonglei     device_add_bootindex_property(obj, &n->nic_conf.bootindex,
2774aa4197c3SGonglei                                   "bootindex", "/ethernet-phy@0",
2775aa4197c3SGonglei                                   DEVICE(n), NULL);
277617ec5a86SKONRAD Frederic }
277717ec5a86SKONRAD Frederic 
277844b1ff31SDr. David Alan Gilbert static int virtio_net_pre_save(void *opaque)
27794d45dcfbSHalil Pasic {
27804d45dcfbSHalil Pasic     VirtIONet *n = opaque;
27814d45dcfbSHalil Pasic 
27824d45dcfbSHalil Pasic     /* At this point, backend must be stopped, otherwise
27834d45dcfbSHalil Pasic      * it might keep writing to memory. */
27844d45dcfbSHalil Pasic     assert(!n->vhost_started);
278544b1ff31SDr. David Alan Gilbert 
278644b1ff31SDr. David Alan Gilbert     return 0;
27874d45dcfbSHalil Pasic }
27884d45dcfbSHalil Pasic 
27894d45dcfbSHalil Pasic static const VMStateDescription vmstate_virtio_net = {
27904d45dcfbSHalil Pasic     .name = "virtio-net",
27914d45dcfbSHalil Pasic     .minimum_version_id = VIRTIO_NET_VM_VERSION,
27924d45dcfbSHalil Pasic     .version_id = VIRTIO_NET_VM_VERSION,
27934d45dcfbSHalil Pasic     .fields = (VMStateField[]) {
27944d45dcfbSHalil Pasic         VMSTATE_VIRTIO_DEVICE,
27954d45dcfbSHalil Pasic         VMSTATE_END_OF_LIST()
27964d45dcfbSHalil Pasic     },
27974d45dcfbSHalil Pasic     .pre_save = virtio_net_pre_save,
27984d45dcfbSHalil Pasic };
2799290c2428SDr. David Alan Gilbert 
280017ec5a86SKONRAD Frederic static Property virtio_net_properties[] = {
2801127833eeSJason Baron     DEFINE_PROP_BIT64("csum", VirtIONet, host_features,
2802127833eeSJason Baron                     VIRTIO_NET_F_CSUM, true),
2803127833eeSJason Baron     DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features,
280487108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_CSUM, true),
2805127833eeSJason Baron     DEFINE_PROP_BIT64("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true),
2806127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso4", VirtIONet, host_features,
280787108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO4, true),
2808127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso6", VirtIONet, host_features,
280987108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO6, true),
2810127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ecn", VirtIONet, host_features,
281187108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ECN, true),
2812127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ufo", VirtIONet, host_features,
281387108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_UFO, true),
2814127833eeSJason Baron     DEFINE_PROP_BIT64("guest_announce", VirtIONet, host_features,
281587108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ANNOUNCE, true),
2816127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso4", VirtIONet, host_features,
281787108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO4, true),
2818127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso6", VirtIONet, host_features,
281987108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO6, true),
2820127833eeSJason Baron     DEFINE_PROP_BIT64("host_ecn", VirtIONet, host_features,
282187108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_ECN, true),
2822127833eeSJason Baron     DEFINE_PROP_BIT64("host_ufo", VirtIONet, host_features,
282387108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_UFO, true),
2824127833eeSJason Baron     DEFINE_PROP_BIT64("mrg_rxbuf", VirtIONet, host_features,
282587108bb2SShannon Zhao                     VIRTIO_NET_F_MRG_RXBUF, true),
2826127833eeSJason Baron     DEFINE_PROP_BIT64("status", VirtIONet, host_features,
282787108bb2SShannon Zhao                     VIRTIO_NET_F_STATUS, true),
2828127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vq", VirtIONet, host_features,
282987108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VQ, true),
2830127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx", VirtIONet, host_features,
283187108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX, true),
2832127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vlan", VirtIONet, host_features,
283387108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VLAN, true),
2834127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx_extra", VirtIONet, host_features,
283587108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX_EXTRA, true),
2836127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_mac_addr", VirtIONet, host_features,
283787108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_MAC_ADDR, true),
2838127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features,
283987108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
2840127833eeSJason Baron     DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
28412974e916SYuri Benditovich     DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features,
28422974e916SYuri Benditovich                     VIRTIO_NET_F_RSC_EXT, false),
28432974e916SYuri Benditovich     DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout,
28442974e916SYuri Benditovich                        VIRTIO_NET_RSC_DEFAULT_INTERVAL),
284517ec5a86SKONRAD Frederic     DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf),
284617ec5a86SKONRAD Frederic     DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer,
284717ec5a86SKONRAD Frederic                        TX_TIMER_INTERVAL),
284817ec5a86SKONRAD Frederic     DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST),
284917ec5a86SKONRAD Frederic     DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx),
28501c0fbfa3SMichael S. Tsirkin     DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size,
28511c0fbfa3SMichael S. Tsirkin                        VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE),
28529b02e161SWei Wang     DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size,
28539b02e161SWei Wang                        VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE),
2854a93e599dSMaxime Coquelin     DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0),
285575ebec11SMaxime Coquelin     DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend,
285675ebec11SMaxime Coquelin                      true),
28579473939eSJason Baron     DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN),
28589473939eSJason Baron     DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str),
285917ec5a86SKONRAD Frederic     DEFINE_PROP_END_OF_LIST(),
286017ec5a86SKONRAD Frederic };
286117ec5a86SKONRAD Frederic 
286217ec5a86SKONRAD Frederic static void virtio_net_class_init(ObjectClass *klass, void *data)
286317ec5a86SKONRAD Frederic {
286417ec5a86SKONRAD Frederic     DeviceClass *dc = DEVICE_CLASS(klass);
286517ec5a86SKONRAD Frederic     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
2866e6f746b3SAndreas Färber 
286717ec5a86SKONRAD Frederic     dc->props = virtio_net_properties;
2868290c2428SDr. David Alan Gilbert     dc->vmsd = &vmstate_virtio_net;
2869125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
2870e6f746b3SAndreas Färber     vdc->realize = virtio_net_device_realize;
2871306ec6c3SAndreas Färber     vdc->unrealize = virtio_net_device_unrealize;
287217ec5a86SKONRAD Frederic     vdc->get_config = virtio_net_get_config;
287317ec5a86SKONRAD Frederic     vdc->set_config = virtio_net_set_config;
287417ec5a86SKONRAD Frederic     vdc->get_features = virtio_net_get_features;
287517ec5a86SKONRAD Frederic     vdc->set_features = virtio_net_set_features;
287617ec5a86SKONRAD Frederic     vdc->bad_features = virtio_net_bad_features;
287717ec5a86SKONRAD Frederic     vdc->reset = virtio_net_reset;
287817ec5a86SKONRAD Frederic     vdc->set_status = virtio_net_set_status;
287917ec5a86SKONRAD Frederic     vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
288017ec5a86SKONRAD Frederic     vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
28812a083ffdSMichael S. Tsirkin     vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO);
2882982b78c5SDr. David Alan Gilbert     vdc->vmsd = &vmstate_virtio_net_device;
288317ec5a86SKONRAD Frederic }
288417ec5a86SKONRAD Frederic 
288517ec5a86SKONRAD Frederic static const TypeInfo virtio_net_info = {
288617ec5a86SKONRAD Frederic     .name = TYPE_VIRTIO_NET,
288717ec5a86SKONRAD Frederic     .parent = TYPE_VIRTIO_DEVICE,
288817ec5a86SKONRAD Frederic     .instance_size = sizeof(VirtIONet),
288917ec5a86SKONRAD Frederic     .instance_init = virtio_net_instance_init,
289017ec5a86SKONRAD Frederic     .class_init = virtio_net_class_init,
289117ec5a86SKONRAD Frederic };
289217ec5a86SKONRAD Frederic 
289317ec5a86SKONRAD Frederic static void virtio_register_types(void)
289417ec5a86SKONRAD Frederic {
289517ec5a86SKONRAD Frederic     type_register_static(&virtio_net_info);
289617ec5a86SKONRAD Frederic }
289717ec5a86SKONRAD Frederic 
289817ec5a86SKONRAD Frederic type_init(virtio_register_types)
2899