]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/ipv4/tcp_input.c
[TCP]: Left out sync->verify (the new meaning of it) & definify
[linux-2.6-omap-h63xx.git] / net / ipv4 / tcp_input.c
index fec8a7a4dbaffa3781685fe9203b4c998482dc8d..b11bd162422725777e9116e9288e25f15fadb538 100644 (file)
@@ -102,11 +102,14 @@ int sysctl_tcp_abc __read_mostly;
 #define FLAG_DATA_LOST         0x80 /* SACK detected data lossage.             */
 #define FLAG_SLOWPATH          0x100 /* Do not skip RFC checks for window update.*/
 #define FLAG_ONLY_ORIG_SACKED  0x200 /* SACKs only non-rexmit sent before RTO */
+#define FLAG_SND_UNA_ADVANCED  0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */
+#define FLAG_DSACKING_ACK      0x800 /* SACK blocks contained DSACK info */
 
 #define FLAG_ACKED             (FLAG_DATA_ACKED|FLAG_SYN_ACKED)
 #define FLAG_NOT_DUP           (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED)
 #define FLAG_CA_ALERT          (FLAG_DATA_SACKED|FLAG_ECE)
 #define FLAG_FORWARD_PROGRESS  (FLAG_ACKED|FLAG_DATA_SACKED)
+#define FLAG_ANY_PROGRESS      (FLAG_FORWARD_PROGRESS|FLAG_SND_UNA_ADVANCED)
 
 #define IsReno(tp) ((tp)->rx_opt.sack_ok == 0)
 #define IsFack(tp) ((tp)->rx_opt.sack_ok & 2)
@@ -115,6 +118,7 @@ int sysctl_tcp_abc __read_mostly;
 #define IsSackFrto() (sysctl_tcp_frto == 0x2)
 
 #define TCP_REMNANT (TCP_FLAG_FIN|TCP_FLAG_URG|TCP_FLAG_SYN|TCP_FLAG_PSH)
+#define TCP_HP_BITS (~(TCP_RESERVED_BITS|TCP_FLAG_PSH))
 
 /* Adapt the MSS value used to make delayed ack decision to the
  * real world.
@@ -195,6 +199,55 @@ static inline int tcp_in_quickack_mode(const struct sock *sk)
        return icsk->icsk_ack.quick && !icsk->icsk_ack.pingpong;
 }
 
+static inline void TCP_ECN_queue_cwr(struct tcp_sock *tp)
+{
+       if (tp->ecn_flags&TCP_ECN_OK)
+               tp->ecn_flags |= TCP_ECN_QUEUE_CWR;
+}
+
+static inline void TCP_ECN_accept_cwr(struct tcp_sock *tp, struct sk_buff *skb)
+{
+       if (tcp_hdr(skb)->cwr)
+               tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR;
+}
+
+static inline void TCP_ECN_withdraw_cwr(struct tcp_sock *tp)
+{
+       tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR;
+}
+
+static inline void TCP_ECN_check_ce(struct tcp_sock *tp, struct sk_buff *skb)
+{
+       if (tp->ecn_flags&TCP_ECN_OK) {
+               if (INET_ECN_is_ce(TCP_SKB_CB(skb)->flags))
+                       tp->ecn_flags |= TCP_ECN_DEMAND_CWR;
+               /* Funny extension: if ECT is not set on a segment,
+                * it is surely retransmit. It is not in ECN RFC,
+                * but Linux follows this rule. */
+               else if (INET_ECN_is_not_ect((TCP_SKB_CB(skb)->flags)))
+                       tcp_enter_quickack_mode((struct sock *)tp);
+       }
+}
+
+static inline void TCP_ECN_rcv_synack(struct tcp_sock *tp, struct tcphdr *th)
+{
+       if ((tp->ecn_flags&TCP_ECN_OK) && (!th->ece || th->cwr))
+               tp->ecn_flags &= ~TCP_ECN_OK;
+}
+
+static inline void TCP_ECN_rcv_syn(struct tcp_sock *tp, struct tcphdr *th)
+{
+       if ((tp->ecn_flags&TCP_ECN_OK) && (!th->ece || !th->cwr))
+               tp->ecn_flags &= ~TCP_ECN_OK;
+}
+
+static inline int TCP_ECN_rcv_ecn_echo(struct tcp_sock *tp, struct tcphdr *th)
+{
+       if (th->ece && !th->syn && (tp->ecn_flags&TCP_ECN_OK))
+               return 1;
+       return 0;
+}
+
 /* Buffer size and advertised window tuning.
  *
  * 1. Tuning sk->sk_sndbuf, when connection enters established state.
@@ -552,6 +605,16 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)
                tcp_grow_window(sk, skb);
 }
 
+static u32 tcp_rto_min(struct sock *sk)
+{
+       struct dst_entry *dst = __sk_dst_get(sk);
+       u32 rto_min = TCP_RTO_MIN;
+
+       if (dst && dst_metric_locked(dst, RTAX_RTO_MIN))
+               rto_min = dst->metrics[RTAX_RTO_MIN-1];
+       return rto_min;
+}
+
 /* Called to compute a smoothed rtt estimate. The data fed to this
  * routine either comes from timestamps, or from segments that were
  * known _not_ to have been retransmitted [see Karn/Partridge
@@ -613,13 +676,13 @@ static void tcp_rtt_estimator(struct sock *sk, const __u32 mrtt)
                        if (tp->mdev_max < tp->rttvar)
                                tp->rttvar -= (tp->rttvar-tp->mdev_max)>>2;
                        tp->rtt_seq = tp->snd_nxt;
-                       tp->mdev_max = TCP_RTO_MIN;
+                       tp->mdev_max = tcp_rto_min(sk);
                }
        } else {
                /* no previous measure. */
                tp->srtt = m<<3;        /* take the measured time to be rtt */
                tp->mdev = m<<1;        /* make sure rto = 3*rtt */
