• 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 }
  • 相关阅读:
    shell 基础进阶 *金字塔
    shell,awk两种方法写9*9乘法表
    shell脚本判断一个用户是否登录成功
    shell 冒泡算法 解决数组排序问题
    shell 石头剪刀布
    应用shell (ssh)远程链接主机
    nmcli命令使用
    光盘yum源autofs按需挂载
    LVM扩容,删除
    LVM创建
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11752007.html
Copyright © 2020-2023  润新知