[PATCH v4 03/19] perf test: Add a workload that forces context switches
From: James Clark
Date: Tue Jun 09 2026 - 10:50:56 EST
This workload launches two processes that block when reading and writing
to each other forcing the other process to be scheduled for each
read/write pair.
Signed-off-by: James Clark <james.clark@xxxxxxxxxx>
---
tools/perf/Documentation/perf-test.txt | 7 +-
tools/perf/tests/builtin-test.c | 1 +
tools/perf/tests/tests.h | 1 +
tools/perf/tests/workloads/Build | 1 +
tools/perf/tests/workloads/context_switch_loop.c | 110 +++++++++++++++++++++++
5 files changed, 117 insertions(+), 3 deletions(-)
diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt
index 2f4a91f5b9dc..213eb62603eb 100644
--- a/tools/perf/Documentation/perf-test.txt
+++ b/tools/perf/Documentation/perf-test.txt
@@ -55,15 +55,16 @@ OPTIONS
-w::
--workload=::
- Run a built-in workload, to list them use '--list-workloads', current ones include:
- noploop, thloop, leafloop, sqrtloop, brstack, datasym and landlock.
+ Run a built-in workload, to list them use '--list-workloads', current
+ ones include: noploop, thloop, leafloop, sqrtloop, brstack, datasym,
+ context_switch_loop and landlock.
Used with the shell script regression tests.
Some accept an extra parameter:
seconds: leafloop, noploop, sqrtloop, thloop
- nrloops: brstack
+ nrloops: brstack, context_switch_loop
The datasym and landlock workloads don't accept any.
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 86ea427eb0aa..9284f897de3c 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -163,6 +163,7 @@ static struct test_workload *workloads[] = {
&workload__traploop,
&workload__inlineloop,
&workload__jitdump,
+ &workload__context_switch_loop,
#ifdef HAVE_RUST_SUPPORT
&workload__code_with_type,
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index bf8ff7d54727..7cd4da4e96d3 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -245,6 +245,7 @@ DECLARE_WORKLOAD(landlock);
DECLARE_WORKLOAD(traploop);
DECLARE_WORKLOAD(inlineloop);
DECLARE_WORKLOAD(jitdump);
+DECLARE_WORKLOAD(context_switch_loop);
#ifdef HAVE_RUST_SUPPORT
DECLARE_WORKLOAD(code_with_type);
diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build
index 0eb6d99528eb..7134a031cb7c 100644
--- a/tools/perf/tests/workloads/Build
+++ b/tools/perf/tests/workloads/Build
@@ -10,6 +10,7 @@ perf-test-y += landlock.o
perf-test-y += traploop.o
perf-test-y += inlineloop.o
perf-test-y += jitdump.o
+perf-test-y += context_switch_loop.o
ifeq ($(CONFIG_RUST_SUPPORT),y)
perf-test-y += code_with_type.o
diff --git a/tools/perf/tests/workloads/context_switch_loop.c b/tools/perf/tests/workloads/context_switch_loop.c
new file mode 100644
index 000000000000..5431af6147e6
--- /dev/null
+++ b/tools/perf/tests/workloads/context_switch_loop.c
@@ -0,0 +1,110 @@
+
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/compiler.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../tests.h"
+
+static int loops = 100;
+static char buf;
+int context_switch_loop_work = 1234;
+
+#define write_block(fd) \
+ do { \
+ if (write(fd, &buf, 1) <= 0) \
+ return 1; \
+ } while (0)
+
+#define read_block(fd) \
+ do { \
+ if (read(fd, &buf, 1) <= 0) \
+ return 1; \
+ } while (0)
+
+/* Not static to avoid LTO clobbering the function name */
+int context_switch_loop_proc1(int in_fd, int out_fd);
+int context_switch_loop_proc1(int in_fd, int out_fd)
+{
+ for (int i = 0; i < loops; i++) {
+ read_block(in_fd);
+ context_switch_loop_work += i * 3;
+ write_block(out_fd);
+ }
+ return 0;
+}
+
+int context_switch_loop_proc2(int in_fd, int out_fd);
+int context_switch_loop_proc2(int in_fd, int out_fd)
+{
+ for (int i = 0; i < loops; i++) {
+ write_block(out_fd);
+ context_switch_loop_work += i * 7;
+ read_block(in_fd);
+ }
+ return 0;
+}
+
+/*
+ * Launches two processes that take turns to execute a multiplication N times
+ */
+static int context_switch_loop(int argc, const char **argv)
+{
+ int a_to_b[2], b_to_a[2];
+ pid_t proc1_pid;
+ int status;
+ int ret;
+
+ if (argc > 0) {
+ loops = atoi(argv[0]);
+ if (loops < 0) {
+ fprintf(stderr, "Invalid number of loops: %s\n", argv[0]);
+ return 1;
+ }
+ }
+
+ if (pipe(a_to_b) || pipe(b_to_a)) {
+ perror("Pipe error");
+ return 1;
+ }
+
+ proc1_pid = fork();
+ if (proc1_pid < 0) {
+ perror("Fork error");
+ return 1;
+ }
+
+ if (!proc1_pid) {
+ close(a_to_b[0]);
+ close(b_to_a[1]);
+ prctl(PR_SET_NAME, "proc1", 0, 0, 0);
+ ret = context_switch_loop_proc1(b_to_a[0], a_to_b[1]);
+ close(a_to_b[1]);
+ close(b_to_a[0]);
+ exit(ret);
+ }
+
+ close(a_to_b[1]);
+ close(b_to_a[0]);
+ prctl(PR_SET_NAME, "proc2", 0, 0, 0);
+ ret = context_switch_loop_proc2(a_to_b[0], b_to_a[1]);
+ close(a_to_b[0]);
+ close(b_to_a[1]);
+
+ if (ret) {
+ kill(proc1_pid, SIGKILL);
+ return ret;
+ }
+
+ if (waitpid(proc1_pid, &status, 0) != proc1_pid || !WIFEXITED(status) ||
+ WEXITSTATUS(status))
+ return 1;
+
+ return 0;
+}
+
+DEFINE_WORKLOAD(context_switch_loop);
--
2.34.1