tipc: add dst_cache support for udp media
authorXin Long <lucien.xin@gmail.com>
Thu, 20 Jun 2019 11:03:41 +0000 (19:03 +0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 28 Jun 2019 05:36:57 +0000 (22:36 -0700)
As other udp/ip tunnels do, tipc udp media should also have a
lockless dst_cache supported on its tx path.

Here we add dst_cache into udp_replicast to support dst cache
for both rmcast and rcast, and rmcast uses ub->rcast and each
rcast uses its own node in ub->rcast.list.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Jon Maloy <jon.maloy@ericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/tipc/udp_media.c

index 1405ccc..b8962df 100644 (file)
@@ -76,6 +76,7 @@ struct udp_media_addr {
 /* struct udp_replicast - container for UDP remote addresses */
 struct udp_replicast {
        struct udp_media_addr addr;
+       struct dst_cache dst_cache;
        struct rcu_head rcu;
        struct list_head list;
 };
@@ -158,22 +159,27 @@ static int tipc_udp_addr2msg(char *msg, struct tipc_media_addr *a)
 /* tipc_send_msg - enqueue a send request */
 static int tipc_udp_xmit(struct net *net, struct sk_buff *skb,
                         struct udp_bearer *ub, struct udp_media_addr *src,
-                        struct udp_media_addr *dst)
+                        struct udp_media_addr *dst, struct dst_cache *cache)
 {
+       struct dst_entry *ndst = dst_cache_get(cache);
        int ttl, err = 0;
-       struct rtable *rt;
 
        if (dst->proto == htons(ETH_P_IP)) {
-               struct flowi4 fl = {
-                       .daddr = dst->ipv4.s_addr,
-                       .saddr = src->ipv4.s_addr,
-                       .flowi4_mark = skb->mark,
-                       .flowi4_proto = IPPROTO_UDP
-               };
-               rt = ip_route_output_key(net, &fl);
-               if (IS_ERR(rt)) {
-                       err = PTR_ERR(rt);
-                       goto tx_error;
+               struct rtable *rt = (struct rtable *)ndst;
+
+               if (!rt) {
+                       struct flowi4 fl = {
+                               .daddr = dst->ipv4.s_addr,
+                               .saddr = src->ipv4.s_addr,
+                               .flowi4_mark = skb->mark,
+                               .flowi4_proto = IPPROTO_UDP
+                       };
+                       rt = ip_route_output_key(net, &fl);
+                       if (IS_ERR(rt)) {
+                               err = PTR_ERR(rt);
+                               goto tx_error;
+                       }
+                       dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
                }
 
                ttl = ip4_dst_hoplimit(&rt->dst);
@@ -182,17 +188,19 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb,
                                    dst->port, false, true);
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
-               struct dst_entry *ndst;
-               struct flowi6 fl6 = {
-                       .flowi6_oif = ub->ifindex,
-                       .daddr = dst->ipv6,
-                       .saddr = src->ipv6,
-                       .flowi6_proto = IPPROTO_UDP
-               };
-               err = ipv6_stub->ipv6_dst_lookup(net, ub->ubsock->sk, &ndst,
-                                                &fl6);
-               if (err)
-                       goto tx_error;
+               if (!ndst) {
+                       struct flowi6 fl6 = {
+                               .flowi6_oif = ub->ifindex,
+                               .daddr = dst->ipv6,
+                               .saddr = src->ipv6,
+                               .flowi6_proto = IPPROTO_UDP
+                       };
+                       err = ipv6_stub->ipv6_dst_lookup(net, ub->ubsock->sk,
+                                                        &ndst, &fl6);
+                       if (err)
+                               goto tx_error;
+                       dst_cache_set_ip6(cache, ndst, &fl6.saddr);
+               }
                ttl = ip6_dst_hoplimit(ndst);
                err = udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb, NULL,
                                           &src->ipv6, &dst->ipv6, 0, ttl, 0,
@@ -230,7 +238,8 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
        }
 
        if (addr->broadcast != TIPC_REPLICAST_SUPPORT)
-               return tipc_udp_xmit(net, skb, ub, src, dst);
+               return tipc_udp_xmit(net, skb, ub, src, dst,
+                                    &ub->rcast.dst_cache);
 
        /* Replicast, send an skb to each configured IP address */
        list_for_each_entry_rcu(rcast, &ub->rcast.list, list) {
@@ -242,7 +251,8 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
                        goto out;
                }
 
-               err = tipc_udp_xmit(net, _skb, ub, src, &rcast->addr);
+               err = tipc_udp_xmit(net, _skb, ub, src, &rcast->addr,
+                                   &rcast->dst_cache);
                if (err)
                        goto out;
        }
@@ -286,6 +296,11 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
        if (!rcast)
                return -ENOMEM;
 
+       if (dst_cache_init(&rcast->dst_cache, GFP_ATOMIC)) {
+               kfree(rcast);
+               return -ENOMEM;
+       }
+
        memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr));
 
        if (ntohs(addr->proto) == ETH_P_IP)
@@ -742,6 +757,10 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
        tuncfg.encap_destroy = NULL;
        setup_udp_tunnel_sock(net, ub->ubsock, &tuncfg);
 
+       err = dst_cache_init(&ub->rcast.dst_cache, GFP_ATOMIC);
+       if (err)
+               goto err;
+
        /**
         * The bcast media address port is used for all peers and the ip
         * is used if it's a multicast address.
@@ -756,6 +775,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
 
        return 0;
 err:
+       dst_cache_destroy(&ub->rcast.dst_cache);
        if (ub->ubsock)
                udp_tunnel_sock_release(ub->ubsock);
        kfree(ub);
@@ -769,10 +789,12 @@ static void cleanup_bearer(struct work_struct *work)
        struct udp_replicast *rcast, *tmp;
 
        list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
+               dst_cache_destroy(&rcast->dst_cache);
                list_del_rcu(&rcast->list);
                kfree_rcu(rcast, rcu);
        }
 
+       dst_cache_destroy(&ub->rcast.dst_cache);
        if (ub->ubsock)
                udp_tunnel_sock_release(ub->ubsock);
        synchronize_net();