2012-06-01 03:26:37 +04:00
/*
* This application is Copyright 2012 Red Hat , Inc .
* Doug Ledford < dledford @ redhat . com >
*
* mq_perf_tests is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , version 3.
*
* mq_perf_tests is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* For the full text of the license , see < http : //www.gnu.org/licenses/>.
*
* mq_perf_tests . c
* Tests various types of message queue workloads , concentrating on those
* situations that invole large message sizes , large message queue depths ,
* or both , and reports back useful metrics about kernel message queue
* performance .
*
*/
# define _GNU_SOURCE
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <fcntl.h>
# include <string.h>
# include <limits.h>
# include <errno.h>
# include <signal.h>
# include <pthread.h>
# include <sched.h>
# include <sys/types.h>
# include <sys/time.h>
# include <sys/resource.h>
# include <sys/stat.h>
2022-04-08 16:36:24 +03:00
# include <sys/param.h>
2012-06-01 03:26:37 +04:00
# include <mqueue.h>
# include <popt.h>
2015-11-02 15:21:46 +03:00
# include <error.h>
2012-06-01 03:26:37 +04:00
2018-05-04 23:26:10 +03:00
# include "../kselftest.h"
2012-06-01 03:26:37 +04:00
static char * usage =
" Usage: \n "
" %s [-c #[,#..] -f] path \n "
" \n "
" -c # Skip most tests and go straight to a high queue depth test \n "
" and then run that test continuously (useful for running at \n "
" the same time as some other workload to see how much the \n "
" cache thrashing caused by adding messages to a very deep \n "
" queue impacts the performance of other programs). The number \n "
" indicates which CPU core we should bind the process to during \n "
" the run. If you have more than one physical CPU, then you \n "
" will need one copy per physical CPU package, and you should \n "
" specify the CPU cores to pin ourself to via a comma separated \n "
" list of CPU values. \n "
" -f Only usable with continuous mode. Pin ourself to the CPUs \n "
" as requested, then instead of looping doing a high mq \n "
" workload, just busy loop. This will allow us to lock up a \n "
" single CPU just like we normally would, but without actually \n "
" thrashing the CPU cache. This is to make it easier to get \n "
" comparable numbers from some other workload running on the \n "
" other CPUs. One set of numbers with # CPUs locked up running \n "
" an mq workload, and another set of numbers with those same \n "
" CPUs locked away from the test workload, but not doing \n "
" anything to trash the cache like the mq workload might. \n "
" path Path name of the message queue to create \n "
" \n "
" Note: this program must be run as root in order to enable all tests \n "
" \n " ;
char * MAX_MSGS = " /proc/sys/fs/mqueue/msg_max " ;
char * MAX_MSGSIZE = " /proc/sys/fs/mqueue/msgsize_max " ;
# define MAX_CPUS 64
char * cpu_option_string ;
int cpus_to_pin [ MAX_CPUS ] ;
int num_cpus_to_pin ;
pthread_t cpu_threads [ MAX_CPUS ] ;
pthread_t main_thread ;
cpu_set_t * cpu_set ;
int cpu_set_size ;
int cpus_online ;
# define MSG_SIZE 16
# define TEST1_LOOPS 10000000
# define TEST2_LOOPS 100000
int continuous_mode ;
int continuous_mode_fake ;
struct rlimit saved_limits , cur_limits ;
int saved_max_msgs , saved_max_msgsize ;
int cur_max_msgs , cur_max_msgsize ;
FILE * max_msgs , * max_msgsize ;
int cur_nice ;
char * queue_path = " /mq_perf_tests " ;
mqd_t queue = - 1 ;
struct mq_attr result ;
int mq_prio_max ;
const struct poptOption options [ ] = {
{
. longName = " continuous " ,
. shortName = ' c ' ,
. argInfo = POPT_ARG_STRING ,
. arg = & cpu_option_string ,
. val = ' c ' ,
. descrip = " Run continuous tests at a high queue depth in "
" order to test the effects of cache thrashing on "
" other tasks on the system. This test is intended "
" to be run on one core of each physical CPU while "
" some other CPU intensive task is run on all the other "
" cores of that same physical CPU and the other task "
" is timed. It is assumed that the process of adding "
" messages to the message queue in a tight loop will "
" impact that other task to some degree. Once the "
" tests are performed in this way, you should then "
" re-run the tests using fake mode in order to check "
" the difference in time required to perform the CPU "
" intensive task " ,
. argDescrip = " cpu[,cpu] " ,
} ,
{
. longName = " fake " ,
. shortName = ' f ' ,
. argInfo = POPT_ARG_NONE ,
. arg = & continuous_mode_fake ,
. val = 0 ,
. descrip = " Tie up the CPUs that we would normally tie up in "
" continuous mode, but don't actually do any mq stuff, "
" just keep the CPU busy so it can't be used to process "
" system level tasks as this would free up resources on "
" the other CPU cores and skew the comparison between "
" the no-mqueue work and mqueue work tests " ,
. argDescrip = NULL ,
} ,
{
. longName = " path " ,
. shortName = ' p ' ,
. argInfo = POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT ,
. arg = & queue_path ,
. val = ' p ' ,
. descrip = " The name of the path to use in the mqueue "
" filesystem for our tests " ,
. argDescrip = " pathname " ,
} ,
POPT_AUTOHELP
POPT_TABLEEND
} ;
static inline void __set ( FILE * stream , int value , char * err_msg ) ;
void shutdown ( int exit_val , char * err_cause , int line_no ) ;
void sig_action_SIGUSR1 ( int signum , siginfo_t * info , void * context ) ;
void sig_action ( int signum , siginfo_t * info , void * context ) ;
static inline int get ( FILE * stream ) ;
static inline void set ( FILE * stream , int value ) ;
static inline int try_set ( FILE * stream , int value ) ;
static inline void getr ( int type , struct rlimit * rlim ) ;
static inline void setr ( int type , struct rlimit * rlim ) ;
static inline void open_queue ( struct mq_attr * attr ) ;
void increase_limits ( void ) ;
static inline void __set ( FILE * stream , int value , char * err_msg )
{
rewind ( stream ) ;
if ( fprintf ( stream , " %d " , value ) < 0 )
perror ( err_msg ) ;
}
void shutdown ( int exit_val , char * err_cause , int line_no )
{
static int in_shutdown = 0 ;
int errno_at_shutdown = errno ;
int i ;
/* In case we get called by multiple threads or from an sighandler */
if ( in_shutdown + + )
return ;
2022-04-08 10:24:31 +03:00
/* Free the cpu_set allocated using CPU_ALLOC in main function */
CPU_FREE ( cpu_set ) ;
2012-06-01 03:26:37 +04:00
for ( i = 0 ; i < num_cpus_to_pin ; i + + )
if ( cpu_threads [ i ] ) {
pthread_kill ( cpu_threads [ i ] , SIGUSR1 ) ;
pthread_join ( cpu_threads [ i ] , NULL ) ;
}
if ( queue ! = - 1 )
if ( mq_close ( queue ) )
perror ( " mq_close() during shutdown " ) ;
if ( queue_path )
/*
* Be silent if this fails , if we cleaned up already it ' s
* expected to fail
*/
mq_unlink ( queue_path ) ;
if ( saved_max_msgs )
__set ( max_msgs , saved_max_msgs ,
" failed to restore saved_max_msgs " ) ;
if ( saved_max_msgsize )
__set ( max_msgsize , saved_max_msgsize ,
" failed to restore saved_max_msgsize " ) ;
if ( exit_val )
error ( exit_val , errno_at_shutdown , " %s at %d " ,
err_cause , line_no ) ;
exit ( 0 ) ;
}
void sig_action_SIGUSR1 ( int signum , siginfo_t * info , void * context )
{
if ( pthread_self ( ) ! = main_thread )
pthread_exit ( 0 ) ;
else {
fprintf ( stderr , " Caught signal %d in SIGUSR1 handler, "
" exiting \n " , signum ) ;
shutdown ( 0 , " " , 0 ) ;
fprintf ( stderr , " \n \n Returned from shutdown?!?! \n \n " ) ;
exit ( 0 ) ;
}
}
void sig_action ( int signum , siginfo_t * info , void * context )
{
if ( pthread_self ( ) ! = main_thread )
pthread_kill ( main_thread , signum ) ;
else {
fprintf ( stderr , " Caught signal %d, exiting \n " , signum ) ;
shutdown ( 0 , " " , 0 ) ;
fprintf ( stderr , " \n \n Returned from shutdown?!?! \n \n " ) ;
exit ( 0 ) ;
}
}
static inline int get ( FILE * stream )
{
int value ;
rewind ( stream ) ;
if ( fscanf ( stream , " %d " , & value ) ! = 1 )
shutdown ( 4 , " Error reading /proc entry " , __LINE__ ) ;
return value ;
}
static inline void set ( FILE * stream , int value )
{
int new_value ;
rewind ( stream ) ;
if ( fprintf ( stream , " %d " , value ) < 0 )
return shutdown ( 5 , " Failed writing to /proc file " , __LINE__ ) ;
new_value = get ( stream ) ;
if ( new_value ! = value )
return shutdown ( 5 , " We didn't get what we wrote to /proc back " ,
__LINE__ ) ;
}
static inline int try_set ( FILE * stream , int value )
{
int new_value ;
rewind ( stream ) ;
fprintf ( stream , " %d " , value ) ;
new_value = get ( stream ) ;
return new_value = = value ;
}
static inline void getr ( int type , struct rlimit * rlim )
{
if ( getrlimit ( type , rlim ) )
shutdown ( 6 , " getrlimit() " , __LINE__ ) ;
}
static inline void setr ( int type , struct rlimit * rlim )
{
if ( setrlimit ( type , rlim ) )
shutdown ( 7 , " setrlimit() " , __LINE__ ) ;
}
/**
* open_queue - open the global queue for testing
* @ attr - An attr struct specifying the desired queue traits
* @ result - An attr struct that lists the actual traits the queue has
*
* This open is not allowed to fail , failure will result in an orderly
* shutdown of the program . The global queue_path is used to set what
* queue to open , the queue descriptor is saved in the global queue
* variable .
*/
static inline void open_queue ( struct mq_attr * attr )
{
int flags = O_RDWR | O_EXCL | O_CREAT | O_NONBLOCK ;
int perms = DEFFILEMODE ;
queue = mq_open ( queue_path , flags , perms , attr ) ;
if ( queue = = - 1 )
shutdown ( 1 , " mq_open() " , __LINE__ ) ;
if ( mq_getattr ( queue , & result ) )
shutdown ( 1 , " mq_getattr() " , __LINE__ ) ;
printf ( " \n \t Queue %s created: \n " , queue_path ) ;
printf ( " \t \t mq_flags: \t \t \t %s \n " , result . mq_flags & O_NONBLOCK ?
" O_NONBLOCK " : " (null) " ) ;
2014-06-25 20:05:17 +04:00
printf ( " \t \t mq_maxmsg: \t \t \t %lu \n " , result . mq_maxmsg ) ;
printf ( " \t \t mq_msgsize: \t \t \t %lu \n " , result . mq_msgsize ) ;
printf ( " \t \t mq_curmsgs: \t \t \t %lu \n " , result . mq_curmsgs ) ;
2012-06-01 03:26:37 +04:00
}
void * fake_cont_thread ( void * arg )
{
int i ;
for ( i = 0 ; i < num_cpus_to_pin ; i + + )
if ( cpu_threads [ i ] = = pthread_self ( ) )
break ;
printf ( " \t Started fake continuous mode thread %d on CPU %d \n " , i ,
cpus_to_pin [ i ] ) ;
while ( 1 )
;
}
void * cont_thread ( void * arg )
{
char buff [ MSG_SIZE ] ;
int i , priority ;
for ( i = 0 ; i < num_cpus_to_pin ; i + + )
if ( cpu_threads [ i ] = = pthread_self ( ) )
break ;
printf ( " \t Started continuous mode thread %d on CPU %d \n " , i ,
cpus_to_pin [ i ] ) ;
while ( 1 ) {
while ( mq_send ( queue , buff , sizeof ( buff ) , 0 ) = = 0 )
;
mq_receive ( queue , buff , sizeof ( buff ) , & priority ) ;
}
}
# define drain_queue() \
while ( mq_receive ( queue , buff , MSG_SIZE , & prio_in ) = = MSG_SIZE )
# define do_untimed_send() \
do { \
if ( mq_send ( queue , buff , MSG_SIZE , prio_out ) ) \
shutdown ( 3 , " Test send failure " , __LINE__ ) ; \
} while ( 0 )
# define do_send_recv() \
do { \
clock_gettime ( clock , & start ) ; \
if ( mq_send ( queue , buff , MSG_SIZE , prio_out ) ) \
shutdown ( 3 , " Test send failure " , __LINE__ ) ; \
clock_gettime ( clock , & middle ) ; \
if ( mq_receive ( queue , buff , MSG_SIZE , & prio_in ) ! = MSG_SIZE ) \
shutdown ( 3 , " Test receive failure " , __LINE__ ) ; \
clock_gettime ( clock , & end ) ; \
nsec = ( ( middle . tv_sec - start . tv_sec ) * 1000000000 ) + \
( middle . tv_nsec - start . tv_nsec ) ; \
send_total . tv_nsec + = nsec ; \
if ( send_total . tv_nsec > = 1000000000 ) { \
send_total . tv_sec + + ; \
send_total . tv_nsec - = 1000000000 ; \
} \
nsec = ( ( end . tv_sec - middle . tv_sec ) * 1000000000 ) + \
( end . tv_nsec - middle . tv_nsec ) ; \
recv_total . tv_nsec + = nsec ; \
if ( recv_total . tv_nsec > = 1000000000 ) { \
recv_total . tv_sec + + ; \
recv_total . tv_nsec - = 1000000000 ; \
} \
} while ( 0 )
struct test {
char * desc ;
void ( * func ) ( int * ) ;
} ;
void const_prio ( int * prio )
{
return ;
}
void inc_prio ( int * prio )
{
if ( + + * prio = = mq_prio_max )
* prio = 0 ;
}
void dec_prio ( int * prio )
{
if ( - - * prio < 0 )
* prio = mq_prio_max - 1 ;
}
void random_prio ( int * prio )
{
* prio = random ( ) % mq_prio_max ;
}
struct test test2 [ ] = {
{ " \n \t Test #2a: Time send/recv message, queue full, constant prio \n " ,
const_prio } ,
{ " \n \t Test #2b: Time send/recv message, queue full, increasing prio \n " ,
inc_prio } ,
{ " \n \t Test #2c: Time send/recv message, queue full, decreasing prio \n " ,
dec_prio } ,
{ " \n \t Test #2d: Time send/recv message, queue full, random prio \n " ,
random_prio } ,
{ NULL , NULL }
} ;
/**
* Tests to perform ( all done with MSG_SIZE messages ) :
*
* 1 ) Time to add / remove message with 0 messages on queue
* 1 a ) with constant prio
* 2 ) Time to add / remove message when queue close to capacity :
* 2 a ) with constant prio
* 2 b ) with increasing prio
* 2 c ) with decreasing prio
* 2 d ) with random prio
* 3 ) Test limits of priorities honored ( double check _SC_MQ_PRIO_MAX )
*/
void * perf_test_thread ( void * arg )
{
char buff [ MSG_SIZE ] ;
int prio_out , prio_in ;
int i ;
clockid_t clock ;
pthread_t * t ;
struct timespec res , start , middle , end , send_total , recv_total ;
unsigned long long nsec ;
struct test * cur_test ;
t = & cpu_threads [ 0 ] ;
printf ( " \n \t Started mqueue performance test thread on CPU %d \n " ,
cpus_to_pin [ 0 ] ) ;
mq_prio_max = sysconf ( _SC_MQ_PRIO_MAX ) ;
if ( mq_prio_max = = - 1 )
shutdown ( 2 , " sysconf(_SC_MQ_PRIO_MAX) " , __LINE__ ) ;
if ( pthread_getcpuclockid ( cpu_threads [ 0 ] , & clock ) ! = 0 )
shutdown ( 2 , " pthread_getcpuclockid " , __LINE__ ) ;
if ( clock_getres ( clock , & res ) )
shutdown ( 2 , " clock_getres() " , __LINE__ ) ;
printf ( " \t \t Max priorities: \t \t \t %d \n " , mq_prio_max ) ;
2014-06-25 20:05:17 +04:00
printf ( " \t \t Clock resolution: \t \t %lu nsec%s \n " , res . tv_nsec ,
2012-06-01 03:26:37 +04:00
res . tv_nsec > 1 ? " s " : " " ) ;
printf ( " \n \t Test #1: Time send/recv message, queue empty \n " ) ;
printf ( " \t \t (%d iterations) \n " , TEST1_LOOPS ) ;
prio_out = 0 ;
send_total . tv_sec = 0 ;
send_total . tv_nsec = 0 ;
recv_total . tv_sec = 0 ;
recv_total . tv_nsec = 0 ;
for ( i = 0 ; i < TEST1_LOOPS ; i + + )
do_send_recv ( ) ;
2014-06-25 20:05:17 +04:00
printf ( " \t \t Send msg: \t \t \t %ld.%lus total time \n " ,
2012-06-01 03:26:37 +04:00
send_total . tv_sec , send_total . tv_nsec ) ;
nsec = ( ( unsigned long long ) send_total . tv_sec * 1000000000 +
send_total . tv_nsec ) / TEST1_LOOPS ;
2014-06-25 20:05:17 +04:00
printf ( " \t \t \t \t \t \t %lld nsec/msg \n " , nsec ) ;
printf ( " \t \t Recv msg: \t \t \t %ld.%lus total time \n " ,
2012-06-01 03:26:37 +04:00
recv_total . tv_sec , recv_total . tv_nsec ) ;
nsec = ( ( unsigned long long ) recv_total . tv_sec * 1000000000 +
recv_total . tv_nsec ) / TEST1_LOOPS ;
2014-06-25 20:05:17 +04:00
printf ( " \t \t \t \t \t \t %lld nsec/msg \n " , nsec ) ;
2012-06-01 03:26:37 +04:00
for ( cur_test = test2 ; cur_test - > desc ! = NULL ; cur_test + + ) {
2014-06-25 20:05:17 +04:00
printf ( " %s: \n " , cur_test - > desc ) ;
2012-06-01 03:26:37 +04:00
printf ( " \t \t (%d iterations) \n " , TEST2_LOOPS ) ;
prio_out = 0 ;
send_total . tv_sec = 0 ;
send_total . tv_nsec = 0 ;
recv_total . tv_sec = 0 ;
recv_total . tv_nsec = 0 ;
printf ( " \t \t Filling queue... " ) ;
fflush ( stdout ) ;
clock_gettime ( clock , & start ) ;
for ( i = 0 ; i < result . mq_maxmsg - 1 ; i + + ) {
do_untimed_send ( ) ;
cur_test - > func ( & prio_out ) ;
}
clock_gettime ( clock , & end ) ;
nsec = ( ( unsigned long long ) ( end . tv_sec - start . tv_sec ) *
1000000000 ) + ( end . tv_nsec - start . tv_nsec ) ;
printf ( " done. \t \t %lld.%llds \n " , nsec / 1000000000 ,
nsec % 1000000000 ) ;
printf ( " \t \t Testing... " ) ;
fflush ( stdout ) ;
for ( i = 0 ; i < TEST2_LOOPS ; i + + ) {
do_send_recv ( ) ;
cur_test - > func ( & prio_out ) ;
}
printf ( " done. \n " ) ;
2014-06-25 20:05:17 +04:00
printf ( " \t \t Send msg: \t \t \t %ld.%lus total time \n " ,
2012-06-01 03:26:37 +04:00
send_total . tv_sec , send_total . tv_nsec ) ;
nsec = ( ( unsigned long long ) send_total . tv_sec * 1000000000 +
send_total . tv_nsec ) / TEST2_LOOPS ;
2014-06-25 20:05:17 +04:00
printf ( " \t \t \t \t \t \t %lld nsec/msg \n " , nsec ) ;
printf ( " \t \t Recv msg: \t \t \t %ld.%lus total time \n " ,
2012-06-01 03:26:37 +04:00
recv_total . tv_sec , recv_total . tv_nsec ) ;
nsec = ( ( unsigned long long ) recv_total . tv_sec * 1000000000 +
recv_total . tv_nsec ) / TEST2_LOOPS ;
2014-06-25 20:05:17 +04:00
printf ( " \t \t \t \t \t \t %lld nsec/msg \n " , nsec ) ;
2012-06-01 03:26:37 +04:00
printf ( " \t \t Draining queue... " ) ;
fflush ( stdout ) ;
clock_gettime ( clock , & start ) ;
drain_queue ( ) ;
clock_gettime ( clock , & end ) ;
nsec = ( ( unsigned long long ) ( end . tv_sec - start . tv_sec ) *
1000000000 ) + ( end . tv_nsec - start . tv_nsec ) ;
printf ( " done. \t \t %lld.%llds \n " , nsec / 1000000000 ,
nsec % 1000000000 ) ;
}
return 0 ;
}
void increase_limits ( void )
{
cur_limits . rlim_cur = RLIM_INFINITY ;
cur_limits . rlim_max = RLIM_INFINITY ;
setr ( RLIMIT_MSGQUEUE , & cur_limits ) ;
while ( try_set ( max_msgs , cur_max_msgs + = 10 ) )
;
cur_max_msgs = get ( max_msgs ) ;
while ( try_set ( max_msgsize , cur_max_msgsize + = 1024 ) )
;
cur_max_msgsize = get ( max_msgsize ) ;
if ( setpriority ( PRIO_PROCESS , 0 , - 20 ) ! = 0 )
shutdown ( 2 , " setpriority() " , __LINE__ ) ;
cur_nice = - 20 ;
}
int main ( int argc , char * argv [ ] )
{
struct mq_attr attr ;
char * option , * next_option ;
2015-01-06 05:54:40 +03:00
int i , cpu , rc ;
2012-06-01 03:26:37 +04:00
struct sigaction sa ;
poptContext popt_context ;
void * retval ;
main_thread = pthread_self ( ) ;
num_cpus_to_pin = 0 ;
if ( sysconf ( _SC_NPROCESSORS_ONLN ) = = - 1 ) {
perror ( " sysconf(_SC_NPROCESSORS_ONLN) " ) ;
exit ( 1 ) ;
}
2022-04-08 10:24:31 +03:00
if ( getuid ( ) ! = 0 )
ksft_exit_skip ( " Not running as root, but almost all tests "
" require root in order to modify \n system settings. "
" Exiting. \n " ) ;
2022-04-08 16:36:24 +03:00
cpus_online = MIN ( MAX_CPUS , sysconf ( _SC_NPROCESSORS_ONLN ) ) ;
2012-06-01 03:26:37 +04:00
cpu_set = CPU_ALLOC ( cpus_online ) ;
if ( cpu_set = = NULL ) {
perror ( " CPU_ALLOC() " ) ;
exit ( 1 ) ;
}
cpu_set_size = CPU_ALLOC_SIZE ( cpus_online ) ;
CPU_ZERO_S ( cpu_set_size , cpu_set ) ;
popt_context = poptGetContext ( NULL , argc , ( const char * * ) argv ,
options , 0 ) ;
while ( ( rc = poptGetNextOpt ( popt_context ) ) > 0 ) {
switch ( rc ) {
case ' c ' :
continuous_mode = 1 ;
option = cpu_option_string ;
do {
next_option = strchr ( option , ' , ' ) ;
if ( next_option )
* next_option = ' \0 ' ;
cpu = atoi ( option ) ;
if ( cpu > = cpus_online )
fprintf ( stderr , " CPU %d exceeds "
" cpus online, ignoring. \n " ,
cpu ) ;
else
cpus_to_pin [ num_cpus_to_pin + + ] = cpu ;
if ( next_option )
option = + + next_option ;
} while ( next_option & & num_cpus_to_pin < MAX_CPUS ) ;
/* Double check that they didn't give us the same CPU
* more than once */
for ( cpu = 0 ; cpu < num_cpus_to_pin ; cpu + + ) {
if ( CPU_ISSET_S ( cpus_to_pin [ cpu ] , cpu_set_size ,
cpu_set ) ) {
fprintf ( stderr , " Any given CPU may "
" only be given once. \n " ) ;
2022-04-08 10:24:31 +03:00
goto err_code ;
2012-06-01 03:26:37 +04:00
} else
CPU_SET_S ( cpus_to_pin [ cpu ] ,
cpu_set_size , cpu_set ) ;
}
break ;
case ' p ' :
/*
* Although we can create a msg queue with a
* non - absolute path name , unlink will fail . So ,
* if the name doesn ' t start with a / , add one
* when we save it .
*/
option = queue_path ;
if ( * option ! = ' / ' ) {
queue_path = malloc ( strlen ( option ) + 2 ) ;
if ( ! queue_path ) {
perror ( " malloc() " ) ;
2022-04-08 10:24:31 +03:00
goto err_code ;
2012-06-01 03:26:37 +04:00
}
queue_path [ 0 ] = ' / ' ;
queue_path [ 1 ] = 0 ;
strcat ( queue_path , option ) ;
free ( option ) ;
}
break ;
}
}
if ( continuous_mode & & num_cpus_to_pin = = 0 ) {
fprintf ( stderr , " Must pass at least one CPU to continuous "
" mode. \n " ) ;
poptPrintUsage ( popt_context , stderr , 0 ) ;
2022-04-08 10:24:31 +03:00
goto err_code ;
2012-06-01 03:26:37 +04:00
} else if ( ! continuous_mode ) {
num_cpus_to_pin = 1 ;
cpus_to_pin [ 0 ] = cpus_online - 1 ;
}
max_msgs = fopen ( MAX_MSGS , " r+ " ) ;
max_msgsize = fopen ( MAX_MSGSIZE , " r+ " ) ;
if ( ! max_msgs )
shutdown ( 2 , " Failed to open msg_max " , __LINE__ ) ;
if ( ! max_msgsize )
shutdown ( 2 , " Failed to open msgsize_max " , __LINE__ ) ;
/* Load up the current system values for everything we can */
getr ( RLIMIT_MSGQUEUE , & saved_limits ) ;
cur_limits = saved_limits ;
saved_max_msgs = cur_max_msgs = get ( max_msgs ) ;
saved_max_msgsize = cur_max_msgsize = get ( max_msgsize ) ;
errno = 0 ;
cur_nice = getpriority ( PRIO_PROCESS , 0 ) ;
if ( errno )
shutdown ( 2 , " getpriority() " , __LINE__ ) ;
/* Tell the user our initial state */
printf ( " \n Initial system state: \n " ) ;
printf ( " \t Using queue path: \t \t \t %s \n " , queue_path ) ;
2014-06-25 20:05:17 +04:00
printf ( " \t RLIMIT_MSGQUEUE(soft): \t \t \t %ld \n " ,
( long ) saved_limits . rlim_cur ) ;
printf ( " \t RLIMIT_MSGQUEUE(hard): \t \t \t %ld \n " ,
( long ) saved_limits . rlim_max ) ;
2012-06-01 03:26:37 +04:00
printf ( " \t Maximum Message Size: \t \t \t %d \n " , saved_max_msgsize ) ;
printf ( " \t Maximum Queue Size: \t \t \t %d \n " , saved_max_msgs ) ;
printf ( " \t Nice value: \t \t \t \t %d \n " , cur_nice ) ;
printf ( " \n " ) ;
increase_limits ( ) ;
printf ( " Adjusted system state for testing: \n " ) ;
if ( cur_limits . rlim_cur = = RLIM_INFINITY ) {
printf ( " \t RLIMIT_MSGQUEUE(soft): \t \t \t (unlimited) \n " ) ;
printf ( " \t RLIMIT_MSGQUEUE(hard): \t \t \t (unlimited) \n " ) ;
} else {
2014-06-25 20:05:17 +04:00
printf ( " \t RLIMIT_MSGQUEUE(soft): \t \t \t %ld \n " ,
( long ) cur_limits . rlim_cur ) ;
printf ( " \t RLIMIT_MSGQUEUE(hard): \t \t \t %ld \n " ,
( long ) cur_limits . rlim_max ) ;
2012-06-01 03:26:37 +04:00
}
printf ( " \t Maximum Message Size: \t \t \t %d \n " , cur_max_msgsize ) ;
printf ( " \t Maximum Queue Size: \t \t \t %d \n " , cur_max_msgs ) ;
printf ( " \t Nice value: \t \t \t \t %d \n " , cur_nice ) ;
printf ( " \t Continuous mode: \t \t \t (%s) \n " , continuous_mode ?
( continuous_mode_fake ? " fake mode " : " enabled " ) :
" disabled " ) ;
printf ( " \t CPUs to pin: \t \t \t \t %d " , cpus_to_pin [ 0 ] ) ;
for ( cpu = 1 ; cpu < num_cpus_to_pin ; cpu + + )
printf ( " ,%d " , cpus_to_pin [ cpu ] ) ;
printf ( " \n " ) ;
sa . sa_sigaction = sig_action_SIGUSR1 ;
sigemptyset ( & sa . sa_mask ) ;
sigaddset ( & sa . sa_mask , SIGHUP ) ;
sigaddset ( & sa . sa_mask , SIGINT ) ;
sigaddset ( & sa . sa_mask , SIGQUIT ) ;
sigaddset ( & sa . sa_mask , SIGTERM ) ;
sa . sa_flags = SA_SIGINFO ;
if ( sigaction ( SIGUSR1 , & sa , NULL ) = = - 1 )
shutdown ( 1 , " sigaction(SIGUSR1) " , __LINE__ ) ;
sa . sa_sigaction = sig_action ;
if ( sigaction ( SIGHUP , & sa , NULL ) = = - 1 )
shutdown ( 1 , " sigaction(SIGHUP) " , __LINE__ ) ;
if ( sigaction ( SIGINT , & sa , NULL ) = = - 1 )
shutdown ( 1 , " sigaction(SIGINT) " , __LINE__ ) ;
if ( sigaction ( SIGQUIT , & sa , NULL ) = = - 1 )
shutdown ( 1 , " sigaction(SIGQUIT) " , __LINE__ ) ;
if ( sigaction ( SIGTERM , & sa , NULL ) = = - 1 )
shutdown ( 1 , " sigaction(SIGTERM) " , __LINE__ ) ;
if ( ! continuous_mode_fake ) {
attr . mq_flags = O_NONBLOCK ;
attr . mq_maxmsg = cur_max_msgs ;
attr . mq_msgsize = MSG_SIZE ;
open_queue ( & attr ) ;
}
for ( i = 0 ; i < num_cpus_to_pin ; i + + ) {
pthread_attr_t thread_attr ;
void * thread_func ;
if ( continuous_mode_fake )
thread_func = & fake_cont_thread ;
else if ( continuous_mode )
thread_func = & cont_thread ;
else
thread_func = & perf_test_thread ;
CPU_ZERO_S ( cpu_set_size , cpu_set ) ;
CPU_SET_S ( cpus_to_pin [ i ] , cpu_set_size , cpu_set ) ;
pthread_attr_init ( & thread_attr ) ;
pthread_attr_setaffinity_np ( & thread_attr , cpu_set_size ,
cpu_set ) ;
if ( pthread_create ( & cpu_threads [ i ] , & thread_attr , thread_func ,
NULL ) )
shutdown ( 1 , " pthread_create() " , __LINE__ ) ;
pthread_attr_destroy ( & thread_attr ) ;
}
if ( ! continuous_mode ) {
pthread_join ( cpu_threads [ 0 ] , & retval ) ;
shutdown ( ( long ) retval , " perf_test_thread() " , __LINE__ ) ;
} else {
while ( 1 )
sleep ( 1 ) ;
}
shutdown ( 0 , " " , 0 ) ;
2022-04-08 10:24:31 +03:00
err_code :
CPU_FREE ( cpu_set ) ;
exit ( 1 ) ;
2012-06-01 03:26:37 +04:00
}