1 /* IBM Corporation */
2 /* 01/02/2003 Port to LTP avenakt@us.ibm.com */
3 /* 06/30/2001 Port to Linux nsharoff@us.ibm.com */
4 /*
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #define _GNU_SOURCE 1
21 #include <sys/types.h>
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <sys/mman.h>
26 #include <sys/wait.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <errno.h>
30 /***** LTP Port *****/
31 #include "test.h"
32 #define FAILED 0
33 #define PASSED 1
34
35 int local_flag = PASSED;
36 char *TCID = "mmapstress09";
37 FILE *temp;
38 int TST_TOTAL = 1;
39
40 int anyfail();
41 void ok_exit();
42 /***** ** ** *****/
43
44 /*
45 * This test is mostly duplicated from the tmmap test, but tests
46 * stress tests anonymous maps. It forks a specified number of children,
47 * who inherit an anonymous shared map, and who, make a given number of
48 * accesses to random pages in the map (reading & writing and comparing data).
49 * Then the child exits and the parent forks another to take its place.
50 * The test fails if a child sees incorrect data.
51 *
52 * This program continues to run until it either receives a SIGINT,
53 * or times out (if a timeout value is specified). When either of
54 * these things happens, it cleans up its kids, then checks
55 * the map to make sure it has the correct data.
56 *
57 * usage:
58 * mmapstress09 -p nprocs [-t minutes -s mapsize -m msync -r -d]
59 *
60 * where:
61 * -p nprocs - specifies the number of mapping children
62 * to create. (nprocs + 1 children actually
63 * get created, since one is the writer child)
64 * -t minutes - specifies minutes to run. If not specified,
65 * default is to run forever until a SIGINT
66 * is received.
67 * -s mapsize - mapsize (defaults to MAPSIZE)
68 * -m - do msyncs
69 * -r - randomize number of pages map children check.
70 * (random % MAXLOOPS). If not specified, each
71 * child checks MAXLOOPS pages.
72 * -d - enable debug outputd
73 */
74
75 #define MAXLOOPS 500 /* max pages for map children to write */
76 #define MAPSIZE (64*1024) /* default mapsize set up by parent */
77 #ifdef roundup
78 #undef roundup
79 #endif
80 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
81 #define min(x, y) (((x) < (y)) ? (x) : (y))
82
83 extern time_t time(time_t *);
84 extern char *ctime(const time_t *);
85 extern void *malloc(size_t);
86 extern void exit(int);
87 extern long lrand48(void);
88 extern void srand(unsigned);
89 extern void srand48(long);
90 extern int rand(void);
91 extern int atoi(const char *);
92
93 typedef unsigned char uchar_t;
94
95 char *usage = "-p nprocs [-t minutes -s mapsize -m -r -d]";
96
97 unsigned int initrand(void);
98 void finish(int sig);
99 void child_mapper(unsigned procno, unsigned nprocs);
100 int mapokay(uchar_t * expbuf);
101
102 int finished = 0;
103 int debug = 0;
104 int mapsize = MAPSIZE;
105 unsigned mappages;
106 int pagesize;
107 unsigned randloops = 0;
108 unsigned dosync = 0;
109 unsigned pattern = 0;
110 caddr_t mapaddr;
111
main(int argc,char * argv[])112 int main(int argc, char *argv[])
113 {
114 char *progname;
115 unsigned c;
116 extern char *optarg;
117 unsigned nprocs = 0;
118 unsigned procno;
119 pid_t *pidarray = NULL;
120 pid_t pid;
121 uchar_t *buf, *ptr;
122 unsigned int seed;
123 float alarmtime = 0;
124 struct sigaction sa;
125 unsigned i, j;
126 uchar_t data;
127 int no_prob = 0;
128 time_t t;
129 int wait_stat;
130
131 progname = *argv;
132 pagesize = sysconf(_SC_PAGE_SIZE);
133
134 if (argc < 2) {
135 (void)fprintf(stderr, "usage: %s %s\n", progname, usage);
136 anyfail();
137 }
138
139 while ((c = getopt(argc, argv, "mdrp:t:s:")) != -1) {
140 switch (c) {
141 case 'd':
142 debug = 1;
143 break;
144 case 't':
145 alarmtime = atof(optarg) * 60;
146 break;
147 case 'p':
148 nprocs = atoi(optarg);
149 break;
150 case 'm':
151 dosync = 1;
152 break;
153 case 's':
154 mapsize = atoi(optarg);
155 if (mapsize < 0) {
156 (void)fprintf(stderr, "error: negative "
157 "mapsize\n");
158 anyfail();
159 }
160 break;
161 case 'r':
162 randloops = 1;
163 break;
164 default:
165 (void)fprintf(stderr, "usage: %s %s\n", progname,
166 usage);
167 anyfail();
168 }
169 }
170
171 /* nprocs is unsigned */
172 if (nprocs > 255) {
173 (void)fprintf(stderr, "invalid nprocs %d - (range 0-255)\n",
174 nprocs);
175 anyfail();
176 }
177 (void)time(&t);
178 // (void)printf("%s: Started %s", argv[0], ctime(&t)); LTP Port
179
180 seed = initrand();
181 pattern = seed & 0xff;
182
183 if (debug) {
184 (void)printf("%s mapsize %d bytes, pattern %d\n",
185 progname, mapsize, pattern);
186 if (alarmtime)
187 (void)printf("running for %f minutes\n",
188 alarmtime / 60);
189 else
190 (void)printf("running with no time limit\n");
191 }
192
193 if ((mapaddr = mmap(0, mapsize, PROT_READ | PROT_WRITE,
194 MAP_ANONYMOUS | MAP_SHARED, 0, 0))
195 == (caddr_t) - 1) {
196 perror("mmap error");
197 anyfail();
198 }
199
200 if ((buf = malloc(pagesize)) == NULL
201 || (pidarray = malloc(nprocs * sizeof(pid_t))) == NULL) {
202 perror("malloc error");
203 anyfail();
204 }
205
206 for (i = 0; i < nprocs; i++)
207 *(pidarray + i) = 0;
208
209 /*
210 * Initialize page compare buffer, then initialize map.
211 */
212
213 for (i = 0, data = 0; i < pagesize; i++) {
214 *(buf + i) = (data + pattern) & 0xff;
215 if (++data == nprocs)
216 data = 0;
217 }
218
219 mappages = roundup(mapsize, pagesize) / pagesize;
220 ptr = (uchar_t *) mapaddr;
221
222 for (i = 0; i < mappages; i++) {
223 for (j = 0; j < pagesize; j++)
224 *ptr++ = *(buf + j);
225 }
226
227 /*
228 * Fork off mmap children.
229 */
230 for (procno = 0; procno < nprocs; procno++) {
231 switch (pid = fork()) {
232
233 case -1:
234 perror("fork error");
235 goto cleanup;
236
237 case 0:
238 child_mapper(procno, nprocs);
239 exit(0);
240
241 default:
242 pidarray[procno] = pid;
243 }
244 }
245
246 /*
247 * Plan for death by signal. User may have specified
248 * a time limit, in which set an alarm and catch SIGALRM.
249 * Also catch and cleanup with SIGINT.
250 */
251 sa.sa_handler = finish;
252 sa.sa_flags = 0;
253 if (sigemptyset(&sa.sa_mask)) {
254 perror("sigemptyset error");
255 goto cleanup;
256 }
257
258 if (sigaction(SIGINT, &sa, 0) == -1) {
259 perror("sigaction error");
260 goto cleanup;
261 }
262
263 if (alarmtime) {
264 if (sigaction(SIGALRM, &sa, 0) == -1) {
265 perror("sigaction error");
266 goto cleanup;
267 }
268 (void)alarm(alarmtime);
269 }
270
271 /*
272 * Now wait for children and refork them as needed.
273 */
274
275 while (!finished) {
276 do {
277 pid = wait(&wait_stat);
278 } while (pid == -1 && errno == EINTR);
279 /*
280 * Block signals while processing child exit.
281 */
282
283 if (sighold(SIGALRM) || sighold(SIGINT)) {
284 perror("sighold error");
285 goto cleanup;
286 }
287
288 if (pid != -1) {
289 /*
290 * Check exit status, then refork with the
291 * appropriate procno.
292 */
293 if (!WIFEXITED(wait_stat)
294 || WEXITSTATUS(wait_stat) != 0) {
295 (void)fprintf(stderr, "child exit with err "
296 "<x%x>\n", wait_stat);
297 goto cleanup;
298 }
299 for (i = 0; i < nprocs; i++)
300 if (pid == pidarray[i])
301 break;
302 if (i == nprocs) {
303 (void)fprintf(stderr,
304 "unknown child pid %d, <x%x>\n",
305 pid, wait_stat);
306 goto cleanup;
307 }
308
309 if ((pid = fork()) == -1) {
310 perror("fork error");
311 pidarray[i] = 0;
312 goto cleanup;
313 } else if (pid == 0) { /* child */
314 child_mapper(i, nprocs);
315 exit(0);
316 } else
317 pidarray[i] = pid;
318 } else {
319 /*
320 * wait returned an error. If EINTR, then
321 * normal finish, else it's an unexpected
322 * error...
323 */
324 if (errno != EINTR || !finished) {
325 perror("unexpected wait error");
326 goto cleanup;
327 }
328 }
329 if (sigrelse(SIGALRM) || sigrelse(SIGINT)) {
330 perror("sigrelse error");
331 goto cleanup;
332 }
333 }
334
335 /*
336 * Finished! Check the map for sanity, then kill all
337 * the children and done!.
338 */
339
340 if (sighold(SIGALRM)) {
341 perror("sighold error");
342 goto cleanup;
343 }
344 (void)alarm(0);
345 no_prob = 1;
346
347 cleanup:
348 for (i = 0; i < nprocs; i++)
349 (void)kill(pidarray[i], SIGKILL); /* failure? oh well. */
350
351 while (wait(&wait_stat) != -1 || errno != ECHILD)
352 continue;
353
354 if (no_prob) { /* only check file if no errors */
355 if (!mapokay(buf)) {
356 (void)fprintf(stderr, "map data incorrect!\n");
357 anyfail();
358 } else
359 (void)printf("map data okay\n");
360 }
361
362 (void)time(&t);
363 // (void)printf("%s: Finished %s", argv[0], ctime(&t)); LTP POrt
364 ok_exit();
365 tst_exit();
366 }
367
368 /*
369 * Child process that reads/writes map. The child reads/writes
370 * its own locations on random pages of the map (its locations being
371 * determined based on nprocs & procno). After a specific number of
372 * iterations, it exits.
373 */
child_mapper(unsigned procno,unsigned nprocs)374 void child_mapper(unsigned procno, unsigned nprocs)
375 {
376 uchar_t *paddr;
377 unsigned randpage;
378 unsigned int seed;
379 unsigned loopcnt;
380 unsigned nloops;
381 unsigned i;
382
383 seed = initrand(); /* initialize random seed */
384
385 nloops = (randloops) ? (lrand48() % MAXLOOPS) : MAXLOOPS;
386
387 if (debug)
388 (void)printf("child %d (pid %d): seed %d, loop %d\n",
389 procno, getpid(), seed, nloops);
390
391 /*
392 * Now loop read/writing random pages.
393 */
394
395 for (loopcnt = 0; loopcnt < nloops; loopcnt++) {
396 randpage = lrand48() % mappages;
397 /* find the page address */
398 paddr = (uchar_t *) (mapaddr + (randpage * pagesize));
399
400 for (i = procno; i < pagesize; i += nprocs) {
401 if (*((unsigned char *)(paddr + i))
402 != ((procno + pattern) & 0xff)) {
403 (void)fprintf(stderr,
404 "child %d: invalid data <x%x>",
405 procno,
406 *((unsigned char *)(paddr + i)));
407 (void)fprintf(stderr,
408 " at pg %d off %d, exp <x%x>\n",
409 randpage, i,
410 (procno + pattern) & 0xff);
411 anyfail();
412 }
413 /*
414 * Now write it.
415 */
416
417 *(paddr + i) = (procno + pattern) & 0xff;
418 }
419 }
420
421 if (dosync) {
422 randpage = (unsigned)lrand48() % mappages;
423 paddr = (uchar_t *) mapaddr + (randpage * pagesize);
424 if (msync((caddr_t) paddr, (mappages - randpage) * pagesize,
425 MS_SYNC) == -1) {
426 perror("msync error");
427 anyfail();
428 }
429 }
430
431 exit(0);
432 }
433
434 /*
435 * Make sure file has all the correct data.
436 */
mapokay(uchar_t * expbuf)437 int mapokay(uchar_t * expbuf)
438 {
439 uchar_t *ptr;
440 unsigned i, j;
441
442 ptr = (uchar_t *) mapaddr;
443 for (i = 0; i < mappages; i++) {
444 /*
445 * Compare read bytes of data.
446 */
447 for (j = 0; j < pagesize; j++) {
448 if (*ptr != expbuf[j]) {
449 (void)fprintf(stderr,
450 "bad map data: exp %c got %c)",
451 expbuf[j], *ptr);
452 (void)fprintf(stderr, ", pg %d off %d\n", i, j);
453 anyfail();
454 }
455 ptr++;
456 }
457 }
458
459 return 1;
460 }
461
finish(int sig)462 /*ARGSUSED*/ void finish(int sig)
463 {
464 finished++;
465 return;
466 }
467
initrand(void)468 unsigned int initrand(void)
469 {
470 unsigned int seed;
471
472 /*
473 * Initialize random seed... Got this from a test written
474 * by scooter:
475 * Use srand/rand to diffuse the information from the
476 * time and pid. If you start several processes, then
477 * the time and pid information don't provide much
478 * variation.
479 */
480 srand((unsigned int)getpid());
481 seed = rand();
482 srand((unsigned int)time(NULL));
483 seed = (seed ^ rand()) % 100000;
484 srand48((long int)seed);
485 return (seed);
486 }
487
488 /***** LTP Port *****/
ok_exit(void)489 void ok_exit(void)
490 {
491 tst_resm(TPASS, "Test passed\n");
492 tst_exit();
493 }
494
anyfail(void)495 int anyfail(void)
496 {
497 tst_brkm(TFAIL, NULL, "Test failed\n");
498 }
499
500 /***** ** ** *****/
501