static char rcsid[] = "$Header: vm_mem.c,v 820.1 86/12/04 19:59:42 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*	vm_mem.c	6.1	83/07/29	*/

#include "../machine/pte.h"
#ifdef s32
#include "../machine/cpu.h"
#endif s32

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/cmap.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/text.h"
#include "../h/vm.h"
#include "../h/file.h"
#include "../h/inode.h"
#include "../h/buf.h"
#include "../h/mount.h"
#include "../h/trace.h"
#include "../h/map.h"
#include "../h/kernel.h"
#ifdef s32
#include "../s32/debug.h"
#endif s32

/*
 * Allocate memory, and always succeed
 * by jolting page-out daemon
 * so as to obtain page frames.
 * To be used in conjunction with vmemfree().
 */
vmemall(pte, size, p, type)
	register struct pte *pte;
	int size;
	struct proc *p;
{
	register int m;

	if (size <= 0 || size > maxmem)
		panic("vmemall size");
	while (size > 0) {
		if (freemem < desfree)
			outofmem();
		while (freemem == 0)
			sleep((caddr_t)&freemem, PSWP+2);
		m = imin(size, freemem);
		(void) memall(pte, m, p, type);
		size -= m;
		pte += m;
	}
	if (freemem < desfree)
		outofmem();
	/*
	 * Always succeeds, but return success for
	 * vgetu and vgetpt (e.g.) which call either
	 * memall or vmemall depending on context.
	 */
	return (1);
}

/*
 * Free valid and reclaimable page frames belonging to the
 * count pages starting at pte.  If a page is valid
 * or reclaimable and locked (but not a system page), then
 * we simply mark the page as c_gone and let the pageout
 * daemon free the page when it is through with it.
 * If a page is reclaimable, and already in the free list, then
 * we mark the page as c_gone, and (of course) don't free it.
 *
 * Determines the largest contiguous cluster of
 * valid pages and frees them in one call to memfree.
 */
vmemfree(pte, count)
	register struct pte *pte;
	register int count;
{
	register struct cmap *c;
	register struct pte *spte;
	register int j;
	int size, pcnt, fileno;

	if (count % CLSIZE)
		panic("vmemfree");
	for (size = 0, pcnt = 0; count > 0; pte += CLSIZE, count -= CLSIZE) {
		if (pte->pg_prot == PG_NOACC && pte->pg_pfnum) {
			printf ("Not releasing virt page 0x%x with *pte 0x%x\n",
			    ptetov(u.u_procp, pte), *((u_long *)pte));
			c = &cmap[pgtocm(((struct pte *)
			    (vtopte(u.u_procp, pte)))->pg_pfnum)];
			printf ("Coremap says 0x%x belongs to pid %d not %d\n",
				(u_long)((struct pte *)
				(vtopte(u.u_procp, pte))->pg_pfnum)<<pageshift,
				(proc + c->c_ndx)->p_pid, u.u_procp->p_pid);
		} else if (pte->pg_fod == 0 && pte->pg_pfnum) {
			c = &cmap[pgtocm(pte->pg_pfnum)];
			pcnt += CLSIZE;
			if (c->c_lock && c->c_type != CSYS) {
				for (j = 0; j < CLSIZE; j++)
					*(int *)(pte+j) &= PG_PROT;
				c->c_gone = 1;
				goto free;
			}
			if (c->c_free) {
				pcnt -= CLSIZE;
				for (j = 0; j < CLSIZE; j++)
					*(int *)(pte+j) &= PG_PROT;
				if (c->c_type == CTEXT)
					distpte(&text[c->c_ndx], (int)c->c_page, pte);
				c->c_gone = 1;
				goto free;
			}
			if (size == 0)
				spte = pte;
			size += CLSIZE;
			continue;
		}
#ifdef notdef
		/* Don't do anything with mapped ptes */
		if (pte->pg_fod && pte->pg_v)
			goto free;
#endif
		if (pte->pg_fod) {
			fileno = ((struct fpte *)pte)->pg_fileno;
			if (fileno < NOFILE)
				panic("vmemfree vread");
			for (j = 0; j < CLSIZE; j++)
				*(int *)(pte+j) &= PG_PROT;
		}
free:
		if (size) {
			memfree(spte, size, 1);
			size = 0;
		}
	}
	if (size)
		memfree(spte, size, 1);
	return (pcnt);
}

