root/tools/perf/tests/workloads/context_switch_loop.c

// 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);