xref: /linux/tools/testing/vsock/vsock_uring_test.c (revision 0e59523b440c90ed1899476f32ad4e949282f43e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* io_uring tests for vsock
3  *
4  * Copyright (C) 2023 SberDevices.
5  *
6  * Author: Arseniy Krasnov <avkrasnov@salutedevices.com>
7  */
8 
9 #include <getopt.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <liburing.h>
14 #include <unistd.h>
15 #include <sys/mman.h>
16 #include <sys/ioctl.h>
17 #include <linux/kernel.h>
18 #include <linux/sockios.h>
19 #include <errno.h>
20 #include <error.h>
21 
22 #include "util.h"
23 #include "control.h"
24 #include "msg_zerocopy_common.h"
25 
26 #ifndef PAGE_SIZE
27 #define PAGE_SIZE		4096
28 #endif
29 
30 #define RING_ENTRIES_NUM	4
31 
32 #define VSOCK_TEST_DATA_MAX_IOV 3
33 
34 #define HINT_CHUNK_SIZE		4096
35 #define HINT_BUF_GROUP		1
36 #define HINT_BUF_ENTRIES	4
37 
38 struct vsock_io_uring_test {
39 	/* Number of valid elements in 'vecs'. */
40 	int vecs_cnt;
41 	struct iovec vecs[VSOCK_TEST_DATA_MAX_IOV];
42 };
43 
44 static struct vsock_io_uring_test test_data_array[] = {
45 	/* All elements have page aligned base and size. */
46 	{
47 		.vecs_cnt = 3,
48 		{
49 			{ NULL, PAGE_SIZE },
50 			{ NULL, 2 * PAGE_SIZE },
51 			{ NULL, 3 * PAGE_SIZE },
52 		}
53 	},
54 	/* Middle element has both non-page aligned base and size. */
55 	{
56 		.vecs_cnt = 3,
57 		{
58 			{ NULL, PAGE_SIZE },
59 			{ (void *)1, 200  },
60 			{ NULL, 3 * PAGE_SIZE },
61 		}
62 	}
63 };
64 
65 static void vsock_io_uring_client(const struct test_opts *opts,
66 				  const struct vsock_io_uring_test *test_data,
67 				  bool msg_zerocopy)
68 {
69 	struct io_uring_sqe *sqe;
70 	struct io_uring_cqe *cqe;
71 	struct io_uring ring;
72 	struct iovec *iovec;
73 	struct msghdr msg;
74 	int fd;
75 
76 	fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
77 	if (fd < 0) {
78 		perror("connect");
79 		exit(EXIT_FAILURE);
80 	}
81 
82 	if (msg_zerocopy)
83 		enable_so_zerocopy_check(fd);
84 
85 	iovec = alloc_test_iovec(test_data->vecs, test_data->vecs_cnt);
86 
87 	if (io_uring_queue_init(RING_ENTRIES_NUM, &ring, 0))
88 		error(1, errno, "io_uring_queue_init");
89 
90 	if (io_uring_register_buffers(&ring, iovec, test_data->vecs_cnt))
91 		error(1, errno, "io_uring_register_buffers");
92 
93 	memset(&msg, 0, sizeof(msg));
94 	msg.msg_iov = iovec;
95 	msg.msg_iovlen = test_data->vecs_cnt;
96 	sqe = io_uring_get_sqe(&ring);
97 
98 	if (msg_zerocopy)
99 		io_uring_prep_sendmsg_zc(sqe, fd, &msg, 0);
100 	else
101 		io_uring_prep_sendmsg(sqe, fd, &msg, 0);
102 
103 	if (io_uring_submit(&ring) != 1)
104 		error(1, errno, "io_uring_submit");
105 
106 	if (io_uring_wait_cqe(&ring, &cqe))
107 		error(1, errno, "io_uring_wait_cqe");
108 
109 	io_uring_cqe_seen(&ring, cqe);
110 
111 	control_writeulong(iovec_hash_djb2(iovec, test_data->vecs_cnt));
112 
113 	control_writeln("DONE");
114 	io_uring_queue_exit(&ring);
115 	free_test_iovec(test_data->vecs, iovec, test_data->vecs_cnt);
116 	close(fd);
117 }
118 
119 static void vsock_io_uring_server(const struct test_opts *opts,
120 				  const struct vsock_io_uring_test *test_data)
121 {
122 	unsigned long remote_hash;
123 	unsigned long local_hash;
124 	struct io_uring ring;
125 	size_t data_len;
126 	size_t recv_len;
127 	void *data;
128 	int fd;
129 
130 	fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
131 	if (fd < 0) {
132 		perror("accept");
133 		exit(EXIT_FAILURE);
134 	}
135 
136 	data_len = iovec_bytes(test_data->vecs, test_data->vecs_cnt);
137 
138 	data = malloc(data_len);
139 	if (!data) {
140 		perror("malloc");
141 		exit(EXIT_FAILURE);
142 	}
143 
144 	if (io_uring_queue_init(RING_ENTRIES_NUM, &ring, 0))
145 		error(1, errno, "io_uring_queue_init");
146 
147 	recv_len = 0;
148 
149 	while (recv_len < data_len) {
150 		struct io_uring_sqe *sqe;
151 		struct io_uring_cqe *cqe;
152 		struct iovec iovec;
153 
154 		sqe = io_uring_get_sqe(&ring);
155 		iovec.iov_base = data + recv_len;
156 		iovec.iov_len = data_len;
157 
158 		io_uring_prep_readv(sqe, fd, &iovec, 1, 0);
159 
160 		if (io_uring_submit(&ring) != 1)
161 			error(1, errno, "io_uring_submit");
162 
163 		if (io_uring_wait_cqe(&ring, &cqe))
164 			error(1, errno, "io_uring_wait_cqe");
165 
166 		recv_len += cqe->res;
167 		io_uring_cqe_seen(&ring, cqe);
168 	}
169 
170 	if (recv_len != data_len) {
171 		fprintf(stderr, "expected %zu, got %zu\n", data_len,
172 			recv_len);
173 		exit(EXIT_FAILURE);
174 	}
175 
176 	local_hash = hash_djb2(data, data_len);
177 
178 	remote_hash = control_readulong();
179 	if (remote_hash != local_hash) {
180 		fprintf(stderr, "hash mismatch\n");
181 		exit(EXIT_FAILURE);
182 	}
183 
184 	control_expectln("DONE");
185 	io_uring_queue_exit(&ring);
186 	free(data);
187 }
188 
189 void test_stream_uring_server(const struct test_opts *opts)
190 {
191 	int i;
192 
193 	for (i = 0; i < ARRAY_SIZE(test_data_array); i++)
194 		vsock_io_uring_server(opts, &test_data_array[i]);
195 }
196 
197 void test_stream_uring_client(const struct test_opts *opts)
198 {
199 	int i;
200 
201 	for (i = 0; i < ARRAY_SIZE(test_data_array); i++)
202 		vsock_io_uring_client(opts, &test_data_array[i], false);
203 }
204 
205 void test_stream_uring_msg_zc_server(const struct test_opts *opts)
206 {
207 	int i;
208 
209 	for (i = 0; i < ARRAY_SIZE(test_data_array); i++)
210 		vsock_io_uring_server(opts, &test_data_array[i]);
211 }
212 
213 void test_stream_uring_msg_zc_client(const struct test_opts *opts)
214 {
215 	int i;
216 
217 	for (i = 0; i < ARRAY_SIZE(test_data_array); i++)
218 		vsock_io_uring_client(opts, &test_data_array[i], true);
219 }
220 
221 struct uring_inq_ctx {
222 	struct io_uring ring;
223 	int fd;
224 };
225 
226 static void inq_server_init(struct uring_inq_ctx *ctx,
227 			    const struct test_opts *opts)
228 {
229 	ctx->fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
230 	if (ctx->fd < 0) {
231 		perror("accept");
232 		exit(EXIT_FAILURE);
233 	}
234 
235 	if (io_uring_queue_init(RING_ENTRIES_NUM, &ctx->ring, 0))
236 		error(1, errno, "io_uring_queue_init");
237 }
238 
239 static void inq_server_exit(struct uring_inq_ctx *ctx)
240 {
241 	io_uring_queue_exit(&ctx->ring);
242 	close(ctx->fd);
243 }
244 
245 /* Submit a single receive and report both its result and its CQE flags. */
246 static int inq_recv(struct uring_inq_ctx *ctx, void *buf, size_t len,
247 		    int flags, unsigned int *cflags)
248 {
249 	struct io_uring_sqe *sqe;
250 	struct io_uring_cqe *cqe;
251 	int res;
252 
253 	sqe = io_uring_get_sqe(&ctx->ring);
254 	io_uring_prep_recv(sqe, ctx->fd, buf, len, flags);
255 
256 	if (io_uring_submit(&ctx->ring) != 1)
257 		error(1, errno, "io_uring_submit");
258 
259 	if (io_uring_wait_cqe(&ctx->ring, &cqe))
260 		error(1, errno, "io_uring_wait_cqe");
261 
262 	res = cqe->res;
263 	*cflags = cqe->flags;
264 	io_uring_cqe_seen(&ctx->ring, cqe);
265 
266 	return res;
267 }
268 
269 static void expect_uring_nonempty(unsigned int cflags, bool expected,
270 				  const char *what)
271 {
272 	bool nonempty = !!(cflags & IORING_CQE_F_SOCK_NONEMPTY);
273 
274 	if (nonempty != expected) {
275 		fprintf(stderr, "%s: expected SOCK_NONEMPTY %d, got %d\n",
276 			what, expected, nonempty);
277 		exit(EXIT_FAILURE);
278 	}
279 }
280 
281 static void expect_uring_more(unsigned int cflags, bool expected,
282 			      const char *what)
283 {
284 	bool more = !!(cflags & IORING_CQE_F_MORE);
285 
286 	if (more != expected) {
287 		fprintf(stderr, "%s: expected F_MORE %d, got %d\n",
288 			what, expected, more);
289 		exit(EXIT_FAILURE);
290 	}
291 }
292 
293 /* Wait until the whole payload is queued, so the hint is deterministic.
294  * Return false if the test has to be skipped.
295  */
296 static bool inq_wait_queued(int fd, int len)
297 {
298 	if (!vsock_ioctl_int(fd, SIOCINQ, len)) {
299 		fprintf(stderr, "Test skipped, SIOCINQ not supported.\n");
300 		return false;
301 	}
302 
303 	return true;
304 }
305 
306 static void inq_send_chunks(const struct test_opts *opts, int chunks)
307 {
308 	char buf[HINT_CHUNK_SIZE];
309 	int fd, i;
310 
311 	fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
312 	if (fd < 0) {
313 		perror("connect");
314 		exit(EXIT_FAILURE);
315 	}
316 
317 	memset(buf, 0xa5, sizeof(buf));
318 	for (i = 0; i < chunks; i++)
319 		send_buf(fd, buf, sizeof(buf), 0, sizeof(buf));
320 
321 	control_writeln("SENT");
322 	control_expectln("DONE");
323 	close(fd);
324 }
325 
326 static void test_stream_uring_inq_client(const struct test_opts *opts)
327 {
328 	inq_send_chunks(opts, 2);
329 }
330 
331 static void test_stream_uring_inq_server(const struct test_opts *opts)
332 {
333 	char buf[HINT_CHUNK_SIZE];
334 	struct uring_inq_ctx ctx;
335 	unsigned int cflags;
336 	int res;
337 
338 	inq_server_init(&ctx, opts);
339 
340 	control_expectln("SENT");
341 	if (!inq_wait_queued(ctx.fd, 2 * HINT_CHUNK_SIZE))
342 		goto out;
343 
344 	/* Data remains after this receive, so the flag must be set. */
345 	res = inq_recv(&ctx, buf, sizeof(buf), 0, &cflags);
346 	expect_res(res, HINT_CHUNK_SIZE, "partial receive");
347 	expect_uring_nonempty(cflags, true, "partial receive");
348 
349 	/* This receive drains the queue while the peer stays connected. */
350 	res = inq_recv(&ctx, buf, sizeof(buf), 0, &cflags);
351 	expect_res(res, HINT_CHUNK_SIZE, "draining receive");
352 	expect_uring_nonempty(cflags, false, "draining receive");
353 
354 out:
355 	control_writeln("DONE");
356 	inq_server_exit(&ctx);
357 }
358 
359 static void test_stream_uring_inq_eof_client(const struct test_opts *opts)
360 {
361 	char buf[HINT_CHUNK_SIZE];
362 	int fd;
363 
364 	fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
365 	if (fd < 0) {
366 		perror("connect");
367 		exit(EXIT_FAILURE);
368 	}
369 
370 	memset(buf, 0x5a, sizeof(buf));
371 	send_buf(fd, buf, sizeof(buf), 0, sizeof(buf));
372 	control_writeln("SENT");
373 
374 	control_expectln("DRAINED");
375 	close(fd);
376 	control_writeln("CLOSED");
377 }
378 
379 static void test_stream_uring_inq_eof_server(const struct test_opts *opts)
380 {
381 	char buf[HINT_CHUNK_SIZE];
382 	struct uring_inq_ctx ctx;
383 	unsigned int cflags;
384 	int res;
385 
386 	inq_server_init(&ctx, opts);
387 
388 	control_expectln("SENT");
389 	if (!inq_wait_queued(ctx.fd, HINT_CHUNK_SIZE)) {
390 		control_writeln("DRAINED");
391 		control_expectln("CLOSED");
392 		goto out;
393 	}
394 
395 	res = inq_recv(&ctx, buf, sizeof(buf), 0, &cflags);
396 	expect_res(res, HINT_CHUNK_SIZE, "drain before EOF");
397 	expect_uring_nonempty(cflags, false, "drain before EOF");
398 
399 	control_writeln("DRAINED");
400 	control_expectln("CLOSED");
401 
402 	/* The queue is empty and the peer is gone.  The hint stays non-zero
403 	 * so that this receive happens and reports EOF, as TCP does after a
404 	 * FIN.
405 	 */
406 	res = inq_recv(&ctx, buf, sizeof(buf), 0, &cflags);
407 	expect_res(res, 0, "receive at EOF");
408 	expect_uring_nonempty(cflags, true, "receive at EOF");
409 
410 out:
411 	inq_server_exit(&ctx);
412 }
413 
414 static void test_stream_uring_inq_empty_client(const struct test_opts *opts)
415 {
416 	int fd;
417 
418 	fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
419 	if (fd < 0) {
420 		perror("connect");
421 		exit(EXIT_FAILURE);
422 	}
423 
424 	control_writeln("READY");
425 	control_expectln("DONE");
426 	close(fd);
427 }
428 
429 static void test_stream_uring_inq_empty_server(const struct test_opts *opts)
430 {
431 	char buf[HINT_CHUNK_SIZE];
432 	struct uring_inq_ctx ctx;
433 	unsigned int cflags;
434 	int res;
435 
436 	inq_server_init(&ctx, opts);
437 
438 	control_expectln("READY");
439 
440 	/* A failed receive must not leave a stale positive hint. */
441 	res = inq_recv(&ctx, buf, sizeof(buf), MSG_DONTWAIT, &cflags);
442 	expect_res(res, -EAGAIN, "empty nonblocking receive");
443 	expect_uring_nonempty(cflags, false, "empty nonblocking receive");
444 
445 	control_writeln("DONE");
446 	inq_server_exit(&ctx);
447 }
448 
449 static void test_stream_uring_inq_zerolen_client(const struct test_opts *opts)
450 {
451 	inq_send_chunks(opts, 1);
452 }
453 
454 static void test_stream_uring_inq_zerolen_server(const struct test_opts *opts)
455 {
456 	char buf[HINT_CHUNK_SIZE];
457 	struct uring_inq_ctx ctx;
458 	unsigned int cflags;
459 	int res;
460 
461 	inq_server_init(&ctx, opts);
462 
463 	control_expectln("SENT");
464 	if (!inq_wait_queued(ctx.fd, HINT_CHUNK_SIZE))
465 		goto out;
466 
467 	/* A zero-length request is not an error and still describes the
468 	 * queue behind it.
469 	 */
470 	res = inq_recv(&ctx, buf, 0, 0, &cflags);
471 	expect_res(res, 0, "zero-length receive");
472 	expect_uring_nonempty(cflags, true, "zero-length receive");
473 
474 out:
475 	control_writeln("DONE");
476 	inq_server_exit(&ctx);
477 }
478 
479 static void test_stream_uring_inq_mshot_client(const struct test_opts *opts)
480 {
481 	char buf[HINT_CHUNK_SIZE];
482 	int fd, i;
483 
484 	fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
485 	if (fd < 0) {
486 		perror("connect");
487 		exit(EXIT_FAILURE);
488 	}
489 
490 	memset(buf, 0x3c, sizeof(buf));
491 	for (i = 0; i < 2; i++)
492 		send_buf(fd, buf, sizeof(buf), 0, sizeof(buf));
493 	control_writeln("SENT");
494 
495 	control_expectln("DRAINED");
496 	close(fd);
497 	control_writeln("CLOSED");
498 
499 	control_expectln("DONE");
500 }
501 
502 static void test_stream_uring_inq_mshot_server(const struct test_opts *opts)
503 {
504 	static char bufs[HINT_BUF_ENTRIES][HINT_CHUNK_SIZE];
505 	struct io_uring_buf_ring *br;
506 	struct uring_inq_ctx ctx;
507 	struct io_uring_sqe *sqe;
508 	struct io_uring_cqe *cqe;
509 	int i, ret;
510 
511 	inq_server_init(&ctx, opts);
512 
513 	br = io_uring_setup_buf_ring(&ctx.ring, HINT_BUF_ENTRIES,
514 				     HINT_BUF_GROUP, 0, &ret);
515 	if (!br) {
516 		fprintf(stderr, "io_uring_setup_buf_ring: %d\n", ret);
517 		exit(EXIT_FAILURE);
518 	}
519 
520 	for (i = 0; i < HINT_BUF_ENTRIES; i++)
521 		io_uring_buf_ring_add(br, bufs[i], HINT_CHUNK_SIZE, i,
522 				      io_uring_buf_ring_mask(HINT_BUF_ENTRIES),
523 				      i);
524 	io_uring_buf_ring_advance(br, HINT_BUF_ENTRIES);
525 
526 	control_expectln("SENT");
527 	if (!inq_wait_queued(ctx.fd, 2 * HINT_CHUNK_SIZE)) {
528 		control_writeln("DRAINED");
529 		control_expectln("CLOSED");
530 		goto out;
531 	}
532 
533 	/* Arm only once both chunks are queued, so every completion knows
534 	 * what is left behind it.
535 	 */
536 	sqe = io_uring_get_sqe(&ctx.ring);
537 	io_uring_prep_recv_multishot(sqe, ctx.fd, NULL, 0, 0);
538 	sqe->flags |= IOSQE_BUFFER_SELECT;
539 	sqe->buf_group = HINT_BUF_GROUP;
540 
541 	if (io_uring_submit(&ctx.ring) != 1)
542 		error(1, errno, "io_uring_submit");
543 
544 	/* A buffer holds one chunk, so the other one is still queued. */
545 	if (io_uring_wait_cqe(&ctx.ring, &cqe))
546 		error(1, errno, "io_uring_wait_cqe");
547 
548 	expect_res(cqe->res, HINT_CHUNK_SIZE, "multishot first chunk");
549 	expect_uring_nonempty(cqe->flags, true, "multishot first chunk");
550 	expect_uring_more(cqe->flags, true, "multishot first chunk");
551 	io_uring_cqe_seen(&ctx.ring, cqe);
552 
553 	/* This completion drains the queue and keeps the request armed. */
554 	if (io_uring_wait_cqe(&ctx.ring, &cqe))
555 		error(1, errno, "io_uring_wait_cqe");
556 
557 	expect_res(cqe->res, HINT_CHUNK_SIZE, "multishot second chunk");
558 	expect_uring_nonempty(cqe->flags, false, "multishot second chunk");
559 	expect_uring_more(cqe->flags, true, "multishot second chunk");
560 	io_uring_cqe_seen(&ctx.ring, cqe);
561 
562 	control_writeln("DRAINED");
563 	control_expectln("CLOSED");
564 
565 	/* EOF ends multishot regardless of the hint. */
566 	if (io_uring_wait_cqe(&ctx.ring, &cqe))
567 		error(1, errno, "io_uring_wait_cqe");
568 
569 	expect_res(cqe->res, 0, "multishot EOF");
570 	expect_uring_more(cqe->flags, false, "multishot EOF");
571 	io_uring_cqe_seen(&ctx.ring, cqe);
572 
573 out:
574 	control_writeln("DONE");
575 	io_uring_free_buf_ring(&ctx.ring, br, HINT_BUF_ENTRIES,
576 			       HINT_BUF_GROUP);
577 	inq_server_exit(&ctx);
578 }
579 
580 static struct test_case test_cases[] = {
581 	{
582 		.name = "SOCK_STREAM io_uring test",
583 		.run_server = test_stream_uring_server,
584 		.run_client = test_stream_uring_client,
585 	},
586 	{
587 		.name = "SOCK_STREAM io_uring MSG_ZEROCOPY test",
588 		.run_server = test_stream_uring_msg_zc_server,
589 		.run_client = test_stream_uring_msg_zc_client,
590 	},
591 	{
592 		.name = "SOCK_STREAM io_uring receive queue hint",
593 		.run_server = test_stream_uring_inq_server,
594 		.run_client = test_stream_uring_inq_client,
595 	},
596 	{
597 		.name = "SOCK_STREAM io_uring receive hint at EOF",
598 		.run_server = test_stream_uring_inq_eof_server,
599 		.run_client = test_stream_uring_inq_eof_client,
600 	},
601 	{
602 		.name = "SOCK_STREAM io_uring receive hint on empty queue",
603 		.run_server = test_stream_uring_inq_empty_server,
604 		.run_client = test_stream_uring_inq_empty_client,
605 	},
606 	{
607 		.name = "SOCK_STREAM io_uring receive hint zero-length",
608 		.run_server = test_stream_uring_inq_zerolen_server,
609 		.run_client = test_stream_uring_inq_zerolen_client,
610 	},
611 	{
612 		.name = "SOCK_STREAM io_uring multishot receive hint",
613 		.run_server = test_stream_uring_inq_mshot_server,
614 		.run_client = test_stream_uring_inq_mshot_client,
615 	},
616 	{},
617 };
618 
619 static const char optstring[] = "";
620 static const struct option longopts[] = {
621 	{
622 		.name = "control-host",
623 		.has_arg = required_argument,
624 		.val = 'H',
625 	},
626 	{
627 		.name = "control-port",
628 		.has_arg = required_argument,
629 		.val = 'P',
630 	},
631 	{
632 		.name = "mode",
633 		.has_arg = required_argument,
634 		.val = 'm',
635 	},
636 	{
637 		.name = "peer-cid",
638 		.has_arg = required_argument,
639 		.val = 'p',
640 	},
641 	{
642 		.name = "peer-port",
643 		.has_arg = required_argument,
644 		.val = 'q',
645 	},
646 	{
647 		.name = "help",
648 		.has_arg = no_argument,
649 		.val = '?',
650 	},
651 	{},
652 };
653 
654 static void usage(void)
655 {
656 	fprintf(stderr, "Usage: vsock_uring_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--peer-port=<port>]\n"
657 		"\n"
658 		"  Server: vsock_uring_test --control-port=1234 --mode=server --peer-cid=3\n"
659 		"  Client: vsock_uring_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n"
660 		"\n"
661 		"Run transmission tests using io_uring. Usage is the same as\n"
662 		"in ./vsock_test\n"
663 		"\n"
664 		"Options:\n"
665 		"  --help                 This help message\n"
666 		"  --control-host <host>  Server IP address to connect to\n"
667 		"  --control-port <port>  Server port to listen on/connect to\n"
668 		"  --mode client|server   Server or client mode\n"
669 		"  --peer-cid <cid>       CID of the other side\n"
670 		"  --peer-port <port>     AF_VSOCK port used for the test [default: %d]\n",
671 		DEFAULT_PEER_PORT
672 		);
673 	exit(EXIT_FAILURE);
674 }
675 
676 int main(int argc, char **argv)
677 {
678 	const char *control_host = NULL;
679 	const char *control_port = NULL;
680 	struct test_opts opts = {
681 		.mode = TEST_MODE_UNSET,
682 		.peer_cid = VMADDR_CID_ANY,
683 		.peer_port = DEFAULT_PEER_PORT,
684 	};
685 
686 	init_signals();
687 
688 	for (;;) {
689 		int opt = getopt_long(argc, argv, optstring, longopts, NULL);
690 
691 		if (opt == -1)
692 			break;
693 
694 		switch (opt) {
695 		case 'H':
696 			control_host = optarg;
697 			break;
698 		case 'm':
699 			if (strcmp(optarg, "client") == 0) {
700 				opts.mode = TEST_MODE_CLIENT;
701 			} else if (strcmp(optarg, "server") == 0) {
702 				opts.mode = TEST_MODE_SERVER;
703 			} else {
704 				fprintf(stderr, "--mode must be \"client\" or \"server\"\n");
705 				return EXIT_FAILURE;
706 			}
707 			break;
708 		case 'p':
709 			opts.peer_cid = parse_cid(optarg);
710 			break;
711 		case 'q':
712 			opts.peer_port = parse_port(optarg);
713 			break;
714 		case 'P':
715 			control_port = optarg;
716 			break;
717 		case '?':
718 		default:
719 			usage();
720 		}
721 	}
722 
723 	if (!control_port)
724 		usage();
725 	if (opts.mode == TEST_MODE_UNSET)
726 		usage();
727 	if (opts.peer_cid == VMADDR_CID_ANY)
728 		usage();
729 
730 	if (!control_host) {
731 		if (opts.mode != TEST_MODE_SERVER)
732 			usage();
733 		control_host = "0.0.0.0";
734 	}
735 
736 	control_init(control_host, control_port,
737 		     opts.mode == TEST_MODE_SERVER);
738 
739 	run_tests(test_cases, &opts);
740 
741 	control_cleanup();
742 
743 	return 0;
744 }
745