ref: 9b69f546334e94ec191d35b15dcaace98fbdcb1b
dir: /sys/src/9/port/page.c/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
Palloc palloc;
ulong
nkpages(Confmem *cm)
{
return ((cm->klimit - cm->kbase) + BY2PG-1) / BY2PG;
}
void
pageinit(void)
{
int color, i, j;
Page *p, **t;
Confmem *cm;
vlong m, v, u;
if(palloc.pages == nil){
ulong np;
np = 0;
for(i=0; i<nelem(conf.mem); i++){
cm = &conf.mem[i];
np += cm->npage - nkpages(cm);
}
palloc.pages = xalloc(np*sizeof(Page));
if(palloc.pages == nil)
panic("pageinit");
}
color = 0;
palloc.freecount = 0;
palloc.head = nil;
t = &palloc.head;
p = palloc.pages;
for(i=0; i<nelem(conf.mem); i++){
cm = &conf.mem[i];
for(j=nkpages(cm); j<cm->npage; j++){
memset(p, 0, sizeof *p);
p->pa = cm->base+j*BY2PG;
if(cankaddr(p->pa) && (KADDR(p->pa) == nil || KADDR(p->pa) == (void*)-BY2PG))
continue;
p->color = color;
color = (color+1)%NCOLOR;
*t = p, t = &p->next;
palloc.freecount++;
p++;
}
}
palloc.user = p - palloc.pages;
u = palloc.user*BY2PG;
v = u + conf.nswap*BY2PG;
/* Paging numbers */
swapalloc.highwater = (palloc.user*5)/100;
swapalloc.headroom = swapalloc.highwater + (swapalloc.highwater/4);
m = 0;
for(i=0; i<nelem(conf.mem); i++)
if(conf.mem[i].npage)
m += conf.mem[i].npage*BY2PG;
m += PGROUND(end - (char*)KTZERO);
print("%lldM memory: ", (m+1024*1024-1)/(1024*1024));
print("%lldM kernel data, ", (m-u+1024*1024-1)/(1024*1024));
print("%lldM user, ", u/(1024*1024));
print("%lldM swap\n", v/(1024*1024));
}
static void
pagechaindone(void)
{
if(palloc.pwait[0].p != nil && wakeup(&palloc.pwait[0]) != nil)
return;
if(palloc.pwait[1].p != nil)
wakeup(&palloc.pwait[1]);
}
void
freepages(Page *head, Page *tail, ulong np)
{
if(head == nil)
return;
if(tail == nil){
tail = head;
for(np = 1;; np++){
tail->ref = 0;
if(tail->next == nil)
break;
tail = tail->next;
}
}
lock(&palloc);
tail->next = palloc.head;
palloc.head = head;
palloc.freecount += np;
pagechaindone();
unlock(&palloc);
}
ulong
pagereclaim(Image *i)
{
Page **h, **e, **l, **x, *p;
Page *fh, *ft;
ulong mp, np;
if(i == nil)
return 0;
lock(i);
mp = i->pgref;
if(mp == 0){
unlock(i);
return 0;
}
np = 0;
fh = ft = nil;
e = &i->pghash[i->pghsize];
for(h = i->pghash; h < e; h++){
l = h;
x = nil;
for(p = *l; p != nil; p = p->next){
if(p->ref == 0)
x = l;
l = &p->next;
}
if(x == nil)
continue;
p = *x;
*x = p->next;
p->next = nil;
p->image = nil;
p->daddr = ~0;
if(fh == nil)
fh = p;
else
ft->next = p;
ft = p;
np++;
if(--i->pgref == 0){
putimage(i);
goto Done;
}
decref(i);
}
unlock(i);
Done:
freepages(fh, ft, np);
return np;
}
static int
ispages(void*)
{
return palloc.freecount > swapalloc.highwater || up->noswap && palloc.freecount > 0;
}
Page*
newpage(int clear, Segment **s, uintptr va)
{
Page *p, **l;
int color;
lock(&palloc);
while(!ispages(nil)){
unlock(&palloc);
if(s != nil)
qunlock(*s);
if(!waserror()){
Rendezq *q;
q = &palloc.pwait[!up->noswap];
eqlock(q);
if(!waserror()){
kickpager();
sleep(q, ispages, nil);
poperror();
}
qunlock(q);
poperror();
}
/*
* 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(s != nil){
*s = nil;
return nil;
}
lock(&palloc);
}
/* First try for our colour */
color = getpgcolor(va);
l = &palloc.head;
for(p = *l; p != nil; p = p->next){
if(p->color == color)
break;
l = &p->next;
}
if(p == nil) {
l = &palloc.head;
p = *l;
}
*l = p->next;
p->next = nil;
palloc.freecount--;
unlock(&palloc);
p->ref = 1;
p->va = va;
p->modref = 0;
inittxtflush(p);
if(clear)
zeropage(p);
return p;
}
/*
* deadpage() decrements the page refcount
* and returns the page when it becomes freeable.
*/
Page*
deadpage(Page *p)
{
if(p->image != nil){
decref(p);
return nil;
}
if(decref(p) != 0)
return nil;
return p;
}
void
putpage(Page *p)
{
p = deadpage(p);
if(p != nil)
freepages(p, p, 1);
}
void
copypage(Page *f, Page *t)
{
KMap *ks, *kd;
ks = kmap(f);
kd = kmap(t);
memmove((void*)VA(kd), (void*)VA(ks), BY2PG);
kunmap(ks);
kunmap(kd);
}
void
zeropage(Page *p)
{
KMap *k;
k = kmap(p);
memset((void*)VA(k), 0, BY2PG);
kunmap(k);
}
void
cachepage(Page *p, Image *i)
{
Page *x, **h;
uintptr daddr;
daddr = p->daddr;
h = &PGHASH(i, daddr);
lock(i);
for(x = *h; x != nil; x = x->next)
if(x->daddr == daddr)
goto done;
if(p->image != nil)
goto done;
p->image = i;
p->next = *h;
*h = p;
incref(i);
i->pgref++;
done:
unlock(i);
}
void
uncachepage(Page *p)
{
Page **l, *x;
Image *i;
i = p->image;
if(i == nil)
return;
l = &PGHASH(i, p->daddr);
lock(i);
if(p->image != i)
goto done;
for(x = *l; x != nil; x = x->next) {
if(x == p){
*l = p->next;
p->next = nil;
p->image = nil;
p->daddr = ~0;
i->pgref--;
putimage(i);
return;
}
l = &x->next;
}
done:
unlock(i);
}
Page*
lookpage(Image *i, uintptr daddr)
{
Page *p, **h, **l;
l = h = &PGHASH(i, daddr);
lock(i);
for(p = *l; p != nil; p = p->next){
if(p->daddr == daddr){
*l = p->next;
p->next = *h;
*h = p;
incref(p);
unlock(i);
return p;
}
l = &p->next;
}
unlock(i);
return nil;
}
void
cachedel(Image *i, uintptr daddr)
{
Page *p;
if((p = lookpage(i, daddr)) != nil){
uncachepage(p);
putpage(p);
}
}
void
zeroprivatepages(void)
{
Page *p, *pe;
/*
* in case of a panic, we might not have a process
* context to do the clearing of the private pages.
*/
if(up == nil){
assert(panicking);
return;
}
lock(&palloc);
pe = palloc.pages + palloc.user;
for(p = palloc.pages; p != pe; p++) {
if(p->modref & PG_PRIV){
incref(p);
zeropage(p);
decref(p);
}
}
unlock(&palloc);
}