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