tcp: add tcp_tsopt_ecr_before helper
[firefly-linux-kernel-4.4.55.git] / net / ipv4 / tcp_input.c
index 944eaca6911596676af66a934a17685c27fc11cc..1e97e73e5ecf1a3068355403a3c1a5ea52de98e0 100644 (file)
@@ -95,6 +95,7 @@ int sysctl_tcp_stdurg __read_mostly;
 int sysctl_tcp_rfc1337 __read_mostly;
 int sysctl_tcp_max_orphans __read_mostly = NR_FILE;
 int sysctl_tcp_frto __read_mostly = 2;
+int sysctl_tcp_min_rtt_wlen __read_mostly = 300;
 
 int sysctl_tcp_thin_dupack __read_mostly;
 
@@ -1047,70 +1048,6 @@ static bool tcp_is_sackblock_valid(struct tcp_sock *tp, bool is_dsack,
        return !before(start_seq, end_seq - tp->max_window);
 }
 
-/* Check for lost retransmit. This superb idea is borrowed from "ratehalving".
- * Event "B". Later note: FACK people cheated me again 8), we have to account
- * for reordering! Ugly, but should help.
- *
- * Search retransmitted skbs from write_queue that were sent when snd_nxt was
- * less than what is now known to be received by the other end (derived from
- * highest SACK block). Also calculate the lowest snd_nxt among the remaining
- * retransmitted skbs to avoid some costly processing per ACKs.
- */
-static void tcp_mark_lost_retrans(struct sock *sk, int *flag)
-{
-       const struct inet_connection_sock *icsk = inet_csk(sk);
-       struct tcp_sock *tp = tcp_sk(sk);
-       struct sk_buff *skb;
-       int cnt = 0;
-       u32 new_low_seq = tp->snd_nxt;
-       u32 received_upto = tcp_highest_sack_seq(tp);
-
-       if (!tcp_is_fack(tp) || !tp->retrans_out ||
-           !after(received_upto, tp->lost_retrans_low) ||
-           icsk->icsk_ca_state != TCP_CA_Recovery)
-               return;
-
-       tcp_for_write_queue(skb, sk) {
-               u32 ack_seq = TCP_SKB_CB(skb)->ack_seq;
-
-               if (skb == tcp_send_head(sk))
-                       break;
-               if (cnt == tp->retrans_out)
-                       break;
-               if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
-                       continue;
-
-               if (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS))
-                       continue;
-
-               /* TODO: We would like to get rid of tcp_is_fack(tp) only
-                * constraint here (see above) but figuring out that at
-                * least tp->reordering SACK blocks reside between ack_seq
-                * and received_upto is not easy task to do cheaply with
-                * the available datastructures.
-                *
-                * Whether FACK should check here for tp->reordering segs
-                * in-between one could argue for either way (it would be
-                * rather simple to implement as we could count fack_count
-                * during the walk and do tp->fackets_out - fack_count).
-                */
-               if (after(received_upto, ack_seq)) {
-                       TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
-                       tp->retrans_out -= tcp_skb_pcount(skb);
-                       *flag |= FLAG_LOST_RETRANS;
-                       tcp_skb_mark_lost_uncond_verify(tp, skb);
-                       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPLOSTRETRANSMIT);
-               } else {
-                       if (before(ack_seq, new_low_seq))
-                               new_low_seq = ack_seq;
-                       cnt += tcp_skb_pcount(skb);
-               }
-       }
-
-       if (tp->retrans_out)
-               tp->lost_retrans_low = new_low_seq;
-}
-
 static bool tcp_check_dsack(struct sock *sk, const struct sk_buff *ack_skb,
                            struct tcp_sack_block_wire *sp, int num_sacks,
                            u32 prior_snd_una)
@@ -1837,7 +1774,6 @@ advance_sp:
            ((inet_csk(sk)->icsk_ca_state != TCP_CA_Loss) || tp->undo_marker))
                tcp_update_reordering(sk, tp->fackets_out - state->reord, 0);
 
-       tcp_mark_lost_retrans(sk, &state->flag);
        tcp_verify_left_out(tp);
 out:
 
@@ -2314,14 +2250,19 @@ static inline void tcp_moderate_cwnd(struct tcp_sock *tp)
        tp->snd_cwnd_stamp = tcp_time_stamp;
 }
 
+static bool tcp_tsopt_ecr_before(const struct tcp_sock *tp, u32 when)
+{
+       return tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
+              before(tp->rx_opt.rcv_tsecr, when);
+}
+
 /* Nothing was retransmitted or returned timestamp is less
  * than timestamp of the first retransmission.
  */
 static inline bool tcp_packet_delayed(const struct tcp_sock *tp)
 {
        return !tp->retrans_stamp ||
-               (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
-                before(tp->rx_opt.rcv_tsecr, tp->retrans_stamp));
+              tcp_tsopt_ecr_before(tp, tp->retrans_stamp);
 }
 
 /* Undo procedures. */
@@ -2915,8 +2856,69 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked,
        tcp_xmit_retransmit_queue(sk);
 }
 