/*
 * Unlink a page frame from the free list -
 *
 * Performed if the page being reclaimed
#ifdef s32
 * is in the free list.  Remove the page from
 * the contiguous memory set that it is in by
 * breaking the set in two if necessary.
#else s32
 * is in the free list.
#endif s32
 */
munlink(pf)
	unsigned pf;
{
	register int next, prev;
	register int cmidx = pgtocm(pf);
	register struct cmap *c = &cmap[cmidx];
	register int tail = c->c_pctail;
	register int head = tail - cmap[tail].c_pcsize + 1;

	/*
	 * Adjust size of upper set to include
	 * all entries above cmidx (may be 0).
	 */
	cmap[tail].c_pcsize = tail - cmidx;
	/*
	 * If we are splitting the contiguous set
	 * into two sets then create the lower
	 * one with its tail immediately below the
	 * cmidx'th entry.
	 */
	if (cmidx != head) {
		tail = cmidx - 1;
		cmap[tail].c_pcsize = tail - head + 1;
		while (head <= tail)
			cmap[head++].c_pctail = tail;
	}
	next = c->c_next;
	prev = c->c_prev;
	cmap[prev].c_next = next;
	cmap[next].c_prev = prev;
	c->c_free = 0;
	if (freemem < minfree)
		outofmem();
	freemem -= CLSIZE;
}

#ifdef	COREMAPTRACE

#define	NBUCKETS	2048

typedef struct {
	u_short	cmt_alloc:1,
		cmt_type:3,
		cmt_pfnum:12;
}coremaptrace_t;

coremaptrace_t	cmt_buffer[NBUCKETS];
u_short		cmt_index = 0;
#endif	COREMAPTRACE

/*
 * Allocate memory -
 *
 * The free list appears as a doubly linked list
 * in the core map with cmap[0] serving as a header.
 */
memall(pte, size, p, type)
	register struct pte *pte;
	int size;
	struct proc *p;
{
	int i;
	int s;

	if (size % CLSIZE)
		panic("memall");
	s = splimp();
	if (size > freemem) {
		splx(s);
		return (0);
	}
	trace(TR_MALL, size, u.u_procp->p_pid);
	for (i = size; i > 0; i -= CLSIZE, pte += CLSIZE)
	   clickall(1,pte,p,type,cmap[CMHEAD].c_next);
	splx(s);
	return (size);
}
	
/*
 * Allocate physically contiguous memory
 *
 * Each entry (click) in the core map has a pointer to (actually
 * an index of) the last click in a physically contiguous set which
 * contains that entry.  The entry for the last click in such a set
 * also has a count of the number of physically contiguous clicks
 * in the set.  This count field is zero if the entry is not the last
 * click in a group.  We allocate physically contiguous memory by
 * scanning for a count field that is big enough to contain the
 * requested size.  We allocate the from the first "align"-page
 * boundary of the smallest group that has enough clicks.  We may
 * have to split the contiguous piece of memory in the free list
 * into two pieces.  We return zero if no group has enough.
 */

/*
 * Given the address of the base of the last
 * page of a physically contiguous set of memory,
 * its size, and a desired alignment; return the
 * largest number of pages which begin at an
 * alignment boundary and are contained in the
 * this set.  This may return 0 or negative values
 * if no subset of pages satisfies the alignment
 * requirement.
 */
#define alsize(top,size,align)	(size - ((size-top) & (align-1)))

