TCP输入 之 tcp_prequeue

在未开启tcp_low_latency的情况下,软中断将skb送上来,加入到prequeue中,然后

在未启用tcp_low_latency且有用户进程在读取数据的情况下,skb入队到prequeue,入队之后,若达到队列长度上限或者内存上限,则将队列中的skb出队,调用tcp_v4_do_rcv处理,若入队skb为队列的第一个skb,则需要唤醒进程,通知可读事件,并设置延迟ack定时器;

 1 bool tcp_prequeue(struct sock *sk, struct sk_buff *skb)
 2 {
 3     struct tcp_sock *tp = tcp_sk(sk);
 4 
 5     /* 启用了latency || 没有进程读取数据 */
 6     if (sysctl_tcp_low_latency || !tp->ucopy.task)
 7         return false;
 8 
 9     /* 长度<= tcp头部长度,无数据&& prequeue队列长度为0,空队列 */
10     /* 队列为空,无数据skb不入队,
11        有数据或者之前队列中有skb,则入队,防止乱序?? 
12     */
13     if (skb->len <= tcp_hdrlen(skb) &&
14         skb_queue_len(&tp->ucopy.prequeue) == 0)
15         return false;
16 
17     /* Before escaping RCU protected region, we need to take care of skb
18      * dst. Prequeue is only enabled for established sockets.
19      * For such sockets, we might need the skb dst only to set sk->sk_rx_dst
20      * Instead of doing full sk_rx_dst validity here, let's perform
21      * an optimistic check.
22      */
23     /* 释放skb路由缓存 */
24     if (likely(sk->sk_rx_dst))
25         skb_dst_drop(skb);
26     else
27         skb_dst_force_safe(skb);
28 
29     /* 加入队列尾部 */
30     __skb_queue_tail(&tp->ucopy.prequeue, skb);
31     /* 内存增加 */
32     tp->ucopy.memory += skb->truesize;
33 
34     /* 队列长度>=32 || 内存消耗> rcvbuf */
35     if (skb_queue_len(&tp->ucopy.prequeue) >= 32 ||
36         tp->ucopy.memory + atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf) {
37         struct sk_buff *skb1;
38 
39         BUG_ON(sock_owned_by_user(sk));
40         __NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPPREQUEUEDROPPED,
41                 skb_queue_len(&tp->ucopy.prequeue));
42 
43         /* skb出队,调用tcp_v4_do_rcv处理 */
44         while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL)
45             sk_backlog_rcv(sk, skb1);
46 
47         /* 重置消耗内存 */
48         tp->ucopy.memory = 0;
49     } 
50     /* 空队列新增的第一个skb */
51     else if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
52         /* 唤醒进程可读 */
53         wake_up_interruptible_sync_poll(sk_sleep(sk),
54                        POLLIN | POLLRDNORM | POLLRDBAND);
55         /* 没有ack要发送,则设置延迟ack定时器 */
56         if (!inet_csk_ack_scheduled(sk))
57             inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
58                           (3 * tcp_rto_min(sk)) / 4,
59                           TCP_RTO_MAX);
60     }
61     return true;
62 }

 

上面函数中的tp->ucopy.task是在tcp_recvmsg中设置的,简要截取一部分代码,后续补充该函数的详细分析;

 1 int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
 2         int flags, int *addr_len)
 3 {
 4        do {
 5             if (!sysctl_tcp_low_latency && tp->ucopy.task == user_recv) {
 6             /* Install new reader */
 7             if (!user_recv && !(flags & (MSG_TRUNC | MSG_PEEK))) {
 8                 user_recv = current;
 9                                 /* 设置task */
10                 tp->ucopy.task = user_recv;
11                 tp->ucopy.msg = msg;
12             }
13           } while (len > 0);
14 
15         /*...*/
16 
17     if (user_recv) {
18         if (!skb_queue_empty(&tp->ucopy.prequeue)) {
19             int chunk;
20 
21             tp->ucopy.len = copied > 0 ? len : 0;
22 
23             tcp_prequeue_process(sk);
24 
25             if (copied > 0 && (chunk = len - tp->ucopy.len) != 0) {
26                 NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, chunk);
27                 len -= chunk;
28                 copied += chunk;
29             }
30         }
31                 /* 清除task */
32         tp->ucopy.task = NULL;
33         tp->ucopy.len = 0;
34     }
35 }

 

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