• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) International Business Machines  Corp., 2004
3  * Copyright (c) Linux Test Project, 2004-2017
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 /*
17  * DESCRIPTION
18  *	hugeshmctl01 - test the IPC_STAT, IPC_SET and IPC_RMID commands as
19  *		   they are used with shmctl()
20  *
21  * ALGORITHM
22  *	loop if that option was specified
23  *	create a large shared memory segment with read and write permission
24  *	set up any test case specific conditions
25  *	call shmctl() using the TEST macro
26  *	check the return code
27  *	  if failure, issue a FAIL message.
28  *	otherwise,
29  *	  if doing functionality testing
30  *		call the correct test function
31  *		if the conditions are correct,
32  *			issue a PASS message
33  *		otherwise
34  *			issue a FAIL message
35  *	  otherwise
36  *	    issue a PASS message
37  *	call cleanup
38  *
39  * HISTORY
40  *	03/2001 - Written by Wayne Boyer
41  *	04/2004 - Updated by Robbie Williamson
42  */
43 
44 #include <limits.h>
45 #include "hugetlb.h"
46 
47 #define FIRST		0
48 #define SECOND		1
49 #define N_ATTACH	4U
50 #define NEWMODE		0066
51 
52 static size_t shm_size;
53 static int shm_id_1 = -1;
54 static struct shmid_ds buf;
55 static time_t save_time;
56 static int stat_time;
57 static void *set_shared;
58 
59 static void stat_setup(void);
60 static void stat_cleanup(void);
61 static void set_setup(void);
62 static void func_stat(void);
63 static void func_set(void);
64 static void func_rmid(void);
65 static void *set_shmat(void);
66 
67 static long hugepages = 128;
68 
69 static struct tst_option options[] = {
70 	{"s:", &nr_opt, "-s   num  Set the number of the been allocated hugepages"},
71 	{NULL, NULL, NULL}
72 };
73 
74 struct tcase {
75 	int cmd;
76 	void (*func_test) (void);
77 	void (*func_setup) (void);
78 } tcases[] = {
79 	{IPC_STAT, func_stat, stat_setup},
80 	{IPC_STAT, func_stat, stat_setup},
81 	{IPC_SET,  func_set,  set_setup},
82 	{IPC_RMID, func_rmid, NULL}
83 };
84 
test_hugeshmctl(void)85 static void test_hugeshmctl(void)
86 {
87 	unsigned int i;
88 
89 	/* initialize stat_time */
90 	stat_time = FIRST;
91 
92 	/*
93 	 * Create a shared memory segment with read and write
94 	 * permissions.  Do this here instead of in setup()
95 	 * so that looping (-i) will work correctly.
96 	 */
97 	shm_id_1 = shmget(shmkey, shm_size,
98 			SHM_HUGETLB | IPC_CREAT | IPC_EXCL | SHM_RW);
99 	if (shm_id_1 == -1)
100 		tst_brk(TBROK | TERRNO, "shmget #main");
101 
102 	for (i = 0; i < ARRAY_SIZE(tcases); i++) {
103 		/*
104 		 * if needed, set up any required conditions by
105 		 * calling the appropriate setup function
106 		 */
107 		if (tcases[i].func_setup != NULL)
108 			(*tcases[i].func_setup) ();
109 
110 		if (shmctl(shm_id_1, tcases[i].cmd, &buf) == -1) {
111 			tst_res(TFAIL | TERRNO, "shmctl #main");
112 			continue;
113 		}
114 		(*tcases[i].func_test) ();
115 	}
116 }
117 
118 /*
119  * set_shmat() - Attach the shared memory and return the pointer.  Use
120  *		 this seperate routine to avoid code duplication in
121  *		 stat_setup() below.
122  */
set_shmat(void)123 void *set_shmat(void)
124 {
125 	void *rval;
126 
127 	rval = shmat(shm_id_1, 0, 0);
128 	if (rval == (void *)-1)
129 		tst_brk(TBROK | TERRNO, "set shmat");
130 
131 	return rval;
132 }
133 
134 /*
135  * stat_setup() - Set up for the IPC_STAT command with shmctl().
136  *		  Make things interesting by forking some children
137  *		  that will either attach or inherit the shared memory.
138  */
stat_setup(void)139 static void stat_setup(void)
140 {
141 	unsigned int i;
142 	void *test;
143 	pid_t pid;
144 
145 	/*
146 	 * The first time through, let the children attach the memory.
147 	 * The second time through, attach the memory first and let
148 	 * the children inherit the memory.
149 	 */
150 
151 	if (stat_time == SECOND) {
152 		/*
153 		 * use the global "set_shared" variable here so that
154 		 * it can be removed in the stat_func() routine.
155 		 */
156 		set_shared = set_shmat();
157 	}
158 
159 	for (i = 0; i < N_ATTACH; i++) {
160 		switch (pid = SAFE_FORK()) {
161 		case 0:
162 			test = (stat_time == FIRST) ? set_shmat() : set_shared;
163 
164 			/* do an assignement for fun */
165 			*(int *)test = i;
166 
167 			TST_CHECKPOINT_WAKE(0);
168 
169 			TST_CHECKPOINT_WAIT(1);
170 
171 			/* now we're back - detach the memory and exit */
172 			if (shmdt(test) == -1)
173 				tst_brk(TBROK | TERRNO,
174 					 "shmdt in stat_setup()");
175 
176 			exit(0);
177 		default:
178 			TST_CHECKPOINT_WAIT(0);
179 		}
180 	}
181 }
182 
183 /*
184  * func_stat() - check the functionality of the IPC_STAT command with shmctl()
185  *		 by looking at the pid of the creator, the segement size,
186  *		 the number of attaches and the mode.
187  */
func_stat(void)188 static void func_stat(void)
189 {
190 	pid_t pid;
191 
192 	/* check perm, pid, nattach and size */
193 	pid = getpid();
194 
195 	if (buf.shm_cpid != pid) {
196 		tst_res(TFAIL, "creator pid is incorrect");
197 		goto fail;
198 	}
199 
200 	if (buf.shm_segsz != shm_size) {
201 		tst_res(TFAIL, "segment size is incorrect");
202 		goto fail;
203 	}
204 
205 	/*
206 	 * The first time through, only the children attach the memory, so
207 	 * the attaches equal N_ATTACH + stat_time (0).  The second time
208 	 * through, the parent attaches the memory and the children inherit
209 	 * that memory so the attaches equal N_ATTACH + stat_time (1).
210 	 */
211 	if (buf.shm_nattch != N_ATTACH + stat_time) {
212 		tst_res(TFAIL, "# of attaches is incorrect - %lu",
213 			 (unsigned long)buf.shm_nattch);
214 		goto fail;
215 	}
216 
217 	/* use MODE_MASK to make sure we are comparing the last 9 bits */
218 	if ((buf.shm_perm.mode & MODE_MASK) != ((SHM_RW) & MODE_MASK)) {
219 		tst_res(TFAIL, "segment mode is incorrect");
220 		goto fail;
221 	}
222 
223 	tst_res(TPASS, "pid, size, # of attaches and mode are correct "
224 		 "- pass #%d", stat_time);
225 
226 fail:
227 	stat_cleanup();
228 
229 	/* save the change time for use in the next test */
230 	save_time = buf.shm_ctime;
231 }
232 
233 /*
234  * stat_cleanup() - signal the children to clean up after themselves and
235  *		    have the parent make dessert, er, um, make that remove
236  *		    the shared memory that is no longer needed.
237  */
stat_cleanup(void)238 static void stat_cleanup(void)
239 {
240 	unsigned int i;
241 	int status;
242 
243 	/* wake up the childern so they can detach the memory and exit */
244 	TST_CHECKPOINT_WAKE2(1, N_ATTACH);
245 
246 	for (i = 0; i < N_ATTACH; i++)
247 		SAFE_WAIT(&status);
248 
249 	/* remove the parent's shared memory the second time through */
250 	if (stat_time == SECOND)
251 		if (shmdt(set_shared) == -1)
252 			tst_res(TBROK | TERRNO, "shmdt in stat_cleanup()");
253 	stat_time++;
254 }
255 
256 /*
257  * set_setup() - set up for the IPC_SET command with shmctl()
258  */
set_setup(void)259 static void set_setup(void)
260 {
261 	/* set up a new mode for the shared memory segment */
262 	buf.shm_perm.mode = SHM_RW | NEWMODE;
263 
264 	/* sleep for one second to get a different shm_ctime value */
265 	sleep(1);
266 }
267 
268 /*
269  * func_set() - check the functionality of the IPC_SET command with shmctl()
270  */
func_set(void)271 static void func_set(void)
272 {
273 	/* first stat the shared memory to get the new data */
274 	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
275 		tst_res(TBROK | TERRNO, "shmctl in func_set()");
276 		return;
277 	}
278 
279 	if ((buf.shm_perm.mode & MODE_MASK) != ((SHM_RW | NEWMODE) & MODE_MASK)) {
280 		tst_res(TFAIL, "new mode is incorrect");
281 		return;
282 	}
283 
284 	if (save_time >= buf.shm_ctime) {
285 		tst_res(TFAIL, "change time is incorrect");
286 		return;
287 	}
288 
289 	tst_res(TPASS, "new mode and change time are correct");
290 }
291 
292 /*
293  * func_rmid() - check the functionality of the IPC_RMID command with shmctl()
294  */
func_rmid(void)295 static void func_rmid(void)
296 {
297 	/* Do another shmctl() - we should get EINVAL */
298 	if (shmctl(shm_id_1, IPC_STAT, &buf) != -1)
299 		tst_brk(TBROK, "shmctl in func_rmid() "
300 			 "succeeded unexpectedly");
301 	if (errno != EINVAL)
302 		tst_res(TFAIL | TERRNO, "shmctl in func_rmid() failed "
303 			 "unexpectedly - expect errno=EINVAL, got");
304 	else
305 		tst_res(TPASS, "shmctl in func_rmid() failed as expected, "
306 			 "shared memory appears to be removed");
307 	shm_id_1 = -1;
308 }
309 
setup(void)310 void setup(void)
311 {
312 	long hpage_size;
313 
314 	save_nr_hugepages();
315 	if (nr_opt)
316 		hugepages = SAFE_STRTOL(nr_opt, 0, LONG_MAX);
317 
318 	set_sys_tune("nr_hugepages", hugepages, 1);
319 	hpage_size = SAFE_READ_MEMINFO("Hugepagesize:") * 1024;
320 
321 	shm_size = hpage_size * hugepages / 2;
322 	update_shm_size(&shm_size);
323 	shmkey = getipckey();
324 }
325 
cleanup(void)326 void cleanup(void)
327 {
328 	rm_shm(shm_id_1);
329 	restore_nr_hugepages();
330 }
331 
332 static struct tst_test test = {
333 	.needs_root = 1,
334 	.forks_child = 1,
335 	.needs_tmpdir = 1,
336 	.options = options,
337 	.setup = setup,
338 	.cleanup = cleanup,
339 	.test_all = test_hugeshmctl,
340 	.needs_checkpoints = 1,
341 };
342