+/* Kathleen Nichols' algorithm for tracking the minimum value of
+ * a data stream over some fixed time interval. (E.g., the minimum
+ * RTT over the past five minutes.) It uses constant space and constant
+ * time per update yet almost always delivers the same minimum as an
+ * implementation that has to keep all the data in the window.
+ *
+ * The algorithm keeps track of the best, 2nd best & 3rd best min
+ * values, maintaining an invariant that the measurement time of the
+ * n'th best >= n-1'th best. It also makes sure that the three values
+ * are widely separated in the time window since that bounds the worse
+ * case error when that data is monotonically increasing over the window.
+ *
+ * Upon getting a new min, we can forget everything earlier because it
+ * has no value - the new min is <= everything else in the window by
+ * definition and it's the most recent. So we restart fresh on every new min
+ * and overwrites 2nd & 3rd choices. The same property holds for 2nd & 3rd
+ * best.
+ */
+static void tcp_update_rtt_min(struct sock *sk, u32 rtt_us)
+{
+       const u32 now = tcp_time_stamp, wlen = sysctl_tcp_min_rtt_wlen * HZ;
+       struct rtt_meas *m = tcp_sk(sk)->rtt_min;
+       struct rtt_meas rttm = { .rtt = (rtt_us ? : 1), .ts = now };
+       u32 elapsed;
+
+       /* Check if the new measurement updates the 1st, 2nd, or 3rd choices */
+       if (unlikely(rttm.rtt <= m[0].rtt))
+               m[0] = m[1] = m[2] = rttm;
+       else if (rttm.rtt <= m[1].rtt)
+               m[1] = m[2] = rttm;
+       else if (rttm.rtt <= m[2].rtt)
+               m[2] = rttm;
+
+       elapsed = now - m[0].ts;
+       if (unlikely(elapsed > wlen)) {
+               /* Passed entire window without a new min so make 2nd choice
+                * the new min & 3rd choice the new 2nd. So forth and so on.
+                */
+               m[0] = m[1];
+               m[1] = m[2];
+               m[2] = rttm;
+               if (now - m[0].ts > wlen) {
+                       m[0] = m[1];
+                       m[1] = rttm;
+                       if (now - m[0].ts > wlen)
+                               m[0] = rttm;
+               }
+       } else if (m[1].ts == m[0].ts && elapsed > wlen / 4) {
+               /* Passed a quarter of the window without a new min so
+                * take 2nd choice from the 2nd quarter of the window.
+                */
+               m[2] = m[1] = rttm;
+       } else if (m[2].ts == m[1].ts && elapsed > wlen / 2) {
+               /* Passed half the window without a new min so take the 3rd
+                * choice from the last half of the window.
+                */
+               m[2] = rttm;
+       }
+}
+
 static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
-                                     long seq_rtt_us, long sack_rtt_us)
+                                     long seq_rtt_us, long sack_rtt_us,
+                                     long ca_rtt_us)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
 
@@ -2925,9 +2927,6 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
         * Karn's algorithm forbids taking RTT if some retransmitted data
         * is acked (RFC6298).
         */
-       if (flag & FLAG_RETRANS_DATA_ACKED)
-               seq_rtt_us = -1L;
-
        if (seq_rtt_us < 0)
                seq_rtt_us = sack_rtt_us;
 
@@ -2939,11 +2938,16 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag,
         */
        if (seq_rtt_us < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
            flag & FLAG_ACKED)
-               seq_rtt_us = jiffies_to_usecs(tcp_time_stamp - tp->rx_opt.rcv_tsecr);
-
+               seq_rtt_us = ca_rtt_us = jiffies_to_usecs(tcp_time_stamp -
+                                                         tp->rx_opt.rcv_tsecr);
        if (seq_rtt_us < 0)
                return false;
 
+       /* ca_rtt_us >= 0 is counting on the invariant that ca_rtt_us is
+        * always taken together with ACK, SACK, or TS-opts. Any negative
+        * values will be skipped with the seq_rtt_us < 0 check above.
+        */
+       tcp_update_rtt_min(sk, ca_rtt_us);
        tcp_rtt_estimator(sk, seq_rtt_us);
        tcp_set_rto(sk);
 
@@ -2964,7 +2968,7 @@ void tcp_synack_rtt_meas(struct sock *sk, struct request_sock *req)
                rtt_us = skb_mstamp_us_delta(&now, &tcp_rsk(req)->snt_synack);
        }
 
-       tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, rtt_us, -1L);
+       tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, rtt_us, -1L, rtt_us);
 }
 
 
@@ -3169,7 +3173,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
                flag |= FLAG_SACK_RENEGING;
 
        skb_mstamp_get(&now);
-       if (likely(first_ackt.v64)) {
+       if (likely(first_ackt.v64) && !(flag & FLAG_RETRANS_DATA_ACKED)) {
                seq_rtt_us = skb_mstamp_us_delta(&now, &first_ackt);
                ca_rtt_us = skb_mstamp_us_delta(&now, &last_ackt);
        }
@@ -3178,7 +3182,8 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
                ca_rtt_us = skb_mstamp_us_delta(&now, &sack->last_sackt);
        }
 
-       rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt_us, sack_rtt_us);
+       rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt_us, sack_rtt_us,
+                                       ca_rtt_us);
 
        if (flag & FLAG_ACKED) {
                tcp_rearm_rto(sk);