Skip to content

Commit 5425077

Browse files
subashab@codeaurora.orgdavem330
authored andcommitted
net: ipv6: Add early demux handler for UDP unicast
While running a single stream UDPv6 test, we observed that amount of CPU spent in NET_RX softirq was much greater than UDPv4 for an equivalent receive rate. The test here was run on an ARM64 based Android system. On further analysis with perf, we found that UDPv6 was spending significant time in the statistics netfilter targets which did socket lookup per packet. These statistics rules perform a lookup when there is no socket associated with the skb. Since there are multiple instances of these rules based on UID, there will be equal number of lookups per skb. By introducing early demux for UDPv6, we avoid the redundant lookups. This also helped to improve the performance (800Mbps -> 870Mbps) on a CPU limited system in a single stream UDPv6 receive test with 1450 byte sized datagrams using iperf. v1->v2: Use IPv6 cookie to validate dst instead of 0 as suggested by Eric Signed-off-by: Subash Abhinov Kasiviswanathan <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 49b4997 commit 5425077

File tree

1 file changed

+59
-0
lines changed

1 file changed

+59
-0
lines changed

net/ipv6/udp.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,64 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
864864
return 0;
865865
}
866866

867+
static struct sock *__udp6_lib_demux_lookup(struct net *net,
868+
__be16 loc_port, const struct in6_addr *loc_addr,
869+
__be16 rmt_port, const struct in6_addr *rmt_addr,
870+
int dif)
871+
{
872+
struct sock *sk;
873+
874+
rcu_read_lock();
875+
sk = __udp6_lib_lookup(net, rmt_addr, rmt_port, loc_addr, loc_port,
876+
dif, &udp_table, NULL);
877+
if (sk && !atomic_inc_not_zero(&sk->sk_refcnt))
878+
sk = NULL;
879+
rcu_read_unlock();
880+
881+
return sk;
882+
}
883+
884+
static void udp_v6_early_demux(struct sk_buff *skb)
885+
{
886+
struct net *net = dev_net(skb->dev);
887+
const struct udphdr *uh;
888+
struct sock *sk;
889+
struct dst_entry *dst;
890+
int dif = skb->dev->ifindex;
891+
892+
if (!pskb_may_pull(skb, skb_transport_offset(skb) +
893+
sizeof(struct udphdr)))
894+
return;
895+
896+
uh = udp_hdr(skb);
897+
898+
if (skb->pkt_type == PACKET_HOST)
899+
sk = __udp6_lib_demux_lookup(net, uh->dest,
900+
&ipv6_hdr(skb)->daddr,
901+
uh->source, &ipv6_hdr(skb)->saddr,
902+
dif);
903+
else
904+
return;
905+
906+
if (!sk)
907+
return;
908+
909+
skb->sk = sk;
910+
skb->destructor = sock_efree;
911+
dst = READ_ONCE(sk->sk_rx_dst);
912+
913+
if (dst)
914+
dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
915+
if (dst) {
916+
if (dst->flags & DST_NOCACHE) {
917+
if (likely(atomic_inc_not_zero(&dst->__refcnt)))
918+
skb_dst_set(skb, dst);
919+
} else {
920+
skb_dst_set_noref(skb, dst);
921+
}
922+
}
923+
}
924+
867925
static __inline__ int udpv6_rcv(struct sk_buff *skb)
868926
{
869927
return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP);
@@ -1379,6 +1437,7 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
13791437
#endif
13801438

13811439
static const struct inet6_protocol udpv6_protocol = {
1440+
.early_demux = udp_v6_early_demux,
13821441
.handler = udpv6_rcv,
13831442
.err_handler = udpv6_err,
13841443
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,

0 commit comments

Comments
 (0)