pcmalloc(havepte, pte, size, p, type, align)
	int havepte;
	struct pte *pte;
	int size;
	struct proc *p;
	int type;
	int align;
{
	register i, besti, bestsize=freemem+1;
	int fragsize;
	int pf;
	int s;

	if (size % CLSIZE)
		panic("memall");
	if (align & (align-1))
		panic("memall: align");
	s = splimp();
	if (size > freemem) {
		splx(s);
		return (0);
	}
	maxpcmem = 0;
	if (align == 1)
	    for (i = cmap[CMHEAD].c_next; i != CMHEAD; i = cmap[i].c_next) {
		if (cmap[i].c_pcsize > maxpcmem) maxpcmem = cmap[i].c_pcsize;
		if (cmap[i].c_pcsize >= size && cmap[i].c_pcsize < bestsize) {
			besti = i; bestsize = cmap[i].c_pcsize;
		}
	    }
	else  /* Special alignment requested */
	    for (i = cmap[CMHEAD].c_next; i != CMHEAD; i = cmap[i].c_next) {
		register int pcsize = cmap[i].c_pcsize;

		if (pcsize > maxpcmem) maxpcmem = pcsize;
		if (alsize(i, pcsize, align) >= size && pcsize < bestsize) {
			besti = i; bestsize = pcsize;
		}
	    }
	if (wantpcmem && maxpcmem >= wantpcmem) {
		wantpcmem = 0;
		wakeup((caddr_t)&wantpcmem);
	}
	if (bestsize == freemem+1) {
		splx(s);
		return(0);
	}
	if (align != 1 && (fragsize = bestsize-alsize(besti,bestsize,align))) {
		int fragtail = besti - bestsize + fragsize;
		int fragi = fragtail;

		bestsize = cmap[besti].c_pcsize -= fragsize;
		cmap[fragtail].c_pcsize = fragsize;
		while (fragsize--)
			cmap[fragi--].c_pctail = fragtail;
	}
	trace(TR_MALL, size, u.u_procp->p_pid);
	pf = cmap[besti].c_pctail;
	pf = cmtopg(pf - cmap[pf].c_pcsize + 1);
	for (i = size; i > 0; i -= CLSIZE, pte += CLSIZE)
	   clickall(havepte,pte,p,type,besti);
	splx(s);
	return(pf);
}

/*
 * Palloc could be removed if all calls to it
 * were replaced with appropriate pcmalloc calls.
 * It exists for backward compatibility with the
 * original Valid UNIX system.
 */
palloc(pages, bytealign)
	int pages;
	int bytealign;
{
	if (bytealign % (int)ctob(1))
		panic("palloc alignment");
	return(pcmalloc(0, 0, pages, 0, CSYS, btoc(bytealign)) << pageshift);
}

/*
 * Scan the core map to update maxpcmem and possibly wake up any waiters
 * on wantpcmem.  Called every few clock ticks via a callout.
 */
