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
notify(int fd)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
wait_for_notify(int fd)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
kick(void)55 void kick(void)
56 {
57 notify(kickfd);
58 }
59
wait_for_kick(void)60 void wait_for_kick(void)
61 {
62 wait_for_notify(kickfd);
63 }
64
call(void)65 void call(void)
66 {
67 notify(callfd);
68 }
69
wait_for_call(void)70 void wait_for_call(void)
71 {
72 wait_for_notify(callfd);
73 }
74
set_affinity(const char * arg)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
poll_used(void)99 void poll_used(void)
100 {
101 while (used_empty())
102 busy_wait();
103 }
104
run_guest(void)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
poll_avail(void)158 void poll_avail(void)
159 {
160 while (avail_empty())
161 busy_wait();
162 }
163
run_host(void)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
start_guest(void * arg)198 void *start_guest(void *arg)
199 {
200 set_affinity(arg);
201 run_guest();
202 pthread_exit(NULL);
203 }
204
start_host(void * arg)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
help(void)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
main(int argc,char ** argv)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