Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kselftest/arm64: Add a GCS stress test

Add a stress test which runs one more process than we have CPUs spinning
through a very recursive function with frequent syscalls immediately prior
to return and signals being injected every 100ms. The goal is to flag up
any scheduling related issues, for example failure to ensure that barriers
are inserted when moving a GCS using task to another CPU. The test runs for
a configurable amount of time, defaulting to 10 seconds.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Tested-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20241001-arm64-gcs-v13-38-222b78d87eee@kernel.org
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Mark Brown and committed by
Catalin Marinas
05e6cfff 794b64ca

+848 -1
+2
tools/testing/selftests/arm64/gcs/.gitignore
··· 1 1 basic-gcs 2 2 libc-gcs 3 3 gcs-locking 4 + gcs-stress 5 + gcs-stress-thread
+5 -1
tools/testing/selftests/arm64/gcs/Makefile
··· 6 6 # nolibc. 7 7 # 8 8 9 - TEST_GEN_PROGS := basic-gcs libc-gcs gcs-locking 9 + TEST_GEN_PROGS := basic-gcs libc-gcs gcs-locking gcs-stress 10 + TEST_GEN_PROGS_EXTENDED := gcs-stress-thread 10 11 11 12 LDLIBS+=-lpthread 12 13 ··· 19 18 -I../../../../../usr/include \ 20 19 -std=gnu99 -I../.. -g \ 21 20 -ffreestanding -Wall $^ -o $@ -lgcc 21 + 22 + $(OUTPUT)/gcs-stress-thread: gcs-stress-thread.S 23 + $(CC) -nostdlib $^ -o $@
tools/testing/selftests/arm64/gcs/asm-offsets.h
+311
tools/testing/selftests/arm64/gcs/gcs-stress-thread.S
··· 1 + // Program that loops for ever doing lots of recursions and system calls, 2 + // intended to be used as part of a stress test for GCS context switching. 3 + // 4 + // Copyright 2015-2023 Arm Ltd 5 + 6 + #include <asm/unistd.h> 7 + 8 + #define sa_sz 32 9 + #define sa_flags 8 10 + #define sa_handler 0 11 + #define sa_mask_sz 8 12 + 13 + #define si_code 8 14 + 15 + #define SIGINT 2 16 + #define SIGABRT 6 17 + #define SIGUSR1 10 18 + #define SIGSEGV 11 19 + #define SIGUSR2 12 20 + #define SIGTERM 15 21 + #define SEGV_CPERR 10 22 + 23 + #define SA_NODEFER 1073741824 24 + #define SA_SIGINFO 4 25 + #define ucontext_regs 184 26 + 27 + #define PR_SET_SHADOW_STACK_STATUS 75 28 + # define PR_SHADOW_STACK_ENABLE (1UL << 0) 29 + 30 + #define GCSPR_EL0 S3_3_C2_C5_1 31 + 32 + .macro function name 33 + .macro endfunction 34 + .type \name, @function 35 + .purgem endfunction 36 + .endm 37 + \name: 38 + .endm 39 + 40 + // Print a single character x0 to stdout 41 + // Clobbers x0-x2,x8 42 + function putc 43 + str x0, [sp, #-16]! 44 + 45 + mov x0, #1 // STDOUT_FILENO 46 + mov x1, sp 47 + mov x2, #1 48 + mov x8, #__NR_write 49 + svc #0 50 + 51 + add sp, sp, #16 52 + ret 53 + endfunction 54 + .globl putc 55 + 56 + // Print a NUL-terminated string starting at address x0 to stdout 57 + // Clobbers x0-x3,x8 58 + function puts 59 + mov x1, x0 60 + 61 + mov x2, #0 62 + 0: ldrb w3, [x0], #1 63 + cbz w3, 1f 64 + add x2, x2, #1 65 + b 0b 66 + 67 + 1: mov w0, #1 // STDOUT_FILENO 68 + mov x8, #__NR_write 69 + svc #0 70 + 71 + ret 72 + endfunction 73 + .globl puts 74 + 75 + // Utility macro to print a literal string 76 + // Clobbers x0-x4,x8 77 + .macro puts string 78 + .pushsection .rodata.str1.1, "aMS", @progbits, 1 79 + .L__puts_literal\@: .string "\string" 80 + .popsection 81 + 82 + ldr x0, =.L__puts_literal\@ 83 + bl puts 84 + .endm 85 + 86 + // Print an unsigned decimal number x0 to stdout 87 + // Clobbers x0-x4,x8 88 + function putdec 89 + mov x1, sp 90 + str x30, [sp, #-32]! // Result can't be > 20 digits 91 + 92 + mov x2, #0 93 + strb w2, [x1, #-1]! // Write the NUL terminator 94 + 95 + mov x2, #10 96 + 0: udiv x3, x0, x2 // div-mod loop to generate the digits 97 + msub x0, x3, x2, x0 98 + add w0, w0, #'0' 99 + strb w0, [x1, #-1]! 100 + mov x0, x3 101 + cbnz x3, 0b 102 + 103 + ldrb w0, [x1] 104 + cbnz w0, 1f 105 + mov w0, #'0' // Print "0" for 0, not "" 106 + strb w0, [x1, #-1]! 107 + 108 + 1: mov x0, x1 109 + bl puts 110 + 111 + ldr x30, [sp], #32 112 + ret 113 + endfunction 114 + .globl putdec 115 + 116 + // Print an unsigned decimal number x0 to stdout, followed by a newline 117 + // Clobbers x0-x5,x8 118 + function putdecn 119 + mov x5, x30 120 + 121 + bl putdec 122 + mov x0, #'\n' 123 + bl putc 124 + 125 + ret x5 126 + endfunction 127 + .globl putdecn 128 + 129 + // Fill x1 bytes starting at x0 with 0. 130 + // Clobbers x1, x2. 131 + function memclr 132 + mov w2, #0 133 + endfunction 134 + .globl memclr 135 + // fall through to memfill 136 + 137 + // Trivial memory fill: fill x1 bytes starting at address x0 with byte w2 138 + // Clobbers x1 139 + function memfill 140 + cmp x1, #0 141 + b.eq 1f 142 + 143 + 0: strb w2, [x0], #1 144 + subs x1, x1, #1 145 + b.ne 0b 146 + 147 + 1: ret 148 + endfunction 149 + .globl memfill 150 + 151 + // w0: signal number 152 + // x1: sa_action 153 + // w2: sa_flags 154 + // Clobbers x0-x6,x8 155 + function setsignal 156 + str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]! 157 + 158 + mov w4, w0 159 + mov x5, x1 160 + mov w6, w2 161 + 162 + add x0, sp, #16 163 + mov x1, #sa_sz 164 + bl memclr 165 + 166 + mov w0, w4 167 + add x1, sp, #16 168 + str w6, [x1, #sa_flags] 169 + str x5, [x1, #sa_handler] 170 + mov x2, #0 171 + mov x3, #sa_mask_sz 172 + mov x8, #__NR_rt_sigaction 173 + svc #0 174 + 175 + cbz w0, 1f 176 + 177 + puts "sigaction failure\n" 178 + b abort 179 + 180 + 1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16) 181 + ret 182 + endfunction 183 + 184 + 185 + function tickle_handler 186 + // Perhaps collect GCSPR_EL0 here in future? 187 + ret 188 + endfunction 189 + 190 + function terminate_handler 191 + mov w21, w0 192 + mov x20, x2 193 + 194 + puts "Terminated by signal " 195 + mov w0, w21 196 + bl putdec 197 + puts ", no error\n" 198 + 199 + mov x0, #0 200 + mov x8, #__NR_exit 201 + svc #0 202 + endfunction 203 + 204 + function segv_handler 205 + // stash the siginfo_t * 206 + mov x20, x1 207 + 208 + // Disable GCS, we don't want additional faults logging things 209 + mov x0, PR_SET_SHADOW_STACK_STATUS 210 + mov x1, xzr 211 + mov x2, xzr 212 + mov x3, xzr 213 + mov x4, xzr 214 + mov x5, xzr 215 + mov x8, #__NR_prctl 216 + svc #0 217 + 218 + puts "Got SIGSEGV code " 219 + 220 + ldr x21, [x20, #si_code] 221 + mov x0, x21 222 + bl putdec 223 + 224 + // GCS faults should have si_code SEGV_CPERR 225 + cmp x21, #SEGV_CPERR 226 + bne 1f 227 + 228 + puts " (GCS violation)" 229 + 1: 230 + mov x0, '\n' 231 + bl putc 232 + b abort 233 + endfunction 234 + 235 + // Recurse x20 times 236 + .macro recurse id 237 + function recurse\id 238 + stp x29, x30, [sp, #-16]! 239 + mov x29, sp 240 + 241 + cmp x20, 0 242 + beq 1f 243 + sub x20, x20, 1 244 + bl recurse\id 245 + 246 + 1: 247 + ldp x29, x30, [sp], #16 248 + 249 + // Do a syscall immediately prior to returning to try to provoke 250 + // scheduling and migration at a point where coherency issues 251 + // might trigger. 252 + mov x8, #__NR_getpid 253 + svc #0 254 + 255 + ret 256 + endfunction 257 + .endm 258 + 259 + // Generate and use two copies so we're changing the GCS contents 260 + recurse 1 261 + recurse 2 262 + 263 + .globl _start 264 + function _start 265 + // Run with GCS 266 + mov x0, PR_SET_SHADOW_STACK_STATUS 267 + mov x1, PR_SHADOW_STACK_ENABLE 268 + mov x2, xzr 269 + mov x3, xzr 270 + mov x4, xzr 271 + mov x5, xzr 272 + mov x8, #__NR_prctl 273 + svc #0 274 + cbz x0, 1f 275 + puts "Failed to enable GCS\n" 276 + b abort 277 + 1: 278 + 279 + mov w0, #SIGTERM 280 + adr x1, terminate_handler 281 + mov w2, #SA_SIGINFO 282 + bl setsignal 283 + 284 + mov w0, #SIGUSR1 285 + adr x1, tickle_handler 286 + mov w2, #SA_SIGINFO 287 + orr w2, w2, #SA_NODEFER 288 + bl setsignal 289 + 290 + mov w0, #SIGSEGV 291 + adr x1, segv_handler 292 + mov w2, #SA_SIGINFO 293 + orr w2, w2, #SA_NODEFER 294 + bl setsignal 295 + 296 + puts "Running\n" 297 + 298 + loop: 299 + // Small recursion depth so we're frequently flipping between 300 + // the two recursors and changing what's on the stack 301 + mov x20, #5 302 + bl recurse1 303 + mov x20, #5 304 + bl recurse2 305 + b loop 306 + endfunction 307 + 308 + abort: 309 + mov x0, #255 310 + mov x8, #__NR_exit 311 + svc #0
+530
tools/testing/selftests/arm64/gcs/gcs-stress.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 + 29 + struct 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 + 39 + static int epoll_fd; 40 + static struct child_data *children; 41 + static struct epoll_event *evs; 42 + static int tests; 43 + static int num_children; 44 + static bool terminate; 45 + 46 + static int startup_pipe[2]; 47 + 48 + static 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 + 59 + static void start_thread(struct child_data *child) 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", child->pid); 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 + 150 + static 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 + 203 + static 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 + 227 + static void child_tickle(struct child_data *child) 228 + { 229 + if (child->output_seen && !child->exited) 230 + kill(child->pid, SIGUSR1); 231 + } 232 + 233 + static void child_stop(struct child_data *child) 234 + { 235 + if (!child->exited) 236 + kill(child->pid, SIGTERM); 237 + } 238 + 239 + static 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); 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 + 288 + static 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 + 307 + static 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 */ 328 + static 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 + 347 + static const struct option options[] = { 348 + { "timeout", required_argument, NULL, 't' }, 349 + { } 350 + }; 351 + 352 + int 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 allocated %d epoll events\n", 437 + tests); 438 + 439 + for (i = 0; i < gcs_threads; i++) 440 + start_thread(&children[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 + }