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