]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
[TCP]: skb pcount with MTU discovery
authorDavid S. Miller <davem@sunset.davemloft.net>
Mon, 25 Apr 2005 02:12:33 +0000 (19:12 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 25 Apr 2005 02:12:33 +0000 (19:12 -0700)
The problem is that when doing MTU discovery, the too-large segments in
the write queue will be calculated as having a pcount of >1.  When
tcp_write_xmit() is trying to send, tcp_snd_test() fails the cwnd test
when pcount > cwnd.

The segments are eventually transmitted one at a time by keepalive, but
this can take a long time.

This patch checks if TSO is enabled when setting pcount.

Signed-off-by: John Heffner <jheffner@psc.edu>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/tcp.h
net/ipv4/tcp_output.c

index 503810a70e21e45832ecfdceb87f28e6838b59a5..9355ae5b1d75b1720d4957725d42a22774679a67 100644 (file)
@@ -1417,19 +1417,20 @@ tcp_nagle_check(const struct tcp_sock *tp, const struct sk_buff *skb,
                  tcp_minshall_check(tp))));
 }
 
-extern void tcp_set_skb_tso_segs(struct sk_buff *, unsigned int);
+extern void tcp_set_skb_tso_segs(struct sock *, struct sk_buff *);
 
 /* This checks if the data bearing packet SKB (usually sk->sk_send_head)
  * should be put on the wire right now.
  */
-static __inline__ int tcp_snd_test(const struct tcp_sock *tp, 
+static __inline__ int tcp_snd_test(struct sock *sk,
                                   struct sk_buff *skb,
                                   unsigned cur_mss, int nonagle)
 {
+       struct tcp_sock *tp = tcp_sk(sk);
        int pkts = tcp_skb_pcount(skb);
 
        if (!pkts) {
-               tcp_set_skb_tso_segs(skb, tp->mss_cache_std);
+               tcp_set_skb_tso_segs(sk, skb);
                pkts = tcp_skb_pcount(skb);
        }
 
@@ -1490,7 +1491,7 @@ static __inline__ void __tcp_push_pending_frames(struct sock *sk,
        if (skb) {
                if (!tcp_skb_is_last(sk, skb))
                        nonagle = TCP_NAGLE_PUSH;
-               if (!tcp_snd_test(tp, skb, cur_mss, nonagle) ||
+               if (!tcp_snd_test(sk, skb, cur_mss, nonagle) ||
                    tcp_write_xmit(sk, nonagle))
                        tcp_check_probe_timer(sk, tp);
        }
@@ -1508,7 +1509,7 @@ static __inline__ int tcp_may_send_now(struct sock *sk, struct tcp_sock *tp)
        struct sk_buff *skb = sk->sk_send_head;
 
        return (skb &&
-               tcp_snd_test(tp, skb, tcp_current_mss(sk, 1),
+               tcp_snd_test(sk, skb, tcp_current_mss(sk, 1),
                             tcp_skb_is_last(sk, skb) ? TCP_NAGLE_PUSH : tp->nonagle));
 }
 
index 13c14cb6dee45254077a0c2d59400c939043631c..a12df6979ffd3cfb412973e277344ef8387f5b9f 100644 (file)
@@ -427,7 +427,7 @@ void tcp_push_one(struct sock *sk, unsigned cur_mss)
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *skb = sk->sk_send_head;
 
-       if (tcp_snd_test(tp, skb, cur_mss, TCP_NAGLE_PUSH)) {
+       if (tcp_snd_test(sk, skb, cur_mss, TCP_NAGLE_PUSH)) {
                /* Send it out now. */
                TCP_SKB_CB(skb)->when = tcp_time_stamp;
                tcp_tso_set_push(skb);
@@ -440,9 +440,12 @@ void tcp_push_one(struct sock *sk, unsigned cur_mss)
        }
 }
 
-void tcp_set_skb_tso_segs(struct sk_buff *skb, unsigned int mss_std)
+void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb)
 {
-       if (skb->len <= mss_std) {
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if (skb->len <= tp->mss_cache_std ||
+           !(sk->sk_route_caps & NETIF_F_TSO)) {
                /* Avoid the costly divide in the normal
                 * non-TSO case.
                 */
@@ -451,10 +454,10 @@ void tcp_set_skb_tso_segs(struct sk_buff *skb, unsigned int mss_std)
        } else {
                unsigned int factor;
 
-               factor = skb->len + (mss_std - 1);
-               factor /= mss_std;
+               factor = skb->len + (tp->mss_cache_std - 1);
+               factor /= tp->mss_cache_std;
                skb_shinfo(skb)->tso_segs = factor;
-               skb_shinfo(skb)->tso_size = mss_std;
+               skb_shinfo(skb)->tso_size = tp->mss_cache_std;
        }
 }
 
@@ -525,8 +528,8 @@ static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len)
        }
 
        /* Fix up tso_factor for both original and new SKB.  */
-       tcp_set_skb_tso_segs(skb, tp->mss_cache_std);
-       tcp_set_skb_tso_segs(buff, tp->mss_cache_std);
+       tcp_set_skb_tso_segs(sk, skb);
+       tcp_set_skb_tso_segs(sk, buff);
 
        if (TCP_SKB_CB(skb)->sacked & TCPCB_LOST) {
                tp->lost_out += tcp_skb_pcount(skb);
@@ -601,7 +604,7 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
         * factor and mss.
         */
        if (tcp_skb_pcount(skb) > 1)
-               tcp_set_skb_tso_segs(skb, tcp_skb_mss(skb));
+               tcp_set_skb_tso_segs(sk, skb);
 
        return 0;
 }
@@ -752,7 +755,7 @@ int tcp_write_xmit(struct sock *sk, int nonagle)
                mss_now = tcp_current_mss(sk, 1);
 
                while ((skb = sk->sk_send_head) &&
-                      tcp_snd_test(tp, skb, mss_now,
+                      tcp_snd_test(sk, skb, mss_now,
                                    tcp_skb_is_last(sk, skb) ? nonagle :
                                                               TCP_NAGLE_PUSH)) {
                        if (skb->len > mss_now) {
@@ -1676,7 +1679,7 @@ int tcp_write_wakeup(struct sock *sk)
                                        tp->mss_cache = tp->mss_cache_std;
                                }
                        } else if (!tcp_skb_pcount(skb))
-                               tcp_set_skb_tso_segs(skb, tp->mss_cache_std);
+                               tcp_set_skb_tso_segs(sk, skb);
 
                        TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
                        TCP_SKB_CB(skb)->when = tcp_time_stamp;