static char rcsid[] = "$Header: uipc_mbuf.c,v 820.1 86/12/04 19:59:29 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 		*
 *	Incoroporated.							*
 *									*
 *	The copyright notice appearing above is included to provide	*
 *	statutory protection in the event of unauthorized or 		*
 *	unintentional public disclosure.				*
 *									*
 ************************************************************************/

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

#include "../machine/pte.h"

#include "../h/param.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/cmap.h"
#include "../h/map.h"
#include "../h/mbuf.h"
#include "../h/vm.h"
#include "../h/kernel.h"

mbinit()
{

#ifdef s32
	if (m_clalloc(8*4096/CLBYTES, MPG_MBUFS) == 0)
		goto bad;
#else s32
	if (m_clalloc(4096/CLBYTES, MPG_MBUFS) == 0)
		goto bad;
	if (m_clalloc(8*4096/CLBYTES, MPG_CLUSTERS) == 0)
		goto bad;
#endif s32
	return;
bad:
	panic("mbinit");
}

caddr_t
m_clalloc(ncl, how)
	register int ncl;
	int how;
{
	int npg, mbx;
	register struct mbuf *m;
	register int i;
	register caddr_t maddr;
	int s;

	npg = ncl * CLSIZE;
	s = splimp();		/* careful: rmalloc isn't reentrant */
	mbx = rmalloc(mbmap, (long)npg);
	splx(s);
	if (mbx == 0)
		return (0);
	m = cltom(mbx / CLSIZE);
	if (memall(&Mbmap[mbx], npg, proc, CSYS) == 0) {
		s = splimp();
		rmfree(mbmap, (long)npg, (long)mbx);
		splx(s);
		return (0);
	}
	vmaccess(&Mbmap[mbx], (caddr_t)m, npg);
	switch (how) {

	case MPG_CLUSTERS:
		s = splimp();
		for (i = 0; i < ncl; i++) {
			m->m_off = 0;
			m->m_next = mclfree;
			mclfree = m;
			m += CLBYTES / sizeof (*m);
			mbstat.m_clfree++;
		}
		mbstat.m_clusters += ncl;
		splx(s);
		break;

	case MPG_MBUFS:
		maddr = (caddr_t)m;
		for (i = ncl * CLBYTES / sizeof (*m); i > 0; i--) {
			m->m_off = 0;
			m->m_type = MT_DATA;
			mbstat.m_mtypes[MT_DATA]++;
			mbstat.m_mbufs++;
			(void) m_free(m);
			m++;
		}
		break;
	}
	return ((caddr_t)m);
}

m_pgfree(addr, n)
	caddr_t addr;
	int n;
{

#ifdef lint
	addr = addr; n = n;
#endif
}

m_expand()
{

	if (m_clalloc(1, MPG_MBUFS) == 0)
		goto steal;
	return (1);
steal:
	/* should ask protocols to free code */
	return (0);
}

/* NEED SOME WAY TO RELEASE SPACE */

/*
 * Space allocation routines.
 * These are also available as macros
 * for critical paths.
 */
struct mbuf *
m_get(canwait, type)
	int canwait, type;
{
	register struct mbuf *m;

	MGET(m, canwait, type);
	return (m);
}

struct mbuf *
m_getclr(canwait, type)
	int canwait, type;
{
	register struct mbuf *m;

	m = m_get(canwait, type);
	if (m == 0)
		return (0);
	bzero(mtod(m, caddr_t), MLEN);
	return (m);
}

struct mbuf *
m_free(m)
	struct mbuf *m;
{
	register struct mbuf *n;

	MFREE(m, n);
	return (n);
}

/*ARGSUSED*/
struct mbuf *
m_more(canwait, type)
	int canwait, type;
{
	register struct mbuf *m;

	if (!m_expand()) {
		mbstat.m_drops++;
		return (NULL);
	}
#define m_more(x,y) (panic("m_more"), (struct mbuf *)0)
	MGET(m, canwait, type);
#undef m_more
	return (m);
}

m_freem(m)
	register struct mbuf *m;
{
	register struct mbuf *n;
	register int s;

	if (m == NULL)
		return;
	s = splimp();
	do {
		MFREE(m, n);
	} while (m = n);
	splx(s);
}

/*
 * Mbuffer utility routines.
 */
