[TCP]: Keep TSO enabled even during loss events.

All we need to do is resegment the queue so that
we record SACK information accurately.  The edges
of the SACK blocks guide our resegmenting decisions.

With help from Herbert Xu.

Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 1afb080..29222b96 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -923,14 +923,6 @@
 	int flag = 0;
 	int i;
 
-	/* So, SACKs for already sent large segments will be lost.
-	 * Not good, but alternative is to resegment the queue. */
-	if (sk->sk_route_caps & NETIF_F_TSO) {
-		sk->sk_route_caps &= ~NETIF_F_TSO;
-		sock_set_flag(sk, SOCK_NO_LARGESEND);
-		tp->mss_cache = tp->mss_cache;
-	}
-
 	if (!tp->sacked_out)
 		tp->fackets_out = 0;
 	prior_fackets = tp->fackets_out;
@@ -978,20 +970,40 @@
 			flag |= FLAG_DATA_LOST;
 
 		sk_stream_for_retrans_queue(skb, sk) {
-			u8 sacked = TCP_SKB_CB(skb)->sacked;
-			int in_sack;
+			int in_sack, pcount;
+			u8 sacked;
 
 			/* The retransmission queue is always in order, so
 			 * we can short-circuit the walk early.
 			 */
-			if(!before(TCP_SKB_CB(skb)->seq, end_seq))
+			if (!before(TCP_SKB_CB(skb)->seq, end_seq))
 				break;
 
-			fack_count += tcp_skb_pcount(skb);
+			pcount = tcp_skb_pcount(skb);
+
+			if (pcount > 1 &&
+			    (after(start_seq, TCP_SKB_CB(skb)->seq) ||
+			     before(end_seq, TCP_SKB_CB(skb)->end_seq))) {
+				unsigned int pkt_len;
+
+				if (after(start_seq, TCP_SKB_CB(skb)->seq))
+					pkt_len = (start_seq -
+						   TCP_SKB_CB(skb)->seq);
+				else
+					pkt_len = (end_seq -
+						   TCP_SKB_CB(skb)->seq);
+				if (tcp_fragment(sk, skb, pkt_len, skb_shinfo(skb)->tso_size))
+					break;
+				pcount = tcp_skb_pcount(skb);
+			}
+
+			fack_count += pcount;
 
 			in_sack = !after(start_seq, TCP_SKB_CB(skb)->seq) &&
 				!before(end_seq, TCP_SKB_CB(skb)->end_seq);
 
+			sacked = TCP_SKB_CB(skb)->sacked;
+
 			/* Account D-SACK for retransmitted packet. */
 			if ((dup_sack && in_sack) &&
 			    (sacked & TCPCB_RETRANS) &&