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