Skip to content

Commit 2e8de85

Browse files
wdebruijdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 15e36f5 commit 2e8de85

File tree

3 files changed

+47
-3
lines changed

3 files changed

+47
-3
lines changed

include/net/udp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ int udp_abort(struct sock *sk, int err);
273273
int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len);
274274
int udp_push_pending_frames(struct sock *sk);
275275
void udp_flush_pending_frames(struct sock *sk);
276+
int udp_cmsg_send(struct sock *sk, struct msghdr *msg, u16 *gso_size);
276277
void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst);
277278
int udp_rcv(struct sk_buff *skb);
278279
int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);

net/ipv4/udp.c

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,43 @@ int udp_push_pending_frames(struct sock *sk)
853853
}
854854
EXPORT_SYMBOL(udp_push_pending_frames);
855855

856+
static int __udp_cmsg_send(struct cmsghdr *cmsg, u16 *gso_size)
857+
{
858+
switch (cmsg->cmsg_type) {
859+
case UDP_SEGMENT:
860+
if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u16)))
861+
return -EINVAL;
862+
*gso_size = *(__u16 *)CMSG_DATA(cmsg);
863+
return 0;
864+
default:
865+
return -EINVAL;
866+
}
867+
}
868+
869+
int udp_cmsg_send(struct sock *sk, struct msghdr *msg, u16 *gso_size)
870+
{
871+
struct cmsghdr *cmsg;
872+
bool need_ip = false;
873+
int err;
874+
875+
for_each_cmsghdr(cmsg, msg) {
876+
if (!CMSG_OK(msg, cmsg))
877+
return -EINVAL;
878+
879+
if (cmsg->cmsg_level != SOL_UDP) {
880+
need_ip = true;
881+
continue;
882+
}
883+
884+
err = __udp_cmsg_send(cmsg, gso_size);
885+
if (err)
886+
return err;
887+
}
888+
889+
return need_ip;
890+
}
891+
EXPORT_SYMBOL_GPL(udp_cmsg_send);
892+
856893
int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
857894
{
858895
struct inet_sock *inet = inet_sk(sk);
@@ -941,8 +978,11 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
941978
ipc.gso_size = up->gso_size;
942979

943980
if (msg->msg_controllen) {
944-
err = ip_cmsg_send(sk, msg, &ipc, sk->sk_family == AF_INET6);
945-
if (unlikely(err)) {
981+
err = udp_cmsg_send(sk, msg, &ipc.gso_size);
982+
if (err > 0)
983+
err = ip_cmsg_send(sk, msg, &ipc,
984+
sk->sk_family == AF_INET6);
985+
if (unlikely(err < 0)) {
946986
kfree(ipc.opt);
947987
return err;
948988
}

net/ipv6/udp.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1276,7 +1276,10 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
12761276
opt->tot_len = sizeof(*opt);
12771277
ipc6.opt = opt;
12781278

1279-
err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, &ipc6, &sockc);
1279+
err = udp_cmsg_send(sk, msg, &ipc6.gso_size);
1280+
if (err > 0)
1281+
err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6,
1282+
&ipc6, &sockc);
12801283
if (err < 0) {
12811284
fl6_sock_release(flowlabel);
12821285
return err;

0 commit comments

Comments
 (0)