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