1 /* 2 * 3 * Copyright (c) International Business Machines Corp., 2004 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 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21 /* 22 * NAME 23 * hugeshmctl01.c 24 * 25 * DESCRIPTION 26 * hugeshmctl01 - test the IPC_STAT, IPC_SET and IPC_RMID commands as 27 * they are used with shmctl() 28 * 29 * ALGORITHM 30 * loop if that option was specified 31 * create a large shared memory segment with read and write permission 32 * set up any test case specific conditions 33 * call shmctl() using the TEST macro 34 * check the return code 35 * if failure, issue a FAIL message. 36 * otherwise, 37 * if doing functionality testing 38 * call the correct test function 39 * if the conditions are correct, 40 * issue a PASS message 41 * otherwise 42 * issue a FAIL message 43 * otherwise 44 * issue a PASS message 45 * call cleanup 46 * 47 * USAGE: <for command-line> 48 * hugeshmctl01 [-c n] [-f] [-i n] [-I x] [-P x] [-t] 49 * where, -c n : Run n copies concurrently. 50 * -f : Turn off functionality Testing. 51 * -i n : Execute test n times. 52 * -I x : Execute test for x seconds. 53 * -P x : Pause for x seconds between iterations. 54 * -t : Turn on syscall timing. 55 * 56 * HISTORY 57 * 03/2001 - Written by Wayne Boyer 58 * 04/2004 - Updated by Robbie Williamson 59 * 60 * RESTRICTIONS 61 * none 62 */ 63 64 #include "test.h" 65 #include "safe_macros.h" 66 #include "mem.h" 67 #include "hugetlb.h" 68 69 char *TCID = "hugeshmctl01"; 70 int TST_TOTAL = 4; 71 72 #define FIRST 0 73 #define SECOND 1 74 #define N_ATTACH 4 75 #define NEWMODE 0066 76 77 static size_t shm_size; 78 static int shm_id_1 = -1; 79 static struct shmid_ds buf; 80 static time_t save_time; 81 static int stat_time; 82 static void *set_shared; 83 static pid_t pid_arr[N_ATTACH]; 84 85 static void sighandler(int sig); 86 static void stat_setup(void); 87 static void stat_cleanup(void); 88 static void set_setup(void); 89 static void func_stat(void); 90 static void func_set(void); 91 static void func_rmid(void); 92 static void *set_shmat(void); 93 94 static long hugepages = 128; 95 static option_t options[] = { 96 {"s:", &sflag, &nr_opt}, 97 {NULL, NULL, NULL} 98 }; 99 100 struct test_case_t { 101 int cmd; 102 void (*func_test) (void); 103 void (*func_setup) (void); 104 } TC[] = { 105 { 106 IPC_STAT, func_stat, stat_setup}, { 107 IPC_STAT, func_stat, stat_setup}, { 108 IPC_SET, func_set, set_setup}, { 109 IPC_RMID, func_rmid, NULL} 110 }; 111 112 int main(int ac, char **av) 113 { 114 int lc, i; 115 116 tst_parse_opts(ac, av, options, NULL); 117 118 if (sflag) 119 hugepages = SAFE_STRTOL(NULL, nr_opt, 0, LONG_MAX); 120 121 setup(); 122 123 for (lc = 0; TEST_LOOPING(lc); lc++) { 124 tst_count = 0; 125 126 /* initialize stat_time */ 127 stat_time = FIRST; 128 129 /* 130 * Create a shared memory segment with read and write 131 * permissions. Do this here instead of in setup() 132 * so that looping (-i) will work correctly. 133 */ 134 shm_id_1 = shmget(shmkey, shm_size, 135 SHM_HUGETLB | IPC_CREAT | IPC_EXCL | SHM_RW); 136 if (shm_id_1 == -1) 137 tst_brkm(TBROK | TERRNO, cleanup, "shmget #main"); 138 139 for (i = 0; i < TST_TOTAL; i++) { 140 /* 141 * if needed, set up any required conditions by 142 * calling the appropriate setup function 143 */ 144 if (TC[i].func_setup != NULL) 145 (*TC[i].func_setup) (); 146 147 if (shmctl(shm_id_1, TC[i].cmd, &buf) == -1) { 148 tst_resm(TFAIL | TERRNO, "shmctl #main"); 149 continue; 150 } 151 (*TC[i].func_test) (); 152 } 153 } 154 cleanup(); 155 tst_exit(); 156 } 157 158 /* 159 * set_shmat() - Attach the shared memory and return the pointer. Use 160 * this seperate routine to avoid code duplication in 161 * stat_setup() below. 162 */ 163 void *set_shmat(void) 164 { 165 void *rval; 166 167 rval = shmat(shm_id_1, 0, 0); 168 if (rval == (void *)-1) 169 tst_brkm(TBROK | TERRNO, cleanup, "set shmat"); 170 171 return rval; 172 } 173 174 /* 175 * stat_setup() - Set up for the IPC_STAT command with shmctl(). 176 * Make things interesting by forking some children 177 * that will either attach or inherit the shared memory. 178 */ 179 static void stat_setup(void) 180 { 181 int i, rval; 182 void *test; 183 pid_t pid; 184 sigset_t newmask, oldmask; 185 186 /* 187 * The first time through, let the children attach the memory. 188 * The second time through, attach the memory first and let 189 * the children inherit the memory. 190 */ 191 192 if (stat_time == SECOND) { 193 /* 194 * use the global "set_shared" variable here so that 195 * it can be removed in the stat_func() routine. 196 */ 197 set_shared = set_shmat(); 198 } 199 200 /* 201 * Block SIGUSR1 before children pause for a signal 202 * Doing so to avoid the risk that the parent cleans up 203 * children by calling stat_cleanup() before children call 204 * call pause() so that children sleep forever(this is a 205 * side effect of the arbitrary usleep time below). 206 * In FIRST, children call shmat. If children sleep forever, 207 * those attached shm can't be released so some other shm 208 * tests will fail a lot. 209 */ 210 sigemptyset(&newmask); 211 sigaddset(&newmask, SIGUSR1); 212 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) 213 tst_brkm(TBROK | TERRNO, cleanup, "block SIGUSR1 error"); 214 215 for (i = 0; i < N_ATTACH; i++) { 216 switch (pid = fork()) { 217 case -1: 218 tst_brkm(TBROK | TERRNO, cleanup, "fork"); 219 case 0: 220 test = (stat_time == FIRST) ? set_shmat() : set_shared; 221 222 /* do an assignement for fun */ 223 *(int *)test = i; 224 225 /* 226 * sigsuspend until we get a signal from stat_cleanup() 227 * use sigsuspend instead of pause to avoid children 228 * infinite sleep without getting SIGUSR1 from parent 229 */ 230 rval = sigsuspend(&oldmask); 231 if (rval != -1) 232 tst_brkm(TBROK | TERRNO, cleanup, "sigsuspend"); 233 234 /* 235 * don't have to block SIGUSR1 any more, 236 * recover the mask 237 */ 238 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) 239 tst_brkm(TBROK | TERRNO, cleanup, 240 "child sigprocmask"); 241 242 /* now we're back - detach the memory and exit */ 243 if (shmdt(test) == -1) 244 tst_brkm(TBROK | TERRNO, cleanup, 245 "shmdt in stat_setup()"); 246 exit(0); 247 default: 248 /* save the child's pid for cleanup later */ 249 pid_arr[i] = pid; 250 } 251 } 252 253 /* parent doesn't have to block SIGUSR1, recover the mask */ 254 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) 255 tst_brkm(TBROK, cleanup, "parent sigprocmask"); 256 257 usleep(250000); 258 } 259 260 /* 261 * func_stat() - check the functionality of the IPC_STAT command with shmctl() 262 * by looking at the pid of the creator, the segement size, 263 * the number of attaches and the mode. 264 */ 265 static void func_stat(void) 266 { 267 pid_t pid; 268 269 /* check perm, pid, nattach and size */ 270 pid = getpid(); 271 272 if (buf.shm_cpid != pid) { 273 tst_resm(TFAIL, "creator pid is incorrect"); 274 goto fail; 275 } 276 277 if (buf.shm_segsz != shm_size) { 278 tst_resm(TFAIL, "segment size is incorrect"); 279 goto fail; 280 } 281 282 /* 283 * The first time through, only the children attach the memory, so 284 * the attaches equal N_ATTACH + stat_time (0). The second time 285 * through, the parent attaches the memory and the children inherit 286 * that memory so the attaches equal N_ATTACH + stat_time (1). 287 */ 288 if (buf.shm_nattch != N_ATTACH + stat_time) { 289 tst_resm(TFAIL, "# of attaches is incorrect - %lu", 290 (unsigned long)buf.shm_nattch); 291 goto fail; 292 } 293 294 /* use MODE_MASK to make sure we are comparing the last 9 bits */ 295 if ((buf.shm_perm.mode & MODE_MASK) != ((SHM_RW) & MODE_MASK)) { 296 tst_resm(TFAIL, "segment mode is incorrect"); 297 goto fail; 298 } 299 300 tst_resm(TPASS, "pid, size, # of attaches and mode are correct " 301 "- pass #%d", stat_time); 302 303 fail: 304 stat_cleanup(); 305 306 /* save the change time for use in the next test */ 307 save_time = buf.shm_ctime; 308 } 309 310 /* 311 * stat_cleanup() - signal the children to clean up after themselves and 312 * have the parent make dessert, er, um, make that remove 313 * the shared memory that is no longer needed. 314 */ 315 static void stat_cleanup(void) 316 { 317 int i; 318 319 /* wake up the childern so they can detach the memory and exit */ 320 for (i = 0; i < N_ATTACH; i++) 321 if (kill(pid_arr[i], SIGUSR1) == -1) 322 tst_brkm(TBROK | TERRNO, cleanup, "kill with SIGUSR1"); 323 324 /* remove the parent's shared memory the second time through */ 325 if (stat_time == SECOND) 326 if (shmdt(set_shared) == -1) 327 tst_resm(TBROK | TERRNO, "shmdt in stat_cleanup()"); 328 stat_time++; 329 } 330 331 /* 332 * set_setup() - set up for the IPC_SET command with shmctl() 333 */ 334 static void set_setup(void) 335 { 336 /* set up a new mode for the shared memory segment */ 337 buf.shm_perm.mode = SHM_RW | NEWMODE; 338 339 /* sleep for one second to get a different shm_ctime value */ 340 sleep(1); 341 } 342 343 /* 344 * func_set() - check the functionality of the IPC_SET command with shmctl() 345 */ 346 static void func_set(void) 347 { 348 /* first stat the shared memory to get the new data */ 349 if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) { 350 tst_resm(TBROK | TERRNO, "shmctl in func_set()"); 351 return; 352 } 353 354 if ((buf.shm_perm.mode & MODE_MASK) != ((SHM_RW | NEWMODE) & MODE_MASK)) { 355 tst_resm(TFAIL, "new mode is incorrect"); 356 return; 357 } 358 359 if (save_time >= buf.shm_ctime) { 360 tst_resm(TFAIL, "change time is incorrect"); 361 return; 362 } 363 364 tst_resm(TPASS, "new mode and change time are correct"); 365 } 366 367 /* 368 * func_rmid() - check the functionality of the IPC_RMID command with shmctl() 369 */ 370 static void func_rmid(void) 371 { 372 /* Do another shmctl() - we should get EINVAL */ 373 if (shmctl(shm_id_1, IPC_STAT, &buf) != -1) 374 tst_brkm(TBROK, cleanup, "shmctl in func_rmid() " 375 "succeeded unexpectedly"); 376 if (errno != EINVAL) 377 tst_resm(TFAIL | TERRNO, "shmctl in func_rmid() failed " 378 "unexpectedly - expect errno=EINVAL, got"); 379 else 380 tst_resm(TPASS, "shmctl in func_rmid() failed as expected, " 381 "shared memory appears to be removed"); 382 shm_id_1 = -1; 383 } 384 385 static void sighandler(int sig) 386 { 387 if (sig != SIGUSR1) 388 tst_resm(TFAIL, "received unexpected signal %d", sig); 389 } 390 391 void setup(void) 392 { 393 long hpage_size; 394 395 tst_require_root(); 396 check_hugepage(); 397 tst_sig(FORK, sighandler, cleanup); 398 tst_tmpdir(); 399 400 orig_hugepages = get_sys_tune("nr_hugepages"); 401 set_sys_tune("nr_hugepages", hugepages, 1); 402 hpage_size = read_meminfo("Hugepagesize:") * 1024; 403 404 shm_size = hpage_size * hugepages / 2; 405 update_shm_size(&shm_size); 406 shmkey = getipckey(cleanup); 407 408 TEST_PAUSE; 409 } 410 411 void cleanup(void) 412 { 413 rm_shm(shm_id_1); 414 415 set_sys_tune("nr_hugepages", orig_hugepages, 0); 416 417 tst_rmdir(); 418 } 419