virtio-net-test.c (9641B)
1/* 2 * QTest testcase for VirtIO NIC 3 * 4 * Copyright (c) 2014 SUSE LINUX Products GmbH 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 */ 9 10#include "qemu/osdep.h" 11#include "qemu-common.h" 12#include "libqtest-single.h" 13#include "qemu/iov.h" 14#include "qemu/module.h" 15#include "qapi/qmp/qdict.h" 16#include "hw/virtio/virtio-net.h" 17#include "libqos/qgraph.h" 18#include "libqos/virtio-net.h" 19 20#ifndef ETH_P_RARP 21#define ETH_P_RARP 0x8035 22#endif 23 24#define PCI_SLOT_HP 0x06 25#define PCI_SLOT 0x04 26 27#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000) 28#define VNET_HDR_SIZE sizeof(struct virtio_net_hdr_mrg_rxbuf) 29 30#ifndef _WIN32 31 32static void rx_test(QVirtioDevice *dev, 33 QGuestAllocator *alloc, QVirtQueue *vq, 34 int socket) 35{ 36 QTestState *qts = global_qtest; 37 uint64_t req_addr; 38 uint32_t free_head; 39 char test[] = "TEST"; 40 char buffer[64]; 41 int len = htonl(sizeof(test)); 42 struct iovec iov[] = { 43 { 44 .iov_base = &len, 45 .iov_len = sizeof(len), 46 }, { 47 .iov_base = test, 48 .iov_len = sizeof(test), 49 }, 50 }; 51 int ret; 52 53 req_addr = guest_alloc(alloc, 64); 54 55 free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false); 56 qvirtqueue_kick(qts, dev, vq, free_head); 57 58 ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); 59 g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); 60 61 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, 62 QVIRTIO_NET_TIMEOUT_US); 63 memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); 64 g_assert_cmpstr(buffer, ==, "TEST"); 65 66 guest_free(alloc, req_addr); 67} 68 69static void tx_test(QVirtioDevice *dev, 70 QGuestAllocator *alloc, QVirtQueue *vq, 71 int socket) 72{ 73 QTestState *qts = global_qtest; 74 uint64_t req_addr; 75 uint32_t free_head; 76 uint32_t len; 77 char buffer[64]; 78 int ret; 79 80 req_addr = guest_alloc(alloc, 64); 81 memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4); 82 83 free_head = qvirtqueue_add(qts, vq, req_addr, 64, false, false); 84 qvirtqueue_kick(qts, dev, vq, free_head); 85 86 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, 87 QVIRTIO_NET_TIMEOUT_US); 88 guest_free(alloc, req_addr); 89 90 ret = qemu_recv(socket, &len, sizeof(len), 0); 91 g_assert_cmpint(ret, ==, sizeof(len)); 92 len = ntohl(len); 93 94 ret = qemu_recv(socket, buffer, len, 0); 95 g_assert_cmpstr(buffer, ==, "TEST"); 96} 97 98static void rx_stop_cont_test(QVirtioDevice *dev, 99 QGuestAllocator *alloc, QVirtQueue *vq, 100 int socket) 101{ 102 QTestState *qts = global_qtest; 103 uint64_t req_addr; 104 uint32_t free_head; 105 char test[] = "TEST"; 106 char buffer[64]; 107 int len = htonl(sizeof(test)); 108 QDict *rsp; 109 struct iovec iov[] = { 110 { 111 .iov_base = &len, 112 .iov_len = sizeof(len), 113 }, { 114 .iov_base = test, 115 .iov_len = sizeof(test), 116 }, 117 }; 118 int ret; 119 120 req_addr = guest_alloc(alloc, 64); 121 122 free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false); 123 qvirtqueue_kick(qts, dev, vq, free_head); 124 125 rsp = qmp("{ 'execute' : 'stop'}"); 126 qobject_unref(rsp); 127 128 ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); 129 g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); 130 131 /* We could check the status, but this command is more importantly to 132 * ensure the packet data gets queued in QEMU, before we do 'cont'. 133 */ 134 rsp = qmp("{ 'execute' : 'query-status'}"); 135 qobject_unref(rsp); 136 rsp = qmp("{ 'execute' : 'cont'}"); 137 qobject_unref(rsp); 138 139 qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, 140 QVIRTIO_NET_TIMEOUT_US); 141 memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); 142 g_assert_cmpstr(buffer, ==, "TEST"); 143 144 guest_free(alloc, req_addr); 145} 146 147static void send_recv_test(void *obj, void *data, QGuestAllocator *t_alloc) 148{ 149 QVirtioNet *net_if = obj; 150 QVirtioDevice *dev = net_if->vdev; 151 QVirtQueue *rx = net_if->queues[0]; 152 QVirtQueue *tx = net_if->queues[1]; 153 int *sv = data; 154 155 rx_test(dev, t_alloc, rx, sv[0]); 156 tx_test(dev, t_alloc, tx, sv[0]); 157} 158 159static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc) 160{ 161 QVirtioNet *net_if = obj; 162 QVirtioDevice *dev = net_if->vdev; 163 QVirtQueue *rx = net_if->queues[0]; 164 int *sv = data; 165 166 rx_stop_cont_test(dev, t_alloc, rx, sv[0]); 167} 168 169#endif 170 171static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) 172{ 173 QVirtioPCIDevice *dev = obj; 174 QTestState *qts = dev->pdev->bus->qts; 175 const char *arch = qtest_get_arch(); 176 177 qtest_qmp_device_add(qts, "virtio-net-pci", "net1", 178 "{'addr': %s}", stringify(PCI_SLOT_HP)); 179 180 if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { 181 qpci_unplug_acpi_device_test(qts, "net1", PCI_SLOT_HP); 182 } 183} 184 185static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc) 186{ 187 int *sv = data; 188 char buffer[60]; 189 int len; 190 QDict *rsp; 191 int ret; 192 uint16_t *proto = (uint16_t *)&buffer[12]; 193 size_t total_received = 0; 194 uint64_t start, now, last_rxt, deadline; 195 196 /* Send a set of packets over a few second period */ 197 rsp = qmp("{ 'execute' : 'announce-self', " 198 " 'arguments': {" 199 " 'initial': 20, 'max': 100," 200 " 'rounds': 300, 'step': 10, 'id': 'bob' } }"); 201 assert(!qdict_haskey(rsp, "error")); 202 qobject_unref(rsp); 203 204 /* Catch the first packet and make sure it's a RARP */ 205 ret = qemu_recv(sv[0], &len, sizeof(len), 0); 206 g_assert_cmpint(ret, ==, sizeof(len)); 207 len = ntohl(len); 208 209 ret = qemu_recv(sv[0], buffer, len, 0); 210 g_assert_cmpint(*proto, ==, htons(ETH_P_RARP)); 211 212 /* 213 * Stop the announcment by settings rounds to 0 on the 214 * existing timer. 215 */ 216 rsp = qmp("{ 'execute' : 'announce-self', " 217 " 'arguments': {" 218 " 'initial': 20, 'max': 100," 219 " 'rounds': 0, 'step': 10, 'id': 'bob' } }"); 220 assert(!qdict_haskey(rsp, "error")); 221 qobject_unref(rsp); 222 223 /* Now make sure the packets stop */ 224 225 /* Times are in us */ 226 start = g_get_monotonic_time(); 227 /* 30 packets, max gap 100ms, * 4 for wiggle */ 228 deadline = start + 1000 * (100 * 30 * 4); 229 last_rxt = start; 230 231 while (true) { 232 int saved_err; 233 ret = qemu_recv(sv[0], buffer, 60, MSG_DONTWAIT); 234 saved_err = errno; 235 now = g_get_monotonic_time(); 236 g_assert_cmpint(now, <, deadline); 237 238 if (ret >= 0) { 239 if (ret) { 240 last_rxt = now; 241 } 242 total_received += ret; 243 244 /* Check it's not spewing loads */ 245 g_assert_cmpint(total_received, <, 60 * 30 * 2); 246 } else { 247 g_assert_cmpint(saved_err, ==, EAGAIN); 248 249 /* 400ms, i.e. 4 worst case gaps */ 250 if ((now - last_rxt) > (1000 * 100 * 4)) { 251 /* Nothings arrived for a while - must have stopped */ 252 break; 253 }; 254 255 /* 100ms */ 256 g_usleep(1000 * 100); 257 } 258 }; 259} 260 261static void virtio_net_test_cleanup(void *sockets) 262{ 263 int *sv = sockets; 264 265 close(sv[0]); 266 qos_invalidate_command_line(); 267 close(sv[1]); 268 g_free(sv); 269} 270 271static void *virtio_net_test_setup(GString *cmd_line, void *arg) 272{ 273 int ret; 274 int *sv = g_new(int, 2); 275 276 ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); 277 g_assert_cmpint(ret, !=, -1); 278 279 g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sv[1]); 280 281 g_test_queue_destroy(virtio_net_test_cleanup, sv); 282 return sv; 283} 284 285static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc) 286{ 287 QVirtioNet *dev = obj; 288 QVirtQueue *vq = dev->queues[1]; 289 uint64_t req_addr; 290 uint32_t free_head; 291 size_t alloc_size = (size_t)data / 64; 292 QTestState *qts = global_qtest; 293 int i; 294 295 /* Bypass the limitation by pointing several descriptors to a single 296 * smaller area */ 297 req_addr = guest_alloc(t_alloc, alloc_size); 298 free_head = qvirtqueue_add(qts, vq, req_addr, alloc_size, false, true); 299 300 for (i = 0; i < 64; i++) { 301 qvirtqueue_add(qts, vq, req_addr, alloc_size, false, i != 63); 302 } 303 qvirtqueue_kick(qts, dev->vdev, vq, free_head); 304 305 qvirtio_wait_used_elem(qts, dev->vdev, vq, free_head, NULL, 306 QVIRTIO_NET_TIMEOUT_US); 307 guest_free(t_alloc, req_addr); 308} 309 310static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg) 311{ 312 g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 "); 313 return arg; 314} 315 316static void register_virtio_net_test(void) 317{ 318 QOSGraphTestOptions opts = { 319 .before = virtio_net_test_setup, 320 }; 321 322 qos_add_test("hotplug", "virtio-pci", hotplug, &opts); 323#ifndef _WIN32 324 qos_add_test("basic", "virtio-net", send_recv_test, &opts); 325 qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts); 326#endif 327 qos_add_test("announce-self", "virtio-net", announce_self, &opts); 328 329 /* These tests do not need a loopback backend. */ 330 opts.before = virtio_net_test_setup_nosocket; 331 opts.arg = (gpointer)UINT_MAX; 332 qos_add_test("large_tx/uint_max", "virtio-net", large_tx, &opts); 333 opts.arg = (gpointer)NET_BUFSIZE; 334 qos_add_test("large_tx/net_bufsize", "virtio-net", large_tx, &opts); 335} 336 337libqos_init(register_virtio_net_test);