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