147abb452a6ccc098bf50338e0c353f4b8896f8a
[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 static void run_guest(void)
100 {
101         int completed_before;
102         int completed = 0;
103         int started = 0;
104         int bufs = runcycles;
105         int spurious = 0;
106         int r;
107         unsigned len;
108         void *buf;
109         int tokick = batch;
110
111         for (;;) {
112                 if (do_sleep)
113                         disable_call();
114                 completed_before = completed;
115                 do {
116                         if (started < bufs &&
117                             started - completed < max_outstanding) {
118                                 r = add_inbuf(0, "Buffer\n", "Hello, world!");
119                                 if (__builtin_expect(r == 0, true)) {
120                                         ++started;
121                                         if (!--tokick) {
122                                                 tokick = batch;
123                                                 if (do_sleep)
124                                                         kick_available();
125                                         }
126
127                                 }
128                         } else
129                                 r = -1;
130
131                         /* Flush out completed bufs if any */
132                         if (get_buf(&len, &buf)) {
133                                 ++completed;
134                                 if (__builtin_expect(completed == bufs, false))
135                                         return;
136                                 r = 0;
137                         }
138                 } while (r == 0);
139                 if (completed == completed_before)
140                         ++spurious;
141                 assert(completed <= bufs);
142                 assert(started <= bufs);
143                 if (do_sleep) {
144                         if (enable_call())
145                                 wait_for_call();
146                 } else {
147                         poll_used();
148                 }
149         }
150 }
151
152 static void run_host(void)
153 {
154         int completed_before;
155         int completed = 0;
156         int spurious = 0;
157         int bufs = runcycles;
158         unsigned len;
159         void *buf;
160
161         for (;;) {
162                 if (do_sleep) {
163                         if (enable_kick())
164                                 wait_for_kick();
165                 } else {
166                         poll_avail();
167                 }
168                 if (do_sleep)
169                         disable_kick();
170                 completed_before = completed;
171                 while (__builtin_expect(use_buf(&len, &buf), true)) {
172                         if (do_sleep)
173                                 call_used();
174                         ++completed;
175                         if (__builtin_expect(completed == bufs, false))
176                                 return;
177                 }
178                 if (completed == completed_before)
179                         ++spurious;
180                 assert(completed <= bufs);
181                 if (completed == bufs)
182                         break;
183         }
184 }
185
186 void *start_guest(void *arg)
187 {
188         set_affinity(arg);
189         run_guest();
190         pthread_exit(NULL);
191 }
192
193 void *start_host(void *arg)
194 {
195         set_affinity(arg);
196         run_host();
197         pthread_exit(NULL);
198 }
199
200 static const char optstring[] = "";
201 static const struct option longopts[] = {
202         {
203                 .name = "help",
204                 .has_arg = no_argument,
205                 .val = 'h',
206         },
207         {
208                 .name = "host-affinity",
209                 .has_arg = required_argument,
210                 .val = 'H',
211         },
212         {
213                 .name = "guest-affinity",
214                 .has_arg = required_argument,
215                 .val = 'G',
216         },
217         {
218                 .name = "ring-size",
219                 .has_arg = required_argument,
220                 .val = 'R',
221         },
222         {
223                 .name = "run-cycles",
224                 .has_arg = required_argument,
225                 .val = 'C',
226         },
227         {
228                 .name = "outstanding",
229                 .has_arg = required_argument,
230                 .val = 'o',
231         },
232         {
233                 .name = "batch",
234                 .has_arg = required_argument,
235                 .val = 'b',
236         },
237         {
238                 .name = "sleep",
239                 .has_arg = no_argument,
240                 .val = 's',
241         },
242         {
243                 .name = "relax",
244                 .has_arg = no_argument,
245                 .val = 'x',
246         },
247         {
248                 .name = "exit",
249                 .has_arg = no_argument,
250                 .val = 'e',
251         },
252         {
253         }
254 };
255
256 static void help(void)
257 {
258         fprintf(stderr, "Usage: <test> [--help]"
259                 " [--host-affinity H]"
260                 " [--guest-affinity G]"
261                 " [--ring-size R (default: %d)]"
262                 " [--run-cycles C (default: %d)]"
263                 " [--batch b]"
264                 " [--outstanding o]"
265                 " [--sleep]"
266                 " [--relax]"
267                 " [--exit]"
268                 "\n",
269                 ring_size,
270                 runcycles);
271 }
272
273 int main(int argc, char **argv)
274 {
275         int ret;
276         pthread_t host, guest;
277         void *tret;
278         char *host_arg = NULL;
279         char *guest_arg = NULL;
280         char *endptr;
281         long int c;
282
283         kickfd = eventfd(0, 0);
284         assert(kickfd >= 0);
285         callfd = eventfd(0, 0);
286         assert(callfd >= 0);
287
288         for (;;) {
289                 int o = getopt_long(argc, argv, optstring, longopts, NULL);
290                 switch (o) {
291                 case -1:
292                         goto done;
293                 case '?':
294                         help();
295                         exit(2);
296                 case 'H':
297                         host_arg = optarg;
298                         break;
299                 case 'G':
300                         guest_arg = optarg;
301                         break;
302                 case 'R':
303                         ring_size = strtol(optarg, &endptr, 0);
304                         assert(ring_size && !(ring_size & (ring_size - 1)));
305                         assert(!*endptr);
306                         break;
307                 case 'C':
308                         c = strtol(optarg, &endptr, 0);
309                         assert(!*endptr);
310                         assert(c > 0 && c < INT_MAX);
311                         runcycles = c;
312                         break;
313                 case 'o':
314                         c = strtol(optarg, &endptr, 0);
315                         assert(!*endptr);
316                         assert(c > 0 && c < INT_MAX);
317                         max_outstanding = c;
318                         break;
319                 case 'b':
320                         c = strtol(optarg, &endptr, 0);
321                         assert(!*endptr);
322                         assert(c > 0 && c < INT_MAX);
323                         batch = c;
324                         break;
325                 case 's':
326                         do_sleep = true;
327                         break;
328                 case 'x':
329                         do_relax = true;
330                         break;
331                 case 'e':
332                         do_exit = true;
333                         break;
334                 default:
335                         help();
336                         exit(4);
337                         break;
338                 }
339         }
340
341         /* does nothing here, used to make sure all smp APIs compile */
342         smp_acquire();
343         smp_release();
344         smp_mb();
345 done:
346
347         if (batch > max_outstanding)
348                 batch = max_outstanding;
349
350         if (optind < argc) {
351                 help();
352                 exit(4);
353         }
354         alloc_ring();
355
356         ret = pthread_create(&host, NULL, start_host, host_arg);
357         assert(!ret);
358         ret = pthread_create(&guest, NULL, start_guest, guest_arg);
359         assert(!ret);
360
361         ret = pthread_join(guest, &tret);
362         assert(!ret);
363         ret = pthread_join(host, &tret);
364         assert(!ret);
365         return 0;
366 }