-               tp->mdev_max = tp->rttvar = max(tp->mdev, TCP_RTO_MIN);
+               tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk));
                tp->rtt_seq = tp->snd_nxt;
        }
 }
@@ -752,7 +815,15 @@ void tcp_update_metrics(struct sock *sk)
        }
 }
 
-/* Numbers are taken from RFC2414.  */
+/* Numbers are taken from RFC3390.
+ *
+ * John Heffner states:
+ *
+ *     The RFC specifies a window of no more than 4380 bytes
+ *     unless 2*MSS > 4380.  Reading the pseudocode in the RFC
+ *     is a bit misleading because they use a clamp at 4380 bytes
+ *     rather than use a multiplier in the relevant range.
+ */
 __u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst)
 {
        __u32 cwnd = (dst ? dst_metric(dst, RTAX_INITCWND) : 0);
@@ -939,6 +1010,39 @@ static void tcp_update_reordering(struct sock *sk, const int metric,
  * Both of these heuristics are not used in Loss state, when we cannot
  * account for retransmits accurately.
  */
+static int tcp_check_dsack(struct tcp_sock *tp, struct sk_buff *ack_skb,
+                          struct tcp_sack_block_wire *sp, int num_sacks,
+                          u32 prior_snd_una)
+{
+       u32 start_seq_0 = ntohl(get_unaligned(&sp[0].start_seq));
+       u32 end_seq_0 = ntohl(get_unaligned(&sp[0].end_seq));
+       int dup_sack = 0;
+
+       if (before(start_seq_0, TCP_SKB_CB(ack_skb)->ack_seq)) {
+               dup_sack = 1;
+               tp->rx_opt.sack_ok |= 4;
+               NET_INC_STATS_BH(LINUX_MIB_TCPDSACKRECV);
+       } else if (num_sacks > 1) {
+               u32 end_seq_1 = ntohl(get_unaligned(&sp[1].end_seq));
+               u32 start_seq_1 = ntohl(get_unaligned(&sp[1].start_seq));
+
+               if (!after(end_seq_0, end_seq_1) &&
+                   !before(start_seq_0, start_seq_1)) {
+                       dup_sack = 1;
+                       tp->rx_opt.sack_ok |= 4;
+                       NET_INC_STATS_BH(LINUX_MIB_TCPDSACKOFORECV);
+               }
+       }
+
+       /* D-SACK for already forgotten data... Do dumb counting. */
+       if (dup_sack &&
+           !after(end_seq_0, prior_snd_una) &&
+           after(end_seq_0, tp->undo_marker))
+               tp->undo_retrans--;
+
+       return dup_sack;
+}
+
 static int
 tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_una)
 {
@@ -958,29 +1062,16 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
        int i;
        int first_sack_index;
 
-       if (!tp->sacked_out)
+       if (!tp->sacked_out) {
                tp->fackets_out = 0;
-       prior_fackets = tp->fackets_out;
-
-       /* Check for D-SACK. */
-       if (before(ntohl(sp[0].start_seq), TCP_SKB_CB(ack_skb)->ack_seq)) {
-               found_dup_sack = 1;
-               tp->rx_opt.sack_ok |= 4;
-               NET_INC_STATS_BH(LINUX_MIB_TCPDSACKRECV);
-       } else if (num_sacks > 1 &&
-                       !after(ntohl(sp[0].end_seq), ntohl(sp[1].end_seq)) &&
-                       !before(ntohl(sp[0].start_seq), ntohl(sp[1].start_seq))) {
-               found_dup_sack = 1;
-               tp->rx_opt.sack_ok |= 4;
-               NET_INC_STATS_BH(LINUX_MIB_TCPDSACKOFORECV);
+               tp->highest_sack = tp->snd_una;
        }
+       prior_fackets = tp->fackets_out;
 
-       /* D-SACK for already forgotten data...
-        * Do dumb counting. */
-       if (found_dup_sack &&
-                       !after(ntohl(sp[0].end_seq), prior_snd_una) &&
-                       after(ntohl(sp[0].end_seq), tp->undo_marker))
-               tp->undo_retrans--;
+       found_dup_sack = tcp_check_dsack(tp, ack_skb, sp,
+                                        num_sacks, prior_snd_una);
+       if (found_dup_sack)
+               flag |= FLAG_DSACKING_ACK;
 
        /* Eliminate too old ACKs, but take into
         * account more or less fresh ones, they can
@@ -1194,6 +1285,10 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
 
                                if (fack_count > tp->fackets_out)
                                        tp->fackets_out = fack_count;
+
+                               if (after(TCP_SKB_CB(skb)->seq,
+                                   tp->highest_sack))
+                                       tp->highest_sack = TCP_SKB_CB(skb)->seq;
                        } else {
                                if (dup_sack && (sacked&TCPCB_RETRANS))
                                        reord = min(fack_count, reord);
@@ -1251,8 +1346,6 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
                }
        }
 
-       tp->left_out = tp->sacked_out + tp->lost_out;
-
        if ((reord < tp->fackets_out) && icsk->icsk_ca_state != TCP_CA_Loss &&
            (!tp->frto_highmark || after(tp->snd_una, tp->frto_highmark)))
                tcp_update_reordering(sk, ((tp->fackets_out + 1) - reord), 0);
@@ -1269,6 +1362,52 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
 /* F-RTO can only be used if TCP has never retransmitted anything other than
  * head (SACK enhanced variant from Appendix B of RFC4138 is more robust here)
  */
+static void tcp_check_reno_reordering(struct sock *sk, const int addend)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       u32 holes;
+
+       holes = max(tp->lost_out, 1U);
+       holes = min(holes, tp->packets_out);
+
+       if ((tp->sacked_out + holes) > tp->packets_out) {
+               tp->sacked_out = tp->packets_out - holes;
+               tcp_update_reordering(sk, tp->packets_out + addend, 0);
+       }
+}
+
+/* Emulate SACKs for SACKless connection: account for a new dupack. */
+
+static void tcp_add_reno_sack(struct sock *sk)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       tp->sacked_out++;
+       tcp_check_reno_reordering(sk, 0);
+       tcp_verify_left_out(tp);
+}
+
+/* Account for ACK, ACKing some data in Reno Recovery phase. */
+
+static void tcp_remove_reno_sacks(struct sock *sk, int acked)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if (acked > 0) {
+               /* One ACK acked hole. The rest eat duplicate ACKs. */
+               if (acked-1 >= tp->sacked_out)
+                       tp->sacked_out = 0;
+               else
+                       tp->sacked_out -= acked-1;
+       }
+       tcp_check_reno_reordering(sk, acked);
+       tcp_verify_left_out(tp);
+}
+
+static inline void tcp_reset_reno_sack(struct tcp_sock *tp)
+{
+       tp->sacked_out = 0;
+}
+
 int tcp_use_frto(struct sock *sk)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
