xref: /openbmc/linux/tools/virtio/ringtest/main.c (revision 4f6cce39)
1 /*
2  * Copyright (C) 2016 Red Hat, Inc.
3  * Author: Michael S. Tsirkin <mst@redhat.com>
4  * This work is licensed under the terms of the GNU GPL, version 2.
5  *
6  * Command line processing and common functions for ring benchmarking.
7  */
8 #define _GNU_SOURCE
9 #include <getopt.h>
10 #include <pthread.h>
11 #include <assert.h>
12 #include <sched.h>
13 #include "main.h"
14 #include <sys/eventfd.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <limits.h>
19 
20 int runcycles = 10000000;
21 int max_outstanding = INT_MAX;
22 int batch = 1;
23 
24 bool do_sleep = false;
25 bool do_relax = false;
26 bool do_exit = true;
27 
28 unsigned ring_size = 256;
29 
30 static int kickfd = -1;
31 static int callfd = -1;
32 
33 void notify(int fd)
34 {
35 	unsigned long long v = 1;
36 	int r;
37 
38 	vmexit();
39 	r = write(fd, &v, sizeof v);
40 	assert(r == sizeof v);
41 	vmentry();
42 }
43 
44 void wait_for_notify(int fd)
45 {
46 	unsigned long long v = 1;
47 	int r;
48 
49 	vmexit();
50 	r = read(fd, &v, sizeof v);
51 	assert(r == sizeof v);
52 	vmentry();
53 }
54 
55 void kick(void)
56 {
57 	notify(kickfd);
58 }
59 
60 void wait_for_kick(void)
61 {
62 	wait_for_notify(kickfd);
63 }
64 
65 void call(void)
66 {
67 	notify(callfd);
68 }
69 
70 void wait_for_call(void)
71 {
72 	wait_for_notify(callfd);
73 }
74 
75 void set_affinity(const char *arg)
76 {
77 	cpu_set_t cpuset;
78 	int ret;
79 	pthread_t self;
80 	long int cpu;
81 	char *endptr;
82 
83 	if (!arg)
84 		return;
85 
86 	cpu = strtol(arg, &endptr, 0);
87 	assert(!*endptr);
88 
89 	assert(cpu >= 0 || cpu < CPU_SETSIZE);
90 
91 	self = pthread_self();
92 	CPU_ZERO(&cpuset);
93 	CPU_SET(cpu, &cpuset);
94 
95 	ret = pthread_setaffinity_np(self, sizeof(cpu_set_t), &cpuset);
96 	assert(!ret);
97 }
98 
99 void poll_used(void)
100 {
101 	while (used_empty())
102 		busy_wait();
103 }
104 
105 static void __attribute__((__flatten__)) run_guest(void)
106 {
107 	int completed_before;
108 	int completed = 0;
109 	int started = 0;
110 	int bufs = runcycles;
111 	int spurious = 0;
112 	int r;
113 	unsigned len;
114 	void *buf;
115 	int tokick = batch;
116 
117 	for (;;) {
118 		if (do_sleep)
119 			disable_call();
120 		completed_before = completed;
121 		do {
122 			if (started < bufs &&
123 			    started - completed < max_outstanding) {
124 				r = add_inbuf(0, "Buffer\n", "Hello, world!");
125 				if (__builtin_expect(r == 0, true)) {
126 					++started;
127 					if (!--tokick) {
128 						tokick = batch;
129 						if (do_sleep)
130 							kick_available();
131 					}
132 
133 				}
134 			} else
135 				r = -1;
136 
137 			/* Flush out completed bufs if any */
138 			if (get_buf(&len, &buf)) {
139 				++completed;
140 				if (__builtin_expect(completed == bufs, false))
141 					return;
142 				r = 0;
143 			}
144 		} while (r == 0);
145 		if (completed == completed_before)
146 			++spurious;
147 		assert(completed <= bufs);
148 		assert(started <= bufs);
149 		if (do_sleep) {
150 			if (used_empty() && enable_call())
151 				wait_for_call();
152 		} else {
153 			poll_used();
154 		}
155 	}
156 }
157 
158 void poll_avail(void)
159 {
160 	while (avail_empty())
161 		busy_wait();
162 }
163 
164 static void __attribute__((__flatten__)) run_host(void)
165 {
166 	int completed_before;
167 	int completed = 0;
168 	int spurious = 0;
169 	int bufs = runcycles;
170 	unsigned len;
171 	void *buf;
172 
173 	for (;;) {
174 		if (do_sleep) {
175 			if (avail_empty() && enable_kick())
176 				wait_for_kick();
177 		} else {
178 			poll_avail();
179 		}
180 		if (do_sleep)
181 			disable_kick();
182 		completed_before = completed;
183 		while (__builtin_expect(use_buf(&len, &buf), true)) {
184 			if (do_sleep)
185 				call_used();
186 			++completed;
187 			if (__builtin_expect(completed == bufs, false))
188 				return;
189 		}
190 		if (completed == completed_before)
191 			++spurious;
192 		assert(completed <= bufs);
193 		if (completed == bufs)
194 			break;
195 	}
196 }
197 
198 void *start_guest(void *arg)
199 {
200 	set_affinity(arg);
201 	run_guest();
202 	pthread_exit(NULL);
203 }
204 
205 void *start_host(void *arg)
206 {
207 	set_affinity(arg);
208 	run_host();
209 	pthread_exit(NULL);
210 }
211 
212 static const char optstring[] = "";
213 static const struct option longopts[] = {
214 	{
215 		.name = "help",
216 		.has_arg = no_argument,
217 		.val = 'h',
218 	},
219 	{
220 		.name = "host-affinity",
221 		.has_arg = required_argument,
222 		.val = 'H',
223 	},
224 	{
225 		.name = "guest-affinity",
226 		.has_arg = required_argument,
227 		.val = 'G',
228 	},
229 	{
230 		.name = "ring-size",
231 		.has_arg = required_argument,
232 		.val = 'R',
233 	},
234 	{
235 		.name = "run-cycles",
236 		.has_arg = required_argument,
237 		.val = 'C',
238 	},
239 	{
240 		.name = "outstanding",
241 		.has_arg = required_argument,
242 		.val = 'o',
243 	},
244 	{
245 		.name = "batch",
246 		.has_arg = required_argument,
247 		.val = 'b',
248 	},
249 	{
250 		.name = "sleep",
251 		.has_arg = no_argument,
252 		.val = 's',
253 	},
254 	{
255 		.name = "relax",
256 		.has_arg = no_argument,
257 		.val = 'x',
258 	},
259 	{
260 		.name = "exit",
261 		.has_arg = no_argument,
262 		.val = 'e',
263 	},
264 	{
265 	}
266 };
267 
268 static void help(void)
269 {
270 	fprintf(stderr, "Usage: <test> [--help]"
271 		" [--host-affinity H]"
272 		" [--guest-affinity G]"
273 		" [--ring-size R (default: %d)]"
274 		" [--run-cycles C (default: %d)]"
275 		" [--batch b]"
276 		" [--outstanding o]"
277 		" [--sleep]"
278 		" [--relax]"
279 		" [--exit]"
280 		"\n",
281 		ring_size,
282 		runcycles);
283 }
284 
285 int main(int argc, char **argv)
286 {
287 	int ret;
288 	pthread_t host, guest;
289 	void *tret;
290 	char *host_arg = NULL;
291 	char *guest_arg = NULL;
292 	char *endptr;
293 	long int c;
294 
295 	kickfd = eventfd(0, 0);
296 	assert(kickfd >= 0);
297 	callfd = eventfd(0, 0);
298 	assert(callfd >= 0);
299 
300 	for (;;) {
301 		int o = getopt_long(argc, argv, optstring, longopts, NULL);
302 		switch (o) {
303 		case -1:
304 			goto done;
305 		case '?':
306 			help();
307 			exit(2);
308 		case 'H':
309 			host_arg = optarg;
310 			break;
311 		case 'G':
312 			guest_arg = optarg;
313 			break;
314 		case 'R':
315 			ring_size = strtol(optarg, &endptr, 0);
316 			assert(ring_size && !(ring_size & (ring_size - 1)));
317 			assert(!*endptr);
318 			break;
319 		case 'C':
320 			c = strtol(optarg, &endptr, 0);
321 			assert(!*endptr);
322 			assert(c > 0 && c < INT_MAX);
323 			runcycles = c;
324 			break;
325 		case 'o':
326 			c = strtol(optarg, &endptr, 0);
327 			assert(!*endptr);
328 			assert(c > 0 && c < INT_MAX);
329 			max_outstanding = c;
330 			break;
331 		case 'b':
332 			c = strtol(optarg, &endptr, 0);
333 			assert(!*endptr);
334 			assert(c > 0 && c < INT_MAX);
335 			batch = c;
336 			break;
337 		case 's':
338 			do_sleep = true;
339 			break;
340 		case 'x':
341 			do_relax = true;
342 			break;
343 		case 'e':
344 			do_exit = true;
345 			break;
346 		default:
347 			help();
348 			exit(4);
349 			break;
350 		}
351 	}
352 
353 	/* does nothing here, used to make sure all smp APIs compile */
354 	smp_acquire();
355 	smp_release();
356 	smp_mb();
357 done:
358 
359 	if (batch > max_outstanding)
360 		batch = max_outstanding;
361 
362 	if (optind < argc) {
363 		help();
364 		exit(4);
365 	}
366 	alloc_ring();
367 
368 	ret = pthread_create(&host, NULL, start_host, host_arg);
369 	assert(!ret);
370 	ret = pthread_create(&guest, NULL, start_guest, guest_arg);
371 	assert(!ret);
372 
373 	ret = pthread_join(guest, &tret);
374 	assert(!ret);
375 	ret = pthread_join(host, &tret);
376 	assert(!ret);
377 	return 0;
378 }
379