semctl01.c revision 60fa8014af7534eaefa901200c8df4b74ce422e6
1/* 2 * 3 * Copyright (c) International Business Machines Corp., 2001 4 * 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. 9 * 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. 14 * 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 18 */ 19 20/* 21 * NAME 22 * semctl01.c 23 * 24 * DESCRIPTION 25 * semctl01 - test the 10 possible semctl() commands 26 * 27 * ALGORITHM 28 * create a semaphore set with read and alter permissions 29 * loop if that option was specified 30 * loop through the test cases 31 * do any setup required for the test case 32 * make the semctl() call using the TEST() macro 33 * check the return code 34 * if failure, issue a FAIL message. 35 * otherwise, 36 * if doing functionality testing 37 * call the appropriate test function 38 * if correct, 39 * issue a PASS message 40 * otherwise 41 * issue a FAIL message 42 * call cleanup 43 * 44 * USAGE: <for command-line> 45 * semctl01 [-c n] [-f] [-i n] [-I x] [-P x] [-t] 46 * where, -c n : Run n copies concurrently. 47 * -f : Turn off functionality Testing. 48 * -i n : Execute test n times. 49 * -I x : Execute test for x seconds. 50 * -P x : Pause for x seconds between iterations. 51 * -t : Turn on syscall timing. 52 * 53 * HISTORY 54 * 03/2001 - Written by Wayne Boyer 55 * 56 * 11/03/2008 Renaud Lottiaux (Renaud.Lottiaux@kerlabs.com) 57 * - Fix concurrency issue. Due to the use of usleep function to 58 * synchronize processes, synchronization issues can occur on a loaded 59 * system. Fix this by using pipes to synchronize processes. 60 * 61 * RESTRICTIONS 62 * none 63 */ 64 65#include "ipcsem.h" 66#include "libtestsuite.h" 67 68char *TCID = "semctl01"; 69int TST_TOTAL = 10; 70extern int Tst_count; 71 72int sem_id_1 = -1; /* a semaphore set with read and alter permissions */ 73 74int sync_pipes[2]; 75 76/* 77 * These are the various setup and check functions for the 10 different 78 * commands that are available for the semctl() call. 79 */ 80void func_stat(void); 81void set_setup(void), func_set(void); 82void func_gall(void); 83void cnt_setup(int), func_cnt(int); 84void pid_setup(void), func_pid(int); 85void gval_setup(void), func_gval(int); 86void sall_setup(void), func_sall(void); 87void func_sval(void); 88void func_rmid(void); 89void child_cnt(void); 90void child_pid(void); 91 92struct semid_ds buf; 93unsigned short array[PSEMS]; 94struct sembuf sops; 95 96#define INCVAL 2 /* a semaphore increment value */ 97#define NEWMODE 066 98#define NCHILD 5 99#define SEM2 2 /* semaphore to use for GETPID and GETVAL */ 100#define SEM4 4 /* semaphore to use for GETNCNT and GETZCNT */ 101#define ONE 1 102#ifdef _XLC_COMPILER 103#define SEMUN_CAST 104#else 105#define SEMUN_CAST (union semun) 106#endif 107 108#ifdef _XLC_COMPILER 109#define SEMUN_CAST 110#else 111#define SEMUN_CAST (union semun) 112#endif 113 114int pid_arr[NCHILD]; 115 116#ifdef UCLINUX 117#define PIPE_NAME "semctl01" 118static char *argv0; 119int sem_op; 120#endif 121 122struct test_case_t { 123 int semnum; /* the primitive semaphore to use */ 124 int cmd; /* the command to test */ 125 void (*func_test) (); /* the test function */ 126 union semun arg; 127 void (*func_setup) (); /* the setup function if necessary */ 128} TC[] = { 129 { 130 0, IPC_STAT, func_stat, SEMUN_CAST & buf, NULL}, { 131 0, IPC_SET, func_set, SEMUN_CAST & buf, set_setup}, { 132 0, GETALL, func_gall, SEMUN_CAST array, NULL}, { 133 SEM4, GETNCNT, func_cnt, SEMUN_CAST & buf, cnt_setup}, { 134 SEM2, GETPID, func_pid, SEMUN_CAST & buf, pid_setup}, { 135 SEM2, GETVAL, func_gval, SEMUN_CAST & buf, NULL}, { 136 SEM4, GETZCNT, func_cnt, SEMUN_CAST & buf, cnt_setup}, { 137 0, SETALL, func_sall, SEMUN_CAST array, sall_setup}, { 138 SEM4, SETVAL, func_sval, SEMUN_CAST INCVAL, NULL}, { 139 0, IPC_RMID, func_rmid, SEMUN_CAST & buf, NULL} 140}; 141 142int main(int ac, char **av) 143{ 144 int lc; /* loop counter */ 145 char *msg; /* message returned from parse_opts */ 146 int i, j; 147 148 /* parse standard options */ 149 if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) { 150 tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); 151 } 152#ifdef UCLINUX 153 argv0 = av[0]; 154 maybe_run_child(&child_pid, "nd", 1, &sem_id_1); 155 maybe_run_child(&child_cnt, "ndd", 2, &sem_id_1, &sem_op); 156#endif 157 158 setup(); /* global setup */ 159 160 /* The following loop checks looping state if -i option given */ 161 162 for (lc = 0; TEST_LOOPING(lc); lc++) { 163 /* reset Tst_count in case we are looping */ 164 Tst_count = 0; 165 166 /* loop through the test cases */ 167 for (i = 0; i < TST_TOTAL; i++) { 168 169 /* 170 * Set up any conditions if needed 171 */ 172 if (TC[i].func_setup != NULL) { 173 /* call the setup function */ 174 switch (TC[i].cmd) { 175 case GETNCNT: 176 (*TC[i].func_setup) (-ONE); 177 break; 178 case GETZCNT: 179 (*TC[i].func_setup) (0); 180 break; 181 default: 182 (*TC[i].func_setup) (); 183 break; 184 } 185 } 186 187 /* 188 * Use TEST macro to make the call 189 */ 190 191 TEST(semctl(sem_id_1, TC[i].semnum, TC[i].cmd, 192 TC[i].arg)); 193 194 if (TEST_RETURN == -1) { 195 tst_resm(TFAIL, "%s call failed - errno = %d " 196 ": %s", TCID, TEST_ERRNO, 197 strerror(TEST_ERRNO)); 198 } else { 199 if (STD_FUNCTIONAL_TEST) { 200 /* 201 * call the appropriate test function 202 * and pass the return value where it 203 * is needed to perform certain tests. 204 */ 205 switch (TC[i].cmd) { 206 case GETNCNT: 207 /*FALLTHROUGH*/ case GETZCNT: 208 /*FALLTHROUGH*/ case GETPID: 209 /*FALLTHROUGH*/ case GETVAL: 210 (*TC[i]. 211 func_test) (TEST_RETURN); 212 break; 213 default: 214 (*TC[i].func_test) (); 215 break; 216 } 217 } else { 218 tst_resm(TPASS, "call succeeded"); 219 } 220 } 221 222 /* 223 * If testing GETNCNT or GETZCNT, clean up the children. 224 */ 225 switch (TC[i].cmd) { 226 case GETNCNT: 227 /*FALLTHROUGH*/ case GETZCNT: 228 for (j = 0; j < NCHILD; j++) { 229 if (kill(pid_arr[j], SIGKILL) == -1) { 230 tst_brkm(TBROK, cleanup, 231 "child kill failed"); 232 } 233 } 234 break; 235 } 236 } 237 /* 238 * recreate the semaphore resource if looping 239 */ 240 if (TEST_LOOPING(lc)) { 241 if ((sem_id_1 = semget(semkey, PSEMS, 242 IPC_CREAT | IPC_EXCL | SEM_RA)) 243 == -1) { 244 tst_brkm(TBROK, cleanup, 245 "couldn't recreate " "semaphore"); 246 } 247 } 248 } 249 250 cleanup(); 251 252 /*NOTREACHED*/ return 0; 253 254} 255 256/* 257 * func_stat() - check the functionality of the IPC_STAT command with semctl() 258 */ 259void func_stat() 260{ 261 /* check the number of semaphores and the ipc_perm.mode value */ 262 if (buf.sem_nsems == PSEMS && buf.sem_perm.mode == (SEM_RA)) { 263 tst_resm(TPASS, "buf.sem_nsems and buf.sem_perm.mode" 264 " are correct"); 265 } else { 266 tst_resm(TFAIL, "semaphore STAT info is incorrect"); 267 } 268} 269 270/* 271 * set_setup() - set up for the IPC_SET command with semctl() 272 */ 273void set_setup() 274{ 275 /* set up a new mode for the semaphore set */ 276 buf.sem_perm.mode = SEM_RA | NEWMODE; 277} 278 279/* 280 * func_set() - check the functionality of the IPC_SET command with semctl() 281 */ 282void func_set() 283{ 284 /* first stat the semaphore to get the new data */ 285 if (semctl(sem_id_1, 0, IPC_STAT, (union semun)&buf) == -1) { 286 tst_resm(TBROK, "stat failed in func_set()"); 287 return; 288 } 289 290 /* check that the new mode is what we set */ 291 if (buf.sem_perm.mode == (SEM_RA | NEWMODE)) { 292 tst_resm(TPASS, "buf.sem_perm.mode is correct"); 293 } else { 294 tst_resm(TFAIL, "semaphore mode info is incorrect"); 295 } 296} 297 298/* 299 * func_gall() - check the functionality of the GETALL command with semctl() 300 */ 301void func_gall() 302{ 303 int i; 304 305 /* the initial value of the primitive semaphores should be zero */ 306 for (i = 0; i < PSEMS; i++) { 307 if (array[i] != 0) { 308 tst_resm(TFAIL, "semaphore %d has unexpected value", i); 309 return; 310 } 311 } 312 tst_resm(TPASS, "semaphores have expected values"); 313} 314 315/* 316 * cnt_setup() - set up for the GETNCNT and GETZCNT commands with semctl() 317 */ 318void cnt_setup(int opval) 319{ 320 int pid, i; 321 322 sops.sem_num = SEM4; 323 sops.sem_flg = 0; 324 325 /* 326 * if seting up for GETZCNT, the semaphore value needs to be positive 327 */ 328 if (opval == 0) { 329 /* initialize the semaphore value to ONE */ 330 sops.sem_op = ONE; 331 if (semop(sem_id_1, &sops, 1) == -1) { 332 tst_brkm(TBROK, cleanup, "semop #1 failed - cnt_setup"); 333 } 334 } 335 336 sops.sem_op = opval; /* set the correct operation */ 337 for (i = 0; i < NCHILD; i++) { 338 if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1) 339 tst_brkm(TBROK, cleanup, "sync_pipe_create failed"); 340 341 /* fork five children to wait */ 342 if ((pid = FORK_OR_VFORK()) == -1) 343 tst_brkm(TBROK, cleanup, "fork failed in cnt_setup"); 344 345 if (pid == 0) { /* child */ 346#ifdef UCLINUX 347 sem_op = sops.sem_op; 348 if (self_exec(argv0, "ndd", 2, sem_id_1, sem_op) < 0) { 349 tst_brkm(TBROK, cleanup, "self_exec failed " 350 "in cnt_setup"); 351 } 352#else 353 child_cnt(); 354#endif 355 } else { /* parent */ 356 if (sync_pipe_wait(sync_pipes) == -1) 357 tst_brkm(TBROK, cleanup, 358 "sync_pipe_wait failed"); 359 360 if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1) 361 tst_brkm(TBROK, cleanup, 362 "sync_pipe_close failed"); 363 364 /* save the pid so we can kill it later */ 365 pid_arr[i] = pid; 366 } 367 } 368 /* After last son has been created, give it a chance to execute the 369 * semop command before we continue. Without this sleep, on SMP machine 370 * the father semctl could be executed before the son semop. 371 */ 372 sleep(1); 373} 374 375void child_cnt() 376{ 377#ifdef UCLINUX 378 sops.sem_op = (short int)sem_op; 379 if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1) 380 tst_brkm(TBROK, cleanup, "sync_pipe_create failed"); 381#endif 382 383 if (sync_pipe_notify(sync_pipes) == -1) 384 tst_brkm(TBROK, cleanup, "sync_pipe_notify failed"); 385 386#ifdef UCLINUX 387 if (sync_pipe_close(sync_pipes, NULL) == -1) 388#else 389 if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1) 390#endif 391 tst_brkm(TBROK, cleanup, "sync_pipe_close failed"); 392 393 sops.sem_num = SEM4; 394 sops.sem_flg = 0; 395 396 /* 397 * Do a semop that will cause the child to sleep. 398 * The child process will be killed in the func_ncnt 399 * routine which should cause an error to be return 400 * by the semop() call. 401 */ 402 if (semop(sem_id_1, &sops, 1) != -1) { 403 tst_resm(TBROK, "semop succeeded - cnt_setup"); 404 } 405 exit(0); 406} 407 408/* 409 * func_cnt() - check the functionality of the GETNCNT and GETZCNT commands 410 * with semctl() 411 */ 412void func_cnt(int rval) 413{ 414 415 if (rval == NCHILD) { 416 tst_resm(TPASS, "number of sleeping processes is correct"); 417 } else { 418 tst_resm(TFAIL, "number of sleeping processes is not correct"); 419 } 420} 421 422/* 423 * pid_setup() - set up for the GETPID command with semctl() 424 */ 425void pid_setup() 426{ 427 int pid; 428 429 if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1) { 430 tst_brkm(TBROK, cleanup, "sync_pipe_create failed"); 431 } 432 433 /* 434 * Fork a child to do a semop that will pass. 435 */ 436 if ((pid = FORK_OR_VFORK()) == -1) { 437 tst_brkm(TBROK, cleanup, "fork failed in pid_setup()"); 438 } 439 440 if (pid == 0) { /* child */ 441#ifdef UCLINUX 442 if (self_exec(argv0, "nd", 1, sem_id_1) < 0) { 443 tst_brkm(TBROK, cleanup, "self_exec failed " 444 "in pid_setup()"); 445 } 446#else 447 child_pid(); 448#endif 449 } else { /* parent */ 450 if (sync_pipe_wait(sync_pipes) == -1) 451 tst_brkm(TBROK, cleanup, "sync_pipe_wait failed"); 452 453 if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1) 454 tst_brkm(TBROK, cleanup, "sync_pipe_close failed"); 455 sleep(1); 456 pid_arr[SEM2] = pid; 457 } 458} 459 460void child_pid() 461{ 462#ifdef UCLINUX 463 if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1) 464 tst_brkm(TBROK, cleanup, "sync_pipe_create failed"); 465#endif 466 467 if (sync_pipe_notify(sync_pipes) == -1) 468 tst_brkm(TBROK, cleanup, "sync_pipe_notify failed"); 469 470 if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1) 471 tst_brkm(TBROK, cleanup, "sync_pipe_close failed"); 472 473 sops.sem_num = SEM2; /* semaphore to change */ 474 sops.sem_op = ONE; /* operation is to increment semaphore */ 475 sops.sem_flg = 0; 476 477 /* 478 * Do a semop that will increment the semaphore. 479 */ 480 if (semop(sem_id_1, &sops, 1) == -1) { 481 tst_resm(TBROK, "semop failed - pid_setup"); 482 } 483 exit(0); 484} 485 486/* 487 * func_pid() - check the functionality of the GETPID command with semctl() 488 */ 489void func_pid(int rval) 490{ 491 /* compare the rval (pid) to the saved pid from the setup */ 492 if (rval == pid_arr[SEM2]) { 493 tst_resm(TPASS, "last pid value is correct"); 494 } else { 495 tst_resm(TFAIL, "last pid value is not correct"); 496 } 497} 498 499/* 500 * func_gval() - check the functionality of the GETVAL command with semctl() 501 */ 502void func_gval(int rval) 503{ 504 /* 505 * This is a simple test. The semaphore value should be equal 506 * to ONE as it was set in the last test (GETPID). 507 */ 508 if (rval == 1) { 509 tst_resm(TPASS, "semaphore value is correct"); 510 } else { 511 tst_resm(TFAIL, "semaphore value is not correct"); 512 } 513 514} 515 516/* 517 * all_setup() - set up for the SETALL command with semctl() 518 */ 519void sall_setup() 520{ 521 int i; 522 523 for (i = 0; i < PSEMS; i++) { 524 /* initialize the array values to 3 */ 525 array[i] = 3; 526 } 527} 528 529/* 530 * func_sall() - check the functionality of the SETALL command with semctl() 531 */ 532void func_sall() 533{ 534 int i; 535 unsigned short rarray[PSEMS]; 536 537 /* 538 * do a GETALL and compare the values to those set above 539 */ 540 541 if (semctl(sem_id_1, 0, GETALL, (union semun)rarray) == -1) { 542 tst_brkm(TBROK, cleanup, "semctl failed in func_sall"); 543 } 544 545 for (i = 0; i < PSEMS; i++) { 546 if (array[i] != rarray[i]) { 547 tst_resm(TFAIL, "semaphore values are not correct"); 548 return; 549 } 550 } 551 552 tst_resm(TPASS, "semaphore values are correct"); 553} 554 555/* 556 * func_sval() - check the functionality of the SETVAL command with semctl() 557 */ 558void func_sval() 559{ 560 int semv; 561 union semun arr; 562 563 /* 564 * do a GETVAL and compare it to the value set above 565 */ 566 567 if ((semv = semctl(sem_id_1, SEM4, GETVAL, arr)) == -1) { 568 tst_brkm(TBROK, cleanup, "semctl failed in func_sval"); 569 } 570 571 if (semv != INCVAL) { 572 tst_resm(TFAIL, "semaphore value is not what was set"); 573 } else { 574 tst_resm(TPASS, "semaphore value is correct"); 575 } 576} 577 578/* 579 * func_rmid() - check the functionality of the IPC_RMID command with semctl() 580 */ 581void func_rmid() 582{ 583 584 /* 585 * do a semop() - we should get EINVAL 586 */ 587 if (semop(sem_id_1, &sops, 1) != -1) { 588 tst_resm(TFAIL, "semop succeeded on expected fail"); 589 } 590 591 if (errno != EINVAL) { 592 tst_resm(TFAIL, "returned errno - %d - is not expected", errno); 593 } else { 594 tst_resm(TPASS, "semaphore appears to be removed"); 595 } 596 597 sem_id_1 = -1; 598} 599 600/* 601 * setup() - performs all the ONE TIME setup for this test. 602 */ 603void setup(void) 604{ 605 /* capture signals */ 606 tst_sig(FORK, DEF_HANDLER, cleanup); 607 608 /* Pause if that option was specified */ 609 TEST_PAUSE; 610 611 /* 612 * Create a temporary directory and cd into it. 613 * This helps to ensure that a unique msgkey is created. 614 * See ../lib/libipc.c for more information. 615 */ 616 tst_tmpdir(); 617 618 /* get an IPC resource key */ 619 semkey = getipckey(); 620 621 /* create a semaphore set with read and alter permissions */ 622 if ((sem_id_1 = 623 semget(semkey, PSEMS, IPC_CREAT | IPC_EXCL | SEM_RA)) == -1) { 624 tst_brkm(TBROK, cleanup, "couldn't create semaphore in setup"); 625 } 626} 627 628/* 629 * cleanup() - performs all the ONE TIME cleanup for this test at completion 630 * or premature exit. 631 */ 632void cleanup(void) 633{ 634 /* if it exists, remove the semaphore resource */ 635 rm_sema(sem_id_1); 636 637 /* Remove the temporary directory */ 638 tst_rmdir(); 639 640 /* 641 * print timing stats if that option was specified. 642 * print errno log if that option was specified. 643 */ 644 TEST_CLEANUP; 645 646 /* exit with return code appropriate for results */ 647 tst_exit(); 648} 649