diff options
author | Stephen Hemminger <stephen@networkplumber.org> | 2013-06-17 17:16:12 -0400 |
---|---|---|
committer | Stephen Hemminger <stephen@networkplumber.org> | 2013-06-24 11:40:32 -0400 |
commit | 3e61aa8f0a68e6e007c223688f442be04a44b0f4 (patch) | |
tree | 2328d1a4f4e8b0c58b6a45ff14531c3644be3814 /drivers/net/vxlan.c | |
parent | 4ad169300a7350a034b86c543070aed109882a86 (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.c | 97 |
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 | */ | ||
176 | static 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 */ |
174 | static struct vxlan_sock *vxlan_find_port(struct net *net, __be16 port) | 182 | static 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 | ||
277 | static void vxlan_fdb_notify(struct vxlan_dev *vxlan, | 285 | static 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, | |||
377 | static int vxlan_fdb_append(struct vxlan_fdb *f, | 391 | static 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, | |||
472 | static void vxlan_fdb_free(struct rcu_head *head) | 485 | static 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; |
602 | skip: | ||
603 | ++idx; | ||
604 | } | 613 | } |
614 | skip: | ||
615 | ++idx; | ||
605 | } | 616 | } |
606 | } | 617 | } |
607 | 618 | out: | |
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); |