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
20int runcycles = 10000000;
21int max_outstanding = INT_MAX;
22int batch = 1;
23int param = 0;
24
25bool do_sleep = false;
26bool do_relax = false;
27bool do_exit = true;
28
29unsigned ring_size = 256;
30
31static int kickfd = -1;
32static int callfd = -1;
33
34void 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
45void 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
56void kick(void)
57{
58	notify(kickfd);
59}
60
61void wait_for_kick(void)
62{
63	wait_for_notify(kickfd);
64}
65
66void call(void)
67{
68	notify(callfd);
69}
70
71void wait_for_call(void)
72{
73	wait_for_notify(callfd);
74}
75
76void 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
100void poll_used(void)
101{
102	while (used_empty())
103		busy_wait();
104}
105
106static 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
159void poll_avail(void)
160{
161	while (avail_empty())
162		busy_wait();
163}
164
165static 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
199void *start_guest(void *arg)
200{
201	set_affinity(arg);
202	run_guest();
203	pthread_exit(NULL);
204}
205
206void *start_host(void *arg)
207{
208	set_affinity(arg);
209	run_host();
210	pthread_exit(NULL);
211}
212
213static const char optstring[] = "";
214static 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
274static void help(void)
275{
276	fprintf(stderr, "Usage: <test> [--help]"
277		" [--host-affinity H]"
278		" [--guest-affinity G]"
279		" [--ring-size R (default: %d)]"
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
292int 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();
370done:
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