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(fd: kickfd); |
59 | } |
60 | |
61 | void wait_for_kick(void) |
62 | { |
63 | wait_for_notify(fd: kickfd); |
64 | } |
65 | |
66 | void call(void) |
67 | { |
68 | notify(fd: callfd); |
69 | } |
70 | |
71 | void wait_for_call(void) |
72 | { |
73 | wait_for_notify(fd: 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 | } |
392 | |