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" 160b8fa32fSMarkus Armbruster #include "qemu/module.h" 176e790746SPaolo Bonzini #include "hw/virtio/virtio.h" 186e790746SPaolo Bonzini #include "net/net.h" 196e790746SPaolo Bonzini #include "net/checksum.h" 206e790746SPaolo Bonzini #include "net/tap.h" 216e790746SPaolo Bonzini #include "qemu/error-report.h" 226e790746SPaolo Bonzini #include "qemu/timer.h" 236e790746SPaolo Bonzini #include "hw/virtio/virtio-net.h" 246e790746SPaolo Bonzini #include "net/vhost_net.h" 259d8c6a25SDr. David Alan Gilbert #include "net/announce.h" 2617ec5a86SKONRAD Frederic #include "hw/virtio/virtio-bus.h" 27e688df6bSMarkus Armbruster #include "qapi/error.h" 289af23989SMarkus Armbruster #include "qapi/qapi-events-net.h" 291399c60dSRusty Russell #include "hw/virtio/virtio-access.h" 30f8d806c9SJuan Quintela #include "migration/misc.h" 319473939eSJason Baron #include "standard-headers/linux/ethtool.h" 329d8c6a25SDr. David Alan Gilbert #include "trace.h" 336e790746SPaolo Bonzini 346e790746SPaolo Bonzini #define VIRTIO_NET_VM_VERSION 11 356e790746SPaolo Bonzini 366e790746SPaolo Bonzini #define MAC_TABLE_ENTRIES 64 376e790746SPaolo Bonzini #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ 386e790746SPaolo Bonzini 391c0fbfa3SMichael S. Tsirkin /* previously fixed value */ 401c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256 419b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256 429b02e161SWei Wang 431c0fbfa3SMichael S. Tsirkin /* for now, only allow larger queues; with virtio-1, guest can downsize */ 441c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 459b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 461c0fbfa3SMichael S. Tsirkin 472974e916SYuri Benditovich #define VIRTIO_NET_IP4_ADDR_SIZE 8 /* ipv4 saddr + daddr */ 482974e916SYuri Benditovich 492974e916SYuri Benditovich #define VIRTIO_NET_TCP_FLAG 0x3F 502974e916SYuri Benditovich #define VIRTIO_NET_TCP_HDR_LENGTH 0xF000 512974e916SYuri Benditovich 522974e916SYuri Benditovich /* IPv4 max payload, 16 bits in the header */ 532974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP4_PAYLOAD (65535 - sizeof(struct ip_header)) 542974e916SYuri Benditovich #define VIRTIO_NET_MAX_TCP_PAYLOAD 65535 552974e916SYuri Benditovich 562974e916SYuri Benditovich /* header length value in ip header without option */ 572974e916SYuri Benditovich #define VIRTIO_NET_IP4_HEADER_LENGTH 5 582974e916SYuri Benditovich 592974e916SYuri Benditovich #define VIRTIO_NET_IP6_ADDR_SIZE 32 /* ipv6 saddr + daddr */ 602974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP6_PAYLOAD VIRTIO_NET_MAX_TCP_PAYLOAD 612974e916SYuri Benditovich 622974e916SYuri Benditovich /* Purge coalesced packets timer interval, This value affects the performance 632974e916SYuri Benditovich a lot, and should be tuned carefully, '300000'(300us) is the recommended 642974e916SYuri Benditovich value to pass the WHQL test, '50000' can gain 2x netperf throughput with 652974e916SYuri Benditovich tso/gso/gro 'off'. */ 662974e916SYuri Benditovich #define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000 672974e916SYuri Benditovich 682974e916SYuri Benditovich /* temporary until standard header include it */ 692974e916SYuri Benditovich #if !defined(VIRTIO_NET_HDR_F_RSC_INFO) 702974e916SYuri Benditovich 712974e916SYuri Benditovich #define VIRTIO_NET_HDR_F_RSC_INFO 4 /* rsc_ext data in csum_ fields */ 72d47e5e31SYuri Benditovich #define VIRTIO_NET_F_RSC_EXT 61 732974e916SYuri Benditovich 742974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_packets( 752974e916SYuri Benditovich struct virtio_net_hdr *hdr) 762974e916SYuri Benditovich { 772974e916SYuri Benditovich return &hdr->csum_start; 782974e916SYuri Benditovich } 792974e916SYuri Benditovich 802974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_dupacks( 812974e916SYuri Benditovich struct virtio_net_hdr *hdr) 822974e916SYuri Benditovich { 832974e916SYuri Benditovich return &hdr->csum_offset; 842974e916SYuri Benditovich } 852974e916SYuri Benditovich 862974e916SYuri Benditovich #endif 872974e916SYuri Benditovich 886e790746SPaolo Bonzini static VirtIOFeature feature_sizes[] = { 89127833eeSJason Baron {.flags = 1ULL << VIRTIO_NET_F_MAC, 90ba550851SStefano Garzarella .end = virtio_endof(struct virtio_net_config, mac)}, 91127833eeSJason Baron {.flags = 1ULL << VIRTIO_NET_F_STATUS, 92ba550851SStefano Garzarella .end = virtio_endof(struct virtio_net_config, status)}, 93127833eeSJason Baron {.flags = 1ULL << VIRTIO_NET_F_MQ, 94ba550851SStefano Garzarella .end = virtio_endof(struct virtio_net_config, max_virtqueue_pairs)}, 95127833eeSJason Baron {.flags = 1ULL << VIRTIO_NET_F_MTU, 96ba550851SStefano Garzarella .end = virtio_endof(struct virtio_net_config, mtu)}, 979473939eSJason Baron {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX, 98ba550851SStefano Garzarella .end = virtio_endof(struct virtio_net_config, duplex)}, 996e790746SPaolo Bonzini {} 1006e790746SPaolo Bonzini }; 1016e790746SPaolo Bonzini 1026e790746SPaolo Bonzini static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc) 1036e790746SPaolo Bonzini { 1046e790746SPaolo Bonzini VirtIONet *n = qemu_get_nic_opaque(nc); 1056e790746SPaolo Bonzini 1066e790746SPaolo Bonzini return &n->vqs[nc->queue_index]; 1076e790746SPaolo Bonzini } 1086e790746SPaolo Bonzini 1096e790746SPaolo Bonzini static int vq2q(int queue_index) 1106e790746SPaolo Bonzini { 1116e790746SPaolo Bonzini return queue_index / 2; 1126e790746SPaolo Bonzini } 1136e790746SPaolo Bonzini 1146e790746SPaolo Bonzini /* TODO 1156e790746SPaolo Bonzini * - we could suppress RX interrupt if we were so inclined. 1166e790746SPaolo Bonzini */ 1176e790746SPaolo Bonzini 1186e790746SPaolo Bonzini static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) 1196e790746SPaolo Bonzini { 12017a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 1216e790746SPaolo Bonzini struct virtio_net_config netcfg; 1226e790746SPaolo Bonzini 1231399c60dSRusty Russell virtio_stw_p(vdev, &netcfg.status, n->status); 1241399c60dSRusty Russell virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues); 125a93e599dSMaxime Coquelin virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu); 1266e790746SPaolo Bonzini memcpy(netcfg.mac, n->mac, ETH_ALEN); 1279473939eSJason Baron virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed); 1289473939eSJason Baron netcfg.duplex = n->net_conf.duplex; 1296e790746SPaolo Bonzini memcpy(config, &netcfg, n->config_size); 1306e790746SPaolo Bonzini } 1316e790746SPaolo Bonzini 1326e790746SPaolo Bonzini static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) 1336e790746SPaolo Bonzini { 13417a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 1356e790746SPaolo Bonzini struct virtio_net_config netcfg = {}; 1366e790746SPaolo Bonzini 1376e790746SPaolo Bonzini memcpy(&netcfg, config, n->config_size); 1386e790746SPaolo Bonzini 13995129d6fSCornelia Huck if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) && 14095129d6fSCornelia Huck !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) && 1416e790746SPaolo Bonzini memcmp(netcfg.mac, n->mac, ETH_ALEN)) { 1426e790746SPaolo Bonzini memcpy(n->mac, netcfg.mac, ETH_ALEN); 1436e790746SPaolo Bonzini qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); 1446e790746SPaolo Bonzini } 1456e790746SPaolo Bonzini } 1466e790746SPaolo Bonzini 1476e790746SPaolo Bonzini static bool virtio_net_started(VirtIONet *n, uint8_t status) 1486e790746SPaolo Bonzini { 14917a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 1506e790746SPaolo Bonzini return (status & VIRTIO_CONFIG_S_DRIVER_OK) && 15117a0ca55SKONRAD Frederic (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running; 1526e790746SPaolo Bonzini } 1536e790746SPaolo Bonzini 154b2c929f0SDr. David Alan Gilbert static void virtio_net_announce_notify(VirtIONet *net) 155b2c929f0SDr. David Alan Gilbert { 156b2c929f0SDr. David Alan Gilbert VirtIODevice *vdev = VIRTIO_DEVICE(net); 157b2c929f0SDr. David Alan Gilbert trace_virtio_net_announce_notify(); 158b2c929f0SDr. David Alan Gilbert 159b2c929f0SDr. David Alan Gilbert net->status |= VIRTIO_NET_S_ANNOUNCE; 160b2c929f0SDr. David Alan Gilbert virtio_notify_config(vdev); 161b2c929f0SDr. David Alan Gilbert } 162b2c929f0SDr. David Alan Gilbert 163f57fcf70SJason Wang static void virtio_net_announce_timer(void *opaque) 164f57fcf70SJason Wang { 165f57fcf70SJason Wang VirtIONet *n = opaque; 1669d8c6a25SDr. David Alan Gilbert trace_virtio_net_announce_timer(n->announce_timer.round); 167f57fcf70SJason Wang 1689d8c6a25SDr. David Alan Gilbert n->announce_timer.round--; 169b2c929f0SDr. David Alan Gilbert virtio_net_announce_notify(n); 170b2c929f0SDr. David Alan Gilbert } 171b2c929f0SDr. David Alan Gilbert 172b2c929f0SDr. David Alan Gilbert static void virtio_net_announce(NetClientState *nc) 173b2c929f0SDr. David Alan Gilbert { 174b2c929f0SDr. David Alan Gilbert VirtIONet *n = qemu_get_nic_opaque(nc); 175b2c929f0SDr. David Alan Gilbert VirtIODevice *vdev = VIRTIO_DEVICE(n); 176b2c929f0SDr. David Alan Gilbert 177b2c929f0SDr. David Alan Gilbert /* 178b2c929f0SDr. David Alan Gilbert * Make sure the virtio migration announcement timer isn't running 179b2c929f0SDr. David Alan Gilbert * If it is, let it trigger announcement so that we do not cause 180b2c929f0SDr. David Alan Gilbert * confusion. 181b2c929f0SDr. David Alan Gilbert */ 182b2c929f0SDr. David Alan Gilbert if (n->announce_timer.round) { 183b2c929f0SDr. David Alan Gilbert return; 184b2c929f0SDr. David Alan Gilbert } 185b2c929f0SDr. David Alan Gilbert 186b2c929f0SDr. David Alan Gilbert if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && 187b2c929f0SDr. David Alan Gilbert virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { 188b2c929f0SDr. David Alan Gilbert virtio_net_announce_notify(n); 189b2c929f0SDr. David Alan Gilbert } 190f57fcf70SJason Wang } 191f57fcf70SJason Wang 1926e790746SPaolo Bonzini static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) 1936e790746SPaolo Bonzini { 19417a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 1956e790746SPaolo Bonzini NetClientState *nc = qemu_get_queue(n->nic); 1966e790746SPaolo Bonzini int queues = n->multiqueue ? n->max_queues : 1; 1976e790746SPaolo Bonzini 198ed8b4afeSNikolay Nikolaev if (!get_vhost_net(nc->peer)) { 1996e790746SPaolo Bonzini return; 2006e790746SPaolo Bonzini } 2016e790746SPaolo Bonzini 2028c1ac475SRadim Krčmář if ((virtio_net_started(n, status) && !nc->peer->link_down) == 2038c1ac475SRadim Krčmář !!n->vhost_started) { 2046e790746SPaolo Bonzini return; 2056e790746SPaolo Bonzini } 2066e790746SPaolo Bonzini if (!n->vhost_started) { 207086abc1cSMichael S. Tsirkin int r, i; 208086abc1cSMichael S. Tsirkin 2091bfa316cSGreg Kurz if (n->needs_vnet_hdr_swap) { 2101bfa316cSGreg Kurz error_report("backend does not support %s vnet headers; " 2111bfa316cSGreg Kurz "falling back on userspace virtio", 2121bfa316cSGreg Kurz virtio_is_big_endian(vdev) ? "BE" : "LE"); 2131bfa316cSGreg Kurz return; 2141bfa316cSGreg Kurz } 2151bfa316cSGreg Kurz 216086abc1cSMichael S. Tsirkin /* Any packets outstanding? Purge them to avoid touching rings 217086abc1cSMichael S. Tsirkin * when vhost is running. 218086abc1cSMichael S. Tsirkin */ 219086abc1cSMichael S. Tsirkin for (i = 0; i < queues; i++) { 220086abc1cSMichael S. Tsirkin NetClientState *qnc = qemu_get_subqueue(n->nic, i); 221086abc1cSMichael S. Tsirkin 222086abc1cSMichael S. Tsirkin /* Purge both directions: TX and RX. */ 223086abc1cSMichael S. Tsirkin qemu_net_queue_purge(qnc->peer->incoming_queue, qnc); 224086abc1cSMichael S. Tsirkin qemu_net_queue_purge(qnc->incoming_queue, qnc->peer); 225086abc1cSMichael S. Tsirkin } 226086abc1cSMichael S. Tsirkin 227a93e599dSMaxime Coquelin if (virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MTU)) { 228a93e599dSMaxime Coquelin r = vhost_net_set_mtu(get_vhost_net(nc->peer), n->net_conf.mtu); 229a93e599dSMaxime Coquelin if (r < 0) { 230a93e599dSMaxime Coquelin error_report("%uBytes MTU not supported by the backend", 231a93e599dSMaxime Coquelin n->net_conf.mtu); 232a93e599dSMaxime Coquelin 233a93e599dSMaxime Coquelin return; 234a93e599dSMaxime Coquelin } 235a93e599dSMaxime Coquelin } 236a93e599dSMaxime Coquelin 2376e790746SPaolo Bonzini n->vhost_started = 1; 23817a0ca55SKONRAD Frederic r = vhost_net_start(vdev, n->nic->ncs, queues); 2396e790746SPaolo Bonzini if (r < 0) { 2406e790746SPaolo Bonzini error_report("unable to start vhost net: %d: " 2416e790746SPaolo Bonzini "falling back on userspace virtio", -r); 2426e790746SPaolo Bonzini n->vhost_started = 0; 2436e790746SPaolo Bonzini } 2446e790746SPaolo Bonzini } else { 24517a0ca55SKONRAD Frederic vhost_net_stop(vdev, n->nic->ncs, queues); 2466e790746SPaolo Bonzini n->vhost_started = 0; 2476e790746SPaolo Bonzini } 2486e790746SPaolo Bonzini } 2496e790746SPaolo Bonzini 2501bfa316cSGreg Kurz static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev, 2511bfa316cSGreg Kurz NetClientState *peer, 2521bfa316cSGreg Kurz bool enable) 2531bfa316cSGreg Kurz { 2541bfa316cSGreg Kurz if (virtio_is_big_endian(vdev)) { 2551bfa316cSGreg Kurz return qemu_set_vnet_be(peer, enable); 2561bfa316cSGreg Kurz } else { 2571bfa316cSGreg Kurz return qemu_set_vnet_le(peer, enable); 2581bfa316cSGreg Kurz } 2591bfa316cSGreg Kurz } 2601bfa316cSGreg Kurz 2611bfa316cSGreg Kurz static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs, 2621bfa316cSGreg Kurz int queues, bool enable) 2631bfa316cSGreg Kurz { 2641bfa316cSGreg Kurz int i; 2651bfa316cSGreg Kurz 2661bfa316cSGreg Kurz for (i = 0; i < queues; i++) { 2671bfa316cSGreg Kurz if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 && 2681bfa316cSGreg Kurz enable) { 2691bfa316cSGreg Kurz while (--i >= 0) { 2701bfa316cSGreg Kurz virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false); 2711bfa316cSGreg Kurz } 2721bfa316cSGreg Kurz 2731bfa316cSGreg Kurz return true; 2741bfa316cSGreg Kurz } 2751bfa316cSGreg Kurz } 2761bfa316cSGreg Kurz 2771bfa316cSGreg Kurz return false; 2781bfa316cSGreg Kurz } 2791bfa316cSGreg Kurz 2801bfa316cSGreg Kurz static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) 2811bfa316cSGreg Kurz { 2821bfa316cSGreg Kurz VirtIODevice *vdev = VIRTIO_DEVICE(n); 2831bfa316cSGreg Kurz int queues = n->multiqueue ? n->max_queues : 1; 2841bfa316cSGreg Kurz 2851bfa316cSGreg Kurz if (virtio_net_started(n, status)) { 2861bfa316cSGreg Kurz /* Before using the device, we tell the network backend about the 2871bfa316cSGreg Kurz * endianness to use when parsing vnet headers. If the backend 2881bfa316cSGreg Kurz * can't do it, we fallback onto fixing the headers in the core 2891bfa316cSGreg Kurz * virtio-net code. 2901bfa316cSGreg Kurz */ 2911bfa316cSGreg Kurz n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs, 2921bfa316cSGreg Kurz queues, true); 2931bfa316cSGreg Kurz } else if (virtio_net_started(n, vdev->status)) { 2941bfa316cSGreg Kurz /* After using the device, we need to reset the network backend to 2951bfa316cSGreg Kurz * the default (guest native endianness), otherwise the guest may 2961bfa316cSGreg Kurz * lose network connectivity if it is rebooted into a different 2971bfa316cSGreg Kurz * endianness. 2981bfa316cSGreg Kurz */ 2991bfa316cSGreg Kurz virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false); 3001bfa316cSGreg Kurz } 3011bfa316cSGreg Kurz } 3021bfa316cSGreg Kurz 303283e2c2aSYuri Benditovich static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq) 304283e2c2aSYuri Benditovich { 305283e2c2aSYuri Benditovich unsigned int dropped = virtqueue_drop_all(vq); 306283e2c2aSYuri Benditovich if (dropped) { 307283e2c2aSYuri Benditovich virtio_notify(vdev, vq); 308283e2c2aSYuri Benditovich } 309283e2c2aSYuri Benditovich } 310283e2c2aSYuri Benditovich 3116e790746SPaolo Bonzini static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) 3126e790746SPaolo Bonzini { 31317a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 3146e790746SPaolo Bonzini VirtIONetQueue *q; 3156e790746SPaolo Bonzini int i; 3166e790746SPaolo Bonzini uint8_t queue_status; 3176e790746SPaolo Bonzini 3181bfa316cSGreg Kurz virtio_net_vnet_endian_status(n, status); 3196e790746SPaolo Bonzini virtio_net_vhost_status(n, status); 3206e790746SPaolo Bonzini 3216e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 32238705bb5SFam Zheng NetClientState *ncs = qemu_get_subqueue(n->nic, i); 32338705bb5SFam Zheng bool queue_started; 3246e790746SPaolo Bonzini q = &n->vqs[i]; 3256e790746SPaolo Bonzini 3266e790746SPaolo Bonzini if ((!n->multiqueue && i != 0) || i >= n->curr_queues) { 3276e790746SPaolo Bonzini queue_status = 0; 3286e790746SPaolo Bonzini } else { 3296e790746SPaolo Bonzini queue_status = status; 3306e790746SPaolo Bonzini } 33138705bb5SFam Zheng queue_started = 33238705bb5SFam Zheng virtio_net_started(n, queue_status) && !n->vhost_started; 33338705bb5SFam Zheng 33438705bb5SFam Zheng if (queue_started) { 33538705bb5SFam Zheng qemu_flush_queued_packets(ncs); 33638705bb5SFam Zheng } 3376e790746SPaolo Bonzini 3386e790746SPaolo Bonzini if (!q->tx_waiting) { 3396e790746SPaolo Bonzini continue; 3406e790746SPaolo Bonzini } 3416e790746SPaolo Bonzini 34238705bb5SFam Zheng if (queue_started) { 3436e790746SPaolo Bonzini if (q->tx_timer) { 344bc72ad67SAlex Bligh timer_mod(q->tx_timer, 345bc72ad67SAlex Bligh qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); 3466e790746SPaolo Bonzini } else { 3476e790746SPaolo Bonzini qemu_bh_schedule(q->tx_bh); 3486e790746SPaolo Bonzini } 3496e790746SPaolo Bonzini } else { 3506e790746SPaolo Bonzini if (q->tx_timer) { 351bc72ad67SAlex Bligh timer_del(q->tx_timer); 3526e790746SPaolo Bonzini } else { 3536e790746SPaolo Bonzini qemu_bh_cancel(q->tx_bh); 3546e790746SPaolo Bonzini } 355283e2c2aSYuri Benditovich if ((n->status & VIRTIO_NET_S_LINK_UP) == 0 && 35670e53e6eSJason Wang (queue_status & VIRTIO_CONFIG_S_DRIVER_OK) && 35770e53e6eSJason Wang vdev->vm_running) { 358283e2c2aSYuri Benditovich /* if tx is waiting we are likely have some packets in tx queue 359283e2c2aSYuri Benditovich * and disabled notification */ 360283e2c2aSYuri Benditovich q->tx_waiting = 0; 361283e2c2aSYuri Benditovich virtio_queue_set_notification(q->tx_vq, 1); 362283e2c2aSYuri Benditovich virtio_net_drop_tx_queue_data(vdev, q->tx_vq); 363283e2c2aSYuri Benditovich } 3646e790746SPaolo Bonzini } 3656e790746SPaolo Bonzini } 3666e790746SPaolo Bonzini } 3676e790746SPaolo Bonzini 3686e790746SPaolo Bonzini static void virtio_net_set_link_status(NetClientState *nc) 3696e790746SPaolo Bonzini { 3706e790746SPaolo Bonzini VirtIONet *n = qemu_get_nic_opaque(nc); 37117a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 3726e790746SPaolo Bonzini uint16_t old_status = n->status; 3736e790746SPaolo Bonzini 3746e790746SPaolo Bonzini if (nc->link_down) 3756e790746SPaolo Bonzini n->status &= ~VIRTIO_NET_S_LINK_UP; 3766e790746SPaolo Bonzini else 3776e790746SPaolo Bonzini n->status |= VIRTIO_NET_S_LINK_UP; 3786e790746SPaolo Bonzini 3796e790746SPaolo Bonzini if (n->status != old_status) 38017a0ca55SKONRAD Frederic virtio_notify_config(vdev); 3816e790746SPaolo Bonzini 38217a0ca55SKONRAD Frederic virtio_net_set_status(vdev, vdev->status); 3836e790746SPaolo Bonzini } 3846e790746SPaolo Bonzini 385b1be4280SAmos Kong static void rxfilter_notify(NetClientState *nc) 386b1be4280SAmos Kong { 387b1be4280SAmos Kong VirtIONet *n = qemu_get_nic_opaque(nc); 388b1be4280SAmos Kong 389b1be4280SAmos Kong if (nc->rxfilter_notify_enabled) { 39096e35046SAmos Kong gchar *path = object_get_canonical_path(OBJECT(n->qdev)); 39106150279SWenchao Xia qapi_event_send_nic_rx_filter_changed(!!n->netclient_name, 3923ab72385SPeter Xu n->netclient_name, path); 39396e35046SAmos Kong g_free(path); 394b1be4280SAmos Kong 395b1be4280SAmos Kong /* disable event notification to avoid events flooding */ 396b1be4280SAmos Kong nc->rxfilter_notify_enabled = 0; 397b1be4280SAmos Kong } 398b1be4280SAmos Kong } 399b1be4280SAmos Kong 400f7bc8ef8SAmos Kong static intList *get_vlan_table(VirtIONet *n) 401f7bc8ef8SAmos Kong { 402f7bc8ef8SAmos Kong intList *list, *entry; 403f7bc8ef8SAmos Kong int i, j; 404f7bc8ef8SAmos Kong 405f7bc8ef8SAmos Kong list = NULL; 406f7bc8ef8SAmos Kong for (i = 0; i < MAX_VLAN >> 5; i++) { 407f7bc8ef8SAmos Kong for (j = 0; n->vlans[i] && j <= 0x1f; j++) { 408f7bc8ef8SAmos Kong if (n->vlans[i] & (1U << j)) { 409f7bc8ef8SAmos Kong entry = g_malloc0(sizeof(*entry)); 410f7bc8ef8SAmos Kong entry->value = (i << 5) + j; 411f7bc8ef8SAmos Kong entry->next = list; 412f7bc8ef8SAmos Kong list = entry; 413f7bc8ef8SAmos Kong } 414f7bc8ef8SAmos Kong } 415f7bc8ef8SAmos Kong } 416f7bc8ef8SAmos Kong 417f7bc8ef8SAmos Kong return list; 418f7bc8ef8SAmos Kong } 419f7bc8ef8SAmos Kong 420b1be4280SAmos Kong static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) 421b1be4280SAmos Kong { 422b1be4280SAmos Kong VirtIONet *n = qemu_get_nic_opaque(nc); 423f7bc8ef8SAmos Kong VirtIODevice *vdev = VIRTIO_DEVICE(n); 424b1be4280SAmos Kong RxFilterInfo *info; 425b1be4280SAmos Kong strList *str_list, *entry; 426f7bc8ef8SAmos Kong int i; 427b1be4280SAmos Kong 428b1be4280SAmos Kong info = g_malloc0(sizeof(*info)); 429b1be4280SAmos Kong info->name = g_strdup(nc->name); 430b1be4280SAmos Kong info->promiscuous = n->promisc; 431b1be4280SAmos Kong 432b1be4280SAmos Kong if (n->nouni) { 433b1be4280SAmos Kong info->unicast = RX_STATE_NONE; 434b1be4280SAmos Kong } else if (n->alluni) { 435b1be4280SAmos Kong info->unicast = RX_STATE_ALL; 436b1be4280SAmos Kong } else { 437b1be4280SAmos Kong info->unicast = RX_STATE_NORMAL; 438b1be4280SAmos Kong } 439b1be4280SAmos Kong 440b1be4280SAmos Kong if (n->nomulti) { 441b1be4280SAmos Kong info->multicast = RX_STATE_NONE; 442b1be4280SAmos Kong } else if (n->allmulti) { 443b1be4280SAmos Kong info->multicast = RX_STATE_ALL; 444b1be4280SAmos Kong } else { 445b1be4280SAmos Kong info->multicast = RX_STATE_NORMAL; 446b1be4280SAmos Kong } 447b1be4280SAmos Kong 448b1be4280SAmos Kong info->broadcast_allowed = n->nobcast; 449b1be4280SAmos Kong info->multicast_overflow = n->mac_table.multi_overflow; 450b1be4280SAmos Kong info->unicast_overflow = n->mac_table.uni_overflow; 451b1be4280SAmos Kong 452b0575ba4SScott Feldman info->main_mac = qemu_mac_strdup_printf(n->mac); 453b1be4280SAmos Kong 454b1be4280SAmos Kong str_list = NULL; 455b1be4280SAmos Kong for (i = 0; i < n->mac_table.first_multi; i++) { 456b1be4280SAmos Kong entry = g_malloc0(sizeof(*entry)); 457b0575ba4SScott Feldman entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN); 458b1be4280SAmos Kong entry->next = str_list; 459b1be4280SAmos Kong str_list = entry; 460b1be4280SAmos Kong } 461b1be4280SAmos Kong info->unicast_table = str_list; 462b1be4280SAmos Kong 463b1be4280SAmos Kong str_list = NULL; 464b1be4280SAmos Kong for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) { 465b1be4280SAmos Kong entry = g_malloc0(sizeof(*entry)); 466b0575ba4SScott Feldman entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN); 467b1be4280SAmos Kong entry->next = str_list; 468b1be4280SAmos Kong str_list = entry; 469b1be4280SAmos Kong } 470b1be4280SAmos Kong info->multicast_table = str_list; 471f7bc8ef8SAmos Kong info->vlan_table = get_vlan_table(n); 472b1be4280SAmos Kong 47395129d6fSCornelia Huck if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) { 474f7bc8ef8SAmos Kong info->vlan = RX_STATE_ALL; 475f7bc8ef8SAmos Kong } else if (!info->vlan_table) { 476f7bc8ef8SAmos Kong info->vlan = RX_STATE_NONE; 477f7bc8ef8SAmos Kong } else { 478f7bc8ef8SAmos Kong info->vlan = RX_STATE_NORMAL; 479b1be4280SAmos Kong } 480b1be4280SAmos Kong 481b1be4280SAmos Kong /* enable event notification after query */ 482b1be4280SAmos Kong nc->rxfilter_notify_enabled = 1; 483b1be4280SAmos Kong 484b1be4280SAmos Kong return info; 485b1be4280SAmos Kong } 486b1be4280SAmos Kong 4876e790746SPaolo Bonzini static void virtio_net_reset(VirtIODevice *vdev) 4886e790746SPaolo Bonzini { 48917a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 49094b52958SGreg Kurz int i; 4916e790746SPaolo Bonzini 4926e790746SPaolo Bonzini /* Reset back to compatibility mode */ 4936e790746SPaolo Bonzini n->promisc = 1; 4946e790746SPaolo Bonzini n->allmulti = 0; 4956e790746SPaolo Bonzini n->alluni = 0; 4966e790746SPaolo Bonzini n->nomulti = 0; 4976e790746SPaolo Bonzini n->nouni = 0; 4986e790746SPaolo Bonzini n->nobcast = 0; 4996e790746SPaolo Bonzini /* multiqueue is disabled by default */ 5006e790746SPaolo Bonzini n->curr_queues = 1; 5019d8c6a25SDr. David Alan Gilbert timer_del(n->announce_timer.tm); 5029d8c6a25SDr. David Alan Gilbert n->announce_timer.round = 0; 503f57fcf70SJason Wang n->status &= ~VIRTIO_NET_S_ANNOUNCE; 5046e790746SPaolo Bonzini 5056e790746SPaolo Bonzini /* Flush any MAC and VLAN filter table state */ 5066e790746SPaolo Bonzini n->mac_table.in_use = 0; 5076e790746SPaolo Bonzini n->mac_table.first_multi = 0; 5086e790746SPaolo Bonzini n->mac_table.multi_overflow = 0; 5096e790746SPaolo Bonzini n->mac_table.uni_overflow = 0; 5106e790746SPaolo Bonzini memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); 5116e790746SPaolo Bonzini memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); 512702d66a8SMichael S. Tsirkin qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); 5136e790746SPaolo Bonzini memset(n->vlans, 0, MAX_VLAN >> 3); 51494b52958SGreg Kurz 51594b52958SGreg Kurz /* Flush any async TX */ 51694b52958SGreg Kurz for (i = 0; i < n->max_queues; i++) { 51794b52958SGreg Kurz NetClientState *nc = qemu_get_subqueue(n->nic, i); 51894b52958SGreg Kurz 51994b52958SGreg Kurz if (nc->peer) { 52094b52958SGreg Kurz qemu_flush_or_purge_queued_packets(nc->peer, true); 52194b52958SGreg Kurz assert(!virtio_net_get_subqueue(nc)->async_tx.elem); 52294b52958SGreg Kurz } 52394b52958SGreg Kurz } 5246e790746SPaolo Bonzini } 5256e790746SPaolo Bonzini 5266e790746SPaolo Bonzini static void peer_test_vnet_hdr(VirtIONet *n) 5276e790746SPaolo Bonzini { 5286e790746SPaolo Bonzini NetClientState *nc = qemu_get_queue(n->nic); 5296e790746SPaolo Bonzini if (!nc->peer) { 5306e790746SPaolo Bonzini return; 5316e790746SPaolo Bonzini } 5326e790746SPaolo Bonzini 533d6085e3aSStefan Hajnoczi n->has_vnet_hdr = qemu_has_vnet_hdr(nc->peer); 5346e790746SPaolo Bonzini } 5356e790746SPaolo Bonzini 5366e790746SPaolo Bonzini static int peer_has_vnet_hdr(VirtIONet *n) 5376e790746SPaolo Bonzini { 5386e790746SPaolo Bonzini return n->has_vnet_hdr; 5396e790746SPaolo Bonzini } 5406e790746SPaolo Bonzini 5416e790746SPaolo Bonzini static int peer_has_ufo(VirtIONet *n) 5426e790746SPaolo Bonzini { 5436e790746SPaolo Bonzini if (!peer_has_vnet_hdr(n)) 5446e790746SPaolo Bonzini return 0; 5456e790746SPaolo Bonzini 546d6085e3aSStefan Hajnoczi n->has_ufo = qemu_has_ufo(qemu_get_queue(n->nic)->peer); 5476e790746SPaolo Bonzini 5486e790746SPaolo Bonzini return n->has_ufo; 5496e790746SPaolo Bonzini } 5506e790746SPaolo Bonzini 551bb9d17f8SCornelia Huck static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, 552bb9d17f8SCornelia Huck int version_1) 5536e790746SPaolo Bonzini { 5546e790746SPaolo Bonzini int i; 5556e790746SPaolo Bonzini NetClientState *nc; 5566e790746SPaolo Bonzini 5576e790746SPaolo Bonzini n->mergeable_rx_bufs = mergeable_rx_bufs; 5586e790746SPaolo Bonzini 559bb9d17f8SCornelia Huck if (version_1) { 560bb9d17f8SCornelia Huck n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf); 561bb9d17f8SCornelia Huck } else { 5626e790746SPaolo Bonzini n->guest_hdr_len = n->mergeable_rx_bufs ? 563bb9d17f8SCornelia Huck sizeof(struct virtio_net_hdr_mrg_rxbuf) : 564bb9d17f8SCornelia Huck sizeof(struct virtio_net_hdr); 565bb9d17f8SCornelia Huck } 5666e790746SPaolo Bonzini 5676e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 5686e790746SPaolo Bonzini nc = qemu_get_subqueue(n->nic, i); 5696e790746SPaolo Bonzini 5706e790746SPaolo Bonzini if (peer_has_vnet_hdr(n) && 571d6085e3aSStefan Hajnoczi qemu_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) { 572d6085e3aSStefan Hajnoczi qemu_set_vnet_hdr_len(nc->peer, n->guest_hdr_len); 5736e790746SPaolo Bonzini n->host_hdr_len = n->guest_hdr_len; 5746e790746SPaolo Bonzini } 5756e790746SPaolo Bonzini } 5766e790746SPaolo Bonzini } 5776e790746SPaolo Bonzini 5782eef278bSMichael S. Tsirkin static int virtio_net_max_tx_queue_size(VirtIONet *n) 5792eef278bSMichael S. Tsirkin { 5802eef278bSMichael S. Tsirkin NetClientState *peer = n->nic_conf.peers.ncs[0]; 5812eef278bSMichael S. Tsirkin 5822eef278bSMichael S. Tsirkin /* 5832eef278bSMichael S. Tsirkin * Backends other than vhost-user don't support max queue size. 5842eef278bSMichael S. Tsirkin */ 5852eef278bSMichael S. Tsirkin if (!peer) { 5862eef278bSMichael S. Tsirkin return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; 5872eef278bSMichael S. Tsirkin } 5882eef278bSMichael S. Tsirkin 5892eef278bSMichael S. Tsirkin if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) { 5902eef278bSMichael S. Tsirkin return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; 5912eef278bSMichael S. Tsirkin } 5922eef278bSMichael S. Tsirkin 5932eef278bSMichael S. Tsirkin return VIRTQUEUE_MAX_SIZE; 5942eef278bSMichael S. Tsirkin } 5952eef278bSMichael S. Tsirkin 5966e790746SPaolo Bonzini static int peer_attach(VirtIONet *n, int index) 5976e790746SPaolo Bonzini { 5986e790746SPaolo Bonzini NetClientState *nc = qemu_get_subqueue(n->nic, index); 5996e790746SPaolo Bonzini 6006e790746SPaolo Bonzini if (!nc->peer) { 6016e790746SPaolo Bonzini return 0; 6026e790746SPaolo Bonzini } 6036e790746SPaolo Bonzini 604f394b2e2SEric Blake if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { 6057263a0adSChangchun Ouyang vhost_set_vring_enable(nc->peer, 1); 6067263a0adSChangchun Ouyang } 6077263a0adSChangchun Ouyang 608f394b2e2SEric Blake if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { 6096e790746SPaolo Bonzini return 0; 6106e790746SPaolo Bonzini } 6116e790746SPaolo Bonzini 6121074b879SJason Wang if (n->max_queues == 1) { 6131074b879SJason Wang return 0; 6141074b879SJason Wang } 6151074b879SJason Wang 6166e790746SPaolo Bonzini return tap_enable(nc->peer); 6176e790746SPaolo Bonzini } 6186e790746SPaolo Bonzini 6196e790746SPaolo Bonzini static int peer_detach(VirtIONet *n, int index) 6206e790746SPaolo Bonzini { 6216e790746SPaolo Bonzini NetClientState *nc = qemu_get_subqueue(n->nic, index); 6226e790746SPaolo Bonzini 6236e790746SPaolo Bonzini if (!nc->peer) { 6246e790746SPaolo Bonzini return 0; 6256e790746SPaolo Bonzini } 6266e790746SPaolo Bonzini 627f394b2e2SEric Blake if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { 6287263a0adSChangchun Ouyang vhost_set_vring_enable(nc->peer, 0); 6297263a0adSChangchun Ouyang } 6307263a0adSChangchun Ouyang 631f394b2e2SEric Blake if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { 6326e790746SPaolo Bonzini return 0; 6336e790746SPaolo Bonzini } 6346e790746SPaolo Bonzini 6356e790746SPaolo Bonzini return tap_disable(nc->peer); 6366e790746SPaolo Bonzini } 6376e790746SPaolo Bonzini 6386e790746SPaolo Bonzini static void virtio_net_set_queues(VirtIONet *n) 6396e790746SPaolo Bonzini { 6406e790746SPaolo Bonzini int i; 641ddfa83eaSJoel Stanley int r; 6426e790746SPaolo Bonzini 64368b5f314SYuri Benditovich if (n->nic->peer_deleted) { 64468b5f314SYuri Benditovich return; 64568b5f314SYuri Benditovich } 64668b5f314SYuri Benditovich 6476e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 6486e790746SPaolo Bonzini if (i < n->curr_queues) { 649ddfa83eaSJoel Stanley r = peer_attach(n, i); 650ddfa83eaSJoel Stanley assert(!r); 6516e790746SPaolo Bonzini } else { 652ddfa83eaSJoel Stanley r = peer_detach(n, i); 653ddfa83eaSJoel Stanley assert(!r); 6546e790746SPaolo Bonzini } 6556e790746SPaolo Bonzini } 6566e790746SPaolo Bonzini } 6576e790746SPaolo Bonzini 658ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue); 6596e790746SPaolo Bonzini 6609d5b731dSJason Wang static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, 6619d5b731dSJason Wang Error **errp) 6626e790746SPaolo Bonzini { 66317a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 6646e790746SPaolo Bonzini NetClientState *nc = qemu_get_queue(n->nic); 6656e790746SPaolo Bonzini 666da3e8a23SShannon Zhao /* Firstly sync all virtio-net possible supported features */ 667da3e8a23SShannon Zhao features |= n->host_features; 668da3e8a23SShannon Zhao 6690cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_MAC); 6706e790746SPaolo Bonzini 6716e790746SPaolo Bonzini if (!peer_has_vnet_hdr(n)) { 6720cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_CSUM); 6730cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4); 6740cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6); 6750cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN); 6766e790746SPaolo Bonzini 6770cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM); 6780cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4); 6790cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6); 6800cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN); 6816e790746SPaolo Bonzini } 6826e790746SPaolo Bonzini 6836e790746SPaolo Bonzini if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { 6840cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO); 6850cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO); 6866e790746SPaolo Bonzini } 6876e790746SPaolo Bonzini 688ed8b4afeSNikolay Nikolaev if (!get_vhost_net(nc->peer)) { 6896e790746SPaolo Bonzini return features; 6906e790746SPaolo Bonzini } 6912974e916SYuri Benditovich 69275ebec11SMaxime Coquelin features = vhost_net_get_features(get_vhost_net(nc->peer), features); 69375ebec11SMaxime Coquelin vdev->backend_features = features; 69475ebec11SMaxime Coquelin 69575ebec11SMaxime Coquelin if (n->mtu_bypass_backend && 69675ebec11SMaxime Coquelin (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) { 69775ebec11SMaxime Coquelin features |= (1ULL << VIRTIO_NET_F_MTU); 69875ebec11SMaxime Coquelin } 69975ebec11SMaxime Coquelin 70075ebec11SMaxime Coquelin return features; 7016e790746SPaolo Bonzini } 7026e790746SPaolo Bonzini 703019a3edbSGerd Hoffmann static uint64_t virtio_net_bad_features(VirtIODevice *vdev) 7046e790746SPaolo Bonzini { 705019a3edbSGerd Hoffmann uint64_t features = 0; 7066e790746SPaolo Bonzini 7076e790746SPaolo Bonzini /* Linux kernel 2.6.25. It understood MAC (as everyone must), 7086e790746SPaolo Bonzini * but also these: */ 7090cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_MAC); 7100cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_CSUM); 7110cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO4); 7120cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO6); 7130cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_HOST_ECN); 7146e790746SPaolo Bonzini 7156e790746SPaolo Bonzini return features; 7166e790746SPaolo Bonzini } 7176e790746SPaolo Bonzini 718644c9858SDmitry Fleytman static void virtio_net_apply_guest_offloads(VirtIONet *n) 719644c9858SDmitry Fleytman { 720ad37bb3bSStefan Hajnoczi qemu_set_offload(qemu_get_queue(n->nic)->peer, 721644c9858SDmitry Fleytman !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)), 722644c9858SDmitry Fleytman !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)), 723644c9858SDmitry Fleytman !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)), 724644c9858SDmitry Fleytman !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)), 725644c9858SDmitry Fleytman !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO))); 726644c9858SDmitry Fleytman } 727644c9858SDmitry Fleytman 728644c9858SDmitry Fleytman static uint64_t virtio_net_guest_offloads_by_features(uint32_t features) 729644c9858SDmitry Fleytman { 730644c9858SDmitry Fleytman static const uint64_t guest_offloads_mask = 731644c9858SDmitry Fleytman (1ULL << VIRTIO_NET_F_GUEST_CSUM) | 732644c9858SDmitry Fleytman (1ULL << VIRTIO_NET_F_GUEST_TSO4) | 733644c9858SDmitry Fleytman (1ULL << VIRTIO_NET_F_GUEST_TSO6) | 734644c9858SDmitry Fleytman (1ULL << VIRTIO_NET_F_GUEST_ECN) | 735644c9858SDmitry Fleytman (1ULL << VIRTIO_NET_F_GUEST_UFO); 736644c9858SDmitry Fleytman 737644c9858SDmitry Fleytman return guest_offloads_mask & features; 738644c9858SDmitry Fleytman } 739644c9858SDmitry Fleytman 740644c9858SDmitry Fleytman static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n) 741644c9858SDmitry Fleytman { 742644c9858SDmitry Fleytman VirtIODevice *vdev = VIRTIO_DEVICE(n); 743644c9858SDmitry Fleytman return virtio_net_guest_offloads_by_features(vdev->guest_features); 744644c9858SDmitry Fleytman } 745644c9858SDmitry Fleytman 746d5aaa1b0SGerd Hoffmann static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) 7476e790746SPaolo Bonzini { 74817a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 7496e790746SPaolo Bonzini int i; 7506e790746SPaolo Bonzini 75175ebec11SMaxime Coquelin if (n->mtu_bypass_backend && 75275ebec11SMaxime Coquelin !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) { 75375ebec11SMaxime Coquelin features &= ~(1ULL << VIRTIO_NET_F_MTU); 75475ebec11SMaxime Coquelin } 75575ebec11SMaxime Coquelin 756ef546f12SCornelia Huck virtio_net_set_multiqueue(n, 75795129d6fSCornelia Huck virtio_has_feature(features, VIRTIO_NET_F_MQ)); 7586e790746SPaolo Bonzini 759ef546f12SCornelia Huck virtio_net_set_mrg_rx_bufs(n, 76095129d6fSCornelia Huck virtio_has_feature(features, 761bb9d17f8SCornelia Huck VIRTIO_NET_F_MRG_RXBUF), 76295129d6fSCornelia Huck virtio_has_feature(features, 763bb9d17f8SCornelia Huck VIRTIO_F_VERSION_1)); 7646e790746SPaolo Bonzini 7652974e916SYuri Benditovich n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) && 7662974e916SYuri Benditovich virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4); 7672974e916SYuri Benditovich n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) && 7682974e916SYuri Benditovich virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6); 7692974e916SYuri Benditovich 7706e790746SPaolo Bonzini if (n->has_vnet_hdr) { 771644c9858SDmitry Fleytman n->curr_guest_offloads = 772644c9858SDmitry Fleytman virtio_net_guest_offloads_by_features(features); 773644c9858SDmitry Fleytman virtio_net_apply_guest_offloads(n); 7746e790746SPaolo Bonzini } 7756e790746SPaolo Bonzini 7766e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 7776e790746SPaolo Bonzini NetClientState *nc = qemu_get_subqueue(n->nic, i); 7786e790746SPaolo Bonzini 779ed8b4afeSNikolay Nikolaev if (!get_vhost_net(nc->peer)) { 7806e790746SPaolo Bonzini continue; 7816e790746SPaolo Bonzini } 782ed8b4afeSNikolay Nikolaev vhost_net_ack_features(get_vhost_net(nc->peer), features); 7836e790746SPaolo Bonzini } 7840b1eaa88SStefan Fritsch 78595129d6fSCornelia Huck if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { 7860b1eaa88SStefan Fritsch memset(n->vlans, 0, MAX_VLAN >> 3); 7870b1eaa88SStefan Fritsch } else { 7880b1eaa88SStefan Fritsch memset(n->vlans, 0xff, MAX_VLAN >> 3); 7890b1eaa88SStefan Fritsch } 7906e790746SPaolo Bonzini } 7916e790746SPaolo Bonzini 7926e790746SPaolo Bonzini static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, 7936e790746SPaolo Bonzini struct iovec *iov, unsigned int iov_cnt) 7946e790746SPaolo Bonzini { 7956e790746SPaolo Bonzini uint8_t on; 7966e790746SPaolo Bonzini size_t s; 797b1be4280SAmos Kong NetClientState *nc = qemu_get_queue(n->nic); 7986e790746SPaolo Bonzini 7996e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on)); 8006e790746SPaolo Bonzini if (s != sizeof(on)) { 8016e790746SPaolo Bonzini return VIRTIO_NET_ERR; 8026e790746SPaolo Bonzini } 8036e790746SPaolo Bonzini 8046e790746SPaolo Bonzini if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) { 8056e790746SPaolo Bonzini n->promisc = on; 8066e790746SPaolo Bonzini } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) { 8076e790746SPaolo Bonzini n->allmulti = on; 8086e790746SPaolo Bonzini } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) { 8096e790746SPaolo Bonzini n->alluni = on; 8106e790746SPaolo Bonzini } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) { 8116e790746SPaolo Bonzini n->nomulti = on; 8126e790746SPaolo Bonzini } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) { 8136e790746SPaolo Bonzini n->nouni = on; 8146e790746SPaolo Bonzini } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) { 8156e790746SPaolo Bonzini n->nobcast = on; 8166e790746SPaolo Bonzini } else { 8176e790746SPaolo Bonzini return VIRTIO_NET_ERR; 8186e790746SPaolo Bonzini } 8196e790746SPaolo Bonzini 820b1be4280SAmos Kong rxfilter_notify(nc); 821b1be4280SAmos Kong 8226e790746SPaolo Bonzini return VIRTIO_NET_OK; 8236e790746SPaolo Bonzini } 8246e790746SPaolo Bonzini 825644c9858SDmitry Fleytman static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, 826644c9858SDmitry Fleytman struct iovec *iov, unsigned int iov_cnt) 827644c9858SDmitry Fleytman { 828644c9858SDmitry Fleytman VirtIODevice *vdev = VIRTIO_DEVICE(n); 829644c9858SDmitry Fleytman uint64_t offloads; 830644c9858SDmitry Fleytman size_t s; 831644c9858SDmitry Fleytman 83295129d6fSCornelia Huck if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { 833644c9858SDmitry Fleytman return VIRTIO_NET_ERR; 834644c9858SDmitry Fleytman } 835644c9858SDmitry Fleytman 836644c9858SDmitry Fleytman s = iov_to_buf(iov, iov_cnt, 0, &offloads, sizeof(offloads)); 837644c9858SDmitry Fleytman if (s != sizeof(offloads)) { 838644c9858SDmitry Fleytman return VIRTIO_NET_ERR; 839644c9858SDmitry Fleytman } 840644c9858SDmitry Fleytman 841644c9858SDmitry Fleytman if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) { 842644c9858SDmitry Fleytman uint64_t supported_offloads; 843644c9858SDmitry Fleytman 844189ae6bbSJason Wang offloads = virtio_ldq_p(vdev, &offloads); 845189ae6bbSJason Wang 846644c9858SDmitry Fleytman if (!n->has_vnet_hdr) { 847644c9858SDmitry Fleytman return VIRTIO_NET_ERR; 848644c9858SDmitry Fleytman } 849644c9858SDmitry Fleytman 8502974e916SYuri Benditovich n->rsc4_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) && 8512974e916SYuri Benditovich virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO4); 8522974e916SYuri Benditovich n->rsc6_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) && 8532974e916SYuri Benditovich virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO6); 8542974e916SYuri Benditovich virtio_clear_feature(&offloads, VIRTIO_NET_F_RSC_EXT); 8552974e916SYuri Benditovich 856644c9858SDmitry Fleytman supported_offloads = virtio_net_supported_guest_offloads(n); 857644c9858SDmitry Fleytman if (offloads & ~supported_offloads) { 858644c9858SDmitry Fleytman return VIRTIO_NET_ERR; 859644c9858SDmitry Fleytman } 860644c9858SDmitry Fleytman 861644c9858SDmitry Fleytman n->curr_guest_offloads = offloads; 862644c9858SDmitry Fleytman virtio_net_apply_guest_offloads(n); 863644c9858SDmitry Fleytman 864644c9858SDmitry Fleytman return VIRTIO_NET_OK; 865644c9858SDmitry Fleytman } else { 866644c9858SDmitry Fleytman return VIRTIO_NET_ERR; 867644c9858SDmitry Fleytman } 868644c9858SDmitry Fleytman } 869644c9858SDmitry Fleytman 8706e790746SPaolo Bonzini static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, 8716e790746SPaolo Bonzini struct iovec *iov, unsigned int iov_cnt) 8726e790746SPaolo Bonzini { 8731399c60dSRusty Russell VirtIODevice *vdev = VIRTIO_DEVICE(n); 8746e790746SPaolo Bonzini struct virtio_net_ctrl_mac mac_data; 8756e790746SPaolo Bonzini size_t s; 876b1be4280SAmos Kong NetClientState *nc = qemu_get_queue(n->nic); 8776e790746SPaolo Bonzini 8786e790746SPaolo Bonzini if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) { 8796e790746SPaolo Bonzini if (iov_size(iov, iov_cnt) != sizeof(n->mac)) { 8806e790746SPaolo Bonzini return VIRTIO_NET_ERR; 8816e790746SPaolo Bonzini } 8826e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac)); 8836e790746SPaolo Bonzini assert(s == sizeof(n->mac)); 8846e790746SPaolo Bonzini qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); 885b1be4280SAmos Kong rxfilter_notify(nc); 886b1be4280SAmos Kong 8876e790746SPaolo Bonzini return VIRTIO_NET_OK; 8886e790746SPaolo Bonzini } 8896e790746SPaolo Bonzini 8906e790746SPaolo Bonzini if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) { 8916e790746SPaolo Bonzini return VIRTIO_NET_ERR; 8926e790746SPaolo Bonzini } 8936e790746SPaolo Bonzini 894cae2e556SAmos Kong int in_use = 0; 895cae2e556SAmos Kong int first_multi = 0; 896cae2e556SAmos Kong uint8_t uni_overflow = 0; 897cae2e556SAmos Kong uint8_t multi_overflow = 0; 898cae2e556SAmos Kong uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); 8996e790746SPaolo Bonzini 9006e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, 9016e790746SPaolo Bonzini sizeof(mac_data.entries)); 9021399c60dSRusty Russell mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); 9036e790746SPaolo Bonzini if (s != sizeof(mac_data.entries)) { 904b1be4280SAmos Kong goto error; 9056e790746SPaolo Bonzini } 9066e790746SPaolo Bonzini iov_discard_front(&iov, &iov_cnt, s); 9076e790746SPaolo Bonzini 9086e790746SPaolo Bonzini if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) { 909b1be4280SAmos Kong goto error; 9106e790746SPaolo Bonzini } 9116e790746SPaolo Bonzini 9126e790746SPaolo Bonzini if (mac_data.entries <= MAC_TABLE_ENTRIES) { 913cae2e556SAmos Kong s = iov_to_buf(iov, iov_cnt, 0, macs, 9146e790746SPaolo Bonzini mac_data.entries * ETH_ALEN); 9156e790746SPaolo Bonzini if (s != mac_data.entries * ETH_ALEN) { 916b1be4280SAmos Kong goto error; 9176e790746SPaolo Bonzini } 918cae2e556SAmos Kong in_use += mac_data.entries; 9196e790746SPaolo Bonzini } else { 920cae2e556SAmos Kong uni_overflow = 1; 9216e790746SPaolo Bonzini } 9226e790746SPaolo Bonzini 9236e790746SPaolo Bonzini iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN); 9246e790746SPaolo Bonzini 925cae2e556SAmos Kong first_multi = in_use; 9266e790746SPaolo Bonzini 9276e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, 9286e790746SPaolo Bonzini sizeof(mac_data.entries)); 9291399c60dSRusty Russell mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); 9306e790746SPaolo Bonzini if (s != sizeof(mac_data.entries)) { 931b1be4280SAmos Kong goto error; 9326e790746SPaolo Bonzini } 9336e790746SPaolo Bonzini 9346e790746SPaolo Bonzini iov_discard_front(&iov, &iov_cnt, s); 9356e790746SPaolo Bonzini 9366e790746SPaolo Bonzini if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) { 937b1be4280SAmos Kong goto error; 9386e790746SPaolo Bonzini } 9396e790746SPaolo Bonzini 940edc24385SMichael S. Tsirkin if (mac_data.entries <= MAC_TABLE_ENTRIES - in_use) { 941cae2e556SAmos Kong s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN], 9426e790746SPaolo Bonzini mac_data.entries * ETH_ALEN); 9436e790746SPaolo Bonzini if (s != mac_data.entries * ETH_ALEN) { 944b1be4280SAmos Kong goto error; 9456e790746SPaolo Bonzini } 946cae2e556SAmos Kong in_use += mac_data.entries; 9476e790746SPaolo Bonzini } else { 948cae2e556SAmos Kong multi_overflow = 1; 9496e790746SPaolo Bonzini } 9506e790746SPaolo Bonzini 951cae2e556SAmos Kong n->mac_table.in_use = in_use; 952cae2e556SAmos Kong n->mac_table.first_multi = first_multi; 953cae2e556SAmos Kong n->mac_table.uni_overflow = uni_overflow; 954cae2e556SAmos Kong n->mac_table.multi_overflow = multi_overflow; 955cae2e556SAmos Kong memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN); 956cae2e556SAmos Kong g_free(macs); 957b1be4280SAmos Kong rxfilter_notify(nc); 958b1be4280SAmos Kong 9596e790746SPaolo Bonzini return VIRTIO_NET_OK; 960b1be4280SAmos Kong 961b1be4280SAmos Kong error: 962cae2e556SAmos Kong g_free(macs); 963b1be4280SAmos Kong return VIRTIO_NET_ERR; 9646e790746SPaolo Bonzini } 9656e790746SPaolo Bonzini 9666e790746SPaolo Bonzini static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, 9676e790746SPaolo Bonzini struct iovec *iov, unsigned int iov_cnt) 9686e790746SPaolo Bonzini { 9691399c60dSRusty Russell VirtIODevice *vdev = VIRTIO_DEVICE(n); 9706e790746SPaolo Bonzini uint16_t vid; 9716e790746SPaolo Bonzini size_t s; 972b1be4280SAmos Kong NetClientState *nc = qemu_get_queue(n->nic); 9736e790746SPaolo Bonzini 9746e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid)); 9751399c60dSRusty Russell vid = virtio_lduw_p(vdev, &vid); 9766e790746SPaolo Bonzini if (s != sizeof(vid)) { 9776e790746SPaolo Bonzini return VIRTIO_NET_ERR; 9786e790746SPaolo Bonzini } 9796e790746SPaolo Bonzini 9806e790746SPaolo Bonzini if (vid >= MAX_VLAN) 9816e790746SPaolo Bonzini return VIRTIO_NET_ERR; 9826e790746SPaolo Bonzini 9836e790746SPaolo Bonzini if (cmd == VIRTIO_NET_CTRL_VLAN_ADD) 9846e790746SPaolo Bonzini n->vlans[vid >> 5] |= (1U << (vid & 0x1f)); 9856e790746SPaolo Bonzini else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL) 9866e790746SPaolo Bonzini n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f)); 9876e790746SPaolo Bonzini else 9886e790746SPaolo Bonzini return VIRTIO_NET_ERR; 9896e790746SPaolo Bonzini 990b1be4280SAmos Kong rxfilter_notify(nc); 991b1be4280SAmos Kong 9926e790746SPaolo Bonzini return VIRTIO_NET_OK; 9936e790746SPaolo Bonzini } 9946e790746SPaolo Bonzini 995f57fcf70SJason Wang static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd, 996f57fcf70SJason Wang struct iovec *iov, unsigned int iov_cnt) 997f57fcf70SJason Wang { 9989d8c6a25SDr. David Alan Gilbert trace_virtio_net_handle_announce(n->announce_timer.round); 999f57fcf70SJason Wang if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK && 1000f57fcf70SJason Wang n->status & VIRTIO_NET_S_ANNOUNCE) { 1001f57fcf70SJason Wang n->status &= ~VIRTIO_NET_S_ANNOUNCE; 10029d8c6a25SDr. David Alan Gilbert if (n->announce_timer.round) { 10039d8c6a25SDr. David Alan Gilbert qemu_announce_timer_step(&n->announce_timer); 1004f57fcf70SJason Wang } 1005f57fcf70SJason Wang return VIRTIO_NET_OK; 1006f57fcf70SJason Wang } else { 1007f57fcf70SJason Wang return VIRTIO_NET_ERR; 1008f57fcf70SJason Wang } 1009f57fcf70SJason Wang } 1010f57fcf70SJason Wang 10116e790746SPaolo Bonzini static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, 10126e790746SPaolo Bonzini struct iovec *iov, unsigned int iov_cnt) 10136e790746SPaolo Bonzini { 101417a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 10156e790746SPaolo Bonzini struct virtio_net_ctrl_mq mq; 10166e790746SPaolo Bonzini size_t s; 10176e790746SPaolo Bonzini uint16_t queues; 10186e790746SPaolo Bonzini 10196e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq)); 10206e790746SPaolo Bonzini if (s != sizeof(mq)) { 10216e790746SPaolo Bonzini return VIRTIO_NET_ERR; 10226e790746SPaolo Bonzini } 10236e790746SPaolo Bonzini 10246e790746SPaolo Bonzini if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { 10256e790746SPaolo Bonzini return VIRTIO_NET_ERR; 10266e790746SPaolo Bonzini } 10276e790746SPaolo Bonzini 10281399c60dSRusty Russell queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs); 10296e790746SPaolo Bonzini 10306e790746SPaolo Bonzini if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || 10316e790746SPaolo Bonzini queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || 10326e790746SPaolo Bonzini queues > n->max_queues || 10336e790746SPaolo Bonzini !n->multiqueue) { 10346e790746SPaolo Bonzini return VIRTIO_NET_ERR; 10356e790746SPaolo Bonzini } 10366e790746SPaolo Bonzini 10376e790746SPaolo Bonzini n->curr_queues = queues; 10386e790746SPaolo Bonzini /* stop the backend before changing the number of queues to avoid handling a 10396e790746SPaolo Bonzini * disabled queue */ 104017a0ca55SKONRAD Frederic virtio_net_set_status(vdev, vdev->status); 10416e790746SPaolo Bonzini virtio_net_set_queues(n); 10426e790746SPaolo Bonzini 10436e790746SPaolo Bonzini return VIRTIO_NET_OK; 10446e790746SPaolo Bonzini } 1045ba7eadb5SGreg Kurz 10466e790746SPaolo Bonzini static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) 10476e790746SPaolo Bonzini { 104817a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 10496e790746SPaolo Bonzini struct virtio_net_ctrl_hdr ctrl; 10506e790746SPaolo Bonzini virtio_net_ctrl_ack status = VIRTIO_NET_ERR; 105151b19ebeSPaolo Bonzini VirtQueueElement *elem; 10526e790746SPaolo Bonzini size_t s; 1053771b6ed3SJason Wang struct iovec *iov, *iov2; 10546e790746SPaolo Bonzini unsigned int iov_cnt; 10556e790746SPaolo Bonzini 105651b19ebeSPaolo Bonzini for (;;) { 105751b19ebeSPaolo Bonzini elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 105851b19ebeSPaolo Bonzini if (!elem) { 105951b19ebeSPaolo Bonzini break; 106051b19ebeSPaolo Bonzini } 106151b19ebeSPaolo Bonzini if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) || 106251b19ebeSPaolo Bonzini iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) { 1063ba7eadb5SGreg Kurz virtio_error(vdev, "virtio-net ctrl missing headers"); 1064ba7eadb5SGreg Kurz virtqueue_detach_element(vq, elem, 0); 1065ba7eadb5SGreg Kurz g_free(elem); 1066ba7eadb5SGreg Kurz break; 10676e790746SPaolo Bonzini } 10686e790746SPaolo Bonzini 106951b19ebeSPaolo Bonzini iov_cnt = elem->out_num; 107051b19ebeSPaolo Bonzini iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num); 10716e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl)); 10726e790746SPaolo Bonzini iov_discard_front(&iov, &iov_cnt, sizeof(ctrl)); 10736e790746SPaolo Bonzini if (s != sizeof(ctrl)) { 10746e790746SPaolo Bonzini status = VIRTIO_NET_ERR; 10756e790746SPaolo Bonzini } else if (ctrl.class == VIRTIO_NET_CTRL_RX) { 10766e790746SPaolo Bonzini status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt); 10776e790746SPaolo Bonzini } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) { 10786e790746SPaolo Bonzini status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt); 10796e790746SPaolo Bonzini } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { 10806e790746SPaolo Bonzini status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt); 1081f57fcf70SJason Wang } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) { 1082f57fcf70SJason Wang status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt); 10836e790746SPaolo Bonzini } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { 10846e790746SPaolo Bonzini status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt); 1085644c9858SDmitry Fleytman } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) { 1086644c9858SDmitry Fleytman status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt); 10876e790746SPaolo Bonzini } 10886e790746SPaolo Bonzini 108951b19ebeSPaolo Bonzini s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status)); 10906e790746SPaolo Bonzini assert(s == sizeof(status)); 10916e790746SPaolo Bonzini 109251b19ebeSPaolo Bonzini virtqueue_push(vq, elem, sizeof(status)); 10936e790746SPaolo Bonzini virtio_notify(vdev, vq); 1094771b6ed3SJason Wang g_free(iov2); 109551b19ebeSPaolo Bonzini g_free(elem); 10966e790746SPaolo Bonzini } 10976e790746SPaolo Bonzini } 10986e790746SPaolo Bonzini 10996e790746SPaolo Bonzini /* RX */ 11006e790746SPaolo Bonzini 11016e790746SPaolo Bonzini static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) 11026e790746SPaolo Bonzini { 110317a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 11046e790746SPaolo Bonzini int queue_index = vq2q(virtio_get_queue_index(vq)); 11056e790746SPaolo Bonzini 11066e790746SPaolo Bonzini qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index)); 11076e790746SPaolo Bonzini } 11086e790746SPaolo Bonzini 11096e790746SPaolo Bonzini static int virtio_net_can_receive(NetClientState *nc) 11106e790746SPaolo Bonzini { 11116e790746SPaolo Bonzini VirtIONet *n = qemu_get_nic_opaque(nc); 111217a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 11136e790746SPaolo Bonzini VirtIONetQueue *q = virtio_net_get_subqueue(nc); 11146e790746SPaolo Bonzini 111517a0ca55SKONRAD Frederic if (!vdev->vm_running) { 11166e790746SPaolo Bonzini return 0; 11176e790746SPaolo Bonzini } 11186e790746SPaolo Bonzini 11196e790746SPaolo Bonzini if (nc->queue_index >= n->curr_queues) { 11206e790746SPaolo Bonzini return 0; 11216e790746SPaolo Bonzini } 11226e790746SPaolo Bonzini 11236e790746SPaolo Bonzini if (!virtio_queue_ready(q->rx_vq) || 112417a0ca55SKONRAD Frederic !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { 11256e790746SPaolo Bonzini return 0; 11266e790746SPaolo Bonzini } 11276e790746SPaolo Bonzini 11286e790746SPaolo Bonzini return 1; 11296e790746SPaolo Bonzini } 11306e790746SPaolo Bonzini 11316e790746SPaolo Bonzini static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) 11326e790746SPaolo Bonzini { 11336e790746SPaolo Bonzini VirtIONet *n = q->n; 11346e790746SPaolo Bonzini if (virtio_queue_empty(q->rx_vq) || 11356e790746SPaolo Bonzini (n->mergeable_rx_bufs && 11366e790746SPaolo Bonzini !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { 11376e790746SPaolo Bonzini virtio_queue_set_notification(q->rx_vq, 1); 11386e790746SPaolo Bonzini 11396e790746SPaolo Bonzini /* To avoid a race condition where the guest has made some buffers 11406e790746SPaolo Bonzini * available after the above check but before notification was 11416e790746SPaolo Bonzini * enabled, check for available buffers again. 11426e790746SPaolo Bonzini */ 11436e790746SPaolo Bonzini if (virtio_queue_empty(q->rx_vq) || 11446e790746SPaolo Bonzini (n->mergeable_rx_bufs && 11456e790746SPaolo Bonzini !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { 11466e790746SPaolo Bonzini return 0; 11476e790746SPaolo Bonzini } 11486e790746SPaolo Bonzini } 11496e790746SPaolo Bonzini 11506e790746SPaolo Bonzini virtio_queue_set_notification(q->rx_vq, 0); 11516e790746SPaolo Bonzini return 1; 11526e790746SPaolo Bonzini } 11536e790746SPaolo Bonzini 11541399c60dSRusty Russell static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr) 1155032a74a1SCédric Le Goater { 11561399c60dSRusty Russell virtio_tswap16s(vdev, &hdr->hdr_len); 11571399c60dSRusty Russell virtio_tswap16s(vdev, &hdr->gso_size); 11581399c60dSRusty Russell virtio_tswap16s(vdev, &hdr->csum_start); 11591399c60dSRusty Russell virtio_tswap16s(vdev, &hdr->csum_offset); 1160032a74a1SCédric Le Goater } 1161032a74a1SCédric Le Goater 11626e790746SPaolo Bonzini /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so 11636e790746SPaolo Bonzini * it never finds out that the packets don't have valid checksums. This 11646e790746SPaolo Bonzini * causes dhclient to get upset. Fedora's carried a patch for ages to 11656e790746SPaolo Bonzini * fix this with Xen but it hasn't appeared in an upstream release of 11666e790746SPaolo Bonzini * dhclient yet. 11676e790746SPaolo Bonzini * 11686e790746SPaolo Bonzini * To avoid breaking existing guests, we catch udp packets and add 11696e790746SPaolo Bonzini * checksums. This is terrible but it's better than hacking the guest 11706e790746SPaolo Bonzini * kernels. 11716e790746SPaolo Bonzini * 11726e790746SPaolo Bonzini * N.B. if we introduce a zero-copy API, this operation is no longer free so 11736e790746SPaolo Bonzini * we should provide a mechanism to disable it to avoid polluting the host 11746e790746SPaolo Bonzini * cache. 11756e790746SPaolo Bonzini */ 11766e790746SPaolo Bonzini static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, 11776e790746SPaolo Bonzini uint8_t *buf, size_t size) 11786e790746SPaolo Bonzini { 11796e790746SPaolo Bonzini if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */ 11806e790746SPaolo Bonzini (size > 27 && size < 1500) && /* normal sized MTU */ 11816e790746SPaolo Bonzini (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */ 11826e790746SPaolo Bonzini (buf[23] == 17) && /* ip.protocol == UDP */ 11836e790746SPaolo Bonzini (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */ 11846e790746SPaolo Bonzini net_checksum_calculate(buf, size); 11856e790746SPaolo Bonzini hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; 11866e790746SPaolo Bonzini } 11876e790746SPaolo Bonzini } 11886e790746SPaolo Bonzini 11896e790746SPaolo Bonzini static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, 11906e790746SPaolo Bonzini const void *buf, size_t size) 11916e790746SPaolo Bonzini { 11926e790746SPaolo Bonzini if (n->has_vnet_hdr) { 11936e790746SPaolo Bonzini /* FIXME this cast is evil */ 11946e790746SPaolo Bonzini void *wbuf = (void *)buf; 11956e790746SPaolo Bonzini work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, 11966e790746SPaolo Bonzini size - n->host_hdr_len); 11971bfa316cSGreg Kurz 11981bfa316cSGreg Kurz if (n->needs_vnet_hdr_swap) { 11991399c60dSRusty Russell virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); 12001bfa316cSGreg Kurz } 12016e790746SPaolo Bonzini iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); 12026e790746SPaolo Bonzini } else { 12036e790746SPaolo Bonzini struct virtio_net_hdr hdr = { 12046e790746SPaolo Bonzini .flags = 0, 12056e790746SPaolo Bonzini .gso_type = VIRTIO_NET_HDR_GSO_NONE 12066e790746SPaolo Bonzini }; 12076e790746SPaolo Bonzini iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr); 12086e790746SPaolo Bonzini } 12096e790746SPaolo Bonzini } 12106e790746SPaolo Bonzini 12116e790746SPaolo Bonzini static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) 12126e790746SPaolo Bonzini { 12136e790746SPaolo Bonzini static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 12146e790746SPaolo Bonzini static const uint8_t vlan[] = {0x81, 0x00}; 12156e790746SPaolo Bonzini uint8_t *ptr = (uint8_t *)buf; 12166e790746SPaolo Bonzini int i; 12176e790746SPaolo Bonzini 12186e790746SPaolo Bonzini if (n->promisc) 12196e790746SPaolo Bonzini return 1; 12206e790746SPaolo Bonzini 12216e790746SPaolo Bonzini ptr += n->host_hdr_len; 12226e790746SPaolo Bonzini 12236e790746SPaolo Bonzini if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { 12247542d3e7SPeter Maydell int vid = lduw_be_p(ptr + 14) & 0xfff; 12256e790746SPaolo Bonzini if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f)))) 12266e790746SPaolo Bonzini return 0; 12276e790746SPaolo Bonzini } 12286e790746SPaolo Bonzini 12296e790746SPaolo Bonzini if (ptr[0] & 1) { // multicast 12306e790746SPaolo Bonzini if (!memcmp(ptr, bcast, sizeof(bcast))) { 12316e790746SPaolo Bonzini return !n->nobcast; 12326e790746SPaolo Bonzini } else if (n->nomulti) { 12336e790746SPaolo Bonzini return 0; 12346e790746SPaolo Bonzini } else if (n->allmulti || n->mac_table.multi_overflow) { 12356e790746SPaolo Bonzini return 1; 12366e790746SPaolo Bonzini } 12376e790746SPaolo Bonzini 12386e790746SPaolo Bonzini for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) { 12396e790746SPaolo Bonzini if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) { 12406e790746SPaolo Bonzini return 1; 12416e790746SPaolo Bonzini } 12426e790746SPaolo Bonzini } 12436e790746SPaolo Bonzini } else { // unicast 12446e790746SPaolo Bonzini if (n->nouni) { 12456e790746SPaolo Bonzini return 0; 12466e790746SPaolo Bonzini } else if (n->alluni || n->mac_table.uni_overflow) { 12476e790746SPaolo Bonzini return 1; 12486e790746SPaolo Bonzini } else if (!memcmp(ptr, n->mac, ETH_ALEN)) { 12496e790746SPaolo Bonzini return 1; 12506e790746SPaolo Bonzini } 12516e790746SPaolo Bonzini 12526e790746SPaolo Bonzini for (i = 0; i < n->mac_table.first_multi; i++) { 12536e790746SPaolo Bonzini if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) { 12546e790746SPaolo Bonzini return 1; 12556e790746SPaolo Bonzini } 12566e790746SPaolo Bonzini } 12576e790746SPaolo Bonzini } 12586e790746SPaolo Bonzini 12596e790746SPaolo Bonzini return 0; 12606e790746SPaolo Bonzini } 12616e790746SPaolo Bonzini 126297cd965cSPaolo Bonzini static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, 126397cd965cSPaolo Bonzini size_t size) 12646e790746SPaolo Bonzini { 12656e790746SPaolo Bonzini VirtIONet *n = qemu_get_nic_opaque(nc); 12666e790746SPaolo Bonzini VirtIONetQueue *q = virtio_net_get_subqueue(nc); 126717a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 12686e790746SPaolo Bonzini struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; 12696e790746SPaolo Bonzini struct virtio_net_hdr_mrg_rxbuf mhdr; 12706e790746SPaolo Bonzini unsigned mhdr_cnt = 0; 12716e790746SPaolo Bonzini size_t offset, i, guest_offset; 12726e790746SPaolo Bonzini 12736e790746SPaolo Bonzini if (!virtio_net_can_receive(nc)) { 12746e790746SPaolo Bonzini return -1; 12756e790746SPaolo Bonzini } 12766e790746SPaolo Bonzini 12776e790746SPaolo Bonzini /* hdr_len refers to the header we supply to the guest */ 12786e790746SPaolo Bonzini if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) { 12796e790746SPaolo Bonzini return 0; 12806e790746SPaolo Bonzini } 12816e790746SPaolo Bonzini 12826e790746SPaolo Bonzini if (!receive_filter(n, buf, size)) 12836e790746SPaolo Bonzini return size; 12846e790746SPaolo Bonzini 12856e790746SPaolo Bonzini offset = i = 0; 12866e790746SPaolo Bonzini 12876e790746SPaolo Bonzini while (offset < size) { 128851b19ebeSPaolo Bonzini VirtQueueElement *elem; 12896e790746SPaolo Bonzini int len, total; 129051b19ebeSPaolo Bonzini const struct iovec *sg; 12916e790746SPaolo Bonzini 12926e790746SPaolo Bonzini total = 0; 12936e790746SPaolo Bonzini 129451b19ebeSPaolo Bonzini elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement)); 129551b19ebeSPaolo Bonzini if (!elem) { 1296ba10b9c0SGreg Kurz if (i) { 1297ba10b9c0SGreg Kurz virtio_error(vdev, "virtio-net unexpected empty queue: " 12986e790746SPaolo Bonzini "i %zd mergeable %d offset %zd, size %zd, " 1299019a3edbSGerd Hoffmann "guest hdr len %zd, host hdr len %zd " 1300019a3edbSGerd Hoffmann "guest features 0x%" PRIx64, 13016e790746SPaolo Bonzini i, n->mergeable_rx_bufs, offset, size, 1302019a3edbSGerd Hoffmann n->guest_hdr_len, n->host_hdr_len, 1303019a3edbSGerd Hoffmann vdev->guest_features); 1304ba10b9c0SGreg Kurz } 1305ba10b9c0SGreg Kurz return -1; 13066e790746SPaolo Bonzini } 13076e790746SPaolo Bonzini 130851b19ebeSPaolo Bonzini if (elem->in_num < 1) { 1309ba10b9c0SGreg Kurz virtio_error(vdev, 1310ba10b9c0SGreg Kurz "virtio-net receive queue contains no in buffers"); 1311ba10b9c0SGreg Kurz virtqueue_detach_element(q->rx_vq, elem, 0); 1312ba10b9c0SGreg Kurz g_free(elem); 1313ba10b9c0SGreg Kurz return -1; 13146e790746SPaolo Bonzini } 13156e790746SPaolo Bonzini 131651b19ebeSPaolo Bonzini sg = elem->in_sg; 13176e790746SPaolo Bonzini if (i == 0) { 13186e790746SPaolo Bonzini assert(offset == 0); 13196e790746SPaolo Bonzini if (n->mergeable_rx_bufs) { 13206e790746SPaolo Bonzini mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg), 132151b19ebeSPaolo Bonzini sg, elem->in_num, 13226e790746SPaolo Bonzini offsetof(typeof(mhdr), num_buffers), 13236e790746SPaolo Bonzini sizeof(mhdr.num_buffers)); 13246e790746SPaolo Bonzini } 13256e790746SPaolo Bonzini 132651b19ebeSPaolo Bonzini receive_header(n, sg, elem->in_num, buf, size); 13276e790746SPaolo Bonzini offset = n->host_hdr_len; 13286e790746SPaolo Bonzini total += n->guest_hdr_len; 13296e790746SPaolo Bonzini guest_offset = n->guest_hdr_len; 13306e790746SPaolo Bonzini } else { 13316e790746SPaolo Bonzini guest_offset = 0; 13326e790746SPaolo Bonzini } 13336e790746SPaolo Bonzini 13346e790746SPaolo Bonzini /* copy in packet. ugh */ 133551b19ebeSPaolo Bonzini len = iov_from_buf(sg, elem->in_num, guest_offset, 13366e790746SPaolo Bonzini buf + offset, size - offset); 13376e790746SPaolo Bonzini total += len; 13386e790746SPaolo Bonzini offset += len; 13396e790746SPaolo Bonzini /* If buffers can't be merged, at this point we 13406e790746SPaolo Bonzini * must have consumed the complete packet. 13416e790746SPaolo Bonzini * Otherwise, drop it. */ 13426e790746SPaolo Bonzini if (!n->mergeable_rx_bufs && offset < size) { 134327e57efeSLadi Prosek virtqueue_unpop(q->rx_vq, elem, total); 134451b19ebeSPaolo Bonzini g_free(elem); 13456e790746SPaolo Bonzini return size; 13466e790746SPaolo Bonzini } 13476e790746SPaolo Bonzini 13486e790746SPaolo Bonzini /* signal other side */ 134951b19ebeSPaolo Bonzini virtqueue_fill(q->rx_vq, elem, total, i++); 135051b19ebeSPaolo Bonzini g_free(elem); 13516e790746SPaolo Bonzini } 13526e790746SPaolo Bonzini 13536e790746SPaolo Bonzini if (mhdr_cnt) { 13541399c60dSRusty Russell virtio_stw_p(vdev, &mhdr.num_buffers, i); 13556e790746SPaolo Bonzini iov_from_buf(mhdr_sg, mhdr_cnt, 13566e790746SPaolo Bonzini 0, 13576e790746SPaolo Bonzini &mhdr.num_buffers, sizeof mhdr.num_buffers); 13586e790746SPaolo Bonzini } 13596e790746SPaolo Bonzini 13606e790746SPaolo Bonzini virtqueue_flush(q->rx_vq, i); 136117a0ca55SKONRAD Frederic virtio_notify(vdev, q->rx_vq); 13626e790746SPaolo Bonzini 13636e790746SPaolo Bonzini return size; 13646e790746SPaolo Bonzini } 13656e790746SPaolo Bonzini 13662974e916SYuri Benditovich static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf, 136797cd965cSPaolo Bonzini size_t size) 136897cd965cSPaolo Bonzini { 136997cd965cSPaolo Bonzini ssize_t r; 137097cd965cSPaolo Bonzini 137197cd965cSPaolo Bonzini rcu_read_lock(); 137297cd965cSPaolo Bonzini r = virtio_net_receive_rcu(nc, buf, size); 137397cd965cSPaolo Bonzini rcu_read_unlock(); 137497cd965cSPaolo Bonzini return r; 137597cd965cSPaolo Bonzini } 137697cd965cSPaolo Bonzini 13772974e916SYuri Benditovich static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain, 13782974e916SYuri Benditovich const uint8_t *buf, 13792974e916SYuri Benditovich VirtioNetRscUnit *unit) 13802974e916SYuri Benditovich { 13812974e916SYuri Benditovich uint16_t ip_hdrlen; 13822974e916SYuri Benditovich struct ip_header *ip; 13832974e916SYuri Benditovich 13842974e916SYuri Benditovich ip = (struct ip_header *)(buf + chain->n->guest_hdr_len 13852974e916SYuri Benditovich + sizeof(struct eth_header)); 13862974e916SYuri Benditovich unit->ip = (void *)ip; 13872974e916SYuri Benditovich ip_hdrlen = (ip->ip_ver_len & 0xF) << 2; 13882974e916SYuri Benditovich unit->ip_plen = &ip->ip_len; 13892974e916SYuri Benditovich unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen); 13902974e916SYuri Benditovich unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10; 13912974e916SYuri Benditovich unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen; 13922974e916SYuri Benditovich } 13932974e916SYuri Benditovich 13942974e916SYuri Benditovich static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain, 13952974e916SYuri Benditovich const uint8_t *buf, 13962974e916SYuri Benditovich VirtioNetRscUnit *unit) 13972974e916SYuri Benditovich { 13982974e916SYuri Benditovich struct ip6_header *ip6; 13992974e916SYuri Benditovich 14002974e916SYuri Benditovich ip6 = (struct ip6_header *)(buf + chain->n->guest_hdr_len 14012974e916SYuri Benditovich + sizeof(struct eth_header)); 14022974e916SYuri Benditovich unit->ip = ip6; 14032974e916SYuri Benditovich unit->ip_plen = &(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); 14042974e916SYuri Benditovich unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip)\ 14052974e916SYuri Benditovich + sizeof(struct ip6_header)); 14062974e916SYuri Benditovich unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10; 14072974e916SYuri Benditovich 14082974e916SYuri Benditovich /* There is a difference between payload lenght in ipv4 and v6, 14092974e916SYuri Benditovich ip header is excluded in ipv6 */ 14102974e916SYuri Benditovich unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen; 14112974e916SYuri Benditovich } 14122974e916SYuri Benditovich 14132974e916SYuri Benditovich static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain, 14142974e916SYuri Benditovich VirtioNetRscSeg *seg) 14152974e916SYuri Benditovich { 14162974e916SYuri Benditovich int ret; 14172974e916SYuri Benditovich struct virtio_net_hdr *h; 14182974e916SYuri Benditovich 14192974e916SYuri Benditovich h = (struct virtio_net_hdr *)seg->buf; 14202974e916SYuri Benditovich h->flags = 0; 14212974e916SYuri Benditovich h->gso_type = VIRTIO_NET_HDR_GSO_NONE; 14222974e916SYuri Benditovich 14232974e916SYuri Benditovich if (seg->is_coalesced) { 14242974e916SYuri Benditovich *virtio_net_rsc_ext_num_packets(h) = seg->packets; 14252974e916SYuri Benditovich *virtio_net_rsc_ext_num_dupacks(h) = seg->dup_ack; 14262974e916SYuri Benditovich h->flags = VIRTIO_NET_HDR_F_RSC_INFO; 14272974e916SYuri Benditovich if (chain->proto == ETH_P_IP) { 14282974e916SYuri Benditovich h->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; 14292974e916SYuri Benditovich } else { 14302974e916SYuri Benditovich h->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; 14312974e916SYuri Benditovich } 14322974e916SYuri Benditovich } 14332974e916SYuri Benditovich 14342974e916SYuri Benditovich ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size); 14352974e916SYuri Benditovich QTAILQ_REMOVE(&chain->buffers, seg, next); 14362974e916SYuri Benditovich g_free(seg->buf); 14372974e916SYuri Benditovich g_free(seg); 14382974e916SYuri Benditovich 14392974e916SYuri Benditovich return ret; 14402974e916SYuri Benditovich } 14412974e916SYuri Benditovich 14422974e916SYuri Benditovich static void virtio_net_rsc_purge(void *opq) 14432974e916SYuri Benditovich { 14442974e916SYuri Benditovich VirtioNetRscSeg *seg, *rn; 14452974e916SYuri Benditovich VirtioNetRscChain *chain = (VirtioNetRscChain *)opq; 14462974e916SYuri Benditovich 14472974e916SYuri Benditovich QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn) { 14482974e916SYuri Benditovich if (virtio_net_rsc_drain_seg(chain, seg) == 0) { 14492974e916SYuri Benditovich chain->stat.purge_failed++; 14502974e916SYuri Benditovich continue; 14512974e916SYuri Benditovich } 14522974e916SYuri Benditovich } 14532974e916SYuri Benditovich 14542974e916SYuri Benditovich chain->stat.timer++; 14552974e916SYuri Benditovich if (!QTAILQ_EMPTY(&chain->buffers)) { 14562974e916SYuri Benditovich timer_mod(chain->drain_timer, 14572974e916SYuri Benditovich qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout); 14582974e916SYuri Benditovich } 14592974e916SYuri Benditovich } 14602974e916SYuri Benditovich 14612974e916SYuri Benditovich static void virtio_net_rsc_cleanup(VirtIONet *n) 14622974e916SYuri Benditovich { 14632974e916SYuri Benditovich VirtioNetRscChain *chain, *rn_chain; 14642974e916SYuri Benditovich VirtioNetRscSeg *seg, *rn_seg; 14652974e916SYuri Benditovich 14662974e916SYuri Benditovich QTAILQ_FOREACH_SAFE(chain, &n->rsc_chains, next, rn_chain) { 14672974e916SYuri Benditovich QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn_seg) { 14682974e916SYuri Benditovich QTAILQ_REMOVE(&chain->buffers, seg, next); 14692974e916SYuri Benditovich g_free(seg->buf); 14702974e916SYuri Benditovich g_free(seg); 14712974e916SYuri Benditovich } 14722974e916SYuri Benditovich 14732974e916SYuri Benditovich timer_del(chain->drain_timer); 14742974e916SYuri Benditovich timer_free(chain->drain_timer); 14752974e916SYuri Benditovich QTAILQ_REMOVE(&n->rsc_chains, chain, next); 14762974e916SYuri Benditovich g_free(chain); 14772974e916SYuri Benditovich } 14782974e916SYuri Benditovich } 14792974e916SYuri Benditovich 14802974e916SYuri Benditovich static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain, 14812974e916SYuri Benditovich NetClientState *nc, 14822974e916SYuri Benditovich const uint8_t *buf, size_t size) 14832974e916SYuri Benditovich { 14842974e916SYuri Benditovich uint16_t hdr_len; 14852974e916SYuri Benditovich VirtioNetRscSeg *seg; 14862974e916SYuri Benditovich 14872974e916SYuri Benditovich hdr_len = chain->n->guest_hdr_len; 14882974e916SYuri Benditovich seg = g_malloc(sizeof(VirtioNetRscSeg)); 14892974e916SYuri Benditovich seg->buf = g_malloc(hdr_len + sizeof(struct eth_header) 14902974e916SYuri Benditovich + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD); 14912974e916SYuri Benditovich memcpy(seg->buf, buf, size); 14922974e916SYuri Benditovich seg->size = size; 14932974e916SYuri Benditovich seg->packets = 1; 14942974e916SYuri Benditovich seg->dup_ack = 0; 14952974e916SYuri Benditovich seg->is_coalesced = 0; 14962974e916SYuri Benditovich seg->nc = nc; 14972974e916SYuri Benditovich 14982974e916SYuri Benditovich QTAILQ_INSERT_TAIL(&chain->buffers, seg, next); 14992974e916SYuri Benditovich chain->stat.cache++; 15002974e916SYuri Benditovich 15012974e916SYuri Benditovich switch (chain->proto) { 15022974e916SYuri Benditovich case ETH_P_IP: 15032974e916SYuri Benditovich virtio_net_rsc_extract_unit4(chain, seg->buf, &seg->unit); 15042974e916SYuri Benditovich break; 15052974e916SYuri Benditovich case ETH_P_IPV6: 15062974e916SYuri Benditovich virtio_net_rsc_extract_unit6(chain, seg->buf, &seg->unit); 15072974e916SYuri Benditovich break; 15082974e916SYuri Benditovich default: 15092974e916SYuri Benditovich g_assert_not_reached(); 15102974e916SYuri Benditovich } 15112974e916SYuri Benditovich } 15122974e916SYuri Benditovich 15132974e916SYuri Benditovich static int32_t virtio_net_rsc_handle_ack(VirtioNetRscChain *chain, 15142974e916SYuri Benditovich VirtioNetRscSeg *seg, 15152974e916SYuri Benditovich const uint8_t *buf, 15162974e916SYuri Benditovich struct tcp_header *n_tcp, 15172974e916SYuri Benditovich struct tcp_header *o_tcp) 15182974e916SYuri Benditovich { 15192974e916SYuri Benditovich uint32_t nack, oack; 15202974e916SYuri Benditovich uint16_t nwin, owin; 15212974e916SYuri Benditovich 15222974e916SYuri Benditovich nack = htonl(n_tcp->th_ack); 15232974e916SYuri Benditovich nwin = htons(n_tcp->th_win); 15242974e916SYuri Benditovich oack = htonl(o_tcp->th_ack); 15252974e916SYuri Benditovich owin = htons(o_tcp->th_win); 15262974e916SYuri Benditovich 15272974e916SYuri Benditovich if ((nack - oack) >= VIRTIO_NET_MAX_TCP_PAYLOAD) { 15282974e916SYuri Benditovich chain->stat.ack_out_of_win++; 15292974e916SYuri Benditovich return RSC_FINAL; 15302974e916SYuri Benditovich } else if (nack == oack) { 15312974e916SYuri Benditovich /* duplicated ack or window probe */ 15322974e916SYuri Benditovich if (nwin == owin) { 15332974e916SYuri Benditovich /* duplicated ack, add dup ack count due to whql test up to 1 */ 15342974e916SYuri Benditovich chain->stat.dup_ack++; 15352974e916SYuri Benditovich return RSC_FINAL; 15362974e916SYuri Benditovich } else { 15372974e916SYuri Benditovich /* Coalesce window update */ 15382974e916SYuri Benditovich o_tcp->th_win = n_tcp->th_win; 15392974e916SYuri Benditovich chain->stat.win_update++; 15402974e916SYuri Benditovich return RSC_COALESCE; 15412974e916SYuri Benditovich } 15422974e916SYuri Benditovich } else { 15432974e916SYuri Benditovich /* pure ack, go to 'C', finalize*/ 15442974e916SYuri Benditovich chain->stat.pure_ack++; 15452974e916SYuri Benditovich return RSC_FINAL; 15462974e916SYuri Benditovich } 15472974e916SYuri Benditovich } 15482974e916SYuri Benditovich 15492974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain, 15502974e916SYuri Benditovich VirtioNetRscSeg *seg, 15512974e916SYuri Benditovich const uint8_t *buf, 15522974e916SYuri Benditovich VirtioNetRscUnit *n_unit) 15532974e916SYuri Benditovich { 15542974e916SYuri Benditovich void *data; 15552974e916SYuri Benditovich uint16_t o_ip_len; 15562974e916SYuri Benditovich uint32_t nseq, oseq; 15572974e916SYuri Benditovich VirtioNetRscUnit *o_unit; 15582974e916SYuri Benditovich 15592974e916SYuri Benditovich o_unit = &seg->unit; 15602974e916SYuri Benditovich o_ip_len = htons(*o_unit->ip_plen); 15612974e916SYuri Benditovich nseq = htonl(n_unit->tcp->th_seq); 15622974e916SYuri Benditovich oseq = htonl(o_unit->tcp->th_seq); 15632974e916SYuri Benditovich 15642974e916SYuri Benditovich /* out of order or retransmitted. */ 15652974e916SYuri Benditovich if ((nseq - oseq) > VIRTIO_NET_MAX_TCP_PAYLOAD) { 15662974e916SYuri Benditovich chain->stat.data_out_of_win++; 15672974e916SYuri Benditovich return RSC_FINAL; 15682974e916SYuri Benditovich } 15692974e916SYuri Benditovich 15702974e916SYuri Benditovich data = ((uint8_t *)n_unit->tcp) + n_unit->tcp_hdrlen; 15712974e916SYuri Benditovich if (nseq == oseq) { 15722974e916SYuri Benditovich if ((o_unit->payload == 0) && n_unit->payload) { 15732974e916SYuri Benditovich /* From no payload to payload, normal case, not a dup ack or etc */ 15742974e916SYuri Benditovich chain->stat.data_after_pure_ack++; 15752974e916SYuri Benditovich goto coalesce; 15762974e916SYuri Benditovich } else { 15772974e916SYuri Benditovich return virtio_net_rsc_handle_ack(chain, seg, buf, 15782974e916SYuri Benditovich n_unit->tcp, o_unit->tcp); 15792974e916SYuri Benditovich } 15802974e916SYuri Benditovich } else if ((nseq - oseq) != o_unit->payload) { 15812974e916SYuri Benditovich /* Not a consistent packet, out of order */ 15822974e916SYuri Benditovich chain->stat.data_out_of_order++; 15832974e916SYuri Benditovich return RSC_FINAL; 15842974e916SYuri Benditovich } else { 15852974e916SYuri Benditovich coalesce: 15862974e916SYuri Benditovich if ((o_ip_len + n_unit->payload) > chain->max_payload) { 15872974e916SYuri Benditovich chain->stat.over_size++; 15882974e916SYuri Benditovich return RSC_FINAL; 15892974e916SYuri Benditovich } 15902974e916SYuri Benditovich 15912974e916SYuri Benditovich /* Here comes the right data, the payload length in v4/v6 is different, 15922974e916SYuri Benditovich so use the field value to update and record the new data len */ 15932974e916SYuri Benditovich o_unit->payload += n_unit->payload; /* update new data len */ 15942974e916SYuri Benditovich 15952974e916SYuri Benditovich /* update field in ip header */ 15962974e916SYuri Benditovich *o_unit->ip_plen = htons(o_ip_len + n_unit->payload); 15972974e916SYuri Benditovich 15982974e916SYuri Benditovich /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced 15992974e916SYuri Benditovich for windows guest, while this may change the behavior for linux 16002974e916SYuri Benditovich guest (only if it uses RSC feature). */ 16012974e916SYuri Benditovich o_unit->tcp->th_offset_flags = n_unit->tcp->th_offset_flags; 16022974e916SYuri Benditovich 16032974e916SYuri Benditovich o_unit->tcp->th_ack = n_unit->tcp->th_ack; 16042974e916SYuri Benditovich o_unit->tcp->th_win = n_unit->tcp->th_win; 16052974e916SYuri Benditovich 16062974e916SYuri Benditovich memmove(seg->buf + seg->size, data, n_unit->payload); 16072974e916SYuri Benditovich seg->size += n_unit->payload; 16082974e916SYuri Benditovich seg->packets++; 16092974e916SYuri Benditovich chain->stat.coalesced++; 16102974e916SYuri Benditovich return RSC_COALESCE; 16112974e916SYuri Benditovich } 16122974e916SYuri Benditovich } 16132974e916SYuri Benditovich 16142974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce4(VirtioNetRscChain *chain, 16152974e916SYuri Benditovich VirtioNetRscSeg *seg, 16162974e916SYuri Benditovich const uint8_t *buf, size_t size, 16172974e916SYuri Benditovich VirtioNetRscUnit *unit) 16182974e916SYuri Benditovich { 16192974e916SYuri Benditovich struct ip_header *ip1, *ip2; 16202974e916SYuri Benditovich 16212974e916SYuri Benditovich ip1 = (struct ip_header *)(unit->ip); 16222974e916SYuri Benditovich ip2 = (struct ip_header *)(seg->unit.ip); 16232974e916SYuri Benditovich if ((ip1->ip_src ^ ip2->ip_src) || (ip1->ip_dst ^ ip2->ip_dst) 16242974e916SYuri Benditovich || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport) 16252974e916SYuri Benditovich || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) { 16262974e916SYuri Benditovich chain->stat.no_match++; 16272974e916SYuri Benditovich return RSC_NO_MATCH; 16282974e916SYuri Benditovich } 16292974e916SYuri Benditovich 16302974e916SYuri Benditovich return virtio_net_rsc_coalesce_data(chain, seg, buf, unit); 16312974e916SYuri Benditovich } 16322974e916SYuri Benditovich 16332974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce6(VirtioNetRscChain *chain, 16342974e916SYuri Benditovich VirtioNetRscSeg *seg, 16352974e916SYuri Benditovich const uint8_t *buf, size_t size, 16362974e916SYuri Benditovich VirtioNetRscUnit *unit) 16372974e916SYuri Benditovich { 16382974e916SYuri Benditovich struct ip6_header *ip1, *ip2; 16392974e916SYuri Benditovich 16402974e916SYuri Benditovich ip1 = (struct ip6_header *)(unit->ip); 16412974e916SYuri Benditovich ip2 = (struct ip6_header *)(seg->unit.ip); 16422974e916SYuri Benditovich if (memcmp(&ip1->ip6_src, &ip2->ip6_src, sizeof(struct in6_address)) 16432974e916SYuri Benditovich || memcmp(&ip1->ip6_dst, &ip2->ip6_dst, sizeof(struct in6_address)) 16442974e916SYuri Benditovich || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport) 16452974e916SYuri Benditovich || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) { 16462974e916SYuri Benditovich chain->stat.no_match++; 16472974e916SYuri Benditovich return RSC_NO_MATCH; 16482974e916SYuri Benditovich } 16492974e916SYuri Benditovich 16502974e916SYuri Benditovich return virtio_net_rsc_coalesce_data(chain, seg, buf, unit); 16512974e916SYuri Benditovich } 16522974e916SYuri Benditovich 16532974e916SYuri Benditovich /* Packets with 'SYN' should bypass, other flag should be sent after drain 16542974e916SYuri Benditovich * to prevent out of order */ 16552974e916SYuri Benditovich static int virtio_net_rsc_tcp_ctrl_check(VirtioNetRscChain *chain, 16562974e916SYuri Benditovich struct tcp_header *tcp) 16572974e916SYuri Benditovich { 16582974e916SYuri Benditovich uint16_t tcp_hdr; 16592974e916SYuri Benditovich uint16_t tcp_flag; 16602974e916SYuri Benditovich 16612974e916SYuri Benditovich tcp_flag = htons(tcp->th_offset_flags); 16622974e916SYuri Benditovich tcp_hdr = (tcp_flag & VIRTIO_NET_TCP_HDR_LENGTH) >> 10; 16632974e916SYuri Benditovich tcp_flag &= VIRTIO_NET_TCP_FLAG; 16642974e916SYuri Benditovich tcp_flag = htons(tcp->th_offset_flags) & 0x3F; 16652974e916SYuri Benditovich if (tcp_flag & TH_SYN) { 16662974e916SYuri Benditovich chain->stat.tcp_syn++; 16672974e916SYuri Benditovich return RSC_BYPASS; 16682974e916SYuri Benditovich } 16692974e916SYuri Benditovich 16702974e916SYuri Benditovich if (tcp_flag & (TH_FIN | TH_URG | TH_RST | TH_ECE | TH_CWR)) { 16712974e916SYuri Benditovich chain->stat.tcp_ctrl_drain++; 16722974e916SYuri Benditovich return RSC_FINAL; 16732974e916SYuri Benditovich } 16742974e916SYuri Benditovich 16752974e916SYuri Benditovich if (tcp_hdr > sizeof(struct tcp_header)) { 16762974e916SYuri Benditovich chain->stat.tcp_all_opt++; 16772974e916SYuri Benditovich return RSC_FINAL; 16782974e916SYuri Benditovich } 16792974e916SYuri Benditovich 16802974e916SYuri Benditovich return RSC_CANDIDATE; 16812974e916SYuri Benditovich } 16822974e916SYuri Benditovich 16832974e916SYuri Benditovich static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain, 16842974e916SYuri Benditovich NetClientState *nc, 16852974e916SYuri Benditovich const uint8_t *buf, size_t size, 16862974e916SYuri Benditovich VirtioNetRscUnit *unit) 16872974e916SYuri Benditovich { 16882974e916SYuri Benditovich int ret; 16892974e916SYuri Benditovich VirtioNetRscSeg *seg, *nseg; 16902974e916SYuri Benditovich 16912974e916SYuri Benditovich if (QTAILQ_EMPTY(&chain->buffers)) { 16922974e916SYuri Benditovich chain->stat.empty_cache++; 16932974e916SYuri Benditovich virtio_net_rsc_cache_buf(chain, nc, buf, size); 16942974e916SYuri Benditovich timer_mod(chain->drain_timer, 16952974e916SYuri Benditovich qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout); 16962974e916SYuri Benditovich return size; 16972974e916SYuri Benditovich } 16982974e916SYuri Benditovich 16992974e916SYuri Benditovich QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) { 17002974e916SYuri Benditovich if (chain->proto == ETH_P_IP) { 17012974e916SYuri Benditovich ret = virtio_net_rsc_coalesce4(chain, seg, buf, size, unit); 17022974e916SYuri Benditovich } else { 17032974e916SYuri Benditovich ret = virtio_net_rsc_coalesce6(chain, seg, buf, size, unit); 17042974e916SYuri Benditovich } 17052974e916SYuri Benditovich 17062974e916SYuri Benditovich if (ret == RSC_FINAL) { 17072974e916SYuri Benditovich if (virtio_net_rsc_drain_seg(chain, seg) == 0) { 17082974e916SYuri Benditovich /* Send failed */ 17092974e916SYuri Benditovich chain->stat.final_failed++; 17102974e916SYuri Benditovich return 0; 17112974e916SYuri Benditovich } 17122974e916SYuri Benditovich 17132974e916SYuri Benditovich /* Send current packet */ 17142974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 17152974e916SYuri Benditovich } else if (ret == RSC_NO_MATCH) { 17162974e916SYuri Benditovich continue; 17172974e916SYuri Benditovich } else { 17182974e916SYuri Benditovich /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */ 17192974e916SYuri Benditovich seg->is_coalesced = 1; 17202974e916SYuri Benditovich return size; 17212974e916SYuri Benditovich } 17222974e916SYuri Benditovich } 17232974e916SYuri Benditovich 17242974e916SYuri Benditovich chain->stat.no_match_cache++; 17252974e916SYuri Benditovich virtio_net_rsc_cache_buf(chain, nc, buf, size); 17262974e916SYuri Benditovich return size; 17272974e916SYuri Benditovich } 17282974e916SYuri Benditovich 17292974e916SYuri Benditovich /* Drain a connection data, this is to avoid out of order segments */ 17302974e916SYuri Benditovich static size_t virtio_net_rsc_drain_flow(VirtioNetRscChain *chain, 17312974e916SYuri Benditovich NetClientState *nc, 17322974e916SYuri Benditovich const uint8_t *buf, size_t size, 17332974e916SYuri Benditovich uint16_t ip_start, uint16_t ip_size, 17342974e916SYuri Benditovich uint16_t tcp_port) 17352974e916SYuri Benditovich { 17362974e916SYuri Benditovich VirtioNetRscSeg *seg, *nseg; 17372974e916SYuri Benditovich uint32_t ppair1, ppair2; 17382974e916SYuri Benditovich 17392974e916SYuri Benditovich ppair1 = *(uint32_t *)(buf + tcp_port); 17402974e916SYuri Benditovich QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) { 17412974e916SYuri Benditovich ppair2 = *(uint32_t *)(seg->buf + tcp_port); 17422974e916SYuri Benditovich if (memcmp(buf + ip_start, seg->buf + ip_start, ip_size) 17432974e916SYuri Benditovich || (ppair1 != ppair2)) { 17442974e916SYuri Benditovich continue; 17452974e916SYuri Benditovich } 17462974e916SYuri Benditovich if (virtio_net_rsc_drain_seg(chain, seg) == 0) { 17472974e916SYuri Benditovich chain->stat.drain_failed++; 17482974e916SYuri Benditovich } 17492974e916SYuri Benditovich 17502974e916SYuri Benditovich break; 17512974e916SYuri Benditovich } 17522974e916SYuri Benditovich 17532974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 17542974e916SYuri Benditovich } 17552974e916SYuri Benditovich 17562974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check4(VirtioNetRscChain *chain, 17572974e916SYuri Benditovich struct ip_header *ip, 17582974e916SYuri Benditovich const uint8_t *buf, size_t size) 17592974e916SYuri Benditovich { 17602974e916SYuri Benditovich uint16_t ip_len; 17612974e916SYuri Benditovich 17622974e916SYuri Benditovich /* Not an ipv4 packet */ 17632974e916SYuri Benditovich if (((ip->ip_ver_len & 0xF0) >> 4) != IP_HEADER_VERSION_4) { 17642974e916SYuri Benditovich chain->stat.ip_option++; 17652974e916SYuri Benditovich return RSC_BYPASS; 17662974e916SYuri Benditovich } 17672974e916SYuri Benditovich 17682974e916SYuri Benditovich /* Don't handle packets with ip option */ 17692974e916SYuri Benditovich if ((ip->ip_ver_len & 0xF) != VIRTIO_NET_IP4_HEADER_LENGTH) { 17702974e916SYuri Benditovich chain->stat.ip_option++; 17712974e916SYuri Benditovich return RSC_BYPASS; 17722974e916SYuri Benditovich } 17732974e916SYuri Benditovich 17742974e916SYuri Benditovich if (ip->ip_p != IPPROTO_TCP) { 17752974e916SYuri Benditovich chain->stat.bypass_not_tcp++; 17762974e916SYuri Benditovich return RSC_BYPASS; 17772974e916SYuri Benditovich } 17782974e916SYuri Benditovich 17792974e916SYuri Benditovich /* Don't handle packets with ip fragment */ 17802974e916SYuri Benditovich if (!(htons(ip->ip_off) & IP_DF)) { 17812974e916SYuri Benditovich chain->stat.ip_frag++; 17822974e916SYuri Benditovich return RSC_BYPASS; 17832974e916SYuri Benditovich } 17842974e916SYuri Benditovich 17852974e916SYuri Benditovich /* Don't handle packets with ecn flag */ 17862974e916SYuri Benditovich if (IPTOS_ECN(ip->ip_tos)) { 17872974e916SYuri Benditovich chain->stat.ip_ecn++; 17882974e916SYuri Benditovich return RSC_BYPASS; 17892974e916SYuri Benditovich } 17902974e916SYuri Benditovich 17912974e916SYuri Benditovich ip_len = htons(ip->ip_len); 17922974e916SYuri Benditovich if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header)) 17932974e916SYuri Benditovich || ip_len > (size - chain->n->guest_hdr_len - 17942974e916SYuri Benditovich sizeof(struct eth_header))) { 17952974e916SYuri Benditovich chain->stat.ip_hacked++; 17962974e916SYuri Benditovich return RSC_BYPASS; 17972974e916SYuri Benditovich } 17982974e916SYuri Benditovich 17992974e916SYuri Benditovich return RSC_CANDIDATE; 18002974e916SYuri Benditovich } 18012974e916SYuri Benditovich 18022974e916SYuri Benditovich static size_t virtio_net_rsc_receive4(VirtioNetRscChain *chain, 18032974e916SYuri Benditovich NetClientState *nc, 18042974e916SYuri Benditovich const uint8_t *buf, size_t size) 18052974e916SYuri Benditovich { 18062974e916SYuri Benditovich int32_t ret; 18072974e916SYuri Benditovich uint16_t hdr_len; 18082974e916SYuri Benditovich VirtioNetRscUnit unit; 18092974e916SYuri Benditovich 18102974e916SYuri Benditovich hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len; 18112974e916SYuri Benditovich 18122974e916SYuri Benditovich if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header) 18132974e916SYuri Benditovich + sizeof(struct tcp_header))) { 18142974e916SYuri Benditovich chain->stat.bypass_not_tcp++; 18152974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 18162974e916SYuri Benditovich } 18172974e916SYuri Benditovich 18182974e916SYuri Benditovich virtio_net_rsc_extract_unit4(chain, buf, &unit); 18192974e916SYuri Benditovich if (virtio_net_rsc_sanity_check4(chain, unit.ip, buf, size) 18202974e916SYuri Benditovich != RSC_CANDIDATE) { 18212974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 18222974e916SYuri Benditovich } 18232974e916SYuri Benditovich 18242974e916SYuri Benditovich ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp); 18252974e916SYuri Benditovich if (ret == RSC_BYPASS) { 18262974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 18272974e916SYuri Benditovich } else if (ret == RSC_FINAL) { 18282974e916SYuri Benditovich return virtio_net_rsc_drain_flow(chain, nc, buf, size, 18292974e916SYuri Benditovich ((hdr_len + sizeof(struct eth_header)) + 12), 18302974e916SYuri Benditovich VIRTIO_NET_IP4_ADDR_SIZE, 18312974e916SYuri Benditovich hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header)); 18322974e916SYuri Benditovich } 18332974e916SYuri Benditovich 18342974e916SYuri Benditovich return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit); 18352974e916SYuri Benditovich } 18362974e916SYuri Benditovich 18372974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check6(VirtioNetRscChain *chain, 18382974e916SYuri Benditovich struct ip6_header *ip6, 18392974e916SYuri Benditovich const uint8_t *buf, size_t size) 18402974e916SYuri Benditovich { 18412974e916SYuri Benditovich uint16_t ip_len; 18422974e916SYuri Benditovich 18432974e916SYuri Benditovich if (((ip6->ip6_ctlun.ip6_un1.ip6_un1_flow & 0xF0) >> 4) 18442974e916SYuri Benditovich != IP_HEADER_VERSION_6) { 18452974e916SYuri Benditovich return RSC_BYPASS; 18462974e916SYuri Benditovich } 18472974e916SYuri Benditovich 18482974e916SYuri Benditovich /* Both option and protocol is checked in this */ 18492974e916SYuri Benditovich if (ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) { 18502974e916SYuri Benditovich chain->stat.bypass_not_tcp++; 18512974e916SYuri Benditovich return RSC_BYPASS; 18522974e916SYuri Benditovich } 18532974e916SYuri Benditovich 18542974e916SYuri Benditovich ip_len = htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); 18552974e916SYuri Benditovich if (ip_len < sizeof(struct tcp_header) || 18562974e916SYuri Benditovich ip_len > (size - chain->n->guest_hdr_len - sizeof(struct eth_header) 18572974e916SYuri Benditovich - sizeof(struct ip6_header))) { 18582974e916SYuri Benditovich chain->stat.ip_hacked++; 18592974e916SYuri Benditovich return RSC_BYPASS; 18602974e916SYuri Benditovich } 18612974e916SYuri Benditovich 18622974e916SYuri Benditovich /* Don't handle packets with ecn flag */ 18632974e916SYuri Benditovich if (IP6_ECN(ip6->ip6_ctlun.ip6_un3.ip6_un3_ecn)) { 18642974e916SYuri Benditovich chain->stat.ip_ecn++; 18652974e916SYuri Benditovich return RSC_BYPASS; 18662974e916SYuri Benditovich } 18672974e916SYuri Benditovich 18682974e916SYuri Benditovich return RSC_CANDIDATE; 18692974e916SYuri Benditovich } 18702974e916SYuri Benditovich 18712974e916SYuri Benditovich static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc, 18722974e916SYuri Benditovich const uint8_t *buf, size_t size) 18732974e916SYuri Benditovich { 18742974e916SYuri Benditovich int32_t ret; 18752974e916SYuri Benditovich uint16_t hdr_len; 18762974e916SYuri Benditovich VirtioNetRscChain *chain; 18772974e916SYuri Benditovich VirtioNetRscUnit unit; 18782974e916SYuri Benditovich 18792974e916SYuri Benditovich chain = (VirtioNetRscChain *)opq; 18802974e916SYuri Benditovich hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len; 18812974e916SYuri Benditovich 18822974e916SYuri Benditovich if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header) 18832974e916SYuri Benditovich + sizeof(tcp_header))) { 18842974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 18852974e916SYuri Benditovich } 18862974e916SYuri Benditovich 18872974e916SYuri Benditovich virtio_net_rsc_extract_unit6(chain, buf, &unit); 18882974e916SYuri Benditovich if (RSC_CANDIDATE != virtio_net_rsc_sanity_check6(chain, 18892974e916SYuri Benditovich unit.ip, buf, size)) { 18902974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 18912974e916SYuri Benditovich } 18922974e916SYuri Benditovich 18932974e916SYuri Benditovich ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp); 18942974e916SYuri Benditovich if (ret == RSC_BYPASS) { 18952974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 18962974e916SYuri Benditovich } else if (ret == RSC_FINAL) { 18972974e916SYuri Benditovich return virtio_net_rsc_drain_flow(chain, nc, buf, size, 18982974e916SYuri Benditovich ((hdr_len + sizeof(struct eth_header)) + 8), 18992974e916SYuri Benditovich VIRTIO_NET_IP6_ADDR_SIZE, 19002974e916SYuri Benditovich hdr_len + sizeof(struct eth_header) 19012974e916SYuri Benditovich + sizeof(struct ip6_header)); 19022974e916SYuri Benditovich } 19032974e916SYuri Benditovich 19042974e916SYuri Benditovich return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit); 19052974e916SYuri Benditovich } 19062974e916SYuri Benditovich 19072974e916SYuri Benditovich static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n, 19082974e916SYuri Benditovich NetClientState *nc, 19092974e916SYuri Benditovich uint16_t proto) 19102974e916SYuri Benditovich { 19112974e916SYuri Benditovich VirtioNetRscChain *chain; 19122974e916SYuri Benditovich 19132974e916SYuri Benditovich if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) { 19142974e916SYuri Benditovich return NULL; 19152974e916SYuri Benditovich } 19162974e916SYuri Benditovich 19172974e916SYuri Benditovich QTAILQ_FOREACH(chain, &n->rsc_chains, next) { 19182974e916SYuri Benditovich if (chain->proto == proto) { 19192974e916SYuri Benditovich return chain; 19202974e916SYuri Benditovich } 19212974e916SYuri Benditovich } 19222974e916SYuri Benditovich 19232974e916SYuri Benditovich chain = g_malloc(sizeof(*chain)); 19242974e916SYuri Benditovich chain->n = n; 19252974e916SYuri Benditovich chain->proto = proto; 19262974e916SYuri Benditovich if (proto == (uint16_t)ETH_P_IP) { 19272974e916SYuri Benditovich chain->max_payload = VIRTIO_NET_MAX_IP4_PAYLOAD; 19282974e916SYuri Benditovich chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; 19292974e916SYuri Benditovich } else { 19302974e916SYuri Benditovich chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD; 19312974e916SYuri Benditovich chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; 19322974e916SYuri Benditovich } 19332974e916SYuri Benditovich chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST, 19342974e916SYuri Benditovich virtio_net_rsc_purge, chain); 19352974e916SYuri Benditovich memset(&chain->stat, 0, sizeof(chain->stat)); 19362974e916SYuri Benditovich 19372974e916SYuri Benditovich QTAILQ_INIT(&chain->buffers); 19382974e916SYuri Benditovich QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next); 19392974e916SYuri Benditovich 19402974e916SYuri Benditovich return chain; 19412974e916SYuri Benditovich } 19422974e916SYuri Benditovich 19432974e916SYuri Benditovich static ssize_t virtio_net_rsc_receive(NetClientState *nc, 19442974e916SYuri Benditovich const uint8_t *buf, 19452974e916SYuri Benditovich size_t size) 19462974e916SYuri Benditovich { 19472974e916SYuri Benditovich uint16_t proto; 19482974e916SYuri Benditovich VirtioNetRscChain *chain; 19492974e916SYuri Benditovich struct eth_header *eth; 19502974e916SYuri Benditovich VirtIONet *n; 19512974e916SYuri Benditovich 19522974e916SYuri Benditovich n = qemu_get_nic_opaque(nc); 19532974e916SYuri Benditovich if (size < (n->host_hdr_len + sizeof(struct eth_header))) { 19542974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 19552974e916SYuri Benditovich } 19562974e916SYuri Benditovich 19572974e916SYuri Benditovich eth = (struct eth_header *)(buf + n->guest_hdr_len); 19582974e916SYuri Benditovich proto = htons(eth->h_proto); 19592974e916SYuri Benditovich 19602974e916SYuri Benditovich chain = virtio_net_rsc_lookup_chain(n, nc, proto); 19612974e916SYuri Benditovich if (chain) { 19622974e916SYuri Benditovich chain->stat.received++; 19632974e916SYuri Benditovich if (proto == (uint16_t)ETH_P_IP && n->rsc4_enabled) { 19642974e916SYuri Benditovich return virtio_net_rsc_receive4(chain, nc, buf, size); 19652974e916SYuri Benditovich } else if (proto == (uint16_t)ETH_P_IPV6 && n->rsc6_enabled) { 19662974e916SYuri Benditovich return virtio_net_rsc_receive6(chain, nc, buf, size); 19672974e916SYuri Benditovich } 19682974e916SYuri Benditovich } 19692974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 19702974e916SYuri Benditovich } 19712974e916SYuri Benditovich 19722974e916SYuri Benditovich static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, 19732974e916SYuri Benditovich size_t size) 19742974e916SYuri Benditovich { 19752974e916SYuri Benditovich VirtIONet *n = qemu_get_nic_opaque(nc); 19762974e916SYuri Benditovich if ((n->rsc4_enabled || n->rsc6_enabled)) { 19772974e916SYuri Benditovich return virtio_net_rsc_receive(nc, buf, size); 19782974e916SYuri Benditovich } else { 19792974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 19802974e916SYuri Benditovich } 19812974e916SYuri Benditovich } 19822974e916SYuri Benditovich 19836e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q); 19846e790746SPaolo Bonzini 19856e790746SPaolo Bonzini static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) 19866e790746SPaolo Bonzini { 19876e790746SPaolo Bonzini VirtIONet *n = qemu_get_nic_opaque(nc); 19886e790746SPaolo Bonzini VirtIONetQueue *q = virtio_net_get_subqueue(nc); 198917a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 19906e790746SPaolo Bonzini 199151b19ebeSPaolo Bonzini virtqueue_push(q->tx_vq, q->async_tx.elem, 0); 199217a0ca55SKONRAD Frederic virtio_notify(vdev, q->tx_vq); 19936e790746SPaolo Bonzini 199451b19ebeSPaolo Bonzini g_free(q->async_tx.elem); 199551b19ebeSPaolo Bonzini q->async_tx.elem = NULL; 19966e790746SPaolo Bonzini 19976e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 1); 19986e790746SPaolo Bonzini virtio_net_flush_tx(q); 19996e790746SPaolo Bonzini } 20006e790746SPaolo Bonzini 20016e790746SPaolo Bonzini /* TX */ 20026e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q) 20036e790746SPaolo Bonzini { 20046e790746SPaolo Bonzini VirtIONet *n = q->n; 200517a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 200651b19ebeSPaolo Bonzini VirtQueueElement *elem; 20076e790746SPaolo Bonzini int32_t num_packets = 0; 20086e790746SPaolo Bonzini int queue_index = vq2q(virtio_get_queue_index(q->tx_vq)); 200917a0ca55SKONRAD Frederic if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { 20106e790746SPaolo Bonzini return num_packets; 20116e790746SPaolo Bonzini } 20126e790746SPaolo Bonzini 201351b19ebeSPaolo Bonzini if (q->async_tx.elem) { 20146e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 0); 20156e790746SPaolo Bonzini return num_packets; 20166e790746SPaolo Bonzini } 20176e790746SPaolo Bonzini 201851b19ebeSPaolo Bonzini for (;;) { 2019bd89dd98SJason Wang ssize_t ret; 202051b19ebeSPaolo Bonzini unsigned int out_num; 202151b19ebeSPaolo Bonzini struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg; 2022feb93f36SJason Wang struct virtio_net_hdr_mrg_rxbuf mhdr; 20236e790746SPaolo Bonzini 202451b19ebeSPaolo Bonzini elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement)); 202551b19ebeSPaolo Bonzini if (!elem) { 202651b19ebeSPaolo Bonzini break; 202751b19ebeSPaolo Bonzini } 202851b19ebeSPaolo Bonzini 202951b19ebeSPaolo Bonzini out_num = elem->out_num; 203051b19ebeSPaolo Bonzini out_sg = elem->out_sg; 20316e790746SPaolo Bonzini if (out_num < 1) { 2032fa5e56c2SGreg Kurz virtio_error(vdev, "virtio-net header not in first element"); 2033fa5e56c2SGreg Kurz virtqueue_detach_element(q->tx_vq, elem, 0); 2034fa5e56c2SGreg Kurz g_free(elem); 2035fa5e56c2SGreg Kurz return -EINVAL; 20366e790746SPaolo Bonzini } 20376e790746SPaolo Bonzini 2038032a74a1SCédric Le Goater if (n->has_vnet_hdr) { 2039feb93f36SJason Wang if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) < 2040feb93f36SJason Wang n->guest_hdr_len) { 2041fa5e56c2SGreg Kurz virtio_error(vdev, "virtio-net header incorrect"); 2042fa5e56c2SGreg Kurz virtqueue_detach_element(q->tx_vq, elem, 0); 2043fa5e56c2SGreg Kurz g_free(elem); 2044fa5e56c2SGreg Kurz return -EINVAL; 2045032a74a1SCédric Le Goater } 20461bfa316cSGreg Kurz if (n->needs_vnet_hdr_swap) { 2047feb93f36SJason Wang virtio_net_hdr_swap(vdev, (void *) &mhdr); 2048feb93f36SJason Wang sg2[0].iov_base = &mhdr; 2049feb93f36SJason Wang sg2[0].iov_len = n->guest_hdr_len; 2050feb93f36SJason Wang out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1, 2051feb93f36SJason Wang out_sg, out_num, 2052feb93f36SJason Wang n->guest_hdr_len, -1); 2053feb93f36SJason Wang if (out_num == VIRTQUEUE_MAX_SIZE) { 2054feb93f36SJason Wang goto drop; 2055032a74a1SCédric Le Goater } 2056feb93f36SJason Wang out_num += 1; 2057feb93f36SJason Wang out_sg = sg2; 2058feb93f36SJason Wang } 2059feb93f36SJason Wang } 20606e790746SPaolo Bonzini /* 20616e790746SPaolo Bonzini * If host wants to see the guest header as is, we can 20626e790746SPaolo Bonzini * pass it on unchanged. Otherwise, copy just the parts 20636e790746SPaolo Bonzini * that host is interested in. 20646e790746SPaolo Bonzini */ 20656e790746SPaolo Bonzini assert(n->host_hdr_len <= n->guest_hdr_len); 20666e790746SPaolo Bonzini if (n->host_hdr_len != n->guest_hdr_len) { 20676e790746SPaolo Bonzini unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg), 20686e790746SPaolo Bonzini out_sg, out_num, 20696e790746SPaolo Bonzini 0, n->host_hdr_len); 20706e790746SPaolo Bonzini sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num, 20716e790746SPaolo Bonzini out_sg, out_num, 20726e790746SPaolo Bonzini n->guest_hdr_len, -1); 20736e790746SPaolo Bonzini out_num = sg_num; 20746e790746SPaolo Bonzini out_sg = sg; 20756e790746SPaolo Bonzini } 20766e790746SPaolo Bonzini 20776e790746SPaolo Bonzini ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index), 20786e790746SPaolo Bonzini out_sg, out_num, virtio_net_tx_complete); 20796e790746SPaolo Bonzini if (ret == 0) { 20806e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 0); 20816e790746SPaolo Bonzini q->async_tx.elem = elem; 20826e790746SPaolo Bonzini return -EBUSY; 20836e790746SPaolo Bonzini } 20846e790746SPaolo Bonzini 2085feb93f36SJason Wang drop: 208651b19ebeSPaolo Bonzini virtqueue_push(q->tx_vq, elem, 0); 208717a0ca55SKONRAD Frederic virtio_notify(vdev, q->tx_vq); 208851b19ebeSPaolo Bonzini g_free(elem); 20896e790746SPaolo Bonzini 20906e790746SPaolo Bonzini if (++num_packets >= n->tx_burst) { 20916e790746SPaolo Bonzini break; 20926e790746SPaolo Bonzini } 20936e790746SPaolo Bonzini } 20946e790746SPaolo Bonzini return num_packets; 20956e790746SPaolo Bonzini } 20966e790746SPaolo Bonzini 20976e790746SPaolo Bonzini static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) 20986e790746SPaolo Bonzini { 209917a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 21006e790746SPaolo Bonzini VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; 21016e790746SPaolo Bonzini 2102283e2c2aSYuri Benditovich if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) { 2103283e2c2aSYuri Benditovich virtio_net_drop_tx_queue_data(vdev, vq); 2104283e2c2aSYuri Benditovich return; 2105283e2c2aSYuri Benditovich } 2106283e2c2aSYuri Benditovich 21076e790746SPaolo Bonzini /* This happens when device was stopped but VCPU wasn't. */ 210817a0ca55SKONRAD Frederic if (!vdev->vm_running) { 21096e790746SPaolo Bonzini q->tx_waiting = 1; 21106e790746SPaolo Bonzini return; 21116e790746SPaolo Bonzini } 21126e790746SPaolo Bonzini 21136e790746SPaolo Bonzini if (q->tx_waiting) { 21146e790746SPaolo Bonzini virtio_queue_set_notification(vq, 1); 2115bc72ad67SAlex Bligh timer_del(q->tx_timer); 21166e790746SPaolo Bonzini q->tx_waiting = 0; 2117fa5e56c2SGreg Kurz if (virtio_net_flush_tx(q) == -EINVAL) { 2118fa5e56c2SGreg Kurz return; 2119fa5e56c2SGreg Kurz } 21206e790746SPaolo Bonzini } else { 2121bc72ad67SAlex Bligh timer_mod(q->tx_timer, 2122bc72ad67SAlex Bligh qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); 21236e790746SPaolo Bonzini q->tx_waiting = 1; 21246e790746SPaolo Bonzini virtio_queue_set_notification(vq, 0); 21256e790746SPaolo Bonzini } 21266e790746SPaolo Bonzini } 21276e790746SPaolo Bonzini 21286e790746SPaolo Bonzini static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) 21296e790746SPaolo Bonzini { 213017a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 21316e790746SPaolo Bonzini VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; 21326e790746SPaolo Bonzini 2133283e2c2aSYuri Benditovich if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) { 2134283e2c2aSYuri Benditovich virtio_net_drop_tx_queue_data(vdev, vq); 2135283e2c2aSYuri Benditovich return; 2136283e2c2aSYuri Benditovich } 2137283e2c2aSYuri Benditovich 21386e790746SPaolo Bonzini if (unlikely(q->tx_waiting)) { 21396e790746SPaolo Bonzini return; 21406e790746SPaolo Bonzini } 21416e790746SPaolo Bonzini q->tx_waiting = 1; 21426e790746SPaolo Bonzini /* This happens when device was stopped but VCPU wasn't. */ 214317a0ca55SKONRAD Frederic if (!vdev->vm_running) { 21446e790746SPaolo Bonzini return; 21456e790746SPaolo Bonzini } 21466e790746SPaolo Bonzini virtio_queue_set_notification(vq, 0); 21476e790746SPaolo Bonzini qemu_bh_schedule(q->tx_bh); 21486e790746SPaolo Bonzini } 21496e790746SPaolo Bonzini 21506e790746SPaolo Bonzini static void virtio_net_tx_timer(void *opaque) 21516e790746SPaolo Bonzini { 21526e790746SPaolo Bonzini VirtIONetQueue *q = opaque; 21536e790746SPaolo Bonzini VirtIONet *n = q->n; 215417a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 2155e8bcf842SMichael S. Tsirkin /* This happens when device was stopped but BH wasn't. */ 2156e8bcf842SMichael S. Tsirkin if (!vdev->vm_running) { 2157e8bcf842SMichael S. Tsirkin /* Make sure tx waiting is set, so we'll run when restarted. */ 2158e8bcf842SMichael S. Tsirkin assert(q->tx_waiting); 2159e8bcf842SMichael S. Tsirkin return; 2160e8bcf842SMichael S. Tsirkin } 21616e790746SPaolo Bonzini 21626e790746SPaolo Bonzini q->tx_waiting = 0; 21636e790746SPaolo Bonzini 21646e790746SPaolo Bonzini /* Just in case the driver is not ready on more */ 216517a0ca55SKONRAD Frederic if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { 21666e790746SPaolo Bonzini return; 216717a0ca55SKONRAD Frederic } 21686e790746SPaolo Bonzini 21696e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 1); 21706e790746SPaolo Bonzini virtio_net_flush_tx(q); 21716e790746SPaolo Bonzini } 21726e790746SPaolo Bonzini 21736e790746SPaolo Bonzini static void virtio_net_tx_bh(void *opaque) 21746e790746SPaolo Bonzini { 21756e790746SPaolo Bonzini VirtIONetQueue *q = opaque; 21766e790746SPaolo Bonzini VirtIONet *n = q->n; 217717a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 21786e790746SPaolo Bonzini int32_t ret; 21796e790746SPaolo Bonzini 2180e8bcf842SMichael S. Tsirkin /* This happens when device was stopped but BH wasn't. */ 2181e8bcf842SMichael S. Tsirkin if (!vdev->vm_running) { 2182e8bcf842SMichael S. Tsirkin /* Make sure tx waiting is set, so we'll run when restarted. */ 2183e8bcf842SMichael S. Tsirkin assert(q->tx_waiting); 2184e8bcf842SMichael S. Tsirkin return; 2185e8bcf842SMichael S. Tsirkin } 21866e790746SPaolo Bonzini 21876e790746SPaolo Bonzini q->tx_waiting = 0; 21886e790746SPaolo Bonzini 21896e790746SPaolo Bonzini /* Just in case the driver is not ready on more */ 219017a0ca55SKONRAD Frederic if (unlikely(!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) { 21916e790746SPaolo Bonzini return; 219217a0ca55SKONRAD Frederic } 21936e790746SPaolo Bonzini 21946e790746SPaolo Bonzini ret = virtio_net_flush_tx(q); 2195fa5e56c2SGreg Kurz if (ret == -EBUSY || ret == -EINVAL) { 2196fa5e56c2SGreg Kurz return; /* Notification re-enable handled by tx_complete or device 2197fa5e56c2SGreg Kurz * broken */ 21986e790746SPaolo Bonzini } 21996e790746SPaolo Bonzini 22006e790746SPaolo Bonzini /* If we flush a full burst of packets, assume there are 22016e790746SPaolo Bonzini * more coming and immediately reschedule */ 22026e790746SPaolo Bonzini if (ret >= n->tx_burst) { 22036e790746SPaolo Bonzini qemu_bh_schedule(q->tx_bh); 22046e790746SPaolo Bonzini q->tx_waiting = 1; 22056e790746SPaolo Bonzini return; 22066e790746SPaolo Bonzini } 22076e790746SPaolo Bonzini 22086e790746SPaolo Bonzini /* If less than a full burst, re-enable notification and flush 22096e790746SPaolo Bonzini * anything that may have come in while we weren't looking. If 22106e790746SPaolo Bonzini * we find something, assume the guest is still active and reschedule */ 22116e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 1); 2212fa5e56c2SGreg Kurz ret = virtio_net_flush_tx(q); 2213fa5e56c2SGreg Kurz if (ret == -EINVAL) { 2214fa5e56c2SGreg Kurz return; 2215fa5e56c2SGreg Kurz } else if (ret > 0) { 22166e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 0); 22176e790746SPaolo Bonzini qemu_bh_schedule(q->tx_bh); 22186e790746SPaolo Bonzini q->tx_waiting = 1; 22196e790746SPaolo Bonzini } 22206e790746SPaolo Bonzini } 22216e790746SPaolo Bonzini 2222f9d6dbf0SWen Congyang static void virtio_net_add_queue(VirtIONet *n, int index) 2223f9d6dbf0SWen Congyang { 2224f9d6dbf0SWen Congyang VirtIODevice *vdev = VIRTIO_DEVICE(n); 2225f9d6dbf0SWen Congyang 22261c0fbfa3SMichael S. Tsirkin n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size, 22271c0fbfa3SMichael S. Tsirkin virtio_net_handle_rx); 22289b02e161SWei Wang 2229f9d6dbf0SWen Congyang if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) { 2230f9d6dbf0SWen Congyang n->vqs[index].tx_vq = 22319b02e161SWei Wang virtio_add_queue(vdev, n->net_conf.tx_queue_size, 22329b02e161SWei Wang virtio_net_handle_tx_timer); 2233f9d6dbf0SWen Congyang n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 2234f9d6dbf0SWen Congyang virtio_net_tx_timer, 2235f9d6dbf0SWen Congyang &n->vqs[index]); 2236f9d6dbf0SWen Congyang } else { 2237f9d6dbf0SWen Congyang n->vqs[index].tx_vq = 22389b02e161SWei Wang virtio_add_queue(vdev, n->net_conf.tx_queue_size, 22399b02e161SWei Wang virtio_net_handle_tx_bh); 2240f9d6dbf0SWen Congyang n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]); 2241f9d6dbf0SWen Congyang } 2242f9d6dbf0SWen Congyang 2243f9d6dbf0SWen Congyang n->vqs[index].tx_waiting = 0; 2244f9d6dbf0SWen Congyang n->vqs[index].n = n; 2245f9d6dbf0SWen Congyang } 2246f9d6dbf0SWen Congyang 2247f9d6dbf0SWen Congyang static void virtio_net_del_queue(VirtIONet *n, int index) 2248f9d6dbf0SWen Congyang { 2249f9d6dbf0SWen Congyang VirtIODevice *vdev = VIRTIO_DEVICE(n); 2250f9d6dbf0SWen Congyang VirtIONetQueue *q = &n->vqs[index]; 2251f9d6dbf0SWen Congyang NetClientState *nc = qemu_get_subqueue(n->nic, index); 2252f9d6dbf0SWen Congyang 2253f9d6dbf0SWen Congyang qemu_purge_queued_packets(nc); 2254f9d6dbf0SWen Congyang 2255f9d6dbf0SWen Congyang virtio_del_queue(vdev, index * 2); 2256f9d6dbf0SWen Congyang if (q->tx_timer) { 2257f9d6dbf0SWen Congyang timer_del(q->tx_timer); 2258f9d6dbf0SWen Congyang timer_free(q->tx_timer); 2259f989c30cSYunjian Wang q->tx_timer = NULL; 2260f9d6dbf0SWen Congyang } else { 2261f9d6dbf0SWen Congyang qemu_bh_delete(q->tx_bh); 2262f989c30cSYunjian Wang q->tx_bh = NULL; 2263f9d6dbf0SWen Congyang } 2264f989c30cSYunjian Wang q->tx_waiting = 0; 2265f9d6dbf0SWen Congyang virtio_del_queue(vdev, index * 2 + 1); 2266f9d6dbf0SWen Congyang } 2267f9d6dbf0SWen Congyang 2268f9d6dbf0SWen Congyang static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues) 2269f9d6dbf0SWen Congyang { 2270f9d6dbf0SWen Congyang VirtIODevice *vdev = VIRTIO_DEVICE(n); 2271f9d6dbf0SWen Congyang int old_num_queues = virtio_get_num_queues(vdev); 2272f9d6dbf0SWen Congyang int new_num_queues = new_max_queues * 2 + 1; 2273f9d6dbf0SWen Congyang int i; 2274f9d6dbf0SWen Congyang 2275f9d6dbf0SWen Congyang assert(old_num_queues >= 3); 2276f9d6dbf0SWen Congyang assert(old_num_queues % 2 == 1); 2277f9d6dbf0SWen Congyang 2278f9d6dbf0SWen Congyang if (old_num_queues == new_num_queues) { 2279f9d6dbf0SWen Congyang return; 2280f9d6dbf0SWen Congyang } 2281f9d6dbf0SWen Congyang 2282f9d6dbf0SWen Congyang /* 2283f9d6dbf0SWen Congyang * We always need to remove and add ctrl vq if 2284f9d6dbf0SWen Congyang * old_num_queues != new_num_queues. Remove ctrl_vq first, 228520f86a75SYuval Shaia * and then we only enter one of the following two loops. 2286f9d6dbf0SWen Congyang */ 2287f9d6dbf0SWen Congyang virtio_del_queue(vdev, old_num_queues - 1); 2288f9d6dbf0SWen Congyang 2289f9d6dbf0SWen Congyang for (i = new_num_queues - 1; i < old_num_queues - 1; i += 2) { 2290f9d6dbf0SWen Congyang /* new_num_queues < old_num_queues */ 2291f9d6dbf0SWen Congyang virtio_net_del_queue(n, i / 2); 2292f9d6dbf0SWen Congyang } 2293f9d6dbf0SWen Congyang 2294f9d6dbf0SWen Congyang for (i = old_num_queues - 1; i < new_num_queues - 1; i += 2) { 2295f9d6dbf0SWen Congyang /* new_num_queues > old_num_queues */ 2296f9d6dbf0SWen Congyang virtio_net_add_queue(n, i / 2); 2297f9d6dbf0SWen Congyang } 2298f9d6dbf0SWen Congyang 2299f9d6dbf0SWen Congyang /* add ctrl_vq last */ 2300f9d6dbf0SWen Congyang n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); 2301f9d6dbf0SWen Congyang } 2302f9d6dbf0SWen Congyang 2303ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) 23046e790746SPaolo Bonzini { 2305f9d6dbf0SWen Congyang int max = multiqueue ? n->max_queues : 1; 2306f9d6dbf0SWen Congyang 23076e790746SPaolo Bonzini n->multiqueue = multiqueue; 2308f9d6dbf0SWen Congyang virtio_net_change_num_queues(n, max); 23096e790746SPaolo Bonzini 23106e790746SPaolo Bonzini virtio_net_set_queues(n); 23116e790746SPaolo Bonzini } 23126e790746SPaolo Bonzini 2313982b78c5SDr. David Alan Gilbert static int virtio_net_post_load_device(void *opaque, int version_id) 2314037dab2fSGreg Kurz { 2315982b78c5SDr. David Alan Gilbert VirtIONet *n = opaque; 2316982b78c5SDr. David Alan Gilbert VirtIODevice *vdev = VIRTIO_DEVICE(n); 2317037dab2fSGreg Kurz int i, link_down; 2318037dab2fSGreg Kurz 23199d8c6a25SDr. David Alan Gilbert trace_virtio_net_post_load_device(); 2320982b78c5SDr. David Alan Gilbert virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs, 232195129d6fSCornelia Huck virtio_vdev_has_feature(vdev, 232295129d6fSCornelia Huck VIRTIO_F_VERSION_1)); 23236e790746SPaolo Bonzini 23246e790746SPaolo Bonzini /* MAC_TABLE_ENTRIES may be different from the saved image */ 2325982b78c5SDr. David Alan Gilbert if (n->mac_table.in_use > MAC_TABLE_ENTRIES) { 23266e790746SPaolo Bonzini n->mac_table.in_use = 0; 23276e790746SPaolo Bonzini } 23286e790746SPaolo Bonzini 2329982b78c5SDr. David Alan Gilbert if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { 23306c666823SMichael S. Tsirkin n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); 23316c666823SMichael S. Tsirkin } 23326c666823SMichael S. Tsirkin 23336c666823SMichael S. Tsirkin if (peer_has_vnet_hdr(n)) { 23346c666823SMichael S. Tsirkin virtio_net_apply_guest_offloads(n); 23356c666823SMichael S. Tsirkin } 23366c666823SMichael S. Tsirkin 23376e790746SPaolo Bonzini virtio_net_set_queues(n); 23386e790746SPaolo Bonzini 23396e790746SPaolo Bonzini /* Find the first multicast entry in the saved MAC filter */ 23406e790746SPaolo Bonzini for (i = 0; i < n->mac_table.in_use; i++) { 23416e790746SPaolo Bonzini if (n->mac_table.macs[i * ETH_ALEN] & 1) { 23426e790746SPaolo Bonzini break; 23436e790746SPaolo Bonzini } 23446e790746SPaolo Bonzini } 23456e790746SPaolo Bonzini n->mac_table.first_multi = i; 23466e790746SPaolo Bonzini 23476e790746SPaolo Bonzini /* nc.link_down can't be migrated, so infer link_down according 23486e790746SPaolo Bonzini * to link status bit in n->status */ 23496e790746SPaolo Bonzini link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0; 23506e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 23516e790746SPaolo Bonzini qemu_get_subqueue(n->nic, i)->link_down = link_down; 23526e790746SPaolo Bonzini } 23536e790746SPaolo Bonzini 23546c666823SMichael S. Tsirkin if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && 23556c666823SMichael S. Tsirkin virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { 23569d8c6a25SDr. David Alan Gilbert qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(), 23579d8c6a25SDr. David Alan Gilbert QEMU_CLOCK_VIRTUAL, 23589d8c6a25SDr. David Alan Gilbert virtio_net_announce_timer, n); 23599d8c6a25SDr. David Alan Gilbert if (n->announce_timer.round) { 23609d8c6a25SDr. David Alan Gilbert timer_mod(n->announce_timer.tm, 23619d8c6a25SDr. David Alan Gilbert qemu_clock_get_ms(n->announce_timer.type)); 23629d8c6a25SDr. David Alan Gilbert } else { 2363*944458b6SDr. David Alan Gilbert qemu_announce_timer_del(&n->announce_timer, false); 23649d8c6a25SDr. David Alan Gilbert } 23656c666823SMichael S. Tsirkin } 23666c666823SMichael S. Tsirkin 23676e790746SPaolo Bonzini return 0; 23686e790746SPaolo Bonzini } 23696e790746SPaolo Bonzini 2370982b78c5SDr. David Alan Gilbert /* tx_waiting field of a VirtIONetQueue */ 2371982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = { 2372982b78c5SDr. David Alan Gilbert .name = "virtio-net-queue-tx_waiting", 2373982b78c5SDr. David Alan Gilbert .fields = (VMStateField[]) { 2374982b78c5SDr. David Alan Gilbert VMSTATE_UINT32(tx_waiting, VirtIONetQueue), 2375982b78c5SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 2376982b78c5SDr. David Alan Gilbert }, 2377982b78c5SDr. David Alan Gilbert }; 2378982b78c5SDr. David Alan Gilbert 2379982b78c5SDr. David Alan Gilbert static bool max_queues_gt_1(void *opaque, int version_id) 2380982b78c5SDr. David Alan Gilbert { 2381982b78c5SDr. David Alan Gilbert return VIRTIO_NET(opaque)->max_queues > 1; 2382982b78c5SDr. David Alan Gilbert } 2383982b78c5SDr. David Alan Gilbert 2384982b78c5SDr. David Alan Gilbert static bool has_ctrl_guest_offloads(void *opaque, int version_id) 2385982b78c5SDr. David Alan Gilbert { 2386982b78c5SDr. David Alan Gilbert return virtio_vdev_has_feature(VIRTIO_DEVICE(opaque), 2387982b78c5SDr. David Alan Gilbert VIRTIO_NET_F_CTRL_GUEST_OFFLOADS); 2388982b78c5SDr. David Alan Gilbert } 2389982b78c5SDr. David Alan Gilbert 2390982b78c5SDr. David Alan Gilbert static bool mac_table_fits(void *opaque, int version_id) 2391982b78c5SDr. David Alan Gilbert { 2392982b78c5SDr. David Alan Gilbert return VIRTIO_NET(opaque)->mac_table.in_use <= MAC_TABLE_ENTRIES; 2393982b78c5SDr. David Alan Gilbert } 2394982b78c5SDr. David Alan Gilbert 2395982b78c5SDr. David Alan Gilbert static bool mac_table_doesnt_fit(void *opaque, int version_id) 2396982b78c5SDr. David Alan Gilbert { 2397982b78c5SDr. David Alan Gilbert return !mac_table_fits(opaque, version_id); 2398982b78c5SDr. David Alan Gilbert } 2399982b78c5SDr. David Alan Gilbert 2400982b78c5SDr. David Alan Gilbert /* This temporary type is shared by all the WITH_TMP methods 2401982b78c5SDr. David Alan Gilbert * although only some fields are used by each. 2402982b78c5SDr. David Alan Gilbert */ 2403982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp { 2404982b78c5SDr. David Alan Gilbert VirtIONet *parent; 2405982b78c5SDr. David Alan Gilbert VirtIONetQueue *vqs_1; 2406982b78c5SDr. David Alan Gilbert uint16_t curr_queues_1; 2407982b78c5SDr. David Alan Gilbert uint8_t has_ufo; 2408982b78c5SDr. David Alan Gilbert uint32_t has_vnet_hdr; 2409982b78c5SDr. David Alan Gilbert }; 2410982b78c5SDr. David Alan Gilbert 2411982b78c5SDr. David Alan Gilbert /* The 2nd and subsequent tx_waiting flags are loaded later than 2412982b78c5SDr. David Alan Gilbert * the 1st entry in the queues and only if there's more than one 2413982b78c5SDr. David Alan Gilbert * entry. We use the tmp mechanism to calculate a temporary 2414982b78c5SDr. David Alan Gilbert * pointer and count and also validate the count. 2415982b78c5SDr. David Alan Gilbert */ 2416982b78c5SDr. David Alan Gilbert 241744b1ff31SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_save(void *opaque) 2418982b78c5SDr. David Alan Gilbert { 2419982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2420982b78c5SDr. David Alan Gilbert 2421982b78c5SDr. David Alan Gilbert tmp->vqs_1 = tmp->parent->vqs + 1; 2422982b78c5SDr. David Alan Gilbert tmp->curr_queues_1 = tmp->parent->curr_queues - 1; 2423982b78c5SDr. David Alan Gilbert if (tmp->parent->curr_queues == 0) { 2424982b78c5SDr. David Alan Gilbert tmp->curr_queues_1 = 0; 2425982b78c5SDr. David Alan Gilbert } 242644b1ff31SDr. David Alan Gilbert 242744b1ff31SDr. David Alan Gilbert return 0; 2428982b78c5SDr. David Alan Gilbert } 2429982b78c5SDr. David Alan Gilbert 2430982b78c5SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_load(void *opaque) 2431982b78c5SDr. David Alan Gilbert { 2432982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2433982b78c5SDr. David Alan Gilbert 2434982b78c5SDr. David Alan Gilbert /* Reuse the pointer setup from save */ 2435982b78c5SDr. David Alan Gilbert virtio_net_tx_waiting_pre_save(opaque); 2436982b78c5SDr. David Alan Gilbert 2437982b78c5SDr. David Alan Gilbert if (tmp->parent->curr_queues > tmp->parent->max_queues) { 2438982b78c5SDr. David Alan Gilbert error_report("virtio-net: curr_queues %x > max_queues %x", 2439982b78c5SDr. David Alan Gilbert tmp->parent->curr_queues, tmp->parent->max_queues); 2440982b78c5SDr. David Alan Gilbert 2441982b78c5SDr. David Alan Gilbert return -EINVAL; 2442982b78c5SDr. David Alan Gilbert } 2443982b78c5SDr. David Alan Gilbert 2444982b78c5SDr. David Alan Gilbert return 0; /* all good */ 2445982b78c5SDr. David Alan Gilbert } 2446982b78c5SDr. David Alan Gilbert 2447982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_tx_waiting = { 2448982b78c5SDr. David Alan Gilbert .name = "virtio-net-tx_waiting", 2449982b78c5SDr. David Alan Gilbert .pre_load = virtio_net_tx_waiting_pre_load, 2450982b78c5SDr. David Alan Gilbert .pre_save = virtio_net_tx_waiting_pre_save, 2451982b78c5SDr. David Alan Gilbert .fields = (VMStateField[]) { 2452982b78c5SDr. David Alan Gilbert VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp, 2453982b78c5SDr. David Alan Gilbert curr_queues_1, 2454982b78c5SDr. David Alan Gilbert vmstate_virtio_net_queue_tx_waiting, 2455982b78c5SDr. David Alan Gilbert struct VirtIONetQueue), 2456982b78c5SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 2457982b78c5SDr. David Alan Gilbert }, 2458982b78c5SDr. David Alan Gilbert }; 2459982b78c5SDr. David Alan Gilbert 2460982b78c5SDr. David Alan Gilbert /* the 'has_ufo' flag is just tested; if the incoming stream has the 2461982b78c5SDr. David Alan Gilbert * flag set we need to check that we have it 2462982b78c5SDr. David Alan Gilbert */ 2463982b78c5SDr. David Alan Gilbert static int virtio_net_ufo_post_load(void *opaque, int version_id) 2464982b78c5SDr. David Alan Gilbert { 2465982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2466982b78c5SDr. David Alan Gilbert 2467982b78c5SDr. David Alan Gilbert if (tmp->has_ufo && !peer_has_ufo(tmp->parent)) { 2468982b78c5SDr. David Alan Gilbert error_report("virtio-net: saved image requires TUN_F_UFO support"); 2469982b78c5SDr. David Alan Gilbert return -EINVAL; 2470982b78c5SDr. David Alan Gilbert } 2471982b78c5SDr. David Alan Gilbert 2472982b78c5SDr. David Alan Gilbert return 0; 2473982b78c5SDr. David Alan Gilbert } 2474982b78c5SDr. David Alan Gilbert 247544b1ff31SDr. David Alan Gilbert static int virtio_net_ufo_pre_save(void *opaque) 2476982b78c5SDr. David Alan Gilbert { 2477982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2478982b78c5SDr. David Alan Gilbert 2479982b78c5SDr. David Alan Gilbert tmp->has_ufo = tmp->parent->has_ufo; 248044b1ff31SDr. David Alan Gilbert 248144b1ff31SDr. David Alan Gilbert return 0; 2482982b78c5SDr. David Alan Gilbert } 2483982b78c5SDr. David Alan Gilbert 2484982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_ufo = { 2485982b78c5SDr. David Alan Gilbert .name = "virtio-net-ufo", 2486982b78c5SDr. David Alan Gilbert .post_load = virtio_net_ufo_post_load, 2487982b78c5SDr. David Alan Gilbert .pre_save = virtio_net_ufo_pre_save, 2488982b78c5SDr. David Alan Gilbert .fields = (VMStateField[]) { 2489982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(has_ufo, struct VirtIONetMigTmp), 2490982b78c5SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 2491982b78c5SDr. David Alan Gilbert }, 2492982b78c5SDr. David Alan Gilbert }; 2493982b78c5SDr. David Alan Gilbert 2494982b78c5SDr. David Alan Gilbert /* the 'has_vnet_hdr' flag is just tested; if the incoming stream has the 2495982b78c5SDr. David Alan Gilbert * flag set we need to check that we have it 2496982b78c5SDr. David Alan Gilbert */ 2497982b78c5SDr. David Alan Gilbert static int virtio_net_vnet_post_load(void *opaque, int version_id) 2498982b78c5SDr. David Alan Gilbert { 2499982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2500982b78c5SDr. David Alan Gilbert 2501982b78c5SDr. David Alan Gilbert if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) { 2502982b78c5SDr. David Alan Gilbert error_report("virtio-net: saved image requires vnet_hdr=on"); 2503982b78c5SDr. David Alan Gilbert return -EINVAL; 2504982b78c5SDr. David Alan Gilbert } 2505982b78c5SDr. David Alan Gilbert 2506982b78c5SDr. David Alan Gilbert return 0; 2507982b78c5SDr. David Alan Gilbert } 2508982b78c5SDr. David Alan Gilbert 250944b1ff31SDr. David Alan Gilbert static int virtio_net_vnet_pre_save(void *opaque) 2510982b78c5SDr. David Alan Gilbert { 2511982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2512982b78c5SDr. David Alan Gilbert 2513982b78c5SDr. David Alan Gilbert tmp->has_vnet_hdr = tmp->parent->has_vnet_hdr; 251444b1ff31SDr. David Alan Gilbert 251544b1ff31SDr. David Alan Gilbert return 0; 2516982b78c5SDr. David Alan Gilbert } 2517982b78c5SDr. David Alan Gilbert 2518982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_vnet = { 2519982b78c5SDr. David Alan Gilbert .name = "virtio-net-vnet", 2520982b78c5SDr. David Alan Gilbert .post_load = virtio_net_vnet_post_load, 2521982b78c5SDr. David Alan Gilbert .pre_save = virtio_net_vnet_pre_save, 2522982b78c5SDr. David Alan Gilbert .fields = (VMStateField[]) { 2523982b78c5SDr. David Alan Gilbert VMSTATE_UINT32(has_vnet_hdr, struct VirtIONetMigTmp), 2524982b78c5SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 2525982b78c5SDr. David Alan Gilbert }, 2526982b78c5SDr. David Alan Gilbert }; 2527982b78c5SDr. David Alan Gilbert 2528982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_device = { 2529982b78c5SDr. David Alan Gilbert .name = "virtio-net-device", 2530982b78c5SDr. David Alan Gilbert .version_id = VIRTIO_NET_VM_VERSION, 2531982b78c5SDr. David Alan Gilbert .minimum_version_id = VIRTIO_NET_VM_VERSION, 2532982b78c5SDr. David Alan Gilbert .post_load = virtio_net_post_load_device, 2533982b78c5SDr. David Alan Gilbert .fields = (VMStateField[]) { 2534982b78c5SDr. David Alan Gilbert VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN), 2535982b78c5SDr. David Alan Gilbert VMSTATE_STRUCT_POINTER(vqs, VirtIONet, 2536982b78c5SDr. David Alan Gilbert vmstate_virtio_net_queue_tx_waiting, 2537982b78c5SDr. David Alan Gilbert VirtIONetQueue), 2538982b78c5SDr. David Alan Gilbert VMSTATE_UINT32(mergeable_rx_bufs, VirtIONet), 2539982b78c5SDr. David Alan Gilbert VMSTATE_UINT16(status, VirtIONet), 2540982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(promisc, VirtIONet), 2541982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(allmulti, VirtIONet), 2542982b78c5SDr. David Alan Gilbert VMSTATE_UINT32(mac_table.in_use, VirtIONet), 2543982b78c5SDr. David Alan Gilbert 2544982b78c5SDr. David Alan Gilbert /* Guarded pair: If it fits we load it, else we throw it away 2545982b78c5SDr. David Alan Gilbert * - can happen if source has a larger MAC table.; post-load 2546982b78c5SDr. David Alan Gilbert * sets flags in this case. 2547982b78c5SDr. David Alan Gilbert */ 2548982b78c5SDr. David Alan Gilbert VMSTATE_VBUFFER_MULTIPLY(mac_table.macs, VirtIONet, 2549982b78c5SDr. David Alan Gilbert 0, mac_table_fits, mac_table.in_use, 2550982b78c5SDr. David Alan Gilbert ETH_ALEN), 2551982b78c5SDr. David Alan Gilbert VMSTATE_UNUSED_VARRAY_UINT32(VirtIONet, mac_table_doesnt_fit, 0, 2552982b78c5SDr. David Alan Gilbert mac_table.in_use, ETH_ALEN), 2553982b78c5SDr. David Alan Gilbert 2554982b78c5SDr. David Alan Gilbert /* Note: This is an array of uint32's that's always been saved as a 2555982b78c5SDr. David Alan Gilbert * buffer; hold onto your endiannesses; it's actually used as a bitmap 2556982b78c5SDr. David Alan Gilbert * but based on the uint. 2557982b78c5SDr. David Alan Gilbert */ 2558982b78c5SDr. David Alan Gilbert VMSTATE_BUFFER_POINTER_UNSAFE(vlans, VirtIONet, 0, MAX_VLAN >> 3), 2559982b78c5SDr. David Alan Gilbert VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp, 2560982b78c5SDr. David Alan Gilbert vmstate_virtio_net_has_vnet), 2561982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(mac_table.multi_overflow, VirtIONet), 2562982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(mac_table.uni_overflow, VirtIONet), 2563982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(alluni, VirtIONet), 2564982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(nomulti, VirtIONet), 2565982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(nouni, VirtIONet), 2566982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(nobcast, VirtIONet), 2567982b78c5SDr. David Alan Gilbert VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp, 2568982b78c5SDr. David Alan Gilbert vmstate_virtio_net_has_ufo), 2569982b78c5SDr. David Alan Gilbert VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0, 2570982b78c5SDr. David Alan Gilbert vmstate_info_uint16_equal, uint16_t), 2571982b78c5SDr. David Alan Gilbert VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1), 2572982b78c5SDr. David Alan Gilbert VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp, 2573982b78c5SDr. David Alan Gilbert vmstate_virtio_net_tx_waiting), 2574982b78c5SDr. David Alan Gilbert VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet, 2575982b78c5SDr. David Alan Gilbert has_ctrl_guest_offloads), 2576982b78c5SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 2577982b78c5SDr. David Alan Gilbert }, 2578982b78c5SDr. David Alan Gilbert }; 2579982b78c5SDr. David Alan Gilbert 25806e790746SPaolo Bonzini static NetClientInfo net_virtio_info = { 2581f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 25826e790746SPaolo Bonzini .size = sizeof(NICState), 25836e790746SPaolo Bonzini .can_receive = virtio_net_can_receive, 25846e790746SPaolo Bonzini .receive = virtio_net_receive, 25856e790746SPaolo Bonzini .link_status_changed = virtio_net_set_link_status, 2586b1be4280SAmos Kong .query_rx_filter = virtio_net_query_rxfilter, 2587b2c929f0SDr. David Alan Gilbert .announce = virtio_net_announce, 25886e790746SPaolo Bonzini }; 25896e790746SPaolo Bonzini 25906e790746SPaolo Bonzini static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) 25916e790746SPaolo Bonzini { 259217a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 25936e790746SPaolo Bonzini NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); 25946e790746SPaolo Bonzini assert(n->vhost_started); 2595ed8b4afeSNikolay Nikolaev return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx); 25966e790746SPaolo Bonzini } 25976e790746SPaolo Bonzini 25986e790746SPaolo Bonzini static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, 25996e790746SPaolo Bonzini bool mask) 26006e790746SPaolo Bonzini { 260117a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 26026e790746SPaolo Bonzini NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); 26036e790746SPaolo Bonzini assert(n->vhost_started); 2604ed8b4afeSNikolay Nikolaev vhost_net_virtqueue_mask(get_vhost_net(nc->peer), 26056e790746SPaolo Bonzini vdev, idx, mask); 26066e790746SPaolo Bonzini } 26076e790746SPaolo Bonzini 2608019a3edbSGerd Hoffmann static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features) 26096e790746SPaolo Bonzini { 26100cd09c3aSCornelia Huck virtio_add_feature(&host_features, VIRTIO_NET_F_MAC); 2611a93e599dSMaxime Coquelin 2612ba550851SStefano Garzarella n->config_size = virtio_feature_get_config_size(feature_sizes, 2613ba550851SStefano Garzarella host_features); 261417ec5a86SKONRAD Frederic } 26156e790746SPaolo Bonzini 26168a253ec2SKONRAD Frederic void virtio_net_set_netclient_name(VirtIONet *n, const char *name, 26178a253ec2SKONRAD Frederic const char *type) 26188a253ec2SKONRAD Frederic { 26198a253ec2SKONRAD Frederic /* 26208a253ec2SKONRAD Frederic * The name can be NULL, the netclient name will be type.x. 26218a253ec2SKONRAD Frederic */ 26228a253ec2SKONRAD Frederic assert(type != NULL); 26238a253ec2SKONRAD Frederic 26248a253ec2SKONRAD Frederic g_free(n->netclient_name); 26258a253ec2SKONRAD Frederic g_free(n->netclient_type); 26268a253ec2SKONRAD Frederic n->netclient_name = g_strdup(name); 26278a253ec2SKONRAD Frederic n->netclient_type = g_strdup(type); 26288a253ec2SKONRAD Frederic } 26298a253ec2SKONRAD Frederic 2630e6f746b3SAndreas Färber static void virtio_net_device_realize(DeviceState *dev, Error **errp) 263117ec5a86SKONRAD Frederic { 2632e6f746b3SAndreas Färber VirtIODevice *vdev = VIRTIO_DEVICE(dev); 2633284a32f0SAndreas Färber VirtIONet *n = VIRTIO_NET(dev); 2634284a32f0SAndreas Färber NetClientState *nc; 26351773d9eeSKONRAD Frederic int i; 263617ec5a86SKONRAD Frederic 2637a93e599dSMaxime Coquelin if (n->net_conf.mtu) { 2638127833eeSJason Baron n->host_features |= (1ULL << VIRTIO_NET_F_MTU); 2639a93e599dSMaxime Coquelin } 2640a93e599dSMaxime Coquelin 26419473939eSJason Baron if (n->net_conf.duplex_str) { 26429473939eSJason Baron if (strncmp(n->net_conf.duplex_str, "half", 5) == 0) { 26439473939eSJason Baron n->net_conf.duplex = DUPLEX_HALF; 26449473939eSJason Baron } else if (strncmp(n->net_conf.duplex_str, "full", 5) == 0) { 26459473939eSJason Baron n->net_conf.duplex = DUPLEX_FULL; 26469473939eSJason Baron } else { 26479473939eSJason Baron error_setg(errp, "'duplex' must be 'half' or 'full'"); 26489473939eSJason Baron } 26499473939eSJason Baron n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX); 26509473939eSJason Baron } else { 26519473939eSJason Baron n->net_conf.duplex = DUPLEX_UNKNOWN; 26529473939eSJason Baron } 26539473939eSJason Baron 26549473939eSJason Baron if (n->net_conf.speed < SPEED_UNKNOWN) { 26559473939eSJason Baron error_setg(errp, "'speed' must be between 0 and INT_MAX"); 26569473939eSJason Baron } else if (n->net_conf.speed >= 0) { 26579473939eSJason Baron n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX); 26589473939eSJason Baron } 26599473939eSJason Baron 2660da3e8a23SShannon Zhao virtio_net_set_config_size(n, n->host_features); 2661284a32f0SAndreas Färber virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size); 266217ec5a86SKONRAD Frederic 26631c0fbfa3SMichael S. Tsirkin /* 26641c0fbfa3SMichael S. Tsirkin * We set a lower limit on RX queue size to what it always was. 26651c0fbfa3SMichael S. Tsirkin * Guests that want a smaller ring can always resize it without 26661c0fbfa3SMichael S. Tsirkin * help from us (using virtio 1 and up). 26671c0fbfa3SMichael S. Tsirkin */ 26681c0fbfa3SMichael S. Tsirkin if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE || 26691c0fbfa3SMichael S. Tsirkin n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE || 26705f997fd1SMichal Privoznik !is_power_of_2(n->net_conf.rx_queue_size)) { 26711c0fbfa3SMichael S. Tsirkin error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), " 26721c0fbfa3SMichael S. Tsirkin "must be a power of 2 between %d and %d.", 26731c0fbfa3SMichael S. Tsirkin n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE, 26741c0fbfa3SMichael S. Tsirkin VIRTQUEUE_MAX_SIZE); 26751c0fbfa3SMichael S. Tsirkin virtio_cleanup(vdev); 26761c0fbfa3SMichael S. Tsirkin return; 26771c0fbfa3SMichael S. Tsirkin } 26781c0fbfa3SMichael S. Tsirkin 26799b02e161SWei Wang if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE || 26809b02e161SWei Wang n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE || 26819b02e161SWei Wang !is_power_of_2(n->net_conf.tx_queue_size)) { 26829b02e161SWei Wang error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), " 26839b02e161SWei Wang "must be a power of 2 between %d and %d", 26849b02e161SWei Wang n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE, 26859b02e161SWei Wang VIRTQUEUE_MAX_SIZE); 26869b02e161SWei Wang virtio_cleanup(vdev); 26879b02e161SWei Wang return; 26889b02e161SWei Wang } 26899b02e161SWei Wang 2690575a1c0eSJiri Pirko n->max_queues = MAX(n->nic_conf.peers.queues, 1); 269187b3bd1cSJason Wang if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) { 26927e0e736eSJason Wang error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " 2693631b22eaSStefan Weil "must be a positive integer less than %d.", 269487b3bd1cSJason Wang n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2); 26957e0e736eSJason Wang virtio_cleanup(vdev); 26967e0e736eSJason Wang return; 26977e0e736eSJason Wang } 26986e790746SPaolo Bonzini n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues); 26996e790746SPaolo Bonzini n->curr_queues = 1; 27001773d9eeSKONRAD Frederic n->tx_timeout = n->net_conf.txtimer; 27016e790746SPaolo Bonzini 27021773d9eeSKONRAD Frederic if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer") 27031773d9eeSKONRAD Frederic && strcmp(n->net_conf.tx, "bh")) { 27040765691eSMarkus Armbruster warn_report("virtio-net: " 27056e790746SPaolo Bonzini "Unknown option tx=%s, valid options: \"timer\" \"bh\"", 27061773d9eeSKONRAD Frederic n->net_conf.tx); 27070765691eSMarkus Armbruster error_printf("Defaulting to \"bh\""); 27086e790746SPaolo Bonzini } 27096e790746SPaolo Bonzini 27102eef278bSMichael S. Tsirkin n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n), 27112eef278bSMichael S. Tsirkin n->net_conf.tx_queue_size); 27129b02e161SWei Wang 2713da51a335SJason Wang for (i = 0; i < n->max_queues; i++) { 2714f9d6dbf0SWen Congyang virtio_net_add_queue(n, i); 2715da51a335SJason Wang } 2716da51a335SJason Wang 271717a0ca55SKONRAD Frederic n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); 27181773d9eeSKONRAD Frederic qemu_macaddr_default_if_unset(&n->nic_conf.macaddr); 27191773d9eeSKONRAD Frederic memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac)); 27206e790746SPaolo Bonzini n->status = VIRTIO_NET_S_LINK_UP; 27219d8c6a25SDr. David Alan Gilbert qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(), 27229d8c6a25SDr. David Alan Gilbert QEMU_CLOCK_VIRTUAL, 2723f57fcf70SJason Wang virtio_net_announce_timer, n); 2724b2c929f0SDr. David Alan Gilbert n->announce_timer.round = 0; 27256e790746SPaolo Bonzini 27268a253ec2SKONRAD Frederic if (n->netclient_type) { 27278a253ec2SKONRAD Frederic /* 27288a253ec2SKONRAD Frederic * Happen when virtio_net_set_netclient_name has been called. 27298a253ec2SKONRAD Frederic */ 27308a253ec2SKONRAD Frederic n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf, 27318a253ec2SKONRAD Frederic n->netclient_type, n->netclient_name, n); 27328a253ec2SKONRAD Frederic } else { 27331773d9eeSKONRAD Frederic n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf, 2734284a32f0SAndreas Färber object_get_typename(OBJECT(dev)), dev->id, n); 27358a253ec2SKONRAD Frederic } 27368a253ec2SKONRAD Frederic 27376e790746SPaolo Bonzini peer_test_vnet_hdr(n); 27386e790746SPaolo Bonzini if (peer_has_vnet_hdr(n)) { 27396e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 2740d6085e3aSStefan Hajnoczi qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); 27416e790746SPaolo Bonzini } 27426e790746SPaolo Bonzini n->host_hdr_len = sizeof(struct virtio_net_hdr); 27436e790746SPaolo Bonzini } else { 27446e790746SPaolo Bonzini n->host_hdr_len = 0; 27456e790746SPaolo Bonzini } 27466e790746SPaolo Bonzini 27471773d9eeSKONRAD Frederic qemu_format_nic_info_str(qemu_get_queue(n->nic), n->nic_conf.macaddr.a); 27486e790746SPaolo Bonzini 27496e790746SPaolo Bonzini n->vqs[0].tx_waiting = 0; 27501773d9eeSKONRAD Frederic n->tx_burst = n->net_conf.txburst; 2751bb9d17f8SCornelia Huck virtio_net_set_mrg_rx_bufs(n, 0, 0); 27526e790746SPaolo Bonzini n->promisc = 1; /* for compatibility */ 27536e790746SPaolo Bonzini 27546e790746SPaolo Bonzini n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); 27556e790746SPaolo Bonzini 27566e790746SPaolo Bonzini n->vlans = g_malloc0(MAX_VLAN >> 3); 27576e790746SPaolo Bonzini 2758b1be4280SAmos Kong nc = qemu_get_queue(n->nic); 2759b1be4280SAmos Kong nc->rxfilter_notify_enabled = 1; 2760b1be4280SAmos Kong 27612974e916SYuri Benditovich QTAILQ_INIT(&n->rsc_chains); 2762284a32f0SAndreas Färber n->qdev = dev; 276317ec5a86SKONRAD Frederic } 276417ec5a86SKONRAD Frederic 2765306ec6c3SAndreas Färber static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) 276617ec5a86SKONRAD Frederic { 2767306ec6c3SAndreas Färber VirtIODevice *vdev = VIRTIO_DEVICE(dev); 2768306ec6c3SAndreas Färber VirtIONet *n = VIRTIO_NET(dev); 2769f9d6dbf0SWen Congyang int i, max_queues; 277017ec5a86SKONRAD Frederic 277117ec5a86SKONRAD Frederic /* This will stop vhost backend if appropriate. */ 277217ec5a86SKONRAD Frederic virtio_net_set_status(vdev, 0); 277317ec5a86SKONRAD Frederic 27748a253ec2SKONRAD Frederic g_free(n->netclient_name); 27758a253ec2SKONRAD Frederic n->netclient_name = NULL; 27768a253ec2SKONRAD Frederic g_free(n->netclient_type); 27778a253ec2SKONRAD Frederic n->netclient_type = NULL; 27788a253ec2SKONRAD Frederic 277917ec5a86SKONRAD Frederic g_free(n->mac_table.macs); 278017ec5a86SKONRAD Frederic g_free(n->vlans); 278117ec5a86SKONRAD Frederic 2782f9d6dbf0SWen Congyang max_queues = n->multiqueue ? n->max_queues : 1; 2783f9d6dbf0SWen Congyang for (i = 0; i < max_queues; i++) { 2784f9d6dbf0SWen Congyang virtio_net_del_queue(n, i); 278517ec5a86SKONRAD Frederic } 278617ec5a86SKONRAD Frederic 2787*944458b6SDr. David Alan Gilbert qemu_announce_timer_del(&n->announce_timer, false); 278817ec5a86SKONRAD Frederic g_free(n->vqs); 278917ec5a86SKONRAD Frederic qemu_del_nic(n->nic); 27902974e916SYuri Benditovich virtio_net_rsc_cleanup(n); 27916a1a8cc7SKONRAD Frederic virtio_cleanup(vdev); 279217ec5a86SKONRAD Frederic } 279317ec5a86SKONRAD Frederic 279417ec5a86SKONRAD Frederic static void virtio_net_instance_init(Object *obj) 279517ec5a86SKONRAD Frederic { 279617ec5a86SKONRAD Frederic VirtIONet *n = VIRTIO_NET(obj); 279717ec5a86SKONRAD Frederic 279817ec5a86SKONRAD Frederic /* 279917ec5a86SKONRAD Frederic * The default config_size is sizeof(struct virtio_net_config). 280017ec5a86SKONRAD Frederic * Can be overriden with virtio_net_set_config_size. 280117ec5a86SKONRAD Frederic */ 280217ec5a86SKONRAD Frederic n->config_size = sizeof(struct virtio_net_config); 2803aa4197c3SGonglei device_add_bootindex_property(obj, &n->nic_conf.bootindex, 2804aa4197c3SGonglei "bootindex", "/ethernet-phy@0", 2805aa4197c3SGonglei DEVICE(n), NULL); 280617ec5a86SKONRAD Frederic } 280717ec5a86SKONRAD Frederic 280844b1ff31SDr. David Alan Gilbert static int virtio_net_pre_save(void *opaque) 28094d45dcfbSHalil Pasic { 28104d45dcfbSHalil Pasic VirtIONet *n = opaque; 28114d45dcfbSHalil Pasic 28124d45dcfbSHalil Pasic /* At this point, backend must be stopped, otherwise 28134d45dcfbSHalil Pasic * it might keep writing to memory. */ 28144d45dcfbSHalil Pasic assert(!n->vhost_started); 281544b1ff31SDr. David Alan Gilbert 281644b1ff31SDr. David Alan Gilbert return 0; 28174d45dcfbSHalil Pasic } 28184d45dcfbSHalil Pasic 28194d45dcfbSHalil Pasic static const VMStateDescription vmstate_virtio_net = { 28204d45dcfbSHalil Pasic .name = "virtio-net", 28214d45dcfbSHalil Pasic .minimum_version_id = VIRTIO_NET_VM_VERSION, 28224d45dcfbSHalil Pasic .version_id = VIRTIO_NET_VM_VERSION, 28234d45dcfbSHalil Pasic .fields = (VMStateField[]) { 28244d45dcfbSHalil Pasic VMSTATE_VIRTIO_DEVICE, 28254d45dcfbSHalil Pasic VMSTATE_END_OF_LIST() 28264d45dcfbSHalil Pasic }, 28274d45dcfbSHalil Pasic .pre_save = virtio_net_pre_save, 28284d45dcfbSHalil Pasic }; 2829290c2428SDr. David Alan Gilbert 283017ec5a86SKONRAD Frederic static Property virtio_net_properties[] = { 2831127833eeSJason Baron DEFINE_PROP_BIT64("csum", VirtIONet, host_features, 2832127833eeSJason Baron VIRTIO_NET_F_CSUM, true), 2833127833eeSJason Baron DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features, 283487108bb2SShannon Zhao VIRTIO_NET_F_GUEST_CSUM, true), 2835127833eeSJason Baron DEFINE_PROP_BIT64("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true), 2836127833eeSJason Baron DEFINE_PROP_BIT64("guest_tso4", VirtIONet, host_features, 283787108bb2SShannon Zhao VIRTIO_NET_F_GUEST_TSO4, true), 2838127833eeSJason Baron DEFINE_PROP_BIT64("guest_tso6", VirtIONet, host_features, 283987108bb2SShannon Zhao VIRTIO_NET_F_GUEST_TSO6, true), 2840127833eeSJason Baron DEFINE_PROP_BIT64("guest_ecn", VirtIONet, host_features, 284187108bb2SShannon Zhao VIRTIO_NET_F_GUEST_ECN, true), 2842127833eeSJason Baron DEFINE_PROP_BIT64("guest_ufo", VirtIONet, host_features, 284387108bb2SShannon Zhao VIRTIO_NET_F_GUEST_UFO, true), 2844127833eeSJason Baron DEFINE_PROP_BIT64("guest_announce", VirtIONet, host_features, 284587108bb2SShannon Zhao VIRTIO_NET_F_GUEST_ANNOUNCE, true), 2846127833eeSJason Baron DEFINE_PROP_BIT64("host_tso4", VirtIONet, host_features, 284787108bb2SShannon Zhao VIRTIO_NET_F_HOST_TSO4, true), 2848127833eeSJason Baron DEFINE_PROP_BIT64("host_tso6", VirtIONet, host_features, 284987108bb2SShannon Zhao VIRTIO_NET_F_HOST_TSO6, true), 2850127833eeSJason Baron DEFINE_PROP_BIT64("host_ecn", VirtIONet, host_features, 285187108bb2SShannon Zhao VIRTIO_NET_F_HOST_ECN, true), 2852127833eeSJason Baron DEFINE_PROP_BIT64("host_ufo", VirtIONet, host_features, 285387108bb2SShannon Zhao VIRTIO_NET_F_HOST_UFO, true), 2854127833eeSJason Baron DEFINE_PROP_BIT64("mrg_rxbuf", VirtIONet, host_features, 285587108bb2SShannon Zhao VIRTIO_NET_F_MRG_RXBUF, true), 2856127833eeSJason Baron DEFINE_PROP_BIT64("status", VirtIONet, host_features, 285787108bb2SShannon Zhao VIRTIO_NET_F_STATUS, true), 2858127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_vq", VirtIONet, host_features, 285987108bb2SShannon Zhao VIRTIO_NET_F_CTRL_VQ, true), 2860127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_rx", VirtIONet, host_features, 286187108bb2SShannon Zhao VIRTIO_NET_F_CTRL_RX, true), 2862127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_vlan", VirtIONet, host_features, 286387108bb2SShannon Zhao VIRTIO_NET_F_CTRL_VLAN, true), 2864127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_rx_extra", VirtIONet, host_features, 286587108bb2SShannon Zhao VIRTIO_NET_F_CTRL_RX_EXTRA, true), 2866127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_mac_addr", VirtIONet, host_features, 286787108bb2SShannon Zhao VIRTIO_NET_F_CTRL_MAC_ADDR, true), 2868127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features, 286987108bb2SShannon Zhao VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true), 2870127833eeSJason Baron DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false), 28712974e916SYuri Benditovich DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features, 28722974e916SYuri Benditovich VIRTIO_NET_F_RSC_EXT, false), 28732974e916SYuri Benditovich DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout, 28742974e916SYuri Benditovich VIRTIO_NET_RSC_DEFAULT_INTERVAL), 287517ec5a86SKONRAD Frederic DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf), 287617ec5a86SKONRAD Frederic DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer, 287717ec5a86SKONRAD Frederic TX_TIMER_INTERVAL), 287817ec5a86SKONRAD Frederic DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST), 287917ec5a86SKONRAD Frederic DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx), 28801c0fbfa3SMichael S. Tsirkin DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size, 28811c0fbfa3SMichael S. Tsirkin VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE), 28829b02e161SWei Wang DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size, 28839b02e161SWei Wang VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE), 2884a93e599dSMaxime Coquelin DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0), 288575ebec11SMaxime Coquelin DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend, 288675ebec11SMaxime Coquelin true), 28879473939eSJason Baron DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN), 28889473939eSJason Baron DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str), 288917ec5a86SKONRAD Frederic DEFINE_PROP_END_OF_LIST(), 289017ec5a86SKONRAD Frederic }; 289117ec5a86SKONRAD Frederic 289217ec5a86SKONRAD Frederic static void virtio_net_class_init(ObjectClass *klass, void *data) 289317ec5a86SKONRAD Frederic { 289417ec5a86SKONRAD Frederic DeviceClass *dc = DEVICE_CLASS(klass); 289517ec5a86SKONRAD Frederic VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 2896e6f746b3SAndreas Färber 289717ec5a86SKONRAD Frederic dc->props = virtio_net_properties; 2898290c2428SDr. David Alan Gilbert dc->vmsd = &vmstate_virtio_net; 2899125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 2900e6f746b3SAndreas Färber vdc->realize = virtio_net_device_realize; 2901306ec6c3SAndreas Färber vdc->unrealize = virtio_net_device_unrealize; 290217ec5a86SKONRAD Frederic vdc->get_config = virtio_net_get_config; 290317ec5a86SKONRAD Frederic vdc->set_config = virtio_net_set_config; 290417ec5a86SKONRAD Frederic vdc->get_features = virtio_net_get_features; 290517ec5a86SKONRAD Frederic vdc->set_features = virtio_net_set_features; 290617ec5a86SKONRAD Frederic vdc->bad_features = virtio_net_bad_features; 290717ec5a86SKONRAD Frederic vdc->reset = virtio_net_reset; 290817ec5a86SKONRAD Frederic vdc->set_status = virtio_net_set_status; 290917ec5a86SKONRAD Frederic vdc->guest_notifier_mask = virtio_net_guest_notifier_mask; 291017ec5a86SKONRAD Frederic vdc->guest_notifier_pending = virtio_net_guest_notifier_pending; 29112a083ffdSMichael S. Tsirkin vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO); 2912982b78c5SDr. David Alan Gilbert vdc->vmsd = &vmstate_virtio_net_device; 291317ec5a86SKONRAD Frederic } 291417ec5a86SKONRAD Frederic 291517ec5a86SKONRAD Frederic static const TypeInfo virtio_net_info = { 291617ec5a86SKONRAD Frederic .name = TYPE_VIRTIO_NET, 291717ec5a86SKONRAD Frederic .parent = TYPE_VIRTIO_DEVICE, 291817ec5a86SKONRAD Frederic .instance_size = sizeof(VirtIONet), 291917ec5a86SKONRAD Frederic .instance_init = virtio_net_instance_init, 292017ec5a86SKONRAD Frederic .class_init = virtio_net_class_init, 292117ec5a86SKONRAD Frederic }; 292217ec5a86SKONRAD Frederic 292317ec5a86SKONRAD Frederic static void virtio_register_types(void) 292417ec5a86SKONRAD Frederic { 292517ec5a86SKONRAD Frederic type_register_static(&virtio_net_info); 292617ec5a86SKONRAD Frederic } 292717ec5a86SKONRAD Frederic 292817ec5a86SKONRAD Frederic type_init(virtio_register_types) 2929