xref: /linux/tools/testing/selftests/net/busy_poller.c (revision c34e9ab9a612ee8b18273398ef75c207b01f516d)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <assert.h>
3 #include <errno.h>
4 #include <error.h>
5 #include <fcntl.h>
6 #include <inttypes.h>
7 #include <limits.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <ynl.h>
13 
14 #include <arpa/inet.h>
15 #include <netinet/in.h>
16 
17 #include <sys/epoll.h>
18 #include <sys/ioctl.h>
19 #include <sys/socket.h>
20 #include <sys/types.h>
21 
22 #include <linux/genetlink.h>
23 #include <linux/netlink.h>
24 
25 #include "netdev-user.h"
26 
27 /* The below ifdef blob is required because:
28  *
29  * - sys/epoll.h does not (yet) have the ioctl definitions included. So,
30  *   systems with older glibcs will not have them available. However,
31  *   sys/epoll.h does include the type definition for epoll_data, which is
32  *   needed by the user program (e.g. epoll_event.data.fd)
33  *
34  * - linux/eventpoll.h does not define the epoll_data type, it is simply an
35  *   opaque __u64. It does, however, include the ioctl definition.
36  *
37  * Including both headers is impossible (types would be redefined), so I've
38  * opted instead to take sys/epoll.h, and include the blob below.
39  *
40  * Someday, when glibc is globally up to date, the blob below can be removed.
41  */
42 #if !defined(EPOLL_IOC_TYPE)
43 struct epoll_params {
44 	uint32_t busy_poll_usecs;
45 	uint16_t busy_poll_budget;
46 	uint8_t prefer_busy_poll;
47 
48 	/* pad the struct to a multiple of 64bits */
49 	uint8_t __pad;
50 };
51 
52 #define EPOLL_IOC_TYPE 0x8A
53 #define EPIOCSPARAMS _IOW(EPOLL_IOC_TYPE, 0x01, struct epoll_params)
54 #define EPIOCGPARAMS _IOR(EPOLL_IOC_TYPE, 0x02, struct epoll_params)
55 #endif
56 
57 static uint32_t cfg_port = 8000;
58 static struct in_addr cfg_bind_addr = { .s_addr = INADDR_ANY };
59 static char *cfg_outfile;
60 static int cfg_max_events = 8;
61 static int cfg_ifindex;
62 
63 /* busy poll params */
64 static uint32_t cfg_busy_poll_usecs;
65 static uint32_t cfg_busy_poll_budget;
66 static uint32_t cfg_prefer_busy_poll;
67 
68 /* IRQ params */
69 static uint32_t cfg_defer_hard_irqs;
70 static uint64_t cfg_gro_flush_timeout;
71 static uint64_t cfg_irq_suspend_timeout;
72 
73 static void usage(const char *filepath)
74 {
75 	error(1, 0,
76 	      "Usage: %s -p<port> -b<addr> -m<max_events> -u<busy_poll_usecs> -P<prefer_busy_poll> -g<busy_poll_budget> -o<outfile> -d<defer_hard_irqs> -r<gro_flush_timeout> -s<irq_suspend_timeout> -i<ifindex>",
77 	      filepath);
78 }
79 
80 static void parse_opts(int argc, char **argv)
81 {
82 	int ret;
83 	int c;
84 
85 	if (argc <= 1)
86 		usage(argv[0]);
87 
88 	while ((c = getopt(argc, argv, "p:m:b:u:P:g:o:d:r:s:i:")) != -1) {
89 		switch (c) {
90 		case 'u':
91 			cfg_busy_poll_usecs = strtoul(optarg, NULL, 0);
92 			if (cfg_busy_poll_usecs == ULONG_MAX ||
93 			    cfg_busy_poll_usecs > UINT32_MAX)
94 				error(1, ERANGE, "busy_poll_usecs too large");
95 			break;
96 		case 'P':
97 			cfg_prefer_busy_poll = strtoul(optarg, NULL, 0);
98 			if (cfg_prefer_busy_poll == ULONG_MAX ||
99 			    cfg_prefer_busy_poll > 1)
100 				error(1, ERANGE,
101 				      "prefer busy poll should be 0 or 1");
102 			break;
103 		case 'g':
104 			cfg_busy_poll_budget = strtoul(optarg, NULL, 0);
105 			if (cfg_busy_poll_budget == ULONG_MAX ||
106 			    cfg_busy_poll_budget > UINT16_MAX)
107 				error(1, ERANGE,
108 				      "busy poll budget must be [0, UINT16_MAX]");
109 			break;
110 		case 'p':
111 			cfg_port = strtoul(optarg, NULL, 0);
112 			if (cfg_port > UINT16_MAX)
113 				error(1, ERANGE, "port must be <= 65535");
114 			break;
115 		case 'b':
116 			ret = inet_aton(optarg, &cfg_bind_addr);
117 			if (ret == 0)
118 				error(1, errno,
119 				      "bind address %s invalid", optarg);
120 			break;
121 		case 'o':
122 			cfg_outfile = strdup(optarg);
123 			if (!cfg_outfile)
124 				error(1, 0, "outfile invalid");
125 			break;
126 		case 'm':
127 			cfg_max_events = strtol(optarg, NULL, 0);
128 
129 			if (cfg_max_events == LONG_MIN ||
130 			    cfg_max_events == LONG_MAX ||
131 			    cfg_max_events <= 0)
132 				error(1, ERANGE,
133 				      "max events must be > 0 and < LONG_MAX");
134 			break;
135 		case 'd':
136 			cfg_defer_hard_irqs = strtoul(optarg, NULL, 0);
137 
138 			if (cfg_defer_hard_irqs == ULONG_MAX ||
139 			    cfg_defer_hard_irqs > INT32_MAX)
140 				error(1, ERANGE,
141 				      "defer_hard_irqs must be <= INT32_MAX");
142 			break;
143 		case 'r':
144 			cfg_gro_flush_timeout = strtoull(optarg, NULL, 0);
145 
146 			if (cfg_gro_flush_timeout == ULLONG_MAX)
147 				error(1, ERANGE,
148 				      "gro_flush_timeout must be < ULLONG_MAX");
149 			break;
150 		case 's':
151 			cfg_irq_suspend_timeout = strtoull(optarg, NULL, 0);
152 
153 			if (cfg_irq_suspend_timeout == ULLONG_MAX)
154 				error(1, ERANGE,
155 				      "irq_suspend_timeout must be < ULLONG_MAX");
156 			break;
157 		case 'i':
158 			cfg_ifindex = strtoul(optarg, NULL, 0);
159 			if (cfg_ifindex == ULONG_MAX)
160 				error(1, ERANGE,
161 				      "ifindex must be < ULONG_MAX");
162 			break;
163 		}
164 	}
165 
166 	if (!cfg_ifindex)
167 		usage(argv[0]);
168 
169 	if (optind != argc)
170 		usage(argv[0]);
171 }
172 
173 static void epoll_ctl_add(int epfd, int fd, uint32_t events)
174 {
175 	struct epoll_event ev;
176 
177 	ev.events = events;
178 	ev.data.fd = fd;
179 	if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1)
180 		error(1, errno, "epoll_ctl add fd: %d", fd);
181 }
182 
183 static void setnonblock(int sockfd)
184 {
185 	int flags;
186 
187 	flags = fcntl(sockfd, F_GETFL, 0);
188 
189 	if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1)
190 		error(1, errno, "unable to set socket to nonblocking mode");
191 }
192 
193 static void write_chunk(int fd, char *buf, ssize_t buflen)
194 {
195 	ssize_t remaining = buflen;
196 	char *buf_offset = buf;
197 	ssize_t writelen = 0;
198 	ssize_t write_result;
199 
200 	while (writelen < buflen) {
201 		write_result = write(fd, buf_offset, remaining);
202 		if (write_result == -1)
203 			error(1, errno, "unable to write data to outfile");
204 
205 		writelen += write_result;
206 		remaining -= write_result;
207 		buf_offset += write_result;
208 	}
209 }
210 
211 static void setup_queue(void)
212 {
213 	struct netdev_napi_get_list *napi_list = NULL;
214 	struct netdev_napi_get_req_dump *req = NULL;
215 	struct netdev_napi_set_req *set_req = NULL;
216 	struct ynl_sock *ys;
217 	struct ynl_error yerr;
218 	uint32_t napi_id;
219 
220 	ys = ynl_sock_create(&ynl_netdev_family, &yerr);
221 	if (!ys)
222 		error(1, 0, "YNL: %s", yerr.msg);
223 
224 	req = netdev_napi_get_req_dump_alloc();
225 	netdev_napi_get_req_dump_set_ifindex(req, cfg_ifindex);
226 	napi_list = netdev_napi_get_dump(ys, req);
227 
228 	/* assume there is 1 NAPI configured and take the first */
229 	if (napi_list->obj._present.id)
230 		napi_id = napi_list->obj.id;
231 	else
232 		error(1, 0, "napi ID not present?");
233 
234 	set_req = netdev_napi_set_req_alloc();
235 	netdev_napi_set_req_set_id(set_req, napi_id);
236 	netdev_napi_set_req_set_defer_hard_irqs(set_req, cfg_defer_hard_irqs);
237 	netdev_napi_set_req_set_gro_flush_timeout(set_req,
238 						  cfg_gro_flush_timeout);
239 	netdev_napi_set_req_set_irq_suspend_timeout(set_req,
240 						    cfg_irq_suspend_timeout);
241 
242 	if (netdev_napi_set(ys, set_req))
243 		error(1, 0, "can't set NAPI params: %s\n", yerr.msg);
244 
245 	netdev_napi_get_list_free(napi_list);
246 	netdev_napi_get_req_dump_free(req);
247 	netdev_napi_set_req_free(set_req);
248 	ynl_sock_destroy(ys);
249 }
250 
251 static void run_poller(void)
252 {
253 	struct epoll_event events[cfg_max_events];
254 	struct epoll_params epoll_params = {0};
255 	struct sockaddr_in server_addr;
256 	int i, epfd, nfds;
257 	ssize_t readlen;
258 	int outfile_fd;
259 	char buf[1024];
260 	int sockfd;
261 	int conn;
262 	int val;
263 
264 	outfile_fd = open(cfg_outfile, O_WRONLY | O_CREAT, 0644);
265 	if (outfile_fd == -1)
266 		error(1, errno, "unable to open outfile: %s", cfg_outfile);
267 
268 	sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
269 	if (sockfd == -1)
270 		error(1, errno, "unable to create listen socket");
271 
272 	server_addr.sin_family = AF_INET;
273 	server_addr.sin_port = htons(cfg_port);
274 	server_addr.sin_addr = cfg_bind_addr;
275 
276 	/* these values are range checked during parse_opts, so casting is safe
277 	 * here
278 	 */
279 	epoll_params.busy_poll_usecs = cfg_busy_poll_usecs;
280 	epoll_params.busy_poll_budget = (uint16_t)cfg_busy_poll_budget;
281 	epoll_params.prefer_busy_poll = (uint8_t)cfg_prefer_busy_poll;
282 	epoll_params.__pad = 0;
283 
284 	val = 1;
285 	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)))
286 		error(1, errno, "poller setsockopt reuseaddr");
287 
288 	setnonblock(sockfd);
289 
290 	if (bind(sockfd, (struct sockaddr *)&server_addr,
291 		 sizeof(struct sockaddr_in)))
292 		error(0, errno, "poller bind to port: %d\n", cfg_port);
293 
294 	if (listen(sockfd, 1))
295 		error(1, errno, "poller listen");
296 
297 	epfd = epoll_create1(0);
298 	if (ioctl(epfd, EPIOCSPARAMS, &epoll_params) == -1)
299 		error(1, errno, "unable to set busy poll params");
300 
301 	epoll_ctl_add(epfd, sockfd, EPOLLIN | EPOLLOUT | EPOLLET);
302 
303 	for (;;) {
304 		nfds = epoll_wait(epfd, events, cfg_max_events, -1);
305 		for (i = 0; i < nfds; i++) {
306 			if (events[i].data.fd == sockfd) {
307 				conn = accept(sockfd, NULL, NULL);
308 				if (conn == -1)
309 					error(1, errno,
310 					      "accepting incoming connection failed");
311 
312 				setnonblock(conn);
313 				epoll_ctl_add(epfd, conn,
314 					      EPOLLIN | EPOLLET | EPOLLRDHUP |
315 					      EPOLLHUP);
316 			} else if (events[i].events & EPOLLIN) {
317 				for (;;) {
318 					readlen = read(events[i].data.fd, buf,
319 						       sizeof(buf));
320 					if (readlen > 0)
321 						write_chunk(outfile_fd, buf,
322 							    readlen);
323 					else
324 						break;
325 				}
326 			} else {
327 				/* spurious event ? */
328 			}
329 			if (events[i].events & (EPOLLRDHUP | EPOLLHUP)) {
330 				epoll_ctl(epfd, EPOLL_CTL_DEL,
331 					  events[i].data.fd, NULL);
332 				close(events[i].data.fd);
333 				close(outfile_fd);
334 				return;
335 			}
336 		}
337 	}
338 }
339 
340 int main(int argc, char *argv[])
341 {
342 	parse_opts(argc, argv);
343 	setup_queue();
344 	run_poller();
345 	return 0;
346 }
347