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