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