diff options
Diffstat (limited to 'net/ipv4/tcp.c')
| -rw-r--r-- | net/ipv4/tcp.c | 22 | 
1 files changed, 20 insertions, 2 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3c6498dab6bd..b7796b4cf0a0 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -862,6 +862,7 @@ struct sk_buff *tcp_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp,  	if (likely(skb)) {  		bool mem_scheduled; +		skb->truesize = SKB_TRUESIZE(skb_end_offset(skb));  		if (force_schedule) {  			mem_scheduled = true;  			sk_forced_mem_schedule(sk, skb->truesize); @@ -1318,6 +1319,15 @@ new_segment:  			copy = min_t(int, copy, pfrag->size - pfrag->offset); +			/* skb changing from pure zc to mixed, must charge zc */ +			if (unlikely(skb_zcopy_pure(skb))) { +				if (!sk_wmem_schedule(sk, skb->data_len)) +					goto wait_for_space; + +				sk_mem_charge(sk, skb->data_len); +				skb_shinfo(skb)->flags &= ~SKBFL_PURE_ZEROCOPY; +			} +  			if (!sk_wmem_schedule(sk, copy))  				goto wait_for_space; @@ -1338,8 +1348,16 @@ new_segment:  			}  			pfrag->offset += copy;  		} else { -			if (!sk_wmem_schedule(sk, copy)) -				goto wait_for_space; +			/* First append to a fragless skb builds initial +			 * pure zerocopy skb +			 */ +			if (!skb->len) +				skb_shinfo(skb)->flags |= SKBFL_PURE_ZEROCOPY; + +			if (!skb_zcopy_pure(skb)) { +				if (!sk_wmem_schedule(sk, copy)) +					goto wait_for_space; +			}  			err = skb_zerocopy_iter_stream(sk, skb, msg, copy, uarg);  			if (err == -EMSGSIZE || err == -EEXIST) {  |