• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
main(int ac,char ** av)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  */
set_shmat(void)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  */
stat_setup(void)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  */
func_stat(void)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  */
stat_cleanup(void)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  */
set_setup(void)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  */
func_set(void)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  */
func_rmid(void)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 
sighandler(int sig)385 static void sighandler(int sig)
386 {
387 	if (sig != SIGUSR1)
388 		tst_resm(TFAIL, "received unexpected signal %d", sig);
389 }
390 
setup(void)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 
cleanup(void)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