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, "flock N", "start N workers locking a single file" },
29 { NULL, "flock-ops N", "stop after N flock bogo operations" },
30 { NULL, NULL, NULL }
31 };
32
33 #if defined(HAVE_FLOCK) && \
34 defined(LOCK_EX) && \
35 defined(LOCK_UN)
36
37 #define MAX_FLOCK_STRESSORS (3)
38
stress_flock_child(const stress_args_t * args,const int fd,const int bad_fd)39 static void stress_flock_child(
40 const stress_args_t *args,
41 const int fd,
42 const int bad_fd)
43 {
44 bool cont;
45 int i;
46
47 for (i = 0; ; i++) {
48 int ret;
49
50 #if defined(LOCK_EX)
51 if (flock(fd, LOCK_EX) == 0) {
52 cont = keep_stressing(args);
53 if (cont)
54 inc_counter(args);
55 (void)flock(fd, LOCK_UN);
56 if (!cont)
57 break;
58 }
59 #endif
60
61 /*
62 * Exercise flock with invalid fd
63 */
64 ret = flock(bad_fd, LOCK_EX);
65 (void)ret;
66 ret = flock(bad_fd, LOCK_UN);
67 (void)ret;
68
69 #if defined(LOCK_NB)
70 if (flock(fd, LOCK_EX | LOCK_NB) == 0) {
71 cont = keep_stressing(args);
72 if (cont)
73 inc_counter(args);
74 (void)flock(fd, LOCK_UN);
75 if (!cont)
76 break;
77 }
78
79 /*
80 * Exercise flock with invalid operation
81 */
82 ret = flock(fd, LOCK_NB);
83 if (ret == 0) {
84 pr_err("%s: flock failed expected EINVAL, instead got "
85 "errno=%d (%s)\n", args->name, errno, strerror(errno));
86 (void)flock(fd, LOCK_UN);
87 }
88 #endif
89
90 #if defined(LOCK_SH)
91 if (!keep_stressing(args))
92 break;
93 if (flock(fd, LOCK_SH) == 0) {
94 cont = keep_stressing(args);
95 if (cont)
96 inc_counter(args);
97 (void)flock(fd, LOCK_UN);
98 if (!cont)
99 break;
100 }
101 #endif
102
103 #if defined(LOCK_SH) && \
104 defined(LOCK_NB)
105 if (!keep_stressing(args))
106 break;
107 if (flock(fd, LOCK_SH | LOCK_NB) == 0) {
108 cont = keep_stressing(args);
109 if (cont)
110 inc_counter(args);
111 (void)flock(fd, LOCK_UN);
112 if (!cont)
113 break;
114 }
115 #endif
116
117 #if defined(LOCK_MAND) && \
118 defined(LOCK_READ)
119 if (!keep_stressing(args))
120 break;
121 if (flock(fd, LOCK_MAND | LOCK_READ) == 0) {
122 cont = keep_stressing(args);
123 if (cont)
124 inc_counter(args);
125 (void)flock(fd, LOCK_UN);
126 if (!cont)
127 break;
128 }
129 #endif
130
131 #if defined(LOCK_MAND) && \
132 defined(LOCK_WRITE)
133 if (!keep_stressing(args))
134 break;
135 if (flock(fd, LOCK_MAND | LOCK_WRITE) == 0) {
136 cont = keep_stressing(args);
137 if (cont)
138 inc_counter(args);
139 (void)flock(fd, LOCK_UN);
140 if (!cont)
141 break;
142 }
143 #endif
144
145 #if defined(LOCK_EX) && \
146 defined(LOCK_SH)
147 if (!keep_stressing(args))
148 break;
149 /* Exercise invalid lock combination */
150 if (flock(fd, LOCK_EX | LOCK_SH) == 0) {
151 cont = keep_stressing(args);
152 if (cont)
153 inc_counter(args);
154 (void)flock(fd, LOCK_UN);
155 if (!cont)
156 break;
157 }
158 #endif
159 #if defined(__linux__)
160 if ((i & 0xff) == 0) {
161 char buf[4096];
162 ssize_t rret;
163
164 rret = system_read("/proc/locks", buf, sizeof(buf));
165 (void)rret;
166 }
167 #endif
168 }
169 }
170
171 /*
172 * stress_flock
173 * stress file locking
174 */
stress_flock(const stress_args_t * args)175 static int stress_flock(const stress_args_t *args)
176 {
177 int fd, ret, rc = EXIT_FAILURE;
178 const int bad_fd = stress_get_bad_fd();
179 size_t i;
180 pid_t pids[MAX_FLOCK_STRESSORS];
181 char filename[PATH_MAX];
182
183 ret = stress_temp_dir_mk_args(args);
184 if (ret < 0)
185 return exit_status(-ret);
186
187 (void)stress_temp_filename_args(args,
188 filename, sizeof(filename), stress_mwc32());
189 fd = open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
190 if (fd < 0) {
191 pr_err("%s: failed to create %s: errno=%d (%s)\n",
192 args->name, filename, errno, strerror(errno));
193 goto err;
194 }
195
196 stress_set_proc_state(args->name, STRESS_STATE_RUN);
197
198 (void)memset(pids, 0, sizeof(pids));
199 for (i = 0; i < MAX_FLOCK_STRESSORS; i++) {
200 pids[i] = fork();
201 if (pids[i] < 0) {
202 goto reap;
203 } else if (pids[i] == 0) {
204 (void)setpgid(0, g_pgrp);
205 stress_parent_died_alarm();
206 (void)sched_settings_apply(true);
207
208 stress_flock_child(args, fd, bad_fd);
209 _exit(EXIT_SUCCESS);
210 }
211 }
212
213 stress_flock_child(args, fd, bad_fd);
214 rc = EXIT_SUCCESS;
215 reap:
216 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
217 (void)close(fd);
218
219 for (i = 0; i < MAX_FLOCK_STRESSORS; i++) {
220 if (pids[i] > 0) {
221 int status;
222
223 (void)kill(pids[i], SIGKILL);
224 (void)shim_waitpid(pids[i], &status, 0);
225 }
226 }
227
228 (void)unlink(filename);
229 err:
230 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
231 (void)stress_temp_dir_rm_args(args);
232
233 return rc;
234 }
235
236 stressor_info_t stress_flock_info = {
237 .stressor = stress_flock,
238 .class = CLASS_FILESYSTEM | CLASS_OS,
239 .help = help
240 };
241 #else
242 stressor_info_t stress_flock_info = {
243 .stressor = stress_not_implemented,
244 .class = CLASS_FILESYSTEM | CLASS_OS,
245 .help = help
246 };
247 #endif
248