@@ -9,8 +9,9 @@
99#include "sleeplock.h"
1010#include "fs.h"
1111#include "buf.h"
1212#include "proc.h"
13#include "swapstat.h"
1314#include "defs.h"
1415
1516// keep at least this many pages free for allocations that cannot
1617// evict.
@@ -32,8 +33,10 @@ struct {
3233// written out.
3334struct {
3536 struct buf b; // only used to wait for the disk; b.data is unused 37 int pageouts; // pages written out since boot
38 int pageins; // pages read in since boot
3639} swapio;
3740
3841// the frame table: for every physical page that is mapped as a
3942// user page, the process and the virtual address that map it.
@@ -204,8 +207,9 @@ evict(void)
205208 return 0;
206209 }
207210 swaprw(s, pa, 1);
211 swapio.pageouts++;
210214 return 1;
211215}
@@ -245,11 +249,27 @@ swapin(pagetable_t pagetable, uint64 va)
245249 s = PTE2SLOT(*pte);
246250
248252 swaprw(s, mem, 0); 253 swapio.pageins++;
250255
253258 swapfree(s);
254259 return (uint64)mem; 255260}
261
262// for the swapstat system call.
263void
264swapstat(struct swapstat *st)
265{
266 st->freepages = kfreecount();
268 st->slotsused = slots.nused;
270 st->nslots = NSWAP;
272 st->pageouts = swapio.pageouts;
273 st->pageins = swapio.pageins;
275}
@@ -0,0 +1,257 @@
1// Test swapping: use more memory than the machine has, check
2// that every page comes back with its contents, use pages in
3// swap as system call buffers, fork a process that is partly in
4// swap, and run several big processes at once.
5
6#include "kernel/types.h"
7#include "kernel/swapstat.h"
8#include "user/user.h"
9
10#define PG 4096
11
12int npages = 36000; // more than the 32,768 pages of RAM
13
14// the value of word w of page i in round r.
15uint64
16val(uint64 i, uint64 w, uint64 r)
17{
18 return (i << 32) ^ (w << 20) ^ (r * 0x9e3779b97f4a7c15ULL);
19}
20
21void
22fill(char *base, int n, int r) 23{
24 for (int i = 0; i < n; i++) {
25 uint64 *p = (uint64 *)(base + (uint64)i * PG); 26 for (int w = 0; w < PG / 8; w++)
27 p[w] = val(i, w, r);
28 }
29}
30
31// check pages [0, n), the last one first if backward.
32// returns the number of pages with a wrong word.
33int
34check(char *base, int n, int r, int backward) 35{
37 for (int k = 0; k < n; k++) {
38 int i = backward ? n - 1 - k : k;
39 uint64 *p = (uint64 *)(base + (uint64)i * PG); 40 for (int w = 0; w < PG / 8; w++) {
41 if (p[w] != val(i, w, r)) {
43 printf("swaptest: page %d word %d is %lx\n", i, w, p[w]); 45 break;
46 }
47 }
48 }
50}
51
52struct swapstat
53sst(void)
54{
55 struct swapstat st;
56 swapstat(&st);
57 return st;
58}
59
60// wait for one child; 1 if it exited with status 0.
61int
62childok(void)
63{
64 int xs = 1;
66 return 0;
67 return xs == 0;
68}
69
70char *
71grow(int n)
72{
75 printf("swaptest: sbrklazy(%d pages) failed\n", n); 77 }
78 return p;
79}
80
81// write npages pages, then check them, newest first.
82void
83bigtest(void)
84{
85 struct swapstat a = sst(), b, c;
86 char *base = grow(npages); 87 fill(base, npages, 1); 88 b = sst();
89 printf("swaptest: big: wrote %d pages: %d out, %d in\n", npages, 90 b.pageouts - a.pageouts, b.pageins - a.pageins);
91 int bad = check(base, npages, 1, 1); 92 c = sst();
93 printf("swaptest: big: checked %d pages, %d wrong: %d out, %d in\n", 94 npages, bad, c.pageouts - b.pageouts, c.pageins - b.pageins); 96}
97
98// the kernel reads and writes pages that are in swap: copy page 2i
99// to page 2i+1 through a pipe, 512 bytes (the pipe's size) at a
100// time, until 4 source pages and 4 destination pages were found
101// in swap (the first write() or read() of the page read it in).
102void
103syscalltest(void)
104{
105 int fds[2], bad = 0, nsrc = 0, ndst = 0, i; 106 char *base = grow(npages); 107 fill(base, npages, 2); 110 for (i = 0; i < npages / 2 && (nsrc < 4 || ndst < 4); i++) {
111 char *src = base + (uint64)(2 * i) * PG; 112 char *dst = src + PG;
113 for (int off = 0; off < PG; off += 512) { 114 struct swapstat a = sst();
115 if (write(fds[1], src + off, 512) != 512) { 116 printf("swaptest: write failed\n"); 118 }
119 struct swapstat b = sst();
120 if (read(fds[0], dst + off, 512) != 512) { 121 printf("swaptest: read failed\n"); 123 }
124 struct swapstat c = sst();
126 nsrc += b.pageins > a.pageins;
127 ndst += c.pageins > b.pageins;
128 }
129 }
130 if (memcmp(src, dst, PG) != 0) 132 }
133 printf("swaptest: syscalls: copied %d pages, %d wrong; %d sources and %d " 134 "destinations were in swap\n",
136 exit(bad != 0 || nsrc < 4 || ndst < 4); 137}
138
139// fill 2000 pages, push them out by using a lot of memory, give
140// that memory back, then fork. both processes check all pages,
141// and the child's writes must not reach the parent.
142void
144{
145 int m = 2000, h = 37000;
146 char *base = grow(m); 148 char *hog = grow(h);
149 for (int i = 0; i < h; i++)
150 hog[(uint64)i * PG] = 1;
152 struct swapstat a = sst();
153 printf("swaptest: fork: %d slots in use at fork\n", a.slotsused); 156 printf("swaptest: fork failed\n"); 158 }
160 int bad = check(base, m, 3, 0); 161 fill(base, m / 2, 4); // must not show in the parent 163 }
164 int cok = childok();
165 int bad = check(base, m, 3, 0); 166 struct swapstat b = sst();
167 printf("swaptest: fork: child %s, parent %d wrong, %d pages read in\n", 168 cok ? "ok" : "FAILED", bad, b.pageins - a.pageins); 169 exit(!cok || bad != 0 || a.slotsused == 0); 170}
171
172// three processes of 12,500 pages each: all three fill their
173// pages before any of them checks, so together they need more
174// memory than there is and evict each other's pages.
175void
176procstest(void)
177{
178 int n = 12500, ready[2], go[2], ok = 1;
179 char c;
180 struct swapstat a = sst();
181 if (pipe(ready) < 0 || pipe(go) < 0) 183 for (int k = 0; k < 3; k++) {
186 printf("swaptest: fork failed\n"); 188 }
192 char *base = grow(n); 193 fill(base, n, 10 + k); 194 write(ready[1], "r", 1); 196 read(go[0], &c, 1); // wait until all three have filled 197 exit(check(base, n, 10 + k, 1) != 0); 198 }
199 }
200 // a child that was killed never writes; its exit closes its end,
201 // so read returns 0 instead of waiting forever.
203 for (int k = 0; k < 3; k++)
204 if (read(ready[0], &c, 1) != 1) 205 break;
206 write(go[1], "ggg", 3); 207 for (int k = 0; k < 3; k++)
208 ok &= childok();
209 struct swapstat b = sst();
210 printf("swaptest: procs: %d out, %d in\n", b.pageouts - a.pageouts, 211 b.pageins - a.pageins);
212 exit(!ok || b.pageouts == a.pageouts); 213}
214
215// run f in a child and print OK or FAIL.
216int
217run(void (*f)(void), char *name) 218{
221 printf("swaptest: fork failed\n"); 223 }
225 f();
226 int ok = childok();
227 printf("swaptest: %s: %s\n", name, ok ? "OK" : "FAIL"); 228 return ok;
229}
230
231int
233{
234 int ok = 1;
235 if (argc > 1)
237 struct swapstat a = sst();
238 ok &= run(bigtest, "big"); 239 ok &= run(syscalltest, "syscalls"); 241 ok &= run(procstest, "procs"); 242 struct swapstat b = sst();
243 // a page moving between memory and swap changes both counts.
244 printf("swaptest: free pages + free slots: %d before, %d after\n", 245 a.freepages + a.nslots - a.slotsused,
246 b.freepages + b.nslots - b.slotsused);
247 if (a.freepages - a.slotsused != b.freepages - b.slotsused) {
248 printf("swaptest: leak: FAIL\n"); 249 ok = 0;
250 } else {
251 printf("swaptest: leak: OK\n"); 252 }
253 printf("swaptest: %d pages written out, %d read in\n", 254 b.pageouts - a.pageouts, b.pageins - a.pageins);
255 printf("swaptest: %s\n", ok ? "ALL OK" : "SOME TESTS FAILED"); 257}