延时定时器 & 重传定时器 (上传之前看的log)

}
/*
 * 延时ACK"定时器在TCP收到必须被确认但无需马上发出
 * 确认的段时设定,TCP在200ms后发送确认响应。如果在
 * 这200ms内,有数据要在该连接上发送,延时ACK响应就
 * 可随数据一起发送回对端,称为捎带确认。
 */
static void tcp_delack_timer(unsigned long data)
{
    struct sock *sk = (struct sock *)data;

    bh_lock_sock(sk);
    if (!sock_owned_by_user(sk)) {
        tcp_delack_timer_handler(sk);
    } else {
        inet_csk(sk)->icsk_ack.blocked = 1;
        __NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED);
        /* deleguate our work to tcp_release_cb() */
        if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags))
            sock_hold(sk);
    }
    bh_unlock_sock(sk);
    sock_put(sk);
}
/* Called with BH disabled */
void tcp_delack_timer_handler(struct sock *sk)
{
    struct tcp_sock *tp = tcp_sk(sk);
    struct inet_connection_sock *icsk = inet_csk(sk);

    /*
     * 回收缓存
     */
    sk_mem_reclaim_partial(sk);
 /*
     * 如果TCP状态为CLOSE,或者没有启动延时发送
     * ACK定时器,则无需作进一步处理。
     */
    if (sk->sk_state == TCP_CLOSE || !(icsk->icsk_ack.pending & ICSK_ACK_TIMER))
        goto out;
 /*
     * 如果超时时间还未到,则重新复位定时器,
     * 然后退出。
     */
    if (time_after(icsk->icsk_ack.timeout, jiffies)) {
        sk_reset_timer(sk, &icsk->icsk_delack_timer, icsk->icsk_ack.timeout);
        goto out;
    }
    
    /*
     * 检测完成后,正式进入延迟确认处理之前,
     * 需去掉ICSK_ACK_TIMER标志。
     */
    icsk->icsk_ack.pending &= ~ICSK_ACK_TIMER;

    /*
     * 如果ucopy控制块中的prequeue队列不为空,则
     * 通过sk_backlog_rcv接口处理sk_backlog_rcv队列中的
     * SKB。TCP中sk_backlog_rcv接口为tcp_v4_do_rcv(),由这个函数添加到sk->sk_receive_queue队列中。
     */
    if (!skb_queue_empty(&tp->ucopy.prequeue)) {
        struct sk_buff *skb;

        __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSCHEDULERFAILED);

        while ((skb = __skb_dequeue(&tp->ucopy.prequeue)) != NULL)
            sk_backlog_rcv(sk, skb);

        tp->ucopy.memory = 0;
    }
  /*
     * 如果此时有ACK需要发送,则调用tcp_send_ack()构造
     * 并发送ACK段,在发送ACK段之前需先离开pingpong
     * 模式,并重新设定延时确认的估算值。
     */
    if (inet_csk_ack_scheduled(sk)) {
        if (!icsk->icsk_ack.pingpong) {
            /* Delayed ACK missed: inflate ATO. */
            icsk->icsk_ack.ato = min(icsk->icsk_ack.ato << 1, icsk->icsk_rto);
        } else {
            /* Delayed ACK missed: leave pingpong mode and
             * deflate ATO.在延迟ACK定时器的超时处理函数中,如果检查到套接口开启了pingpong模式,将执行关闭。
可见在ACK超时之前,本地并没有发送任何数据到对端,表明套接口可能并非交互式应用。
*/

icsk->icsk_ack.pingpong = 0; icsk->icsk_ack.ato = TCP_ATO_MIN; } tcp_send_ack(sk); __NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKS); } out: if (tcp_under_memory_pressure(sk)) sk_mem_reclaim(sk);

 

在TCP三次握手过程中,当客户端套接口接收到服务端回复的SYN+ACK报文后,如果客户端套接口设置了ACK的pingpong模式,表明马上会有数据发送,将延后ACK的回复,等待和数据一起发送。

 

