TCP输入 之 tcp_data_queue

tcp_data_queue作用为数据段的接收处理,其中分为多种情况:

(1) 无数据,释放skb,返回;

(2) 预期接收的数据段,a. 进行0窗口判断;b. 进程上下文,复制数据到用户空间;c. 不满足b或者b未完整拷贝此skb的数据段,则加入到接收队列;d. 更新下一个期望接收的序号;e. 若有fin标记,则处理fin;f. 乱序队列不为空,则处理乱序;g. 快速路径的检查和设置;h. 唤醒用户空间进程读取数据;

(3) 重传的数据段,进入快速ack模式,释放该skb;

(4) 窗口以外的数据段,进入快速ack模式,释放该skb;

(5) 数据段重叠,在进行0窗口判断之后,进行(2)中的加入接收队列,以及>=d的流程;

(6) 乱序的数据段,调用tcp_data_queue_ofo进行乱序数据段的接收处理;

  1 static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
  2 {
  3     struct tcp_sock *tp = tcp_sk(sk);
  4     bool fragstolen = false;
  5     int eaten = -1;
  6 
  7     /* 无数据 */
  8     if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) {
  9         __kfree_skb(skb);
 10         return;
 11     }
 12 
 13     /* 删除路由缓存 */
 14     skb_dst_drop(skb);
 15 
 16     /* 去掉tcp首部 */
 17     __skb_pull(skb, tcp_hdr(skb)->doff * 4);
 18 
 19     tcp_ecn_accept_cwr(tp, skb);
 20 
 21     tp->rx_opt.dsack = 0;
 22 
 23     /*  Queue data for delivery to the user.
 24      *  Packets in sequence go to the receive queue.
 25      *  Out of sequence packets to the out_of_order_queue.
 26      */
 27     /* 预期接收的数据段 */
 28     if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
 29         /* 窗口为0,不能接收数据 */
 30         if (tcp_receive_window(tp) == 0)
 31             goto out_of_window;
 32 
 33         /* Ok. In sequence. In window. */
 34         /* 进程上下文 */
 35 
 36         /* 当前进程读取数据 */
 37         if (tp->ucopy.task == current &&
 38             /* 用户空间读取序号与接收序号一致&& 需要读取的数据不为0 */
 39             tp->copied_seq == tp->rcv_nxt && tp->ucopy.len &&
 40             /* 被用户空间锁定&& 无紧急数据 */
 41             sock_owned_by_user(sk) && !tp->urg_data) {
 42 
 43             /* 带读取长度和数据段长度的较小值 */
 44             int chunk = min_t(unsigned int, skb->len,
 45                       tp->ucopy.len);
 46             /* 设置running状态 */
 47             __set_current_state(TASK_RUNNING);
 48 
 49             /* 拷贝数据 */
 50             if (!skb_copy_datagram_msg(skb, 0, tp->ucopy.msg, chunk)) {
 51                 tp->ucopy.len -= chunk;
 52                 tp->copied_seq += chunk;
 53                 /* 完整读取了该数据段 */
 54                 eaten = (chunk == skb->len);
 55 
 56                 /* 调整接收缓存和窗口 */
 57                 tcp_rcv_space_adjust(sk);
 58             }
 59         }
 60 
 61         /* 未拷贝到用户空间或者未拷贝完整数据段 */
 62         if (eaten <= 0) {
 63 queue_and_out:
 64             /* 没有拷贝到用户空间,对内存进行检查 */
 65             if (eaten < 0) {
 66                 if (skb_queue_len(&sk->sk_receive_queue) == 0)
 67                     sk_forced_mem_schedule(sk, skb->truesize);
 68                 else if (tcp_try_rmem_schedule(sk, skb, skb->truesize))
 69                     goto drop;
 70             }
 71 
 72             /* 添加到接收队列 */
 73             eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen);
 74         }
 75 
 76         /* 更新下一个期望接收的序号*/
 77         tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq);
 78         /* 有数据 */
 79         if (skb->len)
 80             tcp_event_data_recv(sk, skb);
 81 
 82         /* 标记有fin,则处理 */
 83         if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
 84             tcp_fin(sk);
 85 
 86         /* 乱序队列有数据,则处理 */
 87         if (!RB_EMPTY_ROOT(&tp->out_of_order_queue)) {
 88 
 89             /* 将乱序队列中的数据段转移到接收队列 */
 90             tcp_ofo_queue(sk);
 91 
 92             /* RFC2581. 4.2. SHOULD send immediate ACK, when
 93              * gap in queue is filled.
 94              */
 95             /* 乱序数据段处理完毕,需要立即发送ack */
 96             if (RB_EMPTY_ROOT(&tp->out_of_order_queue))
 97                 inet_csk(sk)->icsk_ack.pingpong = 0;
 98         }
 99 
100         if (tp->rx_opt.num_sacks)
101             tcp_sack_remove(tp);
102 
103         /* 快路检查 */
104         tcp_fast_path_check(sk);
105 
106         /* 向用户空间拷贝了数据,则释放skb */
107         if (eaten > 0)
108             kfree_skb_partial(skb, fragstolen);
109 
110         /* 不在销毁状态,则唤醒进程读取数据 */
111         if (!sock_flag(sk, SOCK_DEAD))
112             sk->sk_data_ready(sk);
113         return;
114     }
115 
116     /* 重传 */
117     if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
118         /* A retransmit, 2nd most common case.  Force an immediate ack. */
119         NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOST);
120         tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
121 
122 out_of_window:
123         /* 进入快速ack模式 */
124         tcp_enter_quickack_mode(sk);
125 
126         /*  调度ack */
127         inet_csk_schedule_ack(sk);
128 drop:
129         /* 释放skb */
130         tcp_drop(sk, skb);
131         return;
132     }
133 
134     /* Out of window. F.e. zero window probe. */
135     /* 窗口以外的数据,比如零窗口探测报文段 */
136     if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt + tcp_receive_window(tp)))
137         goto out_of_window;
138 
139     /* 进入快速ack模式 */
140     tcp_enter_quickack_mode(sk);
141 
142     /* 数据段重叠 */
143     if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
144         /* Partial packet, seq < rcv_next < end_seq */
145         SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X\n",
146                tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
147                TCP_SKB_CB(skb)->end_seq);
148 
149         tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, tp->rcv_nxt);
150 
151         /* If window is closed, drop tail of packet. But after
152          * remembering D-SACK for its head made in previous line.
153          */
154         /* 窗口为0,不能接收 */
155         if (!tcp_receive_window(tp))
156             goto out_of_window;
157         goto queue_and_out;
158     }
159 
160     /* 接收乱序数据段 */
161     tcp_data_queue_ofo(sk, skb);
162 }

 

posted @ 2019-10-28 14:39  AlexAlex  阅读(1803)  评论(0编辑  收藏  举报