File:  [Plan 9 NeXT] / lucent / sys / src / 9 / port / page.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 18:01:03 2018 UTC (8 years, 1 month ago) by root
Branches: lucent, MAIN
CVS tags: plan9, HEAD
Plan 9 NeXT

#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

#define	pghash(daddr)	palloc.hash[(daddr>>PGSHIFT)&(PGHSIZE-1)]

static	Lock pglock;
struct	Palloc palloc;

void
pageinit(void)
{
	Page *p;
	ulong np, hw, hr, vmem, pmem;

	np = palloc.np0+palloc.np1;
	palloc.head = xalloc(np*sizeof(Page));
	if(palloc.head == 0)
		panic("pageinit");

	p = palloc.head;
	while(palloc.np0 > 0) {
		p->prev = p-1;
		p->next = p+1;
		p->pa = palloc.p0;
		palloc.p0 += BY2PG;
		palloc.np0--;
		p++;
	}
	while(palloc.np1 > 0) {
		p->prev = p-1;
		p->next = p+1;
		p->pa = palloc.p1;
		palloc.p1 += BY2PG;
		palloc.np1--;
		p++;
	}
	palloc.tail = p - 1;
	palloc.head->prev = 0;
	palloc.tail->next = 0;

	palloc.user = p - palloc.head;
	palloc.freecount = palloc.user;
	pmem = palloc.user*BY2PG/1024;
	vmem = pmem + (conf.nswap*BY2PG)/1024;

	/* Pageing numbers */
	swapalloc.highwater = (palloc.freecount*5)/100;
	swapalloc.headroom = swapalloc.highwater + (swapalloc.highwater/4);

	hw = swapalloc.highwater*BY2PG;
	hr = swapalloc.headroom*BY2PG;

	print("%lud free pages, %dK bytes, swap %dK, highwater %dK, headroom %dK\n", 
	palloc.user, pmem, vmem, hw/1024, hr/1024);/**/
}

Page*
newpage(int clear, Segment **s, ulong va)
{
	Page *p;
	KMap *k;
	int hw, i, dontalloc;

retry:
	lock(&palloc);

	hw = swapalloc.highwater;
	while((palloc.freecount < hw && u->p->kp == 0) || palloc.freecount == 0) {
		palloc.wanted++;
		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);
		palloc.wanted--;
	}

	p = palloc.head;
	if(palloc.head = p->next)		/* = Assign */
		palloc.head->prev = 0;
	else
		palloc.tail = 0;

	palloc.freecount--;
	unlock(&palloc);

	lock(p);
	if(p->ref != 0) {	/* lookpage has priority on steal */
		unlock(p);
		goto retry;
	}
	uncachepage(p);
	p->ref++;
	p->va = va;
	p->modref = 0;
	for(i = 0; i < MAXMACH; i++)
		p->cachectl[i] = PG_NOFLUSH;
	mmunewpage(p);
	unlock(p);

	if(clear){
		k = kmap(p);
		memset((void*)VA(k), 0, BY2PG);
		kunmap(k);
	}

	return p;
}

int
ispages(void *p)
{
	USED(p);
	return palloc.freecount >= swapalloc.highwater;
}

void
putpage(Page *p)
{
	if(onswap(p)) {
		putswap(p);
		return;
	}

	lock(p);
	if(--p->ref == 0) {
		lock(&palloc);
		if(p->image && p->image != &swapimage) {
			if(palloc.tail) {
				p->prev = palloc.tail;
				palloc.tail->next = p;
			}
			else {
				palloc.head = p;
				p->prev = 0;
			}
			palloc.tail = p;
			p->next = 0;
		}
		else {
			if(palloc.head) {
				p->next = palloc.head;
				palloc.head->prev = p;
			}
			else {
				palloc.tail = p;
				p->next = 0;
			}
			palloc.head = p;
			p->prev = 0;
		}

		palloc.freecount++;	/* Release people waiting for memory */
		if(palloc.r.p != 0)
			wakeup(&palloc.r);
		unlock(&palloc);
	}
	unlock(p);
}

void
simpleputpage(Page *pg)			/* Always call with palloc locked */
{
	if(pg->ref != 1)
		panic("simpleputpage");
	pg->ref = 0;
	palloc.freecount++;
	if(palloc.head == 0) {
		palloc.head = palloc.tail = pg;
		pg->prev = pg->next = 0;
		return;
	}
	pg->next = palloc.head;
	palloc.head->prev = pg;
	pg->prev = 0;
	palloc.head = pg;
}