static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
                     const struct tcphdr *th)
{
    struct inet_connection_sock *icsk = inet_csk(sk);
    struct tcp_sock *tp = tcp_sk(sk);
    struct tcp_fastopen_cookie foc = { .len = -1 };
    int saved_clamp = tp->rx_opt.mss_clamp;

    -----------------------------------

        if (sk->sk_write_pending ||
            icsk->icsk_accept_queue.rskq_defer_accept ||
            icsk->icsk_ack.pingpong) {
            /* Save one ACK. Data will be ready after
             * several ticks, if write_pending is set.
             *
             * It may be deleted, but with this feature tcpdumps
             * look so _wonderfully_ clever, that I was not able
             * to stand against the temptation 8)     --ANK
             在TCP三次握手过程中,当客户端套接口接收到服务端回复的SYN+ACK报文后,
             如果客户端套接口设置了ACK的pingpong模式,表明马上会有数据发送,
             将延后ACK的回复,等待和数据一起发送。
             */
            inet_csk_schedule_ack(sk);
            icsk->icsk_ack.lrcvtime = tcp_time_stamp;
            tcp_enter_quickack_mode(sk);
            inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
                          TCP_DELACK_MAX, TCP_RTO_MAX);

discard:
            tcp_drop(sk, skb);
            return 0;
        } else {
            tcp_send_ack(sk);
        }
        return -1;
    }

    /*TCP两端的套接口为交互模式时,数据在两个方向交叉发送,pingpong模式可减少单独ACK报文的发送。没有必要单独发送ack报文 */

 

 

延时确认定时器的激活: tcp的第三次握手可能出现;

比如:tcp_defer_accept 或者不在快速确认模式下; 在准备立即发送ack时,没有内存了,不能构造包、、、延时发送

 重传定时器

 
(1) 定时器的创建

tcp_v4_init_sock

    |-> tcp_init_sock

             |-> tcp_init_xmit_timers

                        |-> inet_csk_init_xmit_timers

在初始化连接时,设置三个定时器实例的处理函数:

icsk->icsk_retransmit_timer的处理函数为tcp_write_timer()

icsk->icsk_delack_timer的处理函数为tcp_delack_timer()

sk->sk_timer的处理函数为tcp_keepalive_timer()

 

 这4个实例分别是:

icsk->icsk_retransmit_timer:超时重传定时器、持续定时器、ER延迟定时器、PTO定时器。

icsk->icsk_delack_timer:ACK延迟定时器。

sk->sk_timer:保活定时器,SYNACK定时器,FIN_WAIT2定时器。

death_row->tw_timer:TIME_WAIT定时器。

(2) 定时器的删除

tcp_done

tcp_disconnect

tcp_v4_destroy_sock

    |-> tcp_clear_xmit_timers

              |-> inet_csk_clear_xmit_timers

 

激活

icsk->icsk_retransmit_timer和icsk->icsk_delack_timer的激活函数为inet_csk_reset_xmit_timer(),

其中,超时重传定时器(ICSK_TIME_RETRANS)在以下几种情况下会被激活:
1. 发现对端把保存在接收缓冲区的SACK段丢弃时。
2. 发送一个数据段时,发现之前网络中不存在发送且未确认的段。
    之后每当收到确认了新数据段的ACK,则重置定时器。
3. 发送SYN包后。
4. 一些特殊情况。

/*
 * 重传定时器在TCP发送数据时设定,如果定时器
 * 已超时而对端确认还未到达,则TCP将重传数据。
 * 重传定时器的超时时间值是动态计算的,取决于
 * TCP为该连接测量的往返时间以及该段已被重传
 * 的次数。
 */ //tcp_write_timer包括数据报重传tcp_retransmit_timer和窗口探测定时器tcp_probe_timer
static void tcp_write_timer(unsigned long data)
{
    struct sock *sk = (struct sock *)data;

    bh_lock_sock(sk);
    if (!sock_owned_by_user(sk)) {
        tcp_write_timer_handler(sk);
    } else {
        /* deleguate our work to tcp_release_cb() *///tcp_release_cb
        /*如果icsk->icsk_retransmit_timer超时时socket被应用进程锁定,
        则设置TCP_WRITE_TIMER_DEFERRED标记,这样在应用进程释放socket时会调用tcp_release_cb函数
        最后 根据tsq_flags调tcp_write_timer_handler
        */
        if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags))
            sock_hold(sk);
    }
    bh_unlock_sock(sk);
    sock_put(sk);
}

 

icsk->icsk_retransmit_timer可同时作为:超时重传定时器、持续定时器、ER延迟定时器、PTO定时器,

