3 * Copyright (c) International Business Machines Corp., 2002
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 /* 06/30/2001 Port to Linux nsharoff@us.ibm.com */
21 /* 11/11/2002 Port to LTP dbarrera@us.ibm.com */
28 * msgget(2) msgctl(2) msgop(2)
31 * Get and manipulate a message queue.
37 #define _XOPEN_SOURCE 500
39 #include <sys/types.h>
54 #ifndef CONFIG_COLDFIRE
55 #define MAXNPROCS 1000000 /* This value is set to an arbitrary high limit. */
57 #define MAXNPROCS 100000 /* Coldfire can't deal with 1000000 */
63 int dotest(key_t
, int);
64 int doreader(long, int, int);
65 int dowriter(long, int, int);
66 int fill_buffer(char *, char, int);
67 int verify(char *, char, int, int);
72 * These globals must be defined in the test.
75 char *TCID
= "msgctl09"; /* Test program identifier. */
76 int TST_TOTAL
= 1; /* Total number of test cases. */
77 extern int Tst_count
; /* Test Case counter for tst_* routines */
79 int exp_enos
[] = { 0 }; /* List must end with 0 */
81 key_t keyarray
[MAXNPROCS
];
91 int pidarray
[MAXNPROCS
];
92 int rkidarray
[MAXNKIDS
];
93 int wkidarray
[MAXNKIDS
];
95 int nprocs
, nreps
, nkids
, MSGMNI
;
101 void do_child_1_uclinux();
102 static key_t key_uclinux
;
103 static int i_uclinux
;
105 void do_child_2_uclinux();
106 static int pid_uclinux
;
107 static int child_process_uclinux
;
109 void do_child_3_uclinux();
110 static int rkid_uclinux
;
112 void cleanup_msgqueue(int i
, int tid
);
114 /*-----------------------------------------------------------------*/
119 register int i
, j
, ok
, pid
;
123 char *msg
; /* message returned from parse_opts */
127 /* parse standard options */
129 parse_opts(argc
, argv
, (option_t
*) NULL
, NULL
)) != (char *)NULL
) {
130 tst_brkm(TBROK
, cleanup
, "OPTION PARSING ERROR - %s", msg
);
133 maybe_run_child(&do_child_1_uclinux
, "ndd", 1, &key_uclinux
,
135 maybe_run_child(&do_child_2_uclinux
, "nddd", 2, &key_uclinux
,
136 &pid_uclinux
, &child_process_uclinux
);
137 maybe_run_child(&do_child_3_uclinux
, "nddd", 3, &key_uclinux
,
138 &rkid_uclinux
, &child_process_uclinux
);
144 /* Set default parameters */
148 } else if (argc
== 4) {
149 if (atoi(argv
[1]) > MAXNREPS
) {
151 "Requested number of iterations too large, setting to Max. of %d",
155 nreps
= atoi(argv
[1]);
157 if (atoi(argv
[2]) > MSGMNI
) {
159 "Requested number of processes too large, setting to Max. of %d",
163 nprocs
= atoi(argv
[2]);
165 if (atoi(argv
[3]) > MAXNKIDS
) {
167 "Requested number of read/write pairs too large; setting to Max. of %d",
171 nkids
= atoi(argv
[3]);
175 " Usage: %s [ number of iterations number of processes number of read/write pairs ]",
181 srand48((unsigned)getpid() + (unsigned)(getppid() << 16));
184 /* Setup signal handleing routine */
185 if (sigset(SIGTERM
, term
) == SIG_ERR
) {
186 tst_resm(TFAIL
, "Sigset SIGTERM failed");
189 /* Set up array of unique keys for use in allocating message
192 for (i
= 0; i
< nprocs
; i
++) {
196 keyarray
[i
] = (key_t
) lrand48();
197 /* Make sure key is unique and not private */
198 if (keyarray
[i
] == IPC_PRIVATE
) {
202 for (j
= 0; j
< i
; j
++) {
203 if (keyarray
[j
] == keyarray
[i
]) {
211 /*-----------------------------------------------------------------*/
212 /* Fork a number of processes (nprocs), each of which will
213 * create a message queue with several (nkids) reader/writer
214 * pairs which will read and write a number (iterations)
215 * of random length messages with specific values (keys).
218 for (i
= 0; i
< nprocs
; i
++) {
220 if ((pid
= FORK_OR_VFORK()) < 0) {
222 "\tFork failed (may be OK if under stress)");
225 /* Child does this */
228 if (self_exec(argv
[0], "ndd", 1, keyarray
[i
], i
) < 0) {
229 tst_resm(TFAIL
, "\tself_exec failed");
234 exit(dotest(keyarray
[i
], i
));
242 if ((wait(&status
)) > 0) {
243 if (status
>> 8 != PASS
) {
244 tst_resm(TFAIL
, "Child exit status = %d",
250 if (errno
!= EINTR
) {
254 tst_resm(TINFO
, "Signal detected during wait");
258 /* Make sure proper number of children exited */
259 if (count
!= nprocs
) {
261 "Wrong number of children exited, Saw %d, Expected %d",
266 tst_resm(TPASS
, "msgctl09 ran successfully!");
274 /*--------------------------------------------------------------------*/
277 void do_child_1_uclinux()
280 exit(dotest(key_uclinux
, i_uclinux
));
283 void do_child_2_uclinux()
286 exit(doreader(key_uclinux
, pid_uclinux
, child_process_uclinux
));
289 void do_child_3_uclinux()
292 exit(dowriter(key_uclinux
, rkid_uclinux
, child_process_uclinux
));
296 void cleanup_msgqueue(int i
, int tid
)
299 * Decrease the value of i by 1 because it
300 * is getting incremented even if the fork
306 * Kill all children & free message queue.
308 for (; i
>= 0; i
--) {
309 (void)kill(rkidarray
[i
], SIGKILL
);
310 (void)kill(wkidarray
[i
], SIGKILL
);
313 if (msgctl(tid
, IPC_RMID
, 0) < 0) {
314 tst_resm(TFAIL
|TERRNO
, "Msgctl error in cleanup");
319 int dotest(key
, child_process
)
324 int i
, count
, status
, exit_status
;
327 if ((id
= msgget(key
, IPC_CREAT
| S_IRUSR
| S_IWUSR
)) < 0) {
328 tst_resm(TFAIL
|TERRNO
, "Msgget error in child %d",
337 for (i
= 0; i
< nkids
; i
++) {
339 if ((pid
= FORK_OR_VFORK()) < 0) {
341 "Fork failure in first child of child group %d",
343 cleanup_msgqueue(i
, tid
);
346 /* First child does this */
349 if (self_exec(argv0
, "nddd", 2, key
, getpid(),
350 child_process
) < 0) {
351 tst_resm(TWARN
, "self_exec failed");
352 cleanup_msgqueue(i
, tid
);
357 exit(doreader(key
, getpid(), child_process
));
362 if ((pid
= FORK_OR_VFORK()) < 0) {
364 "Fork failure in first child of child group %d",
367 * Kill the reader child process
369 (void)kill(rkidarray
[i
], SIGKILL
);
371 cleanup_msgqueue(i
, tid
);
374 /* Second child does this */
377 if (self_exec(argv0
, "nddd", 3, key
, rkidarray
[i
],
378 child_process
) < 0) {
379 tst_resm(TWARN
, "\tFork failure in first child "
380 "of child group %d \n", child_process
);
382 * Kill the reader child process
384 (void)kill(rkidarray
[i
], SIGKILL
);
386 cleanup_msgqueue(i
, tid
);
391 exit(dowriter(key
, rkidarray
[i
], child_process
));
396 /* Parent does this */
399 if ((wait(&status
)) > 0) {
400 if (status
>> 8 != PASS
) {
402 "Child exit status = %d from child group %d",
403 status
>> 8, child_process
);
404 for (i
= 0; i
< nkids
; i
++) {
405 kill(rkidarray
[i
], SIGTERM
);
406 kill(wkidarray
[i
], SIGTERM
);
408 if (msgctl(tid
, IPC_RMID
, 0) < 0) {
409 tst_resm(TFAIL
|TERRNO
,
416 if (errno
!= EINTR
) {
421 /* Make sure proper number of children exited */
422 if (count
!= (nkids
* 2)) {
424 "Wrong number of children exited in child group %d, Saw %d Expected %d",
425 child_process
, count
, (nkids
* 2));
426 if (msgctl(tid
, IPC_RMID
, 0) < 0) {
427 tst_resm(TFAIL
|TERRNO
, "Msgctl error");
431 if (msgctl(id
, IPC_RMID
, 0) < 0) {
432 tst_resm(TFAIL
|TERRNO
, "Msgctl failure in child group %d",
439 int doreader(key
, type
, child
)
446 if ((id
= msgget(key
, 0)) < 0) {
447 tst_resm(TFAIL
|TERRNO
,
448 "Msgget error in reader of child group %d",
454 "Message queue mismatch in reader of child group %d for message queue id %d",
458 for (i
= 0; i
< nreps
; i
++) {
459 if ((size
= msgrcv(id
, &buffer
, 100, type
, 0)) < 0) {
460 tst_resm(TFAIL
|TERRNO
,
461 "Msgrcv error in child %d, read # = %d",
465 if (buffer
.type
!= type
) {
467 "Size mismatch in child %d, read # = %d",
470 "\tfor message size got %d expected %d",
471 size
, buffer
.data
.len
);
474 if (buffer
.data
.len
+ 1 != size
) {
476 "Size mismatch in child %d, read # = %d, size = %d, expected = %d",
477 child
, (i
+ 1), buffer
.data
.len
, size
);
480 if (verify(buffer
.data
.pbytes
, (key
% 255), size
- 1, child
)) {
481 tst_resm(TFAIL
, "in child %d read # = %d,key = %lx",
482 child
, (i
+ 1), key
);
490 int dowriter(key
, type
, child
)
497 if ((id
= msgget(key
, 0)) < 0) {
498 tst_resm(TFAIL
|TERRNO
,
499 "Msgget error in writer of child group %d",
505 "Message queue mismatch in writer of child group %d",
507 tst_resm(TFAIL
, "\tfor message queue id %d expected %d", id
,
512 for (i
= 0; i
< nreps
; i
++) {
514 size
= (lrand48() % 99);
516 fill_buffer(buffer
.data
.pbytes
, (key
% 255), size
);
517 buffer
.data
.len
= size
;
519 if (msgsnd(id
, &buffer
, size
+ 1, 0) < 0) {
520 tst_resm(TFAIL
|TERRNO
,
521 "Msgsnd error in child %d, key = %lx",
530 int fill_buffer(buf
, val
, size
)
537 for (i
= 0; i
< size
; i
++)
544 * Check a buffer for correct values.
547 int verify(buf
, val
, size
, child
)
556 "Verify error in child %d, *buf = %x, val = %x, size = %d",
557 child
, *buf
, val
, size
);
570 tst_resm(TINFO
, "SIGTERM signal received, test killing kids");
572 for (i
= 0; i
< nprocs
; i
++) {
573 if (pidarray
[i
] > 0) {
574 if (kill(pidarray
[i
], SIGTERM
) < 0) {
576 "Kill failed to kill child %d",
593 for (i
= 0; i
< nkids
; i
++) {
594 if (rkidarray
[i
] > 0)
595 kill(rkidarray
[i
], SIGTERM
);
596 if (wkidarray
[i
] > 0)
597 kill(wkidarray
[i
], SIGTERM
);
601 /***************************************************************
602 * setup() - performs all ONE TIME setup for this test.
603 *****************************************************************/
609 /* You will want to enable some signal handling so you can capture
610 * unexpected signals like SIGSEGV.
612 tst_sig(FORK
, DEF_HANDLER
, cleanup
);
614 /* Pause if that option was specified */
615 /* One cavet that hasn't been fixed yet. TEST_PAUSE contains the code to
616 * fork the test with the -c option. You want to make sure you do this
617 * before you create your temporary directory.
621 nr_msgqs
= get_max_msgqueues();
625 nr_msgqs
-= get_used_msgqueues();
628 "Max number of message queues already used, cannot create more.");
633 * Since msgmni scales to the memory size, it may reach huge values
634 * that are not necessary for this test.
635 * That's why we define NR_MSGQUEUES as a high boundary for it.
637 MSGMNI
= min(nr_msgqs
, NR_MSGQUEUES
);
640 /***************************************************************
641 * cleanup() - performs all ONE TIME cleanup for this test at
642 * completion or premature exit.
643 ****************************************************************/
648 * print timing stats if that option was specified.
649 * print errno log if that option was specified.
654 * Remove the message queue from the system
657 tst_resm(TINFO
, "Removing the message queue");
660 (void)msgctl(tid
, IPC_RMID
, (struct msqid_ds
*)NULL
);
661 if ((status
= msgctl(tid
, IPC_STAT
, (struct msqid_ds
*)NULL
)) != -1) {
662 (void)msgctl(tid
, IPC_RMID
, (struct msqid_ds
*)NULL
);
663 tst_resm(TFAIL
, "msgctl(tid, IPC_RMID) failed");
669 /* exit with return code appropriate for results */