Page * lookpage(Image *i, ulong daddr) { Page *f; lock(&palloc.hashlock); for(f = pghash(daddr); f; f = f->hash) { if(f->image == i && f->daddr == daddr) { unlock(&palloc.hashlock); lock(&palloc); lock(f); if(f->image != i || f->daddr != daddr) { unlock(f); unlock(&palloc); return 0; } if(++f->ref == 1) pageunchain(f); unlock(&palloc); unlock(f); return f; } } unlock(&palloc.hashlock); return 0; }
Page * lookpage(Image *i, uint32_t daddr) { Page *f; lock(&pga.hashlock); for(f = pghash(daddr); f; f = f->hash){ if(f->image == i && f->daddr == daddr){ unlock(&pga.hashlock); lock(&pga.l); lock(&f->l); if(f->image != i || f->daddr != daddr){ unlock(&f->l); unlock(&pga.l); return 0; } if(++f->ref == 1) pageunchain(f); unlock(&pga.l); unlock(&f->l); return f; } } unlock(&pga.hashlock); return nil; }
/* * Get an auxiliary page. * Don't do so if less than Nminfree pages. * Only used by cache. * The interface must specify page size. */ Page* auxpage(usize size) { Page *p; Pgsza *pa; int si; si = getpgszi(size); lock(&pga.l); pa = &pga.pgsza[si]; p = pa->head; if(pa->freecount < Nminfree){ unlock(&pga.l); return nil; } pageunchain(p); lock(&p->l); if(p->ref != 0) panic("auxpage"); p->ref++; uncachepage(p); unlock(&p->l); unlock(&pga.l); return p; }
static void freepages(int si, int once) { Proc *up = externup(); Pgsza *pa; Page *p; for(; si < sys->npgsz; si++){ pa = &pga.pgsza[si]; if(pa->freecount > 0){ DBG("kickpager() up %#p: releasing %udK pages\n", up, sys->pgsz[si]/KiB); lock(&pga); if(pa->freecount == 0){ unlock(&pga); continue; } p = pa->head; pageunchain(p); unlock(&pga); if(p->ref != 0) panic("freepages pa %#ullx", p->pa); pgfree(p); if(once) break; } } }
Page* auxpage(void) { Page *p; lock(&palloc); p = palloc.head; if(palloc.freecount < swapalloc.highwater) { unlock(&palloc); return 0; } pageunchain(p); lock(p); if(p->ref != 0) panic("auxpage"); p->ref++; uncachepage(p); unlock(p); unlock(&palloc); return p; }
int duppage(Page *p) /* Always call with p locked */ { Proc *up = externup(); Pgsza *pa; Page *np; int color; int retries; retries = 0; retry: if(retries++ > dupretries){ print("duppage %d, up %#p\n", retries, up); dupretries += 100; if(dupretries > 100000) panic("duppage\n"); uncachepage(p); return 1; } /* don't dup pages with no image */ if(p->ref == 0 || p->image == nil || p->image->notext) return 0; /* * normal lock ordering is to call * lock(&pga.l) before lock(&p->l). * To avoid deadlock, we have to drop * our locks and try again. */ if(!canlock(&pga.l)){ unlock(&p->l); if(up) sched(); lock(&p->l); goto retry; } pa = &pga.pgsza[p->pgszi]; /* No freelist cache when memory is very low */ if(pa->freecount < Nminfree){ unlock(&pga.l); uncachepage(p); return 1; } color = p->color; for(np = pa->head; np; np = np->next) if(np->color == color) break; /* No page of the correct color */ if(np == 0){ unlock(&pga.l); uncachepage(p); return 1; } pageunchain(np); pagechaintail(np); /* * XXX - here's a bug? - np is on the freelist but it's not really free. * when we unlock palloc someone else can come in, decide to * use np, and then try to lock it. they succeed after we've * run copypage and cachepage and unlock(np). then what? * they call pageunchain before locking(np), so it's removed * from the freelist, but still in the cache because of * cachepage below. if someone else looks in the cache * before they remove it, the page will have a nonzero ref * once they finally lock(np). * * What I know is that not doing the pagechaintail, but * doing it at the end, to prevent the race, leads to a * deadlock, even following the pga, pg lock ordering. -nemo */ lock(&np->l); unlock(&pga.l); /* Cache the new version */ uncachepage(np); np->va = p->va; np->daddr = p->daddr; copypage(p, np); cachepage(np, p->image); unlock(&np->l); uncachepage(p); return 0; }
/* * can be called with up == nil during boot. */ Page* newpage(int clear, Segment **s, uintptr_t va, usize size, int color) { Page *p; KMap *k; uint8_t ct; Pgsza *pa; int i, dontalloc, si; // static int once; si = getpgszi(size); //iprint("(remove this print and diea)newpage, size %x, si %d\n", size, si); pa = &pga.pgsza[si]; lock(&pga.l); /* * Beware, new page may enter a loop even if this loop does not * loop more than once, if the segment is lost and fault calls us * again. Either way, we accept any color if we failed a couple of times. */ for(i = 0;; i++){ if(i > 3) color = NOCOLOR; /* * 1. try to reuse a free one. */ p = findpg(pa->head, color); if(p != nil) break; /* * 2. try to allocate a new one from physical memory */ p = pgalloc(size, color); if(p != nil){ pagechainhead(p); break; } /* * 3. out of memory, try with the pager. * but release the segment (if any) while in the pager. */ unlock(&pga.l); dontalloc = 0; if(s && *s) { qunlock(&((*s)->lk)); *s = 0; dontalloc = 1; } /* * Try to get any page of the desired color * or any color for NOCOLOR. */ kickpager(si, color); /* * If called from fault and we lost the segment from * underneath don't waste time allocating and freeing * a page. Fault will call newpage again when it has * reacquired the segment locks */ if(dontalloc) return 0; lock(&pga.l); } assert(p != nil); ct = PG_NEWCOL; pageunchain(p); lock(&p->l); if(p->ref != 0) panic("newpage pa %#ullx", p->pa); uncachepage(p); p->ref++; p->va = va; p->modref = 0; for(i = 0; i < nelem(p->cachectl); i++) p->cachectl[i] = ct; unlock(&p->l); unlock(&pga.l); if(clear) { k = kmap(p); if (VA(k) == 0xfffffe007d800000ULL) trip++; // if (trip) die("trip before memset"); // This will frequently die if we use 3K-1 (3071 -- 0xbff) // it will not if we use 3070. // The fault is a null pointer deref. //memset((void*)VA(k), 0, machp()->pgsz[p->pgszi]); // thinking about it, using memset is stupid. // Don't get upset about this loop; // we make it readable, compilers optimize it. int i; uint64_t *v = (void *)VA(k); if (1) for(i = 0; i < sys->pgsz[p->pgszi]/sizeof(*v); i++) v[i] = 0; //if (trip) die("trip"); kunmap(k); } DBG("newpage: va %#p pa %#ullx pgsz %#ux color %d\n", p->va, p->pa, sys->pgsz[p->pgszi], p->color); return p; }
int duppage(Page *p) /* Always call with p locked */ { Page *np; int color; int retries; retries = 0; retry: if(retries++ > dupretries){ print("duppage %d, up %p\n", retries, up); dupretries += 100; if(dupretries > 100000) panic("duppage\n"); uncachepage(p); return 1; } /* don't dup pages with no image */ if(p->ref == 0 || p->image == nil || p->image->notext) return 0; /* * normal lock ordering is to call * lock(&palloc) before lock(p). * To avoid deadlock, we have to drop * our locks and try again. */ if(!canlock(&palloc)){ unlock(p); if(up) sched(); lock(p); goto retry; } /* No freelist cache when memory is very low */ if(palloc.freecount < swapalloc.highwater) { unlock(&palloc); uncachepage(p); return 1; } color = getpgcolor(p->va); for(np = palloc.head; np; np = np->next) if(np->color == color) break; /* No page of the correct color */ if(np == 0) { unlock(&palloc); uncachepage(p); return 1; } pageunchain(np); pagechaintail(np); /* * XXX - here's a bug? - np is on the freelist but it's not really free. * when we unlock palloc someone else can come in, decide to * use np, and then try to lock it. they succeed after we've * run copypage and cachepage and unlock(np). then what? * they call pageunchain before locking(np), so it's removed * from the freelist, but still in the cache because of * cachepage below. if someone else looks in the cache * before they remove it, the page will have a nonzero ref * once they finally lock(np). */ lock(np); unlock(&palloc); /* Cache the new version */ uncachepage(np); np->va = p->va; np->daddr = p->daddr; copypage(p, np); cachepage(np, p->image); unlock(np); uncachepage(p); return 0; }
Page* newpage(int clear, Segment **s, ulong va) { Page *p; KMap *k; uchar ct; int i, hw, dontalloc, color; lock(&palloc); color = getpgcolor(va); hw = swapalloc.highwater; for(;;) { if(palloc.freecount > hw) break; if(up->kp && palloc.freecount > 0) break; unlock(&palloc); dontalloc = 0; if(s && *s) { qunlock(&((*s)->lk)); *s = 0; dontalloc = 1; } qlock(&palloc.pwait); /* Hold memory requesters here */ while(waserror()) /* Ignore interrupts */ ; kickpager(); tsleep(&palloc.r, ispages, 0, 1000); poperror(); qunlock(&palloc.pwait); /* * If called from fault and we lost the segment from * underneath don't waste time allocating and freeing * a page. Fault will call newpage again when it has * reacquired the segment locks */ if(dontalloc) return 0; lock(&palloc); } /* First try for our colour */ for(p = palloc.head; p; p = p->next) if(p->color == color) break; ct = PG_NOFLUSH; if(p == 0) { p = palloc.head; p->color = color; ct = PG_NEWCOL; } pageunchain(p); lock(p); if(p->ref != 0) panic("newpage: p->ref %d != 0", p->ref); uncachepage(p); p->ref++; p->va = va; p->modref = 0; for(i = 0; i < MAXMACH; i++) p->cachectl[i] = ct; unlock(p); unlock(&palloc); if(clear) { k = kmap(p); memset((void*)VA(k), 0, BY2PG); kunmap(k); } return p; }