swap.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
swap.c (7036B)
---
1 #include "u.h"
2 #include "lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "error.h"
7
8 static int canflush(Proc*, Segment*);
9 static void executeio(void);
10 static int needpages(void *v);
11 static void pageout(Proc*, Segment*);
12 static void pagepte(int, Page**);
13 static void pager(void *v);
14
15 Image swapimage;
16 static Page **iolist;
17 static int ioptr;
18
19 void
20 swapinit(void)
21 {
22 swapalloc.swmap = xalloc(conf.nswap);
23 swapalloc.top = &swapalloc.swmap[conf.nswap];
24 swapalloc.alloc = swapalloc.swmap;
25 swapalloc.last = swapalloc.swmap;
26 swapalloc.free = conf.nswap;
27 iolist = xalloc(conf.nswppo*sizeof(Page*));
28 if(swapalloc.swmap == 0 || iolist == 0)
29 panic("swapinit: not enough memory");
30
31 swapimage.notext = 1;
32 }
33
34 ulong
35 newswap(void)
36 {
37 uchar *look;
38
39 lock(&swapalloc.lk);
40
41 if(swapalloc.free == 0){
42 unlock(&swapalloc.lk);
43 return ~0;
44 }
45
46 look = memchr(swapalloc.last, 0, swapalloc.top-swapalloc.last);
47 if(look == 0)
48 panic("inconsistent swap");
49
50 *look = 1;
51 swapalloc.last = look;
52 swapalloc.free--;
53 unlock(&swapalloc.lk);
54 return (look-swapalloc.swmap) * BY2PG;
55 }
56
57 void
58 putswap(Page *p)
59 {
60 uchar *idx;
61
62 lock(&swapalloc.lk);
63 idx = &swapalloc.swmap[((ulong)p)/BY2PG];
64 if(--(*idx) == 0) {
65 swapalloc.free++;
66 if(idx < swapalloc.last)
67 swapalloc.last = idx;
68 }
69 if(*idx >= 254)
70 panic("putswap %#p == %ud", p, *idx);
71 unlock(&swapalloc.lk);
72 }
73
74 void
75 dupswap(Page *p)
76 {
77 lock(&swapalloc.lk);
78 if(++swapalloc.swmap[((ulong)p)/BY2PG] == 0)
79 panic("dupswap");
80 unlock(&swapalloc.lk);
81 }
82
83 int
84 swapcount(ulong daddr)
85 {
86 return swapalloc.swmap[daddr/BY2PG];
87 }
88
89 void
90 kickpager(void)
91 {
92 static int started;
93
94 if(started)
95 wakeup(&swapalloc.r);
96 else {
97 kproc("pager", pager, 0);
98 started = 1;
99 }
100 }
101
102 static void
103 pager(void *junk)
104 {
105 int i;
106 Segment *s;
107 Proc *p, *ep;
108
109 if(waserror())
110 panic("pager: os error");
111
112 p = proctab(0);
113 ep = &p[conf.nproc];
114
115 loop:
116 up->psstate = "Idle";
117 sleep(&swapalloc.r, needpages, 0);
118
119 while(needpages(junk)) {
120
121 if(swapimage.c) {
122 p++;
123 if(p >= ep)
124 p = proctab(0);
125
126 if(p->state == Dead || p->noswap)
127 continue;
128
129 if(!canqlock(&p->seglock))
130 continue; /* process changing its segments */
131
132 for(i = 0; i < NSEG; i++) {
133 if(!needpages(junk)){
134 qunlock(&p->seglock);
135 goto loop;
136 }
137
138 if((s = p->seg[i])) {
139 switch(s->type&SG_TYPE) {
140 default:
141 break;
142 case SG_TEXT:
143 pageout(p, s);
144 break;
145 case SG_DATA:
146 case SG_BSS:
147 case SG_STACK:
148 case SG_SHARED:
149 up->psstate = "Pageout";
150 pageout(p, s);
151 if(ioptr != 0) {
152 up->psstate = "I/O";
153 executeio();
154 }
155 break;
156 }
157 }
158 }
159 qunlock(&p->seglock);
160 }
161 else {
162 print("out of physical memory; no swap configured\n");
163 if(!cpuserver || freebroken() == 0)
164 killbig("out of memory");
165
166 /* Emulate the old system if no swap channel */
167 tsleep(&up->sleep, return0, 0, 5000);
168 wakeup(&palloc.r);
169 }
170 }
171 goto loop;
172 }
173
174 static void
175 pageout(Proc *p, Segment *s)
176 {
177 int type, i, size;
178 Pte *l;
179 Page **pg, *entry;
180
181 if(!canqlock(&s->lk)) /* We cannot afford to wait, we will surely deadlock */
182 return;
183
184 if(s->steal) { /* Protected by /dev/proc */
185 qunlock(&s->lk);
186 return;
187 }
188
189 if(!canflush(p, s)) { /* Able to invalidate all tlbs with references */
190 qunlock(&s->lk);
191 putseg(s);
192 return;
193 }
194
195 if(waserror()) {
196 qunlock(&s->lk);
197 putseg(s);
198 return;
199 }
200
201 /* Pass through the pte tables looking for memory pages to swap out */
202 type = s->type&SG_TYPE;
203 size = s->mapsize;
204 for(i = 0; i < size; i++) {
205 l = s->map[i];
206 if(l == 0)
207 continue;
208 for(pg = l->first; pg < l->last; pg++) {
209 entry = *pg;
210 if(pagedout(entry))
211 continue;
212
213 if(entry->modref & PG_REF) {
214 entry->modref &= ~PG_REF;
215 continue;
216 }
217
218 pagepte(type, pg);
219
220 if(ioptr >= conf.nswppo)
221 goto out;
222 }
223 }
224 out:
225 poperror();
226 qunlock(&s->lk);
227 putseg(s);
228 }
229
230 static int
231 canflush(Proc *p, Segment *s)
232 {
233 int i;
234 Proc *ep;
235
236 lock(&s->ref.lk);
237 if(s->ref.ref == 1) { /* Easy if we are the only user */
238 s->ref.ref++;
239 unlock(&s->ref.lk);
240 return canpage(p);
241 }
242 s->ref.ref++;
243 unlock(&s->ref.lk);
244
245 /* Now we must do hardwork to ensure all processes which have tlb
246 * entries for this segment will be flushed if we succeed in paging it out
247 */
248 p = proctab(0);
249 ep = &p[conf.nproc];
250 while(p < ep) {
251 if(p->state != Dead) {
252 for(i = 0; i < NSEG; i++)
253 if(p->seg[i] == s)
254 if(!canpage(p))
255 return 0;
256 }
257 p++;
258 }
259 return 1;
260 }
261
262 static void
263 pagepte(int type, Page **pg)
264 {
265 ulong daddr;
266 Page *outp;
267
268 outp = *pg;
269 switch(type) {
270 case SG_TEXT: /* Revert to demand load */
271 putpage(outp);
272 *pg = 0;
273 break;
274
275 case SG_DATA:
276 case SG_BSS:
277 case SG_STACK:
278 case SG_SHARED:
279 /*
280 * get a new swap address and clear any pages
281 * referring to it from the cache
282 */
283 daddr = newswap();
284 if(daddr == ~0)
285 break;
286 cachedel(&swapimage, daddr);
287
288 lock(&outp->lk);
289
290 /* forget anything that it used to cache */
291 uncachepage(outp);
292
293 /*
294 * incr the reference count to make sure it sticks around while
295 * being written
296 */
297 outp->ref++;
298
299 /*
300 * enter it into the cache so that a fault happening
301 * during the write will grab the page from the cache
302 * rather than one partially written to the disk
303 */
304 outp->daddr = daddr;
305 cachepage(outp, &swapimage);
306 *pg = (Page*)(daddr|PG_ONSWAP);
307 unlock(&outp->lk);
308
309 /* Add page to IO transaction list */
310 iolist[ioptr++] = outp;
311 break;
312 }
313 }
314
315 void
316 pagersummary(void)
317 {
318 print("%lud/%lud memory %lud/%lud swap %d iolist\n",
319 palloc.user-palloc.freecount,
320 palloc.user, conf.nswap-swapalloc.free, conf.nswap,
321 ioptr);
322 }
323
324 static void
325 executeio(void)
326 {
327 Page *out;
328 int i, n;
329 Chan *c;
330 char *kaddr;
331 KMap *k;
332
333 c = swapimage.c;
334
335 for(i = 0; i < ioptr; i++) {
336 if(ioptr > conf.nswppo)
337 panic("executeio: ioptr %d > %d", ioptr, conf.nswppo);
338 out = iolist[i];
339 k = kmap(out);
340 kaddr = (char*)VA(k);
341
342 if(waserror())
343 panic("executeio: page out I/O error");
344
345 n = devtab[c->type]->write(c, kaddr, BY2PG, out->daddr);
346 if(n != BY2PG)
347 nexterror();
348
349 kunmap(k);
350 poperror();
351
352 /* Free up the page after I/O */
353 lock(&out->lk);
354 out->ref--;
355 unlock(&out->lk);
356 putpage(out);
357 }
358 ioptr = 0;
359 }
360
361 static int
362 needpages(void *v)
363 {
364 return palloc.freecount < swapalloc.headroom;
365 }
366
367 void
368 setswapchan(Chan *c)
369 {
370 uchar dirbuf[sizeof(Dir)+100];
371 Dir d;
372 int n;
373
374 if(swapimage.c) {
375 if(swapalloc.free != conf.nswap){
376 cclose(c);
377 error(Einuse);
378 }
379 cclose(swapimage.c);
380 }
381
382 /*
383 * if this isn't a file, set the swap space
384 * to be at most the size of the partition
385 */
386 if(devtab[c->type]->dc != L'M'){
387 n = devtab[c->type]->stat(c, dirbuf, sizeof dirbuf);
388 if(n <= 0){
389 cclose(c);
390 error("stat failed in setswapchan");
391 }
392 convM2D(dirbuf, n, &d, nil);
393 if(d.length < conf.nswap*BY2PG){
394 conf.nswap = d.length/BY2PG;
395 swapalloc.top = &swapalloc.swmap[conf.nswap];
396 swapalloc.free = conf.nswap;
397 }
398 }
399
400 swapimage.c = c;
401 }
402
403 int
404 swapfull(void)
405 {
406 return swapalloc.free < conf.nswap/10;
407 }