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@linaro.org --- 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 | 95 ++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 3 deletions(-)
diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index 1faf30d4a7be..9c0d7ac2bc64 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 d5df3efdce3b..1057ee836c30 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -156,6 +156,7 @@ static struct test_workload *workloads[] = { &workload__landlock, &workload__traploop, &workload__inlineloop, + &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 ee00518bf36f..79f50bacfc94 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -242,6 +242,7 @@ DECLARE_WORKLOAD(datasym); DECLARE_WORKLOAD(landlock); DECLARE_WORKLOAD(traploop); DECLARE_WORKLOAD(inlineloop); +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 2ef97f7affce..3bda6da04a35 100644 --- a/tools/perf/tests/workloads/Build +++ b/tools/perf/tests/workloads/Build @@ -9,6 +9,7 @@ perf-test-y += datasym.o perf-test-y += landlock.o perf-test-y += traploop.o perf-test-y += inlineloop.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..73bfcaeff5b0 --- /dev/null +++ b/tools/perf/tests/workloads/context_switch_loop.c @@ -0,0 +1,95 @@ +// 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) \ + exit(1); \ + } while (0) + +#define read_block(fd) \ + do { \ + if (read(fd, &buf, 1) <= 0) \ + exit(1); \ + } while (0) + +/* Not static to avoid LTO clobbering the function name */ +void context_switch_loop_proc1(int in_fd, int out_fd); +noinline void 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); + } +} + +void context_switch_loop_proc2(int in_fd, int out_fd); +noinline void 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); + } +} + +/* + * 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; + + 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); + context_switch_loop_proc1(b_to_a[0], a_to_b[1]); + exit(0); + } + + prctl(PR_SET_NAME, "proc2", 0, 0, 0); + context_switch_loop_proc2(a_to_b[0], b_to_a[1]); + + if (waitpid(proc1_pid, &status, 0) != proc1_pid || !WIFEXITED(status) || + WEXITSTATUS(status)) + return 1; + + return 0; +} + +DEFINE_WORKLOAD(context_switch_loop);