Path: blob/master/tools/testing/selftests/arm64/fp/fp-stress.c
26289 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2022 ARM Limited.3*/45#define _GNU_SOURCE6#define _POSIX_C_SOURCE 199309L78#include <errno.h>9#include <getopt.h>10#include <poll.h>11#include <signal.h>12#include <stdbool.h>13#include <stddef.h>14#include <stdio.h>15#include <stdlib.h>16#include <string.h>17#include <unistd.h>18#include <sys/auxv.h>19#include <sys/epoll.h>20#include <sys/prctl.h>21#include <sys/types.h>22#include <sys/uio.h>23#include <sys/wait.h>24#include <asm/hwcap.h>2526#include "../../kselftest.h"2728#define MAX_VLS 162930#define SIGNAL_INTERVAL_MS 2531#define LOG_INTERVALS (1000 / SIGNAL_INTERVAL_MS)3233struct child_data {34char *name, *output;35pid_t pid;36int stdout;37bool output_seen;38bool exited;39int exit_status;40};4142static int epoll_fd;43static struct child_data *children;44static struct epoll_event *evs;45static int tests;46static int num_children;47static bool terminate;4849static int startup_pipe[2];5051static int num_processors(void)52{53long nproc = sysconf(_SC_NPROCESSORS_CONF);54if (nproc < 0) {55perror("Unable to read number of processors\n");56exit(EXIT_FAILURE);57}5859return nproc;60}6162static void child_start(struct child_data *child, const char *program)63{64int ret, pipefd[2], i;65struct epoll_event ev;6667ret = pipe(pipefd);68if (ret != 0)69ksft_exit_fail_msg("Failed to create stdout pipe: %s (%d)\n",70strerror(errno), errno);7172child->pid = fork();73if (child->pid == -1)74ksft_exit_fail_msg("fork() failed: %s (%d)\n",75strerror(errno), errno);7677if (!child->pid) {78/*79* In child, replace stdout with the pipe, errors to80* stderr from here as kselftest prints to stdout.81*/82ret = dup2(pipefd[1], 1);83if (ret == -1) {84printf("dup2() %d\n", errno);85exit(EXIT_FAILURE);86}8788/*89* Duplicate the read side of the startup pipe to90* FD 3 so we can close everything else.91*/92ret = dup2(startup_pipe[0], 3);93if (ret == -1) {94printf("dup2() %d\n", errno);95exit(EXIT_FAILURE);96}9798/*99* Very dumb mechanism to clean open FDs other than100* stdio. We don't want O_CLOEXEC for the pipes...101*/102for (i = 4; i < 8192; i++)103close(i);104105/*106* Read from the startup pipe, there should be no data107* and we should block until it is closed. We just108* carry on on error since this isn't super critical.109*/110ret = read(3, &i, sizeof(i));111if (ret < 0)112printf("read(startp pipe) failed: %s (%d)\n",113strerror(errno), errno);114if (ret > 0)115printf("%d bytes of data on startup pipe\n", ret);116close(3);117118ret = execl(program, program, NULL);119printf("execl(%s) failed: %d (%s)\n",120program, errno, strerror(errno));121122exit(EXIT_FAILURE);123} else {124/*125* In parent, remember the child and close our copy of the126* write side of stdout.127*/128close(pipefd[1]);129child->stdout = pipefd[0];130child->output = NULL;131child->exited = false;132child->output_seen = false;133134ev.events = EPOLLIN | EPOLLHUP;135ev.data.ptr = child;136137ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, child->stdout, &ev);138if (ret < 0) {139ksft_exit_fail_msg("%s EPOLL_CTL_ADD failed: %s (%d)\n",140child->name, strerror(errno), errno);141}142}143}144145static bool child_output_read(struct child_data *child)146{147char read_data[1024];148char work[1024];149int ret, len, cur_work, cur_read;150151ret = read(child->stdout, read_data, sizeof(read_data));152if (ret < 0) {153if (errno == EINTR)154return true;155156ksft_print_msg("%s: read() failed: %s (%d)\n",157child->name, strerror(errno),158errno);159return false;160}161len = ret;162163child->output_seen = true;164165/* Pick up any partial read */166if (child->output) {167strncpy(work, child->output, sizeof(work) - 1);168cur_work = strnlen(work, sizeof(work));169free(child->output);170child->output = NULL;171} else {172cur_work = 0;173}174175cur_read = 0;176while (cur_read < len) {177work[cur_work] = read_data[cur_read++];178179if (work[cur_work] == '\n') {180work[cur_work] = '\0';181ksft_print_msg("%s: %s\n", child->name, work);182cur_work = 0;183} else {184cur_work++;185}186}187188if (cur_work) {189work[cur_work] = '\0';190ret = asprintf(&child->output, "%s", work);191if (ret == -1)192ksft_exit_fail_msg("Out of memory\n");193}194195return false;196}197198static void child_output(struct child_data *child, uint32_t events,199bool flush)200{201bool read_more;202203if (events & EPOLLIN) {204do {205read_more = child_output_read(child);206} while (read_more);207}208209if (events & EPOLLHUP) {210close(child->stdout);211child->stdout = -1;212flush = true;213}214215if (flush && child->output) {216ksft_print_msg("%s: %s<EOF>\n", child->name, child->output);217free(child->output);218child->output = NULL;219}220}221222static void child_tickle(struct child_data *child)223{224if (child->output_seen && !child->exited)225kill(child->pid, SIGUSR1);226}227228static void child_stop(struct child_data *child)229{230if (!child->exited)231kill(child->pid, SIGTERM);232}233234static void child_cleanup(struct child_data *child)235{236pid_t ret;237int status;238bool fail = false;239240if (!child->exited) {241do {242ret = waitpid(child->pid, &status, 0);243if (ret == -1 && errno == EINTR)244continue;245246if (ret == -1) {247ksft_print_msg("waitpid(%d) failed: %s (%d)\n",248child->pid, strerror(errno),249errno);250fail = true;251break;252}253} while (!WIFEXITED(status));254child->exit_status = WEXITSTATUS(status);255}256257if (!child->output_seen) {258ksft_print_msg("%s no output seen\n", child->name);259fail = true;260}261262if (child->exit_status != 0) {263ksft_print_msg("%s exited with error code %d\n",264child->name, child->exit_status);265fail = true;266}267268ksft_test_result(!fail, "%s\n", child->name);269}270271static void handle_child_signal(int sig, siginfo_t *info, void *context)272{273int i;274bool found = false;275276for (i = 0; i < num_children; i++) {277if (children[i].pid == info->si_pid) {278children[i].exited = true;279children[i].exit_status = info->si_status;280found = true;281break;282}283}284285if (!found)286ksft_print_msg("SIGCHLD for unknown PID %d with status %d\n",287info->si_pid, info->si_status);288}289290static void handle_exit_signal(int sig, siginfo_t *info, void *context)291{292int i;293294/* If we're already exiting then don't signal again */295if (terminate)296return;297298ksft_print_msg("Got signal, exiting...\n");299300terminate = true;301302/*303* This should be redundant, the main loop should clean up304* after us, but for safety stop everything we can here.305*/306for (i = 0; i < num_children; i++)307child_stop(&children[i]);308}309310static void start_fpsimd(struct child_data *child, int cpu, int copy)311{312int ret;313314ret = asprintf(&child->name, "FPSIMD-%d-%d", cpu, copy);315if (ret == -1)316ksft_exit_fail_msg("asprintf() failed\n");317318child_start(child, "./fpsimd-test");319320ksft_print_msg("Started %s\n", child->name);321}322323static void start_kernel(struct child_data *child, int cpu, int copy)324{325int ret;326327ret = asprintf(&child->name, "KERNEL-%d-%d", cpu, copy);328if (ret == -1)329ksft_exit_fail_msg("asprintf() failed\n");330331child_start(child, "./kernel-test");332333ksft_print_msg("Started %s\n", child->name);334}335336static void start_sve(struct child_data *child, int vl, int cpu)337{338int ret;339340ret = prctl(PR_SVE_SET_VL, vl | PR_SVE_VL_INHERIT);341if (ret < 0)342ksft_exit_fail_msg("Failed to set SVE VL %d\n", vl);343344ret = asprintf(&child->name, "SVE-VL-%d-%d", vl, cpu);345if (ret == -1)346ksft_exit_fail_msg("asprintf() failed\n");347348child_start(child, "./sve-test");349350ksft_print_msg("Started %s\n", child->name);351}352353static void start_ssve(struct child_data *child, int vl, int cpu)354{355int ret;356357ret = asprintf(&child->name, "SSVE-VL-%d-%d", vl, cpu);358if (ret == -1)359ksft_exit_fail_msg("asprintf() failed\n");360361ret = prctl(PR_SME_SET_VL, vl | PR_SME_VL_INHERIT);362if (ret < 0)363ksft_exit_fail_msg("Failed to set SME VL %d\n", ret);364365child_start(child, "./ssve-test");366367ksft_print_msg("Started %s\n", child->name);368}369370static void start_za(struct child_data *child, int vl, int cpu)371{372int ret;373374ret = prctl(PR_SME_SET_VL, vl | PR_SVE_VL_INHERIT);375if (ret < 0)376ksft_exit_fail_msg("Failed to set SME VL %d\n", ret);377378ret = asprintf(&child->name, "ZA-VL-%d-%d", vl, cpu);379if (ret == -1)380ksft_exit_fail_msg("asprintf() failed\n");381382child_start(child, "./za-test");383384ksft_print_msg("Started %s\n", child->name);385}386387static void start_zt(struct child_data *child, int cpu)388{389int ret;390391ret = asprintf(&child->name, "ZT-%d", cpu);392if (ret == -1)393ksft_exit_fail_msg("asprintf() failed\n");394395child_start(child, "./zt-test");396397ksft_print_msg("Started %s\n", child->name);398}399400static void probe_vls(int vls[], int *vl_count, int set_vl)401{402unsigned int vq;403int vl;404405*vl_count = 0;406407for (vq = SVE_VQ_MAX; vq > 0; vq /= 2) {408vl = prctl(set_vl, vq * 16);409if (vl == -1)410ksft_exit_fail_msg("SET_VL failed: %s (%d)\n",411strerror(errno), errno);412413vl &= PR_SVE_VL_LEN_MASK;414415if (*vl_count && (vl == vls[*vl_count - 1]))416break;417418vq = sve_vq_from_vl(vl);419420vls[*vl_count] = vl;421*vl_count += 1;422}423}424425/* Handle any pending output without blocking */426static void drain_output(bool flush)427{428int ret = 1;429int i;430431while (ret > 0) {432ret = epoll_wait(epoll_fd, evs, tests, 0);433if (ret < 0) {434if (errno == EINTR)435continue;436ksft_print_msg("epoll_wait() failed: %s (%d)\n",437strerror(errno), errno);438}439440for (i = 0; i < ret; i++)441child_output(evs[i].data.ptr, evs[i].events, flush);442}443}444445static const struct option options[] = {446{ "timeout", required_argument, NULL, 't' },447{ }448};449450int main(int argc, char **argv)451{452int ret;453int timeout = 10 * (1000 / SIGNAL_INTERVAL_MS);454int poll_interval = 5000;455int cpus, i, j, c;456int sve_vl_count, sme_vl_count;457bool all_children_started = false;458int seen_children;459int sve_vls[MAX_VLS], sme_vls[MAX_VLS];460bool have_sme2;461struct sigaction sa;462463while ((c = getopt_long(argc, argv, "t:", options, NULL)) != -1) {464switch (c) {465case 't':466ret = sscanf(optarg, "%d", &timeout);467if (ret != 1)468ksft_exit_fail_msg("Failed to parse timeout %s\n",469optarg);470break;471default:472ksft_exit_fail_msg("Unknown argument\n");473}474}475476cpus = num_processors();477tests = 0;478479if (getauxval(AT_HWCAP) & HWCAP_SVE) {480probe_vls(sve_vls, &sve_vl_count, PR_SVE_SET_VL);481tests += sve_vl_count * cpus;482} else {483sve_vl_count = 0;484}485486if (getauxval(AT_HWCAP2) & HWCAP2_SME) {487probe_vls(sme_vls, &sme_vl_count, PR_SME_SET_VL);488tests += sme_vl_count * cpus * 2;489} else {490sme_vl_count = 0;491}492493if (getauxval(AT_HWCAP2) & HWCAP2_SME2) {494tests += cpus;495have_sme2 = true;496} else {497have_sme2 = false;498}499500tests += cpus * 2;501502ksft_print_header();503ksft_set_plan(tests);504505ksft_print_msg("%d CPUs, %d SVE VLs, %d SME VLs, SME2 %s\n",506cpus, sve_vl_count, sme_vl_count,507have_sme2 ? "present" : "absent");508509if (timeout > 0)510ksft_print_msg("Will run for %d\n", timeout);511else512ksft_print_msg("Will run until terminated\n");513514children = calloc(sizeof(*children), tests);515if (!children)516ksft_exit_fail_msg("Unable to allocate child data\n");517518ret = epoll_create1(EPOLL_CLOEXEC);519if (ret < 0)520ksft_exit_fail_msg("epoll_create1() failed: %s (%d)\n",521strerror(errno), ret);522epoll_fd = ret;523524/* Create a pipe which children will block on before execing */525ret = pipe(startup_pipe);526if (ret != 0)527ksft_exit_fail_msg("Failed to create startup pipe: %s (%d)\n",528strerror(errno), errno);529530/* Get signal handers ready before we start any children */531memset(&sa, 0, sizeof(sa));532sa.sa_sigaction = handle_exit_signal;533sa.sa_flags = SA_RESTART | SA_SIGINFO;534sigemptyset(&sa.sa_mask);535ret = sigaction(SIGINT, &sa, NULL);536if (ret < 0)537ksft_print_msg("Failed to install SIGINT handler: %s (%d)\n",538strerror(errno), errno);539ret = sigaction(SIGTERM, &sa, NULL);540if (ret < 0)541ksft_print_msg("Failed to install SIGTERM handler: %s (%d)\n",542strerror(errno), errno);543sa.sa_sigaction = handle_child_signal;544ret = sigaction(SIGCHLD, &sa, NULL);545if (ret < 0)546ksft_print_msg("Failed to install SIGCHLD handler: %s (%d)\n",547strerror(errno), errno);548549evs = calloc(tests, sizeof(*evs));550if (!evs)551ksft_exit_fail_msg("Failed to allocated %d epoll events\n",552tests);553554for (i = 0; i < cpus; i++) {555start_fpsimd(&children[num_children++], i, 0);556start_kernel(&children[num_children++], i, 0);557558for (j = 0; j < sve_vl_count; j++)559start_sve(&children[num_children++], sve_vls[j], i);560561for (j = 0; j < sme_vl_count; j++) {562start_ssve(&children[num_children++], sme_vls[j], i);563start_za(&children[num_children++], sme_vls[j], i);564}565566if (have_sme2)567start_zt(&children[num_children++], i);568}569570/*571* All children started, close the startup pipe and let them572* run.573*/574close(startup_pipe[0]);575close(startup_pipe[1]);576577for (;;) {578/* Did we get a signal asking us to exit? */579if (terminate)580break;581582/*583* Timeout is counted in poll intervals with no584* output, the tests print during startup then are585* silent when running so this should ensure they all586* ran enough to install the signal handler, this is587* especially useful in emulation where we will both588* be slow and likely to have a large set of VLs.589*/590ret = epoll_wait(epoll_fd, evs, tests, poll_interval);591if (ret < 0) {592if (errno == EINTR)593continue;594ksft_exit_fail_msg("epoll_wait() failed: %s (%d)\n",595strerror(errno), errno);596}597598/* Output? */599if (ret > 0) {600for (i = 0; i < ret; i++) {601child_output(evs[i].data.ptr, evs[i].events,602false);603}604continue;605}606607/* Otherwise epoll_wait() timed out */608609/*610* If the child processes have not produced output they611* aren't actually running the tests yet .612*/613if (!all_children_started) {614seen_children = 0;615616for (i = 0; i < num_children; i++)617if (children[i].output_seen ||618children[i].exited)619seen_children++;620621if (seen_children != num_children) {622ksft_print_msg("Waiting for %d children\n",623num_children - seen_children);624continue;625}626627all_children_started = true;628poll_interval = SIGNAL_INTERVAL_MS;629}630631if ((timeout % LOG_INTERVALS) == 0)632ksft_print_msg("Sending signals, timeout remaining: %d\n",633timeout);634635for (i = 0; i < num_children; i++)636child_tickle(&children[i]);637638/* Negative timeout means run indefinitely */639if (timeout < 0)640continue;641if (--timeout == 0)642break;643}644645ksft_print_msg("Finishing up...\n");646terminate = true;647648for (i = 0; i < tests; i++)649child_stop(&children[i]);650651drain_output(false);652653for (i = 0; i < tests; i++)654child_cleanup(&children[i]);655656drain_output(true);657658ksft_finished();659}660661662