vxlan: convert remotes list to list_rcu
authorStephen Hemminger <stephen@networkplumber.org>
Mon, 17 Jun 2013 21:16:12 +0000 (14:16 -0700)
committerStephen Hemminger <stephen@networkplumber.org>
Mon, 24 Jun 2013 15:40:32 +0000 (08:40 -0700)
Based on initial work by Mike Rapoport <mike.rapoport@ravellosystems.com>
Use list macros and RCU for tracking multiple remotes.

Note: this code assumes list always has at least one entry,
because delete is not supported.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
drivers/net/vxlan.c

index e65241c3d17629e86203f479cef84a1a25ceb2bd..117b7fa6f33bee60491faba70b8964c97b193164 100644 (file)
@@ -102,7 +102,7 @@ struct vxlan_rdst {
        __be16                   remote_port;
        u32                      remote_vni;
        u32                      remote_ifindex;
-       struct vxlan_rdst       *remote_next;
+       struct list_head         list;
 };
 
 /* Forwarding table entry */
@@ -111,7 +111,7 @@ struct vxlan_fdb {
        struct rcu_head   rcu;
        unsigned long     updated;      /* jiffies */
        unsigned long     used;
-       struct vxlan_rdst remote;
+       struct list_head  remotes;
        u16               state;        /* see ndm_state */
        u8                flags;        /* see ndm_flags */
        u8                eth_addr[ETH_ALEN];
@@ -170,6 +170,14 @@ static inline struct hlist_head *vs_head(struct net *net, __be16 port)
        return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)];
 }
 
+/* First remote destination for a forwarding entry.
+ * Guaranteed to be non-NULL because remotes are never deleted.
+ */
+static inline struct vxlan_rdst *first_remote(struct vxlan_fdb *fdb)
+{
+       return list_first_or_null_rcu(&fdb->remotes, struct vxlan_rdst, list);
+}
+
 /* Find VXLAN socket based on network namespace and UDP port */
 static struct vxlan_sock *vxlan_find_port(struct net *net, __be16 port)
 {
@@ -275,7 +283,7 @@ static inline size_t vxlan_nlmsg_size(void)
 }
 
 static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
-                            const struct vxlan_fdb *fdb, int type)
+                            struct vxlan_fdb *fdb, int type)
 {
        struct net *net = dev_net(vxlan->dev);
        struct sk_buff *skb;
@@ -285,7 +293,7 @@ static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
        if (skb == NULL)
                goto errout;
 
-       err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, &fdb->remote);
+       err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, first_remote(fdb));
        if (err < 0) {
                /* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */
                WARN_ON(err == -EMSGSIZE);
@@ -304,11 +312,16 @@ static void vxlan_ip_miss(struct net_device *dev, __be32 ipa)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_fdb f;
+       struct vxlan_rdst remote;
 
        memset(&f, 0, sizeof f);
        f.state = NUD_STALE;
-       f.remote.remote_ip = ipa; /* goes to NDA_DST */
-       f.remote.remote_vni = VXLAN_N_VID;
+
+       remote.remote_ip = ipa; /* goes to NDA_DST */
+       remote.remote_vni = VXLAN_N_VID;
+
+       INIT_LIST_HEAD(&f.remotes);
+       list_add_rcu(&remote.list, &f.remotes);
 
        vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH);
 }
@@ -318,6 +331,7 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
        struct vxlan_fdb        f;
 
        memset(&f, 0, sizeof f);
+       INIT_LIST_HEAD(&f.remotes);
        f.state = NUD_STALE;
        memcpy(f.eth_addr, eth_addr, ETH_ALEN);
 
