2 #include "../port/lib.h"
6 #include "../port/error.h"
11 static int canflush(Proc*, Segment*);
12 static void executeio(void);
13 static void pageout(Proc*, Segment*);
14 static void pagepte(int, Page**);
15 static void pager(void*);
21 static Chan *swapchan;
22 static uchar *swapbuf;
23 static AESstate *swapkey;
28 static ushort ageclock;
33 swapalloc.swmap = xalloc(conf.nswap);
34 swapalloc.top = &swapalloc.swmap[conf.nswap];
35 swapalloc.alloc = swapalloc.swmap;
36 swapalloc.last = swapalloc.swmap;
37 swapalloc.free = conf.nswap;
40 iolist = xalloc(conf.nswppo*sizeof(Page*));
41 if(swapalloc.swmap == nil || iolist == nil)
42 panic("swapinit: not enough memory");
51 if(swapalloc.free == 0) {
55 look = memchr(swapalloc.last, 0, swapalloc.top-swapalloc.last);
57 look = memchr(swapalloc.swmap, 0, swapalloc.last-swapalloc.swmap);
58 *look = 2; /* ref for pte + io transaction */
59 swapalloc.last = look;
62 return (look-swapalloc.swmap) * BY2PG;
71 idx = &swapalloc.swmap[((uintptr)p)/BY2PG];
73 panic("putswap %#p ref == 0", p);
76 if(swapalloc.xref == 0)
77 panic("putswap %#p xref == 0", p);
79 if(--swapalloc.xref == 0) {
80 for(idx = swapalloc.swmap; idx < swapalloc.top; idx++) {
100 idx = &swapalloc.swmap[((uintptr)p)/BY2PG];
105 swapalloc.xref += 255;
111 swapcount(uintptr daddr)
113 return swapalloc.swmap[daddr/BY2PG];
121 if(started.ref || incref(&started) != 1)
122 wakeup(&swapalloc.r);
124 kproc("pager", pager, 0);
133 if((np = pagereclaim(&fscache, 1000)) > 0) {
134 if(0) print("reclaim: %lud fscache\n", np);
135 } else if((np = pagereclaim(&swapimage, 1000)) > 0) {
136 if(0) print("reclaim: %lud swap\n", np);
137 } else if((np = imagereclaim(1000)) > 0) {
138 if(0) print("reclaim: %lud image\n", np);
141 return 1; /* have pages, done */
143 return 0; /* didnt reclaim, need to swap */
162 up->psstate = "Reclaim";
164 up->psstate = "Idle";
165 wakeup(&palloc.pwait[0]);
166 wakeup(&palloc.pwait[1]);
167 sleep(&swapalloc.r, needpages, nil);
171 if(swapimage.c == nil || swapalloc.free == 0){
174 killbig("out of memory");
186 } while(p->state == Dead || p->noswap || !canqlock(&p->seglock));
187 up->psstate = "Pageout";
188 for(i = 0; i < NSEG; i++) {
189 if((s = p->seg[i]) != nil) {
190 switch(s->type&SG_TYPE) {
205 qunlock(&p->seglock);
215 pageout(Proc *p, Segment *s)
222 if(!canqlock(s)) /* We cannot afford to wait, we will surely deadlock */
225 if(!canflush(p, s) /* Able to invalidate all tlbs with references */
232 /* Pass through the pte tables looking for memory pages to swap out */
233 type = s->type&SG_TYPE;
235 for(i = 0; i < size; i++) {
239 for(pg = l->first; pg <= l->last; pg++) {
243 if(entry->modref & PG_REF) {
244 entry->modref &= ~PG_REF;
245 entry->refage = ageclock;
248 age = (short)(ageclock - entry->refage);
260 canflush(Proc *p, Segment *s)
265 if(incref(s) == 2) /* Easy if we are the only user */
268 /* Now we must do hardwork to ensure all processes which have tlb
269 * entries for this segment will be flushed if we succeed in paging it out
274 if(p->state != Dead) {
275 for(i = 0; i < NSEG; i++)
286 pagepte(int type, Page **pg)
293 case SG_TEXT: /* Revert to demand load */
302 if(ioptr >= conf.nswppo)
306 * get a new swap address with swapcount 2, one for the pte
307 * and one extra ref for us while we write the page to disk
313 /* clear any pages referring to it from the cache */
314 cachedel(&swapimage, daddr);
316 /* forget anything that it used to cache */
320 * enter it into the cache so that a fault happening
321 * during the write will grab the page from the cache
322 * rather than one partially written to the disk
325 cachepage(outp, &swapimage);
326 *pg = (Page*)(daddr|PG_ONSWAP);
328 /* Add page to IO transaction list */
329 iolist[ioptr++] = outp;
337 print("%lud/%lud memory %lud/%lud swap %d iolist\n",
338 palloc.user-palloc.freecount,
339 palloc.user, conf.nswap-swapalloc.free, conf.nswap,
353 for(i = 0; i < ioptr; i++) {
354 if(ioptr > conf.nswppo)
355 panic("executeio: ioptr %d > %d", ioptr, conf.nswppo);
358 assert(outp->ref > 0);
359 assert(outp->image == &swapimage);
360 assert(outp->daddr != ~0);
362 /* only write when swap address still in use */
363 if(swapcount(outp->daddr) > 1){
365 kaddr = (char*)VA(k);
368 panic("executeio: page outp I/O error");
370 n = devtab[c->type]->write(c, kaddr, BY2PG, outp->daddr);
378 /* drop our extra swap reference */
379 putswap((Page*)outp->daddr);
381 /* Free up the page after I/O */
390 return palloc.freecount < swapalloc.headroom;
396 uchar buf[sizeof(Dir)+100];
404 if(swapimage.c != nil) {
405 if(swapalloc.free != conf.nswap)
412 * if this isn't a file, set the swap space
413 * to be at most the size of the partition
415 if(devtab[c->type]->dc != L'M'){
416 n = devtab[c->type]->stat(c, buf, sizeof buf);
417 if(n <= 0 || convM2D(buf, n, &d, nil) == 0)
418 error("stat failed in setswapchan");
419 if(d.length < conf.nswppo*BY2PG)
420 error("swap device too small");
421 if(d.length < conf.nswap*BY2PG){
422 conf.nswap = d.length/BY2PG;
423 swapalloc.top = &swapalloc.swmap[conf.nswap];
424 swapalloc.free = conf.nswap;
432 swapimage.c = namec("#¶/swapfile", Aopen, ORDWR, 0);
441 static Dirtab swapdir[]={
442 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
443 "swap", {Qswap}, 0, 0664,
444 "swapfile", {Qswapfile}, 0, 0600,
448 swapattach(char *spec)
450 return devattach(L'¶', spec);
454 swapwalk(Chan *c, Chan *nc, char **name, int nname)
456 return devwalk(c, nc, name, nname, swapdir, nelem(swapdir), devgen);
460 swapstat(Chan *c, uchar *dp, int n)
462 return devstat(c, dp, n, swapdir, nelem(swapdir), devgen);
466 swapopen(Chan *c, int omode)
470 switch((ulong)c->qid.path){
472 if(!iseve() || omode != ORDWR)
474 if(swapimage.c != nil)
479 c->mode = openmode(omode);
483 swapbuf = mallocalign(BY2PG, BY2PG, 0, 0);
484 swapkey = secalloc(sizeof(AESstate)*2);
485 if(swapbuf == nil || swapkey == nil)
488 genrandom(key, sizeof(key));
489 setupAESstate(&swapkey[0], key, sizeof(key), nil);
490 genrandom(key, sizeof(key));
491 setupAESstate(&swapkey[1], key, sizeof(key), nil);
492 memset(key, 0, sizeof(key));
496 return devopen(c, omode, swapdir, nelem(swapdir), devgen);
502 if((c->flag & COPEN) == 0)
504 switch((ulong)c->qid.path){
517 swapread(Chan *c, void *va, long n, vlong off)
519 char tmp[256]; /* must be >= 18*NUMSIZE (Qswap) */
521 switch((ulong)c->qid.path){
523 return devdirread(c, va, n, swapdir, nelem(swapdir), devgen);
525 snprint(tmp, sizeof tmp,
531 "%llud/%llud/%llud kernel malloc\n"
532 "%llud/%llud/%llud kernel draw\n"
533 "%llud/%llud/%llud kernel secret\n",
534 (uvlong)conf.npage*BY2PG,
536 conf.npage-conf.upages,
537 palloc.user-palloc.freecount-fscache.pgref-swapimage.pgref, palloc.user,
538 conf.nswap-swapalloc.free, conf.nswap,
539 (uvlong)mainmem->curalloc,
540 (uvlong)mainmem->cursize,
541 (uvlong)mainmem->maxsize,
542 (uvlong)imagmem->curalloc,
543 (uvlong)imagmem->cursize,
544 (uvlong)imagmem->maxsize,
545 (uvlong)secrmem->curalloc,
546 (uvlong)secrmem->cursize,
547 (uvlong)secrmem->maxsize);
548 return readstr((ulong)off, va, n, tmp);
552 if(devtab[swapchan->type]->read(swapchan, va, n, off) != n)
554 aes_xts_decrypt(&swapkey[0], &swapkey[1], off, va, va, n);
562 swapwrite(Chan *c, void *va, long n, vlong off)
566 switch((ulong)c->qid.path){
572 memmove(buf, va, n); /* so we can NUL-terminate */
574 /* start a pager if not already started */
575 if(strncmp(buf, "start", 5) == 0)
577 else if(buf[0]>='0' && buf[0]<='9')
578 setswapchan(fdtochan(strtoul(buf, nil, 0), ORDWR, 1, 1));
585 aes_xts_encrypt(&swapkey[0], &swapkey[1], off, va, swapbuf, n);
586 if(devtab[swapchan->type]->write(swapchan, swapbuf, n, off) != n)