mbuf.c
上传用户:hepax88
上传日期:2007-01-03
资源大小:1101k
文件大小:14k
源码类别:

TCP/IP协议栈

开发平台:

Visual C++

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