diff options
Diffstat (limited to 'net/ipv4/tcp.c')
| -rw-r--r-- | net/ipv4/tcp.c | 22 | 
1 files changed, 21 insertions, 1 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 10c93930abd..ab450c099aa 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2887,6 +2887,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,  	unsigned int mss;  	struct sk_buff *gso_skb = skb;  	__sum16 newcheck; +	bool ooo_okay, copy_destructor;  	if (!pskb_may_pull(skb, sizeof(*th)))  		goto out; @@ -2927,10 +2928,18 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,  		goto out;  	} +	copy_destructor = gso_skb->destructor == tcp_wfree; +	ooo_okay = gso_skb->ooo_okay; +	/* All segments but the first should have ooo_okay cleared */ +	skb->ooo_okay = 0; +  	segs = skb_segment(skb, features);  	if (IS_ERR(segs))  		goto out; +	/* Only first segment might have ooo_okay set */ +	segs->ooo_okay = ooo_okay; +  	delta = htonl(oldlen + (thlen + mss));  	skb = segs; @@ -2950,6 +2959,17 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,  						    thlen, skb->csum));  		seq += mss; +		if (copy_destructor) { +			skb->destructor = gso_skb->destructor; +			skb->sk = gso_skb->sk; +			/* {tcp|sock}_wfree() use exact truesize accounting : +			 * sum(skb->truesize) MUST be exactly be gso_skb->truesize +			 * So we account mss bytes of 'true size' for each segment. +			 * The last segment will contain the remaining. +			 */ +			skb->truesize = mss; +			gso_skb->truesize -= mss; +		}  		skb = skb->next;  		th = tcp_hdr(skb); @@ -2962,7 +2982,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,  	 * is freed at TX completion, and not right now when gso_skb  	 * is freed by GSO engine  	 */ -	if (gso_skb->destructor == tcp_wfree) { +	if (copy_destructor) {  		swap(gso_skb->sk, skb->sk);  		swap(gso_skb->destructor, skb->destructor);  		swap(gso_skb->truesize, skb->truesize);  |