xref: /plan9-contrib/sys/src/9k/port/page.c (revision 094d68186d4cdde21fdab9786d6c843a03693e4e)
19ef1f84bSDavid du Colombier #include	"u.h"
29ef1f84bSDavid du Colombier #include	"../port/lib.h"
39ef1f84bSDavid du Colombier #include	"mem.h"
49ef1f84bSDavid du Colombier #include	"dat.h"
59ef1f84bSDavid du Colombier #include	"fns.h"
69ef1f84bSDavid du Colombier 
7*094d6818SDavid du Colombier enum
8*094d6818SDavid du Colombier {
9*094d6818SDavid du Colombier 	Nfreepgs = 0, // 1*GiB/PGSZ
10*094d6818SDavid du Colombier };
11*094d6818SDavid du Colombier 
129ef1f84bSDavid du Colombier #define pghash(daddr)	palloc.hash[(daddr>>PGSHFT)&(PGHSIZE-1)]
139ef1f84bSDavid du Colombier 
149ef1f84bSDavid du Colombier struct	Palloc palloc;
159ef1f84bSDavid du Colombier 
169ef1f84bSDavid du Colombier static	uint	highwater;	/* TO DO */
179ef1f84bSDavid du Colombier 
18*094d6818SDavid du Colombier char*
seprintpagestats(char * s,char * e)19*094d6818SDavid du Colombier seprintpagestats(char *s, char *e)
209ef1f84bSDavid du Colombier {
21*094d6818SDavid du Colombier 	Pallocpg *pg;
22*094d6818SDavid du Colombier 	int i;
239ef1f84bSDavid du Colombier 
24*094d6818SDavid du Colombier 	lock(&palloc);
25*094d6818SDavid du Colombier 	for(i = 0; i < nelem(palloc.avail); i++){
26*094d6818SDavid du Colombier 		pg = &palloc.avail[i];
27*094d6818SDavid du Colombier 		if(pg->freecount != 0)
28*094d6818SDavid du Colombier 			s = seprint(s, e, "%lud/%lud %dK user pages avail\n",
29*094d6818SDavid du Colombier 				pg->freecount,
30*094d6818SDavid du Colombier 				pg->count, (1<<i)/KiB);
319ef1f84bSDavid du Colombier 	}
32*094d6818SDavid du Colombier 	unlock(&palloc);
33*094d6818SDavid du Colombier 	return s;
349ef1f84bSDavid du Colombier }
359ef1f84bSDavid du Colombier 
369ef1f84bSDavid du Colombier void
pageinit(void)379ef1f84bSDavid du Colombier pageinit(void)
389ef1f84bSDavid du Colombier {
39*094d6818SDavid du Colombier 	uintmem avail;
40*094d6818SDavid du Colombier 	uvlong pkb, kkb, kmkb, mkb;
419ef1f84bSDavid du Colombier 
42*094d6818SDavid du Colombier 	avail = sys->pmpaged;
43*094d6818SDavid du Colombier 	palloc.user = avail/PGSZ;
449ef1f84bSDavid du Colombier 
459ef1f84bSDavid du Colombier 	/* user, kernel, kernel malloc area, memory */
469ef1f84bSDavid du Colombier 	pkb = palloc.user*PGSZ/KiB;
479ef1f84bSDavid du Colombier 	kkb = ROUNDUP((uintptr)end - KTZERO, PGSZ)/KiB;
489ef1f84bSDavid du Colombier 	kmkb = ROUNDUP(sys->vmend - (uintptr)end, PGSZ)/KiB;
499ef1f84bSDavid du Colombier 	mkb = sys->pmoccupied/KiB;
509ef1f84bSDavid du Colombier 
519ef1f84bSDavid du Colombier 	/* Paging numbers */
529ef1f84bSDavid du Colombier 	highwater = (palloc.user*5)/100;
539ef1f84bSDavid du Colombier 	if(highwater >= 64*MiB/PGSZ)
549ef1f84bSDavid du Colombier 		highwater = 64*MiB/PGSZ;
559ef1f84bSDavid du Colombier 
569ef1f84bSDavid du Colombier 	print("%lldM memory: %lldK+%lldM kernel, %lldM user, %lldM lost\n",
57*094d6818SDavid du Colombier 		mkb/KiB, kkb, kmkb/KiB, pkb/KiB, (vlong)(mkb-kkb-kmkb-pkb)/KiB);
589ef1f84bSDavid du Colombier }
599ef1f84bSDavid du Colombier 
60*094d6818SDavid du Colombier Page*
pgalloc(uint lg2size,int color)61*094d6818SDavid du Colombier pgalloc(uint lg2size, int color)
62*094d6818SDavid du Colombier {
63*094d6818SDavid du Colombier 	Page *pg;
64*094d6818SDavid du Colombier 
65*094d6818SDavid du Colombier 	if((pg = malloc(sizeof(Page))) == nil){
66*094d6818SDavid du Colombier 		DBG("pgalloc: malloc failed\n");
67*094d6818SDavid du Colombier 		return nil;
68*094d6818SDavid du Colombier 	}
69*094d6818SDavid du Colombier 	memset(pg, 0, sizeof *pg);
70*094d6818SDavid du Colombier 	if((pg->pa = physalloc(1<<lg2size, &color, pg)) == 0){
71*094d6818SDavid du Colombier 		DBG("pgalloc: physalloc failed: size %#ux color %d\n", 1<<lg2size, color);
72*094d6818SDavid du Colombier 		free(pg);
73*094d6818SDavid du Colombier 		return nil;
74*094d6818SDavid du Colombier 	}
75*094d6818SDavid du Colombier 	pg->lg2size = lg2size;
76*094d6818SDavid du Colombier 	palloc.avail[pg->lg2size].count++;
77*094d6818SDavid du Colombier 	pg->color = color;
78*094d6818SDavid du Colombier 	return pg;
79*094d6818SDavid du Colombier }
80*094d6818SDavid du Colombier 
81*094d6818SDavid du Colombier void
pgfree(Page * pg)82*094d6818SDavid du Colombier pgfree(Page* pg)
83*094d6818SDavid du Colombier {
84*094d6818SDavid du Colombier 	palloc.avail[pg->lg2size].count--;
85*094d6818SDavid du Colombier 	physfree(pg->pa, pagesize(pg));
86*094d6818SDavid du Colombier 	free(pg);
87*094d6818SDavid du Colombier }
88*094d6818SDavid du Colombier 
89*094d6818SDavid du Colombier void
pageunchain(Page * p)909ef1f84bSDavid du Colombier pageunchain(Page *p)
919ef1f84bSDavid du Colombier {
92*094d6818SDavid du Colombier 	Pallocpg *pg;
93*094d6818SDavid du Colombier 
949ef1f84bSDavid du Colombier 	if(canlock(&palloc))
959ef1f84bSDavid du Colombier 		panic("pageunchain (palloc %#p)", &palloc);
96*094d6818SDavid du Colombier 	pg = &palloc.avail[p->lg2size];
979ef1f84bSDavid du Colombier 	if(p->prev)
989ef1f84bSDavid du Colombier 		p->prev->next = p->next;
999ef1f84bSDavid du Colombier 	else
100*094d6818SDavid du Colombier 		pg->head = p->next;
1019ef1f84bSDavid du Colombier 	if(p->next)
1029ef1f84bSDavid du Colombier 		p->next->prev = p->prev;
1039ef1f84bSDavid du Colombier 	else
104*094d6818SDavid du Colombier 		pg->tail = p->prev;
1059ef1f84bSDavid du Colombier 	p->prev = p->next = nil;
106*094d6818SDavid du Colombier 	pg->freecount--;
1079ef1f84bSDavid du Colombier }
1089ef1f84bSDavid du Colombier 
1099ef1f84bSDavid du Colombier void
pagechaintail(Page * p)1109ef1f84bSDavid du Colombier pagechaintail(Page *p)
1119ef1f84bSDavid du Colombier {
112*094d6818SDavid du Colombier 	Pallocpg *pg;
113*094d6818SDavid du Colombier 
1149ef1f84bSDavid du Colombier 	if(canlock(&palloc))
1159ef1f84bSDavid du Colombier 		panic("pagechaintail");
116*094d6818SDavid du Colombier 	pg = &palloc.avail[p->lg2size];
117*094d6818SDavid du Colombier 	if(pg->tail) {
118*094d6818SDavid du Colombier 		p->prev = pg->tail;
119*094d6818SDavid du Colombier 		pg->tail->next = p;
1209ef1f84bSDavid du Colombier 	}
1219ef1f84bSDavid du Colombier 	else {
122*094d6818SDavid du Colombier 		pg->head = p;
1234498a243SDavid du Colombier 		p->prev = nil;
1249ef1f84bSDavid du Colombier 	}
125*094d6818SDavid du Colombier 	pg->tail = p;
1264498a243SDavid du Colombier 	p->next = nil;
127*094d6818SDavid du Colombier 	pg->freecount++;
1289ef1f84bSDavid du Colombier }
1299ef1f84bSDavid du Colombier 
1309ef1f84bSDavid du Colombier void
pagechainhead(Page * p)1319ef1f84bSDavid du Colombier pagechainhead(Page *p)
1329ef1f84bSDavid du Colombier {
133*094d6818SDavid du Colombier 	Pallocpg *pg;
134*094d6818SDavid du Colombier 
1359ef1f84bSDavid du Colombier 	if(canlock(&palloc))
1369ef1f84bSDavid du Colombier 		panic("pagechainhead");
137*094d6818SDavid du Colombier 	pg = &palloc.avail[p->lg2size];
138*094d6818SDavid du Colombier 	if(pg->head) {
139*094d6818SDavid du Colombier 		p->next = pg->head;
140*094d6818SDavid du Colombier 		pg->head->prev = p;
1419ef1f84bSDavid du Colombier 	}
1429ef1f84bSDavid du Colombier 	else {
143*094d6818SDavid du Colombier 		pg->tail = p;
1444498a243SDavid du Colombier 		p->next = nil;
1459ef1f84bSDavid du Colombier 	}
146*094d6818SDavid du Colombier 	pg->head = p;
1474498a243SDavid du Colombier 	p->prev = nil;
148*094d6818SDavid du Colombier 	pg->freecount++;
149*094d6818SDavid du Colombier }
150*094d6818SDavid du Colombier 
151*094d6818SDavid du Colombier static Page*
findpg(Page * pl,int color)152*094d6818SDavid du Colombier findpg(Page *pl, int color)
153*094d6818SDavid du Colombier {
154*094d6818SDavid du Colombier 	Page *p;
155*094d6818SDavid du Colombier 
156*094d6818SDavid du Colombier 	for(p = pl; p != nil; p = p->next)
157*094d6818SDavid du Colombier 		if(color == NOCOLOR || p->color == color)
158*094d6818SDavid du Colombier 			return p;
159*094d6818SDavid du Colombier 	return nil;
1609ef1f84bSDavid du Colombier }
1619ef1f84bSDavid du Colombier 
1629ef1f84bSDavid du Colombier /*
1639ef1f84bSDavid du Colombier  * allocate and return a new page for the given virtual address in segment s;
1649ef1f84bSDavid du Colombier  * return nil iff s was locked on entry and had to be unlocked to wait for memory.
1659ef1f84bSDavid du Colombier  */
1669ef1f84bSDavid du Colombier Page*
newpage(int clear,Segment * s,uintptr va,uint lg2pgsize,int color,int locked)167*094d6818SDavid du Colombier newpage(int clear, Segment *s, uintptr va, uint lg2pgsize, int color, int locked)
1689ef1f84bSDavid du Colombier {
1699ef1f84bSDavid du Colombier 	Page *p;
1709ef1f84bSDavid du Colombier 	KMap *k;
1719ef1f84bSDavid du Colombier 	uchar ct;
172*094d6818SDavid du Colombier 	Pallocpg *pg;
173*094d6818SDavid du Colombier 	int i, hw, dontalloc;
174*094d6818SDavid du Colombier 	static int once;
1759ef1f84bSDavid du Colombier 
1769ef1f84bSDavid du Colombier 	lock(&palloc);
177*094d6818SDavid du Colombier 	pg = &palloc.avail[lg2pgsize];
1789ef1f84bSDavid du Colombier 	hw = highwater;
179*094d6818SDavid du Colombier 	for(i = 0;; i++) {
180*094d6818SDavid du Colombier 		if(pg->freecount > hw)
1819ef1f84bSDavid du Colombier 			break;
182*094d6818SDavid du Colombier 		if(up != nil && up->kp && pg->freecount > 0)
1839ef1f84bSDavid du Colombier 			break;
1849ef1f84bSDavid du Colombier 
185*094d6818SDavid du Colombier 		if(i > 3)
186*094d6818SDavid du Colombier 			color = NOCOLOR;
187*094d6818SDavid du Colombier 
188*094d6818SDavid du Colombier 		p = findpg(pg->head, color);
189*094d6818SDavid du Colombier 		if(p != nil)
190*094d6818SDavid du Colombier 			break;
191*094d6818SDavid du Colombier 
192*094d6818SDavid du Colombier 		p = pgalloc(lg2pgsize, color);
193*094d6818SDavid du Colombier 		if(p != nil){
194*094d6818SDavid du Colombier 			pagechainhead(p);
195*094d6818SDavid du Colombier 			break;
196*094d6818SDavid du Colombier 		}
197*094d6818SDavid du Colombier 
1989ef1f84bSDavid du Colombier 		unlock(&palloc);
1999ef1f84bSDavid du Colombier 		dontalloc = 0;
2009ef1f84bSDavid du Colombier 		if(locked){
2019ef1f84bSDavid du Colombier 			qunlock(&s->lk);
2029ef1f84bSDavid du Colombier 			locked = 0;
2039ef1f84bSDavid du Colombier 			dontalloc = 1;
2049ef1f84bSDavid du Colombier 		}
2059ef1f84bSDavid du Colombier 		qlock(&palloc.pwait);	/* Hold memory requesters here */
2069ef1f84bSDavid du Colombier 
2079ef1f84bSDavid du Colombier 		while(waserror())	/* Ignore interrupts */
2089ef1f84bSDavid du Colombier 			;
2099ef1f84bSDavid du Colombier 
210*094d6818SDavid du Colombier 		kickpager(lg2pgsize, color);
211*094d6818SDavid du Colombier 		tsleep(&palloc.r, ispages, pg, 1000);
2129ef1f84bSDavid du Colombier 
2139ef1f84bSDavid du Colombier 		poperror();
2149ef1f84bSDavid du Colombier 
2159ef1f84bSDavid du Colombier 		qunlock(&palloc.pwait);
2169ef1f84bSDavid du Colombier 
2179ef1f84bSDavid du Colombier 		/*
2189ef1f84bSDavid du Colombier 		 * If called from fault and we lost the segment from
2199ef1f84bSDavid du Colombier 		 * underneath don't waste time allocating and freeing
2209ef1f84bSDavid du Colombier 		 * a page. Fault will call newpage again when it has
2219ef1f84bSDavid du Colombier 		 * reacquired the segment locks
2229ef1f84bSDavid du Colombier 		 */
2239ef1f84bSDavid du Colombier 		if(dontalloc)
2249ef1f84bSDavid du Colombier 			return nil;
2259ef1f84bSDavid du Colombier 
2269ef1f84bSDavid du Colombier 		lock(&palloc);
2279ef1f84bSDavid du Colombier 	}
2289ef1f84bSDavid du Colombier 
2299ef1f84bSDavid du Colombier 	/* First try for our colour */
230*094d6818SDavid du Colombier 	for(p = pg->head; p; p = p->next)
231*094d6818SDavid du Colombier 		if(color == NOCOLOR || p->color == color)
2329ef1f84bSDavid du Colombier 			break;
2339ef1f84bSDavid du Colombier 
2349ef1f84bSDavid du Colombier 	ct = PG_NOFLUSH;
2354498a243SDavid du Colombier 	if(p == nil) {
236*094d6818SDavid du Colombier 		p = pg->head;
2379ef1f84bSDavid du Colombier 		p->color = color;
2389ef1f84bSDavid du Colombier 		ct = PG_NEWCOL;
2399ef1f84bSDavid du Colombier 	}
2409ef1f84bSDavid du Colombier 
2419ef1f84bSDavid du Colombier 	pageunchain(p);
2429ef1f84bSDavid du Colombier 
2439ef1f84bSDavid du Colombier 	lock(p);
2449ef1f84bSDavid du Colombier 	if(p->ref != 0)
245406c76faSDavid du Colombier 		panic("newpage: p->ref %d != 0", p->ref);
2469ef1f84bSDavid du Colombier 
2479ef1f84bSDavid du Colombier 	uncachepage(p);
2489ef1f84bSDavid du Colombier 	p->ref++;
2499ef1f84bSDavid du Colombier 	p->va = va;
2509ef1f84bSDavid du Colombier 	p->modref = 0;
2510d74731bSDavid du Colombier 	mmucachectl(p, ct);
2529ef1f84bSDavid du Colombier 	unlock(p);
2539ef1f84bSDavid du Colombier 	unlock(&palloc);
2549ef1f84bSDavid du Colombier 
2559ef1f84bSDavid du Colombier 	if(clear) {
2569ef1f84bSDavid du Colombier 		k = kmap(p);
257a2a67f40SDavid du Colombier 		memset((void*)VA(k), 0, pagesize(p));
2589ef1f84bSDavid du Colombier 		kunmap(k);
2599ef1f84bSDavid du Colombier 	}
2609ef1f84bSDavid du Colombier 
2619ef1f84bSDavid du Colombier 	return p;
2629ef1f84bSDavid du Colombier }
2639ef1f84bSDavid du Colombier 
2649ef1f84bSDavid du Colombier int
ispages(void * a)265*094d6818SDavid du Colombier ispages(void *a)
2669ef1f84bSDavid du Colombier {
267*094d6818SDavid du Colombier 	return ((Pallocpg*)a)->freecount > highwater;
2689ef1f84bSDavid du Colombier }
2699ef1f84bSDavid du Colombier 
2709ef1f84bSDavid du Colombier void
putpage(Page * p)2719ef1f84bSDavid du Colombier putpage(Page *p)
2729ef1f84bSDavid du Colombier {
273*094d6818SDavid du Colombier 	Pallocpg *pg;
274*094d6818SDavid du Colombier 	int rlse;
275*094d6818SDavid du Colombier 
2769ef1f84bSDavid du Colombier 	lock(&palloc);
2779ef1f84bSDavid du Colombier 	lock(p);
2789ef1f84bSDavid du Colombier 
2799ef1f84bSDavid du Colombier 	if(p->ref == 0)
2809ef1f84bSDavid du Colombier 		panic("putpage");
2819ef1f84bSDavid du Colombier 
2829ef1f84bSDavid du Colombier 	if(--p->ref > 0) {
2839ef1f84bSDavid du Colombier 		unlock(p);
2849ef1f84bSDavid du Colombier 		unlock(&palloc);
2859ef1f84bSDavid du Colombier 		return;
2869ef1f84bSDavid du Colombier 	}
2879ef1f84bSDavid du Colombier 
288*094d6818SDavid du Colombier 	rlse = 0;
2899ef1f84bSDavid du Colombier 	if(p->image != nil)
2909ef1f84bSDavid du Colombier 		pagechaintail(p);
291*094d6818SDavid du Colombier 	else{
292*094d6818SDavid du Colombier 		/*
293*094d6818SDavid du Colombier 		 * Free pages if we have plenty in the free list.
294*094d6818SDavid du Colombier 		 */
295*094d6818SDavid du Colombier 		pg = &palloc.avail[p->lg2size];
296*094d6818SDavid du Colombier 		if(pg->freecount > Nfreepgs)
297*094d6818SDavid du Colombier 			rlse = 1;
2989ef1f84bSDavid du Colombier 		else
2999ef1f84bSDavid du Colombier 			pagechainhead(p);
300*094d6818SDavid du Colombier 	}
3019ef1f84bSDavid du Colombier 
3024498a243SDavid du Colombier 	if(palloc.r.p != nil)
3039ef1f84bSDavid du Colombier 		wakeup(&palloc.r);
3049ef1f84bSDavid du Colombier 
3059ef1f84bSDavid du Colombier 	unlock(p);
306*094d6818SDavid du Colombier 	if(rlse)
307*094d6818SDavid du Colombier 		pgfree(p);
3089ef1f84bSDavid du Colombier 	unlock(&palloc);
3099ef1f84bSDavid du Colombier }
3109ef1f84bSDavid du Colombier 
3119ef1f84bSDavid du Colombier Page*
auxpage(uint lg2size)312*094d6818SDavid du Colombier auxpage(uint lg2size)
3139ef1f84bSDavid du Colombier {
3149ef1f84bSDavid du Colombier 	Page *p;
315*094d6818SDavid du Colombier 	Pallocpg *pg;
3169ef1f84bSDavid du Colombier 
3179ef1f84bSDavid du Colombier 	lock(&palloc);
318*094d6818SDavid du Colombier 	pg = &palloc.avail[lg2size];
319*094d6818SDavid du Colombier 	p = pg->head;
320*094d6818SDavid du Colombier 	if(pg->freecount <= highwater) {
3219ef1f84bSDavid du Colombier 		/* memory's tight, don't use it for file cache */
3229ef1f84bSDavid du Colombier 		unlock(&palloc);
3234498a243SDavid du Colombier 		return nil;
3249ef1f84bSDavid du Colombier 	}
3259ef1f84bSDavid du Colombier 	pageunchain(p);
3269ef1f84bSDavid du Colombier 
3279ef1f84bSDavid du Colombier 	lock(p);
3289ef1f84bSDavid du Colombier 	if(p->ref != 0)
3299ef1f84bSDavid du Colombier 		panic("auxpage");
3309ef1f84bSDavid du Colombier 	p->ref++;
3319ef1f84bSDavid du Colombier 	uncachepage(p);
3329ef1f84bSDavid du Colombier 	unlock(p);
3339ef1f84bSDavid du Colombier 	unlock(&palloc);
3349ef1f84bSDavid du Colombier 
3359ef1f84bSDavid du Colombier 	return p;
3369ef1f84bSDavid du Colombier }
3379ef1f84bSDavid du Colombier 
3389ef1f84bSDavid du Colombier static int dupretries = 15000;
3399ef1f84bSDavid du Colombier 
3409ef1f84bSDavid du Colombier int
duppage(Page * p)3419ef1f84bSDavid du Colombier duppage(Page *p)				/* Always call with p locked */
3429ef1f84bSDavid du Colombier {
343*094d6818SDavid du Colombier 	Pallocpg *pg;
3449ef1f84bSDavid du Colombier 	Page *np;
3459ef1f84bSDavid du Colombier 	int color;
3469ef1f84bSDavid du Colombier 	int retries;
3479ef1f84bSDavid du Colombier 
3489ef1f84bSDavid du Colombier 	retries = 0;
3499ef1f84bSDavid du Colombier retry:
3509ef1f84bSDavid du Colombier 	/* don't dup shared page */
3519ef1f84bSDavid du Colombier 	if(p->ref != 1)
3529ef1f84bSDavid du Colombier 		return 0;
3539ef1f84bSDavid du Colombier 
3549ef1f84bSDavid du Colombier 	if(retries++ > dupretries){
3559ef1f84bSDavid du Colombier 		print("duppage %d, up %#p\n", retries, up);
3569ef1f84bSDavid du Colombier 		dupretries += 100;
3579ef1f84bSDavid du Colombier 		if(dupretries > 100000)
3589ef1f84bSDavid du Colombier 			panic("duppage\n");
3599ef1f84bSDavid du Colombier 		uncachepage(p);
3609ef1f84bSDavid du Colombier 		return 1;
3619ef1f84bSDavid du Colombier 	}
3629ef1f84bSDavid du Colombier 
3639ef1f84bSDavid du Colombier 	/* don't dup pages with no image */
3649ef1f84bSDavid du Colombier 	if(p->ref == 0 || p->image == nil || p->image->notext)
3659ef1f84bSDavid du Colombier 		return 0;
3669ef1f84bSDavid du Colombier 
3679ef1f84bSDavid du Colombier 	/* don't dup large pages TO DO? */
3689ef1f84bSDavid du Colombier 	if(p->lg2size != PGSHFT){
3699ef1f84bSDavid du Colombier 		uncachepage(p);
3709ef1f84bSDavid du Colombier 		return 1;
3719ef1f84bSDavid du Colombier 	}
3729ef1f84bSDavid du Colombier 
3739ef1f84bSDavid du Colombier 	/*
3749ef1f84bSDavid du Colombier 	 *  normal lock ordering is to call
3759ef1f84bSDavid du Colombier 	 *  lock(&palloc) before lock(p).
3769ef1f84bSDavid du Colombier 	 *  To avoid deadlock, we have to drop
3779ef1f84bSDavid du Colombier 	 *  our locks and try again.
3789ef1f84bSDavid du Colombier 	 */
3799ef1f84bSDavid du Colombier 	if(!canlock(&palloc)){
3809ef1f84bSDavid du Colombier 		unlock(p);
3819ef1f84bSDavid du Colombier 		if(up)
3829ef1f84bSDavid du Colombier 			sched();
3839ef1f84bSDavid du Colombier 		lock(p);
3849ef1f84bSDavid du Colombier 		goto retry;
3859ef1f84bSDavid du Colombier 	}
3869ef1f84bSDavid du Colombier 
387*094d6818SDavid du Colombier 	pg = &palloc.avail[p->lg2size];
3889ef1f84bSDavid du Colombier 	/* No freelist cache when memory is relatively low */
389*094d6818SDavid du Colombier 	if(pg->freecount <= highwater) {
3909ef1f84bSDavid du Colombier 		unlock(&palloc);
3919ef1f84bSDavid du Colombier 		uncachepage(p);
3929ef1f84bSDavid du Colombier 		return 1;
3939ef1f84bSDavid du Colombier 	}
3949ef1f84bSDavid du Colombier 
395*094d6818SDavid du Colombier 	color = p->color;
396*094d6818SDavid du Colombier 	for(np = pg->head; np; np = np->next)
3979ef1f84bSDavid du Colombier 		if(np->color == color)
3989ef1f84bSDavid du Colombier 			break;
3999ef1f84bSDavid du Colombier 
4009ef1f84bSDavid du Colombier 	/* No page of the correct color */
4014498a243SDavid du Colombier 	if(np == nil) {
4029ef1f84bSDavid du Colombier 		unlock(&palloc);
4039ef1f84bSDavid du Colombier 		uncachepage(p);
4049ef1f84bSDavid du Colombier 		return 1;
4059ef1f84bSDavid du Colombier 	}
4069ef1f84bSDavid du Colombier 
4079ef1f84bSDavid du Colombier 	pageunchain(np);
4089ef1f84bSDavid du Colombier 	pagechaintail(np);
4099ef1f84bSDavid du Colombier 
4109ef1f84bSDavid du Colombier /*
4119ef1f84bSDavid du Colombier * XXX - here's a bug? - np is on the freelist but it's not really free.
4129ef1f84bSDavid du Colombier * when we unlock palloc someone else can come in, decide to
4139ef1f84bSDavid du Colombier * use np, and then try to lock it.  they succeed after we've
4149ef1f84bSDavid du Colombier * run copypage and cachepage and unlock(np).  then what?
4159ef1f84bSDavid du Colombier * they call pageunchain before locking(np), so it's removed
4169ef1f84bSDavid du Colombier * from the freelist, but still in the cache because of
4179ef1f84bSDavid du Colombier * cachepage below.  if someone else looks in the cache
4189ef1f84bSDavid du Colombier * before they remove it, the page will have a nonzero ref
4199ef1f84bSDavid du Colombier * once they finally lock(np).
4209ef1f84bSDavid du Colombier */
4219ef1f84bSDavid du Colombier 	lock(np);
4229ef1f84bSDavid du Colombier 	unlock(&palloc);
4239ef1f84bSDavid du Colombier 
4249ef1f84bSDavid du Colombier 	/* Cache the new version */
4259ef1f84bSDavid du Colombier 	uncachepage(np);
4269ef1f84bSDavid du Colombier 	np->va = p->va;
4279ef1f84bSDavid du Colombier 	np->daddr = p->daddr;
4289ef1f84bSDavid du Colombier 	copypage(p, np);
4299ef1f84bSDavid du Colombier 	cachepage(np, p->image);
4309ef1f84bSDavid du Colombier 	unlock(np);
4319ef1f84bSDavid du Colombier 	uncachepage(p);
4329ef1f84bSDavid du Colombier 
4339ef1f84bSDavid du Colombier 	return 0;
4349ef1f84bSDavid du Colombier }
4359ef1f84bSDavid du Colombier 
4369ef1f84bSDavid du Colombier void
copypage(Page * f,Page * t)4379ef1f84bSDavid du Colombier copypage(Page *f, Page *t)
4389ef1f84bSDavid du Colombier {
4399ef1f84bSDavid du Colombier 	KMap *ks, *kd;
4409ef1f84bSDavid du Colombier 
4419ef1f84bSDavid du Colombier 	if(f->lg2size != t->lg2size)
4429ef1f84bSDavid du Colombier 		panic("copypage");
4439ef1f84bSDavid du Colombier 	ks = kmap(f);
4449ef1f84bSDavid du Colombier 	kd = kmap(t);
445a2a67f40SDavid du Colombier 	memmove((void*)VA(kd), (void*)VA(ks), pagesize(t));
4469ef1f84bSDavid du Colombier 	kunmap(ks);
4479ef1f84bSDavid du Colombier 	kunmap(kd);
4489ef1f84bSDavid du Colombier }
4499ef1f84bSDavid du Colombier 
4509ef1f84bSDavid du Colombier void
uncachepage(Page * p)4519ef1f84bSDavid du Colombier uncachepage(Page *p)			/* Always called with a locked page */
4529ef1f84bSDavid du Colombier {
4539ef1f84bSDavid du Colombier 	Page **l, *f;
4549ef1f84bSDavid du Colombier 
4554498a243SDavid du Colombier 	if(p->image == nil)
4569ef1f84bSDavid du Colombier 		return;
4579ef1f84bSDavid du Colombier 
4589ef1f84bSDavid du Colombier 	lock(&palloc.hashlock);
4599ef1f84bSDavid du Colombier 	l = &pghash(p->daddr);
4604498a243SDavid du Colombier 	for(f = *l; f != nil; f = f->hash) {
4619ef1f84bSDavid du Colombier 		if(f == p) {
4629ef1f84bSDavid du Colombier 			*l = p->hash;
4639ef1f84bSDavid du Colombier 			break;
4649ef1f84bSDavid du Colombier 		}
4659ef1f84bSDavid du Colombier 		l = &f->hash;
4669ef1f84bSDavid du Colombier 	}
4679ef1f84bSDavid du Colombier 	unlock(&palloc.hashlock);
4689ef1f84bSDavid du Colombier 	putimage(p->image);
4694498a243SDavid du Colombier 	p->image = nil;
4709ef1f84bSDavid du Colombier 	p->daddr = 0;
4719ef1f84bSDavid du Colombier }
4729ef1f84bSDavid du Colombier 
4739ef1f84bSDavid du Colombier void
cachepage(Page * p,Image * i)4749ef1f84bSDavid du Colombier cachepage(Page *p, Image *i)
4759ef1f84bSDavid du Colombier {
4769ef1f84bSDavid du Colombier 	Page **l;
4779ef1f84bSDavid du Colombier 
4789ef1f84bSDavid du Colombier 	/* If this ever happens it should be fixed by calling
4799ef1f84bSDavid du Colombier 	 * uncachepage instead of panic. I think there is a race
4809ef1f84bSDavid du Colombier 	 * with pio in which this can happen. Calling uncachepage is
4819ef1f84bSDavid du Colombier 	 * correct - I just wanted to see if we got here.
4829ef1f84bSDavid du Colombier 	 */
4839ef1f84bSDavid du Colombier 	if(p->image)
4849ef1f84bSDavid du Colombier 		panic("cachepage");
4859ef1f84bSDavid du Colombier 
4869ef1f84bSDavid du Colombier 	incref(i);
4879ef1f84bSDavid du Colombier 	lock(&palloc.hashlock);
4889ef1f84bSDavid du Colombier 	p->image = i;
4899ef1f84bSDavid du Colombier 	l = &pghash(p->daddr);
4909ef1f84bSDavid du Colombier 	p->hash = *l;
4919ef1f84bSDavid du Colombier 	*l = p;
4929ef1f84bSDavid du Colombier 	unlock(&palloc.hashlock);
4939ef1f84bSDavid du Colombier }
4949ef1f84bSDavid du Colombier 
4959ef1f84bSDavid du Colombier void
cachedel(Image * i,ulong daddr)4969ef1f84bSDavid du Colombier cachedel(Image *i, ulong daddr)
4979ef1f84bSDavid du Colombier {
4989ef1f84bSDavid du Colombier 	Page *f, **l;
4999ef1f84bSDavid du Colombier 
5009ef1f84bSDavid du Colombier 	lock(&palloc.hashlock);
5019ef1f84bSDavid du Colombier 	l = &pghash(daddr);
5024498a243SDavid du Colombier 	for(f = *l; f != nil; f = f->hash) {
5039ef1f84bSDavid du Colombier 		if(f->image == i && f->daddr == daddr) {
5049ef1f84bSDavid du Colombier 			lock(f);
5059ef1f84bSDavid du Colombier 			if(f->image == i && f->daddr == daddr){
5069ef1f84bSDavid du Colombier 				*l = f->hash;
5079ef1f84bSDavid du Colombier 				putimage(f->image);
5084498a243SDavid du Colombier 				f->image = nil;
5099ef1f84bSDavid du Colombier 				f->daddr = 0;
5109ef1f84bSDavid du Colombier 			}
5119ef1f84bSDavid du Colombier 			unlock(f);
5129ef1f84bSDavid du Colombier 			break;
5139ef1f84bSDavid du Colombier 		}
5149ef1f84bSDavid du Colombier 		l = &f->hash;
5159ef1f84bSDavid du Colombier 	}
5169ef1f84bSDavid du Colombier 	unlock(&palloc.hashlock);
5179ef1f84bSDavid du Colombier }
5189ef1f84bSDavid du Colombier 
5199ef1f84bSDavid du Colombier Page *
lookpage(Image * i,ulong daddr)5209ef1f84bSDavid du Colombier lookpage(Image *i, ulong daddr)
5219ef1f84bSDavid du Colombier {
5229ef1f84bSDavid du Colombier 	Page *f;
5239ef1f84bSDavid du Colombier 
5249ef1f84bSDavid du Colombier 	lock(&palloc.hashlock);
5254498a243SDavid du Colombier 	for(f = pghash(daddr); f != nil; f = f->hash) {
5269ef1f84bSDavid du Colombier 		if(f->image == i && f->daddr == daddr) {
5279ef1f84bSDavid du Colombier 			unlock(&palloc.hashlock);
5289ef1f84bSDavid du Colombier 
5299ef1f84bSDavid du Colombier 			lock(&palloc);
5309ef1f84bSDavid du Colombier 			lock(f);
5319ef1f84bSDavid du Colombier 			if(f->image != i || f->daddr != daddr) {
5329ef1f84bSDavid du Colombier 				unlock(f);
5339ef1f84bSDavid du Colombier 				unlock(&palloc);
5349ef1f84bSDavid du Colombier 				return 0;
5359ef1f84bSDavid du Colombier 			}
5369ef1f84bSDavid du Colombier 			if(++f->ref == 1)
5379ef1f84bSDavid du Colombier 				pageunchain(f);
5389ef1f84bSDavid du Colombier 			unlock(&palloc);
5399ef1f84bSDavid du Colombier 			unlock(f);
5409ef1f84bSDavid du Colombier 
5419ef1f84bSDavid du Colombier 			return f;
5429ef1f84bSDavid du Colombier 		}
5439ef1f84bSDavid du Colombier 	}
5449ef1f84bSDavid du Colombier 	unlock(&palloc.hashlock);
5459ef1f84bSDavid du Colombier 
5464498a243SDavid du Colombier 	return nil;
5479ef1f84bSDavid du Colombier }
5489ef1f84bSDavid du Colombier 
5499ef1f84bSDavid du Colombier uvlong
pagereclaim(int npages)5509ef1f84bSDavid du Colombier pagereclaim(int npages)
5519ef1f84bSDavid du Colombier {
5529ef1f84bSDavid du Colombier 	Page *p;
5539ef1f84bSDavid du Colombier 	uvlong ticks;
554*094d6818SDavid du Colombier 	int i;
5559ef1f84bSDavid du Colombier 
5569ef1f84bSDavid du Colombier 	lock(&palloc);
5579ef1f84bSDavid du Colombier 	ticks = fastticks(nil);
5589ef1f84bSDavid du Colombier 
5599ef1f84bSDavid du Colombier 	/*
5609ef1f84bSDavid du Colombier 	 * All the pages with images backing them are at the
5619ef1f84bSDavid du Colombier 	 * end of the list (see putpage) so start there and work
5629ef1f84bSDavid du Colombier 	 * backward.
5639ef1f84bSDavid du Colombier 	 */
564*094d6818SDavid du Colombier 	for(i = 0; i < nelem(palloc.avail); i++) {
565*094d6818SDavid du Colombier 		for(p = palloc.avail[i].tail; p != nil && p->image != nil && npages > 0; p = p->prev) {
5669ef1f84bSDavid du Colombier 			if(p->ref == 0 && canlock(p)) {
5679ef1f84bSDavid du Colombier 				if(p->ref == 0) {
5689ef1f84bSDavid du Colombier 					npages--;
5699ef1f84bSDavid du Colombier 					uncachepage(p);
570d95ff865SDavid du Colombier 					pageunchain(p);
571d95ff865SDavid du Colombier 					pagechainhead(p);
5729ef1f84bSDavid du Colombier 				}
5739ef1f84bSDavid du Colombier 				unlock(p);
5749ef1f84bSDavid du Colombier 			}
5759ef1f84bSDavid du Colombier 		}
576*094d6818SDavid du Colombier 	}
5779ef1f84bSDavid du Colombier 	ticks = fastticks(nil) - ticks;
5789ef1f84bSDavid du Colombier 	unlock(&palloc);
5799ef1f84bSDavid du Colombier 
5809ef1f84bSDavid du Colombier 	return ticks;
5819ef1f84bSDavid du Colombier }
5829ef1f84bSDavid du Colombier 
5839ef1f84bSDavid du Colombier Pte*
ptecpy(Pte * old)5849ef1f84bSDavid du Colombier ptecpy(Pte *old)
5859ef1f84bSDavid du Colombier {
5869ef1f84bSDavid du Colombier 	Pte *new;
5879ef1f84bSDavid du Colombier 	Page **src, **dst, *pg;
5889ef1f84bSDavid du Colombier 
5899ef1f84bSDavid du Colombier 	new = ptealloc();
5909ef1f84bSDavid du Colombier 	dst = &new->pages[old->first-old->pages];
5919ef1f84bSDavid du Colombier 	new->first = dst;
5929ef1f84bSDavid du Colombier 	for(src = old->first; src <= old->last; src++, dst++){
5939ef1f84bSDavid du Colombier 		if((pg = *src) != nil){
5949ef1f84bSDavid du Colombier 			lock(pg);
5959ef1f84bSDavid du Colombier 			pg->ref++;
5969ef1f84bSDavid du Colombier 			unlock(pg);
5979ef1f84bSDavid du Colombier 			new->last = dst;
5989ef1f84bSDavid du Colombier 			*dst = pg;
5999ef1f84bSDavid du Colombier 		}
6009ef1f84bSDavid du Colombier 	}
6019ef1f84bSDavid du Colombier 
6029ef1f84bSDavid du Colombier 	return new;
6039ef1f84bSDavid du Colombier }
6049ef1f84bSDavid du Colombier 
6059ef1f84bSDavid du Colombier Pte*
ptealloc(void)6069ef1f84bSDavid du Colombier ptealloc(void)
6079ef1f84bSDavid du Colombier {
6089ef1f84bSDavid du Colombier 	Pte *new;
6099ef1f84bSDavid du Colombier 
6109ef1f84bSDavid du Colombier 	new = smalloc(sizeof(Pte));
6119ef1f84bSDavid du Colombier 	new->first = &new->pages[PTEPERTAB];
6129ef1f84bSDavid du Colombier 	new->last = new->pages;
6139ef1f84bSDavid du Colombier 	return new;
6149ef1f84bSDavid du Colombier }
6159ef1f84bSDavid du Colombier 
6169ef1f84bSDavid du Colombier void
freepte(Segment * s,Pte * p)6179ef1f84bSDavid du Colombier freepte(Segment *s, Pte *p)
6189ef1f84bSDavid du Colombier {
6199ef1f84bSDavid du Colombier 	int ref;
6209ef1f84bSDavid du Colombier 	void (*fn)(Page*);
6219ef1f84bSDavid du Colombier 	Page *pt, **pg, **ptop;
6229ef1f84bSDavid du Colombier 
6239ef1f84bSDavid du Colombier 	switch(s->type&SG_TYPE) {
6249ef1f84bSDavid du Colombier 	case SG_PHYSICAL:
6259ef1f84bSDavid du Colombier 		fn = s->pseg->pgfree;
6269ef1f84bSDavid du Colombier 		ptop = &p->pages[PTEPERTAB];
6279ef1f84bSDavid du Colombier 		if(fn) {
6289ef1f84bSDavid du Colombier 			for(pg = p->pages; pg < ptop; pg++) {
6294498a243SDavid du Colombier 				if(*pg == nil)
6309ef1f84bSDavid du Colombier 					continue;
6319ef1f84bSDavid du Colombier 				(*fn)(*pg);
6324498a243SDavid du Colombier 				*pg = nil;
6339ef1f84bSDavid du Colombier 			}
6349ef1f84bSDavid du Colombier 			break;
6359ef1f84bSDavid du Colombier 		}
6369ef1f84bSDavid du Colombier 		for(pg = p->pages; pg < ptop; pg++) {
6379ef1f84bSDavid du Colombier 			pt = *pg;
6384498a243SDavid du Colombier 			if(pt == nil)
6399ef1f84bSDavid du Colombier 				continue;
6409ef1f84bSDavid du Colombier 			lock(pt);
6419ef1f84bSDavid du Colombier 			ref = --pt->ref;
6429ef1f84bSDavid du Colombier 			unlock(pt);
6439ef1f84bSDavid du Colombier 			if(ref == 0)
6449ef1f84bSDavid du Colombier 				free(pt);
6459ef1f84bSDavid du Colombier 		}
6469ef1f84bSDavid du Colombier 		break;
6479ef1f84bSDavid du Colombier 	default:
6489ef1f84bSDavid du Colombier 		for(pg = p->first; pg <= p->last; pg++)
6494498a243SDavid du Colombier 			if(*pg != nil) {
6509ef1f84bSDavid du Colombier 				putpage(*pg);
6514498a243SDavid du Colombier 				*pg = nil;
6529ef1f84bSDavid du Colombier 			}
6539ef1f84bSDavid du Colombier 	}
6549ef1f84bSDavid du Colombier 	free(p);
6559ef1f84bSDavid du Colombier }
656