1 /******************************************************************************/
2 /* */
3 /* Copyright (c) International Business Machines Corp., 2001 */
4 /* Copyright (c) 2001 Manoj Iyer <manjo@austin.ibm.com> */
5 /* Copyright (c) 2003 Robbie Williamson <robbiew@us.ibm.com> */
6 /* Copyright (c) 2004 Paul Larson <plars@linuxtestproject.org> */
7 /* Copyright (c) 2007 <rsalveti@linux.vnet.ibm.com> */
8 /* Copyright (c) 2007 Suzuki K P <suzuki@in.ibm.com> */
9 /* Copyright (c) 2011 Cyril Hrubis <chrubis@suse.cz> */
10 /* */
11 /* This program is free software; you can redistribute it and/or modify */
12 /* it under the terms of the GNU General Public License as published by */
13 /* the Free Software Foundation; either version 2 of the License, or */
14 /* (at your option) any later version. */
15 /* */
16 /* This program is distributed in the hope that it will be useful, */
17 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
18 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
19 /* the GNU General Public License for more details. */
20 /* */
21 /* You should have received a copy of the GNU General Public License */
22 /* along with this program; if not, write to the Free Software */
23 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
24 /* */
25 /******************************************************************************/
26 /******************************************************************************/
27 /* Description: Test the LINUX memory manager. The program is aimed at */
28 /* stressing the memory manager by simultanious map/unmap/read */
29 /* by light weight processes, the test is scheduled to run for */
30 /* a mininum of 24 hours. */
31 /* */
32 /* Create two light weight processes X and Y. */
33 /* X - maps, writes and unmap a file in a loop. */
34 /* Y - read from this mapped region in a loop. */
35 /* read must be a success between map and unmap of the region. */
36 /* */
37 /******************************************************************************/
38 #include <stdio.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <sched.h>
43 #include <unistd.h>
44 #include <errno.h>
45 #include <sys/mman.h>
46 #include <sched.h>
47 #include <stdlib.h>
48 #include <signal.h>
49 #include <sys/time.h>
50 #include <sys/wait.h>
51 #include <setjmp.h>
52 #include <pthread.h>
53 #include <signal.h>
54 #include <string.h>
55 #include "test.h"
56
57 #define DISTANT_MMAP_SIZE (64*1024*1024)
58 #define OPT_MISSING(prog, opt) do { \
59 fprintf(stderr, "%s: option -%c ", prog, opt); \
60 fprintf(stderr, "requires an argument\n"); \
61 usage(prog); \
62 } while (0)
63
64 static int verbose_print = 0;
65 static char *volatile map_address;
66 static jmp_buf jmpbuf;
67 static volatile char read_lock = 0;
68 static void *distant_area;
69
70 char *TCID = "mmap1";
71 int TST_TOTAL = 1;
72
sig_handler(int signal,siginfo_t * info,void * ut)73 static void sig_handler(int signal, siginfo_t * info, void *ut)
74 {
75 switch (signal) {
76 case SIGALRM:
77 tst_resm(TPASS, "Test ended, success");
78 _exit(TPASS);
79 case SIGSEGV:
80 longjmp(jmpbuf, 1);
81 break;
82 default:
83 fprintf(stderr, "Unexpected signal - %d --- exiting\n", signal);
84 _exit(TBROK);
85 }
86 }
87
88 /*
89 * Signal handler that is active, when file is mapped, eg. we do not expect
90 * SIGSEGV to be delivered.
91 */
sig_handler_mapped(int signal,siginfo_t * info,void * ut)92 static void sig_handler_mapped(int signal, siginfo_t * info, void *ut)
93 {
94 switch (signal) {
95 case SIGALRM:
96 tst_resm(TPASS, "Test ended, success");
97 _exit(TPASS);
98 case SIGSEGV:
99 tst_resm(TINFO, "[%lu] Unexpected page fault at %p",
100 pthread_self(), info->si_addr);
101 _exit(TFAIL);
102 break;
103 default:
104 fprintf(stderr, "Unexpected signal - %d --- exiting\n", signal);
105 _exit(TBROK);
106 }
107 }
108
mkfile(int size)109 int mkfile(int size)
110 {
111 char template[] = "/tmp/ashfileXXXXXX";
112 int fd, i;
113
114 if ((fd = mkstemp(template)) == -1)
115 tst_brkm(TBROK | TERRNO, NULL, "mkstemp() failed");
116
117 unlink(template);
118
119 for (i = 0; i < size; i++)
120 if (write(fd, "a", 1) == -1)
121 tst_brkm(TBROK | TERRNO, NULL, "write() failed");
122
123 if (write(fd, "\0", 1) == -1)
124 tst_brkm(TBROK | TERRNO, NULL, "write() failed");
125
126 if (fsync(fd) == -1)
127 tst_brkm(TBROK | TERRNO, NULL, "fsync() failed");
128
129 return fd;
130 }
131
map_write_unmap(void * ptr)132 void *map_write_unmap(void *ptr)
133 {
134 struct sigaction sa;
135 long *args = ptr;
136 long i;
137 int j;
138
139 tst_resm(TINFO, "[%lu] - map, change contents, unmap files %ld times",
140 pthread_self(), args[2]);
141
142 if (verbose_print)
143 tst_resm(TINFO, "map_write_unmap() arguments are: "
144 "fd - arg[0]: %ld; "
145 "size of file - arg[1]: %ld; "
146 "num of map/write/unmap - arg[2]: %ld",
147 args[0], args[1], args[2]);
148
149 for (i = 0; i < args[2]; i++) {
150 map_address = mmap(distant_area, (size_t) args[1],
151 PROT_WRITE | PROT_READ, MAP_SHARED, (int)args[0], 0);
152
153 if (map_address == (void *)-1) {
154 perror("map_write_unmap(): mmap()");
155 pthread_exit((void *)1);
156 }
157
158 while (read_lock)
159 sched_yield();
160
161 sigfillset(&sa.sa_mask);
162 sigdelset(&sa.sa_mask, SIGSEGV);
163 sa.sa_flags = SA_SIGINFO | SA_NODEFER;
164 sa.sa_sigaction = sig_handler_mapped;
165
166 if (sigaction(SIGSEGV, &sa, NULL)) {
167 perror("map_write_unmap(): sigaction()");
168 pthread_exit((void *)1);
169 }
170
171 if (verbose_print)
172 tst_resm(TINFO, "map address = %p", map_address);
173
174 for (j = 0; j < args[1]; j++) {
175 map_address[j] = 'a';
176 if (random() % 2)
177 sched_yield();
178 }
179
180 if (verbose_print)
181 tst_resm(TINFO,
182 "[%ld] times done: of total [%ld] iterations, "
183 "map_write_unmap():memset() content of memory = %s",
184 i, args[2], (char *)map_address);
185
186 sigfillset(&sa.sa_mask);
187 sigdelset(&sa.sa_mask, SIGSEGV);
188 sa.sa_flags = SA_SIGINFO | SA_NODEFER;
189 sa.sa_sigaction = sig_handler;
190
191 if (sigaction(SIGSEGV, &sa, NULL)) {
192 perror("map_write_unmap(): sigaction()");
193 pthread_exit((void *)1);
194 }
195
196 if (munmap(map_address, (size_t) args[1]) == -1) {
197 perror("map_write_unmap(): mmap()");
198 pthread_exit((void *)1);
199 }
200 }
201
202 pthread_exit(NULL);
203 }
204
read_mem(void * ptr)205 void *read_mem(void *ptr)
206 {
207 long i;
208 long *args = ptr;
209 int j;
210
211 tst_resm(TINFO, "[%lu] - read contents of memory %p %ld times",
212 pthread_self(), map_address, args[2]);
213
214 if (verbose_print)
215 tst_resm(TINFO, "read_mem() arguments are: "
216 "number of reads to be performed - arg[2]: %ld; "
217 "read from address %p", args[2], map_address);
218
219 for (i = 0; i < args[2]; i++) {
220
221 if (verbose_print)
222 tst_resm(TINFO, "read_mem() in while loop %ld times "
223 "to go %ld times", i, args[2]);
224
225 if (setjmp(jmpbuf) == 1) {
226 read_lock = 0;
227 if (verbose_print)
228 tst_resm(TINFO, "page fault occurred due to "
229 "a read after an unmap");
230 } else {
231 if (verbose_print) {
232 read_lock = 1;
233 tst_resm(TINFO,
234 "read_mem(): content of memory: %s",
235 (char *)map_address);
236 read_lock = 0;
237 }
238 for (j = 0; j < args[1]; j++) {
239 read_lock = 1;
240 if (map_address[j] != 'a')
241 pthread_exit((void *)-1);
242 read_lock = 0;
243 if (random() % 2)
244 sched_yield();
245 }
246 }
247 }
248
249 pthread_exit(NULL);
250 }
251
usage(char * progname)252 static void usage(char *progname)
253 {
254 fprintf(stderr, "Usage: %s -d -l -s -v -x\n"
255 "\t -h help, usage message.\n"
256 "\t -l number of mmap/write/unmap default: 1000\n"
257 "\t -s size of the file to be mmapped default: 1024 bytes\n"
258 "\t -v print more info. default: quiet\n"
259 "\t -x test execution time default: 24 Hrs\n",
260 progname);
261
262 exit(-1);
263 }
264
265 struct signal_info {
266 int signum;
267 char *signame;
268 };
269
270 static struct signal_info sig_info[] = {
271 {SIGHUP, "SIGHUP"},
272 {SIGINT, "SIGINT"},
273 {SIGQUIT, "SIGQUIT"},
274 {SIGABRT, "SIGABRT"},
275 {SIGBUS, "SIGBUS"},
276 {SIGSEGV, "SIGSEGV"},
277 {SIGALRM, "SIGALRM"},
278 {SIGUSR1, "SIGUSR1"},
279 {SIGUSR2, "SIGUSR2"},
280 {-1, "ENDSIG"}
281 };
282
main(int argc,char ** argv)283 int main(int argc, char **argv)
284 {
285 int c, i;
286 int file_size;
287 int num_iter;
288 double exec_time;
289 int fd;
290 void *status;
291 pthread_t thid[2];
292 long chld_args[3];
293 extern char *optarg;
294 struct sigaction sigptr;
295 int ret;
296
297 /* set up the default values */
298 file_size = 1024;
299 num_iter = 1000;
300 exec_time = 24;
301
302 while ((c = getopt(argc, argv, "hvl:s:x:")) != -1) {
303 switch (c) {
304 case 'h':
305 usage(argv[0]);
306 break;
307 case 'l':
308 if ((num_iter = atoi(optarg)) == 0)
309 OPT_MISSING(argv[0], optopt);
310 else if (num_iter < 0)
311 printf
312 ("WARNING: bad argument. Using default %d\n",
313 (num_iter = 1000));
314 break;
315 case 's':
316 if ((file_size = atoi(optarg)) == 0)
317 OPT_MISSING(argv[0], optopt);
318 else if (file_size < 0)
319 printf
320 ("WARNING: bad argument. Using default %d\n",
321 (file_size = 1024));
322 break;
323 case 'v':
324 verbose_print = 1;
325 break;
326 case 'x':
327 exec_time = atof(optarg);
328 if (exec_time == 0)
329 OPT_MISSING(argv[0], optopt);
330 else if (exec_time < 0)
331 printf
332 ("WARNING: bad argument. Using default %.0f\n",
333 (exec_time = 24));
334 break;
335 default:
336 usage(argv[0]);
337 break;
338 }
339 }
340
341 /* We don't want other mmap calls to map into same area as is
342 * used for test (mmap_address). The test expects read to return
343 * test pattern or read must fail with SIGSEGV. Find an area
344 * that we can use, which is unlikely to be chosen for other
345 * mmap calls. */
346 distant_area = mmap(0, DISTANT_MMAP_SIZE, PROT_WRITE | PROT_READ,
347 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
348 if (distant_area == (void *)-1)
349 tst_brkm(TBROK | TERRNO, NULL, "distant_area: mmap()");
350 if (munmap(distant_area, (size_t) DISTANT_MMAP_SIZE) == -1)
351 tst_brkm(TBROK | TERRNO, NULL, "distant_area: munmap()");
352 distant_area += DISTANT_MMAP_SIZE / 2;
353
354 if (verbose_print)
355 tst_resm(TINFO, "Input parameters are: File size: %d; "
356 "Scheduled to run: %lf hours; "
357 "Number of mmap/write/read: %d",
358 file_size, exec_time, num_iter);
359
360 alarm(exec_time * 3600);
361
362 /* Do not mask SIGSEGV, as we are interested in handling it. */
363 sigptr.sa_sigaction = sig_handler;
364 sigfillset(&sigptr.sa_mask);
365 sigdelset(&sigptr.sa_mask, SIGSEGV);
366 sigptr.sa_flags = SA_SIGINFO | SA_NODEFER;
367
368 for (i = 0; sig_info[i].signum != -1; i++) {
369 if (sigaction(sig_info[i].signum, &sigptr, NULL) == -1) {
370 perror("man(): sigaction()");
371 fprintf(stderr,
372 "could not set handler for %s, errno = %d\n",
373 sig_info[i].signame, errno);
374 exit(-1);
375 }
376 }
377
378 for (;;) {
379 if ((fd = mkfile(file_size)) == -1)
380 tst_brkm(TBROK, NULL,
381 "main(): mkfile(): Failed to create temp file");
382
383 if (verbose_print)
384 tst_resm(TINFO, "Tmp file created");
385
386 chld_args[0] = fd;
387 chld_args[1] = file_size;
388 chld_args[2] = num_iter;
389
390 if ((ret =
391 pthread_create(&thid[0], NULL, map_write_unmap,
392 chld_args)))
393 tst_brkm(TBROK, NULL, "main(): pthread_create(): %s",
394 strerror(ret));
395
396 tst_resm(TINFO, "created writing thread[%lu]", thid[0]);
397
398 if ((ret = pthread_create(&thid[1], NULL, read_mem, chld_args)))
399 tst_brkm(TBROK, NULL, "main(): pthread_create(): %s",
400 strerror(ret));
401
402 tst_resm(TINFO, "created reading thread[%lu]", thid[1]);
403
404 for (i = 0; i < 2; i++) {
405 if ((ret = pthread_join(thid[i], &status)))
406 tst_brkm(TBROK, NULL,
407 "main(): pthread_join(): %s",
408 strerror(ret));
409
410 if (status)
411 tst_brkm(TFAIL, NULL,
412 "thread [%lu] - process exited "
413 "with %ld", thid[i], (long)status);
414 }
415
416 close(fd);
417 }
418
419 exit(0);
420 }
421