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: %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
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