123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "../port/error.h"
- static int canflush(Proc*, Segment*);
- static void executeio(void);
- static int needpages(void*);
- static void pageout(Proc*, Segment*);
- static void pagepte(int, Page**);
- static void pager(void*);
- Image swapimage;
- static int swopen;
- static Page **iolist;
- static int ioptr;
- void
- swapinit(void)
- {
- swapalloc.swmap = xalloc(conf.nswap);
- swapalloc.top = &swapalloc.swmap[conf.nswap];
- swapalloc.alloc = swapalloc.swmap;
- swapalloc.last = swapalloc.swmap;
- swapalloc.free = conf.nswap;
- iolist = xalloc(conf.nswppo*sizeof(Page*));
- if(swapalloc.swmap == 0 || iolist == 0)
- panic("swapinit: not enough memory");
- swapimage.notext = 1;
- }
- ulong
- newswap(void)
- {
- uchar *look;
- lock(&swapalloc);
- if(swapalloc.free == 0){
- unlock(&swapalloc);
- return ~0;
- }
- look = memchr(swapalloc.last, 0, swapalloc.top-swapalloc.last);
- if(look == 0)
- panic("inconsistent swap");
- *look = 1;
- swapalloc.last = look;
- swapalloc.free--;
- unlock(&swapalloc);
- return (look-swapalloc.swmap) * BY2PG;
- }
- void
- putswap(Page *p)
- {
- uchar *idx;
- lock(&swapalloc);
- idx = &swapalloc.swmap[((ulong)p)/BY2PG];
- if(--(*idx) == 0) {
- swapalloc.free++;
- if(idx < swapalloc.last)
- swapalloc.last = idx;
- }
- if(*idx >= 254)
- panic("putswap %lux == %ud", p, *idx);
- unlock(&swapalloc);
- }
- void
- dupswap(Page *p)
- {
- lock(&swapalloc);
- if(++swapalloc.swmap[((ulong)p)/BY2PG] == 0)
- panic("dupswap");
- unlock(&swapalloc);
- }
- int
- swapcount(ulong daddr)
- {
- return swapalloc.swmap[daddr/BY2PG];
- }
- void
- kickpager(void)
- {
- static int started;
- if(started)
- wakeup(&swapalloc.r);
- else {
- kproc("pager", pager, 0);
- started = 1;
- }
- }
- static void
- pager(void *junk)
- {
- int i;
- Segment *s;
- Proc *p, *ep;
- if(waserror())
- panic("pager: os error\n");
- p = proctab(0);
- ep = &p[conf.nproc];
- loop:
- up->psstate = "Idle";
- sleep(&swapalloc.r, needpages, 0);
- while(needpages(junk)) {
- if(swapimage.c) {
- p++;
- if(p >= ep)
- p = proctab(0);
-
- if(p->state == Dead || p->noswap)
- continue;
- if(!canqlock(&p->seglock))
- continue; /* process changing its segments */
- for(i = 0; i < NSEG; i++) {
- if(!needpages(junk)){
- qunlock(&p->seglock);
- goto loop;
- }
- if(s = p->seg[i]) {
- switch(s->type&SG_TYPE) {
- default:
- break;
- case SG_TEXT:
- pageout(p, s);
- break;
- case SG_DATA:
- case SG_BSS:
- case SG_STACK:
- case SG_SHARED:
- up->psstate = "Pageout";
- pageout(p, s);
- if(ioptr != 0) {
- up->psstate = "I/O";
- executeio();
- }
- break;
- }
- }
- }
- qunlock(&p->seglock);
- }
- else {
- if(!cpuserver)
- freebroken(); /* can use the memory */
- else
- killbig();
- /* Emulate the old system if no swap channel */
- print("no physical memory\n");
- tsleep(&up->sleep, return0, 0, 5000);
- wakeup(&palloc.r);
- }
- }
- goto loop;
- }
- static void
- pageout(Proc *p, Segment *s)
- {
- int type, i, size;
- Pte *l;
- Page **pg, *entry;
- if(!canqlock(&s->lk)) /* We cannot afford to wait, we will surely deadlock */
- return;
- if(s->steal) { /* Protected by /dev/proc */
- qunlock(&s->lk);
- return;
- }
- if(!canflush(p, s)) { /* Able to invalidate all tlbs with references */
- qunlock(&s->lk);
- putseg(s);
- return;
- }
- if(waserror()) {
- qunlock(&s->lk);
- putseg(s);
- return;
- }
- /* Pass through the pte tables looking for memory pages to swap out */
- type = s->type&SG_TYPE;
- size = s->mapsize;
- for(i = 0; i < size; i++) {
- l = s->map[i];
- if(l == 0)
- continue;
- for(pg = l->first; pg < l->last; pg++) {
- entry = *pg;
- if(pagedout(entry))
- continue;
- if(entry->modref & PG_REF) {
- entry->modref &= ~PG_REF;
- continue;
- }
- pagepte(type, pg);
- if(ioptr >= conf.nswppo)
- goto out;
- }
- }
- out:
- poperror();
- qunlock(&s->lk);
- putseg(s);
- }
- static int
- canflush(Proc *p, Segment *s)
- {
- int i;
- Proc *ep;
- lock(s);
- if(s->ref == 1) { /* Easy if we are the only user */
- s->ref++;
- unlock(s);
- return canpage(p);
- }
- s->ref++;
- unlock(s);
- /* Now we must do hardwork to ensure all processes which have tlb
- * entries for this segment will be flushed if we succeed in paging it out
- */
- p = proctab(0);
- ep = &p[conf.nproc];
- while(p < ep) {
- if(p->state != Dead) {
- for(i = 0; i < NSEG; i++)
- if(p->seg[i] == s)
- if(!canpage(p))
- return 0;
- }
- p++;
- }
- return 1;
- }
- static void
- pagepte(int type, Page **pg)
- {
- ulong daddr;
- Page *outp;
- outp = *pg;
- switch(type) {
- case SG_TEXT: /* Revert to demand load */
- putpage(outp);
- *pg = 0;
- break;
- case SG_DATA:
- case SG_BSS:
- case SG_STACK:
- case SG_SHARED:
- /*
- * get a new swap address and clear any pages
- * referring to it from the cache
- */
- daddr = newswap();
- if(daddr == ~0)
- break;
- cachedel(&swapimage, daddr);
- lock(outp);
- /* forget anything that it used to cache */
- uncachepage(outp);
- /*
- * incr the reference count to make sure it sticks around while
- * being written
- */
- outp->ref++;
- /*
- * enter it into the cache so that a fault happening
- * during the write will grab the page from the cache
- * rather than one partially written to the disk
- */
- outp->daddr = daddr;
- cachepage(outp, &swapimage);
- *pg = (Page*)(daddr|PG_ONSWAP);
- unlock(outp);
- /* Add page to IO transaction list */
- iolist[ioptr++] = outp;
- break;
- }
- }
- void
- pagersummary(void)
- {
- print("%lud/%lud memory %lud/%lud swap %d iolist\n",
- palloc.user-palloc.freecount,
- palloc.user, conf.nswap-swapalloc.free, conf.nswap,
- ioptr);
- }
- static void
- executeio(void)
- {
- Page *out;
- int i, n;
- Chan *c;
- char *kaddr;
- KMap *k;
- c = swapimage.c;
- for(i = 0; i < ioptr; i++) {
- if(ioptr > conf.nswppo)
- panic("executeio: ioptr %d > %d\n", ioptr, conf.nswppo);
- out = iolist[i];
- k = kmap(out);
- kaddr = (char*)VA(k);
- if(waserror())
- panic("executeio: page out I/O error");
- n = devtab[c->type]->write(c, kaddr, BY2PG, out->daddr);
- if(n != BY2PG)
- nexterror();
- kunmap(k);
- poperror();
- /* Free up the page after I/O */
- lock(out);
- out->ref--;
- unlock(out);
- putpage(out);
- }
- ioptr = 0;
- }
- static int
- needpages(void*)
- {
- return palloc.freecount < swapalloc.headroom;
- }
- void
- setswapchan(Chan *c)
- {
- uchar dirbuf[sizeof(Dir)+100];
- Dir d;
- int n;
- if(swapimage.c) {
- if(swapalloc.free != conf.nswap){
- cclose(c);
- error(Einuse);
- }
- cclose(swapimage.c);
- }
- /*
- * if this isn't a file, set the swap space
- * to be at most the size of the partition
- */
- if(devtab[c->type]->dc != L'M'){
- n = devtab[c->type]->stat(c, dirbuf, sizeof dirbuf);
- if(n <= 0){
- cclose(c);
- error("stat failed in setswapchan");
- }
- convM2D(dirbuf, n, &d, nil);
- if(d.length < conf.nswap*BY2PG){
- conf.nswap = d.length/BY2PG;
- swapalloc.top = &swapalloc.swmap[conf.nswap];
- swapalloc.free = conf.nswap;
- }
- }
- swapimage.c = c;
- }
- int
- swapfull(void)
- {
- return swapalloc.free < conf.nswap/10;
- }
|