ringtest: poll for new buffers once before updating event index
[cascardo/linux.git] / tools / virtio / ringtest / main.c
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
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
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
55 void kick(void)
56 {
57         notify(kickfd);
58 }
59
60 void wait_for_kick(void)
61 {
62         wait_for_notify(kickfd);
63 }
64
65 void call(void)
66 {
67         notify(callfd);
68 }
69
70 void wait_for_call(void)
71 {
72         wait_for_notify(callfd);
73 }
74
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
99 void poll_used(void)
100 {
101         while (used_empty())
102                 busy_wait();
103 }
104
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
158 void poll_avail(void)
159 {
160         while (avail_empty())
161                 busy_wait();
162 }
163
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
198 void *start_guest(void *arg)
199 {
200         set_affinity(arg);
201         run_guest();
202         pthread_exit(NULL);
203 }
204
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
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
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 }