1 /*
2 * Copyright (C) Bull S.A. 1996
3 * Copyright (c) International Business Machines Corp., 2001
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 | shmem_test_04 |
21 | ==================================================================== |
22 | |
23 | Description: Test to verify mapping a file into memory (can also |
24 | be used as another anonymous shared memory test). |
25 | |
26 | Algorithm: o Create a file and map it into memory |
27 | o Fork N processes |
28 | o Each process writes ordered values to the memory |
29 | segment and calculates a corresponding checksum |
30 | o Each process then reads the values from the memory |
31 | segment, checks the value and creates a comparison |
32 | checksum |
33 | o Verify checksums match |
34 | o Unmap file |
35 | |
36 | System calls: The following system calls are tested: |
37 | |
38 | mmap () - maps a file-system object into virtual |
39 | memory |
40 | munmap () - unmaps a memory region |
41 | |
42 | Usage: shmem_test_04 |
43 | |
44 | To Compile: cc -O -o shmem_test_04 shmem_test_04.c |
45 | |
46 | Last update: Ver. 1.3, 1/30/94 00:40:27 |
47 | |
48 | Author: Scott Porter (scott@austin.ibm.com) |
49 | |
50 | Change Activity |
51 | |
52 | Version Date Name Reason |
53 | 0.1 070193 SLP Initial version for AIX 3.2.5 VMM testing |
54 | 0.2 011194 DJK Modified for AIX 4.1 testing |
55 | 1.2 020794 DJK Moved to "prod" directory |
56 | |
57 +---------------------------------------------------------------------*/
58
59 #include <unistd.h>
60 #include <errno.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <fcntl.h>
65 #include <signal.h>
66 #include <stdint.h>
67 #include <sys/mman.h>
68
69 #ifdef _LINUX_
70 typedef unsigned long ulong_t;
71 //#include <sys/except.h>
72 #ifndef PAGE_SIZE
73 #define PAGE_SIZE 0x400
74 #endif
75 #ifndef SIGDANGER
76 #define SIGDANGER 4
77 #endif
78 #else
79 #include <sys/m_except.h>
80 #include <sys/machine.h>
81 #endif
82
83 #ifdef _IA64
84 #include "dsi.h"
85 #endif
86
87 /* Defines
88 *
89 * USAGE: usage statement
90 */
91 #define TEMPDIR "."
92 #define TEMPNAME "tmpfile"
93
94 #define MB (1024*1024)
95 #define WPERMB (MB/sizeof(int))
96
97 #define FILEOBJ 1
98 #define MEMOBJ 2
99
100 #define MAXPROCS 1000
101 #define USAGE "\nUsage: %s [{-F | M}] [-l nloops] [-p nprocs] \n" \
102 "\t[{-m totmegs | -b totbytes}] [-v] [-W]\n\n" \
103 "\t-F Map a file\n" \
104 "\t-M Map annonymous memory\n" \
105 "\t-l nloops Number of loops\n" \
106 "\t-p nprocs Number of processes\n" \
107 "\t-m totmegs Length in MB\n" \
108 "\t-b totbytes Length in bytes\n" \
109 "\t-v Verbose\n" \
110 "\t-W Allocate to pgsp warning level\n\n"
111
112 /*
113 * Function prototypes
114 *
115 * parse_args (): Parse command line arguments
116 * sys_error (): System error message function
117 * error (): Error message function
118 */
119 static int mkemptyfile(uint);
120 static void parse_args(int, char **);
121 static void cleanup(int);
122 static void setup_signal_handlers();
123 static void int_handler(int signal);
124 static void segv_handler(int signal, int code, struct sigcontext *scp);
125 static void bus_handler(int signal, int code, struct sigcontext *scp);
126 static void sys_error(const char *, int);
127 static void error(const char *, int);
128
129 /*
130 * Global variables
131 *
132 * *flg: command line parsing variables
133 * filename:
134 * fd:
135 * nprocs:
136 * childpid:
137 */
138 static int bflg, lflg, mflg, pflg = 0;
139 static int Fflg, Mflg, Vflg, Wflg = 0;
140 char *filename;
141 int fd;
142 int nprocs;
143 int childpid[MAXPROCS];
144
145 int nloops, objtype, pgspblks;
146 pid_t parent_pid;
147 size_t length;
148
149 /*---------------------------------------------------------------------+
150 | main |
151 | ==================================================================== |
152 | |
153 | |
154 | Function: Main program (see prolog for more details) |
155 | |
156 | Returns: (0) Successful completion |
157 | (-1) Error occurred |
158 | |
159 +---------------------------------------------------------------------*/
main(int argc,char ** argv)160 int main(int argc, char **argv)
161 {
162 caddr_t region;
163 int *wptr;
164 int rc;
165 int word, nwords, addr, last, checksum, checksum2;
166 int loop;
167 pid_t pid;
168 int proc;
169 int fd;
170 int map_flags;
171
172 /*
173 * Parse command line arguments and print out program header
174 */
175 parse_args(argc, argv);
176 printf("%s: IPC Shared Memory TestSuite program\n", *argv);
177
178 setup_signal_handlers();
179 parent_pid = getpid();
180
181 /*
182 * Show options in effect.
183 */
184 printf("\n\tObject type to map = %s\n",
185 (objtype == MEMOBJ) ? "Anonymous memory" : "File");
186 printf("\tNumber of loops = %d\n", nloops);
187 printf("\tNumber of procs = %d\n", nprocs);
188 printf("\tBytes per process = %ld (%ldMB)\n", (long)length,
189 (long)length / MB);
190
191 /*
192 * Determine the number of words for that size.
193 */
194 nwords = length / sizeof(int);
195
196 /*
197 * Create shared memory mapping before forking
198 */
199 if (objtype == FILEOBJ) {
200 fd = mkemptyfile(length);
201 map_flags = MAP_FILE;
202 } else {
203 fd = -1;
204 map_flags = MAP_ANONYMOUS;
205 }
206
207 /*
208 * Map the object with the specified flags.
209 */
210 if ((region = mmap(0, length, PROT_READ | PROT_WRITE,
211 map_flags | MAP_SHARED, fd, 0))
212 == MAP_FAILED) {
213 sys_error("mmap failed", __LINE__);
214 }
215
216 /*
217 * Fork off the additional processes.
218 */
219 for (proc = 1; proc < nprocs; proc++) {
220 /*
221 * Child leaves loop, parent continues to fork.
222 */
223 if ((pid = fork()) < (pid_t) 0) {
224 sys_error("fork failed\n", __LINE__);
225 } else if (pid == 0)
226 break;
227 else
228 childpid[proc] = pid;
229 }
230
231 pid = getpid();
232
233 /*
234 * Initialize each word in the region with a unique value.
235 */
236 checksum = 0;
237 for (word = 0, wptr = (int *)region; word < nwords; word++, wptr++) {
238 if (Vflg) {
239 if (word && word % WPERMB == 0)
240 printf("\t[%d] %ldMB initialized...\n",
241 pid, (long)word / WPERMB);
242 }
243 *wptr = word;
244 checksum += word;
245 }
246 if (Vflg) {
247 printf("\t[%d] checksum = %d\n", pid, checksum);
248 }
249
250 for (loop = 1; loop <= nloops; loop++) {
251
252 /*
253 * Read back each word in the region and check its value.
254 */
255 checksum2 = 0;
256 last = -1;
257 for (word = 0, wptr = (int *)region; word < nwords;
258 word++, wptr++) {
259 if (Vflg) {
260 if (word && word % WPERMB == 0)
261 printf("\t[%d][%d] %ldMB verified...\n",
262 pid, loop, (long)word / WPERMB);
263 }
264 if (*wptr != word) {
265 addr = ((intptr_t) wptr & 0x0fffffff) / 4096;
266 if (addr != last) {
267 last = addr;
268 if (Vflg) {
269 printf
270 ("\t[%d][%d] page in error at addr = %d\n",
271 pid, loop, addr);
272 }
273 }
274 if (Vflg) {
275 printf
276 ("\t[%d][%d] Word = %d, Value = %d\n",
277 pid, loop, word, *wptr);
278 }
279 }
280 checksum2 += *wptr;
281 }
282 if (Vflg) {
283 printf("\t[%d][%d] checksum2 = %d\n", pid, loop,
284 checksum2);
285 }
286
287 if (checksum2 == checksum) {
288 if (Vflg) {
289 printf("\t[%d][%d] Check sums compare\n", pid,
290 loop);
291 }
292 } else {
293 if (Vflg) {
294 printf("\t[%d][%d] Check sums DON'T compare\n",
295 pid, loop);
296 }
297 }
298
299 } /* end loop */
300
301 /*
302 * Unmap the region.
303 */
304 if ((rc = munmap(region, length)) != 0) {
305 sys_error("munmap failed", __LINE__);
306 }
307 /*
308 * Program completed successfully -- exit
309 */
310 if (pid != parent_pid)
311 exit(0);
312
313 printf("\nsuccessful!\n");
314 cleanup(0);
315 return 0;
316 }
317
cleanup(int rc)318 static void cleanup(int rc)
319 {
320 int i;
321
322 /*
323 * Close and remove any file we've created.
324 */
325 if (Fflg) {
326 close(fd);
327 unlink(filename);
328 }
329
330 /*
331 * Kill any child processes we've started.
332 */
333 if (rc) {
334 for (i = 1; i < nprocs; i++) {
335 kill(childpid[i], SIGKILL);
336 }
337 }
338
339 exit(rc);
340 }
341
342 /*
343 * segv_handler - signal handler for SIGSEGV
344 */
segv_handler(int signal,int code,struct sigcontext * scp)345 static void segv_handler(int signal, int code, struct sigcontext *scp)
346 {
347 #ifndef _LINUX_
348 int except;
349 ulong_t dar, dsisr;
350 int rc;
351
352 /*
353 * Get the exception type. This is either an errno value
354 * or an exception value from <sys/m_except.h>.
355 */
356 #ifdef _IA64
357 except = scp->sc_context.__excp_type;
358 #else
359 except = scp->sc_jmpbuf.jmp_context.excp_type;
360 #endif
361
362 /*
363 * Get the Data Address Register and Interrupt Status Register
364 * for the exception.
365 */
366 #ifdef _IA64
367 dar = scp->sc_context.__ifa;
368 dsisr = scp->sc_context.__isr;
369 #else
370 dar = scp->sc_jmpbuf.jmp_context.except[0];
371 dsisr = scp->sc_jmpbuf.jmp_context.except[1];
372 #endif
373
374 printf("SIGSEGV occurred on address 0x%08x.\n", dar);
375
376 /*
377 * Determine if the operation was a load or a store.
378 * Definition of bits in DSISR are in <sys/machine.h>.
379 */
380 if (dsisr & DSISR_ST) {
381 printf("The operation was a store.\n");
382 } else {
383 printf("The operation was a load.\n");
384 }
385
386 switch (except) {
387
388 case EFAULT:
389 printf("Exception was due to a bad address.\n");
390 break;
391
392 case EXCEPT_PROT:
393 printf("Exception was due to a protection violation.\n");
394 break;
395
396 default:
397 printf("Exception type = 0x%08x.\n", except);
398 }
399 #else
400 printf("An unexpected segmentation fault occurred... Exiting\n");
401 #endif
402
403 cleanup(1);
404 }
405
406 /*
407 * bus_handler - signal handler for SIGBUS
408 */
bus_handler(int signal,int code,struct sigcontext * scp)409 static void bus_handler(int signal, int code, struct sigcontext *scp)
410 {
411 #ifndef _LINUX_
412 int except;
413 ulong_t dar, dsisr;
414 int rc;
415
416 /*
417 * Get the exception type. This is either an errno value
418 * or an exception value from <sys/m_except.h>.
419 */
420 #ifdef _IA64
421 except = scp->sc_context.__excp_type;
422 #else
423 except = scp->sc_jmpbuf.jmp_context.excp_type;
424 #endif
425
426 /*
427 * Get the Data Address Register and Interrupt Status Register
428 * for the exception.
429 */
430 #ifdef _IA64
431 dar = scp->sc_context.__ifa;
432 dsisr = scp->sc_context.__isr;
433 #else
434 dar = scp->sc_jmpbuf.jmp_context.except[0];
435 dsisr = scp->sc_jmpbuf.jmp_context.except[1];
436 #endif
437
438 printf("SIGBUS occurred on address 0x%08x:\n", dar);
439
440 switch (except) {
441
442 case ENOSPC:
443 printf("A mapper fault required disk allocation and \
444 no space is left on the device.\n");
445 break;
446
447 case EDQUOT:
448 printf("A mapper fault required disk allocation and \
449 the disc quota was exceeded.\n");
450 break;
451
452 case EXCEPT_EOF:
453 printf("An mmap() mapper referenced beyond end-of-file.\n");
454 break;
455
456 default:
457 printf("Exception type = 0x%08x.\n", except);
458 }
459 #else
460 printf("An unexpected bus error occurred... Exiting\n");
461 #endif
462
463 cleanup(1);
464 }
465
466 /*
467 * int_handler - signal handler for SIGINT
468 */
int_handler(int sig)469 static void int_handler(int sig)
470 {
471 cleanup(1);
472 }
473
474 /*
475 * mkemptyfile()
476 *
477 * Make an empty temporary file of a given size and return its descriptor.
478 */
mkemptyfile(uint size)479 static int mkemptyfile(uint size)
480 {
481 #ifdef _LINUX_
482
483 filename = malloc(256);
484
485 sprintf(filename, "%s/%sXXXXXX", TEMPDIR, TEMPNAME);
486 fd = mkstemp(filename);
487
488 #else
489 /* Get a new file name
490 */
491 filename = tempnam(TEMPDIR, TEMPNAME);
492
493 /* Create the file.
494 * O_EXCL: I'm supposed to be getting a unique name so if
495 * a file already exists by this name then fail.
496 * 0700: Set up for r/w access by owner only.
497 */
498 if ((fd = open(filename, O_CREAT | O_EXCL | O_RDWR, 0700)) == -1)
499 return (-1);
500
501 #endif // _LINUX_
502
503 /* Now extend it to the requested length.
504 */
505 if (lseek(fd, size - 1, SEEK_SET) == -1)
506 return (-1);
507
508 if (write(fd, "\0", 1) == -1)
509 return (-1);
510
511 /* Sync the file out.
512 */
513 fsync(fd);
514
515 return (fd);
516 }
517
518 /*---------------------------------------------------------------------+
519 | setup_signal_handlers |
520 | ==================================================================== |
521 | |
522 | Function: Sets up signal handlers for following signals: |
523 | |
524 | SIGINT - interrupt, generated from terminal spec. char |
525 | SIGBUS - bus error (specification execption) |
526 | SIGSEGV - segmentation violation |
527 | |
528 +---------------------------------------------------------------------*/
setup_signal_handlers()529 static void setup_signal_handlers()
530 {
531 struct sigaction sigact;
532
533 sigact.sa_flags = 0;
534 sigfillset(&sigact.sa_mask);
535
536 /*
537 * Establish the signal handlers for SIGSEGV, SIGBUS & SIGINT
538 */
539 sigact.sa_handler = (void (*)(int))segv_handler;
540 if (sigaction(SIGSEGV, &sigact, NULL) < 0)
541 sys_error("sigaction failed", __LINE__);
542
543 sigact.sa_handler = (void (*)(int))bus_handler;
544 if (sigaction(SIGBUS, &sigact, NULL) < 0)
545 sys_error("sigaction failed", __LINE__);
546
547 sigact.sa_handler = (void (*)(int))int_handler;
548 if (sigaction(SIGINT, &sigact, NULL) < 0)
549 sys_error("sigaction failed", __LINE__);
550 }
551
552 /*---------------------------------------------------------------------+
553 | parse_args () |
554 | ==================================================================== |
555 | |
556 | Function: Parse the command line arguments & initialize global |
557 | variables. |
558 | |
559 | Updates: (command line options) |
560 | |
561 | [-s] size: shared memory segment size |
562 | |
563 +---------------------------------------------------------------------*/
parse_args(int argc,char ** argv)564 static void parse_args(int argc, char **argv)
565 {
566 int opt;
567 int bytes = 0, megabytes = 0;
568 int errflag = 0;
569 char *program_name = *argv;
570 extern char *optarg; /* Command line option */
571
572 /*
573 * Parse command line options.
574 */
575 while ((opt = getopt(argc, argv, "DFMWvb:l:m:p:")) != EOF) {
576 switch (opt) {
577 case 'F': /* map a file */
578 Fflg++;
579 break;
580 case 'M': /* map anonymous memory */
581 Mflg++;
582 break;
583 case 'v': /* verbose */
584 Vflg++;
585 break;
586 case 'W': // allocate to pgsp warning level
587 Wflg++;
588 break;
589 case 'b': /* length in bytes */
590 bflg++;
591 bytes = atoi(optarg);
592 break;
593 case 'l': /* number of loops */
594 lflg++;
595 nloops = atoi(optarg);
596 break;
597 case 'm': /* length in MB */
598 mflg++;
599 megabytes = atoi(optarg);
600 break;
601 case 'p': /* number of processes */
602 pflg++;
603 nprocs = atoi(optarg);
604 break;
605 default:
606 errflag++;
607 break;
608 }
609 }
610 if (errflag) {
611 fprintf(stderr, USAGE, program_name);
612 exit(2);
613 }
614
615 /*
616 * Determine the number of processes to run.
617 */
618 if (pflg) {
619 if (nprocs > MAXPROCS)
620 nprocs = MAXPROCS;
621 } else {
622 nprocs = 1;
623 }
624
625 /*
626 * Determine the type of object to map.
627 */
628 if (Fflg) {
629 objtype = FILEOBJ;
630 } else if (Mflg) {
631 objtype = MEMOBJ;
632 } else {
633 objtype = MEMOBJ;
634 }
635
636 /*
637 * The 'W' flag is used to determine the size of an anonymous
638 * region that will use the amount of paging space available
639 * close to the paging space warning level.
640 */
641
642 if (Wflg)
643 #ifdef _LINUX_
644 printf
645 ("Option 'W' not implemented in Linux (psdanger() and SIGDANGER)\n");
646 #else
647 {
648 pgspblks = psdanger(SIGDANGER);
649 pgspblks -= 256; // leave a little room
650 if (pgspblks < 0)
651 pgspblks = 0;
652 bytes = pgspblks * PAGE_SIZE;
653 bflg = 1;
654 mflg = 0;
655 }
656 #endif
657
658 /*
659 * Determine size of region to map in bytes for each process.
660 */
661 if (mflg) {
662 length = megabytes * MB;
663 } else if (bflg) {
664 length = bytes;
665 } else {
666 length = 16 * MB;
667 }
668
669 /*
670 * Set default loops.
671 */
672 if (!lflg) {
673 nloops = 1;
674 }
675 }
676
677 /*---------------------------------------------------------------------+
678 | sys_error () |
679 | ==================================================================== |
680 | |
681 | Function: Creates system error message and calls error () |
682 | |
683 +---------------------------------------------------------------------*/
sys_error(const char * msg,int line)684 static void sys_error(const char *msg, int line)
685 {
686 char syserr_msg[256];
687
688 sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno));
689 error(syserr_msg, line);
690 }
691
692 /*---------------------------------------------------------------------+
693 | error () |
694 | ==================================================================== |
695 | |
696 | Function: Prints out message and exits... |
697 | |
698 +---------------------------------------------------------------------*/
error(const char * msg,int line)699 static void error(const char *msg, int line)
700 {
701 fprintf(stderr, "ERROR [line: %d] %s\n", line, msg);
702 cleanup(1);
703 }
704