162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2022 ARM Limited. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define _GNU_SOURCE 762306a36Sopenharmony_ci#define _POSIX_C_SOURCE 199309L 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <errno.h> 1062306a36Sopenharmony_ci#include <getopt.h> 1162306a36Sopenharmony_ci#include <poll.h> 1262306a36Sopenharmony_ci#include <signal.h> 1362306a36Sopenharmony_ci#include <stdbool.h> 1462306a36Sopenharmony_ci#include <stddef.h> 1562306a36Sopenharmony_ci#include <stdio.h> 1662306a36Sopenharmony_ci#include <stdlib.h> 1762306a36Sopenharmony_ci#include <string.h> 1862306a36Sopenharmony_ci#include <unistd.h> 1962306a36Sopenharmony_ci#include <sys/auxv.h> 2062306a36Sopenharmony_ci#include <sys/epoll.h> 2162306a36Sopenharmony_ci#include <sys/prctl.h> 2262306a36Sopenharmony_ci#include <sys/types.h> 2362306a36Sopenharmony_ci#include <sys/uio.h> 2462306a36Sopenharmony_ci#include <sys/wait.h> 2562306a36Sopenharmony_ci#include <asm/hwcap.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "../../kselftest.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define MAX_VLS 16 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct child_data { 3262306a36Sopenharmony_ci char *name, *output; 3362306a36Sopenharmony_ci pid_t pid; 3462306a36Sopenharmony_ci int stdout; 3562306a36Sopenharmony_ci bool output_seen; 3662306a36Sopenharmony_ci bool exited; 3762306a36Sopenharmony_ci int exit_status; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int epoll_fd; 4162306a36Sopenharmony_cistatic struct child_data *children; 4262306a36Sopenharmony_cistatic struct epoll_event *evs; 4362306a36Sopenharmony_cistatic int tests; 4462306a36Sopenharmony_cistatic int num_children; 4562306a36Sopenharmony_cistatic bool terminate; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int startup_pipe[2]; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int num_processors(void) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci long nproc = sysconf(_SC_NPROCESSORS_CONF); 5262306a36Sopenharmony_ci if (nproc < 0) { 5362306a36Sopenharmony_ci perror("Unable to read number of processors\n"); 5462306a36Sopenharmony_ci exit(EXIT_FAILURE); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return nproc; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void child_start(struct child_data *child, const char *program) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci int ret, pipefd[2], i; 6362306a36Sopenharmony_ci struct epoll_event ev; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ret = pipe(pipefd); 6662306a36Sopenharmony_ci if (ret != 0) 6762306a36Sopenharmony_ci ksft_exit_fail_msg("Failed to create stdout pipe: %s (%d)\n", 6862306a36Sopenharmony_ci strerror(errno), errno); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci child->pid = fork(); 7162306a36Sopenharmony_ci if (child->pid == -1) 7262306a36Sopenharmony_ci ksft_exit_fail_msg("fork() failed: %s (%d)\n", 7362306a36Sopenharmony_ci strerror(errno), errno); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!child->pid) { 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * In child, replace stdout with the pipe, errors to 7862306a36Sopenharmony_ci * stderr from here as kselftest prints to stdout. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci ret = dup2(pipefd[1], 1); 8162306a36Sopenharmony_ci if (ret == -1) { 8262306a36Sopenharmony_ci fprintf(stderr, "dup2() %d\n", errno); 8362306a36Sopenharmony_ci exit(EXIT_FAILURE); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * Duplicate the read side of the startup pipe to 8862306a36Sopenharmony_ci * FD 3 so we can close everything else. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci ret = dup2(startup_pipe[0], 3); 9162306a36Sopenharmony_ci if (ret == -1) { 9262306a36Sopenharmony_ci fprintf(stderr, "dup2() %d\n", errno); 9362306a36Sopenharmony_ci exit(EXIT_FAILURE); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * Very dumb mechanism to clean open FDs other than 9862306a36Sopenharmony_ci * stdio. We don't want O_CLOEXEC for the pipes... 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci for (i = 4; i < 8192; i++) 10162306a36Sopenharmony_ci close(i); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * Read from the startup pipe, there should be no data 10562306a36Sopenharmony_ci * and we should block until it is closed. We just 10662306a36Sopenharmony_ci * carry on on error since this isn't super critical. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci ret = read(3, &i, sizeof(i)); 10962306a36Sopenharmony_ci if (ret < 0) 11062306a36Sopenharmony_ci fprintf(stderr, "read(startp pipe) failed: %s (%d)\n", 11162306a36Sopenharmony_ci strerror(errno), errno); 11262306a36Sopenharmony_ci if (ret > 0) 11362306a36Sopenharmony_ci fprintf(stderr, "%d bytes of data on startup pipe\n", 11462306a36Sopenharmony_ci ret); 11562306a36Sopenharmony_ci close(3); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci ret = execl(program, program, NULL); 11862306a36Sopenharmony_ci fprintf(stderr, "execl(%s) failed: %d (%s)\n", 11962306a36Sopenharmony_ci program, errno, strerror(errno)); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci exit(EXIT_FAILURE); 12262306a36Sopenharmony_ci } else { 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * In parent, remember the child and close our copy of the 12562306a36Sopenharmony_ci * write side of stdout. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci close(pipefd[1]); 12862306a36Sopenharmony_ci child->stdout = pipefd[0]; 12962306a36Sopenharmony_ci child->output = NULL; 13062306a36Sopenharmony_ci child->exited = false; 13162306a36Sopenharmony_ci child->output_seen = false; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci ev.events = EPOLLIN | EPOLLHUP; 13462306a36Sopenharmony_ci ev.data.ptr = child; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, child->stdout, &ev); 13762306a36Sopenharmony_ci if (ret < 0) { 13862306a36Sopenharmony_ci ksft_exit_fail_msg("%s EPOLL_CTL_ADD failed: %s (%d)\n", 13962306a36Sopenharmony_ci child->name, strerror(errno), errno); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic bool child_output_read(struct child_data *child) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci char read_data[1024]; 14762306a36Sopenharmony_ci char work[1024]; 14862306a36Sopenharmony_ci int ret, len, cur_work, cur_read; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ret = read(child->stdout, read_data, sizeof(read_data)); 15162306a36Sopenharmony_ci if (ret < 0) { 15262306a36Sopenharmony_ci if (errno == EINTR) 15362306a36Sopenharmony_ci return true; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ksft_print_msg("%s: read() failed: %s (%d)\n", 15662306a36Sopenharmony_ci child->name, strerror(errno), 15762306a36Sopenharmony_ci errno); 15862306a36Sopenharmony_ci return false; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci len = ret; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci child->output_seen = true; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* Pick up any partial read */ 16562306a36Sopenharmony_ci if (child->output) { 16662306a36Sopenharmony_ci strncpy(work, child->output, sizeof(work) - 1); 16762306a36Sopenharmony_ci cur_work = strnlen(work, sizeof(work)); 16862306a36Sopenharmony_ci free(child->output); 16962306a36Sopenharmony_ci child->output = NULL; 17062306a36Sopenharmony_ci } else { 17162306a36Sopenharmony_ci cur_work = 0; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci cur_read = 0; 17562306a36Sopenharmony_ci while (cur_read < len) { 17662306a36Sopenharmony_ci work[cur_work] = read_data[cur_read++]; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (work[cur_work] == '\n') { 17962306a36Sopenharmony_ci work[cur_work] = '\0'; 18062306a36Sopenharmony_ci ksft_print_msg("%s: %s\n", child->name, work); 18162306a36Sopenharmony_ci cur_work = 0; 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci cur_work++; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (cur_work) { 18862306a36Sopenharmony_ci work[cur_work] = '\0'; 18962306a36Sopenharmony_ci ret = asprintf(&child->output, "%s", work); 19062306a36Sopenharmony_ci if (ret == -1) 19162306a36Sopenharmony_ci ksft_exit_fail_msg("Out of memory\n"); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return false; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void child_output(struct child_data *child, uint32_t events, 19862306a36Sopenharmony_ci bool flush) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci bool read_more; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (events & EPOLLIN) { 20362306a36Sopenharmony_ci do { 20462306a36Sopenharmony_ci read_more = child_output_read(child); 20562306a36Sopenharmony_ci } while (read_more); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (events & EPOLLHUP) { 20962306a36Sopenharmony_ci close(child->stdout); 21062306a36Sopenharmony_ci child->stdout = -1; 21162306a36Sopenharmony_ci flush = true; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (flush && child->output) { 21562306a36Sopenharmony_ci ksft_print_msg("%s: %s<EOF>\n", child->name, child->output); 21662306a36Sopenharmony_ci free(child->output); 21762306a36Sopenharmony_ci child->output = NULL; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic void child_tickle(struct child_data *child) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci if (child->output_seen && !child->exited) 22462306a36Sopenharmony_ci kill(child->pid, SIGUSR2); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void child_stop(struct child_data *child) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci if (!child->exited) 23062306a36Sopenharmony_ci kill(child->pid, SIGTERM); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void child_cleanup(struct child_data *child) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci pid_t ret; 23662306a36Sopenharmony_ci int status; 23762306a36Sopenharmony_ci bool fail = false; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!child->exited) { 24062306a36Sopenharmony_ci do { 24162306a36Sopenharmony_ci ret = waitpid(child->pid, &status, 0); 24262306a36Sopenharmony_ci if (ret == -1 && errno == EINTR) 24362306a36Sopenharmony_ci continue; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (ret == -1) { 24662306a36Sopenharmony_ci ksft_print_msg("waitpid(%d) failed: %s (%d)\n", 24762306a36Sopenharmony_ci child->pid, strerror(errno), 24862306a36Sopenharmony_ci errno); 24962306a36Sopenharmony_ci fail = true; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci } while (!WIFEXITED(status)); 25362306a36Sopenharmony_ci child->exit_status = WEXITSTATUS(status); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (!child->output_seen) { 25762306a36Sopenharmony_ci ksft_print_msg("%s no output seen\n", child->name); 25862306a36Sopenharmony_ci fail = true; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (child->exit_status != 0) { 26262306a36Sopenharmony_ci ksft_print_msg("%s exited with error code %d\n", 26362306a36Sopenharmony_ci child->name, child->exit_status); 26462306a36Sopenharmony_ci fail = true; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ksft_test_result(!fail, "%s\n", child->name); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void handle_child_signal(int sig, siginfo_t *info, void *context) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci int i; 27362306a36Sopenharmony_ci bool found = false; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci for (i = 0; i < num_children; i++) { 27662306a36Sopenharmony_ci if (children[i].pid == info->si_pid) { 27762306a36Sopenharmony_ci children[i].exited = true; 27862306a36Sopenharmony_ci children[i].exit_status = info->si_status; 27962306a36Sopenharmony_ci found = true; 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (!found) 28562306a36Sopenharmony_ci ksft_print_msg("SIGCHLD for unknown PID %d with status %d\n", 28662306a36Sopenharmony_ci info->si_pid, info->si_status); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void handle_exit_signal(int sig, siginfo_t *info, void *context) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci int i; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* If we're already exiting then don't signal again */ 29462306a36Sopenharmony_ci if (terminate) 29562306a36Sopenharmony_ci return; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ksft_print_msg("Got signal, exiting...\n"); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci terminate = true; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * This should be redundant, the main loop should clean up 30362306a36Sopenharmony_ci * after us, but for safety stop everything we can here. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci for (i = 0; i < num_children; i++) 30662306a36Sopenharmony_ci child_stop(&children[i]); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void start_fpsimd(struct child_data *child, int cpu, int copy) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci int ret; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ret = asprintf(&child->name, "FPSIMD-%d-%d", cpu, copy); 31462306a36Sopenharmony_ci if (ret == -1) 31562306a36Sopenharmony_ci ksft_exit_fail_msg("asprintf() failed\n"); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci child_start(child, "./fpsimd-test"); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ksft_print_msg("Started %s\n", child->name); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void start_sve(struct child_data *child, int vl, int cpu) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci int ret; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ret = prctl(PR_SVE_SET_VL, vl | PR_SVE_VL_INHERIT); 32762306a36Sopenharmony_ci if (ret < 0) 32862306a36Sopenharmony_ci ksft_exit_fail_msg("Failed to set SVE VL %d\n", vl); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci ret = asprintf(&child->name, "SVE-VL-%d-%d", vl, cpu); 33162306a36Sopenharmony_ci if (ret == -1) 33262306a36Sopenharmony_ci ksft_exit_fail_msg("asprintf() failed\n"); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci child_start(child, "./sve-test"); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci ksft_print_msg("Started %s\n", child->name); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic void start_ssve(struct child_data *child, int vl, int cpu) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci int ret; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ret = asprintf(&child->name, "SSVE-VL-%d-%d", vl, cpu); 34462306a36Sopenharmony_ci if (ret == -1) 34562306a36Sopenharmony_ci ksft_exit_fail_msg("asprintf() failed\n"); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ret = prctl(PR_SME_SET_VL, vl | PR_SME_VL_INHERIT); 34862306a36Sopenharmony_ci if (ret < 0) 34962306a36Sopenharmony_ci ksft_exit_fail_msg("Failed to set SME VL %d\n", ret); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci child_start(child, "./ssve-test"); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ksft_print_msg("Started %s\n", child->name); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic void start_za(struct child_data *child, int vl, int cpu) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci int ret; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci ret = prctl(PR_SME_SET_VL, vl | PR_SVE_VL_INHERIT); 36162306a36Sopenharmony_ci if (ret < 0) 36262306a36Sopenharmony_ci ksft_exit_fail_msg("Failed to set SME VL %d\n", ret); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ret = asprintf(&child->name, "ZA-VL-%d-%d", vl, cpu); 36562306a36Sopenharmony_ci if (ret == -1) 36662306a36Sopenharmony_ci ksft_exit_fail_msg("asprintf() failed\n"); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci child_start(child, "./za-test"); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci ksft_print_msg("Started %s\n", child->name); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void start_zt(struct child_data *child, int cpu) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci int ret; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci ret = asprintf(&child->name, "ZT-%d", cpu); 37862306a36Sopenharmony_ci if (ret == -1) 37962306a36Sopenharmony_ci ksft_exit_fail_msg("asprintf() failed\n"); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci child_start(child, "./zt-test"); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ksft_print_msg("Started %s\n", child->name); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void probe_vls(int vls[], int *vl_count, int set_vl) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci unsigned int vq; 38962306a36Sopenharmony_ci int vl; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci *vl_count = 0; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci for (vq = SVE_VQ_MAX; vq > 0; vq /= 2) { 39462306a36Sopenharmony_ci vl = prctl(set_vl, vq * 16); 39562306a36Sopenharmony_ci if (vl == -1) 39662306a36Sopenharmony_ci ksft_exit_fail_msg("SET_VL failed: %s (%d)\n", 39762306a36Sopenharmony_ci strerror(errno), errno); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci vl &= PR_SVE_VL_LEN_MASK; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (*vl_count && (vl == vls[*vl_count - 1])) 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci vq = sve_vq_from_vl(vl); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci vls[*vl_count] = vl; 40762306a36Sopenharmony_ci *vl_count += 1; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/* Handle any pending output without blocking */ 41262306a36Sopenharmony_cistatic void drain_output(bool flush) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci int ret = 1; 41562306a36Sopenharmony_ci int i; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci while (ret > 0) { 41862306a36Sopenharmony_ci ret = epoll_wait(epoll_fd, evs, tests, 0); 41962306a36Sopenharmony_ci if (ret < 0) { 42062306a36Sopenharmony_ci if (errno == EINTR) 42162306a36Sopenharmony_ci continue; 42262306a36Sopenharmony_ci ksft_print_msg("epoll_wait() failed: %s (%d)\n", 42362306a36Sopenharmony_ci strerror(errno), errno); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci for (i = 0; i < ret; i++) 42762306a36Sopenharmony_ci child_output(evs[i].data.ptr, evs[i].events, flush); 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic const struct option options[] = { 43262306a36Sopenharmony_ci { "timeout", required_argument, NULL, 't' }, 43362306a36Sopenharmony_ci { } 43462306a36Sopenharmony_ci}; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ciint main(int argc, char **argv) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int ret; 43962306a36Sopenharmony_ci int timeout = 10; 44062306a36Sopenharmony_ci int cpus, i, j, c; 44162306a36Sopenharmony_ci int sve_vl_count, sme_vl_count, fpsimd_per_cpu; 44262306a36Sopenharmony_ci bool all_children_started = false; 44362306a36Sopenharmony_ci int seen_children; 44462306a36Sopenharmony_ci int sve_vls[MAX_VLS], sme_vls[MAX_VLS]; 44562306a36Sopenharmony_ci bool have_sme2; 44662306a36Sopenharmony_ci struct sigaction sa; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci while ((c = getopt_long(argc, argv, "t:", options, NULL)) != -1) { 44962306a36Sopenharmony_ci switch (c) { 45062306a36Sopenharmony_ci case 't': 45162306a36Sopenharmony_ci ret = sscanf(optarg, "%d", &timeout); 45262306a36Sopenharmony_ci if (ret != 1) 45362306a36Sopenharmony_ci ksft_exit_fail_msg("Failed to parse timeout %s\n", 45462306a36Sopenharmony_ci optarg); 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci default: 45762306a36Sopenharmony_ci ksft_exit_fail_msg("Unknown argument\n"); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci cpus = num_processors(); 46262306a36Sopenharmony_ci tests = 0; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (getauxval(AT_HWCAP) & HWCAP_SVE) { 46562306a36Sopenharmony_ci probe_vls(sve_vls, &sve_vl_count, PR_SVE_SET_VL); 46662306a36Sopenharmony_ci tests += sve_vl_count * cpus; 46762306a36Sopenharmony_ci } else { 46862306a36Sopenharmony_ci sve_vl_count = 0; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (getauxval(AT_HWCAP2) & HWCAP2_SME) { 47262306a36Sopenharmony_ci probe_vls(sme_vls, &sme_vl_count, PR_SME_SET_VL); 47362306a36Sopenharmony_ci tests += sme_vl_count * cpus * 2; 47462306a36Sopenharmony_ci } else { 47562306a36Sopenharmony_ci sme_vl_count = 0; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (getauxval(AT_HWCAP2) & HWCAP2_SME2) { 47962306a36Sopenharmony_ci tests += cpus; 48062306a36Sopenharmony_ci have_sme2 = true; 48162306a36Sopenharmony_ci } else { 48262306a36Sopenharmony_ci have_sme2 = false; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Force context switching if we only have FPSIMD */ 48662306a36Sopenharmony_ci if (!sve_vl_count && !sme_vl_count) 48762306a36Sopenharmony_ci fpsimd_per_cpu = 2; 48862306a36Sopenharmony_ci else 48962306a36Sopenharmony_ci fpsimd_per_cpu = 1; 49062306a36Sopenharmony_ci tests += cpus * fpsimd_per_cpu; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ksft_print_header(); 49362306a36Sopenharmony_ci ksft_set_plan(tests); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ksft_print_msg("%d CPUs, %d SVE VLs, %d SME VLs, SME2 %s\n", 49662306a36Sopenharmony_ci cpus, sve_vl_count, sme_vl_count, 49762306a36Sopenharmony_ci have_sme2 ? "present" : "absent"); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (timeout > 0) 50062306a36Sopenharmony_ci ksft_print_msg("Will run for %ds\n", timeout); 50162306a36Sopenharmony_ci else 50262306a36Sopenharmony_ci ksft_print_msg("Will run until terminated\n"); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci children = calloc(sizeof(*children), tests); 50562306a36Sopenharmony_ci if (!children) 50662306a36Sopenharmony_ci ksft_exit_fail_msg("Unable to allocate child data\n"); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ret = epoll_create1(EPOLL_CLOEXEC); 50962306a36Sopenharmony_ci if (ret < 0) 51062306a36Sopenharmony_ci ksft_exit_fail_msg("epoll_create1() failed: %s (%d)\n", 51162306a36Sopenharmony_ci strerror(errno), ret); 51262306a36Sopenharmony_ci epoll_fd = ret; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Create a pipe which children will block on before execing */ 51562306a36Sopenharmony_ci ret = pipe(startup_pipe); 51662306a36Sopenharmony_ci if (ret != 0) 51762306a36Sopenharmony_ci ksft_exit_fail_msg("Failed to create startup pipe: %s (%d)\n", 51862306a36Sopenharmony_ci strerror(errno), errno); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Get signal handers ready before we start any children */ 52162306a36Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 52262306a36Sopenharmony_ci sa.sa_sigaction = handle_exit_signal; 52362306a36Sopenharmony_ci sa.sa_flags = SA_RESTART | SA_SIGINFO; 52462306a36Sopenharmony_ci sigemptyset(&sa.sa_mask); 52562306a36Sopenharmony_ci ret = sigaction(SIGINT, &sa, NULL); 52662306a36Sopenharmony_ci if (ret < 0) 52762306a36Sopenharmony_ci ksft_print_msg("Failed to install SIGINT handler: %s (%d)\n", 52862306a36Sopenharmony_ci strerror(errno), errno); 52962306a36Sopenharmony_ci ret = sigaction(SIGTERM, &sa, NULL); 53062306a36Sopenharmony_ci if (ret < 0) 53162306a36Sopenharmony_ci ksft_print_msg("Failed to install SIGTERM handler: %s (%d)\n", 53262306a36Sopenharmony_ci strerror(errno), errno); 53362306a36Sopenharmony_ci sa.sa_sigaction = handle_child_signal; 53462306a36Sopenharmony_ci ret = sigaction(SIGCHLD, &sa, NULL); 53562306a36Sopenharmony_ci if (ret < 0) 53662306a36Sopenharmony_ci ksft_print_msg("Failed to install SIGCHLD handler: %s (%d)\n", 53762306a36Sopenharmony_ci strerror(errno), errno); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci evs = calloc(tests, sizeof(*evs)); 54062306a36Sopenharmony_ci if (!evs) 54162306a36Sopenharmony_ci ksft_exit_fail_msg("Failed to allocated %d epoll events\n", 54262306a36Sopenharmony_ci tests); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci for (i = 0; i < cpus; i++) { 54562306a36Sopenharmony_ci for (j = 0; j < fpsimd_per_cpu; j++) 54662306a36Sopenharmony_ci start_fpsimd(&children[num_children++], i, j); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci for (j = 0; j < sve_vl_count; j++) 54962306a36Sopenharmony_ci start_sve(&children[num_children++], sve_vls[j], i); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci for (j = 0; j < sme_vl_count; j++) { 55262306a36Sopenharmony_ci start_ssve(&children[num_children++], sme_vls[j], i); 55362306a36Sopenharmony_ci start_za(&children[num_children++], sme_vls[j], i); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (have_sme2) 55762306a36Sopenharmony_ci start_zt(&children[num_children++], i); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* 56162306a36Sopenharmony_ci * All children started, close the startup pipe and let them 56262306a36Sopenharmony_ci * run. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci close(startup_pipe[0]); 56562306a36Sopenharmony_ci close(startup_pipe[1]); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci for (;;) { 56862306a36Sopenharmony_ci /* Did we get a signal asking us to exit? */ 56962306a36Sopenharmony_ci if (terminate) 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* 57362306a36Sopenharmony_ci * Timeout is counted in seconds with no output, the 57462306a36Sopenharmony_ci * tests print during startup then are silent when 57562306a36Sopenharmony_ci * running so this should ensure they all ran enough 57662306a36Sopenharmony_ci * to install the signal handler, this is especially 57762306a36Sopenharmony_ci * useful in emulation where we will both be slow and 57862306a36Sopenharmony_ci * likely to have a large set of VLs. 57962306a36Sopenharmony_ci */ 58062306a36Sopenharmony_ci ret = epoll_wait(epoll_fd, evs, tests, 1000); 58162306a36Sopenharmony_ci if (ret < 0) { 58262306a36Sopenharmony_ci if (errno == EINTR) 58362306a36Sopenharmony_ci continue; 58462306a36Sopenharmony_ci ksft_exit_fail_msg("epoll_wait() failed: %s (%d)\n", 58562306a36Sopenharmony_ci strerror(errno), errno); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* Output? */ 58962306a36Sopenharmony_ci if (ret > 0) { 59062306a36Sopenharmony_ci for (i = 0; i < ret; i++) { 59162306a36Sopenharmony_ci child_output(evs[i].data.ptr, evs[i].events, 59262306a36Sopenharmony_ci false); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci continue; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Otherwise epoll_wait() timed out */ 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* 60062306a36Sopenharmony_ci * If the child processes have not produced output they 60162306a36Sopenharmony_ci * aren't actually running the tests yet . 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_ci if (!all_children_started) { 60462306a36Sopenharmony_ci seen_children = 0; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci for (i = 0; i < num_children; i++) 60762306a36Sopenharmony_ci if (children[i].output_seen || 60862306a36Sopenharmony_ci children[i].exited) 60962306a36Sopenharmony_ci seen_children++; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (seen_children != num_children) { 61262306a36Sopenharmony_ci ksft_print_msg("Waiting for %d children\n", 61362306a36Sopenharmony_ci num_children - seen_children); 61462306a36Sopenharmony_ci continue; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci all_children_started = true; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ksft_print_msg("Sending signals, timeout remaining: %d\n", 62162306a36Sopenharmony_ci timeout); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci for (i = 0; i < num_children; i++) 62462306a36Sopenharmony_ci child_tickle(&children[i]); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* Negative timeout means run indefinitely */ 62762306a36Sopenharmony_ci if (timeout < 0) 62862306a36Sopenharmony_ci continue; 62962306a36Sopenharmony_ci if (--timeout == 0) 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ksft_print_msg("Finishing up...\n"); 63462306a36Sopenharmony_ci terminate = true; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci for (i = 0; i < tests; i++) 63762306a36Sopenharmony_ci child_stop(&children[i]); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci drain_output(false); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci for (i = 0; i < tests; i++) 64262306a36Sopenharmony_ci child_cleanup(&children[i]); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci drain_output(true); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ksft_print_cnts(); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci return 0; 64962306a36Sopenharmony_ci} 650