我跟进的函数调用路径为ip_rcv->pskb_trim_rcsum->__pskb_trim->___pskb_trim
前面的这些函数都非常简单,就不必列出代码分析了。只需要分析___pskb_trim这个函数。
- /* Trims skb to length len. It can change skb pointers.
- */
- /* 针对skb中存在非线性数据的情形,将skb的数据长度裁减到len长度,最终skb->len = len
- * 多余的数据会被clean掉。 */
- int ___pskb_trim(struct sk_buff *skb, unsigned int len)
- {
- struct sk_buff **fragp;
- struct sk_buff *frag;
- int offset = skb_headlen(skb);
- int nfrags = skb_shinfo(skb)->nr_frags;
- int i;
- int err;
- /* 若该skb被克隆过,那么对数据缓冲区获取一份私有的拷贝,因为该函数
- * 会对数据缓冲区进行修改,因此要确保数据缓冲区是独占的 */
- if (skb_cloned(skb) &&
- unlikely((err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))))
- return err;
- i = 0;
- /* offset 是线性数据缓冲区的长度,len是要裁减到的长度,offset大于等于len,
- * 说明线性缓冲区有一部分数据是多余的,而非线性缓冲区的数据都是多余的,因此
- * 需要把非线性缓冲区释放掉。 */
- if (offset >= len)
- goto drop_pages;
- /* 对unmapped page进行trim */
- for (; i < nfrags; i++) {
- int end = offset + skb_shinfo(skb)->frags[i].size;
- if (end < len) {
- offset = end;
- continue; /* 非多余数据 */
- }
- skb_shinfo(skb)->frags[i++].size = len - offset; /* 修改长度,部分多余 */
- drop_pages:
- skb_shinfo(skb)->nr_frags = i; /* 更新unmapped page个数 */
- for (; i < nfrags; i++)
- put_page(skb_shinfo(skb)->frags[i].page); /* 释放掉 */
- /* 整个frag_list中的所有skb的数据都是多余...全部释放 */
- if (skb_shinfo(skb)->frag_list)
- skb_drop_fraglist(skb);
- goto done;
- }
- /* unmapped page中的数据都有效,对frag_list中的数据进行trim */
- for (fragp = &skb_shinfo(skb)->frag_list; (frag = *fragp);
- fragp = &frag->next) {
- int end = offset + frag->len;
- /* 该skb是共享的,则克隆一个,将克隆的加入到链表中 */
- /* 为何要这样?因为接下来可能会对该skb进行修改... */
- if (skb_shared(frag)) {
- struct sk_buff *nfrag;
- nfrag = skb_clone(frag, GFP_ATOMIC);
- if (unlikely(!nfrag))
- return -ENOMEM;
- nfrag->next = frag->next;
- kfree_skb(frag);
- frag = nfrag;
- *fragp = frag;
- }
- if (end < len) {
- offset = end;
- continue;
- }
- /* 该skb有部分数据多余...调用pskb_trim对该skb进行trim.相当于是递归了.. */
- if (end > len &&
- unlikely((err = pskb_trim(frag, len - offset))))
- return err;
- /* frag_list链中若还有skb,则这些skb包含的都是多余的数据,释放掉 */
- if (frag->next)
- skb_drop_list(&frag->next);
- break;
- }
- done: /* trim 完成了... */
- /* 若trim到的长度大于线性数据缓冲区的长度,则非线性数据缓冲区的长度要减小 */
- * 不管if 如何。skb->len的长度最终都一定是len...为啥还用两个语句呢? 不像内核
- * 抠门的风格啊! */
- if (len > skb_headlen(skb)) {
- skb->data_len -= skb->len - len;
- skb->len = len;
- } else {
- skb->len = len;
- skb->data_len = 0;
- skb_set_tail_pointer(skb, len);
- }
- return 0;
- }
阅读(577) | 评论(0) | 转发(0) |