TCP定时器 之 延迟确认定时器

TCP在收到数据段但是无需马上确认时设定,如果在超时时间之内有数据要发送到对端,则确认会随着数据一起发送,即捎带ACK,如果达到超时时间则执行定时器回调立即发送ack;

启动定时器:

延迟确认定时器调用inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, xx, xxx)函数进行启动,启动时间点包含以下三个;

1. 建立连接时,客户端的第三次握手可能会被延迟确认,如果有数据要输出、设置了TCP_DEFER_ACCEPT选项或者不在快速确认模式下,则启动延迟确认定时器;

 1 static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
 2                      const struct tcphdr *th)
 3 {
 4         if (sk->sk_write_pending ||
 5             icsk->icsk_accept_queue.rskq_defer_accept ||
 6             icsk->icsk_ack.pingpong) {
 7             /* Save one ACK. Data will be ready after
 8              * several ticks, if write_pending is set.
 9              *
10              * It may be deleted, but with this feature tcpdumps
11              * look so _wonderfully_ clever, that I was not able
12              * to stand against the temptation 8)     --ANK
13              */
14             inet_csk_schedule_ack(sk);
15             tcp_enter_quickack_mode(sk);
16             inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
17                           TCP_DELACK_MAX, TCP_RTO_MAX);
18 
19 discard:
20             tcp_drop(sk, skb);
21             return 0;
22         } else {
23             tcp_send_ack(sk);
24         }
25 }

 

2. 在立即发送确认时,如果分配内存失败,则启动延迟确认定时器;

 1 /* This routine sends an ack and also updates the window. */
 2 /* TCPTODO */
 3 void tcp_send_ack(struct sock *sk)
 4 {
 5     struct sk_buff *buff;
 6 
 7     /* If we have been reset, we may not send again. */
 8     if (sk->sk_state == TCP_CLOSE)
 9         return;
10 
11     tcp_ca_event(sk, CA_EVENT_NON_DELAYED_ACK);
12 
13     /* We are not putting this on the write queue, so
14      * tcp_transmit_skb() will set the ownership to this
15      * sock.
16      */
17     buff = alloc_skb(MAX_TCP_HEADER,
18              sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN));
19     if (unlikely(!buff)) {
20         inet_csk_schedule_ack(sk);
21         inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN;
22         inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
23                       TCP_DELACK_MAX, TCP_RTO_MAX);
24         return;
25     }
26 
27     /* Reserve space for headers and prepare control bits. */
28     skb_reserve(buff, MAX_TCP_HEADER);
29     tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK);
30 
31     /* We do not want pure acks influencing TCP Small Queues or fq/pacing
32      * too much.
33      * SKB_TRUESIZE(max(1 .. 66, MAX_TCP_HEADER)) is unfortunately ~784
34      */
35     skb_set_tcp_pure_ack(buff);
36 
37     /* Send it off, this clears delayed acks for us. */
38     skb_mstamp_get(&buff->skb_mstamp);
39     tcp_transmit_skb(sk, buff, 0, (__force gfp_t)0);
40 }

 

3. 未启用tcp_low_latency情况下,当有进程正在读取TCP流时,此时prequeue队列存在TCP段且消耗的内存未达到上限,且没有ACK需要发送时,启动延迟确认定时器;

 1 bool tcp_prequeue(struct sock *sk, struct sk_buff *skb)
 2 {
 3     if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
 4         wake_up_interruptible_sync_poll(sk_sleep(sk),
 5                        POLLIN | POLLRDNORM | POLLRDBAND);
 6         if (!inet_csk_ack_scheduled(sk))
 7             inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
 8                           (3 * tcp_rto_min(sk)) / 4,
 9                           TCP_RTO_MAX);
10     }
11     return true;
12 }

 

定时器回调函数:

