11e8a1faeSThomas Huth /* 21e8a1faeSThomas Huth * QTest testcase for VirtIO NIC 31e8a1faeSThomas Huth * 41e8a1faeSThomas Huth * Copyright (c) 2014 SUSE LINUX Products GmbH 51e8a1faeSThomas Huth * 61e8a1faeSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or later. 71e8a1faeSThomas Huth * See the COPYING file in the top-level directory. 81e8a1faeSThomas Huth */ 91e8a1faeSThomas Huth 101e8a1faeSThomas Huth #include "qemu/osdep.h" 111e8a1faeSThomas Huth #include "libqtest-single.h" 121e8a1faeSThomas Huth #include "qemu/iov.h" 131e8a1faeSThomas Huth #include "qemu/module.h" 141e8a1faeSThomas Huth #include "qapi/qmp/qdict.h" 151e8a1faeSThomas Huth #include "hw/virtio/virtio-net.h" 161e8a1faeSThomas Huth #include "libqos/qgraph.h" 171e8a1faeSThomas Huth #include "libqos/virtio-net.h" 181e8a1faeSThomas Huth 191e8a1faeSThomas Huth #ifndef ETH_P_RARP 201e8a1faeSThomas Huth #define ETH_P_RARP 0x8035 211e8a1faeSThomas Huth #endif 221e8a1faeSThomas Huth 231e8a1faeSThomas Huth #define PCI_SLOT_HP 0x06 241e8a1faeSThomas Huth #define PCI_SLOT 0x04 251e8a1faeSThomas Huth 261e8a1faeSThomas Huth #define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000) 271e8a1faeSThomas Huth #define VNET_HDR_SIZE sizeof(struct virtio_net_hdr_mrg_rxbuf) 281e8a1faeSThomas Huth 291e8a1faeSThomas Huth #ifndef _WIN32 301e8a1faeSThomas Huth 311e8a1faeSThomas Huth static void rx_test(QVirtioDevice *dev, 321e8a1faeSThomas Huth QGuestAllocator *alloc, QVirtQueue *vq, 331e8a1faeSThomas Huth int socket) 341e8a1faeSThomas Huth { 351e8a1faeSThomas Huth QTestState *qts = global_qtest; 361e8a1faeSThomas Huth uint64_t req_addr; 371e8a1faeSThomas Huth uint32_t free_head; 381e8a1faeSThomas Huth char test[] = "TEST"; 391e8a1faeSThomas Huth char buffer[64]; 401e8a1faeSThomas Huth int len = htonl(sizeof(test)); 411e8a1faeSThomas Huth struct iovec iov[] = { 421e8a1faeSThomas Huth { 431e8a1faeSThomas Huth .iov_base = &len, 441e8a1faeSThomas Huth .iov_len = sizeof(len), 451e8a1faeSThomas Huth }, { 461e8a1faeSThomas Huth .iov_base = test, 471e8a1faeSThomas Huth .iov_len = sizeof(test), 481e8a1faeSThomas Huth }, 491e8a1faeSThomas Huth }; 501e8a1faeSThomas Huth int ret; 511e8a1faeSThomas Huth 521e8a1faeSThomas Huth req_addr = guest_alloc(alloc, 64); 531e8a1faeSThomas Huth 541e8a1faeSThomas Huth free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false); 551e8a1faeSThomas Huth qvirtqueue_kick(qts, dev, vq, free_head); 561e8a1faeSThomas Huth 571e8a1faeSThomas Huth ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); 581e8a1faeSThomas Huth g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); 591e8a1faeSThomas Huth 601e8a1faeSThomas Huth qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, 611e8a1faeSThomas Huth QVIRTIO_NET_TIMEOUT_US); 621e8a1faeSThomas Huth memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); 631e8a1faeSThomas Huth g_assert_cmpstr(buffer, ==, "TEST"); 641e8a1faeSThomas Huth 651e8a1faeSThomas Huth guest_free(alloc, req_addr); 661e8a1faeSThomas Huth } 671e8a1faeSThomas Huth 681e8a1faeSThomas Huth static void tx_test(QVirtioDevice *dev, 691e8a1faeSThomas Huth QGuestAllocator *alloc, QVirtQueue *vq, 701e8a1faeSThomas Huth int socket) 711e8a1faeSThomas Huth { 721e8a1faeSThomas Huth QTestState *qts = global_qtest; 731e8a1faeSThomas Huth uint64_t req_addr; 741e8a1faeSThomas Huth uint32_t free_head; 751e8a1faeSThomas Huth uint32_t len; 761e8a1faeSThomas Huth char buffer[64]; 771e8a1faeSThomas Huth int ret; 781e8a1faeSThomas Huth 791e8a1faeSThomas Huth req_addr = guest_alloc(alloc, 64); 801e8a1faeSThomas Huth memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4); 811e8a1faeSThomas Huth 821e8a1faeSThomas Huth free_head = qvirtqueue_add(qts, vq, req_addr, 64, false, false); 831e8a1faeSThomas Huth qvirtqueue_kick(qts, dev, vq, free_head); 841e8a1faeSThomas Huth 851e8a1faeSThomas Huth qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, 861e8a1faeSThomas Huth QVIRTIO_NET_TIMEOUT_US); 871e8a1faeSThomas Huth guest_free(alloc, req_addr); 881e8a1faeSThomas Huth 89e7b79428SMarc-André Lureau ret = recv(socket, &len, sizeof(len), 0); 901e8a1faeSThomas Huth g_assert_cmpint(ret, ==, sizeof(len)); 911e8a1faeSThomas Huth len = ntohl(len); 921e8a1faeSThomas Huth 93e7b79428SMarc-André Lureau ret = recv(socket, buffer, len, 0); 941e8a1faeSThomas Huth g_assert_cmpstr(buffer, ==, "TEST"); 951e8a1faeSThomas Huth } 961e8a1faeSThomas Huth 971e8a1faeSThomas Huth static void rx_stop_cont_test(QVirtioDevice *dev, 981e8a1faeSThomas Huth QGuestAllocator *alloc, QVirtQueue *vq, 991e8a1faeSThomas Huth int socket) 1001e8a1faeSThomas Huth { 1011e8a1faeSThomas Huth QTestState *qts = global_qtest; 1021e8a1faeSThomas Huth uint64_t req_addr; 1031e8a1faeSThomas Huth uint32_t free_head; 1041e8a1faeSThomas Huth char test[] = "TEST"; 1051e8a1faeSThomas Huth char buffer[64]; 1061e8a1faeSThomas Huth int len = htonl(sizeof(test)); 1071e8a1faeSThomas Huth QDict *rsp; 1081e8a1faeSThomas Huth struct iovec iov[] = { 1091e8a1faeSThomas Huth { 1101e8a1faeSThomas Huth .iov_base = &len, 1111e8a1faeSThomas Huth .iov_len = sizeof(len), 1121e8a1faeSThomas Huth }, { 1131e8a1faeSThomas Huth .iov_base = test, 1141e8a1faeSThomas Huth .iov_len = sizeof(test), 1151e8a1faeSThomas Huth }, 1161e8a1faeSThomas Huth }; 1171e8a1faeSThomas Huth int ret; 1181e8a1faeSThomas Huth 1191e8a1faeSThomas Huth req_addr = guest_alloc(alloc, 64); 1201e8a1faeSThomas Huth 1211e8a1faeSThomas Huth free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false); 1221e8a1faeSThomas Huth qvirtqueue_kick(qts, dev, vq, free_head); 1231e8a1faeSThomas Huth 1241e8a1faeSThomas Huth rsp = qmp("{ 'execute' : 'stop'}"); 1251e8a1faeSThomas Huth qobject_unref(rsp); 1261e8a1faeSThomas Huth 1271e8a1faeSThomas Huth ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); 1281e8a1faeSThomas Huth g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); 1291e8a1faeSThomas Huth 1301e8a1faeSThomas Huth /* We could check the status, but this command is more importantly to 1311e8a1faeSThomas Huth * ensure the packet data gets queued in QEMU, before we do 'cont'. 1321e8a1faeSThomas Huth */ 1331e8a1faeSThomas Huth rsp = qmp("{ 'execute' : 'query-status'}"); 1341e8a1faeSThomas Huth qobject_unref(rsp); 1351e8a1faeSThomas Huth rsp = qmp("{ 'execute' : 'cont'}"); 1361e8a1faeSThomas Huth qobject_unref(rsp); 1371e8a1faeSThomas Huth 1381e8a1faeSThomas Huth qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, 1391e8a1faeSThomas Huth QVIRTIO_NET_TIMEOUT_US); 1401e8a1faeSThomas Huth memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); 1411e8a1faeSThomas Huth g_assert_cmpstr(buffer, ==, "TEST"); 1421e8a1faeSThomas Huth 1431e8a1faeSThomas Huth guest_free(alloc, req_addr); 1441e8a1faeSThomas Huth } 1451e8a1faeSThomas Huth 1461e8a1faeSThomas Huth static void send_recv_test(void *obj, void *data, QGuestAllocator *t_alloc) 1471e8a1faeSThomas Huth { 1481e8a1faeSThomas Huth QVirtioNet *net_if = obj; 1491e8a1faeSThomas Huth QVirtioDevice *dev = net_if->vdev; 1501e8a1faeSThomas Huth QVirtQueue *rx = net_if->queues[0]; 1511e8a1faeSThomas Huth QVirtQueue *tx = net_if->queues[1]; 1521e8a1faeSThomas Huth int *sv = data; 1531e8a1faeSThomas Huth 1541e8a1faeSThomas Huth rx_test(dev, t_alloc, rx, sv[0]); 1551e8a1faeSThomas Huth tx_test(dev, t_alloc, tx, sv[0]); 1561e8a1faeSThomas Huth } 1571e8a1faeSThomas Huth 1581e8a1faeSThomas Huth static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc) 1591e8a1faeSThomas Huth { 1601e8a1faeSThomas Huth QVirtioNet *net_if = obj; 1611e8a1faeSThomas Huth QVirtioDevice *dev = net_if->vdev; 1621e8a1faeSThomas Huth QVirtQueue *rx = net_if->queues[0]; 1631e8a1faeSThomas Huth int *sv = data; 1641e8a1faeSThomas Huth 1651e8a1faeSThomas Huth rx_stop_cont_test(dev, t_alloc, rx, sv[0]); 1661e8a1faeSThomas Huth } 1671e8a1faeSThomas Huth 1681e8a1faeSThomas Huth static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) 1691e8a1faeSThomas Huth { 1701e8a1faeSThomas Huth QVirtioPCIDevice *dev = obj; 1711e8a1faeSThomas Huth QTestState *qts = dev->pdev->bus->qts; 1721e8a1faeSThomas Huth const char *arch = qtest_get_arch(); 1731e8a1faeSThomas Huth 17402ee7a8aSEric Auger if (dev->pdev->bus->not_hotpluggable) { 17502ee7a8aSEric Auger g_test_skip("pci bus does not support hotplug"); 17602ee7a8aSEric Auger return; 17702ee7a8aSEric Auger } 17802ee7a8aSEric Auger 1791e8a1faeSThomas Huth qtest_qmp_device_add(qts, "virtio-net-pci", "net1", 1801e8a1faeSThomas Huth "{'addr': %s}", stringify(PCI_SLOT_HP)); 1811e8a1faeSThomas Huth 1821e8a1faeSThomas Huth if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { 1831e8a1faeSThomas Huth qpci_unplug_acpi_device_test(qts, "net1", PCI_SLOT_HP); 1841e8a1faeSThomas Huth } 1851e8a1faeSThomas Huth } 1861e8a1faeSThomas Huth 1871e8a1faeSThomas Huth static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc) 1881e8a1faeSThomas Huth { 1891e8a1faeSThomas Huth int *sv = data; 1901e8a1faeSThomas Huth char buffer[60]; 1911e8a1faeSThomas Huth int len; 1921e8a1faeSThomas Huth QDict *rsp; 1931e8a1faeSThomas Huth int ret; 1941e8a1faeSThomas Huth uint16_t *proto = (uint16_t *)&buffer[12]; 1951e8a1faeSThomas Huth size_t total_received = 0; 1961e8a1faeSThomas Huth uint64_t start, now, last_rxt, deadline; 1971e8a1faeSThomas Huth 1981e8a1faeSThomas Huth /* Send a set of packets over a few second period */ 1991e8a1faeSThomas Huth rsp = qmp("{ 'execute' : 'announce-self', " 2001e8a1faeSThomas Huth " 'arguments': {" 2011e8a1faeSThomas Huth " 'initial': 20, 'max': 100," 2021e8a1faeSThomas Huth " 'rounds': 300, 'step': 10, 'id': 'bob' } }"); 2031e8a1faeSThomas Huth assert(!qdict_haskey(rsp, "error")); 2041e8a1faeSThomas Huth qobject_unref(rsp); 2051e8a1faeSThomas Huth 2061e8a1faeSThomas Huth /* Catch the first packet and make sure it's a RARP */ 207e7b79428SMarc-André Lureau ret = recv(sv[0], &len, sizeof(len), 0); 2081e8a1faeSThomas Huth g_assert_cmpint(ret, ==, sizeof(len)); 2091e8a1faeSThomas Huth len = ntohl(len); 2101e8a1faeSThomas Huth 211e7b79428SMarc-André Lureau ret = recv(sv[0], buffer, len, 0); 2121e8a1faeSThomas Huth g_assert_cmpint(*proto, ==, htons(ETH_P_RARP)); 2131e8a1faeSThomas Huth 2141e8a1faeSThomas Huth /* 2151e8a1faeSThomas Huth * Stop the announcment by settings rounds to 0 on the 2161e8a1faeSThomas Huth * existing timer. 2171e8a1faeSThomas Huth */ 2181e8a1faeSThomas Huth rsp = qmp("{ 'execute' : 'announce-self', " 2191e8a1faeSThomas Huth " 'arguments': {" 2201e8a1faeSThomas Huth " 'initial': 20, 'max': 100," 2211e8a1faeSThomas Huth " 'rounds': 0, 'step': 10, 'id': 'bob' } }"); 2221e8a1faeSThomas Huth assert(!qdict_haskey(rsp, "error")); 2231e8a1faeSThomas Huth qobject_unref(rsp); 2241e8a1faeSThomas Huth 2251e8a1faeSThomas Huth /* Now make sure the packets stop */ 2261e8a1faeSThomas Huth 2271e8a1faeSThomas Huth /* Times are in us */ 2281e8a1faeSThomas Huth start = g_get_monotonic_time(); 2291e8a1faeSThomas Huth /* 30 packets, max gap 100ms, * 4 for wiggle */ 2301e8a1faeSThomas Huth deadline = start + 1000 * (100 * 30 * 4); 2311e8a1faeSThomas Huth last_rxt = start; 2321e8a1faeSThomas Huth 2331e8a1faeSThomas Huth while (true) { 2341e8a1faeSThomas Huth int saved_err; 235e7b79428SMarc-André Lureau ret = recv(sv[0], buffer, 60, MSG_DONTWAIT); 2361e8a1faeSThomas Huth saved_err = errno; 2371e8a1faeSThomas Huth now = g_get_monotonic_time(); 2381e8a1faeSThomas Huth g_assert_cmpint(now, <, deadline); 2391e8a1faeSThomas Huth 2401e8a1faeSThomas Huth if (ret >= 0) { 2411e8a1faeSThomas Huth if (ret) { 2421e8a1faeSThomas Huth last_rxt = now; 2431e8a1faeSThomas Huth } 2441e8a1faeSThomas Huth total_received += ret; 2451e8a1faeSThomas Huth 2461e8a1faeSThomas Huth /* Check it's not spewing loads */ 2471e8a1faeSThomas Huth g_assert_cmpint(total_received, <, 60 * 30 * 2); 2481e8a1faeSThomas Huth } else { 2491e8a1faeSThomas Huth g_assert_cmpint(saved_err, ==, EAGAIN); 2501e8a1faeSThomas Huth 2511e8a1faeSThomas Huth /* 400ms, i.e. 4 worst case gaps */ 2521e8a1faeSThomas Huth if ((now - last_rxt) > (1000 * 100 * 4)) { 2531e8a1faeSThomas Huth /* Nothings arrived for a while - must have stopped */ 2541e8a1faeSThomas Huth break; 2551e8a1faeSThomas Huth }; 2561e8a1faeSThomas Huth 2571e8a1faeSThomas Huth /* 100ms */ 2581e8a1faeSThomas Huth g_usleep(1000 * 100); 2591e8a1faeSThomas Huth } 2601e8a1faeSThomas Huth }; 2611e8a1faeSThomas Huth } 2621e8a1faeSThomas Huth 2631e8a1faeSThomas Huth static void virtio_net_test_cleanup(void *sockets) 2641e8a1faeSThomas Huth { 2651e8a1faeSThomas Huth int *sv = sockets; 2661e8a1faeSThomas Huth 2671e8a1faeSThomas Huth close(sv[0]); 2681e8a1faeSThomas Huth qos_invalidate_command_line(); 2691e8a1faeSThomas Huth close(sv[1]); 2701e8a1faeSThomas Huth g_free(sv); 2711e8a1faeSThomas Huth } 2721e8a1faeSThomas Huth 2731e8a1faeSThomas Huth static void *virtio_net_test_setup(GString *cmd_line, void *arg) 2741e8a1faeSThomas Huth { 2751e8a1faeSThomas Huth int ret; 2761e8a1faeSThomas Huth int *sv = g_new(int, 2); 2771e8a1faeSThomas Huth 2781e8a1faeSThomas Huth ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); 2791e8a1faeSThomas Huth g_assert_cmpint(ret, !=, -1); 2801e8a1faeSThomas Huth 2811e8a1faeSThomas Huth g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sv[1]); 2821e8a1faeSThomas Huth 2831e8a1faeSThomas Huth g_test_queue_destroy(virtio_net_test_cleanup, sv); 2841e8a1faeSThomas Huth return sv; 2851e8a1faeSThomas Huth } 2861e8a1faeSThomas Huth 287cac4373aSBin Meng #endif /* _WIN32 */ 288cac4373aSBin Meng 2891e8a1faeSThomas Huth static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc) 2901e8a1faeSThomas Huth { 2911e8a1faeSThomas Huth QVirtioNet *dev = obj; 2921e8a1faeSThomas Huth QVirtQueue *vq = dev->queues[1]; 2931e8a1faeSThomas Huth uint64_t req_addr; 2941e8a1faeSThomas Huth uint32_t free_head; 2951e8a1faeSThomas Huth size_t alloc_size = (size_t)data / 64; 2961e8a1faeSThomas Huth QTestState *qts = global_qtest; 2971e8a1faeSThomas Huth int i; 2981e8a1faeSThomas Huth 2991e8a1faeSThomas Huth /* Bypass the limitation by pointing several descriptors to a single 3001e8a1faeSThomas Huth * smaller area */ 3011e8a1faeSThomas Huth req_addr = guest_alloc(t_alloc, alloc_size); 3021e8a1faeSThomas Huth free_head = qvirtqueue_add(qts, vq, req_addr, alloc_size, false, true); 3031e8a1faeSThomas Huth 3041e8a1faeSThomas Huth for (i = 0; i < 64; i++) { 3051e8a1faeSThomas Huth qvirtqueue_add(qts, vq, req_addr, alloc_size, false, i != 63); 3061e8a1faeSThomas Huth } 3071e8a1faeSThomas Huth qvirtqueue_kick(qts, dev->vdev, vq, free_head); 3081e8a1faeSThomas Huth 3091e8a1faeSThomas Huth qvirtio_wait_used_elem(qts, dev->vdev, vq, free_head, NULL, 3101e8a1faeSThomas Huth QVIRTIO_NET_TIMEOUT_US); 3111e8a1faeSThomas Huth guest_free(t_alloc, req_addr); 3121e8a1faeSThomas Huth } 3131e8a1faeSThomas Huth 3141e8a1faeSThomas Huth static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg) 3151e8a1faeSThomas Huth { 3161e8a1faeSThomas Huth g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 "); 3171e8a1faeSThomas Huth return arg; 3181e8a1faeSThomas Huth } 3191e8a1faeSThomas Huth 3201e8a1faeSThomas Huth static void register_virtio_net_test(void) 3211e8a1faeSThomas Huth { 322cac4373aSBin Meng QOSGraphTestOptions opts = { 0 }; 3231e8a1faeSThomas Huth 3241e8a1faeSThomas Huth #ifndef _WIN32 325cac4373aSBin Meng opts.before = virtio_net_test_setup; 326cac4373aSBin Meng qos_add_test("hotplug", "virtio-net-pci", hotplug, &opts); 3271e8a1faeSThomas Huth qos_add_test("basic", "virtio-net", send_recv_test, &opts); 3281e8a1faeSThomas Huth qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts); 3291e8a1faeSThomas Huth qos_add_test("announce-self", "virtio-net", announce_self, &opts); 330cac4373aSBin Meng #endif 3311e8a1faeSThomas Huth 3321e8a1faeSThomas Huth /* These tests do not need a loopback backend. */ 3331e8a1faeSThomas Huth opts.before = virtio_net_test_setup_nosocket; 3341e8a1faeSThomas Huth opts.arg = (gpointer)UINT_MAX; 3351e8a1faeSThomas Huth qos_add_test("large_tx/uint_max", "virtio-net", large_tx, &opts); 3361e8a1faeSThomas Huth opts.arg = (gpointer)NET_BUFSIZE; 3371e8a1faeSThomas Huth qos_add_test("large_tx/net_bufsize", "virtio-net", large_tx, &opts); 3381e8a1faeSThomas Huth } 3391e8a1faeSThomas Huth 3401e8a1faeSThomas Huth libqos_init(register_virtio_net_test); 341