• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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