/* Copyright 2000 (c) by David Schweikert, Swiss Federal Institute of Technology, Computer Engineering and Networks Laboratory. TOPSY -- A Teachable Operating System. Implementation of a tiny and simple micro kernel for teaching purposes. For further information, please visit http://www.tik.ee.ethz.ch/~topsy This software is provided under the terms of the GNU General Public Licence. A full copy of the GNU GPL is provided in the file COPYING found in the development root of Topsy. */ /* File: $Source: /usr/drwho/vault/cvs/topsy/Topsy/Net/NetBuf.c,v $ Author(s): Affiliation: ETH Zuerich, TIK Version: $Revision: 1.2 $ Creation Date: Last Date of Change: $Date: 2000/04/03 17:45:29 $ by: $Author: gfa $ $Log: NetBuf.c,v $ Revision 1.2 2000/04/03 17:45:29 gfa *** empty log message *** Revision 1.1 2000/03/31 17:50:33 gfa Merged with /Net from several term projects */ #include #include #include #include #include #include #include #define NETBUF_BUFSIZE(s) (stats.sizes[s]) #define NETBUF_POOLFULLMASK(s) (ULONG_MAX >> (NETBUF_MAXBUFS(0)-NETBUF_MAXBUFS(s))) static LockDesc netbuflock; static NetBufStats stats; static NetBufsPool freepools[NETBUF_SIZES]; NetBufStats *netbufStats = &stats; /** netbufInit: Initialize netbuf manager. */ void netbufInit() { int i; lockInit(&netbuflock); for(i=0; i= size) return i; } return NETBUF_SIZES-1; } /** netbufAlloc: Allocates a new netbuf of at least payload-size 'size'. If it is * not possible, a linked list of smaller netbufs is returned. */ int netbufAlloc(NetBuf *bufPtr, unsigned int size) { int s = netbufSizeNr(size + sizeof(NetBufHdr)); NetBufsPool pool; NetBuf buf; unsigned long u,totalsize; unsigned short bufnr, datasize; netdbgPrintf(NETDEBUG_NETBUFS, "NetBuf: New buffer of size %d requested.\n", NETBUF_BUFSIZE(s)); /* GET LOCK */ lock(&netbuflock); netdbgDisplay(NETDEBUG_NETBUFS, "NetBuf: got lock.\n"); /* search for a free buf */ pool = freepools[s]; if(!pool) { /* no free buf in pool found... */ netdbgDisplay(NETDEBUG_NETBUFS, "NetBuf: sending VM_ALLOC to NetMain.\n"); /* alloc a new pool */ if(netAlloc((Address *) &pool, NETBUF_POOLSIZE) != VM_ALLOCOK) { unlock(&netbuflock); netdbgDisplay(NETDEBUG_GENERIC, "NetBuf: Couldn't get new pool!!\n"); return 0; } /* init pool */ pool->next = 0; pool->prev = 0; pool->used = 0; pool->sizenr = s; freepools[s] = pool; stats.pools[s]++; netdbgPrintf(NETDEBUG_NETBUFS, "NetBuf: New pool allocated for size %d (%p)\n", NETBUF_BUFSIZE(s), pool); } /* get first unused buf */ for(u=pool->used, bufnr=0; u % 2; u >>= 1) bufnr++; /* mark as used */ pool->used |= 1UL << bufnr; /* if full remove from freepools */ if(pool->used == NETBUF_POOLFULLMASK(s)) { netdbgPrintf(NETDEBUG_NETBUFS, "NetBuf: Pool %p is full. Removing from freepools.\n",pool); if(pool->prev) pool->prev->next = pool->next; else freepools[s] = pool->next; if(pool->next) pool->next->prev = pool->prev; } /* increment statistics counter */ stats.usage[s]++; /* FREE LOCK */ unlock(&netbuflock); netdbgDisplay(NETDEBUG_NETBUFS, "NetBuf: lock freed.\n"); /* Warning: pointer arithmetic! */ buf = (NetBuf) ((char *) pool + sizeof(NetBufsPoolHdr)+ bufnr*NETBUF_BUFSIZE(s)); *bufPtr = buf; datasize = NETBUF_BUFSIZE(s) - sizeof(NetBufHdr); /* init buf */ buf->pool = pool; buf->bufnr = bufnr; buf->size = datasize; buf->start = sizeof(NetBufHdr); buf->end = sizeof(NetBufHdr); netdbgPrintf(NETDEBUG_NETBUFS, "NetBuf: New buffer (%p): pool=%p, bufnr=%d, size=%d, start=%p, end=%p\n", buf, buf->pool, buf->bufnr, buf->size, buf->start, buf->end); totalsize=datasize; /* alloc further NetBufs if needed */ if(datasize < size) { if(!(datasize=netbufAlloc(&(buf->next), size-datasize))) return 0; totalsize+=datasize; } else { buf->next = 0; } return totalsize; } static void netbufFreeBuf(NetBuf buf) /* no lock !!! Use netbufFree */ { unsigned short s; int wasfull=0; NetBufsPool pool = buf->pool; netdbgPrintf(NETDEBUG_NETBUFS, "NetBuf: Freeing buffer %p in pool %p (prev=%p, next=%p).\n", buf, pool,pool->prev,pool->next); s = pool->sizenr; lock(&netbuflock); if(pool->used == NETBUF_POOLFULLMASK(s)) wasfull=1; if(!(pool->used & (1UL<bufnr))) { netdbgPrintf(NETDEBUG_GENERIC, "NetBuf: Freeing already freed buffer!\n"); } //pool->used &= ~(1UL << (buf->bufnr)); pool->used ^= 1UL << buf->bufnr; stats.usage[s]--; if(pool->used == 0 && freepools[s] && freepools[s]->next) { /* threshold of 1 */ if(!wasfull) { /* remove from freepools */ if(pool->prev) pool->prev->next = pool->next; else freepools[s] = pool->next; if(pool->next) pool->next->prev = pool->prev; } netdbgPrintf(NETDEBUG_NETBUFS, "NetBuf: Freeing pool %p.\n", pool); netFree(pool); stats.pools[s]--; } else { if(wasfull) { /* add to freepools */ if(freepools[s]) freepools[s]->prev=pool; pool->next=freepools[s]; pool->prev=0; freepools[s]=pool; } } unlock(&netbuflock); } /** netbufFreeBuf for the whole linked list */ void netbufFree(NetBuf buf) { NetBuf next; while(buf) { next=buf->next; netbufFreeBuf(buf); buf=next; } } /*************** UTILITIES ******************/ /** netbufAddHead: Controls if there is enough space left before buf->start * (mininum: len) and if not, adds another netbuf to the netbuf-list * (to the head) of size len. * If there is enough space, the buf->start index is decreased * accordingly. */ int netbufAddHead(NetBuf *bufPtr, unsigned int len) { NetBuf newbuf; /* if there isn't enough space, add a netbuf to the head */ if(NETBUF_HEADSPACE(*bufPtr) < len) { if(!netbufAlloc(&newbuf, len)) return 0; newbuf->next = *bufPtr; newbuf->end = NETBUF_HEADERSIZE+newbuf->size; newbuf->start = newbuf->end-len; *bufPtr = newbuf; /* netdbgDisplay(NETDEBUG_GENERIC, * "NetBuf: Adding new buf at head of list.\n"); * netdbgPrintf(NETDEBUG_GENERIC, * "NetBuf: *bufPtr=%p, next=%p, end=%d, start=%d\n", * *bufPtr, newbuf->next, newbuf->end, newbuf->start); */ } else { /* netdbgPrintf(NETDEBUG_GENERIC, "Adding header: start (before) = %d", (*bufPtr)->start); */ (*bufPtr)->start -= len; /* netdbgPrintf(NETDEBUG_GENERIC, "Adding header: start (after) = %d", (*bufPtr)->start); */ } return 1; } /** netbufLen: Returns the total length of the netbuf-list */ unsigned int netbufLen(NetBuf buf) { unsigned int len=0; for(;buf;buf=buf->next) len+=NETBUF_LEN(buf); return len; } /** netbufTrim: If total length of the netbuf-list is bigger than 'len', removes * bytes at the end, so that they are equal. */ void netbufTrim(NetBuf buf, unsigned int len) { unsigned int tlen=0, blen; for(;buf;buf=buf->next) { blen=NETBUF_LEN(buf); if(tlen+blen>len) { buf->end = buf->start + (len - tlen); if(buf->next) { netbufFree(buf->next); buf->next=0; } return; } tlen+=blen; } return; } /** netbufPosition: 'position' refers to the absolute byte position in a virtual big buffer * which is in reality a linked list of netbufs. * After the function-call, buf points to the netbuf which contains * that byte, and position is adjusted to the position in that netbuf. * Returns 1 on success. */ int netbufPosition(NetBuf *buf, unsigned int *position) { for(;*buf;*buf=(*buf)->next) { if(NETBUF_LEN(*buf)>*position) return 1; *position -= NETBUF_LEN(*buf); } return 0; } /** netbufClone: returns an exact copy of buf. */ int netbufClone(NetBuf *dest, NetBuf src) { NetBuf tmp; if(!src) return 0; /* Copy the first */ if(!netbufAlloc(dest,NETBUF_SIZE(src))) return 0; tmp = *dest; tmp->start=src->start; tmp->end =src->end; byteCopy(NETBUF_DATA(tmp),NETBUF_DATA(src),NETBUF_LEN(src)); src = src->next; /* Copy the following */ while(src) { if(!netbufAlloc(&(tmp->next),NETBUF_SIZE(src))) { netbufFree(*dest); return 0; } tmp = tmp->next; tmp->start=src->start; tmp->end =src->end; byteCopy(NETBUF_DATA(tmp),NETBUF_DATA(src),NETBUF_LEN(src)); src=src->next; } return 1; } /** netbufCopy: returns a copy of buf from pos to pos+len */ int netbufCopy(NetBuf *dest, NetBuf src, int pos, int len) { NetBuf dst; int totalsize, dstpos=0; /* Alloc buffer */ totalsize = netbufAlloc(dest,len); if(!totalsize) return 0; dst = *dest; /* Leave space at the beginning */ dst->start = NETBUF_HEADERSIZE+(totalsize-len); /* Search for pos in the src-linkedlist */ if(!netbufPosition(&src,&pos)) return 0; while(1) { int clen,tmp,whatnext; /* minimize between NETBUF_LEN(src), len and * NETBUF_SIZE(dst) */ clen = NETBUF_LEN(src)-pos; if(clen>=len) { clen=len; whatnext=0; /* finished copying */ } else whatnext=1; /* next src */ tmp = NETBUF_SIZE(src)-dstpos; if(clen>tmp) { clen=tmp; whatnext=2; /* next dst */ } if(tmp<0) { /* we have a problem... */ goto error; } /* Set dst->end */ dst->end = dst->start+dstpos+clen; /* Do the copy */ byteCopy(NETBUF_DATA(dst)+dstpos, NETBUF_DATA(src)+pos, clen); switch(whatnext) { case 0: return 1; break; case 1: if(!src->next) goto error; src = src->next; dstpos = clen; break; case 2: if(!dst->next) goto error; dst = dst->next; dst->start = NETBUF_HEADERSIZE; pos = clen; break; } } error: netbufFree(*dest); return 0; } /* netbufWrite */ /* Fills linear data into a allocated NetBuf (size of NetBuf must be n32Len+n32DestOffset). */ /* n32DestOffset bytes are and not changed at the beginning of the buffer */ long netbufWrite(NetBuf pDest,unsigned char *pSrc,long n32Len,long n32DestOffset) { long int n32TmpLen; long int n32CopiedLen=0L; long int nCurrentSrcPos=0L; long int nCurrentDestPos; long int nRemainingSize; NetBuf pCurrentNetBuf; pCurrentNetBuf=pDest; while ((n32CopiedLensize) { //copy data nCurrentDestPos=n32DestOffset+NETBUF_START(pDest); n32TmpLen=n32Len-n32CopiedLen; //bytes to copy nRemainingSize=pCurrentNetBuf->size-NETBUF_HEADSPACE(pCurrentNetBuf)-n32DestOffset; //free bytes in buffer if (n32TmpLen > nRemainingSize) { n32TmpLen=nRemainingSize; } byteCopy((char *)pCurrentNetBuf+nCurrentDestPos,pSrc+nCurrentSrcPos,n32TmpLen); n32CopiedLen+=n32TmpLen; nCurrentSrcPos+=n32TmpLen; n32DestOffset=0L; pCurrentNetBuf->end = nCurrentDestPos+n32TmpLen; nCurrentDestPos=0L; pCurrentNetBuf=pCurrentNetBuf->next; } else { //next buf n32DestOffset-=pCurrentNetBuf->size; pCurrentNetBuf=pCurrentNetBuf->next; } } return n32CopiedLen; } /* netbufRead */ /* Fills NetBuf into a allocated linear buffer (size of the linear buffer must be n32Len) */ /* The first n32SrcOffset bytes in the NetBuf are not copied */ long netbufRead(unsigned char *pDest,NetBuf pSrc,long n32Len,long n32SrcOffset) { long int n32TmpLen; long int n32CopiedLen=0L; long int nCurrentSrcPos; long int nCurrentDestPos=0L; long int nDataSize; NetBuf pCurrentNetBuf; pCurrentNetBuf=pSrc; while ((n32CopiedLenstart; n32TmpLen=n32Len-n32CopiedLen; //bytes to copy if (n32TmpLen > nDataSize-n32SrcOffset) { n32TmpLen=nDataSize-n32SrcOffset; } byteCopy(pDest+nCurrentDestPos,(char *)pCurrentNetBuf+nCurrentSrcPos,n32TmpLen); n32CopiedLen+=n32TmpLen; nCurrentSrcPos=0L; nCurrentDestPos+=n32TmpLen; n32SrcOffset=0L; pCurrentNetBuf=pCurrentNetBuf->next; } else { //next buf n32SrcOffset-=nDataSize; pCurrentNetBuf=pCurrentNetBuf->next; } } return n32CopiedLen; } /** netbufStatistics: displays internal info about netbufs */ void netbufStatistics() { int n; netdbgPrintf(NETDEBUG_TCP,"%s","size\t\tpools\tused\tfree\tmemory\n"); for(n=0; nsizes[n], netbufStats->pools[n], netbufStats->usage[n], netbufStats->pools[n]*NETBUF_MAXBUFS(n) - netbufStats->usage[n], NETBUF_POOLSIZE*netbufStats->pools[n]/1024); } }