392 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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_SOURCE9#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 20int runcycles = 10000000;21int max_outstanding = INT_MAX;22int batch = 1;23int param = 0;24 25bool do_sleep = false;26bool do_relax = false;27bool do_exit = true;28 29unsigned ring_size = 256;30 31static int kickfd = -1;32static int callfd = -1;33 34void 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 45void 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 56void kick(void)57{58 notify(kickfd);59}60 61void wait_for_kick(void)62{63 wait_for_notify(kickfd);64}65 66void call(void)67{68 notify(callfd);69}70 71void wait_for_call(void)72{73 wait_for_notify(callfd);74}75 76void 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 100void poll_used(void)101{102 while (used_empty())103 busy_wait();104}105 106static 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 } else136 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 159void poll_avail(void)160{161 while (avail_empty())162 busy_wait();163}164 165static 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 199void *start_guest(void *arg)200{201 set_affinity(arg);202 run_guest();203 pthread_exit(NULL);204}205 206void *start_host(void *arg)207{208 set_affinity(arg);209 run_host();210 pthread_exit(NULL);211}212 213static const char optstring[] = "";214static 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 274static void help(void)275{276 fprintf(stderr, "Usage: <test> [--help]"277 " [--host-affinity H]"278 " [--guest-affinity G]"279 " [--ring-size R (default: %u)]"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 292int 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();370done: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