tcp_delack_timer函数进行必要的状态检查,处理队列中的数据包,如果有ack要发送,则使用即使ack模式立即发送ack;

 1 /**
 2  *  tcp_delack_timer() - The TCP delayed ACK timeout handler
 3  *  @data:  Pointer to the current socket. (gets casted to struct sock *)
 4  *
 5  *  This function gets (indirectly) called when the kernel timer for a TCP packet
 6  *  of this socket expires. Calls tcp_delack_timer_handler() to do the actual work.
 7  *
 8  *  Returns: Nothing (void)
 9  */
10 static void tcp_delack_timer(unsigned long data)
11 {
12     struct sock *sk = (struct sock *)data;
13 
14     bh_lock_sock(sk);
15     /* 传输控制块未被锁定 */
16     if (!sock_owned_by_user(sk)) {
17         /* 调用超时处理函数 */
18         tcp_delack_timer_handler(sk);
19     } else {
20         /* 打阻塞标记告知ack要立即发送*/
21         inet_csk(sk)->icsk_ack.blocked = 1;
22         __NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED);
23         /* deleguate our work to tcp_release_cb() */
24         /* 交给tcp_release_cb处理超时回调 */
25         if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &sk->sk_tsq_flags))
26             sock_hold(sk);
27     }
28     bh_unlock_sock(sk);
29     sock_put(sk);
30 }

 

 1 /* Called with BH disabled */
 2 void tcp_delack_timer_handler(struct sock *sk)
 3 {
 4     struct tcp_sock *tp = tcp_sk(sk);
 5     struct inet_connection_sock *icsk = inet_csk(sk);
 6 
 7     sk_mem_reclaim_partial(sk);
 8 
 9     /* 关闭或者监听状态 || 未启动延迟ack定时器*/
10     if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) ||
11         !(icsk->icsk_ack.pending & ICSK_ACK_TIMER))
12         goto out;
13 
14     /* 尚未达到超时时间,重新设定定时器 */
15     if (time_after(icsk->icsk_ack.timeout, jiffies)) {
16         sk_reset_timer(sk, &icsk->icsk_delack_timer, icsk->icsk_ack.timeout);
17         goto out;
18     }
19 
20     /* 清除延迟ack标记 */
21     icsk->icsk_ack.pending &= ~ICSK_ACK_TIMER;
22 
23     /* prequeue队列不为空 */
24     if (!skb_queue_empty(&tp->ucopy.prequeue)) {
25         struct sk_buff *skb;
26 
27         __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSCHEDULERFAILED);
28 
29         /* 数据包出队,调用接收函数tcp_v4_do_rcv处理包 */
30         while ((skb = __skb_dequeue(&tp->ucopy.prequeue)) != NULL)
31             sk_backlog_rcv(sk, skb);
32 
33         /* 消耗的内存清0 */
34         tp->ucopy.memory = 0;
35     }
36 
37     /* 有ack需要发送 */
38     if (inet_csk_ack_scheduled(sk)) {
39 
40         /*  即时ack模式 */
41         if (!icsk->icsk_ack.pingpong) {
42             /* Delayed ACK missed: inflate ATO. */
43             /* 计算超时时间 */
44             icsk->icsk_ack.ato = min(icsk->icsk_ack.ato << 1, icsk->icsk_rto);
45         } 
46         /* 延迟ack模式 */
47         else {
48             /* Delayed ACK missed: leave pingpong mode and
49              * deflate ATO.
50              */
51             /* 设置为即时ack模式 */
52             icsk->icsk_ack.pingpong = 0;
53 
54             /* 重置超时时间 */
55             icsk->icsk_ack.ato      = TCP_ATO_MIN;
56         }
57 
58         /* 发送ack */
59         tcp_send_ack(sk);
60         __NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKS);
61     }
62 
63 out:
64     if (tcp_under_memory_pressure(sk))
65         sk_mem_reclaim(sk);
66 }

 

posted @ 2019-10-27 22:15  AlexAlex  阅读(896)  评论(0编辑  收藏  举报