scanpcmem()
{
	register i;
	int s;
	static recent = 0;

#ifdef NoLonger

#define MEMERR	++errors, printf
	/*
	 * Check the consistency of the free list and
	 * physically contiguous sets.  If there are any
	 * problems report them and enter the debugger.
	 */
	if (debug & D_PMEM) {
	    int cmidx;
	    int freecount = 0;
	    int errors = 0;
	    int tail = -1;
	    int size = 0;
	    struct cmap *c;

	    s = splimp();
	    for (cmidx=1; cmidx < ecmx; ++cmidx) {
	        c = &cmap[cmidx];
	        if (c->c_free) {
	            ++freecount;
	            if (tail != -1 && c->c_pctail != tail)
	                MEMERR("bad tail 0x%x in entry 0x%x, should be 0x%x\n",
	                    c->c_pctail, cmidx, tail);
	            if (tail == -1)
	                tail = c->c_pctail, size = 0;
	            ++size;
	            if (tail == 0)
	                MEMERR("zero length tail for 0x%x\n", cmidx);
	            if (cmidx > tail)
	                MEMERR("bad tail 0x%x for entry 0x%x\n", tail, cmidx);
	            if (cmidx != tail && c->c_pcsize != 0)
	                MEMERR("size 0x%x on non-tail 0x%x\n",
	                    c->c_pcsize, cmidx);
	            if (cmidx == tail && c->c_pcsize != size)
	                MEMERR("bad size 0x%x for tail 0x%x, should be 0x%x\n",
	                    c->c_pcsize, tail, size);
	            if (cmidx == tail && cmidx < ecmx-1 &&
	                cmap[cmidx+1].c_free)
	                MEMERR("tail 0x%x followed by free entry\n", cmidx);
	            if (cmidx == tail) tail = -1;
	        }
	        else if (tail != -1)
	            MEMERR("allocated entry 0x%x within set 0x%x\n",
	                cmidx, tail);
	    }
	    if (freecount != freemem)
	        MEMERR("scanpcmem: array free count = 0x%x, freemem = 0x%x\n",
	            freecount, freemem);
	    freecount = 0;
	    c = &cmap[cmap[CMHEAD].c_next];
	    cmidx = cmap[CMHEAD].c_next;
	    for (; cmidx != CMHEAD; cmidx = cmap[cmidx].c_next) {
	        c = &cmap[cmidx];
	        if (c->c_free == 0)
	            MEMERR("entry 0x%x not free but in free list\n", c - cmap);
	        if (cmap[c->c_next].c_prev != cmidx)
	            MEMERR("prev/next mismatch: 0x%x -> 0x%x but 0x%x <- 0x%x\n",
	                cmidx, c->c_next,
	                cmap[c->c_next].c_prev, c->c_next);
	        ++freecount;
	    }
	    if (freecount != freemem)
	        MEMERR("scanpcmem: list free count = 0x%x, freemem = 0x%x\n",
	            freecount, freemem);
	    if (errors) cdebugger("scanpcmem");
	    splx(s);
	}
#endif NoLonger
	if (wantpcmem || ++recent > 6) {
		s = splimp();
		maxpcmem = 0;
		for (i = cmap[CMHEAD].c_next; i != CMHEAD; i = cmap[i].c_next)
			if (cmap[i].c_pcsize > maxpcmem)
				 maxpcmem = cmap[i].c_pcsize;
		if (wantpcmem && maxpcmem >= wantpcmem) {
			wantpcmem = 0;
			wakeup((caddr_t)&wantpcmem);
		}
		splx(s);
		recent = 0;
	}
	timeout(scanpcmem,0,hz);
}


