Commit d5ac99a6 authored by David S. Miller's avatar David S. Miller Committed by David S. Miller

[TCP]: skb pcount with MTU discovery

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: default avatarJohn Heffner <jheffner@psc.edu>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 158a0e45
...@@ -1417,19 +1417,20 @@ tcp_nagle_check(const struct tcp_sock *tp, const struct sk_buff *skb, ...@@ -1417,19 +1417,20 @@ tcp_nagle_check(const struct tcp_sock *tp, const struct sk_buff *skb,
tcp_minshall_check(tp)))); 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) /* This checks if the data bearing packet SKB (usually sk->sk_send_head)
* should be put on the wire right now. * 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, struct sk_buff *skb,
unsigned cur_mss, int nonagle) unsigned cur_mss, int nonagle)
{ {
struct tcp_sock *tp = tcp_sk(sk);
int pkts = tcp_skb_pcount(skb); int pkts = tcp_skb_pcount(skb);
if (!pkts) { if (!pkts) {
tcp_set_skb_tso_segs(skb, tp->mss_cache_std); tcp_set_skb_tso_segs(sk, skb);
pkts = tcp_skb_pcount(skb); pkts = tcp_skb_pcount(skb);
} }
...@@ -1490,7 +1491,7 @@ static __inline__ void __tcp_push_pending_frames(struct sock *sk, ...@@ -1490,7 +1491,7 @@ static __inline__ void __tcp_push_pending_frames(struct sock *sk,
if (skb) { if (skb) {
if (!tcp_skb_is_last(sk, skb)) if (!tcp_skb_is_last(sk, skb))
nonagle = TCP_NAGLE_PUSH; 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_write_xmit(sk, nonagle))
tcp_check_probe_timer(sk, tp); tcp_check_probe_timer(sk, tp);
} }
...@@ -1508,7 +1509,7 @@ static __inline__ int tcp_may_send_now(struct sock *sk, struct tcp_sock *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; struct sk_buff *skb = sk->sk_send_head;
return (skb && 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)); tcp_skb_is_last(sk, skb) ? TCP_NAGLE_PUSH : tp->nonagle));
} }
......
...@@ -427,7 +427,7 @@ void tcp_push_one(struct sock *sk, unsigned cur_mss) ...@@ -427,7 +427,7 @@ void tcp_push_one(struct sock *sk, unsigned cur_mss)
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb = sk->sk_send_head; 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. */ /* Send it out now. */
TCP_SKB_CB(skb)->when = tcp_time_stamp; TCP_SKB_CB(skb)->when = tcp_time_stamp;
tcp_tso_set_push(skb); tcp_tso_set_push(skb);
...@@ -440,9 +440,12 @@ void tcp_push_one(struct sock *sk, unsigned cur_mss) ...@@ -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 /* Avoid the costly divide in the normal
* non-TSO case. * non-TSO case.
*/ */
...@@ -451,10 +454,10 @@ void tcp_set_skb_tso_segs(struct sk_buff *skb, unsigned int mss_std) ...@@ -451,10 +454,10 @@ void tcp_set_skb_tso_segs(struct sk_buff *skb, unsigned int mss_std)
} else { } else {
unsigned int factor; unsigned int factor;
factor = skb->len + (mss_std - 1); factor = skb->len + (tp->mss_cache_std - 1);
factor /= mss_std; factor /= tp->mss_cache_std;
skb_shinfo(skb)->tso_segs = factor; 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) ...@@ -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. */ /* 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(sk, skb);
tcp_set_skb_tso_segs(buff, tp->mss_cache_std); tcp_set_skb_tso_segs(sk, buff);
if (TCP_SKB_CB(skb)->sacked & TCPCB_LOST) { if (TCP_SKB_CB(skb)->sacked & TCPCB_LOST) {
tp->lost_out += tcp_skb_pcount(skb); tp->lost_out += tcp_skb_pcount(skb);
...@@ -601,7 +604,7 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) ...@@ -601,7 +604,7 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
* factor and mss. * factor and mss.
*/ */
if (tcp_skb_pcount(skb) > 1) 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; return 0;
} }
...@@ -752,7 +755,7 @@ int tcp_write_xmit(struct sock *sk, int nonagle) ...@@ -752,7 +755,7 @@ int tcp_write_xmit(struct sock *sk, int nonagle)
mss_now = tcp_current_mss(sk, 1); mss_now = tcp_current_mss(sk, 1);
while ((skb = sk->sk_send_head) && 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_skb_is_last(sk, skb) ? nonagle :
TCP_NAGLE_PUSH)) { TCP_NAGLE_PUSH)) {
if (skb->len > mss_now) { if (skb->len > mss_now) {
...@@ -1676,7 +1679,7 @@ int tcp_write_wakeup(struct sock *sk) ...@@ -1676,7 +1679,7 @@ int tcp_write_wakeup(struct sock *sk)
tp->mss_cache = tp->mss_cache_std; tp->mss_cache = tp->mss_cache_std;
} }
} else if (!tcp_skb_pcount(skb)) } 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)->flags |= TCPCB_FLAG_PSH;
TCP_SKB_CB(skb)->when = tcp_time_stamp; TCP_SKB_CB(skb)->when = tcp_time_stamp;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment