xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/exit_group/exit_group01.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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