home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 1 / HamRadio.cdr / misc / tcpipsrc / mbuf.c < prev    next >
C/C++ Source or Header  |  1991-01-29  |  13KB  |  578 lines

  1. /* mbuf (message buffer) primitives
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. #include <stdio.h>
  5. #include "global.h"
  6. #include "mbuf.h"
  7. #include "proc.h"
  8. #include "config.h"
  9.  
  10. /* Interrupt buffer pool */
  11. int Intqlen;            /* Number of free mbufs on Intq */
  12. struct mbuf *Intq;        /* Mbuf pool for interrupt handlers */
  13. struct mbuf *Garbq;        /* List of buffers freed at interrupt time */
  14. long Ibuffail;            /* Allocate failures */
  15.  
  16. void
  17. refiq()
  18. {
  19.     register struct mbuf *bp;
  20.     char i_state;
  21.  
  22.     /* Empty the garbage */
  23.     if(Garbq != NULLBUF){
  24.         i_state = dirps();
  25.         bp = Garbq;
  26.         Garbq = NULLBUF;
  27.         restore(i_state);
  28.         free_p(bp);
  29.     }
  30.     /* Replenish interrupt buffer pool */
  31.     while(Intqlen < Nibufs){
  32.         if((bp = alloc_mbuf(Ibufsize)) == NULLBUF)
  33.             break;
  34.         i_state = dirps();
  35.         bp->next = Intq;
  36.         Intq = bp;
  37.         restore(i_state);
  38.         Intqlen++;
  39.     }
  40. }
  41.  
  42. void
  43. iqstat()
  44. {
  45.     tprintf("Intqlen %u Ibufsize %u Ibuffail %lu\n",
  46.         Intqlen,Ibufsize,Ibuffail);
  47. }
  48. /* Allocate mbuf with associated buffer of 'size' bytes. If interrupts
  49.  * are enabled, use the regular heap. If they're off, use the special
  50.  * interrupt buffer pool.
  51.  */
  52. struct mbuf *
  53. alloc_mbuf(size)
  54. register int16 size;
  55. {
  56.     register struct mbuf *bp;
  57.  
  58.     if(istate()){
  59.         /* Interrupts are enabled, use the heap normally */
  60.         bp = (struct mbuf *)malloc((unsigned)(size + sizeof(struct mbuf)));
  61.         if(bp == NULLBUF)
  62.             return NULLBUF;
  63.         /* Clear just the header portion */
  64.         memset((char *)bp,0,sizeof(struct mbuf));
  65.         if((bp->size = size) != 0)
  66.             bp->data = (char *)(bp + 1);
  67.         bp->refcnt++;
  68.     } else {
  69.         /* Interrupts are off, use special interrupt buffer pool */
  70.         if(size > Ibufsize || Intq == NULLBUF){
  71.             Ibuffail++;
  72.             return NULLBUF;
  73.         }
  74.         bp = Intq;
  75.         Intq = bp->next;
  76.         bp->next = NULLBUF;
  77.         Intqlen--;
  78.     }
  79.     return bp;
  80. }
  81. /* Allocate mbuf, waiting if memory is unavailable */
  82. struct mbuf *
  83. ambufw(size)
  84. int16 size;
  85. {
  86.     register struct mbuf *bp;
  87.  
  88.     bp = (struct mbuf *)mallocw((unsigned)(size + sizeof(struct mbuf)));
  89.  
  90.     /* Clear just the header portion */
  91.     memset((char *)bp,0,sizeof(struct mbuf));
  92.     if((bp->size = size) != 0)
  93.         bp->data = (char *)(bp + 1);
  94.     bp->refcnt++;
  95.     return bp;
  96. }
  97.  
  98. /* Decrement the reference pointer in an mbuf. If it goes to zero,
  99.  * free all resources associated with mbuf.
  100.  * Return pointer to next mbuf in packet chain
  101.  */
  102. struct mbuf *
  103. free_mbuf(bp)
  104. register struct mbuf *bp;
  105. {
  106.     register struct mbuf *bp1 = NULLBUF;
  107.  
  108.     if(bp == NULLBUF)
  109.         return NULLBUF;
  110.  
  111.     bp1 = bp->next;
  112.     if(bp->dup != NULLBUF){
  113.         free_mbuf(bp->dup);    /* Follow indirection */
  114.         bp->dup = NULLBUF;
  115.     }
  116.     /* Decrement reference count. If it has gone to zero, free it. */
  117.     if(--bp->refcnt <= 0){
  118.         if(istate()){
  119.             free((char *)bp);
  120.         } else {
  121.             /* If the interrupt pool isn't full and this buffer
  122.              * appears to have come from it, put it back.
  123.              * Otherwise put it on the garbage list where it
  124.              * will be freed by refiq() later with interrupts
  125.              * enabled.
  126.              *
  127.              * This test handles the common special case of
  128.              * an interrupt handler allocating a buffer and
  129.              * then freeing it before returning (e.g., due to
  130.              * a receive abort or CRC failure).
  131.              */
  132.             if(bp->size == Ibufsize && Intqlen < Nibufs){
  133.                 bp->next = Intq;
  134.                 bp->anext = NULLBUF;
  135.                 bp->data = (char *)(bp + 1);
  136.                 bp->cnt = 0;
  137.                 Intq = bp;
  138.                 Intqlen++;
  139.             } else {
  140.                 bp->refcnt = 1;    /* Adjust */
  141.                 bp->next = Garbq;
  142.                 Garbq = bp;
  143.             }
  144.         }
  145.     }
  146.     return bp1;
  147. }
  148.  
  149. /* Free packet (a chain of mbufs). Return pointer to next packet on queue,
  150.  * if any
  151.  */
  152. struct mbuf *
  153. free_p(bp)
  154. register struct mbuf *bp;
  155. {
  156.     register struct mbuf *abp;
  157.  
  158.     if(bp == NULLBUF)
  159.         return NULLBUF;
  160.     abp = bp->anext;
  161.     while(bp != NULLBUF)
  162.         bp = free_mbuf(bp);
  163.     return abp;
  164. }        
  165. /* Free entire queue of packets (of mbufs) */
  166. void
  167. free_q(q)
  168. struct mbuf **q;
  169. {
  170.     register struct mbuf *bp;
  171.  
  172.     while((bp = dequeue(q)) != NULLBUF)
  173.         free_p(bp);
  174. }
  175.  
  176. /* Count up the total number of bytes in a packet */
  177. int16
  178. len_p(bp)
  179. register struct mbuf *bp;
  180. {
  181.     register int16 cnt = 0;
  182.  
  183.     while(bp != NULLBUF){
  184.         cnt += bp->cnt;
  185.         bp = bp->next;
  186.     }
  187.     return cnt;
  188. }
  189. /* Count up the number of packets in a queue */
  190. int16
  191. len_q(bp)
  192. register struct mbuf *bp;
  193. {
  194.     register int16 cnt;
  195.  
  196.     for(cnt=0;bp != NULLBUF;cnt++,bp = bp->anext)
  197.         ;
  198.     return cnt;
  199. }
  200. /* Trim mbuf to specified length by lopping off end */
  201. void
  202. trim_mbuf(bpp,length)
  203. struct mbuf **bpp;
  204. int16 length;
  205. {
  206.     register int16 tot = 0;
  207.     register struct mbuf *bp;
  208.  
  209.     if(bpp == NULLBUFP || *bpp == NULLBUF)
  210.         return;    /* Nothing to trim */
  211.  
  212.     if(length == 0){
  213.         /* Toss the whole thing */
  214.         free_p(*bpp);
  215.         *bpp = NULLBUF;
  216.         return;
  217.     }
  218.     /* Find the point at which to trim. If length is greater than
  219.      * the packet, we'll just fall through without doing anything
  220.      */
  221.     for( bp = *bpp; bp != NULLBUF; bp = bp->next){
  222.         if(tot + bp->cnt < length){
  223.             tot += bp->cnt;
  224.         } else {
  225.             /* Cut here */
  226.             bp->cnt = length - tot;
  227.             free_p(bp->next);
  228.             bp->next = NULLBUF;
  229.             break;
  230.         }
  231.     }
  232. }
  233. /* Duplicate/enqueue/dequeue operations based on mbufs */
  234.  
  235. /* Duplicate first 'cnt' bytes of packet starting at 'offset'.
  236.  * This is done without copying data; only the headers are duplicated,
  237.  * but without data segments of their own. The pointers are set up to
  238.  * share the data segments of the original copy. The return pointer is
  239.  * passed back through the first argument, and the return value is the
  240.  * number of bytes actually duplicated.
  241.  */
  242. int16
  243. dup_p(hp,bp,offset,cnt)
  244. struct mbuf **hp;
  245. register struct mbuf *bp;
  246. register int16 offset;
  247. register int16 cnt;
  248. {
  249.     register struct mbuf *cp;
  250.     int16 tot;
  251.  
  252.     if(cnt == 0 || bp == NULLBUF || hp == NULLBUFP){
  253.         if(hp != NULLBUFP)
  254.             *hp = NULLBUF;
  255.         return 0;
  256.     }
  257.     if((*hp = cp = alloc_mbuf(0)) == NULLBUF){
  258.         return 0;
  259.     }
  260.     /* Skip over leading mbufs that are smaller than the offset */
  261.     while(bp != NULLBUF && bp->cnt <= offset){
  262.         offset -= bp->cnt;
  263.         bp = bp->next;
  264.     }
  265.     if(bp == NULLBUF){
  266.         free_mbuf(cp);
  267.         *hp = NULLBUF;
  268.         return 0;    /* Offset was too big */
  269.     }
  270.     tot = 0;
  271.     for(;;){
  272.         /* Make sure we get the original, "real" buffer (i.e. handle the
  273.          * case of duping a dupe)
  274.          */
  275.         if(bp->dup != NULLBUF)
  276.             cp->dup = bp->dup;
  277.         else
  278.             cp->dup = bp;
  279.  
  280.         /* Increment the duplicated buffer's reference count */
  281.         cp->dup->refcnt++;
  282.  
  283.         cp->data = bp->data + offset;
  284.         cp->cnt = min(cnt,bp->cnt - offset);
  285.         offset = 0;
  286.         cnt -= cp->cnt;
  287.         tot += cp->cnt;
  288.         bp = bp->next;
  289.         if(cnt == 0 || bp == NULLBUF || (cp->next = alloc_mbuf(0)) == NULLBUF)
  290.             break;
  291.         cp = cp->next;
  292.     }
  293.     return tot;
  294. }
  295. /* Copy first 'cnt' bytes of packet into a new, single mbuf */
  296. struct mbuf *
  297. copy_p(bp,cnt)
  298. register struct mbuf *bp;
  299. register int16 cnt;
  300. {
  301.     register struct mbuf *cp;
  302.     register char *wp;
  303.     register int16 n;
  304.  
  305.     if(bp == NULLBUF || cnt == 0 || (cp = alloc_mbuf(cnt)) == NULLBUF)
  306.         return NULLBUF;
  307.     wp = cp->data;
  308.     while(cnt != 0 && bp != NULLBUF){
  309.         n = min(cnt,bp->cnt);
  310.         memcpy(wp,bp->data,n);
  311.         wp += n;
  312.         cp->cnt += n;
  313.         cnt -= n;
  314.         bp = bp->next;
  315.     }
  316.     return cp;
  317. }
  318. /* Copy and delete "cnt" bytes from beginning of packet. Return number of
  319.  * bytes actually pulled off
  320.  */
  321. int16
  322. pullup(bph,buf,cnt)
  323. struct mbuf **bph;
  324. char *buf;
  325. int16 cnt;
  326. {
  327.     register struct mbuf *bp;
  328.     int16 n,tot;
  329.  
  330.     tot = 0;
  331.     if(bph == NULLBUFP)
  332.         return 0;
  333.     while(cnt != 0 && (bp = *bph) != NULLBUF){
  334.         n = min(cnt,bp->cnt);
  335.         if(buf != NULLCHAR){
  336.             if(n == 1)    /* Common case optimization */
  337.                 *buf = *bp->data;
  338.             else if(n > 1)
  339.                 memcpy(buf,bp->data,n);
  340.             buf += n;
  341.         }
  342.         tot += n;
  343.         cnt -= n;
  344.         bp->data += n;
  345.         bp->cnt -= n;        
  346.         if(bp->cnt == 0){
  347.             /* If this is the last mbuf of a packet but there
  348.              * are others on the queue, return a pointer to
  349.              * the next on the queue. This allows pullups to
  350.              * to work on a packet queue
  351.              */
  352.             if(bp->next == NULLBUF && bp->anext != NULLBUF){
  353.                 *bph = bp->anext;
  354.                 free_mbuf(bp);
  355.             } else
  356.                 *bph = free_mbuf(bp);
  357.         }
  358.     }
  359.     return tot;
  360. }
  361. /* Append mbuf to end of mbuf chain */
  362. void
  363. append(bph,bp)
  364. struct mbuf **bph;
  365. struct mbuf *bp;
  366. {
  367.     register struct mbuf *p;
  368.  
  369.     if(bph == NULLBUFP || bp == NULLBUF)
  370.         return;
  371.     if(*bph == NULLBUF){
  372.         /* First one on chain */
  373.         *bph = bp;
  374.     } else {
  375.         for(p = *bph ; p->next != NULLBUF ; p = p->next)
  376.             ;
  377.         p->next = bp;
  378.     }
  379. }
  380. /* Insert specified amount of contiguous new space at the beginning of an
  381.  * mbuf chain. If enough space is available in the first mbuf, no new space
  382.  * is allocated. Otherwise a mbuf of the appropriate size is allocated and
  383.  * tacked on the front of the chain.
  384.  *
  385.  * This operation is the logical inverse of pullup(), hence the name.
  386.  */
  387. struct mbuf *
  388. pushdown(bp,size)
  389. register struct mbuf *bp;
  390. int16 size;
  391. {
  392.     register struct mbuf *nbp;
  393.  
  394.     /* Check that bp is real, that it hasn't been duplicated, and
  395.      * that it itself isn't a duplicate before checking to see if
  396.      * there's enough space at its front.
  397.      */
  398.     if(bp != NULLBUF && bp->refcnt == 1 && bp->size != 0
  399.      && bp->data - (char *)(bp+1) >= size){
  400.         /* No need to alloc new mbuf, just adjust this one */
  401.         bp->data -= size;
  402.         bp->cnt += size;
  403.     } else {
  404.         nbp = ambufw(size);
  405.         nbp->next = bp;
  406.         nbp->cnt = size;
  407.         bp = nbp;
  408.     }
  409.     return bp;
  410. }
  411. /* Append packet to end of packet queue */
  412. void
  413. enqueue(q,bp)
  414. struct mbuf **q;
  415. struct mbuf *bp;
  416. {
  417.     register struct mbuf *p;
  418.     char i_state;
  419.  
  420.     if(q == NULLBUFP || bp == NULLBUF)
  421.         return;
  422.     i_state = dirps();
  423.     if(*q == NULLBUF){
  424.         /* List is empty, stick at front */
  425.         *q = bp;
  426.     } else {
  427.         for(p = *q ; p->anext != NULLBUF ; p = p->anext)
  428.             ;
  429.         p->anext = bp;
  430.     }
  431.     restore(i_state);
  432.     psignal(q,1);
  433. }
  434. /* Unlink a packet from the head of the queue */
  435. struct mbuf *
  436. dequeue(q)
  437. register struct mbuf **q;
  438. {
  439.     register struct mbuf *bp;
  440.     char i_state;
  441.  
  442.     if(q == NULLBUFP)
  443.         return NULLBUF;
  444.     i_state = dirps();
  445.     if((bp = *q) != NULLBUF){
  446.         *q = bp->anext;
  447.         bp->anext = NULLBUF;
  448.     }
  449.     restore(i_state);
  450.     return bp;
  451. }    
  452.  
  453. /* Copy user data into an mbuf */
  454. struct mbuf *
  455. qdata(data,cnt)
  456. char *data;
  457. int16 cnt;
  458. {
  459.     register struct mbuf *bp;
  460.  
  461.     bp = ambufw(cnt);
  462.     memcpy(bp->data,data,cnt);
  463.     bp->cnt = cnt;
  464.     return bp;
  465. }
  466. /* Copy mbuf data into user buffer */
  467. int16
  468. dqdata(bp,buf,cnt)
  469. struct mbuf *bp;
  470. char *buf;
  471. unsigned cnt;
  472. {
  473.     int16 tot;
  474.     unsigned n;
  475.     struct mbuf *bp1;
  476.  
  477.     if(buf == NULLCHAR)
  478.         return 0;
  479.     
  480.     tot = 0;
  481.     for(bp1 = bp;bp1 != NULLBUF; bp1 = bp1->next){
  482.         n = min(bp1->cnt,cnt);
  483.         memcpy(buf,bp1->data,n);
  484.         cnt -= n;
  485.         buf += n;
  486.         tot += n;
  487.     }
  488.     free_p(bp);
  489.     return tot;
  490. }
  491. /* Pull a 32-bit integer in host order from buffer in network byte order.
  492.  * On error, return 0. Note that this is indistinguishable from a normal
  493.  * return.
  494.  */
  495. int32
  496. pull32(bpp)
  497. struct mbuf **bpp;
  498. {
  499.     char buf[4];
  500.  
  501.     if(pullup(bpp,buf,4) != 4){
  502.         /* Return zero if insufficient buffer */
  503.         return 0;
  504.     }
  505.     return get32(buf);
  506. }
  507. /* Pull a 16-bit integer in host order from buffer in network byte order.
  508.  * Return -1 on error
  509.  */
  510. long
  511. pull16(bpp)
  512. struct mbuf **bpp;
  513. {
  514.     char buf[2];
  515.  
  516.     if(pullup(bpp,buf,2) != 2){
  517.         /* Return -1 if insufficient buffer */
  518.         return -1;
  519.     }
  520.     return get16(buf);
  521. }
  522. /* Pull single character from mbuf */
  523. int
  524. pullchar(bpp)
  525. struct mbuf **bpp;
  526. {
  527.     char c;
  528.  
  529.     if(pullup(bpp,&c,1) != 1)
  530.         return -1;        /* Nothing left */
  531.     return (int)uchar(c);
  532. }
  533. int
  534. write_p(fp,bp)
  535. FILE *fp;
  536. struct mbuf *bp;
  537. {
  538.     while(bp != NULLBUF){
  539.         if(fwrite(bp->data,1,bp->cnt,fp) != bp->cnt)
  540.             return -1;
  541.         bp = bp->next;
  542.     }
  543.     return 0;
  544. }
  545. /* Reclaim unused space in a mbuf chain. If the argument is a chain of mbufs
  546.  * and/or it appears to have wasted space, copy it to a single new mbuf and
  547.  * free the old mbuf(s). But refuse to move mbufs that merely
  548.  * reference other mbufs, or that have other headers referencing them.
  549.  *
  550.  * Be extremely careful that there aren't any other pointers to
  551.  * (or into) this mbuf, since we have no way of detecting them here.
  552.  * This function is meant to be called only when free memory is in
  553.  * short supply.
  554.  */
  555. void
  556. mbuf_crunch(bpp)
  557. struct mbuf **bpp;
  558. {
  559.     register struct mbuf *bp = *bpp;
  560.     struct mbuf *nbp;
  561.  
  562.     if(bp->refcnt > 1 || bp->dup != NULLBUF){
  563.         /* Can't crunch, there are other refs */
  564.         return;
  565.     }
  566.     if(bp->next == NULLBUF && bp->cnt == bp->size){
  567.         /* Nothing to be gained by crunching */
  568.         return;
  569.     }
  570.     if((nbp = copy_p(bp,len_p(bp))) == NULLBUF){
  571.         /* Copy failed due to lack of (contiguous) space */
  572.         return;
  573.     }
  574.     nbp->anext = bp->anext;
  575.     free_p(bp);
  576.     *bpp = nbp;
  577. }
  578.