所以需要判断是哪种定时器触发的,然后采取相应的处理措施。

/* Called with BH disabled */
void tcp_write_timer_handler(struct sock *sk)
{
    struct inet_connection_sock *icsk = inet_csk(sk);
    int event;
 /*
         * TCP状态为CLOSE或未定义定时器事件,则
         * 无需作处理。
         */
    if (sk->sk_state == TCP_CLOSE || !icsk->icsk_pending)
        goto out;

    if (time_after(icsk->icsk_timeout, jiffies)) {///* 如果定时器还没有超时,那么继续计时 */
        sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout);
        goto out;
    }

    event = icsk->icsk_pending;

    /*
     * 由于重传定时器和持续定时器功能是共用了
     * 一个定时器实现的,因此需根据定时器事件
     * 来区分激活的是哪种定时器;如果event为
     * ICSK_TIME_RETRANS,则调用tcp_retransmit_timer()进行重传
     * 处理;如果为ICSK_TIME_PROBE0,则调用tcp_probe_timer()
     * 进行持续定时器的处理.
     */
    switch (event) {
    case ICSK_TIME_EARLY_RETRANS:
        tcp_resume_early_retransmit(sk);
        break;
    case ICSK_TIME_LOSS_PROBE:
/*
如果拥塞窗口较小且数据的最后一段数据丢失时,快速重传算法会因为无法收到足够数量的ACK而无法及时重传丢失的报文。尾部丢失探测(Tail Loss Probe)定时器就是为了解决这个问题而设计的。
*/
        tcp_send_loss_probe(sk);
        break;
    case ICSK_TIME_RETRANS:
        icsk->icsk_pending = 0;
        tcp_retransmit_timer(sk);/* 超时重传定时器触发的 */

break;
    case ICSK_TIME_PROBE0:
        icsk->icsk_pending = 0;
        tcp_probe_timer(sk);
        break;
    }

out:
    sk_mem_reclaim(sk);

 

 

 

/*
 *    The TCP retransmit timer.
 */
 /*tcp_write_timer包括数据报重传tcp_retransmit_timer和窗口探测定时器tcp_probe_timer
//见tcp_event_new_data_sent,prior_packets为0时才会重启定时器,  
而prior_packets则是发送未确认的段的个数,也就是说  
如果发送了很多段,如果前面的段没有确认,那么后面发送的时候不会重启这个定时器.
//tcp_rearm_rto ///为0说明所有的传输的段都已经acked。此时remove定时器   
。否则重启定时器。  
*/

