#endif
        u32                             rcv_isn;
        u32                             snt_isn;
+       u32                             snt_synack; /* synack sent time */
 };
 
 static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)
 
 #endif
 #define TCP_RTO_MAX    ((unsigned)(120*HZ))
 #define TCP_RTO_MIN    ((unsigned)(HZ/5))
-#define TCP_TIMEOUT_INIT ((unsigned)(3*HZ))    /* RFC 1122 initial RTO value   */
+#define TCP_TIMEOUT_INIT ((unsigned)(1*HZ))    /* RFC2988bis initial RTO value */
+#define TCP_TIMEOUT_FALLBACK ((unsigned)(3*HZ))        /* RFC 1122 initial RTO value, now
+                                                * used as a fallback RTO for the
+                                                * initial data transmission if no
+                                                * valid RTT sample has been acquired,
+                                                * most likely due to retrans in 3WHS.
+                                                */
 
 #define TCP_RESOURCE_PROBE_INTERVAL ((unsigned)(HZ/2U)) /* Maximal interval between probes
                                                         * for local resources.
 static inline int tcp_synq_no_recent_overflow(const struct sock *sk)
 {
        unsigned long last_overflow = tcp_sk(sk)->rx_opt.ts_recent_stamp;
-       return time_after(jiffies, last_overflow + TCP_TIMEOUT_INIT);
+       return time_after(jiffies, last_overflow + TCP_TIMEOUT_FALLBACK);
 }
 
 extern struct proto tcp_prot;
 extern int tcp_mtu_to_mss(struct sock *sk, int pmtu);
 extern int tcp_mss_to_mtu(struct sock *sk, int mss);
 extern void tcp_mtup_init(struct sock *sk);
+extern void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt);
 
 static inline void tcp_bound_rto(const struct sock *sk)
 {
 
        ireq->wscale_ok         = tcp_opt.wscale_ok;
        ireq->tstamp_ok         = tcp_opt.saw_tstamp;
        req->ts_recent          = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
+       treq->snt_synack        = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0;
 
        /* We throwed the options of the initial SYN away, so we hope
         * the ACK carries the same options again (see RFC1122 4.2.3.8)
 
                tp->snd_ssthresh = dst_metric(dst, RTAX_SSTHRESH);
                if (tp->snd_ssthresh > tp->snd_cwnd_clamp)
                        tp->snd_ssthresh = tp->snd_cwnd_clamp;
+       } else {
+               /* ssthresh may have been reduced unnecessarily during.
+                * 3WHS. Restore it back to its initial default.
+                */
+               tp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
        }
        if (dst_metric(dst, RTAX_REORDERING) &&
            tp->reordering != dst_metric(dst, RTAX_REORDERING)) {
                tp->reordering = dst_metric(dst, RTAX_REORDERING);
        }
 
-       if (dst_metric(dst, RTAX_RTT) == 0)
-               goto reset;
-
-       if (!tp->srtt && dst_metric_rtt(dst, RTAX_RTT) < (TCP_TIMEOUT_INIT << 3))
+       if (dst_metric(dst, RTAX_RTT) == 0 || tp->srtt == 0)
                goto reset;
 
        /* Initial rtt is determined from SYN,SYN-ACK.
                tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk));
        }
        tcp_set_rto(sk);
-       if (inet_csk(sk)->icsk_rto < TCP_TIMEOUT_INIT && !tp->rx_opt.saw_tstamp) {
 reset:
-               /* Play conservative. If timestamps are not
-                * supported, TCP will fail to recalculate correct
-                * rtt, if initial rto is too small. FORGET ALL AND RESET!
+       if (tp->srtt == 0) {
+               /* RFC2988bis: We've failed to get a valid RTT sample from
+                * 3WHS. This is most likely due to retransmission,
+                * including spurious one. Reset the RTO back to 3secs
+                * from the more aggressive 1sec to avoid more spurious
+                * retransmission.
                 */
-               if (!tp->rx_opt.saw_tstamp && tp->srtt) {
-                       tp->srtt = 0;
-                       tp->mdev = tp->mdev_max = tp->rttvar = TCP_TIMEOUT_INIT;
-                       inet_csk(sk)->icsk_rto = TCP_TIMEOUT_INIT;
-               }
+               tp->mdev = tp->mdev_max = tp->rttvar = TCP_TIMEOUT_FALLBACK;
+               inet_csk(sk)->icsk_rto = TCP_TIMEOUT_FALLBACK;
        }
