In case of UDP traffic with datagram length below MTU this give about 3% performance increase when tunneling over ipv4 and about 70% when tunneling over ipv6.
Signed-off-by: Paolo Abeni <pab...@redhat.com> Suggested-and-acked-by: Hannes Frederic Sowa <han...@stressinduktion.org> --- drivers/net/vxlan.c | 84 ++++++++++++++++++++++++++++++++++++++++------------- include/net/vxlan.h | 1 + 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 65f5247..59c1337 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -480,6 +480,8 @@ static int vxlan_fdb_replace(struct vxlan_fdb *f, rd = list_first_entry_or_null(&f->remotes, struct vxlan_rdst, list); if (!rd) return 0; + + dst_cache_reset(&rd->dst_cache); rd->remote_ip = *ip; rd->remote_port = port; rd->remote_vni = vni; @@ -501,6 +503,12 @@ static int vxlan_fdb_append(struct vxlan_fdb *f, rd = kmalloc(sizeof(*rd), GFP_ATOMIC); if (rd == NULL) return -ENOBUFS; + + if (dst_cache_init(&rd->dst_cache, GFP_ATOMIC)) { + kfree(rd); + return -ENOBUFS; + } + rd->remote_ip = *ip; rd->remote_port = port; rd->remote_vni = vni; @@ -749,8 +757,10 @@ static void vxlan_fdb_free(struct rcu_head *head) struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu); struct vxlan_rdst *rd, *nd; - list_for_each_entry_safe(rd, nd, &f->remotes, list) + list_for_each_entry_safe(rd, nd, &f->remotes, list) { + dst_cache_destroy(&rd->dst_cache); kfree(rd); + } kfree(f); } @@ -1857,6 +1867,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct rtable *rt = NULL; const struct iphdr *old_iph; union vxlan_addr *dst; + struct dst_entry *ndst; union vxlan_addr remote_ip; struct vxlan_metadata _md; struct vxlan_metadata *md = &_md; @@ -1866,7 +1877,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, __u8 tos, ttl; int err; u32 flags = vxlan->flags; - bool udp_sum = false; + bool use_cache, udp_sum = false; bool xnet = !net_eq(vxlan->net, dev_net(vxlan->dev)); info = skb_tunnel_info(skb); @@ -1907,9 +1918,17 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ttl = 1; tos = vxlan->cfg.tos; - if (tos == 1) + if (tos == 1) { tos = ip_tunnel_get_dsfield(old_iph, skb); + /* the dst cache can be used only if the routing decision + * do not take in account per frame info, i.e. this packet tos + */ + use_cache = false; + } else { + use_cache = true; + } + src_port = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min, vxlan->cfg.port_max, true); @@ -1917,6 +1936,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ttl = info->key.ttl; tos = info->key.tos; udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM); + use_cache = true; if (info->options_len) md = ip_tunnel_info_opts(info); @@ -1938,14 +1958,27 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, udp_sum = !!(flags & VXLAN_F_UDP_CSUM); } - rt = vxlan_get_route(vxlan, skb, - rdst ? rdst->remote_ifindex : 0, tos, - dst->sin.sin_addr.s_addr, &saddr); - if (IS_ERR(rt)) { - netdev_dbg(dev, "no route to %pI4\n", - &dst->sin.sin_addr.s_addr); - dev->stats.tx_carrier_errors++; - goto tx_error; + use_cache = use_cache && rdst && !skb->mark; + if (use_cache) + rt = dst_cache_get_ip4(&rdst->dst_cache, &saddr); + else + rt = NULL; + + if (!rt) { + rt = vxlan_get_route(vxlan, skb, + rdst ? rdst->remote_ifindex : 0, + tos, dst->sin.sin_addr.s_addr, + &saddr); + if (IS_ERR(rt)) { + netdev_dbg(dev, "no route to %pI4\n", + &dst->sin.sin_addr.s_addr); + dev->stats.tx_carrier_errors++; + goto tx_error; + } + + if (use_cache) + dst_cache_set_ip4(&rdst->dst_cache, &rt->dst, + saddr); } if (rt->dst.dev == dev) { @@ -1982,7 +2015,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, src_port, dst_port, xnet, !udp_sum); #if IS_ENABLED(CONFIG_IPV6) } else { - struct dst_entry *ndst; struct in6_addr saddr; u32 rt6i_flags; @@ -1990,14 +2022,26 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, goto drop; sk = vxlan->vn6_sock->sock->sk; - ndst = vxlan6_get_route(vxlan, skb, - rdst ? rdst->remote_ifindex : 0, - &dst->sin6.sin6_addr, &saddr); - if (IS_ERR(ndst)) { - netdev_dbg(dev, "no route to %pI6\n", - &dst->sin6.sin6_addr); - dev->stats.tx_carrier_errors++; - goto tx_error; + use_cache = rdst && !skb->mark; + if (use_cache) + ndst = dst_cache_get_ip6(&rdst->dst_cache, &saddr); + else + ndst = NULL; + + if (!ndst) { + ndst = vxlan6_get_route(vxlan, skb, + rdst ? rdst->remote_ifindex : 0, + &dst->sin6.sin6_addr, &saddr); + if (IS_ERR(ndst)) { + netdev_dbg(dev, "no route to %pI6\n", + &dst->sin6.sin6_addr); + dev->stats.tx_carrier_errors++; + goto tx_error; + } + + if (use_cache) + dst_cache_set_ip6(&rdst->dst_cache, ndst, + &saddr); } if (ndst->dev == dev) { diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 25bd919..b314e4a 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -148,6 +148,7 @@ struct vxlan_rdst { u32 remote_ifindex; struct list_head list; struct rcu_head rcu; + struct dst_cache dst_cache; }; struct vxlan_config { -- 1.8.3.1