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,	"bind-mount N",	    "start N workers exercising bind mounts" },
29 	{ NULL,	"bind-mount-ops N", "stop after N bogo bind mount operations" },
30 	{ NULL,	NULL,		    NULL }
31 };
32 
33 #if defined(__linux__) && \
34     defined(MS_BIND) && \
35     defined(MS_REC) && \
36     defined(HAVE_CLONE) && \
37     defined(CLONE_NEWUSER) && \
38     defined(CLONE_NEWNS) && \
39     defined(CLONE_VM)
40 
41 #define CLONE_STACK_SIZE	(128*1024)
42 
stress_bind_mount_child_handler(int signum)43 static void NORETURN MLOCKED_TEXT stress_bind_mount_child_handler(int signum)
44 {
45 	(void)signum;
46 
47 	_exit(0);
48 }
49 
50 /*
51  *  stress_bind_mount_child()
52  *	aggressively perform bind mounts, this can force out of memory
53  *	situations
54  */
stress_bind_mount_child(void * parg)55 static int stress_bind_mount_child(void *parg)
56 {
57 	const stress_args_t *args = ((stress_pthread_args_t *)parg)->args;
58 
59 	if (stress_sighandler(args->name, SIGALRM,
60 	    stress_bind_mount_child_handler, NULL) < 0) {
61 		pr_fail("%s: SIGALRM sighandler failed, errno=%d (%s)\n",
62 			args->name, errno, strerror(errno));
63 		return EXIT_FAILURE;
64 	}
65 	if (stress_sighandler(args->name, SIGSEGV,
66 	    stress_bind_mount_child_handler, NULL) < 0) {
67 		pr_fail("%s: SIGSEGV sighandler failed, errno=%d (%s)\n",
68 			args->name, errno, strerror(errno));
69 		return EXIT_FAILURE;
70 	}
71 	(void)setpgid(0, g_pgrp);
72 	stress_parent_died_alarm();
73 
74 	do {
75 		int rc;
76 
77 		rc = mount("/", "/", "", MS_BIND | MS_REC, 0);
78 		if (rc < 0) {
79 			if (errno != ENOSPC)
80 				pr_fail("%s: bind mount failed, errno=%d (%s)\n",
81 					args->name, errno, strerror(errno));
82 			break;
83 		}
84 		/*
85 		 * The following fails with -EBUSY, but try it anyhow
86 		 *  just to make the kernel work harder
87 		 */
88 		(void)umount("/");
89 		inc_counter(args);
90 	} while (keep_stressing_flag() &&
91 		 (!args->max_ops || (get_counter(args) < args->max_ops)));
92 
93 	return 0;
94 }
95 
96 /*
97  *  stress_bind_mount()
98  *      stress bind mounting
99  */
stress_bind_mount(const stress_args_t * args)100 static int stress_bind_mount(const stress_args_t *args)
101 {
102 	int pid = 0, status;
103 	stress_pthread_args_t pargs = { args, NULL, 0 };
104 
105 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
106 
107 	do {
108 		int ret;
109 		static char stack[CLONE_STACK_SIZE];
110 		char *stack_top = (char *)stress_get_stack_top((void *)stack, CLONE_STACK_SIZE);
111 
112 		(void)memset(stack, 0, sizeof stack);
113 
114 		pid = clone(stress_bind_mount_child,
115 			stress_align_stack(stack_top),
116 			CLONE_NEWUSER | CLONE_NEWNS | CLONE_VM | SIGCHLD,
117 			(void *)&pargs, 0);
118 		if (pid < 0) {
119 			int rc = exit_status(errno);
120 
121 			pr_fail("%s: clone failed, errno=%d (%s)\n",
122 				args->name, errno, strerror(errno));
123 			return rc;
124 		}
125 		ret = shim_waitpid(pid, &status, 0);
126 		(void)ret;
127 	} while (keep_stressing(args));
128 
129 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
130 
131 	return EXIT_SUCCESS;
132 }
133 
134 stressor_info_t stress_bind_mount_info = {
135 	.stressor = stress_bind_mount,
136 	.class = CLASS_FILESYSTEM | CLASS_OS | CLASS_PATHOLOGICAL,
137 	.help = help
138 };
139 #else
140 stressor_info_t stress_bind_mount_info = {
141 	.stressor = stress_not_implemented,
142 	.class = CLASS_FILESYSTEM | CLASS_OS | CLASS_PATHOLOGICAL,
143 	.help = help
144 };
145 #endif
146