void tcp_retransmit_timer(struct sock *sk)
{
    struct tcp_sock *tp = tcp_sk(sk);
    struct net *net = sock_net(sk);
    struct inet_connection_sock *icsk = inet_csk(sk);

    if (tp->fastopen_rsk) {
        WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV &&
                 sk->sk_state != TCP_FIN_WAIT1);
        /* fastopen重传syn+ack */
        tcp_fastopen_synack_timer(sk);
        /* Before we receive ACK to our SYN-ACK don't retransmit
         * anything else (e.g., data or FIN segments).
         */
        return;
    } /* 发送队列列出的段都已经得到确认 没有发送且未确认的数据段 */
    if (!tp->packets_out)
        goto out;

    WARN_ON(tcp_write_queue_empty(sk));

    tp->tlp_high_seq = 0;//发生RTO超时表明数据包的确丢失了
 /* 
        对端窗口为0,套接口状态不是DEAD,
        连接不是出于连接过程中的状态
    */
    if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) &&
        !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) {
        /* Receiver dastardly shrinks window. Our retransmits
         * become zero probes, but we should not timeout this
         * connection. If the socket is an orphan, time it out,
         * we cannot allow such beasts to hang infinitely.
         */
        struct inet_sock *inet = inet_sk(sk);
        if (sk->sk_family == AF_INET) {
            net_dbg_ratelimited("Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
                        &inet->inet_daddr,
                        ntohs(inet->inet_dport),
                        inet->inet_num,
                        tp->snd_una, tp->snd_nxt);
        }
#if IS_ENABLED(CONFIG_IPV6)
        else if (sk->sk_family == AF_INET6) {
            net_dbg_ratelimited("Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
                        &sk->sk_v6_daddr,
                        ntohs(inet->inet_dport),
                        inet->inet_num,
                        tp->snd_una, tp->snd_nxt);
        }
#endif
/*
     * 在重传过程中,如果超时重传超时上限TCP_RTO_MAX(120s)还没有接收
     * 到对方的确认,则认为有错误发生,调用tcp_write_err()报告错误并
     * 关闭套接字,然后返回;否则TCP进入拥塞控制的LOSS状态,并重新
     * 传送重传队列中的第一个段。
     */
        if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {
            tcp_write_err(sk);
            goto out;
        }
        tcp_enter_loss(sk);

/* 进入Loss状态,标志丢失的数据段 */

/* 重传发送队列的head 第一个数据段 */


        tcp_retransmit_skb(sk, tcp_write_queue_head(sk), 1);
        /*
         * 由于发生了重传,传输控制块中的路由缓存项需更新,
         * 因此将其清除,最后跳转到out_reset_timer标签处处理。
         */
        __sk_dst_reset(sk);
        goto out_reset_timer;
    }
 //走到下面说明是处于连接建立阶段或者对方的滑动窗口为0了

    /*
     * 当发生重传之后,需要检测当前的资源使用
     * 情况和重传的次数.如果重传次数达到上限,
     * 则需要报告错误并强行关闭套接字.如果只
     * 是使用的资源达到使用的上限,则不进行此
     * 次重传.
     */
    if (tcp_write_timeout(sk))
        goto out;

    /*
     * 如果重传次数为0,说明刚进入重传阶段,则
     * 根据不同的拥塞状态进行相关的数据统计.   第一次重传可能是对方滑动窗口满,需要进行拥塞控制
     */
    if (icsk->icsk_retransmits == 0) {
        int mib_idx;

        if (icsk->icsk_ca_state == TCP_CA_Recovery) {
            if (tcp_is_sack(tp))
                mib_idx = LINUX_MIB_TCPSACKRECOVERYFAIL;
            else
                mib_idx = LINUX_MIB_TCPRENORECOVERYFAIL;
        } else if (icsk->icsk_ca_state == TCP_CA_Loss) {
            mib_idx = LINUX_MIB_TCPLOSSFAILURES;
        } else if ((icsk->icsk_ca_state == TCP_CA_Disorder) ||
               tp->sacked_out) {
            if (tcp_is_sack(tp))
                mib_idx = LINUX_MIB_TCPSACKFAILURES;
            else
                mib_idx = LINUX_MIB_TCPRENOFAILURES;
        } else {
            mib_idx = LINUX_MIB_TCPTIMEOUTS;
        }
        __NET_INC_STATS(sock_net(sk), mib_idx);
    }

    /*老版本内核为
     * 判断是否可使用F-RTO算法进行处理,
     * 如果可以则调用tcp_enter_frto()进行F-RTO
     * 算法的处理,否则调用tcp_enter_loss()进入
     * 常规的RTO慢启动重传恢复阶段.
    *现在最新的版本为 直接进入loss版本
     */
    tcp_enter_loss(sk);
 /*
     * 如果发送重传队列上的第一个SKB失败,则复位
     * 重传定时器,等待下次重传.
     */
    if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk), 1) > 0) {
        /* Retransmission failed because of local congestion,
         * do not backoff.
         */
        if (!icsk->icsk_retransmits)
            icsk->icsk_retransmits = 1;
        inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
                      min(icsk->icsk_rto, TCP_RESOURCE_PROBE_INTERVAL),
                      TCP_RTO_MAX);
        goto out;
    }

    /* Increase the timeout each time we retransmit.  Note that
     * we do not increase the rtt estimate.  rto is initialized
     * from rtt, but increases here.  Jacobson (SIGCOMM 88) suggests
     * that doubling rto each time is the least we can get away with.
     * In KA9Q, Karn uses this for the first few times, and then
     * goes to quadratic.  netBSD doubles, but only goes up to *64,
     * and clamps at 1 to 64 sec afterwards.  Note that 120 sec is
     * defined in the protocol as the maximum possible RTT.  I guess
     * we'll have to use something other than TCP to talk to the
     * University of Mars.
     *
     * PAWS allows us longer timeouts and large windows, so once
     * implemented ftp to mars will work nicely. We will have to fix
     * the 120 second clamps though!
     */
     /*
     * 发送成功后,递增指数退避算法指数icsk_backoff
     * 和累计重传次数icsk_retransmits.
     */
    icsk->icsk_backoff++;
    icsk->icsk_retransmits++;

