forked from mirrors/linux
		
	udp: add gso segment cmsg
Allow specifying segment size in the send call. The new control message performs the same function as socket option UDP_SEGMENT while avoiding the extra system call. [ Export udp_cmsg_send for ipv6. -DaveM ] Signed-off-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									15e36f5b8e
								
							
						
					
					
						commit
						2e8de85763
					
				
					 3 changed files with 47 additions and 3 deletions
				
			
		|  | @ -273,6 +273,7 @@ int udp_abort(struct sock *sk, int err); | |||
| int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); | ||||
| int udp_push_pending_frames(struct sock *sk); | ||||
| void udp_flush_pending_frames(struct sock *sk); | ||||
| int udp_cmsg_send(struct sock *sk, struct msghdr *msg, u16 *gso_size); | ||||
| void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst); | ||||
| int udp_rcv(struct sk_buff *skb); | ||||
| int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); | ||||
|  |  | |||
|  | @ -853,6 +853,43 @@ int udp_push_pending_frames(struct sock *sk) | |||
| } | ||||
| EXPORT_SYMBOL(udp_push_pending_frames); | ||||
| 
 | ||||
| static int __udp_cmsg_send(struct cmsghdr *cmsg, u16 *gso_size) | ||||
| { | ||||
| 	switch (cmsg->cmsg_type) { | ||||
| 	case UDP_SEGMENT: | ||||
| 		if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u16))) | ||||
| 			return -EINVAL; | ||||
| 		*gso_size = *(__u16 *)CMSG_DATA(cmsg); | ||||
| 		return 0; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int udp_cmsg_send(struct sock *sk, struct msghdr *msg, u16 *gso_size) | ||||
| { | ||||
| 	struct cmsghdr *cmsg; | ||||
| 	bool need_ip = false; | ||||
| 	int err; | ||||
| 
 | ||||
| 	for_each_cmsghdr(cmsg, msg) { | ||||
| 		if (!CMSG_OK(msg, cmsg)) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		if (cmsg->cmsg_level != SOL_UDP) { | ||||
| 			need_ip = true; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		err = __udp_cmsg_send(cmsg, gso_size); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return need_ip; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(udp_cmsg_send); | ||||
| 
 | ||||
| int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | ||||
| { | ||||
| 	struct inet_sock *inet = inet_sk(sk); | ||||
|  | @ -941,8 +978,11 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | |||
| 	ipc.gso_size = up->gso_size; | ||||
| 
 | ||||
| 	if (msg->msg_controllen) { | ||||
| 		err = ip_cmsg_send(sk, msg, &ipc, sk->sk_family == AF_INET6); | ||||
| 		if (unlikely(err)) { | ||||
| 		err = udp_cmsg_send(sk, msg, &ipc.gso_size); | ||||
| 		if (err > 0) | ||||
| 			err = ip_cmsg_send(sk, msg, &ipc, | ||||
| 					   sk->sk_family == AF_INET6); | ||||
| 		if (unlikely(err < 0)) { | ||||
| 			kfree(ipc.opt); | ||||
| 			return err; | ||||
| 		} | ||||
|  |  | |||
|  | @ -1276,7 +1276,10 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) | |||
| 		opt->tot_len = sizeof(*opt); | ||||
| 		ipc6.opt = opt; | ||||
| 
 | ||||
| 		err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, &ipc6, &sockc); | ||||
| 		err = udp_cmsg_send(sk, msg, &ipc6.gso_size); | ||||
| 		if (err > 0) | ||||
| 			err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, | ||||
| 						    &ipc6, &sockc); | ||||
| 		if (err < 0) { | ||||
| 			fl6_sock_release(flowlabel); | ||||
| 			return err; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Willem de Bruijn
						Willem de Bruijn