home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 1 / HamRadio.cdr / misc / src0131 / ombuf.c < prev    next >
C/C++ Source or Header  |  1991-01-26  |  12KB  |  559 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.         bp1 = bp->next;
  110.         if(istate()){
  111.             /* Follow indirection, if any */
  112.             free_mbuf(bp->dup);
  113.  
  114.             /* If reference count has gone to zero, put it
  115.              * back on the heap
  116.              */
  117.             if(--bp->refcnt <= 0)
  118.                 free((char *)bp);
  119.         } else {
  120.             /* Quickly put buffer on garbage list, where it will
  121.              * be freed by refiq() later with interrupts enabled
  122.              */
  123.             bp->next = Garbq;
  124.             Garbq = bp;
  125.         }
  126.     }
  127.     return bp1;
  128. }
  129.  
  130. /* Free packet (a chain of mbufs). Return pointer to next packet on queue,
  131.  * if any
  132.  */
  133. struct mbuf *
  134. free_p(bp)
  135. register struct mbuf *bp;
  136. {
  137.     register struct mbuf *abp;
  138.  
  139.     if(bp == NULLBUF)
  140.         return NULLBUF;
  141.     abp = bp->anext;
  142.     while(bp != NULLBUF)
  143.         bp = free_mbuf(bp);
  144.     return abp;
  145. }        
  146. /* Free entire queue of packets (of mbufs) */
  147. void
  148. free_q(q)
  149. struct mbuf **q;
  150. {
  151.     register struct mbuf *bp;
  152.  
  153.     while((bp = dequeue(q)) != NULLBUF)
  154.         free_p(bp);
  155. }
  156.  
  157. /* Count up the total number of bytes in a packet */
  158. int16
  159. len_p(bp)
  160. register struct mbuf *bp;
  161. {
  162.     register int16 cnt = 0;
  163.  
  164.     while(bp != NULLBUF){
  165.         cnt += bp->cnt;
  166.         bp = bp->next;
  167.     }
  168.     return cnt;
  169. }
  170. /* Count up the number of packets in a queue */
  171. int16
  172. len_q(bp)
  173. register struct mbuf *bp;
  174. {
  175.     register int16 cnt;
  176.  
  177.     for(cnt=0;bp != NULLBUF;cnt++,bp = bp->anext)
  178.         ;
  179.     return cnt;
  180. }
  181. /* Trim mbuf to specified length by lopping off end */
  182. void
  183. trim_mbuf(bpp,length)
  184. struct mbuf **bpp;
  185. int16 length;
  186. {
  187.     register int16 tot = 0;
  188.     register struct mbuf *bp;
  189.  
  190.     if(bpp == NULLBUFP || *bpp == NULLBUF)
  191.         return;    /* Nothing to trim */
  192.  
  193.     if(length == 0){
  194.         /* Toss the whole thing */
  195.         free_p(*bpp);
  196.         *bpp = NULLBUF;
  197.         return;
  198.     }
  199.     /* Find the point at which to trim. If length is greater than
  200.      * the packet, we'll just fall through without doing anything
  201.      */
  202.     for( bp = *bpp; bp != NULLBUF; bp = bp->next){
  203.         if(tot + bp->cnt < length){
  204.             tot += bp->cnt;
  205.         } else {
  206.             /* Cut here */
  207.             bp->cnt = length - tot;
  208.             free_p(bp->next);
  209.             bp->next = NULLBUF;
  210.             break;
  211.         }
  212.     }
  213. }
  214. /* Duplicate/enqueue/dequeue operations based on mbufs */
  215.  
  216. /* Duplicate first 'cnt' bytes of packet starting at 'offset'.
  217.  * This is done without copying data; only the headers are duplicated,
  218.  * but without data segments of their own. The pointers are set up to
  219.  * share the data segments of the original copy. The return pointer is
  220.  * passed back through the first argument, and the return value is the
  221.  * number of bytes actually duplicated.
  222.  */
  223. int16
  224. dup_p(hp,bp,offset,cnt)
  225. struct mbuf **hp;
  226. register struct mbuf *bp;
  227. register int16 offset;
  228. register int16 cnt;
  229. {
  230.     register struct mbuf *cp;
  231.     int16 tot;
  232.  
  233.     if(cnt == 0 || bp == NULLBUF || hp == NULLBUFP){
  234.         if(hp != NULLBUFP)
  235.             *hp = NULLBUF;
  236.         return 0;
  237.     }
  238.     if((*hp = cp = alloc_mbuf(0)) == NULLBUF){
  239.         return 0;
  240.     }
  241.     /* Skip over leading mbufs that are smaller than the offset */
  242.     while(bp != NULLBUF && bp->cnt <= offset){
  243.         offset -= bp->cnt;
  244.         bp = bp->next;
  245.     }
  246.     if(bp == NULLBUF){
  247.         free_mbuf(cp);
  248.         *hp = NULLBUF;
  249.         return 0;    /* Offset was too big */
  250.     }
  251.     tot = 0;
  252.     for(;;){
  253.         /* Make sure we get the original, "real" buffer (i.e. handle the
  254.          * case of duping a dupe)
  255.          */
  256.         if(bp->dup != NULLBUF)
  257.             cp->dup = bp->dup;
  258.         else
  259.             cp->dup = bp;
  260.  
  261.         /* Increment the duplicated buffer's reference count */
  262.         cp->dup->refcnt++;
  263.  
  264.         cp->data = bp->data + offset;
  265.         cp->cnt = min(cnt,bp->cnt - offset);
  266.         offset = 0;
  267.         cnt -= cp->cnt;
  268.         tot += cp->cnt;
  269.         bp = bp->next;
  270.         if(cnt == 0 || bp == NULLBUF || (cp->next = alloc_mbuf(0)) == NULLBUF)
  271.             break;
  272.         cp = cp->next;
  273.     }
  274.     return tot;
  275. }
  276. /* Copy first 'cnt' bytes of packet into a new, single mbuf */
  277. struct mbuf *
  278. copy_p(bp,cnt)
  279. register struct mbuf *bp;
  280. register int16 cnt;
  281. {
  282.     register struct mbuf *cp;
  283.     register char *wp;
  284.     register int16 n;
  285.  
  286.     if(bp == NULLBUF || cnt == 0 || (cp = alloc_mbuf(cnt)) == NULLBUF)
  287.         return NULLBUF;
  288.     wp = cp->data;
  289.     while(cnt != 0 && bp != NULLBUF){
  290.         n = min(cnt,bp->cnt);
  291.         memcpy(wp,bp->data,n);
  292.         wp += n;
  293.         cp->cnt += n;
  294.         cnt -= n;
  295.         bp = bp->next;
  296.     }
  297.     return cp;
  298. }
  299. /* Copy and delete "cnt" bytes from beginning of packet. Return number of
  300.  * bytes actually pulled off
  301.  */
  302. int16
  303. pullup(bph,buf,cnt)
  304. struct mbuf **bph;
  305. char *buf;
  306. int16 cnt;
  307. {
  308.     register struct mbuf *bp;
  309.     int16 n,tot;
  310.  
  311.     tot = 0;
  312.     if(bph == NULLBUFP)
  313.         return 0;
  314.     while(cnt != 0 && (bp = *bph) != NULLBUF){
  315.         n = min(cnt,bp->cnt);
  316.         if(buf != NULLCHAR){
  317.             if(n == 1)    /* Common case optimization */
  318.                 *buf = *bp->data;
  319.             else if(n > 1)
  320.                 memcpy(buf,bp->data,n);
  321.             buf += n;
  322.         }
  323.         tot += n;
  324.         cnt -= n;
  325.         bp->data += n;
  326.         bp->cnt -= n;        
  327.         if(bp->cnt == 0){
  328.             /* If this is the last mbuf of a packet but there
  329.              * are others on the queue, return a pointer to
  330.              * the next on the queue. This allows pullups to
  331.              * to work on a packet queue
  332.              */
  333.             if(bp->next == NULLBUF && bp->anext != NULLBUF){
  334.                 *bph = bp->anext;
  335.                 free_mbuf(bp);
  336.             } else
  337.                 *bph = free_mbuf(bp);
  338.         }
  339.     }
  340.     return tot;
  341. }
  342. /* Append mbuf to end of mbuf chain */
  343. void
  344. append(bph,bp)
  345. struct mbuf **bph;
  346. struct mbuf *bp;
  347. {
  348.     register struct mbuf *p;
  349.  
  350.     if(bph == NULLBUFP || bp == NULLBUF)
  351.         return;
  352.     if(*bph == NULLBUF){
  353.         /* First one on chain */
  354.         *bph = bp;
  355.     } else {
  356.         for(p = *bph ; p->next != NULLBUF ; p = p->next)
  357.             ;
  358.         p->next = bp;
  359.     }
  360. }
  361. /* Insert specified amount of contiguous new space at the beginning of an
  362.  * mbuf chain. If enough space is available in the first mbuf, no new space
  363.  * is allocated. Otherwise a mbuf of the appropriate size is allocated and
  364.  * tacked on the front of the chain.
  365.  *
  366.  * This operation is the logical inverse of pullup(), hence the name.
  367.  */
  368. struct mbuf *
  369. pushdown(bp,size)
  370. register struct mbuf *bp;
  371. int16 size;
  372. {
  373.     register struct mbuf *nbp;
  374.  
  375.     /* Check that bp is real, that it hasn't been duplicated, and
  376.      * that it itself isn't a duplicate before checking to see if
  377.      * there's enough space at its front.
  378.      */
  379.     if(bp != NULLBUF && bp->refcnt == 1 && bp->size != 0
  380.      && bp->data - (char *)(bp+1) >= size){
  381.         /* No need to alloc new mbuf, just adjust this one */
  382.         bp->data -= size;
  383.         bp->cnt += size;
  384.     } else {
  385.         nbp = ambufw(size);
  386.         nbp->next = bp;
  387.         nbp->cnt = size;
  388.         bp = nbp;
  389.     }
  390.     return bp;
  391. }
  392. /* Append packet to end of packet queue */
  393. void
  394. enqueue(q,bp)
  395. struct mbuf **q;
  396. struct mbuf *bp;
  397. {
  398.     register struct mbuf *p;
  399.     char i_state;
  400.  
  401.     if(q == NULLBUFP || bp == NULLBUF)
  402.         return;
  403.     i_state = dirps();
  404.     if(*q == NULLBUF){
  405.         /* List is empty, stick at front */
  406.         *q = bp;
  407.     } else {
  408.         for(p = *q ; p->anext != NULLBUF ; p = p->anext)
  409.             ;
  410.         p->anext = bp;
  411.     }
  412.     restore(i_state);
  413.     psignal(q,1);
  414. }
  415. /* Unlink a packet from the head of the queue */
  416. struct mbuf *
  417. dequeue(q)
  418. register struct mbuf **q;
  419. {
  420.     register struct mbuf *bp;
  421.     char i_state;
  422.  
  423.     if(q == NULLBUFP)
  424.         return NULLBUF;
  425.     i_state = dirps();
  426.     if((bp = *q) != NULLBUF){
  427.         *q = bp->anext;
  428.         bp->anext = NULLBUF;
  429.     }
  430.     restore(i_state);
  431.     return bp;
  432. }    
  433.  
  434. /* Copy user data into an mbuf */
  435. struct mbuf *
  436. qdata(data,cnt)
  437. char *data;
  438. int16 cnt;
  439. {
  440.     register struct mbuf *bp;
  441.  
  442.     bp = ambufw(cnt);
  443.     memcpy(bp->data,data,cnt);
  444.     bp->cnt = cnt;
  445.     return bp;
  446. }
  447. /* Copy mbuf data into user buffer */
  448. int16
  449. dqdata(bp,buf,cnt)
  450. struct mbuf *bp;
  451. char *buf;
  452. unsigned cnt;
  453. {
  454.     int16 tot;
  455.     unsigned n;
  456.     struct mbuf *bp1;
  457.  
  458.     if(buf == NULLCHAR)
  459.         return 0;
  460.     
  461.     tot = 0;
  462.     for(bp1 = bp;bp1 != NULLBUF; bp1 = bp1->next){
  463.         n = min(bp1->cnt,cnt);
  464.         memcpy(buf,bp1->data,n);
  465.         cnt -= n;
  466.         buf += n;
  467.         tot += n;
  468.     }
  469.     free_p(bp);
  470.     return tot;
  471. }
  472. /* Pull a 32-bit integer in host order from buffer in network byte order.
  473.  * On error, return 0. Note that this is indistinguishable from a normal
  474.  * return.
  475.  */
  476. int32
  477. pull32(bpp)
  478. struct mbuf **bpp;
  479. {
  480.     char buf[4];
  481.  
  482.     if(pullup(bpp,buf,4) != 4){
  483.         /* Return zero if insufficient buffer */
  484.         return 0;
  485.     }
  486.     return get32(buf);
  487. }
  488. /* Pull a 16-bit integer in host order from buffer in network byte order.
  489.  * Return -1 on error
  490.  */
  491. long
  492. pull16(bpp)
  493. struct mbuf **bpp;
  494. {
  495.     char buf[2];
  496.  
  497.     if(pullup(bpp,buf,2) != 2){
  498.         /* Return -1 if insufficient buffer */
  499.         return -1;
  500.     }
  501.     return get16(buf);
  502. }
  503. /* Pull single character from mbuf */
  504. int
  505. pullchar(bpp)
  506. struct mbuf **bpp;
  507. {
  508.     char c;
  509.  
  510.     if(pullup(bpp,&c,1) != 1)
  511.         return -1;        /* Nothing left */
  512.     return (int)uchar(c);
  513. }
  514. int
  515. write_p(fp,bp)
  516. FILE *fp;
  517. struct mbuf *bp;
  518. {
  519.     while(bp != NULLBUF){
  520.         if(fwrite(bp->data,1,bp->cnt,fp) != bp->cnt)
  521.             return -1;
  522.         bp = bp->next;
  523.     }
  524.     return 0;
  525. }
  526. /* Reclaim unused space in a mbuf chain. If the argument is a chain of mbufs
  527.  * and/or it appears to have wasted space, copy it to a single new mbuf and
  528.  * free the old mbuf(s). But refuse to move mbufs that merely
  529.  * reference other mbufs, or that have other headers referencing them.
  530.  *
  531.  * Be extremely careful that there aren't any other pointers to
  532.  * (or into) this mbuf, since we have no way of detecting them here.
  533.  * This function is meant to be called only when free memory is in
  534.  * short supply.
  535.  */
  536. void
  537. mbuf_crunch(bpp)
  538. struct mbuf **bpp;
  539. {
  540.     register struct mbuf *bp = *bpp;
  541.     struct mbuf *nbp;
  542.  
  543.     if(bp->refcnt > 1 || bp->dup != NULLBUF){
  544.         /* Can't crunch, there are other refs */
  545.         return;
  546.     }
  547.     if(bp->next == NULLBUF && bp->cnt == bp->size){
  548.         /* Nothing to be gained by crunching */
  549.         return;
  550.     }
  551.     if((nbp = copy_p(bp,len_p(bp))) == NULLBUF){
  552.         /* Copy failed due to lack of (contiguous) space */
  553.         return;
  554.     }
  555.     nbp->anext = bp->anext;
  556.     free_p(bp);
  557.     *bpp = nbp;
  558. }
  559.