home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / network / src_1218.zip / MBUF.C < prev    next >
C/C++ Source or Header  |  1991-12-12  |  13KB  |  592 lines

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