| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Copyright 2014, Michael Ellerman, IBM Corp. | |
| */ | |
| #define _GNU_SOURCE /* For CPU_ZERO etc. */ | |
| #include <errno.h> | |
| #include <sched.h> | |
| #include <setjmp.h> | |
| #include <stdlib.h> | |
| #include <sys/wait.h> | |
| #include "utils.h" | |
| #include "lib.h" | |
| int bind_to_cpu(int cpu) | |
| { | |
| cpu_set_t mask; | |
| printf("Binding to cpu %d\n", cpu); | |
| CPU_ZERO(&mask); | |
| CPU_SET(cpu, &mask); | |
| return sched_setaffinity(0, sizeof(mask), &mask); | |
| } | |
| #define PARENT_TOKEN 0xAA | |
| #define CHILD_TOKEN 0x55 | |
| int sync_with_child(union pipe read_pipe, union pipe write_pipe) | |
| { | |
| char c = PARENT_TOKEN; | |
| FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); | |
| FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1); | |
| if (c != CHILD_TOKEN) /* sometimes expected */ | |
| return 1; | |
| return 0; | |
| } | |
| int wait_for_parent(union pipe read_pipe) | |
| { | |
| char c; | |
| FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1); | |
| FAIL_IF(c != PARENT_TOKEN); | |
| return 0; | |
| } | |
| int notify_parent(union pipe write_pipe) | |
| { | |
| char c = CHILD_TOKEN; | |
| FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); | |
| return 0; | |
| } | |
| int notify_parent_of_error(union pipe write_pipe) | |
| { | |
| char c = ~CHILD_TOKEN; | |
| FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1); | |
| return 0; | |
| } | |
| int wait_for_child(pid_t child_pid) | |
| { | |
| int rc; | |
| if (waitpid(child_pid, &rc, 0) == -1) { | |
| perror("waitpid"); | |
| return 1; | |
| } | |
| if (WIFEXITED(rc)) | |
| rc = WEXITSTATUS(rc); | |
| else | |
| rc = 1; /* Signal or other */ | |
| return rc; | |
| } | |
| int kill_child_and_wait(pid_t child_pid) | |
| { | |
| kill(child_pid, SIGTERM); | |
| return wait_for_child(child_pid); | |
| } | |
| static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe) | |
| { | |
| volatile int i = 0; | |
| /* | |
| * We are just here to eat cpu and die. So make sure we can be killed, | |
| * and also don't do any custom SIGTERM handling. | |
| */ | |
| signal(SIGTERM, SIG_DFL); | |
| notify_parent(write_pipe); | |
| wait_for_parent(read_pipe); | |
| /* Soak up cpu forever */ | |
| while (1) i++; | |
| return 0; | |
| } | |
| pid_t eat_cpu(int (test_function)(void)) | |
| { | |
| union pipe read_pipe, write_pipe; | |
| int cpu, rc; | |
| pid_t pid; | |
| cpu = pick_online_cpu(); | |
| FAIL_IF(cpu < 0); | |
| FAIL_IF(bind_to_cpu(cpu)); | |
| if (pipe(read_pipe.fds) == -1) | |
| return -1; | |
| if (pipe(write_pipe.fds) == -1) | |
| return -1; | |
| pid = fork(); | |
| if (pid == 0) | |
| exit(eat_cpu_child(write_pipe, read_pipe)); | |
| if (sync_with_child(read_pipe, write_pipe)) { | |
| rc = -1; | |
| goto out; | |
| } | |
| printf("main test running as pid %d\n", getpid()); | |
| rc = test_function(); | |
| out: | |
| kill(pid, SIGKILL); | |
| return rc; | |
| } | |
| struct addr_range libc, vdso; | |
| int parse_proc_maps(void) | |
| { | |
| unsigned long start, end; | |
| char execute, name[128]; | |
| FILE *f; | |
| int rc; | |
| f = fopen("/proc/self/maps", "r"); | |
| if (!f) { | |
| perror("fopen"); | |
| return -1; | |
| } | |
| do { | |
| /* This skips line with no executable which is what we want */ | |
| rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n", | |
| &start, &end, &execute, name); | |
| if (rc <= 0) | |
| break; | |
| if (execute != 'x') | |
| continue; | |
| if (strstr(name, "libc")) { | |
| libc.first = start; | |
| libc.last = end - 1; | |
| } else if (strstr(name, "[vdso]")) { | |
| vdso.first = start; | |
| vdso.last = end - 1; | |
| } | |
| } while(1); | |
| fclose(f); | |
| return 0; | |
| } | |
| #define PARANOID_PATH "/proc/sys/kernel/perf_event_paranoid" | |
| bool require_paranoia_below(int level) | |
| { | |
| long current; | |
| char *end, buf[16]; | |
| FILE *f; | |
| bool rc; | |
| rc = false; | |
| f = fopen(PARANOID_PATH, "r"); | |
| if (!f) { | |
| perror("fopen"); | |
| goto out; | |
| } | |
| if (!fgets(buf, sizeof(buf), f)) { | |
| printf("Couldn't read " PARANOID_PATH "?\n"); | |
| goto out_close; | |
| } | |
| current = strtol(buf, &end, 10); | |
| if (end == buf) { | |
| printf("Couldn't parse " PARANOID_PATH "?\n"); | |
| goto out_close; | |
| } | |
| if (current >= level) | |
| goto out_close; | |
| rc = true; | |
| out_close: | |
| fclose(f); | |
| out: | |
| return rc; | |
| } | |