Ultrix-3.1/sys/sys/mbuf11.c
/**********************************************************************
* Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. *
* All Rights Reserved. *
* Reference "/usr/src/COPYRIGHT" for applicable restrictions. *
**********************************************************************/
/*
* SCCSID: @(#)mbuf11.c 3.0 4/21/86
* based on mbuf11.c 1.01 82/09/23
*/
#include <sys/param.h>
#ifdef UCB_NET
#include <sys/seg.h>
#include <sys/mbuf.h>
#include <netinet/in_systm.h>
/*#define debug 1 /* consistency checks */
#ifdef debug
#define MBTRACE(type,arg) mbtrace(type,arg)
#else
#define MBTRACE(t,a)
#endif
#define IOSIZE 8192 /* area for DMA buffers */
#define NMBCACHE 8 /* cache of mbufs */
extern int nwords; /* init arena size, bytes=(nwords-2)*WORD */
#define MBXGET(c) { \
if ((c) = mbfree) { \
mapseg5(mbfree,MBMAPSIZE); if (MBX->m_ref) panic("MBXGET"); \
MBX->m_ref = 1; MBTRACE(6,mbfree); mbfree = (u_int)MBX->m_mbuf; }}
#define MBXFREE(c) { \
mapseg5(c,MBMAPSIZE); if (MBX->m_ref != 1) panic("MBXFREE"); \
MBX->m_ref = 0; MBX->m_type = 0; MBTRACE(7,c); MBX->m_mbuf = (struct mbuf *)mbfree; \
mbfree = (c); }
struct mbuf *mbcache[NMBCACHE]; /* mbuf cache for fast allocation */
int mbicache; /* # entries in cache and index of next add */
extern memaddr mbbase; /* click address of mbuffer base */
extern u_int mbsize; /* size of mbuf area */
u_int mbend;
u_int miobase; /* click of DMA area */
extern u_int miosize; /* size of area used for DMA */
u_int mbfree; /* free list */
int m_want, ms_want;
/*
* Get an mbuf, consisting of an inaddress header (mbuf) and
* an external data portion (mbufx). Try to save some time by using
* the cache.
*/
struct mbuf *
m_bget(canwait, type)
int canwait;
int type;
{
register struct mbuf *m;
int s = splimp();
u_int click;
segm map5;
saveseg5(map5);
for (;;) {
if (mbicache) {
m = mbcache[--mbicache];
mbstat.m_mbfree--;
splx(s);
MBTRACE(0,m);
m->m_next = m->m_act = m->m_len = 0;
/*@*/ m->m_off = MMINOFF;
mapseg5(m->m_click, MBMAPSIZE);
MBX->m_type = type;
restorseg5(map5);
return(m);
}
/* otherwise do it the hard way */
MSGET(m, struct mbuf, MT_DATA, 0, canwait);
if (m == 0)
break;
MBXGET(click);
if (!click) {
MSFREE(m);
if (canwait) {
restorseg5(map5);
m_want++;
sleep(&mbfree, PZERO - 1);
continue;
} else {
mbstat.m_drops++;
m = 0;
}
break;
}
m->m_click = click;
m->m_next = m->m_act = m->m_len = 0;
m->m_off = MMINOFF;
MBX->m_mbuf = m;
MBX->m_type = type;
mbstat.m_mbfree--;
mbstat.m_mbufs = MIN(mbstat.m_mbfree,mbstat.m_mbufs);
break;
}
restorseg5(map5);
splx(s);
MBTRACE(1,m);
return (m);
}
/*
* Return an mbuf. If not the last reference, just return the header;
* else return both mbuf/mbufx. Use the cache if empty slot exists.
*/
struct mbuf *
m_bfree(m)
struct mbuf *m;
{
register struct mbuf *n;
int s = splimp();
u_int click;
MAPSAVE();
n = m->m_next;
#ifdef debug
MBTRACE(2,m);
if (m->m_click < mbbase || m->m_click > mbend)
panic("m_bf");
#endif
click = m->m_click;
mapseg5(click,MBMAPSIZE);
if (MBX->m_ref != 1) {
if (MBX->m_ref < 1 || MBX->m_ref > 4) panic("m_bf2");
MBX->m_ref--;
MSFREE(m);
goto out;
}
mbstat.m_mbfree++;
if (mbicache < NMBCACHE) {
int *ip = (int *) m; ip--;
if ((*ip & 01) == 0) panic("m_bf3");
mbcache[mbicache++] = m;
goto out;
}
MSFREE(m);
MBXFREE(click);
out:
MAPREST();
if (m_want) {
m_want = 0;
wakeup((caddr_t)m_want);
}
splx(s);
return (n);
}
/*
* Allocate a contiguous buffer for DMA IO. Called from if_ubainit().
* TODO: fix net device drivers to handle scatter/gather to mbufs
* on their own; thus avoiding the copy to/from this area.
*/
u_int
m_ioget(size)
{
u_int base;
size = ((size + 077) & ~077); /* round up byte size */
if (size > miosize) return(0);
miosize -= size;
base = miobase;
miobase += (size>>6);
MBTRACE(3,base);
return(base);
}
/* C storage allocator
* circular first-fit strategy
* works with noncontiguous, but monotonically linked, arena
* each block is preceded by a ptr to the (pointer of)
* the next following block
* blocks are exact number of words long
* aligned to the data type requirements of ALIGN
* pointers to blocks must have BUSY bit 0
* bit in ptr is 1 for busy, 0 for idle
* gaps in arena are merely noted as busy blocks
* last block of arena (pointed to by alloct) is empty and
* has a pointer to first
* idle blocks are coalesced during space search
*
* a different implementation may need to redefine
* ALIGN, NALIGN, BLOCK, BUSY, INT
* where INT is integer type to which a pointer can be cast
*/
#define INT int
#define ALIGN int
#define NALIGN 1
#define WORD sizeof(union store)
#define BLOCK 1024 /* a multiple of WORD*/
#define BUSY 1
#define NULL 0
#define testbusy(p) ((INT)(p)&BUSY)
#define setbusy(p) (union store *)((INT)(p)|BUSY)
#define clearbusy(p) (union store *)((INT)(p)&~BUSY)
union store { union store *ptr;
ALIGN dummy[NALIGN];
int calloc; /*calloc clears an array of integers*/
};
extern union store allocs[]; /* initial arena */
union store *allocp; /*search ptr*/
union store *alloct; /*arena top*/
#ifdef debug
#define ASSERT(p) if(!(p))botch("p");else
botch(s)
char *s;
{
printf("botch %s\n",s);
panic("mbuf11");
}
#ifdef longdebug
allock()
{
register union store *p;
int x;
x = 0;
for(p= &allocs[0]; clearbusy(p->ptr) > p; p=clearbusy(p->ptr)) {
if(p==allocp)
x++;
}
ASSERT(p==alloct);
return(x==1||p==allocp);
}
#endif longdebug
#else
#define ASSERT(p)
#endif debug
char *
m_sget(nbytes,clr,canwait)
unsigned nbytes,clr;
{
register union store *p, *q;
register nw;
int temp,val;
int s = splimp();
nw = (nbytes+WORD+WORD-1)/WORD;
top:
ASSERT(allocp>=allocs && allocp<=alloct);
#ifdef longdebug
ASSERT(allock());
#endif logndebug
for(p=allocp; ; ) {
for(temp=0; ; ) {
if(!testbusy(p->ptr)) {
while(!testbusy((q=p->ptr)->ptr)) {
ASSERT(q>p&&q<alloct);
p->ptr = q->ptr;
}
if(q>=p+nw && p+nw>=p)
goto found;
}
q = p;
p = clearbusy(p->ptr);
if(p>q)
ASSERT(p<=alloct);
else if(q!=alloct || p!=allocs) {
ASSERT(q==alloct&&p==allocs);
goto bad;
} else if(++temp>1)
break;
}
/* malloc would call sbrk here and try again */
mbstat.m_drops++;
goto bad;
}
found:
allocp = p + nw;
ASSERT(allocp<=alloct);
if(q>allocp) {
allocp->ptr = p->ptr;
}
p->ptr = setbusy(allocp);
mbstat.m_clfree -= /* (clearbusy(p->ptr) - p) */ nw * WORD;
mbstat.m_clusters = MIN(mbstat.m_clusters, mbstat.m_clfree);
val = (char *)(p+1);
splx(s);
if (clr)
bzero(val,((nbytes+1)& ~1));
MBTRACE(4,val);
return(val);
bad:
if (canwait == M_WAIT) {
ms_want++;
sleep((caddr_t)&allocp, PZERO - 1);
goto top;
}
splx(s);
return(NULL);
}
/* freeing strategy tuned for LIFO allocation
*/
m_sfree(ap)
register char *ap;
{
register union store *p = (union store *)ap;
int s = splimp();
MBTRACE(5,ap);
/* ASSERT(p>clearbusy(allocs[1].ptr)&&p<=alloct); */
ASSERT(p>allocs&&p<=alloct);
#ifdef longdebug
ASSERT(allock());
#endif longdebug
/* allocp = --p; leaves old blocks around longer for debugging */
--p;
ASSERT(testbusy(p->ptr));
p->ptr = clearbusy(p->ptr);
ASSERT(p->ptr > p && p->ptr <= alloct);
mbstat.m_clfree += ((p->ptr) - p) * WORD;
if (ms_want) {
ms_want = 0;
wakeup((caddr_t)&allocp);
}
splx(s);
}
/*
* Mbuf address to data address, with bounds checking. Called from
* "mtod" macro which does type cast.
*/
mtodf(m)
register struct mbuf *m;
{
#ifdef debug
if (m < (struct mbuf *)&allocs[0] || m > (struct mbuf *) alloct)
panic("allocs-mtodf");
if (m->m_click < mbbase || m->m_click > mbend)
panic("m_click-mtodf");
if (m->m_off < MMINOFF)
panic("m_off-MIN-mtodf");
if (m->m_off > MMAXOFF) {
printf("m %o, m_off %o\n",m, m->m_off);
panic("m_off-MAX-mtodf");
}
if (m->m_len < 0 || m->m_len > MLEN)
panic("m_len-mtodf");
#else !debug
if (m < (struct mbuf *)&allocs[0] || m > (struct mbuf *) alloct
#ifdef debug
|| m->m_click < mbbase || m->m_click > mbend
|| m->m_off < MMINOFF || m->m_off > MMAXOFF
|| m->m_len < 0 || m->m_len > MLEN
#endif
) panic("mtodf");
#endif debug
mapseg5(m->m_click,MBMAPSIZE);
MBX->m_mbuf = m;
return ((int)MBX + m->m_off);
}
/*
* Initialize the buffer pool. Called from netinit/main.
*/
mbinit()
{
register i;
u_int base;
MAPSAVE();
/* setup the arena */
allocs[0].ptr = &allocs[nwords-1];
allocs[nwords-1].ptr = setbusy(&allocs[0]);
alloct = &allocs[nwords-1];
allocp = &allocs[0];
mbstat.m_clusters = mbstat.m_clfree = (nwords-2) * WORD;
/* setup DMA IO area */
miobase = mbbase;
mbbase += (miosize>>6);
mbsize -= miosize;
/* link the mbufs */
mbstat.m_mbufs = mbstat.m_mbfree = mbsize/MSIZE;
base = mbbase;
mbend = mbbase + (mbsize >> 6);
for(i=0 ; i<(mbsize/MSIZE); i++) {
mapseg5(base,MBMAPSIZE);
MBX->m_ref = 1;
MBXFREE(base);
base += (MSIZE>>6);
}
MAPREST();
}
#ifdef debug
#define NMBTBUF 40
struct mbtbuf {
u_int mt_type; /* type plus KISA6 */
u_int mt_pc;
u_int mt_arg;
} mbtbuf[NMBTBUF], *mbitbuf = mbtbuf;
mbtrace(type,arg)
{
int s = spl7();
register int *ip;
register struct mbtbuf *mt = mbitbuf;
extern int _ovno;
if (++mbitbuf >= &mbtbuf[NMBTBUF])
mbitbuf = mbtbuf;
mt->mt_type = (type << 12) | _ovno;
ip = &type; ip--; ip--;
ip = *ip; ip++;
mt->mt_pc = *ip;
mt->mt_arg = arg;
splx(s);
}
#ifdef notdef
mbprint(m,s)
register struct mbuf *m;
char *s;
{
register char *ba;
int col,i,bc;
#ifndef debug
return;
#endif
MAPSAVE();
printf("MB %s\n",s);
for (;;) {
if (m == 0) break;
ba = mtod(m, char *);
col = 0; bc = m->m_len;
printf("m%o next%o off%o len%o click%o act%o back%o ref%o\n",
m, m->m_next, m->m_off, m->m_len, m->m_click, m->m_act,
MBX->m_mbuf, MBX->m_ref);
for(; bc ; bc--) {
i = *ba++ & 0377;
printf("%o ",i);
if(++col > 31) {
col = 0;
printf("\n ");
}
}
printf("\n");
m = m->m_next;
}
MAPREST();
}
#endif notdef
#endif debug
#endif UCB_NET