struct mbuf *
m_copy(m, off, len)
	register struct mbuf *m;
	int off;
	register int len;
{
	register struct mbuf *n, **np;
	struct mbuf *top, *p;
	int type;

	if (len == 0)
		return (0);
	if (off < 0 || len < 0)
		panic("m_copy");
	type = m->m_type;
	while (off > 0) {
		if (m == 0)
			panic("m_copy");
		if (off < m->m_len)
			break;
		off -= m->m_len;
		m = m->m_next;
	}
	np = &top;
	top = 0;
	while (len > 0) {
		if (m == 0) {
			if (len != M_COPYALL)
				panic("m_copy");
			break;
		}
		MGET(n, M_WAIT, type);
		*np = n;
		if (n == 0)
			goto nospace;
		n->m_len = MIN(len, m->m_len - off);
		if (m->m_off > MMAXOFF) {
			p = mtod(m, struct mbuf *);
			n->m_off = ((int)p - (int)n) + off;
			mclrefcnt[mtocl(p)]++;
		} else
			bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t),
			    (unsigned)n->m_len);
		if (len != M_COPYALL)
			len -= n->m_len;
		off = 0;
		m = m->m_next;
		np = &n->m_next;
	}
	return (top);
nospace:
	m_freem(top);
	return (0);
}

m_cat(m, n)
	register struct mbuf *m, *n;
{
	while (m->m_next)
		m = m->m_next;
	while (n) {
		if (m->m_off >= MMAXOFF ||
		    m->m_off + m->m_len + n->m_len > MMAXOFF) {
			/* just join the two chains */
			m->m_next = n;
			return;
		}
		/* splat the data from one into the other */
		bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
		    (u_int)n->m_len);
		m->m_len += n->m_len;
		n = m_free(n);
	}
}

m_adj(mp, len)
	struct mbuf *mp;
	register int len;
{
	register struct mbuf *m, *n;

	if ((m = mp) == NULL)
		return;
	if (len >= 0) {
		while (m != NULL && len > 0) {
			if (m->m_len <= len) {
				len -= m->m_len;
				m->m_len = 0;
				m = m->m_next;
			} else {
				m->m_len -= len;
				m->m_off += len;
				break;
			}
		}
	} else {
		/* a 2 pass algorithm might be better */
		len = -len;
		while (len > 0 && m->m_len != 0) {
			while (m != NULL && m->m_len != 0) {
				n = m;
				m = m->m_next;
			}
			if (n->m_len <= len) {
				len -= n->m_len;
				n->m_len = 0;
				m = mp;
			} else {
				n->m_len -= len;
				break;
			}
		}
	}
}

struct mbuf *
m_pullup(m0, len)
	struct mbuf *m0;
	int len;
{
	register struct mbuf *m, *n;
	int count;

	n = m0;
	if (len > MLEN)
		goto bad;
	MGET(m, M_DONTWAIT, n->m_type);
	if (m == 0)
		goto bad;
	m->m_len = 0;
	do {
		count = MIN(MLEN - m->m_len, len);
		if (count > n->m_len)
			count = n->m_len;
		bcopy(mtod(n, caddr_t), mtod(m, caddr_t)+m->m_len,
		  (unsigned)count);
		len -= count;
		m->m_len += count;
		n->m_off += count;
		n->m_len -= count;
		if (n->m_len)
			break;
		n = m_free(n);
	} while (n);
	if (len) {
		(void) m_free(m);
		goto bad;
	}
	m->m_next = n;
	return (m);
bad:
	m_freem(n);
	return (0);
}

/*
 * mbufcount --
 *	counts the number of mbufs in a chain.
 */
mbufcount(m)
	struct mbuf *m;
{
	register struct mbuf *mp;
	register int i = 0;

	for (mp = m; mp; mp = mp->m_next)
		i ++;

	return i;
}

/*
 * mbuflen --
 *	Returns sum of lengths
 */
mbuflen(m)
	struct mbuf *m;
{
	register struct mbuf *mp;
	register int i = 0;

	for (mp = m; mp; mp = mp->m_next)
		i += mp->m_len;

	return i;
}


/*
 * m_compact --
 *	panic
 */
m_compact()
{
	panic("m_compact");
}

/*
 * mbufvtop --
 *	Convert mbuf virtual addresses into physical addresses
 */
mbufvtop(v)
	register u_long v;
{
	register u_long index;
	register struct pte *p;
	register u_long returnval;
	register short pfnum;

	if (v < (u_long)( proc0Initd
				? mbutl(u.u_procp)
				: mbUtl
			)
	   )
		panic("mbufvtop");

	index = (v - (u_long)( proc0Initd
					? mbutl(u.u_procp)
					: mbUtl
			     )
		) >> PGSHIFT;
	p = &Mbmap[index];
	pfnum = p->pg_pfnum;

	if (!pfnum)
		panic("mbufvtop: pfnum");

	returnval = (pfnum << PGSHIFT | v & PGOFSET);

	return(returnval);
}
