|  | /* | 
|  | * Copyright (C) 2016 Red Hat, Inc. | 
|  | * Author: Michael S. Tsirkin <mst@redhat.com> | 
|  | * This work is licensed under the terms of the GNU GPL, version 2. | 
|  | * | 
|  | * Command line processing and common functions for ring benchmarking. | 
|  | */ | 
|  | #define _GNU_SOURCE | 
|  | #include <getopt.h> | 
|  | #include <pthread.h> | 
|  | #include <assert.h> | 
|  | #include <sched.h> | 
|  | #include "main.h" | 
|  | #include <sys/eventfd.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <unistd.h> | 
|  | #include <limits.h> | 
|  |  | 
|  | int runcycles = 10000000; | 
|  | int max_outstanding = INT_MAX; | 
|  | int batch = 1; | 
|  | int param = 0; | 
|  |  | 
|  | bool do_sleep = false; | 
|  | bool do_relax = false; | 
|  | bool do_exit = true; | 
|  |  | 
|  | unsigned ring_size = 256; | 
|  |  | 
|  | static int kickfd = -1; | 
|  | static int callfd = -1; | 
|  |  | 
|  | void notify(int fd) | 
|  | { | 
|  | unsigned long long v = 1; | 
|  | int r; | 
|  |  | 
|  | vmexit(); | 
|  | r = write(fd, &v, sizeof v); | 
|  | assert(r == sizeof v); | 
|  | vmentry(); | 
|  | } | 
|  |  | 
|  | void wait_for_notify(int fd) | 
|  | { | 
|  | unsigned long long v = 1; | 
|  | int r; | 
|  |  | 
|  | vmexit(); | 
|  | r = read(fd, &v, sizeof v); | 
|  | assert(r == sizeof v); | 
|  | vmentry(); | 
|  | } | 
|  |  | 
|  | void kick(void) | 
|  | { | 
|  | notify(kickfd); | 
|  | } | 
|  |  | 
|  | void wait_for_kick(void) | 
|  | { | 
|  | wait_for_notify(kickfd); | 
|  | } | 
|  |  | 
|  | void call(void) | 
|  | { | 
|  | notify(callfd); | 
|  | } | 
|  |  | 
|  | void wait_for_call(void) | 
|  | { | 
|  | wait_for_notify(callfd); | 
|  | } | 
|  |  | 
|  | void set_affinity(const char *arg) | 
|  | { | 
|  | cpu_set_t cpuset; | 
|  | int ret; | 
|  | pthread_t self; | 
|  | long int cpu; | 
|  | char *endptr; | 
|  |  | 
|  | if (!arg) | 
|  | return; | 
|  |  | 
|  | cpu = strtol(arg, &endptr, 0); | 
|  | assert(!*endptr); | 
|  |  | 
|  | assert(cpu >= 0 && cpu < CPU_SETSIZE); | 
|  |  | 
|  | self = pthread_self(); | 
|  | CPU_ZERO(&cpuset); | 
|  | CPU_SET(cpu, &cpuset); | 
|  |  | 
|  | ret = pthread_setaffinity_np(self, sizeof(cpu_set_t), &cpuset); | 
|  | assert(!ret); | 
|  | } | 
|  |  | 
|  | void poll_used(void) | 
|  | { | 
|  | while (used_empty()) | 
|  | busy_wait(); | 
|  | } | 
|  |  | 
|  | static void __attribute__((__flatten__)) run_guest(void) | 
|  | { | 
|  | int completed_before; | 
|  | int completed = 0; | 
|  | int started = 0; | 
|  | int bufs = runcycles; | 
|  | int spurious = 0; | 
|  | int r; | 
|  | unsigned len; | 
|  | void *buf; | 
|  | int tokick = batch; | 
|  |  | 
|  | for (;;) { | 
|  | if (do_sleep) | 
|  | disable_call(); | 
|  | completed_before = completed; | 
|  | do { | 
|  | if (started < bufs && | 
|  | started - completed < max_outstanding) { | 
|  | r = add_inbuf(0, "Buffer\n", "Hello, world!"); | 
|  | if (__builtin_expect(r == 0, true)) { | 
|  | ++started; | 
|  | if (!--tokick) { | 
|  | tokick = batch; | 
|  | if (do_sleep) | 
|  | kick_available(); | 
|  | } | 
|  |  | 
|  | } | 
|  | } else | 
|  | r = -1; | 
|  |  | 
|  | /* Flush out completed bufs if any */ | 
|  | if (get_buf(&len, &buf)) { | 
|  | ++completed; | 
|  | if (__builtin_expect(completed == bufs, false)) | 
|  | return; | 
|  | r = 0; | 
|  | } | 
|  | } while (r == 0); | 
|  | if (completed == completed_before) | 
|  | ++spurious; | 
|  | assert(completed <= bufs); | 
|  | assert(started <= bufs); | 
|  | if (do_sleep) { | 
|  | if (used_empty() && enable_call()) | 
|  | wait_for_call(); | 
|  | } else { | 
|  | poll_used(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void poll_avail(void) | 
|  | { | 
|  | while (avail_empty()) | 
|  | busy_wait(); | 
|  | } | 
|  |  | 
|  | static void __attribute__((__flatten__)) run_host(void) | 
|  | { | 
|  | int completed_before; | 
|  | int completed = 0; | 
|  | int spurious = 0; | 
|  | int bufs = runcycles; | 
|  | unsigned len; | 
|  | void *buf; | 
|  |  | 
|  | for (;;) { | 
|  | if (do_sleep) { | 
|  | if (avail_empty() && enable_kick()) | 
|  | wait_for_kick(); | 
|  | } else { | 
|  | poll_avail(); | 
|  | } | 
|  | if (do_sleep) | 
|  | disable_kick(); | 
|  | completed_before = completed; | 
|  | while (__builtin_expect(use_buf(&len, &buf), true)) { | 
|  | if (do_sleep) | 
|  | call_used(); | 
|  | ++completed; | 
|  | if (__builtin_expect(completed == bufs, false)) | 
|  | return; | 
|  | } | 
|  | if (completed == completed_before) | 
|  | ++spurious; | 
|  | assert(completed <= bufs); | 
|  | if (completed == bufs) | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void *start_guest(void *arg) | 
|  | { | 
|  | set_affinity(arg); | 
|  | run_guest(); | 
|  | pthread_exit(NULL); | 
|  | } | 
|  |  | 
|  | void *start_host(void *arg) | 
|  | { | 
|  | set_affinity(arg); | 
|  | run_host(); | 
|  | pthread_exit(NULL); | 
|  | } | 
|  |  | 
|  | static const char optstring[] = ""; | 
|  | static const struct option longopts[] = { | 
|  | { | 
|  | .name = "help", | 
|  | .has_arg = no_argument, | 
|  | .val = 'h', | 
|  | }, | 
|  | { | 
|  | .name = "host-affinity", | 
|  | .has_arg = required_argument, | 
|  | .val = 'H', | 
|  | }, | 
|  | { | 
|  | .name = "guest-affinity", | 
|  | .has_arg = required_argument, | 
|  | .val = 'G', | 
|  | }, | 
|  | { | 
|  | .name = "ring-size", | 
|  | .has_arg = required_argument, | 
|  | .val = 'R', | 
|  | }, | 
|  | { | 
|  | .name = "run-cycles", | 
|  | .has_arg = required_argument, | 
|  | .val = 'C', | 
|  | }, | 
|  | { | 
|  | .name = "outstanding", | 
|  | .has_arg = required_argument, | 
|  | .val = 'o', | 
|  | }, | 
|  | { | 
|  | .name = "batch", | 
|  | .has_arg = required_argument, | 
|  | .val = 'b', | 
|  | }, | 
|  | { | 
|  | .name = "param", | 
|  | .has_arg = required_argument, | 
|  | .val = 'p', | 
|  | }, | 
|  | { | 
|  | .name = "sleep", | 
|  | .has_arg = no_argument, | 
|  | .val = 's', | 
|  | }, | 
|  | { | 
|  | .name = "relax", | 
|  | .has_arg = no_argument, | 
|  | .val = 'x', | 
|  | }, | 
|  | { | 
|  | .name = "exit", | 
|  | .has_arg = no_argument, | 
|  | .val = 'e', | 
|  | }, | 
|  | { | 
|  | } | 
|  | }; | 
|  |  | 
|  | static void help(void) | 
|  | { | 
|  | fprintf(stderr, "Usage: <test> [--help]" | 
|  | " [--host-affinity H]" | 
|  | " [--guest-affinity G]" | 
|  | " [--ring-size R (default: %d)]" | 
|  | " [--run-cycles C (default: %d)]" | 
|  | " [--batch b]" | 
|  | " [--outstanding o]" | 
|  | " [--param p]" | 
|  | " [--sleep]" | 
|  | " [--relax]" | 
|  | " [--exit]" | 
|  | "\n", | 
|  | ring_size, | 
|  | runcycles); | 
|  | } | 
|  |  | 
|  | int main(int argc, char **argv) | 
|  | { | 
|  | int ret; | 
|  | pthread_t host, guest; | 
|  | void *tret; | 
|  | char *host_arg = NULL; | 
|  | char *guest_arg = NULL; | 
|  | char *endptr; | 
|  | long int c; | 
|  |  | 
|  | kickfd = eventfd(0, 0); | 
|  | assert(kickfd >= 0); | 
|  | callfd = eventfd(0, 0); | 
|  | assert(callfd >= 0); | 
|  |  | 
|  | for (;;) { | 
|  | int o = getopt_long(argc, argv, optstring, longopts, NULL); | 
|  | switch (o) { | 
|  | case -1: | 
|  | goto done; | 
|  | case '?': | 
|  | help(); | 
|  | exit(2); | 
|  | case 'H': | 
|  | host_arg = optarg; | 
|  | break; | 
|  | case 'G': | 
|  | guest_arg = optarg; | 
|  | break; | 
|  | case 'R': | 
|  | ring_size = strtol(optarg, &endptr, 0); | 
|  | assert(ring_size && !(ring_size & (ring_size - 1))); | 
|  | assert(!*endptr); | 
|  | break; | 
|  | case 'C': | 
|  | c = strtol(optarg, &endptr, 0); | 
|  | assert(!*endptr); | 
|  | assert(c > 0 && c < INT_MAX); | 
|  | runcycles = c; | 
|  | break; | 
|  | case 'o': | 
|  | c = strtol(optarg, &endptr, 0); | 
|  | assert(!*endptr); | 
|  | assert(c > 0 && c < INT_MAX); | 
|  | max_outstanding = c; | 
|  | break; | 
|  | case 'p': | 
|  | c = strtol(optarg, &endptr, 0); | 
|  | assert(!*endptr); | 
|  | assert(c > 0 && c < INT_MAX); | 
|  | param = c; | 
|  | break; | 
|  | case 'b': | 
|  | c = strtol(optarg, &endptr, 0); | 
|  | assert(!*endptr); | 
|  | assert(c > 0 && c < INT_MAX); | 
|  | batch = c; | 
|  | break; | 
|  | case 's': | 
|  | do_sleep = true; | 
|  | break; | 
|  | case 'x': | 
|  | do_relax = true; | 
|  | break; | 
|  | case 'e': | 
|  | do_exit = true; | 
|  | break; | 
|  | default: | 
|  | help(); | 
|  | exit(4); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* does nothing here, used to make sure all smp APIs compile */ | 
|  | smp_acquire(); | 
|  | smp_release(); | 
|  | smp_mb(); | 
|  | done: | 
|  |  | 
|  | if (batch > max_outstanding) | 
|  | batch = max_outstanding; | 
|  |  | 
|  | if (optind < argc) { | 
|  | help(); | 
|  | exit(4); | 
|  | } | 
|  | alloc_ring(); | 
|  |  | 
|  | ret = pthread_create(&host, NULL, start_host, host_arg); | 
|  | assert(!ret); | 
|  | ret = pthread_create(&guest, NULL, start_guest, guest_arg); | 
|  | assert(!ret); | 
|  |  | 
|  | ret = pthread_join(guest, &tret); | 
|  | assert(!ret); | 
|  | ret = pthread_join(host, &tret); | 
|  | assert(!ret); | 
|  | return 0; | 
|  | } |