Re: [rt-tests v1 2/3] signaltest: Implement thread placing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On Sun, 15 Nov 2020, Daniel Wagner wrote:

> Without setting the thread affinity, the scheduler will move the
> threads around which will lead to spikes. Since any proper realtime
> application will use thread affinity, let's pin down the threads to
> CPUs.
> 
> Signed-off-by: Daniel Wagner <dwagner@xxxxxxx>
> ---
>  Makefile                    |   4 +-
>  src/signaltest/signaltest.8 |  22 ++++++-
>  src/signaltest/signaltest.c | 116 ++++++++++++++++++++++++++++++++++--
>  3 files changed, 133 insertions(+), 9 deletions(-)
> 
> diff --git a/Makefile b/Makefile
> index 3afdfd4d53a7..a410cb24cf34 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -127,8 +127,8 @@ cyclicdeadline: $(OBJDIR)/cyclicdeadline.o $(OBJDIR)/librttest.a
>  deadline_test: $(OBJDIR)/deadline_test.o $(OBJDIR)/librttest.a
>  	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB)
>  
> -signaltest: $(OBJDIR)/signaltest.o $(OBJDIR)/librttest.a
> -	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB)
> +signaltest: $(OBJDIR)/signaltest.o $(OBJDIR)/librttest.a $(OBJDIR)/librttestnuma.a
> +	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(RTTESTNUMA)
>  
>  pi_stress: $(OBJDIR)/pi_stress.o $(OBJDIR)/librttest.a
>  	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB)
> diff --git a/src/signaltest/signaltest.8 b/src/signaltest/signaltest.8
> index dc3eaab2293a..5ce119b54461 100644
> --- a/src/signaltest/signaltest.8
> +++ b/src/signaltest/signaltest.8
> @@ -1,15 +1,28 @@
>  .\"
> -.TH SIGNALTEST 8 "September 18, 2020"
> +.TH SIGNALTEST 8 "November 15, 2020"
>  .\" Please adjust this date whenever updating this manpage
>  .SH NAME
>  signaltest \- signal roundtrip test software
>  .SH SYNOPSIS
>  .LP
> -signaltest [ -b|--backtrace USEC ] [-D|--duration TIME] [-h|--help] [-l|--loops LOOPS ] [-p|--prio PRIO] [-q|--quiet] [-t|--threads NUM] [-m|--mlockall ] [-v|--verbose ]
> +signaltest [ -a|--affinity NUM] [ -b|--backtrace USEC ] [-D|--duration TIME] [-h|--help] [-l|--loops LOOPS ] [-p|--prio PRIO] [-q|--quiet] [-S|--smp] [-t|--threads NUM] [-m|--mlockall ] [-v|--verbose ]
>  .SH OPTIONS
>  These programs follow the usual GNU command line syntax, with long options
>  starting with two dashes ('\-\-').
>  .TP
> +.B \-a, \-\-affinity[=PROC-SET]
> +Run threads on the set of processors given by PROC-SET.  If PROC-SET is not
> +specified, all processors will be used.  Threads will be assigned to processors
> +in the set in numeric order, in a round\-robin fashion.
> +.br
> +The set of processors can be specified as A,B,C, or A-C, or A-B,D-F, and so on*.
> +The ! character can be used to negate a set.  For example, !B-D means to use all
> +available CPUs except B through D.  The cpu numbers are the same as shown in the
> +.I processor
> +field in /proc/cpuinfo.  See numa(3) for more information on specifying CPU sets.
> +* Support for CPU sets requires libnuma version >= 2.  For libnuma v1, PROC-SET,
> +if specified, must be a single CPU number.
> +.TP
>  .B \-b, \-\-breaktrace=USEC
>  Send break trace command when latency > USEC
>  .TP
> @@ -31,6 +44,11 @@ Priority of highest priority thread
>  .B \-q, \-\-quiet
>  print a summary only on exit
>  .TP
> +.B \\-S, \-\-smp
> +Set options for standard testing on SMP systems. Equivalent to using
> +the options: "\-t \-a" as well keeping any specified priority
> +equal across all threads
> +.TP
>  .B \-t, \-\-threads=NUM
>  number of threads: default=2
>  .TP
> diff --git a/src/signaltest/signaltest.c b/src/signaltest/signaltest.c
> index dacaa63673c4..e19877a395ba 100644
> --- a/src/signaltest/signaltest.c
> +++ b/src/signaltest/signaltest.c
> @@ -20,6 +20,8 @@
>  #include <string.h>
>  #include <time.h>
>  #include <unistd.h>
> +#include <errno.h>
> +#include <sched.h>
>  
>  #include <linux/unistd.h>
>  
> @@ -31,6 +33,7 @@
>  
>  #include "error.h"
>  #include "rt-utils.h"
> +#include "rt-numa.h"
>  
>  /* Must be power of 2 ! */
>  #define VALBUF_SIZE		16384
> @@ -43,6 +46,7 @@ struct thread_param {
>  	unsigned long max_cycles;
>  	struct thread_stat *stats;
>  	int bufmsk;
> +	int cpu;
>  };
>  
>  /* Struct for statistics */
> @@ -79,9 +83,20 @@ void *signalthread(void *param)
>  	int policy = par->prio ? SCHED_FIFO : SCHED_OTHER;
>  	int stopped = 0;
>  	int first = 1;
> +	pthread_t thread;
> +	cpu_set_t mask;
>  
>  	stat->tid = gettid();
>  
> +	if (par->cpu != -1) {
> +		CPU_ZERO(&mask);
> +		CPU_SET(par->cpu, &mask);
> +		thread = pthread_self();
> +		if (pthread_setaffinity_np(thread, sizeof(mask), &mask) != 0)
> +			warn("Could not set CPU affinity to CPU #%d\n",
> +			     par->cpu);
> +	}
> +
>  	sigemptyset(&sigset);
>  	sigaddset(&sigset, par->signal);
>  	sigprocmask(SIG_BLOCK, &sigset, NULL);
> @@ -164,6 +179,8 @@ static void display_help(int error)
>  	printf("signaltest V %1.2f\n", VERSION);
>  	printf("Usage:\n"
>  		"signaltest <options>\n\n"
> +		"-a [NUM] --affinity        run thread #N on processor #N, if possible\n"
> +		"                           with NUM pin all threads to the processor NUM\n"
>  		"-b USEC  --breaktrace=USEC send break trace command when latency > USEC\n"
>  		"-D       --duration=TIME   specify a length for the test run.\n"
>  		"                           Append 'm', 'h', or 'd' to specify minutes, hours or\n"
> @@ -187,16 +204,22 @@ static int duration;
>  static int verbose;
>  static int quiet;
>  static int lockall;
> +static struct bitmask *affinity_mask = NULL;
> +static int smp = 0;
> +static int numa = 0;
> +static int setaffinity = AFFINITY_UNSPECIFIED;
>  
>  /* Process commandline options */
> -static void process_options(int argc, char *argv[])
> +static void process_options(int argc, char *argv[], unsigned int max_cpus)
>  {
> +	int option_affinity = 0;
>  	int error = 0;
>  
>  	for (;;) {
>  		int option_index = 0;
>  		/** Options for getopt */
>  		static struct option long_options[] = {
> +			{"affinity",		optional_argument,	NULL, 'a'},
>  			{"breaktrace",		required_argument,	NULL, 'b'},
>  			{"duration",		required_argument,	NULL, 'D'},
>  			{"help",		no_argument,		NULL, 'h'},
> @@ -204,15 +227,43 @@ static void process_options(int argc, char *argv[])
>  			{"mlockall",		no_argument,		NULL, 'm'},
>  			{"priority",		required_argument,	NULL, 'p'},
>  			{"quiet",		no_argument,		NULL, 'q'},
> +			{"smp",			no_argument,		NULL, 'S'},
>  			{"threads",		required_argument,	NULL, 't'},
>  			{"verbose",		no_argument,		NULL, 'v'},
>  			{NULL, 0, NULL, 0}
>  		};
> -		int c = getopt_long(argc, argv, "b:D:hl:mp:qt:v",
> +		int c = getopt_long(argc, argv, "a::b:D:hl:mp:qSt:v",
>  				long_options, &option_index);
>  		if (c == -1)
>  			break;
>  		switch (c) {
> +		case 'a':
> +			option_affinity = 1;
> +			/* smp sets AFFINITY_USEALL in OPT_SMP */
> +			if (smp)
> +				break;
> +			if (numa_initialize())
> +				fatal("Couldn't initilize libnuma");
> +			numa = 1;
> +			if (optarg) {
> +				parse_cpumask(optarg, max_cpus, &affinity_mask);
> +				setaffinity = AFFINITY_SPECIFIED;
> +			} else if (optind < argc &&
> +				   (atoi(argv[optind]) ||
> +				    argv[optind][0] == '0' ||
> +				    argv[optind][0] == '!')) {
> +				parse_cpumask(argv[optind], max_cpus, &affinity_mask);
> +				setaffinity = AFFINITY_SPECIFIED;
> +			} else {
> +				setaffinity = AFFINITY_USEALL;
> +			}
> +
> +			if (setaffinity == AFFINITY_SPECIFIED && !affinity_mask)
> +				display_help(1);
> +			if (verbose)
> +				printf("Using %u cpus.\n",
> +					numa_bitmask_weight(affinity_mask));
> +			break;
>  		case 'b': tracelimit = atoi(optarg); break;
>  		case 'D': duration = parse_time_string(optarg); break;
>  		case '?':
> @@ -221,6 +272,13 @@ static void process_options(int argc, char *argv[])
>  		case 'm': lockall = 1; break;
>  		case 'p': priority = atoi(optarg); break;
>  		case 'q': quiet = 1; break;
> +		case 'S':
> +			if (numa)
> +				fatal("numa and smp options are mutually exclusive\n");
> +			smp = 1;
> +			num_threads = -1; /* update after parsing */
> +			setaffinity = AFFINITY_USEALL;
> +			break;
>  		case 't': num_threads = atoi(optarg); break;
>  		case 'v': verbose = 1; break;
>  		}
> @@ -232,11 +290,31 @@ static void process_options(int argc, char *argv[])
>  	if (priority < 0 || priority > 99)
>  		error = 1;
>  
> +	if (num_threads == -1)
> +		num_threads = get_available_cpus(affinity_mask);
> +
>  	if (num_threads < 2)
>  		error = 1;
>  
> -	if (error)
> +	/* if smp wasn't requested, test for numa automatically */
> +	if (!smp) {
> +		if (numa_initialize())
> +			fatal("Couldn't initilize libnuma");
> +		numa = 1;
> +		if (setaffinity == AFFINITY_UNSPECIFIED)
> +			setaffinity = AFFINITY_USEALL;
> +	}
> +
> +	if (option_affinity) {
> +		if (smp)
> +			warn("-a ignored due to smp mode\n");
> +	}
> +
> +	if (error) {
> +		if (affinity_mask)
> +			numa_bitmask_free(affinity_mask);
>  		display_help(error);
> +	}
>  }
>  
>  static void sighand(int sig)
> @@ -273,9 +351,10 @@ int main(int argc, char **argv)
>  	struct thread_param *par;
>  	struct thread_stat *stat;
>  	int i, ret = -1;
> -	int status;
> +	int status, cpu;
> +	int max_cpus = sysconf(_SC_NPROCESSORS_ONLN);
>  
> -	process_options(argc, argv);
> +	process_options(argc, argv, max_cpus);
>  
>  	if (check_privs())
>  		exit(1);
> @@ -287,6 +366,16 @@ int main(int argc, char **argv)
>  			goto out;
>  		}
>  
> +	/* Restrict the main pid to the affinity specified by the user */
> +	if (affinity_mask != NULL) {
> +		int res;
> +
> +		errno = 0;
> +		res = numa_sched_setaffinity(getpid(), affinity_mask);
> +		if (res != 0)
> +			warn("Couldn't setaffinity in main thread: %s\n", strerror(errno));
> +	}
> +
>  	sigemptyset(&sigset);
>  	sigaddset(&sigset, signum);
>  	sigprocmask(SIG_BLOCK, &sigset, NULL);
> @@ -313,6 +402,22 @@ int main(int argc, char **argv)
>  			par[i].bufmsk = VALBUF_SIZE - 1;
>  		}
>  
> +		switch (setaffinity) {
> +		case AFFINITY_UNSPECIFIED:
> +			cpu = -1;
> +			break;
> +		case AFFINITY_SPECIFIED:
> +			cpu = cpu_for_thread_sp(i, max_cpus, affinity_mask);
> +			if (verbose)
> +				printf("Thread %d using cpu %d.\n", i, cpu);
> +			break;
> +		case AFFINITY_USEALL:
> +			cpu = cpu_for_thread_ua(i, max_cpus);
> +			break;
> +		default:
> +			cpu = -1;
> +		}
> +
>  		par[i].id = i;
>  		par[i].prio = priority;
>  #if 0
> @@ -322,6 +427,7 @@ int main(int argc, char **argv)
>  		par[i].signal = signum;
>  		par[i].max_cycles = max_cycles;
>  		par[i].stats = &stat[i];
> +		par[i].cpu = cpu;
>  		stat[i].min = 1000000;
>  		stat[i].max = -1000000;
>  		stat[i].avg = 0.0;
> -- 
> 2.29.2
> 
> 
    - Fixed spelling of "initialize" in the code
    Signed-off-by: John Kacur <jkacur@xxxxxxxxxx>




[Index of Archives]     [RT Stable]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]

  Powered by Linux