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 } 392