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 sigjmp_buf jmp_env;
28 static volatile bool do_jmp = true;
29
30 static const stress_help_t help[] = {
31 { NULL, "fault N", "start N workers producing page faults" },
32 { NULL, "fault-ops N", "stop after N page fault bogo operations" },
33 { NULL, NULL, NULL }
34 };
35
36 /*
37 * stress_segvhandler()
38 * SEGV handler
39 */
stress_segvhandler(int signum)40 static void MLOCKED_TEXT stress_segvhandler(int signum)
41 {
42 (void)signum;
43
44 if (do_jmp)
45 siglongjmp(jmp_env, 1); /* Ugly, bounce back */
46 }
47
48 /*
49 * stress_fault()
50 * stress min and max page faulting
51 */
stress_fault(const stress_args_t * args)52 static int stress_fault(const stress_args_t *args)
53 {
54 #if defined(HAVE_GETRUSAGE) && \
55 defined(RUSAGE_SELF) && \
56 defined(HAVE_RUSAGE_RU_MINFLT)
57 struct rusage usage;
58 #endif
59 char filename[PATH_MAX];
60 int ret;
61 NOCLOBBER int i;
62 char *start, *end;
63 const size_t len = stress_text_addr(&start, &end);
64 const size_t page_size = args->page_size;
65 void *mapto;
66 #if defined(HAVE_GETRUSAGE) && \
67 defined(RUSAGE_SELF) && \
68 defined(HAVE_RUSAGE_RU_MINFLT)
69 double t1 = 0.0, t2 = 0.0, dt;
70 #endif
71
72 ret = stress_temp_dir_mk_args(args);
73 if (ret < 0)
74 return exit_status(-ret);
75
76 (void)stress_temp_filename_args(args,
77 filename, sizeof(filename), stress_mwc32());
78 i = 0;
79
80 if (stress_sighandler(args->name, SIGSEGV, stress_segvhandler, NULL) < 0)
81 return EXIT_FAILURE;
82 if (stress_sighandler(args->name, SIGBUS, stress_segvhandler, NULL) < 0)
83 return EXIT_FAILURE;
84
85 mapto = mmap(NULL, page_size, PROT_READ,
86 MAP_ANONYMOUS | MAP_SHARED, -1, 0);
87
88 stress_set_proc_state(args->name, STRESS_STATE_RUN);
89
90 #if defined(HAVE_GETRUSAGE) && \
91 defined(RUSAGE_SELF) && \
92 defined(HAVE_RUSAGE_RU_MINFLT)
93 t1 = stress_time_now();
94 #endif
95 do {
96 int fd;
97 uint8_t *ptr;
98
99 ret = sigsetjmp(jmp_env, 1);
100 if (ret) {
101 do_jmp = false;
102 pr_err("%s: unexpected segmentation fault\n",
103 args->name);
104 break;
105 }
106
107 fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
108 if (fd < 0) {
109 if ((errno == ENOSPC) || (errno == ENOMEM))
110 continue; /* Try again */
111 pr_fail("%s: open %s failed, errno=%d (%s)\n",
112 args->name, filename, errno, strerror(errno));
113 break;
114 }
115 #if defined(HAVE_POSIX_FALLOCATE)
116 if (posix_fallocate(fd, 0, 1) < 0) {
117 if (errno == ENOSPC) {
118 (void)close(fd);
119 continue; /* Try again */
120 }
121 (void)close(fd);
122 pr_fail("%s: posix_fallocate failed, errno=%d (%s)\n",
123 args->name, errno, strerror(errno));
124 break;
125 }
126 #else
127 {
128 char buffer[1];
129
130 redo:
131 if (keep_stressing_flag() &&
132 (write(fd, buffer, sizeof(buffer)) < 0)) {
133 if ((errno == EAGAIN) || (errno == EINTR))
134 goto redo;
135 if (errno == ENOSPC) {
136 (void)close(fd);
137 continue;
138 }
139 (void)close(fd);
140 pr_fail("%s: write failed, errno=%d (%s)\n",
141 args->name, errno, strerror(errno));
142 break;
143 }
144 }
145 #endif
146 ret = sigsetjmp(jmp_env, 1);
147 if (ret) {
148 if (!keep_stressing(args))
149 do_jmp = false;
150 if (fd != -1)
151 (void)close(fd);
152 goto next;
153 }
154
155 /*
156 * Removing file here causes major fault when we touch
157 * ptr later
158 */
159 if (i & 1)
160 (void)unlink(filename);
161
162 ptr = (uint8_t *)mmap(NULL, 1, PROT_READ | PROT_WRITE,
163 MAP_SHARED, fd, 0);
164 (void)close(fd);
165 fd = -1;
166 (void)fd;
167
168 if (ptr == MAP_FAILED) {
169 if ((errno == EAGAIN) ||
170 (errno == ENOMEM) ||
171 (errno == ENFILE))
172 goto next;
173 pr_err("%s: mmap failed: errno=%d (%s)\n",
174 args->name, errno, strerror(errno));
175 break;
176
177 }
178 *ptr = 0; /* Cause the page fault */
179
180 if (munmap((void *)ptr, 1) < 0) {
181 pr_err("%s: munmap failed: errno=%d (%s)\n",
182 args->name, errno, strerror(errno));
183 break;
184 }
185
186 next:
187 /* Remove file on-non major fault case */
188 if (!(i & 1))
189 (void)unlink(filename);
190
191 /*
192 * Force a minor page fault by remapping an existing
193 * page in the text segment onto page mapto and then
194 * force reading a byte from the start of the page.
195 */
196 if (len > (page_size << 1)) {
197 if (mapto != MAP_FAILED) {
198 ptr = (uint8_t *)mmap(mapto, page_size, PROT_READ,
199 MAP_ANONYMOUS | MAP_SHARED, -1, 0);
200 if (ptr != MAP_FAILED) {
201 stress_uint8_put(*ptr);
202 (void)munmap((void *)ptr, page_size);
203 }
204 }
205 }
206 i++;
207 inc_counter(args);
208 } while (keep_stressing(args));
209 #if defined(HAVE_GETRUSAGE) && \
210 defined(RUSAGE_SELF) && \
211 defined(HAVE_RUSAGE_RU_MINFLT)
212 t2 = stress_time_now();
213 #endif
214 /* Clean up, most times this is redundant */
215
216 stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
217
218 if (mapto != MAP_FAILED)
219 (void)munmap(mapto, page_size);
220 (void)unlink(filename);
221 (void)stress_temp_dir_rm_args(args);
222
223 #if defined(HAVE_GETRUSAGE) && \
224 defined(RUSAGE_SELF) && \
225 defined(HAVE_RUSAGE_RU_MINFLT)
226 if (!shim_getrusage(RUSAGE_SELF, &usage)) {
227 pr_dbg("%s: page faults: minor: %lu, major: %lu\n",
228 args->name, usage.ru_minflt, usage.ru_majflt);
229 }
230 dt = t2 - t1;
231 if (dt > 0.0) {
232 stress_misc_stats_set(args->misc_stats, 0, "minor page faults per sec",
233 (double)usage.ru_minflt / dt);
234 stress_misc_stats_set(args->misc_stats, 1, "major page faults per sec",
235 (double)usage.ru_majflt / dt);
236 }
237 #endif
238 return EXIT_SUCCESS;
239 }
240
241 stressor_info_t stress_fault_info = {
242 .stressor = stress_fault,
243 .class = CLASS_INTERRUPT | CLASS_SCHEDULER | CLASS_OS,
244 .help = help
245 };
246