在未开启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 }