@@ -1357,7 +1496,7 @@ void tcp_enter_frto(struct sock *sk)
                TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
                tp->retrans_out -= tcp_skb_pcount(skb);
        }
-       tcp_sync_left_out(tp);
+       tcp_verify_left_out(tp);
 
        /* Earlier loss recovery underway (see RFC4138; Appendix B).
         * The last condition is necessary at least in tp->frto_counter case.
@@ -1382,17 +1521,15 @@ static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *skb;
-       int cnt = 0;
 
-       tp->sacked_out = 0;
        tp->lost_out = 0;
-       tp->fackets_out = 0;
        tp->retrans_out = 0;
+       if (IsReno(tp))
+               tcp_reset_reno_sack(tp);
 
        tcp_for_write_queue(skb, sk) {
                if (skb == tcp_send_head(sk))
                        break;
-               cnt += tcp_skb_pcount(skb);
                /*
                 * Count the retransmission made on RTO correctly (only when
                 * waiting for the first ACK and did not get it)...
@@ -1406,22 +1543,15 @@ static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag)
                } else {
                        TCP_SKB_CB(skb)->sacked &= ~(TCPCB_LOST|TCPCB_SACKED_RETRANS);
                }
-               if (!(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED)) {
 
-                       /* Do not mark those segments lost that were
-                        * forward transmitted after RTO
-                        */
-                       if (!after(TCP_SKB_CB(skb)->end_seq,
-                                  tp->frto_highmark)) {
-                               TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
-                               tp->lost_out += tcp_skb_pcount(skb);
-                       }
-               } else {
-                       tp->sacked_out += tcp_skb_pcount(skb);
-                       tp->fackets_out = cnt;
+               /* Don't lost mark skbs that were fwd transmitted after RTO */
+               if (!(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED) &&
+                   !after(TCP_SKB_CB(skb)->end_seq, tp->frto_highmark)) {
+                       TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
+                       tp->lost_out += tcp_skb_pcount(skb);
                }
        }
