• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) International Business Machines  Corp., 2002
3  *
4  * This program is free software;  you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12  * the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program;  if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  * 06/30/2001   Port to Linux   nsharoff@us.ibm.com
19  * 11/11/2002   Port to LTP     dbarrera@us.ibm.com
20  */
21 
22 /*
23  * Get and manipulate a message queue.
24  * Same as msgstress02 but gets the actual msgmni value under procfs.
25  */
26 
27 #define _XOPEN_SOURCE 500
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/ipc.h>
31 #include <sys/msg.h>
32 #include <sys/wait.h>
33 #include <signal.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include "test.h"
40 #include "ipcmsg.h"
41 #include "libmsgctl.h"
42 
43 char *TCID = "msgstress04";
44 int TST_TOTAL = 1;
45 
46 #define MAXNREPS	1000
47 #ifndef CONFIG_COLDFIRE
48 #define MAXNPROCS	 1000000	/* This value is set to an arbitrary high limit. */
49 #else
50 #define MAXNPROCS	 100000	/* Coldfire can't deal with 1000000 */
51 #endif
52 #define MAXNKIDS	10
53 #define DEFNKIDS	2
54 
55 static int maxnkids = MAXNKIDS;	/* Used if pid_max is exceeded */
56 static key_t keyarray[MAXNPROCS];
57 static int pidarray[MAXNPROCS];
58 static int rkidarray[MAXNKIDS];
59 static int wkidarray[MAXNKIDS];
60 static int tid;
61 static int nprocs, nreps, nkids, MSGMNI;
62 static int maxnprocs;
63 static int procstat;
64 
65 void setup(void);
66 void cleanup(void);
67 
68 static void term(int);
69 static int dotest(key_t, int);
70 static void dotest_iteration(int off);
71 static void cleanup_msgqueue(int i, int tid);
72 
73 static char *opt_maxnprocs;
74 static char *opt_nkids;
75 static char *opt_nreps;
76 
77 static option_t options[] = {
78 	{"n:", NULL, &opt_maxnprocs},
79 	{"c:", NULL, &opt_nkids},
80 	{"l:", NULL, &opt_nreps},
81 	{NULL, NULL, NULL},
82 };
83 
usage(void)84 static void usage(void)
85 {
86 	printf("  -n      Number of processes\n");
87 	printf("  -c      Number of read/write child pairs\n");
88 	printf("  -l      Number of iterations\n");
89 }
90 
91 
main(int argc,char ** argv)92 int main(int argc, char **argv)
93 {
94 	int i, j, ok;
95 
96 	tst_parse_opts(argc, argv, options, usage);
97 
98 	setup();
99 
100 	nreps = MAXNREPS;
101 	nkids = MAXNKIDS;
102 
103 	if (opt_nreps) {
104 		nreps = atoi(opt_nreps);
105 		if (nreps > MAXNREPS) {
106 			tst_resm(TINFO,
107 				 "Requested number of iterations too large, "
108 				 "setting to Max. of %d", MAXNREPS);
109 			nreps = MAXNREPS;
110 		}
111 	}
112 
113 	if (opt_nkids) {
114 		nkids = atoi(opt_nkids);
115 		if (nkids > MAXNKIDS) {
116 			tst_resm(TINFO,
117 				 "Requested number of read/write pairs too "
118 				 "large, setting to Max. of %d", MAXNKIDS);
119 			nkids = MAXNKIDS;
120 		}
121 	}
122 
123 
124 	if (opt_maxnprocs) {
125 		if (atoi(opt_maxnprocs) > maxnprocs) {
126 			tst_resm(TINFO,
127 				 "Requested number of processes too large, "
128 				 "setting to Max. of %d", MSGMNI);
129 		} else {
130 			maxnprocs = atoi(opt_maxnprocs);
131 		}
132 	}
133 
134 	procstat = 0;
135 	srand48((unsigned)getpid() + (unsigned)(getppid() << 16));
136 	tid = -1;
137 
138 	/* Setup signal handling routine */
139 	if (sigset(SIGTERM, term) == SIG_ERR)
140 		tst_brkm(TFAIL, cleanup, "Sigset SIGTERM failed");
141 
142 	/* Set up array of unique keys for use in allocating message
143 	 * queues
144 	 */
145 	for (i = 0; i < MSGMNI; i++) {
146 		ok = 1;
147 		do {
148 			/* Get random key */
149 			keyarray[i] = (key_t) lrand48();
150 			/* Make sure key is unique and not private */
151 			if (keyarray[i] == IPC_PRIVATE) {
152 				ok = 0;
153 				continue;
154 			}
155 			for (j = 0; j < i; j++) {
156 				if (keyarray[j] == keyarray[i]) {
157 					ok = 0;
158 					break;
159 				}
160 				ok = 1;
161 			}
162 		} while (ok == 0);
163 	}
164 	/* Fork a number of processes, each of which will
165 	 * create a message queue with several (nkids) reader/writer
166 	 * pairs which will read and write a number (iterations)
167 	 * of random length messages with specific values (keys).
168 	 *
169 	 * We do not fork more than maxnprocs at a time and
170 	 * we fork until all the message queues get used.
171 	 */
172 
173 	if (MSGMNI <= maxnprocs) {
174 		nprocs = MSGMNI;
175 		dotest_iteration(0);
176 	} else {
177 		for (i = 0; i < (MSGMNI / maxnprocs); i++) {
178 			nprocs = maxnprocs;
179 			dotest_iteration(i * maxnprocs);
180 		}
181 
182 		nprocs = MSGMNI % maxnprocs;
183 		dotest_iteration(i * maxnprocs);
184 	}
185 
186 	tst_resm(TPASS, "Test ran successfully!");
187 
188 	cleanup();
189 	tst_exit();
190 }
191 
dotest_iteration(int off)192 static void dotest_iteration(int off)
193 {
194 	key_t key;
195 	int i, count, status;
196 	pid_t pid;
197 
198 	memset(pidarray, 0, sizeof(pidarray));
199 
200 	for (i = 0; i < nprocs; i++) {
201 		key = keyarray[off + i];
202 
203 		if ((pid = FORK_OR_VFORK()) < 0)
204 			tst_brkm(TFAIL, cleanup,
205 				 "Fork failed (may be OK if under stress)");
206 
207 		/* Child does this */
208 		if (pid == 0) {
209 			procstat = 1;
210 			exit(dotest(key, i));
211 		}
212 		pidarray[i] = pid;
213 	}
214 
215 	count = 0;
216 	while (1) {
217 		if ((wait(&status)) > 0) {
218 			if (status >> 8 != PASS)
219 				tst_brkm(TFAIL, cleanup,
220 					"Child exit status = %d", status >> 8);
221 			count++;
222 		} else {
223 			if (errno != EINTR) {
224 				break;
225 			}
226 #ifdef DEBUG
227 			tst_resm(TINFO, "Signal detected during wait");
228 #endif
229 		}
230 	}
231 	/* Make sure proper number of children exited */
232 	if (count != nprocs)
233 		tst_brkm(TFAIL, cleanup,
234 			 "Wrong number of children exited, Saw %d, Expected %d",
235 			 count, nprocs);
236 }
237 
cleanup_msgqueue(int i,int tid)238 static void cleanup_msgqueue(int i, int tid)
239 {
240 	/*
241 	 * Decrease the value of i by 1 because it
242 	 * is getting incremented even if the fork
243 	 * is failing.
244 	 */
245 
246 	i--;
247 	/*
248 	 * Kill all children & free message queue.
249 	 */
250 	for (; i >= 0; i--) {
251 		(void)kill(rkidarray[i], SIGKILL);
252 		(void)kill(wkidarray[i], SIGKILL);
253 	}
254 
255 	if (msgctl(tid, IPC_RMID, 0) < 0) {
256 		printf("Msgctl error in cleanup_msgqueue %d\n", errno);
257 		exit(FAIL);
258 	}
259 }
260 
dotest(key_t key,int child_process)261 static int dotest(key_t key, int child_process)
262 {
263 	int id, pid;
264 	int i, count, status, exit_status;
265 
266 	sighold(SIGTERM);
267 	if ((id = msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)) < 0) {
268 		printf("msgget() error in child %d: %s\n",
269 			child_process, strerror(errno));
270 		return FAIL;
271 	}
272 	tid = id;
273 	sigrelse(SIGTERM);
274 
275 	exit_status = PASS;
276 
277 	for (i = 0; i < nkids; i++) {
278 		if ((pid = FORK_OR_VFORK()) < 0) {
279 			printf("Fork failure in the first child of child group %d\n",
280 				child_process);
281 			cleanup_msgqueue(i, tid);
282 			return FAIL;
283 		}
284 		/* First child does this */
285 		if (pid == 0) {
286 			procstat = 2;
287 			exit(doreader(key, tid, getpid(),
288 					child_process, nreps));
289 		}
290 		rkidarray[i] = pid;
291 		if ((pid = FORK_OR_VFORK()) < 0) {
292 			printf("Fork failure in the second child of child group %d\n",
293 				child_process);
294 			/*
295 			 * Kill the reader child process
296 			 */
297 			(void)kill(rkidarray[i], SIGKILL);
298 
299 			cleanup_msgqueue(i, tid);
300 			return FAIL;
301 		}
302 		/* Second child does this */
303 		if (pid == 0) {
304 			procstat = 2;
305 			exit(dowriter(key, tid, rkidarray[i],
306 					child_process, nreps));
307 		}
308 		wkidarray[i] = pid;
309 	}
310 	/* Parent does this */
311 	count = 0;
312 	while (1) {
313 		if ((wait(&status)) > 0) {
314 			if (status >> 8 != PASS) {
315 				printf("Child exit status = %d from child group %d\n",
316 					status >> 8, child_process);
317 				for (i = 0; i < nkids; i++) {
318 					kill(rkidarray[i], SIGTERM);
319 					kill(wkidarray[i], SIGTERM);
320 				}
321 				if (msgctl(tid, IPC_RMID, 0) < 0) {
322 					printf("msgctl() error: %s\n",
323 						strerror(errno));
324 				}
325 				return FAIL;
326 			}
327 			count++;
328 		} else {
329 			if (errno != EINTR) {
330 				break;
331 			}
332 		}
333 	}
334 	/* Make sure proper number of children exited */
335 	if (count != (nkids * 2)) {
336 		printf("Wrong number of children exited in child group %d, saw %d, expected %d\n",
337 			child_process, count, (nkids * 2));
338 		if (msgctl(tid, IPC_RMID, 0) < 0) {
339 			printf("msgctl() error: %s\n", strerror(errno));
340 		}
341 		return FAIL;
342 	}
343 	if (msgctl(id, IPC_RMID, 0) < 0) {
344 		printf("msgctl() failure in child group %d: %s\n",
345 			child_process, strerror(errno));
346 		return FAIL;
347 	}
348 	return exit_status;
349 }
350 
351 /* ARGSUSED */
term(int sig LTP_ATTRIBUTE_UNUSED)352 static void term(int sig LTP_ATTRIBUTE_UNUSED)
353 {
354 	int i;
355 
356 	if (procstat == 0) {
357 #ifdef DEBUG
358 		tst_resm(TINFO, "SIGTERM signal received, test killing kids");
359 #endif
360 		for (i = 0; i < nprocs; i++) {
361 			if (pidarray[i] > 0) {
362 				if (kill(pidarray[i], SIGTERM) < 0) {
363 					tst_resm(TBROK,
364 						 "Kill failed to kill child %d",
365 						 i);
366 					exit(FAIL);
367 				}
368 			}
369 		}
370 		return;
371 	}
372 
373 	if (procstat == 2) {
374 		exit(PASS);
375 	}
376 
377 	if (tid == -1) {
378 		exit(FAIL);
379 	}
380 	for (i = 0; i < nkids; i++) {
381 		if (rkidarray[i] > 0)
382 			kill(rkidarray[i], SIGTERM);
383 		if (wkidarray[i] > 0)
384 			kill(wkidarray[i], SIGTERM);
385 	}
386 }
387 
setup(void)388 void setup(void)
389 {
390 	int nr_msgqs, free_pids;
391 
392 	tst_tmpdir();
393 	/* You will want to enable some signal handling so you can capture
394 	 * unexpected signals like SIGSEGV.
395 	 */
396 	tst_sig(FORK, DEF_HANDLER, cleanup);
397 
398 	/* One cavet that hasn't been fixed yet.  TEST_PAUSE contains the code to
399 	 * fork the test with the -c option.  You want to make sure you do this
400 	 * before you create your temporary directory.
401 	 */
402 	TEST_PAUSE;
403 
404 	nr_msgqs = get_max_msgqueues();
405 	if (nr_msgqs < 0)
406 		tst_brkm(TBROK, cleanup, "get_max_msgqueues() failed");
407 
408 	MSGMNI = nr_msgqs - get_used_msgqueues();
409 	if (MSGMNI <= 0)
410 		tst_brkm(TBROK, cleanup,
411 			 "Max number of message queues already used, cannot create more.");
412 
413 	tst_resm(TINFO, "Found %d available message queues", MSGMNI);
414 
415 	free_pids = tst_get_free_pids(cleanup);
416 	/* We don't use more than a half of available pids.
417 	 * For each child we fork up to 2*maxnkids grandchildren. */
418 	maxnprocs = (free_pids / 2) / (1 + 2 * maxnkids);
419 
420 	if (!maxnprocs)
421 		tst_brkm(TBROK, cleanup, "Not enough free pids");
422 
423 	tst_resm(TINFO, "Using upto %d pids", free_pids / 2);
424 }
425 
cleanup(void)426 void cleanup(void)
427 {
428 	int status;
429 
430 	/*
431 	 * Remove the message queue from the system
432 	 */
433 #ifdef DEBUG
434 	tst_resm(TINFO, "Removing the message queue");
435 #endif
436 	(void)msgctl(tid, IPC_RMID, NULL);
437 	if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) {
438 		(void)msgctl(tid, IPC_RMID, NULL);
439 		tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed");
440 
441 	}
442 
443 	tst_rmdir();
444 }
445