File Index
Files and directories
Sign in
FILE

gcs-stress.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/arm64/gcs/gcs-stress.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2022-3 ARM Limited.
  4 */
  5
  6#define _GNU_SOURCE
  7#define _POSIX_C_SOURCE 199309L
  8
  9#include <errno.h>
 10#include <getopt.h>
 11#include <poll.h>
 12#include <signal.h>
 13#include <stdbool.h>
 14#include <stddef.h>
 15#include <stdio.h>
 16#include <stdlib.h>
 17#include <string.h>
 18#include <unistd.h>
 19#include <sys/auxv.h>
 20#include <sys/epoll.h>
 21#include <sys/prctl.h>
 22#include <sys/types.h>
 23#include <sys/uio.h>
 24#include <sys/wait.h>
 25#include <asm/hwcap.h>
 26
 27#include "kselftest.h"
 28
 29struct child_data {
 30	char *name, *output;
 31	pid_t pid;
 32	int stdout;
 33	bool output_seen;
 34	bool exited;
 35	int exit_status;
 36	int exit_signal;
 37};
 38
 39static int epoll_fd;
 40static struct child_data *children;
 41static struct epoll_event *evs;
 42static int tests;
 43static int num_children;
 44static bool terminate;
 45
 46static int startup_pipe[2];
 47
 48static int num_processors(void)
 49{
 50	long nproc = sysconf(_SC_NPROCESSORS_CONF);
 51	if (nproc < 0) {
 52		perror("Unable to read number of processors\n");
 53		exit(EXIT_FAILURE);
 54	}
 55
 56	return nproc;
 57}
 58
 59static void start_thread(struct child_data *child, int id)
 60{
 61	int ret, pipefd[2], i;
 62	struct epoll_event ev;
 63
 64	ret = pipe(pipefd);
 65	if (ret != 0)
 66		ksft_exit_fail_msg("Failed to create stdout pipe: %s (%d)\n",
 67				   strerror(errno), errno);
 68
 69	child->pid = fork();
 70	if (child->pid == -1)
 71		ksft_exit_fail_msg("fork() failed: %s (%d)\n",
 72				   strerror(errno), errno);
 73
 74	if (!child->pid) {
 75		/*
 76		 * In child, replace stdout with the pipe, errors to
 77		 * stderr from here as kselftest prints to stdout.
 78		 */
 79		ret = dup2(pipefd[1], 1);
 80		if (ret == -1) {
 81			fprintf(stderr, "dup2() %d\n", errno);
 82			exit(EXIT_FAILURE);
 83		}
 84
 85		/*
 86		 * Duplicate the read side of the startup pipe to
 87		 * FD 3 so we can close everything else.
 88		 */
 89		ret = dup2(startup_pipe[0], 3);
 90		if (ret == -1) {
 91			fprintf(stderr, "dup2() %d\n", errno);
 92			exit(EXIT_FAILURE);
 93		}
 94
 95		/*
 96		 * Very dumb mechanism to clean open FDs other than
 97		 * stdio. We don't want O_CLOEXEC for the pipes...
 98		 */
 99		for (i = 4; i < 8192; i++)
100			close(i);
101
102		/*
103		 * Read from the startup pipe, there should be no data
104		 * and we should block until it is closed.  We just
105		 * carry on on error since this isn't super critical.
106		 */
107		ret = read(3, &i, sizeof(i));
108		if (ret < 0)
109			fprintf(stderr, "read(startp pipe) failed: %s (%d)\n",
110				strerror(errno), errno);
111		if (ret > 0)
112			fprintf(stderr, "%d bytes of data on startup pipe\n",
113				ret);
114		close(3);
115
116		ret = execl("gcs-stress-thread", "gcs-stress-thread", NULL);
117		fprintf(stderr, "execl(gcs-stress-thread) failed: %d (%s)\n",
118			errno, strerror(errno));
119
120		exit(EXIT_FAILURE);
121	} else {
122		/*
123		 * In parent, remember the child and close our copy of the
124		 * write side of stdout.
125		 */
126		close(pipefd[1]);
127		child->stdout = pipefd[0];
128		child->output = NULL;
129		child->exited = false;
130		child->output_seen = false;
131
132		ev.events = EPOLLIN | EPOLLHUP;
133		ev.data.ptr = child;
134
135		ret = asprintf(&child->name, "Thread-%d", id);
136		if (ret == -1)
137			ksft_exit_fail_msg("asprintf() failed\n");
138
139		ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, child->stdout, &ev);
140		if (ret < 0) {
141			ksft_exit_fail_msg("%s EPOLL_CTL_ADD failed: %s (%d)\n",
142					   child->name, strerror(errno), errno);
143		}
144	}
145
146	ksft_print_msg("Started %s\n", child->name);
147	num_children++;
148}
149
150static bool child_output_read(struct child_data *child)
151{
152	char read_data[1024];
153	char work[1024];
154	int ret, len, cur_work, cur_read;
155
156	ret = read(child->stdout, read_data, sizeof(read_data));
157	if (ret < 0) {
158		if (errno == EINTR)
159			return true;
160
161		ksft_print_msg("%s: read() failed: %s (%d)\n",
162			       child->name, strerror(errno),
163			       errno);
164		return false;
165	}
166	len = ret;
167
168	child->output_seen = true;
169
170	/* Pick up any partial read */
171	if (child->output) {
172		strncpy(work, child->output, sizeof(work) - 1);
173		cur_work = strnlen(work, sizeof(work));
174		free(child->output);
175		child->output = NULL;
176	} else {
177		cur_work = 0;
178	}
179
180	cur_read = 0;
181	while (cur_read < len) {
182		work[cur_work] = read_data[cur_read++];
183
184		if (work[cur_work] == '\n') {
185			work[cur_work] = '\0';
186			ksft_print_msg("%s: %s\n", child->name, work);
187			cur_work = 0;
188		} else {
189			cur_work++;
190		}
191	}
192
193	if (cur_work) {
194		work[cur_work] = '\0';
195		ret = asprintf(&child->output, "%s", work);
196		if (ret == -1)
197			ksft_exit_fail_msg("Out of memory\n");
198	}
199
200	return false;
201}
202
203static void child_output(struct child_data *child, uint32_t events,
204			 bool flush)
205{
206	bool read_more;
207
208	if (events & EPOLLIN) {
209		do {
210			read_more = child_output_read(child);
211		} while (read_more);
212	}
213
214	if (events & EPOLLHUP) {
215		close(child->stdout);
216		child->stdout = -1;
217		flush = true;
218	}
219
220	if (flush && child->output) {
221		ksft_print_msg("%s: %s<EOF>\n", child->name, child->output);
222		free(child->output);
223		child->output = NULL;
224	}
225}
226
227static void child_tickle(struct child_data *child)
228{
229	if (child->output_seen && !child->exited)
230		kill(child->pid, SIGUSR1);
231}
232
233static void child_stop(struct child_data *child)
234{
235	if (!child->exited)
236		kill(child->pid, SIGTERM);
237}
238
239static void child_cleanup(struct child_data *child)
240{
241	pid_t ret;
242	int status;
243	bool fail = false;
244
245	if (!child->exited) {
246		do {
247			ret = waitpid(child->pid, &status, 0);
248			if (ret == -1 && errno == EINTR)
249				continue;
250
251			if (ret == -1) {
252				ksft_print_msg("waitpid(%d) failed: %s (%d)\n",
253					       child->pid, strerror(errno),
254					       errno);
255				fail = true;
256				break;
257			}
258
259			if (WIFEXITED(status)) {
260				child->exit_status = WEXITSTATUS(status);
261				child->exited = true;
262			}
263
264			if (WIFSIGNALED(status)) {
265				child->exit_signal = WTERMSIG(status);
266				ksft_print_msg("%s: Exited due to signal %d\n",
267					       child->name, child->exit_signal);
268				fail = true;
269				child->exited = true;
270			}
271		} while (!child->exited);
272	}
273
274	if (!child->output_seen) {
275		ksft_print_msg("%s no output seen\n", child->name);
276		fail = true;
277	}
278
279	if (child->exit_status != 0) {
280		ksft_print_msg("%s exited with error code %d\n",
281			       child->name, child->exit_status);
282		fail = true;
283	}
284
285	ksft_test_result(!fail, "%s\n", child->name);
286}
287
288static void handle_child_signal(int sig, siginfo_t *info, void *context)
289{
290	int i;
291	bool found = false;
292
293	for (i = 0; i < num_children; i++) {
294		if (children[i].pid == info->si_pid) {
295			children[i].exited = true;
296			children[i].exit_status = info->si_status;
297			found = true;
298			break;
299		}
300	}
301
302	if (!found)
303		ksft_print_msg("SIGCHLD for unknown PID %d with status %d\n",
304			       info->si_pid, info->si_status);
305}
306
307static void handle_exit_signal(int sig, siginfo_t *info, void *context)
308{
309	int i;
310
311	/* If we're already exiting then don't signal again */
312	if (terminate)
313		return;
314
315	ksft_print_msg("Got signal, exiting...\n");
316
317	terminate = true;
318
319	/*
320	 * This should be redundant, the main loop should clean up
321	 * after us, but for safety stop everything we can here.
322	 */
323	for (i = 0; i < num_children; i++)
324		child_stop(&children[i]);
325}
326
327/* Handle any pending output without blocking */
328static void drain_output(bool flush)
329{
330	int ret = 1;
331	int i;
332
333	while (ret > 0) {
334		ret = epoll_wait(epoll_fd, evs, tests, 0);
335		if (ret < 0) {
336			if (errno == EINTR)
337				continue;
338			ksft_print_msg("epoll_wait() failed: %s (%d)\n",
339				       strerror(errno), errno);
340		}
341
342		for (i = 0; i < ret; i++)
343			child_output(evs[i].data.ptr, evs[i].events, flush);
344	}
345}
346
347static const struct option options[] = {
348	{ "timeout",	required_argument, NULL, 't' },
349	{ }
350};
351
352int main(int argc, char **argv)
353{
354	int seen_children;
355	bool all_children_started = false;
356	int gcs_threads;
357	int timeout = 10;
358	int ret, cpus, i, c;
359	struct sigaction sa;
360
361	while ((c = getopt_long(argc, argv, "t:", options, NULL)) != -1) {
362		switch (c) {
363		case 't':
364			ret = sscanf(optarg, "%d", &timeout);
365			if (ret != 1)
366				ksft_exit_fail_msg("Failed to parse timeout %s\n",
367						   optarg);
368			break;
369		default:
370			ksft_exit_fail_msg("Unknown argument\n");
371		}
372	}
373
374	cpus = num_processors();
375	tests = 0;
376
377	if (getauxval(AT_HWCAP) & HWCAP_GCS) {
378		/* One extra thread, trying to trigger migrations */
379		gcs_threads = cpus + 1;
380		tests += gcs_threads;
381	} else {
382		gcs_threads = 0;
383	}
384
385	ksft_print_header();
386	ksft_set_plan(tests);
387
388	ksft_print_msg("%d CPUs, %d GCS threads\n",
389		       cpus, gcs_threads);
390
391	if (!tests)
392		ksft_exit_skip("No tests scheduled\n");
393
394	if (timeout > 0)
395		ksft_print_msg("Will run for %ds\n", timeout);
396	else
397		ksft_print_msg("Will run until terminated\n");
398
399	children = calloc(sizeof(*children), tests);
400	if (!children)
401		ksft_exit_fail_msg("Unable to allocate child data\n");
402
403	ret = epoll_create1(EPOLL_CLOEXEC);
404	if (ret < 0)
405		ksft_exit_fail_msg("epoll_create1() failed: %s (%d)\n",
406				   strerror(errno), ret);
407	epoll_fd = ret;
408
409	/* Create a pipe which children will block on before execing */
410	ret = pipe(startup_pipe);
411	if (ret != 0)
412		ksft_exit_fail_msg("Failed to create startup pipe: %s (%d)\n",
413				   strerror(errno), errno);
414
415	/* Get signal handers ready before we start any children */
416	memset(&sa, 0, sizeof(sa));
417	sa.sa_sigaction = handle_exit_signal;
418	sa.sa_flags = SA_RESTART | SA_SIGINFO;
419	sigemptyset(&sa.sa_mask);
420	ret = sigaction(SIGINT, &sa, NULL);
421	if (ret < 0)
422		ksft_print_msg("Failed to install SIGINT handler: %s (%d)\n",
423			       strerror(errno), errno);
424	ret = sigaction(SIGTERM, &sa, NULL);
425	if (ret < 0)
426		ksft_print_msg("Failed to install SIGTERM handler: %s (%d)\n",
427			       strerror(errno), errno);
428	sa.sa_sigaction = handle_child_signal;
429	ret = sigaction(SIGCHLD, &sa, NULL);
430	if (ret < 0)
431		ksft_print_msg("Failed to install SIGCHLD handler: %s (%d)\n",
432			       strerror(errno), errno);
433
434	evs = calloc(tests, sizeof(*evs));
435	if (!evs)
436		ksft_exit_fail_msg("Failed to allocate %d epoll events\n",
437				   tests);
438
439	for (i = 0; i < gcs_threads; i++)
440		start_thread(&children[i], i);
441
442	/*
443	 * All children started, close the startup pipe and let them
444	 * run.
445	 */
446	close(startup_pipe[0]);
447	close(startup_pipe[1]);
448
449	timeout *= 10;
450	for (;;) {
451		/* Did we get a signal asking us to exit? */
452		if (terminate)
453			break;
454
455		/*
456		 * Timeout is counted in 100ms with no output, the
457		 * tests print during startup then are silent when
458		 * running so this should ensure they all ran enough
459		 * to install the signal handler, this is especially
460		 * useful in emulation where we will both be slow and
461		 * likely to have a large set of VLs.
462		 */
463		ret = epoll_wait(epoll_fd, evs, tests, 100);
464		if (ret < 0) {
465			if (errno == EINTR)
466				continue;
467			ksft_exit_fail_msg("epoll_wait() failed: %s (%d)\n",
468					   strerror(errno), errno);
469		}
470
471		/* Output? */
472		if (ret > 0) {
473			for (i = 0; i < ret; i++) {
474				child_output(evs[i].data.ptr, evs[i].events,
475					     false);
476			}
477			continue;
478		}
479
480		/* Otherwise epoll_wait() timed out */
481
482		/*
483		 * If the child processes have not produced output they
484		 * aren't actually running the tests yet.
485		 */
486		if (!all_children_started) {
487			seen_children = 0;
488
489			for (i = 0; i < num_children; i++)
490				if (children[i].output_seen ||
491				    children[i].exited)
492					seen_children++;
493
494			if (seen_children != num_children) {
495				ksft_print_msg("Waiting for %d children\n",
496					       num_children - seen_children);
497				continue;
498			}
499
500			all_children_started = true;
501		}
502
503		ksft_print_msg("Sending signals, timeout remaining: %d00ms\n",
504			       timeout);
505
506		for (i = 0; i < num_children; i++)
507			child_tickle(&children[i]);
508
509		/* Negative timeout means run indefinitely */
510		if (timeout < 0)
511			continue;
512		if (--timeout == 0)
513			break;
514	}
515
516	ksft_print_msg("Finishing up...\n");
517	terminate = true;
518
519	for (i = 0; i < tests; i++)
520		child_stop(&children[i]);
521
522	drain_output(false);
523
524	for (i = 0; i < tests; i++)
525		child_cleanup(&children[i]);
526
527	drain_output(true);
528
529	ksft_finished();
530}
~/ Read-only file preview.