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