-       tcp_sync_left_out(tp);
+       tcp_verify_left_out(tp);
 
        tp->snd_cwnd = tcp_packets_in_flight(tp) + allowed_segments;
        tp->snd_cwnd_cnt = 0;
@@ -1440,7 +1570,6 @@ static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag)
 
 void tcp_clear_retrans(struct tcp_sock *tp)
 {
-       tp->left_out = 0;
        tp->retrans_out = 0;
 
        tp->fackets_out = 0;
@@ -1497,7 +1626,7 @@ void tcp_enter_loss(struct sock *sk, int how)
                        tp->fackets_out = cnt;
                }
        }
-       tcp_sync_left_out(tp);
+       tcp_verify_left_out(tp);
 
        tp->reordering = min_t(unsigned int, tp->reordering,
                                             sysctl_tcp_reordering);
@@ -1685,55 +1814,18 @@ static int tcp_time_to_recover(struct sock *sk)
        return 0;
 }
 
-/* If we receive more dupacks than we expected counting segments
- * in assumption of absent reordering, interpret this as reordering.
- * The only another reason could be bug in receiver TCP.
+/* RFC: This is from the original, I doubt that this is necessary at all:
+ * clear xmit_retrans hint if seq of this skb is beyond hint. How could we
+ * retransmitted past LOST markings in the first place? I'm not fully sure
+ * about undo and end of connection cases, which can cause R without L?
  */
-static void tcp_check_reno_reordering(struct sock *sk, const int addend)
+static void tcp_verify_retransmit_hint(struct tcp_sock *tp,
+                                      struct sk_buff *skb)
 {
-       struct tcp_sock *tp = tcp_sk(sk);
-       u32 holes;
-
-       holes = max(tp->lost_out, 1U);
-       holes = min(holes, tp->packets_out);
-
-       if ((tp->sacked_out + holes) > tp->packets_out) {
-               tp->sacked_out = tp->packets_out - holes;
-               tcp_update_reordering(sk, tp->packets_out + addend, 0);
-       }
-}
-
-/* Emulate SACKs for SACKless connection: account for a new dupack. */
-
-static void tcp_add_reno_sack(struct sock *sk)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-       tp->sacked_out++;
-       tcp_check_reno_reordering(sk, 0);
-       tcp_sync_left_out(tp);
-}
-
-/* Account for ACK, ACKing some data in Reno Recovery phase. */
-
-static void tcp_remove_reno_sacks(struct sock *sk, int acked)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-
-       if (acked > 0) {
-               /* One ACK acked hole. The rest eat duplicate ACKs. */
-               if (acked-1 >= tp->sacked_out)
-                       tp->sacked_out = 0;
-               else
-                       tp->sacked_out -= acked-1;
-       }
-       tcp_check_reno_reordering(sk, acked);
-       tcp_sync_left_out(tp);
-}
-
-static inline void tcp_reset_reno_sack(struct tcp_sock *tp)
-{
-       tp->sacked_out = 0;
-       tp->left_out = tp->lost_out;
+       if ((tp->retransmit_skb_hint != NULL) &&
+           before(TCP_SKB_CB(skb)->seq,
+           TCP_SKB_CB(tp->retransmit_skb_hint)->seq))
+               tp->retransmit_skb_hint = NULL;
 }
 
 /* Mark head of queue up as lost. */
