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 #if defined(F_SETLEASE) && \
28 defined(F_WRLCK) && \
29 defined(F_UNLCK)
30 static uint64_t lease_sigio;
31 #endif
32
33 static const stress_help_t help[] = {
34 { NULL, "lease N", "start N workers holding and breaking a lease" },
35 { NULL, "lease-ops N", "stop after N lease bogo operations" },
36 { NULL, "lease-breakers N", "number of lease breaking workers to start" },
37 { NULL, NULL, NULL }
38 };
39
stress_set_lease_breakers(const char * opt)40 static int stress_set_lease_breakers(const char *opt)
41 {
42 uint64_t lease_breakers;
43
44 lease_breakers = stress_get_uint64(opt);
45 stress_check_range("lease-breakers", lease_breakers,
46 MIN_LEASE_BREAKERS, MAX_LEASE_BREAKERS);
47 return stress_set_setting("lease-breakers", TYPE_ID_UINT64, &lease_breakers);
48 }
49
50 static const stress_opt_set_func_t opt_set_funcs[] = {
51 { OPT_lease_breakers, stress_set_lease_breakers },
52 { 0, NULL }
53 };
54
55 #if defined(F_SETLEASE) && \
56 defined(F_WRLCK) && \
57 defined(F_UNLCK)
58
59 /*
60 * stress_lease_handler()
61 * lease signal handler
62 */
stress_lease_handler(int signum)63 static void MLOCKED_TEXT stress_lease_handler(int signum)
64 {
65 (void)signum;
66
67 lease_sigio++;
68 }
69
70 /*
71 * stress_get_lease()
72 * exercise getting the lease on fd
73 */
stress_get_lease(const int fd)74 static int stress_get_lease(const int fd)
75 {
76 return fcntl(fd, F_GETLEASE);
77 }
78
79 /*
80 * stress_lease_spawn()
81 * spawn a process
82 */
stress_lease_spawn(const stress_args_t * args,const char * filename)83 static pid_t stress_lease_spawn(
84 const stress_args_t *args,
85 const char *filename)
86 {
87 pid_t pid;
88 int count = 0;
89
90 again:
91 pid = fork();
92 if (pid < 0) {
93 if (stress_redo_fork(errno))
94 goto again;
95 return -1;
96 }
97 if (pid == 0) {
98 (void)setpgid(0, g_pgrp);
99 stress_parent_died_alarm();
100 (void)sched_settings_apply(true);
101
102 do {
103 int fd;
104
105 errno = 0;
106 fd = open(filename, O_NONBLOCK | O_WRONLY, S_IRUSR | S_IWUSR);
107 if (fd < 0) {
108 if ((errno != EWOULDBLOCK) &&
109 (errno != EACCES)) {
110 pr_dbg("%s: open failed (child): errno=%d: (%s)\n",
111 args->name, errno, strerror(errno));
112 if (count++ > 3)
113 break;
114 }
115 continue;
116 }
117 (void)stress_get_lease(fd);
118 (void)close(fd);
119 } while (keep_stressing(args));
120 _exit(EXIT_SUCCESS);
121 }
122 (void)setpgid(pid, g_pgrp);
123 return pid;
124 }
125
126 /*
127 * stress_try_lease()
128 * try and get a lease with lock type 'lock'
129 */
stress_try_lease(const stress_args_t * args,const char * filename,const int flags,const int lock)130 static int stress_try_lease(
131 const stress_args_t *args,
132 const char *filename,
133 const int flags,
134 const int lock)
135 {
136 int fd;
137
138 fd = open(filename, flags, S_IRUSR | S_IWUSR);
139 if (fd < 0) {
140 int ret;
141
142 ret = exit_status(errno);
143 pr_err("%s: open failed (parent): errno=%d: (%s)\n",
144 args->name, errno, strerror(errno));
145 return ret;
146 }
147
148 /*
149 * attempt a lease lock
150 */
151 while (fcntl(fd, F_SETLEASE, lock) < 0) {
152 if (!keep_stressing_flag())
153 goto tidy;
154 }
155 (void)stress_get_lease(fd);
156
157 inc_counter(args);
158 (void)shim_sched_yield();
159
160 /*
161 * attempt a lease unlock
162 */
163 while (fcntl(fd, F_SETLEASE, F_UNLCK) < 0) {
164 if (!keep_stressing_flag())
165 break;
166 if (errno != EAGAIN) {
167 pr_err("%s: fcntl failed: errno=%d: (%s)\n",
168 args->name, errno, strerror(errno));
169 break;
170 }
171 }
172 tidy:
173 (void)close(fd);
174
175 return EXIT_SUCCESS;
176 }
177
178 /*
179 * stress_lease
180 * stress fcntl lease activity
181 */
stress_lease(const stress_args_t * args)182 static int stress_lease(const stress_args_t *args)
183 {
184 char filename[PATH_MAX];
185 int ret, fd, status;
186 pid_t l_pids[MAX_LEASE_BREAKERS];
187 uint64_t i, lease_breakers = DEFAULT_LEASE_BREAKERS;
188 double t1 = 0.0, t2 = 0.0, dt;
189
190 if (!stress_get_setting("lease-breakers", &lease_breakers)) {
191 if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
192 lease_breakers = MAX_LEASE_BREAKERS;
193 if (g_opt_flags & OPT_FLAGS_MINIMIZE)
194 lease_breakers = MIN_LEASE_BREAKERS;
195 }
196
197 (void)memset(l_pids, 0, sizeof(l_pids));
198
199 if (stress_sighandler(args->name, SIGIO, stress_lease_handler, NULL) < 0)
200 return EXIT_FAILURE;
201
202 ret = stress_temp_dir_mk_args(args);
203 if (ret < 0)
204 return exit_status(-ret);
205 (void)stress_temp_filename_args(args,
206 filename, sizeof(filename), stress_mwc32());
207
208 fd = creat(filename, S_IRUSR | S_IWUSR);
209 if (fd < 0) {
210 ret = exit_status(errno);
211 pr_err("%s: creat failed: errno=%d: (%s)\n",
212 args->name, errno, strerror(errno));
213 (void)stress_temp_dir_rm_args(args);
214 return ret;
215 }
216 (void)close(fd);
217
218 /*
219 * start lease breaker child processes
220 */
221 for (i = 0; i < lease_breakers; i++) {
222 l_pids[i] = stress_lease_spawn(args, filename);
223 if (l_pids[i] < 0) {
224 pr_err("%s: failed to start all the lease breaker processes\n", args->name);
225 goto reap;
226 }
227 }
228
229 stress_set_proc_state(args->name, STRESS_STATE_RUN);
230
231 t1 = stress_time_now();
232 do {
233 ret = stress_try_lease(args, filename, O_WRONLY | O_APPEND, F_WRLCK);
234 if (ret != EXIT_SUCCESS)
235 break;
236 ret = stress_try_lease(args, filename, O_RDONLY, F_RDLCK);
237 if (ret != EXIT_SUCCESS)
238 break;
239 } while (keep_stressing(args));
240 t2 = stress_time_now();
241
242 reap:
243 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
244
245 for (i = 0; i < lease_breakers; i++) {
246 if (l_pids[i]) {
247 (void)kill(l_pids[i], SIGKILL);
248 (void)shim_waitpid(l_pids[i], &status, 0);
249 }
250 }
251
252 (void)unlink(filename);
253 (void)stress_temp_dir_rm_args(args);
254
255 pr_dbg("%s: %" PRIu64 " lease sigio interrupts caught\n", args->name, lease_sigio);
256 dt = t2 - t1;
257 if (dt > 0.0) {
258 stress_misc_stats_set(args->misc_stats, 0, "lease sigio interrupts per sec",
259 (double)lease_sigio / dt);
260 }
261
262 return ret;
263 }
264
265 stressor_info_t stress_lease_info = {
266 .stressor = stress_lease,
267 .class = CLASS_FILESYSTEM | CLASS_OS,
268 .opt_set_funcs = opt_set_funcs,
269 .help = help
270 };
271 #else
272 stressor_info_t stress_lease_info = {
273 .stressor = stress_not_implemented,
274 .class = CLASS_FILESYSTEM | CLASS_OS,
275 .opt_set_funcs = opt_set_funcs,
276 .help = help
277 };
278 #endif
279