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 msgctl09 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 "../lib/libmsgctl.h"
42
43 char *TCID = "msgctl11";
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 #ifdef UCLINUX
74 static char *argv0;
75 static key_t key_uclinux;
76 static int i_uclinux;
77 static int pid_uclinux;
78 static int child_process_uclinux;
79 static int rkid_uclinux;
80
81 static void do_child_1_uclinux(void);
82 static void do_child_2_uclinux(void);
83 static void do_child_3_uclinux(void);
84 #endif
85
main(int argc,char ** argv)86 int main(int argc, char **argv)
87 {
88 int i, j, ok;
89
90 #ifdef UCLINUX
91
92 argv0 = argv[0];
93
94 tst_parse_opts(argc, argv, NULL, NULL);
95
96 maybe_run_child(&do_child_1_uclinux, "ndd", 1, &key_uclinux,
97 &i_uclinux);
98 maybe_run_child(&do_child_2_uclinux, "nddd", 2, &key_uclinux,
99 &pid_uclinux, &child_process_uclinux);
100 maybe_run_child(&do_child_3_uclinux, "nddd", 3, &key_uclinux,
101 &rkid_uclinux, &child_process_uclinux);
102 #endif
103
104 setup();
105
106 if (argc == 1) {
107 /* Set default parameters */
108 nreps = MAXNREPS;
109 nkids = DEFNKIDS;
110 } else if (argc == 4) {
111 if (atoi(argv[1]) > MAXNREPS) {
112 tst_resm(TINFO,
113 "Requested number of iterations too large, setting to Max. of %d",
114 MAXNREPS);
115 nreps = MAXNREPS;
116 } else {
117 nreps = atoi(argv[1]);
118 }
119 if (atoi(argv[2]) > maxnprocs) {
120 tst_resm(TINFO,
121 "Requested number of processes too large, setting to Max. of %d",
122 maxnprocs);
123 } else {
124 maxnprocs = atoi(argv[2]);
125 }
126 if (atoi(argv[3]) > maxnkids) {
127 tst_resm(TINFO,
128 "Requested number of read/write pairs too large; setting to Max. of %d",
129 maxnkids);
130 nkids = maxnkids;
131 } else {
132 nkids = atoi(argv[3]);
133 }
134 } else {
135 tst_brkm(TCONF, cleanup,
136 " Usage: %s [ number of iterations number of processes number of read/write pairs ]",
137 argv[0]);
138 }
139
140 procstat = 0;
141 srand48((unsigned)getpid() + (unsigned)(getppid() << 16));
142 tid = -1;
143
144 /* Setup signal handling routine */
145 if (sigset(SIGTERM, term) == SIG_ERR)
146 tst_brkm(TFAIL, cleanup, "Sigset SIGTERM failed");
147
148 /* Set up array of unique keys for use in allocating message
149 * queues
150 */
151 for (i = 0; i < MSGMNI; i++) {
152 ok = 1;
153 do {
154 /* Get random key */
155 keyarray[i] = (key_t) lrand48();
156 /* Make sure key is unique and not private */
157 if (keyarray[i] == IPC_PRIVATE) {
158 ok = 0;
159 continue;
160 }
161 for (j = 0; j < i; j++) {
162 if (keyarray[j] == keyarray[i]) {
163 ok = 0;
164 break;
165 }
166 ok = 1;
167 }
168 } while (ok == 0);
169 }
170 /* Fork a number of processes, each of which will
171 * create a message queue with several (nkids) reader/writer
172 * pairs which will read and write a number (iterations)
173 * of random length messages with specific values (keys).
174 *
175 * We do not fork more than maxnprocs at a time and
176 * we fork until all the message queues get used.
177 */
178
179 if (MSGMNI <= maxnprocs) {
180 nprocs = MSGMNI;
181 dotest_iteration(0);
182 } else {
183 for (i = 0; i < (MSGMNI / maxnprocs); i++) {
184 nprocs = maxnprocs;
185 dotest_iteration(i*(MSGMNI / maxnprocs));
186 }
187
188 nprocs = MSGMNI % maxnprocs;
189 dotest_iteration(i*(MSGMNI / maxnprocs));
190 }
191
192 tst_resm(TPASS, "msgctl11 ran successfully!");
193
194 cleanup();
195 tst_exit();
196 }
197
dotest_iteration(int off)198 static void dotest_iteration(int off)
199 {
200 key_t key;
201 int i, count, status;
202 pid_t pid;
203
204 memset(pidarray, 0, sizeof(pidarray));
205
206 for (i = 0; i < nprocs; i++) {
207 key = keyarray[off + i];
208
209 if ((pid = FORK_OR_VFORK()) < 0)
210 tst_brkm(TFAIL, cleanup,
211 "Fork failed (may be OK if under stress)");
212
213 /* Child does this */
214 if (pid == 0) {
215 #ifdef UCLINUX
216 if (self_exec(argv0, "ndd", 1, key, i) < 0) {
217 printf("\tself_exec failed\n");
218 exit(FAIL);
219 }
220 #else
221 procstat = 1;
222 exit(dotest(key, i));
223 #endif
224 }
225 pidarray[i] = pid;
226 }
227
228 count = 0;
229 while (1) {
230 if ((wait(&status)) > 0) {
231 if (status >> 8 != PASS)
232 tst_brkm(TFAIL, cleanup,
233 "Child exit status = %d", status >> 8);
234 count++;
235 } else {
236 if (errno != EINTR) {
237 break;
238 }
239 #ifdef DEBUG
240 tst_resm(TINFO, "Signal detected during wait");
241 #endif
242 }
243 }
244 /* Make sure proper number of children exited */
245 if (count != nprocs)
246 tst_brkm(TFAIL, cleanup,
247 "Wrong number of children exited, Saw %d, Expected %d",
248 count, nprocs);
249 }
250
251 #ifdef UCLINUX
do_child_1_uclinux(void)252 static void do_child_1_uclinux(void)
253 {
254 procstat = 1;
255 exit(dotest(key_uclinux, i_uclinux));
256 }
257
do_child_2_uclinux(void)258 static void do_child_2_uclinux(void)
259 {
260 procstat = 2;
261 exit(doreader(key_uclinux, tid, pid_uclinux,
262 child_process_uclinux, nreps));
263 }
264
do_child_3_uclinux(void)265 static void do_child_3_uclinux(void)
266 {
267 procstat = 2;
268 exit(dowriter(key_uclinux, tid, rkid_uclinux,
269 child_process_uclinux, nreps));
270 }
271 #endif
272
cleanup_msgqueue(int i,int tid)273 static void cleanup_msgqueue(int i, int tid)
274 {
275 /*
276 * Decrease the value of i by 1 because it
277 * is getting incremented even if the fork
278 * is failing.
279 */
280
281 i--;
282 /*
283 * Kill all children & free message queue.
284 */
285 for (; i >= 0; i--) {
286 (void)kill(rkidarray[i], SIGKILL);
287 (void)kill(wkidarray[i], SIGKILL);
288 }
289
290 if (msgctl(tid, IPC_RMID, 0) < 0) {
291 printf("Msgctl error in cleanup_msgqueue %d\n", errno);
292 exit(FAIL);
293 }
294 }
295
dotest(key_t key,int child_process)296 static int dotest(key_t key, int child_process)
297 {
298 int id, pid;
299 int i, count, status, exit_status;
300
301 sighold(SIGTERM);
302 if ((id = msgget(key, IPC_CREAT | S_IRUSR | S_IWUSR)) < 0) {
303 printf("msgget() error in child %d: %s\n",
304 child_process, strerror(errno));
305 return FAIL;
306 }
307 tid = id;
308 sigrelse(SIGTERM);
309
310 exit_status = PASS;
311
312 for (i = 0; i < nkids; i++) {
313 if ((pid = FORK_OR_VFORK()) < 0) {
314 printf("Fork failure in the first child of child group %d\n",
315 child_process);
316 cleanup_msgqueue(i, tid);
317 return FAIL;
318 }
319 /* First child does this */
320 if (pid == 0) {
321 #ifdef UCLINUX
322 if (self_exec(argv0, "nddd", 2, key, getpid(),
323 child_process) < 0) {
324 printf("self_exec failed\n");
325 cleanup_msgqueue(i, tid);
326 return FAIL;
327 }
328 #else
329 procstat = 2;
330 exit(doreader(key, tid, getpid(),
331 child_process, nreps));
332 #endif
333 }
334 rkidarray[i] = pid;
335 if ((pid = FORK_OR_VFORK()) < 0) {
336 printf("Fork failure in the second child of child group %d\n",
337 child_process);
338 /*
339 * Kill the reader child process
340 */
341 (void)kill(rkidarray[i], SIGKILL);
342
343 cleanup_msgqueue(i, tid);
344 return FAIL;
345 }
346 /* Second child does this */
347 if (pid == 0) {
348 #ifdef UCLINUX
349 if (self_exec(argv0, "nddd", 3, key, rkidarray[i],
350 child_process) < 0) {
351 printf("\tFork failure in the first child of child group %d\n",
352 child_process);
353 /*
354 * Kill the reader child process
355 */
356 (void)kill(rkidarray[i], SIGKILL);
357
358 cleanup_msgqueue(i, tid);
359 return FAIL;
360 }
361 #else
362 procstat = 2;
363 exit(dowriter(key, tid, rkidarray[i],
364 child_process, nreps));
365 #endif
366 }
367 wkidarray[i] = pid;
368 }
369 /* Parent does this */
370 count = 0;
371 while (1) {
372 if ((wait(&status)) > 0) {
373 if (status >> 8 != PASS) {
374 printf("Child exit status = %d from child group %d\n",
375 status >> 8, child_process);
376 for (i = 0; i < nkids; i++) {
377 kill(rkidarray[i], SIGTERM);
378 kill(wkidarray[i], SIGTERM);
379 }
380 if (msgctl(tid, IPC_RMID, 0) < 0) {
381 printf("msgctl() error: %s\n",
382 strerror(errno));
383 }
384 return FAIL;
385 }
386 count++;
387 } else {
388 if (errno != EINTR) {
389 break;
390 }
391 }
392 }
393 /* Make sure proper number of children exited */
394 if (count != (nkids * 2)) {
395 printf("Wrong number of children exited in child group %d, saw %d, expected %d\n",
396 child_process, count, (nkids * 2));
397 if (msgctl(tid, IPC_RMID, 0) < 0) {
398 printf("msgctl() error: %s\n", strerror(errno));
399 }
400 return FAIL;
401 }
402 if (msgctl(id, IPC_RMID, 0) < 0) {
403 printf("msgctl() failure in child group %d: %s\n",
404 child_process, strerror(errno));
405 return FAIL;
406 }
407 return exit_status;
408 }
409
410 /* ARGSUSED */
term(int sig LTP_ATTRIBUTE_UNUSED)411 static void term(int sig LTP_ATTRIBUTE_UNUSED)
412 {
413 int i;
414
415 if (procstat == 0) {
416 #ifdef DEBUG
417 tst_resm(TINFO, "SIGTERM signal received, test killing kids");
418 #endif
419 for (i = 0; i < nprocs; i++) {
420 if (pidarray[i] > 0) {
421 if (kill(pidarray[i], SIGTERM) < 0) {
422 tst_resm(TBROK,
423 "Kill failed to kill child %d",
424 i);
425 exit(FAIL);
426 }
427 }
428 }
429 return;
430 }
431
432 if (procstat == 2) {
433 exit(PASS);
434 }
435
436 if (tid == -1) {
437 exit(FAIL);
438 }
439 for (i = 0; i < nkids; i++) {
440 if (rkidarray[i] > 0)
441 kill(rkidarray[i], SIGTERM);
442 if (wkidarray[i] > 0)
443 kill(wkidarray[i], SIGTERM);
444 }
445 }
446
setup(void)447 void setup(void)
448 {
449 int nr_msgqs, free_pids;
450
451 tst_tmpdir();
452 /* You will want to enable some signal handling so you can capture
453 * unexpected signals like SIGSEGV.
454 */
455 tst_sig(FORK, DEF_HANDLER, cleanup);
456
457 /* One cavet that hasn't been fixed yet. TEST_PAUSE contains the code to
458 * fork the test with the -c option. You want to make sure you do this
459 * before you create your temporary directory.
460 */
461 TEST_PAUSE;
462
463 nr_msgqs = get_max_msgqueues();
464 if (nr_msgqs < 0)
465 tst_brkm(TBROK, cleanup, "get_max_msgqueues() failed");
466
467 MSGMNI = nr_msgqs - get_used_msgqueues();
468 if (MSGMNI <= 0)
469 tst_brkm(TBROK, cleanup,
470 "Max number of message queues already used, cannot create more.");
471
472 tst_resm(TINFO, "Found %d available message queues", MSGMNI);
473
474 free_pids = tst_get_free_pids(cleanup);
475 if (free_pids < 0) {
476 tst_brkm(TBROK, cleanup, "Can't obtain free_pid count");
477 } else if (!free_pids) {
478 tst_brkm(TBROK, cleanup, "No free pids");
479 }
480
481 /* We don't use more than a half of available pids.
482 * For each child we fork up to 2*maxnkids grandchildren. */
483 maxnprocs = (free_pids / 2) / (1 + 2 * maxnkids);
484
485 if (!maxnprocs)
486 tst_brkm(TBROK, cleanup, "Not enough free pids");
487
488 tst_resm(TINFO, "Using upto %d pids", free_pids / 2);
489 }
490
cleanup(void)491 void cleanup(void)
492 {
493 int status;
494
495 /*
496 * Remove the message queue from the system
497 */
498 #ifdef DEBUG
499 tst_resm(TINFO, "Removing the message queue");
500 #endif
501 (void)msgctl(tid, IPC_RMID, NULL);
502 if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) {
503 (void)msgctl(tid, IPC_RMID, NULL);
504 tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed");
505
506 }
507
508 tst_rmdir();
509 }
510