1 /*
2  * Copyright (C) 2013-2021 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * This code is a complete clean re-write of the stress tool by
19  * Colin Ian King <colin.king@canonical.com> and attempts to be
20  * backwardly compatible with the stress tool by Amos Waterland
21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
22  * functionality.
23  *
24  */
25 #include "stress-ng.h"
26 
27 static const stress_help_t help[] = {
28 	{ NULL,	"exit-group N",		"start N workers that exercise exit_group" },
29 	{ NULL,	"exit-group-ops N",	"stop exit_group workers after N bogo exit_group loops" },
30 	{ NULL,	NULL,			NULL }
31 };
32 
33 #if defined(HAVE_LIB_PTHREAD) && 	\
34     defined(__NR_exit_group)
35 
36 #define STRESS_PTHREAD_EXIT_GROUP_MAX	(16)
37 
38 /* per pthread data */
39 typedef struct {
40 	pthread_t pthread;	/* The pthread */
41 	int	  ret;		/* pthread create return */
42 } stress_exit_group_info_t;
43 
44 static pthread_mutex_t mutex;
45 static volatile bool keep_running_flag;
46 static uint64_t pthread_count;
47 static stress_exit_group_info_t pthreads[MAX_PTHREAD];
48 
stop_running(void)49 static inline void stop_running(void)
50 {
51 	keep_running_flag = false;
52 }
53 
54 /*
55  *  keep_running()
56  *  	Check if SIGALRM is pending, set flags
57  * 	to tell pthreads and main pthread stressor
58  *	to stop. Returns false if we need to stop.
59  */
keep_running(void)60 static bool keep_running(void)
61 {
62 	if (stress_sigalrm_pending())
63 		stop_running();
64 	return keep_running_flag;
65 }
66 
67 /*
68  *  stress_exit_group_sleep()
69  *	tiny delay
70  */
stress_exit_group_sleep(void)71 static void stress_exit_group_sleep(void)
72 {
73 	shim_nanosleep_uint64(10000);
74 }
75 
76 /*
77  *  stress_exit_group_func()
78  *	pthread specific system call stressor
79  */
stress_exit_group_func(void * arg)80 static void *stress_exit_group_func(void *arg)
81 {
82 	int ret;
83 	static void *nowt = NULL;
84 
85 	(void)arg;
86 
87 	ret = pthread_mutex_lock(&mutex);
88 	if (ret == 0) {
89 		pthread_count++;
90 		ret = pthread_mutex_unlock(&mutex);
91 		(void)ret;
92 	}
93 
94 	while (keep_running_flag &&
95 	       (pthread_count < STRESS_PTHREAD_EXIT_GROUP_MAX)) {
96 		stress_exit_group_sleep();
97 	}
98 	shim_exit_group(0);
99 
100 	/* should never get here */
101 	return &nowt;
102 }
103 
104 /*
105  *  stress_exit_group()
106  *	stress by creating pthreads
107  */
stress_exit_group_child(const stress_args_t * args)108 static void NORETURN stress_exit_group_child(const stress_args_t *args)
109 {
110 	int ret;
111 	sigset_t set;
112 	size_t i, j;
113 
114 	keep_running_flag = true;
115 
116 	/*
117 	 *  Block SIGALRM, instead use sigpending
118 	 *  in pthread or this process to check if
119 	 *  SIGALRM has been sent.
120 	 */
121 	sigemptyset(&set);
122 	sigaddset(&set, SIGALRM);
123 	sigprocmask(SIG_BLOCK, &set, NULL);
124 
125 	pthread_count = 0;
126 
127 	(void)memset(&pthreads, 0, sizeof(pthreads));
128 	ret = pthread_mutex_lock(&mutex);
129 	if (ret) {
130 		stop_running();
131 		shim_exit_group(0);
132 	}
133 	for (i = 0; i < STRESS_PTHREAD_EXIT_GROUP_MAX; i++)
134 		pthreads[i].ret = -1;
135 
136 	for (i = 0; i < STRESS_PTHREAD_EXIT_GROUP_MAX; i++) {
137 		pthreads[i].ret = pthread_create(&pthreads[i].pthread, NULL,
138 			stress_exit_group_func, NULL);
139 		if (pthreads[i].ret) {
140 			/* Out of resources, don't try any more */
141 			if (pthreads[i].ret == EAGAIN)
142 				break;
143 			/* Something really unexpected */
144 			pr_fail("%s: pthread_create failed, errno=%d (%s)\n",
145 				args->name, pthreads[i].ret, strerror(pthreads[i].ret));
146 			stop_running();
147 			shim_exit_group(0);
148 		}
149 		if (!(keep_running() && keep_stressing(args)))
150 			break;
151 	}
152 	ret = pthread_mutex_unlock(&mutex);
153 	if (ret) {
154 		stop_running();
155 		shim_exit_group(0);
156 	}
157 	/*
158 	 *  Wait until they are all started or
159 	 *  we get bored waiting..
160 	 */
161 	for (j = 0; j < 1000; j++) {
162 		bool all_running = false;
163 
164 		if (!keep_stressing(args)) {
165 			stop_running();
166 			shim_exit_group(0);
167 			break;
168 		}
169 
170 		all_running = (pthread_count == i);
171 		if (all_running)
172 			break;
173 
174 		stress_exit_group_sleep();
175 	}
176 	shim_exit_group(0);
177 	/* Should never get here */
178 	_exit(0);
179 }
180 
181 /*
182  *  stress_exit_group()
183  *	stress by creating pthreads
184  */
stress_exit_group(const stress_args_t * args)185 static int stress_exit_group(const stress_args_t *args)
186 {
187         stress_set_proc_state(args->name, STRESS_STATE_RUN);
188 
189 	while (keep_stressing(args)) {
190 		pid_t pid;
191 		int ret;
192 
193 		ret = pthread_mutex_init(&mutex, NULL);
194 		if (ret) {
195 			pr_fail("%s: pthread_mutex_init failed, errno=%d (%s)\n",
196 				args->name, ret, strerror(ret));
197 			return EXIT_FAILURE;
198 		}
199 
200 again:
201 		pid = fork();
202 		if (pid < 0) {
203 			if (stress_redo_fork(errno))
204 				goto again;
205 			(void)pthread_mutex_destroy(&mutex);
206 			break;
207 		} else if (pid == 0) {
208 			stress_exit_group_child(args);
209 		} else {
210 			int status;
211 
212 			ret = waitpid(pid, &status, 0);
213 			(void)pthread_mutex_destroy(&mutex);
214 			if (ret < 0)
215 				break;
216 
217 			inc_counter(args);
218 		}
219 	}
220 
221         stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
222 
223 	return EXIT_SUCCESS;
224 }
225 
226 
227 stressor_info_t stress_exit_group_info = {
228 	.stressor = stress_exit_group,
229 	.class = CLASS_SCHEDULER | CLASS_OS,
230 	.help = help
231 };
232 #else
233 stressor_info_t stress_exit_group_info = {
234 	.stressor = stress_not_implemented,
235 	.class = CLASS_SCHEDULER | CLASS_OS,
236 	.help = help
237 };
238 #endif
239