xref: /aosp_15_r20/external/ltp/testcases/kernel/mem/hugetlb/hugeshmctl/hugeshmctl01.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) International Business Machines  Corp., 2004
4  * Copyright (c) Linux Test Project, 2004-2023
5  * Original author: Wayne Boyer, modified by Robbie Williamson
6  */
7 
8 /*\
9  * [Description]
10  *
11  * Test the IPC_STAT, IPC_SET and IPC_RMID commands used by shmctl().
12  */
13 
14 #include <limits.h>
15 #include "hugetlb.h"
16 
17 #define N_ATTACH	4U
18 #define NEWMODE		0066
19 
20 static size_t shm_size;
21 static int shm_id_1 = -1;
22 static struct shmid_ds buf;
23 static time_t save_time;
24 static void *attach_to_parent;
25 
26 static void stat_setup_1(void);
27 static void stat_cleanup(void);
28 static void stat_setup_2(void);
29 static void set_setup(void);
30 static void func_stat(void);
31 static void func_set(void);
32 static void func_rmid(void);
33 static void *set_shmat(void);
34 
35 static struct tcase {
36 	int cmd;
37 	void (*func_test)(void);
38 	void (*func_setup)(void);
39 } tcases[] = {
40 	{IPC_STAT, func_stat, stat_setup_1},
41 	{IPC_STAT, func_stat, stat_setup_2},
42 	{IPC_SET,  func_set,  set_setup},
43 	{IPC_RMID, func_rmid, NULL}
44 };
45 
test_hugeshmctl(unsigned int i)46 static void test_hugeshmctl(unsigned int i)
47 {
48 	/*
49 	 * Create a shared memory segment with read and write
50 	 * permissions.  Do this here instead of in setup()
51 	 * so that looping (-i) will work correctly.
52 	 */
53 	if (i == 0) {
54 		shm_id_1 = shmget(shmkey, shm_size,
55 			SHM_HUGETLB | IPC_CREAT | IPC_EXCL | SHM_RW);
56 	}
57 
58 	if (shm_id_1 == -1)
59 		tst_brk(TBROK | TERRNO, "shmget #main");
60 
61 	if (tcases[i].func_setup != NULL)
62 		(*tcases[i].func_setup) ();
63 
64 	if (shmctl(shm_id_1, tcases[i].cmd, &buf) == -1) {
65 		tst_res(TFAIL | TERRNO, "shmctl #main");
66 		return;
67 	}
68 	(*tcases[i].func_test)();
69 }
70 
71 /*
72  * set_shmat() - Attach the shared memory and return the pointer.
73  */
set_shmat(void)74 static void *set_shmat(void)
75 {
76 	void *rval;
77 
78 	rval = shmat(shm_id_1, 0, 0);
79 	if (rval == (void *)-1)
80 		tst_brk(TBROK | TERRNO, "set shmat");
81 
82 	return rval;
83 }
84 
85 /*
86  * stat_setup_2() - Set up for the IPC_STAT command with shmctl().
87  *                Attach the shared memory to parent process and
88  *                some children will inherit the shared memory.
89  */
stat_setup_2(void)90 static void stat_setup_2(void)
91 {
92 	if (!attach_to_parent)
93 		attach_to_parent = set_shmat();
94 	stat_setup_1();
95 }
96 
97 /*
98  * stat_setup_1() - Set up for the IPC_STAT command with shmctl().
99  *                some children will inherit or attatch the shared memory.
100  *                It deponds on whther we attach the shared memory
101  *                to parent process.
102  */
stat_setup_1(void)103 static void stat_setup_1(void)
104 {
105 	unsigned int i;
106 	void *test;
107 	pid_t pid;
108 
109 	for (i = 0; i < N_ATTACH; i++) {
110 		switch (pid = SAFE_FORK()) {
111 		case 0:
112 			test = (attach_to_parent == NULL) ? set_shmat() : attach_to_parent;
113 			/* do an assignement for fun */
114 			*(int *)test = i;
115 
116 			TST_CHECKPOINT_WAKE(0);
117 
118 			TST_CHECKPOINT_WAIT(1);
119 
120 			/* now we're back - detach the memory and exit */
121 			if (shmdt(test) == -1)
122 				tst_brk(TBROK | TERRNO,
123 					 "shmdt in this function broke");
124 
125 			exit(0);
126 		default:
127 			TST_CHECKPOINT_WAIT(0);
128 		}
129 	}
130 }
131 
132 
133 /*
134  * func_stat() - check the functionality of the IPC_STAT command with shmctl()
135  *		 by looking at the pid of the creator, the segement size,
136  *		 the number of attaches and the mode.
137  */
func_stat(void)138 static void func_stat(void)
139 {
140 	pid_t pid;
141 	unsigned int num;
142 
143 	/* check perm, pid, nattach and size */
144 	pid = getpid();
145 
146 	if (buf.shm_cpid != pid) {
147 		tst_res(TFAIL, "creator pid is incorrect");
148 		goto fail;
149 	}
150 
151 	if (buf.shm_segsz != shm_size) {
152 		tst_res(TFAIL, "segment size is incorrect");
153 		goto fail;
154 	}
155 
156 	/*
157 	 * The first case, only the children attach the memory, so
158 	 * the attaches equal N_ATTACH. The second case, the parent
159 	 * attaches the memory and the children inherit that memory
160 	 * so the attaches equal N_ATTACH + 1.
161 	 */
162 	num = (attach_to_parent == NULL) ? 0 : 1;
163 	if (buf.shm_nattch != N_ATTACH + num) {
164 		tst_res(TFAIL, "# of attaches is incorrect - %lu",
165 			 (unsigned long)buf.shm_nattch);
166 		goto fail;
167 	}
168 
169 	/* use MODE_MASK to make sure we are comparing the last 9 bits */
170 	if ((buf.shm_perm.mode & MODE_MASK) != ((SHM_RW) & MODE_MASK)) {
171 		tst_res(TFAIL, "segment mode is incorrect");
172 		goto fail;
173 	}
174 
175 	tst_res(TPASS, "pid, size, # of attaches and mode are correct "
176 		 "- pass #%d", num);
177 
178 fail:
179 	stat_cleanup();
180 
181 	/* save the change time for use in the next test */
182 	save_time = buf.shm_ctime;
183 }
184 
185 /*
186  * stat_cleanup() - signal the children to clean up after themselves and
187  *		    have the parent make dessert, er, um, make that remove
188  *		    the shared memory that is no longer needed.
189  */
stat_cleanup(void)190 static void stat_cleanup(void)
191 {
192 	unsigned int i;
193 	int status;
194 
195 	/* wake up the childern so they can detach the memory and exit */
196 	TST_CHECKPOINT_WAKE2(1, N_ATTACH);
197 
198 	for (i = 0; i < N_ATTACH; i++)
199 		SAFE_WAIT(&status);
200 
201 	/* remove the parent's shared memory if we set*/
202 	if (attach_to_parent) {
203 		if (shmdt(attach_to_parent) == -1)
204 			tst_res(TFAIL | TERRNO,
205 				"shmdt in this function failed");
206 		attach_to_parent = NULL;
207 	}
208 }
209 
210 /*
211  * set_setup() - set up for the IPC_SET command with shmctl()
212  */
set_setup(void)213 static void set_setup(void)
214 {
215 	/* set up a new mode for the shared memory segment */
216 	buf.shm_perm.mode = SHM_RW | NEWMODE;
217 
218 	/* sleep for one second to get a different shm_ctime value */
219 	sleep(1);
220 }
221 
222 /*
223  * func_set() - check the functionality of the IPC_SET command with shmctl()
224  */
func_set(void)225 static void func_set(void)
226 {
227 	/* first stat the shared memory to get the new data */
228 	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1) {
229 		tst_res(TFAIL | TERRNO, "shmctl in this function failed");
230 		return;
231 	}
232 
233 	if ((buf.shm_perm.mode & MODE_MASK) != ((SHM_RW | NEWMODE) & MODE_MASK)) {
234 		tst_res(TFAIL, "new mode is incorrect");
235 		return;
236 	}
237 
238 	if (save_time >= buf.shm_ctime) {
239 		tst_res(TFAIL, "change time is incorrect");
240 		return;
241 	}
242 
243 	tst_res(TPASS, "new mode and change time are correct");
244 }
245 
246 /*
247  * func_rmid() - check the functionality of the IPC_RMID command with shmctl()
248  */
func_rmid(void)249 static void func_rmid(void)
250 {
251 	/* Do another shmctl() - we should get EINVAL */
252 	if (shmctl(shm_id_1, IPC_STAT, &buf) != -1)
253 		tst_brk(TBROK, "shmctl in this function "
254 			 "succeeded unexpectedly");
255 	if (errno != EINVAL)
256 		tst_res(TFAIL | TERRNO, "shmctl in this function failed "
257 			 "unexpectedly - expect errno=EINVAL, got");
258 	else
259 		tst_res(TPASS, "shmctl in this function failed as expected, "
260 			 "shared memory appears to be removed");
261 	shm_id_1 = -1;
262 }
263 
setup(void)264 static void setup(void)
265 {
266 	long hpage_size;
267 
268 	if (tst_hugepages == 0)
269 		tst_brk(TCONF, "No enough hugepages for testing.");
270 
271 	hpage_size = SAFE_READ_MEMINFO("Hugepagesize:") * 1024;
272 
273 	shm_size = hpage_size * tst_hugepages / 2;
274 	update_shm_size(&shm_size);
275 	shmkey = getipckey();
276 }
277 
cleanup(void)278 static void cleanup(void)
279 {
280 	rm_shm(shm_id_1);
281 }
282 
283 static struct tst_test test = {
284 	.tcnt = ARRAY_SIZE(tcases),
285 	.needs_root = 1,
286 	.forks_child = 1,
287 	.options = (struct tst_option[]) {
288 		{"s:", &nr_opt, "Set the number of the been allocated hugepages"},
289 		{}
290 	},
291 	.setup = setup,
292 	.cleanup = cleanup,
293 	.test = test_hugeshmctl,
294 	.needs_checkpoints = 1,
295 	.hugepages = {128, TST_REQUEST},
296 };
297