367 lines
6 KiB
C
367 lines
6 KiB
C
![]() |
/*
|
||
|
* 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;
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
static void 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, NULL, "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 (enable_call())
|
||
|
wait_for_call();
|
||
|
} else {
|
||
|
poll_used();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void run_host(void)
|
||
|
{
|
||
|
int completed_before;
|
||
|
int completed = 0;
|
||
|
int spurious = 0;
|
||
|
int bufs = runcycles;
|
||
|
unsigned len;
|
||
|
void *buf;
|
||
|
|
||
|
for (;;) {
|
||
|
if (do_sleep) {
|
||
|
if (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 = "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]"
|
||
|
" [--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 '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;
|
||
|
}
|