1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
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 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/*
21 * NAME
22 *	hugeshmat01.c
23 *
24 * DESCRIPTION
25 *	hugeshmat01 - test that shmat() works correctly
26 *
27 * ALGORITHM
28 *	create a large shared memory resouce with read/write permissions
29 *	loop if that option was specified
30 *	call shmat() with the TEST() macro using three valid conditions
31 *	check the return code
32 *	  if failure, issue a FAIL message.
33 *	otherwise,
34 *	  if doing functionality testing
35 *		check for the correct conditions after the call
36 *		if correct,
37 *			issue a PASS message
38 *		otherwise
39 *			issue a FAIL message
40 *	call cleanup
41 *
42 * USAGE:  <for command-line>
43 *  hugeshmat01 [-c n] [-f] [-i n] [-I x] [-P x] [-t]
44 *     where,  -c n : Run n copies concurrently.
45 *             -f   : Turn off functionality Testing.
46 *	       -i n : Execute test n times.
47 *	       -I x : Execute test for x seconds.
48 *	       -P x : Pause for x seconds between iterations.
49 *	       -t   : Turn on syscall timing.
50 *
51 * HISTORY
52 *	03/2001 - Written by Wayne Boyer
53 *	04/2004 - Updated by Robbie Williamson
54 *
55 * RESTRICTIONS
56 *	none
57 */
58
59#include "hugetlb.h"
60#include "safe_macros.h"
61#include "mem.h"
62
63char *TCID = "hugeshmat01";
64int TST_TOTAL = 3;
65
66#define CASE0		10	/* values to write into the shared */
67#define CASE1		20	/* memory location.                */
68
69static size_t shm_size;
70static int shm_id_1 = -1;
71static void *addr;
72
73static long hugepages = 128;
74static option_t options[] = {
75	{"s:", &sflag, &nr_opt},
76	{NULL, NULL, NULL}
77};
78
79struct test_case_t {
80	int *shmid;
81	void *addr;
82	int flags;
83} TC[] = {
84	/* a straight forward read/write attach */
85	{
86	&shm_id_1, 0, 0},
87	    /*
88	     * an attach using non aligned memory
89	     * -1 will be replaced with an unaligned addr
90	     */
91	{
92	&shm_id_1, (void *)-1, SHM_RND},
93	    /* a read only attach */
94	{
95	&shm_id_1, 0, SHM_RDONLY}
96};
97
98static void check_functionality(int i);
99
100int main(int ac, char **av)
101{
102	int lc, i;
103
104	tst_parse_opts(ac, av, options, NULL);
105
106	if (sflag)
107		hugepages = SAFE_STRTOL(NULL, nr_opt, 0, LONG_MAX);
108
109	setup();
110
111	for (lc = 0; TEST_LOOPING(lc); lc++) {
112		tst_count = 0;
113
114		for (i = 0; i < TST_TOTAL; i++) {
115			addr = shmat(*(TC[i].shmid), TC[i].addr, TC[i].flags);
116			if (addr == (void *)-1) {
117				tst_brkm(TFAIL | TERRNO, cleanup, "shmat");
118			} else {
119				check_functionality(i);
120			}
121
122			/*
123			 * addr in TC[0] will be used to generate an unaligned
124			 * address for TC[1]
125			 */
126			if (i == 0 && addr != (void *)-1)
127				TC[1].addr = (void *)(((unsigned long)addr &
128						       ~(SHMLBA - 1)) + SHMLBA -
129						      1);
130			if (shmdt(addr) == -1)
131				tst_brkm(TBROK | TERRNO, cleanup, "shmdt");
132		}
133	}
134	cleanup();
135	tst_exit();
136}
137
138/*
139 * check_functionality - check various conditions to make sure they
140 *			 are correct.
141 */
142static void check_functionality(int i)
143{
144	void *orig_add;
145	int *shared;
146	struct shmid_ds buf;
147
148	shared = (int *)addr;
149
150	/* stat the shared memory ID */
151	if (shmctl(shm_id_1, IPC_STAT, &buf) == -1)
152		tst_brkm(TBROK | TERRNO, cleanup, "shmctl");
153
154	/* check the number of attaches */
155	if (buf.shm_nattch != 1) {
156		tst_resm(TFAIL, "# of attaches is incorrect");
157		return;
158	}
159
160	/* check the size of the segment */
161	if (buf.shm_segsz != shm_size) {
162		tst_resm(TFAIL, "segment size is incorrect");
163		return;
164	}
165
166	/* check for specific conditions depending on the type of attach */
167	switch (i) {
168	case 0:
169		/*
170		 * Check the functionality of the first call by simply
171		 * "writing" a value to the shared memory space.
172		 * If this fails the program will get a SIGSEGV, dump
173		 * core and exit.
174		 */
175		*shared = CASE0;
176		break;
177	case 1:
178		/*
179		 * Check the functionality of the second call by writing
180		 * a value to the shared memory space and then checking
181		 * that the original address given was rounded down as
182		 * specified in the man page.
183		 */
184		*shared = CASE1;
185		orig_add = addr + ((unsigned long)TC[i].addr % SHMLBA);
186		if (orig_add != TC[i].addr) {
187			tst_resm(TFAIL, "shared memory address is not "
188				 "correct");
189			return;
190		}
191		break;
192	case 2:
193		/*
194		 * This time the shared memory is read only.  Read the value
195		 * and check that it is equal to the value set in case #2,
196		 * because shared memory is persistent.
197		 */
198		if (*shared != CASE1) {
199			tst_resm(TFAIL, "shared memory value isn't correct");
200			return;
201		}
202		break;
203	}
204	tst_resm(TPASS, "conditions and functionality are correct");
205}
206
207void setup(void)
208{
209	long hpage_size;
210
211	tst_require_root();
212	check_hugepage();
213	tst_sig(NOFORK, DEF_HANDLER, cleanup);
214	tst_tmpdir();
215
216	orig_hugepages = get_sys_tune("nr_hugepages");
217	set_sys_tune("nr_hugepages", hugepages, 1);
218	hpage_size = read_meminfo("Hugepagesize:") * 1024;
219
220	shm_size = hpage_size * hugepages / 2;
221	update_shm_size(&shm_size);
222	shmkey = getipckey(cleanup);
223	shm_id_1 = shmget(shmkey++, shm_size,
224			  SHM_HUGETLB | SHM_RW | IPC_CREAT | IPC_EXCL);
225	if (shm_id_1 == -1)
226		tst_brkm(TBROK | TERRNO, cleanup, "shmget");
227
228	TEST_PAUSE;
229}
230
231void cleanup(void)
232{
233	rm_shm(shm_id_1);
234
235	set_sys_tune("nr_hugepages", orig_hugepages, 0);
236
237	tst_rmdir();
238}
239