• TCP输入 之 tcp_queue_rcv


    tcp_queue_rcv用于将接收到的skb加入到接收队列receive_queue中,首先会调用tcp_try_coalesce进行分段合并到队列中最后一个skb的尝试,若失败则调用__skb_queue_tail添加该skb到队列尾部;

     1 static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen,
     2           bool *fragstolen)
     3 {
     4     int eaten;
     5 
     6     /* 取队尾 */
     7     struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue);
     8 
     9     __skb_pull(skb, hdrlen);
    10 
    11     /* 尝试进行分段合并 */
    12     eaten = (tail &&
    13          tcp_try_coalesce(sk, tail, skb, fragstolen)) ? 1 : 0;
    14 
    15     /* 更新下一个期望接收的序号 */
    16     tcp_rcv_nxt_update(tcp_sk(sk), TCP_SKB_CB(skb)->end_seq);
    17 
    18     /* 未合并 */
    19     if (!eaten) {
    20         /* 添加到队列尾部 */
    21         __skb_queue_tail(&sk->sk_receive_queue, skb);
    22 
    23         /* 关联控制块 */
    24         skb_set_owner_r(skb, sk);
    25     }
    26     return eaten;
    27 }

    tcp_try_coalesce函数进行合并数据段操作,若合并成功,则更新CB中的对应字段值;

     1 static bool tcp_try_coalesce(struct sock *sk,
     2                  struct sk_buff *to,
     3                  struct sk_buff *from,
     4                  bool *fragstolen)
     5 {
     6     int delta;
     7 
     8     *fragstolen = false;
     9 
    10     /* Its possible this segment overlaps with prior segment in queue */
    11     /* 序号对不上 */
    12     if (TCP_SKB_CB(from)->seq != TCP_SKB_CB(to)->end_seq)
    13         return false;
    14 
    15     /* 尝试合并到前一个数据段 */
    16     if (!skb_try_coalesce(to, from, fragstolen, &delta))
    17         return false;
    18 
    19     /* 调整内存使用 */
    20     atomic_add(delta, &sk->sk_rmem_alloc);
    21     sk_mem_charge(sk, delta);
    22     NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRCVCOALESCE);
    23 
    24     /* 更新cb相关字段 */
    25     TCP_SKB_CB(to)->end_seq = TCP_SKB_CB(from)->end_seq;
    26     TCP_SKB_CB(to)->ack_seq = TCP_SKB_CB(from)->ack_seq;
    27     TCP_SKB_CB(to)->tcp_flags |= TCP_SKB_CB(from)->tcp_flags;
    28     return true;
    29 }

    skb_try_coalesce函数为详细的合并过程,在进行了必要的合并检查之后进行合并;其中当skb线性区域有数据的时候,会将该线性区域处理成frag,并合并到模板skb中;对于非线性区域,则直接进行拷贝,如果是clone的,还需要增加frag的引用计数;合并完成之后,调整skb数据长度值;

     1 bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
     2               bool *fragstolen, int *delta_truesize)
     3 {
     4     int i, delta, len = from->len;
     5 
     6     *fragstolen = false;
     7 
     8     /* 不能为克隆 */
     9     if (skb_cloned(to))
    10         return false;
    11 
    12     /* to尾部能够容纳得下新数据 */
    13     if (len <= skb_tailroom(to)) {
    14         /* from拷贝到to尾部 */
    15         if (len)
    16             BUG_ON(skb_copy_bits(from, 0, skb_put(to, len), len));
    17         *delta_truesize = 0;
    18         return true;
    19     }
    20 
    21     /* to或者from有分片 */
    22     if (skb_has_frag_list(to) || skb_has_frag_list(from))
    23         return false;
    24 
    25     /* 线性缓冲区数据长度不为0 */
    26     if (skb_headlen(from) != 0) {
    27         struct page *page;
    28         unsigned int offset;
    29 
    30         /* 达到最大frags限制 */
    31         if (skb_shinfo(to)->nr_frags +
    32             skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS)
    33             return false;
    34         /* skb被锁定 */
    35         if (skb_head_is_locked(from))
    36             return false;
    37 
    38         /* 计算数据增量,去掉头部 */
    39         delta = from->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff));
    40 
    41         /* 找到对应的页和偏移 */
    42         page = virt_to_head_page(from->head);
    43         offset = from->data - (unsigned char *)page_address(page);
    44 
    45         /* 根据from的页和偏移在to的frags上增加一个frag */
    46         skb_fill_page_desc(to, skb_shinfo(to)->nr_frags,
    47                    page, offset, skb_headlen(from));
    48         *fragstolen = true;
    49     } else {
    50 
    51         /* 达到最大frags限制 */
    52         if (skb_shinfo(to)->nr_frags +
    53             skb_shinfo(from)->nr_frags > MAX_SKB_FRAGS)
    54             return false;
    55 
    56         /* 计算增量,减掉所有头部和无数据线性区域 */
    57         delta = from->truesize - SKB_TRUESIZE(skb_end_offset(from));
    58     }
    59 
    60     WARN_ON_ONCE(delta < len);
    61 
    62     /* 拷贝frags */
    63     memcpy(skb_shinfo(to)->frags + skb_shinfo(to)->nr_frags,
    64            skb_shinfo(from)->frags,
    65            skb_shinfo(from)->nr_frags * sizeof(skb_frag_t));
    66     /* 增加frags数量 */
    67     skb_shinfo(to)->nr_frags += skb_shinfo(from)->nr_frags;
    68 
    69     /* 不是克隆的,设置from的frags为0 */
    70     if (!skb_cloned(from))
    71         skb_shinfo(from)->nr_frags = 0;
    72 
    73     /* if the skb is not cloned this does nothing
    74      * since we set nr_frags to 0.
    75      */
    76     /* 克隆的,则需要对frags增加引用 */
    77     for (i = 0; i < skb_shinfo(from)->nr_frags; i++)
    78         skb_frag_ref(from, i);
    79 
    80     /* 总长度加上增量 */
    81     to->truesize += delta;
    82 
    83     /* 总数据长度增加 */
    84     to->len += len;
    85     /* 非线性数据长度增加 */
    86     to->data_len += len;
    87 
    88     /* 记录增量 */
    89     *delta_truesize = delta;
    90     return true;
    91 }
  • 相关阅读:
    聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类
    windows curl ssl版本号编译
    扩展MSEG 加入Z字段
    HDU 1565 1569 方格取数(最大点权独立集)
    Codeforces Round #277.5 (Div. 2)
    葡萄城公布新版ActiveReports 9报表控件和报表server
    we标签
    ADO.NET (二)—— ADO和ADO .NET对照
    补:小玩文件2--将文本文件里的全部行加上行号后写到新文件里
    poj3061 Subsequence ,尺取法
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11752122.html
Copyright © 2020-2023  润新知