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:     Nov - 21 - 2001 Created - Manoj Iyer, IBM Austin TX.          */
24/*                               email:manjo@austin.ibm.com                   */
25/*                                                                            */
26/*		Nov - 26 - 2001 Modified - Manoj Iyer, IBM Austin Tx.         */
27/*				- Added function rm_shared_mem.               */
28/*                                                                            */
29/*		Dec - 03 - 2001 Modified - Manoj Iyer, IBM Austin Tx.         */
30/*				- Added code to spawn threads.		      */
31/*				- Removed dead code.		              */
32/*				- Checked in the initial version to CVS       */
33/*								              */
34/*		Feb - 27 - 2001 Modified - Manoj Iyer, IBM Austin TX.         */
35/*				- removed compiler warnings.                  */
36/*				- removed compiler errors.                    */
37/*                                                                            */
38/* File:	shm_test.c				                      */
39/*									      */
40/* Description:	This program is designed to stress the Memory management sub -*/
41/*		system of Linux. This program will spawn multiple pairs of    */
42/*		reader and writer threads. One thread will create the shared  */
43/*		segment of random size and write to this memory, the other    */
44/*		pair will read from this memory.		              */
45/*									      */
46/******************************************************************************/
47#include <pthread.h>		/* required by pthread functions                      */
48#include <stdio.h>		/* required by fprintf()                              */
49#include <stdlib.h>		/* required by exit(), atoi()                         */
50#include <string.h>		/* required by strncpy()                              */
51#include <unistd.h>		/* required by getopt(), mmap()                       */
52#include <sys/types.h>		/* required by open(), shmat(), shmdt()               */
53#include <sys/stat.h>		/* required by open()                                 */
54#include <sys/ipc.h>		/* required by shmat() shmdt(), shmctl()              */
55#include <sys/shm.h>		/* required by shmat() shmdt(), shmctl()              */
56#include <sys/mman.h>		/* required by mmap()                                 */
57#include <fcntl.h>		/* required by open()                                 */
58#include <stdint.h>		/* required by uintptr_t                              */
59
60void noprintf(char *string, ...)
61{
62}
63
64#ifdef DEBUG
65#define dprt	printf
66#else
67#define dprt	noprintf
68#endif
69
70#define PTHREAD_EXIT(val)    do {\
71			exit_val = val; \
72                        dprt("pid[%d]: exiting with %d\n", getpid(),exit_val); \
73			pthread_exit((void *)(uintptr_t)exit_val); \
74				} while (0)
75
76#define OPT_MISSING(prog, opt)   do{\
77			       fprintf(stderr, "%s: option -%c ", prog, opt); \
78                               fprintf(stderr, "requires an argument\n"); \
79                               usage(prog); \
80                                   } while (0)
81
82#define MAXT	30		/* default number of threads to create.               */
83#define MAXR	1000		/* default number of repatetions to execute           */
84#define WRITER  0		/* cause thread function to shmat and write           */
85#define READER  1		/* cause thread function to shmat and read            */
86
87/******************************************************************************/
88/*								 	      */
89/* Function:	usage							      */
90/*									      */
91/* Description:	Print the usage message.				      */
92/*									      */
93/* Return:	exits with -1						      */
94/*									      */
95/******************************************************************************/
96static void usage(char *progname)
97{				/* name of this program                       */
98	fprintf(stderr,
99		"Usage: %s -d NUMDIR -f NUMFILES -h -t NUMTHRD\n"
100		"\t -h Help!\n"
101		"\t -l Number of repatetions to execute:       Default: 1000\n"
102		"\t -t Number of threads to generate:          Default: 30\n",
103		progname);
104	exit(-1);
105}
106
107/******************************************************************************/
108/*								 	      */
109/* Function:	rm_shared_mem						      */
110/*									      */
111/* Description:	This function removes the shared segments that were created   */
112/*		This function is called when shmat fails or logical end of    */
113/*		the while loop is reached in shmat_rd_wr function.         */
114/*									      */
115/* Input:	shm_id   - id of the shared memory segment to be removed      */
116/*		shm_addr - address of the shared memory segment to be removed */
117/*		cmd      - remove id only or remove id and detach??           */
118/*			   0 - remove id dont detach segment.                 */
119/*			   1 - remove id and detach segment.                  */
120/*									      */
121/* Output:	NONE.                                                         */
122/*									      */
123/* Return:	exits with -1 on error, 0 on success                          */
124/*									      */
125/******************************************************************************/
126static int rm_shared_mem(key_t shm_id,	/* id of shared memory segment to be removed  */
127			 char *shm_addr,	/* address of shared mem seg to be removed    */
128			 int cmd)
129{				/* remove id only or remove id and detach seg */
130	struct shmid *shmbuf = NULL;	/* info about the segment pointed by shmkey   */
131
132	dprt("pid[%d]: rm_shared_mem(): shm_id = %d shm_addr = %#x cmd = %d\n",
133	     getpid(), shm_id, shm_addr, cmd);
134	if (shmctl(shm_id, IPC_RMID, (struct shmid_ds *)shmbuf) == -1) {
135		dprt("pid[%d]: rm_shared_mem(): shmctl unable to remove shm_id[%d]\n", getpid(), shm_id);
136		perror("rm_shared_mem(): shmctl()");
137		return -1;
138	}
139
140	if (cmd) {
141		if (shmdt((void *)shm_addr) == -1) {
142			dprt("pid[%d]:rm_shared_mem(): shmdt unable to detach addr = %#x\n", getpid(), shm_addr);
143			perror("rm_shared_mem(): shmdt()");
144			return -1;
145		}
146	}
147	return 0;
148}
149
150/******************************************************************************/
151/*								 	      */
152/* Function:	shmat_rd_wr						      */
153/*									      */
154/* Description:	This function repeatedly attaches and detaches the memory     */
155/*		The size of the file is a multiple of page size.              */
156/*		The function acts as either reader or writer thread depending */
157/*		on arg[3]. The reader and writer thread use the same key so   */
158/*		they get access to the same shared memory segment.            */
159/*									      */
160/* Input:	The argument pointer contains the following.                  */
161/*		arg[0] - number of repatetions of the above operation         */
162/*		arg[1] - shared memory key.				      */
163/*		arg[2] - size of the memory that is to be attached.           */
164/*		arg[3] - reader or writer.                                    */
165/*									      */
166/* Return:	exits with -1 on error, 0 on success                          */
167/*									      */
168/******************************************************************************/
169static void *shmat_rd_wr(void *args)
170{				/* arguments to the thread function             */
171	int shmndx = 0;		/* index to the number of attach and detach   */
172	int index = 0;		/* index to the number of blocks touched      */
173	int reader = 0;		/* this thread is a reader thread if set to 1 */
174	key_t shm_id = 0;	/* shared memory id                           */
175	long *locargs =		/* local pointer to arguments                 */
176	    (long *)args;
177	volatile int exit_val = 0;	/* exit value of the pthread                  */
178	char *read_from_mem;	/* ptr to touch each (4096) block in memory   */
179	char *write_to_mem;	/* ptr to touch each (4096) block in memory   */
180	char *shmat_addr;	/* address of the attached memory             */
181	char buff;		/* temporary buffer                           */
182
183	reader = (int)locargs[3];
184	while (shmndx++ < (int)locargs[0]) {
185		dprt("pid[%d]: shmat_rd_wr(): locargs[1] = %#x\n",
186		     getpid(), (int)locargs[1]);
187
188		/* get shared memory id */
189		if ((shm_id =
190		     shmget((int)locargs[1], (int)locargs[2], IPC_CREAT | 0666))
191		    == -1) {
192			dprt("pid[%d]: shmat_rd_wr(): shmget failed\n",
193			     getpid());
194			perror("do_shmat_shmadt(): shmget()");
195			PTHREAD_EXIT(-1);
196		}
197
198		fprintf(stdout, "pid[%d]: shmat_rd_wr(): shmget():"
199			"success got segment id %d\n", getpid(), shm_id);
200
201		/* get shared memory segment */
202		if ((shmat_addr = shmat(shm_id, NULL, 0)) == (void *)-1) {
203			rm_shared_mem(shm_id, shmat_addr, 0);
204			fprintf(stderr,
205				"pid[%d]: do_shmat_shmadt(): shmat_addr = %#lx\n",
206				getpid(), (long)shmat_addr);
207			perror("do_shmat_shmadt(): shmat()");
208			PTHREAD_EXIT(-1);
209		}
210		dprt("pid[%d]: do_shmat_shmadt(): content of memory shmat_addr = %s\n", getpid(), shmat_addr);
211
212		fprintf(stdout,
213			"pid[%d]: do_shmat_shmadt(): got shmat address = %#lx\n",
214			getpid(), (long)shmat_addr);
215
216		if (!reader) {
217			/* write character 'Y' to that memory area */
218			index = 0;
219			write_to_mem = shmat_addr;
220			while (index < (int)locargs[2]) {
221				dprt("pid[%d]: do_shmat_shmatd(): write_to_mem = %#x\n", getpid(), write_to_mem);
222				*write_to_mem = 'Y';
223				index++;
224				write_to_mem++;
225				sched_yield();
226			}
227		} else {
228			/* read from the memory area */
229			index = 0;
230			read_from_mem = shmat_addr;
231			while (index < (int)locargs[2]) {
232				buff = *read_from_mem;
233				index++;
234				read_from_mem++;
235				sched_yield();
236			}
237		}
238
239		sched_yield();
240
241		/* remove the shared memory */
242		if (rm_shared_mem(shm_id, shmat_addr, 1) == -1) {
243			fprintf(stderr,
244				"pid[%d]: do_shmat_shmatd(): rm_shared_mem(): faild to rm id\n",
245				getpid());
246			PTHREAD_EXIT(-1);
247		}
248	}
249
250	PTHREAD_EXIT(0);
251}
252
253/******************************************************************************/
254/*								 	      */
255/* Function:	main							      */
256/*									      */
257/* Description:	This is the entry point to the program. This function will    */
258/*		parse the input arguments and set the values accordingly. If  */
259/*		no arguments (or desired) are provided default values are used*/
260/*		refer the usage function for the arguments that this program  */
261/*		takes. It also creates the threads which do most of the dirty */
262/*		work. If the threads exits with a value '0' the program exits */
263/*		with success '0' else it exits with failure '-1'.             */
264/*									      */
265/* Return:	-1 on failure						      */
266/*		 0 on success						      */
267/*									      */
268/******************************************************************************/
269int main(int argc,		/* number of input parameters                 */
270	 char **argv)
271{				/* pointer to the command line arguments.     */
272	int c;			/* command line options                       */
273	int num_thrd = MAXT;	/* number of threads to create                */
274	int num_reps = MAXR;	/* number of repatitions the test is run      */
275	int thrd_ndx;		/* index into the array of thread ids         */
276	void *th_status;	/* exit status of LWP's                       */
277	int map_size;		/* size of the file mapped.                   */
278	int shmkey = 1969;	/* key used to generate shmid by shmget()     */
279	pthread_t thrdid[30];	/* maxinum of 30 threads allowed              */
280	long chld_args[4];	/* arguments to the thread function           */
281	char *map_address = NULL;
282	/* address in memory of the mapped file       */
283	extern int optopt;	/* options to the program                     */
284
285	while ((c = getopt(argc, argv, "hl:t:")) != -1) {
286		switch (c) {
287		case 'h':
288			usage(argv[0]);
289			break;
290		case 'l':	/* how many repetitions of the test to exec   */
291			if ((num_reps = atoi(optarg)) == 0)
292				OPT_MISSING(argv[0], optopt);
293			else if (num_reps < 0) {
294				fprintf(stdout,
295					"WARNING: bad argument. Using default\n");
296				num_reps = MAXR;
297			}
298			break;
299		case 't':
300			if ((num_thrd = atoi(optarg)) == 0)
301				OPT_MISSING(argv[0], optopt);
302			else if (num_thrd < 0) {
303				fprintf(stdout,
304					"WARNING: bad argument. Using default\n");
305				num_thrd = MAXT;
306			}
307			break;
308		default:
309			usage(argv[0]);
310			break;
311		}
312	}
313
314	chld_args[0] = num_reps;
315
316	for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx += 2) {
317		srand(time(NULL) % 100);
318		map_size =
319		    (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096;
320
321		chld_args[1] = shmkey++;
322		chld_args[2] = map_size;
323
324		dprt("main(): thrd_ndx = %d map_address = %#x map_size = %d\n",
325		     thrd_ndx, map_address, map_size);
326
327		chld_args[3] = WRITER;
328
329		if (pthread_create
330		    (&thrdid[thrd_ndx], NULL, shmat_rd_wr, chld_args)) {
331			perror("shmat_rd_wr(): pthread_create()");
332			exit(-1);
333		}
334
335		chld_args[3] = READER;
336
337		if (pthread_create
338		    (&thrdid[thrd_ndx + 1], NULL, shmat_rd_wr, chld_args)) {
339			perror("shmat_rd_wr(): pthread_create()");
340			exit(-1);
341		}
342	}
343
344	sync();
345
346	for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) {
347		if (pthread_join(thrdid[thrd_ndx], &th_status) != 0) {
348			perror("shmat_rd_wr(): pthread_join()");
349			exit(-1);
350		} else {
351			dprt("WE ARE HERE %d\n", __LINE__);
352			if (th_status == (void *)-1) {
353				fprintf(stderr,
354					"thread [%ld] - process exited with errors\n",
355					(long)thrdid[thrd_ndx]);
356				exit(-1);
357			}
358		}
359	}
360	exit(0);
361}
362