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