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" 159711cd0dSJens Freimann #include "qemu/atomic.h" 166e790746SPaolo Bonzini #include "qemu/iov.h" 17db725815SMarkus Armbruster #include "qemu/main-loop.h" 180b8fa32fSMarkus Armbruster #include "qemu/module.h" 196e790746SPaolo Bonzini #include "hw/virtio/virtio.h" 206e790746SPaolo Bonzini #include "net/net.h" 216e790746SPaolo Bonzini #include "net/checksum.h" 226e790746SPaolo Bonzini #include "net/tap.h" 236e790746SPaolo Bonzini #include "qemu/error-report.h" 246e790746SPaolo Bonzini #include "qemu/timer.h" 259711cd0dSJens Freimann #include "qemu/option.h" 269711cd0dSJens Freimann #include "qemu/option_int.h" 279711cd0dSJens Freimann #include "qemu/config-file.h" 289711cd0dSJens Freimann #include "qapi/qmp/qdict.h" 296e790746SPaolo Bonzini #include "hw/virtio/virtio-net.h" 306e790746SPaolo Bonzini #include "net/vhost_net.h" 319d8c6a25SDr. David Alan Gilbert #include "net/announce.h" 3217ec5a86SKONRAD Frederic #include "hw/virtio/virtio-bus.h" 33e688df6bSMarkus Armbruster #include "qapi/error.h" 349af23989SMarkus Armbruster #include "qapi/qapi-events-net.h" 35a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 369711cd0dSJens Freimann #include "qapi/qapi-types-migration.h" 379711cd0dSJens Freimann #include "qapi/qapi-events-migration.h" 381399c60dSRusty Russell #include "hw/virtio/virtio-access.h" 39f8d806c9SJuan Quintela #include "migration/misc.h" 409473939eSJason Baron #include "standard-headers/linux/ethtool.h" 412f780b6aSMarkus Armbruster #include "sysemu/sysemu.h" 429d8c6a25SDr. David Alan Gilbert #include "trace.h" 439711cd0dSJens Freimann #include "monitor/qdev.h" 449711cd0dSJens Freimann #include "hw/pci/pci.h" 456e790746SPaolo Bonzini 466e790746SPaolo Bonzini #define VIRTIO_NET_VM_VERSION 11 476e790746SPaolo Bonzini 486e790746SPaolo Bonzini #define MAC_TABLE_ENTRIES 64 496e790746SPaolo Bonzini #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ 506e790746SPaolo Bonzini 511c0fbfa3SMichael S. Tsirkin /* previously fixed value */ 521c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256 539b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256 549b02e161SWei Wang 551c0fbfa3SMichael S. Tsirkin /* for now, only allow larger queues; with virtio-1, guest can downsize */ 561c0fbfa3SMichael S. Tsirkin #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 579b02e161SWei Wang #define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 581c0fbfa3SMichael S. Tsirkin 592974e916SYuri Benditovich #define VIRTIO_NET_IP4_ADDR_SIZE 8 /* ipv4 saddr + daddr */ 602974e916SYuri Benditovich 612974e916SYuri Benditovich #define VIRTIO_NET_TCP_FLAG 0x3F 622974e916SYuri Benditovich #define VIRTIO_NET_TCP_HDR_LENGTH 0xF000 632974e916SYuri Benditovich 642974e916SYuri Benditovich /* IPv4 max payload, 16 bits in the header */ 652974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP4_PAYLOAD (65535 - sizeof(struct ip_header)) 662974e916SYuri Benditovich #define VIRTIO_NET_MAX_TCP_PAYLOAD 65535 672974e916SYuri Benditovich 682974e916SYuri Benditovich /* header length value in ip header without option */ 692974e916SYuri Benditovich #define VIRTIO_NET_IP4_HEADER_LENGTH 5 702974e916SYuri Benditovich 712974e916SYuri Benditovich #define VIRTIO_NET_IP6_ADDR_SIZE 32 /* ipv6 saddr + daddr */ 722974e916SYuri Benditovich #define VIRTIO_NET_MAX_IP6_PAYLOAD VIRTIO_NET_MAX_TCP_PAYLOAD 732974e916SYuri Benditovich 742974e916SYuri Benditovich /* Purge coalesced packets timer interval, This value affects the performance 752974e916SYuri Benditovich a lot, and should be tuned carefully, '300000'(300us) is the recommended 762974e916SYuri Benditovich value to pass the WHQL test, '50000' can gain 2x netperf throughput with 772974e916SYuri Benditovich tso/gso/gro 'off'. */ 782974e916SYuri Benditovich #define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000 792974e916SYuri Benditovich 80*59079029SYuri Benditovich #define VIRTIO_NET_RSS_SUPPORTED_HASHES (VIRTIO_NET_RSS_HASH_TYPE_IPv4 | \ 81*59079029SYuri Benditovich VIRTIO_NET_RSS_HASH_TYPE_TCPv4 | \ 82*59079029SYuri Benditovich VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | \ 83*59079029SYuri Benditovich VIRTIO_NET_RSS_HASH_TYPE_IPv6 | \ 84*59079029SYuri Benditovich VIRTIO_NET_RSS_HASH_TYPE_TCPv6 | \ 85*59079029SYuri Benditovich VIRTIO_NET_RSS_HASH_TYPE_UDPv6 | \ 86*59079029SYuri Benditovich VIRTIO_NET_RSS_HASH_TYPE_IP_EX | \ 87*59079029SYuri Benditovich VIRTIO_NET_RSS_HASH_TYPE_TCP_EX | \ 88*59079029SYuri Benditovich VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) 89*59079029SYuri Benditovich 902974e916SYuri Benditovich /* temporary until standard header include it */ 912974e916SYuri Benditovich #if !defined(VIRTIO_NET_HDR_F_RSC_INFO) 922974e916SYuri Benditovich 932974e916SYuri Benditovich #define VIRTIO_NET_HDR_F_RSC_INFO 4 /* rsc_ext data in csum_ fields */ 94d47e5e31SYuri Benditovich #define VIRTIO_NET_F_RSC_EXT 61 952974e916SYuri Benditovich 969904adfaSCornelia Huck #endif 979904adfaSCornelia Huck 982974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_packets( 992974e916SYuri Benditovich struct virtio_net_hdr *hdr) 1002974e916SYuri Benditovich { 1012974e916SYuri Benditovich return &hdr->csum_start; 1022974e916SYuri Benditovich } 1032974e916SYuri Benditovich 1042974e916SYuri Benditovich static inline __virtio16 *virtio_net_rsc_ext_num_dupacks( 1052974e916SYuri Benditovich struct virtio_net_hdr *hdr) 1062974e916SYuri Benditovich { 1072974e916SYuri Benditovich return &hdr->csum_offset; 1082974e916SYuri Benditovich } 1092974e916SYuri Benditovich 1106e790746SPaolo Bonzini static VirtIOFeature feature_sizes[] = { 111127833eeSJason Baron {.flags = 1ULL << VIRTIO_NET_F_MAC, 1125d5b33c0SMax Reitz .end = endof(struct virtio_net_config, mac)}, 113127833eeSJason Baron {.flags = 1ULL << VIRTIO_NET_F_STATUS, 1145d5b33c0SMax Reitz .end = endof(struct virtio_net_config, status)}, 115127833eeSJason Baron {.flags = 1ULL << VIRTIO_NET_F_MQ, 1165d5b33c0SMax Reitz .end = endof(struct virtio_net_config, max_virtqueue_pairs)}, 117127833eeSJason Baron {.flags = 1ULL << VIRTIO_NET_F_MTU, 1185d5b33c0SMax Reitz .end = endof(struct virtio_net_config, mtu)}, 1199473939eSJason Baron {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX, 1205d5b33c0SMax Reitz .end = endof(struct virtio_net_config, duplex)}, 121*59079029SYuri Benditovich {.flags = 1ULL << VIRTIO_NET_F_RSS, 122*59079029SYuri Benditovich .end = endof(struct virtio_net_config, supported_hash_types)}, 1236e790746SPaolo Bonzini {} 1246e790746SPaolo Bonzini }; 1256e790746SPaolo Bonzini 1266e790746SPaolo Bonzini static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc) 1276e790746SPaolo Bonzini { 1286e790746SPaolo Bonzini VirtIONet *n = qemu_get_nic_opaque(nc); 1296e790746SPaolo Bonzini 1306e790746SPaolo Bonzini return &n->vqs[nc->queue_index]; 1316e790746SPaolo Bonzini } 1326e790746SPaolo Bonzini 1336e790746SPaolo Bonzini static int vq2q(int queue_index) 1346e790746SPaolo Bonzini { 1356e790746SPaolo Bonzini return queue_index / 2; 1366e790746SPaolo Bonzini } 1376e790746SPaolo Bonzini 1386e790746SPaolo Bonzini /* TODO 1396e790746SPaolo Bonzini * - we could suppress RX interrupt if we were so inclined. 1406e790746SPaolo Bonzini */ 1416e790746SPaolo Bonzini 1426e790746SPaolo Bonzini static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) 1436e790746SPaolo Bonzini { 14417a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 1456e790746SPaolo Bonzini struct virtio_net_config netcfg; 1466e790746SPaolo Bonzini 1471399c60dSRusty Russell virtio_stw_p(vdev, &netcfg.status, n->status); 1481399c60dSRusty Russell virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues); 149a93e599dSMaxime Coquelin virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu); 1506e790746SPaolo Bonzini memcpy(netcfg.mac, n->mac, ETH_ALEN); 1519473939eSJason Baron virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed); 1529473939eSJason Baron netcfg.duplex = n->net_conf.duplex; 153*59079029SYuri Benditovich netcfg.rss_max_key_size = VIRTIO_NET_RSS_MAX_KEY_SIZE; 154*59079029SYuri Benditovich virtio_stw_p(vdev, &netcfg.rss_max_indirection_table_length, 155*59079029SYuri Benditovich VIRTIO_NET_RSS_MAX_TABLE_LEN); 156*59079029SYuri Benditovich virtio_stl_p(vdev, &netcfg.supported_hash_types, 157*59079029SYuri Benditovich VIRTIO_NET_RSS_SUPPORTED_HASHES); 1586e790746SPaolo Bonzini memcpy(config, &netcfg, n->config_size); 1596e790746SPaolo Bonzini } 1606e790746SPaolo Bonzini 1616e790746SPaolo Bonzini static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) 1626e790746SPaolo Bonzini { 16317a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 1646e790746SPaolo Bonzini struct virtio_net_config netcfg = {}; 1656e790746SPaolo Bonzini 1666e790746SPaolo Bonzini memcpy(&netcfg, config, n->config_size); 1676e790746SPaolo Bonzini 16895129d6fSCornelia Huck if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) && 16995129d6fSCornelia Huck !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) && 1706e790746SPaolo Bonzini memcmp(netcfg.mac, n->mac, ETH_ALEN)) { 1716e790746SPaolo Bonzini memcpy(n->mac, netcfg.mac, ETH_ALEN); 1726e790746SPaolo Bonzini qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); 1736e790746SPaolo Bonzini } 1746e790746SPaolo Bonzini } 1756e790746SPaolo Bonzini 1766e790746SPaolo Bonzini static bool virtio_net_started(VirtIONet *n, uint8_t status) 1776e790746SPaolo Bonzini { 17817a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 1796e790746SPaolo Bonzini return (status & VIRTIO_CONFIG_S_DRIVER_OK) && 18017a0ca55SKONRAD Frederic (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running; 1816e790746SPaolo Bonzini } 1826e790746SPaolo Bonzini 183b2c929f0SDr. David Alan Gilbert static void virtio_net_announce_notify(VirtIONet *net) 184b2c929f0SDr. David Alan Gilbert { 185b2c929f0SDr. David Alan Gilbert VirtIODevice *vdev = VIRTIO_DEVICE(net); 186b2c929f0SDr. David Alan Gilbert trace_virtio_net_announce_notify(); 187b2c929f0SDr. David Alan Gilbert 188b2c929f0SDr. David Alan Gilbert net->status |= VIRTIO_NET_S_ANNOUNCE; 189b2c929f0SDr. David Alan Gilbert virtio_notify_config(vdev); 190b2c929f0SDr. David Alan Gilbert } 191b2c929f0SDr. David Alan Gilbert 192f57fcf70SJason Wang static void virtio_net_announce_timer(void *opaque) 193f57fcf70SJason Wang { 194f57fcf70SJason Wang VirtIONet *n = opaque; 1959d8c6a25SDr. David Alan Gilbert trace_virtio_net_announce_timer(n->announce_timer.round); 196f57fcf70SJason Wang 1979d8c6a25SDr. David Alan Gilbert n->announce_timer.round--; 198b2c929f0SDr. David Alan Gilbert virtio_net_announce_notify(n); 199b2c929f0SDr. David Alan Gilbert } 200b2c929f0SDr. David Alan Gilbert 201b2c929f0SDr. David Alan Gilbert static void virtio_net_announce(NetClientState *nc) 202b2c929f0SDr. David Alan Gilbert { 203b2c929f0SDr. David Alan Gilbert VirtIONet *n = qemu_get_nic_opaque(nc); 204b2c929f0SDr. David Alan Gilbert VirtIODevice *vdev = VIRTIO_DEVICE(n); 205b2c929f0SDr. David Alan Gilbert 206b2c929f0SDr. David Alan Gilbert /* 207b2c929f0SDr. David Alan Gilbert * Make sure the virtio migration announcement timer isn't running 208b2c929f0SDr. David Alan Gilbert * If it is, let it trigger announcement so that we do not cause 209b2c929f0SDr. David Alan Gilbert * confusion. 210b2c929f0SDr. David Alan Gilbert */ 211b2c929f0SDr. David Alan Gilbert if (n->announce_timer.round) { 212b2c929f0SDr. David Alan Gilbert return; 213b2c929f0SDr. David Alan Gilbert } 214b2c929f0SDr. David Alan Gilbert 215b2c929f0SDr. David Alan Gilbert if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && 216b2c929f0SDr. David Alan Gilbert virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { 217b2c929f0SDr. David Alan Gilbert virtio_net_announce_notify(n); 218b2c929f0SDr. David Alan Gilbert } 219f57fcf70SJason Wang } 220f57fcf70SJason Wang 2216e790746SPaolo Bonzini static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) 2226e790746SPaolo Bonzini { 22317a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 2246e790746SPaolo Bonzini NetClientState *nc = qemu_get_queue(n->nic); 2256e790746SPaolo Bonzini int queues = n->multiqueue ? n->max_queues : 1; 2266e790746SPaolo Bonzini 227ed8b4afeSNikolay Nikolaev if (!get_vhost_net(nc->peer)) { 2286e790746SPaolo Bonzini return; 2296e790746SPaolo Bonzini } 2306e790746SPaolo Bonzini 2318c1ac475SRadim Krčmář if ((virtio_net_started(n, status) && !nc->peer->link_down) == 2328c1ac475SRadim Krčmář !!n->vhost_started) { 2336e790746SPaolo Bonzini return; 2346e790746SPaolo Bonzini } 2356e790746SPaolo Bonzini if (!n->vhost_started) { 236086abc1cSMichael S. Tsirkin int r, i; 237086abc1cSMichael S. Tsirkin 2381bfa316cSGreg Kurz if (n->needs_vnet_hdr_swap) { 2391bfa316cSGreg Kurz error_report("backend does not support %s vnet headers; " 2401bfa316cSGreg Kurz "falling back on userspace virtio", 2411bfa316cSGreg Kurz virtio_is_big_endian(vdev) ? "BE" : "LE"); 2421bfa316cSGreg Kurz return; 2431bfa316cSGreg Kurz } 2441bfa316cSGreg Kurz 245086abc1cSMichael S. Tsirkin /* Any packets outstanding? Purge them to avoid touching rings 246086abc1cSMichael S. Tsirkin * when vhost is running. 247086abc1cSMichael S. Tsirkin */ 248086abc1cSMichael S. Tsirkin for (i = 0; i < queues; i++) { 249086abc1cSMichael S. Tsirkin NetClientState *qnc = qemu_get_subqueue(n->nic, i); 250086abc1cSMichael S. Tsirkin 251086abc1cSMichael S. Tsirkin /* Purge both directions: TX and RX. */ 252086abc1cSMichael S. Tsirkin qemu_net_queue_purge(qnc->peer->incoming_queue, qnc); 253086abc1cSMichael S. Tsirkin qemu_net_queue_purge(qnc->incoming_queue, qnc->peer); 254086abc1cSMichael S. Tsirkin } 255086abc1cSMichael S. Tsirkin 256a93e599dSMaxime Coquelin if (virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MTU)) { 257a93e599dSMaxime Coquelin r = vhost_net_set_mtu(get_vhost_net(nc->peer), n->net_conf.mtu); 258a93e599dSMaxime Coquelin if (r < 0) { 259a93e599dSMaxime Coquelin error_report("%uBytes MTU not supported by the backend", 260a93e599dSMaxime Coquelin n->net_conf.mtu); 261a93e599dSMaxime Coquelin 262a93e599dSMaxime Coquelin return; 263a93e599dSMaxime Coquelin } 264a93e599dSMaxime Coquelin } 265a93e599dSMaxime Coquelin 2666e790746SPaolo Bonzini n->vhost_started = 1; 26717a0ca55SKONRAD Frederic r = vhost_net_start(vdev, n->nic->ncs, queues); 2686e790746SPaolo Bonzini if (r < 0) { 2696e790746SPaolo Bonzini error_report("unable to start vhost net: %d: " 2706e790746SPaolo Bonzini "falling back on userspace virtio", -r); 2716e790746SPaolo Bonzini n->vhost_started = 0; 2726e790746SPaolo Bonzini } 2736e790746SPaolo Bonzini } else { 27417a0ca55SKONRAD Frederic vhost_net_stop(vdev, n->nic->ncs, queues); 2756e790746SPaolo Bonzini n->vhost_started = 0; 2766e790746SPaolo Bonzini } 2776e790746SPaolo Bonzini } 2786e790746SPaolo Bonzini 2791bfa316cSGreg Kurz static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev, 2801bfa316cSGreg Kurz NetClientState *peer, 2811bfa316cSGreg Kurz bool enable) 2821bfa316cSGreg Kurz { 2831bfa316cSGreg Kurz if (virtio_is_big_endian(vdev)) { 2841bfa316cSGreg Kurz return qemu_set_vnet_be(peer, enable); 2851bfa316cSGreg Kurz } else { 2861bfa316cSGreg Kurz return qemu_set_vnet_le(peer, enable); 2871bfa316cSGreg Kurz } 2881bfa316cSGreg Kurz } 2891bfa316cSGreg Kurz 2901bfa316cSGreg Kurz static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs, 2911bfa316cSGreg Kurz int queues, bool enable) 2921bfa316cSGreg Kurz { 2931bfa316cSGreg Kurz int i; 2941bfa316cSGreg Kurz 2951bfa316cSGreg Kurz for (i = 0; i < queues; i++) { 2961bfa316cSGreg Kurz if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 && 2971bfa316cSGreg Kurz enable) { 2981bfa316cSGreg Kurz while (--i >= 0) { 2991bfa316cSGreg Kurz virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, false); 3001bfa316cSGreg Kurz } 3011bfa316cSGreg Kurz 3021bfa316cSGreg Kurz return true; 3031bfa316cSGreg Kurz } 3041bfa316cSGreg Kurz } 3051bfa316cSGreg Kurz 3061bfa316cSGreg Kurz return false; 3071bfa316cSGreg Kurz } 3081bfa316cSGreg Kurz 3091bfa316cSGreg Kurz static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) 3101bfa316cSGreg Kurz { 3111bfa316cSGreg Kurz VirtIODevice *vdev = VIRTIO_DEVICE(n); 3121bfa316cSGreg Kurz int queues = n->multiqueue ? n->max_queues : 1; 3131bfa316cSGreg Kurz 3141bfa316cSGreg Kurz if (virtio_net_started(n, status)) { 3151bfa316cSGreg Kurz /* Before using the device, we tell the network backend about the 3161bfa316cSGreg Kurz * endianness to use when parsing vnet headers. If the backend 3171bfa316cSGreg Kurz * can't do it, we fallback onto fixing the headers in the core 3181bfa316cSGreg Kurz * virtio-net code. 3191bfa316cSGreg Kurz */ 3201bfa316cSGreg Kurz n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs, 3211bfa316cSGreg Kurz queues, true); 3221bfa316cSGreg Kurz } else if (virtio_net_started(n, vdev->status)) { 3231bfa316cSGreg Kurz /* After using the device, we need to reset the network backend to 3241bfa316cSGreg Kurz * the default (guest native endianness), otherwise the guest may 3251bfa316cSGreg Kurz * lose network connectivity if it is rebooted into a different 3261bfa316cSGreg Kurz * endianness. 3271bfa316cSGreg Kurz */ 3281bfa316cSGreg Kurz virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false); 3291bfa316cSGreg Kurz } 3301bfa316cSGreg Kurz } 3311bfa316cSGreg Kurz 332283e2c2aSYuri Benditovich static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq) 333283e2c2aSYuri Benditovich { 334283e2c2aSYuri Benditovich unsigned int dropped = virtqueue_drop_all(vq); 335283e2c2aSYuri Benditovich if (dropped) { 336283e2c2aSYuri Benditovich virtio_notify(vdev, vq); 337283e2c2aSYuri Benditovich } 338283e2c2aSYuri Benditovich } 339283e2c2aSYuri Benditovich 3406e790746SPaolo Bonzini static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) 3416e790746SPaolo Bonzini { 34217a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 3436e790746SPaolo Bonzini VirtIONetQueue *q; 3446e790746SPaolo Bonzini int i; 3456e790746SPaolo Bonzini uint8_t queue_status; 3466e790746SPaolo Bonzini 3471bfa316cSGreg Kurz virtio_net_vnet_endian_status(n, status); 3486e790746SPaolo Bonzini virtio_net_vhost_status(n, status); 3496e790746SPaolo Bonzini 3506e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 35138705bb5SFam Zheng NetClientState *ncs = qemu_get_subqueue(n->nic, i); 35238705bb5SFam Zheng bool queue_started; 3536e790746SPaolo Bonzini q = &n->vqs[i]; 3546e790746SPaolo Bonzini 3556e790746SPaolo Bonzini if ((!n->multiqueue && i != 0) || i >= n->curr_queues) { 3566e790746SPaolo Bonzini queue_status = 0; 3576e790746SPaolo Bonzini } else { 3586e790746SPaolo Bonzini queue_status = status; 3596e790746SPaolo Bonzini } 36038705bb5SFam Zheng queue_started = 36138705bb5SFam Zheng virtio_net_started(n, queue_status) && !n->vhost_started; 36238705bb5SFam Zheng 36338705bb5SFam Zheng if (queue_started) { 36438705bb5SFam Zheng qemu_flush_queued_packets(ncs); 36538705bb5SFam Zheng } 3666e790746SPaolo Bonzini 3676e790746SPaolo Bonzini if (!q->tx_waiting) { 3686e790746SPaolo Bonzini continue; 3696e790746SPaolo Bonzini } 3706e790746SPaolo Bonzini 37138705bb5SFam Zheng if (queue_started) { 3726e790746SPaolo Bonzini if (q->tx_timer) { 373bc72ad67SAlex Bligh timer_mod(q->tx_timer, 374bc72ad67SAlex Bligh qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); 3756e790746SPaolo Bonzini } else { 3766e790746SPaolo Bonzini qemu_bh_schedule(q->tx_bh); 3776e790746SPaolo Bonzini } 3786e790746SPaolo Bonzini } else { 3796e790746SPaolo Bonzini if (q->tx_timer) { 380bc72ad67SAlex Bligh timer_del(q->tx_timer); 3816e790746SPaolo Bonzini } else { 3826e790746SPaolo Bonzini qemu_bh_cancel(q->tx_bh); 3836e790746SPaolo Bonzini } 384283e2c2aSYuri Benditovich if ((n->status & VIRTIO_NET_S_LINK_UP) == 0 && 38570e53e6eSJason Wang (queue_status & VIRTIO_CONFIG_S_DRIVER_OK) && 38670e53e6eSJason Wang vdev->vm_running) { 387283e2c2aSYuri Benditovich /* if tx is waiting we are likely have some packets in tx queue 388283e2c2aSYuri Benditovich * and disabled notification */ 389283e2c2aSYuri Benditovich q->tx_waiting = 0; 390283e2c2aSYuri Benditovich virtio_queue_set_notification(q->tx_vq, 1); 391283e2c2aSYuri Benditovich virtio_net_drop_tx_queue_data(vdev, q->tx_vq); 392283e2c2aSYuri Benditovich } 3936e790746SPaolo Bonzini } 3946e790746SPaolo Bonzini } 3956e790746SPaolo Bonzini } 3966e790746SPaolo Bonzini 3976e790746SPaolo Bonzini static void virtio_net_set_link_status(NetClientState *nc) 3986e790746SPaolo Bonzini { 3996e790746SPaolo Bonzini VirtIONet *n = qemu_get_nic_opaque(nc); 40017a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 4016e790746SPaolo Bonzini uint16_t old_status = n->status; 4026e790746SPaolo Bonzini 4036e790746SPaolo Bonzini if (nc->link_down) 4046e790746SPaolo Bonzini n->status &= ~VIRTIO_NET_S_LINK_UP; 4056e790746SPaolo Bonzini else 4066e790746SPaolo Bonzini n->status |= VIRTIO_NET_S_LINK_UP; 4076e790746SPaolo Bonzini 4086e790746SPaolo Bonzini if (n->status != old_status) 40917a0ca55SKONRAD Frederic virtio_notify_config(vdev); 4106e790746SPaolo Bonzini 41117a0ca55SKONRAD Frederic virtio_net_set_status(vdev, vdev->status); 4126e790746SPaolo Bonzini } 4136e790746SPaolo Bonzini 414b1be4280SAmos Kong static void rxfilter_notify(NetClientState *nc) 415b1be4280SAmos Kong { 416b1be4280SAmos Kong VirtIONet *n = qemu_get_nic_opaque(nc); 417b1be4280SAmos Kong 418b1be4280SAmos Kong if (nc->rxfilter_notify_enabled) { 419ddfb0baaSMarkus Armbruster char *path = object_get_canonical_path(OBJECT(n->qdev)); 42006150279SWenchao Xia qapi_event_send_nic_rx_filter_changed(!!n->netclient_name, 4213ab72385SPeter Xu n->netclient_name, path); 42296e35046SAmos Kong g_free(path); 423b1be4280SAmos Kong 424b1be4280SAmos Kong /* disable event notification to avoid events flooding */ 425b1be4280SAmos Kong nc->rxfilter_notify_enabled = 0; 426b1be4280SAmos Kong } 427b1be4280SAmos Kong } 428b1be4280SAmos Kong 429f7bc8ef8SAmos Kong static intList *get_vlan_table(VirtIONet *n) 430f7bc8ef8SAmos Kong { 431f7bc8ef8SAmos Kong intList *list, *entry; 432f7bc8ef8SAmos Kong int i, j; 433f7bc8ef8SAmos Kong 434f7bc8ef8SAmos Kong list = NULL; 435f7bc8ef8SAmos Kong for (i = 0; i < MAX_VLAN >> 5; i++) { 436f7bc8ef8SAmos Kong for (j = 0; n->vlans[i] && j <= 0x1f; j++) { 437f7bc8ef8SAmos Kong if (n->vlans[i] & (1U << j)) { 438f7bc8ef8SAmos Kong entry = g_malloc0(sizeof(*entry)); 439f7bc8ef8SAmos Kong entry->value = (i << 5) + j; 440f7bc8ef8SAmos Kong entry->next = list; 441f7bc8ef8SAmos Kong list = entry; 442f7bc8ef8SAmos Kong } 443f7bc8ef8SAmos Kong } 444f7bc8ef8SAmos Kong } 445f7bc8ef8SAmos Kong 446f7bc8ef8SAmos Kong return list; 447f7bc8ef8SAmos Kong } 448f7bc8ef8SAmos Kong 449b1be4280SAmos Kong static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) 450b1be4280SAmos Kong { 451b1be4280SAmos Kong VirtIONet *n = qemu_get_nic_opaque(nc); 452f7bc8ef8SAmos Kong VirtIODevice *vdev = VIRTIO_DEVICE(n); 453b1be4280SAmos Kong RxFilterInfo *info; 454b1be4280SAmos Kong strList *str_list, *entry; 455f7bc8ef8SAmos Kong int i; 456b1be4280SAmos Kong 457b1be4280SAmos Kong info = g_malloc0(sizeof(*info)); 458b1be4280SAmos Kong info->name = g_strdup(nc->name); 459b1be4280SAmos Kong info->promiscuous = n->promisc; 460b1be4280SAmos Kong 461b1be4280SAmos Kong if (n->nouni) { 462b1be4280SAmos Kong info->unicast = RX_STATE_NONE; 463b1be4280SAmos Kong } else if (n->alluni) { 464b1be4280SAmos Kong info->unicast = RX_STATE_ALL; 465b1be4280SAmos Kong } else { 466b1be4280SAmos Kong info->unicast = RX_STATE_NORMAL; 467b1be4280SAmos Kong } 468b1be4280SAmos Kong 469b1be4280SAmos Kong if (n->nomulti) { 470b1be4280SAmos Kong info->multicast = RX_STATE_NONE; 471b1be4280SAmos Kong } else if (n->allmulti) { 472b1be4280SAmos Kong info->multicast = RX_STATE_ALL; 473b1be4280SAmos Kong } else { 474b1be4280SAmos Kong info->multicast = RX_STATE_NORMAL; 475b1be4280SAmos Kong } 476b1be4280SAmos Kong 477b1be4280SAmos Kong info->broadcast_allowed = n->nobcast; 478b1be4280SAmos Kong info->multicast_overflow = n->mac_table.multi_overflow; 479b1be4280SAmos Kong info->unicast_overflow = n->mac_table.uni_overflow; 480b1be4280SAmos Kong 481b0575ba4SScott Feldman info->main_mac = qemu_mac_strdup_printf(n->mac); 482b1be4280SAmos Kong 483b1be4280SAmos Kong str_list = NULL; 484b1be4280SAmos Kong for (i = 0; i < n->mac_table.first_multi; i++) { 485b1be4280SAmos Kong entry = g_malloc0(sizeof(*entry)); 486b0575ba4SScott Feldman entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN); 487b1be4280SAmos Kong entry->next = str_list; 488b1be4280SAmos Kong str_list = entry; 489b1be4280SAmos Kong } 490b1be4280SAmos Kong info->unicast_table = str_list; 491b1be4280SAmos Kong 492b1be4280SAmos Kong str_list = NULL; 493b1be4280SAmos Kong for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) { 494b1be4280SAmos Kong entry = g_malloc0(sizeof(*entry)); 495b0575ba4SScott Feldman entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN); 496b1be4280SAmos Kong entry->next = str_list; 497b1be4280SAmos Kong str_list = entry; 498b1be4280SAmos Kong } 499b1be4280SAmos Kong info->multicast_table = str_list; 500f7bc8ef8SAmos Kong info->vlan_table = get_vlan_table(n); 501b1be4280SAmos Kong 50295129d6fSCornelia Huck if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) { 503f7bc8ef8SAmos Kong info->vlan = RX_STATE_ALL; 504f7bc8ef8SAmos Kong } else if (!info->vlan_table) { 505f7bc8ef8SAmos Kong info->vlan = RX_STATE_NONE; 506f7bc8ef8SAmos Kong } else { 507f7bc8ef8SAmos Kong info->vlan = RX_STATE_NORMAL; 508b1be4280SAmos Kong } 509b1be4280SAmos Kong 510b1be4280SAmos Kong /* enable event notification after query */ 511b1be4280SAmos Kong nc->rxfilter_notify_enabled = 1; 512b1be4280SAmos Kong 513b1be4280SAmos Kong return info; 514b1be4280SAmos Kong } 515b1be4280SAmos Kong 5166e790746SPaolo Bonzini static void virtio_net_reset(VirtIODevice *vdev) 5176e790746SPaolo Bonzini { 51817a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 51994b52958SGreg Kurz int i; 5206e790746SPaolo Bonzini 5216e790746SPaolo Bonzini /* Reset back to compatibility mode */ 5226e790746SPaolo Bonzini n->promisc = 1; 5236e790746SPaolo Bonzini n->allmulti = 0; 5246e790746SPaolo Bonzini n->alluni = 0; 5256e790746SPaolo Bonzini n->nomulti = 0; 5266e790746SPaolo Bonzini n->nouni = 0; 5276e790746SPaolo Bonzini n->nobcast = 0; 5286e790746SPaolo Bonzini /* multiqueue is disabled by default */ 5296e790746SPaolo Bonzini n->curr_queues = 1; 5309d8c6a25SDr. David Alan Gilbert timer_del(n->announce_timer.tm); 5319d8c6a25SDr. David Alan Gilbert n->announce_timer.round = 0; 532f57fcf70SJason Wang n->status &= ~VIRTIO_NET_S_ANNOUNCE; 5336e790746SPaolo Bonzini 5346e790746SPaolo Bonzini /* Flush any MAC and VLAN filter table state */ 5356e790746SPaolo Bonzini n->mac_table.in_use = 0; 5366e790746SPaolo Bonzini n->mac_table.first_multi = 0; 5376e790746SPaolo Bonzini n->mac_table.multi_overflow = 0; 5386e790746SPaolo Bonzini n->mac_table.uni_overflow = 0; 5396e790746SPaolo Bonzini memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); 5406e790746SPaolo Bonzini memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); 541702d66a8SMichael S. Tsirkin qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); 5426e790746SPaolo Bonzini memset(n->vlans, 0, MAX_VLAN >> 3); 54394b52958SGreg Kurz 54494b52958SGreg Kurz /* Flush any async TX */ 54594b52958SGreg Kurz for (i = 0; i < n->max_queues; i++) { 54694b52958SGreg Kurz NetClientState *nc = qemu_get_subqueue(n->nic, i); 54794b52958SGreg Kurz 54894b52958SGreg Kurz if (nc->peer) { 54994b52958SGreg Kurz qemu_flush_or_purge_queued_packets(nc->peer, true); 55094b52958SGreg Kurz assert(!virtio_net_get_subqueue(nc)->async_tx.elem); 55194b52958SGreg Kurz } 55294b52958SGreg Kurz } 5536e790746SPaolo Bonzini } 5546e790746SPaolo Bonzini 5556e790746SPaolo Bonzini static void peer_test_vnet_hdr(VirtIONet *n) 5566e790746SPaolo Bonzini { 5576e790746SPaolo Bonzini NetClientState *nc = qemu_get_queue(n->nic); 5586e790746SPaolo Bonzini if (!nc->peer) { 5596e790746SPaolo Bonzini return; 5606e790746SPaolo Bonzini } 5616e790746SPaolo Bonzini 562d6085e3aSStefan Hajnoczi n->has_vnet_hdr = qemu_has_vnet_hdr(nc->peer); 5636e790746SPaolo Bonzini } 5646e790746SPaolo Bonzini 5656e790746SPaolo Bonzini static int peer_has_vnet_hdr(VirtIONet *n) 5666e790746SPaolo Bonzini { 5676e790746SPaolo Bonzini return n->has_vnet_hdr; 5686e790746SPaolo Bonzini } 5696e790746SPaolo Bonzini 5706e790746SPaolo Bonzini static int peer_has_ufo(VirtIONet *n) 5716e790746SPaolo Bonzini { 5726e790746SPaolo Bonzini if (!peer_has_vnet_hdr(n)) 5736e790746SPaolo Bonzini return 0; 5746e790746SPaolo Bonzini 575d6085e3aSStefan Hajnoczi n->has_ufo = qemu_has_ufo(qemu_get_queue(n->nic)->peer); 5766e790746SPaolo Bonzini 5776e790746SPaolo Bonzini return n->has_ufo; 5786e790746SPaolo Bonzini } 5796e790746SPaolo Bonzini 580bb9d17f8SCornelia Huck static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, 581bb9d17f8SCornelia Huck int version_1) 5826e790746SPaolo Bonzini { 5836e790746SPaolo Bonzini int i; 5846e790746SPaolo Bonzini NetClientState *nc; 5856e790746SPaolo Bonzini 5866e790746SPaolo Bonzini n->mergeable_rx_bufs = mergeable_rx_bufs; 5876e790746SPaolo Bonzini 588bb9d17f8SCornelia Huck if (version_1) { 589bb9d17f8SCornelia Huck n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf); 590bb9d17f8SCornelia Huck } else { 5916e790746SPaolo Bonzini n->guest_hdr_len = n->mergeable_rx_bufs ? 592bb9d17f8SCornelia Huck sizeof(struct virtio_net_hdr_mrg_rxbuf) : 593bb9d17f8SCornelia Huck sizeof(struct virtio_net_hdr); 594bb9d17f8SCornelia Huck } 5956e790746SPaolo Bonzini 5966e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 5976e790746SPaolo Bonzini nc = qemu_get_subqueue(n->nic, i); 5986e790746SPaolo Bonzini 5996e790746SPaolo Bonzini if (peer_has_vnet_hdr(n) && 600d6085e3aSStefan Hajnoczi qemu_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) { 601d6085e3aSStefan Hajnoczi qemu_set_vnet_hdr_len(nc->peer, n->guest_hdr_len); 6026e790746SPaolo Bonzini n->host_hdr_len = n->guest_hdr_len; 6036e790746SPaolo Bonzini } 6046e790746SPaolo Bonzini } 6056e790746SPaolo Bonzini } 6066e790746SPaolo Bonzini 6072eef278bSMichael S. Tsirkin static int virtio_net_max_tx_queue_size(VirtIONet *n) 6082eef278bSMichael S. Tsirkin { 6092eef278bSMichael S. Tsirkin NetClientState *peer = n->nic_conf.peers.ncs[0]; 6102eef278bSMichael S. Tsirkin 6112eef278bSMichael S. Tsirkin /* 6122eef278bSMichael S. Tsirkin * Backends other than vhost-user don't support max queue size. 6132eef278bSMichael S. Tsirkin */ 6142eef278bSMichael S. Tsirkin if (!peer) { 6152eef278bSMichael S. Tsirkin return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; 6162eef278bSMichael S. Tsirkin } 6172eef278bSMichael S. Tsirkin 6182eef278bSMichael S. Tsirkin if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) { 6192eef278bSMichael S. Tsirkin return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; 6202eef278bSMichael S. Tsirkin } 6212eef278bSMichael S. Tsirkin 6222eef278bSMichael S. Tsirkin return VIRTQUEUE_MAX_SIZE; 6232eef278bSMichael S. Tsirkin } 6242eef278bSMichael S. Tsirkin 6256e790746SPaolo Bonzini static int peer_attach(VirtIONet *n, int index) 6266e790746SPaolo Bonzini { 6276e790746SPaolo Bonzini NetClientState *nc = qemu_get_subqueue(n->nic, index); 6286e790746SPaolo Bonzini 6296e790746SPaolo Bonzini if (!nc->peer) { 6306e790746SPaolo Bonzini return 0; 6316e790746SPaolo Bonzini } 6326e790746SPaolo Bonzini 633f394b2e2SEric Blake if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { 6347263a0adSChangchun Ouyang vhost_set_vring_enable(nc->peer, 1); 6357263a0adSChangchun Ouyang } 6367263a0adSChangchun Ouyang 637f394b2e2SEric Blake if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { 6386e790746SPaolo Bonzini return 0; 6396e790746SPaolo Bonzini } 6406e790746SPaolo Bonzini 6411074b879SJason Wang if (n->max_queues == 1) { 6421074b879SJason Wang return 0; 6431074b879SJason Wang } 6441074b879SJason Wang 6456e790746SPaolo Bonzini return tap_enable(nc->peer); 6466e790746SPaolo Bonzini } 6476e790746SPaolo Bonzini 6486e790746SPaolo Bonzini static int peer_detach(VirtIONet *n, int index) 6496e790746SPaolo Bonzini { 6506e790746SPaolo Bonzini NetClientState *nc = qemu_get_subqueue(n->nic, index); 6516e790746SPaolo Bonzini 6526e790746SPaolo Bonzini if (!nc->peer) { 6536e790746SPaolo Bonzini return 0; 6546e790746SPaolo Bonzini } 6556e790746SPaolo Bonzini 656f394b2e2SEric Blake if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { 6577263a0adSChangchun Ouyang vhost_set_vring_enable(nc->peer, 0); 6587263a0adSChangchun Ouyang } 6597263a0adSChangchun Ouyang 660f394b2e2SEric Blake if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { 6616e790746SPaolo Bonzini return 0; 6626e790746SPaolo Bonzini } 6636e790746SPaolo Bonzini 6646e790746SPaolo Bonzini return tap_disable(nc->peer); 6656e790746SPaolo Bonzini } 6666e790746SPaolo Bonzini 6676e790746SPaolo Bonzini static void virtio_net_set_queues(VirtIONet *n) 6686e790746SPaolo Bonzini { 6696e790746SPaolo Bonzini int i; 670ddfa83eaSJoel Stanley int r; 6716e790746SPaolo Bonzini 67268b5f314SYuri Benditovich if (n->nic->peer_deleted) { 67368b5f314SYuri Benditovich return; 67468b5f314SYuri Benditovich } 67568b5f314SYuri Benditovich 6766e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 6776e790746SPaolo Bonzini if (i < n->curr_queues) { 678ddfa83eaSJoel Stanley r = peer_attach(n, i); 679ddfa83eaSJoel Stanley assert(!r); 6806e790746SPaolo Bonzini } else { 681ddfa83eaSJoel Stanley r = peer_detach(n, i); 682ddfa83eaSJoel Stanley assert(!r); 6836e790746SPaolo Bonzini } 6846e790746SPaolo Bonzini } 6856e790746SPaolo Bonzini } 6866e790746SPaolo Bonzini 687ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue); 6886e790746SPaolo Bonzini 6899d5b731dSJason Wang static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, 6909d5b731dSJason Wang Error **errp) 6916e790746SPaolo Bonzini { 69217a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 6936e790746SPaolo Bonzini NetClientState *nc = qemu_get_queue(n->nic); 6946e790746SPaolo Bonzini 695da3e8a23SShannon Zhao /* Firstly sync all virtio-net possible supported features */ 696da3e8a23SShannon Zhao features |= n->host_features; 697da3e8a23SShannon Zhao 6980cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_MAC); 6996e790746SPaolo Bonzini 7006e790746SPaolo Bonzini if (!peer_has_vnet_hdr(n)) { 7010cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_CSUM); 7020cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4); 7030cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6); 7040cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN); 7056e790746SPaolo Bonzini 7060cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM); 7070cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4); 7080cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6); 7090cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN); 7106e790746SPaolo Bonzini } 7116e790746SPaolo Bonzini 7126e790746SPaolo Bonzini if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { 7130cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO); 7140cd09c3aSCornelia Huck virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO); 7156e790746SPaolo Bonzini } 7166e790746SPaolo Bonzini 717ed8b4afeSNikolay Nikolaev if (!get_vhost_net(nc->peer)) { 7186e790746SPaolo Bonzini return features; 7196e790746SPaolo Bonzini } 7202974e916SYuri Benditovich 721*59079029SYuri Benditovich virtio_clear_feature(&features, VIRTIO_NET_F_RSS); 72275ebec11SMaxime Coquelin features = vhost_net_get_features(get_vhost_net(nc->peer), features); 72375ebec11SMaxime Coquelin vdev->backend_features = features; 72475ebec11SMaxime Coquelin 72575ebec11SMaxime Coquelin if (n->mtu_bypass_backend && 72675ebec11SMaxime Coquelin (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) { 72775ebec11SMaxime Coquelin features |= (1ULL << VIRTIO_NET_F_MTU); 72875ebec11SMaxime Coquelin } 72975ebec11SMaxime Coquelin 73075ebec11SMaxime Coquelin return features; 7316e790746SPaolo Bonzini } 7326e790746SPaolo Bonzini 733019a3edbSGerd Hoffmann static uint64_t virtio_net_bad_features(VirtIODevice *vdev) 7346e790746SPaolo Bonzini { 735019a3edbSGerd Hoffmann uint64_t features = 0; 7366e790746SPaolo Bonzini 7376e790746SPaolo Bonzini /* Linux kernel 2.6.25. It understood MAC (as everyone must), 7386e790746SPaolo Bonzini * but also these: */ 7390cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_MAC); 7400cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_CSUM); 7410cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO4); 7420cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO6); 7430cd09c3aSCornelia Huck virtio_add_feature(&features, VIRTIO_NET_F_HOST_ECN); 7446e790746SPaolo Bonzini 7456e790746SPaolo Bonzini return features; 7466e790746SPaolo Bonzini } 7476e790746SPaolo Bonzini 748644c9858SDmitry Fleytman static void virtio_net_apply_guest_offloads(VirtIONet *n) 749644c9858SDmitry Fleytman { 750ad37bb3bSStefan Hajnoczi qemu_set_offload(qemu_get_queue(n->nic)->peer, 751644c9858SDmitry Fleytman !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)), 752644c9858SDmitry Fleytman !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)), 753644c9858SDmitry Fleytman !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)), 754644c9858SDmitry Fleytman !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)), 755644c9858SDmitry Fleytman !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO))); 756644c9858SDmitry Fleytman } 757644c9858SDmitry Fleytman 758644c9858SDmitry Fleytman static uint64_t virtio_net_guest_offloads_by_features(uint32_t features) 759644c9858SDmitry Fleytman { 760644c9858SDmitry Fleytman static const uint64_t guest_offloads_mask = 761644c9858SDmitry Fleytman (1ULL << VIRTIO_NET_F_GUEST_CSUM) | 762644c9858SDmitry Fleytman (1ULL << VIRTIO_NET_F_GUEST_TSO4) | 763644c9858SDmitry Fleytman (1ULL << VIRTIO_NET_F_GUEST_TSO6) | 764644c9858SDmitry Fleytman (1ULL << VIRTIO_NET_F_GUEST_ECN) | 765644c9858SDmitry Fleytman (1ULL << VIRTIO_NET_F_GUEST_UFO); 766644c9858SDmitry Fleytman 767644c9858SDmitry Fleytman return guest_offloads_mask & features; 768644c9858SDmitry Fleytman } 769644c9858SDmitry Fleytman 770644c9858SDmitry Fleytman static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n) 771644c9858SDmitry Fleytman { 772644c9858SDmitry Fleytman VirtIODevice *vdev = VIRTIO_DEVICE(n); 773644c9858SDmitry Fleytman return virtio_net_guest_offloads_by_features(vdev->guest_features); 774644c9858SDmitry Fleytman } 775644c9858SDmitry Fleytman 7769711cd0dSJens Freimann static void failover_add_primary(VirtIONet *n, Error **errp) 7779711cd0dSJens Freimann { 7789711cd0dSJens Freimann Error *err = NULL; 7799711cd0dSJens Freimann 780117378bfSJens Freimann if (n->primary_dev) { 781117378bfSJens Freimann return; 782117378bfSJens Freimann } 783117378bfSJens Freimann 7849711cd0dSJens Freimann n->primary_device_opts = qemu_opts_find(qemu_find_opts("device"), 7859711cd0dSJens Freimann n->primary_device_id); 7869711cd0dSJens Freimann if (n->primary_device_opts) { 7879711cd0dSJens Freimann n->primary_dev = qdev_device_add(n->primary_device_opts, &err); 7889711cd0dSJens Freimann if (err) { 7899711cd0dSJens Freimann qemu_opts_del(n->primary_device_opts); 7909711cd0dSJens Freimann } 7919711cd0dSJens Freimann if (n->primary_dev) { 7929711cd0dSJens Freimann n->primary_bus = n->primary_dev->parent_bus; 7939711cd0dSJens Freimann if (err) { 7949711cd0dSJens Freimann qdev_unplug(n->primary_dev, &err); 7959711cd0dSJens Freimann qdev_set_id(n->primary_dev, ""); 7969711cd0dSJens Freimann 7979711cd0dSJens Freimann } 7989711cd0dSJens Freimann } 7999711cd0dSJens Freimann } else { 8009711cd0dSJens Freimann error_setg(errp, "Primary device not found"); 8019711cd0dSJens Freimann error_append_hint(errp, "Virtio-net failover will not work. Make " 8029711cd0dSJens Freimann "sure primary device has parameter" 8039711cd0dSJens Freimann " failover_pair_id=<virtio-net-id>\n"); 8049711cd0dSJens Freimann } 8059711cd0dSJens Freimann if (err) { 8069711cd0dSJens Freimann error_propagate(errp, err); 8079711cd0dSJens Freimann } 8089711cd0dSJens Freimann } 8099711cd0dSJens Freimann 8109711cd0dSJens Freimann static int is_my_primary(void *opaque, QemuOpts *opts, Error **errp) 8119711cd0dSJens Freimann { 8129711cd0dSJens Freimann VirtIONet *n = opaque; 8139711cd0dSJens Freimann int ret = 0; 8149711cd0dSJens Freimann 8159711cd0dSJens Freimann const char *standby_id = qemu_opt_get(opts, "failover_pair_id"); 8169711cd0dSJens Freimann 8179711cd0dSJens Freimann if (standby_id != NULL && (g_strcmp0(standby_id, n->netclient_name) == 0)) { 8189711cd0dSJens Freimann n->primary_device_id = g_strdup(opts->id); 8199711cd0dSJens Freimann ret = 1; 8209711cd0dSJens Freimann } 8219711cd0dSJens Freimann 8229711cd0dSJens Freimann return ret; 8239711cd0dSJens Freimann } 8249711cd0dSJens Freimann 8259711cd0dSJens Freimann static DeviceState *virtio_net_find_primary(VirtIONet *n, Error **errp) 8269711cd0dSJens Freimann { 8279711cd0dSJens Freimann DeviceState *dev = NULL; 8289711cd0dSJens Freimann Error *err = NULL; 8299711cd0dSJens Freimann 8309711cd0dSJens Freimann if (qemu_opts_foreach(qemu_find_opts("device"), 8319711cd0dSJens Freimann is_my_primary, n, &err)) { 8329711cd0dSJens Freimann if (err) { 8339711cd0dSJens Freimann error_propagate(errp, err); 8349711cd0dSJens Freimann return NULL; 8359711cd0dSJens Freimann } 8369711cd0dSJens Freimann if (n->primary_device_id) { 8379711cd0dSJens Freimann dev = qdev_find_recursive(sysbus_get_default(), 8389711cd0dSJens Freimann n->primary_device_id); 8399711cd0dSJens Freimann } else { 8409711cd0dSJens Freimann error_setg(errp, "Primary device id not found"); 8419711cd0dSJens Freimann return NULL; 8429711cd0dSJens Freimann } 8439711cd0dSJens Freimann } 8449711cd0dSJens Freimann return dev; 8459711cd0dSJens Freimann } 8469711cd0dSJens Freimann 8479711cd0dSJens Freimann 8489711cd0dSJens Freimann 8499711cd0dSJens Freimann static DeviceState *virtio_connect_failover_devices(VirtIONet *n, 8509711cd0dSJens Freimann DeviceState *dev, 8519711cd0dSJens Freimann Error **errp) 8529711cd0dSJens Freimann { 8539711cd0dSJens Freimann DeviceState *prim_dev = NULL; 8549711cd0dSJens Freimann Error *err = NULL; 8559711cd0dSJens Freimann 8569711cd0dSJens Freimann prim_dev = virtio_net_find_primary(n, &err); 8579711cd0dSJens Freimann if (prim_dev) { 8589711cd0dSJens Freimann n->primary_device_id = g_strdup(prim_dev->id); 8599711cd0dSJens Freimann n->primary_device_opts = prim_dev->opts; 8609711cd0dSJens Freimann } else { 8619711cd0dSJens Freimann if (err) { 8629711cd0dSJens Freimann error_propagate(errp, err); 8639711cd0dSJens Freimann } 8649711cd0dSJens Freimann } 8659711cd0dSJens Freimann 8669711cd0dSJens Freimann return prim_dev; 8679711cd0dSJens Freimann } 8689711cd0dSJens Freimann 869d5aaa1b0SGerd Hoffmann static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) 8706e790746SPaolo Bonzini { 87117a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 8729711cd0dSJens Freimann Error *err = NULL; 8736e790746SPaolo Bonzini int i; 8746e790746SPaolo Bonzini 87575ebec11SMaxime Coquelin if (n->mtu_bypass_backend && 87675ebec11SMaxime Coquelin !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) { 87775ebec11SMaxime Coquelin features &= ~(1ULL << VIRTIO_NET_F_MTU); 87875ebec11SMaxime Coquelin } 87975ebec11SMaxime Coquelin 880ef546f12SCornelia Huck virtio_net_set_multiqueue(n, 881*59079029SYuri Benditovich virtio_has_feature(features, VIRTIO_NET_F_RSS) || 88295129d6fSCornelia Huck virtio_has_feature(features, VIRTIO_NET_F_MQ)); 8836e790746SPaolo Bonzini 884ef546f12SCornelia Huck virtio_net_set_mrg_rx_bufs(n, 88595129d6fSCornelia Huck virtio_has_feature(features, 886bb9d17f8SCornelia Huck VIRTIO_NET_F_MRG_RXBUF), 88795129d6fSCornelia Huck virtio_has_feature(features, 888bb9d17f8SCornelia Huck VIRTIO_F_VERSION_1)); 8896e790746SPaolo Bonzini 8902974e916SYuri Benditovich n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) && 8912974e916SYuri Benditovich virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4); 8922974e916SYuri Benditovich n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) && 8932974e916SYuri Benditovich virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6); 8942974e916SYuri Benditovich 8956e790746SPaolo Bonzini if (n->has_vnet_hdr) { 896644c9858SDmitry Fleytman n->curr_guest_offloads = 897644c9858SDmitry Fleytman virtio_net_guest_offloads_by_features(features); 898644c9858SDmitry Fleytman virtio_net_apply_guest_offloads(n); 8996e790746SPaolo Bonzini } 9006e790746SPaolo Bonzini 9016e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 9026e790746SPaolo Bonzini NetClientState *nc = qemu_get_subqueue(n->nic, i); 9036e790746SPaolo Bonzini 904ed8b4afeSNikolay Nikolaev if (!get_vhost_net(nc->peer)) { 9056e790746SPaolo Bonzini continue; 9066e790746SPaolo Bonzini } 907ed8b4afeSNikolay Nikolaev vhost_net_ack_features(get_vhost_net(nc->peer), features); 9086e790746SPaolo Bonzini } 9090b1eaa88SStefan Fritsch 91095129d6fSCornelia Huck if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { 9110b1eaa88SStefan Fritsch memset(n->vlans, 0, MAX_VLAN >> 3); 9120b1eaa88SStefan Fritsch } else { 9130b1eaa88SStefan Fritsch memset(n->vlans, 0xff, MAX_VLAN >> 3); 9140b1eaa88SStefan Fritsch } 9159711cd0dSJens Freimann 9169711cd0dSJens Freimann if (virtio_has_feature(features, VIRTIO_NET_F_STANDBY)) { 9179711cd0dSJens Freimann qapi_event_send_failover_negotiated(n->netclient_name); 9189711cd0dSJens Freimann atomic_set(&n->primary_should_be_hidden, false); 9199711cd0dSJens Freimann failover_add_primary(n, &err); 9209711cd0dSJens Freimann if (err) { 9219711cd0dSJens Freimann n->primary_dev = virtio_connect_failover_devices(n, n->qdev, &err); 9229711cd0dSJens Freimann if (err) { 9239711cd0dSJens Freimann goto out_err; 9249711cd0dSJens Freimann } 9259711cd0dSJens Freimann failover_add_primary(n, &err); 9269711cd0dSJens Freimann if (err) { 9279711cd0dSJens Freimann goto out_err; 9289711cd0dSJens Freimann } 9299711cd0dSJens Freimann } 9309711cd0dSJens Freimann } 9319711cd0dSJens Freimann return; 9329711cd0dSJens Freimann 9339711cd0dSJens Freimann out_err: 9349711cd0dSJens Freimann if (err) { 9359711cd0dSJens Freimann warn_report_err(err); 9369711cd0dSJens Freimann } 9376e790746SPaolo Bonzini } 9386e790746SPaolo Bonzini 9396e790746SPaolo Bonzini static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, 9406e790746SPaolo Bonzini struct iovec *iov, unsigned int iov_cnt) 9416e790746SPaolo Bonzini { 9426e790746SPaolo Bonzini uint8_t on; 9436e790746SPaolo Bonzini size_t s; 944b1be4280SAmos Kong NetClientState *nc = qemu_get_queue(n->nic); 9456e790746SPaolo Bonzini 9466e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on)); 9476e790746SPaolo Bonzini if (s != sizeof(on)) { 9486e790746SPaolo Bonzini return VIRTIO_NET_ERR; 9496e790746SPaolo Bonzini } 9506e790746SPaolo Bonzini 9516e790746SPaolo Bonzini if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) { 9526e790746SPaolo Bonzini n->promisc = on; 9536e790746SPaolo Bonzini } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) { 9546e790746SPaolo Bonzini n->allmulti = on; 9556e790746SPaolo Bonzini } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) { 9566e790746SPaolo Bonzini n->alluni = on; 9576e790746SPaolo Bonzini } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) { 9586e790746SPaolo Bonzini n->nomulti = on; 9596e790746SPaolo Bonzini } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) { 9606e790746SPaolo Bonzini n->nouni = on; 9616e790746SPaolo Bonzini } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) { 9626e790746SPaolo Bonzini n->nobcast = on; 9636e790746SPaolo Bonzini } else { 9646e790746SPaolo Bonzini return VIRTIO_NET_ERR; 9656e790746SPaolo Bonzini } 9666e790746SPaolo Bonzini 967b1be4280SAmos Kong rxfilter_notify(nc); 968b1be4280SAmos Kong 9696e790746SPaolo Bonzini return VIRTIO_NET_OK; 9706e790746SPaolo Bonzini } 9716e790746SPaolo Bonzini 972644c9858SDmitry Fleytman static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, 973644c9858SDmitry Fleytman struct iovec *iov, unsigned int iov_cnt) 974644c9858SDmitry Fleytman { 975644c9858SDmitry Fleytman VirtIODevice *vdev = VIRTIO_DEVICE(n); 976644c9858SDmitry Fleytman uint64_t offloads; 977644c9858SDmitry Fleytman size_t s; 978644c9858SDmitry Fleytman 97995129d6fSCornelia Huck if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { 980644c9858SDmitry Fleytman return VIRTIO_NET_ERR; 981644c9858SDmitry Fleytman } 982644c9858SDmitry Fleytman 983644c9858SDmitry Fleytman s = iov_to_buf(iov, iov_cnt, 0, &offloads, sizeof(offloads)); 984644c9858SDmitry Fleytman if (s != sizeof(offloads)) { 985644c9858SDmitry Fleytman return VIRTIO_NET_ERR; 986644c9858SDmitry Fleytman } 987644c9858SDmitry Fleytman 988644c9858SDmitry Fleytman if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) { 989644c9858SDmitry Fleytman uint64_t supported_offloads; 990644c9858SDmitry Fleytman 991189ae6bbSJason Wang offloads = virtio_ldq_p(vdev, &offloads); 992189ae6bbSJason Wang 993644c9858SDmitry Fleytman if (!n->has_vnet_hdr) { 994644c9858SDmitry Fleytman return VIRTIO_NET_ERR; 995644c9858SDmitry Fleytman } 996644c9858SDmitry Fleytman 9972974e916SYuri Benditovich n->rsc4_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) && 9982974e916SYuri Benditovich virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO4); 9992974e916SYuri Benditovich n->rsc6_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) && 10002974e916SYuri Benditovich virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO6); 10012974e916SYuri Benditovich virtio_clear_feature(&offloads, VIRTIO_NET_F_RSC_EXT); 10022974e916SYuri Benditovich 1003644c9858SDmitry Fleytman supported_offloads = virtio_net_supported_guest_offloads(n); 1004644c9858SDmitry Fleytman if (offloads & ~supported_offloads) { 1005644c9858SDmitry Fleytman return VIRTIO_NET_ERR; 1006644c9858SDmitry Fleytman } 1007644c9858SDmitry Fleytman 1008644c9858SDmitry Fleytman n->curr_guest_offloads = offloads; 1009644c9858SDmitry Fleytman virtio_net_apply_guest_offloads(n); 1010644c9858SDmitry Fleytman 1011644c9858SDmitry Fleytman return VIRTIO_NET_OK; 1012644c9858SDmitry Fleytman } else { 1013644c9858SDmitry Fleytman return VIRTIO_NET_ERR; 1014644c9858SDmitry Fleytman } 1015644c9858SDmitry Fleytman } 1016644c9858SDmitry Fleytman 10176e790746SPaolo Bonzini static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, 10186e790746SPaolo Bonzini struct iovec *iov, unsigned int iov_cnt) 10196e790746SPaolo Bonzini { 10201399c60dSRusty Russell VirtIODevice *vdev = VIRTIO_DEVICE(n); 10216e790746SPaolo Bonzini struct virtio_net_ctrl_mac mac_data; 10226e790746SPaolo Bonzini size_t s; 1023b1be4280SAmos Kong NetClientState *nc = qemu_get_queue(n->nic); 10246e790746SPaolo Bonzini 10256e790746SPaolo Bonzini if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) { 10266e790746SPaolo Bonzini if (iov_size(iov, iov_cnt) != sizeof(n->mac)) { 10276e790746SPaolo Bonzini return VIRTIO_NET_ERR; 10286e790746SPaolo Bonzini } 10296e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac)); 10306e790746SPaolo Bonzini assert(s == sizeof(n->mac)); 10316e790746SPaolo Bonzini qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); 1032b1be4280SAmos Kong rxfilter_notify(nc); 1033b1be4280SAmos Kong 10346e790746SPaolo Bonzini return VIRTIO_NET_OK; 10356e790746SPaolo Bonzini } 10366e790746SPaolo Bonzini 10376e790746SPaolo Bonzini if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) { 10386e790746SPaolo Bonzini return VIRTIO_NET_ERR; 10396e790746SPaolo Bonzini } 10406e790746SPaolo Bonzini 1041cae2e556SAmos Kong int in_use = 0; 1042cae2e556SAmos Kong int first_multi = 0; 1043cae2e556SAmos Kong uint8_t uni_overflow = 0; 1044cae2e556SAmos Kong uint8_t multi_overflow = 0; 1045cae2e556SAmos Kong uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); 10466e790746SPaolo Bonzini 10476e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, 10486e790746SPaolo Bonzini sizeof(mac_data.entries)); 10491399c60dSRusty Russell mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); 10506e790746SPaolo Bonzini if (s != sizeof(mac_data.entries)) { 1051b1be4280SAmos Kong goto error; 10526e790746SPaolo Bonzini } 10536e790746SPaolo Bonzini iov_discard_front(&iov, &iov_cnt, s); 10546e790746SPaolo Bonzini 10556e790746SPaolo Bonzini if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) { 1056b1be4280SAmos Kong goto error; 10576e790746SPaolo Bonzini } 10586e790746SPaolo Bonzini 10596e790746SPaolo Bonzini if (mac_data.entries <= MAC_TABLE_ENTRIES) { 1060cae2e556SAmos Kong s = iov_to_buf(iov, iov_cnt, 0, macs, 10616e790746SPaolo Bonzini mac_data.entries * ETH_ALEN); 10626e790746SPaolo Bonzini if (s != mac_data.entries * ETH_ALEN) { 1063b1be4280SAmos Kong goto error; 10646e790746SPaolo Bonzini } 1065cae2e556SAmos Kong in_use += mac_data.entries; 10666e790746SPaolo Bonzini } else { 1067cae2e556SAmos Kong uni_overflow = 1; 10686e790746SPaolo Bonzini } 10696e790746SPaolo Bonzini 10706e790746SPaolo Bonzini iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN); 10716e790746SPaolo Bonzini 1072cae2e556SAmos Kong first_multi = in_use; 10736e790746SPaolo Bonzini 10746e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, 10756e790746SPaolo Bonzini sizeof(mac_data.entries)); 10761399c60dSRusty Russell mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); 10776e790746SPaolo Bonzini if (s != sizeof(mac_data.entries)) { 1078b1be4280SAmos Kong goto error; 10796e790746SPaolo Bonzini } 10806e790746SPaolo Bonzini 10816e790746SPaolo Bonzini iov_discard_front(&iov, &iov_cnt, s); 10826e790746SPaolo Bonzini 10836e790746SPaolo Bonzini if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) { 1084b1be4280SAmos Kong goto error; 10856e790746SPaolo Bonzini } 10866e790746SPaolo Bonzini 1087edc24385SMichael S. Tsirkin if (mac_data.entries <= MAC_TABLE_ENTRIES - in_use) { 1088cae2e556SAmos Kong s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN], 10896e790746SPaolo Bonzini mac_data.entries * ETH_ALEN); 10906e790746SPaolo Bonzini if (s != mac_data.entries * ETH_ALEN) { 1091b1be4280SAmos Kong goto error; 10926e790746SPaolo Bonzini } 1093cae2e556SAmos Kong in_use += mac_data.entries; 10946e790746SPaolo Bonzini } else { 1095cae2e556SAmos Kong multi_overflow = 1; 10966e790746SPaolo Bonzini } 10976e790746SPaolo Bonzini 1098cae2e556SAmos Kong n->mac_table.in_use = in_use; 1099cae2e556SAmos Kong n->mac_table.first_multi = first_multi; 1100cae2e556SAmos Kong n->mac_table.uni_overflow = uni_overflow; 1101cae2e556SAmos Kong n->mac_table.multi_overflow = multi_overflow; 1102cae2e556SAmos Kong memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN); 1103cae2e556SAmos Kong g_free(macs); 1104b1be4280SAmos Kong rxfilter_notify(nc); 1105b1be4280SAmos Kong 11066e790746SPaolo Bonzini return VIRTIO_NET_OK; 1107b1be4280SAmos Kong 1108b1be4280SAmos Kong error: 1109cae2e556SAmos Kong g_free(macs); 1110b1be4280SAmos Kong return VIRTIO_NET_ERR; 11116e790746SPaolo Bonzini } 11126e790746SPaolo Bonzini 11136e790746SPaolo Bonzini static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, 11146e790746SPaolo Bonzini struct iovec *iov, unsigned int iov_cnt) 11156e790746SPaolo Bonzini { 11161399c60dSRusty Russell VirtIODevice *vdev = VIRTIO_DEVICE(n); 11176e790746SPaolo Bonzini uint16_t vid; 11186e790746SPaolo Bonzini size_t s; 1119b1be4280SAmos Kong NetClientState *nc = qemu_get_queue(n->nic); 11206e790746SPaolo Bonzini 11216e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid)); 11221399c60dSRusty Russell vid = virtio_lduw_p(vdev, &vid); 11236e790746SPaolo Bonzini if (s != sizeof(vid)) { 11246e790746SPaolo Bonzini return VIRTIO_NET_ERR; 11256e790746SPaolo Bonzini } 11266e790746SPaolo Bonzini 11276e790746SPaolo Bonzini if (vid >= MAX_VLAN) 11286e790746SPaolo Bonzini return VIRTIO_NET_ERR; 11296e790746SPaolo Bonzini 11306e790746SPaolo Bonzini if (cmd == VIRTIO_NET_CTRL_VLAN_ADD) 11316e790746SPaolo Bonzini n->vlans[vid >> 5] |= (1U << (vid & 0x1f)); 11326e790746SPaolo Bonzini else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL) 11336e790746SPaolo Bonzini n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f)); 11346e790746SPaolo Bonzini else 11356e790746SPaolo Bonzini return VIRTIO_NET_ERR; 11366e790746SPaolo Bonzini 1137b1be4280SAmos Kong rxfilter_notify(nc); 1138b1be4280SAmos Kong 11396e790746SPaolo Bonzini return VIRTIO_NET_OK; 11406e790746SPaolo Bonzini } 11416e790746SPaolo Bonzini 1142f57fcf70SJason Wang static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd, 1143f57fcf70SJason Wang struct iovec *iov, unsigned int iov_cnt) 1144f57fcf70SJason Wang { 11459d8c6a25SDr. David Alan Gilbert trace_virtio_net_handle_announce(n->announce_timer.round); 1146f57fcf70SJason Wang if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK && 1147f57fcf70SJason Wang n->status & VIRTIO_NET_S_ANNOUNCE) { 1148f57fcf70SJason Wang n->status &= ~VIRTIO_NET_S_ANNOUNCE; 11499d8c6a25SDr. David Alan Gilbert if (n->announce_timer.round) { 11509d8c6a25SDr. David Alan Gilbert qemu_announce_timer_step(&n->announce_timer); 1151f57fcf70SJason Wang } 1152f57fcf70SJason Wang return VIRTIO_NET_OK; 1153f57fcf70SJason Wang } else { 1154f57fcf70SJason Wang return VIRTIO_NET_ERR; 1155f57fcf70SJason Wang } 1156f57fcf70SJason Wang } 1157f57fcf70SJason Wang 1158*59079029SYuri Benditovich static void virtio_net_disable_rss(VirtIONet *n) 1159*59079029SYuri Benditovich { 1160*59079029SYuri Benditovich if (n->rss_data.enabled) { 1161*59079029SYuri Benditovich trace_virtio_net_rss_disable(); 1162*59079029SYuri Benditovich } 1163*59079029SYuri Benditovich n->rss_data.enabled = false; 1164*59079029SYuri Benditovich } 1165*59079029SYuri Benditovich 1166*59079029SYuri Benditovich static uint16_t virtio_net_handle_rss(VirtIONet *n, 1167*59079029SYuri Benditovich struct iovec *iov, unsigned int iov_cnt) 1168*59079029SYuri Benditovich { 1169*59079029SYuri Benditovich VirtIODevice *vdev = VIRTIO_DEVICE(n); 1170*59079029SYuri Benditovich struct virtio_net_rss_config cfg; 1171*59079029SYuri Benditovich size_t s, offset = 0, size_get; 1172*59079029SYuri Benditovich uint16_t queues, i; 1173*59079029SYuri Benditovich struct { 1174*59079029SYuri Benditovich uint16_t us; 1175*59079029SYuri Benditovich uint8_t b; 1176*59079029SYuri Benditovich } QEMU_PACKED temp; 1177*59079029SYuri Benditovich const char *err_msg = ""; 1178*59079029SYuri Benditovich uint32_t err_value = 0; 1179*59079029SYuri Benditovich 1180*59079029SYuri Benditovich if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_RSS)) { 1181*59079029SYuri Benditovich err_msg = "RSS is not negotiated"; 1182*59079029SYuri Benditovich goto error; 1183*59079029SYuri Benditovich } 1184*59079029SYuri Benditovich size_get = offsetof(struct virtio_net_rss_config, indirection_table); 1185*59079029SYuri Benditovich s = iov_to_buf(iov, iov_cnt, offset, &cfg, size_get); 1186*59079029SYuri Benditovich if (s != size_get) { 1187*59079029SYuri Benditovich err_msg = "Short command buffer"; 1188*59079029SYuri Benditovich err_value = (uint32_t)s; 1189*59079029SYuri Benditovich goto error; 1190*59079029SYuri Benditovich } 1191*59079029SYuri Benditovich n->rss_data.hash_types = virtio_ldl_p(vdev, &cfg.hash_types); 1192*59079029SYuri Benditovich n->rss_data.indirections_len = 1193*59079029SYuri Benditovich virtio_lduw_p(vdev, &cfg.indirection_table_mask); 1194*59079029SYuri Benditovich n->rss_data.indirections_len++; 1195*59079029SYuri Benditovich if (!is_power_of_2(n->rss_data.indirections_len)) { 1196*59079029SYuri Benditovich err_msg = "Invalid size of indirection table"; 1197*59079029SYuri Benditovich err_value = n->rss_data.indirections_len; 1198*59079029SYuri Benditovich goto error; 1199*59079029SYuri Benditovich } 1200*59079029SYuri Benditovich if (n->rss_data.indirections_len > VIRTIO_NET_RSS_MAX_TABLE_LEN) { 1201*59079029SYuri Benditovich err_msg = "Too large indirection table"; 1202*59079029SYuri Benditovich err_value = n->rss_data.indirections_len; 1203*59079029SYuri Benditovich goto error; 1204*59079029SYuri Benditovich } 1205*59079029SYuri Benditovich n->rss_data.default_queue = 1206*59079029SYuri Benditovich virtio_lduw_p(vdev, &cfg.unclassified_queue); 1207*59079029SYuri Benditovich if (n->rss_data.default_queue >= n->max_queues) { 1208*59079029SYuri Benditovich err_msg = "Invalid default queue"; 1209*59079029SYuri Benditovich err_value = n->rss_data.default_queue; 1210*59079029SYuri Benditovich goto error; 1211*59079029SYuri Benditovich } 1212*59079029SYuri Benditovich offset += size_get; 1213*59079029SYuri Benditovich size_get = sizeof(uint16_t) * n->rss_data.indirections_len; 1214*59079029SYuri Benditovich g_free(n->rss_data.indirections_table); 1215*59079029SYuri Benditovich n->rss_data.indirections_table = g_malloc(size_get); 1216*59079029SYuri Benditovich if (!n->rss_data.indirections_table) { 1217*59079029SYuri Benditovich err_msg = "Can't allocate indirections table"; 1218*59079029SYuri Benditovich err_value = n->rss_data.indirections_len; 1219*59079029SYuri Benditovich goto error; 1220*59079029SYuri Benditovich } 1221*59079029SYuri Benditovich s = iov_to_buf(iov, iov_cnt, offset, 1222*59079029SYuri Benditovich n->rss_data.indirections_table, size_get); 1223*59079029SYuri Benditovich if (s != size_get) { 1224*59079029SYuri Benditovich err_msg = "Short indirection table buffer"; 1225*59079029SYuri Benditovich err_value = (uint32_t)s; 1226*59079029SYuri Benditovich goto error; 1227*59079029SYuri Benditovich } 1228*59079029SYuri Benditovich for (i = 0; i < n->rss_data.indirections_len; ++i) { 1229*59079029SYuri Benditovich uint16_t val = n->rss_data.indirections_table[i]; 1230*59079029SYuri Benditovich n->rss_data.indirections_table[i] = virtio_lduw_p(vdev, &val); 1231*59079029SYuri Benditovich } 1232*59079029SYuri Benditovich offset += size_get; 1233*59079029SYuri Benditovich size_get = sizeof(temp); 1234*59079029SYuri Benditovich s = iov_to_buf(iov, iov_cnt, offset, &temp, size_get); 1235*59079029SYuri Benditovich if (s != size_get) { 1236*59079029SYuri Benditovich err_msg = "Can't get queues"; 1237*59079029SYuri Benditovich err_value = (uint32_t)s; 1238*59079029SYuri Benditovich goto error; 1239*59079029SYuri Benditovich } 1240*59079029SYuri Benditovich queues = virtio_lduw_p(vdev, &temp.us); 1241*59079029SYuri Benditovich if (queues == 0 || queues > n->max_queues) { 1242*59079029SYuri Benditovich err_msg = "Invalid number of queues"; 1243*59079029SYuri Benditovich err_value = queues; 1244*59079029SYuri Benditovich goto error; 1245*59079029SYuri Benditovich } 1246*59079029SYuri Benditovich if (temp.b > VIRTIO_NET_RSS_MAX_KEY_SIZE) { 1247*59079029SYuri Benditovich err_msg = "Invalid key size"; 1248*59079029SYuri Benditovich err_value = temp.b; 1249*59079029SYuri Benditovich goto error; 1250*59079029SYuri Benditovich } 1251*59079029SYuri Benditovich if (!temp.b && n->rss_data.hash_types) { 1252*59079029SYuri Benditovich err_msg = "No key provided"; 1253*59079029SYuri Benditovich err_value = 0; 1254*59079029SYuri Benditovich goto error; 1255*59079029SYuri Benditovich } 1256*59079029SYuri Benditovich if (!temp.b && !n->rss_data.hash_types) { 1257*59079029SYuri Benditovich virtio_net_disable_rss(n); 1258*59079029SYuri Benditovich return queues; 1259*59079029SYuri Benditovich } 1260*59079029SYuri Benditovich offset += size_get; 1261*59079029SYuri Benditovich size_get = temp.b; 1262*59079029SYuri Benditovich s = iov_to_buf(iov, iov_cnt, offset, n->rss_data.key, size_get); 1263*59079029SYuri Benditovich if (s != size_get) { 1264*59079029SYuri Benditovich err_msg = "Can get key buffer"; 1265*59079029SYuri Benditovich err_value = (uint32_t)s; 1266*59079029SYuri Benditovich goto error; 1267*59079029SYuri Benditovich } 1268*59079029SYuri Benditovich n->rss_data.enabled = true; 1269*59079029SYuri Benditovich trace_virtio_net_rss_enable(n->rss_data.hash_types, 1270*59079029SYuri Benditovich n->rss_data.indirections_len, 1271*59079029SYuri Benditovich temp.b); 1272*59079029SYuri Benditovich return queues; 1273*59079029SYuri Benditovich error: 1274*59079029SYuri Benditovich trace_virtio_net_rss_error(err_msg, err_value); 1275*59079029SYuri Benditovich virtio_net_disable_rss(n); 1276*59079029SYuri Benditovich return 0; 1277*59079029SYuri Benditovich } 1278*59079029SYuri Benditovich 12796e790746SPaolo Bonzini static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, 12806e790746SPaolo Bonzini struct iovec *iov, unsigned int iov_cnt) 12816e790746SPaolo Bonzini { 128217a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 12836e790746SPaolo Bonzini uint16_t queues; 12846e790746SPaolo Bonzini 1285*59079029SYuri Benditovich virtio_net_disable_rss(n); 1286*59079029SYuri Benditovich if (cmd == VIRTIO_NET_CTRL_MQ_RSS_CONFIG) { 1287*59079029SYuri Benditovich queues = virtio_net_handle_rss(n, iov, iov_cnt); 1288*59079029SYuri Benditovich } else if (cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { 1289*59079029SYuri Benditovich struct virtio_net_ctrl_mq mq; 1290*59079029SYuri Benditovich size_t s; 1291*59079029SYuri Benditovich if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_MQ)) { 1292*59079029SYuri Benditovich return VIRTIO_NET_ERR; 1293*59079029SYuri Benditovich } 12946e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq)); 12956e790746SPaolo Bonzini if (s != sizeof(mq)) { 12966e790746SPaolo Bonzini return VIRTIO_NET_ERR; 12976e790746SPaolo Bonzini } 1298*59079029SYuri Benditovich queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs); 12996e790746SPaolo Bonzini 1300*59079029SYuri Benditovich } else { 13016e790746SPaolo Bonzini return VIRTIO_NET_ERR; 13026e790746SPaolo Bonzini } 13036e790746SPaolo Bonzini 13046e790746SPaolo Bonzini if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || 13056e790746SPaolo Bonzini queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || 13066e790746SPaolo Bonzini queues > n->max_queues || 13076e790746SPaolo Bonzini !n->multiqueue) { 13086e790746SPaolo Bonzini return VIRTIO_NET_ERR; 13096e790746SPaolo Bonzini } 13106e790746SPaolo Bonzini 13116e790746SPaolo Bonzini n->curr_queues = queues; 13126e790746SPaolo Bonzini /* stop the backend before changing the number of queues to avoid handling a 13136e790746SPaolo Bonzini * disabled queue */ 131417a0ca55SKONRAD Frederic virtio_net_set_status(vdev, vdev->status); 13156e790746SPaolo Bonzini virtio_net_set_queues(n); 13166e790746SPaolo Bonzini 13176e790746SPaolo Bonzini return VIRTIO_NET_OK; 13186e790746SPaolo Bonzini } 1319ba7eadb5SGreg Kurz 13206e790746SPaolo Bonzini static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) 13216e790746SPaolo Bonzini { 132217a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 13236e790746SPaolo Bonzini struct virtio_net_ctrl_hdr ctrl; 13246e790746SPaolo Bonzini virtio_net_ctrl_ack status = VIRTIO_NET_ERR; 132551b19ebeSPaolo Bonzini VirtQueueElement *elem; 13266e790746SPaolo Bonzini size_t s; 1327771b6ed3SJason Wang struct iovec *iov, *iov2; 13286e790746SPaolo Bonzini unsigned int iov_cnt; 13296e790746SPaolo Bonzini 133051b19ebeSPaolo Bonzini for (;;) { 133151b19ebeSPaolo Bonzini elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 133251b19ebeSPaolo Bonzini if (!elem) { 133351b19ebeSPaolo Bonzini break; 133451b19ebeSPaolo Bonzini } 133551b19ebeSPaolo Bonzini if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) || 133651b19ebeSPaolo Bonzini iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) { 1337ba7eadb5SGreg Kurz virtio_error(vdev, "virtio-net ctrl missing headers"); 1338ba7eadb5SGreg Kurz virtqueue_detach_element(vq, elem, 0); 1339ba7eadb5SGreg Kurz g_free(elem); 1340ba7eadb5SGreg Kurz break; 13416e790746SPaolo Bonzini } 13426e790746SPaolo Bonzini 134351b19ebeSPaolo Bonzini iov_cnt = elem->out_num; 134451b19ebeSPaolo Bonzini iov2 = iov = g_memdup(elem->out_sg, sizeof(struct iovec) * elem->out_num); 13456e790746SPaolo Bonzini s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl)); 13466e790746SPaolo Bonzini iov_discard_front(&iov, &iov_cnt, sizeof(ctrl)); 13476e790746SPaolo Bonzini if (s != sizeof(ctrl)) { 13486e790746SPaolo Bonzini status = VIRTIO_NET_ERR; 13496e790746SPaolo Bonzini } else if (ctrl.class == VIRTIO_NET_CTRL_RX) { 13506e790746SPaolo Bonzini status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt); 13516e790746SPaolo Bonzini } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) { 13526e790746SPaolo Bonzini status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt); 13536e790746SPaolo Bonzini } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { 13546e790746SPaolo Bonzini status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt); 1355f57fcf70SJason Wang } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) { 1356f57fcf70SJason Wang status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt); 13576e790746SPaolo Bonzini } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { 13586e790746SPaolo Bonzini status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt); 1359644c9858SDmitry Fleytman } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) { 1360644c9858SDmitry Fleytman status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt); 13616e790746SPaolo Bonzini } 13626e790746SPaolo Bonzini 136351b19ebeSPaolo Bonzini s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status)); 13646e790746SPaolo Bonzini assert(s == sizeof(status)); 13656e790746SPaolo Bonzini 136651b19ebeSPaolo Bonzini virtqueue_push(vq, elem, sizeof(status)); 13676e790746SPaolo Bonzini virtio_notify(vdev, vq); 1368771b6ed3SJason Wang g_free(iov2); 136951b19ebeSPaolo Bonzini g_free(elem); 13706e790746SPaolo Bonzini } 13716e790746SPaolo Bonzini } 13726e790746SPaolo Bonzini 13736e790746SPaolo Bonzini /* RX */ 13746e790746SPaolo Bonzini 13756e790746SPaolo Bonzini static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) 13766e790746SPaolo Bonzini { 137717a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 13786e790746SPaolo Bonzini int queue_index = vq2q(virtio_get_queue_index(vq)); 13796e790746SPaolo Bonzini 13806e790746SPaolo Bonzini qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index)); 13816e790746SPaolo Bonzini } 13826e790746SPaolo Bonzini 1383b8c4b67eSPhilippe Mathieu-Daudé static bool virtio_net_can_receive(NetClientState *nc) 13846e790746SPaolo Bonzini { 13856e790746SPaolo Bonzini VirtIONet *n = qemu_get_nic_opaque(nc); 138617a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 13876e790746SPaolo Bonzini VirtIONetQueue *q = virtio_net_get_subqueue(nc); 13886e790746SPaolo Bonzini 138917a0ca55SKONRAD Frederic if (!vdev->vm_running) { 1390b8c4b67eSPhilippe Mathieu-Daudé return false; 13916e790746SPaolo Bonzini } 13926e790746SPaolo Bonzini 13936e790746SPaolo Bonzini if (nc->queue_index >= n->curr_queues) { 1394b8c4b67eSPhilippe Mathieu-Daudé return false; 13956e790746SPaolo Bonzini } 13966e790746SPaolo Bonzini 13976e790746SPaolo Bonzini if (!virtio_queue_ready(q->rx_vq) || 139817a0ca55SKONRAD Frederic !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { 1399b8c4b67eSPhilippe Mathieu-Daudé return false; 14006e790746SPaolo Bonzini } 14016e790746SPaolo Bonzini 1402b8c4b67eSPhilippe Mathieu-Daudé return true; 14036e790746SPaolo Bonzini } 14046e790746SPaolo Bonzini 14056e790746SPaolo Bonzini static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) 14066e790746SPaolo Bonzini { 14076e790746SPaolo Bonzini VirtIONet *n = q->n; 14086e790746SPaolo Bonzini if (virtio_queue_empty(q->rx_vq) || 14096e790746SPaolo Bonzini (n->mergeable_rx_bufs && 14106e790746SPaolo Bonzini !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { 14116e790746SPaolo Bonzini virtio_queue_set_notification(q->rx_vq, 1); 14126e790746SPaolo Bonzini 14136e790746SPaolo Bonzini /* To avoid a race condition where the guest has made some buffers 14146e790746SPaolo Bonzini * available after the above check but before notification was 14156e790746SPaolo Bonzini * enabled, check for available buffers again. 14166e790746SPaolo Bonzini */ 14176e790746SPaolo Bonzini if (virtio_queue_empty(q->rx_vq) || 14186e790746SPaolo Bonzini (n->mergeable_rx_bufs && 14196e790746SPaolo Bonzini !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { 14206e790746SPaolo Bonzini return 0; 14216e790746SPaolo Bonzini } 14226e790746SPaolo Bonzini } 14236e790746SPaolo Bonzini 14246e790746SPaolo Bonzini virtio_queue_set_notification(q->rx_vq, 0); 14256e790746SPaolo Bonzini return 1; 14266e790746SPaolo Bonzini } 14276e790746SPaolo Bonzini 14281399c60dSRusty Russell static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr) 1429032a74a1SCédric Le Goater { 14301399c60dSRusty Russell virtio_tswap16s(vdev, &hdr->hdr_len); 14311399c60dSRusty Russell virtio_tswap16s(vdev, &hdr->gso_size); 14321399c60dSRusty Russell virtio_tswap16s(vdev, &hdr->csum_start); 14331399c60dSRusty Russell virtio_tswap16s(vdev, &hdr->csum_offset); 1434032a74a1SCédric Le Goater } 1435032a74a1SCédric Le Goater 14366e790746SPaolo Bonzini /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so 14376e790746SPaolo Bonzini * it never finds out that the packets don't have valid checksums. This 14386e790746SPaolo Bonzini * causes dhclient to get upset. Fedora's carried a patch for ages to 14396e790746SPaolo Bonzini * fix this with Xen but it hasn't appeared in an upstream release of 14406e790746SPaolo Bonzini * dhclient yet. 14416e790746SPaolo Bonzini * 14426e790746SPaolo Bonzini * To avoid breaking existing guests, we catch udp packets and add 14436e790746SPaolo Bonzini * checksums. This is terrible but it's better than hacking the guest 14446e790746SPaolo Bonzini * kernels. 14456e790746SPaolo Bonzini * 14466e790746SPaolo Bonzini * N.B. if we introduce a zero-copy API, this operation is no longer free so 14476e790746SPaolo Bonzini * we should provide a mechanism to disable it to avoid polluting the host 14486e790746SPaolo Bonzini * cache. 14496e790746SPaolo Bonzini */ 14506e790746SPaolo Bonzini static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, 14516e790746SPaolo Bonzini uint8_t *buf, size_t size) 14526e790746SPaolo Bonzini { 14536e790746SPaolo Bonzini if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */ 14546e790746SPaolo Bonzini (size > 27 && size < 1500) && /* normal sized MTU */ 14556e790746SPaolo Bonzini (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */ 14566e790746SPaolo Bonzini (buf[23] == 17) && /* ip.protocol == UDP */ 14576e790746SPaolo Bonzini (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */ 14586e790746SPaolo Bonzini net_checksum_calculate(buf, size); 14596e790746SPaolo Bonzini hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; 14606e790746SPaolo Bonzini } 14616e790746SPaolo Bonzini } 14626e790746SPaolo Bonzini 14636e790746SPaolo Bonzini static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, 14646e790746SPaolo Bonzini const void *buf, size_t size) 14656e790746SPaolo Bonzini { 14666e790746SPaolo Bonzini if (n->has_vnet_hdr) { 14676e790746SPaolo Bonzini /* FIXME this cast is evil */ 14686e790746SPaolo Bonzini void *wbuf = (void *)buf; 14696e790746SPaolo Bonzini work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, 14706e790746SPaolo Bonzini size - n->host_hdr_len); 14711bfa316cSGreg Kurz 14721bfa316cSGreg Kurz if (n->needs_vnet_hdr_swap) { 14731399c60dSRusty Russell virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); 14741bfa316cSGreg Kurz } 14756e790746SPaolo Bonzini iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); 14766e790746SPaolo Bonzini } else { 14776e790746SPaolo Bonzini struct virtio_net_hdr hdr = { 14786e790746SPaolo Bonzini .flags = 0, 14796e790746SPaolo Bonzini .gso_type = VIRTIO_NET_HDR_GSO_NONE 14806e790746SPaolo Bonzini }; 14816e790746SPaolo Bonzini iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr); 14826e790746SPaolo Bonzini } 14836e790746SPaolo Bonzini } 14846e790746SPaolo Bonzini 14856e790746SPaolo Bonzini static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) 14866e790746SPaolo Bonzini { 14876e790746SPaolo Bonzini static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 14886e790746SPaolo Bonzini static const uint8_t vlan[] = {0x81, 0x00}; 14896e790746SPaolo Bonzini uint8_t *ptr = (uint8_t *)buf; 14906e790746SPaolo Bonzini int i; 14916e790746SPaolo Bonzini 14926e790746SPaolo Bonzini if (n->promisc) 14936e790746SPaolo Bonzini return 1; 14946e790746SPaolo Bonzini 14956e790746SPaolo Bonzini ptr += n->host_hdr_len; 14966e790746SPaolo Bonzini 14976e790746SPaolo Bonzini if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { 14987542d3e7SPeter Maydell int vid = lduw_be_p(ptr + 14) & 0xfff; 14996e790746SPaolo Bonzini if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f)))) 15006e790746SPaolo Bonzini return 0; 15016e790746SPaolo Bonzini } 15026e790746SPaolo Bonzini 15036e790746SPaolo Bonzini if (ptr[0] & 1) { // multicast 15046e790746SPaolo Bonzini if (!memcmp(ptr, bcast, sizeof(bcast))) { 15056e790746SPaolo Bonzini return !n->nobcast; 15066e790746SPaolo Bonzini } else if (n->nomulti) { 15076e790746SPaolo Bonzini return 0; 15086e790746SPaolo Bonzini } else if (n->allmulti || n->mac_table.multi_overflow) { 15096e790746SPaolo Bonzini return 1; 15106e790746SPaolo Bonzini } 15116e790746SPaolo Bonzini 15126e790746SPaolo Bonzini for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) { 15136e790746SPaolo Bonzini if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) { 15146e790746SPaolo Bonzini return 1; 15156e790746SPaolo Bonzini } 15166e790746SPaolo Bonzini } 15176e790746SPaolo Bonzini } else { // unicast 15186e790746SPaolo Bonzini if (n->nouni) { 15196e790746SPaolo Bonzini return 0; 15206e790746SPaolo Bonzini } else if (n->alluni || n->mac_table.uni_overflow) { 15216e790746SPaolo Bonzini return 1; 15226e790746SPaolo Bonzini } else if (!memcmp(ptr, n->mac, ETH_ALEN)) { 15236e790746SPaolo Bonzini return 1; 15246e790746SPaolo Bonzini } 15256e790746SPaolo Bonzini 15266e790746SPaolo Bonzini for (i = 0; i < n->mac_table.first_multi; i++) { 15276e790746SPaolo Bonzini if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) { 15286e790746SPaolo Bonzini return 1; 15296e790746SPaolo Bonzini } 15306e790746SPaolo Bonzini } 15316e790746SPaolo Bonzini } 15326e790746SPaolo Bonzini 15336e790746SPaolo Bonzini return 0; 15346e790746SPaolo Bonzini } 15356e790746SPaolo Bonzini 153697cd965cSPaolo Bonzini static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, 153797cd965cSPaolo Bonzini size_t size) 15386e790746SPaolo Bonzini { 15396e790746SPaolo Bonzini VirtIONet *n = qemu_get_nic_opaque(nc); 15406e790746SPaolo Bonzini VirtIONetQueue *q = virtio_net_get_subqueue(nc); 154117a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 15426e790746SPaolo Bonzini struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; 15436e790746SPaolo Bonzini struct virtio_net_hdr_mrg_rxbuf mhdr; 15446e790746SPaolo Bonzini unsigned mhdr_cnt = 0; 15456e790746SPaolo Bonzini size_t offset, i, guest_offset; 15466e790746SPaolo Bonzini 15476e790746SPaolo Bonzini if (!virtio_net_can_receive(nc)) { 15486e790746SPaolo Bonzini return -1; 15496e790746SPaolo Bonzini } 15506e790746SPaolo Bonzini 15516e790746SPaolo Bonzini /* hdr_len refers to the header we supply to the guest */ 15526e790746SPaolo Bonzini if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) { 15536e790746SPaolo Bonzini return 0; 15546e790746SPaolo Bonzini } 15556e790746SPaolo Bonzini 15566e790746SPaolo Bonzini if (!receive_filter(n, buf, size)) 15576e790746SPaolo Bonzini return size; 15586e790746SPaolo Bonzini 15596e790746SPaolo Bonzini offset = i = 0; 15606e790746SPaolo Bonzini 15616e790746SPaolo Bonzini while (offset < size) { 156251b19ebeSPaolo Bonzini VirtQueueElement *elem; 15636e790746SPaolo Bonzini int len, total; 156451b19ebeSPaolo Bonzini const struct iovec *sg; 15656e790746SPaolo Bonzini 15666e790746SPaolo Bonzini total = 0; 15676e790746SPaolo Bonzini 156851b19ebeSPaolo Bonzini elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement)); 156951b19ebeSPaolo Bonzini if (!elem) { 1570ba10b9c0SGreg Kurz if (i) { 1571ba10b9c0SGreg Kurz virtio_error(vdev, "virtio-net unexpected empty queue: " 15726e790746SPaolo Bonzini "i %zd mergeable %d offset %zd, size %zd, " 1573019a3edbSGerd Hoffmann "guest hdr len %zd, host hdr len %zd " 1574019a3edbSGerd Hoffmann "guest features 0x%" PRIx64, 15756e790746SPaolo Bonzini i, n->mergeable_rx_bufs, offset, size, 1576019a3edbSGerd Hoffmann n->guest_hdr_len, n->host_hdr_len, 1577019a3edbSGerd Hoffmann vdev->guest_features); 1578ba10b9c0SGreg Kurz } 1579ba10b9c0SGreg Kurz return -1; 15806e790746SPaolo Bonzini } 15816e790746SPaolo Bonzini 158251b19ebeSPaolo Bonzini if (elem->in_num < 1) { 1583ba10b9c0SGreg Kurz virtio_error(vdev, 1584ba10b9c0SGreg Kurz "virtio-net receive queue contains no in buffers"); 1585ba10b9c0SGreg Kurz virtqueue_detach_element(q->rx_vq, elem, 0); 1586ba10b9c0SGreg Kurz g_free(elem); 1587ba10b9c0SGreg Kurz return -1; 15886e790746SPaolo Bonzini } 15896e790746SPaolo Bonzini 159051b19ebeSPaolo Bonzini sg = elem->in_sg; 15916e790746SPaolo Bonzini if (i == 0) { 15926e790746SPaolo Bonzini assert(offset == 0); 15936e790746SPaolo Bonzini if (n->mergeable_rx_bufs) { 15946e790746SPaolo Bonzini mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg), 159551b19ebeSPaolo Bonzini sg, elem->in_num, 15966e790746SPaolo Bonzini offsetof(typeof(mhdr), num_buffers), 15976e790746SPaolo Bonzini sizeof(mhdr.num_buffers)); 15986e790746SPaolo Bonzini } 15996e790746SPaolo Bonzini 160051b19ebeSPaolo Bonzini receive_header(n, sg, elem->in_num, buf, size); 16016e790746SPaolo Bonzini offset = n->host_hdr_len; 16026e790746SPaolo Bonzini total += n->guest_hdr_len; 16036e790746SPaolo Bonzini guest_offset = n->guest_hdr_len; 16046e790746SPaolo Bonzini } else { 16056e790746SPaolo Bonzini guest_offset = 0; 16066e790746SPaolo Bonzini } 16076e790746SPaolo Bonzini 16086e790746SPaolo Bonzini /* copy in packet. ugh */ 160951b19ebeSPaolo Bonzini len = iov_from_buf(sg, elem->in_num, guest_offset, 16106e790746SPaolo Bonzini buf + offset, size - offset); 16116e790746SPaolo Bonzini total += len; 16126e790746SPaolo Bonzini offset += len; 16136e790746SPaolo Bonzini /* If buffers can't be merged, at this point we 16146e790746SPaolo Bonzini * must have consumed the complete packet. 16156e790746SPaolo Bonzini * Otherwise, drop it. */ 16166e790746SPaolo Bonzini if (!n->mergeable_rx_bufs && offset < size) { 161727e57efeSLadi Prosek virtqueue_unpop(q->rx_vq, elem, total); 161851b19ebeSPaolo Bonzini g_free(elem); 16196e790746SPaolo Bonzini return size; 16206e790746SPaolo Bonzini } 16216e790746SPaolo Bonzini 16226e790746SPaolo Bonzini /* signal other side */ 162351b19ebeSPaolo Bonzini virtqueue_fill(q->rx_vq, elem, total, i++); 162451b19ebeSPaolo Bonzini g_free(elem); 16256e790746SPaolo Bonzini } 16266e790746SPaolo Bonzini 16276e790746SPaolo Bonzini if (mhdr_cnt) { 16281399c60dSRusty Russell virtio_stw_p(vdev, &mhdr.num_buffers, i); 16296e790746SPaolo Bonzini iov_from_buf(mhdr_sg, mhdr_cnt, 16306e790746SPaolo Bonzini 0, 16316e790746SPaolo Bonzini &mhdr.num_buffers, sizeof mhdr.num_buffers); 16326e790746SPaolo Bonzini } 16336e790746SPaolo Bonzini 16346e790746SPaolo Bonzini virtqueue_flush(q->rx_vq, i); 163517a0ca55SKONRAD Frederic virtio_notify(vdev, q->rx_vq); 16366e790746SPaolo Bonzini 16376e790746SPaolo Bonzini return size; 16386e790746SPaolo Bonzini } 16396e790746SPaolo Bonzini 16402974e916SYuri Benditovich static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf, 164197cd965cSPaolo Bonzini size_t size) 164297cd965cSPaolo Bonzini { 1643068ddfa9SDr. David Alan Gilbert RCU_READ_LOCK_GUARD(); 164497cd965cSPaolo Bonzini 1645068ddfa9SDr. David Alan Gilbert return virtio_net_receive_rcu(nc, buf, size); 164697cd965cSPaolo Bonzini } 164797cd965cSPaolo Bonzini 16482974e916SYuri Benditovich static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain, 16492974e916SYuri Benditovich const uint8_t *buf, 16502974e916SYuri Benditovich VirtioNetRscUnit *unit) 16512974e916SYuri Benditovich { 16522974e916SYuri Benditovich uint16_t ip_hdrlen; 16532974e916SYuri Benditovich struct ip_header *ip; 16542974e916SYuri Benditovich 16552974e916SYuri Benditovich ip = (struct ip_header *)(buf + chain->n->guest_hdr_len 16562974e916SYuri Benditovich + sizeof(struct eth_header)); 16572974e916SYuri Benditovich unit->ip = (void *)ip; 16582974e916SYuri Benditovich ip_hdrlen = (ip->ip_ver_len & 0xF) << 2; 16592974e916SYuri Benditovich unit->ip_plen = &ip->ip_len; 16602974e916SYuri Benditovich unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen); 16612974e916SYuri Benditovich unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10; 16622974e916SYuri Benditovich unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen; 16632974e916SYuri Benditovich } 16642974e916SYuri Benditovich 16652974e916SYuri Benditovich static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain, 16662974e916SYuri Benditovich const uint8_t *buf, 16672974e916SYuri Benditovich VirtioNetRscUnit *unit) 16682974e916SYuri Benditovich { 16692974e916SYuri Benditovich struct ip6_header *ip6; 16702974e916SYuri Benditovich 16712974e916SYuri Benditovich ip6 = (struct ip6_header *)(buf + chain->n->guest_hdr_len 16722974e916SYuri Benditovich + sizeof(struct eth_header)); 16732974e916SYuri Benditovich unit->ip = ip6; 16742974e916SYuri Benditovich unit->ip_plen = &(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); 167578ee6bd0SPhilippe Mathieu-Daudé unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) 16762974e916SYuri Benditovich + sizeof(struct ip6_header)); 16772974e916SYuri Benditovich unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10; 16782974e916SYuri Benditovich 16792974e916SYuri Benditovich /* There is a difference between payload lenght in ipv4 and v6, 16802974e916SYuri Benditovich ip header is excluded in ipv6 */ 16812974e916SYuri Benditovich unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen; 16822974e916SYuri Benditovich } 16832974e916SYuri Benditovich 16842974e916SYuri Benditovich static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain, 16852974e916SYuri Benditovich VirtioNetRscSeg *seg) 16862974e916SYuri Benditovich { 16872974e916SYuri Benditovich int ret; 16882974e916SYuri Benditovich struct virtio_net_hdr *h; 16892974e916SYuri Benditovich 16902974e916SYuri Benditovich h = (struct virtio_net_hdr *)seg->buf; 16912974e916SYuri Benditovich h->flags = 0; 16922974e916SYuri Benditovich h->gso_type = VIRTIO_NET_HDR_GSO_NONE; 16932974e916SYuri Benditovich 16942974e916SYuri Benditovich if (seg->is_coalesced) { 16952974e916SYuri Benditovich *virtio_net_rsc_ext_num_packets(h) = seg->packets; 16962974e916SYuri Benditovich *virtio_net_rsc_ext_num_dupacks(h) = seg->dup_ack; 16972974e916SYuri Benditovich h->flags = VIRTIO_NET_HDR_F_RSC_INFO; 16982974e916SYuri Benditovich if (chain->proto == ETH_P_IP) { 16992974e916SYuri Benditovich h->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; 17002974e916SYuri Benditovich } else { 17012974e916SYuri Benditovich h->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; 17022974e916SYuri Benditovich } 17032974e916SYuri Benditovich } 17042974e916SYuri Benditovich 17052974e916SYuri Benditovich ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size); 17062974e916SYuri Benditovich QTAILQ_REMOVE(&chain->buffers, seg, next); 17072974e916SYuri Benditovich g_free(seg->buf); 17082974e916SYuri Benditovich g_free(seg); 17092974e916SYuri Benditovich 17102974e916SYuri Benditovich return ret; 17112974e916SYuri Benditovich } 17122974e916SYuri Benditovich 17132974e916SYuri Benditovich static void virtio_net_rsc_purge(void *opq) 17142974e916SYuri Benditovich { 17152974e916SYuri Benditovich VirtioNetRscSeg *seg, *rn; 17162974e916SYuri Benditovich VirtioNetRscChain *chain = (VirtioNetRscChain *)opq; 17172974e916SYuri Benditovich 17182974e916SYuri Benditovich QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn) { 17192974e916SYuri Benditovich if (virtio_net_rsc_drain_seg(chain, seg) == 0) { 17202974e916SYuri Benditovich chain->stat.purge_failed++; 17212974e916SYuri Benditovich continue; 17222974e916SYuri Benditovich } 17232974e916SYuri Benditovich } 17242974e916SYuri Benditovich 17252974e916SYuri Benditovich chain->stat.timer++; 17262974e916SYuri Benditovich if (!QTAILQ_EMPTY(&chain->buffers)) { 17272974e916SYuri Benditovich timer_mod(chain->drain_timer, 17282974e916SYuri Benditovich qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout); 17292974e916SYuri Benditovich } 17302974e916SYuri Benditovich } 17312974e916SYuri Benditovich 17322974e916SYuri Benditovich static void virtio_net_rsc_cleanup(VirtIONet *n) 17332974e916SYuri Benditovich { 17342974e916SYuri Benditovich VirtioNetRscChain *chain, *rn_chain; 17352974e916SYuri Benditovich VirtioNetRscSeg *seg, *rn_seg; 17362974e916SYuri Benditovich 17372974e916SYuri Benditovich QTAILQ_FOREACH_SAFE(chain, &n->rsc_chains, next, rn_chain) { 17382974e916SYuri Benditovich QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn_seg) { 17392974e916SYuri Benditovich QTAILQ_REMOVE(&chain->buffers, seg, next); 17402974e916SYuri Benditovich g_free(seg->buf); 17412974e916SYuri Benditovich g_free(seg); 17422974e916SYuri Benditovich } 17432974e916SYuri Benditovich 17442974e916SYuri Benditovich timer_del(chain->drain_timer); 17452974e916SYuri Benditovich timer_free(chain->drain_timer); 17462974e916SYuri Benditovich QTAILQ_REMOVE(&n->rsc_chains, chain, next); 17472974e916SYuri Benditovich g_free(chain); 17482974e916SYuri Benditovich } 17492974e916SYuri Benditovich } 17502974e916SYuri Benditovich 17512974e916SYuri Benditovich static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain, 17522974e916SYuri Benditovich NetClientState *nc, 17532974e916SYuri Benditovich const uint8_t *buf, size_t size) 17542974e916SYuri Benditovich { 17552974e916SYuri Benditovich uint16_t hdr_len; 17562974e916SYuri Benditovich VirtioNetRscSeg *seg; 17572974e916SYuri Benditovich 17582974e916SYuri Benditovich hdr_len = chain->n->guest_hdr_len; 17592974e916SYuri Benditovich seg = g_malloc(sizeof(VirtioNetRscSeg)); 17602974e916SYuri Benditovich seg->buf = g_malloc(hdr_len + sizeof(struct eth_header) 17612974e916SYuri Benditovich + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD); 17622974e916SYuri Benditovich memcpy(seg->buf, buf, size); 17632974e916SYuri Benditovich seg->size = size; 17642974e916SYuri Benditovich seg->packets = 1; 17652974e916SYuri Benditovich seg->dup_ack = 0; 17662974e916SYuri Benditovich seg->is_coalesced = 0; 17672974e916SYuri Benditovich seg->nc = nc; 17682974e916SYuri Benditovich 17692974e916SYuri Benditovich QTAILQ_INSERT_TAIL(&chain->buffers, seg, next); 17702974e916SYuri Benditovich chain->stat.cache++; 17712974e916SYuri Benditovich 17722974e916SYuri Benditovich switch (chain->proto) { 17732974e916SYuri Benditovich case ETH_P_IP: 17742974e916SYuri Benditovich virtio_net_rsc_extract_unit4(chain, seg->buf, &seg->unit); 17752974e916SYuri Benditovich break; 17762974e916SYuri Benditovich case ETH_P_IPV6: 17772974e916SYuri Benditovich virtio_net_rsc_extract_unit6(chain, seg->buf, &seg->unit); 17782974e916SYuri Benditovich break; 17792974e916SYuri Benditovich default: 17802974e916SYuri Benditovich g_assert_not_reached(); 17812974e916SYuri Benditovich } 17822974e916SYuri Benditovich } 17832974e916SYuri Benditovich 17842974e916SYuri Benditovich static int32_t virtio_net_rsc_handle_ack(VirtioNetRscChain *chain, 17852974e916SYuri Benditovich VirtioNetRscSeg *seg, 17862974e916SYuri Benditovich const uint8_t *buf, 17872974e916SYuri Benditovich struct tcp_header *n_tcp, 17882974e916SYuri Benditovich struct tcp_header *o_tcp) 17892974e916SYuri Benditovich { 17902974e916SYuri Benditovich uint32_t nack, oack; 17912974e916SYuri Benditovich uint16_t nwin, owin; 17922974e916SYuri Benditovich 17932974e916SYuri Benditovich nack = htonl(n_tcp->th_ack); 17942974e916SYuri Benditovich nwin = htons(n_tcp->th_win); 17952974e916SYuri Benditovich oack = htonl(o_tcp->th_ack); 17962974e916SYuri Benditovich owin = htons(o_tcp->th_win); 17972974e916SYuri Benditovich 17982974e916SYuri Benditovich if ((nack - oack) >= VIRTIO_NET_MAX_TCP_PAYLOAD) { 17992974e916SYuri Benditovich chain->stat.ack_out_of_win++; 18002974e916SYuri Benditovich return RSC_FINAL; 18012974e916SYuri Benditovich } else if (nack == oack) { 18022974e916SYuri Benditovich /* duplicated ack or window probe */ 18032974e916SYuri Benditovich if (nwin == owin) { 18042974e916SYuri Benditovich /* duplicated ack, add dup ack count due to whql test up to 1 */ 18052974e916SYuri Benditovich chain->stat.dup_ack++; 18062974e916SYuri Benditovich return RSC_FINAL; 18072974e916SYuri Benditovich } else { 18082974e916SYuri Benditovich /* Coalesce window update */ 18092974e916SYuri Benditovich o_tcp->th_win = n_tcp->th_win; 18102974e916SYuri Benditovich chain->stat.win_update++; 18112974e916SYuri Benditovich return RSC_COALESCE; 18122974e916SYuri Benditovich } 18132974e916SYuri Benditovich } else { 18142974e916SYuri Benditovich /* pure ack, go to 'C', finalize*/ 18152974e916SYuri Benditovich chain->stat.pure_ack++; 18162974e916SYuri Benditovich return RSC_FINAL; 18172974e916SYuri Benditovich } 18182974e916SYuri Benditovich } 18192974e916SYuri Benditovich 18202974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain, 18212974e916SYuri Benditovich VirtioNetRscSeg *seg, 18222974e916SYuri Benditovich const uint8_t *buf, 18232974e916SYuri Benditovich VirtioNetRscUnit *n_unit) 18242974e916SYuri Benditovich { 18252974e916SYuri Benditovich void *data; 18262974e916SYuri Benditovich uint16_t o_ip_len; 18272974e916SYuri Benditovich uint32_t nseq, oseq; 18282974e916SYuri Benditovich VirtioNetRscUnit *o_unit; 18292974e916SYuri Benditovich 18302974e916SYuri Benditovich o_unit = &seg->unit; 18312974e916SYuri Benditovich o_ip_len = htons(*o_unit->ip_plen); 18322974e916SYuri Benditovich nseq = htonl(n_unit->tcp->th_seq); 18332974e916SYuri Benditovich oseq = htonl(o_unit->tcp->th_seq); 18342974e916SYuri Benditovich 18352974e916SYuri Benditovich /* out of order or retransmitted. */ 18362974e916SYuri Benditovich if ((nseq - oseq) > VIRTIO_NET_MAX_TCP_PAYLOAD) { 18372974e916SYuri Benditovich chain->stat.data_out_of_win++; 18382974e916SYuri Benditovich return RSC_FINAL; 18392974e916SYuri Benditovich } 18402974e916SYuri Benditovich 18412974e916SYuri Benditovich data = ((uint8_t *)n_unit->tcp) + n_unit->tcp_hdrlen; 18422974e916SYuri Benditovich if (nseq == oseq) { 18432974e916SYuri Benditovich if ((o_unit->payload == 0) && n_unit->payload) { 18442974e916SYuri Benditovich /* From no payload to payload, normal case, not a dup ack or etc */ 18452974e916SYuri Benditovich chain->stat.data_after_pure_ack++; 18462974e916SYuri Benditovich goto coalesce; 18472974e916SYuri Benditovich } else { 18482974e916SYuri Benditovich return virtio_net_rsc_handle_ack(chain, seg, buf, 18492974e916SYuri Benditovich n_unit->tcp, o_unit->tcp); 18502974e916SYuri Benditovich } 18512974e916SYuri Benditovich } else if ((nseq - oseq) != o_unit->payload) { 18522974e916SYuri Benditovich /* Not a consistent packet, out of order */ 18532974e916SYuri Benditovich chain->stat.data_out_of_order++; 18542974e916SYuri Benditovich return RSC_FINAL; 18552974e916SYuri Benditovich } else { 18562974e916SYuri Benditovich coalesce: 18572974e916SYuri Benditovich if ((o_ip_len + n_unit->payload) > chain->max_payload) { 18582974e916SYuri Benditovich chain->stat.over_size++; 18592974e916SYuri Benditovich return RSC_FINAL; 18602974e916SYuri Benditovich } 18612974e916SYuri Benditovich 18622974e916SYuri Benditovich /* Here comes the right data, the payload length in v4/v6 is different, 18632974e916SYuri Benditovich so use the field value to update and record the new data len */ 18642974e916SYuri Benditovich o_unit->payload += n_unit->payload; /* update new data len */ 18652974e916SYuri Benditovich 18662974e916SYuri Benditovich /* update field in ip header */ 18672974e916SYuri Benditovich *o_unit->ip_plen = htons(o_ip_len + n_unit->payload); 18682974e916SYuri Benditovich 18692974e916SYuri Benditovich /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced 18702974e916SYuri Benditovich for windows guest, while this may change the behavior for linux 18712974e916SYuri Benditovich guest (only if it uses RSC feature). */ 18722974e916SYuri Benditovich o_unit->tcp->th_offset_flags = n_unit->tcp->th_offset_flags; 18732974e916SYuri Benditovich 18742974e916SYuri Benditovich o_unit->tcp->th_ack = n_unit->tcp->th_ack; 18752974e916SYuri Benditovich o_unit->tcp->th_win = n_unit->tcp->th_win; 18762974e916SYuri Benditovich 18772974e916SYuri Benditovich memmove(seg->buf + seg->size, data, n_unit->payload); 18782974e916SYuri Benditovich seg->size += n_unit->payload; 18792974e916SYuri Benditovich seg->packets++; 18802974e916SYuri Benditovich chain->stat.coalesced++; 18812974e916SYuri Benditovich return RSC_COALESCE; 18822974e916SYuri Benditovich } 18832974e916SYuri Benditovich } 18842974e916SYuri Benditovich 18852974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce4(VirtioNetRscChain *chain, 18862974e916SYuri Benditovich VirtioNetRscSeg *seg, 18872974e916SYuri Benditovich const uint8_t *buf, size_t size, 18882974e916SYuri Benditovich VirtioNetRscUnit *unit) 18892974e916SYuri Benditovich { 18902974e916SYuri Benditovich struct ip_header *ip1, *ip2; 18912974e916SYuri Benditovich 18922974e916SYuri Benditovich ip1 = (struct ip_header *)(unit->ip); 18932974e916SYuri Benditovich ip2 = (struct ip_header *)(seg->unit.ip); 18942974e916SYuri Benditovich if ((ip1->ip_src ^ ip2->ip_src) || (ip1->ip_dst ^ ip2->ip_dst) 18952974e916SYuri Benditovich || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport) 18962974e916SYuri Benditovich || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) { 18972974e916SYuri Benditovich chain->stat.no_match++; 18982974e916SYuri Benditovich return RSC_NO_MATCH; 18992974e916SYuri Benditovich } 19002974e916SYuri Benditovich 19012974e916SYuri Benditovich return virtio_net_rsc_coalesce_data(chain, seg, buf, unit); 19022974e916SYuri Benditovich } 19032974e916SYuri Benditovich 19042974e916SYuri Benditovich static int32_t virtio_net_rsc_coalesce6(VirtioNetRscChain *chain, 19052974e916SYuri Benditovich VirtioNetRscSeg *seg, 19062974e916SYuri Benditovich const uint8_t *buf, size_t size, 19072974e916SYuri Benditovich VirtioNetRscUnit *unit) 19082974e916SYuri Benditovich { 19092974e916SYuri Benditovich struct ip6_header *ip1, *ip2; 19102974e916SYuri Benditovich 19112974e916SYuri Benditovich ip1 = (struct ip6_header *)(unit->ip); 19122974e916SYuri Benditovich ip2 = (struct ip6_header *)(seg->unit.ip); 19132974e916SYuri Benditovich if (memcmp(&ip1->ip6_src, &ip2->ip6_src, sizeof(struct in6_address)) 19142974e916SYuri Benditovich || memcmp(&ip1->ip6_dst, &ip2->ip6_dst, sizeof(struct in6_address)) 19152974e916SYuri Benditovich || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport) 19162974e916SYuri Benditovich || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) { 19172974e916SYuri Benditovich chain->stat.no_match++; 19182974e916SYuri Benditovich return RSC_NO_MATCH; 19192974e916SYuri Benditovich } 19202974e916SYuri Benditovich 19212974e916SYuri Benditovich return virtio_net_rsc_coalesce_data(chain, seg, buf, unit); 19222974e916SYuri Benditovich } 19232974e916SYuri Benditovich 19242974e916SYuri Benditovich /* Packets with 'SYN' should bypass, other flag should be sent after drain 19252974e916SYuri Benditovich * to prevent out of order */ 19262974e916SYuri Benditovich static int virtio_net_rsc_tcp_ctrl_check(VirtioNetRscChain *chain, 19272974e916SYuri Benditovich struct tcp_header *tcp) 19282974e916SYuri Benditovich { 19292974e916SYuri Benditovich uint16_t tcp_hdr; 19302974e916SYuri Benditovich uint16_t tcp_flag; 19312974e916SYuri Benditovich 19322974e916SYuri Benditovich tcp_flag = htons(tcp->th_offset_flags); 19332974e916SYuri Benditovich tcp_hdr = (tcp_flag & VIRTIO_NET_TCP_HDR_LENGTH) >> 10; 19342974e916SYuri Benditovich tcp_flag &= VIRTIO_NET_TCP_FLAG; 19352974e916SYuri Benditovich tcp_flag = htons(tcp->th_offset_flags) & 0x3F; 19362974e916SYuri Benditovich if (tcp_flag & TH_SYN) { 19372974e916SYuri Benditovich chain->stat.tcp_syn++; 19382974e916SYuri Benditovich return RSC_BYPASS; 19392974e916SYuri Benditovich } 19402974e916SYuri Benditovich 19412974e916SYuri Benditovich if (tcp_flag & (TH_FIN | TH_URG | TH_RST | TH_ECE | TH_CWR)) { 19422974e916SYuri Benditovich chain->stat.tcp_ctrl_drain++; 19432974e916SYuri Benditovich return RSC_FINAL; 19442974e916SYuri Benditovich } 19452974e916SYuri Benditovich 19462974e916SYuri Benditovich if (tcp_hdr > sizeof(struct tcp_header)) { 19472974e916SYuri Benditovich chain->stat.tcp_all_opt++; 19482974e916SYuri Benditovich return RSC_FINAL; 19492974e916SYuri Benditovich } 19502974e916SYuri Benditovich 19512974e916SYuri Benditovich return RSC_CANDIDATE; 19522974e916SYuri Benditovich } 19532974e916SYuri Benditovich 19542974e916SYuri Benditovich static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain, 19552974e916SYuri Benditovich NetClientState *nc, 19562974e916SYuri Benditovich const uint8_t *buf, size_t size, 19572974e916SYuri Benditovich VirtioNetRscUnit *unit) 19582974e916SYuri Benditovich { 19592974e916SYuri Benditovich int ret; 19602974e916SYuri Benditovich VirtioNetRscSeg *seg, *nseg; 19612974e916SYuri Benditovich 19622974e916SYuri Benditovich if (QTAILQ_EMPTY(&chain->buffers)) { 19632974e916SYuri Benditovich chain->stat.empty_cache++; 19642974e916SYuri Benditovich virtio_net_rsc_cache_buf(chain, nc, buf, size); 19652974e916SYuri Benditovich timer_mod(chain->drain_timer, 19662974e916SYuri Benditovich qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout); 19672974e916SYuri Benditovich return size; 19682974e916SYuri Benditovich } 19692974e916SYuri Benditovich 19702974e916SYuri Benditovich QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) { 19712974e916SYuri Benditovich if (chain->proto == ETH_P_IP) { 19722974e916SYuri Benditovich ret = virtio_net_rsc_coalesce4(chain, seg, buf, size, unit); 19732974e916SYuri Benditovich } else { 19742974e916SYuri Benditovich ret = virtio_net_rsc_coalesce6(chain, seg, buf, size, unit); 19752974e916SYuri Benditovich } 19762974e916SYuri Benditovich 19772974e916SYuri Benditovich if (ret == RSC_FINAL) { 19782974e916SYuri Benditovich if (virtio_net_rsc_drain_seg(chain, seg) == 0) { 19792974e916SYuri Benditovich /* Send failed */ 19802974e916SYuri Benditovich chain->stat.final_failed++; 19812974e916SYuri Benditovich return 0; 19822974e916SYuri Benditovich } 19832974e916SYuri Benditovich 19842974e916SYuri Benditovich /* Send current packet */ 19852974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 19862974e916SYuri Benditovich } else if (ret == RSC_NO_MATCH) { 19872974e916SYuri Benditovich continue; 19882974e916SYuri Benditovich } else { 19892974e916SYuri Benditovich /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */ 19902974e916SYuri Benditovich seg->is_coalesced = 1; 19912974e916SYuri Benditovich return size; 19922974e916SYuri Benditovich } 19932974e916SYuri Benditovich } 19942974e916SYuri Benditovich 19952974e916SYuri Benditovich chain->stat.no_match_cache++; 19962974e916SYuri Benditovich virtio_net_rsc_cache_buf(chain, nc, buf, size); 19972974e916SYuri Benditovich return size; 19982974e916SYuri Benditovich } 19992974e916SYuri Benditovich 20002974e916SYuri Benditovich /* Drain a connection data, this is to avoid out of order segments */ 20012974e916SYuri Benditovich static size_t virtio_net_rsc_drain_flow(VirtioNetRscChain *chain, 20022974e916SYuri Benditovich NetClientState *nc, 20032974e916SYuri Benditovich const uint8_t *buf, size_t size, 20042974e916SYuri Benditovich uint16_t ip_start, uint16_t ip_size, 20052974e916SYuri Benditovich uint16_t tcp_port) 20062974e916SYuri Benditovich { 20072974e916SYuri Benditovich VirtioNetRscSeg *seg, *nseg; 20082974e916SYuri Benditovich uint32_t ppair1, ppair2; 20092974e916SYuri Benditovich 20102974e916SYuri Benditovich ppair1 = *(uint32_t *)(buf + tcp_port); 20112974e916SYuri Benditovich QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) { 20122974e916SYuri Benditovich ppair2 = *(uint32_t *)(seg->buf + tcp_port); 20132974e916SYuri Benditovich if (memcmp(buf + ip_start, seg->buf + ip_start, ip_size) 20142974e916SYuri Benditovich || (ppair1 != ppair2)) { 20152974e916SYuri Benditovich continue; 20162974e916SYuri Benditovich } 20172974e916SYuri Benditovich if (virtio_net_rsc_drain_seg(chain, seg) == 0) { 20182974e916SYuri Benditovich chain->stat.drain_failed++; 20192974e916SYuri Benditovich } 20202974e916SYuri Benditovich 20212974e916SYuri Benditovich break; 20222974e916SYuri Benditovich } 20232974e916SYuri Benditovich 20242974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 20252974e916SYuri Benditovich } 20262974e916SYuri Benditovich 20272974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check4(VirtioNetRscChain *chain, 20282974e916SYuri Benditovich struct ip_header *ip, 20292974e916SYuri Benditovich const uint8_t *buf, size_t size) 20302974e916SYuri Benditovich { 20312974e916SYuri Benditovich uint16_t ip_len; 20322974e916SYuri Benditovich 20332974e916SYuri Benditovich /* Not an ipv4 packet */ 20342974e916SYuri Benditovich if (((ip->ip_ver_len & 0xF0) >> 4) != IP_HEADER_VERSION_4) { 20352974e916SYuri Benditovich chain->stat.ip_option++; 20362974e916SYuri Benditovich return RSC_BYPASS; 20372974e916SYuri Benditovich } 20382974e916SYuri Benditovich 20392974e916SYuri Benditovich /* Don't handle packets with ip option */ 20402974e916SYuri Benditovich if ((ip->ip_ver_len & 0xF) != VIRTIO_NET_IP4_HEADER_LENGTH) { 20412974e916SYuri Benditovich chain->stat.ip_option++; 20422974e916SYuri Benditovich return RSC_BYPASS; 20432974e916SYuri Benditovich } 20442974e916SYuri Benditovich 20452974e916SYuri Benditovich if (ip->ip_p != IPPROTO_TCP) { 20462974e916SYuri Benditovich chain->stat.bypass_not_tcp++; 20472974e916SYuri Benditovich return RSC_BYPASS; 20482974e916SYuri Benditovich } 20492974e916SYuri Benditovich 20502974e916SYuri Benditovich /* Don't handle packets with ip fragment */ 20512974e916SYuri Benditovich if (!(htons(ip->ip_off) & IP_DF)) { 20522974e916SYuri Benditovich chain->stat.ip_frag++; 20532974e916SYuri Benditovich return RSC_BYPASS; 20542974e916SYuri Benditovich } 20552974e916SYuri Benditovich 20562974e916SYuri Benditovich /* Don't handle packets with ecn flag */ 20572974e916SYuri Benditovich if (IPTOS_ECN(ip->ip_tos)) { 20582974e916SYuri Benditovich chain->stat.ip_ecn++; 20592974e916SYuri Benditovich return RSC_BYPASS; 20602974e916SYuri Benditovich } 20612974e916SYuri Benditovich 20622974e916SYuri Benditovich ip_len = htons(ip->ip_len); 20632974e916SYuri Benditovich if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header)) 20642974e916SYuri Benditovich || ip_len > (size - chain->n->guest_hdr_len - 20652974e916SYuri Benditovich sizeof(struct eth_header))) { 20662974e916SYuri Benditovich chain->stat.ip_hacked++; 20672974e916SYuri Benditovich return RSC_BYPASS; 20682974e916SYuri Benditovich } 20692974e916SYuri Benditovich 20702974e916SYuri Benditovich return RSC_CANDIDATE; 20712974e916SYuri Benditovich } 20722974e916SYuri Benditovich 20732974e916SYuri Benditovich static size_t virtio_net_rsc_receive4(VirtioNetRscChain *chain, 20742974e916SYuri Benditovich NetClientState *nc, 20752974e916SYuri Benditovich const uint8_t *buf, size_t size) 20762974e916SYuri Benditovich { 20772974e916SYuri Benditovich int32_t ret; 20782974e916SYuri Benditovich uint16_t hdr_len; 20792974e916SYuri Benditovich VirtioNetRscUnit unit; 20802974e916SYuri Benditovich 20812974e916SYuri Benditovich hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len; 20822974e916SYuri Benditovich 20832974e916SYuri Benditovich if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header) 20842974e916SYuri Benditovich + sizeof(struct tcp_header))) { 20852974e916SYuri Benditovich chain->stat.bypass_not_tcp++; 20862974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 20872974e916SYuri Benditovich } 20882974e916SYuri Benditovich 20892974e916SYuri Benditovich virtio_net_rsc_extract_unit4(chain, buf, &unit); 20902974e916SYuri Benditovich if (virtio_net_rsc_sanity_check4(chain, unit.ip, buf, size) 20912974e916SYuri Benditovich != RSC_CANDIDATE) { 20922974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 20932974e916SYuri Benditovich } 20942974e916SYuri Benditovich 20952974e916SYuri Benditovich ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp); 20962974e916SYuri Benditovich if (ret == RSC_BYPASS) { 20972974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 20982974e916SYuri Benditovich } else if (ret == RSC_FINAL) { 20992974e916SYuri Benditovich return virtio_net_rsc_drain_flow(chain, nc, buf, size, 21002974e916SYuri Benditovich ((hdr_len + sizeof(struct eth_header)) + 12), 21012974e916SYuri Benditovich VIRTIO_NET_IP4_ADDR_SIZE, 21022974e916SYuri Benditovich hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header)); 21032974e916SYuri Benditovich } 21042974e916SYuri Benditovich 21052974e916SYuri Benditovich return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit); 21062974e916SYuri Benditovich } 21072974e916SYuri Benditovich 21082974e916SYuri Benditovich static int32_t virtio_net_rsc_sanity_check6(VirtioNetRscChain *chain, 21092974e916SYuri Benditovich struct ip6_header *ip6, 21102974e916SYuri Benditovich const uint8_t *buf, size_t size) 21112974e916SYuri Benditovich { 21122974e916SYuri Benditovich uint16_t ip_len; 21132974e916SYuri Benditovich 21142974e916SYuri Benditovich if (((ip6->ip6_ctlun.ip6_un1.ip6_un1_flow & 0xF0) >> 4) 21152974e916SYuri Benditovich != IP_HEADER_VERSION_6) { 21162974e916SYuri Benditovich return RSC_BYPASS; 21172974e916SYuri Benditovich } 21182974e916SYuri Benditovich 21192974e916SYuri Benditovich /* Both option and protocol is checked in this */ 21202974e916SYuri Benditovich if (ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) { 21212974e916SYuri Benditovich chain->stat.bypass_not_tcp++; 21222974e916SYuri Benditovich return RSC_BYPASS; 21232974e916SYuri Benditovich } 21242974e916SYuri Benditovich 21252974e916SYuri Benditovich ip_len = htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); 21262974e916SYuri Benditovich if (ip_len < sizeof(struct tcp_header) || 21272974e916SYuri Benditovich ip_len > (size - chain->n->guest_hdr_len - sizeof(struct eth_header) 21282974e916SYuri Benditovich - sizeof(struct ip6_header))) { 21292974e916SYuri Benditovich chain->stat.ip_hacked++; 21302974e916SYuri Benditovich return RSC_BYPASS; 21312974e916SYuri Benditovich } 21322974e916SYuri Benditovich 21332974e916SYuri Benditovich /* Don't handle packets with ecn flag */ 21342974e916SYuri Benditovich if (IP6_ECN(ip6->ip6_ctlun.ip6_un3.ip6_un3_ecn)) { 21352974e916SYuri Benditovich chain->stat.ip_ecn++; 21362974e916SYuri Benditovich return RSC_BYPASS; 21372974e916SYuri Benditovich } 21382974e916SYuri Benditovich 21392974e916SYuri Benditovich return RSC_CANDIDATE; 21402974e916SYuri Benditovich } 21412974e916SYuri Benditovich 21422974e916SYuri Benditovich static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc, 21432974e916SYuri Benditovich const uint8_t *buf, size_t size) 21442974e916SYuri Benditovich { 21452974e916SYuri Benditovich int32_t ret; 21462974e916SYuri Benditovich uint16_t hdr_len; 21472974e916SYuri Benditovich VirtioNetRscChain *chain; 21482974e916SYuri Benditovich VirtioNetRscUnit unit; 21492974e916SYuri Benditovich 21502974e916SYuri Benditovich chain = (VirtioNetRscChain *)opq; 21512974e916SYuri Benditovich hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len; 21522974e916SYuri Benditovich 21532974e916SYuri Benditovich if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header) 21542974e916SYuri Benditovich + sizeof(tcp_header))) { 21552974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 21562974e916SYuri Benditovich } 21572974e916SYuri Benditovich 21582974e916SYuri Benditovich virtio_net_rsc_extract_unit6(chain, buf, &unit); 21592974e916SYuri Benditovich if (RSC_CANDIDATE != virtio_net_rsc_sanity_check6(chain, 21602974e916SYuri Benditovich unit.ip, buf, size)) { 21612974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 21622974e916SYuri Benditovich } 21632974e916SYuri Benditovich 21642974e916SYuri Benditovich ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp); 21652974e916SYuri Benditovich if (ret == RSC_BYPASS) { 21662974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 21672974e916SYuri Benditovich } else if (ret == RSC_FINAL) { 21682974e916SYuri Benditovich return virtio_net_rsc_drain_flow(chain, nc, buf, size, 21692974e916SYuri Benditovich ((hdr_len + sizeof(struct eth_header)) + 8), 21702974e916SYuri Benditovich VIRTIO_NET_IP6_ADDR_SIZE, 21712974e916SYuri Benditovich hdr_len + sizeof(struct eth_header) 21722974e916SYuri Benditovich + sizeof(struct ip6_header)); 21732974e916SYuri Benditovich } 21742974e916SYuri Benditovich 21752974e916SYuri Benditovich return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit); 21762974e916SYuri Benditovich } 21772974e916SYuri Benditovich 21782974e916SYuri Benditovich static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n, 21792974e916SYuri Benditovich NetClientState *nc, 21802974e916SYuri Benditovich uint16_t proto) 21812974e916SYuri Benditovich { 21822974e916SYuri Benditovich VirtioNetRscChain *chain; 21832974e916SYuri Benditovich 21842974e916SYuri Benditovich if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) { 21852974e916SYuri Benditovich return NULL; 21862974e916SYuri Benditovich } 21872974e916SYuri Benditovich 21882974e916SYuri Benditovich QTAILQ_FOREACH(chain, &n->rsc_chains, next) { 21892974e916SYuri Benditovich if (chain->proto == proto) { 21902974e916SYuri Benditovich return chain; 21912974e916SYuri Benditovich } 21922974e916SYuri Benditovich } 21932974e916SYuri Benditovich 21942974e916SYuri Benditovich chain = g_malloc(sizeof(*chain)); 21952974e916SYuri Benditovich chain->n = n; 21962974e916SYuri Benditovich chain->proto = proto; 21972974e916SYuri Benditovich if (proto == (uint16_t)ETH_P_IP) { 21982974e916SYuri Benditovich chain->max_payload = VIRTIO_NET_MAX_IP4_PAYLOAD; 21992974e916SYuri Benditovich chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; 22002974e916SYuri Benditovich } else { 22012974e916SYuri Benditovich chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD; 22022974e916SYuri Benditovich chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; 22032974e916SYuri Benditovich } 22042974e916SYuri Benditovich chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST, 22052974e916SYuri Benditovich virtio_net_rsc_purge, chain); 22062974e916SYuri Benditovich memset(&chain->stat, 0, sizeof(chain->stat)); 22072974e916SYuri Benditovich 22082974e916SYuri Benditovich QTAILQ_INIT(&chain->buffers); 22092974e916SYuri Benditovich QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next); 22102974e916SYuri Benditovich 22112974e916SYuri Benditovich return chain; 22122974e916SYuri Benditovich } 22132974e916SYuri Benditovich 22142974e916SYuri Benditovich static ssize_t virtio_net_rsc_receive(NetClientState *nc, 22152974e916SYuri Benditovich const uint8_t *buf, 22162974e916SYuri Benditovich size_t size) 22172974e916SYuri Benditovich { 22182974e916SYuri Benditovich uint16_t proto; 22192974e916SYuri Benditovich VirtioNetRscChain *chain; 22202974e916SYuri Benditovich struct eth_header *eth; 22212974e916SYuri Benditovich VirtIONet *n; 22222974e916SYuri Benditovich 22232974e916SYuri Benditovich n = qemu_get_nic_opaque(nc); 22242974e916SYuri Benditovich if (size < (n->host_hdr_len + sizeof(struct eth_header))) { 22252974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 22262974e916SYuri Benditovich } 22272974e916SYuri Benditovich 22282974e916SYuri Benditovich eth = (struct eth_header *)(buf + n->guest_hdr_len); 22292974e916SYuri Benditovich proto = htons(eth->h_proto); 22302974e916SYuri Benditovich 22312974e916SYuri Benditovich chain = virtio_net_rsc_lookup_chain(n, nc, proto); 22322974e916SYuri Benditovich if (chain) { 22332974e916SYuri Benditovich chain->stat.received++; 22342974e916SYuri Benditovich if (proto == (uint16_t)ETH_P_IP && n->rsc4_enabled) { 22352974e916SYuri Benditovich return virtio_net_rsc_receive4(chain, nc, buf, size); 22362974e916SYuri Benditovich } else if (proto == (uint16_t)ETH_P_IPV6 && n->rsc6_enabled) { 22372974e916SYuri Benditovich return virtio_net_rsc_receive6(chain, nc, buf, size); 22382974e916SYuri Benditovich } 22392974e916SYuri Benditovich } 22402974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 22412974e916SYuri Benditovich } 22422974e916SYuri Benditovich 22432974e916SYuri Benditovich static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, 22442974e916SYuri Benditovich size_t size) 22452974e916SYuri Benditovich { 22462974e916SYuri Benditovich VirtIONet *n = qemu_get_nic_opaque(nc); 22472974e916SYuri Benditovich if ((n->rsc4_enabled || n->rsc6_enabled)) { 22482974e916SYuri Benditovich return virtio_net_rsc_receive(nc, buf, size); 22492974e916SYuri Benditovich } else { 22502974e916SYuri Benditovich return virtio_net_do_receive(nc, buf, size); 22512974e916SYuri Benditovich } 22522974e916SYuri Benditovich } 22532974e916SYuri Benditovich 22546e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q); 22556e790746SPaolo Bonzini 22566e790746SPaolo Bonzini static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) 22576e790746SPaolo Bonzini { 22586e790746SPaolo Bonzini VirtIONet *n = qemu_get_nic_opaque(nc); 22596e790746SPaolo Bonzini VirtIONetQueue *q = virtio_net_get_subqueue(nc); 226017a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 22616e790746SPaolo Bonzini 226251b19ebeSPaolo Bonzini virtqueue_push(q->tx_vq, q->async_tx.elem, 0); 226317a0ca55SKONRAD Frederic virtio_notify(vdev, q->tx_vq); 22646e790746SPaolo Bonzini 226551b19ebeSPaolo Bonzini g_free(q->async_tx.elem); 226651b19ebeSPaolo Bonzini q->async_tx.elem = NULL; 22676e790746SPaolo Bonzini 22686e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 1); 22696e790746SPaolo Bonzini virtio_net_flush_tx(q); 22706e790746SPaolo Bonzini } 22716e790746SPaolo Bonzini 22726e790746SPaolo Bonzini /* TX */ 22736e790746SPaolo Bonzini static int32_t virtio_net_flush_tx(VirtIONetQueue *q) 22746e790746SPaolo Bonzini { 22756e790746SPaolo Bonzini VirtIONet *n = q->n; 227617a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 227751b19ebeSPaolo Bonzini VirtQueueElement *elem; 22786e790746SPaolo Bonzini int32_t num_packets = 0; 22796e790746SPaolo Bonzini int queue_index = vq2q(virtio_get_queue_index(q->tx_vq)); 228017a0ca55SKONRAD Frederic if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { 22816e790746SPaolo Bonzini return num_packets; 22826e790746SPaolo Bonzini } 22836e790746SPaolo Bonzini 228451b19ebeSPaolo Bonzini if (q->async_tx.elem) { 22856e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 0); 22866e790746SPaolo Bonzini return num_packets; 22876e790746SPaolo Bonzini } 22886e790746SPaolo Bonzini 228951b19ebeSPaolo Bonzini for (;;) { 2290bd89dd98SJason Wang ssize_t ret; 229151b19ebeSPaolo Bonzini unsigned int out_num; 229251b19ebeSPaolo Bonzini struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg; 2293feb93f36SJason Wang struct virtio_net_hdr_mrg_rxbuf mhdr; 22946e790746SPaolo Bonzini 229551b19ebeSPaolo Bonzini elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement)); 229651b19ebeSPaolo Bonzini if (!elem) { 229751b19ebeSPaolo Bonzini break; 229851b19ebeSPaolo Bonzini } 229951b19ebeSPaolo Bonzini 230051b19ebeSPaolo Bonzini out_num = elem->out_num; 230151b19ebeSPaolo Bonzini out_sg = elem->out_sg; 23026e790746SPaolo Bonzini if (out_num < 1) { 2303fa5e56c2SGreg Kurz virtio_error(vdev, "virtio-net header not in first element"); 2304fa5e56c2SGreg Kurz virtqueue_detach_element(q->tx_vq, elem, 0); 2305fa5e56c2SGreg Kurz g_free(elem); 2306fa5e56c2SGreg Kurz return -EINVAL; 23076e790746SPaolo Bonzini } 23086e790746SPaolo Bonzini 2309032a74a1SCédric Le Goater if (n->has_vnet_hdr) { 2310feb93f36SJason Wang if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) < 2311feb93f36SJason Wang n->guest_hdr_len) { 2312fa5e56c2SGreg Kurz virtio_error(vdev, "virtio-net header incorrect"); 2313fa5e56c2SGreg Kurz virtqueue_detach_element(q->tx_vq, elem, 0); 2314fa5e56c2SGreg Kurz g_free(elem); 2315fa5e56c2SGreg Kurz return -EINVAL; 2316032a74a1SCédric Le Goater } 23171bfa316cSGreg Kurz if (n->needs_vnet_hdr_swap) { 2318feb93f36SJason Wang virtio_net_hdr_swap(vdev, (void *) &mhdr); 2319feb93f36SJason Wang sg2[0].iov_base = &mhdr; 2320feb93f36SJason Wang sg2[0].iov_len = n->guest_hdr_len; 2321feb93f36SJason Wang out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1, 2322feb93f36SJason Wang out_sg, out_num, 2323feb93f36SJason Wang n->guest_hdr_len, -1); 2324feb93f36SJason Wang if (out_num == VIRTQUEUE_MAX_SIZE) { 2325feb93f36SJason Wang goto drop; 2326032a74a1SCédric Le Goater } 2327feb93f36SJason Wang out_num += 1; 2328feb93f36SJason Wang out_sg = sg2; 2329feb93f36SJason Wang } 2330feb93f36SJason Wang } 23316e790746SPaolo Bonzini /* 23326e790746SPaolo Bonzini * If host wants to see the guest header as is, we can 23336e790746SPaolo Bonzini * pass it on unchanged. Otherwise, copy just the parts 23346e790746SPaolo Bonzini * that host is interested in. 23356e790746SPaolo Bonzini */ 23366e790746SPaolo Bonzini assert(n->host_hdr_len <= n->guest_hdr_len); 23376e790746SPaolo Bonzini if (n->host_hdr_len != n->guest_hdr_len) { 23386e790746SPaolo Bonzini unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg), 23396e790746SPaolo Bonzini out_sg, out_num, 23406e790746SPaolo Bonzini 0, n->host_hdr_len); 23416e790746SPaolo Bonzini sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num, 23426e790746SPaolo Bonzini out_sg, out_num, 23436e790746SPaolo Bonzini n->guest_hdr_len, -1); 23446e790746SPaolo Bonzini out_num = sg_num; 23456e790746SPaolo Bonzini out_sg = sg; 23466e790746SPaolo Bonzini } 23476e790746SPaolo Bonzini 23486e790746SPaolo Bonzini ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index), 23496e790746SPaolo Bonzini out_sg, out_num, virtio_net_tx_complete); 23506e790746SPaolo Bonzini if (ret == 0) { 23516e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 0); 23526e790746SPaolo Bonzini q->async_tx.elem = elem; 23536e790746SPaolo Bonzini return -EBUSY; 23546e790746SPaolo Bonzini } 23556e790746SPaolo Bonzini 2356feb93f36SJason Wang drop: 235751b19ebeSPaolo Bonzini virtqueue_push(q->tx_vq, elem, 0); 235817a0ca55SKONRAD Frederic virtio_notify(vdev, q->tx_vq); 235951b19ebeSPaolo Bonzini g_free(elem); 23606e790746SPaolo Bonzini 23616e790746SPaolo Bonzini if (++num_packets >= n->tx_burst) { 23626e790746SPaolo Bonzini break; 23636e790746SPaolo Bonzini } 23646e790746SPaolo Bonzini } 23656e790746SPaolo Bonzini return num_packets; 23666e790746SPaolo Bonzini } 23676e790746SPaolo Bonzini 23686e790746SPaolo Bonzini static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) 23696e790746SPaolo Bonzini { 237017a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 23716e790746SPaolo Bonzini VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; 23726e790746SPaolo Bonzini 2373283e2c2aSYuri Benditovich if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) { 2374283e2c2aSYuri Benditovich virtio_net_drop_tx_queue_data(vdev, vq); 2375283e2c2aSYuri Benditovich return; 2376283e2c2aSYuri Benditovich } 2377283e2c2aSYuri Benditovich 23786e790746SPaolo Bonzini /* This happens when device was stopped but VCPU wasn't. */ 237917a0ca55SKONRAD Frederic if (!vdev->vm_running) { 23806e790746SPaolo Bonzini q->tx_waiting = 1; 23816e790746SPaolo Bonzini return; 23826e790746SPaolo Bonzini } 23836e790746SPaolo Bonzini 23846e790746SPaolo Bonzini if (q->tx_waiting) { 23856e790746SPaolo Bonzini virtio_queue_set_notification(vq, 1); 2386bc72ad67SAlex Bligh timer_del(q->tx_timer); 23876e790746SPaolo Bonzini q->tx_waiting = 0; 2388fa5e56c2SGreg Kurz if (virtio_net_flush_tx(q) == -EINVAL) { 2389fa5e56c2SGreg Kurz return; 2390fa5e56c2SGreg Kurz } 23916e790746SPaolo Bonzini } else { 2392bc72ad67SAlex Bligh timer_mod(q->tx_timer, 2393bc72ad67SAlex Bligh qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); 23946e790746SPaolo Bonzini q->tx_waiting = 1; 23956e790746SPaolo Bonzini virtio_queue_set_notification(vq, 0); 23966e790746SPaolo Bonzini } 23976e790746SPaolo Bonzini } 23986e790746SPaolo Bonzini 23996e790746SPaolo Bonzini static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) 24006e790746SPaolo Bonzini { 240117a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 24026e790746SPaolo Bonzini VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; 24036e790746SPaolo Bonzini 2404283e2c2aSYuri Benditovich if (unlikely((n->status & VIRTIO_NET_S_LINK_UP) == 0)) { 2405283e2c2aSYuri Benditovich virtio_net_drop_tx_queue_data(vdev, vq); 2406283e2c2aSYuri Benditovich return; 2407283e2c2aSYuri Benditovich } 2408283e2c2aSYuri Benditovich 24096e790746SPaolo Bonzini if (unlikely(q->tx_waiting)) { 24106e790746SPaolo Bonzini return; 24116e790746SPaolo Bonzini } 24126e790746SPaolo Bonzini q->tx_waiting = 1; 24136e790746SPaolo Bonzini /* This happens when device was stopped but VCPU wasn't. */ 241417a0ca55SKONRAD Frederic if (!vdev->vm_running) { 24156e790746SPaolo Bonzini return; 24166e790746SPaolo Bonzini } 24176e790746SPaolo Bonzini virtio_queue_set_notification(vq, 0); 24186e790746SPaolo Bonzini qemu_bh_schedule(q->tx_bh); 24196e790746SPaolo Bonzini } 24206e790746SPaolo Bonzini 24216e790746SPaolo Bonzini static void virtio_net_tx_timer(void *opaque) 24226e790746SPaolo Bonzini { 24236e790746SPaolo Bonzini VirtIONetQueue *q = opaque; 24246e790746SPaolo Bonzini VirtIONet *n = q->n; 242517a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 2426e8bcf842SMichael S. Tsirkin /* This happens when device was stopped but BH wasn't. */ 2427e8bcf842SMichael S. Tsirkin if (!vdev->vm_running) { 2428e8bcf842SMichael S. Tsirkin /* Make sure tx waiting is set, so we'll run when restarted. */ 2429e8bcf842SMichael S. Tsirkin assert(q->tx_waiting); 2430e8bcf842SMichael S. Tsirkin return; 2431e8bcf842SMichael S. Tsirkin } 24326e790746SPaolo Bonzini 24336e790746SPaolo Bonzini q->tx_waiting = 0; 24346e790746SPaolo Bonzini 24356e790746SPaolo Bonzini /* Just in case the driver is not ready on more */ 243617a0ca55SKONRAD Frederic if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { 24376e790746SPaolo Bonzini return; 243817a0ca55SKONRAD Frederic } 24396e790746SPaolo Bonzini 24406e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 1); 24416e790746SPaolo Bonzini virtio_net_flush_tx(q); 24426e790746SPaolo Bonzini } 24436e790746SPaolo Bonzini 24446e790746SPaolo Bonzini static void virtio_net_tx_bh(void *opaque) 24456e790746SPaolo Bonzini { 24466e790746SPaolo Bonzini VirtIONetQueue *q = opaque; 24476e790746SPaolo Bonzini VirtIONet *n = q->n; 244817a0ca55SKONRAD Frederic VirtIODevice *vdev = VIRTIO_DEVICE(n); 24496e790746SPaolo Bonzini int32_t ret; 24506e790746SPaolo Bonzini 2451e8bcf842SMichael S. Tsirkin /* This happens when device was stopped but BH wasn't. */ 2452e8bcf842SMichael S. Tsirkin if (!vdev->vm_running) { 2453e8bcf842SMichael S. Tsirkin /* Make sure tx waiting is set, so we'll run when restarted. */ 2454e8bcf842SMichael S. Tsirkin assert(q->tx_waiting); 2455e8bcf842SMichael S. Tsirkin return; 2456e8bcf842SMichael S. Tsirkin } 24576e790746SPaolo Bonzini 24586e790746SPaolo Bonzini q->tx_waiting = 0; 24596e790746SPaolo Bonzini 24606e790746SPaolo Bonzini /* Just in case the driver is not ready on more */ 246117a0ca55SKONRAD Frederic if (unlikely(!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) { 24626e790746SPaolo Bonzini return; 246317a0ca55SKONRAD Frederic } 24646e790746SPaolo Bonzini 24656e790746SPaolo Bonzini ret = virtio_net_flush_tx(q); 2466fa5e56c2SGreg Kurz if (ret == -EBUSY || ret == -EINVAL) { 2467fa5e56c2SGreg Kurz return; /* Notification re-enable handled by tx_complete or device 2468fa5e56c2SGreg Kurz * broken */ 24696e790746SPaolo Bonzini } 24706e790746SPaolo Bonzini 24716e790746SPaolo Bonzini /* If we flush a full burst of packets, assume there are 24726e790746SPaolo Bonzini * more coming and immediately reschedule */ 24736e790746SPaolo Bonzini if (ret >= n->tx_burst) { 24746e790746SPaolo Bonzini qemu_bh_schedule(q->tx_bh); 24756e790746SPaolo Bonzini q->tx_waiting = 1; 24766e790746SPaolo Bonzini return; 24776e790746SPaolo Bonzini } 24786e790746SPaolo Bonzini 24796e790746SPaolo Bonzini /* If less than a full burst, re-enable notification and flush 24806e790746SPaolo Bonzini * anything that may have come in while we weren't looking. If 24816e790746SPaolo Bonzini * we find something, assume the guest is still active and reschedule */ 24826e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 1); 2483fa5e56c2SGreg Kurz ret = virtio_net_flush_tx(q); 2484fa5e56c2SGreg Kurz if (ret == -EINVAL) { 2485fa5e56c2SGreg Kurz return; 2486fa5e56c2SGreg Kurz } else if (ret > 0) { 24876e790746SPaolo Bonzini virtio_queue_set_notification(q->tx_vq, 0); 24886e790746SPaolo Bonzini qemu_bh_schedule(q->tx_bh); 24896e790746SPaolo Bonzini q->tx_waiting = 1; 24906e790746SPaolo Bonzini } 24916e790746SPaolo Bonzini } 24926e790746SPaolo Bonzini 2493f9d6dbf0SWen Congyang static void virtio_net_add_queue(VirtIONet *n, int index) 2494f9d6dbf0SWen Congyang { 2495f9d6dbf0SWen Congyang VirtIODevice *vdev = VIRTIO_DEVICE(n); 2496f9d6dbf0SWen Congyang 24971c0fbfa3SMichael S. Tsirkin n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size, 24981c0fbfa3SMichael S. Tsirkin virtio_net_handle_rx); 24999b02e161SWei Wang 2500f9d6dbf0SWen Congyang if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) { 2501f9d6dbf0SWen Congyang n->vqs[index].tx_vq = 25029b02e161SWei Wang virtio_add_queue(vdev, n->net_conf.tx_queue_size, 25039b02e161SWei Wang virtio_net_handle_tx_timer); 2504f9d6dbf0SWen Congyang n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 2505f9d6dbf0SWen Congyang virtio_net_tx_timer, 2506f9d6dbf0SWen Congyang &n->vqs[index]); 2507f9d6dbf0SWen Congyang } else { 2508f9d6dbf0SWen Congyang n->vqs[index].tx_vq = 25099b02e161SWei Wang virtio_add_queue(vdev, n->net_conf.tx_queue_size, 25109b02e161SWei Wang virtio_net_handle_tx_bh); 2511f9d6dbf0SWen Congyang n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]); 2512f9d6dbf0SWen Congyang } 2513f9d6dbf0SWen Congyang 2514f9d6dbf0SWen Congyang n->vqs[index].tx_waiting = 0; 2515f9d6dbf0SWen Congyang n->vqs[index].n = n; 2516f9d6dbf0SWen Congyang } 2517f9d6dbf0SWen Congyang 2518f9d6dbf0SWen Congyang static void virtio_net_del_queue(VirtIONet *n, int index) 2519f9d6dbf0SWen Congyang { 2520f9d6dbf0SWen Congyang VirtIODevice *vdev = VIRTIO_DEVICE(n); 2521f9d6dbf0SWen Congyang VirtIONetQueue *q = &n->vqs[index]; 2522f9d6dbf0SWen Congyang NetClientState *nc = qemu_get_subqueue(n->nic, index); 2523f9d6dbf0SWen Congyang 2524f9d6dbf0SWen Congyang qemu_purge_queued_packets(nc); 2525f9d6dbf0SWen Congyang 2526f9d6dbf0SWen Congyang virtio_del_queue(vdev, index * 2); 2527f9d6dbf0SWen Congyang if (q->tx_timer) { 2528f9d6dbf0SWen Congyang timer_del(q->tx_timer); 2529f9d6dbf0SWen Congyang timer_free(q->tx_timer); 2530f989c30cSYunjian Wang q->tx_timer = NULL; 2531f9d6dbf0SWen Congyang } else { 2532f9d6dbf0SWen Congyang qemu_bh_delete(q->tx_bh); 2533f989c30cSYunjian Wang q->tx_bh = NULL; 2534f9d6dbf0SWen Congyang } 2535f989c30cSYunjian Wang q->tx_waiting = 0; 2536f9d6dbf0SWen Congyang virtio_del_queue(vdev, index * 2 + 1); 2537f9d6dbf0SWen Congyang } 2538f9d6dbf0SWen Congyang 2539f9d6dbf0SWen Congyang static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues) 2540f9d6dbf0SWen Congyang { 2541f9d6dbf0SWen Congyang VirtIODevice *vdev = VIRTIO_DEVICE(n); 2542f9d6dbf0SWen Congyang int old_num_queues = virtio_get_num_queues(vdev); 2543f9d6dbf0SWen Congyang int new_num_queues = new_max_queues * 2 + 1; 2544f9d6dbf0SWen Congyang int i; 2545f9d6dbf0SWen Congyang 2546f9d6dbf0SWen Congyang assert(old_num_queues >= 3); 2547f9d6dbf0SWen Congyang assert(old_num_queues % 2 == 1); 2548f9d6dbf0SWen Congyang 2549f9d6dbf0SWen Congyang if (old_num_queues == new_num_queues) { 2550f9d6dbf0SWen Congyang return; 2551f9d6dbf0SWen Congyang } 2552f9d6dbf0SWen Congyang 2553f9d6dbf0SWen Congyang /* 2554f9d6dbf0SWen Congyang * We always need to remove and add ctrl vq if 2555f9d6dbf0SWen Congyang * old_num_queues != new_num_queues. Remove ctrl_vq first, 255620f86a75SYuval Shaia * and then we only enter one of the following two loops. 2557f9d6dbf0SWen Congyang */ 2558f9d6dbf0SWen Congyang virtio_del_queue(vdev, old_num_queues - 1); 2559f9d6dbf0SWen Congyang 2560f9d6dbf0SWen Congyang for (i = new_num_queues - 1; i < old_num_queues - 1; i += 2) { 2561f9d6dbf0SWen Congyang /* new_num_queues < old_num_queues */ 2562f9d6dbf0SWen Congyang virtio_net_del_queue(n, i / 2); 2563f9d6dbf0SWen Congyang } 2564f9d6dbf0SWen Congyang 2565f9d6dbf0SWen Congyang for (i = old_num_queues - 1; i < new_num_queues - 1; i += 2) { 2566f9d6dbf0SWen Congyang /* new_num_queues > old_num_queues */ 2567f9d6dbf0SWen Congyang virtio_net_add_queue(n, i / 2); 2568f9d6dbf0SWen Congyang } 2569f9d6dbf0SWen Congyang 2570f9d6dbf0SWen Congyang /* add ctrl_vq last */ 2571f9d6dbf0SWen Congyang n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); 2572f9d6dbf0SWen Congyang } 2573f9d6dbf0SWen Congyang 2574ec57db16SJason Wang static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) 25756e790746SPaolo Bonzini { 2576f9d6dbf0SWen Congyang int max = multiqueue ? n->max_queues : 1; 2577f9d6dbf0SWen Congyang 25786e790746SPaolo Bonzini n->multiqueue = multiqueue; 2579f9d6dbf0SWen Congyang virtio_net_change_num_queues(n, max); 25806e790746SPaolo Bonzini 25816e790746SPaolo Bonzini virtio_net_set_queues(n); 25826e790746SPaolo Bonzini } 25836e790746SPaolo Bonzini 2584982b78c5SDr. David Alan Gilbert static int virtio_net_post_load_device(void *opaque, int version_id) 2585037dab2fSGreg Kurz { 2586982b78c5SDr. David Alan Gilbert VirtIONet *n = opaque; 2587982b78c5SDr. David Alan Gilbert VirtIODevice *vdev = VIRTIO_DEVICE(n); 2588037dab2fSGreg Kurz int i, link_down; 2589037dab2fSGreg Kurz 25909d8c6a25SDr. David Alan Gilbert trace_virtio_net_post_load_device(); 2591982b78c5SDr. David Alan Gilbert virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs, 259295129d6fSCornelia Huck virtio_vdev_has_feature(vdev, 259395129d6fSCornelia Huck VIRTIO_F_VERSION_1)); 25946e790746SPaolo Bonzini 25956e790746SPaolo Bonzini /* MAC_TABLE_ENTRIES may be different from the saved image */ 2596982b78c5SDr. David Alan Gilbert if (n->mac_table.in_use > MAC_TABLE_ENTRIES) { 25976e790746SPaolo Bonzini n->mac_table.in_use = 0; 25986e790746SPaolo Bonzini } 25996e790746SPaolo Bonzini 2600982b78c5SDr. David Alan Gilbert if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { 26016c666823SMichael S. Tsirkin n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); 26026c666823SMichael S. Tsirkin } 26036c666823SMichael S. Tsirkin 26047788c3f2SMikhail Sennikovsky /* 26057788c3f2SMikhail Sennikovsky * curr_guest_offloads will be later overwritten by the 26067788c3f2SMikhail Sennikovsky * virtio_set_features_nocheck call done from the virtio_load. 26077788c3f2SMikhail Sennikovsky * Here we make sure it is preserved and restored accordingly 26087788c3f2SMikhail Sennikovsky * in the virtio_net_post_load_virtio callback. 26097788c3f2SMikhail Sennikovsky */ 26107788c3f2SMikhail Sennikovsky n->saved_guest_offloads = n->curr_guest_offloads; 26116c666823SMichael S. Tsirkin 26126e790746SPaolo Bonzini virtio_net_set_queues(n); 26136e790746SPaolo Bonzini 26146e790746SPaolo Bonzini /* Find the first multicast entry in the saved MAC filter */ 26156e790746SPaolo Bonzini for (i = 0; i < n->mac_table.in_use; i++) { 26166e790746SPaolo Bonzini if (n->mac_table.macs[i * ETH_ALEN] & 1) { 26176e790746SPaolo Bonzini break; 26186e790746SPaolo Bonzini } 26196e790746SPaolo Bonzini } 26206e790746SPaolo Bonzini n->mac_table.first_multi = i; 26216e790746SPaolo Bonzini 26226e790746SPaolo Bonzini /* nc.link_down can't be migrated, so infer link_down according 26236e790746SPaolo Bonzini * to link status bit in n->status */ 26246e790746SPaolo Bonzini link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0; 26256e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 26266e790746SPaolo Bonzini qemu_get_subqueue(n->nic, i)->link_down = link_down; 26276e790746SPaolo Bonzini } 26286e790746SPaolo Bonzini 26296c666823SMichael S. Tsirkin if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && 26306c666823SMichael S. Tsirkin virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { 26319d8c6a25SDr. David Alan Gilbert qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(), 26329d8c6a25SDr. David Alan Gilbert QEMU_CLOCK_VIRTUAL, 26339d8c6a25SDr. David Alan Gilbert virtio_net_announce_timer, n); 26349d8c6a25SDr. David Alan Gilbert if (n->announce_timer.round) { 26359d8c6a25SDr. David Alan Gilbert timer_mod(n->announce_timer.tm, 26369d8c6a25SDr. David Alan Gilbert qemu_clock_get_ms(n->announce_timer.type)); 26379d8c6a25SDr. David Alan Gilbert } else { 2638944458b6SDr. David Alan Gilbert qemu_announce_timer_del(&n->announce_timer, false); 26399d8c6a25SDr. David Alan Gilbert } 26406c666823SMichael S. Tsirkin } 26416c666823SMichael S. Tsirkin 26426e790746SPaolo Bonzini return 0; 26436e790746SPaolo Bonzini } 26446e790746SPaolo Bonzini 26457788c3f2SMikhail Sennikovsky static int virtio_net_post_load_virtio(VirtIODevice *vdev) 26467788c3f2SMikhail Sennikovsky { 26477788c3f2SMikhail Sennikovsky VirtIONet *n = VIRTIO_NET(vdev); 26487788c3f2SMikhail Sennikovsky /* 26497788c3f2SMikhail Sennikovsky * The actual needed state is now in saved_guest_offloads, 26507788c3f2SMikhail Sennikovsky * see virtio_net_post_load_device for detail. 26517788c3f2SMikhail Sennikovsky * Restore it back and apply the desired offloads. 26527788c3f2SMikhail Sennikovsky */ 26537788c3f2SMikhail Sennikovsky n->curr_guest_offloads = n->saved_guest_offloads; 26547788c3f2SMikhail Sennikovsky if (peer_has_vnet_hdr(n)) { 26557788c3f2SMikhail Sennikovsky virtio_net_apply_guest_offloads(n); 26567788c3f2SMikhail Sennikovsky } 26577788c3f2SMikhail Sennikovsky 26587788c3f2SMikhail Sennikovsky return 0; 26597788c3f2SMikhail Sennikovsky } 26607788c3f2SMikhail Sennikovsky 2661982b78c5SDr. David Alan Gilbert /* tx_waiting field of a VirtIONetQueue */ 2662982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = { 2663982b78c5SDr. David Alan Gilbert .name = "virtio-net-queue-tx_waiting", 2664982b78c5SDr. David Alan Gilbert .fields = (VMStateField[]) { 2665982b78c5SDr. David Alan Gilbert VMSTATE_UINT32(tx_waiting, VirtIONetQueue), 2666982b78c5SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 2667982b78c5SDr. David Alan Gilbert }, 2668982b78c5SDr. David Alan Gilbert }; 2669982b78c5SDr. David Alan Gilbert 2670982b78c5SDr. David Alan Gilbert static bool max_queues_gt_1(void *opaque, int version_id) 2671982b78c5SDr. David Alan Gilbert { 2672982b78c5SDr. David Alan Gilbert return VIRTIO_NET(opaque)->max_queues > 1; 2673982b78c5SDr. David Alan Gilbert } 2674982b78c5SDr. David Alan Gilbert 2675982b78c5SDr. David Alan Gilbert static bool has_ctrl_guest_offloads(void *opaque, int version_id) 2676982b78c5SDr. David Alan Gilbert { 2677982b78c5SDr. David Alan Gilbert return virtio_vdev_has_feature(VIRTIO_DEVICE(opaque), 2678982b78c5SDr. David Alan Gilbert VIRTIO_NET_F_CTRL_GUEST_OFFLOADS); 2679982b78c5SDr. David Alan Gilbert } 2680982b78c5SDr. David Alan Gilbert 2681982b78c5SDr. David Alan Gilbert static bool mac_table_fits(void *opaque, int version_id) 2682982b78c5SDr. David Alan Gilbert { 2683982b78c5SDr. David Alan Gilbert return VIRTIO_NET(opaque)->mac_table.in_use <= MAC_TABLE_ENTRIES; 2684982b78c5SDr. David Alan Gilbert } 2685982b78c5SDr. David Alan Gilbert 2686982b78c5SDr. David Alan Gilbert static bool mac_table_doesnt_fit(void *opaque, int version_id) 2687982b78c5SDr. David Alan Gilbert { 2688982b78c5SDr. David Alan Gilbert return !mac_table_fits(opaque, version_id); 2689982b78c5SDr. David Alan Gilbert } 2690982b78c5SDr. David Alan Gilbert 2691982b78c5SDr. David Alan Gilbert /* This temporary type is shared by all the WITH_TMP methods 2692982b78c5SDr. David Alan Gilbert * although only some fields are used by each. 2693982b78c5SDr. David Alan Gilbert */ 2694982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp { 2695982b78c5SDr. David Alan Gilbert VirtIONet *parent; 2696982b78c5SDr. David Alan Gilbert VirtIONetQueue *vqs_1; 2697982b78c5SDr. David Alan Gilbert uint16_t curr_queues_1; 2698982b78c5SDr. David Alan Gilbert uint8_t has_ufo; 2699982b78c5SDr. David Alan Gilbert uint32_t has_vnet_hdr; 2700982b78c5SDr. David Alan Gilbert }; 2701982b78c5SDr. David Alan Gilbert 2702982b78c5SDr. David Alan Gilbert /* The 2nd and subsequent tx_waiting flags are loaded later than 2703982b78c5SDr. David Alan Gilbert * the 1st entry in the queues and only if there's more than one 2704982b78c5SDr. David Alan Gilbert * entry. We use the tmp mechanism to calculate a temporary 2705982b78c5SDr. David Alan Gilbert * pointer and count and also validate the count. 2706982b78c5SDr. David Alan Gilbert */ 2707982b78c5SDr. David Alan Gilbert 270844b1ff31SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_save(void *opaque) 2709982b78c5SDr. David Alan Gilbert { 2710982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2711982b78c5SDr. David Alan Gilbert 2712982b78c5SDr. David Alan Gilbert tmp->vqs_1 = tmp->parent->vqs + 1; 2713982b78c5SDr. David Alan Gilbert tmp->curr_queues_1 = tmp->parent->curr_queues - 1; 2714982b78c5SDr. David Alan Gilbert if (tmp->parent->curr_queues == 0) { 2715982b78c5SDr. David Alan Gilbert tmp->curr_queues_1 = 0; 2716982b78c5SDr. David Alan Gilbert } 271744b1ff31SDr. David Alan Gilbert 271844b1ff31SDr. David Alan Gilbert return 0; 2719982b78c5SDr. David Alan Gilbert } 2720982b78c5SDr. David Alan Gilbert 2721982b78c5SDr. David Alan Gilbert static int virtio_net_tx_waiting_pre_load(void *opaque) 2722982b78c5SDr. David Alan Gilbert { 2723982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2724982b78c5SDr. David Alan Gilbert 2725982b78c5SDr. David Alan Gilbert /* Reuse the pointer setup from save */ 2726982b78c5SDr. David Alan Gilbert virtio_net_tx_waiting_pre_save(opaque); 2727982b78c5SDr. David Alan Gilbert 2728982b78c5SDr. David Alan Gilbert if (tmp->parent->curr_queues > tmp->parent->max_queues) { 2729982b78c5SDr. David Alan Gilbert error_report("virtio-net: curr_queues %x > max_queues %x", 2730982b78c5SDr. David Alan Gilbert tmp->parent->curr_queues, tmp->parent->max_queues); 2731982b78c5SDr. David Alan Gilbert 2732982b78c5SDr. David Alan Gilbert return -EINVAL; 2733982b78c5SDr. David Alan Gilbert } 2734982b78c5SDr. David Alan Gilbert 2735982b78c5SDr. David Alan Gilbert return 0; /* all good */ 2736982b78c5SDr. David Alan Gilbert } 2737982b78c5SDr. David Alan Gilbert 2738982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_tx_waiting = { 2739982b78c5SDr. David Alan Gilbert .name = "virtio-net-tx_waiting", 2740982b78c5SDr. David Alan Gilbert .pre_load = virtio_net_tx_waiting_pre_load, 2741982b78c5SDr. David Alan Gilbert .pre_save = virtio_net_tx_waiting_pre_save, 2742982b78c5SDr. David Alan Gilbert .fields = (VMStateField[]) { 2743982b78c5SDr. David Alan Gilbert VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp, 2744982b78c5SDr. David Alan Gilbert curr_queues_1, 2745982b78c5SDr. David Alan Gilbert vmstate_virtio_net_queue_tx_waiting, 2746982b78c5SDr. David Alan Gilbert struct VirtIONetQueue), 2747982b78c5SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 2748982b78c5SDr. David Alan Gilbert }, 2749982b78c5SDr. David Alan Gilbert }; 2750982b78c5SDr. David Alan Gilbert 2751982b78c5SDr. David Alan Gilbert /* the 'has_ufo' flag is just tested; if the incoming stream has the 2752982b78c5SDr. David Alan Gilbert * flag set we need to check that we have it 2753982b78c5SDr. David Alan Gilbert */ 2754982b78c5SDr. David Alan Gilbert static int virtio_net_ufo_post_load(void *opaque, int version_id) 2755982b78c5SDr. David Alan Gilbert { 2756982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2757982b78c5SDr. David Alan Gilbert 2758982b78c5SDr. David Alan Gilbert if (tmp->has_ufo && !peer_has_ufo(tmp->parent)) { 2759982b78c5SDr. David Alan Gilbert error_report("virtio-net: saved image requires TUN_F_UFO support"); 2760982b78c5SDr. David Alan Gilbert return -EINVAL; 2761982b78c5SDr. David Alan Gilbert } 2762982b78c5SDr. David Alan Gilbert 2763982b78c5SDr. David Alan Gilbert return 0; 2764982b78c5SDr. David Alan Gilbert } 2765982b78c5SDr. David Alan Gilbert 276644b1ff31SDr. David Alan Gilbert static int virtio_net_ufo_pre_save(void *opaque) 2767982b78c5SDr. David Alan Gilbert { 2768982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2769982b78c5SDr. David Alan Gilbert 2770982b78c5SDr. David Alan Gilbert tmp->has_ufo = tmp->parent->has_ufo; 277144b1ff31SDr. David Alan Gilbert 277244b1ff31SDr. David Alan Gilbert return 0; 2773982b78c5SDr. David Alan Gilbert } 2774982b78c5SDr. David Alan Gilbert 2775982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_ufo = { 2776982b78c5SDr. David Alan Gilbert .name = "virtio-net-ufo", 2777982b78c5SDr. David Alan Gilbert .post_load = virtio_net_ufo_post_load, 2778982b78c5SDr. David Alan Gilbert .pre_save = virtio_net_ufo_pre_save, 2779982b78c5SDr. David Alan Gilbert .fields = (VMStateField[]) { 2780982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(has_ufo, struct VirtIONetMigTmp), 2781982b78c5SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 2782982b78c5SDr. David Alan Gilbert }, 2783982b78c5SDr. David Alan Gilbert }; 2784982b78c5SDr. David Alan Gilbert 2785982b78c5SDr. David Alan Gilbert /* the 'has_vnet_hdr' flag is just tested; if the incoming stream has the 2786982b78c5SDr. David Alan Gilbert * flag set we need to check that we have it 2787982b78c5SDr. David Alan Gilbert */ 2788982b78c5SDr. David Alan Gilbert static int virtio_net_vnet_post_load(void *opaque, int version_id) 2789982b78c5SDr. David Alan Gilbert { 2790982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2791982b78c5SDr. David Alan Gilbert 2792982b78c5SDr. David Alan Gilbert if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) { 2793982b78c5SDr. David Alan Gilbert error_report("virtio-net: saved image requires vnet_hdr=on"); 2794982b78c5SDr. David Alan Gilbert return -EINVAL; 2795982b78c5SDr. David Alan Gilbert } 2796982b78c5SDr. David Alan Gilbert 2797982b78c5SDr. David Alan Gilbert return 0; 2798982b78c5SDr. David Alan Gilbert } 2799982b78c5SDr. David Alan Gilbert 280044b1ff31SDr. David Alan Gilbert static int virtio_net_vnet_pre_save(void *opaque) 2801982b78c5SDr. David Alan Gilbert { 2802982b78c5SDr. David Alan Gilbert struct VirtIONetMigTmp *tmp = opaque; 2803982b78c5SDr. David Alan Gilbert 2804982b78c5SDr. David Alan Gilbert tmp->has_vnet_hdr = tmp->parent->has_vnet_hdr; 280544b1ff31SDr. David Alan Gilbert 280644b1ff31SDr. David Alan Gilbert return 0; 2807982b78c5SDr. David Alan Gilbert } 2808982b78c5SDr. David Alan Gilbert 2809982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_has_vnet = { 2810982b78c5SDr. David Alan Gilbert .name = "virtio-net-vnet", 2811982b78c5SDr. David Alan Gilbert .post_load = virtio_net_vnet_post_load, 2812982b78c5SDr. David Alan Gilbert .pre_save = virtio_net_vnet_pre_save, 2813982b78c5SDr. David Alan Gilbert .fields = (VMStateField[]) { 2814982b78c5SDr. David Alan Gilbert VMSTATE_UINT32(has_vnet_hdr, struct VirtIONetMigTmp), 2815982b78c5SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 2816982b78c5SDr. David Alan Gilbert }, 2817982b78c5SDr. David Alan Gilbert }; 2818982b78c5SDr. David Alan Gilbert 2819982b78c5SDr. David Alan Gilbert static const VMStateDescription vmstate_virtio_net_device = { 2820982b78c5SDr. David Alan Gilbert .name = "virtio-net-device", 2821982b78c5SDr. David Alan Gilbert .version_id = VIRTIO_NET_VM_VERSION, 2822982b78c5SDr. David Alan Gilbert .minimum_version_id = VIRTIO_NET_VM_VERSION, 2823982b78c5SDr. David Alan Gilbert .post_load = virtio_net_post_load_device, 2824982b78c5SDr. David Alan Gilbert .fields = (VMStateField[]) { 2825982b78c5SDr. David Alan Gilbert VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN), 2826982b78c5SDr. David Alan Gilbert VMSTATE_STRUCT_POINTER(vqs, VirtIONet, 2827982b78c5SDr. David Alan Gilbert vmstate_virtio_net_queue_tx_waiting, 2828982b78c5SDr. David Alan Gilbert VirtIONetQueue), 2829982b78c5SDr. David Alan Gilbert VMSTATE_UINT32(mergeable_rx_bufs, VirtIONet), 2830982b78c5SDr. David Alan Gilbert VMSTATE_UINT16(status, VirtIONet), 2831982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(promisc, VirtIONet), 2832982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(allmulti, VirtIONet), 2833982b78c5SDr. David Alan Gilbert VMSTATE_UINT32(mac_table.in_use, VirtIONet), 2834982b78c5SDr. David Alan Gilbert 2835982b78c5SDr. David Alan Gilbert /* Guarded pair: If it fits we load it, else we throw it away 2836982b78c5SDr. David Alan Gilbert * - can happen if source has a larger MAC table.; post-load 2837982b78c5SDr. David Alan Gilbert * sets flags in this case. 2838982b78c5SDr. David Alan Gilbert */ 2839982b78c5SDr. David Alan Gilbert VMSTATE_VBUFFER_MULTIPLY(mac_table.macs, VirtIONet, 2840982b78c5SDr. David Alan Gilbert 0, mac_table_fits, mac_table.in_use, 2841982b78c5SDr. David Alan Gilbert ETH_ALEN), 2842982b78c5SDr. David Alan Gilbert VMSTATE_UNUSED_VARRAY_UINT32(VirtIONet, mac_table_doesnt_fit, 0, 2843982b78c5SDr. David Alan Gilbert mac_table.in_use, ETH_ALEN), 2844982b78c5SDr. David Alan Gilbert 2845982b78c5SDr. David Alan Gilbert /* Note: This is an array of uint32's that's always been saved as a 2846982b78c5SDr. David Alan Gilbert * buffer; hold onto your endiannesses; it's actually used as a bitmap 2847982b78c5SDr. David Alan Gilbert * but based on the uint. 2848982b78c5SDr. David Alan Gilbert */ 2849982b78c5SDr. David Alan Gilbert VMSTATE_BUFFER_POINTER_UNSAFE(vlans, VirtIONet, 0, MAX_VLAN >> 3), 2850982b78c5SDr. David Alan Gilbert VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp, 2851982b78c5SDr. David Alan Gilbert vmstate_virtio_net_has_vnet), 2852982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(mac_table.multi_overflow, VirtIONet), 2853982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(mac_table.uni_overflow, VirtIONet), 2854982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(alluni, VirtIONet), 2855982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(nomulti, VirtIONet), 2856982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(nouni, VirtIONet), 2857982b78c5SDr. David Alan Gilbert VMSTATE_UINT8(nobcast, VirtIONet), 2858982b78c5SDr. David Alan Gilbert VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp, 2859982b78c5SDr. David Alan Gilbert vmstate_virtio_net_has_ufo), 2860982b78c5SDr. David Alan Gilbert VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0, 2861982b78c5SDr. David Alan Gilbert vmstate_info_uint16_equal, uint16_t), 2862982b78c5SDr. David Alan Gilbert VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1), 2863982b78c5SDr. David Alan Gilbert VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp, 2864982b78c5SDr. David Alan Gilbert vmstate_virtio_net_tx_waiting), 2865982b78c5SDr. David Alan Gilbert VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet, 2866982b78c5SDr. David Alan Gilbert has_ctrl_guest_offloads), 2867982b78c5SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 2868982b78c5SDr. David Alan Gilbert }, 2869982b78c5SDr. David Alan Gilbert }; 2870982b78c5SDr. David Alan Gilbert 28716e790746SPaolo Bonzini static NetClientInfo net_virtio_info = { 2872f394b2e2SEric Blake .type = NET_CLIENT_DRIVER_NIC, 28736e790746SPaolo Bonzini .size = sizeof(NICState), 28746e790746SPaolo Bonzini .can_receive = virtio_net_can_receive, 28756e790746SPaolo Bonzini .receive = virtio_net_receive, 28766e790746SPaolo Bonzini .link_status_changed = virtio_net_set_link_status, 2877b1be4280SAmos Kong .query_rx_filter = virtio_net_query_rxfilter, 2878b2c929f0SDr. David Alan Gilbert .announce = virtio_net_announce, 28796e790746SPaolo Bonzini }; 28806e790746SPaolo Bonzini 28816e790746SPaolo Bonzini static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) 28826e790746SPaolo Bonzini { 288317a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 28846e790746SPaolo Bonzini NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); 28856e790746SPaolo Bonzini assert(n->vhost_started); 2886ed8b4afeSNikolay Nikolaev return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx); 28876e790746SPaolo Bonzini } 28886e790746SPaolo Bonzini 28896e790746SPaolo Bonzini static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, 28906e790746SPaolo Bonzini bool mask) 28916e790746SPaolo Bonzini { 289217a0ca55SKONRAD Frederic VirtIONet *n = VIRTIO_NET(vdev); 28936e790746SPaolo Bonzini NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); 28946e790746SPaolo Bonzini assert(n->vhost_started); 2895ed8b4afeSNikolay Nikolaev vhost_net_virtqueue_mask(get_vhost_net(nc->peer), 28966e790746SPaolo Bonzini vdev, idx, mask); 28976e790746SPaolo Bonzini } 28986e790746SPaolo Bonzini 2899019a3edbSGerd Hoffmann static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features) 29006e790746SPaolo Bonzini { 29010cd09c3aSCornelia Huck virtio_add_feature(&host_features, VIRTIO_NET_F_MAC); 2902a93e599dSMaxime Coquelin 2903ba550851SStefano Garzarella n->config_size = virtio_feature_get_config_size(feature_sizes, 2904ba550851SStefano Garzarella host_features); 290517ec5a86SKONRAD Frederic } 29066e790746SPaolo Bonzini 29078a253ec2SKONRAD Frederic void virtio_net_set_netclient_name(VirtIONet *n, const char *name, 29088a253ec2SKONRAD Frederic const char *type) 29098a253ec2SKONRAD Frederic { 29108a253ec2SKONRAD Frederic /* 29118a253ec2SKONRAD Frederic * The name can be NULL, the netclient name will be type.x. 29128a253ec2SKONRAD Frederic */ 29138a253ec2SKONRAD Frederic assert(type != NULL); 29148a253ec2SKONRAD Frederic 29158a253ec2SKONRAD Frederic g_free(n->netclient_name); 29168a253ec2SKONRAD Frederic g_free(n->netclient_type); 29178a253ec2SKONRAD Frederic n->netclient_name = g_strdup(name); 29188a253ec2SKONRAD Frederic n->netclient_type = g_strdup(type); 29198a253ec2SKONRAD Frederic } 29208a253ec2SKONRAD Frederic 29219711cd0dSJens Freimann static bool failover_unplug_primary(VirtIONet *n) 29229711cd0dSJens Freimann { 29239711cd0dSJens Freimann HotplugHandler *hotplug_ctrl; 29249711cd0dSJens Freimann PCIDevice *pci_dev; 29259711cd0dSJens Freimann Error *err = NULL; 29269711cd0dSJens Freimann 29279711cd0dSJens Freimann hotplug_ctrl = qdev_get_hotplug_handler(n->primary_dev); 29289711cd0dSJens Freimann if (hotplug_ctrl) { 29299711cd0dSJens Freimann pci_dev = PCI_DEVICE(n->primary_dev); 29309711cd0dSJens Freimann pci_dev->partially_hotplugged = true; 29319711cd0dSJens Freimann hotplug_handler_unplug_request(hotplug_ctrl, n->primary_dev, &err); 29329711cd0dSJens Freimann if (err) { 29339711cd0dSJens Freimann error_report_err(err); 29349711cd0dSJens Freimann return false; 29359711cd0dSJens Freimann } 29369711cd0dSJens Freimann } else { 29379711cd0dSJens Freimann return false; 29389711cd0dSJens Freimann } 29399711cd0dSJens Freimann return true; 29409711cd0dSJens Freimann } 29419711cd0dSJens Freimann 29429711cd0dSJens Freimann static bool failover_replug_primary(VirtIONet *n, Error **errp) 29439711cd0dSJens Freimann { 29445a0948d3SMarkus Armbruster Error *err = NULL; 29459711cd0dSJens Freimann HotplugHandler *hotplug_ctrl; 29469711cd0dSJens Freimann PCIDevice *pdev = PCI_DEVICE(n->primary_dev); 29479711cd0dSJens Freimann 29489711cd0dSJens Freimann if (!pdev->partially_hotplugged) { 29499711cd0dSJens Freimann return true; 29509711cd0dSJens Freimann } 29519711cd0dSJens Freimann if (!n->primary_device_opts) { 29529711cd0dSJens Freimann n->primary_device_opts = qemu_opts_from_qdict( 29539711cd0dSJens Freimann qemu_find_opts("device"), 29549711cd0dSJens Freimann n->primary_device_dict, errp); 2955150ab54aSJens Freimann if (!n->primary_device_opts) { 29565a0948d3SMarkus Armbruster return false; 29579711cd0dSJens Freimann } 2958150ab54aSJens Freimann } 29599711cd0dSJens Freimann n->primary_bus = n->primary_dev->parent_bus; 2960150ab54aSJens Freimann if (!n->primary_bus) { 2961150ab54aSJens Freimann error_setg(errp, "virtio_net: couldn't find primary bus"); 29625a0948d3SMarkus Armbruster return false; 29639711cd0dSJens Freimann } 29649711cd0dSJens Freimann qdev_set_parent_bus(n->primary_dev, n->primary_bus); 29659711cd0dSJens Freimann n->primary_should_be_hidden = false; 29669711cd0dSJens Freimann qemu_opt_set_bool(n->primary_device_opts, 29675a0948d3SMarkus Armbruster "partially_hotplugged", true, &err); 29685a0948d3SMarkus Armbruster if (err) { 29695a0948d3SMarkus Armbruster goto out; 29705a0948d3SMarkus Armbruster } 29719711cd0dSJens Freimann hotplug_ctrl = qdev_get_hotplug_handler(n->primary_dev); 29729711cd0dSJens Freimann if (hotplug_ctrl) { 29735a0948d3SMarkus Armbruster hotplug_handler_pre_plug(hotplug_ctrl, n->primary_dev, &err); 29745a0948d3SMarkus Armbruster if (err) { 29755a0948d3SMarkus Armbruster goto out; 29765a0948d3SMarkus Armbruster } 29779711cd0dSJens Freimann hotplug_handler_plug(hotplug_ctrl, n->primary_dev, errp); 29789711cd0dSJens Freimann } 2979150ab54aSJens Freimann 2980150ab54aSJens Freimann out: 29815a0948d3SMarkus Armbruster error_propagate(errp, err); 29825a0948d3SMarkus Armbruster return !err; 29839711cd0dSJens Freimann } 29849711cd0dSJens Freimann 29859711cd0dSJens Freimann static void virtio_net_handle_migration_primary(VirtIONet *n, 29869711cd0dSJens Freimann MigrationState *s) 29879711cd0dSJens Freimann { 29889711cd0dSJens Freimann bool should_be_hidden; 29899711cd0dSJens Freimann Error *err = NULL; 29909711cd0dSJens Freimann 29919711cd0dSJens Freimann should_be_hidden = atomic_read(&n->primary_should_be_hidden); 29929711cd0dSJens Freimann 29939711cd0dSJens Freimann if (!n->primary_dev) { 29949711cd0dSJens Freimann n->primary_dev = virtio_connect_failover_devices(n, n->qdev, &err); 29959711cd0dSJens Freimann if (!n->primary_dev) { 29969711cd0dSJens Freimann return; 29979711cd0dSJens Freimann } 29989711cd0dSJens Freimann } 29999711cd0dSJens Freimann 30004dbac1aeSMarkus Armbruster if (migration_in_setup(s) && !should_be_hidden) { 30019711cd0dSJens Freimann if (failover_unplug_primary(n)) { 30023cad405bSMarc-André Lureau vmstate_unregister(VMSTATE_IF(n->primary_dev), 30033cad405bSMarc-André Lureau qdev_get_vmsd(n->primary_dev), 30049711cd0dSJens Freimann n->primary_dev); 30059711cd0dSJens Freimann qapi_event_send_unplug_primary(n->primary_device_id); 30069711cd0dSJens Freimann atomic_set(&n->primary_should_be_hidden, true); 30079711cd0dSJens Freimann } else { 30089711cd0dSJens Freimann warn_report("couldn't unplug primary device"); 30099711cd0dSJens Freimann } 30109711cd0dSJens Freimann } else if (migration_has_failed(s)) { 3011150ab54aSJens Freimann /* We already unplugged the device let's plug it back */ 30129711cd0dSJens Freimann if (!failover_replug_primary(n, &err)) { 30139711cd0dSJens Freimann if (err) { 30149711cd0dSJens Freimann error_report_err(err); 30159711cd0dSJens Freimann } 30169711cd0dSJens Freimann } 30179711cd0dSJens Freimann } 30189711cd0dSJens Freimann } 30199711cd0dSJens Freimann 30209711cd0dSJens Freimann static void virtio_net_migration_state_notifier(Notifier *notifier, void *data) 30219711cd0dSJens Freimann { 30229711cd0dSJens Freimann MigrationState *s = data; 30239711cd0dSJens Freimann VirtIONet *n = container_of(notifier, VirtIONet, migration_state); 30249711cd0dSJens Freimann virtio_net_handle_migration_primary(n, s); 30259711cd0dSJens Freimann } 30269711cd0dSJens Freimann 30279711cd0dSJens Freimann static int virtio_net_primary_should_be_hidden(DeviceListener *listener, 30289711cd0dSJens Freimann QemuOpts *device_opts) 30299711cd0dSJens Freimann { 30309711cd0dSJens Freimann VirtIONet *n = container_of(listener, VirtIONet, primary_listener); 30314d0e59acSJens Freimann bool match_found = false; 30324d0e59acSJens Freimann bool hide = false; 30339711cd0dSJens Freimann 30344d0e59acSJens Freimann if (!device_opts) { 30354d0e59acSJens Freimann return -1; 30364d0e59acSJens Freimann } 30379711cd0dSJens Freimann n->primary_device_dict = qemu_opts_to_qdict(device_opts, 30389711cd0dSJens Freimann n->primary_device_dict); 30399711cd0dSJens Freimann if (n->primary_device_dict) { 30409711cd0dSJens Freimann g_free(n->standby_id); 30419711cd0dSJens Freimann n->standby_id = g_strdup(qdict_get_try_str(n->primary_device_dict, 30429711cd0dSJens Freimann "failover_pair_id")); 30439711cd0dSJens Freimann } 30444d0e59acSJens Freimann if (g_strcmp0(n->standby_id, n->netclient_name) == 0) { 30459711cd0dSJens Freimann match_found = true; 30469711cd0dSJens Freimann } else { 30479711cd0dSJens Freimann match_found = false; 30489711cd0dSJens Freimann hide = false; 30499711cd0dSJens Freimann g_free(n->standby_id); 30509711cd0dSJens Freimann n->primary_device_dict = NULL; 30519711cd0dSJens Freimann goto out; 30529711cd0dSJens Freimann } 30539711cd0dSJens Freimann 30549711cd0dSJens Freimann n->primary_device_opts = device_opts; 30559711cd0dSJens Freimann 30569711cd0dSJens Freimann /* primary_should_be_hidden is set during feature negotiation */ 30579711cd0dSJens Freimann hide = atomic_read(&n->primary_should_be_hidden); 30589711cd0dSJens Freimann 30599711cd0dSJens Freimann if (n->primary_device_dict) { 30609711cd0dSJens Freimann g_free(n->primary_device_id); 30619711cd0dSJens Freimann n->primary_device_id = g_strdup(qdict_get_try_str( 30629711cd0dSJens Freimann n->primary_device_dict, "id")); 30639711cd0dSJens Freimann if (!n->primary_device_id) { 30649711cd0dSJens Freimann warn_report("primary_device_id not set"); 30659711cd0dSJens Freimann } 30669711cd0dSJens Freimann } 30679711cd0dSJens Freimann 30689711cd0dSJens Freimann out: 30699711cd0dSJens Freimann if (match_found && hide) { 30709711cd0dSJens Freimann return 1; 30719711cd0dSJens Freimann } else if (match_found && !hide) { 30729711cd0dSJens Freimann return 0; 30739711cd0dSJens Freimann } else { 30749711cd0dSJens Freimann return -1; 30759711cd0dSJens Freimann } 30769711cd0dSJens Freimann } 30779711cd0dSJens Freimann 3078e6f746b3SAndreas Färber static void virtio_net_device_realize(DeviceState *dev, Error **errp) 307917ec5a86SKONRAD Frederic { 3080e6f746b3SAndreas Färber VirtIODevice *vdev = VIRTIO_DEVICE(dev); 3081284a32f0SAndreas Färber VirtIONet *n = VIRTIO_NET(dev); 3082284a32f0SAndreas Färber NetClientState *nc; 30831773d9eeSKONRAD Frederic int i; 308417ec5a86SKONRAD Frederic 3085a93e599dSMaxime Coquelin if (n->net_conf.mtu) { 3086127833eeSJason Baron n->host_features |= (1ULL << VIRTIO_NET_F_MTU); 3087a93e599dSMaxime Coquelin } 3088a93e599dSMaxime Coquelin 30899473939eSJason Baron if (n->net_conf.duplex_str) { 30909473939eSJason Baron if (strncmp(n->net_conf.duplex_str, "half", 5) == 0) { 30919473939eSJason Baron n->net_conf.duplex = DUPLEX_HALF; 30929473939eSJason Baron } else if (strncmp(n->net_conf.duplex_str, "full", 5) == 0) { 30939473939eSJason Baron n->net_conf.duplex = DUPLEX_FULL; 30949473939eSJason Baron } else { 30959473939eSJason Baron error_setg(errp, "'duplex' must be 'half' or 'full'"); 3096843c4cfcSMarkus Armbruster return; 30979473939eSJason Baron } 30989473939eSJason Baron n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX); 30999473939eSJason Baron } else { 31009473939eSJason Baron n->net_conf.duplex = DUPLEX_UNKNOWN; 31019473939eSJason Baron } 31029473939eSJason Baron 31039473939eSJason Baron if (n->net_conf.speed < SPEED_UNKNOWN) { 31049473939eSJason Baron error_setg(errp, "'speed' must be between 0 and INT_MAX"); 3105843c4cfcSMarkus Armbruster return; 3106843c4cfcSMarkus Armbruster } 3107843c4cfcSMarkus Armbruster if (n->net_conf.speed >= 0) { 31089473939eSJason Baron n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX); 31099473939eSJason Baron } 31109473939eSJason Baron 31119711cd0dSJens Freimann if (n->failover) { 31129711cd0dSJens Freimann n->primary_listener.should_be_hidden = 31139711cd0dSJens Freimann virtio_net_primary_should_be_hidden; 31149711cd0dSJens Freimann atomic_set(&n->primary_should_be_hidden, true); 31159711cd0dSJens Freimann device_listener_register(&n->primary_listener); 31169711cd0dSJens Freimann n->migration_state.notify = virtio_net_migration_state_notifier; 31179711cd0dSJens Freimann add_migration_state_change_notifier(&n->migration_state); 31189711cd0dSJens Freimann n->host_features |= (1ULL << VIRTIO_NET_F_STANDBY); 31199711cd0dSJens Freimann } 31209711cd0dSJens Freimann 3121da3e8a23SShannon Zhao virtio_net_set_config_size(n, n->host_features); 3122284a32f0SAndreas Färber virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size); 312317ec5a86SKONRAD Frederic 31241c0fbfa3SMichael S. Tsirkin /* 31251c0fbfa3SMichael S. Tsirkin * We set a lower limit on RX queue size to what it always was. 31261c0fbfa3SMichael S. Tsirkin * Guests that want a smaller ring can always resize it without 31271c0fbfa3SMichael S. Tsirkin * help from us (using virtio 1 and up). 31281c0fbfa3SMichael S. Tsirkin */ 31291c0fbfa3SMichael S. Tsirkin if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE || 31301c0fbfa3SMichael S. Tsirkin n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE || 31315f997fd1SMichal Privoznik !is_power_of_2(n->net_conf.rx_queue_size)) { 31321c0fbfa3SMichael S. Tsirkin error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), " 31331c0fbfa3SMichael S. Tsirkin "must be a power of 2 between %d and %d.", 31341c0fbfa3SMichael S. Tsirkin n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE, 31351c0fbfa3SMichael S. Tsirkin VIRTQUEUE_MAX_SIZE); 31361c0fbfa3SMichael S. Tsirkin virtio_cleanup(vdev); 31371c0fbfa3SMichael S. Tsirkin return; 31381c0fbfa3SMichael S. Tsirkin } 31391c0fbfa3SMichael S. Tsirkin 31409b02e161SWei Wang if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE || 31419b02e161SWei Wang n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE || 31429b02e161SWei Wang !is_power_of_2(n->net_conf.tx_queue_size)) { 31439b02e161SWei Wang error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), " 31449b02e161SWei Wang "must be a power of 2 between %d and %d", 31459b02e161SWei Wang n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE, 31469b02e161SWei Wang VIRTQUEUE_MAX_SIZE); 31479b02e161SWei Wang virtio_cleanup(vdev); 31489b02e161SWei Wang return; 31499b02e161SWei Wang } 31509b02e161SWei Wang 3151575a1c0eSJiri Pirko n->max_queues = MAX(n->nic_conf.peers.queues, 1); 315287b3bd1cSJason Wang if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) { 31537e0e736eSJason Wang error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " 3154631b22eaSStefan Weil "must be a positive integer less than %d.", 315587b3bd1cSJason Wang n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2); 31567e0e736eSJason Wang virtio_cleanup(vdev); 31577e0e736eSJason Wang return; 31587e0e736eSJason Wang } 31596e790746SPaolo Bonzini n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues); 31606e790746SPaolo Bonzini n->curr_queues = 1; 31611773d9eeSKONRAD Frederic n->tx_timeout = n->net_conf.txtimer; 31626e790746SPaolo Bonzini 31631773d9eeSKONRAD Frederic if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer") 31641773d9eeSKONRAD Frederic && strcmp(n->net_conf.tx, "bh")) { 31650765691eSMarkus Armbruster warn_report("virtio-net: " 31666e790746SPaolo Bonzini "Unknown option tx=%s, valid options: \"timer\" \"bh\"", 31671773d9eeSKONRAD Frederic n->net_conf.tx); 31680765691eSMarkus Armbruster error_printf("Defaulting to \"bh\""); 31696e790746SPaolo Bonzini } 31706e790746SPaolo Bonzini 31712eef278bSMichael S. Tsirkin n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n), 31722eef278bSMichael S. Tsirkin n->net_conf.tx_queue_size); 31739b02e161SWei Wang 3174da51a335SJason Wang for (i = 0; i < n->max_queues; i++) { 3175f9d6dbf0SWen Congyang virtio_net_add_queue(n, i); 3176da51a335SJason Wang } 3177da51a335SJason Wang 317817a0ca55SKONRAD Frederic n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); 31791773d9eeSKONRAD Frederic qemu_macaddr_default_if_unset(&n->nic_conf.macaddr); 31801773d9eeSKONRAD Frederic memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac)); 31816e790746SPaolo Bonzini n->status = VIRTIO_NET_S_LINK_UP; 31829d8c6a25SDr. David Alan Gilbert qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(), 31839d8c6a25SDr. David Alan Gilbert QEMU_CLOCK_VIRTUAL, 3184f57fcf70SJason Wang virtio_net_announce_timer, n); 3185b2c929f0SDr. David Alan Gilbert n->announce_timer.round = 0; 31866e790746SPaolo Bonzini 31878a253ec2SKONRAD Frederic if (n->netclient_type) { 31888a253ec2SKONRAD Frederic /* 31898a253ec2SKONRAD Frederic * Happen when virtio_net_set_netclient_name has been called. 31908a253ec2SKONRAD Frederic */ 31918a253ec2SKONRAD Frederic n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf, 31928a253ec2SKONRAD Frederic n->netclient_type, n->netclient_name, n); 31938a253ec2SKONRAD Frederic } else { 31941773d9eeSKONRAD Frederic n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf, 3195284a32f0SAndreas Färber object_get_typename(OBJECT(dev)), dev->id, n); 31968a253ec2SKONRAD Frederic } 31978a253ec2SKONRAD Frederic 31986e790746SPaolo Bonzini peer_test_vnet_hdr(n); 31996e790746SPaolo Bonzini if (peer_has_vnet_hdr(n)) { 32006e790746SPaolo Bonzini for (i = 0; i < n->max_queues; i++) { 3201d6085e3aSStefan Hajnoczi qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); 32026e790746SPaolo Bonzini } 32036e790746SPaolo Bonzini n->host_hdr_len = sizeof(struct virtio_net_hdr); 32046e790746SPaolo Bonzini } else { 32056e790746SPaolo Bonzini n->host_hdr_len = 0; 32066e790746SPaolo Bonzini } 32076e790746SPaolo Bonzini 32081773d9eeSKONRAD Frederic qemu_format_nic_info_str(qemu_get_queue(n->nic), n->nic_conf.macaddr.a); 32096e790746SPaolo Bonzini 32106e790746SPaolo Bonzini n->vqs[0].tx_waiting = 0; 32111773d9eeSKONRAD Frederic n->tx_burst = n->net_conf.txburst; 3212bb9d17f8SCornelia Huck virtio_net_set_mrg_rx_bufs(n, 0, 0); 32136e790746SPaolo Bonzini n->promisc = 1; /* for compatibility */ 32146e790746SPaolo Bonzini 32156e790746SPaolo Bonzini n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); 32166e790746SPaolo Bonzini 32176e790746SPaolo Bonzini n->vlans = g_malloc0(MAX_VLAN >> 3); 32186e790746SPaolo Bonzini 3219b1be4280SAmos Kong nc = qemu_get_queue(n->nic); 3220b1be4280SAmos Kong nc->rxfilter_notify_enabled = 1; 3221b1be4280SAmos Kong 32222974e916SYuri Benditovich QTAILQ_INIT(&n->rsc_chains); 3223284a32f0SAndreas Färber n->qdev = dev; 322417ec5a86SKONRAD Frederic } 322517ec5a86SKONRAD Frederic 3226b69c3c21SMarkus Armbruster static void virtio_net_device_unrealize(DeviceState *dev) 322717ec5a86SKONRAD Frederic { 3228306ec6c3SAndreas Färber VirtIODevice *vdev = VIRTIO_DEVICE(dev); 3229306ec6c3SAndreas Färber VirtIONet *n = VIRTIO_NET(dev); 3230f9d6dbf0SWen Congyang int i, max_queues; 323117ec5a86SKONRAD Frederic 323217ec5a86SKONRAD Frederic /* This will stop vhost backend if appropriate. */ 323317ec5a86SKONRAD Frederic virtio_net_set_status(vdev, 0); 323417ec5a86SKONRAD Frederic 32358a253ec2SKONRAD Frederic g_free(n->netclient_name); 32368a253ec2SKONRAD Frederic n->netclient_name = NULL; 32378a253ec2SKONRAD Frederic g_free(n->netclient_type); 32388a253ec2SKONRAD Frederic n->netclient_type = NULL; 32398a253ec2SKONRAD Frederic 324017ec5a86SKONRAD Frederic g_free(n->mac_table.macs); 324117ec5a86SKONRAD Frederic g_free(n->vlans); 324217ec5a86SKONRAD Frederic 32439711cd0dSJens Freimann if (n->failover) { 32449711cd0dSJens Freimann g_free(n->primary_device_id); 32459711cd0dSJens Freimann g_free(n->standby_id); 32469711cd0dSJens Freimann qobject_unref(n->primary_device_dict); 32479711cd0dSJens Freimann n->primary_device_dict = NULL; 32489711cd0dSJens Freimann } 32499711cd0dSJens Freimann 3250f9d6dbf0SWen Congyang max_queues = n->multiqueue ? n->max_queues : 1; 3251f9d6dbf0SWen Congyang for (i = 0; i < max_queues; i++) { 3252f9d6dbf0SWen Congyang virtio_net_del_queue(n, i); 325317ec5a86SKONRAD Frederic } 3254d945d9f1SYuri Benditovich /* delete also control vq */ 3255d945d9f1SYuri Benditovich virtio_del_queue(vdev, max_queues * 2); 3256944458b6SDr. David Alan Gilbert qemu_announce_timer_del(&n->announce_timer, false); 325717ec5a86SKONRAD Frederic g_free(n->vqs); 325817ec5a86SKONRAD Frederic qemu_del_nic(n->nic); 32592974e916SYuri Benditovich virtio_net_rsc_cleanup(n); 3260*59079029SYuri Benditovich g_free(n->rss_data.indirections_table); 32616a1a8cc7SKONRAD Frederic virtio_cleanup(vdev); 326217ec5a86SKONRAD Frederic } 326317ec5a86SKONRAD Frederic 326417ec5a86SKONRAD Frederic static void virtio_net_instance_init(Object *obj) 326517ec5a86SKONRAD Frederic { 326617ec5a86SKONRAD Frederic VirtIONet *n = VIRTIO_NET(obj); 326717ec5a86SKONRAD Frederic 326817ec5a86SKONRAD Frederic /* 326917ec5a86SKONRAD Frederic * The default config_size is sizeof(struct virtio_net_config). 327017ec5a86SKONRAD Frederic * Can be overriden with virtio_net_set_config_size. 327117ec5a86SKONRAD Frederic */ 327217ec5a86SKONRAD Frederic n->config_size = sizeof(struct virtio_net_config); 3273aa4197c3SGonglei device_add_bootindex_property(obj, &n->nic_conf.bootindex, 3274aa4197c3SGonglei "bootindex", "/ethernet-phy@0", 327540c2281cSMarkus Armbruster DEVICE(n)); 327617ec5a86SKONRAD Frederic } 327717ec5a86SKONRAD Frederic 327844b1ff31SDr. David Alan Gilbert static int virtio_net_pre_save(void *opaque) 32794d45dcfbSHalil Pasic { 32804d45dcfbSHalil Pasic VirtIONet *n = opaque; 32814d45dcfbSHalil Pasic 32824d45dcfbSHalil Pasic /* At this point, backend must be stopped, otherwise 32834d45dcfbSHalil Pasic * it might keep writing to memory. */ 32844d45dcfbSHalil Pasic assert(!n->vhost_started); 328544b1ff31SDr. David Alan Gilbert 328644b1ff31SDr. David Alan Gilbert return 0; 32874d45dcfbSHalil Pasic } 32884d45dcfbSHalil Pasic 32899711cd0dSJens Freimann static bool primary_unplug_pending(void *opaque) 32909711cd0dSJens Freimann { 32919711cd0dSJens Freimann DeviceState *dev = opaque; 32929711cd0dSJens Freimann VirtIODevice *vdev = VIRTIO_DEVICE(dev); 32939711cd0dSJens Freimann VirtIONet *n = VIRTIO_NET(vdev); 32949711cd0dSJens Freimann 3295284f42a5SJens Freimann if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_STANDBY)) { 3296284f42a5SJens Freimann return false; 3297284f42a5SJens Freimann } 32989711cd0dSJens Freimann return n->primary_dev ? n->primary_dev->pending_deleted_event : false; 32999711cd0dSJens Freimann } 33009711cd0dSJens Freimann 33019711cd0dSJens Freimann static bool dev_unplug_pending(void *opaque) 33029711cd0dSJens Freimann { 33039711cd0dSJens Freimann DeviceState *dev = opaque; 33049711cd0dSJens Freimann VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev); 33059711cd0dSJens Freimann 33069711cd0dSJens Freimann return vdc->primary_unplug_pending(dev); 33079711cd0dSJens Freimann } 33089711cd0dSJens Freimann 33094d45dcfbSHalil Pasic static const VMStateDescription vmstate_virtio_net = { 33104d45dcfbSHalil Pasic .name = "virtio-net", 33114d45dcfbSHalil Pasic .minimum_version_id = VIRTIO_NET_VM_VERSION, 33124d45dcfbSHalil Pasic .version_id = VIRTIO_NET_VM_VERSION, 33134d45dcfbSHalil Pasic .fields = (VMStateField[]) { 33144d45dcfbSHalil Pasic VMSTATE_VIRTIO_DEVICE, 33154d45dcfbSHalil Pasic VMSTATE_END_OF_LIST() 33164d45dcfbSHalil Pasic }, 33174d45dcfbSHalil Pasic .pre_save = virtio_net_pre_save, 33189711cd0dSJens Freimann .dev_unplug_pending = dev_unplug_pending, 33194d45dcfbSHalil Pasic }; 3320290c2428SDr. David Alan Gilbert 332117ec5a86SKONRAD Frederic static Property virtio_net_properties[] = { 3322127833eeSJason Baron DEFINE_PROP_BIT64("csum", VirtIONet, host_features, 3323127833eeSJason Baron VIRTIO_NET_F_CSUM, true), 3324127833eeSJason Baron DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features, 332587108bb2SShannon Zhao VIRTIO_NET_F_GUEST_CSUM, true), 3326127833eeSJason Baron DEFINE_PROP_BIT64("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true), 3327127833eeSJason Baron DEFINE_PROP_BIT64("guest_tso4", VirtIONet, host_features, 332887108bb2SShannon Zhao VIRTIO_NET_F_GUEST_TSO4, true), 3329127833eeSJason Baron DEFINE_PROP_BIT64("guest_tso6", VirtIONet, host_features, 333087108bb2SShannon Zhao VIRTIO_NET_F_GUEST_TSO6, true), 3331127833eeSJason Baron DEFINE_PROP_BIT64("guest_ecn", VirtIONet, host_features, 333287108bb2SShannon Zhao VIRTIO_NET_F_GUEST_ECN, true), 3333127833eeSJason Baron DEFINE_PROP_BIT64("guest_ufo", VirtIONet, host_features, 333487108bb2SShannon Zhao VIRTIO_NET_F_GUEST_UFO, true), 3335127833eeSJason Baron DEFINE_PROP_BIT64("guest_announce", VirtIONet, host_features, 333687108bb2SShannon Zhao VIRTIO_NET_F_GUEST_ANNOUNCE, true), 3337127833eeSJason Baron DEFINE_PROP_BIT64("host_tso4", VirtIONet, host_features, 333887108bb2SShannon Zhao VIRTIO_NET_F_HOST_TSO4, true), 3339127833eeSJason Baron DEFINE_PROP_BIT64("host_tso6", VirtIONet, host_features, 334087108bb2SShannon Zhao VIRTIO_NET_F_HOST_TSO6, true), 3341127833eeSJason Baron DEFINE_PROP_BIT64("host_ecn", VirtIONet, host_features, 334287108bb2SShannon Zhao VIRTIO_NET_F_HOST_ECN, true), 3343127833eeSJason Baron DEFINE_PROP_BIT64("host_ufo", VirtIONet, host_features, 334487108bb2SShannon Zhao VIRTIO_NET_F_HOST_UFO, true), 3345127833eeSJason Baron DEFINE_PROP_BIT64("mrg_rxbuf", VirtIONet, host_features, 334687108bb2SShannon Zhao VIRTIO_NET_F_MRG_RXBUF, true), 3347127833eeSJason Baron DEFINE_PROP_BIT64("status", VirtIONet, host_features, 334887108bb2SShannon Zhao VIRTIO_NET_F_STATUS, true), 3349127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_vq", VirtIONet, host_features, 335087108bb2SShannon Zhao VIRTIO_NET_F_CTRL_VQ, true), 3351127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_rx", VirtIONet, host_features, 335287108bb2SShannon Zhao VIRTIO_NET_F_CTRL_RX, true), 3353127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_vlan", VirtIONet, host_features, 335487108bb2SShannon Zhao VIRTIO_NET_F_CTRL_VLAN, true), 3355127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_rx_extra", VirtIONet, host_features, 335687108bb2SShannon Zhao VIRTIO_NET_F_CTRL_RX_EXTRA, true), 3357127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_mac_addr", VirtIONet, host_features, 335887108bb2SShannon Zhao VIRTIO_NET_F_CTRL_MAC_ADDR, true), 3359127833eeSJason Baron DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features, 336087108bb2SShannon Zhao VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true), 3361127833eeSJason Baron DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false), 3362*59079029SYuri Benditovich DEFINE_PROP_BIT64("rss", VirtIONet, host_features, 3363*59079029SYuri Benditovich VIRTIO_NET_F_RSS, false), 33642974e916SYuri Benditovich DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features, 33652974e916SYuri Benditovich VIRTIO_NET_F_RSC_EXT, false), 33662974e916SYuri Benditovich DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout, 33672974e916SYuri Benditovich VIRTIO_NET_RSC_DEFAULT_INTERVAL), 336817ec5a86SKONRAD Frederic DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf), 336917ec5a86SKONRAD Frederic DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer, 337017ec5a86SKONRAD Frederic TX_TIMER_INTERVAL), 337117ec5a86SKONRAD Frederic DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST), 337217ec5a86SKONRAD Frederic DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx), 33731c0fbfa3SMichael S. Tsirkin DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size, 33741c0fbfa3SMichael S. Tsirkin VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE), 33759b02e161SWei Wang DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size, 33769b02e161SWei Wang VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE), 3377a93e599dSMaxime Coquelin DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0), 337875ebec11SMaxime Coquelin DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend, 337975ebec11SMaxime Coquelin true), 33809473939eSJason Baron DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN), 33819473939eSJason Baron DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str), 33829711cd0dSJens Freimann DEFINE_PROP_BOOL("failover", VirtIONet, failover, false), 338317ec5a86SKONRAD Frederic DEFINE_PROP_END_OF_LIST(), 338417ec5a86SKONRAD Frederic }; 338517ec5a86SKONRAD Frederic 338617ec5a86SKONRAD Frederic static void virtio_net_class_init(ObjectClass *klass, void *data) 338717ec5a86SKONRAD Frederic { 338817ec5a86SKONRAD Frederic DeviceClass *dc = DEVICE_CLASS(klass); 338917ec5a86SKONRAD Frederic VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 3390e6f746b3SAndreas Färber 33914f67d30bSMarc-André Lureau device_class_set_props(dc, virtio_net_properties); 3392290c2428SDr. David Alan Gilbert dc->vmsd = &vmstate_virtio_net; 3393125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 3394e6f746b3SAndreas Färber vdc->realize = virtio_net_device_realize; 3395306ec6c3SAndreas Färber vdc->unrealize = virtio_net_device_unrealize; 339617ec5a86SKONRAD Frederic vdc->get_config = virtio_net_get_config; 339717ec5a86SKONRAD Frederic vdc->set_config = virtio_net_set_config; 339817ec5a86SKONRAD Frederic vdc->get_features = virtio_net_get_features; 339917ec5a86SKONRAD Frederic vdc->set_features = virtio_net_set_features; 340017ec5a86SKONRAD Frederic vdc->bad_features = virtio_net_bad_features; 340117ec5a86SKONRAD Frederic vdc->reset = virtio_net_reset; 340217ec5a86SKONRAD Frederic vdc->set_status = virtio_net_set_status; 340317ec5a86SKONRAD Frederic vdc->guest_notifier_mask = virtio_net_guest_notifier_mask; 340417ec5a86SKONRAD Frederic vdc->guest_notifier_pending = virtio_net_guest_notifier_pending; 34052a083ffdSMichael S. Tsirkin vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO); 34067788c3f2SMikhail Sennikovsky vdc->post_load = virtio_net_post_load_virtio; 3407982b78c5SDr. David Alan Gilbert vdc->vmsd = &vmstate_virtio_net_device; 34089711cd0dSJens Freimann vdc->primary_unplug_pending = primary_unplug_pending; 340917ec5a86SKONRAD Frederic } 341017ec5a86SKONRAD Frederic 341117ec5a86SKONRAD Frederic static const TypeInfo virtio_net_info = { 341217ec5a86SKONRAD Frederic .name = TYPE_VIRTIO_NET, 341317ec5a86SKONRAD Frederic .parent = TYPE_VIRTIO_DEVICE, 341417ec5a86SKONRAD Frederic .instance_size = sizeof(VirtIONet), 341517ec5a86SKONRAD Frederic .instance_init = virtio_net_instance_init, 341617ec5a86SKONRAD Frederic .class_init = virtio_net_class_init, 341717ec5a86SKONRAD Frederic }; 341817ec5a86SKONRAD Frederic 341917ec5a86SKONRAD Frederic static void virtio_register_types(void) 342017ec5a86SKONRAD Frederic { 342117ec5a86SKONRAD Frederic type_register_static(&virtio_net_info); 342217ec5a86SKONRAD Frederic } 342317ec5a86SKONRAD Frederic 342417ec5a86SKONRAD Frederic type_init(virtio_register_types) 3425