Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/arm64/gcs/gcs-stress.c
26292 views
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, 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
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, 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
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], 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
}
531
532