Home | History | Annotate | Download | only in hugeshmctl
      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