@@ -1766,17 +1858,10 @@ static void tcp_mark_head_lost(struct sock *sk,
                if (!(TCP_SKB_CB(skb)->sacked&TCPCB_TAGBITS)) {
                        TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
                        tp->lost_out += tcp_skb_pcount(skb);
-
-                       /* clear xmit_retransmit_queue hints
-                        *  if this is beyond hint */
-                       if (tp->retransmit_skb_hint != NULL &&
-                           before(TCP_SKB_CB(skb)->seq,
-                                  TCP_SKB_CB(tp->retransmit_skb_hint)->seq))
-                               tp->retransmit_skb_hint = NULL;
-
+                       tcp_verify_retransmit_hint(tp, skb);
                }
        }
-       tcp_sync_left_out(tp);
+       tcp_verify_left_out(tp);
 }
 
 /* Account newly detected lost packet(s) */
@@ -1814,19 +1899,13 @@ static void tcp_update_scoreboard(struct sock *sk)
                        if (!(TCP_SKB_CB(skb)->sacked&TCPCB_TAGBITS)) {
                                TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
                                tp->lost_out += tcp_skb_pcount(skb);
-
-                               /* clear xmit_retrans hint */
-                               if (tp->retransmit_skb_hint &&
-                                   before(TCP_SKB_CB(skb)->seq,
-                                          TCP_SKB_CB(tp->retransmit_skb_hint)->seq))
-
-                                       tp->retransmit_skb_hint = NULL;
+                               tcp_verify_retransmit_hint(tp, skb);
                        }
                }
 
                tp->scoreboard_skb_hint = skb;
 
-               tcp_sync_left_out(tp);
+               tcp_verify_left_out(tp);
        }
 }
 
@@ -1851,19 +1930,22 @@ static inline u32 tcp_cwnd_min(const struct sock *sk)
 }
 
 /* Decrease cwnd each second ack. */
-static void tcp_cwnd_down(struct sock *sk)
+static void tcp_cwnd_down(struct sock *sk, int flag)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        int decr = tp->snd_cwnd_cnt + 1;
 
-       tp->snd_cwnd_cnt = decr&1;
-       decr >>= 1;
+       if ((flag&(FLAG_ANY_PROGRESS|FLAG_DSACKING_ACK)) ||
+           (IsReno(tp) && !(flag&FLAG_NOT_DUP))) {
+               tp->snd_cwnd_cnt = decr&1;
+               decr >>= 1;
 
-       if (decr && tp->snd_cwnd > tcp_cwnd_min(sk))
-               tp->snd_cwnd -= decr;
+               if (decr && tp->snd_cwnd > tcp_cwnd_min(sk))
+                       tp->snd_cwnd -= decr;
 
-       tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp)+1);
-       tp->snd_cwnd_stamp = tcp_time_stamp;
+               tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp)+1);
+               tp->snd_cwnd_stamp = tcp_time_stamp;
+       }
 }
 
 /* Nothing was retransmitted or returned timestamp is less
@@ -1887,7 +1969,7 @@ static void DBGUNDO(struct sock *sk, const char *msg)
        printk(KERN_DEBUG "Undo %s %u.%u.%u.%u/%u c%u l%u ss%u/%u p%u\n",
               msg,
               NIPQUAD(inet->daddr), ntohs(inet->dport),
-              tp->snd_cwnd, tp->left_out,
+              tp->snd_cwnd, tcp_left_out(tp),
               tp->snd_ssthresh, tp->prior_ssthresh,
               tp->packets_out);
 }
@@ -2016,7 +2098,6 @@ static int tcp_try_undo_loss(struct sock *sk)
 
                DBGUNDO(sk, "partial loss");
                tp->lost_out = 0;
-               tp->left_out = tp->sacked_out;
                tcp_undo_cwr(sk, 1);
                NET_INC_STATS_BH(LINUX_MIB_TCPLOSSUNDO);
                inet_csk(sk)->icsk_retransmits = 0;
@@ -2040,8 +2121,6 @@ static void tcp_try_to_open(struct sock *sk, int flag)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       tcp_sync_left_out(tp);
-
        if (tp->retrans_out == 0)
                tp->retrans_stamp = 0;
 
@@ -2051,7 +2130,7 @@ static void tcp_try_to_open(struct sock *sk, int flag)
        if (inet_csk(sk)->icsk_ca_state != TCP_CA_CWR) {
                int state = TCP_CA_Open;
 
-               if (tp->left_out || tp->retrans_out || tp->undo_marker)
+               if (tp->sacked_out || tp->retrans_out || tp->undo_marker)
                        state = TCP_CA_Disorder;
 
                if (inet_csk(sk)->icsk_ca_state != state) {
@@ -2060,7 +2139,7 @@ static void tcp_try_to_open(struct sock *sk, int flag)
                }
                tcp_moderate_cwnd(tp);
        } else {
-               tcp_cwnd_down(sk);
+               tcp_cwnd_down(sk, flag);
        }
 }
 
@@ -2104,12 +2183,13 @@ static void tcp_mtup_probe_success(struct sock *sk, struct sk_buff *skb)
  * tcp_xmit_retransmit_queue().
  */
 static void
