semctl01.c revision 56207cec7732e09c216c751c0b5f88a242bacae6
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, (option_t *) NULL, NULL)) != (char *)NULL) { 150 tst_brkm(TBROK, cleanup, "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 if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1) 387 tst_brkm(TBROK, cleanup, "sync_pipe_close failed"); 388 389 sops.sem_num = SEM4; 390 sops.sem_flg = 0; 391 392 /* 393 * Do a semop that will cause the child to sleep. 394 * The child process will be killed in the func_ncnt 395 * routine which should cause an error to be return 396 * by the semop() call. 397 */ 398 if (semop(sem_id_1, &sops, 1) != -1) { 399 tst_resm(TBROK, "semop succeeded - cnt_setup"); 400 } 401 exit(0); 402} 403 404/* 405 * func_cnt() - check the functionality of the GETNCNT and GETZCNT commands 406 * with semctl() 407 */ 408void func_cnt(int rval) 409{ 410 411 if (rval == NCHILD) { 412 tst_resm(TPASS, "number of sleeping processes is correct"); 413 } else { 414 tst_resm(TFAIL, "number of sleeping processes is not correct"); 415 } 416} 417 418/* 419 * pid_setup() - set up for the GETPID command with semctl() 420 */ 421void pid_setup() 422{ 423 int pid; 424 425 if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1) { 426 tst_brkm(TBROK, cleanup, "sync_pipe_create failed"); 427 } 428 429 /* 430 * Fork a child to do a semop that will pass. 431 */ 432 if ((pid = FORK_OR_VFORK()) == -1) { 433 tst_brkm(TBROK, cleanup, "fork failed in pid_setup()"); 434 } 435 436 if (pid == 0) { /* child */ 437#ifdef UCLINUX 438 if (self_exec(argv0, "nd", 1, sem_id_1) < 0) { 439 tst_brkm(TBROK, cleanup, "self_exec failed " 440 "in pid_setup()"); 441 } 442#else 443 child_pid(); 444#endif 445 } else { /* parent */ 446 if (sync_pipe_wait(sync_pipes) == -1) 447 tst_brkm(TBROK, cleanup, "sync_pipe_wait failed"); 448 449 if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1) 450 tst_brkm(TBROK, cleanup, "sync_pipe_close failed"); 451 sleep(1); 452 pid_arr[SEM2] = pid; 453 } 454} 455 456void child_pid() 457{ 458#ifdef UCLINUX 459 if (sync_pipe_create(sync_pipes, PIPE_NAME) == -1) 460 tst_brkm(TBROK, cleanup, "sync_pipe_create failed"); 461#endif 462 463 if (sync_pipe_notify(sync_pipes) == -1) 464 tst_brkm(TBROK, cleanup, "sync_pipe_notify failed"); 465 466 if (sync_pipe_close(sync_pipes, PIPE_NAME) == -1) 467 tst_brkm(TBROK, cleanup, "sync_pipe_close failed"); 468 469 sops.sem_num = SEM2; /* semaphore to change */ 470 sops.sem_op = ONE; /* operation is to increment semaphore */ 471 sops.sem_flg = 0; 472 473 /* 474 * Do a semop that will increment the semaphore. 475 */ 476 if (semop(sem_id_1, &sops, 1) == -1) { 477 tst_resm(TBROK, "semop failed - pid_setup"); 478 } 479 exit(0); 480} 481 482/* 483 * func_pid() - check the functionality of the GETPID command with semctl() 484 */ 485void func_pid(int rval) 486{ 487 /* compare the rval (pid) to the saved pid from the setup */ 488 if (rval == pid_arr[SEM2]) { 489 tst_resm(TPASS, "last pid value is correct"); 490 } else { 491 tst_resm(TFAIL, "last pid value is not correct"); 492 } 493} 494 495/* 496 * func_gval() - check the functionality of the GETVAL command with semctl() 497 */ 498void func_gval(int rval) 499{ 500 /* 501 * This is a simple test. The semaphore value should be equal 502 * to ONE as it was set in the last test (GETPID). 503 */ 504 if (rval == 1) { 505 tst_resm(TPASS, "semaphore value is correct"); 506 } else { 507 tst_resm(TFAIL, "semaphore value is not correct"); 508 } 509 510} 511 512/* 513 * all_setup() - set up for the SETALL command with semctl() 514 */ 515void sall_setup() 516{ 517 int i; 518 519 for (i = 0; i < PSEMS; i++) { 520 /* initialize the array values to 3 */ 521 array[i] = 3; 522 } 523} 524 525/* 526 * func_sall() - check the functionality of the SETALL command with semctl() 527 */ 528void func_sall() 529{ 530 int i; 531 unsigned short rarray[PSEMS]; 532 533 /* 534 * do a GETALL and compare the values to those set above 535 */ 536 537 if (semctl(sem_id_1, 0, GETALL, (union semun)rarray) == -1) { 538 tst_brkm(TBROK, cleanup, "semctl failed in func_sall"); 539 } 540 541 for (i = 0; i < PSEMS; i++) { 542 if (array[i] != rarray[i]) { 543 tst_resm(TFAIL, "semaphore values are not correct"); 544 return; 545 } 546 } 547 548 tst_resm(TPASS, "semaphore values are correct"); 549} 550 551/* 552 * func_sval() - check the functionality of the SETVAL command with semctl() 553 */ 554void func_sval() 555{ 556 int semv; 557 union semun arr; 558 559 /* 560 * do a GETVAL and compare it to the value set above 561 */ 562 563 if ((semv = semctl(sem_id_1, SEM4, GETVAL, arr)) == -1) { 564 tst_brkm(TBROK, cleanup, "semctl failed in func_sval"); 565 } 566 567 if (semv != INCVAL) { 568 tst_resm(TFAIL, "semaphore value is not what was set"); 569 } else { 570 tst_resm(TPASS, "semaphore value is correct"); 571 } 572} 573 574/* 575 * func_rmid() - check the functionality of the IPC_RMID command with semctl() 576 */ 577void func_rmid() 578{ 579 580 /* 581 * do a semop() - we should get EINVAL 582 */ 583 if (semop(sem_id_1, &sops, 1) != -1) { 584 tst_resm(TFAIL, "semop succeeded on expected fail"); 585 } 586 587 if (errno != EINVAL) { 588 tst_resm(TFAIL, "returned errno - %d - is not expected", errno); 589 } else { 590 tst_resm(TPASS, "semaphore appears to be removed"); 591 } 592 593 sem_id_1 = -1; 594} 595 596/* 597 * setup() - performs all the ONE TIME setup for this test. 598 */ 599void setup(void) 600{ 601 /* capture signals */ 602 tst_sig(FORK, DEF_HANDLER, cleanup); 603 604 /* Pause if that option was specified */ 605 TEST_PAUSE; 606 607 /* 608 * Create a temporary directory and cd into it. 609 * This helps to ensure that a unique msgkey is created. 610 * See ../lib/libipc.c for more information. 611 */ 612 tst_tmpdir(); 613 614 /* get an IPC resource key */ 615 semkey = getipckey(); 616 617 /* create a semaphore set with read and alter permissions */ 618 if ((sem_id_1 = 619 semget(semkey, PSEMS, IPC_CREAT | IPC_EXCL | SEM_RA)) == -1) { 620 tst_brkm(TBROK, cleanup, "couldn't create semaphore in setup"); 621 } 622} 623 624/* 625 * cleanup() - performs all the ONE TIME cleanup for this test at completion 626 * or premature exit. 627 */ 628void cleanup(void) 629{ 630 /* if it exists, remove the semaphore resource */ 631 rm_sema(sem_id_1); 632 633 /* Remove the temporary directory */ 634 tst_rmdir(); 635 636 /* 637 * print timing stats if that option was specified. 638 * print errno log if that option was specified. 639 */ 640 TEST_CLEANUP; 641 642 /* exit with return code appropriate for results */ 643 tst_exit(); 644} 645