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