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 43char *TCID = "msgctl11"; 44int 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 55static int maxnkids = MAXNKIDS; /* Used if pid_max is exceeded */ 56static key_t keyarray[MAXNPROCS]; 57static int pidarray[MAXNPROCS]; 58static int rkidarray[MAXNKIDS]; 59static int wkidarray[MAXNKIDS]; 60static int tid; 61static int nprocs, nreps, nkids, MSGMNI; 62static int maxnprocs; 63static int procstat; 64 65void setup(void); 66void cleanup(void); 67 68static void term(int); 69static int dotest(key_t, int); 70static void dotest_iteration(int off); 71static void cleanup_msgqueue(int i, int tid); 72 73static char *opt_maxnprocs; 74static char *opt_nkids; 75static char *opt_nreps; 76 77static option_t options[] = { 78 {"n:", NULL, &opt_maxnprocs}, 79 {"c:", NULL, &opt_nkids}, 80 {"l:", NULL, &opt_nreps}, 81 {NULL, NULL, NULL}, 82}; 83 84static 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 92int 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*(MSGMNI / maxnprocs)); 180 } 181 182 nprocs = MSGMNI % maxnprocs; 183 dotest_iteration(i*(MSGMNI / maxnprocs)); 184 } 185 186 tst_resm(TPASS, "msgctl11 ran successfully!"); 187 188 cleanup(); 189 tst_exit(); 190} 191 192static 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 238static 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 261static 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 */ 352static 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 388void 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 if (free_pids < 0) { 417 tst_brkm(TBROK, cleanup, "Can't obtain free_pid count"); 418 } else if (!free_pids) { 419 tst_brkm(TBROK, cleanup, "No free pids"); 420 } 421 422 /* We don't use more than a half of available pids. 423 * For each child we fork up to 2*maxnkids grandchildren. */ 424 maxnprocs = (free_pids / 2) / (1 + 2 * maxnkids); 425 426 if (!maxnprocs) 427 tst_brkm(TBROK, cleanup, "Not enough free pids"); 428 429 tst_resm(TINFO, "Using upto %d pids", free_pids / 2); 430} 431 432void cleanup(void) 433{ 434 int status; 435 436 /* 437 * Remove the message queue from the system 438 */ 439#ifdef DEBUG 440 tst_resm(TINFO, "Removing the message queue"); 441#endif 442 (void)msgctl(tid, IPC_RMID, NULL); 443 if ((status = msgctl(tid, IPC_STAT, NULL)) != -1) { 444 (void)msgctl(tid, IPC_RMID, NULL); 445 tst_resm(TFAIL, "msgctl(tid, IPC_RMID) failed"); 446 447 } 448 449 tst_rmdir(); 450} 451