]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/ipv4/tcp_input.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-2.6-omap-h63xx.git] / net / ipv4 / tcp_input.c
index 051f0f815f179143bd0ccb9486db2a09dbafed30..69f9f1ef3ef69bc1d3ed88fa410443ac32af40a3 100644 (file)
@@ -953,7 +953,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
        int prior_fackets;
        u32 lost_retrans = 0;
        int flag = 0;
-       int dup_sack = 0;
+       int found_dup_sack = 0;
        int cached_fack_count;
        int i;
        int first_sack_index;
@@ -964,20 +964,20 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
 
        /* Check for D-SACK. */
        if (before(ntohl(sp[0].start_seq), TCP_SKB_CB(ack_skb)->ack_seq)) {
-               dup_sack = 1;
+               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))) {
-               dup_sack = 1;
+               found_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 &&
+       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--;
@@ -1058,6 +1058,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
                __u32 start_seq = ntohl(sp->start_seq);
                __u32 end_seq = ntohl(sp->end_seq);
                int fack_count;
+               int dup_sack = (found_dup_sack && (i == first_sack_index));
 
                skb = cached_skb;
                fack_count = cached_fack_count;
@@ -1265,20 +1266,15 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
        return flag;
 }
 
-/* F-RTO can only be used if these conditions are satisfied:
- *  - there must be some unsent new data
- *  - the advertised window should allow sending it
- *  - TCP has never retransmitted anything other than head (SACK enhanced
- *    variant from Appendix B of RFC4138 is more robust here)
+/* 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)
  */
 int tcp_use_frto(struct sock *sk)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *skb;
 
-       if (!sysctl_tcp_frto || !tcp_send_head(sk) ||
-               after(TCP_SKB_CB(tcp_send_head(sk))->end_seq,
-                     tp->snd_una + tp->snd_wnd))
+       if (!sysctl_tcp_frto)
                return 0;
 
        if (IsSackFrto())
@@ -1506,6 +1502,8 @@ void tcp_enter_loss(struct sock *sk, int how)
        tcp_set_ca_state(sk, TCP_CA_Loss);
        tp->high_seq = tp->snd_nxt;
        TCP_ECN_queue_cwr(tp);
+       /* Abort FRTO algorithm if one is in progress */
+       tp->frto_counter = 0;
 
        clear_all_retrans_hints(tp);
 }
@@ -2040,7 +2038,7 @@ static void tcp_try_to_open(struct sock *sk, int flag)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       tp->left_out = tp->sacked_out;
+       tcp_sync_left_out(tp);
 
        if (tp->retrans_out == 0)
                tp->retrans_stamp = 0;
@@ -2410,9 +2408,9 @@ static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p)
        struct sk_buff *skb;
        __u32 now = tcp_time_stamp;
        int acked = 0;
+       int prior_packets = tp->packets_out;
        __s32 seq_rtt = -1;
-       u32 pkts_acked = 0;
-       ktime_t last_ackt = ktime_set(0,0);
+       ktime_t last_ackt = net_invalid_timestamp();
 
        while ((skb = tcp_write_queue_head(sk)) &&
               skb != tcp_send_head(sk)) {
@@ -2440,7 +2438,6 @@ static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p)
                 */
                if (!(scb->flags & TCPCB_FLAG_SYN)) {
                        acked |= FLAG_DATA_ACKED;
-                       ++pkts_acked;
                } else {
                        acked |= FLAG_SYN_ACKED;
                        tp->retrans_stamp = 0;
@@ -2484,12 +2481,17 @@ static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p)
        }
 
        if (acked&FLAG_ACKED) {
+               u32 pkts_acked = prior_packets - tp->packets_out;
                const struct tcp_congestion_ops *ca_ops
                        = inet_csk(sk)->icsk_ca_ops;
 
                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)
                        ca_ops->pkts_acked(sk, pkts_acked, last_ackt);
        }
@@ -2613,6 +2615,7 @@ static void tcp_conservative_spur_to_response(struct tcp_sock *tp)
 {
        tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh);
        tp->snd_cwnd_cnt = 0;
+       TCP_ECN_queue_cwr(tp);
        tcp_moderate_cwnd(tp);
 }
 
@@ -2642,7 +2645,9 @@ static void tcp_undo_spur_to_response(struct sock *sk, int flag)
  *                  algorithm is not part of the F-RTO detection algorithm
  *                  given in RFC4138 but can be selected separately).
  * Otherwise (basically on duplicate ACK), RTO was (likely) caused by a loss
- * and TCP falls back to conventional RTO recovery.
+ * and TCP falls back to conventional RTO recovery. F-RTO allows overriding
+ * of Nagle, this is done using frto_counter states 2 and 3, when a new data
+ * segment of any size sent during F-RTO, state 2 is upgraded to 3.
  *
  * Rationale: if the RTO was spurious, new ACKs should arrive from the
  * original window even after we transmit two new data segments.
@@ -2671,7 +2676,7 @@ static int tcp_process_frto(struct sock *sk, u32 prior_snd_una, int flag)
                inet_csk(sk)->icsk_retransmits = 0;
 
        if (!before(tp->snd_una, tp->frto_highmark)) {
-               tcp_enter_frto_loss(sk, tp->frto_counter + 1, flag);
+               tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 2 : 3), flag);
                return 1;
        }
 
@@ -2697,7 +2702,7 @@ static int tcp_process_frto(struct sock *sk, u32 prior_snd_una, int flag)
                        return 1;
                }
 
-               if ((tp->frto_counter == 2) &&
+               if ((tp->frto_counter >= 2) &&
                    (!(flag&FLAG_FORWARD_PROGRESS) ||
                     ((flag&FLAG_DATA_SACKED) && !(flag&FLAG_ONLY_ORIG_SACKED)))) {
                        /* RFC4138 shortcoming (see comment above) */
@@ -2710,10 +2715,19 @@ static int tcp_process_frto(struct sock *sk, u32 prior_snd_una, int flag)
        }
 
        if (tp->frto_counter == 1) {
+               /* Sending of the next skb must be allowed or no FRTO */
+               if (!tcp_send_head(sk) ||
+                   after(TCP_SKB_CB(tcp_send_head(sk))->end_seq,
+                                    tp->snd_una + tp->snd_wnd)) {
+                       tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 2 : 3),
+                                           flag);
+                       return 1;
+               }
+
                tp->snd_cwnd = tcp_packets_in_flight(tp) + 2;
                tp->frto_counter = 2;
                return 1;
-       } else /* frto_counter == 2 */ {
+       } else {
                switch (sysctl_tcp_frto_response) {
                case 2:
                        tcp_undo_spur_to_response(sk, flag);
@@ -2923,6 +2937,7 @@ void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx,
                                           opt_rx->sack_ok) {
                                                TCP_SKB_CB(skb)->sacked = (ptr - 2) - (unsigned char *)th;
                                        }
+                                       break;
 #ifdef CONFIG_TCP_MD5SIG
                                case TCPOPT_MD5SIG:
                                        /*