out_reset_timer:
    /* If stream is thin, use linear timeouts. Since 'icsk_backoff' is
     * used to reset timer, set to 0. Recalculate 'icsk_rto' as this
     * might be increased if the stream oscillates between thin and thick,
     * thus the old value might already be too high compared to the value
     * set by 'tcp_set_rto' in tcp_input.c which resets the rto without
     * backoff. Limit to TCP_THIN_LINEAR_RETRIES before initiating
     * exponential backoff behaviour to avoid continue hammering
     * linear-timeout retransmissions into a black hole
     */
    if (sk->sk_state == TCP_ESTABLISHED &&
        (tp->thin_lto || sysctl_tcp_thin_linear_timeouts) &&
        tcp_stream_is_thin(tp) &&
        icsk->icsk_retransmits <= TCP_THIN_LINEAR_RETRIES) {
        icsk->icsk_backoff = 0;
        icsk->icsk_rto = min(__tcp_set_rto(tp), TCP_RTO_MAX);
    } else {//计算rto,并重启定时器,这里使用karn算法,也就是下次超时时
        /* Use normal (exponential) backoff */
        icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
    }
    
    /*
     * 完成重传后,需要设重传超时时间,然后复位重传
     * 定时器,等待下次重传.
     */
    inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
    if (retransmits_timed_out(sk, net->ipv4.sysctl_tcp_retries1 + 1, 0, 0))
        __sk_dst_reset(sk);

out:
    return;
}

   很明显,重传不能无限的进行下去,当重传的次数超过设定的上限时,就会判定连接超时,关闭该连接。
此后相应的socket函数,比如connect和send,就会返回-1,errno设为ETIMEDOUT,表示连接超时。判定连接是否超时,如果超过了最大等待时间,就放弃此连接。

