diff options
Diffstat (limited to 'src/mbuf.c')
-rw-r--r-- | src/mbuf.c | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/src/mbuf.c b/src/mbuf.c new file mode 100644 index 0000000..b9346ec --- /dev/null +++ b/src/mbuf.c @@ -0,0 +1,440 @@ +/*- + * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org> + * based on work by Toshiharu OHNO <tony-o@iij.ad.jp> + * Internet Initiative Japan, Inc (IIJ) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/ppp/mbuf.c,v 1.46.26.1 2010/12/21 17:10:29 kensmith Exp $ + */ + +#include <sys/types.h> + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <termios.h> + +#include "defs.h" +#include "command.h" +#include "mbuf.h" +#include "log.h" +#include "descriptor.h" +#include "prompt.h" +#include "main.h" + +#define BUCKET_CHUNK 20 +#define BUCKET_HASH 256 + +struct mbucket; + +struct mfree { + struct mbucket *next; + size_t count; +}; + +static struct mbucket { + union { + struct mbuf m; + struct mfree f; + } u; +} *bucket[(M_MAXLEN + sizeof(struct mbuf)) / BUCKET_HASH]; + +#define M_BINDEX(sz) (((sz) + sizeof(struct mbuf) - 1) / BUCKET_HASH) +#define M_BUCKET(sz) (bucket + M_BINDEX(sz)) +#define M_ROUNDUP(sz) ((M_BINDEX(sz) + 1) * BUCKET_HASH) + +static struct memmap { + struct mbuf *queue; + size_t fragments; + size_t octets; +} MemMap[MB_MAX + 1]; + +static unsigned long long mbuf_Mallocs, mbuf_Frees; + +size_t +m_length(struct mbuf *bp) +{ + size_t len; + + for (len = 0; bp; bp = bp->m_next) + len += bp->m_len; + return len; +} + +static const char * +mbuftype(int type) +{ + static const char * const mbufdesc[MB_MAX] = { + "ip in", "ip out", "ipv6 in", "ipv6 out", "nat in", "nat out", + "mp in", "mp out", "vj in", "vj out", "icompd in", "icompd out", + "compd in", "compd out", "lqr in", "lqr out", "echo in", "echo out", + "proto in", "proto out", "acf in", "acf out", "sync in", "sync out", + "hdlc in", "hdlc out", "async in", "async out", "cbcp in", "cbcp out", + "chap in", "chap out", "pap in", "pap out", "ccp in", "ccp out", + "ipcp in", "ipcp out", "ipv6cp in", "ipv6cp out", "lcp in", "lcp out" + }; + + return type < 0 || type >= MB_MAX ? "unknown" : mbufdesc[type]; +} + +struct mbuf * +m_get(size_t m_len, int type) +{ + struct mbucket **mb; + struct mbuf *bp; + size_t size; + + if (type > MB_MAX) { + log_Printf(LogERROR, "Bad mbuf type %d\n", type); + type = MB_UNKNOWN; + } + + if (m_len > M_MAXLEN || m_len == 0) { + log_Printf(LogERROR, "Request for mbuf size %lu (\"%s\") denied !\n", + (u_long)m_len, mbuftype(type)); + AbortProgram(EX_OSERR); + } + + mb = M_BUCKET(m_len); + size = M_ROUNDUP(m_len); + + if (*mb) { + /* We've got some free blocks of the right size */ + bp = &(*mb)->u.m; + if (--(*mb)->u.f.count == 0) + *mb = (*mb)->u.f.next; + else { + ((struct mbucket *)((char *)*mb + size))->u.f.count = (*mb)->u.f.count; + *mb = (struct mbucket *)((char *)*mb + size); + (*mb)->u.f.next = NULL; + } + } else { + /* + * Allocate another chunk of mbufs, use the first and put the rest on + * the free list + */ + *mb = (struct mbucket *)malloc(BUCKET_CHUNK * size); + if (*mb == NULL) { + log_Printf(LogALERT, "Failed to allocate memory (%lu)\n", + (unsigned long)BUCKET_CHUNK * size); + AbortProgram(EX_OSERR); + } + bp = &(*mb)->u.m; + *mb = (struct mbucket *)((char *)*mb + size); + (*mb)->u.f.count = BUCKET_CHUNK - 1; + (*mb)->u.f.next = NULL; + } + + mbuf_Mallocs++; + + memset(bp, '\0', sizeof(struct mbuf)); + bp->m_size = size - sizeof *bp; + bp->m_len = m_len; + bp->m_type = type; + + MemMap[type].fragments++; + MemMap[type].octets += bp->m_size; + + return bp; +} + +struct mbuf * +m_free(struct mbuf *bp) +{ + struct mbucket **mb, *f; + struct mbuf *nbp; + + if ((f = (struct mbucket *)bp) != NULL) { + MemMap[bp->m_type].fragments--; + MemMap[bp->m_type].octets -= bp->m_size; + + nbp = bp->m_next; + mb = M_BUCKET(bp->m_size); + f->u.f.next = *mb; + f->u.f.count = 1; + *mb = f; + + mbuf_Frees++; + bp = nbp; + } + + return bp; +} + +void +m_freem(struct mbuf *bp) +{ + while (bp) + bp = m_free(bp); +} + +struct mbuf * +mbuf_Read(struct mbuf *bp, void *v, size_t len) +{ + int nb; + u_char *ptr = v; + + while (bp && len > 0) { + if (len > bp->m_len) + nb = bp->m_len; + else + nb = len; + if (nb) { + memcpy(ptr, MBUF_CTOP(bp), nb); + ptr += nb; + bp->m_len -= nb; + len -= nb; + bp->m_offset += nb; + } + if (bp->m_len == 0) + bp = m_free(bp); + } + + while (bp && bp->m_len == 0) + bp = m_free(bp); + + return bp; +} + +size_t +mbuf_View(struct mbuf *bp, void *v, size_t len) +{ + size_t nb, l = len; + u_char *ptr = v; + + while (bp && l > 0) { + if (l > bp->m_len) + nb = bp->m_len; + else + nb = l; + memcpy(ptr, MBUF_CTOP(bp), nb); + ptr += nb; + l -= nb; + bp = bp->m_next; + } + + return len - l; +} + +struct mbuf * +m_prepend(struct mbuf *bp, const void *ptr, size_t len, u_short extra) +{ + struct mbuf *head; + + if (bp && bp->m_offset) { + if (bp->m_offset >= len) { + bp->m_offset -= len; + bp->m_len += len; + if (ptr) + memcpy(MBUF_CTOP(bp), ptr, len); + return bp; + } + len -= bp->m_offset; + if (ptr) + memcpy(bp + 1, (const char *)ptr + len, bp->m_offset); + bp->m_len += bp->m_offset; + bp->m_offset = 0; + } + + head = m_get(len + extra, bp ? bp->m_type : MB_UNKNOWN); + head->m_offset = extra; + head->m_len -= extra; + if (ptr) + memcpy(MBUF_CTOP(head), ptr, len); + head->m_next = bp; + + return head; +} + +struct mbuf * +m_adj(struct mbuf *bp, ssize_t n) +{ + if (n > 0) { + while (bp) { + if ((size_t)n < bp->m_len) { + bp->m_len = n; + bp->m_offset += n; + return bp; + } + n -= bp->m_len; + bp = m_free(bp); + } + } else { + if ((n = m_length(bp) + n) <= 0) { + m_freem(bp); + return NULL; + } + for (; bp; bp = bp->m_next, n -= bp->m_len) + if ((size_t)n < bp->m_len) { + bp->m_len = n; + m_freem(bp->m_next); + bp->m_next = NULL; + break; + } + } + + return bp; +} + +void +mbuf_Write(struct mbuf *bp, const void *ptr, size_t m_len) +{ + size_t plen; + int nb; + + plen = m_length(bp); + if (plen < m_len) + m_len = plen; + + while (m_len > 0) { + nb = (m_len < bp->m_len) ? m_len : bp->m_len; + memcpy(MBUF_CTOP(bp), ptr, nb); + m_len -= bp->m_len; + bp = bp->m_next; + } +} + +int +mbuf_Show(struct cmdargs const *arg) +{ + int i; + + prompt_Printf(arg->prompt, "Fragments (octets) in use:\n"); + for (i = 0; i < MB_MAX; i += 2) + prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\t" + "%10.10s: %04lu (%06lu)\n", + mbuftype(i), (u_long)MemMap[i].fragments, + (u_long)MemMap[i].octets, mbuftype(i+1), + (u_long)MemMap[i+1].fragments, (u_long)MemMap[i+1].octets); + + if (i == MB_MAX) + prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\n", + mbuftype(i), (u_long)MemMap[i].fragments, + (u_long)MemMap[i].octets); + + prompt_Printf(arg->prompt, "Mallocs: %llu, Frees: %llu\n", + mbuf_Mallocs, mbuf_Frees); + + return 0; +} + +struct mbuf * +m_dequeue(struct mqueue *q) +{ + struct mbuf *bp; + + log_Printf(LogDEBUG, "m_dequeue: queue len = %lu\n", (u_long)q->len); + bp = q->top; + if (bp) { + q->top = q->top->m_nextpkt; + q->len--; + if (q->top == NULL) { + q->last = q->top; + if (q->len) + log_Printf(LogERROR, "m_dequeue: Not zero (%lu)!!!\n", + (u_long)q->len); + } + bp->m_nextpkt = NULL; + } + + return bp; +} + +void +m_enqueue(struct mqueue *queue, struct mbuf *bp) +{ + if (bp != NULL) { + if (queue->last) { + queue->last->m_nextpkt = bp; + queue->last = bp; + } else + queue->last = queue->top = bp; + queue->len++; + log_Printf(LogDEBUG, "m_enqueue: len = %lu\n", (unsigned long)queue->len); + } +} + +struct mbuf * +m_pullup(struct mbuf *bp) +{ + /* Put it all in one contigous (aligned) mbuf */ + + if (bp != NULL) { + if (bp->m_next != NULL) { + struct mbuf *nbp; + u_char *cp; + + nbp = m_get(m_length(bp), bp->m_type); + + for (cp = MBUF_CTOP(nbp); bp; bp = m_free(bp)) { + memcpy(cp, MBUF_CTOP(bp), bp->m_len); + cp += bp->m_len; + } + bp = nbp; + } +#ifndef __i386__ /* Do any other archs not care about alignment ? */ + else if ((bp->m_offset & (sizeof(long) - 1)) != 0) { + bcopy(MBUF_CTOP(bp), bp + 1, bp->m_len); + bp->m_offset = 0; + } +#endif + } + + return bp; +} + +void +m_settype(struct mbuf *bp, int type) +{ + for (; bp; bp = bp->m_next) + if (type != bp->m_type) { + MemMap[bp->m_type].fragments--; + MemMap[bp->m_type].octets -= bp->m_size; + bp->m_type = type; + MemMap[type].fragments++; + MemMap[type].octets += bp->m_size; + } +} + +struct mbuf * +m_append(struct mbuf *bp, const void *v, size_t sz) +{ + struct mbuf *m = bp; + + if (m) { + while (m->m_next) + m = m->m_next; + if (m->m_size - m->m_len >= sz) { + if (v) + memcpy((char *)(m + 1) + m->m_len, v, sz); + m->m_len += sz; + } else + m->m_next = m_prepend(NULL, v, sz, 0); + } else + bp = m_prepend(NULL, v, sz, 0); + + return bp; +} |