void
duppage(Page *p)				/* Always call with p locked */
{
	Page *np;

	/* No dup for swap pages */
	if(p->image == &swapimage) {
		uncachepage(p);	
		return;
	}

	lock(&palloc);
	/* No freelist cache when memory is very low */
	if(palloc.freecount < swapalloc.highwater) {
		unlock(&palloc);
		uncachepage(p);	
		return;
	}

	np = palloc.head;		/* Allocate a new page from freelist */
	if(palloc.head = np->next)	/* = Assign */
		palloc.head->prev = 0;
	else
		palloc.tail = 0;

	if(palloc.tail) {		/* Link back onto tail to give us lru */
		np->prev = palloc.tail;
		palloc.tail->next = np;
		np->next = 0;
		palloc.tail = np;
	}
	else {
		palloc.head = palloc.tail = np;
		np->prev = np->next = 0;
	}

	unlock(&palloc);

	lock(np);				/* Cache the new version */
	if(np->ref != 0) {			/* Stolen by lookpage */
		uncachepage(p);
		unlock(np);
		return;
	}
	
	uncachepage(np);
	np->va = p->va;
	np->daddr = p->daddr;
	mmunewpage(np);
	copypage(p, np);
	cachepage(np, p->image);
	unlock(np);
	uncachepage(p);
}

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
uncachepage(Page *p)			/* Always called with a locked page */
{
	Page **l, *f;

	if(p->image == 0)
		return;

	lock(&palloc.hashlock);
	l = &pghash(p->daddr);
	for(f = *l; f; f = f->hash) {
		if(f == p) {
			*l = p->hash;
			break;
		}
		l = &f->hash;
	}
	unlock(&palloc.hashlock);
	putimage(p->image);
	p->image = 0;
}

void
cachepage(Page *p, Image *i)
{
	Page **l;

	/* If this ever happens it should be fixed by calling
	 * uncachepage instead of panic. I think there is a race
	 * with pio in which this can happen. Calling uncachepage is
	 * correct - I just wanted to see if we got here.
	 */
	if(p->image)
		panic("cachepage");

	incref(i);
	lock(&palloc.hashlock);
	p->image = i;
	l = &pghash(p->daddr);
	p->hash = *l;
	*l = p;
	unlock(&palloc.hashlock);
}

void
cachedel(Image *i, ulong daddr)
{
	Page *f, **l;

	lock(&palloc.hashlock);
	l = &pghash(daddr);
	for(f = *l; f; f = f->hash) {
		if(f->image == i && f->daddr == daddr) {
			*l = f->hash;
			break;
		}
		l = &f->hash;
	}
	unlock(&palloc.hashlock);
}

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(f);
			if(f->image != i || f->daddr != daddr) {
				unlock(f);
				return 0;
			}

			lock(&palloc);
			if(++f->ref == 1) {
				if(f->prev) 
					f->prev->next = f->next;
				else
					palloc.head = f->next;

				if(f->next)
					f->next->prev = f->prev;
				else
					palloc.tail = f->prev;
				palloc.freecount--;
			}
			unlock(&palloc);

			unlock(f);
			return f;	
		}
	}
	unlock(&palloc.hashlock);
	return 0;
}

Pte*
ptecpy(Pte *old)
{
	Pte *new;
	Page **src, **dst;

	new = ptealloc();
	dst = &new->pages[old->first-old->pages];
	new->first = dst;
	for(src = old->first; src <= old->last; src++, dst++)
		if(*src) {
			if(onswap(*src))
				dupswap(*src);
			else {
				lock(*src);
				(*src)->ref++;
				unlock(*src);
			}
			new->last = dst;
			*dst = *src;
		}

	return new;		
}

Pte*
ptealloc(void)
{
	Pte *new;

	new = smalloc(sizeof(Pte));
	new->first = &new->pages[PTEPERTAB];
	new->last = new->pages;
	return new;
}

void
freepte(Segment *s, Pte *p)
{
	int ref;
	Page *pt, **pg, **ptop;
	void (*fn)(Page*);

	switch(s->type&SG_TYPE) {
	case SG_PHYSICAL:
		fn = s->pseg->pgfree;
		ptop = &p->pages[PTEPERTAB];
		if(fn) {
			for(pg = p->pages; pg < ptop; pg++) {
				if(*pg == 0)
					continue;
				(*fn)(*pg);
				*pg = 0;
			}
			break;
		}
		for(pg = p->pages; pg < ptop; pg++) {
			pt = *pg;
			if(pt == 0) 
				continue;
			lock(pt);
			ref = --pt->ref;
			unlock(pt);
			if(ref == 0)
				free(pt);
		}
		break;
	default:
		for(pg = p->first; pg <= p->last; pg++)
			if(*pg) {
				putpage(*pg);
				*pg = 0;
			}
	}
	free(p);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.