2019-06-04 11:11:15 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-01-21 15:44:10 +03:00
/*
* Copyright ( C ) 2016 Red Hat , Inc .
* Author : Michael S . Tsirkin < mst @ redhat . com >
*
* 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 ;
2017-04-07 08:44:23 +03:00
int param = 0 ;
2016-01-21 15:44:10 +03:00
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 ) ;
2017-04-15 22:22:14 +03:00
assert ( cpu > = 0 & & cpu < CPU_SETSIZE ) ;
2016-01-21 15:44:10 +03:00
self = pthread_self ( ) ;
CPU_ZERO ( & cpuset ) ;
CPU_SET ( cpu , & cpuset ) ;
ret = pthread_setaffinity_np ( self , sizeof ( cpu_set_t ) , & cpuset ) ;
assert ( ! ret ) ;
}
2016-10-06 12:39:11 +03:00
void poll_used ( void )
{
while ( used_empty ( ) )
busy_wait ( ) ;
}
2016-10-06 12:39:10 +03:00
static void __attribute__ ( ( __flatten__ ) ) run_guest ( void )
2016-01-21 15:44:10 +03:00
{
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 ) {
2016-05-22 15:10:49 +03:00
r = add_inbuf ( 0 , " Buffer \n " , " Hello, world! " ) ;
2016-01-21 15:44:10 +03:00
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 ) {
2016-10-06 12:39:12 +03:00
if ( used_empty ( ) & & enable_call ( ) )
2016-01-21 15:44:10 +03:00
wait_for_call ( ) ;
} else {
poll_used ( ) ;
}
}
}
2016-10-06 12:39:11 +03:00
void poll_avail ( void )
{
while ( avail_empty ( ) )
busy_wait ( ) ;
}
2016-10-06 12:39:10 +03:00
static void __attribute__ ( ( __flatten__ ) ) run_host ( void )
2016-01-21 15:44:10 +03:00
{
int completed_before ;
int completed = 0 ;
int spurious = 0 ;
int bufs = runcycles ;
unsigned len ;
void * buf ;
for ( ; ; ) {
if ( do_sleep ) {
2016-10-06 12:39:12 +03:00
if ( avail_empty ( ) & & enable_kick ( ) )
2016-01-21 15:44:10 +03:00
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 ' ,
} ,
2017-04-07 08:44:23 +03:00
{
. name = " param " ,
. has_arg = required_argument ,
. val = ' p ' ,
} ,
2016-01-21 15:44:10 +03:00
{
. 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] "
2017-04-07 08:44:23 +03:00
" [--param p] "
2016-01-21 15:44:10 +03:00
" [--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 ;
2017-04-07 08:44:23 +03:00
case ' p ' :
c = strtol ( optarg , & endptr , 0 ) ;
assert ( ! * endptr ) ;
assert ( c > 0 & & c < INT_MAX ) ;
param = c ;
break ;
2016-01-21 15:44:10 +03:00
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 ;
}