clickall(havepte, pte, p, type, cmidx)
	int havepte;
	register struct pte *pte;
	struct proc *p;
	int type;
	int cmidx;
{
	register struct cmap *c, *c1, *c2;
	register struct pte *rpte;
	register struct proc *rp;
	int j, curpos;
	unsigned pf;

	curpos = cmap[cmidx].c_pctail;
	curpos -= --cmap[curpos].c_pcsize;
	c = &cmap[curpos];
#ifdef	COREMAPTRACE
	cmt_buffer[cmt_index].cmt_alloc = 1;
	cmt_buffer[cmt_index].cmt_type = type;
	cmt_buffer[cmt_index].cmt_pfnum = cmtopg (curpos);
	if (++cmt_index == NBUCKETS) cmt_index = 0;
#endif	COREMAPTRACE
	freemem -= CLSIZE;
	cmap[c->c_prev].c_next = c->c_next;
	cmap[c->c_next].c_prev = c->c_prev;
	if (c->c_free == 0)
		panic("dup mem alloc");
	if (cmtopg(curpos) > maxfree)
		panic("bad mem alloc");
	if (c->c_gone == 0 && c->c_type != CSYS) {
		if (c->c_type == CTEXT)
			rp = text[c->c_ndx].x_caddr;
		else
			rp = &proc[c->c_ndx];
		while (rp->p_flag & SNOVM)
			rp = rp->p_xlink;
		switch (c->c_type) {

		case CTEXT:
			rpte = tptopte(rp, c->c_page);
			break;

		case CDATA:
			rpte = dptopte(rp, c->c_page);
			break;

		case CSTACK:
			rpte = sptopte(rp, c->c_page);
			break;
		}
		zapcl(rpte, pg_pfnum) = 0;
		if (c->c_type == CTEXT)
			distpte(&text[c->c_ndx], (int)c->c_page, rpte);
	}
#ifdef s32
	if (havepte)
#endif s32
	switch (type) {

	case CSYS:
		c->c_ndx = p->p_ndx;
		break;

	case CTEXT:
		c->c_page = vtotp(p, ptetov(p, pte));
		c->c_ndx = p->p_textp - &text[0];
		break;

	case CDATA:
		c->c_page = vtodp(p, ptetov(p, pte));
		c->c_ndx = p->p_ndx;
		break;

	case CSTACK:
		c->c_page = vtosp(p, ptetov(p, pte));
		c->c_ndx = p->p_ndx;
		break;
	}
	if (c->c_blkno) {
		/*
		 * This is very like munhash(), except
		 * that we really don't want to bother
		 * to calculate a dev to pass to it.
		 */
		j = CMHASH(c->c_blkno);
		c1 = &cmap[cmhash[j]];
		if (c1 == c)
			cmhash[j] = c1->c_hlink;
		else {
			for (;;) {
				if (c1 == ecmap)
					panic("memall ecmap");
				c2 = c1;
				c1 = &cmap[c2->c_hlink];
				if (c1 == c)
					break;
			}
			c2->c_hlink = c1->c_hlink;
		}
		if (mfind(c->c_mdev == MSWAPX ?
		      swapdev : mount[c->c_mdev].m_dev,
		      (daddr_t)(u_long)c->c_blkno))
			panic("memall mfind");
		c1->c_mdev = 0;
		c1->c_blkno = 0;
		c1->c_hlink = 0;
	}
	pf = cmtopg(curpos);
#ifdef s32
	if (havepte) for (j = 0; j < CLSIZE; j++)
#else s32
	for (j = 0; j < CLSIZE; j++)
#endif s32
		*(int *)pte++ = pf++;
	c->c_free = 0;
	c->c_gone = 0;
	if (c->c_intrans || c->c_want)
		panic("memall intrans|want");
	c->c_lock = 1;
	trace (TR_MALL, ptetov(p, rpte), p->p_pid);
	c->c_type = type;
}


/*
 * Free memory -
 *
 * The page frames being returned are inserted
 * to the head/tail of the free list depending
 * on whether there is any possible future use of them.
 *
 * If the freemem count had been zero,
 * the processes sleeping for memory
 * are awakened.
 */
memfree(pte, size, detach)
	register struct pte *pte;
	register int size;
{
	register int i, j, prev, next;
	register struct cmap *c;
	int s;
	
	if (size % CLSIZE)
		panic("memfree");
	if (freemem < CLSIZE * KLMAX)
		wakeup((caddr_t)&freemem);
	while (size > 0) {
		size -= CLSIZE;
		trace (TR_MFREE, ptetov(u.u_procp, pte), u.u_procp->p_pid);
		i = pte->pg_pfnum;
#ifdef s32 /* LOCKPAGES */
		/*
		 * Assumes that the reason that the page was
		 * locked has gone away.
		 */
		pte->pg_ulock = 0;
#endif s32 /* LOCKPAGES */
		if (i < firstfree || i > maxfree)
			panic("bad mem free");
		i = pgtocm(i);
		c = &cmap[i];
#ifdef	COREMAPTRACE
		cmt_buffer[cmt_index].cmt_alloc = 0;
		cmt_buffer[cmt_index].cmt_type = c->c_type;
		cmt_buffer[cmt_index].cmt_pfnum = cmtopg (i);
		if (++cmt_index == NBUCKETS) cmt_index = 0;
#endif	COREMAPTRACE
		if (c->c_free)
			panic("dup mem free");
		if (detach && c->c_type != CSYS) {
			for (j = 0; j < CLSIZE; j++)
				*(int *)(pte+j) &= PG_PROT;
			c->c_gone = 1;
		}
		s = splimp();

		if (i+1 < ecmx && cmap[i+1].c_free) {
			c->c_pctail = cmap[i+1].c_pctail;
			++cmap[c->c_pctail].c_pcsize;
		}
		else {	c->c_pctail = i; c->c_pcsize = 1;  }
		if (i > 1 && cmap[i-1].c_free) {
			if (cmap[i-1].c_pctail != i-1 ||
			    cmap[i-1].c_pcsize == 0)
				panic("memfree low merge");
			cmap[c->c_pctail].c_pcsize +=
				cmap[i-1].c_pcsize;
			for (c = &cmap[i-cmap[i-1].c_pcsize]; c != &cmap[i];
									c++)
				c->c_pctail = cmap[i].c_pctail;
			cmap[i-1].c_pcsize = 0;
		}

		if (detach && c->c_blkno == 0) {
			next = cmap[CMHEAD].c_next;
			cmap[next].c_prev = i;
			c->c_prev = CMHEAD;
			c->c_next = next;
			cmap[CMHEAD].c_next = i;
		} else {
			prev = cmap[CMHEAD].c_prev;
			cmap[prev].c_next = i;
			c->c_next = CMHEAD;
			c->c_prev = prev;
			cmap[CMHEAD].c_prev = i;
		}
		c->c_free = 1;
		freemem += CLSIZE;
		splx(s);
		pte += CLSIZE;
	}
}