@@ -377,17 +391,17 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
 static int vxlan_fdb_append(struct vxlan_fdb *f,
                            __be32 ip, __be16 port, __u32 vni, __u32 ifindex)
 {
-       struct vxlan_rdst *rd_prev, *rd;
+       struct vxlan_rdst *rd;
 
-       rd_prev = NULL;
-       for (rd = &f->remote; rd; rd = rd->remote_next) {
+       /* protected by vxlan->hash_lock */
+       list_for_each_entry(rd, &f->remotes, list) {
                if (rd->remote_ip == ip &&
                    rd->remote_port == port &&
                    rd->remote_vni == vni &&
                    rd->remote_ifindex == ifindex)
                        return 0;
-               rd_prev = rd;
        }
+
        rd = kmalloc(sizeof(*rd), GFP_ATOMIC);
        if (rd == NULL)
                return -ENOBUFS;
@@ -395,8 +409,9 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
        rd->remote_port = port;
        rd->remote_vni = vni;
        rd->remote_ifindex = ifindex;
-       rd->remote_next = NULL;
-       rd_prev->remote_next = rd;
+
+       list_add_tail_rcu(&rd->list, &f->remotes);
+
        return 1;
 }
 
@@ -448,16 +463,14 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
                        return -ENOMEM;
 
                notify = 1;
-               f->remote.remote_ip = ip;
-               f->remote.remote_port = port;
-               f->remote.remote_vni = vni;
-               f->remote.remote_ifindex = ifindex;
-               f->remote.remote_next = NULL;
                f->state = state;
                f->flags = ndm_flags;
                f->updated = f->used = jiffies;
+               INIT_LIST_HEAD(&f->remotes);
                memcpy(f->eth_addr, mac, ETH_ALEN);
 
+               vxlan_fdb_append(f, ip, port, vni, ifindex);
+
                ++vxlan->addrcnt;
                hlist_add_head_rcu(&f->hlist,
                                   vxlan_fdb_head(vxlan, mac));
@@ -472,13 +485,10 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
 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;
 
-       while (f->remote.remote_next) {
-               struct vxlan_rdst *rd = f->remote.remote_next;
-
-               f->remote.remote_next = rd->remote_next;
+       list_for_each_entry_safe(rd, nd, &f->remotes, list)
                kfree(rd);
-       }
        kfree(f);
 }
 
@@ -588,23 +598,24 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
 
                hlist_for_each_entry_rcu(f, &vxlan->fdb_head[h], hlist) {
                        struct vxlan_rdst *rd;
-                       for (rd = &f->remote; rd; rd = rd->remote_next) {
-                               if (idx < cb->args[0])
-                                       goto skip;
 
+                       if (idx < cb->args[0])
+                               goto skip;
+
+                       list_for_each_entry_rcu(rd, &f->remotes, list) {
                                err = vxlan_fdb_info(skb, vxlan, f,
                                                     NETLINK_CB(cb->skb).portid,
                                                     cb->nlh->nlmsg_seq,
                                                     RTM_NEWNEIGH,
                                                     NLM_F_MULTI, rd);
                                if (err < 0)
-                                       break;
-skip:
-                               ++idx;
+                                       goto out;
                        }
+skip:
+                       ++idx;
                }
        }
-
+out:
        return idx;
 }
 
@@ -620,7 +631,9 @@ static bool vxlan_snoop(struct net_device *dev,
 
        f = vxlan_find_mac(vxlan, src_mac);
        if (likely(f)) {
-               if (likely(f->remote.remote_ip == src_ip))
+               struct vxlan_rdst *rdst = first_remote(f);
+
+               if (likely(rdst->remote_ip == src_ip))
                        return false;
 
                /* Don't migrate static entries, drop packets */
@@ -630,9 +643,9 @@ static bool vxlan_snoop(struct net_device *dev,
                if (net_ratelimit())
                        netdev_info(dev,
                                    "%pM migrated from %pI4 to %pI4\n",
-                                   src_mac, &f->remote.remote_ip, &src_ip);
+                                   src_mac, &rdst->remote_ip, &src_ip);
 
-               f->remote.remote_ip = src_ip;
+               rdst->remote_ip = src_ip;
                f->updated = jiffies;
                vxlan_fdb_notify(vxlan, f, RTM_NEWNEIGH);
        } else {
@@ -866,7 +879,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
                }
 
                f = vxlan_find_mac(vxlan, n->ha);
-               if (f && f->remote.remote_ip == htonl(INADDR_ANY)) {
+               if (f && first_remote(f)->remote_ip == htonl(INADDR_ANY)) {
                        /* bridge-local neighbor */
                        neigh_release(n);
                        goto out;
@@ -1165,17 +1178,17 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
                    (vxlan->flags & VXLAN_F_L2MISS) &&
                    !is_multicast_ether_addr(eth->h_dest))
                        vxlan_fdb_miss(vxlan, eth->h_dest);
-       } else
-               rdst0 = &f->remote;
-
+       } else {
+               rdst = rdst0 = first_remote(f);
 
-       /* if there are multiple destinations, send copies */
-       for (rdst = rdst0->remote_next; rdst; rdst = rdst->remote_next) {
-               struct sk_buff *skb1;
+               /* if there are multiple destinations, send copies */
+               list_for_each_entry_continue_rcu(rdst, &f->remotes, list) {
+                       struct sk_buff *skb1;
 
-               skb1 = skb_clone(skb, GFP_ATOMIC);
-               if (skb1)
-                       vxlan_xmit_one(skb1, dev, rdst, did_rsc);
+                       skb1 = skb_clone(skb, GFP_ATOMIC);
+                       if (skb1)
+                               vxlan_xmit_one(skb1, dev, rdst, did_rsc);
+               }
        }
 
        vxlan_xmit_one(skb, dev, rdst0, did_rsc);