]> err.no Git - linux-2.6/blobdiff - net/ipv4/tcp_input.c
Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
[linux-2.6] / net / ipv4 / tcp_input.c
index 69d8c38ccd3900aa7c9bedac6506f0b6cf34aaea..ca9590f4f520a4b08a2913659a1a48b374d341d9 100644 (file)
@@ -1330,12 +1330,15 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
                cached_fack_count = 0;
        }
 
-       for (i=0; i<num_sacks; i++, sp++) {
+       for (i = 0; i < num_sacks; i++) {
                struct sk_buff *skb;
                __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));
+               int next_dup = (found_dup_sack && (i+1 == first_sack_index));
+
+               sp++;
 
                if (!tcp_is_sackblock_valid(tp, dup_sack, start_seq, end_seq)) {
                        if (dup_sack) {
@@ -1361,7 +1364,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
                        flag |= FLAG_DATA_LOST;
 
                tcp_for_write_queue_from(skb, sk) {
-                       int in_sack;
+                       int in_sack = 0;
                        u8 sacked;
 
                        if (skb == tcp_send_head(sk))
@@ -1380,7 +1383,23 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
                        if (!before(TCP_SKB_CB(skb)->seq, end_seq))
                                break;
 
-                       in_sack = tcp_match_skb_to_sack(sk, skb, start_seq, end_seq);
+                       dup_sack = (found_dup_sack && (i == first_sack_index));
+
+                       /* Due to sorting DSACK may reside within this SACK block! */
+                       if (next_dup) {
+                               u32 dup_start = ntohl(sp->start_seq);
+                               u32 dup_end = ntohl(sp->end_seq);
+
+                               if (before(TCP_SKB_CB(skb)->seq, dup_end)) {
+                                       in_sack = tcp_match_skb_to_sack(sk, skb, dup_start, dup_end);
+                                       if (in_sack > 0)
+                                               dup_sack = 1;
+                               }
+                       }
+
+                       /* DSACK info lost if out-of-mem, try SACK still */
+                       if (in_sack <= 0)
+                               in_sack = tcp_match_skb_to_sack(sk, skb, start_seq, end_seq);
                        if (in_sack < 0)
                                break;
 
@@ -2059,7 +2078,7 @@ static void tcp_update_scoreboard(struct sock *sk)
                        if (!tcp_skb_timedout(sk, skb))
                                break;
 
-                       if (!(TCP_SKB_CB(skb)->sacked&TCPCB_TAGBITS)) {
+                       if (!(TCP_SKB_CB(skb)->sacked & (TCPCB_SACKED_ACKED|TCPCB_LOST))) {
                                TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
                                tp->lost_out += tcp_skb_pcount(skb);
                                tcp_verify_retransmit_hint(tp, skb);