blob: f31353fac5415d8b9f5614e6f46f71a8f062f09b [file] [log] [blame]
Michael S. Tsirkin481eaec2016-01-21 14:44:10 +02001/*
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
20int runcycles = 10000000;
21int max_outstanding = INT_MAX;
22int batch = 1;
23
24bool do_sleep = false;
25bool do_relax = false;
26bool do_exit = true;
27
28unsigned ring_size = 256;
29
30static int kickfd = -1;
31static int callfd = -1;
32
33void 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
44void 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
55void kick(void)
56{
57 notify(kickfd);
58}
59
60void wait_for_kick(void)
61{
62 wait_for_notify(kickfd);
63}
64
65void call(void)
66{
67 notify(callfd);
68}
69
70void wait_for_call(void)
71{
72 wait_for_notify(callfd);
73}
74
75void 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
Paolo Bonzinid3c35892016-10-06 11:39:11 +020099void poll_used(void)
100{
101 while (used_empty())
102 busy_wait();
103}
104
Paolo Bonzini44d65ea2016-10-06 11:39:10 +0200105static void __attribute__((__flatten__)) run_guest(void)
Michael S. Tsirkin481eaec2016-01-21 14:44:10 +0200106{
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) {
Michael S. Tsirkinbb991282016-05-22 15:10:49 +0300124 r = add_inbuf(0, "Buffer\n", "Hello, world!");
Michael S. Tsirkin481eaec2016-01-21 14:44:10 +0200125 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) {
Paolo Bonzini948a8ac2016-10-06 11:39:12 +0200150 if (used_empty() && enable_call())
Michael S. Tsirkin481eaec2016-01-21 14:44:10 +0200151 wait_for_call();
152 } else {
153 poll_used();
154 }
155 }
156}
157
Paolo Bonzinid3c35892016-10-06 11:39:11 +0200158void poll_avail(void)
159{
160 while (avail_empty())
161 busy_wait();
162}
163
Paolo Bonzini44d65ea2016-10-06 11:39:10 +0200164static void __attribute__((__flatten__)) run_host(void)
Michael S. Tsirkin481eaec2016-01-21 14:44:10 +0200165{
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) {
Paolo Bonzini948a8ac2016-10-06 11:39:12 +0200175 if (avail_empty() && enable_kick())
Michael S. Tsirkin481eaec2016-01-21 14:44:10 +0200176 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
198void *start_guest(void *arg)
199{
200 set_affinity(arg);
201 run_guest();
202 pthread_exit(NULL);
203}
204
205void *start_host(void *arg)
206{
207 set_affinity(arg);
208 run_host();
209 pthread_exit(NULL);
210}
211
212static const char optstring[] = "";
213static 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
268static 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
285int 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();
357done:
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}