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