1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) Crackerjack Project., 2007
4 * Ported to LTP by Manas Kumar Nayak <[email protected]>
5 * Copyright (c) 2015 Linux Test Project
6 * Copyright (C) 2015 Cyril Hrubis <[email protected]>
7 * Copyright (C) 2023 SUSE LLC Andrea Cervesato <[email protected]>
8 */
9
10 /*\
11 * [Description]
12 *
13 * This test checks if exit_group() correctly ends a spawned child and all its
14 * running threads.
15 */
16
17 #include <sched.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include "tst_test.h"
21 #include "lapi/syscalls.h"
22 #include "tst_safe_pthread.h"
23
24 static int cpu_count;
25
26 static struct worker_data {
27 pid_t tid;
28 int counter;
29 } *workers_data;
30
worker(void * arg)31 static void *worker(void *arg)
32 {
33 struct worker_data *data;
34
35 data = (struct worker_data *)arg;
36 data->tid = tst_gettid();
37
38 while (1) {
39 tst_atomic_inc(&data->counter);
40 sched_yield();
41 }
42
43 return arg;
44 }
45
spawn_threads(void)46 static void spawn_threads(void)
47 {
48 pthread_t threads[cpu_count];
49
50 for (int i = 0; i < cpu_count; i++)
51 SAFE_PTHREAD_CREATE(&threads[i], NULL, worker, (void *)(workers_data + i));
52 }
53
check_counters(void)54 static void check_counters(void)
55 {
56 struct worker_data data_copy[cpu_count];
57
58 memset(data_copy, 0, sizeof(struct worker_data) * cpu_count);
59 memcpy(data_copy, workers_data, sizeof(struct worker_data) * cpu_count);
60
61 tst_res(TINFO, "Checking if threads are still running");
62 usleep(100000);
63
64 struct worker_data *old_data;
65 struct worker_data *new_data;
66
67 for (int i = 0; i < cpu_count; i++) {
68 old_data = data_copy + i;
69 new_data = workers_data + i;
70
71 if (old_data->counter != new_data->counter) {
72 tst_res(TFAIL, "Counter value has changed for thread[%d]", i);
73 return;
74 }
75 }
76
77 tst_res(TINFO, "Threads counters value didn't change");
78 }
79
run(void)80 static void run(void)
81 {
82 pid_t pid;
83 int status;
84
85 pid = SAFE_FORK();
86 if (!pid) {
87 spawn_threads();
88
89 TEST(tst_syscall(__NR_exit_group, 4));
90 if (TST_RET == -1)
91 tst_brk(TBROK | TERRNO, "exit_group() error");
92
93 return;
94 }
95
96 SAFE_WAITPID(pid, &status, 0);
97
98 TST_EXP_EXPR(WIFEXITED(status) && WEXITSTATUS(status) == 4,
99 "exit_group() succeeded");
100
101 check_counters();
102 }
103
setup(void)104 static void setup(void)
105 {
106 cpu_count = MAX(2, tst_ncpus());
107
108 workers_data = SAFE_MMAP(
109 NULL,
110 sizeof(struct worker_data) * cpu_count,
111 PROT_READ | PROT_WRITE,
112 MAP_SHARED | MAP_ANONYMOUS,
113 -1, 0);
114 }
115
cleanup(void)116 static void cleanup(void)
117 {
118 SAFE_MUNMAP(workers_data, sizeof(struct worker_data) * cpu_count);
119 }
120
121 static struct tst_test test = {
122 .setup = setup,
123 .cleanup = cleanup,
124 .test_all = run,
125 .forks_child = 1,
126 .needs_checkpoints = 1,
127 };
128