/*
 * Allocate wired-down (non-paged) pages in kernel virtual memory.
 */
caddr_t
wmemall(pmemall, n)
	int (*pmemall)(), n;
{
	register int npg;
	register caddr_t va;
	register int a;

	npg = clrnd(btoc(n));
	a = rmalloc(kernelmap, (long)npg);
	if (a == 0)
		return (0);
	if ((*pmemall)(&Usrptmap[a], npg, &proc[0], CSYS) == 0) {
		rmfree(kernelmap, (long)npg, (long)a);
		return (0);
	}
	va = (caddr_t) kmxtob(a);
	vmaccess(&Usrptmap[a], va, npg);
	return (va);
}

/*
 * Allocate wired-down (non-paged) pages in kernel virtual memory.
 * (and clear them)
 */
caddr_t
zmemall(pmemall, n)
	int (*pmemall)(), n;
{
	register int npg;
	register caddr_t va;
	register int a;

	npg = clrnd(btoc(n));
	a = rmalloc(kernelmap, (long)npg);
	if (a == 0)
		return (0);
	if ((*pmemall)(&Usrptmap[a], npg, &proc[0], CSYS) == 0) {
		rmfree(kernelmap, (long)npg, (long)a);
		return (0);
	}
	va = (caddr_t) kmxtob(a);
	vmaccess(&Usrptmap[a], va, npg);
	while (--npg >= 0)
		clearpage((unsigned)(PG_PFNUM & *(int *)&Usrptmap[a++]));
	return (va);
}

wmemfree(va, n)
	caddr_t va;
	int n;
{
	register int a, npg;

	a = btokmx((struct pte *) va);
	npg = clrnd(btoc(n));
		(void) memfree(&Usrptmap[a], npg, 0);
	rmfree(kernelmap, (long)npg, (long)a);
}

/*
 * Enter clist block c on the hash chains.
 * It contains file system block bn from device dev.
 * Dev must either be a mounted file system or the swap device
 * so we panic if getfsx() cannot find it.
 */
mhash(c, dev, bn)
	register struct cmap *c;
	dev_t dev;
	daddr_t bn;
{
	register int i = CMHASH(bn);

	c->c_hlink = cmhash[i];
	cmhash[i] = c - cmap;
	c->c_blkno = bn;
	i = getfsx(dev);
	if (i == -1)
		panic("mhash");
	c->c_mdev = i;
}

/*
 * Pull the clist entry of <dev,bn> off the hash chains.
 * We have checked before calling (using mfind) that the
 * entry really needs to be unhashed, so panic if we can't
 * find it (can't happen).
 */
