diff options
Diffstat (limited to 'net/ipv4/ip_output.c')
| -rw-r--r-- | net/ipv4/ip_output.c | 45 | 
1 files changed, 27 insertions, 18 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index d54abc097800..af5a830ff6ad 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -878,11 +878,14 @@ static int __ip_append_data(struct sock *sk,  	struct rtable *rt = (struct rtable *)cork->dst;  	unsigned int wmem_alloc_delta = 0;  	u32 tskey = 0; +	bool paged;  	skb = skb_peek_tail(queue);  	exthdrlen = !skb ? rt->dst.header_len : 0; -	mtu = cork->fragsize; +	mtu = cork->gso_size ? IP_MAX_MTU : cork->fragsize; +	paged = !!cork->gso_size; +  	if (cork->tx_flags & SKBTX_ANY_SW_TSTAMP &&  	    sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)  		tskey = sk->sk_tskey++; @@ -906,8 +909,8 @@ static int __ip_append_data(struct sock *sk,  	if (transhdrlen &&  	    length + fragheaderlen <= mtu &&  	    rt->dst.dev->features & (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM) && -	    !(flags & MSG_MORE) && -	    !exthdrlen) +	    (!(flags & MSG_MORE) || cork->gso_size) && +	    (!exthdrlen || (rt->dst.dev->features & NETIF_F_HW_ESP_TX_CSUM)))  		csummode = CHECKSUM_PARTIAL;  	cork->length += length; @@ -933,6 +936,7 @@ static int __ip_append_data(struct sock *sk,  			unsigned int fraglen;  			unsigned int fraggap;  			unsigned int alloclen; +			unsigned int pagedlen = 0;  			struct sk_buff *skb_prev;  alloc_new_skb:  			skb_prev = skb; @@ -953,8 +957,12 @@ alloc_new_skb:  			if ((flags & MSG_MORE) &&  			    !(rt->dst.dev->features&NETIF_F_SG))  				alloclen = mtu; -			else +			else if (!paged)  				alloclen = fraglen; +			else { +				alloclen = min_t(int, fraglen, MAX_HEADER); +				pagedlen = fraglen - alloclen; +			}  			alloclen += exthdrlen; @@ -998,7 +1006,7 @@ alloc_new_skb:  			/*  			 *	Find where to start putting bytes.  			 */ -			data = skb_put(skb, fraglen + exthdrlen); +			data = skb_put(skb, fraglen + exthdrlen - pagedlen);  			skb_set_network_header(skb, exthdrlen);  			skb->transport_header = (skb->network_header +  						 fragheaderlen); @@ -1014,7 +1022,7 @@ alloc_new_skb:  				pskb_trim_unique(skb_prev, maxfraglen);  			} -			copy = datalen - transhdrlen - fraggap; +			copy = datalen - transhdrlen - fraggap - pagedlen;  			if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) {  				err = -EFAULT;  				kfree_skb(skb); @@ -1022,7 +1030,7 @@ alloc_new_skb:  			}  			offset += copy; -			length -= datalen - fraggap; +			length -= copy + transhdrlen;  			transhdrlen = 0;  			exthdrlen = 0;  			csummode = CHECKSUM_NONE; @@ -1136,6 +1144,8 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,  	*rtp = NULL;  	cork->fragsize = ip_sk_use_pmtu(sk) ?  			 dst_mtu(&rt->dst) : rt->dst.dev->mtu; + +	cork->gso_size = sk->sk_type == SOCK_DGRAM ? ipc->gso_size : 0;  	cork->dst = &rt->dst;  	cork->length = 0;  	cork->ttl = ipc->ttl; @@ -1215,7 +1225,7 @@ ssize_t	ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,  		return -EOPNOTSUPP;  	hh_len = LL_RESERVED_SPACE(rt->dst.dev); -	mtu = cork->fragsize; +	mtu = cork->gso_size ? IP_MAX_MTU : cork->fragsize;  	fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);  	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; @@ -1471,9 +1481,8 @@ struct sk_buff *ip_make_skb(struct sock *sk,  					int len, int odd, struct sk_buff *skb),  			    void *from, int length, int transhdrlen,  			    struct ipcm_cookie *ipc, struct rtable **rtp, -			    unsigned int flags) +			    struct inet_cork *cork, unsigned int flags)  { -	struct inet_cork cork;  	struct sk_buff_head queue;  	int err; @@ -1482,22 +1491,22 @@ struct sk_buff *ip_make_skb(struct sock *sk,  	__skb_queue_head_init(&queue); -	cork.flags = 0; -	cork.addr = 0; -	cork.opt = NULL; -	err = ip_setup_cork(sk, &cork, ipc, rtp); +	cork->flags = 0; +	cork->addr = 0; +	cork->opt = NULL; +	err = ip_setup_cork(sk, cork, ipc, rtp);  	if (err)  		return ERR_PTR(err); -	err = __ip_append_data(sk, fl4, &queue, &cork, +	err = __ip_append_data(sk, fl4, &queue, cork,  			       ¤t->task_frag, getfrag,  			       from, length, transhdrlen, flags);  	if (err) { -		__ip_flush_pending_frames(sk, &queue, &cork); +		__ip_flush_pending_frames(sk, &queue, cork);  		return ERR_PTR(err);  	} -	return __ip_make_skb(sk, fl4, &queue, &cork); +	return __ip_make_skb(sk, fl4, &queue, cork);  }  /* @@ -1553,7 +1562,7 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb,  		oif = skb->skb_iif;  	flowi4_init_output(&fl4, oif, -			   IP4_REPLY_MARK(net, skb->mark), +			   IP4_REPLY_MARK(net, skb->mark) ?: sk->sk_mark,  			   RT_TOS(arg->tos),  			   RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol,  			   ip_reply_arg_flowi_flags(arg),  | 
