1 | /*
|
---|
2 | * libhugetlbfs - Easy use of Linux hugepages
|
---|
3 | * Copyright (C) 2008 David Gibson, IBM Corporation.
|
---|
4 | *
|
---|
5 | * This library is free software; you can redistribute it and/or
|
---|
6 | * modify it under the terms of the GNU Lesser General Public License
|
---|
7 | * as published by the Free Software Foundation; either version 2.1 of
|
---|
8 | * the License, or (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This library is distributed in the hope that it will be useful, but
|
---|
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
13 | * Lesser General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU Lesser General Public
|
---|
16 | * License along with this library; if not, write to the Free Software
|
---|
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
---|
18 | */
|
---|
19 | #define _GNU_SOURCE
|
---|
20 |
|
---|
21 | #include <sys/types.h>
|
---|
22 | //#include <sys/shm.h>
|
---|
23 | #include <sys/wait.h>
|
---|
24 | #include <sys/mman.h>
|
---|
25 | #include <errno.h>
|
---|
26 | #include <setjmp.h>
|
---|
27 | #include <stdio.h>
|
---|
28 | #include <stdlib.h>
|
---|
29 | #include <string.h>
|
---|
30 | #include <unistd.h>
|
---|
31 | #include <sys/stat.h>
|
---|
32 | #include <fcntl.h>
|
---|
33 | #include <unistd.h>
|
---|
34 | //#include <hugetlbfs.h>
|
---|
35 | #include "hugetests.h"
|
---|
36 |
|
---|
37 | /*
|
---|
38 | * Test rationale:
|
---|
39 | *
|
---|
40 | * This checks copy-on-write semantics, specifically the semantics of
|
---|
41 | * a MAP_PRIVATE mapping across a fork(). Some versions of the
|
---|
42 | * powerpc kernel had a bug in huge_ptep_set_wrprotect() which would
|
---|
43 | * fail to flush the hash table after setting the write protect bit in
|
---|
44 | * the parent's page tables, thus allowing the parent to pollute the
|
---|
45 | * child's mapping.
|
---|
46 | */
|
---|
47 |
|
---|
48 | #define RANDOM_CONSTANT 0x1234ABCD
|
---|
49 | #define OTHER_CONSTANT 0xfeef5678
|
---|
50 |
|
---|
51 | /*
|
---|
52 | * The parent uses this to check if the child terminated badly.
|
---|
53 | */
|
---|
54 | static void sigchld_handler(int signum, siginfo_t *si, void *uc)
|
---|
55 | {
|
---|
56 | if (WEXITSTATUS(si->si_status) != 0)
|
---|
57 | FAIL("Child failed: %d", WEXITSTATUS(si->si_status));
|
---|
58 | if (WIFSIGNALED(si->si_status))
|
---|
59 | FAIL("Child recived signal %s",
|
---|
60 | strsignal(WTERMSIG(si->si_status)));
|
---|
61 | }
|
---|
62 |
|
---|
63 | static sigjmp_buf sig_escape;
|
---|
64 | static void *sig_expected = MAP_FAILED;
|
---|
65 |
|
---|
66 | static void sig_handler(int signum, siginfo_t *si, void *uc)
|
---|
67 | {
|
---|
68 | if (signum == SIGSEGV) {
|
---|
69 | verbose_printf("SIGSEGV at %p (sig_expected=%p)\n", si->si_addr,
|
---|
70 | sig_expected);
|
---|
71 | if (si->si_addr == sig_expected) {
|
---|
72 | siglongjmp(sig_escape, 1);
|
---|
73 | }
|
---|
74 | FAIL("SIGSEGV somewhere unexpected");
|
---|
75 | }
|
---|
76 | FAIL("Unexpected signal %s", strsignal(signum));
|
---|
77 | }
|
---|
78 |
|
---|
79 | static int test_read(void *p)
|
---|
80 | {
|
---|
81 | volatile unsigned long *pl = p;
|
---|
82 | unsigned long x;
|
---|
83 |
|
---|
84 | if (sigsetjmp(sig_escape, 1)) {
|
---|
85 | /* We got a SEGV */
|
---|
86 | sig_expected = MAP_FAILED;
|
---|
87 | return -1;
|
---|
88 | }
|
---|
89 |
|
---|
90 | sig_expected = p;
|
---|
91 | barrier();
|
---|
92 | x = *pl;
|
---|
93 | verbose_printf("Read back %lu\n", x);
|
---|
94 | barrier();
|
---|
95 | sig_expected = MAP_FAILED;
|
---|
96 | /*
|
---|
97 | * gcc 5 complains about x not ever being used, the following
|
---|
98 | * statement is solely here to shut it up
|
---|
99 | */
|
---|
100 | pl = (unsigned long *)x;
|
---|
101 |
|
---|
102 | return 0;
|
---|
103 | }
|
---|
104 |
|
---|
105 | static int test_write(void *p, unsigned long val)
|
---|
106 | {
|
---|
107 | volatile unsigned long *pl = p;
|
---|
108 | unsigned long x;
|
---|
109 |
|
---|
110 | if (sigsetjmp(sig_escape, 1)) {
|
---|
111 | /* We got a SEGV */
|
---|
112 | sig_expected = MAP_FAILED;
|
---|
113 | return -1;
|
---|
114 | }
|
---|
115 |
|
---|
116 | sig_expected = p;
|
---|
117 | barrier();
|
---|
118 | *pl = val;
|
---|
119 | x = *pl;
|
---|
120 | barrier();
|
---|
121 | sig_expected = MAP_FAILED;
|
---|
122 |
|
---|
123 | return (x != val);
|
---|
124 | }
|
---|
125 |
|
---|
126 |
|
---|
127 | int main(int argc, char ** argv)
|
---|
128 | {
|
---|
129 | int fd, ret, status;
|
---|
130 | void *syncarea;
|
---|
131 | volatile unsigned int *p;
|
---|
132 | volatile unsigned int *trigger, *child_readback;
|
---|
133 | unsigned int parent_readback;
|
---|
134 | long hpage_size;
|
---|
135 | pid_t pid;
|
---|
136 | int prot;
|
---|
137 | int err;
|
---|
138 | struct sigaction sa = {
|
---|
139 | .sa_sigaction = sigchld_handler,
|
---|
140 | .sa_flags = SA_SIGINFO,
|
---|
141 | };
|
---|
142 |
|
---|
143 | test_init(argc, argv);
|
---|
144 |
|
---|
145 | // check_free_huge_pages(2);
|
---|
146 |
|
---|
147 | if (argc != 1)
|
---|
148 | CONFIG("Usage: fork-cow\n");
|
---|
149 |
|
---|
150 | /* Get a shared normal page for synchronization */
|
---|
151 | verbose_printf("Mapping synchronization area..");
|
---|
152 | syncarea = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE,
|
---|
153 | MAP_SHARED|MAP_ANONYMOUS, -1, 0);
|
---|
154 | if (syncarea == MAP_FAILED)
|
---|
155 | FAIL("mmap() sync area: %s", strerror(errno));
|
---|
156 | verbose_printf("done\n");
|
---|
157 |
|
---|
158 | trigger = syncarea;
|
---|
159 | *trigger = 0;
|
---|
160 |
|
---|
161 | child_readback = trigger + 1;
|
---|
162 | *child_readback = 0;
|
---|
163 |
|
---|
164 | hpage_size = check_hugepagesize();
|
---|
165 |
|
---|
166 | fd = hugetlbfs_unlinked_fd(hpage_size*8);
|
---|
167 | if (fd < 0)
|
---|
168 | CONFIG("hugetlbfs_unlinked_fd() failed\n");
|
---|
169 |
|
---|
170 | verbose_printf("Mapping hugepage area...");
|
---|
171 | p = mmap(NULL, hpage_size*8, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
|
---|
172 | if (p == MAP_FAILED)
|
---|
173 | FAIL("mmap(): %s", strerror(errno));
|
---|
174 | verbose_printf("mapped at %p\n", p);
|
---|
175 |
|
---|
176 | /* Touch the page for write in parent */
|
---|
177 | verbose_printf("Parent writes pre-fork...");
|
---|
178 | *p = RANDOM_CONSTANT;
|
---|
179 | verbose_printf("%x\n", RANDOM_CONSTANT);
|
---|
180 |
|
---|
181 | prot = 0;
|
---|
182 | verbose_printf("mprotect()ing to prot=%x\n", prot);
|
---|
183 | err = mprotect(p, hpage_size, prot);
|
---|
184 | if (err != 0)
|
---|
185 | FAIL("mprotect(prot=%x): %s", prot,
|
---|
186 | strerror(errno));
|
---|
187 |
|
---|
188 |
|
---|
189 | ret = sigaction(SIGCHLD, &sa, NULL);
|
---|
190 | if (ret)
|
---|
191 | FAIL("sigaction(): %s", strerror(errno));
|
---|
192 |
|
---|
193 | struct sigaction sa2 = {
|
---|
194 | .sa_sigaction = sig_handler,
|
---|
195 | .sa_flags = SA_SIGINFO,
|
---|
196 | };
|
---|
197 |
|
---|
198 | err = sigaction(SIGSEGV, &sa2, NULL);
|
---|
199 | if (err)
|
---|
200 | FAIL("Can't install SIGSEGV handler: %s", strerror(errno));
|
---|
201 |
|
---|
202 |
|
---|
203 | if ((pid = fork()) < 0)
|
---|
204 | FAIL("fork(): %s", strerror(errno));
|
---|
205 |
|
---|
206 | if (pid != 0) {
|
---|
207 | int r;
|
---|
208 | /* Parent */
|
---|
209 | verbose_printf("Parent reading..");
|
---|
210 | r = test_read(p);
|
---|
211 | verbose_printf("%d\n", r);
|
---|
212 |
|
---|
213 | *trigger = 1;
|
---|
214 |
|
---|
215 | while (*trigger != 2)
|
---|
216 | ;
|
---|
217 |
|
---|
218 | if (prot & PROT_READ) {
|
---|
219 | if (r != 0)
|
---|
220 | FAIL("Parent read failed on mmap(prot=%x)", prot);
|
---|
221 | } else {
|
---|
222 | if (r != -1)
|
---|
223 | FAIL("Parent read succeeded on mmap(prot=%x)", prot);
|
---|
224 | }
|
---|
225 | } else {
|
---|
226 | int r;
|
---|
227 | /* Child */
|
---|
228 | verbose_printf("Child starts..\n");
|
---|
229 | while (*trigger != 1)
|
---|
230 | ;
|
---|
231 |
|
---|
232 | verbose_printf("Child reading..");
|
---|
233 | r = test_read(p);
|
---|
234 | verbose_printf("%d\n", r);
|
---|
235 |
|
---|
236 | *trigger = 2;
|
---|
237 |
|
---|
238 | if (prot & PROT_READ) {
|
---|
239 | if (r != 0)
|
---|
240 | FAIL("Child read failed on mmap(prot=%x)", prot);
|
---|
241 | } else {
|
---|
242 | if (r != -1)
|
---|
243 | FAIL("Child read succeeded on mmap(prot=%x)", prot);
|
---|
244 | }
|
---|
245 |
|
---|
246 | verbose_printf("Child exits...\n");
|
---|
247 | exit(0);
|
---|
248 | }
|
---|
249 |
|
---|
250 | ret = waitpid(pid, &status, 0);
|
---|
251 | if (ret < 0)
|
---|
252 | FAIL("waitpid(): %s", strerror(errno));
|
---|
253 |
|
---|
254 | PASS();
|
---|
255 | }
|
---|