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 }