munhash(dev, bn)
	dev_t dev;
	daddr_t bn;
{
	register int i = CMHASH(bn);
	register struct cmap *c1, *c2;
	int si = splimp();

	c1 = &cmap[cmhash[i]];
	if (c1 == ecmap)
		panic("munhash");
	if (c1->c_blkno == bn && getfsx(dev) == c1->c_mdev)
		cmhash[i] = c1->c_hlink;
	else {
		for (;;) {
			c2 = c1;
			c1 = &cmap[c2->c_hlink];
			if (c1 == ecmap)
				panic("munhash");
			if (c1->c_blkno == bn && getfsx(dev) == c1->c_mdev)
				break;
		}
		c2->c_hlink = c1->c_hlink;
	}
	if (mfind(dev, bn))
		panic("munhash mfind");
	c1->c_mdev = 0;
	c1->c_blkno = 0;
	c1->c_hlink = 0;
	splx(si);
}

/*
 * Look for block bn of device dev in the free pool.
 * Currently it should not be possible to find it unless it is
 * c_free and c_gone, although this may later not be true.
 * (This is because active texts are locked against file system
 * writes by the system.)
 */
struct cmap *
mfind(dev, bn)
	dev_t dev;
	daddr_t bn;
{
	register struct cmap *c1 = &cmap[cmhash[CMHASH(bn)]];
	int si = splimp();

	while (c1 != ecmap) {
		if (c1->c_blkno == bn && c1->c_mdev == getfsx(dev))
			return (c1);
		c1 = &cmap[c1->c_hlink];
	}
	splx(si);
	return ((struct cmap *)0);
}

/*
 * Purge blocks from device dev from incore cache
 * before umount().
 */
mpurge(mdev)
	int mdev;
{
	register struct cmap *c1, *c2;
	register int i;
	int si = splimp();

	for (i = 0; i < CMHSIZ; i++) {
more:
		c1 = &cmap[cmhash[i]];
		if (c1 == ecmap)
			continue;
		if (c1->c_mdev == mdev)
			cmhash[i] = c1->c_hlink;
		else {
			for (;;) {
				c2 = c1;
				c1 = &cmap[c1->c_hlink];
				if (c1 == ecmap)
					goto cont;
				if (c1->c_mdev == mdev)
					break;
			}
			c2->c_hlink = c1->c_hlink;
		}
		c1->c_mdev = 0;
		c1->c_blkno = 0;
		c1->c_hlink = 0;
		goto more;
cont:
		;
	}
	splx(si);
}

/*
 * Initialize core map
 */
#ifdef s32
meminit(first, last)
	int first, last;
{
	register int i;
	register struct cmap *c;
	register int lastmap = -1;
	register int firstmap;
	firstfree = clrnd(first);
	maxfree = clrnd(last - (CLSIZE - 1));
	freemem = 0;
	ecmx = ecmap - cmap;
	for (i=1; i<ecmx; ++i)
	{
		register int page = cmtopg(i);
		register int limit = page + CLSIZE;
		register int pagesfree = 0;

		while (page < limit)
			if (ispagfree(page++))
				++pagesfree;
		/*
		 * All pages in this click are free so
		 * link the map entry into the free list.
		 */
		if (pagesfree == CLSIZE)
		{
			freemem += CLSIZE;
			if (lastmap == -1)
				firstmap = i;
			else
				cmap[lastmap].c_next = i;
			c = &cmap[i];
			c->c_prev = lastmap;
			c->c_free = 1;
			c->c_gone = 1;
			c->c_type = CSYS;
			c->c_mdev = 0;
			c->c_blkno = 0;
			lastmap = i;
		}
		/*
		 * There is at least one page which is not
		 * free so the click cannot be freed.  Make
		 * sure that the click will never be used.
		 */
		else
		{
			c = &cmap[i];
			c->c_free = 0;
			c->c_gone = 1;
			c->c_type = CSYS;
			c->c_mdev = 0;
			c->c_blkno = 0;
		}
	}
	cmap[lastmap].c_next = CMHEAD;
	cmap[firstmap].c_prev = CMHEAD;
	for (i = 0; i < CMHSIZ; i++)
		cmhash[i] = ecmx;
	cmap[CMHEAD].c_next = firstmap;
	cmap[CMHEAD].c_prev = lastmap;
	cmap[CMHEAD].c_type = CSYS;
	avefree = freemem;
	hand = 0;
	{
		register int j=CMHEAD;
		for (i=1; i<=ecmx; i++) {
			if (i < ecmx && cmap[i].c_free && j==CMHEAD) j=i;
			else if (i==ecmx || (!cmap[i].c_free && j != CMHEAD)) {
				cmap[i-1].c_pcsize = i-j;
				while (j < i) cmap[j++].c_pctail = i-1;
				j = CMHEAD;
			}
		}
	}
}
#else s32
meminit(first, last)
	int first, last;
{
	register int i;
	register struct cmap *c;

	firstfree = clrnd(first);
	maxfree = clrnd(last - (CLSIZE - 1));
	freemem = maxfree - firstfree;
	ecmx = ecmap - cmap;
	if (ecmx < freemem / CLSIZE)
		freemem = ecmx * CLSIZE;
	for (i = 1; i <= freemem / CLSIZE; i++) {
		cmap[i-1].c_next = i;
		c = &cmap[i];
		c->c_prev = i-1;
		c->c_free = 1;
		c->c_gone = 1;
		c->c_type = CSYS;
		c->c_mdev = 0;
		c->c_blkno = 0;
	}
	cmap[freemem / CLSIZE].c_next = CMHEAD;
	for (i = 0; i < CMHSIZ; i++)
		cmhash[i] = ecmx;
	cmap[CMHEAD].c_prev = freemem / CLSIZE;
	cmap[CMHEAD].c_type = CSYS;
	avefree = freemem;
	hand = 0;
}
#endif s32

