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 
noprintf(char * string,...)60 void 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 
85 struct child_args
86 {
87 	pthread_t threadid;
88 	int num_reps;
89 	int shmkey;
90 	int map_size;
91 	int is_reader;
92 };
93 
94 
95 /******************************************************************************/
96 /*								 	      */
97 /* Function:	usage							      */
98 /*									      */
99 /* Description:	Print the usage message.				      */
100 /*									      */
101 /* Return:	exits with -1						      */
102 /*									      */
103 /******************************************************************************/
usage(char * progname)104 static void usage(char *progname)
105 {				/* name of this program                       */
106 	fprintf(stderr,
107 		"Usage: %s -d NUMDIR -f NUMFILES -h -t NUMTHRD\n"
108 		"\t -h Help!\n"
109 		"\t -l Number of repatetions to execute:       Default: 1000\n"
110 		"\t -t Number of threads to generate:          Default: 30\n",
111 		progname);
112 	exit(-1);
113 }
114 
115 /******************************************************************************/
116 /*								 	      */
117 /* Function:	rm_shared_mem						      */
118 /*									      */
119 /* Description:	This function removes the shared segments that were created   */
120 /*		This function is called when shmat fails or logical end of    */
121 /*		the while loop is reached in shmat_rd_wr function.         */
122 /*									      */
123 /* Input:	shm_id   - id of the shared memory segment to be removed      */
124 /*		shm_addr - address of the shared memory segment to be removed */
125 /*		cmd      - remove id only or remove id and detach??           */
126 /*			   0 - remove id dont detach segment.                 */
127 /*			   1 - remove id and detach segment.                  */
128 /*									      */
129 /* Output:	NONE.                                                         */
130 /*									      */
131 /* Return:	exits with -1 on error, 0 on success                          */
132 /*									      */
133 /******************************************************************************/
rm_shared_mem(key_t shm_id,char * shm_addr,int cmd)134 static int rm_shared_mem(key_t shm_id,	/* id of shared memory segment to be removed  */
135 			 char *shm_addr,	/* address of shared mem seg to be removed    */
136 			 int cmd)
137 {				/* remove id only or remove id and detach seg */
138 	struct shmid *shmbuf = NULL;	/* info about the segment pointed by shmkey   */
139 
140 	dprt("pid[%d]: rm_shared_mem(): shm_id = %d shm_addr = %#x cmd = %d\n",
141 	     getpid(), shm_id, shm_addr, cmd);
142 	if (shmctl(shm_id, IPC_RMID, (struct shmid_ds *)shmbuf) == -1) {
143 		dprt("pid[%d]: rm_shared_mem(): shmctl unable to remove shm_id[%d]\n", getpid(), shm_id);
144 		perror("rm_shared_mem(): shmctl()");
145 		return -1;
146 	}
147 
148 	if (cmd) {
149 		if (shmdt((void *)shm_addr) == -1) {
150 			dprt("pid[%d]:rm_shared_mem(): shmdt unable to detach addr = %#x\n", getpid(), shm_addr);
151 			perror("rm_shared_mem(): shmdt()");
152 			return -1;
153 		}
154 	}
155 	return 0;
156 }
157 
158 /******************************************************************************/
159 /*								 	      */
160 /* Function:	shmat_rd_wr						      */
161 /*									      */
162 /* Description:	This function repeatedly attaches and detaches the memory     */
163 /*		The size of the file is a multiple of page size.              */
164 /*		The function acts as either reader or writer thread depending */
165 /*		on arg[3]. The reader and writer thread use the same key so   */
166 /*		they get access to the same shared memory segment.            */
167 /*									      */
168 /* Input:	The argument pointer contains the following.                  */
169 /*		arg[0] - number of repatetions of the above operation         */
170 /*		arg[1] - shared memory key.				      */
171 /*		arg[2] - size of the memory that is to be attached.           */
172 /*		arg[3] - reader or writer.                                    */
173 /*									      */
174 /* Return:	exits with -1 on error, 0 on success                          */
175 /*									      */
176 /******************************************************************************/
shmat_rd_wr(void * vargs)177 static void *shmat_rd_wr(void *vargs)
178 {				/* arguments to the thread function             */
179 	int shmndx = 0;		/* index to the number of attach and detach   */
180 	int index = 0;		/* index to the number of blocks touched      */
181 	key_t shm_id = 0;	/* shared memory id                           */
182 	struct child_args *args = vargs;
183 	volatile int exit_val = 0;	/* exit value of the pthread                  */
184 	char *read_from_mem;	/* ptr to touch each (4096) block in memory   */
185 	char *write_to_mem;	/* ptr to touch each (4096) block in memory   */
186 	char *shmat_addr;	/* address of the attached memory             */
187 	char buff;		/* temporary buffer                           */
188 
189 	while (shmndx++ < args->num_reps) {
190 		dprt("pid[%d]: shmat_rd_wr(): locargs[1] = %#x\n",
191 		     getpid(), args->shmkey);
192 
193 		/* get shared memory id */
194 		if ((shm_id =
195 		     shmget(args->shmkey, args->map_size, IPC_CREAT | 0666))
196 		    == -1) {
197 			dprt("pid[%d]: shmat_rd_wr(): shmget failed\n",
198 			     getpid());
199 			perror("do_shmat_shmadt(): shmget()");
200 			PTHREAD_EXIT(-1);
201 		}
202 
203 		fprintf(stdout, "pid[%d]: shmat_rd_wr(): shmget():"
204 			"success got segment id %d\n", getpid(), shm_id);
205 
206 		/* get shared memory segment */
207 		if ((shmat_addr = shmat(shm_id, NULL, 0)) == (void *)-1) {
208 			rm_shared_mem(shm_id, shmat_addr, 0);
209 			fprintf(stderr,
210 				"pid[%d]: do_shmat_shmadt(): shmat_addr = %#lx\n",
211 				getpid(), (long)shmat_addr);
212 			perror("do_shmat_shmadt(): shmat()");
213 			PTHREAD_EXIT(-1);
214 		}
215 		dprt("pid[%d]: do_shmat_shmadt(): content of memory shmat_addr = %s\n", getpid(), shmat_addr);
216 
217 		fprintf(stdout,
218 			"pid[%d]: do_shmat_shmadt(): got shmat address = %#lx\n",
219 			getpid(), (long)shmat_addr);
220 
221 		if (args->is_reader) {
222 			/* write character 'Y' to that memory area */
223 			index = 0;
224 			write_to_mem = shmat_addr;
225 			while (index < args->map_size) {
226 				dprt("pid[%d]: do_shmat_shmatd(): write_to_mem = %#x\n", getpid(), write_to_mem);
227 				*write_to_mem = 'Y';
228 				index++;
229 				write_to_mem++;
230 				sched_yield();
231 			}
232 		} else {
233 			/* read from the memory area */
234 			index = 0;
235 			read_from_mem = shmat_addr;
236 			while (index < args->map_size) {
237 				buff = *read_from_mem;
238 				index++;
239 				read_from_mem++;
240 				sched_yield();
241 			}
242 		}
243 
244 		sched_yield();
245 
246 		/* remove the shared memory */
247 		if (rm_shared_mem(shm_id, shmat_addr, 1) == -1) {
248 			fprintf(stderr,
249 				"pid[%d]: do_shmat_shmatd(): rm_shared_mem(): faild to rm id\n",
250 				getpid());
251 			PTHREAD_EXIT(-1);
252 		}
253 	}
254 
255 	PTHREAD_EXIT(0);
256 }
257 
258 /******************************************************************************/
259 /*								 	      */
260 /* Function:	main							      */
261 /*									      */
262 /* Description:	This is the entry point to the program. This function will    */
263 /*		parse the input arguments and set the values accordingly. If  */
264 /*		no arguments (or desired) are provided default values are used*/
265 /*		refer the usage function for the arguments that this program  */
266 /*		takes. It also creates the threads which do most of the dirty */
267 /*		work. If the threads exits with a value '0' the program exits */
268 /*		with success '0' else it exits with failure '-1'.             */
269 /*									      */
270 /* Return:	-1 on failure						      */
271 /*		 0 on success						      */
272 /*									      */
273 /******************************************************************************/
main(int argc,char ** argv)274 int main(int argc,		/* number of input parameters                 */
275 	 char **argv)
276 {				/* pointer to the command line arguments.     */
277 	int c;			/* command line options                       */
278 	int num_thrd = MAXT;	/* number of threads to create                */
279 	int num_reps = MAXR;	/* number of repatitions the test is run      */
280 	int i;
281 	void *th_status;	/* exit status of LWP's                       */
282 	int map_size;		/* size of the file mapped.                   */
283 	int shmkey = 1969;	/* key used to generate shmid by shmget()     */
284 	struct child_args chld_args[30];	/* arguments to the thread function */
285 	char *map_address = NULL;
286 	/* address in memory of the mapped file       */
287 	extern int optopt;	/* options to the program                     */
288 
289 	while ((c = getopt(argc, argv, "hl:t:")) != -1) {
290 		switch (c) {
291 		case 'h':
292 			usage(argv[0]);
293 			break;
294 		case 'l':	/* how many repetitions of the test to exec   */
295 			if ((num_reps = atoi(optarg)) == 0)
296 				OPT_MISSING(argv[0], optopt);
297 			else if (num_reps < 0) {
298 				fprintf(stdout,
299 					"WARNING: bad argument. Using default\n");
300 				num_reps = MAXR;
301 			}
302 			break;
303 		case 't':
304 			if ((num_thrd = atoi(optarg)) == 0)
305 				OPT_MISSING(argv[0], optopt);
306 			else if (num_thrd < 0 || num_thrd > MAXT) {
307 				fprintf(stdout,
308 					"WARNING: bad argument. Using default\n");
309 				num_thrd = MAXT;
310 			}
311 			break;
312 		default:
313 			usage(argv[0]);
314 			break;
315 		}
316 	}
317 
318 	for (i = 0; i < num_thrd; i += 2) {
319 		srand(time(NULL) % 100);
320 		map_size = (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096;
321 
322 		dprt("main(): thrd_ndx = %d map_address = %#x map_size = %d\n",
323 		     i, map_address, map_size);
324 
325 		chld_args[i].num_reps = num_reps;
326 		chld_args[i].map_size = map_size;
327 		chld_args[i].shmkey = shmkey++;
328 		chld_args[i].is_reader = 0;
329 		if (pthread_create
330 		    (&chld_args[i].threadid, NULL, shmat_rd_wr, &chld_args[i])) {
331 			perror("shmat_rd_wr(): pthread_create()");
332 			exit(-1);
333 		}
334 
335 		chld_args[i + 1] = chld_args[i];
336 		chld_args[i + 1].is_reader = 1;
337 		if (pthread_create
338 		    (&chld_args[i + 1].threadid, NULL, shmat_rd_wr, &chld_args[i + 1])) {
339 			perror("shmat_rd_wr(): pthread_create()");
340 			exit(-1);
341 		}
342 	}
343 
344 	sync();
345 
346 	for (i = 0; i < num_thrd; i++) {
347 		if (pthread_join(chld_args[i].threadid, &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)chld_args[i].threadid);
356 				exit(-1);
357 			}
358 		}
359 	}
360 	exit(0);
361 }
362