Skip to contentSkip to conversation

rt-tests: cyclictest: Add option to specify main pid affinity

PATCH In review Jonathan Schwender 3 participants 10 messages May 18, 2021 – May 22, 2021

lore
Share & tools
lore
#0 lore raw

v4 cover rt-tests: cyclictest: Add option to specify main pid affinity

Hi John,

Changes in v4:
- Rolled back the error wrapper changes around numa_sched_setaffinity. I still extracted the error handling code into a function, but since it's only used locally it looks a lot nicer.
- Rebased onto latest commit c9051a36 ("ssdd: Add JSON output feature") in unstable/devel/latest.

This patch adds the option --mainaffinity to specify the affinity of
the main pid.
This is mainly useful if you want to bind the main thread to a
different (e.g. housekeeping ) CPU than the measurement threads.
Some of the potential benefits of using this option are presented in
a previous email: https://lore.kernel.org/linux-rt-users/[email protected]/

Jonathan Schwender (2):
cyclictest: Move main pid setaffinity handling into a function
cyclictest: Add --mainaffinity=[CPUSET] option.

 src/cyclictest/cyclictest.c | 39 ++++++++++++++++++++++++++++---------
 1 file changed, 30 insertions(+), 9 deletions(-)
Signature · 1 line
2.31.1
#1 lore raw

PATCH v4 1/2 cyclictest: Move main pid setaffinity handling into a function

Move error handling for setting the affinity of the main thread into a separate function.
This prevents duplicating the code in the next commit, where the main thread pid can be restricted to one of two bitmasks depending on the passed parameters.

---
 src/cyclictest/cyclictest.c | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)
src/cyclictest/cyclictest.c +12 -7
index 59dda19..3bab3b2 100644
@@ -1768,6 +1768,17 @@ static void write_stats(FILE *f, void *data)
 	fprintf(f, "  }\n");
 }
 
