-
-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tools/virtio: add ringtest utilities
This adds micro-benchmarks useful for tuning virtio ring layouts. Three layouts are currently implemented: - virtio 0.9 compatible one - an experimental extension bypassing the ring index, polling ring itself instead - an experimental extension bypassing avail and used ring completely Typical use: sh run-on-all.sh perf stat -r 10 --log-fd 1 -- ./ring It doesn't depend on the kernel directly, but it's handy to have as much virtio stuff as possible in one tree. Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
- Loading branch information
Showing
8 changed files
with
1,123 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
all: | ||
|
||
all: ring virtio_ring_0_9 virtio_ring_poll | ||
|
||
CFLAGS += -Wall | ||
CFLAGS += -pthread -O2 -ggdb | ||
LDFLAGS += -pthread -O2 -ggdb | ||
|
||
main.o: main.c main.h | ||
ring.o: ring.c main.h | ||
virtio_ring_0_9.o: virtio_ring_0_9.c main.h | ||
virtio_ring_poll.o: virtio_ring_poll.c virtio_ring_0_9.c main.h | ||
ring: ring.o main.o | ||
virtio_ring_0_9: virtio_ring_0_9.o main.o | ||
virtio_ring_poll: virtio_ring_poll.o main.o | ||
clean: | ||
-rm main.o | ||
-rm ring.o ring | ||
-rm virtio_ring_0_9.o virtio_ring_0_9 | ||
-rm virtio_ring_poll.o virtio_ring_poll | ||
|
||
.PHONY: all clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Partial implementation of various ring layouts, useful to tune virtio design. | ||
Uses shared memory heavily. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,366 @@ | ||
/* | ||
* 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; | ||
} |
Oops, something went wrong.