1
2
3
4
5
6
7
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
132
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
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
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
214
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
224
225
226 start = g_get_monotonic_time();
227
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
245 g_assert_cmpint(total_received, <, 60 * 30 * 2);
246 } else {
247 g_assert_cmpint(saved_err, ==, EAGAIN);
248
249
250 if ((now - last_rxt) > (1000 * 100 * 4)) {
251
252 break;
253 };
254
255
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
296
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
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);
338