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, "swap N", "start N workers exercising swapon/swapoff" },
29 { NULL, "swap-ops N", "stop after N swapon/swapoff operations" },
30 { NULL, NULL, NULL }
31 };
32
33 #if defined(HAVE_SYS_SWAP_H) && \
34 defined(HAVE_SWAP)
35
36 #define SWAP_VERSION (1)
37 #define SWAP_UUID_LENGTH (16)
38 #define SWAP_LABEL_LENGTH (16)
39 #define SWAP_SIGNATURE "SWAPSPACE2"
40 #define SWAP_SIGNATURE_SZ (sizeof(SWAP_SIGNATURE) - 1)
41
42 #define MIN_SWAP_PAGES (32)
43 #define MAX_SWAP_PAGES (256)
44
45 #if !defined(SWAP_FLAG_PRIO_SHIFT)
46 #define SWAP_FLAG_PRIO_SHIFT (0)
47 #endif
48 #if !defined(SWAP_FLAG_PRIO_MASK)
49 #define SWAP_FLAG_PRIO_MASK (0x7fff)
50 #endif
51
52 #define SWAP_HDR_SANE (0x01)
53 #define SWAP_HDR_BAD_SIGNATURE (0x02)
54 #define SWAP_HDR_BAD_VERSION (0x04)
55 #define SWAP_HDR_ZERO_LAST_PAGE (0x08)
56 #define SWAP_HDR_BAD_LAST_PAGE (0x10)
57 #define SWAP_HDR_BAD_NR_BAD (0x20)
58
59 static const int bad_header_flags[] = {
60 SWAP_HDR_BAD_SIGNATURE,
61 SWAP_HDR_BAD_VERSION,
62 SWAP_HDR_ZERO_LAST_PAGE,
63 SWAP_HDR_BAD_LAST_PAGE,
64 SWAP_HDR_BAD_NR_BAD,
65 };
66
67 typedef struct {
68 uint8_t bootbits[1024]; /* cppcheck-suppress unusedStructMember */
69 uint32_t version;
70 uint32_t last_page;
71 uint32_t nr_badpages;
72 uint8_t sws_uuid[SWAP_UUID_LENGTH];
73 uint8_t sws_volume[SWAP_LABEL_LENGTH];
74 uint32_t padding[117]; /* cppcheck-suppress unusedStructMember */
75 uint32_t badpages[1]; /* cppcheck-suppress unusedStructMember */
76 } stress_swap_info_t;
77
78 /*
79 * stress_swap_supported()
80 * check if we can run this with SHIM_CAP_SYS_ADMIN capability
81 */
stress_swap_supported(const char * name)82 static int stress_swap_supported(const char *name)
83 {
84 if (!stress_check_capability(SHIM_CAP_SYS_ADMIN)) {
85 pr_inf_skip("%s stressor will be skipped, "
86 "need to be running with CAP_SYS_ADMIN "
87 "rights for this stressor\n", name);
88 return -1;
89 }
90 return 0;
91 }
92
stress_swap_zero(const stress_args_t * args,const int fd,const uint32_t npages,const uint8_t * page)93 static int stress_swap_zero(
94 const stress_args_t *args,
95 const int fd,
96 const uint32_t npages,
97 const uint8_t *page)
98 {
99 uint32_t i;
100
101 if (lseek(fd, 0, SEEK_SET) < 0) {
102 pr_fail("%s: lseek failed, errno=%d (%s)\n",
103 args->name, errno, strerror(errno));
104 return -1;
105 }
106
107 for (i = 0; i < npages; i++) {
108 if (write(fd, page, args->page_size) < 0) {
109 pr_fail("%s: write failed, errno=%d (%s)\n",
110 args->name, errno, strerror(errno));
111 return -1;
112 }
113 }
114 return 0;
115 }
116
stress_swap_set_size(const stress_args_t * args,const int fd,const uint32_t npages,const int bad_flags)117 static int stress_swap_set_size(
118 const stress_args_t *args,
119 const int fd,
120 const uint32_t npages,
121 const int bad_flags)
122 {
123 char signature[] = SWAP_SIGNATURE;
124 stress_swap_info_t swap_info;
125 size_t i;
126
127 if (npages < MIN_SWAP_PAGES) {
128 pr_fail("%s: incorrect swap size, must be > 16\n", args->name);
129 return -1;
130 }
131 if (lseek(fd, 0, SEEK_SET) < 0) {
132 pr_fail("%s: lseek failed, errno=%d (%s)\n",
133 args->name, errno, strerror(errno));
134 return -1;
135 }
136
137 if (bad_flags & SWAP_HDR_BAD_SIGNATURE)
138 signature[0]++; /* Invalid */
139
140 (void)memset(&swap_info, 0, sizeof(swap_info));
141 for (i = 0; i < sizeof(swap_info.sws_uuid); i++)
142 swap_info.sws_uuid[i] = stress_mwc8();
143 (void)snprintf((char *)swap_info.sws_volume,
144 sizeof(swap_info.sws_volume),
145 "SNG-SWP-%" PRIx32, args->instance);
146
147 if (bad_flags & SWAP_HDR_BAD_VERSION)
148 swap_info.version = ~SWAP_VERSION; /* Invalid */
149 else
150 swap_info.version = SWAP_VERSION;
151
152 if (bad_flags & SWAP_HDR_ZERO_LAST_PAGE)
153 swap_info.last_page = 0; /* Invalid */
154 else
155 swap_info.last_page = npages - 1;
156
157 if (bad_flags & SWAP_HDR_BAD_LAST_PAGE)
158 swap_info.last_page = npages + 1; /* Invalid */
159 else
160 swap_info.last_page = npages - 1;
161
162 if (bad_flags & SWAP_HDR_BAD_NR_BAD)
163 swap_info.nr_badpages = ~0; /* Dire */
164 else
165 swap_info.nr_badpages = 0;
166
167 if (write(fd, &swap_info, sizeof(swap_info)) < 0) {
168 pr_fail("%s: write of swap info failed, errno=%d (%s)\n",
169 args->name, errno, strerror(errno));
170 return -1;
171 }
172 if (lseek(fd, (off_t)(args->page_size - SWAP_SIGNATURE_SZ), SEEK_SET) < 0) {
173 pr_fail("%s: lseek failed, errno=%d (%s)\n",
174 args->name, errno, strerror(errno));
175 return -1;
176 }
177 if (write(fd, signature, SWAP_SIGNATURE_SZ) < 0) {
178 pr_fail("%s: write of swap signature failed, errno=%d (%s)\n",
179 args->name, errno, strerror(errno));
180 return -1;
181 }
182 return 0;
183 }
184
185 /*
186 * stress_swap()
187 * stress swap operations
188 */
stress_swap(const stress_args_t * args)189 static int stress_swap(const stress_args_t *args)
190 {
191 char filename[PATH_MAX];
192 int fd, ret;
193 uint8_t *page;
194
195 page = calloc(1, args->page_size);
196 if (!page) {
197 pr_err("%s: failed to allocate 1 page: errno=%d (%s)\n",
198 args->name, errno, strerror(errno));
199 ret = EXIT_NO_RESOURCE;
200 goto tidy_ret;
201 }
202
203 ret = stress_temp_dir_mk_args(args);
204 if (ret < 0) {
205 ret = exit_status(-ret);
206 goto tidy_free;
207 }
208
209 (void)stress_temp_filename_args(args,
210 filename, sizeof(filename), stress_mwc32());
211 fd = open(filename, O_CREAT | O_RDWR, S_IRUSR);
212 if (fd < 0) {
213 ret = exit_status(errno);
214 pr_fail("%s: open swap file %s failed, errno=%d (%s)\n",
215 args->name, filename, errno, strerror(errno));
216 goto tidy_rm;
217 }
218
219 if (stress_swap_zero(args, fd, MAX_SWAP_PAGES, page) < 0) {
220 ret = EXIT_FAILURE;
221 goto tidy_close;
222 }
223
224 stress_set_proc_state(args->name, STRESS_STATE_RUN);
225
226 do {
227 int swapflags = 0;
228 int bad_flags;
229 uint32_t npages = (stress_mwc32() % (MAX_SWAP_PAGES - MIN_SWAP_PAGES)) +
230 MIN_SWAP_PAGES;
231
232 #if defined(SWAP_FLAG_PREFER)
233 if (stress_mwc1()) {
234 swapflags = (stress_mwc8() << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK;
235 swapflags |= SWAP_FLAG_PREFER;
236 }
237 #endif
238 #if defined(SWAP_FLAG_DISCARD)
239 if (stress_mwc1())
240 swapflags |= SWAP_FLAG_DISCARD;
241 #endif
242 /* Periodically create bad swap header */
243 if (stress_mwc8() < 16) {
244 const size_t idx = stress_mwc8() % SIZEOF_ARRAY(bad_header_flags);
245 bad_flags = bad_header_flags[idx];
246 } else {
247 bad_flags = SWAP_HDR_SANE; /* No bad header */
248 }
249
250 if (stress_swap_set_size(args, fd, npages, bad_flags) < 0) {
251 ret = EXIT_FAILURE;
252 goto tidy_close;
253 }
254 ret = swapon(filename, swapflags);
255 if ((bad_flags == SWAP_HDR_SANE) && (ret < 0)) {
256 switch (errno) {
257 case EPERM:
258 case EINVAL:
259 /*
260 * We may hit EPERM if we request
261 * too many swap files
262 */
263 pr_inf_skip("%s: cannot enable swap file on the filesystem, skipping test\n",
264 args->name);
265 ret = EXIT_NO_RESOURCE;
266 break;
267 default:
268 pr_fail("%s: swapon failed, errno=%d (%s)\n",
269 args->name, errno, strerror(errno));
270 ret = EXIT_FAILURE;
271 break;
272 }
273 goto tidy_close;
274 }
275
276 ret = swapoff(filename);
277 if ((bad_flags == SWAP_HDR_SANE) && (ret < 0)) {
278 pr_fail("%s: swapoff failed, errno=%d (%s)\n",
279 args->name, errno, strerror(errno));
280 ret = EXIT_FAILURE;
281 (void)stress_thrash_stop();
282 goto tidy_close;
283 }
284
285 /* Exercise illegal swapon filename */
286 ret = swapon("", swapflags);
287 if (ret == 0)
288 ret = swapoff(""); /* Should never happen */
289 (void)ret;
290
291 /* Exercise illegal swapoff filename */
292 ret = swapoff("");
293 if (ret == 0)
294 ret = swapon("", swapflags); /* Should never happen */
295 (void)ret;
296
297 /* Exercise illegal swapon flags */
298 ret = swapon(filename, ~0);
299 if (ret == 0)
300 ret = swapoff(filename); /* Should never happen */
301 (void)ret;
302
303 inc_counter(args);
304 } while (keep_stressing(args));
305
306 ret = EXIT_SUCCESS;
307 tidy_close:
308 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
309 (void)close(fd);
310 tidy_rm:
311 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
312 (void)unlink(filename);
313 (void)stress_temp_dir_rm_args(args);
314 tidy_free:
315 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
316 free(page);
317 tidy_ret:
318 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
319 return ret;
320 }
321
322 stressor_info_t stress_swap_info = {
323 .stressor = stress_swap,
324 .supported = stress_swap_supported,
325 .class = CLASS_VM | CLASS_OS,
326 .help = help
327 };
328 #else
329 stressor_info_t stress_swap_info = {
330 .stressor = stress_not_implemented,
331 .class = CLASS_VM | CLASS_OS,
332 .help = help
333 };
334 #endif
335