• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Description: run NAPI receive test. Meant to be run from the associated
4  *		script, napi-test.sh. That will invoke this test program
5  *		as either a sender or receiver, with the queue flags passed
6  *		in for testing.
7  *
8  */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12 #include <assert.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <arpa/inet.h>
18 #include <linux/if_packet.h>
19 #include <linux/socket.h>
20 #include <sys/socket.h>
21 #include <sys/stat.h>
22 
23 #include "liburing.h"
24 #include "helpers.h"
25 
26 static const char receiver_address[] = "10.10.10.20";
27 static const int port = 9999;
28 #define BUF_SIZE 4096
29 
30 static char buffer[BUF_SIZE];
31 static unsigned current_byte = 0;
32 
do_setsockopt(int fd,int level,int optname,int val)33 static void do_setsockopt(int fd, int level, int optname, int val)
34 {
35 	int ret = setsockopt(fd, level, optname, &val, sizeof(val));
36 
37 	assert(ret == 0);
38 }
39 
sender(int queue_flags)40 static int sender(int queue_flags)
41 {
42 	unsigned long long written = 0;
43 	struct sockaddr_in addr;
44 	struct io_uring ring;
45 	int i, ret, fd;
46 
47 	/*
48 	 * Sender doesn't use the ring, but try and set one up with the same
49 	 * flags that the receiver will use. If that fails, we know the
50 	 * receiver will have failed too - just skip the test in that case.
51 	 */
52 	ret = io_uring_queue_init(1, &ring, queue_flags);
53 	if (ret)
54 		return T_EXIT_SKIP;
55 	io_uring_queue_exit(&ring);
56 
57 	memset(&addr, 0, sizeof(addr));
58 	addr.sin_family = AF_INET;
59 	addr.sin_port = htons(port);
60 	ret = inet_pton(AF_INET, receiver_address, &addr.sin_addr);
61 	assert(ret == 1);
62 
63 	fd = socket(PF_INET, SOCK_STREAM, 0);
64 	assert(fd >= 0);
65 
66 	/* don't race with receiver, give it 1 second to connect */
67 	i = 0;
68 	do {
69 		ret = connect(fd, (void *)&addr, sizeof(addr));
70 		if (!ret)
71 			break;
72 		if (ret == -1 && errno == ECONNREFUSED) {
73 			if (i >= 10000) {
74 				fprintf(stderr, "Gave up trying to connect\n");
75 				return 1;
76 			}
77 			usleep(100);
78 			continue;
79 		}
80 		i++;
81 	} while (1);
82 
83 	while (written < 8 * 1024 * 1024) {
84 		for (i = 0; i < BUF_SIZE; i++)
85 			buffer[i] = current_byte + i;
86 
87 		ret = write(fd, buffer, BUF_SIZE);
88 		if (ret <= 0) {
89 			if (!ret || errno == ECONNRESET)
90 				break;
91 			fprintf(stderr, "write failed %i %i\n", ret, errno);
92 			return 1;
93 		}
94 		written += ret;
95 		current_byte += ret;
96 	}
97 
98 	close(fd);
99 	return 0;
100 }
101 
receiver(int queue_flags)102 static int receiver(int queue_flags)
103 {
104 	struct io_uring_sqe *sqe;
105 	struct io_uring_cqe *cqe;
106 	struct io_uring ring;
107 	struct io_uring_napi napi = { };
108 	struct sockaddr_in addr;
109 	int fd, listen_fd;
110 	int i, ret;
111 
112 	ret = io_uring_queue_init(8, &ring, queue_flags);
113 	if (ret < 0) {
114 		if (ret == -EINVAL)
115 			return T_EXIT_SKIP;
116 		fprintf(stderr, "queue_init: %s\n", strerror(-ret));
117 		return 1;
118 	}
119 
120 	napi.prefer_busy_poll = 1;
121 	napi.busy_poll_to = 50;
122 	io_uring_register_napi(&ring, &napi);
123 
124 	memset(&addr, 0, sizeof(addr));
125 	addr.sin_family = AF_INET;
126 	addr.sin_port = htons(port);
127 	addr.sin_addr.s_addr = INADDR_ANY;
128 
129 	listen_fd = socket(AF_INET, SOCK_STREAM, 0);
130 	assert(listen_fd >= 0);
131 
132 	do_setsockopt(listen_fd, SOL_SOCKET, SO_REUSEPORT, 1);
133 	ret = bind(listen_fd, (void *)&addr, sizeof(addr));
134 	if (ret) {
135 		fprintf(stderr, "bind failed %i %i\n", ret, errno);
136 		return 1;
137 	}
138 
139 	ret = listen(listen_fd, 8);
140 	assert(ret == 0);
141 
142 	fd = accept(listen_fd, NULL, NULL);
143 	assert(fd >= 0);
144 
145 	while (1) {
146 		sqe = io_uring_get_sqe(&ring);
147 		io_uring_prep_recv(sqe, fd, buffer, BUF_SIZE, 0);
148 
149 		ret = io_uring_submit(&ring);
150 		if (ret != 1) {
151 			fprintf(stderr, "io_uring_submit: %i\n", ret);
152 			return 1;
153 		}
154 
155 		ret = io_uring_wait_cqe(&ring, &cqe);
156 		if (ret < 0) {
157 			fprintf(stderr, "io_uring_wait_cqe: %i\n", ret);
158 			return 1;
159 		}
160 
161 		ret = cqe->res;
162 		if (ret <= 0) {
163 			if (!ret)
164 				break;
165 			fprintf(stderr, "recv failed %i %i\n", ret, errno);
166 			return 1;
167 		}
168 
169 		for (i = 0; i < ret; i++) {
170 			char expected = current_byte + i;
171 
172 			if (buffer[i] != expected) {
173 				fprintf(stderr, "data mismatch: idx %i, %c vs %c\n",
174 					i, buffer[i], expected);
175 				return 1;
176 			}
177 		}
178 
179 		current_byte += ret;
180 		io_uring_cqe_seen(&ring, cqe);
181 	}
182 
183 	close(fd);
184 	io_uring_queue_exit(&ring);
185 	return 0;
186 }
187 
main(int argc,char ** argv)188 int main(int argc, char **argv)
189 {
190 	int queue_flags;
191 	int is_rx;
192 
193 	if (geteuid()) {
194 		fprintf(stdout, "NAPI test requires root\n");
195 		return T_EXIT_SKIP;
196 	}
197 
198 	if (argc == 1) {
199 		struct stat sb;
200 
201 		if (!stat("napi-test.sh", &sb)) {
202 			return system("bash napi-test.sh");
203 		} else if (!stat("test/napi-test.sh", &sb)) {
204 			return system("bash test/napi-test.sh");
205 		} else {
206 			fprintf(stderr, "Can't find napi-test.sh\n");
207 			return T_EXIT_SKIP;
208 		}
209 	} else if (argc == 2) {
210 		return T_EXIT_SKIP;
211 	} else if (argc != 3) {
212 		return T_EXIT_SKIP;
213 	}
214 
215 	if (!strcmp(argv[1], "receive"))
216 		is_rx = 1;
217 	else if (!strcmp(argv[1], "send"))
218 		is_rx = 0;
219 	else
220 		return T_EXIT_FAIL;
221 
222 	queue_flags = strtoul(argv[2], NULL, 16);
223 
224 	if (is_rx)
225 		return receiver(queue_flags);
226 
227 	return sender(queue_flags);
228 }
229