aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/vxlan.c
diff options
context:
space:
mode:
authorStephen Hemminger <stephen@networkplumber.org>2013-06-17 17:16:12 -0400
committerStephen Hemminger <stephen@networkplumber.org>2013-06-24 11:40:32 -0400
commit3e61aa8f0a68e6e007c223688f442be04a44b0f4 (patch)
tree2328d1a4f4e8b0c58b6a45ff14531c3644be3814 /drivers/net/vxlan.c
parent4ad169300a7350a034b86c543070aed109882a86 (diff)
vxlan: convert remotes list to list_rcu
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>
Diffstat (limited to 'drivers/net/vxlan.c')
-rw-r--r--drivers/net/vxlan.c97
1 files changed, 55 insertions, 42 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index e65241c3d176..117b7fa6f33b 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -102,7 +102,7 @@ struct vxlan_rdst {
102 __be16 remote_port; 102 __be16 remote_port;
103 u32 remote_vni; 103 u32 remote_vni;
104 u32 remote_ifindex; 104 u32 remote_ifindex;
105 struct vxlan_rdst *remote_next; 105 struct list_head list;
106}; 106};
107 107
108/* Forwarding table entry */ 108/* Forwarding table entry */
@@ -111,7 +111,7 @@ struct vxlan_fdb {
111 struct rcu_head rcu; 111 struct rcu_head rcu;
112 unsigned long updated; /* jiffies */ 112 unsigned long updated; /* jiffies */
113 unsigned long used; 113 unsigned long used;
114 struct vxlan_rdst remote; 114 struct list_head remotes;
115 u16 state; /* see ndm_state */ 115 u16 state; /* see ndm_state */
116 u8 flags; /* see ndm_flags */ 116 u8 flags; /* see ndm_flags */
117 u8 eth_addr[ETH_ALEN]; 117 u8 eth_addr[ETH_ALEN];
@@ -170,6 +170,14 @@ static inline struct hlist_head *vs_head(struct net *net, __be16 port)
170 return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)]; 170 return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)];
171} 171}
172 172
173/* First remote destination for a forwarding entry.
174 * Guaranteed to be non-NULL because remotes are never deleted.
175 */
176static inline struct vxlan_rdst *first_remote(struct vxlan_fdb *fdb)
177{
178 return list_first_or_null_rcu(&fdb->remotes, struct vxlan_rdst, list);
179}
180
173/* Find VXLAN socket based on network namespace and UDP port */ 181/* Find VXLAN socket based on network namespace and UDP port */
174static struct vxlan_sock *vxlan_find_port(struct net *net, __be16 port) 182static struct vxlan_sock *vxlan_find_port(struct net *net, __be16 port)
175{ 183{
@@ -275,7 +283,7 @@ static inline size_t vxlan_nlmsg_size(void)
275} 283}
276 284
277static void vxlan_fdb_notify(struct vxlan_dev *vxlan, 285static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
278 const struct vxlan_fdb *fdb, int type) 286 struct vxlan_fdb *fdb, int type)
279{ 287{
280 struct net *net = dev_net(vxlan->dev); 288 struct net *net = dev_net(vxlan->dev);
281 struct sk_buff *skb; 289 struct sk_buff *skb;
@@ -285,7 +293,7 @@ static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
285 if (skb == NULL) 293 if (skb == NULL)
286 goto errout; 294 goto errout;
287 295
288 err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, &fdb->remote); 296 err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, first_remote(fdb));
289 if (err < 0) { 297 if (err < 0) {
290 /* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */ 298 /* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */
291 WARN_ON(err == -EMSGSIZE); 299 WARN_ON(err == -EMSGSIZE);
@@ -304,11 +312,16 @@ static void vxlan_ip_miss(struct net_device *dev, __be32 ipa)
304{ 312{
305 struct vxlan_dev *vxlan = netdev_priv(dev); 313 struct vxlan_dev *vxlan = netdev_priv(dev);
306 struct vxlan_fdb f; 314 struct vxlan_fdb f;
315 struct vxlan_rdst remote;
307 316
308 memset(&f, 0, sizeof f); 317 memset(&f, 0, sizeof f);
309 f.state = NUD_STALE; 318 f.state = NUD_STALE;
310 f.remote.remote_ip = ipa; /* goes to NDA_DST */ 319
311 f.remote.remote_vni = VXLAN_N_VID; 320 remote.remote_ip = ipa; /* goes to NDA_DST */
321 remote.remote_vni = VXLAN_N_VID;
322
323 INIT_LIST_HEAD(&f.remotes);
324 list_add_rcu(&remote.list, &f.remotes);
312 325
313 vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH); 326 vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH);
314} 327}
@@ -318,6 +331,7 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
318 struct vxlan_fdb f; 331 struct vxlan_fdb f;
319 332
320 memset(&f, 0, sizeof f); 333 memset(&f, 0, sizeof f);
334 INIT_LIST_HEAD(&f.remotes);
321 f.state = NUD_STALE; 335 f.state = NUD_STALE;
322 memcpy(f.eth_addr, eth_addr, ETH_ALEN); 336 memcpy(f.eth_addr, eth_addr, ETH_ALEN);
323 337
@@ -377,17 +391,17 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
377static int vxlan_fdb_append(struct vxlan_fdb *f, 391static int vxlan_fdb_append(struct vxlan_fdb *f,
378 __be32 ip, __be16 port, __u32 vni, __u32 ifindex) 392 __be32 ip, __be16 port, __u32 vni, __u32 ifindex)
379{ 393{
380 struct vxlan_rdst *rd_prev, *rd; 394 struct vxlan_rdst *rd;
381 395
382 rd_prev = NULL; 396 /* protected by vxlan->hash_lock */
383 for (rd = &f->remote; rd; rd = rd->remote_next) { 397 list_for_each_entry(rd, &f->remotes, list) {
384 if (rd->remote_ip == ip && 398 if (rd->remote_ip == ip &&
385 rd->remote_port == port && 399 rd->remote_port == port &&
386 rd->remote_vni == vni && 400 rd->remote_vni == vni &&
387 rd->remote_ifindex == ifindex) 401 rd->remote_ifindex == ifindex)
388 return 0; 402 return 0;
389 rd_prev = rd;
390 } 403 }
404
391 rd = kmalloc(sizeof(*rd), GFP_ATOMIC); 405 rd = kmalloc(sizeof(*rd), GFP_ATOMIC);
392 if (rd == NULL) 406 if (rd == NULL)
393 return -ENOBUFS; 407 return -ENOBUFS;
@@ -395,8 +409,9 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
395 rd->remote_port = port; 409 rd->remote_port = port;
396 rd->remote_vni = vni; 410 rd->remote_vni = vni;
397 rd->remote_ifindex = ifindex; 411 rd->remote_ifindex = ifindex;
398 rd->remote_next = NULL; 412
399 rd_prev->remote_next = rd; 413 list_add_tail_rcu(&rd->list, &f->remotes);
414
400 return 1; 415 return 1;
401} 416}
402 417
@@ -448,16 +463,14 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
448 return -ENOMEM; 463 return -ENOMEM;
449 464
450 notify = 1; 465 notify = 1;
451 f->remote.remote_ip = ip;
452 f->remote.remote_port = port;
453 f->remote.remote_vni = vni;
454 f->remote.remote_ifindex = ifindex;
455 f->remote.remote_next = NULL;
456 f->state = state; 466 f->state = state;
457 f->flags = ndm_flags; 467 f->flags = ndm_flags;
458 f->updated = f->used = jiffies; 468 f->updated = f->used = jiffies;
469 INIT_LIST_HEAD(&f->remotes);
459 memcpy(f->eth_addr, mac, ETH_ALEN); 470 memcpy(f->eth_addr, mac, ETH_ALEN);
460 471
472 vxlan_fdb_append(f, ip, port, vni, ifindex);
473
461 ++vxlan->addrcnt; 474 ++vxlan->addrcnt;
462 hlist_add_head_rcu(&f->hlist, 475 hlist_add_head_rcu(&f->hlist,
463 vxlan_fdb_head(vxlan, mac)); 476 vxlan_fdb_head(vxlan, mac));
@@ -472,13 +485,10 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
472static void vxlan_fdb_free(struct rcu_head *head) 485static void vxlan_fdb_free(struct rcu_head *head)
473{ 486{
474 struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu); 487 struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu);
488 struct vxlan_rdst *rd, *nd;
475 489
476 while (f->remote.remote_next) { 490 list_for_each_entry_safe(rd, nd, &f->remotes, list)
477 struct vxlan_rdst *rd = f->remote.remote_next;
478
479 f->remote.remote_next = rd->remote_next;
480 kfree(rd); 491 kfree(rd);
481 }
482 kfree(f); 492 kfree(f);
483} 493}
484 494
@@ -588,23 +598,24 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
588 598
589 hlist_for_each_entry_rcu(f, &vxlan->fdb_head[h], hlist) { 599 hlist_for_each_entry_rcu(f, &vxlan->fdb_head[h], hlist) {
590 struct vxlan_rdst *rd; 600 struct vxlan_rdst *rd;
591 for (rd = &f->remote; rd; rd = rd->remote_next) {
592 if (idx < cb->args[0])
593 goto skip;
594 601
602 if (idx < cb->args[0])
603 goto skip;
604
605 list_for_each_entry_rcu(rd, &f->remotes, list) {
595 err = vxlan_fdb_info(skb, vxlan, f, 606 err = vxlan_fdb_info(skb, vxlan, f,
596 NETLINK_CB(cb->skb).portid, 607 NETLINK_CB(cb->skb).portid,
597 cb->nlh->nlmsg_seq, 608 cb->nlh->nlmsg_seq,
598 RTM_NEWNEIGH, 609 RTM_NEWNEIGH,
599 NLM_F_MULTI, rd); 610 NLM_F_MULTI, rd);
600 if (err < 0) 611 if (err < 0)
601 break; 612 goto out;
602skip:
603 ++idx;
604 } 613 }
614skip:
615 ++idx;
605 } 616 }
606 } 617 }
607 618out:
608 return idx; 619 return idx;
609} 620}
610 621
@@ -620,7 +631,9 @@ static bool vxlan_snoop(struct net_device *dev,
620 631
621 f = vxlan_find_mac(vxlan, src_mac); 632 f = vxlan_find_mac(vxlan, src_mac);
622 if (likely(f)) { 633 if (likely(f)) {
623 if (likely(f->remote.remote_ip == src_ip)) 634 struct vxlan_rdst *rdst = first_remote(f);
635
636 if (likely(rdst->remote_ip == src_ip))
624 return false; 637 return false;
625 638
626 /* Don't migrate static entries, drop packets */ 639 /* Don't migrate static entries, drop packets */
@@ -630,9 +643,9 @@ static bool vxlan_snoop(struct net_device *dev,
630 if (net_ratelimit()) 643 if (net_ratelimit())
631 netdev_info(dev, 644 netdev_info(dev,
632 "%pM migrated from %pI4 to %pI4\n", 645 "%pM migrated from %pI4 to %pI4\n",
633 src_mac, &f->remote.remote_ip, &src_ip); 646 src_mac, &rdst->remote_ip, &src_ip);
634 647
635 f->remote.remote_ip = src_ip; 648 rdst->remote_ip = src_ip;
636 f->updated = jiffies; 649 f->updated = jiffies;
637 vxlan_fdb_notify(vxlan, f, RTM_NEWNEIGH); 650 vxlan_fdb_notify(vxlan, f, RTM_NEWNEIGH);
638 } else { 651 } else {
@@ -866,7 +879,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
866 } 879 }
867 880
868 f = vxlan_find_mac(vxlan, n->ha); 881 f = vxlan_find_mac(vxlan, n->ha);
869 if (f && f->remote.remote_ip == htonl(INADDR_ANY)) { 882 if (f && first_remote(f)->remote_ip == htonl(INADDR_ANY)) {
870 /* bridge-local neighbor */ 883 /* bridge-local neighbor */
871 neigh_release(n); 884 neigh_release(n);
872 goto out; 885 goto out;
@@ -1165,17 +1178,17 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
1165 (vxlan->flags & VXLAN_F_L2MISS) && 1178 (vxlan->flags & VXLAN_F_L2MISS) &&
1166 !is_multicast_ether_addr(eth->h_dest)) 1179 !is_multicast_ether_addr(eth->h_dest))
1167 vxlan_fdb_miss(vxlan, eth->h_dest); 1180 vxlan_fdb_miss(vxlan, eth->h_dest);
1168 } else 1181 } else {
1169 rdst0 = &f->remote; 1182 rdst = rdst0 = first_remote(f);
1170
1171 1183
1172 /* if there are multiple destinations, send copies */ 1184 /* if there are multiple destinations, send copies */
1173 for (rdst = rdst0->remote_next; rdst; rdst = rdst->remote_next) { 1185 list_for_each_entry_continue_rcu(rdst, &f->remotes, list) {
1174 struct sk_buff *skb1; 1186 struct sk_buff *skb1;
1175 1187
1176 skb1 = skb_clone(skb, GFP_ATOMIC); 1188 skb1 = skb_clone(skb, GFP_ATOMIC);
1177 if (skb1) 1189 if (skb1)
1178 vxlan_xmit_one(skb1, dev, rdst, did_rsc); 1190 vxlan_xmit_one(skb1, dev, rdst, did_rsc);
1191 }
1179 } 1192 }
1180 1193
1181 vxlan_xmit_one(skb, dev, rdst0, did_rsc); 1194 vxlan_xmit_one(skb, dev, rdst0, did_rsc);