xref: /openbmc/qemu/hw/net/virtio-net.c (revision ba550851f5de642b8a1359e2c5c27cbb5a52ae61)
16e790746SPaolo Bonzini /*
26e790746SPaolo Bonzini  * Virtio Network Device
36e790746SPaolo Bonzini  *
46e790746SPaolo Bonzini  * Copyright IBM, Corp. 2007
56e790746SPaolo Bonzini  *
66e790746SPaolo Bonzini  * Authors:
76e790746SPaolo Bonzini  *  Anthony Liguori   <aliguori@us.ibm.com>
86e790746SPaolo Bonzini  *
96e790746SPaolo Bonzini  * This work is licensed under the terms of the GNU GPL, version 2.  See
106e790746SPaolo Bonzini  * the COPYING file in the top-level directory.
116e790746SPaolo Bonzini  *
126e790746SPaolo Bonzini  */
136e790746SPaolo Bonzini 
149b8bfe21SPeter Maydell #include "qemu/osdep.h"
156e790746SPaolo Bonzini #include "qemu/iov.h"
166e790746SPaolo Bonzini #include "hw/virtio/virtio.h"
176e790746SPaolo Bonzini #include "net/net.h"
186e790746SPaolo Bonzini #include "net/checksum.h"
196e790746SPaolo Bonzini #include "net/tap.h"
206e790746SPaolo Bonzini #include "qemu/error-report.h"
216e790746SPaolo Bonzini #include "qemu/timer.h"
226e790746SPaolo Bonzini #include "hw/virtio/virtio-net.h"
236e790746SPaolo Bonzini #include "net/vhost_net.h"
2417ec5a86SKONRAD Frederic #include "hw/virtio/virtio-bus.h"
25e688df6bSMarkus Armbruster #include "qapi/error.h"
269af23989SMarkus Armbruster #include "qapi/qapi-events-net.h"
271399c60dSRusty Russell #include "hw/virtio/virtio-access.h"
28f8d806c9SJuan Quintela #include "migration/misc.h"
299473939eSJason Baron #include "standard-headers/linux/ethtool.h"
306e790746SPaolo Bonzini 
316e790746SPaolo Bonzini #define VIRTIO_NET_VM_VERSION    11
326e790746SPaolo Bonzini 
336e790746SPaolo Bonzini #define MAC_TABLE_ENTRIES    64
346e790746SPaolo Bonzini #define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
356e790746SPaolo Bonzini 
361c0fbfa3SMichael S. Tsirkin /* previously fixed value */
371c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256
389b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256
399b02e161SWei Wang 
401c0fbfa3SMichael S. Tsirkin /* for now, only allow larger queues; with virtio-1, guest can downsize */
411c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE
429b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE
431c0fbfa3SMichael S. Tsirkin 
442974e916SYuri Benditovich #define VIRTIO_NET_IP4_ADDR_SIZE   8        /* ipv4 saddr + daddr */
452974e916SYuri Benditovich 
462974e916SYuri Benditovich #define VIRTIO_NET_TCP_FLAG         0x3F
472974e916SYuri Benditovich #define VIRTIO_NET_TCP_HDR_LENGTH   0xF000
482974e916SYuri Benditovich 
492974e916SYuri Benditovich /* IPv4 max payload, 16 bits in the header */
502974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP4_PAYLOAD (65535 - sizeof(struct ip_header))
512974e916SYuri Benditovich #define VIRTIO_NET_MAX_TCP_PAYLOAD 65535
522974e916SYuri Benditovich 
532974e916SYuri Benditovich /* header length value in ip header without option */
542974e916SYuri Benditovich #define VIRTIO_NET_IP4_HEADER_LENGTH 5
552974e916SYuri Benditovich 
562974e916SYuri Benditovich #define VIRTIO_NET_IP6_ADDR_SIZE   32      /* ipv6 saddr + daddr */
572974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP6_PAYLOAD VIRTIO_NET_MAX_TCP_PAYLOAD
582974e916SYuri Benditovich 
592974e916SYuri Benditovich /* Purge coalesced packets timer interval, This value affects the performance
602974e916SYuri Benditovich    a lot, and should be tuned carefully, '300000'(300us) is the recommended
612974e916SYuri Benditovich    value to pass the WHQL test, '50000' can gain 2x netperf throughput with
622974e916SYuri Benditovich    tso/gso/gro 'off'. */
632974e916SYuri Benditovich #define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000
642974e916SYuri Benditovich 
652974e916SYuri Benditovich /* temporary until standard header include it */
662974e916SYuri Benditovich #if !defined(VIRTIO_NET_HDR_F_RSC_INFO)
672974e916SYuri Benditovich 
682974e916SYuri Benditovich #define VIRTIO_NET_HDR_F_RSC_INFO  4 /* rsc_ext data in csum_ fields */
69d47e5e31SYuri Benditovich #define VIRTIO_NET_F_RSC_EXT       61
702974e916SYuri Benditovich 
712974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_packets(
722974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
732974e916SYuri Benditovich {
742974e916SYuri Benditovich     return &hdr->csum_start;
752974e916SYuri Benditovich }
762974e916SYuri Benditovich 
772974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
782974e916SYuri Benditovich     struct virtio_net_hdr *hdr)
792974e916SYuri Benditovich {
802974e916SYuri Benditovich     return &hdr->csum_offset;
812974e916SYuri Benditovich }
822974e916SYuri Benditovich 
832974e916SYuri Benditovich #endif
842974e916SYuri Benditovich 
856e790746SPaolo Bonzini static VirtIOFeature feature_sizes[] = {
86127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MAC,
87*ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, mac)},
88127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_STATUS,
89*ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, status)},
90127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MQ,
91*ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, max_virtqueue_pairs)},
92127833eeSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_MTU,
93*ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, mtu)},
949473939eSJason Baron     {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
95*ba550851SStefano Garzarella      .end = virtio_endof(struct virtio_net_config, duplex)},
966e790746SPaolo Bonzini     {}
976e790746SPaolo Bonzini };
986e790746SPaolo Bonzini 
996e790746SPaolo Bonzini static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc)
1006e790746SPaolo Bonzini {
1016e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
1026e790746SPaolo Bonzini 
1036e790746SPaolo Bonzini     return &n->vqs[nc->queue_index];
1046e790746SPaolo Bonzini }
1056e790746SPaolo Bonzini 
1066e790746SPaolo Bonzini static int vq2q(int queue_index)
1076e790746SPaolo Bonzini {
1086e790746SPaolo Bonzini     return queue_index / 2;
1096e790746SPaolo Bonzini }
1106e790746SPaolo Bonzini 
1116e790746SPaolo Bonzini /* TODO
1126e790746SPaolo Bonzini  * - we could suppress RX interrupt if we were so inclined.
1136e790746SPaolo Bonzini  */
1146e790746SPaolo Bonzini 
1156e790746SPaolo Bonzini static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
1166e790746SPaolo Bonzini {
11717a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1186e790746SPaolo Bonzini     struct virtio_net_config netcfg;
1196e790746SPaolo Bonzini 
1201399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.status, n->status);
1211399c60dSRusty Russell     virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues);
122a93e599dSMaxime Coquelin     virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu);
1236e790746SPaolo Bonzini     memcpy(netcfg.mac, n->mac, ETH_ALEN);
1249473939eSJason Baron     virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed);
1259473939eSJason Baron     netcfg.duplex = n->net_conf.duplex;
1266e790746SPaolo Bonzini     memcpy(config, &netcfg, n->config_size);
1276e790746SPaolo Bonzini }
1286e790746SPaolo Bonzini 
1296e790746SPaolo Bonzini static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
1306e790746SPaolo Bonzini {
13117a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
1326e790746SPaolo Bonzini     struct virtio_net_config netcfg = {};
1336e790746SPaolo Bonzini 
1346e790746SPaolo Bonzini     memcpy(&netcfg, config, n->config_size);
1356e790746SPaolo Bonzini 
13695129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
13795129d6fSCornelia Huck         !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) &&
1386e790746SPaolo Bonzini         memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
1396e790746SPaolo Bonzini         memcpy(n->mac, netcfg.mac, ETH_ALEN);
1406e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
1416e790746SPaolo Bonzini     }
1426e790746SPaolo Bonzini }
1436e790746SPaolo Bonzini 
1446e790746SPaolo Bonzini static bool virtio_net_started(VirtIONet *n, uint8_t status)
1456e790746SPaolo Bonzini {
14617a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1476e790746SPaolo Bonzini     return (status & VIRTIO_CONFIG_S_DRIVER_OK) &&
14817a0ca55SKONRAD Frederic         (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running;
1496e790746SPaolo Bonzini }
1506e790746SPaolo Bonzini 
151f57fcf70SJason Wang static void virtio_net_announce_timer(void *opaque)
152f57fcf70SJason Wang {
153f57fcf70SJason Wang     VirtIONet *n = opaque;
154f57fcf70SJason Wang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
155f57fcf70SJason Wang 
156f57fcf70SJason Wang     n->announce_counter--;
157f57fcf70SJason Wang     n->status |= VIRTIO_NET_S_ANNOUNCE;
158f57fcf70SJason Wang     virtio_notify_config(vdev);
159f57fcf70SJason Wang }
160f57fcf70SJason Wang 
1616e790746SPaolo Bonzini static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
1626e790746SPaolo Bonzini {
16317a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
1646e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
1656e790746SPaolo Bonzini     int queues = n->multiqueue ? n->max_queues : 1;
1666e790746SPaolo Bonzini 
167ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
1686e790746SPaolo Bonzini         return;
1696e790746SPaolo Bonzini     }
1706e790746SPaolo Bonzini 
1718c1ac475SRadim Krčmář     if ((virtio_net_started(n, status) && !nc->peer->link_down) ==
1728c1ac475SRadim Krčmář         !!n->vhost_started) {
1736e790746SPaolo Bonzini         return;
1746e790746SPaolo Bonzini     }
1756e790746SPaolo Bonzini     if (!n->vhost_started) {
176086abc1cSMichael S. Tsirkin         int r, i;
177086abc1cSMichael S. Tsirkin 
1781bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
1791bfa316cSGreg Kurz             error_report("backend does not support %s vnet headers; "
1801bfa316cSGreg Kurz                          "falling back on userspace virtio",
1811bfa316cSGreg Kurz                          virtio_is_big_endian(vdev) ? "BE" : "LE");
1821bfa316cSGreg Kurz             return;
1831bfa316cSGreg Kurz         }
1841bfa316cSGreg Kurz 
185086abc1cSMichael S. Tsirkin         /* Any packets outstanding? Purge them to avoid touching rings
186086abc1cSMichael S. Tsirkin          * when vhost is running.
187086abc1cSMichael S. Tsirkin          */
188086abc1cSMichael S. Tsirkin         for (i = 0;  i < queues; i++) {
189086abc1cSMichael S. Tsirkin             NetClientState *qnc = qemu_get_subqueue(n->nic, i);
190086abc1cSMichael S. Tsirkin 
191086abc1cSMichael S. Tsirkin             /* Purge both directions: TX and RX. */
192086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->peer->incoming_queue, qnc);
193086abc1cSMichael S. Tsirkin             qemu_net_queue_purge(qnc->incoming_queue, qnc->peer);
194086abc1cSMichael S. Tsirkin         }
195086abc1cSMichael S. Tsirkin 
196a93e599dSMaxime Coquelin         if (virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MTU)) {
197a93e599dSMaxime Coquelin             r = vhost_net_set_mtu(get_vhost_net(nc->peer), n->net_conf.mtu);
198a93e599dSMaxime Coquelin             if (r < 0) {
199a93e599dSMaxime Coquelin                 error_report("%uBytes MTU not supported by the backend",
200a93e599dSMaxime Coquelin                              n->net_conf.mtu);
201a93e599dSMaxime Coquelin 
202a93e599dSMaxime Coquelin                 return;
203a93e599dSMaxime Coquelin             }
204a93e599dSMaxime Coquelin         }
205a93e599dSMaxime Coquelin 
2066e790746SPaolo Bonzini         n->vhost_started = 1;
20717a0ca55SKONRAD Frederic         r = vhost_net_start(vdev, n->nic->ncs, queues);
2086e790746SPaolo Bonzini         if (r < 0) {
2096e790746SPaolo Bonzini             error_report("unable to start vhost net: %d: "
2106e790746SPaolo Bonzini                          "falling back on userspace virtio", -r);
2116e790746SPaolo Bonzini             n->vhost_started = 0;
2126e790746SPaolo Bonzini         }
2136e790746SPaolo Bonzini     } else {
21417a0ca55SKONRAD Frederic         vhost_net_stop(vdev, n->nic->ncs, queues);
2156e790746SPaolo Bonzini         n->vhost_started = 0;
2166e790746SPaolo Bonzini     }
2176e790746SPaolo Bonzini }
2186e790746SPaolo Bonzini 
2191bfa316cSGreg Kurz static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev,
2201bfa316cSGreg Kurz                                           NetClientState *peer,
2211bfa316cSGreg Kurz                                           bool enable)
2221bfa316cSGreg Kurz {
2231bfa316cSGreg Kurz     if (virtio_is_big_endian(vdev)) {
2241bfa316cSGreg Kurz         return qemu_set_vnet_be(peer, enable);
2251bfa316cSGreg Kurz     } else {
2261bfa316cSGreg Kurz         return qemu_set_vnet_le(peer, enable);
2271bfa316cSGreg Kurz     }
2281bfa316cSGreg Kurz }
2291bfa316cSGreg Kurz 
2301bfa316cSGreg Kurz static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs,
2311bfa316cSGreg Kurz                                        int queues, bool enable)
2321bfa316cSGreg Kurz {
2331bfa316cSGreg Kurz     int i;
2341bfa316cSGreg Kurz 
2351bfa316cSGreg Kurz     for (i = 0; i < queues; i++) {
2361bfa316cSGreg Kurz         if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 &&
2371bfa316cSGreg Kurz             enable) {
2381bfa316cSGreg Kurz             while (--i >= 0) {
2391bfa316cSGreg Kurz                 virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false);
2401bfa316cSGreg Kurz             }
2411bfa316cSGreg Kurz 
2421bfa316cSGreg Kurz             return true;
2431bfa316cSGreg Kurz         }
2441bfa316cSGreg Kurz     }
2451bfa316cSGreg Kurz 
2461bfa316cSGreg Kurz     return false;
2471bfa316cSGreg Kurz }
2481bfa316cSGreg Kurz 
2491bfa316cSGreg Kurz static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status)
2501bfa316cSGreg Kurz {
2511bfa316cSGreg Kurz     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2521bfa316cSGreg Kurz     int queues = n->multiqueue ? n->max_queues : 1;
2531bfa316cSGreg Kurz 
2541bfa316cSGreg Kurz     if (virtio_net_started(n, status)) {
2551bfa316cSGreg Kurz         /* Before using the device, we tell the network backend about the
2561bfa316cSGreg Kurz          * endianness to use when parsing vnet headers. If the backend
2571bfa316cSGreg Kurz          * can't do it, we fallback onto fixing the headers in the core
2581bfa316cSGreg Kurz          * virtio-net code.
2591bfa316cSGreg Kurz          */
2601bfa316cSGreg Kurz         n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs,
2611bfa316cSGreg Kurz                                                             queues, true);
2621bfa316cSGreg Kurz     } else if (virtio_net_started(n, vdev->status)) {
2631bfa316cSGreg Kurz         /* After using the device, we need to reset the network backend to
2641bfa316cSGreg Kurz          * the default (guest native endianness), otherwise the guest may
2651bfa316cSGreg Kurz          * lose network connectivity if it is rebooted into a different
2661bfa316cSGreg Kurz          * endianness.
2671bfa316cSGreg Kurz          */
2681bfa316cSGreg Kurz         virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false);
2691bfa316cSGreg Kurz     }
2701bfa316cSGreg Kurz }
2711bfa316cSGreg Kurz 
272283e2c2aSYuri Benditovich static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq)
273283e2c2aSYuri Benditovich {
274283e2c2aSYuri Benditovich     unsigned int dropped = virtqueue_drop_all(vq);
275283e2c2aSYuri Benditovich     if (dropped) {
276283e2c2aSYuri Benditovich         virtio_notify(vdev, vq);
277283e2c2aSYuri Benditovich     }
278283e2c2aSYuri Benditovich }
279283e2c2aSYuri Benditovich 
2806e790746SPaolo Bonzini static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
2816e790746SPaolo Bonzini {
28217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
2836e790746SPaolo Bonzini     VirtIONetQueue *q;
2846e790746SPaolo Bonzini     int i;
2856e790746SPaolo Bonzini     uint8_t queue_status;
2866e790746SPaolo Bonzini 
2871bfa316cSGreg Kurz     virtio_net_vnet_endian_status(n, status);
2886e790746SPaolo Bonzini     virtio_net_vhost_status(n, status);
2896e790746SPaolo Bonzini 
2906e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
29138705bb5SFam Zheng         NetClientState *ncs = qemu_get_subqueue(n->nic, i);
29238705bb5SFam Zheng         bool queue_started;
2936e790746SPaolo Bonzini         q = &n->vqs[i];
2946e790746SPaolo Bonzini 
2956e790746SPaolo Bonzini         if ((!n->multiqueue && i != 0) || i >= n->curr_queues) {
2966e790746SPaolo Bonzini             queue_status = 0;
2976e790746SPaolo Bonzini         } else {
2986e790746SPaolo Bonzini             queue_status = status;
2996e790746SPaolo Bonzini         }
30038705bb5SFam Zheng         queue_started =
30138705bb5SFam Zheng             virtio_net_started(n, queue_status) && !n->vhost_started;
30238705bb5SFam Zheng 
30338705bb5SFam Zheng         if (queue_started) {
30438705bb5SFam Zheng             qemu_flush_queued_packets(ncs);
30538705bb5SFam Zheng         }
3066e790746SPaolo Bonzini 
3076e790746SPaolo Bonzini         if (!q->tx_waiting) {
3086e790746SPaolo Bonzini             continue;
3096e790746SPaolo Bonzini         }
3106e790746SPaolo Bonzini 
31138705bb5SFam Zheng         if (queue_started) {
3126e790746SPaolo Bonzini             if (q->tx_timer) {
313bc72ad67SAlex Bligh                 timer_mod(q->tx_timer,
314bc72ad67SAlex Bligh                                qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
3156e790746SPaolo Bonzini             } else {
3166e790746SPaolo Bonzini                 qemu_bh_schedule(q->tx_bh);
3176e790746SPaolo Bonzini             }
3186e790746SPaolo Bonzini         } else {
3196e790746SPaolo Bonzini             if (q->tx_timer) {
320bc72ad67SAlex Bligh                 timer_del(q->tx_timer);
3216e790746SPaolo Bonzini             } else {
3226e790746SPaolo Bonzini                 qemu_bh_cancel(q->tx_bh);
3236e790746SPaolo Bonzini             }
324283e2c2aSYuri Benditovich             if ((n->status & VIRTIO_NET_S_LINK_UP) == 0 &&
32570e53e6eSJason Wang                 (queue_status & VIRTIO_CONFIG_S_DRIVER_OK) &&
32670e53e6eSJason Wang                 vdev->vm_running) {
327283e2c2aSYuri Benditovich                 /* if tx is waiting we are likely have some packets in tx queue
328283e2c2aSYuri Benditovich                  * and disabled notification */
329283e2c2aSYuri Benditovich                 q->tx_waiting = 0;
330283e2c2aSYuri Benditovich                 virtio_queue_set_notification(q->tx_vq, 1);
331283e2c2aSYuri Benditovich                 virtio_net_drop_tx_queue_data(vdev, q->tx_vq);
332283e2c2aSYuri Benditovich             }
3336e790746SPaolo Bonzini         }
3346e790746SPaolo Bonzini     }
3356e790746SPaolo Bonzini }
3366e790746SPaolo Bonzini 
3376e790746SPaolo Bonzini static void virtio_net_set_link_status(NetClientState *nc)
3386e790746SPaolo Bonzini {
3396e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
34017a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
3416e790746SPaolo Bonzini     uint16_t old_status = n->status;
3426e790746SPaolo Bonzini 
3436e790746SPaolo Bonzini     if (nc->link_down)
3446e790746SPaolo Bonzini         n->status &= ~VIRTIO_NET_S_LINK_UP;
3456e790746SPaolo Bonzini     else
3466e790746SPaolo Bonzini         n->status |= VIRTIO_NET_S_LINK_UP;
3476e790746SPaolo Bonzini 
3486e790746SPaolo Bonzini     if (n->status != old_status)
34917a0ca55SKONRAD Frederic         virtio_notify_config(vdev);
3506e790746SPaolo Bonzini 
35117a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
3526e790746SPaolo Bonzini }
3536e790746SPaolo Bonzini 
354b1be4280SAmos Kong static void rxfilter_notify(NetClientState *nc)
355b1be4280SAmos Kong {
356b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
357b1be4280SAmos Kong 
358b1be4280SAmos Kong     if (nc->rxfilter_notify_enabled) {
35996e35046SAmos Kong         gchar *path = object_get_canonical_path(OBJECT(n->qdev));
36006150279SWenchao Xia         qapi_event_send_nic_rx_filter_changed(!!n->netclient_name,
3613ab72385SPeter Xu                                               n->netclient_name, path);
36296e35046SAmos Kong         g_free(path);
363b1be4280SAmos Kong 
364b1be4280SAmos Kong         /* disable event notification to avoid events flooding */
365b1be4280SAmos Kong         nc->rxfilter_notify_enabled = 0;
366b1be4280SAmos Kong     }
367b1be4280SAmos Kong }
368b1be4280SAmos Kong 
369f7bc8ef8SAmos Kong static intList *get_vlan_table(VirtIONet *n)
370f7bc8ef8SAmos Kong {
371f7bc8ef8SAmos Kong     intList *list, *entry;
372f7bc8ef8SAmos Kong     int i, j;
373f7bc8ef8SAmos Kong 
374f7bc8ef8SAmos Kong     list = NULL;
375f7bc8ef8SAmos Kong     for (i = 0; i < MAX_VLAN >> 5; i++) {
376f7bc8ef8SAmos Kong         for (j = 0; n->vlans[i] && j <= 0x1f; j++) {
377f7bc8ef8SAmos Kong             if (n->vlans[i] & (1U << j)) {
378f7bc8ef8SAmos Kong                 entry = g_malloc0(sizeof(*entry));
379f7bc8ef8SAmos Kong                 entry->value = (i << 5) + j;
380f7bc8ef8SAmos Kong                 entry->next = list;
381f7bc8ef8SAmos Kong                 list = entry;
382f7bc8ef8SAmos Kong             }
383f7bc8ef8SAmos Kong         }
384f7bc8ef8SAmos Kong     }
385f7bc8ef8SAmos Kong 
386f7bc8ef8SAmos Kong     return list;
387f7bc8ef8SAmos Kong }
388f7bc8ef8SAmos Kong 
389b1be4280SAmos Kong static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
390b1be4280SAmos Kong {
391b1be4280SAmos Kong     VirtIONet *n = qemu_get_nic_opaque(nc);
392f7bc8ef8SAmos Kong     VirtIODevice *vdev = VIRTIO_DEVICE(n);
393b1be4280SAmos Kong     RxFilterInfo *info;
394b1be4280SAmos Kong     strList *str_list, *entry;
395f7bc8ef8SAmos Kong     int i;
396b1be4280SAmos Kong 
397b1be4280SAmos Kong     info = g_malloc0(sizeof(*info));
398b1be4280SAmos Kong     info->name = g_strdup(nc->name);
399b1be4280SAmos Kong     info->promiscuous = n->promisc;
400b1be4280SAmos Kong 
401b1be4280SAmos Kong     if (n->nouni) {
402b1be4280SAmos Kong         info->unicast = RX_STATE_NONE;
403b1be4280SAmos Kong     } else if (n->alluni) {
404b1be4280SAmos Kong         info->unicast = RX_STATE_ALL;
405b1be4280SAmos Kong     } else {
406b1be4280SAmos Kong         info->unicast = RX_STATE_NORMAL;
407b1be4280SAmos Kong     }
408b1be4280SAmos Kong 
409b1be4280SAmos Kong     if (n->nomulti) {
410b1be4280SAmos Kong         info->multicast = RX_STATE_NONE;
411b1be4280SAmos Kong     } else if (n->allmulti) {
412b1be4280SAmos Kong         info->multicast = RX_STATE_ALL;
413b1be4280SAmos Kong     } else {
414b1be4280SAmos Kong         info->multicast = RX_STATE_NORMAL;
415b1be4280SAmos Kong     }
416b1be4280SAmos Kong 
417b1be4280SAmos Kong     info->broadcast_allowed = n->nobcast;
418b1be4280SAmos Kong     info->multicast_overflow = n->mac_table.multi_overflow;
419b1be4280SAmos Kong     info->unicast_overflow = n->mac_table.uni_overflow;
420b1be4280SAmos Kong 
421b0575ba4SScott Feldman     info->main_mac = qemu_mac_strdup_printf(n->mac);
422b1be4280SAmos Kong 
423b1be4280SAmos Kong     str_list = NULL;
424b1be4280SAmos Kong     for (i = 0; i < n->mac_table.first_multi; i++) {
425b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
426b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
427b1be4280SAmos Kong         entry->next = str_list;
428b1be4280SAmos Kong         str_list = entry;
429b1be4280SAmos Kong     }
430b1be4280SAmos Kong     info->unicast_table = str_list;
431b1be4280SAmos Kong 
432b1be4280SAmos Kong     str_list = NULL;
433b1be4280SAmos Kong     for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
434b1be4280SAmos Kong         entry = g_malloc0(sizeof(*entry));
435b0575ba4SScott Feldman         entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
436b1be4280SAmos Kong         entry->next = str_list;
437b1be4280SAmos Kong         str_list = entry;
438b1be4280SAmos Kong     }
439b1be4280SAmos Kong     info->multicast_table = str_list;
440f7bc8ef8SAmos Kong     info->vlan_table = get_vlan_table(n);
441b1be4280SAmos Kong 
44295129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) {
443f7bc8ef8SAmos Kong         info->vlan = RX_STATE_ALL;
444f7bc8ef8SAmos Kong     } else if (!info->vlan_table) {
445f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NONE;
446f7bc8ef8SAmos Kong     } else {
447f7bc8ef8SAmos Kong         info->vlan = RX_STATE_NORMAL;
448b1be4280SAmos Kong     }
449b1be4280SAmos Kong 
450b1be4280SAmos Kong     /* enable event notification after query */
451b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
452b1be4280SAmos Kong 
453b1be4280SAmos Kong     return info;
454b1be4280SAmos Kong }
455b1be4280SAmos Kong 
4566e790746SPaolo Bonzini static void virtio_net_reset(VirtIODevice *vdev)
4576e790746SPaolo Bonzini {
45817a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
45994b52958SGreg Kurz     int i;
4606e790746SPaolo Bonzini 
4616e790746SPaolo Bonzini     /* Reset back to compatibility mode */
4626e790746SPaolo Bonzini     n->promisc = 1;
4636e790746SPaolo Bonzini     n->allmulti = 0;
4646e790746SPaolo Bonzini     n->alluni = 0;
4656e790746SPaolo Bonzini     n->nomulti = 0;
4666e790746SPaolo Bonzini     n->nouni = 0;
4676e790746SPaolo Bonzini     n->nobcast = 0;
4686e790746SPaolo Bonzini     /* multiqueue is disabled by default */
4696e790746SPaolo Bonzini     n->curr_queues = 1;
470f57fcf70SJason Wang     timer_del(n->announce_timer);
471f57fcf70SJason Wang     n->announce_counter = 0;
472f57fcf70SJason Wang     n->status &= ~VIRTIO_NET_S_ANNOUNCE;
4736e790746SPaolo Bonzini 
4746e790746SPaolo Bonzini     /* Flush any MAC and VLAN filter table state */
4756e790746SPaolo Bonzini     n->mac_table.in_use = 0;
4766e790746SPaolo Bonzini     n->mac_table.first_multi = 0;
4776e790746SPaolo Bonzini     n->mac_table.multi_overflow = 0;
4786e790746SPaolo Bonzini     n->mac_table.uni_overflow = 0;
4796e790746SPaolo Bonzini     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
4806e790746SPaolo Bonzini     memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac));
481702d66a8SMichael S. Tsirkin     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
4826e790746SPaolo Bonzini     memset(n->vlans, 0, MAX_VLAN >> 3);
48394b52958SGreg Kurz 
48494b52958SGreg Kurz     /* Flush any async TX */
48594b52958SGreg Kurz     for (i = 0;  i < n->max_queues; i++) {
48694b52958SGreg Kurz         NetClientState *nc = qemu_get_subqueue(n->nic, i);
48794b52958SGreg Kurz 
48894b52958SGreg Kurz         if (nc->peer) {
48994b52958SGreg Kurz             qemu_flush_or_purge_queued_packets(nc->peer, true);
49094b52958SGreg Kurz             assert(!virtio_net_get_subqueue(nc)->async_tx.elem);
49194b52958SGreg Kurz         }
49294b52958SGreg Kurz     }
4936e790746SPaolo Bonzini }
4946e790746SPaolo Bonzini 
4956e790746SPaolo Bonzini static void peer_test_vnet_hdr(VirtIONet *n)
4966e790746SPaolo Bonzini {
4976e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
4986e790746SPaolo Bonzini     if (!nc->peer) {
4996e790746SPaolo Bonzini         return;
5006e790746SPaolo Bonzini     }
5016e790746SPaolo Bonzini 
502d6085e3aSStefan Hajnoczi     n->has_vnet_hdr = qemu_has_vnet_hdr(nc->peer);
5036e790746SPaolo Bonzini }
5046e790746SPaolo Bonzini 
5056e790746SPaolo Bonzini static int peer_has_vnet_hdr(VirtIONet *n)
5066e790746SPaolo Bonzini {
5076e790746SPaolo Bonzini     return n->has_vnet_hdr;
5086e790746SPaolo Bonzini }
5096e790746SPaolo Bonzini 
5106e790746SPaolo Bonzini static int peer_has_ufo(VirtIONet *n)
5116e790746SPaolo Bonzini {
5126e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n))
5136e790746SPaolo Bonzini         return 0;
5146e790746SPaolo Bonzini 
515d6085e3aSStefan Hajnoczi     n->has_ufo = qemu_has_ufo(qemu_get_queue(n->nic)->peer);
5166e790746SPaolo Bonzini 
5176e790746SPaolo Bonzini     return n->has_ufo;
5186e790746SPaolo Bonzini }
5196e790746SPaolo Bonzini 
520bb9d17f8SCornelia Huck static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
521bb9d17f8SCornelia Huck                                        int version_1)
5226e790746SPaolo Bonzini {
5236e790746SPaolo Bonzini     int i;
5246e790746SPaolo Bonzini     NetClientState *nc;
5256e790746SPaolo Bonzini 
5266e790746SPaolo Bonzini     n->mergeable_rx_bufs = mergeable_rx_bufs;
5276e790746SPaolo Bonzini 
528bb9d17f8SCornelia Huck     if (version_1) {
529bb9d17f8SCornelia Huck         n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
530bb9d17f8SCornelia Huck     } else {
5316e790746SPaolo Bonzini         n->guest_hdr_len = n->mergeable_rx_bufs ?
532bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr_mrg_rxbuf) :
533bb9d17f8SCornelia Huck             sizeof(struct virtio_net_hdr);
534bb9d17f8SCornelia Huck     }
5356e790746SPaolo Bonzini 
5366e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
5376e790746SPaolo Bonzini         nc = qemu_get_subqueue(n->nic, i);
5386e790746SPaolo Bonzini 
5396e790746SPaolo Bonzini         if (peer_has_vnet_hdr(n) &&
540d6085e3aSStefan Hajnoczi             qemu_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) {
541d6085e3aSStefan Hajnoczi             qemu_set_vnet_hdr_len(nc->peer, n->guest_hdr_len);
5426e790746SPaolo Bonzini             n->host_hdr_len = n->guest_hdr_len;
5436e790746SPaolo Bonzini         }
5446e790746SPaolo Bonzini     }
5456e790746SPaolo Bonzini }
5466e790746SPaolo Bonzini 
5472eef278bSMichael S. Tsirkin static int virtio_net_max_tx_queue_size(VirtIONet *n)
5482eef278bSMichael S. Tsirkin {
5492eef278bSMichael S. Tsirkin     NetClientState *peer = n->nic_conf.peers.ncs[0];
5502eef278bSMichael S. Tsirkin 
5512eef278bSMichael S. Tsirkin     /*
5522eef278bSMichael S. Tsirkin      * Backends other than vhost-user don't support max queue size.
5532eef278bSMichael S. Tsirkin      */
5542eef278bSMichael S. Tsirkin     if (!peer) {
5552eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
5562eef278bSMichael S. Tsirkin     }
5572eef278bSMichael S. Tsirkin 
5582eef278bSMichael S. Tsirkin     if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) {
5592eef278bSMichael S. Tsirkin         return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE;
5602eef278bSMichael S. Tsirkin     }
5612eef278bSMichael S. Tsirkin 
5622eef278bSMichael S. Tsirkin     return VIRTQUEUE_MAX_SIZE;
5632eef278bSMichael S. Tsirkin }
5642eef278bSMichael S. Tsirkin 
5656e790746SPaolo Bonzini static int peer_attach(VirtIONet *n, int index)
5666e790746SPaolo Bonzini {
5676e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
5686e790746SPaolo Bonzini 
5696e790746SPaolo Bonzini     if (!nc->peer) {
5706e790746SPaolo Bonzini         return 0;
5716e790746SPaolo Bonzini     }
5726e790746SPaolo Bonzini 
573f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
5747263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 1);
5757263a0adSChangchun Ouyang     }
5767263a0adSChangchun Ouyang 
577f394b2e2SEric Blake     if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
5786e790746SPaolo Bonzini         return 0;
5796e790746SPaolo Bonzini     }
5806e790746SPaolo Bonzini 
5811074b879SJason Wang     if (n->max_queues == 1) {
5821074b879SJason Wang         return 0;
5831074b879SJason Wang     }
5841074b879SJason Wang 
5856e790746SPaolo Bonzini     return tap_enable(nc->peer);
5866e790746SPaolo Bonzini }
5876e790746SPaolo Bonzini 
5886e790746SPaolo Bonzini static int peer_detach(VirtIONet *n, int index)
5896e790746SPaolo Bonzini {
5906e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, index);
5916e790746SPaolo Bonzini 
5926e790746SPaolo Bonzini     if (!nc->peer) {
5936e790746SPaolo Bonzini         return 0;
5946e790746SPaolo Bonzini     }
5956e790746SPaolo Bonzini 
596f394b2e2SEric Blake     if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
5977263a0adSChangchun Ouyang         vhost_set_vring_enable(nc->peer, 0);
5987263a0adSChangchun Ouyang     }
5997263a0adSChangchun Ouyang 
600f394b2e2SEric Blake     if (nc->peer->info->type !=  NET_CLIENT_DRIVER_TAP) {
6016e790746SPaolo Bonzini         return 0;
6026e790746SPaolo Bonzini     }
6036e790746SPaolo Bonzini 
6046e790746SPaolo Bonzini     return tap_disable(nc->peer);
6056e790746SPaolo Bonzini }
6066e790746SPaolo Bonzini 
6076e790746SPaolo Bonzini static void virtio_net_set_queues(VirtIONet *n)
6086e790746SPaolo Bonzini {
6096e790746SPaolo Bonzini     int i;
610ddfa83eaSJoel Stanley     int r;
6116e790746SPaolo Bonzini 
61268b5f314SYuri Benditovich     if (n->nic->peer_deleted) {
61368b5f314SYuri Benditovich         return;
61468b5f314SYuri Benditovich     }
61568b5f314SYuri Benditovich 
6166e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
6176e790746SPaolo Bonzini         if (i < n->curr_queues) {
618ddfa83eaSJoel Stanley             r = peer_attach(n, i);
619ddfa83eaSJoel Stanley             assert(!r);
6206e790746SPaolo Bonzini         } else {
621ddfa83eaSJoel Stanley             r = peer_detach(n, i);
622ddfa83eaSJoel Stanley             assert(!r);
6236e790746SPaolo Bonzini         }
6246e790746SPaolo Bonzini     }
6256e790746SPaolo Bonzini }
6266e790746SPaolo Bonzini 
627ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue);
6286e790746SPaolo Bonzini 
6299d5b731dSJason Wang static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
6309d5b731dSJason Wang                                         Error **errp)
6316e790746SPaolo Bonzini {
63217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
6336e790746SPaolo Bonzini     NetClientState *nc = qemu_get_queue(n->nic);
6346e790746SPaolo Bonzini 
635da3e8a23SShannon Zhao     /* Firstly sync all virtio-net possible supported features */
636da3e8a23SShannon Zhao     features |= n->host_features;
637da3e8a23SShannon Zhao 
6380cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
6396e790746SPaolo Bonzini 
6406e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n)) {
6410cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_CSUM);
6420cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4);
6430cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6);
6440cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN);
6456e790746SPaolo Bonzini 
6460cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM);
6470cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4);
6480cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6);
6490cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN);
6506e790746SPaolo Bonzini     }
6516e790746SPaolo Bonzini 
6526e790746SPaolo Bonzini     if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) {
6530cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO);
6540cd09c3aSCornelia Huck         virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO);
6556e790746SPaolo Bonzini     }
6566e790746SPaolo Bonzini 
657ed8b4afeSNikolay Nikolaev     if (!get_vhost_net(nc->peer)) {
6586e790746SPaolo Bonzini         return features;
6596e790746SPaolo Bonzini     }
6602974e916SYuri Benditovich 
66175ebec11SMaxime Coquelin     features = vhost_net_get_features(get_vhost_net(nc->peer), features);
66275ebec11SMaxime Coquelin     vdev->backend_features = features;
66375ebec11SMaxime Coquelin 
66475ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
66575ebec11SMaxime Coquelin             (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) {
66675ebec11SMaxime Coquelin         features |= (1ULL << VIRTIO_NET_F_MTU);
66775ebec11SMaxime Coquelin     }
66875ebec11SMaxime Coquelin 
66975ebec11SMaxime Coquelin     return features;
6706e790746SPaolo Bonzini }
6716e790746SPaolo Bonzini 
672019a3edbSGerd Hoffmann static uint64_t virtio_net_bad_features(VirtIODevice *vdev)
6736e790746SPaolo Bonzini {
674019a3edbSGerd Hoffmann     uint64_t features = 0;
6756e790746SPaolo Bonzini 
6766e790746SPaolo Bonzini     /* Linux kernel 2.6.25.  It understood MAC (as everyone must),
6776e790746SPaolo Bonzini      * but also these: */
6780cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_MAC);
6790cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_CSUM);
6800cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO4);
6810cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO6);
6820cd09c3aSCornelia Huck     virtio_add_feature(&features, VIRTIO_NET_F_HOST_ECN);
6836e790746SPaolo Bonzini 
6846e790746SPaolo Bonzini     return features;
6856e790746SPaolo Bonzini }
6866e790746SPaolo Bonzini 
687644c9858SDmitry Fleytman static void virtio_net_apply_guest_offloads(VirtIONet *n)
688644c9858SDmitry Fleytman {
689ad37bb3bSStefan Hajnoczi     qemu_set_offload(qemu_get_queue(n->nic)->peer,
690644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)),
691644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)),
692644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)),
693644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)),
694644c9858SDmitry Fleytman             !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO)));
695644c9858SDmitry Fleytman }
696644c9858SDmitry Fleytman 
697644c9858SDmitry Fleytman static uint64_t virtio_net_guest_offloads_by_features(uint32_t features)
698644c9858SDmitry Fleytman {
699644c9858SDmitry Fleytman     static const uint64_t guest_offloads_mask =
700644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_CSUM) |
701644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO4) |
702644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_TSO6) |
703644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_ECN)  |
704644c9858SDmitry Fleytman         (1ULL << VIRTIO_NET_F_GUEST_UFO);
705644c9858SDmitry Fleytman 
706644c9858SDmitry Fleytman     return guest_offloads_mask & features;
707644c9858SDmitry Fleytman }
708644c9858SDmitry Fleytman 
709644c9858SDmitry Fleytman static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n)
710644c9858SDmitry Fleytman {
711644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
712644c9858SDmitry Fleytman     return virtio_net_guest_offloads_by_features(vdev->guest_features);
713644c9858SDmitry Fleytman }
714644c9858SDmitry Fleytman 
715d5aaa1b0SGerd Hoffmann static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
7166e790746SPaolo Bonzini {
71717a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
7186e790746SPaolo Bonzini     int i;
7196e790746SPaolo Bonzini 
72075ebec11SMaxime Coquelin     if (n->mtu_bypass_backend &&
72175ebec11SMaxime Coquelin             !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) {
72275ebec11SMaxime Coquelin         features &= ~(1ULL << VIRTIO_NET_F_MTU);
72375ebec11SMaxime Coquelin     }
72475ebec11SMaxime Coquelin 
725ef546f12SCornelia Huck     virtio_net_set_multiqueue(n,
72695129d6fSCornelia Huck                               virtio_has_feature(features, VIRTIO_NET_F_MQ));
7276e790746SPaolo Bonzini 
728ef546f12SCornelia Huck     virtio_net_set_mrg_rx_bufs(n,
72995129d6fSCornelia Huck                                virtio_has_feature(features,
730bb9d17f8SCornelia Huck                                                   VIRTIO_NET_F_MRG_RXBUF),
73195129d6fSCornelia Huck                                virtio_has_feature(features,
732bb9d17f8SCornelia Huck                                                   VIRTIO_F_VERSION_1));
7336e790746SPaolo Bonzini 
7342974e916SYuri Benditovich     n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
7352974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4);
7362974e916SYuri Benditovich     n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) &&
7372974e916SYuri Benditovich         virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6);
7382974e916SYuri Benditovich 
7396e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
740644c9858SDmitry Fleytman         n->curr_guest_offloads =
741644c9858SDmitry Fleytman             virtio_net_guest_offloads_by_features(features);
742644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
7436e790746SPaolo Bonzini     }
7446e790746SPaolo Bonzini 
7456e790746SPaolo Bonzini     for (i = 0;  i < n->max_queues; i++) {
7466e790746SPaolo Bonzini         NetClientState *nc = qemu_get_subqueue(n->nic, i);
7476e790746SPaolo Bonzini 
748ed8b4afeSNikolay Nikolaev         if (!get_vhost_net(nc->peer)) {
7496e790746SPaolo Bonzini             continue;
7506e790746SPaolo Bonzini         }
751ed8b4afeSNikolay Nikolaev         vhost_net_ack_features(get_vhost_net(nc->peer), features);
7526e790746SPaolo Bonzini     }
7530b1eaa88SStefan Fritsch 
75495129d6fSCornelia Huck     if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
7550b1eaa88SStefan Fritsch         memset(n->vlans, 0, MAX_VLAN >> 3);
7560b1eaa88SStefan Fritsch     } else {
7570b1eaa88SStefan Fritsch         memset(n->vlans, 0xff, MAX_VLAN >> 3);
7580b1eaa88SStefan Fritsch     }
7596e790746SPaolo Bonzini }
7606e790746SPaolo Bonzini 
7616e790746SPaolo Bonzini static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
7626e790746SPaolo Bonzini                                      struct iovec *iov, unsigned int iov_cnt)
7636e790746SPaolo Bonzini {
7646e790746SPaolo Bonzini     uint8_t on;
7656e790746SPaolo Bonzini     size_t s;
766b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
7676e790746SPaolo Bonzini 
7686e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on));
7696e790746SPaolo Bonzini     if (s != sizeof(on)) {
7706e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
7716e790746SPaolo Bonzini     }
7726e790746SPaolo Bonzini 
7736e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) {
7746e790746SPaolo Bonzini         n->promisc = on;
7756e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) {
7766e790746SPaolo Bonzini         n->allmulti = on;
7776e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) {
7786e790746SPaolo Bonzini         n->alluni = on;
7796e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) {
7806e790746SPaolo Bonzini         n->nomulti = on;
7816e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) {
7826e790746SPaolo Bonzini         n->nouni = on;
7836e790746SPaolo Bonzini     } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) {
7846e790746SPaolo Bonzini         n->nobcast = on;
7856e790746SPaolo Bonzini     } else {
7866e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
7876e790746SPaolo Bonzini     }
7886e790746SPaolo Bonzini 
789b1be4280SAmos Kong     rxfilter_notify(nc);
790b1be4280SAmos Kong 
7916e790746SPaolo Bonzini     return VIRTIO_NET_OK;
7926e790746SPaolo Bonzini }
7936e790746SPaolo Bonzini 
794644c9858SDmitry Fleytman static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd,
795644c9858SDmitry Fleytman                                      struct iovec *iov, unsigned int iov_cnt)
796644c9858SDmitry Fleytman {
797644c9858SDmitry Fleytman     VirtIODevice *vdev = VIRTIO_DEVICE(n);
798644c9858SDmitry Fleytman     uint64_t offloads;
799644c9858SDmitry Fleytman     size_t s;
800644c9858SDmitry Fleytman 
80195129d6fSCornelia Huck     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
802644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
803644c9858SDmitry Fleytman     }
804644c9858SDmitry Fleytman 
805644c9858SDmitry Fleytman     s = iov_to_buf(iov, iov_cnt, 0, &offloads, sizeof(offloads));
806644c9858SDmitry Fleytman     if (s != sizeof(offloads)) {
807644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
808644c9858SDmitry Fleytman     }
809644c9858SDmitry Fleytman 
810644c9858SDmitry Fleytman     if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) {
811644c9858SDmitry Fleytman         uint64_t supported_offloads;
812644c9858SDmitry Fleytman 
813189ae6bbSJason Wang         offloads = virtio_ldq_p(vdev, &offloads);
814189ae6bbSJason Wang 
815644c9858SDmitry Fleytman         if (!n->has_vnet_hdr) {
816644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
817644c9858SDmitry Fleytman         }
818644c9858SDmitry Fleytman 
8192974e916SYuri Benditovich         n->rsc4_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
8202974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO4);
8212974e916SYuri Benditovich         n->rsc6_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) &&
8222974e916SYuri Benditovich             virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO6);
8232974e916SYuri Benditovich         virtio_clear_feature(&offloads, VIRTIO_NET_F_RSC_EXT);
8242974e916SYuri Benditovich 
825644c9858SDmitry Fleytman         supported_offloads = virtio_net_supported_guest_offloads(n);
826644c9858SDmitry Fleytman         if (offloads & ~supported_offloads) {
827644c9858SDmitry Fleytman             return VIRTIO_NET_ERR;
828644c9858SDmitry Fleytman         }
829644c9858SDmitry Fleytman 
830644c9858SDmitry Fleytman         n->curr_guest_offloads = offloads;
831644c9858SDmitry Fleytman         virtio_net_apply_guest_offloads(n);
832644c9858SDmitry Fleytman 
833644c9858SDmitry Fleytman         return VIRTIO_NET_OK;
834644c9858SDmitry Fleytman     } else {
835644c9858SDmitry Fleytman         return VIRTIO_NET_ERR;
836644c9858SDmitry Fleytman     }
837644c9858SDmitry Fleytman }
838644c9858SDmitry Fleytman 
8396e790746SPaolo Bonzini static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
8406e790746SPaolo Bonzini                                  struct iovec *iov, unsigned int iov_cnt)
8416e790746SPaolo Bonzini {
8421399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
8436e790746SPaolo Bonzini     struct virtio_net_ctrl_mac mac_data;
8446e790746SPaolo Bonzini     size_t s;
845b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
8466e790746SPaolo Bonzini 
8476e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {
8486e790746SPaolo Bonzini         if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {
8496e790746SPaolo Bonzini             return VIRTIO_NET_ERR;
8506e790746SPaolo Bonzini         }
8516e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
8526e790746SPaolo Bonzini         assert(s == sizeof(n->mac));
8536e790746SPaolo Bonzini         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
854b1be4280SAmos Kong         rxfilter_notify(nc);
855b1be4280SAmos Kong 
8566e790746SPaolo Bonzini         return VIRTIO_NET_OK;
8576e790746SPaolo Bonzini     }
8586e790746SPaolo Bonzini 
8596e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) {
8606e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
8616e790746SPaolo Bonzini     }
8626e790746SPaolo Bonzini 
863cae2e556SAmos Kong     int in_use = 0;
864cae2e556SAmos Kong     int first_multi = 0;
865cae2e556SAmos Kong     uint8_t uni_overflow = 0;
866cae2e556SAmos Kong     uint8_t multi_overflow = 0;
867cae2e556SAmos Kong     uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
8686e790746SPaolo Bonzini 
8696e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
8706e790746SPaolo Bonzini                    sizeof(mac_data.entries));
8711399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
8726e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
873b1be4280SAmos Kong         goto error;
8746e790746SPaolo Bonzini     }
8756e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
8766e790746SPaolo Bonzini 
8776e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {
878b1be4280SAmos Kong         goto error;
8796e790746SPaolo Bonzini     }
8806e790746SPaolo Bonzini 
8816e790746SPaolo Bonzini     if (mac_data.entries <= MAC_TABLE_ENTRIES) {
882cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, macs,
8836e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
8846e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
885b1be4280SAmos Kong             goto error;
8866e790746SPaolo Bonzini         }
887cae2e556SAmos Kong         in_use += mac_data.entries;
8886e790746SPaolo Bonzini     } else {
889cae2e556SAmos Kong         uni_overflow = 1;
8906e790746SPaolo Bonzini     }
8916e790746SPaolo Bonzini 
8926e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN);
8936e790746SPaolo Bonzini 
894cae2e556SAmos Kong     first_multi = in_use;
8956e790746SPaolo Bonzini 
8966e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries,
8976e790746SPaolo Bonzini                    sizeof(mac_data.entries));
8981399c60dSRusty Russell     mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries);
8996e790746SPaolo Bonzini     if (s != sizeof(mac_data.entries)) {
900b1be4280SAmos Kong         goto error;
9016e790746SPaolo Bonzini     }
9026e790746SPaolo Bonzini 
9036e790746SPaolo Bonzini     iov_discard_front(&iov, &iov_cnt, s);
9046e790746SPaolo Bonzini 
9056e790746SPaolo Bonzini     if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {
906b1be4280SAmos Kong         goto error;
9076e790746SPaolo Bonzini     }
9086e790746SPaolo Bonzini 
909edc24385SMichael S. Tsirkin     if (mac_data.entries <= MAC_TABLE_ENTRIES - in_use) {
910cae2e556SAmos Kong         s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN],
9116e790746SPaolo Bonzini                        mac_data.entries * ETH_ALEN);
9126e790746SPaolo Bonzini         if (s != mac_data.entries * ETH_ALEN) {
913b1be4280SAmos Kong             goto error;
9146e790746SPaolo Bonzini         }
915cae2e556SAmos Kong         in_use += mac_data.entries;
9166e790746SPaolo Bonzini     } else {
917cae2e556SAmos Kong         multi_overflow = 1;
9186e790746SPaolo Bonzini     }
9196e790746SPaolo Bonzini 
920cae2e556SAmos Kong     n->mac_table.in_use = in_use;
921cae2e556SAmos Kong     n->mac_table.first_multi = first_multi;
922cae2e556SAmos Kong     n->mac_table.uni_overflow = uni_overflow;
923cae2e556SAmos Kong     n->mac_table.multi_overflow = multi_overflow;
924cae2e556SAmos Kong     memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN);
925cae2e556SAmos Kong     g_free(macs);
926b1be4280SAmos Kong     rxfilter_notify(nc);
927b1be4280SAmos Kong 
9286e790746SPaolo Bonzini     return VIRTIO_NET_OK;
929b1be4280SAmos Kong 
930b1be4280SAmos Kong error:
931cae2e556SAmos Kong     g_free(macs);
932b1be4280SAmos Kong     return VIRTIO_NET_ERR;
9336e790746SPaolo Bonzini }
9346e790746SPaolo Bonzini 
9356e790746SPaolo Bonzini static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
9366e790746SPaolo Bonzini                                         struct iovec *iov, unsigned int iov_cnt)
9376e790746SPaolo Bonzini {
9381399c60dSRusty Russell     VirtIODevice *vdev = VIRTIO_DEVICE(n);
9396e790746SPaolo Bonzini     uint16_t vid;
9406e790746SPaolo Bonzini     size_t s;
941b1be4280SAmos Kong     NetClientState *nc = qemu_get_queue(n->nic);
9426e790746SPaolo Bonzini 
9436e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid));
9441399c60dSRusty Russell     vid = virtio_lduw_p(vdev, &vid);
9456e790746SPaolo Bonzini     if (s != sizeof(vid)) {
9466e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9476e790746SPaolo Bonzini     }
9486e790746SPaolo Bonzini 
9496e790746SPaolo Bonzini     if (vid >= MAX_VLAN)
9506e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9516e790746SPaolo Bonzini 
9526e790746SPaolo Bonzini     if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
9536e790746SPaolo Bonzini         n->vlans[vid >> 5] |= (1U << (vid & 0x1f));
9546e790746SPaolo Bonzini     else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL)
9556e790746SPaolo Bonzini         n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f));
9566e790746SPaolo Bonzini     else
9576e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9586e790746SPaolo Bonzini 
959b1be4280SAmos Kong     rxfilter_notify(nc);
960b1be4280SAmos Kong 
9616e790746SPaolo Bonzini     return VIRTIO_NET_OK;
9626e790746SPaolo Bonzini }
9636e790746SPaolo Bonzini 
964f57fcf70SJason Wang static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd,
965f57fcf70SJason Wang                                       struct iovec *iov, unsigned int iov_cnt)
966f57fcf70SJason Wang {
967f57fcf70SJason Wang     if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK &&
968f57fcf70SJason Wang         n->status & VIRTIO_NET_S_ANNOUNCE) {
969f57fcf70SJason Wang         n->status &= ~VIRTIO_NET_S_ANNOUNCE;
970f57fcf70SJason Wang         if (n->announce_counter) {
971f57fcf70SJason Wang             timer_mod(n->announce_timer,
972f57fcf70SJason Wang                       qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
973f57fcf70SJason Wang                       self_announce_delay(n->announce_counter));
974f57fcf70SJason Wang         }
975f57fcf70SJason Wang         return VIRTIO_NET_OK;
976f57fcf70SJason Wang     } else {
977f57fcf70SJason Wang         return VIRTIO_NET_ERR;
978f57fcf70SJason Wang     }
979f57fcf70SJason Wang }
980f57fcf70SJason Wang 
9816e790746SPaolo Bonzini static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
9826e790746SPaolo Bonzini                                 struct iovec *iov, unsigned int iov_cnt)
9836e790746SPaolo Bonzini {
98417a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
9856e790746SPaolo Bonzini     struct virtio_net_ctrl_mq mq;
9866e790746SPaolo Bonzini     size_t s;
9876e790746SPaolo Bonzini     uint16_t queues;
9886e790746SPaolo Bonzini 
9896e790746SPaolo Bonzini     s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq));
9906e790746SPaolo Bonzini     if (s != sizeof(mq)) {
9916e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9926e790746SPaolo Bonzini     }
9936e790746SPaolo Bonzini 
9946e790746SPaolo Bonzini     if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
9956e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
9966e790746SPaolo Bonzini     }
9976e790746SPaolo Bonzini 
9981399c60dSRusty Russell     queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs);
9996e790746SPaolo Bonzini 
10006e790746SPaolo Bonzini     if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
10016e790746SPaolo Bonzini         queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX ||
10026e790746SPaolo Bonzini         queues > n->max_queues ||
10036e790746SPaolo Bonzini         !n->multiqueue) {
10046e790746SPaolo Bonzini         return VIRTIO_NET_ERR;
10056e790746SPaolo Bonzini     }
10066e790746SPaolo Bonzini 
10076e790746SPaolo Bonzini     n->curr_queues = queues;
10086e790746SPaolo Bonzini     /* stop the backend before changing the number of queues to avoid handling a
10096e790746SPaolo Bonzini      * disabled queue */
101017a0ca55SKONRAD Frederic     virtio_net_set_status(vdev, vdev->status);
10116e790746SPaolo Bonzini     virtio_net_set_queues(n);
10126e790746SPaolo Bonzini 
10136e790746SPaolo Bonzini     return VIRTIO_NET_OK;
10146e790746SPaolo Bonzini }
1015ba7eadb5SGreg Kurz 
10166e790746SPaolo Bonzini static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
10176e790746SPaolo Bonzini {
101817a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
10196e790746SPaolo Bonzini     struct virtio_net_ctrl_hdr ctrl;
10206e790746SPaolo Bonzini     virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
102151b19ebeSPaolo Bonzini     VirtQueueElement *elem;
10226e790746SPaolo Bonzini     size_t s;
1023771b6ed3SJason Wang     struct iovec *iov, *iov2;
10246e790746SPaolo Bonzini     unsigned int iov_cnt;
10256e790746SPaolo Bonzini 
102651b19ebeSPaolo Bonzini     for (;;) {
102751b19ebeSPaolo Bonzini         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
102851b19ebeSPaolo Bonzini         if (!elem) {
102951b19ebeSPaolo Bonzini             break;
103051b19ebeSPaolo Bonzini         }
103151b19ebeSPaolo Bonzini         if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) ||
103251b19ebeSPaolo Bonzini             iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) {
1033ba7eadb5SGreg Kurz             virtio_error(vdev, "virtio-net ctrl missing headers");
1034ba7eadb5SGreg Kurz             virtqueue_detach_element(vq, elem, 0);
1035ba7eadb5SGreg Kurz             g_free(elem);
1036ba7eadb5SGreg Kurz             break;
10376e790746SPaolo Bonzini         }
10386e790746SPaolo Bonzini 
103951b19ebeSPaolo Bonzini         iov_cnt = elem->out_num;
104051b19ebeSPaolo Bonzini         iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num);
10416e790746SPaolo Bonzini         s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl));
10426e790746SPaolo Bonzini         iov_discard_front(&iov, &iov_cnt, sizeof(ctrl));
10436e790746SPaolo Bonzini         if (s != sizeof(ctrl)) {
10446e790746SPaolo Bonzini             status = VIRTIO_NET_ERR;
10456e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_RX) {
10466e790746SPaolo Bonzini             status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt);
10476e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) {
10486e790746SPaolo Bonzini             status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt);
10496e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) {
10506e790746SPaolo Bonzini             status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt);
1051f57fcf70SJason Wang         } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) {
1052f57fcf70SJason Wang             status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt);
10536e790746SPaolo Bonzini         } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) {
10546e790746SPaolo Bonzini             status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt);
1055644c9858SDmitry Fleytman         } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) {
1056644c9858SDmitry Fleytman             status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt);
10576e790746SPaolo Bonzini         }
10586e790746SPaolo Bonzini 
105951b19ebeSPaolo Bonzini         s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status));
10606e790746SPaolo Bonzini         assert(s == sizeof(status));
10616e790746SPaolo Bonzini 
106251b19ebeSPaolo Bonzini         virtqueue_push(vq, elem, sizeof(status));
10636e790746SPaolo Bonzini         virtio_notify(vdev, vq);
1064771b6ed3SJason Wang         g_free(iov2);
106551b19ebeSPaolo Bonzini         g_free(elem);
10666e790746SPaolo Bonzini     }
10676e790746SPaolo Bonzini }
10686e790746SPaolo Bonzini 
10696e790746SPaolo Bonzini /* RX */
10706e790746SPaolo Bonzini 
10716e790746SPaolo Bonzini static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
10726e790746SPaolo Bonzini {
107317a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
10746e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(vq));
10756e790746SPaolo Bonzini 
10766e790746SPaolo Bonzini     qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index));
10776e790746SPaolo Bonzini }
10786e790746SPaolo Bonzini 
10796e790746SPaolo Bonzini static int virtio_net_can_receive(NetClientState *nc)
10806e790746SPaolo Bonzini {
10816e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
108217a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
10836e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
10846e790746SPaolo Bonzini 
108517a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
10866e790746SPaolo Bonzini         return 0;
10876e790746SPaolo Bonzini     }
10886e790746SPaolo Bonzini 
10896e790746SPaolo Bonzini     if (nc->queue_index >= n->curr_queues) {
10906e790746SPaolo Bonzini         return 0;
10916e790746SPaolo Bonzini     }
10926e790746SPaolo Bonzini 
10936e790746SPaolo Bonzini     if (!virtio_queue_ready(q->rx_vq) ||
109417a0ca55SKONRAD Frederic         !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
10956e790746SPaolo Bonzini         return 0;
10966e790746SPaolo Bonzini     }
10976e790746SPaolo Bonzini 
10986e790746SPaolo Bonzini     return 1;
10996e790746SPaolo Bonzini }
11006e790746SPaolo Bonzini 
11016e790746SPaolo Bonzini static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize)
11026e790746SPaolo Bonzini {
11036e790746SPaolo Bonzini     VirtIONet *n = q->n;
11046e790746SPaolo Bonzini     if (virtio_queue_empty(q->rx_vq) ||
11056e790746SPaolo Bonzini         (n->mergeable_rx_bufs &&
11066e790746SPaolo Bonzini          !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
11076e790746SPaolo Bonzini         virtio_queue_set_notification(q->rx_vq, 1);
11086e790746SPaolo Bonzini 
11096e790746SPaolo Bonzini         /* To avoid a race condition where the guest has made some buffers
11106e790746SPaolo Bonzini          * available after the above check but before notification was
11116e790746SPaolo Bonzini          * enabled, check for available buffers again.
11126e790746SPaolo Bonzini          */
11136e790746SPaolo Bonzini         if (virtio_queue_empty(q->rx_vq) ||
11146e790746SPaolo Bonzini             (n->mergeable_rx_bufs &&
11156e790746SPaolo Bonzini              !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
11166e790746SPaolo Bonzini             return 0;
11176e790746SPaolo Bonzini         }
11186e790746SPaolo Bonzini     }
11196e790746SPaolo Bonzini 
11206e790746SPaolo Bonzini     virtio_queue_set_notification(q->rx_vq, 0);
11216e790746SPaolo Bonzini     return 1;
11226e790746SPaolo Bonzini }
11236e790746SPaolo Bonzini 
11241399c60dSRusty Russell static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr)
1125032a74a1SCédric Le Goater {
11261399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->hdr_len);
11271399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->gso_size);
11281399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_start);
11291399c60dSRusty Russell     virtio_tswap16s(vdev, &hdr->csum_offset);
1130032a74a1SCédric Le Goater }
1131032a74a1SCédric Le Goater 
11326e790746SPaolo Bonzini /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so
11336e790746SPaolo Bonzini  * it never finds out that the packets don't have valid checksums.  This
11346e790746SPaolo Bonzini  * causes dhclient to get upset.  Fedora's carried a patch for ages to
11356e790746SPaolo Bonzini  * fix this with Xen but it hasn't appeared in an upstream release of
11366e790746SPaolo Bonzini  * dhclient yet.
11376e790746SPaolo Bonzini  *
11386e790746SPaolo Bonzini  * To avoid breaking existing guests, we catch udp packets and add
11396e790746SPaolo Bonzini  * checksums.  This is terrible but it's better than hacking the guest
11406e790746SPaolo Bonzini  * kernels.
11416e790746SPaolo Bonzini  *
11426e790746SPaolo Bonzini  * N.B. if we introduce a zero-copy API, this operation is no longer free so
11436e790746SPaolo Bonzini  * we should provide a mechanism to disable it to avoid polluting the host
11446e790746SPaolo Bonzini  * cache.
11456e790746SPaolo Bonzini  */
11466e790746SPaolo Bonzini static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
11476e790746SPaolo Bonzini                                         uint8_t *buf, size_t size)
11486e790746SPaolo Bonzini {
11496e790746SPaolo Bonzini     if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
11506e790746SPaolo Bonzini         (size > 27 && size < 1500) && /* normal sized MTU */
11516e790746SPaolo Bonzini         (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
11526e790746SPaolo Bonzini         (buf[23] == 17) && /* ip.protocol == UDP */
11536e790746SPaolo Bonzini         (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
11546e790746SPaolo Bonzini         net_checksum_calculate(buf, size);
11556e790746SPaolo Bonzini         hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
11566e790746SPaolo Bonzini     }
11576e790746SPaolo Bonzini }
11586e790746SPaolo Bonzini 
11596e790746SPaolo Bonzini static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt,
11606e790746SPaolo Bonzini                            const void *buf, size_t size)
11616e790746SPaolo Bonzini {
11626e790746SPaolo Bonzini     if (n->has_vnet_hdr) {
11636e790746SPaolo Bonzini         /* FIXME this cast is evil */
11646e790746SPaolo Bonzini         void *wbuf = (void *)buf;
11656e790746SPaolo Bonzini         work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len,
11666e790746SPaolo Bonzini                                     size - n->host_hdr_len);
11671bfa316cSGreg Kurz 
11681bfa316cSGreg Kurz         if (n->needs_vnet_hdr_swap) {
11691399c60dSRusty Russell             virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf);
11701bfa316cSGreg Kurz         }
11716e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr));
11726e790746SPaolo Bonzini     } else {
11736e790746SPaolo Bonzini         struct virtio_net_hdr hdr = {
11746e790746SPaolo Bonzini             .flags = 0,
11756e790746SPaolo Bonzini             .gso_type = VIRTIO_NET_HDR_GSO_NONE
11766e790746SPaolo Bonzini         };
11776e790746SPaolo Bonzini         iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr);
11786e790746SPaolo Bonzini     }
11796e790746SPaolo Bonzini }
11806e790746SPaolo Bonzini 
11816e790746SPaolo Bonzini static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
11826e790746SPaolo Bonzini {
11836e790746SPaolo Bonzini     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
11846e790746SPaolo Bonzini     static const uint8_t vlan[] = {0x81, 0x00};
11856e790746SPaolo Bonzini     uint8_t *ptr = (uint8_t *)buf;
11866e790746SPaolo Bonzini     int i;
11876e790746SPaolo Bonzini 
11886e790746SPaolo Bonzini     if (n->promisc)
11896e790746SPaolo Bonzini         return 1;
11906e790746SPaolo Bonzini 
11916e790746SPaolo Bonzini     ptr += n->host_hdr_len;
11926e790746SPaolo Bonzini 
11936e790746SPaolo Bonzini     if (!memcmp(&ptr[12], vlan, sizeof(vlan))) {
11947542d3e7SPeter Maydell         int vid = lduw_be_p(ptr + 14) & 0xfff;
11956e790746SPaolo Bonzini         if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f))))
11966e790746SPaolo Bonzini             return 0;
11976e790746SPaolo Bonzini     }
11986e790746SPaolo Bonzini 
11996e790746SPaolo Bonzini     if (ptr[0] & 1) { // multicast
12006e790746SPaolo Bonzini         if (!memcmp(ptr, bcast, sizeof(bcast))) {
12016e790746SPaolo Bonzini             return !n->nobcast;
12026e790746SPaolo Bonzini         } else if (n->nomulti) {
12036e790746SPaolo Bonzini             return 0;
12046e790746SPaolo Bonzini         } else if (n->allmulti || n->mac_table.multi_overflow) {
12056e790746SPaolo Bonzini             return 1;
12066e790746SPaolo Bonzini         }
12076e790746SPaolo Bonzini 
12086e790746SPaolo Bonzini         for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
12096e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
12106e790746SPaolo Bonzini                 return 1;
12116e790746SPaolo Bonzini             }
12126e790746SPaolo Bonzini         }
12136e790746SPaolo Bonzini     } else { // unicast
12146e790746SPaolo Bonzini         if (n->nouni) {
12156e790746SPaolo Bonzini             return 0;
12166e790746SPaolo Bonzini         } else if (n->alluni || n->mac_table.uni_overflow) {
12176e790746SPaolo Bonzini             return 1;
12186e790746SPaolo Bonzini         } else if (!memcmp(ptr, n->mac, ETH_ALEN)) {
12196e790746SPaolo Bonzini             return 1;
12206e790746SPaolo Bonzini         }
12216e790746SPaolo Bonzini 
12226e790746SPaolo Bonzini         for (i = 0; i < n->mac_table.first_multi; i++) {
12236e790746SPaolo Bonzini             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
12246e790746SPaolo Bonzini                 return 1;
12256e790746SPaolo Bonzini             }
12266e790746SPaolo Bonzini         }
12276e790746SPaolo Bonzini     }
12286e790746SPaolo Bonzini 
12296e790746SPaolo Bonzini     return 0;
12306e790746SPaolo Bonzini }
12316e790746SPaolo Bonzini 
123297cd965cSPaolo Bonzini static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
123397cd965cSPaolo Bonzini                                       size_t size)
12346e790746SPaolo Bonzini {
12356e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
12366e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
123717a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
12386e790746SPaolo Bonzini     struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
12396e790746SPaolo Bonzini     struct virtio_net_hdr_mrg_rxbuf mhdr;
12406e790746SPaolo Bonzini     unsigned mhdr_cnt = 0;
12416e790746SPaolo Bonzini     size_t offset, i, guest_offset;
12426e790746SPaolo Bonzini 
12436e790746SPaolo Bonzini     if (!virtio_net_can_receive(nc)) {
12446e790746SPaolo Bonzini         return -1;
12456e790746SPaolo Bonzini     }
12466e790746SPaolo Bonzini 
12476e790746SPaolo Bonzini     /* hdr_len refers to the header we supply to the guest */
12486e790746SPaolo Bonzini     if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) {
12496e790746SPaolo Bonzini         return 0;
12506e790746SPaolo Bonzini     }
12516e790746SPaolo Bonzini 
12526e790746SPaolo Bonzini     if (!receive_filter(n, buf, size))
12536e790746SPaolo Bonzini         return size;
12546e790746SPaolo Bonzini 
12556e790746SPaolo Bonzini     offset = i = 0;
12566e790746SPaolo Bonzini 
12576e790746SPaolo Bonzini     while (offset < size) {
125851b19ebeSPaolo Bonzini         VirtQueueElement *elem;
12596e790746SPaolo Bonzini         int len, total;
126051b19ebeSPaolo Bonzini         const struct iovec *sg;
12616e790746SPaolo Bonzini 
12626e790746SPaolo Bonzini         total = 0;
12636e790746SPaolo Bonzini 
126451b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement));
126551b19ebeSPaolo Bonzini         if (!elem) {
1266ba10b9c0SGreg Kurz             if (i) {
1267ba10b9c0SGreg Kurz                 virtio_error(vdev, "virtio-net unexpected empty queue: "
12686e790746SPaolo Bonzini                              "i %zd mergeable %d offset %zd, size %zd, "
1269019a3edbSGerd Hoffmann                              "guest hdr len %zd, host hdr len %zd "
1270019a3edbSGerd Hoffmann                              "guest features 0x%" PRIx64,
12716e790746SPaolo Bonzini                              i, n->mergeable_rx_bufs, offset, size,
1272019a3edbSGerd Hoffmann                              n->guest_hdr_len, n->host_hdr_len,
1273019a3edbSGerd Hoffmann                              vdev->guest_features);
1274ba10b9c0SGreg Kurz             }
1275ba10b9c0SGreg Kurz             return -1;
12766e790746SPaolo Bonzini         }
12776e790746SPaolo Bonzini 
127851b19ebeSPaolo Bonzini         if (elem->in_num < 1) {
1279ba10b9c0SGreg Kurz             virtio_error(vdev,
1280ba10b9c0SGreg Kurz                          "virtio-net receive queue contains no in buffers");
1281ba10b9c0SGreg Kurz             virtqueue_detach_element(q->rx_vq, elem, 0);
1282ba10b9c0SGreg Kurz             g_free(elem);
1283ba10b9c0SGreg Kurz             return -1;
12846e790746SPaolo Bonzini         }
12856e790746SPaolo Bonzini 
128651b19ebeSPaolo Bonzini         sg = elem->in_sg;
12876e790746SPaolo Bonzini         if (i == 0) {
12886e790746SPaolo Bonzini             assert(offset == 0);
12896e790746SPaolo Bonzini             if (n->mergeable_rx_bufs) {
12906e790746SPaolo Bonzini                 mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg),
129151b19ebeSPaolo Bonzini                                     sg, elem->in_num,
12926e790746SPaolo Bonzini                                     offsetof(typeof(mhdr), num_buffers),
12936e790746SPaolo Bonzini                                     sizeof(mhdr.num_buffers));
12946e790746SPaolo Bonzini             }
12956e790746SPaolo Bonzini 
129651b19ebeSPaolo Bonzini             receive_header(n, sg, elem->in_num, buf, size);
12976e790746SPaolo Bonzini             offset = n->host_hdr_len;
12986e790746SPaolo Bonzini             total += n->guest_hdr_len;
12996e790746SPaolo Bonzini             guest_offset = n->guest_hdr_len;
13006e790746SPaolo Bonzini         } else {
13016e790746SPaolo Bonzini             guest_offset = 0;
13026e790746SPaolo Bonzini         }
13036e790746SPaolo Bonzini 
13046e790746SPaolo Bonzini         /* copy in packet.  ugh */
130551b19ebeSPaolo Bonzini         len = iov_from_buf(sg, elem->in_num, guest_offset,
13066e790746SPaolo Bonzini                            buf + offset, size - offset);
13076e790746SPaolo Bonzini         total += len;
13086e790746SPaolo Bonzini         offset += len;
13096e790746SPaolo Bonzini         /* If buffers can't be merged, at this point we
13106e790746SPaolo Bonzini          * must have consumed the complete packet.
13116e790746SPaolo Bonzini          * Otherwise, drop it. */
13126e790746SPaolo Bonzini         if (!n->mergeable_rx_bufs && offset < size) {
131327e57efeSLadi Prosek             virtqueue_unpop(q->rx_vq, elem, total);
131451b19ebeSPaolo Bonzini             g_free(elem);
13156e790746SPaolo Bonzini             return size;
13166e790746SPaolo Bonzini         }
13176e790746SPaolo Bonzini 
13186e790746SPaolo Bonzini         /* signal other side */
131951b19ebeSPaolo Bonzini         virtqueue_fill(q->rx_vq, elem, total, i++);
132051b19ebeSPaolo Bonzini         g_free(elem);
13216e790746SPaolo Bonzini     }
13226e790746SPaolo Bonzini 
13236e790746SPaolo Bonzini     if (mhdr_cnt) {
13241399c60dSRusty Russell         virtio_stw_p(vdev, &mhdr.num_buffers, i);
13256e790746SPaolo Bonzini         iov_from_buf(mhdr_sg, mhdr_cnt,
13266e790746SPaolo Bonzini                      0,
13276e790746SPaolo Bonzini                      &mhdr.num_buffers, sizeof mhdr.num_buffers);
13286e790746SPaolo Bonzini     }
13296e790746SPaolo Bonzini 
13306e790746SPaolo Bonzini     virtqueue_flush(q->rx_vq, i);
133117a0ca55SKONRAD Frederic     virtio_notify(vdev, q->rx_vq);
13326e790746SPaolo Bonzini 
13336e790746SPaolo Bonzini     return size;
13346e790746SPaolo Bonzini }
13356e790746SPaolo Bonzini 
13362974e916SYuri Benditovich static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf,
133797cd965cSPaolo Bonzini                                   size_t size)
133897cd965cSPaolo Bonzini {
133997cd965cSPaolo Bonzini     ssize_t r;
134097cd965cSPaolo Bonzini 
134197cd965cSPaolo Bonzini     rcu_read_lock();
134297cd965cSPaolo Bonzini     r = virtio_net_receive_rcu(nc, buf, size);
134397cd965cSPaolo Bonzini     rcu_read_unlock();
134497cd965cSPaolo Bonzini     return r;
134597cd965cSPaolo Bonzini }
134697cd965cSPaolo Bonzini 
13472974e916SYuri Benditovich static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
13482974e916SYuri Benditovich                                          const uint8_t *buf,
13492974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
13502974e916SYuri Benditovich {
13512974e916SYuri Benditovich     uint16_t ip_hdrlen;
13522974e916SYuri Benditovich     struct ip_header *ip;
13532974e916SYuri Benditovich 
13542974e916SYuri Benditovich     ip = (struct ip_header *)(buf + chain->n->guest_hdr_len
13552974e916SYuri Benditovich                               + sizeof(struct eth_header));
13562974e916SYuri Benditovich     unit->ip = (void *)ip;
13572974e916SYuri Benditovich     ip_hdrlen = (ip->ip_ver_len & 0xF) << 2;
13582974e916SYuri Benditovich     unit->ip_plen = &ip->ip_len;
13592974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen);
13602974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
13612974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen;
13622974e916SYuri Benditovich }
13632974e916SYuri Benditovich 
13642974e916SYuri Benditovich static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
13652974e916SYuri Benditovich                                          const uint8_t *buf,
13662974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
13672974e916SYuri Benditovich {
13682974e916SYuri Benditovich     struct ip6_header *ip6;
13692974e916SYuri Benditovich 
13702974e916SYuri Benditovich     ip6 = (struct ip6_header *)(buf + chain->n->guest_hdr_len
13712974e916SYuri Benditovich                                  + sizeof(struct eth_header));
13722974e916SYuri Benditovich     unit->ip = ip6;
13732974e916SYuri Benditovich     unit->ip_plen = &(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
13742974e916SYuri Benditovich     unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip)\
13752974e916SYuri Benditovich                                         + sizeof(struct ip6_header));
13762974e916SYuri Benditovich     unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
13772974e916SYuri Benditovich 
13782974e916SYuri Benditovich     /* There is a difference between payload lenght in ipv4 and v6,
13792974e916SYuri Benditovich        ip header is excluded in ipv6 */
13802974e916SYuri Benditovich     unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen;
13812974e916SYuri Benditovich }
13822974e916SYuri Benditovich 
13832974e916SYuri Benditovich static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain,
13842974e916SYuri Benditovich                                        VirtioNetRscSeg *seg)
13852974e916SYuri Benditovich {
13862974e916SYuri Benditovich     int ret;
13872974e916SYuri Benditovich     struct virtio_net_hdr *h;
13882974e916SYuri Benditovich 
13892974e916SYuri Benditovich     h = (struct virtio_net_hdr *)seg->buf;
13902974e916SYuri Benditovich     h->flags = 0;
13912974e916SYuri Benditovich     h->gso_type = VIRTIO_NET_HDR_GSO_NONE;
13922974e916SYuri Benditovich 
13932974e916SYuri Benditovich     if (seg->is_coalesced) {
13942974e916SYuri Benditovich         *virtio_net_rsc_ext_num_packets(h) = seg->packets;
13952974e916SYuri Benditovich         *virtio_net_rsc_ext_num_dupacks(h) = seg->dup_ack;
13962974e916SYuri Benditovich         h->flags = VIRTIO_NET_HDR_F_RSC_INFO;
13972974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
13982974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
13992974e916SYuri Benditovich         } else {
14002974e916SYuri Benditovich             h->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
14012974e916SYuri Benditovich         }
14022974e916SYuri Benditovich     }
14032974e916SYuri Benditovich 
14042974e916SYuri Benditovich     ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size);
14052974e916SYuri Benditovich     QTAILQ_REMOVE(&chain->buffers, seg, next);
14062974e916SYuri Benditovich     g_free(seg->buf);
14072974e916SYuri Benditovich     g_free(seg);
14082974e916SYuri Benditovich 
14092974e916SYuri Benditovich     return ret;
14102974e916SYuri Benditovich }
14112974e916SYuri Benditovich 
14122974e916SYuri Benditovich static void virtio_net_rsc_purge(void *opq)
14132974e916SYuri Benditovich {
14142974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn;
14152974e916SYuri Benditovich     VirtioNetRscChain *chain = (VirtioNetRscChain *)opq;
14162974e916SYuri Benditovich 
14172974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn) {
14182974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
14192974e916SYuri Benditovich             chain->stat.purge_failed++;
14202974e916SYuri Benditovich             continue;
14212974e916SYuri Benditovich         }
14222974e916SYuri Benditovich     }
14232974e916SYuri Benditovich 
14242974e916SYuri Benditovich     chain->stat.timer++;
14252974e916SYuri Benditovich     if (!QTAILQ_EMPTY(&chain->buffers)) {
14262974e916SYuri Benditovich         timer_mod(chain->drain_timer,
14272974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
14282974e916SYuri Benditovich     }
14292974e916SYuri Benditovich }
14302974e916SYuri Benditovich 
14312974e916SYuri Benditovich static void virtio_net_rsc_cleanup(VirtIONet *n)
14322974e916SYuri Benditovich {
14332974e916SYuri Benditovich     VirtioNetRscChain *chain, *rn_chain;
14342974e916SYuri Benditovich     VirtioNetRscSeg *seg, *rn_seg;
14352974e916SYuri Benditovich 
14362974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(chain, &n->rsc_chains, next, rn_chain) {
14372974e916SYuri Benditovich         QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn_seg) {
14382974e916SYuri Benditovich             QTAILQ_REMOVE(&chain->buffers, seg, next);
14392974e916SYuri Benditovich             g_free(seg->buf);
14402974e916SYuri Benditovich             g_free(seg);
14412974e916SYuri Benditovich         }
14422974e916SYuri Benditovich 
14432974e916SYuri Benditovich         timer_del(chain->drain_timer);
14442974e916SYuri Benditovich         timer_free(chain->drain_timer);
14452974e916SYuri Benditovich         QTAILQ_REMOVE(&n->rsc_chains, chain, next);
14462974e916SYuri Benditovich         g_free(chain);
14472974e916SYuri Benditovich     }
14482974e916SYuri Benditovich }
14492974e916SYuri Benditovich 
14502974e916SYuri Benditovich static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain,
14512974e916SYuri Benditovich                                      NetClientState *nc,
14522974e916SYuri Benditovich                                      const uint8_t *buf, size_t size)
14532974e916SYuri Benditovich {
14542974e916SYuri Benditovich     uint16_t hdr_len;
14552974e916SYuri Benditovich     VirtioNetRscSeg *seg;
14562974e916SYuri Benditovich 
14572974e916SYuri Benditovich     hdr_len = chain->n->guest_hdr_len;
14582974e916SYuri Benditovich     seg = g_malloc(sizeof(VirtioNetRscSeg));
14592974e916SYuri Benditovich     seg->buf = g_malloc(hdr_len + sizeof(struct eth_header)
14602974e916SYuri Benditovich         + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD);
14612974e916SYuri Benditovich     memcpy(seg->buf, buf, size);
14622974e916SYuri Benditovich     seg->size = size;
14632974e916SYuri Benditovich     seg->packets = 1;
14642974e916SYuri Benditovich     seg->dup_ack = 0;
14652974e916SYuri Benditovich     seg->is_coalesced = 0;
14662974e916SYuri Benditovich     seg->nc = nc;
14672974e916SYuri Benditovich 
14682974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&chain->buffers, seg, next);
14692974e916SYuri Benditovich     chain->stat.cache++;
14702974e916SYuri Benditovich 
14712974e916SYuri Benditovich     switch (chain->proto) {
14722974e916SYuri Benditovich     case ETH_P_IP:
14732974e916SYuri Benditovich         virtio_net_rsc_extract_unit4(chain, seg->buf, &seg->unit);
14742974e916SYuri Benditovich         break;
14752974e916SYuri Benditovich     case ETH_P_IPV6:
14762974e916SYuri Benditovich         virtio_net_rsc_extract_unit6(chain, seg->buf, &seg->unit);
14772974e916SYuri Benditovich         break;
14782974e916SYuri Benditovich     default:
14792974e916SYuri Benditovich         g_assert_not_reached();
14802974e916SYuri Benditovich     }
14812974e916SYuri Benditovich }
14822974e916SYuri Benditovich 
14832974e916SYuri Benditovich static int32_t virtio_net_rsc_handle_ack(VirtioNetRscChain *chain,
14842974e916SYuri Benditovich                                          VirtioNetRscSeg *seg,
14852974e916SYuri Benditovich                                          const uint8_t *buf,
14862974e916SYuri Benditovich                                          struct tcp_header *n_tcp,
14872974e916SYuri Benditovich                                          struct tcp_header *o_tcp)
14882974e916SYuri Benditovich {
14892974e916SYuri Benditovich     uint32_t nack, oack;
14902974e916SYuri Benditovich     uint16_t nwin, owin;
14912974e916SYuri Benditovich 
14922974e916SYuri Benditovich     nack = htonl(n_tcp->th_ack);
14932974e916SYuri Benditovich     nwin = htons(n_tcp->th_win);
14942974e916SYuri Benditovich     oack = htonl(o_tcp->th_ack);
14952974e916SYuri Benditovich     owin = htons(o_tcp->th_win);
14962974e916SYuri Benditovich 
14972974e916SYuri Benditovich     if ((nack - oack) >= VIRTIO_NET_MAX_TCP_PAYLOAD) {
14982974e916SYuri Benditovich         chain->stat.ack_out_of_win++;
14992974e916SYuri Benditovich         return RSC_FINAL;
15002974e916SYuri Benditovich     } else if (nack == oack) {
15012974e916SYuri Benditovich         /* duplicated ack or window probe */
15022974e916SYuri Benditovich         if (nwin == owin) {
15032974e916SYuri Benditovich             /* duplicated ack, add dup ack count due to whql test up to 1 */
15042974e916SYuri Benditovich             chain->stat.dup_ack++;
15052974e916SYuri Benditovich             return RSC_FINAL;
15062974e916SYuri Benditovich         } else {
15072974e916SYuri Benditovich             /* Coalesce window update */
15082974e916SYuri Benditovich             o_tcp->th_win = n_tcp->th_win;
15092974e916SYuri Benditovich             chain->stat.win_update++;
15102974e916SYuri Benditovich             return RSC_COALESCE;
15112974e916SYuri Benditovich         }
15122974e916SYuri Benditovich     } else {
15132974e916SYuri Benditovich         /* pure ack, go to 'C', finalize*/
15142974e916SYuri Benditovich         chain->stat.pure_ack++;
15152974e916SYuri Benditovich         return RSC_FINAL;
15162974e916SYuri Benditovich     }
15172974e916SYuri Benditovich }
15182974e916SYuri Benditovich 
15192974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain,
15202974e916SYuri Benditovich                                             VirtioNetRscSeg *seg,
15212974e916SYuri Benditovich                                             const uint8_t *buf,
15222974e916SYuri Benditovich                                             VirtioNetRscUnit *n_unit)
15232974e916SYuri Benditovich {
15242974e916SYuri Benditovich     void *data;
15252974e916SYuri Benditovich     uint16_t o_ip_len;
15262974e916SYuri Benditovich     uint32_t nseq, oseq;
15272974e916SYuri Benditovich     VirtioNetRscUnit *o_unit;
15282974e916SYuri Benditovich 
15292974e916SYuri Benditovich     o_unit = &seg->unit;
15302974e916SYuri Benditovich     o_ip_len = htons(*o_unit->ip_plen);
15312974e916SYuri Benditovich     nseq = htonl(n_unit->tcp->th_seq);
15322974e916SYuri Benditovich     oseq = htonl(o_unit->tcp->th_seq);
15332974e916SYuri Benditovich 
15342974e916SYuri Benditovich     /* out of order or retransmitted. */
15352974e916SYuri Benditovich     if ((nseq - oseq) > VIRTIO_NET_MAX_TCP_PAYLOAD) {
15362974e916SYuri Benditovich         chain->stat.data_out_of_win++;
15372974e916SYuri Benditovich         return RSC_FINAL;
15382974e916SYuri Benditovich     }
15392974e916SYuri Benditovich 
15402974e916SYuri Benditovich     data = ((uint8_t *)n_unit->tcp) + n_unit->tcp_hdrlen;
15412974e916SYuri Benditovich     if (nseq == oseq) {
15422974e916SYuri Benditovich         if ((o_unit->payload == 0) && n_unit->payload) {
15432974e916SYuri Benditovich             /* From no payload to payload, normal case, not a dup ack or etc */
15442974e916SYuri Benditovich             chain->stat.data_after_pure_ack++;
15452974e916SYuri Benditovich             goto coalesce;
15462974e916SYuri Benditovich         } else {
15472974e916SYuri Benditovich             return virtio_net_rsc_handle_ack(chain, seg, buf,
15482974e916SYuri Benditovich                                              n_unit->tcp, o_unit->tcp);
15492974e916SYuri Benditovich         }
15502974e916SYuri Benditovich     } else if ((nseq - oseq) != o_unit->payload) {
15512974e916SYuri Benditovich         /* Not a consistent packet, out of order */
15522974e916SYuri Benditovich         chain->stat.data_out_of_order++;
15532974e916SYuri Benditovich         return RSC_FINAL;
15542974e916SYuri Benditovich     } else {
15552974e916SYuri Benditovich coalesce:
15562974e916SYuri Benditovich         if ((o_ip_len + n_unit->payload) > chain->max_payload) {
15572974e916SYuri Benditovich             chain->stat.over_size++;
15582974e916SYuri Benditovich             return RSC_FINAL;
15592974e916SYuri Benditovich         }
15602974e916SYuri Benditovich 
15612974e916SYuri Benditovich         /* Here comes the right data, the payload length in v4/v6 is different,
15622974e916SYuri Benditovich            so use the field value to update and record the new data len */
15632974e916SYuri Benditovich         o_unit->payload += n_unit->payload; /* update new data len */
15642974e916SYuri Benditovich 
15652974e916SYuri Benditovich         /* update field in ip header */
15662974e916SYuri Benditovich         *o_unit->ip_plen = htons(o_ip_len + n_unit->payload);
15672974e916SYuri Benditovich 
15682974e916SYuri Benditovich         /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced
15692974e916SYuri Benditovich            for windows guest, while this may change the behavior for linux
15702974e916SYuri Benditovich            guest (only if it uses RSC feature). */
15712974e916SYuri Benditovich         o_unit->tcp->th_offset_flags = n_unit->tcp->th_offset_flags;
15722974e916SYuri Benditovich 
15732974e916SYuri Benditovich         o_unit->tcp->th_ack = n_unit->tcp->th_ack;
15742974e916SYuri Benditovich         o_unit->tcp->th_win = n_unit->tcp->th_win;
15752974e916SYuri Benditovich 
15762974e916SYuri Benditovich         memmove(seg->buf + seg->size, data, n_unit->payload);
15772974e916SYuri Benditovich         seg->size += n_unit->payload;
15782974e916SYuri Benditovich         seg->packets++;
15792974e916SYuri Benditovich         chain->stat.coalesced++;
15802974e916SYuri Benditovich         return RSC_COALESCE;
15812974e916SYuri Benditovich     }
15822974e916SYuri Benditovich }
15832974e916SYuri Benditovich 
15842974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce4(VirtioNetRscChain *chain,
15852974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
15862974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
15872974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
15882974e916SYuri Benditovich {
15892974e916SYuri Benditovich     struct ip_header *ip1, *ip2;
15902974e916SYuri Benditovich 
15912974e916SYuri Benditovich     ip1 = (struct ip_header *)(unit->ip);
15922974e916SYuri Benditovich     ip2 = (struct ip_header *)(seg->unit.ip);
15932974e916SYuri Benditovich     if ((ip1->ip_src ^ ip2->ip_src) || (ip1->ip_dst ^ ip2->ip_dst)
15942974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
15952974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
15962974e916SYuri Benditovich         chain->stat.no_match++;
15972974e916SYuri Benditovich         return RSC_NO_MATCH;
15982974e916SYuri Benditovich     }
15992974e916SYuri Benditovich 
16002974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
16012974e916SYuri Benditovich }
16022974e916SYuri Benditovich 
16032974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce6(VirtioNetRscChain *chain,
16042974e916SYuri Benditovich                                         VirtioNetRscSeg *seg,
16052974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
16062974e916SYuri Benditovich                                         VirtioNetRscUnit *unit)
16072974e916SYuri Benditovich {
16082974e916SYuri Benditovich     struct ip6_header *ip1, *ip2;
16092974e916SYuri Benditovich 
16102974e916SYuri Benditovich     ip1 = (struct ip6_header *)(unit->ip);
16112974e916SYuri Benditovich     ip2 = (struct ip6_header *)(seg->unit.ip);
16122974e916SYuri Benditovich     if (memcmp(&ip1->ip6_src, &ip2->ip6_src, sizeof(struct in6_address))
16132974e916SYuri Benditovich         || memcmp(&ip1->ip6_dst, &ip2->ip6_dst, sizeof(struct in6_address))
16142974e916SYuri Benditovich         || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport)
16152974e916SYuri Benditovich         || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) {
16162974e916SYuri Benditovich             chain->stat.no_match++;
16172974e916SYuri Benditovich             return RSC_NO_MATCH;
16182974e916SYuri Benditovich     }
16192974e916SYuri Benditovich 
16202974e916SYuri Benditovich     return virtio_net_rsc_coalesce_data(chain, seg, buf, unit);
16212974e916SYuri Benditovich }
16222974e916SYuri Benditovich 
16232974e916SYuri Benditovich /* Packets with 'SYN' should bypass, other flag should be sent after drain
16242974e916SYuri Benditovich  * to prevent out of order */
16252974e916SYuri Benditovich static int virtio_net_rsc_tcp_ctrl_check(VirtioNetRscChain *chain,
16262974e916SYuri Benditovich                                          struct tcp_header *tcp)
16272974e916SYuri Benditovich {
16282974e916SYuri Benditovich     uint16_t tcp_hdr;
16292974e916SYuri Benditovich     uint16_t tcp_flag;
16302974e916SYuri Benditovich 
16312974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags);
16322974e916SYuri Benditovich     tcp_hdr = (tcp_flag & VIRTIO_NET_TCP_HDR_LENGTH) >> 10;
16332974e916SYuri Benditovich     tcp_flag &= VIRTIO_NET_TCP_FLAG;
16342974e916SYuri Benditovich     tcp_flag = htons(tcp->th_offset_flags) & 0x3F;
16352974e916SYuri Benditovich     if (tcp_flag & TH_SYN) {
16362974e916SYuri Benditovich         chain->stat.tcp_syn++;
16372974e916SYuri Benditovich         return RSC_BYPASS;
16382974e916SYuri Benditovich     }
16392974e916SYuri Benditovich 
16402974e916SYuri Benditovich     if (tcp_flag & (TH_FIN | TH_URG | TH_RST | TH_ECE | TH_CWR)) {
16412974e916SYuri Benditovich         chain->stat.tcp_ctrl_drain++;
16422974e916SYuri Benditovich         return RSC_FINAL;
16432974e916SYuri Benditovich     }
16442974e916SYuri Benditovich 
16452974e916SYuri Benditovich     if (tcp_hdr > sizeof(struct tcp_header)) {
16462974e916SYuri Benditovich         chain->stat.tcp_all_opt++;
16472974e916SYuri Benditovich         return RSC_FINAL;
16482974e916SYuri Benditovich     }
16492974e916SYuri Benditovich 
16502974e916SYuri Benditovich     return RSC_CANDIDATE;
16512974e916SYuri Benditovich }
16522974e916SYuri Benditovich 
16532974e916SYuri Benditovich static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain,
16542974e916SYuri Benditovich                                          NetClientState *nc,
16552974e916SYuri Benditovich                                          const uint8_t *buf, size_t size,
16562974e916SYuri Benditovich                                          VirtioNetRscUnit *unit)
16572974e916SYuri Benditovich {
16582974e916SYuri Benditovich     int ret;
16592974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
16602974e916SYuri Benditovich 
16612974e916SYuri Benditovich     if (QTAILQ_EMPTY(&chain->buffers)) {
16622974e916SYuri Benditovich         chain->stat.empty_cache++;
16632974e916SYuri Benditovich         virtio_net_rsc_cache_buf(chain, nc, buf, size);
16642974e916SYuri Benditovich         timer_mod(chain->drain_timer,
16652974e916SYuri Benditovich               qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout);
16662974e916SYuri Benditovich         return size;
16672974e916SYuri Benditovich     }
16682974e916SYuri Benditovich 
16692974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
16702974e916SYuri Benditovich         if (chain->proto == ETH_P_IP) {
16712974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce4(chain, seg, buf, size, unit);
16722974e916SYuri Benditovich         } else {
16732974e916SYuri Benditovich             ret = virtio_net_rsc_coalesce6(chain, seg, buf, size, unit);
16742974e916SYuri Benditovich         }
16752974e916SYuri Benditovich 
16762974e916SYuri Benditovich         if (ret == RSC_FINAL) {
16772974e916SYuri Benditovich             if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
16782974e916SYuri Benditovich                 /* Send failed */
16792974e916SYuri Benditovich                 chain->stat.final_failed++;
16802974e916SYuri Benditovich                 return 0;
16812974e916SYuri Benditovich             }
16822974e916SYuri Benditovich 
16832974e916SYuri Benditovich             /* Send current packet */
16842974e916SYuri Benditovich             return virtio_net_do_receive(nc, buf, size);
16852974e916SYuri Benditovich         } else if (ret == RSC_NO_MATCH) {
16862974e916SYuri Benditovich             continue;
16872974e916SYuri Benditovich         } else {
16882974e916SYuri Benditovich             /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */
16892974e916SYuri Benditovich             seg->is_coalesced = 1;
16902974e916SYuri Benditovich             return size;
16912974e916SYuri Benditovich         }
16922974e916SYuri Benditovich     }
16932974e916SYuri Benditovich 
16942974e916SYuri Benditovich     chain->stat.no_match_cache++;
16952974e916SYuri Benditovich     virtio_net_rsc_cache_buf(chain, nc, buf, size);
16962974e916SYuri Benditovich     return size;
16972974e916SYuri Benditovich }
16982974e916SYuri Benditovich 
16992974e916SYuri Benditovich /* Drain a connection data, this is to avoid out of order segments */
17002974e916SYuri Benditovich static size_t virtio_net_rsc_drain_flow(VirtioNetRscChain *chain,
17012974e916SYuri Benditovich                                         NetClientState *nc,
17022974e916SYuri Benditovich                                         const uint8_t *buf, size_t size,
17032974e916SYuri Benditovich                                         uint16_t ip_start, uint16_t ip_size,
17042974e916SYuri Benditovich                                         uint16_t tcp_port)
17052974e916SYuri Benditovich {
17062974e916SYuri Benditovich     VirtioNetRscSeg *seg, *nseg;
17072974e916SYuri Benditovich     uint32_t ppair1, ppair2;
17082974e916SYuri Benditovich 
17092974e916SYuri Benditovich     ppair1 = *(uint32_t *)(buf + tcp_port);
17102974e916SYuri Benditovich     QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
17112974e916SYuri Benditovich         ppair2 = *(uint32_t *)(seg->buf + tcp_port);
17122974e916SYuri Benditovich         if (memcmp(buf + ip_start, seg->buf + ip_start, ip_size)
17132974e916SYuri Benditovich             || (ppair1 != ppair2)) {
17142974e916SYuri Benditovich             continue;
17152974e916SYuri Benditovich         }
17162974e916SYuri Benditovich         if (virtio_net_rsc_drain_seg(chain, seg) == 0) {
17172974e916SYuri Benditovich             chain->stat.drain_failed++;
17182974e916SYuri Benditovich         }
17192974e916SYuri Benditovich 
17202974e916SYuri Benditovich         break;
17212974e916SYuri Benditovich     }
17222974e916SYuri Benditovich 
17232974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
17242974e916SYuri Benditovich }
17252974e916SYuri Benditovich 
17262974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check4(VirtioNetRscChain *chain,
17272974e916SYuri Benditovich                                             struct ip_header *ip,
17282974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
17292974e916SYuri Benditovich {
17302974e916SYuri Benditovich     uint16_t ip_len;
17312974e916SYuri Benditovich 
17322974e916SYuri Benditovich     /* Not an ipv4 packet */
17332974e916SYuri Benditovich     if (((ip->ip_ver_len & 0xF0) >> 4) != IP_HEADER_VERSION_4) {
17342974e916SYuri Benditovich         chain->stat.ip_option++;
17352974e916SYuri Benditovich         return RSC_BYPASS;
17362974e916SYuri Benditovich     }
17372974e916SYuri Benditovich 
17382974e916SYuri Benditovich     /* Don't handle packets with ip option */
17392974e916SYuri Benditovich     if ((ip->ip_ver_len & 0xF) != VIRTIO_NET_IP4_HEADER_LENGTH) {
17402974e916SYuri Benditovich         chain->stat.ip_option++;
17412974e916SYuri Benditovich         return RSC_BYPASS;
17422974e916SYuri Benditovich     }
17432974e916SYuri Benditovich 
17442974e916SYuri Benditovich     if (ip->ip_p != IPPROTO_TCP) {
17452974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
17462974e916SYuri Benditovich         return RSC_BYPASS;
17472974e916SYuri Benditovich     }
17482974e916SYuri Benditovich 
17492974e916SYuri Benditovich     /* Don't handle packets with ip fragment */
17502974e916SYuri Benditovich     if (!(htons(ip->ip_off) & IP_DF)) {
17512974e916SYuri Benditovich         chain->stat.ip_frag++;
17522974e916SYuri Benditovich         return RSC_BYPASS;
17532974e916SYuri Benditovich     }
17542974e916SYuri Benditovich 
17552974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
17562974e916SYuri Benditovich     if (IPTOS_ECN(ip->ip_tos)) {
17572974e916SYuri Benditovich         chain->stat.ip_ecn++;
17582974e916SYuri Benditovich         return RSC_BYPASS;
17592974e916SYuri Benditovich     }
17602974e916SYuri Benditovich 
17612974e916SYuri Benditovich     ip_len = htons(ip->ip_len);
17622974e916SYuri Benditovich     if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header))
17632974e916SYuri Benditovich         || ip_len > (size - chain->n->guest_hdr_len -
17642974e916SYuri Benditovich                      sizeof(struct eth_header))) {
17652974e916SYuri Benditovich         chain->stat.ip_hacked++;
17662974e916SYuri Benditovich         return RSC_BYPASS;
17672974e916SYuri Benditovich     }
17682974e916SYuri Benditovich 
17692974e916SYuri Benditovich     return RSC_CANDIDATE;
17702974e916SYuri Benditovich }
17712974e916SYuri Benditovich 
17722974e916SYuri Benditovich static size_t virtio_net_rsc_receive4(VirtioNetRscChain *chain,
17732974e916SYuri Benditovich                                       NetClientState *nc,
17742974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
17752974e916SYuri Benditovich {
17762974e916SYuri Benditovich     int32_t ret;
17772974e916SYuri Benditovich     uint16_t hdr_len;
17782974e916SYuri Benditovich     VirtioNetRscUnit unit;
17792974e916SYuri Benditovich 
17802974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
17812974e916SYuri Benditovich 
17822974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header)
17832974e916SYuri Benditovich         + sizeof(struct tcp_header))) {
17842974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
17852974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
17862974e916SYuri Benditovich     }
17872974e916SYuri Benditovich 
17882974e916SYuri Benditovich     virtio_net_rsc_extract_unit4(chain, buf, &unit);
17892974e916SYuri Benditovich     if (virtio_net_rsc_sanity_check4(chain, unit.ip, buf, size)
17902974e916SYuri Benditovich         != RSC_CANDIDATE) {
17912974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
17922974e916SYuri Benditovich     }
17932974e916SYuri Benditovich 
17942974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
17952974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
17962974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
17972974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
17982974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
17992974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 12),
18002974e916SYuri Benditovich                 VIRTIO_NET_IP4_ADDR_SIZE,
18012974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header));
18022974e916SYuri Benditovich     }
18032974e916SYuri Benditovich 
18042974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
18052974e916SYuri Benditovich }
18062974e916SYuri Benditovich 
18072974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check6(VirtioNetRscChain *chain,
18082974e916SYuri Benditovich                                             struct ip6_header *ip6,
18092974e916SYuri Benditovich                                             const uint8_t *buf, size_t size)
18102974e916SYuri Benditovich {
18112974e916SYuri Benditovich     uint16_t ip_len;
18122974e916SYuri Benditovich 
18132974e916SYuri Benditovich     if (((ip6->ip6_ctlun.ip6_un1.ip6_un1_flow & 0xF0) >> 4)
18142974e916SYuri Benditovich         != IP_HEADER_VERSION_6) {
18152974e916SYuri Benditovich         return RSC_BYPASS;
18162974e916SYuri Benditovich     }
18172974e916SYuri Benditovich 
18182974e916SYuri Benditovich     /* Both option and protocol is checked in this */
18192974e916SYuri Benditovich     if (ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) {
18202974e916SYuri Benditovich         chain->stat.bypass_not_tcp++;
18212974e916SYuri Benditovich         return RSC_BYPASS;
18222974e916SYuri Benditovich     }
18232974e916SYuri Benditovich 
18242974e916SYuri Benditovich     ip_len = htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen);
18252974e916SYuri Benditovich     if (ip_len < sizeof(struct tcp_header) ||
18262974e916SYuri Benditovich         ip_len > (size - chain->n->guest_hdr_len - sizeof(struct eth_header)
18272974e916SYuri Benditovich                   - sizeof(struct ip6_header))) {
18282974e916SYuri Benditovich         chain->stat.ip_hacked++;
18292974e916SYuri Benditovich         return RSC_BYPASS;
18302974e916SYuri Benditovich     }
18312974e916SYuri Benditovich 
18322974e916SYuri Benditovich     /* Don't handle packets with ecn flag */
18332974e916SYuri Benditovich     if (IP6_ECN(ip6->ip6_ctlun.ip6_un3.ip6_un3_ecn)) {
18342974e916SYuri Benditovich         chain->stat.ip_ecn++;
18352974e916SYuri Benditovich         return RSC_BYPASS;
18362974e916SYuri Benditovich     }
18372974e916SYuri Benditovich 
18382974e916SYuri Benditovich     return RSC_CANDIDATE;
18392974e916SYuri Benditovich }
18402974e916SYuri Benditovich 
18412974e916SYuri Benditovich static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc,
18422974e916SYuri Benditovich                                       const uint8_t *buf, size_t size)
18432974e916SYuri Benditovich {
18442974e916SYuri Benditovich     int32_t ret;
18452974e916SYuri Benditovich     uint16_t hdr_len;
18462974e916SYuri Benditovich     VirtioNetRscChain *chain;
18472974e916SYuri Benditovich     VirtioNetRscUnit unit;
18482974e916SYuri Benditovich 
18492974e916SYuri Benditovich     chain = (VirtioNetRscChain *)opq;
18502974e916SYuri Benditovich     hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len;
18512974e916SYuri Benditovich 
18522974e916SYuri Benditovich     if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header)
18532974e916SYuri Benditovich         + sizeof(tcp_header))) {
18542974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18552974e916SYuri Benditovich     }
18562974e916SYuri Benditovich 
18572974e916SYuri Benditovich     virtio_net_rsc_extract_unit6(chain, buf, &unit);
18582974e916SYuri Benditovich     if (RSC_CANDIDATE != virtio_net_rsc_sanity_check6(chain,
18592974e916SYuri Benditovich                                                  unit.ip, buf, size)) {
18602974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18612974e916SYuri Benditovich     }
18622974e916SYuri Benditovich 
18632974e916SYuri Benditovich     ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp);
18642974e916SYuri Benditovich     if (ret == RSC_BYPASS) {
18652974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
18662974e916SYuri Benditovich     } else if (ret == RSC_FINAL) {
18672974e916SYuri Benditovich         return virtio_net_rsc_drain_flow(chain, nc, buf, size,
18682974e916SYuri Benditovich                 ((hdr_len + sizeof(struct eth_header)) + 8),
18692974e916SYuri Benditovich                 VIRTIO_NET_IP6_ADDR_SIZE,
18702974e916SYuri Benditovich                 hdr_len + sizeof(struct eth_header)
18712974e916SYuri Benditovich                 + sizeof(struct ip6_header));
18722974e916SYuri Benditovich     }
18732974e916SYuri Benditovich 
18742974e916SYuri Benditovich     return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit);
18752974e916SYuri Benditovich }
18762974e916SYuri Benditovich 
18772974e916SYuri Benditovich static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n,
18782974e916SYuri Benditovich                                                       NetClientState *nc,
18792974e916SYuri Benditovich                                                       uint16_t proto)
18802974e916SYuri Benditovich {
18812974e916SYuri Benditovich     VirtioNetRscChain *chain;
18822974e916SYuri Benditovich 
18832974e916SYuri Benditovich     if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) {
18842974e916SYuri Benditovich         return NULL;
18852974e916SYuri Benditovich     }
18862974e916SYuri Benditovich 
18872974e916SYuri Benditovich     QTAILQ_FOREACH(chain, &n->rsc_chains, next) {
18882974e916SYuri Benditovich         if (chain->proto == proto) {
18892974e916SYuri Benditovich             return chain;
18902974e916SYuri Benditovich         }
18912974e916SYuri Benditovich     }
18922974e916SYuri Benditovich 
18932974e916SYuri Benditovich     chain = g_malloc(sizeof(*chain));
18942974e916SYuri Benditovich     chain->n = n;
18952974e916SYuri Benditovich     chain->proto = proto;
18962974e916SYuri Benditovich     if (proto == (uint16_t)ETH_P_IP) {
18972974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP4_PAYLOAD;
18982974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
18992974e916SYuri Benditovich     } else {
19002974e916SYuri Benditovich         chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD;
19012974e916SYuri Benditovich         chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
19022974e916SYuri Benditovich     }
19032974e916SYuri Benditovich     chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST,
19042974e916SYuri Benditovich                                       virtio_net_rsc_purge, chain);
19052974e916SYuri Benditovich     memset(&chain->stat, 0, sizeof(chain->stat));
19062974e916SYuri Benditovich 
19072974e916SYuri Benditovich     QTAILQ_INIT(&chain->buffers);
19082974e916SYuri Benditovich     QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next);
19092974e916SYuri Benditovich 
19102974e916SYuri Benditovich     return chain;
19112974e916SYuri Benditovich }
19122974e916SYuri Benditovich 
19132974e916SYuri Benditovich static ssize_t virtio_net_rsc_receive(NetClientState *nc,
19142974e916SYuri Benditovich                                       const uint8_t *buf,
19152974e916SYuri Benditovich                                       size_t size)
19162974e916SYuri Benditovich {
19172974e916SYuri Benditovich     uint16_t proto;
19182974e916SYuri Benditovich     VirtioNetRscChain *chain;
19192974e916SYuri Benditovich     struct eth_header *eth;
19202974e916SYuri Benditovich     VirtIONet *n;
19212974e916SYuri Benditovich 
19222974e916SYuri Benditovich     n = qemu_get_nic_opaque(nc);
19232974e916SYuri Benditovich     if (size < (n->host_hdr_len + sizeof(struct eth_header))) {
19242974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19252974e916SYuri Benditovich     }
19262974e916SYuri Benditovich 
19272974e916SYuri Benditovich     eth = (struct eth_header *)(buf + n->guest_hdr_len);
19282974e916SYuri Benditovich     proto = htons(eth->h_proto);
19292974e916SYuri Benditovich 
19302974e916SYuri Benditovich     chain = virtio_net_rsc_lookup_chain(n, nc, proto);
19312974e916SYuri Benditovich     if (chain) {
19322974e916SYuri Benditovich         chain->stat.received++;
19332974e916SYuri Benditovich         if (proto == (uint16_t)ETH_P_IP && n->rsc4_enabled) {
19342974e916SYuri Benditovich             return virtio_net_rsc_receive4(chain, nc, buf, size);
19352974e916SYuri Benditovich         } else if (proto == (uint16_t)ETH_P_IPV6 && n->rsc6_enabled) {
19362974e916SYuri Benditovich             return virtio_net_rsc_receive6(chain, nc, buf, size);
19372974e916SYuri Benditovich         }
19382974e916SYuri Benditovich     }
19392974e916SYuri Benditovich     return virtio_net_do_receive(nc, buf, size);
19402974e916SYuri Benditovich }
19412974e916SYuri Benditovich 
19422974e916SYuri Benditovich static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf,
19432974e916SYuri Benditovich                                   size_t size)
19442974e916SYuri Benditovich {
19452974e916SYuri Benditovich     VirtIONet *n = qemu_get_nic_opaque(nc);
19462974e916SYuri Benditovich     if ((n->rsc4_enabled || n->rsc6_enabled)) {
19472974e916SYuri Benditovich         return virtio_net_rsc_receive(nc, buf, size);
19482974e916SYuri Benditovich     } else {
19492974e916SYuri Benditovich         return virtio_net_do_receive(nc, buf, size);
19502974e916SYuri Benditovich     }
19512974e916SYuri Benditovich }
19522974e916SYuri Benditovich 
19536e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q);
19546e790746SPaolo Bonzini 
19556e790746SPaolo Bonzini static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
19566e790746SPaolo Bonzini {
19576e790746SPaolo Bonzini     VirtIONet *n = qemu_get_nic_opaque(nc);
19586e790746SPaolo Bonzini     VirtIONetQueue *q = virtio_net_get_subqueue(nc);
195917a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
19606e790746SPaolo Bonzini 
196151b19ebeSPaolo Bonzini     virtqueue_push(q->tx_vq, q->async_tx.elem, 0);
196217a0ca55SKONRAD Frederic     virtio_notify(vdev, q->tx_vq);
19636e790746SPaolo Bonzini 
196451b19ebeSPaolo Bonzini     g_free(q->async_tx.elem);
196551b19ebeSPaolo Bonzini     q->async_tx.elem = NULL;
19666e790746SPaolo Bonzini 
19676e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
19686e790746SPaolo Bonzini     virtio_net_flush_tx(q);
19696e790746SPaolo Bonzini }
19706e790746SPaolo Bonzini 
19716e790746SPaolo Bonzini /* TX */
19726e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
19736e790746SPaolo Bonzini {
19746e790746SPaolo Bonzini     VirtIONet *n = q->n;
197517a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
197651b19ebeSPaolo Bonzini     VirtQueueElement *elem;
19776e790746SPaolo Bonzini     int32_t num_packets = 0;
19786e790746SPaolo Bonzini     int queue_index = vq2q(virtio_get_queue_index(q->tx_vq));
197917a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
19806e790746SPaolo Bonzini         return num_packets;
19816e790746SPaolo Bonzini     }
19826e790746SPaolo Bonzini 
198351b19ebeSPaolo Bonzini     if (q->async_tx.elem) {
19846e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
19856e790746SPaolo Bonzini         return num_packets;
19866e790746SPaolo Bonzini     }
19876e790746SPaolo Bonzini 
198851b19ebeSPaolo Bonzini     for (;;) {
1989bd89dd98SJason Wang         ssize_t ret;
199051b19ebeSPaolo Bonzini         unsigned int out_num;
199151b19ebeSPaolo Bonzini         struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
1992feb93f36SJason Wang         struct virtio_net_hdr_mrg_rxbuf mhdr;
19936e790746SPaolo Bonzini 
199451b19ebeSPaolo Bonzini         elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
199551b19ebeSPaolo Bonzini         if (!elem) {
199651b19ebeSPaolo Bonzini             break;
199751b19ebeSPaolo Bonzini         }
199851b19ebeSPaolo Bonzini 
199951b19ebeSPaolo Bonzini         out_num = elem->out_num;
200051b19ebeSPaolo Bonzini         out_sg = elem->out_sg;
20016e790746SPaolo Bonzini         if (out_num < 1) {
2002fa5e56c2SGreg Kurz             virtio_error(vdev, "virtio-net header not in first element");
2003fa5e56c2SGreg Kurz             virtqueue_detach_element(q->tx_vq, elem, 0);
2004fa5e56c2SGreg Kurz             g_free(elem);
2005fa5e56c2SGreg Kurz             return -EINVAL;
20066e790746SPaolo Bonzini         }
20076e790746SPaolo Bonzini 
2008032a74a1SCédric Le Goater         if (n->has_vnet_hdr) {
2009feb93f36SJason Wang             if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
2010feb93f36SJason Wang                 n->guest_hdr_len) {
2011fa5e56c2SGreg Kurz                 virtio_error(vdev, "virtio-net header incorrect");
2012fa5e56c2SGreg Kurz                 virtqueue_detach_element(q->tx_vq, elem, 0);
2013fa5e56c2SGreg Kurz                 g_free(elem);
2014fa5e56c2SGreg Kurz                 return -EINVAL;
2015032a74a1SCédric Le Goater             }
20161bfa316cSGreg Kurz             if (n->needs_vnet_hdr_swap) {
2017feb93f36SJason Wang                 virtio_net_hdr_swap(vdev, (void *) &mhdr);
2018feb93f36SJason Wang                 sg2[0].iov_base = &mhdr;
2019feb93f36SJason Wang                 sg2[0].iov_len = n->guest_hdr_len;
2020feb93f36SJason Wang                 out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
2021feb93f36SJason Wang                                    out_sg, out_num,
2022feb93f36SJason Wang                                    n->guest_hdr_len, -1);
2023feb93f36SJason Wang                 if (out_num == VIRTQUEUE_MAX_SIZE) {
2024feb93f36SJason Wang                     goto drop;
2025032a74a1SCédric Le Goater                 }
2026feb93f36SJason Wang                 out_num += 1;
2027feb93f36SJason Wang                 out_sg = sg2;
2028feb93f36SJason Wang             }
2029feb93f36SJason Wang         }
20306e790746SPaolo Bonzini         /*
20316e790746SPaolo Bonzini          * If host wants to see the guest header as is, we can
20326e790746SPaolo Bonzini          * pass it on unchanged. Otherwise, copy just the parts
20336e790746SPaolo Bonzini          * that host is interested in.
20346e790746SPaolo Bonzini          */
20356e790746SPaolo Bonzini         assert(n->host_hdr_len <= n->guest_hdr_len);
20366e790746SPaolo Bonzini         if (n->host_hdr_len != n->guest_hdr_len) {
20376e790746SPaolo Bonzini             unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg),
20386e790746SPaolo Bonzini                                        out_sg, out_num,
20396e790746SPaolo Bonzini                                        0, n->host_hdr_len);
20406e790746SPaolo Bonzini             sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num,
20416e790746SPaolo Bonzini                              out_sg, out_num,
20426e790746SPaolo Bonzini                              n->guest_hdr_len, -1);
20436e790746SPaolo Bonzini             out_num = sg_num;
20446e790746SPaolo Bonzini             out_sg = sg;
20456e790746SPaolo Bonzini         }
20466e790746SPaolo Bonzini 
20476e790746SPaolo Bonzini         ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
20486e790746SPaolo Bonzini                                       out_sg, out_num, virtio_net_tx_complete);
20496e790746SPaolo Bonzini         if (ret == 0) {
20506e790746SPaolo Bonzini             virtio_queue_set_notification(q->tx_vq, 0);
20516e790746SPaolo Bonzini             q->async_tx.elem = elem;
20526e790746SPaolo Bonzini             return -EBUSY;
20536e790746SPaolo Bonzini         }
20546e790746SPaolo Bonzini 
2055feb93f36SJason Wang drop:
205651b19ebeSPaolo Bonzini         virtqueue_push(q->tx_vq, elem, 0);
205717a0ca55SKONRAD Frederic         virtio_notify(vdev, q->tx_vq);
205851b19ebeSPaolo Bonzini         g_free(elem);
20596e790746SPaolo Bonzini 
20606e790746SPaolo Bonzini         if (++num_packets >= n->tx_burst) {
20616e790746SPaolo Bonzini             break;
20626e790746SPaolo Bonzini         }
20636e790746SPaolo Bonzini     }
20646e790746SPaolo Bonzini     return num_packets;
20656e790746SPaolo Bonzini }
20666e790746SPaolo Bonzini 
20676e790746SPaolo Bonzini static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
20686e790746SPaolo Bonzini {
206917a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
20706e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
20716e790746SPaolo Bonzini 
2072283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2073283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2074283e2c2aSYuri Benditovich         return;
2075283e2c2aSYuri Benditovich     }
2076283e2c2aSYuri Benditovich 
20776e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
207817a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
20796e790746SPaolo Bonzini         q->tx_waiting = 1;
20806e790746SPaolo Bonzini         return;
20816e790746SPaolo Bonzini     }
20826e790746SPaolo Bonzini 
20836e790746SPaolo Bonzini     if (q->tx_waiting) {
20846e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 1);
2085bc72ad67SAlex Bligh         timer_del(q->tx_timer);
20866e790746SPaolo Bonzini         q->tx_waiting = 0;
2087fa5e56c2SGreg Kurz         if (virtio_net_flush_tx(q) == -EINVAL) {
2088fa5e56c2SGreg Kurz             return;
2089fa5e56c2SGreg Kurz         }
20906e790746SPaolo Bonzini     } else {
2091bc72ad67SAlex Bligh         timer_mod(q->tx_timer,
2092bc72ad67SAlex Bligh                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
20936e790746SPaolo Bonzini         q->tx_waiting = 1;
20946e790746SPaolo Bonzini         virtio_queue_set_notification(vq, 0);
20956e790746SPaolo Bonzini     }
20966e790746SPaolo Bonzini }
20976e790746SPaolo Bonzini 
20986e790746SPaolo Bonzini static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
20996e790746SPaolo Bonzini {
210017a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
21016e790746SPaolo Bonzini     VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
21026e790746SPaolo Bonzini 
2103283e2c2aSYuri Benditovich     if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) {
2104283e2c2aSYuri Benditovich         virtio_net_drop_tx_queue_data(vdev, vq);
2105283e2c2aSYuri Benditovich         return;
2106283e2c2aSYuri Benditovich     }
2107283e2c2aSYuri Benditovich 
21086e790746SPaolo Bonzini     if (unlikely(q->tx_waiting)) {
21096e790746SPaolo Bonzini         return;
21106e790746SPaolo Bonzini     }
21116e790746SPaolo Bonzini     q->tx_waiting = 1;
21126e790746SPaolo Bonzini     /* This happens when device was stopped but VCPU wasn't. */
211317a0ca55SKONRAD Frederic     if (!vdev->vm_running) {
21146e790746SPaolo Bonzini         return;
21156e790746SPaolo Bonzini     }
21166e790746SPaolo Bonzini     virtio_queue_set_notification(vq, 0);
21176e790746SPaolo Bonzini     qemu_bh_schedule(q->tx_bh);
21186e790746SPaolo Bonzini }
21196e790746SPaolo Bonzini 
21206e790746SPaolo Bonzini static void virtio_net_tx_timer(void *opaque)
21216e790746SPaolo Bonzini {
21226e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
21236e790746SPaolo Bonzini     VirtIONet *n = q->n;
212417a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2125e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2126e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2127e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2128e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2129e8bcf842SMichael S. Tsirkin         return;
2130e8bcf842SMichael S. Tsirkin     }
21316e790746SPaolo Bonzini 
21326e790746SPaolo Bonzini     q->tx_waiting = 0;
21336e790746SPaolo Bonzini 
21346e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
213517a0ca55SKONRAD Frederic     if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
21366e790746SPaolo Bonzini         return;
213717a0ca55SKONRAD Frederic     }
21386e790746SPaolo Bonzini 
21396e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
21406e790746SPaolo Bonzini     virtio_net_flush_tx(q);
21416e790746SPaolo Bonzini }
21426e790746SPaolo Bonzini 
21436e790746SPaolo Bonzini static void virtio_net_tx_bh(void *opaque)
21446e790746SPaolo Bonzini {
21456e790746SPaolo Bonzini     VirtIONetQueue *q = opaque;
21466e790746SPaolo Bonzini     VirtIONet *n = q->n;
214717a0ca55SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(n);
21486e790746SPaolo Bonzini     int32_t ret;
21496e790746SPaolo Bonzini 
2150e8bcf842SMichael S. Tsirkin     /* This happens when device was stopped but BH wasn't. */
2151e8bcf842SMichael S. Tsirkin     if (!vdev->vm_running) {
2152e8bcf842SMichael S. Tsirkin         /* Make sure tx waiting is set, so we'll run when restarted. */
2153e8bcf842SMichael S. Tsirkin         assert(q->tx_waiting);
2154e8bcf842SMichael S. Tsirkin         return;
2155e8bcf842SMichael S. Tsirkin     }
21566e790746SPaolo Bonzini 
21576e790746SPaolo Bonzini     q->tx_waiting = 0;
21586e790746SPaolo Bonzini 
21596e790746SPaolo Bonzini     /* Just in case the driver is not ready on more */
216017a0ca55SKONRAD Frederic     if (unlikely(!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) {
21616e790746SPaolo Bonzini         return;
216217a0ca55SKONRAD Frederic     }
21636e790746SPaolo Bonzini 
21646e790746SPaolo Bonzini     ret = virtio_net_flush_tx(q);
2165fa5e56c2SGreg Kurz     if (ret == -EBUSY || ret == -EINVAL) {
2166fa5e56c2SGreg Kurz         return; /* Notification re-enable handled by tx_complete or device
2167fa5e56c2SGreg Kurz                  * broken */
21686e790746SPaolo Bonzini     }
21696e790746SPaolo Bonzini 
21706e790746SPaolo Bonzini     /* If we flush a full burst of packets, assume there are
21716e790746SPaolo Bonzini      * more coming and immediately reschedule */
21726e790746SPaolo Bonzini     if (ret >= n->tx_burst) {
21736e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
21746e790746SPaolo Bonzini         q->tx_waiting = 1;
21756e790746SPaolo Bonzini         return;
21766e790746SPaolo Bonzini     }
21776e790746SPaolo Bonzini 
21786e790746SPaolo Bonzini     /* If less than a full burst, re-enable notification and flush
21796e790746SPaolo Bonzini      * anything that may have come in while we weren't looking.  If
21806e790746SPaolo Bonzini      * we find something, assume the guest is still active and reschedule */
21816e790746SPaolo Bonzini     virtio_queue_set_notification(q->tx_vq, 1);
2182fa5e56c2SGreg Kurz     ret = virtio_net_flush_tx(q);
2183fa5e56c2SGreg Kurz     if (ret == -EINVAL) {
2184fa5e56c2SGreg Kurz         return;
2185fa5e56c2SGreg Kurz     } else if (ret > 0) {
21866e790746SPaolo Bonzini         virtio_queue_set_notification(q->tx_vq, 0);
21876e790746SPaolo Bonzini         qemu_bh_schedule(q->tx_bh);
21886e790746SPaolo Bonzini         q->tx_waiting = 1;
21896e790746SPaolo Bonzini     }
21906e790746SPaolo Bonzini }
21916e790746SPaolo Bonzini 
2192f9d6dbf0SWen Congyang static void virtio_net_add_queue(VirtIONet *n, int index)
2193f9d6dbf0SWen Congyang {
2194f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2195f9d6dbf0SWen Congyang 
21961c0fbfa3SMichael S. Tsirkin     n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size,
21971c0fbfa3SMichael S. Tsirkin                                            virtio_net_handle_rx);
21989b02e161SWei Wang 
2199f9d6dbf0SWen Congyang     if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) {
2200f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
22019b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
22029b02e161SWei Wang                              virtio_net_handle_tx_timer);
2203f9d6dbf0SWen Congyang         n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
2204f9d6dbf0SWen Congyang                                               virtio_net_tx_timer,
2205f9d6dbf0SWen Congyang                                               &n->vqs[index]);
2206f9d6dbf0SWen Congyang     } else {
2207f9d6dbf0SWen Congyang         n->vqs[index].tx_vq =
22089b02e161SWei Wang             virtio_add_queue(vdev, n->net_conf.tx_queue_size,
22099b02e161SWei Wang                              virtio_net_handle_tx_bh);
2210f9d6dbf0SWen Congyang         n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]);
2211f9d6dbf0SWen Congyang     }
2212f9d6dbf0SWen Congyang 
2213f9d6dbf0SWen Congyang     n->vqs[index].tx_waiting = 0;
2214f9d6dbf0SWen Congyang     n->vqs[index].n = n;
2215f9d6dbf0SWen Congyang }
2216f9d6dbf0SWen Congyang 
2217f9d6dbf0SWen Congyang static void virtio_net_del_queue(VirtIONet *n, int index)
2218f9d6dbf0SWen Congyang {
2219f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2220f9d6dbf0SWen Congyang     VirtIONetQueue *q = &n->vqs[index];
2221f9d6dbf0SWen Congyang     NetClientState *nc = qemu_get_subqueue(n->nic, index);
2222f9d6dbf0SWen Congyang 
2223f9d6dbf0SWen Congyang     qemu_purge_queued_packets(nc);
2224f9d6dbf0SWen Congyang 
2225f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2);
2226f9d6dbf0SWen Congyang     if (q->tx_timer) {
2227f9d6dbf0SWen Congyang         timer_del(q->tx_timer);
2228f9d6dbf0SWen Congyang         timer_free(q->tx_timer);
2229f989c30cSYunjian Wang         q->tx_timer = NULL;
2230f9d6dbf0SWen Congyang     } else {
2231f9d6dbf0SWen Congyang         qemu_bh_delete(q->tx_bh);
2232f989c30cSYunjian Wang         q->tx_bh = NULL;
2233f9d6dbf0SWen Congyang     }
2234f989c30cSYunjian Wang     q->tx_waiting = 0;
2235f9d6dbf0SWen Congyang     virtio_del_queue(vdev, index * 2 + 1);
2236f9d6dbf0SWen Congyang }
2237f9d6dbf0SWen Congyang 
2238f9d6dbf0SWen Congyang static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues)
2239f9d6dbf0SWen Congyang {
2240f9d6dbf0SWen Congyang     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2241f9d6dbf0SWen Congyang     int old_num_queues = virtio_get_num_queues(vdev);
2242f9d6dbf0SWen Congyang     int new_num_queues = new_max_queues * 2 + 1;
2243f9d6dbf0SWen Congyang     int i;
2244f9d6dbf0SWen Congyang 
2245f9d6dbf0SWen Congyang     assert(old_num_queues >= 3);
2246f9d6dbf0SWen Congyang     assert(old_num_queues % 2 == 1);
2247f9d6dbf0SWen Congyang 
2248f9d6dbf0SWen Congyang     if (old_num_queues == new_num_queues) {
2249f9d6dbf0SWen Congyang         return;
2250f9d6dbf0SWen Congyang     }
2251f9d6dbf0SWen Congyang 
2252f9d6dbf0SWen Congyang     /*
2253f9d6dbf0SWen Congyang      * We always need to remove and add ctrl vq if
2254f9d6dbf0SWen Congyang      * old_num_queues != new_num_queues. Remove ctrl_vq first,
2255f9d6dbf0SWen Congyang      * and then we only enter one of the following too loops.
2256f9d6dbf0SWen Congyang      */
2257f9d6dbf0SWen Congyang     virtio_del_queue(vdev, old_num_queues - 1);
2258f9d6dbf0SWen Congyang 
2259f9d6dbf0SWen Congyang     for (i = new_num_queues - 1; i < old_num_queues - 1; i += 2) {
2260f9d6dbf0SWen Congyang         /* new_num_queues < old_num_queues */
2261f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i / 2);
2262f9d6dbf0SWen Congyang     }
2263f9d6dbf0SWen Congyang 
2264f9d6dbf0SWen Congyang     for (i = old_num_queues - 1; i < new_num_queues - 1; i += 2) {
2265f9d6dbf0SWen Congyang         /* new_num_queues > old_num_queues */
2266f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i / 2);
2267f9d6dbf0SWen Congyang     }
2268f9d6dbf0SWen Congyang 
2269f9d6dbf0SWen Congyang     /* add ctrl_vq last */
2270f9d6dbf0SWen Congyang     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
2271f9d6dbf0SWen Congyang }
2272f9d6dbf0SWen Congyang 
2273ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
22746e790746SPaolo Bonzini {
2275f9d6dbf0SWen Congyang     int max = multiqueue ? n->max_queues : 1;
2276f9d6dbf0SWen Congyang 
22776e790746SPaolo Bonzini     n->multiqueue = multiqueue;
2278f9d6dbf0SWen Congyang     virtio_net_change_num_queues(n, max);
22796e790746SPaolo Bonzini 
22806e790746SPaolo Bonzini     virtio_net_set_queues(n);
22816e790746SPaolo Bonzini }
22826e790746SPaolo Bonzini 
2283982b78c5SDr. David Alan Gilbert static int virtio_net_post_load_device(void *opaque, int version_id)
2284037dab2fSGreg Kurz {
2285982b78c5SDr. David Alan Gilbert     VirtIONet *n = opaque;
2286982b78c5SDr. David Alan Gilbert     VirtIODevice *vdev = VIRTIO_DEVICE(n);
2287037dab2fSGreg Kurz     int i, link_down;
2288037dab2fSGreg Kurz 
2289982b78c5SDr. David Alan Gilbert     virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
229095129d6fSCornelia Huck                                virtio_vdev_has_feature(vdev,
229195129d6fSCornelia Huck                                                        VIRTIO_F_VERSION_1));
22926e790746SPaolo Bonzini 
22936e790746SPaolo Bonzini     /* MAC_TABLE_ENTRIES may be different from the saved image */
2294982b78c5SDr. David Alan Gilbert     if (n->mac_table.in_use > MAC_TABLE_ENTRIES) {
22956e790746SPaolo Bonzini         n->mac_table.in_use = 0;
22966e790746SPaolo Bonzini     }
22976e790746SPaolo Bonzini 
2298982b78c5SDr. David Alan Gilbert     if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
22996c666823SMichael S. Tsirkin         n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
23006c666823SMichael S. Tsirkin     }
23016c666823SMichael S. Tsirkin 
23026c666823SMichael S. Tsirkin     if (peer_has_vnet_hdr(n)) {
23036c666823SMichael S. Tsirkin         virtio_net_apply_guest_offloads(n);
23046c666823SMichael S. Tsirkin     }
23056c666823SMichael S. Tsirkin 
23066e790746SPaolo Bonzini     virtio_net_set_queues(n);
23076e790746SPaolo Bonzini 
23086e790746SPaolo Bonzini     /* Find the first multicast entry in the saved MAC filter */
23096e790746SPaolo Bonzini     for (i = 0; i < n->mac_table.in_use; i++) {
23106e790746SPaolo Bonzini         if (n->mac_table.macs[i * ETH_ALEN] & 1) {
23116e790746SPaolo Bonzini             break;
23126e790746SPaolo Bonzini         }
23136e790746SPaolo Bonzini     }
23146e790746SPaolo Bonzini     n->mac_table.first_multi = i;
23156e790746SPaolo Bonzini 
23166e790746SPaolo Bonzini     /* nc.link_down can't be migrated, so infer link_down according
23176e790746SPaolo Bonzini      * to link status bit in n->status */
23186e790746SPaolo Bonzini     link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0;
23196e790746SPaolo Bonzini     for (i = 0; i < n->max_queues; i++) {
23206e790746SPaolo Bonzini         qemu_get_subqueue(n->nic, i)->link_down = link_down;
23216e790746SPaolo Bonzini     }
23226e790746SPaolo Bonzini 
23236c666823SMichael S. Tsirkin     if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
23246c666823SMichael S. Tsirkin         virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
23256c666823SMichael S. Tsirkin         n->announce_counter = SELF_ANNOUNCE_ROUNDS;
23266c666823SMichael S. Tsirkin         timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL));
23276c666823SMichael S. Tsirkin     }
23286c666823SMichael S. Tsirkin 
23296e790746SPaolo Bonzini     return 0;
23306e790746SPaolo Bonzini }
23316e790746SPaolo Bonzini 
2332982b78c5SDr. David Alan Gilbert /* tx_waiting field of a VirtIONetQueue */
2333982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = {
2334982b78c5SDr. David Alan Gilbert     .name = "virtio-net-queue-tx_waiting",
2335982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2336982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(tx_waiting, VirtIONetQueue),
2337982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2338982b78c5SDr. David Alan Gilbert    },
2339982b78c5SDr. David Alan Gilbert };
2340982b78c5SDr. David Alan Gilbert 
2341982b78c5SDr. David Alan Gilbert static bool max_queues_gt_1(void *opaque, int version_id)
2342982b78c5SDr. David Alan Gilbert {
2343982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->max_queues > 1;
2344982b78c5SDr. David Alan Gilbert }
2345982b78c5SDr. David Alan Gilbert 
2346982b78c5SDr. David Alan Gilbert static bool has_ctrl_guest_offloads(void *opaque, int version_id)
2347982b78c5SDr. David Alan Gilbert {
2348982b78c5SDr. David Alan Gilbert     return virtio_vdev_has_feature(VIRTIO_DEVICE(opaque),
2349982b78c5SDr. David Alan Gilbert                                    VIRTIO_NET_F_CTRL_GUEST_OFFLOADS);
2350982b78c5SDr. David Alan Gilbert }
2351982b78c5SDr. David Alan Gilbert 
2352982b78c5SDr. David Alan Gilbert static bool mac_table_fits(void *opaque, int version_id)
2353982b78c5SDr. David Alan Gilbert {
2354982b78c5SDr. David Alan Gilbert     return VIRTIO_NET(opaque)->mac_table.in_use <= MAC_TABLE_ENTRIES;
2355982b78c5SDr. David Alan Gilbert }
2356982b78c5SDr. David Alan Gilbert 
2357982b78c5SDr. David Alan Gilbert static bool mac_table_doesnt_fit(void *opaque, int version_id)
2358982b78c5SDr. David Alan Gilbert {
2359982b78c5SDr. David Alan Gilbert     return !mac_table_fits(opaque, version_id);
2360982b78c5SDr. David Alan Gilbert }
2361982b78c5SDr. David Alan Gilbert 
2362982b78c5SDr. David Alan Gilbert /* This temporary type is shared by all the WITH_TMP methods
2363982b78c5SDr. David Alan Gilbert  * although only some fields are used by each.
2364982b78c5SDr. David Alan Gilbert  */
2365982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp {
2366982b78c5SDr. David Alan Gilbert     VirtIONet      *parent;
2367982b78c5SDr. David Alan Gilbert     VirtIONetQueue *vqs_1;
2368982b78c5SDr. David Alan Gilbert     uint16_t        curr_queues_1;
2369982b78c5SDr. David Alan Gilbert     uint8_t         has_ufo;
2370982b78c5SDr. David Alan Gilbert     uint32_t        has_vnet_hdr;
2371982b78c5SDr. David Alan Gilbert };
2372982b78c5SDr. David Alan Gilbert 
2373982b78c5SDr. David Alan Gilbert /* The 2nd and subsequent tx_waiting flags are loaded later than
2374982b78c5SDr. David Alan Gilbert  * the 1st entry in the queues and only if there's more than one
2375982b78c5SDr. David Alan Gilbert  * entry.  We use the tmp mechanism to calculate a temporary
2376982b78c5SDr. David Alan Gilbert  * pointer and count and also validate the count.
2377982b78c5SDr. David Alan Gilbert  */
2378982b78c5SDr. David Alan Gilbert 
237944b1ff31SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_save(void *opaque)
2380982b78c5SDr. David Alan Gilbert {
2381982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2382982b78c5SDr. David Alan Gilbert 
2383982b78c5SDr. David Alan Gilbert     tmp->vqs_1 = tmp->parent->vqs + 1;
2384982b78c5SDr. David Alan Gilbert     tmp->curr_queues_1 = tmp->parent->curr_queues - 1;
2385982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues == 0) {
2386982b78c5SDr. David Alan Gilbert         tmp->curr_queues_1 = 0;
2387982b78c5SDr. David Alan Gilbert     }
238844b1ff31SDr. David Alan Gilbert 
238944b1ff31SDr. David Alan Gilbert     return 0;
2390982b78c5SDr. David Alan Gilbert }
2391982b78c5SDr. David Alan Gilbert 
2392982b78c5SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_load(void *opaque)
2393982b78c5SDr. David Alan Gilbert {
2394982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2395982b78c5SDr. David Alan Gilbert 
2396982b78c5SDr. David Alan Gilbert     /* Reuse the pointer setup from save */
2397982b78c5SDr. David Alan Gilbert     virtio_net_tx_waiting_pre_save(opaque);
2398982b78c5SDr. David Alan Gilbert 
2399982b78c5SDr. David Alan Gilbert     if (tmp->parent->curr_queues > tmp->parent->max_queues) {
2400982b78c5SDr. David Alan Gilbert         error_report("virtio-net: curr_queues %x > max_queues %x",
2401982b78c5SDr. David Alan Gilbert             tmp->parent->curr_queues, tmp->parent->max_queues);
2402982b78c5SDr. David Alan Gilbert 
2403982b78c5SDr. David Alan Gilbert         return -EINVAL;
2404982b78c5SDr. David Alan Gilbert     }
2405982b78c5SDr. David Alan Gilbert 
2406982b78c5SDr. David Alan Gilbert     return 0; /* all good */
2407982b78c5SDr. David Alan Gilbert }
2408982b78c5SDr. David Alan Gilbert 
2409982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_tx_waiting = {
2410982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-tx_waiting",
2411982b78c5SDr. David Alan Gilbert     .pre_load  = virtio_net_tx_waiting_pre_load,
2412982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_tx_waiting_pre_save,
2413982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2414982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp,
2415982b78c5SDr. David Alan Gilbert                                      curr_queues_1,
2416982b78c5SDr. David Alan Gilbert                                      vmstate_virtio_net_queue_tx_waiting,
2417982b78c5SDr. David Alan Gilbert                                      struct VirtIONetQueue),
2418982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2419982b78c5SDr. David Alan Gilbert     },
2420982b78c5SDr. David Alan Gilbert };
2421982b78c5SDr. David Alan Gilbert 
2422982b78c5SDr. David Alan Gilbert /* the 'has_ufo' flag is just tested; if the incoming stream has the
2423982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2424982b78c5SDr. David Alan Gilbert  */
2425982b78c5SDr. David Alan Gilbert static int virtio_net_ufo_post_load(void *opaque, int version_id)
2426982b78c5SDr. David Alan Gilbert {
2427982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2428982b78c5SDr. David Alan Gilbert 
2429982b78c5SDr. David Alan Gilbert     if (tmp->has_ufo && !peer_has_ufo(tmp->parent)) {
2430982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires TUN_F_UFO support");
2431982b78c5SDr. David Alan Gilbert         return -EINVAL;
2432982b78c5SDr. David Alan Gilbert     }
2433982b78c5SDr. David Alan Gilbert 
2434982b78c5SDr. David Alan Gilbert     return 0;
2435982b78c5SDr. David Alan Gilbert }
2436982b78c5SDr. David Alan Gilbert 
243744b1ff31SDr. David Alan Gilbert static int virtio_net_ufo_pre_save(void *opaque)
2438982b78c5SDr. David Alan Gilbert {
2439982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2440982b78c5SDr. David Alan Gilbert 
2441982b78c5SDr. David Alan Gilbert     tmp->has_ufo = tmp->parent->has_ufo;
244244b1ff31SDr. David Alan Gilbert 
244344b1ff31SDr. David Alan Gilbert     return 0;
2444982b78c5SDr. David Alan Gilbert }
2445982b78c5SDr. David Alan Gilbert 
2446982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_ufo = {
2447982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-ufo",
2448982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_ufo_post_load,
2449982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_ufo_pre_save,
2450982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2451982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(has_ufo, struct VirtIONetMigTmp),
2452982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2453982b78c5SDr. David Alan Gilbert     },
2454982b78c5SDr. David Alan Gilbert };
2455982b78c5SDr. David Alan Gilbert 
2456982b78c5SDr. David Alan Gilbert /* the 'has_vnet_hdr' flag is just tested; if the incoming stream has the
2457982b78c5SDr. David Alan Gilbert  * flag set we need to check that we have it
2458982b78c5SDr. David Alan Gilbert  */
2459982b78c5SDr. David Alan Gilbert static int virtio_net_vnet_post_load(void *opaque, int version_id)
2460982b78c5SDr. David Alan Gilbert {
2461982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2462982b78c5SDr. David Alan Gilbert 
2463982b78c5SDr. David Alan Gilbert     if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) {
2464982b78c5SDr. David Alan Gilbert         error_report("virtio-net: saved image requires vnet_hdr=on");
2465982b78c5SDr. David Alan Gilbert         return -EINVAL;
2466982b78c5SDr. David Alan Gilbert     }
2467982b78c5SDr. David Alan Gilbert 
2468982b78c5SDr. David Alan Gilbert     return 0;
2469982b78c5SDr. David Alan Gilbert }
2470982b78c5SDr. David Alan Gilbert 
247144b1ff31SDr. David Alan Gilbert static int virtio_net_vnet_pre_save(void *opaque)
2472982b78c5SDr. David Alan Gilbert {
2473982b78c5SDr. David Alan Gilbert     struct VirtIONetMigTmp *tmp = opaque;
2474982b78c5SDr. David Alan Gilbert 
2475982b78c5SDr. David Alan Gilbert     tmp->has_vnet_hdr = tmp->parent->has_vnet_hdr;
247644b1ff31SDr. David Alan Gilbert 
247744b1ff31SDr. David Alan Gilbert     return 0;
2478982b78c5SDr. David Alan Gilbert }
2479982b78c5SDr. David Alan Gilbert 
2480982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_vnet = {
2481982b78c5SDr. David Alan Gilbert     .name      = "virtio-net-vnet",
2482982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_vnet_post_load,
2483982b78c5SDr. David Alan Gilbert     .pre_save  = virtio_net_vnet_pre_save,
2484982b78c5SDr. David Alan Gilbert     .fields    = (VMStateField[]) {
2485982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(has_vnet_hdr, struct VirtIONetMigTmp),
2486982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2487982b78c5SDr. David Alan Gilbert     },
2488982b78c5SDr. David Alan Gilbert };
2489982b78c5SDr. David Alan Gilbert 
2490982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_device = {
2491982b78c5SDr. David Alan Gilbert     .name = "virtio-net-device",
2492982b78c5SDr. David Alan Gilbert     .version_id = VIRTIO_NET_VM_VERSION,
2493982b78c5SDr. David Alan Gilbert     .minimum_version_id = VIRTIO_NET_VM_VERSION,
2494982b78c5SDr. David Alan Gilbert     .post_load = virtio_net_post_load_device,
2495982b78c5SDr. David Alan Gilbert     .fields = (VMStateField[]) {
2496982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN),
2497982b78c5SDr. David Alan Gilbert         VMSTATE_STRUCT_POINTER(vqs, VirtIONet,
2498982b78c5SDr. David Alan Gilbert                                vmstate_virtio_net_queue_tx_waiting,
2499982b78c5SDr. David Alan Gilbert                                VirtIONetQueue),
2500982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mergeable_rx_bufs, VirtIONet),
2501982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16(status, VirtIONet),
2502982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(promisc, VirtIONet),
2503982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(allmulti, VirtIONet),
2504982b78c5SDr. David Alan Gilbert         VMSTATE_UINT32(mac_table.in_use, VirtIONet),
2505982b78c5SDr. David Alan Gilbert 
2506982b78c5SDr. David Alan Gilbert         /* Guarded pair: If it fits we load it, else we throw it away
2507982b78c5SDr. David Alan Gilbert          * - can happen if source has a larger MAC table.; post-load
2508982b78c5SDr. David Alan Gilbert          *  sets flags in this case.
2509982b78c5SDr. David Alan Gilbert          */
2510982b78c5SDr. David Alan Gilbert         VMSTATE_VBUFFER_MULTIPLY(mac_table.macs, VirtIONet,
2511982b78c5SDr. David Alan Gilbert                                 0, mac_table_fits, mac_table.in_use,
2512982b78c5SDr. David Alan Gilbert                                  ETH_ALEN),
2513982b78c5SDr. David Alan Gilbert         VMSTATE_UNUSED_VARRAY_UINT32(VirtIONet, mac_table_doesnt_fit, 0,
2514982b78c5SDr. David Alan Gilbert                                      mac_table.in_use, ETH_ALEN),
2515982b78c5SDr. David Alan Gilbert 
2516982b78c5SDr. David Alan Gilbert         /* Note: This is an array of uint32's that's always been saved as a
2517982b78c5SDr. David Alan Gilbert          * buffer; hold onto your endiannesses; it's actually used as a bitmap
2518982b78c5SDr. David Alan Gilbert          * but based on the uint.
2519982b78c5SDr. David Alan Gilbert          */
2520982b78c5SDr. David Alan Gilbert         VMSTATE_BUFFER_POINTER_UNSAFE(vlans, VirtIONet, 0, MAX_VLAN >> 3),
2521982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2522982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_vnet),
2523982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.multi_overflow, VirtIONet),
2524982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(mac_table.uni_overflow, VirtIONet),
2525982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(alluni, VirtIONet),
2526982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nomulti, VirtIONet),
2527982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nouni, VirtIONet),
2528982b78c5SDr. David Alan Gilbert         VMSTATE_UINT8(nobcast, VirtIONet),
2529982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2530982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_has_ufo),
2531982b78c5SDr. David Alan Gilbert         VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0,
2532982b78c5SDr. David Alan Gilbert                             vmstate_info_uint16_equal, uint16_t),
2533982b78c5SDr. David Alan Gilbert         VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1),
2534982b78c5SDr. David Alan Gilbert         VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
2535982b78c5SDr. David Alan Gilbert                          vmstate_virtio_net_tx_waiting),
2536982b78c5SDr. David Alan Gilbert         VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet,
2537982b78c5SDr. David Alan Gilbert                             has_ctrl_guest_offloads),
2538982b78c5SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
2539982b78c5SDr. David Alan Gilbert    },
2540982b78c5SDr. David Alan Gilbert };
2541982b78c5SDr. David Alan Gilbert 
25426e790746SPaolo Bonzini static NetClientInfo net_virtio_info = {
2543f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
25446e790746SPaolo Bonzini     .size = sizeof(NICState),
25456e790746SPaolo Bonzini     .can_receive = virtio_net_can_receive,
25466e790746SPaolo Bonzini     .receive = virtio_net_receive,
25476e790746SPaolo Bonzini     .link_status_changed = virtio_net_set_link_status,
2548b1be4280SAmos Kong     .query_rx_filter = virtio_net_query_rxfilter,
25496e790746SPaolo Bonzini };
25506e790746SPaolo Bonzini 
25516e790746SPaolo Bonzini static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
25526e790746SPaolo Bonzini {
255317a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
25546e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
25556e790746SPaolo Bonzini     assert(n->vhost_started);
2556ed8b4afeSNikolay Nikolaev     return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx);
25576e790746SPaolo Bonzini }
25586e790746SPaolo Bonzini 
25596e790746SPaolo Bonzini static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
25606e790746SPaolo Bonzini                                            bool mask)
25616e790746SPaolo Bonzini {
256217a0ca55SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(vdev);
25636e790746SPaolo Bonzini     NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
25646e790746SPaolo Bonzini     assert(n->vhost_started);
2565ed8b4afeSNikolay Nikolaev     vhost_net_virtqueue_mask(get_vhost_net(nc->peer),
25666e790746SPaolo Bonzini                              vdev, idx, mask);
25676e790746SPaolo Bonzini }
25686e790746SPaolo Bonzini 
2569019a3edbSGerd Hoffmann static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features)
25706e790746SPaolo Bonzini {
25710cd09c3aSCornelia Huck     virtio_add_feature(&host_features, VIRTIO_NET_F_MAC);
2572a93e599dSMaxime Coquelin 
2573*ba550851SStefano Garzarella     n->config_size = virtio_feature_get_config_size(feature_sizes,
2574*ba550851SStefano Garzarella                                                     host_features);
257517ec5a86SKONRAD Frederic }
25766e790746SPaolo Bonzini 
25778a253ec2SKONRAD Frederic void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
25788a253ec2SKONRAD Frederic                                    const char *type)
25798a253ec2SKONRAD Frederic {
25808a253ec2SKONRAD Frederic     /*
25818a253ec2SKONRAD Frederic      * The name can be NULL, the netclient name will be type.x.
25828a253ec2SKONRAD Frederic      */
25838a253ec2SKONRAD Frederic     assert(type != NULL);
25848a253ec2SKONRAD Frederic 
25858a253ec2SKONRAD Frederic     g_free(n->netclient_name);
25868a253ec2SKONRAD Frederic     g_free(n->netclient_type);
25878a253ec2SKONRAD Frederic     n->netclient_name = g_strdup(name);
25888a253ec2SKONRAD Frederic     n->netclient_type = g_strdup(type);
25898a253ec2SKONRAD Frederic }
25908a253ec2SKONRAD Frederic 
2591e6f746b3SAndreas Färber static void virtio_net_device_realize(DeviceState *dev, Error **errp)
259217ec5a86SKONRAD Frederic {
2593e6f746b3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
2594284a32f0SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
2595284a32f0SAndreas Färber     NetClientState *nc;
25961773d9eeSKONRAD Frederic     int i;
259717ec5a86SKONRAD Frederic 
2598a93e599dSMaxime Coquelin     if (n->net_conf.mtu) {
2599127833eeSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_MTU);
2600a93e599dSMaxime Coquelin     }
2601a93e599dSMaxime Coquelin 
26029473939eSJason Baron     if (n->net_conf.duplex_str) {
26039473939eSJason Baron         if (strncmp(n->net_conf.duplex_str, "half", 5) == 0) {
26049473939eSJason Baron             n->net_conf.duplex = DUPLEX_HALF;
26059473939eSJason Baron         } else if (strncmp(n->net_conf.duplex_str, "full", 5) == 0) {
26069473939eSJason Baron             n->net_conf.duplex = DUPLEX_FULL;
26079473939eSJason Baron         } else {
26089473939eSJason Baron             error_setg(errp, "'duplex' must be 'half' or 'full'");
26099473939eSJason Baron         }
26109473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
26119473939eSJason Baron     } else {
26129473939eSJason Baron         n->net_conf.duplex = DUPLEX_UNKNOWN;
26139473939eSJason Baron     }
26149473939eSJason Baron 
26159473939eSJason Baron     if (n->net_conf.speed < SPEED_UNKNOWN) {
26169473939eSJason Baron         error_setg(errp, "'speed' must be between 0 and INT_MAX");
26179473939eSJason Baron     } else if (n->net_conf.speed >= 0) {
26189473939eSJason Baron         n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
26199473939eSJason Baron     }
26209473939eSJason Baron 
2621da3e8a23SShannon Zhao     virtio_net_set_config_size(n, n->host_features);
2622284a32f0SAndreas Färber     virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size);
262317ec5a86SKONRAD Frederic 
26241c0fbfa3SMichael S. Tsirkin     /*
26251c0fbfa3SMichael S. Tsirkin      * We set a lower limit on RX queue size to what it always was.
26261c0fbfa3SMichael S. Tsirkin      * Guests that want a smaller ring can always resize it without
26271c0fbfa3SMichael S. Tsirkin      * help from us (using virtio 1 and up).
26281c0fbfa3SMichael S. Tsirkin      */
26291c0fbfa3SMichael S. Tsirkin     if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE ||
26301c0fbfa3SMichael S. Tsirkin         n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE ||
26315f997fd1SMichal Privoznik         !is_power_of_2(n->net_conf.rx_queue_size)) {
26321c0fbfa3SMichael S. Tsirkin         error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), "
26331c0fbfa3SMichael S. Tsirkin                    "must be a power of 2 between %d and %d.",
26341c0fbfa3SMichael S. Tsirkin                    n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE,
26351c0fbfa3SMichael S. Tsirkin                    VIRTQUEUE_MAX_SIZE);
26361c0fbfa3SMichael S. Tsirkin         virtio_cleanup(vdev);
26371c0fbfa3SMichael S. Tsirkin         return;
26381c0fbfa3SMichael S. Tsirkin     }
26391c0fbfa3SMichael S. Tsirkin 
26409b02e161SWei Wang     if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE ||
26419b02e161SWei Wang         n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE ||
26429b02e161SWei Wang         !is_power_of_2(n->net_conf.tx_queue_size)) {
26439b02e161SWei Wang         error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), "
26449b02e161SWei Wang                    "must be a power of 2 between %d and %d",
26459b02e161SWei Wang                    n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE,
26469b02e161SWei Wang                    VIRTQUEUE_MAX_SIZE);
26479b02e161SWei Wang         virtio_cleanup(vdev);
26489b02e161SWei Wang         return;
26499b02e161SWei Wang     }
26509b02e161SWei Wang 
2651575a1c0eSJiri Pirko     n->max_queues = MAX(n->nic_conf.peers.queues, 1);
265287b3bd1cSJason Wang     if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) {
26537e0e736eSJason Wang         error_setg(errp, "Invalid number of queues (= %" PRIu32 "), "
2654631b22eaSStefan Weil                    "must be a positive integer less than %d.",
265587b3bd1cSJason Wang                    n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2);
26567e0e736eSJason Wang         virtio_cleanup(vdev);
26577e0e736eSJason Wang         return;
26587e0e736eSJason Wang     }
26596e790746SPaolo Bonzini     n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues);
26606e790746SPaolo Bonzini     n->curr_queues = 1;
26611773d9eeSKONRAD Frederic     n->tx_timeout = n->net_conf.txtimer;
26626e790746SPaolo Bonzini 
26631773d9eeSKONRAD Frederic     if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer")
26641773d9eeSKONRAD Frederic                        && strcmp(n->net_conf.tx, "bh")) {
26650765691eSMarkus Armbruster         warn_report("virtio-net: "
26666e790746SPaolo Bonzini                     "Unknown option tx=%s, valid options: \"timer\" \"bh\"",
26671773d9eeSKONRAD Frederic                     n->net_conf.tx);
26680765691eSMarkus Armbruster         error_printf("Defaulting to \"bh\"");
26696e790746SPaolo Bonzini     }
26706e790746SPaolo Bonzini 
26712eef278bSMichael S. Tsirkin     n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n),
26722eef278bSMichael S. Tsirkin                                     n->net_conf.tx_queue_size);
26739b02e161SWei Wang 
2674da51a335SJason Wang     for (i = 0; i < n->max_queues; i++) {
2675f9d6dbf0SWen Congyang         virtio_net_add_queue(n, i);
2676da51a335SJason Wang     }
2677da51a335SJason Wang 
267817a0ca55SKONRAD Frederic     n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
26791773d9eeSKONRAD Frederic     qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);
26801773d9eeSKONRAD Frederic     memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac));
26816e790746SPaolo Bonzini     n->status = VIRTIO_NET_S_LINK_UP;
2682f57fcf70SJason Wang     n->announce_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
2683f57fcf70SJason Wang                                      virtio_net_announce_timer, n);
26846e790746SPaolo Bonzini 
26858a253ec2SKONRAD Frederic     if (n->netclient_type) {
26868a253ec2SKONRAD Frederic         /*
26878a253ec2SKONRAD Frederic          * Happen when virtio_net_set_netclient_name has been called.
26888a253ec2SKONRAD Frederic          */
26898a253ec2SKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
26908a253ec2SKONRAD Frederic                               n->netclient_type, n->netclient_name, n);
26918a253ec2SKONRAD Frederic     } else {
26921773d9eeSKONRAD Frederic         n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
2693284a32f0SAndreas Färber                               object_get_typename(OBJECT(dev)), dev->id, n);
26948a253ec2SKONRAD Frederic     }
26958a253ec2SKONRAD Frederic 
26966e790746SPaolo Bonzini     peer_test_vnet_hdr(n);
26976e790746SPaolo Bonzini     if (peer_has_vnet_hdr(n)) {
26986e790746SPaolo Bonzini         for (i = 0; i < n->max_queues; i++) {
2699d6085e3aSStefan Hajnoczi             qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true);
27006e790746SPaolo Bonzini         }
27016e790746SPaolo Bonzini         n->host_hdr_len = sizeof(struct virtio_net_hdr);
27026e790746SPaolo Bonzini     } else {
27036e790746SPaolo Bonzini         n->host_hdr_len = 0;
27046e790746SPaolo Bonzini     }
27056e790746SPaolo Bonzini 
27061773d9eeSKONRAD Frederic     qemu_format_nic_info_str(qemu_get_queue(n->nic), n->nic_conf.macaddr.a);
27076e790746SPaolo Bonzini 
27086e790746SPaolo Bonzini     n->vqs[0].tx_waiting = 0;
27091773d9eeSKONRAD Frederic     n->tx_burst = n->net_conf.txburst;
2710bb9d17f8SCornelia Huck     virtio_net_set_mrg_rx_bufs(n, 0, 0);
27116e790746SPaolo Bonzini     n->promisc = 1; /* for compatibility */
27126e790746SPaolo Bonzini 
27136e790746SPaolo Bonzini     n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
27146e790746SPaolo Bonzini 
27156e790746SPaolo Bonzini     n->vlans = g_malloc0(MAX_VLAN >> 3);
27166e790746SPaolo Bonzini 
2717b1be4280SAmos Kong     nc = qemu_get_queue(n->nic);
2718b1be4280SAmos Kong     nc->rxfilter_notify_enabled = 1;
2719b1be4280SAmos Kong 
27202974e916SYuri Benditovich     QTAILQ_INIT(&n->rsc_chains);
2721284a32f0SAndreas Färber     n->qdev = dev;
272217ec5a86SKONRAD Frederic }
272317ec5a86SKONRAD Frederic 
2724306ec6c3SAndreas Färber static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
272517ec5a86SKONRAD Frederic {
2726306ec6c3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
2727306ec6c3SAndreas Färber     VirtIONet *n = VIRTIO_NET(dev);
2728f9d6dbf0SWen Congyang     int i, max_queues;
272917ec5a86SKONRAD Frederic 
273017ec5a86SKONRAD Frederic     /* This will stop vhost backend if appropriate. */
273117ec5a86SKONRAD Frederic     virtio_net_set_status(vdev, 0);
273217ec5a86SKONRAD Frederic 
27338a253ec2SKONRAD Frederic     g_free(n->netclient_name);
27348a253ec2SKONRAD Frederic     n->netclient_name = NULL;
27358a253ec2SKONRAD Frederic     g_free(n->netclient_type);
27368a253ec2SKONRAD Frederic     n->netclient_type = NULL;
27378a253ec2SKONRAD Frederic 
273817ec5a86SKONRAD Frederic     g_free(n->mac_table.macs);
273917ec5a86SKONRAD Frederic     g_free(n->vlans);
274017ec5a86SKONRAD Frederic 
2741f9d6dbf0SWen Congyang     max_queues = n->multiqueue ? n->max_queues : 1;
2742f9d6dbf0SWen Congyang     for (i = 0; i < max_queues; i++) {
2743f9d6dbf0SWen Congyang         virtio_net_del_queue(n, i);
274417ec5a86SKONRAD Frederic     }
274517ec5a86SKONRAD Frederic 
2746f57fcf70SJason Wang     timer_del(n->announce_timer);
2747f57fcf70SJason Wang     timer_free(n->announce_timer);
274817ec5a86SKONRAD Frederic     g_free(n->vqs);
274917ec5a86SKONRAD Frederic     qemu_del_nic(n->nic);
27502974e916SYuri Benditovich     virtio_net_rsc_cleanup(n);
27516a1a8cc7SKONRAD Frederic     virtio_cleanup(vdev);
275217ec5a86SKONRAD Frederic }
275317ec5a86SKONRAD Frederic 
275417ec5a86SKONRAD Frederic static void virtio_net_instance_init(Object *obj)
275517ec5a86SKONRAD Frederic {
275617ec5a86SKONRAD Frederic     VirtIONet *n = VIRTIO_NET(obj);
275717ec5a86SKONRAD Frederic 
275817ec5a86SKONRAD Frederic     /*
275917ec5a86SKONRAD Frederic      * The default config_size is sizeof(struct virtio_net_config).
276017ec5a86SKONRAD Frederic      * Can be overriden with virtio_net_set_config_size.
276117ec5a86SKONRAD Frederic      */
276217ec5a86SKONRAD Frederic     n->config_size = sizeof(struct virtio_net_config);
2763aa4197c3SGonglei     device_add_bootindex_property(obj, &n->nic_conf.bootindex,
2764aa4197c3SGonglei                                   "bootindex", "/ethernet-phy@0",
2765aa4197c3SGonglei                                   DEVICE(n), NULL);
276617ec5a86SKONRAD Frederic }
276717ec5a86SKONRAD Frederic 
276844b1ff31SDr. David Alan Gilbert static int virtio_net_pre_save(void *opaque)
27694d45dcfbSHalil Pasic {
27704d45dcfbSHalil Pasic     VirtIONet *n = opaque;
27714d45dcfbSHalil Pasic 
27724d45dcfbSHalil Pasic     /* At this point, backend must be stopped, otherwise
27734d45dcfbSHalil Pasic      * it might keep writing to memory. */
27744d45dcfbSHalil Pasic     assert(!n->vhost_started);
277544b1ff31SDr. David Alan Gilbert 
277644b1ff31SDr. David Alan Gilbert     return 0;
27774d45dcfbSHalil Pasic }
27784d45dcfbSHalil Pasic 
27794d45dcfbSHalil Pasic static const VMStateDescription vmstate_virtio_net = {
27804d45dcfbSHalil Pasic     .name = "virtio-net",
27814d45dcfbSHalil Pasic     .minimum_version_id = VIRTIO_NET_VM_VERSION,
27824d45dcfbSHalil Pasic     .version_id = VIRTIO_NET_VM_VERSION,
27834d45dcfbSHalil Pasic     .fields = (VMStateField[]) {
27844d45dcfbSHalil Pasic         VMSTATE_VIRTIO_DEVICE,
27854d45dcfbSHalil Pasic         VMSTATE_END_OF_LIST()
27864d45dcfbSHalil Pasic     },
27874d45dcfbSHalil Pasic     .pre_save = virtio_net_pre_save,
27884d45dcfbSHalil Pasic };
2789290c2428SDr. David Alan Gilbert 
279017ec5a86SKONRAD Frederic static Property virtio_net_properties[] = {
2791127833eeSJason Baron     DEFINE_PROP_BIT64("csum", VirtIONet, host_features,
2792127833eeSJason Baron                     VIRTIO_NET_F_CSUM, true),
2793127833eeSJason Baron     DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features,
279487108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_CSUM, true),
2795127833eeSJason Baron     DEFINE_PROP_BIT64("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true),
2796127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso4", VirtIONet, host_features,
279787108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO4, true),
2798127833eeSJason Baron     DEFINE_PROP_BIT64("guest_tso6", VirtIONet, host_features,
279987108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_TSO6, true),
2800127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ecn", VirtIONet, host_features,
280187108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ECN, true),
2802127833eeSJason Baron     DEFINE_PROP_BIT64("guest_ufo", VirtIONet, host_features,
280387108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_UFO, true),
2804127833eeSJason Baron     DEFINE_PROP_BIT64("guest_announce", VirtIONet, host_features,
280587108bb2SShannon Zhao                     VIRTIO_NET_F_GUEST_ANNOUNCE, true),
2806127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso4", VirtIONet, host_features,
280787108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO4, true),
2808127833eeSJason Baron     DEFINE_PROP_BIT64("host_tso6", VirtIONet, host_features,
280987108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_TSO6, true),
2810127833eeSJason Baron     DEFINE_PROP_BIT64("host_ecn", VirtIONet, host_features,
281187108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_ECN, true),
2812127833eeSJason Baron     DEFINE_PROP_BIT64("host_ufo", VirtIONet, host_features,
281387108bb2SShannon Zhao                     VIRTIO_NET_F_HOST_UFO, true),
2814127833eeSJason Baron     DEFINE_PROP_BIT64("mrg_rxbuf", VirtIONet, host_features,
281587108bb2SShannon Zhao                     VIRTIO_NET_F_MRG_RXBUF, true),
2816127833eeSJason Baron     DEFINE_PROP_BIT64("status", VirtIONet, host_features,
281787108bb2SShannon Zhao                     VIRTIO_NET_F_STATUS, true),
2818127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vq", VirtIONet, host_features,
281987108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VQ, true),
2820127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx", VirtIONet, host_features,
282187108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX, true),
2822127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_vlan", VirtIONet, host_features,
282387108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_VLAN, true),
2824127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_rx_extra", VirtIONet, host_features,
282587108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_RX_EXTRA, true),
2826127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_mac_addr", VirtIONet, host_features,
282787108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_MAC_ADDR, true),
2828127833eeSJason Baron     DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features,
282987108bb2SShannon Zhao                     VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
2830127833eeSJason Baron     DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
28312974e916SYuri Benditovich     DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features,
28322974e916SYuri Benditovich                     VIRTIO_NET_F_RSC_EXT, false),
28332974e916SYuri Benditovich     DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout,
28342974e916SYuri Benditovich                        VIRTIO_NET_RSC_DEFAULT_INTERVAL),
283517ec5a86SKONRAD Frederic     DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf),
283617ec5a86SKONRAD Frederic     DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer,
283717ec5a86SKONRAD Frederic                        TX_TIMER_INTERVAL),
283817ec5a86SKONRAD Frederic     DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST),
283917ec5a86SKONRAD Frederic     DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx),
28401c0fbfa3SMichael S. Tsirkin     DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size,
28411c0fbfa3SMichael S. Tsirkin                        VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE),
28429b02e161SWei Wang     DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size,
28439b02e161SWei Wang                        VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE),
2844a93e599dSMaxime Coquelin     DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0),
284575ebec11SMaxime Coquelin     DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend,
284675ebec11SMaxime Coquelin                      true),
28479473939eSJason Baron     DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN),
28489473939eSJason Baron     DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str),
284917ec5a86SKONRAD Frederic     DEFINE_PROP_END_OF_LIST(),
285017ec5a86SKONRAD Frederic };
285117ec5a86SKONRAD Frederic 
285217ec5a86SKONRAD Frederic static void virtio_net_class_init(ObjectClass *klass, void *data)
285317ec5a86SKONRAD Frederic {
285417ec5a86SKONRAD Frederic     DeviceClass *dc = DEVICE_CLASS(klass);
285517ec5a86SKONRAD Frederic     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
2856e6f746b3SAndreas Färber 
285717ec5a86SKONRAD Frederic     dc->props = virtio_net_properties;
2858290c2428SDr. David Alan Gilbert     dc->vmsd = &vmstate_virtio_net;
2859125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
2860e6f746b3SAndreas Färber     vdc->realize = virtio_net_device_realize;
2861306ec6c3SAndreas Färber     vdc->unrealize = virtio_net_device_unrealize;
286217ec5a86SKONRAD Frederic     vdc->get_config = virtio_net_get_config;
286317ec5a86SKONRAD Frederic     vdc->set_config = virtio_net_set_config;
286417ec5a86SKONRAD Frederic     vdc->get_features = virtio_net_get_features;
286517ec5a86SKONRAD Frederic     vdc->set_features = virtio_net_set_features;
286617ec5a86SKONRAD Frederic     vdc->bad_features = virtio_net_bad_features;
286717ec5a86SKONRAD Frederic     vdc->reset = virtio_net_reset;
286817ec5a86SKONRAD Frederic     vdc->set_status = virtio_net_set_status;
286917ec5a86SKONRAD Frederic     vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
287017ec5a86SKONRAD Frederic     vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
28712a083ffdSMichael S. Tsirkin     vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO);
2872982b78c5SDr. David Alan Gilbert     vdc->vmsd = &vmstate_virtio_net_device;
287317ec5a86SKONRAD Frederic }
287417ec5a86SKONRAD Frederic 
287517ec5a86SKONRAD Frederic static const TypeInfo virtio_net_info = {
287617ec5a86SKONRAD Frederic     .name = TYPE_VIRTIO_NET,
287717ec5a86SKONRAD Frederic     .parent = TYPE_VIRTIO_DEVICE,
287817ec5a86SKONRAD Frederic     .instance_size = sizeof(VirtIONet),
287917ec5a86SKONRAD Frederic     .instance_init = virtio_net_instance_init,
288017ec5a86SKONRAD Frederic     .class_init = virtio_net_class_init,
288117ec5a86SKONRAD Frederic };
288217ec5a86SKONRAD Frederic 
288317ec5a86SKONRAD Frederic static void virtio_register_types(void)
288417ec5a86SKONRAD Frederic {
288517ec5a86SKONRAD Frederic     type_register_static(&virtio_net_info);
288617ec5a86SKONRAD Frederic }
288717ec5a86SKONRAD Frederic 
288817ec5a86SKONRAD Frederic type_init(virtio_register_types)
2889