Chinaunix首页 | 论坛 | 博客
  • 博客访问: 378927
  • 博文数量: 56
  • 博客积分: 1449
  • 博客等级: 中尉
  • 技术积分: 822
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-08 10:24
文章分类

全部博文(56)

文章存档

2014年(7)

2012年(13)

2011年(10)

2010年(26)

分类: LINUX

2012-05-24 11:27:27

我跟进的函数调用路径为ip_rcv->pskb_trim_rcsum->__pskb_trim->___pskb_trim
前面的这些函数都非常简单,就不必列出代码分析了。只需要分析___pskb_trim这个函数。

点击(此处)折叠或打开

  1. /* Trims skb to length len. It can change skb pointers.
  2.  */
  3. /* 针对skb中存在非线性数据的情形,将skb的数据长度裁减到len长度,最终skb->len = len
  4.  * 多余的数据会被clean掉。 */
  5. int ___pskb_trim(struct sk_buff *skb, unsigned int len)
  6. {
  7.     struct sk_buff **fragp;
  8.     struct sk_buff *frag;
  9.     int offset = skb_headlen(skb);
  10.     int nfrags = skb_shinfo(skb)->nr_frags;
  11.     int i;
  12.     int err;

  13.     /* 若该skb被克隆过,那么对数据缓冲区获取一份私有的拷贝,因为该函数
  14.      * 会对数据缓冲区进行修改,因此要确保数据缓冲区是独占的 */
  15.     if (skb_cloned(skb) &&
  16.      unlikely((err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))))
  17.         return err;

  18.     i = 0;
  19.     /* offset 是线性数据缓冲区的长度,len是要裁减到的长度,offset大于等于len,
  20.      * 说明线性缓冲区有一部分数据是多余的,而非线性缓冲区的数据都是多余的,因此
  21.      * 需要把非线性缓冲区释放掉。 */
  22.     if (offset >= len)
  23.         goto drop_pages;
  24.     /* 对unmapped page进行trim */
  25.     for (; i < nfrags; i++) {
  26.         int end = offset + skb_shinfo(skb)->frags[i].size;

  27.         if (end < len) {
  28.             offset = end;
  29.             continue;        /* 非多余数据 */
  30.         }

  31.         skb_shinfo(skb)->frags[i++].size = len - offset;    /* 修改长度,部分多余 */

  32. drop_pages:
  33.         skb_shinfo(skb)->nr_frags = i;    /* 更新unmapped page个数 */

  34.         for (; i < nfrags; i++)
  35.             put_page(skb_shinfo(skb)->frags[i].page);    /* 释放掉 */
  36.     /* 整个frag_list中的所有skb的数据都是多余...全部释放 */
  37.         if (skb_shinfo(skb)->frag_list)
  38.             skb_drop_fraglist(skb);
  39.         goto done;
  40.     }
  41.     /* unmapped page中的数据都有效,对frag_list中的数据进行trim */
  42.     for (fragp = &skb_shinfo(skb)->frag_list; (frag = *fragp);
  43.      fragp = &frag->next) {
  44.         int end = offset + frag->len;

  45.     /* 该skb是共享的,则克隆一个,将克隆的加入到链表中 */
  46.     /* 为何要这样?因为接下来可能会对该skb进行修改... */
  47.         if (skb_shared(frag)) {
  48.             struct sk_buff *nfrag;

  49.             nfrag = skb_clone(frag, GFP_ATOMIC);
  50.             if (unlikely(!nfrag))
  51.                 return -ENOMEM;

  52.             nfrag->next = frag->next;
  53.             kfree_skb(frag);
  54.             frag = nfrag;
  55.             *fragp = frag;
  56.         }

  57.         if (end < len) {
  58.             offset = end;
  59.             continue;
  60.         }
  61.     /* 该skb有部分数据多余...调用pskb_trim对该skb进行trim.相当于是递归了.. */
  62.         if (end > len &&
  63.          unlikely((err = pskb_trim(frag, len - offset))))
  64.             return err;
  65.     /* frag_list链中若还有skb,则这些skb包含的都是多余的数据,释放掉 */
  66.         if (frag->next)
  67.             skb_drop_list(&frag->next);
  68.         break;
  69.     }

  70. done:        /* trim 完成了... */
  71.     /* 若trim到的长度大于线性数据缓冲区的长度,则非线性数据缓冲区的长度要减小 */
  72.      * 不管if 如何。skb->len的长度最终都一定是len...为啥还用两个语句呢? 不像内核
  73.      * 抠门的风格啊! */
  74.     if (len > skb_headlen(skb)) {
  75.         skb->data_len -= skb->len - len;
  76.         skb->len = len;
  77.     } else {
  78.         skb->len = len;
  79.         skb->data_len = 0;
  80.         skb_set_tail_pointer(skb, len);
  81.     }

  82.     return 0;
  83. }

阅读(4626) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~