mbuf.c revision 98243
1/*-
2 * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
3 *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
4 *                           Internet Initiative Japan, Inc (IIJ)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: head/usr.sbin/ppp/mbuf.c 98243 2002-06-15 08:03:30Z brian $
29 */
30
31#include <sys/types.h>
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <sysexits.h>
37#include <termios.h>
38
39#include "defs.h"
40#include "command.h"
41#include "mbuf.h"
42#include "log.h"
43#include "descriptor.h"
44#include "prompt.h"
45#include "main.h"
46
47#define BUCKET_CHUNK	20
48#define BUCKET_HASH	256
49
50struct mbucket;
51
52struct mfree {
53  struct mbucket *next;
54  size_t count;
55};
56
57static struct mbucket {
58  union {
59    struct mbuf m;
60    struct mfree f;
61  } u;
62} *bucket[(M_MAXLEN + sizeof(struct mbuf)) / BUCKET_HASH];
63
64#define M_BINDEX(sz)	(((sz) + sizeof(struct mbuf) - 1) / BUCKET_HASH)
65#define M_BUCKET(sz)	(bucket + M_BINDEX(sz))
66#define M_ROUNDUP(sz)	((M_BINDEX(sz) + 1) * BUCKET_HASH)
67
68static struct memmap {
69  struct mbuf *queue;
70  size_t fragments;
71  size_t octets;
72} MemMap[MB_MAX + 1];
73
74static unsigned long long mbuf_Mallocs, mbuf_Frees;
75
76int
77m_length(struct mbuf *bp)
78{
79  int len;
80
81  for (len = 0; bp; bp = bp->m_next)
82    len += bp->m_len;
83  return len;
84}
85
86static const char *
87mbuftype(int type)
88{
89  static const char * const mbufdesc[MB_MAX] = {
90    "ip in", "ip out", "ipv6 in", "ipv6 out", "nat in", "nat out",
91    "mp in", "mp out", "vj in", "vj out", "icompd in", "icompd out",
92    "compd in", "compd out", "lqr in", "lqr out", "echo in", "echo out",
93    "proto in", "proto out", "acf in", "acf out", "sync in", "sync out",
94    "hdlc in", "hdlc out", "async in", "async out", "cbcp in", "cbcp out",
95    "chap in", "chap out", "pap in", "pap out", "ccp in", "ccp out",
96    "ipcp in", "ipcp out", "ipv6cp in", "ipv6cp out", "lcp in", "lcp out"
97  };
98
99  return type < 0 || type >= MB_MAX ? "unknown" : mbufdesc[type];
100}
101
102struct mbuf *
103m_get(size_t m_len, int type)
104{
105  struct mbucket **mb;
106  struct mbuf *bp;
107  size_t size;
108
109  if (type > MB_MAX) {
110    log_Printf(LogERROR, "Bad mbuf type %d\n", type);
111    type = MB_UNKNOWN;
112  }
113
114  if (m_len > M_MAXLEN || m_len == 0) {
115    log_Printf(LogERROR, "Request for mbuf size %lu (\"%s\") denied !\n",
116               (u_long)m_len, mbuftype(type));
117    AbortProgram(EX_OSERR);
118  }
119
120  mb = M_BUCKET(m_len);
121  size = M_ROUNDUP(m_len);
122
123  if (*mb) {
124    /* We've got some free blocks of the right size */
125    bp = &(*mb)->u.m;
126    if (--(*mb)->u.f.count == 0)
127      *mb = (*mb)->u.f.next;
128    else {
129      ((struct mbucket *)((char *)*mb + size))->u.f.count = (*mb)->u.f.count;
130      *mb = (struct mbucket *)((char *)*mb + size);
131      (*mb)->u.f.next = NULL;
132    }
133  } else {
134    /*
135     * Allocate another chunk of mbufs, use the first and put the rest on
136     * the free list
137     */
138    *mb = (struct mbucket *)malloc(BUCKET_CHUNK * size);
139    if (*mb == NULL) {
140      log_Printf(LogALERT, "Failed to allocate memory (%lu)\n",
141                 (unsigned long)BUCKET_CHUNK * size);
142      AbortProgram(EX_OSERR);
143    }
144    bp = &(*mb)->u.m;
145    *mb = (struct mbucket *)((char *)*mb + size);
146    (*mb)->u.f.count = BUCKET_CHUNK - 1;
147    (*mb)->u.f.next = NULL;
148  }
149
150  mbuf_Mallocs++;
151
152  memset(bp, '\0', sizeof(struct mbuf));
153  bp->m_size = size - sizeof *bp;
154  bp->m_len = m_len;
155  bp->m_type = type;
156
157  MemMap[type].fragments++;
158  MemMap[type].octets += bp->m_size;
159
160  return bp;
161}
162
163struct mbuf *
164m_free(struct mbuf *bp)
165{
166  struct mbucket **mb, *f;
167  struct mbuf *nbp;
168
169  if ((f = (struct mbucket *)bp) != NULL) {
170    MemMap[bp->m_type].fragments--;
171    MemMap[bp->m_type].octets -= bp->m_size;
172
173    nbp = bp->m_next;
174    mb = M_BUCKET(bp->m_size);
175    f->u.f.next = *mb;
176    f->u.f.count = 1;
177    *mb = f;
178
179    mbuf_Frees++;
180    bp = nbp;
181  }
182
183  return bp;
184}
185
186void
187m_freem(struct mbuf *bp)
188{
189  while (bp)
190    bp = m_free(bp);
191}
192
193struct mbuf *
194mbuf_Read(struct mbuf *bp, void *v, size_t len)
195{
196  int nb;
197  u_char *ptr = v;
198
199  while (bp && len > 0) {
200    if (len > bp->m_len)
201      nb = bp->m_len;
202    else
203      nb = len;
204    if (nb) {
205      memcpy(ptr, MBUF_CTOP(bp), nb);
206      ptr += nb;
207      bp->m_len -= nb;
208      len -= nb;
209      bp->m_offset += nb;
210    }
211    if (bp->m_len == 0)
212      bp = m_free(bp);
213  }
214
215  while (bp && bp->m_len == 0)
216    bp = m_free(bp);
217
218  return bp;
219}
220
221size_t
222mbuf_View(struct mbuf *bp, void *v, size_t len)
223{
224  size_t nb, l = len;
225  u_char *ptr = v;
226
227  while (bp && l > 0) {
228    if (l > bp->m_len)
229      nb = bp->m_len;
230    else
231      nb = l;
232    memcpy(ptr, MBUF_CTOP(bp), nb);
233    ptr += nb;
234    l -= nb;
235    bp = bp->m_next;
236  }
237
238  return len - l;
239}
240
241struct mbuf *
242m_prepend(struct mbuf *bp, const void *ptr, size_t len, size_t extra)
243{
244  struct mbuf *head;
245
246  if (bp && bp->m_offset) {
247    if (bp->m_offset >= len) {
248      bp->m_offset -= len;
249      bp->m_len += len;
250      memcpy(MBUF_CTOP(bp), ptr, len);
251      return bp;
252    }
253    len -= bp->m_offset;
254    memcpy(bp + 1, (const char *)ptr + len, bp->m_offset);
255    bp->m_len += bp->m_offset;
256    bp->m_offset = 0;
257  }
258
259  head = m_get(len + extra, bp ? bp->m_type : MB_UNKNOWN);
260  head->m_offset = extra;
261  head->m_len -= extra;
262  if (ptr)
263    memcpy(MBUF_CTOP(head), ptr, len);
264  head->m_next = bp;
265
266  return head;
267}
268
269struct mbuf *
270m_adj(struct mbuf *bp, ssize_t n)
271{
272  if (n > 0) {
273    while (bp) {
274      if (n < bp->m_len) {
275        bp->m_len = n;
276        bp->m_offset += n;
277        return bp;
278      }
279      n -= bp->m_len;
280      bp = m_free(bp);
281    }
282  } else {
283    if ((n = m_length(bp) + n) <= 0) {
284      m_freem(bp);
285      return NULL;
286    }
287    for (; bp; bp = bp->m_next, n -= bp->m_len)
288      if (n < bp->m_len) {
289        bp->m_len = n;
290        m_freem(bp->m_next);
291        bp->m_next = NULL;
292        break;
293      }
294  }
295
296  return bp;
297}
298
299void
300mbuf_Write(struct mbuf *bp, const void *ptr, size_t m_len)
301{
302  int plen;
303  int nb;
304
305  plen = m_length(bp);
306  if (plen < m_len)
307    m_len = plen;
308
309  while (m_len > 0) {
310    nb = (m_len < bp->m_len) ? m_len : bp->m_len;
311    memcpy(MBUF_CTOP(bp), ptr, nb);
312    m_len -= bp->m_len;
313    bp = bp->m_next;
314  }
315}
316
317int
318mbuf_Show(struct cmdargs const *arg)
319{
320  int i;
321
322  prompt_Printf(arg->prompt, "Fragments (octets) in use:\n");
323  for (i = 0; i < MB_MAX; i += 2)
324    prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\t"
325                  "%10.10s: %04lu (%06lu)\n",
326	          mbuftype(i), (u_long)MemMap[i].fragments,
327                  (u_long)MemMap[i].octets, mbuftype(i+1),
328                  (u_long)MemMap[i+1].fragments, (u_long)MemMap[i+1].octets);
329
330  if (i == MB_MAX)
331    prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\n",
332                  mbuftype(i), (u_long)MemMap[i].fragments,
333                  (u_long)MemMap[i].octets);
334
335  prompt_Printf(arg->prompt, "Mallocs: %llu,   Frees: %llu\n",
336                mbuf_Mallocs, mbuf_Frees);
337
338  return 0;
339}
340
341struct mbuf *
342m_dequeue(struct mqueue *q)
343{
344  struct mbuf *bp;
345
346  log_Printf(LogDEBUG, "m_dequeue: queue len = %lu\n", (u_long)q->len);
347  bp = q->top;
348  if (bp) {
349    q->top = q->top->m_nextpkt;
350    q->len--;
351    if (q->top == NULL) {
352      q->last = q->top;
353      if (q->len)
354	log_Printf(LogERROR, "m_dequeue: Not zero (%lu)!!!\n",
355                   (u_long)q->len);
356    }
357    bp->m_nextpkt = NULL;
358  }
359
360  return bp;
361}
362
363void
364m_enqueue(struct mqueue *queue, struct mbuf *bp)
365{
366  if (bp != NULL) {
367    if (queue->last) {
368      queue->last->m_nextpkt = bp;
369      queue->last = bp;
370    } else
371      queue->last = queue->top = bp;
372    queue->len++;
373    log_Printf(LogDEBUG, "m_enqueue: len = %lu\n", (unsigned long)queue->len);
374  }
375}
376
377struct mbuf *
378m_pullup(struct mbuf *bp)
379{
380  /* Put it all in one contigous (aligned) mbuf */
381
382  if (bp != NULL) {
383    if (bp->m_next != NULL) {
384      struct mbuf *nbp;
385      u_char *cp;
386
387      nbp = m_get(m_length(bp), bp->m_type);
388
389      for (cp = MBUF_CTOP(nbp); bp; bp = m_free(bp)) {
390        memcpy(cp, MBUF_CTOP(bp), bp->m_len);
391        cp += bp->m_len;
392      }
393      bp = nbp;
394    }
395#ifndef __i386__	/* Do any other archs not care about alignment ? */
396    else if ((bp->m_offset & (sizeof(long) - 1)) != 0) {
397      bcopy(MBUF_CTOP(bp), bp + 1, bp->m_len);
398      bp->m_offset = 0;
399    }
400#endif
401  }
402
403  return bp;
404}
405
406void
407m_settype(struct mbuf *bp, int type)
408{
409  for (; bp; bp = bp->m_next)
410    if (type != bp->m_type) {
411      MemMap[bp->m_type].fragments--;
412      MemMap[bp->m_type].octets -= bp->m_size;
413      bp->m_type = type;
414      MemMap[type].fragments++;
415      MemMap[type].octets += bp->m_size;
416    }
417}
418
419struct mbuf *
420m_append(struct mbuf *bp, const void *v, size_t sz)
421{
422  struct mbuf *m = bp;
423
424  if (m) {
425    while (m->m_next)
426      m = m->m_next;
427    if (m->m_size - m->m_len > sz)
428      m->m_len += sz;
429    else
430      m->m_next = m_prepend(NULL, v, sz, 0);
431  } else
432    bp = m_prepend(NULL, v, sz, 0);
433
434  return bp;
435}
436