@@ -0,0 +1,301 @@
1//
2// tests for superpages.
3// each test prints "supertest: <name>: OK" or "... FAIL".
4//
5
6#include "kernel/types.h"
7#include "kernel/riscv.h"
8#include "user/user.h"
9
10#define MB (1024 * 1024)
11#define BATCH (64 * PGSIZE) // fragment: 64 pages per turn
12
13int failed;
14char *base; // 4 megabytes of heap, 2-megabyte aligned 15char *heap0; // the break before the test grew the heap
16
17void
18result(char *name, int ok) 19{
20 printf("supertest: %s: %s\n", name, ok ? "OK" : "FAIL"); 21 if (!ok)
22 failed = 1;
23}
24
25// wait for one child; return 1 if it exited with status 0.
26int
27childok(void)
28{
29 int xstatus;
30
31 if (wait(&xstatus) < 0) 32 return 0;
33 return xstatus == 0;
34}
35
36// count free pages by allocating them all with sbrk, in a child:
37// the page-table pages that the count needs are freed when it
38// exits, so they cannot pin 2-megabyte chunks.
39int
41{
42 int n = 0;
44
46 return -1;
49 n++;
51 }
53 return n;
54}
55
56// the byte that offset i of the region should hold.
57char
58pattern(uint64 i)
59{
60 return (i * 131 + (i >> 12)) & 0xff;
61}
62
63// do bytes [from, to) of the region hold the pattern?
64int
65checkpattern(uint64 from, uint64 to)
66{
67 for (uint64 i = from; i < to; i++)
68 if (base[i] != pattern(i)) 69 return 0;
70 return 1;
71}
72
73// grow the heap to a 2-megabyte boundary, then by 4 megabytes:
74// two superpages.
75void
76supertest(void)
77{
78 int free0;
79 char *pad;
80
82 pad = sbrk(SUPERPGROUNDUP((uint64)heap0) - (uint64)heap0); 83 free0 = freesuper();
86 printf("supertest: sbrk failed\n"); 88 }
89 printf("supertest: heap at %p, superpages at %p and %p\n", heap0, base, 91 result("superpages",
92 pglevel(base) == 1 && pglevel(base + 2 * MB) == 1 && 93 pglevel(base + 4 * MB - 1) == 1 && pglevel(heap0) == 0 && 94 freesuper() == free0 - 2);
95}
96
97// read and write every byte of the two superpages.
98void
99bytetest(void)
100{
101 int ok = 1;
102
103 for (uint64 i = 0; i < 4 * MB; i++)
105 ok = 0;
106 for (uint64 i = 0; i < 4 * MB; i++)
107 base[i] = pattern(i); 108 result("every byte", ok && checkpattern(0, 4 * MB));
109}
110
111// the kernel reads and writes superpages too (copyin, copyout):
112// send 400 bytes that straddle a page boundary in the second
113// superpage through a pipe, and read them back into the first
114// superpage, across a page boundary there too.
115void
116syscalltest(void)
117{
118 uint64 from = 2 * MB + 3 * PGSIZE - 200; 119 uint64 to = 1 * MB + 5 * PGSIZE - 100; 120 int fds[2], ok;
121
123 result("system calls", 0);
124 return;
125 }
126 ok = write(fds[1], base + from, 400) == 400 && 127 read(fds[0], base + to, 400) == 400; 128 for (int i = 0; i < 400; i++)
129 if (base[to + i] != pattern(from + i)) 130 ok = 0;
131 for (uint64 i = to; i < to + 400; i++) // put the pattern back
132 base[i] = pattern(i); 135 result("system calls", ok);
136}
137
138// fork: the child must see the same bytes in pages of the given
139// level, and its writes must not reach the parent.
140int
141forkcheck(int level)
142{
144
146 printf("supertest: fork failed\n"); 147 return 0;
148 }
150 if (pglevel(base) != level || pglevel(base + 2 * MB) != level) 152 if (!checkpattern(0, 4 * MB))
154 for (uint64 i = 0; i < 4 * MB; i += 997)
155 base[i] = ~pattern(i); 157 }
158 int ok = childok();
159 return ok && checkpattern(0, 4 * MB);
160}
161
162// fill memory so that every 2-megabyte chunk holds pages of two
163// processes, then let one of them exit: half of memory is free,
164// but not one superpage. Returns the pid of the one left (it
165// exits when its pipe closes), or -1.
166int
167fragment(int *keep)
168{
169 int cmd[2][2], ack[2], pid[2]; 170 char c;
171
173 return -1;
174 for (int k = 0; k < 2; k++) {
176 return -1;
178 sbrk(-(sbrk(0) - heap0)); // give back the inherited superpages 180 while (read(cmd[k][0], &c, 1) == 1) { 181 if (c == 'q')
184 write(ack[1], &c, 1); 185 }
187 }
189 }
190 // take turns until no superpage is left.
191 for (int k = 0; freesuper() > 0; k = 1 - k) {
193 if (read(ack[0], &c, 1) != 1 || c != 'y') 194 break;
195 }
203}
204
205// with no superpage free, fork must still copy a superpage, into
206// 512 pages.
207void
208demotefork(void)
209{
211
212 if ((pid = fragment(&keep)) < 0) { 213 result("fork without free superpages", 0);
214 return;
215 }
216 int n = freesuper();
218 printf("supertest: fragmented: %d free superpages, %d free pages\n", n, 219 npages);
220 int ok = forkcheck(0);
221 close(keep); // the other process exits 223 result("fork without free superpages", n == 0 && ok);
224}
225
226// shrink into the middle of the second superpage: it must be split,
227// and the bytes below the new break kept.
228void
229shrinktest(void)
230{
231 uint64 cut = 3 * MB - 2 * PGSIZE; // new end, inside superpage 2 232 int ok;
233
235 result("shrink splits", 0);
236 return;
237 }
238 ok = pglevel(base) == 1 && pglevel(base + 2 * MB) == 0 && 239 pglevel(base + cut - 1) == 0 && pglevel(base + cut) == -1 && 240 checkpattern(0, cut);
241 // grow back: the new pages must be zero.
243 ok = 0;
244 for (uint64 i = cut; ok && i < 4 * MB; i++)
246 ok = 0;
247 result("shrink splits", ok);
248}
249
250// growth that is not 2-megabyte aligned, and lazy growth, use
251// 4096-byte pages.
252void
253smalltest(void)
254{
255 char *top = sbrk(0), *p; 256 int ok;
257
258 sbrk(SUPERPGROUNDUP((uint64)top) - (uint64)top + PGSIZE); 259 p = sbrk(2 * MB); // starts one page past a boundary 260 ok = p != SBRK_ERROR && pglevel(p) == 0 && pglevel(p + 2 * MB - 1) == 0; 262 result("unaligned growth uses pages", ok);
263
264 sbrk(SUPERPGROUNDUP((uint64)top) - (uint64)top); 265 p = sbrklazy(2 * MB); // 2-megabyte aligned, but lazy 267 for (uint64 i = 0; i < 2 * MB; i += PGSIZE) 268 p[i] = 1;
269 ok = ok && pglevel(p) == 0 && pglevel(p + 2 * MB - 1) == 0;
271 result("lazy growth uses pages", ok);
272}
273
274int
276{
277 int super0 = freesuper();
279
280 // run the tests in a child, so that every page they use,
281 // page-table pages included, is free again when it exits.
283 supertest();
284 bytetest();
285 syscalltest();
286 result("fork copies superpages", forkcheck(1));
287 demotefork();
288 shrinktest();
289 smalltest();
291 }
292 failed = !childok();
293
294 int super1 = freesuper();
296 printf("supertest: free pages %d before, %d after\n", free0, free1); 297 printf("supertest: free superpages %d before, %d after\n", super0, super1); 298 result("no leaks", free0 == free1 && super0 == super1);
299 printf("supertest: %s\n", failed ? "SOME TESTS FAILED" : "ALL OK"); 301}