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