Make ipv4/udp to use ubuf_info passed in struct msghdr if it was specified. Signed-off-by: Pavel Begunkov <asml.silence@xxxxxxxxx> --- include/net/ip.h | 3 ++- net/ipv4/ip_output.c | 31 ++++++++++++++++++++++++------- net/ipv4/udp.c | 2 +- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/include/net/ip.h b/include/net/ip.h index b71e88507c4a..e9c61b83a770 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -232,7 +232,8 @@ struct sk_buff *ip_make_skb(struct sock *sk, struct flowi4 *fl4, int len, int odd, struct sk_buff *skb), void *from, int length, int transhdrlen, struct ipcm_cookie *ipc, struct rtable **rtp, - struct inet_cork *cork, unsigned int flags); + struct inet_cork *cork, unsigned int flags, + struct ubuf_info *uarg); int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 9bca57ef8b83..f9aab355d283 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -948,10 +948,10 @@ static int __ip_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), void *from, int length, int transhdrlen, - unsigned int flags) + unsigned int flags, + struct ubuf_info *uarg) { struct inet_sock *inet = inet_sk(sk); - struct ubuf_info *uarg = NULL; struct sk_buff *skb; struct ip_options *opt = cork->opt; @@ -967,6 +967,7 @@ static int __ip_append_data(struct sock *sk, unsigned int wmem_alloc_delta = 0; bool paged, extra_uref = false; u32 tskey = 0; + bool zc = false; skb = skb_peek_tail(queue); @@ -1001,7 +1002,21 @@ static int __ip_append_data(struct sock *sk, (!exthdrlen || (rt->dst.dev->features & NETIF_F_HW_ESP_TX_CSUM))) csummode = CHECKSUM_PARTIAL; - if (flags & MSG_ZEROCOPY && length && sock_flag(sk, SOCK_ZEROCOPY)) { + if (uarg) { + if (skb_zcopy(skb) && uarg != skb_zcopy(skb)) + return -EINVAL; + + /* If it's not zerocopy, just drop uarg, the caller should + * be able to handle it. + */ + if (rt->dst.dev->features & NETIF_F_SG && + csummode == CHECKSUM_PARTIAL) { + paged = true; + zc = true; + } else { + uarg = NULL; + } + } else if (flags & MSG_ZEROCOPY && length && sock_flag(sk, SOCK_ZEROCOPY)) { uarg = msg_zerocopy_realloc(sk, length, skb_zcopy(skb)); if (!uarg) return -ENOBUFS; @@ -1009,6 +1024,7 @@ static int __ip_append_data(struct sock *sk, if (rt->dst.dev->features & NETIF_F_SG && csummode == CHECKSUM_PARTIAL) { paged = true; + zc = true; } else { uarg->zerocopy = 0; skb_zcopy_set(skb, uarg, &extra_uref); @@ -1172,7 +1188,7 @@ static int __ip_append_data(struct sock *sk, err = -EFAULT; goto error; } - } else if (!uarg || !uarg->zerocopy) { + } else if (!zc) { int i = skb_shinfo(skb)->nr_frags; err = -ENOMEM; @@ -1309,7 +1325,7 @@ int ip_append_data(struct sock *sk, struct flowi4 *fl4, return __ip_append_data(sk, fl4, &sk->sk_write_queue, &inet->cork.base, sk_page_frag(sk), getfrag, - from, length, transhdrlen, flags); + from, length, transhdrlen, flags, NULL); } ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, @@ -1601,7 +1617,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, - struct inet_cork *cork, unsigned int flags) + struct inet_cork *cork, unsigned int flags, + struct ubuf_info *uarg) { struct sk_buff_head queue; int err; @@ -1620,7 +1637,7 @@ struct sk_buff *ip_make_skb(struct sock *sk, err = __ip_append_data(sk, fl4, &queue, cork, ¤t->task_frag, getfrag, - from, length, transhdrlen, flags); + from, length, transhdrlen, flags, uarg); if (err) { __ip_flush_pending_frames(sk, &queue, cork); return ERR_PTR(err); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 8bcecdd6aeda..8c514bff48d4 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1247,7 +1247,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) skb = ip_make_skb(sk, fl4, getfrag, msg, ulen, sizeof(struct udphdr), &ipc, &rt, - &cork, msg->msg_flags); + &cork, msg->msg_flags, msg->msg_ubuf); err = PTR_ERR(skb); if (!IS_ERR_OR_NULL(skb)) err = udp_send_skb(skb, fl4, &cork); -- 2.34.0