1 /* IBM Corporation */
2 /* 01/02/2003 Port to LTP [email protected] */
3 /* 06/30/2001 Port to Linux [email protected] */
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
82 extern time_t time(time_t *);
83 extern char *ctime(const time_t *);
84 extern void *malloc(size_t);
85 extern void exit(int);
86 extern long lrand48(void);
87 extern void srand(unsigned);
88 extern void srand48(long);
89 extern int rand(void);
90 extern int atoi(const char *);
91
92 typedef unsigned char uchar_t;
93
94 char *usage = "-p nprocs [-t minutes -s mapsize -m -r -d]";
95
96 unsigned int initrand(void);
97 void finish(int sig);
98 void child_mapper(unsigned procno, unsigned nprocs);
99 int mapokay(uchar_t * expbuf);
100
101 int finished = 0;
102 int debug = 0;
103 int mapsize = MAPSIZE;
104 unsigned mappages;
105 int pagesize;
106 unsigned randloops = 0;
107 unsigned dosync = 0;
108 unsigned pattern = 0;
109 caddr_t mapaddr;
110
main(int argc,char * argv[])111 int main(int argc, char *argv[])
112 {
113 char *progname;
114 unsigned c;
115 extern char *optarg;
116 unsigned nprocs = 0;
117 unsigned procno;
118 pid_t *pidarray = NULL;
119 pid_t pid;
120 uchar_t *buf, *ptr;
121 unsigned int seed;
122 float alarmtime = 0;
123 struct sigaction sa;
124 unsigned i, j;
125 uchar_t data;
126 int no_prob = 0;
127 time_t t;
128 int wait_stat;
129
130 progname = *argv;
131 pagesize = sysconf(_SC_PAGE_SIZE);
132
133 if (argc < 2) {
134 (void)fprintf(stderr, "usage: %s %s\n", progname, usage);
135 anyfail();
136 }
137
138 while ((c = getopt(argc, argv, "mdrp:t:s:")) != -1) {
139 switch (c) {
140 case 'd':
141 debug = 1;
142 break;
143 case 't':
144 alarmtime = atof(optarg) * 60;
145 break;
146 case 'p':
147 nprocs = atoi(optarg);
148 break;
149 case 'm':
150 dosync = 1;
151 break;
152 case 's':
153 mapsize = atoi(optarg);
154 if (mapsize < 0) {
155 (void)fprintf(stderr, "error: negative "
156 "mapsize\n");
157 anyfail();
158 }
159 break;
160 case 'r':
161 randloops = 1;
162 break;
163 default:
164 (void)fprintf(stderr, "usage: %s %s\n", progname,
165 usage);
166 anyfail();
167 }
168 }
169
170 /* nprocs is unsigned */
171 if (nprocs > 255) {
172 (void)fprintf(stderr, "invalid nprocs %d - (range 0-255)\n",
173 nprocs);
174 anyfail();
175 }
176 (void)time(&t);
177 // (void)printf("%s: Started %s", argv[0], ctime(&t)); LTP Port
178
179 seed = initrand();
180 pattern = seed & 0xff;
181
182 if (debug) {
183 (void)printf("%s mapsize %d bytes, pattern %d\n",
184 progname, mapsize, pattern);
185 if (alarmtime)
186 (void)printf("running for %f minutes\n",
187 alarmtime / 60);
188 else
189 (void)printf("running with no time limit\n");
190 }
191
192 if ((mapaddr = mmap(0, mapsize, PROT_READ | PROT_WRITE,
193 MAP_ANONYMOUS | MAP_SHARED, 0, 0))
194 == (caddr_t) - 1) {
195 perror("mmap error");
196 anyfail();
197 }
198
199 if ((buf = malloc(pagesize)) == NULL
200 || (pidarray = malloc(nprocs * sizeof(pid_t))) == NULL) {
201 perror("malloc error");
202 anyfail();
203 }
204
205 for (i = 0; i < nprocs; i++)
206 *(pidarray + i) = 0;
207
208 /*
209 * Initialize page compare buffer, then initialize map.
210 */
211
212 for (i = 0, data = 0; i < pagesize; i++) {
213 *(buf + i) = (data + pattern) & 0xff;
214 if (++data == nprocs)
215 data = 0;
216 }
217
218 mappages = roundup(mapsize, pagesize) / pagesize;
219 ptr = (uchar_t *) mapaddr;
220
221 for (i = 0; i < mappages; i++) {
222 for (j = 0; j < pagesize; j++)
223 *ptr++ = *(buf + j);
224 }
225
226 /*
227 * Fork off mmap children.
228 */
229 for (procno = 0; procno < nprocs; procno++) {
230 switch (pid = fork()) {
231
232 case -1:
233 perror("fork error");
234 goto cleanup;
235
236 case 0:
237 child_mapper(procno, nprocs);
238 exit(0);
239
240 default:
241 pidarray[procno] = pid;
242 }
243 }
244
245 /*
246 * Plan for death by signal. User may have specified
247 * a time limit, in which set an alarm and catch SIGALRM.
248 * Also catch and cleanup with SIGINT.
249 */
250 sa.sa_handler = finish;
251 sa.sa_flags = 0;
252 if (sigemptyset(&sa.sa_mask)) {
253 perror("sigemptyset error");
254 goto cleanup;
255 }
256
257 if (sigaction(SIGINT, &sa, 0) == -1) {
258 perror("sigaction error");
259 goto cleanup;
260 }
261
262 if (alarmtime) {
263 if (sigaction(SIGALRM, &sa, 0) == -1) {
264 perror("sigaction error");
265 goto cleanup;
266 }
267 (void)alarm(alarmtime);
268 }
269
270 /*
271 * Now wait for children and refork them as needed.
272 */
273
274 while (!finished) {
275 do {
276 pid = wait(&wait_stat);
277 } while (pid == -1 && errno == EINTR);
278 /*
279 * Block signals while processing child exit.
280 */
281
282 if (sighold(SIGALRM) || sighold(SIGINT)) {
283 perror("sighold error");
284 goto cleanup;
285 }
286
287 if (pid != -1) {
288 /*
289 * Check exit status, then refork with the
290 * appropriate procno.
291 */
292 if (!WIFEXITED(wait_stat)
293 || WEXITSTATUS(wait_stat) != 0) {
294 (void)fprintf(stderr, "child exit with err "
295 "<x%x>\n", wait_stat);
296 goto cleanup;
297 }
298 for (i = 0; i < nprocs; i++)
299 if (pid == pidarray[i])
300 break;
301 if (i == nprocs) {
302 (void)fprintf(stderr,
303 "unknown child pid %d, <x%x>\n",
304 pid, wait_stat);
305 goto cleanup;
306 }
307
308 if ((pid = fork()) == -1) {
309 perror("fork error");
310 pidarray[i] = 0;
311 goto cleanup;
312 } else if (pid == 0) { /* child */
313 child_mapper(i, nprocs);
314 exit(0);
315 } else
316 pidarray[i] = pid;
317 } else {
318 /*
319 * wait returned an error. If EINTR, then
320 * normal finish, else it's an unexpected
321 * error...
322 */
323 if (errno != EINTR || !finished) {
324 perror("unexpected wait error");
325 goto cleanup;
326 }
327 }
328 if (sigrelse(SIGALRM) || sigrelse(SIGINT)) {
329 perror("sigrelse error");
330 goto cleanup;
331 }
332 }
333
334 /*
335 * Finished! Check the map for sanity, then kill all
336 * the children and done!.
337 */
338
339 if (sighold(SIGALRM)) {
340 perror("sighold error");
341 goto cleanup;
342 }
343 (void)alarm(0);
344 no_prob = 1;
345
346 cleanup:
347 for (i = 0; i < nprocs; i++)
348 (void)kill(pidarray[i], SIGKILL); /* failure? oh well. */
349
350 while (wait(&wait_stat) != -1 || errno != ECHILD)
351 continue;
352
353 if (no_prob) { /* only check file if no errors */
354 if (!mapokay(buf)) {
355 (void)fprintf(stderr, "map data incorrect!\n");
356 anyfail();
357 } else
358 (void)printf("map data okay\n");
359 }
360
361 (void)time(&t);
362 // (void)printf("%s: Finished %s", argv[0], ctime(&t)); LTP POrt
363 ok_exit();
364 tst_exit();
365 }
366
367 /*
368 * Child process that reads/writes map. The child reads/writes
369 * its own locations on random pages of the map (its locations being
370 * determined based on nprocs & procno). After a specific number of
371 * iterations, it exits.
372 */
child_mapper(unsigned procno,unsigned nprocs)373 void child_mapper(unsigned procno, unsigned nprocs)
374 {
375 uchar_t *paddr;
376 unsigned randpage;
377 unsigned int seed;
378 unsigned loopcnt;
379 unsigned nloops;
380 unsigned i;
381
382 seed = initrand(); /* initialize random seed */
383
384 nloops = (randloops) ? (lrand48() % MAXLOOPS) : MAXLOOPS;
385
386 if (debug)
387 (void)printf("child %d (pid %d): seed %d, loop %d\n",
388 procno, getpid(), seed, nloops);
389
390 /*
391 * Now loop read/writing random pages.
392 */
393
394 for (loopcnt = 0; loopcnt < nloops; loopcnt++) {
395 randpage = lrand48() % mappages;
396 /* find the page address */
397 paddr = (uchar_t *) (mapaddr + (randpage * pagesize));
398
399 for (i = procno; i < pagesize; i += nprocs) {
400 if (*((unsigned char *)(paddr + i))
401 != ((procno + pattern) & 0xff)) {
402 (void)fprintf(stderr,
403 "child %d: invalid data <x%x>",
404 procno,
405 *((unsigned char *)(paddr + i)));
406 (void)fprintf(stderr,
407 " at pg %d off %d, exp <x%x>\n",
408 randpage, i,
409 (procno + pattern) & 0xff);
410 anyfail();
411 }
412 /*
413 * Now write it.
414 */
415
416 *(paddr + i) = (procno + pattern) & 0xff;
417 }
418 }
419
420 if (dosync) {
421 randpage = (unsigned)lrand48() % mappages;
422 paddr = (uchar_t *) mapaddr + (randpage * pagesize);
423 if (msync((caddr_t) paddr, (mappages - randpage) * pagesize,
424 MS_SYNC) == -1) {
425 perror("msync error");
426 anyfail();
427 }
428 }
429
430 exit(0);
431 }
432
433 /*
434 * Make sure file has all the correct data.
435 */
mapokay(uchar_t * expbuf)436 int mapokay(uchar_t * expbuf)
437 {
438 uchar_t *ptr;
439 unsigned i, j;
440
441 ptr = (uchar_t *) mapaddr;
442 for (i = 0; i < mappages; i++) {
443 /*
444 * Compare read bytes of data.
445 */
446 for (j = 0; j < pagesize; j++) {
447 if (*ptr != expbuf[j]) {
448 (void)fprintf(stderr,
449 "bad map data: exp %c got %c)",
450 expbuf[j], *ptr);
451 (void)fprintf(stderr, ", pg %d off %d\n", i, j);
452 anyfail();
453 }
454 ptr++;
455 }
456 }
457
458 return 1;
459 }
460
finish(int sig)461 /*ARGSUSED*/ void finish(int sig)
462 {
463 finished++;
464 return;
465 }
466
initrand(void)467 unsigned int initrand(void)
468 {
469 unsigned int seed;
470
471 /*
472 * Initialize random seed... Got this from a test written
473 * by scooter:
474 * Use srand/rand to diffuse the information from the
475 * time and pid. If you start several processes, then
476 * the time and pid information don't provide much
477 * variation.
478 */
479 srand((unsigned int)getpid());
480 seed = rand();
481 srand((unsigned int)time(NULL));
482 seed = (seed ^ rand()) % 100000;
483 srand48((long int)seed);
484 return (seed);
485 }
486
487 /***** LTP Port *****/
ok_exit(void)488 void ok_exit(void)
489 {
490 tst_resm(TPASS, "Test passed");
491 tst_exit();
492 }
493
anyfail(void)494 int anyfail(void)
495 {
496 tst_brkm(TFAIL, NULL, "Test failed");
497 }
498
499 /***** ** ** *****/
500