/*
 * Wait for frame pf to become unlocked
 * if it is currently locked.
 *
 * THIS ROUTINE SHOULD TAKE A CMAP STRUCTURE AS ARGUMENT.
 */
mwait(pf)
	unsigned pf;
{

	mlock(pf);
	munlock(pf);
}

/*
 * Lock a page frame.
 *
 * THIS ROUTINE SHOULD TAKE A CMAP STRUCTURE AS ARGUMENT.
 */
mlock(pf)
	unsigned pf;
{
	register struct cmap *c = &cmap[pgtocm(pf)];

	while (c->c_lock) {
		c->c_want = 1;
		sleep((caddr_t)c, PSWP+1);
	}
	c->c_lock = 1;
}

/*
 * Unlock a page frame.
 *
 * THIS ROUTINE SHOULD TAKE A CMAP STRUCTURE AS ARGUMENT.
 */
munlock(pf)
	unsigned pf;
{
	register struct cmap *c = &cmap[pgtocm(pf)];

	if (c->c_lock == 0)
		panic("dup page unlock");
	if (c->c_want)
		wakeup((caddr_t)c);
	c->c_lock = 0;
	c->c_want = 0;
}

/* 
 * Lock a virtual segment.
 *
 * For each cluster of pages, if the cluster is not valid,
 * touch it to fault it in, otherwise just lock page frame.
 * Called from physio to ensure that the pages 
 * participating in raw i/o are valid and locked.
 */
vslock(base, count)
	caddr_t base;
{
	register unsigned v;
	register int npf;
	register struct pte *pte;

	v = btop(base);
	pte = vtopte(u.u_procp, v);
	npf = btoc(count + ((int)base & CLOFSET));
	while (npf > 0) {
		if (pte->pg_v) 
			mlock(pte->pg_pfnum);
		else
			pagein(ctob(v), 1);	/* return it locked */
		pte += CLSIZE;
		v += CLSIZE;
		npf -= CLSIZE;
	}
}

/* 
 * Unlock a virtual segment.
 */
vsunlock(base, count, rw)
	caddr_t base;
{
	register struct pte *pte;
	register int npf;

	pte = vtopte(u.u_procp, btop(base));
	npf = btoc(count + ((int)base & CLOFSET));
	while (npf > 0) {
		munlock(pte->pg_pfnum);
		if (rw == B_READ)	/* Reading from device writes memory */
			pte->pg_m = 1;
		pte += CLSIZE;
		npf -= CLSIZE;
	}
}
