libevl/tests/fpu-stress.c

153 lines
3.1 KiB
C

/*
* SPDX-License-Identifier: MIT
*
* Adapted from Xenomai's smokey/fpu-test test (https://xenomai.org/)
* Copyright (C) Philippe Gerum <rpm@xenomai.org>
*
* PURPOSE: stresses the out-of-band context switching code with
* respect to FPU management, checking that the unit is safely shared
* between the in-band and out-of-band execution contexts. This test
* borrows the testing logic from the 'hectic' benchmarking program.
*/
#include <sched.h>
#include <pthread.h>
#include <stdbool.h>
#include <getopt.h>
#include <unistd.h>
#include <error.h>
#include <errno.h>
#include <evl/evl.h>
#include <uapi/asm/evl/fptest.h>
#include "helpers.h"
#define TEST_PERIOD_USEC 1000UL
#define do_trace(__fmt, __args...) \
do { \
if (verbose) \
evl_printf(__fmt "\n", ##__args); \
} while (0)
static unsigned int fp_features;
static bool verbose;
static int timeout_secs = 3; /* Default to 3s runtime. */
static void *stress_loop(void *arg)
{
int tfd, ret;
__Tcall_assert(tfd, evl_attach_self("fpu-stresser:%d", getpid()));
for (;;) {
evl_set_fpregs(fp_features, 0xf1f5f1f5);
__Tcall_assert(ret, evl_usleep(TEST_PERIOD_USEC));
}
return NULL;
}
static void usage(void)
{
fprintf(stderr, "usage: fpu-stress [options]:\n");
fprintf(stderr, "-T --timeout=<seconds> seconds before test stops (0 means never/infinite) [=3]\n");
fprintf(stderr, "-v --verbose turn on verbosity\n");
}
#define short_optlist "T:v"
static const struct option options[] = {
{
.name = "timeout",
.has_arg = required_argument,
.val = 'T',
},
{
.name = "verbose",
.has_arg = no_argument,
.val = 'v',
},
{ /* Sentinel */ }
};
static void fail(unsigned int fpval, int bad_reg)
{
if (bad_reg < 0)
do_trace("FPU corruption detected");
else
do_trace("fpreg%d corrupted", bad_reg);
__Texpr_assert(0);
}
int main(int argc, char *argv[])
{
unsigned int sleep_ms, n, loops;
struct timespec ts;
int c, bad_reg;
pthread_t tid;
for (;;) {
c = getopt_long(argc, argv, short_optlist, options, NULL);
if (c == EOF)
break;
switch (c) {
case 0:
break;
case 'v':
verbose = true;
break;
case 'T':
timeout_secs = atoi(optarg);
if (timeout_secs < 0)
error(1, EINVAL, "invalid timeout");
break;
case '?':
default:
usage();
return 1;
}
}
if (optind < argc) {
usage();
return 1;
}
/*
* Do not attach to the core. We want the main thread to keep
* running as a plain POSIX thread.
*/
fp_features = evl_detect_fpu();
if (fp_features == 0)
return EXIT_NO_SUPPORT;
ts.tv_sec = 0;
ts.tv_nsec = TEST_PERIOD_USEC * 1000;
sleep_ms = 1000000UL / ts.tv_nsec;
loops = timeout_secs * 1000UL / sleep_ms;
new_thread(&tid, SCHED_FIFO, 10, stress_loop, NULL);
if (loops)
do_trace("running for %d seconds", timeout_secs);
else
do_trace("running indefinitely...");
for (n = 0; loops == 0 || n < loops; n++) {
evl_set_fpregs(fp_features, n);
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
if (evl_check_fpregs(fp_features, n, bad_reg) != n)
fail(n, bad_reg);
}
pthread_cancel(tid);
pthread_join(tid, NULL);
return 0;
}