staticint num_processors(void)
{ long nproc = sysconf(_SC_NPROCESSORS_CONF); if (nproc < 0) {
perror("Unable to read number of processors\n"); exit(EXIT_FAILURE);
}
return nproc;
}
staticvoid start_thread(struct child_data *child, int id)
{ int ret, pipefd[2], i; struct epoll_event ev;
ret = pipe(pipefd); if (ret != 0)
ksft_exit_fail_msg("Failed to create stdout pipe: %s (%d)\n",
strerror(errno), errno);
if (!child->pid) { /* * In child, replace stdout with the pipe, errors to * stderr from here as kselftest prints to stdout.
*/
ret = dup2(pipefd[1], 1); if (ret == -1) {
fprintf(stderr, "dup2() %d\n", errno); exit(EXIT_FAILURE);
}
/* * Duplicate the read side of the startup pipe to * FD 3 so we can close everything else.
*/
ret = dup2(startup_pipe[0], 3); if (ret == -1) {
fprintf(stderr, "dup2() %d\n", errno); exit(EXIT_FAILURE);
}
/* * Very dumb mechanism to clean open FDs other than * stdio. We don't want O_CLOEXEC for the pipes...
*/ for (i = 4; i < 8192; i++)
close(i);
/* * Read from the startup pipe, there should be no data * and we should block until it is closed. We just * carry on on error since this isn't super critical.
*/
ret = read(3, &i, sizeof(i)); if (ret < 0)
fprintf(stderr, "read(startp pipe) failed: %s (%d)\n",
strerror(errno), errno); if (ret > 0)
fprintf(stderr, "%d bytes of data on startup pipe\n",
ret);
close(3);
exit(EXIT_FAILURE);
} else { /* * In parent, remember the child and close our copy of the * write side of stdout.
*/
close(pipefd[1]);
child->stdout = pipefd[0];
child->output = NULL;
child->exited = false;
child->output_seen = false;
if (WIFEXITED(status)) {
child->exit_status = WEXITSTATUS(status);
child->exited = true;
}
if (WIFSIGNALED(status)) {
child->exit_signal = WTERMSIG(status);
ksft_print_msg("%s: Exited due to signal %d\n",
child->name, child->exit_signal);
fail = true;
child->exited = true;
}
} while (!child->exited);
}
if (!child->output_seen) {
ksft_print_msg("%s no output seen\n", child->name);
fail = true;
}
if (child->exit_status != 0) {
ksft_print_msg("%s exited with error code %d\n",
child->name, child->exit_status);
fail = true;
}
ksft_test_result(!fail, "%s\n", child->name);
}
staticvoid handle_child_signal(int sig, siginfo_t *info, void *context)
{ int i; bool found = false;
for (i = 0; i < num_children; i++) { if (children[i].pid == info->si_pid) {
children[i].exited = true;
children[i].exit_status = info->si_status;
found = true; break;
}
}
if (!found)
ksft_print_msg("SIGCHLD for unknown PID %d with status %d\n",
info->si_pid, info->si_status);
}
staticvoid handle_exit_signal(int sig, siginfo_t *info, void *context)
{ int i;
/* If we're already exiting then don't signal again */ if (terminate) return;
ksft_print_msg("Got signal, exiting...\n");
terminate = true;
/* * This should be redundant, the main loop should clean up * after us, but for safety stop everything we can here.
*/ for (i = 0; i < num_children; i++)
child_stop(&children[i]);
}
/* Handle any pending output without blocking */ staticvoid drain_output(bool flush)
{ int ret = 1; int i;
while (ret > 0) {
ret = epoll_wait(epoll_fd, evs, tests, 0); if (ret < 0) { if (errno == EINTR) continue;
ksft_print_msg("epoll_wait() failed: %s (%d)\n",
strerror(errno), errno);
}
for (i = 0; i < ret; i++)
child_output(evs[i].data.ptr, evs[i].events, flush);
}
}
int main(int argc, char **argv)
{ int seen_children; bool all_children_started = false; int gcs_threads; int timeout = 10; int ret, cpus, i, c; struct sigaction sa;
while ((c = getopt_long(argc, argv, "t:", options, NULL)) != -1) { switch (c) { case't':
ret = sscanf(optarg, "%d", &timeout); if (ret != 1)
ksft_exit_fail_msg("Failed to parse timeout %s\n",
optarg); break; default:
ksft_exit_fail_msg("Unknown argument\n");
}
}
cpus = num_processors();
tests = 0;
if (getauxval(AT_HWCAP) & HWCAP_GCS) { /* One extra thread, trying to trigger migrations */
gcs_threads = cpus + 1;
tests += gcs_threads;
} else {
gcs_threads = 0;
}
if (!tests)
ksft_exit_skip("No tests scheduled\n");
if (timeout > 0)
ksft_print_msg("Will run for %ds\n", timeout); else
ksft_print_msg("Will run until terminated\n");
children = calloc(sizeof(*children), tests); if (!children)
ksft_exit_fail_msg("Unable to allocate child data\n");
ret = epoll_create1(EPOLL_CLOEXEC); if (ret < 0)
ksft_exit_fail_msg("epoll_create1() failed: %s (%d)\n",
strerror(errno), ret);
epoll_fd = ret;
/* Create a pipe which children will block on before execing */
ret = pipe(startup_pipe); if (ret != 0)
ksft_exit_fail_msg("Failed to create startup pipe: %s (%d)\n",
strerror(errno), errno);
/* Get signal handers ready before we start any children */
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handle_exit_signal;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
ret = sigaction(SIGINT, &sa, NULL); if (ret < 0)
ksft_print_msg("Failed to install SIGINT handler: %s (%d)\n",
strerror(errno), errno);
ret = sigaction(SIGTERM, &sa, NULL); if (ret < 0)
ksft_print_msg("Failed to install SIGTERM handler: %s (%d)\n",
strerror(errno), errno);
sa.sa_sigaction = handle_child_signal;
ret = sigaction(SIGCHLD, &sa, NULL); if (ret < 0)
ksft_print_msg("Failed to install SIGCHLD handler: %s (%d)\n",
strerror(errno), errno);
evs = calloc(tests, sizeof(*evs)); if (!evs)
ksft_exit_fail_msg("Failed to allocated %d epoll events\n",
tests);
for (i = 0; i < gcs_threads; i++)
start_thread(&children[i], i);
/* * All children started, close the startup pipe and let them * run.
*/
close(startup_pipe[0]);
close(startup_pipe[1]);
timeout *= 10; for (;;) { /* Did we get a signal asking us to exit? */ if (terminate) break;
/* * Timeout is counted in 100ms with no output, the * tests print during startup then are silent when * running so this should ensure they all ran enough * to install the signal handler, this is especially * useful in emulation where we will both be slow and * likely to have a large set of VLs.
*/
ret = epoll_wait(epoll_fd, evs, tests, 100); if (ret < 0) { if (errno == EINTR) continue;
ksft_exit_fail_msg("epoll_wait() failed: %s (%d)\n",
strerror(errno), errno);
}
/* Output? */ if (ret > 0) { for (i = 0; i < ret; i++) {
child_output(evs[i].data.ptr, evs[i].events, false);
} continue;
}
/* Otherwise epoll_wait() timed out */
/* * If the child processes have not produced output they * aren't actually running the tests yet.
*/ if (!all_children_started) {
seen_children = 0;
for (i = 0; i < num_children; i++) if (children[i].output_seen ||
children[i].exited)
seen_children++;
if (seen_children != num_children) {
ksft_print_msg("Waiting for %d children\n",
num_children - seen_children); continue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.