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,	"mmaphuge N",		"start N workers stressing mmap with huge mappings" },
29 	{ NULL,	"mmaphuge-ops N",	"stop after N mmaphuge bogo operations" },
30 	{ NULL, "mmaphuge-mmaps N",	"select number of memory mappings per iteration" },
31 	{ NULL,	NULL,			NULL }
32 };
33 
34 /*
35  *  stress_set_mmaphuge_mmaps()
36  *      set number of huge memory mappings to make per loop
37  */
stress_set_mmaphuge_mmaps(const char * opt)38 static int stress_set_mmaphuge_mmaps(const char *opt)
39 {
40 	size_t mmaphuge_mmaps;
41 
42 	mmaphuge_mmaps = (size_t)stress_get_uint64(opt);
43 	stress_check_range("mmaphuge-mmaps", mmaphuge_mmaps,
44 		1, 65536 );
45 	return stress_set_setting("mmaphuge-mmaps", TYPE_ID_SIZE_T, &mmaphuge_mmaps);
46 }
47 
48 static const stress_opt_set_func_t opt_set_funcs[] = {
49 	{ OPT_mmaphuge_mmaps,  stress_set_mmaphuge_mmaps },
50 	{ 0,                    NULL }
51 };
52 
53 #if defined(MAP_HUGETLB)
54 
55 #define MAX_MMAP_BUFS	(8192)
56 
57 #if !defined(MAP_HUGE_2MB) && defined(MAP_HUGE_SHIFT)
58 #define MAP_HUGE_2MB    (21 << MAP_HUGE_SHIFT)
59 #endif
60 
61 #if !defined(MAP_HUGE_1GB) && defined(MAP_HUGE_SHIFT)
62 #define MAP_HUGE_1GB    (30 << MAP_HUGE_SHIFT)
63 #endif
64 
65 typedef struct {
66 	uint8_t	*buf;		/* mapping start */
67 	size_t	sz;		/* mapping size */
68 } stress_mmaphuge_buf_t;
69 
70 typedef struct {
71 	const int	flags;	/* MMAP flag */
72 	const ssize_t	sz;	/* MMAP size */
73 } stress_mmaphuge_setting_t;
74 
75 typedef struct {
76 	stress_mmaphuge_buf_t	*bufs;	/* mmap'd buffers */
77 	size_t mmaphuge_mmaps;	/* number of mmap'd buffers */
78 } stress_mmaphuge_context_t;
79 
80 static const stress_mmaphuge_setting_t stress_mmap_settings[] =
81 {
82 #if defined(MAP_HUGE_2MB)
83 	{ MAP_HUGETLB | MAP_HUGE_2MB,	2 * MB },
84 #endif
85 #if defined(MAP_HUGE_1GB)
86 	{ MAP_HUGETLB | MAP_HUGE_1GB,	1 * GB },
87 #endif
88 	{ MAP_HUGETLB, 1 * GB },
89 	{ MAP_HUGETLB, 16 * MB },	/* ppc64 */
90 	{ MAP_HUGETLB, 2 * MB },
91 	{ 0, 1 * GB },			/* for THP */
92 	{ 0, 16 * MB },			/* for THP */
93 	{ 0, 2 * MB },			/* for THP */
94 };
95 
stress_mmaphuge_child(const stress_args_t * args,void * v_ctxt)96 static int stress_mmaphuge_child(const stress_args_t *args, void *v_ctxt)
97 {
98 	stress_mmaphuge_context_t *ctxt = (stress_mmaphuge_context_t *)v_ctxt;
99 	const size_t page_size = args->page_size;
100 	stress_mmaphuge_buf_t *bufs = (stress_mmaphuge_buf_t *)ctxt->bufs;
101 	size_t idx = 0;
102 
103 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
104 
105 	do {
106 		size_t i;
107 
108 		for (i = 0; i < ctxt->mmaphuge_mmaps; i++)
109 			bufs[i].buf = MAP_FAILED;
110 
111 		for (i = 0; keep_stressing(args) && (i < ctxt->mmaphuge_mmaps); i++) {
112 			size_t shmall, freemem, totalmem, freeswap, last_freeswap;
113 			int flags;
114 			size_t j;
115 
116 			stress_get_memlimits(&shmall, &freemem, &totalmem, &last_freeswap);
117 
118 			for (j = 0; j < SIZEOF_ARRAY(stress_mmap_settings); j++) {
119 				uint8_t *buf;
120 				const size_t sz = stress_mmap_settings[idx].sz;
121 
122 				flags = MAP_ANONYMOUS;
123 				flags |= (stress_mwc1() ? MAP_PRIVATE : MAP_SHARED);
124 				flags |= stress_mmap_settings[idx].flags;
125 
126 				bufs[i].sz = sz;
127 				buf = (uint8_t *)mmap(NULL, sz,
128 							PROT_READ | PROT_WRITE,
129 							flags, -1, 0);
130 				bufs[i].buf = buf;
131 				idx++;
132 				if (idx >= SIZEOF_ARRAY(stress_mmap_settings))
133 					idx = 0;
134 
135 				if (buf != MAP_FAILED) {
136 					register uint8_t val = stress_mwc8();
137 					register size_t k;
138 
139 					/* Touch every other 64 pages */
140 					for (k = 0; k < sz; k += page_size * 64) {
141 						buf[k] = val;
142 					}
143 					inc_counter(args);
144 					break;
145 				}
146 			}
147 			stress_get_memlimits(&shmall, &freemem, &totalmem, &freeswap);
148 
149 			/* Check if we eat into swap */
150 			if (last_freeswap > freeswap)
151 				break;
152 		}
153 
154 		for (i = 0; keep_stressing(args) && (i < ctxt->mmaphuge_mmaps); i++) {
155 			if (bufs[i].buf != MAP_FAILED)
156 				continue;
157 			/* Try Transparent Huge Pages THP */
158 #if defined(MADV_HUGEPAGE)
159 			(void)shim_madvise(bufs[i].buf, bufs[i].sz, MADV_NOHUGEPAGE);
160 #endif
161 #if defined(MADV_HUGEPAGE)
162 			(void)shim_madvise(bufs[i].buf, bufs[i].sz, MADV_HUGEPAGE);
163 #endif
164 		}
165 
166 		for (i = 0; i < ctxt->mmaphuge_mmaps; i++) {
167 			uint8_t *buf = bufs[i].buf;
168 			size_t sz;
169 
170 			if (buf == MAP_FAILED)
171 				continue;
172 
173 			sz = bufs[i].sz;
174 			if (page_size < sz) {
175 				uint8_t *end_page = buf + (sz - page_size);
176 				int ret;
177 
178 				*buf = stress_mwc8();
179 				*end_page = stress_mwc8();
180 				/* Unmapping small page may fail on huge pages */
181 				ret = munmap((void *)end_page, page_size);
182 				if (ret == 0)
183 					ret = munmap((void *)buf, sz - page_size);
184 				if (ret != 0)
185 					(void)munmap((void *)buf, sz);
186 			} else {
187 				*buf = stress_mwc8();
188 				(void)munmap((void *)buf, sz);
189 			}
190 			bufs[i].buf = MAP_FAILED;
191 		}
192 	} while (keep_stressing(args));
193 
194 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
195 
196 	return EXIT_SUCCESS;
197 }
198 
199 /*
200  *  stress_mmaphuge()
201  *	stress huge page mmappings and unmappings
202  */
stress_mmaphuge(const stress_args_t * args)203 static int stress_mmaphuge(const stress_args_t *args)
204 {
205 	stress_mmaphuge_context_t ctxt;
206 
207 	int ret;
208 
209 	ctxt.mmaphuge_mmaps = MAX_MMAP_BUFS;
210 	(void)stress_get_setting("mmaphuge-mmaps", &ctxt.mmaphuge_mmaps);
211 
212 	ctxt.bufs = calloc(ctxt.mmaphuge_mmaps, sizeof(*ctxt.bufs));
213 	if (!ctxt.bufs) {
214 		pr_inf("%s: cannot allocate buffer array, skipping stressor\n",
215 			args->name);
216 		return EXIT_NO_RESOURCE;
217 	}
218 
219 	ret = stress_oomable_child(args, (void *)&ctxt, stress_mmaphuge_child, STRESS_OOMABLE_QUIET);
220 	free(ctxt.bufs);
221 
222 	return ret;
223 }
224 
225 stressor_info_t stress_mmaphuge_info = {
226 	.stressor = stress_mmaphuge,
227 	.class = CLASS_VM | CLASS_OS,
228 	.opt_set_funcs = opt_set_funcs,
229 	.help = help
230 };
231 
232 #else
233 
234 stressor_info_t stress_mmaphuge_info = {
235 	.stressor = stress_not_implemented,
236 	.class = CLASS_VM | CLASS_OS,
237 	.opt_set_funcs = opt_set_funcs,
238 	.help = help
239 };
240 
241 #endif
242