Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: preserve MSG_ZEROCOPY with forwarding

MSG_ZEROCOPY data must be copied before data is queued to local
sockets, to avoid indefinite timeout until memory release.

This test is performed by skb_orphan_frags_rx, which is called when
looping an egress skb to packet sockets, error queue or ingress path.

To preserve zerocopy for skbs that are looped to ingress but are then
forwarded to an egress device rather than delivered locally, defer
this last check until an skb enters the local IP receive path.

This is analogous to existing behavior of skb_clear_delivery_time.

Signed-off-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20250630194312.1571410-2-willemdebruijn.kernel@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Willem de Bruijn and committed by
Jakub Kicinski
d2527ad3 04b1d18c

+13 -2
-2
net/core/dev.c
··· 5937 5937 } 5938 5938 5939 5939 if (pt_prev) { 5940 - if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) 5941 - goto drop; 5942 5940 *ppt_prev = pt_prev; 5943 5941 } else { 5944 5942 drop:
+6
net/ipv4/ip_input.c
··· 226 226 227 227 static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb) 228 228 { 229 + if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) { 230 + __IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS); 231 + kfree_skb_reason(skb, SKB_DROP_REASON_NOMEM); 232 + return 0; 233 + } 234 + 229 235 skb_clear_delivery_time(skb); 230 236 __skb_pull(skb, skb_network_header_len(skb)); 231 237
+7
net/ipv6/ip6_input.c
··· 478 478 479 479 static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb) 480 480 { 481 + if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) { 482 + __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), 483 + IPSTATS_MIB_INDISCARDS); 484 + kfree_skb_reason(skb, SKB_DROP_REASON_NOMEM); 485 + return 0; 486 + } 487 + 481 488 skb_clear_delivery_time(skb); 482 489 ip6_protocol_deliver_rcu(net, skb, 0, false); 483 490