mbuf.c revision 641bf847576fdcb22f110d9c8e4fbe99a5947f1b
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/*
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * Copyright (c) 1995 Danny Gasparovski
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync *
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * Please read the file COPYRIGHT for the
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * terms and conditions of the copyright.
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/*
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * mbuf's in SLiRP are much simpler than the real mbufs in
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * FreeBSD. They are fixed size, determined by the MTU,
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * so that one whole packet can fit. Mbuf's cannot be
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * chained together. If there's more data than the mbuf
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * could hold, an external malloced buffer is pointed to
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * by m_ext (and the data pointers) and M_EXT is set in
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * the flags
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#include <slirp.h>
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync
930b5f872e89407f445d4000d4e4aaecaa6a0998vboxsync#define MBUF_ZONE_SIZE 100
930b5f872e89407f445d4000d4e4aaecaa6a0998vboxsyncstatic int mbuf_zone_init(PNATState pData)
930b5f872e89407f445d4000d4e4aaecaa6a0998vboxsync{
930b5f872e89407f445d4000d4e4aaecaa6a0998vboxsync struct mbuf_zone *mzone;
930b5f872e89407f445d4000d4e4aaecaa6a0998vboxsync int i;
930b5f872e89407f445d4000d4e4aaecaa6a0998vboxsync struct mbuf *m;
930b5f872e89407f445d4000d4e4aaecaa6a0998vboxsync uint8_t *zone = RTMemAlloc(msize * MBUF_ZONE_SIZE);
930b5f872e89407f445d4000d4e4aaecaa6a0998vboxsync if (zone == NULL)
930b5f872e89407f445d4000d4e4aaecaa6a0998vboxsync {
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync LogRel(("NAT: can't allocate new zone\n"));
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync return -1;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync }
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync mzone = RTMemAllocZ(sizeof (struct mbuf_zone));
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync if (mzone == NULL)
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync {
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync RTMemFree(zone);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync LogRel(("NAT: can't allocate zone descriptor\n"));
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync return -1;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync }
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync for (i = 0; i < MBUF_ZONE_SIZE; ++i)
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync {
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync m = (struct mbuf *)((char *)zone + i*msize);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync memset(m, 0, sizeof(struct mbuf));
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#ifdef M_BUF_DEBUG
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync m->m_hdr.mh_id = pData->mbuf_zone_count * MBUF_ZONE_SIZE + i;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#endif
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync insque(pData, m, &m_freelist);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync }
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync mzone->mbuf_zone_base_addr = zone;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync LIST_INSERT_HEAD(&pData->mbuf_zone_head, mzone, list);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync pData->mbuf_zone_count++;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync pData->mbuf_water_line_limit = pData->mbuf_zone_count * MBUF_ZONE_SIZE;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync return 0;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync}
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncvoid m_fini(PNATState pData)
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync{
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync struct mbuf_zone *mz;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync struct mbuf *m;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync int i;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync void *zone;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync while(!LIST_EMPTY(&pData->mbuf_zone_head))
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync {
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync mz = LIST_FIRST(&pData->mbuf_zone_head);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync zone = mz->mbuf_zone_base_addr;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync for (i = 0; i < MBUF_ZONE_SIZE; ++i)
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync {
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync m = (struct mbuf *)((char *)zone + i*msize);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync if ( (m->m_flags & M_EXT)
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync && m->m_ext != NULL)
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync RTMemFree(m->m_ext);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync }
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync RTMemFree(zone);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync LIST_REMOVE(mz, list);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync RTMemFree(mz);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync }
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync}
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncvoid
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncm_init(PNATState pData)
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync{
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync int rc = 0;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync m_freelist.m_next = m_freelist.m_prev = &m_freelist;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync m_usedlist.m_next = m_usedlist.m_prev = &m_usedlist;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync mbuf_alloced = 0;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync msize_init(pData);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#if 1
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync rc = RTCritSectInit(&pData->cs_mbuf_zone);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync AssertRC(rc);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync rc = mbuf_zone_init(pData);
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync Assert((rc == 0));
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#endif
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync}
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncvoid
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsyncmsize_init(PNATState pData)
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync{
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync /*
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * Find a nice value for msize
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync */
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync msize = (if_mtu>if_mru ? if_mtu : if_mru)
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync + sizeof(struct m_hdr) + sizeof(void *) /*pointer to the backstore*/
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync + if_maxlinkhdr ;
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync}
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#ifdef m_get
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync# undef m_get
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#endif
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#ifdef m_free
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync# undef m_free
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync#endif
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync/*
3194da424708abdd288b28d96892b3a5f3f7df0bvboxsync * Get an mbuf from the free list, if there are none
* malloc one
*
* Because fragmentation can occur if we alloc new mbufs and
* free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE,
* which tells m_free to actually free() it
*/
struct mbuf *
m_get(PNATState pData)
{
register struct mbuf *m;
int flags = 0;
int rc = 0;
DEBUG_CALL("m_get");
rc = RTCritSectEnter(&pData->cs_mbuf_zone);
AssertRC(rc);
recheck_zone:
if (m_freelist.m_next == &m_freelist)
{
#if 1
rc = mbuf_zone_init(pData);
if (rc == 0)
goto recheck_zone;
AssertMsgFailed(("No mbufs on free list\n"));
return NULL;
#else
m = (struct mbuf *)RTMemAlloc(msize);
if (m == NULL)
goto end_error;
mbuf_alloced++;
if (mbuf_alloced > mbuf_thresh)
flags = M_DOFREE;
if (mbuf_alloced > mbuf_max)
mbuf_max = mbuf_alloced;
#endif
}
else
{
m = m_freelist.m_next;
remque(pData, m);
}
STAM_COUNTER_INC(&pData->StatMBufAllocation);
/* Insert it in the used list */
mbuf_alloced++;
#if 0
if (mbuf_alloced >= MBUF_ZONE_SIZE/2)
{
pData->fmbuf_water_line = 1;
}
#endif
insque(pData, m, &m_usedlist);
m->m_flags = (flags | M_USEDLIST);
/* Initialise it */
m->m_size = msize - sizeof(struct m_hdr);
m->m_data = m->m_dat;
m->m_len = 0;
m->m_nextpkt = 0;
m->m_prevpkt = 0;
m->m_la = NULL;
memset(m->m_data, 0, if_maxlinkhdr); /*initialization of ether area */
end_error:
DEBUG_ARG("m = %lx", (long )m);
rc = RTCritSectLeave(&pData->cs_mbuf_zone);
AssertRC(rc);
return m;
}
void
m_free(PNATState pData, struct mbuf *m)
{
int rc;
DEBUG_CALL("m_free");
DEBUG_ARG("m = %lx", (long )m);
rc = RTCritSectEnter(&pData->cs_mbuf_zone);
AssertRC(rc);
mbuf_alloced--;
if(m)
{
/* Remove from m_usedlist */
if (m->m_flags & M_USEDLIST)
remque(pData, m);
/* If it's M_EXT, free() it */
if (m->m_flags & M_EXT)
RTMemFree(m->m_ext);
/*
* Either free() it or put it on the free list
*/
if (m->m_flags & M_DOFREE)
{
#if 1
if ((m->m_flags & M_EXT) == 0)
memset(m->m_dat, 0, if_mtu);
insque(pData, m, &m_freelist);
m->m_flags = M_FREELIST; /* Clobber other flags */
#else
RTMemFree(m);
#endif
mbuf_alloced--;
}
else if ((m->m_flags & M_FREELIST) == 0)
{
insque(pData, m,&m_freelist);
m->m_flags = M_FREELIST; /* Clobber other flags */
}
STAM_COUNTER_INC(&pData->StatMBufAllocation);
} /* if(m) */
rc = RTCritSectLeave(&pData->cs_mbuf_zone);
AssertRC(rc);
}
/* update macros for m_get/m_free*/
#undef m_get
#undef m_free
#include "mbuf.h"
/*
* Copy data from one mbuf to the end of
* the other.. if result is too big for one mbuf, malloc()
* an M_EXT data segment
*/
void
m_cat(PNATState pData, register struct mbuf *m, register struct mbuf *n)
{
/*
* If there's no room, realloc
*/
if (M_FREEROOM(m) < n->m_len)
m_inc(m,m->m_size+MINCSIZE);
memcpy(m->m_data+m->m_len, n->m_data, n->m_len);
m->m_len += n->m_len;
m_free(pData, n);
}
/* make m size bytes large */
void
m_inc(struct mbuf *m, int size)
{
int datasize;
/* some compiles throw up on gotos. This one we can fake. */
if (m->m_size > size)
return;
if (m->m_flags & M_EXT)
{
void *pvNew;
datasize = m->m_data - m->m_ext;
pvNew = (char *)RTMemRealloc(m->m_ext, size);
if (pvNew)
return; /** @todo better error reporting. */
m->m_ext = (char *)pvNew;
m->m_data = m->m_ext + datasize;
}
else
{
char *dat;
datasize = m->m_data - m->m_dat;
dat = (char *)RTMemAlloc(size);
if (!dat)
return; /** @todo better error reporting. */
memcpy(dat, m->m_dat, m->m_size);
m->m_ext = dat;
m->m_data = m->m_ext + datasize;
m->m_flags |= M_EXT;
}
m->m_size = size;
}
void
m_adj(struct mbuf *m, int len)
{
if (m == NULL)
return;
if (len >= 0)
{
/* Trim from head */
m->m_data += len;
m->m_len -= len;
}
else
{
/* Trim from tail */
len = -len;
m->m_len -= len;
}
Assert(m->m_len >= 0);
}
/*
* Copy len bytes from m, starting off bytes into n
*/
int
m_copy(struct mbuf *n, struct mbuf *m, int off, int len)
{
if (len > M_FREEROOM(n))
return -1;
memcpy((n->m_data + n->m_len), (m->m_data + off), len);
n->m_len += len;
return 0;
}
/*
* Given a pointer into an mbuf, return the mbuf
* XXX This is a kludge, I should eliminate the need for it
* Fortunately, it's not used often
*/
struct mbuf *
dtom(PNATState pData, void *dat)
{
struct mbuf *m;
DEBUG_CALL("dtom");
DEBUG_ARG("dat = %lx", (long )dat);
/* bug corrected for M_EXT buffers */
for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next)
{
if (m->m_flags & M_EXT)
{
if ( (char *)dat >= m->m_ext
&& (char *)dat < (m->m_ext + m->m_size))
return m;
}
else
{
if ( (char *)dat >= m->m_dat
&& (char *)dat < (m->m_dat + m->m_size))
return m;
}
}
DEBUG_ERROR((dfd, "dtom failed"));
return (struct mbuf *)0;
}
#ifndef VBOX_WITH_SLIRP_BSD_MBUF
/**
* Interface that DrvNAT.cpp uses for allocating a buffer.
*
* @returns Opaque m_buf pointer.
*
* @param pData The NAT state.
* @param cbMin The minimum buffer size.
* @param ppvBuf Where to return the pointer to the start of the data
* buffer.
* @param pcbBuf Where to return the actual buffer size.
*/
struct mbuf *slirp_ext_m_get(PNATState pData, size_t cbMin, void **ppvBuf, size_t *pcbBuf)
{
struct mbuf *m = m_get(pData);
if (!m)
return NULL;
if (cbMin > M_FREEROOM(m))
{
m_inc(m, cbMin);
if (RT_UNLIKELY(cbMin > M_FREEROOM(m)))
{
m_free(pData, m);
return NULL;
}
}
*ppvBuf = mtod(m, void *);
*pcbBuf = M_FREEROOM(m);
return m;
}
void slirp_ext_m_free(PNATState pData, struct mbuf *m)
{
m_free(pData, m);
}
#endif /* VBOX_WITH_SLIRP_BSD_MBUF */