Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -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));
}

+14 −11
Original line number Diff line number Diff line
@@ -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;