/* A write timeout has occurred. Process the after effects.很明显,重传不能无限的进行下去,当重传的次数超过设定的上限时,就会判定连接超时,关闭该连接 */
static int tcp_write_timeout(struct sock *sk)
{
    struct inet_connection_sock *icsk = inet_csk(sk);
    struct tcp_sock *tp = tcp_sk(sk);
    struct net *net = sock_net(sk);
    int retry_until;
    bool do_reset, syn_set = false;
/*
     * 在建立连接阶段超时,则需要检测使用的
     * 路由缓存项,并获取重试次数的最大值.
     */
    if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
        if (icsk->icsk_retransmits) {
            dst_negative_advice(sk);
            if (tp->syn_fastopen || tp->syn_data)
                tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
            if (tp->syn_data && icsk->icsk_retransmits == 1)
                NET_INC_STATS(sock_net(sk),
                          LINUX_MIB_TCPFASTOPENACTIVEFAIL);
        }
        retry_until = icsk->icsk_syn_retries ? : net->ipv4.sysctl_tcp_syn_retries;
        syn_set = true;
    } else {
     /*
         * 当重传次数达到sysctl_tcp_retries1时,则需要进行
         * 黑洞检测.完成黑洞检测后还需检测使用的
         * 路由缓存项.
         * 系统启用路径MTU发现时,如果路径MTU发现的
         * 控制数据块中的开关没有开启,则将其开启,
         * 并根据PMTU同步MSS.否则,将当前路径MTU发现
         * 区间左端点的一半作为新区间的左端点重新
         * 设定路径MTU发现区间,并根据路径MTU同步MSS.
         */
                 /* tcp_retries1默认为3,当重传次数超过此值时,表示可能遇到了黑洞,需要进行PMTU
         * 检测,同时更新路由缓存。
         */
        if (retransmits_timed_out(sk, net->ipv4.sysctl_tcp_retries1, 0, 0)) {
            /* Some middle-boxes may black-hole Fast Open _after_
             * the handshake. Therefore we conservatively disable
             * Fast Open on this path on recurring timeouts with
             * few or zero bytes acked after Fast Open.
             */
            if (tp->syn_data_acked &&
                tp->bytes_acked <= tp->rx_opt.mss_clamp) {
                tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
                if (icsk->icsk_retransmits == net->ipv4.sysctl_tcp_retries1)
                    NET_INC_STATS(sock_net(sk),
                              LINUX_MIB_TCPFASTOPENACTIVEFAIL);
            }
            /* Black hole detection */
            tcp_mtu_probing(icsk, sk);

            dst_negative_advice(sk);
        }
  /*
         * 如果当前套接字连接已断开并即将关闭,则
         * 需要对当前使用的资源进行检测.当前的孤
         * 儿套接字数量达到sysctl_tcp_max_orphans或者当前
         * 已使用内存达到硬性限制时,需要即刻关闭
         * 该套接字,这虽然不符合TCP的规范,但为了防
         * 止DoS攻击必须这么处理.
         */
        retry_until = net->ipv4.sysctl_tcp_retries2; /* 在断开TCP连接之前,最多进行多少次重传,默认值为15 */
        if (sock_flag(sk, SOCK_DEAD)) {
            const bool alive = icsk->icsk_rto < TCP_RTO_MAX;

            retry_until = tcp_orphan_retries(sk, alive);
            do_reset = alive ||
                !retransmits_timed_out(sk, retry_until, 0, 0);

            if (tcp_out_of_resources(sk, do_reset))
                return 1;
        }
    }
 /*
     * 当重传次数达到建立连接重传上限、超时
     * 重传上限或确认连接异常期间重试上限这
     * 三种上限之一时,都必须关闭套接字,并
     * 且需要报告相应的错误。
     判断连接是否超时,要分为3种情况。
     1. SYN包:当SYN包的重传次数达到上限时,判定连接超时。(默认允许重传5次,初始超时时间为1s,总共历时31s)
     2. 非SYN包,用户使用TCP_USER_TIMEOUT:当数据包发出去后的等待时间超过用户设置的时间时,判定连接超时。
     3. 非SYN包,用户没有使用TCP_USER_TIMEOUT:当数据包发出去后的等待时间超过以TCP_RTO_MIN为初始超时时间
     ,重传boundary次所花费的时间后,判定连接超时。?如果返回值为真,判定连接超时,则关闭连接,把errno设置为ETIMEDOUT,Socket函数返回-1。
boundary为最大重传次数,timeout为用户设置的超时时间。(通过TCP_USER_TIMEOUT选项设置)
全文地址请点击:https://blog.csdn.net/zhangskd/article/details/35281345?utm_source=copy 
     */
    if (retransmits_timed_out(sk, retry_until,
                  syn_set ? 0 : icsk->icsk_user_timeout, syn_set)) {
        /* Has it gone just too far? */
        tcp_write_err(sk);
        return 1;
    }
    return 0;
}

 

 

/* This function calculates a "timeout" which is equivalent to the timeout of a
 * TCP connection after "boundary" unsuccessful, exponentially backed-off
 * retransmissions with an initial RTO of TCP_RTO_MIN or TCP_TIMEOUT_INIT if
 * syn_set flag is set.
 */
static bool retransmits_timed_out(struct sock *sk,
                  unsigned int boundary,
                  unsigned int timeout,
                  bool syn_set)
{
    unsigned int linear_backoff_thresh, start_ts;
    unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN;

    if (!inet_csk(sk)->icsk_retransmits)
        return false;
    /* start_ts为开始计时点,注意是原始包第一次被发送时的时间戳,不是重传包的 */
    start_ts = tcp_sk(sk)->retrans_stamp;
    if (unlikely(!start_ts))
        start_ts = tcp_skb_timestamp(tcp_write_queue_head(sk));
 
    /* 包括两种情况:
     * SYN包:timeout始终设置为0,因为在三次握手时TCP_USER_TIMEOUT是无效的。
     * 非SYN包:用户没有使用TCP_USER_TIMEOUT选项时。
     */
    if (likely(timeout == 0)) {
        linear_backoff_thresh = ilog2(TCP_RTO_MAX/rto_base);

        if (boundary <= linear_backoff_thresh)
            timeout = ((2 << boundary) - 1) * rto_base;
        else
            timeout = ((2 << linear_backoff_thresh) - 1) * rto_base +
                (boundary - linear_backoff_thresh) * TCP_RTO_MAX;
    }
    return (tcp_time_stamp - start_ts) >= timeout;
}

 

posted @ 2020-05-10 16:56  codestacklinuxer  阅读(497)  评论(0)    收藏  举报