-tcp_fastretrans_alert(struct sock *sk, u32 prior_snd_una,
-                     int prior_packets, int flag)
+tcp_fastretrans_alert(struct sock *sk, int prior_packets, int flag)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
-       int is_dupack = (tp->snd_una == prior_snd_una && !(flag&FLAG_NOT_DUP));
+       int is_dupack = !(flag&(FLAG_SND_UNA_ADVANCED|FLAG_NOT_DUP));
+       int do_lost = is_dupack || ((flag&FLAG_DATA_SACKED) &&
+                                   (tp->fackets_out > tp->reordering));
 
        /* Some technical things:
         * 1. Reno does not count dupacks (sacked_out) automatically. */
@@ -2137,8 +2217,8 @@ tcp_fastretrans_alert(struct sock *sk, u32 prior_snd_una,
                NET_INC_STATS_BH(LINUX_MIB_TCPLOSS);
        }
 
-       /* D. Synchronize left_out to current state. */
-       tcp_sync_left_out(tp);
+       /* D. Check consistency of the current state. */
+       tcp_verify_left_out(tp);
 
        /* E. Check state exit conditions. State can be terminated
         *    when high_seq is ACKed. */
@@ -2186,14 +2266,14 @@ tcp_fastretrans_alert(struct sock *sk, u32 prior_snd_una,
        /* F. Process state. */
        switch (icsk->icsk_ca_state) {
        case TCP_CA_Recovery:
-               if (prior_snd_una == tp->snd_una) {
+               if (!(flag & FLAG_SND_UNA_ADVANCED)) {
                        if (IsReno(tp) && is_dupack)
                                tcp_add_reno_sack(sk);
                } else {
                        int acked = prior_packets - tp->packets_out;
                        if (IsReno(tp))
                                tcp_remove_reno_sacks(sk, acked);
-                       is_dupack = tcp_try_undo_partial(sk, acked);
+                       do_lost = tcp_try_undo_partial(sk, acked);
                }
                break;
        case TCP_CA_Loss:
@@ -2209,7 +2289,7 @@ tcp_fastretrans_alert(struct sock *sk, u32 prior_snd_una,
                /* Loss is undone; fall through to processing in Open state. */
        default:
                if (IsReno(tp)) {
-                       if (tp->snd_una != prior_snd_una)
+                       if (flag & FLAG_SND_UNA_ADVANCED)
                                tcp_reset_reno_sack(tp);
                        if (is_dupack)
                                tcp_add_reno_sack(sk);
@@ -2258,9 +2338,9 @@ tcp_fastretrans_alert(struct sock *sk, u32 prior_snd_una,
                tcp_set_ca_state(sk, TCP_CA_Recovery);
        }
 
-       if (is_dupack || tcp_head_timedout(sk))
+       if (do_lost || tcp_head_timedout(sk))
                tcp_update_scoreboard(sk);
-       tcp_cwnd_down(sk);
+       tcp_cwnd_down(sk, flag);
        tcp_xmit_retransmit_queue(sk);
 }
 
@@ -2393,6 +2473,9 @@ static int tcp_tso_acked(struct sock *sk, struct sk_buff *skb,
                        __u32 dval = min(tp->fackets_out, packets_acked);
                        tp->fackets_out -= dval;
                }
+               /* hint's skb might be NULL but we don't need to care */
+               tp->fastpath_cnt_hint -= min_t(u32, packets_acked,
+                                              tp->fastpath_cnt_hint);
                tp->packets_out -= packets_acked;
 
                BUG_ON(tcp_skb_pcount(skb) == 0);
@@ -2490,12 +2573,23 @@ static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p)
                tcp_ack_update_rtt(sk, acked, seq_rtt);
                tcp_ack_packets_out(sk);
 
-               /* Is the ACK triggering packet unambiguous? */
-               if (acked & FLAG_RETRANS_DATA_ACKED)
-                       last_ackt = net_invalid_timestamp();
+               if (ca_ops->pkts_acked) {
+                       s32 rtt_us = -1;
+
+                       /* Is the ACK triggering packet unambiguous? */
+                       if (!(acked & FLAG_RETRANS_DATA_ACKED)) {
+                               /* High resolution needed and available? */
+                               if (ca_ops->flags & TCP_CONG_RTT_STAMP &&
+                                   !ktime_equal(last_ackt,
+                                                net_invalid_timestamp()))
+                                       rtt_us = ktime_us_delta(ktime_get_real(),
+                                                               last_ackt);
+                               else if (seq_rtt > 0)
+                                       rtt_us = jiffies_to_usecs(seq_rtt);
+                       }
 
-               if (ca_ops->pkts_acked)
-                       ca_ops->pkts_acked(sk, pkts_acked, last_ackt);
+                       ca_ops->pkts_acked(sk, pkts_acked, rtt_us);
+               }
        }
 
 #if FASTRETRANS_DEBUG > 0
@@ -2667,11 +2761,11 @@ static void tcp_undo_spur_to_response(struct sock *sk, int flag)
  *     to prove that the RTO is indeed spurious. It transfers the control
  *     from F-RTO to the conventional RTO recovery
  */
-static int tcp_process_frto(struct sock *sk, u32 prior_snd_una, int flag)
+static int tcp_process_frto(struct sock *sk, int flag)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       tcp_sync_left_out(tp);
+       tcp_verify_left_out(tp);
 
        /* Duplicate the behavior from Loss state (fastretrans_alert) */
        if (flag&FLAG_DATA_ACKED)
@@ -2687,8 +2781,7 @@ static int tcp_process_frto(struct sock *sk, u32 prior_snd_una, int flag)
                 * ACK isn't duplicate nor advances window, e.g., opposite dir
                 * data, winupdate
                 */
-               if ((tp->snd_una == prior_snd_una) && (flag&FLAG_NOT_DUP) &&
-                   !(flag&FLAG_FORWARD_PROGRESS))
+               if (!(flag&FLAG_ANY_PROGRESS) && (flag&FLAG_NOT_DUP))
                        return 1;
 
                if (!(flag&FLAG_DATA_ACKED)) {
@@ -2768,6 +2861,9 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
        if (before(ack, prior_snd_una))
                goto old_ack;
 
+       if (after(ack, prior_snd_una))
+               flag |= FLAG_SND_UNA_ADVANCED;
+
        if (sysctl_tcp_abc) {
                if (icsk->icsk_ca_state < TCP_CA_CWR)
                        tp->bytes_acked += ack - prior_snd_una;
@@ -2820,14 +2916,14 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
        flag |= tcp_clean_rtx_queue(sk, &seq_rtt);
 
        if (tp->frto_counter)
-               frto_cwnd = tcp_process_frto(sk, prior_snd_una, flag);
+               frto_cwnd = tcp_process_frto(sk, flag);
 
        if (tcp_ack_is_dubious(sk, flag)) {
                /* Advance CWND, if state allows this. */
                if ((flag & FLAG_DATA_ACKED) && !frto_cwnd &&
                    tcp_may_raise_cwnd(sk, flag))
                        tcp_cong_avoid(sk, ack, prior_in_flight, 0);
-               tcp_fastretrans_alert(sk, prior_snd_una, prior_packets, flag);
+               tcp_fastretrans_alert(sk, prior_packets, flag);
        } else {
                if ((flag & FLAG_DATA_ACKED) && !frto_cwnd)
                        tcp_cong_avoid(sk, ack, prior_in_flight, 1);