GNU Linux-libre 5.10.219-gnu1
[releases.git] / tools / virtio / ringtest / main.c
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
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
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
56 void kick(void)
57 {
58         notify(kickfd);
59 }
60
61 void wait_for_kick(void)
62 {
63         wait_for_notify(kickfd);
64 }
65
66 void call(void)
67 {
68         notify(callfd);
69 }
70
71 void wait_for_call(void)
72 {
73         wait_for_notify(callfd);
74 }
75
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
100 void poll_used(void)
101 {
102         while (used_empty())
103                 busy_wait();
104 }
105
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
159 void poll_avail(void)
160 {
161         while (avail_empty())
162                 busy_wait();
163 }
164
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
199 void *start_guest(void *arg)
200 {
201         set_affinity(arg);
202         run_guest();
203         pthread_exit(NULL);
204 }
205
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
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
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 }