+static void set_main_thread_affinity(struct bitmask *cpumask)
+{
+	int res;
+
+	errno = 0;
+	res = numa_sched_setaffinity(getpid(), cpumask);
+	if (res != 0)
+		warn("Couldn't setaffinity in main thread: %s\n",
+		     strerror(errno));
+}
+
 int main(int argc, char **argv)
 {
 	sigset_t sigset;
@@ -1792,13 +1803,7 @@ int main(int argc, char **argv)
 
 	/* 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));
-
+		set_main_thread_affinity(affinity_mask);
 		if (verbose)
 			printf("Using %u cpus.\n",
 				numa_bitmask_weight(affinity_mask));
Signature · 1 line
2.31.1
#2 lore raw

Re: [PATCH v4 1/2] cyclictest: Move main pid setaffinity handling into a function

Quoted 16 lines · Jonathan Schwender

Move error handling for setting the affinity of the main thread into a separate function.
This prevents duplicating the code in the next commit, where the main thread pid can be restricted to one of two bitmasks depending on the passed parameters.

---
 src/cyclictest/cyclictest.c | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)
src/cyclictest/cyclictest.c +0 -0
index 59dda19..3bab3b2 100644

src/cyclictest/cyclictest.c static void write_stats(FILE *f, void *data) · line 1770

Quoted 3 lines
src/cyclictest/cyclictest.c +0 -0
@@ -1768,6 +1768,17 @@ static void write_stats(FILE *f, void *data)
 	fprintf(f, "  }\n");
 }
 

How about set_thread_affinity() since you want to use the function for the main thread plus other threads.

Quoted 33 lines
src/cyclictest/cyclictest.c +12 -7
+static void set_main_thread_affinity(struct bitmask *cpumask)
+{
+	int res;
+
+	errno = 0;
+	res = numa_sched_setaffinity(getpid(), cpumask);
+	if (res != 0)
+		warn("Couldn't setaffinity in main thread: %s\n",
+		     strerror(errno));
+}
+
 int main(int argc, char **argv)
 {
 	sigset_t sigset;
@@ -1792,13 +1803,7 @@ int main(int argc, char **argv)
 
 	/* 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));
-
+		set_main_thread_affinity(affinity_mask);
 		if (verbose)
 			printf("Using %u cpus.\n",
 				numa_bitmask_weight(affinity_mask));
Signature · 1 line
2.31.1
#3 lore raw

Re: [PATCH v4 1/2] cyclictest: Move main pid setaffinity handling into a function

Am 21.05.2021 um 22:19 schrieb John Kacur:

Quoted 15 lines

How about set_thread_affinity() since you want to use the function for the main thread plus other threads.

src/cyclictest/cyclictest.c static void write_stats(FILE *f, void *data) · line 1781

Quoted 12 lines
src/cyclictest/cyclictest.c +11 -0
+static void set_main_thread_affinity(struct bitmask *cpumask)
+{
+	int res;
+
+	errno = 0;
+	res = numa_sched_setaffinity(getpid(), cpumask);
+	if (res != 0)
+		warn("Couldn't setaffinity in main thread: %s\n",
+		     strerror(errno));
+}
+

Actually, I only intended to use this for the main thread.
I didn't touch the affinity setting of the timerthreads (and don't see a need to).

I'm not familiar with the whole cyclictest code and all options. Did you have any specific threads in mind that also use numa_sched_setaffinity() and could use this function?

Maybe you got that impression from the previous v3 iteration, where I moved the functionality to rt_numa and added a parameter for the pid/tid. My motivation there was mainly,
that if I'm moving it into a library, then I should probably add a parameter for the pid to the function in case someone has a usecase for that.

#4 lore raw

PATCH v4 2/2 cyclictest: Add --mainaffinity=[CPUSET] option.

In reply to Jonathan Schwender

This allows the user to specify a separate cpuset for the main pid, e.g. on a housekeeping CPU.
If --mainaffinity is not specified, but --affinity is, then the current behaviour is preserved and the main thread is bound to the cpuset specified by --affinity

---
 src/cyclictest/cyclictest.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)
src/cyclictest/cyclictest.c +18 -2
index 3bab3b2..a2103c7 100644
@@ -836,6 +836,8 @@ static void display_help(int error)
 	       "	 --laptop	   Save battery when running cyclictest\n"
 	       "			   This will give you poorer realtime results\n"
 	       "			   but will not drain your battery so quickly\n"
+	       "         --mainaffinity=[CPUSET]  Run the main thread on CPU #N. This only affects\n"
+	       "                           the main thread and not the measurement threads\n"
 	       "-m       --mlockall        lock current and future memory allocations\n"
 	       "-M       --refresh_on_max  delay updating the screen until a new max\n"
 	       "			   latency is hit. Useful for low bandwidth.\n"
@@ -891,6 +893,7 @@ static int quiet;
 static int interval = DEFAULT_INTERVAL;
 static int distance = -1;
 static struct bitmask *affinity_mask = NULL;
+static struct bitmask *main_affinity_mask = NULL;
 static int smp = 0;
 static int setaffinity = AFFINITY_UNSPECIFIED;
 
@@ -944,7 +947,7 @@ enum option_values {
 	OPT_AFFINITY=1, OPT_BREAKTRACE, OPT_CLOCK,
 	OPT_DISTANCE, OPT_DURATION, OPT_LATENCY,
 	OPT_FIFO, OPT_HISTOGRAM, OPT_HISTOFALL, OPT_HISTFILE,
-	OPT_INTERVAL, OPT_LOOPS, OPT_MLOCKALL, OPT_REFRESH,
+	OPT_INTERVAL, OPT_LOOPS, OPT_MAINAFFINITY, OPT_MLOCKALL, OPT_REFRESH,
 	OPT_NANOSLEEP, OPT_NSECS, OPT_OSCOPE, OPT_PRIORITY,
 	OPT_QUIET, OPT_PRIOSPREAD, OPT_RELATIVE, OPT_RESOLUTION,
 	OPT_SYSTEM, OPT_SMP, OPT_THREADS, OPT_TRIGGER,
@@ -981,6 +984,7 @@ static void process_options(int argc, char *argv[], int max_cpus)
 			{"interval",         required_argument, NULL, OPT_INTERVAL },
 			{"laptop",	     no_argument,	NULL, OPT_LAPTOP },
 			{"loops",            required_argument, NULL, OPT_LOOPS },
+			{"mainaffinity",     required_argument, NULL, OPT_MAINAFFINITY},
 			{"mlockall",         no_argument,       NULL, OPT_MLOCKALL },
 			{"refresh_on_max",   no_argument,       NULL, OPT_REFRESH },
 			{"nsecs",            no_argument,       NULL, OPT_NSECS },
@@ -1083,6 +1087,16 @@ static void process_options(int argc, char *argv[], int max_cpus)
 		case 'l':
 		case OPT_LOOPS:
 			max_cycles = atoi(optarg); break;
+		case OPT_MAINAFFINITY:
+			if (optarg) {
+				parse_cpumask(optarg, max_cpus, &main_affinity_mask);
+			} else if (optind < argc &&
+			           (atoi(argv[optind]) ||
+			            argv[optind][0] == '0' ||
+			            argv[optind][0] == '!')) {
+				parse_cpumask(argv[optind], max_cpus, &main_affinity_mask);
+			}
+			break;
 		case 'm':
 		case OPT_MLOCKALL:
 			lockall = 1; break;
@@ -1802,7 +1816,9 @@ int main(int argc, char **argv)
 	}
 
 	/* Restrict the main pid to the affinity specified by the user */
-	if (affinity_mask != NULL) {
+	if (main_affinity_mask != NULL) {
+		set_main_thread_affinity(main_affinity_mask);
+	} else if (affinity_mask != NULL) {
 		set_main_thread_affinity(affinity_mask);
 		if (verbose)
 			printf("Using %u cpus.\n",
Signature · 1 line
2.31.1
#5 lore raw

Re: [PATCH v4 2/2] cyclictest: Add --mainaffinity=[CPUSET] option.

Hi Jonathan,

Quoted 16 lines · Jonathan Schwender

This allows the user to specify a separate cpuset for the main pid, e.g. on a housekeeping CPU.
If --mainaffinity is not specified, but --affinity is, then the current behaviour is preserved and the main thread is bound to the cpuset specified by --affinity

---
 src/cyclictest/cyclictest.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)
src/cyclictest/cyclictest.c +0 -0
index 3bab3b2..a2103c7 100644

src/cyclictest/cyclictest.c static void display_help(int error) · line 840

Quoted 5 lines
src/cyclictest/cyclictest.c +2 -0
@@ -836,6 +836,8 @@ static void display_help(int error)
 	       "	 --laptop	   Save battery when running cyclictest\n"
 	       "			   This will give you poorer realtime results\n"
 	       "			   but will not drain your battery so quickly\n"
+	       "         --mainaffinity=[CPUSET]  Run the main thread on CPU #N. This only affects\n"
+	       "                           the main thread and not the measurement threads\n"

The main thread is allowed to run on any CPU which CPUSET specifies. CPU #N does not really make sense.

While at it, please update man page accordingly.

Thanks,
Daniel

#6 lore raw

Re: [PATCH v4 2/2] cyclictest: Add --mainaffinity=[CPUSET] option.

Hi Daniel,

Am 19.05.2021 um 17:55 schrieb Daniel Wagner:

Quoted 7 lines

src/cyclictest/cyclictest.c static void display_help(int error) · line 840

Quoted 3 lines
src/cyclictest/cyclictest.c +2 -0
+	       "         --mainaffinity=[CPUSET]  Run the main thread on CPU #N. This only affects\n"
+	       "                           the main thread and not the measurement threads\n"

The main thread is allowed to run on any CPU which CPUSET specifies. CPU #N does not really make sense.

While at it, please update man page accordingly.

Thanks for catching this. I'll include it in the next iteration.

Quoted 2 lines

Thanks,
Daniel

Regards,

Jonathan

#7 lore raw

Re: [PATCH v4 2/2] cyclictest: Add --mainaffinity=[CPUSET] option.

In reply to Jonathan Schwender
Quoted 67 lines · Jonathan Schwender

This allows the user to specify a separate cpuset for the main pid, e.g. on a housekeeping CPU.
If --mainaffinity is not specified, but --affinity is, then the current behaviour is preserved and the main thread is bound to the cpuset specified by --affinity

---
 src/cyclictest/cyclictest.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)
src/cyclictest/cyclictest.c +15 -1
index 3bab3b2..a2103c7 100644
@@ -836,6 +836,8 @@ static void display_help(int error)
 	       "	 --laptop	   Save battery when running cyclictest\n"
 	       "			   This will give you poorer realtime results\n"
 	       "			   but will not drain your battery so quickly\n"
+	       "         --mainaffinity=[CPUSET]  Run the main thread on CPU #N. This only affects\n"
+	       "                           the main thread and not the measurement threads\n"
 	       "-m       --mlockall        lock current and future memory allocations\n"
 	       "-M       --refresh_on_max  delay updating the screen until a new max\n"
 	       "			   latency is hit. Useful for low bandwidth.\n"
@@ -891,6 +893,7 @@ static int quiet;
 static int interval = DEFAULT_INTERVAL;
 static int distance = -1;
 static struct bitmask *affinity_mask = NULL;
+static struct bitmask *main_affinity_mask = NULL;
 static int smp = 0;
 static int setaffinity = AFFINITY_UNSPECIFIED;
 
@@ -944,7 +947,7 @@ enum option_values {
 	OPT_AFFINITY=1, OPT_BREAKTRACE, OPT_CLOCK,
 	OPT_DISTANCE, OPT_DURATION, OPT_LATENCY,
 	OPT_FIFO, OPT_HISTOGRAM, OPT_HISTOFALL, OPT_HISTFILE,
-	OPT_INTERVAL, OPT_LOOPS, OPT_MLOCKALL, OPT_REFRESH,
+	OPT_INTERVAL, OPT_LOOPS, OPT_MAINAFFINITY, OPT_MLOCKALL, OPT_REFRESH,
 	OPT_NANOSLEEP, OPT_NSECS, OPT_OSCOPE, OPT_PRIORITY,
 	OPT_QUIET, OPT_PRIOSPREAD, OPT_RELATIVE, OPT_RESOLUTION,
 	OPT_SYSTEM, OPT_SMP, OPT_THREADS, OPT_TRIGGER,
@@ -981,6 +984,7 @@ static void process_options(int argc, char *argv[], int max_cpus)
 			{"interval",         required_argument, NULL, OPT_INTERVAL },
 			{"laptop",	     no_argument,	NULL, OPT_LAPTOP },
 			{"loops",            required_argument, NULL, OPT_LOOPS },
+			{"mainaffinity",     required_argument, NULL, OPT_MAINAFFINITY},
 			{"mlockall",         no_argument,       NULL, OPT_MLOCKALL },
 			{"refresh_on_max",   no_argument,       NULL, OPT_REFRESH },
 			{"nsecs",            no_argument,       NULL, OPT_NSECS },
@@ -1083,6 +1087,16 @@ static void process_options(int argc, char *argv[], int max_cpus)
 		case 'l':
 		case OPT_LOOPS:
 			max_cycles = atoi(optarg); break;
+		case OPT_MAINAFFINITY:
+			if (optarg) {
+				parse_cpumask(optarg, max_cpus, &main_affinity_mask);
+			} else if (optind < argc &&
+			           (atoi(argv[optind]) ||
+			            argv[optind][0] == '0' ||
+			            argv[optind][0] == '!')) {
+				parse_cpumask(argv[optind], max_cpus, &main_affinity_mask);
+			}
+			break;
 		case 'm':
 		case OPT_MLOCKALL:
 			lockall = 1; break;

src/cyclictest/cyclictest.c int main(int argc, char **argv) · line 1820

Quoted 6 lines
src/cyclictest/cyclictest.c +2 -1
@@ -1802,7 +1816,9 @@ int main(int argc, char **argv)
 	}
 
 	/* Restrict the main pid to the affinity specified by the user */
-	if (affinity_mask != NULL) {
+	if (main_affinity_mask != NULL) {
+		set_main_thread_affinity(main_affinity_mask);

Am I missing something here, if there is a main_affinity_mask we set that but then skip over the affinity_mask. Don't we want to check both?

Quoted 8 lines
src/cyclictest/cyclictest.c +1 -0
+	} else if (affinity_mask != NULL) {
 		set_main_thread_affinity(affinity_mask);
 		if (verbose)
 			printf("Using %u cpus.\n",
Signature · 1 line
2.31.1
#8 lore raw

Re: [PATCH v4 2/2] cyclictest: Add --mainaffinity=[CPUSET] option.

In reply to Jonathan Schwender
Quoted 67 lines · Jonathan Schwender

This allows the user to specify a separate cpuset for the main pid, e.g. on a housekeeping CPU.
If --mainaffinity is not specified, but --affinity is, then the current behaviour is preserved and the main thread is bound to the cpuset specified by --affinity

---
 src/cyclictest/cyclictest.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)
src/cyclictest/cyclictest.c +15 -1
index 3bab3b2..a2103c7 100644
@@ -836,6 +836,8 @@ static void display_help(int error)
 	       "	 --laptop	   Save battery when running cyclictest\n"
 	       "			   This will give you poorer realtime results\n"
 	       "			   but will not drain your battery so quickly\n"
+	       "         --mainaffinity=[CPUSET]  Run the main thread on CPU #N. This only affects\n"
+	       "                           the main thread and not the measurement threads\n"
 	       "-m       --mlockall        lock current and future memory allocations\n"
 	       "-M       --refresh_on_max  delay updating the screen until a new max\n"
 	       "			   latency is hit. Useful for low bandwidth.\n"
@@ -891,6 +893,7 @@ static int quiet;
 static int interval = DEFAULT_INTERVAL;
 static int distance = -1;
 static struct bitmask *affinity_mask = NULL;
+static struct bitmask *main_affinity_mask = NULL;
 static int smp = 0;
 static int setaffinity = AFFINITY_UNSPECIFIED;
 
@@ -944,7 +947,7 @@ enum option_values {
 	OPT_AFFINITY=1, OPT_BREAKTRACE, OPT_CLOCK,
 	OPT_DISTANCE, OPT_DURATION, OPT_LATENCY,
 	OPT_FIFO, OPT_HISTOGRAM, OPT_HISTOFALL, OPT_HISTFILE,
-	OPT_INTERVAL, OPT_LOOPS, OPT_MLOCKALL, OPT_REFRESH,
+	OPT_INTERVAL, OPT_LOOPS, OPT_MAINAFFINITY, OPT_MLOCKALL, OPT_REFRESH,
 	OPT_NANOSLEEP, OPT_NSECS, OPT_OSCOPE, OPT_PRIORITY,
 	OPT_QUIET, OPT_PRIOSPREAD, OPT_RELATIVE, OPT_RESOLUTION,
 	OPT_SYSTEM, OPT_SMP, OPT_THREADS, OPT_TRIGGER,
@@ -981,6 +984,7 @@ static void process_options(int argc, char *argv[], int max_cpus)
 			{"interval",         required_argument, NULL, OPT_INTERVAL },
 			{"laptop",	     no_argument,	NULL, OPT_LAPTOP },
 			{"loops",            required_argument, NULL, OPT_LOOPS },
+			{"mainaffinity",     required_argument, NULL, OPT_MAINAFFINITY},
 			{"mlockall",         no_argument,       NULL, OPT_MLOCKALL },
 			{"refresh_on_max",   no_argument,       NULL, OPT_REFRESH },
 			{"nsecs",            no_argument,       NULL, OPT_NSECS },
@@ -1083,6 +1087,16 @@ static void process_options(int argc, char *argv[], int max_cpus)
 		case 'l':
 		case OPT_LOOPS:
 			max_cycles = atoi(optarg); break;
+		case OPT_MAINAFFINITY:
+			if (optarg) {
+				parse_cpumask(optarg, max_cpus, &main_affinity_mask);
+			} else if (optind < argc &&
+			           (atoi(argv[optind]) ||
+			            argv[optind][0] == '0' ||
+			            argv[optind][0] == '!')) {
+				parse_cpumask(argv[optind], max_cpus, &main_affinity_mask);
+			}
+			break;
 		case 'm':
 		case OPT_MLOCKALL:
 			lockall = 1; break;

src/cyclictest/cyclictest.c int main(int argc, char **argv) · line 1820

Quoted 6 lines
src/cyclictest/cyclictest.c +2 -1
@@ -1802,7 +1816,9 @@ int main(int argc, char **argv)
 	}
 
 	/* Restrict the main pid to the affinity specified by the user */
-	if (affinity_mask != NULL) {
+	if (main_affinity_mask != NULL) {
+		set_main_thread_affinity(main_affinity_mask);

Am I missing something here, if there is a main_affinity_mask we set that but then skip over the affinity_mask. Don't we want to check both?

Quoted 8 lines
src/cyclictest/cyclictest.c +1 -0
+	} else if (affinity_mask != NULL) {
 		set_main_thread_affinity(affinity_mask);
 		if (verbose)
 			printf("Using %u cpus.\n",
Signature · 1 line
2.31.1
#9 lore raw

Re: [PATCH v4 2/2] cyclictest: Add --mainaffinity=[CPUSET] option.

Am 21.05.2021 um 22:24 schrieb John Kacur:

Quoted 12 lines · Jonathan Schwender
Quoted 10 lines

This allows the user to specify a separate cpuset for the main pid, e.g. on a housekeeping CPU.
If --mainaffinity is not specified, but --affinity is, then the current behaviour is preserved and the main thread is bound to the cpuset specified by --affinity

(quoted diff) +0 -0
@@ -1802,7 +1816,9 @@ int main(int argc, char **argv)
  	}
 
Quoted 6 lines

src/cyclictest/cyclictest.c int main(int argc, char **argv) · line 1820

Quoted 4 lines
src/cyclictest/cyclictest.c +2 -1
  	/* Restrict the main pid to the affinity specified by the user */
-	if (affinity_mask != NULL) {
+	if (main_affinity_mask != NULL) {
+		set_main_thread_affinity(main_affinity_mask);

Am I missing something here, if there is a main_affinity_mask we set that but then skip over the affinity_mask. Don't we want to check both?

So the idea here is that if --mainaffinity is specified, then we set the affinity of  the main thread to the specified value, _regardless_ of --affinity. E.g.:

cyclictest --affinity=1,2,3 --mainaffinity=0 [other options]

The intended behavior is that the main thread is bound to CPU 0, and the other threads to CPUs 1-3. I don't see a reason why mainaffinity should be restricted to a subset of affinity. E.g., if for some reason (e.g. cyclictest in a container) you spawn multiple cyclictest instances with non-overlapping affinity cpusets but want to offload the main pids to a common CPU (e.g. CPU 0). Restricting mainaffinity to a subset of affinity would mean that now every cyclictest instance also must spawn a measurement thread on CPU 0, even if you aren't really interested in the latencies of the housekeeping CPU. Due to the (potentially) high priority of the timer threads, they might even negatively influence isolated CPUs by delaying offloaded tasks.

Another way to look at it is that --mainaffinity (if present) overrides --affinity when determining the target affinity for the main thread. The affinity for the measurement threads is not affected by this in any way.

Quoted 9 lines
Quoted 9 lines
src/cyclictest/cyclictest.c +1 -0
+	} else if (affinity_mask != NULL) {
  		set_main_thread_affinity(affinity_mask);
  		if (verbose)
  			printf("Using %u cpus.\n",
Signature · 1 line
2.31.1