-       tp->snd_cwnd = tcp_init_cwnd(tp, dst);
+       /* Cut cwnd down to 1 per RFC5681 if SYN or SYN-ACK has been
+        * retransmitted. In light of RFC2988bis' more aggressive 1sec
+        * initRTO, we only reset cwnd when more than 1 SYN/SYN-ACK
+        * retransmission has occurred.
+        */
+       if (tp->total_retrans > 1)
+               tp->snd_cwnd = 1;
+       else
+               tp->snd_cwnd = tcp_init_cwnd(tp, dst);
        tp->snd_cwnd_stamp = tcp_time_stamp;
 }
 
        tcp_xmit_retransmit_queue(sk);
 }
 
-static void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt)
+void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt)
 {
        tcp_rtt_estimator(sk, seq_rtt);
        tcp_set_rto(sk);
        inet_csk(sk)->icsk_backoff = 0;
 }
+EXPORT_SYMBOL(tcp_valid_rtt_meas);
 
 /* Read draft-ietf-tcplw-high-performance before mucking
  * with this code. (Supersedes RFC1323)
                                              tp->rx_opt.snd_wscale;
                                tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
 
-                               /* tcp_ack considers this ACK as duplicate
-                                * and does not calculate rtt.
-                                * Force it here.
-                                */
-                               tcp_ack_update_rtt(sk, 0, 0);
-
                                if (tp->rx_opt.tstamp_ok)
                                        tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
 
 
                        break;
 
                icsk->icsk_backoff--;
-               inet_csk(sk)->icsk_rto = __tcp_set_rto(tp) <<
-                                        icsk->icsk_backoff;
+               inet_csk(sk)->icsk_rto = (tp->srtt ? __tcp_set_rto(tp) :
+                       TCP_TIMEOUT_INIT) << icsk->icsk_backoff;
                tcp_bound_rto(sk);
 
                skb = tcp_write_queue_head(sk);
                isn = tcp_v4_init_sequence(skb);
        }
        tcp_rsk(req)->snt_isn = isn;
+       tcp_rsk(req)->snt_synack = tcp_time_stamp;
 
        if (tcp_v4_send_synack(sk, dst, req,
                               (struct request_values *)&tmp_ext) ||
                newtp->advmss = tcp_sk(sk)->rx_opt.user_mss;
 
        tcp_initialize_rcv_mss(newsk);
+       if (tcp_rsk(req)->snt_synack)
+               tcp_valid_rtt_meas(newsk,
+                   tcp_time_stamp - tcp_rsk(req)->snt_synack);
+       newtp->total_retrans = req->retrans;
 
 #ifdef CONFIG_TCP_MD5SIG
        /* Copy over the MD5 key from the original socket */
         * algorithms that we must have the following bandaid to talk
         * efficiently to them.  -DaveM
         */
-       tp->snd_cwnd = 2;
+       tp->snd_cwnd = TCP_INIT_CWND;
 
        /* See draft-stevens-tcpca-spec-01 for discussion of the
         * initialization of these values.
 
                 * algorithms that we must have the following bandaid to talk
                 * efficiently to them.  -DaveM
                 */
-               newtp->snd_cwnd = 2;
+               newtp->snd_cwnd = TCP_INIT_CWND;
                newtp->snd_cwnd_cnt = 0;
                newtp->bytes_acked = 0;
 
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDEFERACCEPTDROP);
                return NULL;
        }
+       if (tmp_opt.saw_tstamp && tmp_opt.rcv_tsecr)
+               tcp_rsk(req)->snt_synack = tmp_opt.rcv_tsecr;
+       else if (req->retrans) /* don't take RTT sample if retrans && ~TS */
+               tcp_rsk(req)->snt_synack = 0;
 
        /* OK, ACK is valid, create big socket and
         * feed this segment to it. It will repeat all
 
        ireq->wscale_ok         = tcp_opt.wscale_ok;
        ireq->tstamp_ok         = tcp_opt.saw_tstamp;
        req->ts_recent          = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
+       treq->snt_synack        = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0;
        treq->rcv_isn = ntohl(th->seq) - 1;
        treq->snt_isn = cookie;
 
 
        }
 have_isn:
        tcp_rsk(req)->snt_isn = isn;
+       tcp_rsk(req)->snt_synack = tcp_time_stamp;
 
        security_inet_conn_request(sk, skb, req);
 
        tcp_sync_mss(newsk, dst_mtu(dst));
        newtp->advmss = dst_metric_advmss(dst);
        tcp_initialize_rcv_mss(newsk);
+       if (tcp_rsk(req)->snt_synack)
+               tcp_valid_rtt_meas(newsk,
+                   tcp_time_stamp - tcp_rsk(req)->snt_synack);
+       newtp->total_retrans = req->retrans;
 
        newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6;
        newinet->inet_rcv_saddr = LOOPBACK4_IPV6;