xref: /openbmc/linux/tools/virtio/ringtest/main.c (revision 7587eb18)
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 static void run_guest(void)
100 {
101 	int completed_before;
102 	int completed = 0;
103 	int started = 0;
104 	int bufs = runcycles;
105 	int spurious = 0;
106 	int r;
107 	unsigned len;
108 	void *buf;
109 	int tokick = batch;
110 
111 	for (;;) {
112 		if (do_sleep)
113 			disable_call();
114 		completed_before = completed;
115 		do {
116 			if (started < bufs &&
117 			    started - completed < max_outstanding) {
118 				r = add_inbuf(0, "Buffer\n", "Hello, world!");
119 				if (__builtin_expect(r == 0, true)) {
120 					++started;
121 					if (!--tokick) {
122 						tokick = batch;
123 						if (do_sleep)
124 							kick_available();
125 					}
126 
127 				}
128 			} else
129 				r = -1;
130 
131 			/* Flush out completed bufs if any */
132 			if (get_buf(&len, &buf)) {
133 				++completed;
134 				if (__builtin_expect(completed == bufs, false))
135 					return;
136 				r = 0;
137 			}
138 		} while (r == 0);
139 		if (completed == completed_before)
140 			++spurious;
141 		assert(completed <= bufs);
142 		assert(started <= bufs);
143 		if (do_sleep) {
144 			if (enable_call())
145 				wait_for_call();
146 		} else {
147 			poll_used();
148 		}
149 	}
150 }
151 
152 static void run_host(void)
153 {
154 	int completed_before;
155 	int completed = 0;
156 	int spurious = 0;
157 	int bufs = runcycles;
158 	unsigned len;
159 	void *buf;
160 
161 	for (;;) {
162 		if (do_sleep) {
163 			if (enable_kick())
164 				wait_for_kick();
165 		} else {
166 			poll_avail();
167 		}
168 		if (do_sleep)
169 			disable_kick();
170 		completed_before = completed;
171 		while (__builtin_expect(use_buf(&len, &buf), true)) {
172 			if (do_sleep)
173 				call_used();
174 			++completed;
175 			if (__builtin_expect(completed == bufs, false))
176 				return;
177 		}
178 		if (completed == completed_before)
179 			++spurious;
180 		assert(completed <= bufs);
181 		if (completed == bufs)
182 			break;
183 	}
184 }
185 
186 void *start_guest(void *arg)
187 {
188 	set_affinity(arg);
189 	run_guest();
190 	pthread_exit(NULL);
191 }
192 
193 void *start_host(void *arg)
194 {
195 	set_affinity(arg);
196 	run_host();
197 	pthread_exit(NULL);
198 }
199 
200 static const char optstring[] = "";
201 static const struct option longopts[] = {
202 	{
203 		.name = "help",
204 		.has_arg = no_argument,
205 		.val = 'h',
206 	},
207 	{
208 		.name = "host-affinity",
209 		.has_arg = required_argument,
210 		.val = 'H',
211 	},
212 	{
213 		.name = "guest-affinity",
214 		.has_arg = required_argument,
215 		.val = 'G',
216 	},
217 	{
218 		.name = "ring-size",
219 		.has_arg = required_argument,
220 		.val = 'R',
221 	},
222 	{
223 		.name = "run-cycles",
224 		.has_arg = required_argument,
225 		.val = 'C',
226 	},
227 	{
228 		.name = "outstanding",
229 		.has_arg = required_argument,
230 		.val = 'o',
231 	},
232 	{
233 		.name = "batch",
234 		.has_arg = required_argument,
235 		.val = 'b',
236 	},
237 	{
238 		.name = "sleep",
239 		.has_arg = no_argument,
240 		.val = 's',
241 	},
242 	{
243 		.name = "relax",
244 		.has_arg = no_argument,
245 		.val = 'x',
246 	},
247 	{
248 		.name = "exit",
249 		.has_arg = no_argument,
250 		.val = 'e',
251 	},
252 	{
253 	}
254 };
255 
256 static void help(void)
257 {
258 	fprintf(stderr, "Usage: <test> [--help]"
259 		" [--host-affinity H]"
260 		" [--guest-affinity G]"
261 		" [--ring-size R (default: %d)]"
262 		" [--run-cycles C (default: %d)]"
263 		" [--batch b]"
264 		" [--outstanding o]"
265 		" [--sleep]"
266 		" [--relax]"
267 		" [--exit]"
268 		"\n",
269 		ring_size,
270 		runcycles);
271 }
272 
273 int main(int argc, char **argv)
274 {
275 	int ret;
276 	pthread_t host, guest;
277 	void *tret;
278 	char *host_arg = NULL;
279 	char *guest_arg = NULL;
280 	char *endptr;
281 	long int c;
282 
283 	kickfd = eventfd(0, 0);
284 	assert(kickfd >= 0);
285 	callfd = eventfd(0, 0);
286 	assert(callfd >= 0);
287 
288 	for (;;) {
289 		int o = getopt_long(argc, argv, optstring, longopts, NULL);
290 		switch (o) {
291 		case -1:
292 			goto done;
293 		case '?':
294 			help();
295 			exit(2);
296 		case 'H':
297 			host_arg = optarg;
298 			break;
299 		case 'G':
300 			guest_arg = optarg;
301 			break;
302 		case 'R':
303 			ring_size = strtol(optarg, &endptr, 0);
304 			assert(ring_size && !(ring_size & (ring_size - 1)));
305 			assert(!*endptr);
306 			break;
307 		case 'C':
308 			c = strtol(optarg, &endptr, 0);
309 			assert(!*endptr);
310 			assert(c > 0 && c < INT_MAX);
311 			runcycles = c;
312 			break;
313 		case 'o':
314 			c = strtol(optarg, &endptr, 0);
315 			assert(!*endptr);
316 			assert(c > 0 && c < INT_MAX);
317 			max_outstanding = c;
318 			break;
319 		case 'b':
320 			c = strtol(optarg, &endptr, 0);
321 			assert(!*endptr);
322 			assert(c > 0 && c < INT_MAX);
323 			batch = c;
324 			break;
325 		case 's':
326 			do_sleep = true;
327 			break;
328 		case 'x':
329 			do_relax = true;
330 			break;
331 		case 'e':
332 			do_exit = true;
333 			break;
334 		default:
335 			help();
336 			exit(4);
337 			break;
338 		}
339 	}
340 
341 	/* does nothing here, used to make sure all smp APIs compile */
342 	smp_acquire();
343 	smp_release();
344 	smp_mb();
345 done:
346 
347 	if (batch > max_outstanding)
348 		batch = max_outstanding;
349 
350 	if (optind < argc) {
351 		help();
352 		exit(4);
353 	}
354 	alloc_ring();
355 
356 	ret = pthread_create(&host, NULL, start_host, host_arg);
357 	assert(!ret);
358 	ret = pthread_create(&guest, NULL, start_guest, guest_arg);
359 	assert(!ret);
360 
361 	ret = pthread_join(guest, &tret);
362 	assert(!ret);
363 	ret = pthread_join(host, &tret);
364 	assert(!ret);
365 	return 0;
366 }
367