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