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 fork_help[] = {
28 { "f N","fork N", "start N workers spinning on fork() and exit()" },
29 { NULL, "fork-ops N", "stop after N fork bogo operations" },
30 { NULL, "fork-max P", "create P workers per iteration, default is 1" },
31 { NULL, "fork-vm", "enable extra virtual memory pressure" },
32 { NULL, NULL, NULL }
33 };
34
35 static const stress_help_t vfork_help[] = {
36 { NULL, "vfork N", "start N workers spinning on vfork() and exit()" },
37 { NULL, "vfork-ops N", "stop after N vfork bogo operations" },
38 { NULL, "vfork-max P", "create P processes per iteration, default is 1" },
39 { NULL, "vfork-vm", "enable extra virtual memory pressure" },
40 { NULL, NULL, NULL }
41 };
42
43 #define STRESS_FORK (0)
44 #define STRESS_VFORK (1)
45
46 /*
47 * stress_set_fork_max()
48 * set maximum number of forks allowed
49 */
stress_set_fork_max(const char * opt)50 static int stress_set_fork_max(const char *opt)
51 {
52 uint32_t fork_max;
53
54 fork_max = stress_get_uint32(opt);
55 stress_check_range("fork-max", fork_max,
56 MIN_FORKS, MAX_FORKS);
57 return stress_set_setting("fork-max", TYPE_ID_UINT32, &fork_max);
58 }
59
60 /*
61 * stress_set_fork_vm()
62 * set fork-vm flag on
63 */
stress_set_fork_vm(const char * opt)64 static int stress_set_fork_vm(const char *opt)
65 {
66 bool vm = true;
67
68 (void)opt;
69
70 return stress_set_setting("fork-vm", TYPE_ID_BOOL, &vm);
71 }
72
73 /*
74 * stress_set_vfork_max()
75 * set maximum number of vforks allowed
76 */
stress_set_vfork_max(const char * opt)77 static int stress_set_vfork_max(const char *opt)
78 {
79 uint32_t vfork_max;
80
81 vfork_max = stress_get_uint32(opt);
82 stress_check_range("vfork-max", vfork_max,
83 MIN_VFORKS, MAX_VFORKS);
84 return stress_set_setting("vfork-max", TYPE_ID_UINT32, &vfork_max);
85 }
86
87 /*
88 * stress_set_vfork_vm()
89 * set vfork-vm flag on
90 */
stress_set_vfork_vm(const char * opt)91 static int stress_set_vfork_vm(const char *opt)
92 {
93 bool vm = true;
94
95 (void)opt;
96
97 return stress_set_setting("vfork-vm", TYPE_ID_BOOL, &vm);
98 }
99
100 typedef struct {
101 pid_t pid; /* Child PID */
102 int err; /* Saved fork errno */
103 } fork_info_t;
104
105 /*
106 * stress_fork_fn()
107 * stress by forking and exiting using
108 * fork function fork_fn (fork or vfork)
109 */
stress_fork_fn(const stress_args_t * args,const int which,const uint32_t fork_max,const bool vm)110 static int stress_fork_fn(
111 const stress_args_t *args,
112 const int which,
113 const uint32_t fork_max,
114 const bool vm)
115 {
116 static fork_info_t info[MAX_FORKS];
117
118 int ret;
119 #if defined(__APPLE__)
120 double time_end = stress_time_now() + (double)g_opt_timeout;
121 #endif
122
123 stress_set_oom_adjustment(args->name, true);
124
125 /* Explicitly drop capabilities, makes it more OOM-able */
126 ret = stress_drop_capabilities(args->name);
127 (void)ret;
128
129 do {
130 NOCLOBBER uint32_t i, n;
131 NOCLOBBER char *fork_fn_name;
132
133 (void)memset(info, 0, sizeof(info));
134
135 for (n = 0; n < fork_max; n++) {
136 pid_t pid;
137
138 switch (which) {
139 case STRESS_FORK:
140 fork_fn_name = "fork";
141 pid = fork();
142 break;
143 case STRESS_VFORK:
144 fork_fn_name = "vfork";
145 pid = shim_vfork();
146 break;
147 default:
148 /* This should not happen */
149 fork_fn_name = "unknown";
150 pid = -1;
151 pr_err("%s: bad fork/vfork function, aborting\n", args->name);
152 errno = ENOSYS;
153 break;
154 }
155
156 if (pid == 0) {
157 #if defined(HAVE_GETPGID)
158 const pid_t my_pid = getpid();
159 const pid_t my_pgid = getpgid(my_pid);
160 #endif
161
162 /*
163 * With new session and capabilities
164 * dropped vhangup will always fail
165 * but it's useful to exercise this
166 * to get more kernel coverage
167 */
168 if (setsid() != (pid_t) -1)
169 shim_vhangup();
170 if (vm) {
171 int flags = 0;
172
173 #if defined(MADV_MERGEABLE)
174 flags |= MADV_MERGEABLE;
175 #endif
176 #if defined(MADV_WILLNEED)
177 flags |= MADV_WILLNEED;
178 #endif
179 #if defined(MADV_HUGEPAGE)
180 flags |= MADV_HUGEPAGE;
181 #endif
182 #if defined(MADV_RANDOM)
183 flags |= MADV_RANDOM;
184 #endif
185 if (flags)
186 stress_madvise_pid_all_pages(getpid(), flags);
187 }
188
189 /* exercise some setpgid calls before we die */
190 ret = setpgid(0, 0);
191 (void)ret;
192 #if defined(HAVE_GETPGID)
193 ret = setpgid(my_pid, my_pgid);
194 (void)ret;
195 #endif
196
197 /* -ve pgid is EINVAL */
198 ret = setpgid(0, -1);
199 (void)ret;
200 /* -ve pid is EINVAL */
201 ret = setpgid(-1, 0);
202 (void)ret;
203
204 (void)shim_sched_yield();
205 _exit(0);
206 } else if (pid < 0) {
207 info[n].err = errno;
208 }
209 if (pid > -1)
210 (void)setpgid(pid, g_pgrp);
211 info[n].pid = pid;
212 if (!keep_stressing(args))
213 break;
214 }
215 for (i = 0; i < n; i++) {
216 if (info[i].pid > 0) {
217 int status;
218 /* Parent, kill and then wait for child */
219 /* (void)kill(info[i].pid, SIGKILL); no need to kill */
220 (void)shim_waitpid(info[i].pid, &status, 0);
221 inc_counter(args);
222 }
223 }
224
225 for (i = 0; i < n; i++) {
226 if ((info[i].pid < 0) && (g_opt_flags & OPT_FLAGS_VERIFY)) {
227 switch (info[i].err) {
228 case EAGAIN:
229 case ENOMEM:
230 break;
231 default:
232 pr_fail("%s: %s failed, errno=%d (%s)\n", args->name,
233 fork_fn_name, info[i].err, strerror(info[i].err));
234 break;
235 }
236 }
237 }
238 #if defined(__APPLE__)
239 /*
240 * SIGALRMs don't get reliably delivered on OS X on
241 * vfork so check the time in case SIGARLM was not
242 * delivered.
243 */
244 if ((which == STRESS_VFORK) && (stress_time_now() > time_end))
245 break;
246 #endif
247 } while (keep_stressing(args));
248
249 return EXIT_SUCCESS;
250 }
251
252 /*
253 * stress_fork()
254 * stress by forking and exiting
255 */
stress_fork(const stress_args_t * args)256 static int stress_fork(const stress_args_t *args)
257 {
258 uint32_t fork_max = DEFAULT_FORKS;
259 int rc;
260 bool vm = false;
261
262 (void)stress_get_setting("fork-vm", &vm);
263
264 if (!stress_get_setting("fork-max", &fork_max)) {
265 if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
266 fork_max = MAX_FORKS;
267 if (g_opt_flags & OPT_FLAGS_MINIMIZE)
268 fork_max = MIN_FORKS;
269 }
270
271 stress_set_proc_state(args->name, STRESS_STATE_RUN);
272 rc = stress_fork_fn(args, STRESS_FORK, fork_max, vm);
273 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
274
275 return rc;
276 }
277
278
279 /*
280 * stress_vfork()
281 * stress by vforking and exiting
282 */
283 STRESS_PRAGMA_PUSH
284 STRESS_PRAGMA_WARN_OFF
stress_vfork(const stress_args_t * args)285 static int stress_vfork(const stress_args_t *args)
286 {
287 uint32_t vfork_max = DEFAULT_VFORKS;
288 int rc;
289 bool vm = false;
290
291 (void)stress_get_setting("vfork-vm", &vm);
292
293 if (!stress_get_setting("vfork-max", &vfork_max)) {
294 if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
295 vfork_max = MAX_VFORKS;
296 if (g_opt_flags & OPT_FLAGS_MINIMIZE)
297 vfork_max = MIN_VFORKS;
298 }
299
300 stress_set_proc_state(args->name, STRESS_STATE_RUN);
301 rc = stress_fork_fn(args, STRESS_VFORK, vfork_max, vm);
302 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
303
304 return rc;
305 }
306 STRESS_PRAGMA_POP
307
308 static const stress_opt_set_func_t fork_opt_set_funcs[] = {
309 { OPT_fork_max, stress_set_fork_max },
310 { OPT_fork_vm, stress_set_fork_vm },
311 { 0, NULL }
312 };
313
314 static const stress_opt_set_func_t vfork_opt_set_funcs[] = {
315 { OPT_vfork_max, stress_set_vfork_max },
316 { OPT_vfork_vm, stress_set_vfork_vm },
317 { 0, NULL }
318 };
319
320 stressor_info_t stress_fork_info = {
321 .stressor = stress_fork,
322 .class = CLASS_SCHEDULER | CLASS_OS,
323 .opt_set_funcs = fork_opt_set_funcs,
324 .help = fork_help
325 };
326
327 stressor_info_t stress_vfork_info = {
328 .stressor = stress_vfork,
329 .class = CLASS_SCHEDULER | CLASS_OS,
330 .opt_set_funcs = vfork_opt_set_funcs,
331 .help = vfork_help
332 };
333