1/*****************************************************************************/
2/*									      */
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
21/******************************************************************************/
22/*									      */
23/* History:	July - 16 - 2001 Created by Manoj Iyer, IBM Austin TX.	      */
24/*			         email:manjo@austin.ibm.com		      */
25/*                                                                            */
26/*		July - 30 - 2001 Modified - Added function write_to_mem.      */
27/*							                      */
28/*              Aug  - 14 - 2001 Modified - Added code to remove the shared   */
29/*			         memory segment ids.                          */
30/*							                      */
31/*              Aug  - 15 - 2001 Modified - Added for loop to run the test    */
32/*			         repeatedly.                                  */
33/*                                                                            */
34/*		Oct  - 22 - 2001 Modified - Fixed bad code in main().         */
35/*				 removed stray code and options. Pthread_join */
36/*			         part fixed, older version was completely bad */
37/*                                                                            */
38/*		Nov  - 09 - 2001 Modified - Removed compile errors	      */
39/*				 - added missing header file string.h         */
40/*				 - removed unused variables.                  */
41/*				 - made read_ndx and write_ndx static variable*/
42/*                                                                            */
43/*		Nov - 91 - 2001	Modified - Changed scope of status variable   */
44/*				 - change the status of status variable from  */
45/*				   int *status to int status[1]               */
46/*                                                                            */
47/* File:	shmat1.c						      */
48/*			         					      */
49/* Description: Test the LINUX memory manager. The program is aimed at        */
50/*              stressing the memory manager by repeaded shmat/write/read/    */
51/*		shmatd of file/memory of random size (maximum 1000 * 4096)    */
52/*		done by multiple processes.				      */
53/*			         					      */
54/*		Create a file of random size upto 1000 times 4096. 	      */
55/*		process X shmats and un-shmats this file in memory.	      */
56/*		process Y changes content of the file to Y, ie writes to it.  */
57/*		process Z reads from this memory location, and varifies the   */
58/*		the content of the file.			              */
59/*			         					      */
60/******************************************************************************/
61
62/* Include Files							      */
63#include <stdio.h>		/* definitions for standard I/O               */
64#include <unistd.h>		/* required by usleep()                       */
65#include <errno.h>		/* definitions for errno                      */
66#include <sched.h>		/* definitions for sched_yield()              */
67#include <stdlib.h>		/* definitions for WEXIT macros               */
68#include <signal.h>		/* required by sigaction & sig handling fncs  */
69#include <sys/time.h>		/* definitions of settimer()                  */
70#include <sys/wait.h>		/* definitions of itimer structure            */
71#include <pthread.h>		/* definitions for pthread_create etc         */
72#include <setjmp.h>		/* required by setjmp longjmp                 */
73#include <sys/ucontext.h>	/* required by the signal handler             */
74#include <sys/ipc.h>		/* required by shmat shmget etc               */
75#include <sys/shm.h>		/* required by shmat shmget etc               */
76#include <string.h>		/* required by strncmp                        */
77
78/* Defines								      */
79#ifndef TRUE
80#define TRUE 1
81#endif
82#ifndef FALSE
83#define FALSE 0
84#endif
85#define prtln() printf(" I AM HERE ==> %s %d\n", __FILE__, __LINE__);
86
87#define STR_SHMAT  "  "
88#define STR_WRITER "    "
89#define STR_READER "      "
90
91/* Global Variables						              */
92void *map_address;		/* pointer to file in memory                  */
93sigjmp_buf jmpbuf;		/* argument to setjmp and longjmp             */
94int fsize;			/* size of the file to be created.            */
95int done_shmat = 0;		/* disallow read and writes before shmat      */
96
97/******************************************************************************/
98/*									      */
99/* Function:	sig_handler						      */
100/*									      */
101/* Description:	handle SIGALRM raised by set_timer(), SIGALRM is raised when  */
102/*		the timer expires. If any other signal is recived exit the    */
103/*		test.							      */
104/*									      */
105/* Input:	signal - signal number, intrested in SIGALRM!		      */
106/*									      */
107/* Return:	exit -1 if unexpected signal is recived			      */
108/*		exit 0 if SIGALRM is recieved			              */
109/*									      */
110/******************************************************************************/
111static void sig_handler(int signal,	/* signal number, set to handle SIGALRM       */
112			int code, ucontext_t *ut)
113{				/* contains pointer to sigcontext structure   */
114#ifdef __i386__
115	unsigned long except;	/* exception type.                            */
116	int ret = 0;		/* exit code from signal handler.             */
117	struct sigcontext *scp =	/* pointer to sigcontext structure            */
118	    (struct sigcontext *)&ut->uc_mcontext;
119#endif
120
121	if (signal == SIGALRM) {
122		fprintf(stdout, "Test ended, success\n");
123		exit(0);
124	}
125#ifdef __i386__
126	else {
127		except = scp->trapno;
128		fprintf(stderr, "signal caught - [%d] ", signal);
129	}
130
131	switch (except) {
132	case 10:
133		fprintf(stderr,
134			"Exception - invalid TSS, exception #[%ld]\n", except);
135		break;
136	case 11:
137		fprintf(stderr,
138			"Exception - segment not present, exception #[%ld]\n",
139			except);
140		break;
141	case 12:
142		fprintf(stderr,
143			"Exception - stack segment not present, exception #[%ld]\n",
144			except);
145		break;
146	case 13:
147		fprintf(stderr,
148			"Exception - general protection, exception #[%ld]\n",
149			except);
150		break;
151	case 14:
152		fprintf(stderr,
153			"Exception - page fault, exception #[%ld]\n", except);
154		ret = 1;
155		break;
156	default:
157		fprintf(stderr,
158			"Exception type not handled... unknown exception #[%ld]\n",
159			except);
160		break;
161	}
162
163	if (ret) {
164		if (scp->edi == (int)map_address) {
165			fprintf(stdout,
166				"page fault at [%#lx] - ignore\n", scp->edi);
167			siglongjmp(jmpbuf, 1);
168		} else if (scp->esi == (int)map_address) {
169			fprintf(stdout,
170				"page fault at [%#lx] - ignore\n", scp->esi);
171			siglongjmp(jmpbuf, 1);
172		} else {
173			fprintf(stderr,
174				"address at which sigfault occured: [%lx]\n"
175				"address at which sigfault occured: [%lx]\n"
176				"address at which memory was shmat: [%p]\n",
177				(unsigned long)scp->edi,
178				(unsigned long)scp->esi, map_address);
179			fprintf(stderr, "bad page fault exit test\n");
180			exit(-1);
181		}
182	} else
183		exit(-1);
184#else
185	fprintf(stderr, "caught signal %d -- exiting.\n", signal);
186	exit(-1);
187#endif
188}
189
190										/******************************************************************************//*                                                                            */
191/* Function:	usage							      */
192/*									      */
193/* Description:	Print the usage message.				      */
194/*									      */
195/* Return:	exits with -1						      */
196/*									      */
197/******************************************************************************/
198static void usage(char *progname)
199{				/* name of this program                       */
200	fprintf(stderr,
201		"Usage: %s -h -l -x\n"
202		"\t -h help, usage message.\n"
203		"\t -l number of map - write - unmap.    default: 1000\n"
204		"\t -x time for which test is to be run. default: 24 Hrs\n",
205		progname);
206	exit(-1);
207}
208
209/******************************************************************************/
210/*									      */
211/* Function:	shmat_shmdt						      */
212/*									      */
213/* Description:	Thread X function.					      */
214/*		shmat a random size file and shm-detach this file, this is    */
215/*		done for user defined number of times.			      */
216/*									      */
217/* Input:	arg[0]		   number of times shmat shmdt is done        */
218/*									      */
219/* Return:	-1 on error.				                      */
220/*               0 on errorless completion of the loop.                       */
221/*									      */
222/******************************************************************************/
223void *shmat_shmdt(void *args)
224{				/* arguments to the thread X function.          */
225	int shm_ndx = 0;	/* index to number of shmat/shmdt             */
226	key_t shmkey = 0;	/* IPC_PRIVATE (key for shmget)               */
227	int shmid;		/* shared memory id                           */
228	long *locargs =		/* local pointer to arguments                 */
229	    (long *)args;
230
231	while (shm_ndx++ < (int)locargs[0]) {
232		/* put the reader and writer threads to sleep                         */
233		done_shmat = 0;
234
235		/* generate a random size, we will ask for this amount of shared mem  */
236		srand(time(NULL) % 100);
237		fsize = (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096;
238
239		if ((shmid = shmget(shmkey, fsize, IPC_CREAT | 0666)) == -1) {
240			perror("shmat_shmdt(): shmget()");
241			pthread_exit((void *)-1);
242		} else {
243			fprintf(stdout,
244				"%s[%#lx]: shmget(): success, got segment of size %d\n",
245				STR_SHMAT, pthread_self(), fsize);
246		}
247
248		if ((map_address = shmat(shmid, NULL, 0))
249		    == (void *)-1) {
250			fprintf(stderr, "shmat_shmat(): map address = %p\n",
251				map_address);
252			perror("shmat_shmdt(): shmat()");
253			pthread_exit((void *)-1);
254		} else {
255			/* Wake up the reader and writer threads.                             */
256			/* Write 'X' into map_address. We are not sure about
257			   reader/writer interleaving. So the reader may expect
258			   to find 'X' or 'Y'
259			 */
260			memset(map_address, 'X', 1);
261			done_shmat = 1;
262			usleep(0);
263		}
264
265		fprintf(stdout, "%s[%#lx]: Map address = %p\n",
266			STR_SHMAT, pthread_self(), map_address);
267		fprintf(stdout,
268			"%s[%#lx]: Num iter: [%d] Total Num Iter: [%d]\n",
269			STR_SHMAT, pthread_self(), shm_ndx, (int)locargs[0]);
270		usleep(0);
271		sched_yield();
272
273		/* put the threads to sleep before un-shmatting                       */
274		done_shmat = 0;
275		if (shmdt((void *)map_address) == -1) {
276			perror("shmat_shmdt(): shmdt()");
277			pthread_exit((void *)-1);
278		}
279		if (shmctl(shmid, IPC_RMID, NULL)) {
280			perror("shmat_shmdt(): shmctl()");
281			pthread_exit((void *)-1);
282		}
283	}
284	pthread_exit(NULL);
285}
286
287/******************************************************************************/
288/*									      */
289/* Function:	write_to_mem						      */
290/*									      */
291/* Description:	Thread Y function.					      */
292/*		Writes 'Y' to the memory location shmat by process X.         */
293/*									      */
294/* Input:	arg[0]		   number of times write is performed         */
295/*									      */
296/* Return:	-1 on error.				                      */
297/*               0 on errorless completion of the loop.                       */
298/*									      */
299/******************************************************************************/
300void *write_to_mem(void *args)
301{
302	static int write_ndx = 0;	/* index to the number of writes to perform   */
303	long *locargs =		/* local pointer to the arguments             */
304	    (long *)args;
305
306	while (write_ndx++ < (int)locargs[0]) {
307		/* wait for the thread to shmat, and dont sleep on the processor. */
308		while (!done_shmat)
309			usleep(0);
310
311		if (sigsetjmp(jmpbuf, 1) == 1) {
312			fprintf(stdout,
313				"page fault ocurred due a write after an shmdt from [%p]\n",
314				map_address);
315		}
316
317		fprintf(stdout,
318			"%s[%#lx]: write_to_mem(): memory address: [%p]\n",
319			STR_WRITER, pthread_self(), map_address);
320		memset(map_address, 'Y', 1);
321		usleep(1);
322		sched_yield();
323	}
324	pthread_exit(NULL);
325}
326
327/******************************************************************************/
328/*									      */
329/* Function:	read_from_mem						      */
330/*									      */
331/* Description:	Thread Z function.					      */
332/*		reads from the memory location shmat by process X.            */
333/*									      */
334/* Input:	arg[0]		   number of times read is performed          */
335/*									      */
336/* Return:	-1 on error.				                      */
337/*               0 on errorless completion of the loop.                       */
338/*									      */
339/******************************************************************************/
340void *read_from_mem(void *args)
341{
342	static int read_ndx = 0;	/* index to the number of writes to perform   */
343	long *locargs =		/* local pointer to the arguments             */
344	    (long *)args;
345
346	while (read_ndx++ < (int)locargs[0]) {
347		/* wait for the shmat to happen */
348		while (!done_shmat)
349			usleep(0);
350
351		fprintf(stdout,
352			"%s[%#lx]: read_from_mem():  memory address: [%p]\n",
353			STR_READER, pthread_self(), map_address);
354		if (sigsetjmp(jmpbuf, 1) == 1) {
355			fprintf(stdout,
356				"page fault ocurred due a read after an shmdt from %p\n",
357				map_address);
358		}
359
360		fprintf(stdout, "%s[%#lx]: read_mem(): content of memory: %s\n",
361			STR_READER, pthread_self(), (char *)map_address);
362
363		if (strncmp(map_address, "Y", 1) != 0) {
364			if (strncmp(map_address, "X", 1) != 0) {
365				pthread_exit((void *)-1);
366			}
367		}
368		usleep(1);
369		sched_yield();
370	}
371	pthread_exit(NULL);
372}
373
374/******************************************************************************/
375/*                                                                            */
376/* Function:    main                                                          */
377/*                                                                            */
378/* Descrption:	Create a large file of size up to a  Giga Bytes.  write to it */
379/*		lower case alphabet 'a'. Map the file and change the contents */
380/*		to 'A's (upper case alphabet), write the contents to the file,*/
381/*		and unmap the file from memory. Spwan a certian number of     */
382/*		LWP's that will do the above.                                 */
383/*                                                                            */
384/* Return:	exits with -1 on error					      */
385/*		exits with a 0 on success.				      */
386/*                                                                            */
387/******************************************************************************/
388int main(int argc,		/* number of input parameters.                        */
389	 char **argv)
390{				/* pointer to the command line arguments.       */
391	int c;			/* command line options                       */
392	int num_iter;		/* number of iteration to perform             */
393	int thrd_ndx;		/* index into the array of threads.           */
394	double exec_time;	/* period for which the test is executed      */
395	void *status;		/* exit status for light weight process       */
396	int sig_ndx;		/* index into signal handler structure.       */
397	pthread_t thid[1000];	/* pids of process that will map/write/unmap  */
398	long chld_args[3];	/* arguments to funcs execed by child process */
399	extern char *optarg;	/* arguments passed to each option            */
400	struct sigaction sigptr;	/* set up signal, for interval timer          */
401
402	static struct signal_info {
403		int signum;	/* signal number that hasto be handled                */
404		char *signame;	/* name of the signal to be handled.                  */
405	} sig_info[] = {
406		{
407		SIGHUP, "SIGHUP"}, {
408		SIGINT, "SIGINT"}, {
409		SIGQUIT, "SIGQUIT"}, {
410		SIGABRT, "SIGABRT"}, {
411		SIGBUS, "SIGBUS"}, {
412		SIGSEGV, "SIGSEGV"}, {
413		SIGALRM, "SIGALRM"}, {
414		SIGUSR1, "SIGUSR1"}, {
415		SIGUSR2, "SIGUSR2"}, {
416		-1, "ENDSIG"}
417	};
418
419	/* set up the default values */
420	num_iter = 1000;	/* repeate map - write - unmap operation 1000 times   */
421	exec_time = 24.0;	/* minimum time period for which to run the tests     */
422
423	while ((c = getopt(argc, argv, "h:l:x:")) != -1) {
424		switch (c) {
425		case 'h':
426			usage(argv[0]);
427			break;
428		case 'l':	/* number of times to loop in the thread function     */
429			if ((num_iter = atoi(optarg)) == 0)
430				num_iter = 1000;
431			break;
432		case 'x':	/* time in hrs to run this test.                      */
433			if ((exec_time = atof(optarg)) == 0)
434				exec_time = 24;
435			break;
436		default:
437			usage(argv[0]);
438			break;
439		}
440	}
441
442	fprintf(stdout,
443		"\n\n\nTest is set to run with the following parameters:\n"
444		"\tDuration of test: [%f]hrs\n"
445		"\tnumber of shmat  shm detach: [%d]\n", exec_time, num_iter);
446
447	/* set up signals */
448	sigptr.sa_handler = (void (*)(int signal))sig_handler;
449	sigfillset(&sigptr.sa_mask);
450	sigptr.sa_flags = SA_SIGINFO;
451	for (sig_ndx = 0; sig_info[sig_ndx].signum != -1; sig_ndx++) {
452		sigaddset(&sigptr.sa_mask, sig_info[sig_ndx].signum);
453		if (sigaction(sig_info[sig_ndx].signum, &sigptr,
454			      NULL) == -1) {
455			perror("man(): sigaction()");
456			fprintf(stderr,
457				"could not set handler for SIGALRM, errno = %d\n",
458				errno);
459			exit(-1);
460		}
461	}
462
463	chld_args[0] = num_iter;
464	alarm(exec_time * 3600.00);
465
466	for (;;) {
467		/* create 3 threads */
468		if (pthread_create(&thid[0], NULL, shmat_shmdt, chld_args)) {
469			perror("main(): pthread_create()");
470			exit(-1);
471		} else {
472			fprintf(stdout,
473				"created thread id[%#lx], execs fn shmat_shmdt()\n",
474				thid[0]);
475		}
476		sched_yield();
477
478		if (pthread_create(&thid[1], NULL, write_to_mem, chld_args)) {
479			perror("main(): pthread_create()");
480			exit(-1);
481		} else {
482			fprintf(stdout,
483				"created thread id[%#lx], execs fn write_to_mem()\n",
484				thid[1]);
485		}
486		sched_yield();
487
488		if (pthread_create(&thid[2], NULL, read_from_mem, chld_args)) {
489			perror("main(): pthread_create()");
490			exit(-1);
491		} else {
492			fprintf(stdout,
493				"created thread id[%#lx], execs fn read_from_mem()\n",
494				thid[2]);
495		}
496		sched_yield();
497
498		/* wait for the children to terminate */
499		for (thrd_ndx = 0; thrd_ndx < 3; thrd_ndx++) {
500			if (pthread_join(thid[thrd_ndx], &status)) {
501				perror("main(): pthread_create()");
502				exit(-1);
503			}
504			if (status == (void *)-1) {
505				fprintf(stderr,
506					"thread [%#lx] - process exited with errors %ld\n",
507					thid[thrd_ndx], (long)status);
508				exit(-1);
509			}
510		}
511	}
512	fprintf(stdout, "TEST PASSED\n");
513	return 0;
514}
515