diff options
Diffstat (limited to 'drivers/net/vxlan.c')
-rw-r--r-- | drivers/net/vxlan.c | 59 |
1 files changed, 41 insertions, 18 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index a5ba8dd7e6be..767f7af3bd40 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c | |||
@@ -136,7 +136,8 @@ struct vxlan_dev { | |||
136 | u32 flags; /* VXLAN_F_* below */ | 136 | u32 flags; /* VXLAN_F_* below */ |
137 | 137 | ||
138 | struct work_struct sock_work; | 138 | struct work_struct sock_work; |
139 | struct work_struct igmp_work; | 139 | struct work_struct igmp_join; |
140 | struct work_struct igmp_leave; | ||
140 | 141 | ||
141 | unsigned long age_interval; | 142 | unsigned long age_interval; |
142 | struct timer_list age_timer; | 143 | struct timer_list age_timer; |
@@ -736,7 +737,6 @@ static bool vxlan_snoop(struct net_device *dev, | |||
736 | return false; | 737 | return false; |
737 | } | 738 | } |
738 | 739 | ||
739 | |||
740 | /* See if multicast group is already in use by other ID */ | 740 | /* See if multicast group is already in use by other ID */ |
741 | static bool vxlan_group_used(struct vxlan_net *vn, __be32 remote_ip) | 741 | static bool vxlan_group_used(struct vxlan_net *vn, __be32 remote_ip) |
742 | { | 742 | { |
@@ -770,12 +770,13 @@ static void vxlan_sock_release(struct vxlan_net *vn, struct vxlan_sock *vs) | |||
770 | queue_work(vxlan_wq, &vs->del_work); | 770 | queue_work(vxlan_wq, &vs->del_work); |
771 | } | 771 | } |
772 | 772 | ||
773 | /* Callback to update multicast group membership. | 773 | /* Callback to update multicast group membership when first VNI on |
774 | * Scheduled when vxlan goes up/down. | 774 | * multicast asddress is brought up |
775 | * Done as workqueue because ip_mc_join_group acquires RTNL. | ||
775 | */ | 776 | */ |
776 | static void vxlan_igmp_work(struct work_struct *work) | 777 | static void vxlan_igmp_join(struct work_struct *work) |
777 | { | 778 | { |
778 | struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, igmp_work); | 779 | struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, igmp_join); |
779 | struct vxlan_net *vn = net_generic(dev_net(vxlan->dev), vxlan_net_id); | 780 | struct vxlan_net *vn = net_generic(dev_net(vxlan->dev), vxlan_net_id); |
780 | struct vxlan_sock *vs = vxlan->vn_sock; | 781 | struct vxlan_sock *vs = vxlan->vn_sock; |
781 | struct sock *sk = vs->sock->sk; | 782 | struct sock *sk = vs->sock->sk; |
@@ -785,10 +786,27 @@ static void vxlan_igmp_work(struct work_struct *work) | |||
785 | }; | 786 | }; |
786 | 787 | ||
787 | lock_sock(sk); | 788 | lock_sock(sk); |
788 | if (vxlan_group_used(vn, vxlan->default_dst.remote_ip)) | 789 | ip_mc_join_group(sk, &mreq); |
789 | ip_mc_join_group(sk, &mreq); | 790 | release_sock(sk); |
790 | else | 791 | |
791 | ip_mc_leave_group(sk, &mreq); | 792 | vxlan_sock_release(vn, vs); |
793 | dev_put(vxlan->dev); | ||
794 | } | ||
795 | |||
796 | /* Inverse of vxlan_igmp_join when last VNI is brought down */ | ||
797 | static void vxlan_igmp_leave(struct work_struct *work) | ||
798 | { | ||
799 | struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, igmp_leave); | ||
800 | struct vxlan_net *vn = net_generic(dev_net(vxlan->dev), vxlan_net_id); | ||
801 | struct vxlan_sock *vs = vxlan->vn_sock; | ||
802 | struct sock *sk = vs->sock->sk; | ||
803 | struct ip_mreqn mreq = { | ||
804 | .imr_multiaddr.s_addr = vxlan->default_dst.remote_ip, | ||
805 | .imr_ifindex = vxlan->default_dst.remote_ifindex, | ||
806 | }; | ||
807 | |||
808 | lock_sock(sk); | ||
809 | ip_mc_leave_group(sk, &mreq); | ||
792 | release_sock(sk); | 810 | release_sock(sk); |
793 | 811 | ||
794 | vxlan_sock_release(vn, vs); | 812 | vxlan_sock_release(vn, vs); |
@@ -1359,6 +1377,7 @@ static void vxlan_uninit(struct net_device *dev) | |||
1359 | /* Start ageing timer and join group when device is brought up */ | 1377 | /* Start ageing timer and join group when device is brought up */ |
1360 | static int vxlan_open(struct net_device *dev) | 1378 | static int vxlan_open(struct net_device *dev) |
1361 | { | 1379 | { |
1380 | struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); | ||
1362 | struct vxlan_dev *vxlan = netdev_priv(dev); | 1381 | struct vxlan_dev *vxlan = netdev_priv(dev); |
1363 | struct vxlan_sock *vs = vxlan->vn_sock; | 1382 | struct vxlan_sock *vs = vxlan->vn_sock; |
1364 | 1383 | ||
@@ -1366,10 +1385,11 @@ static int vxlan_open(struct net_device *dev) | |||
1366 | if (!vs) | 1385 | if (!vs) |
1367 | return -ENOTCONN; | 1386 | return -ENOTCONN; |
1368 | 1387 | ||
1369 | if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip))) { | 1388 | if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip)) && |
1389 | vxlan_group_used(vn, vxlan->default_dst.remote_ip)) { | ||
1370 | vxlan_sock_hold(vs); | 1390 | vxlan_sock_hold(vs); |
1371 | dev_hold(dev); | 1391 | dev_hold(dev); |
1372 | queue_work(vxlan_wq, &vxlan->igmp_work); | 1392 | queue_work(vxlan_wq, &vxlan->igmp_join); |
1373 | } | 1393 | } |
1374 | 1394 | ||
1375 | if (vxlan->age_interval) | 1395 | if (vxlan->age_interval) |
@@ -1400,13 +1420,15 @@ static void vxlan_flush(struct vxlan_dev *vxlan) | |||
1400 | /* Cleanup timer and forwarding table on shutdown */ | 1420 | /* Cleanup timer and forwarding table on shutdown */ |
1401 | static int vxlan_stop(struct net_device *dev) | 1421 | static int vxlan_stop(struct net_device *dev) |
1402 | { | 1422 | { |
1423 | struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); | ||
1403 | struct vxlan_dev *vxlan = netdev_priv(dev); | 1424 | struct vxlan_dev *vxlan = netdev_priv(dev); |
1404 | struct vxlan_sock *vs = vxlan->vn_sock; | 1425 | struct vxlan_sock *vs = vxlan->vn_sock; |
1405 | 1426 | ||
1406 | if (vs && IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip))) { | 1427 | if (vs && IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip)) && |
1428 | ! vxlan_group_used(vn, vxlan->default_dst.remote_ip)) { | ||
1407 | vxlan_sock_hold(vs); | 1429 | vxlan_sock_hold(vs); |
1408 | dev_hold(dev); | 1430 | dev_hold(dev); |
1409 | queue_work(vxlan_wq, &vxlan->igmp_work); | 1431 | queue_work(vxlan_wq, &vxlan->igmp_leave); |
1410 | } | 1432 | } |
1411 | 1433 | ||
1412 | del_timer_sync(&vxlan->age_timer); | 1434 | del_timer_sync(&vxlan->age_timer); |
@@ -1471,7 +1493,8 @@ static void vxlan_setup(struct net_device *dev) | |||
1471 | 1493 | ||
1472 | INIT_LIST_HEAD(&vxlan->next); | 1494 | INIT_LIST_HEAD(&vxlan->next); |
1473 | spin_lock_init(&vxlan->hash_lock); | 1495 | spin_lock_init(&vxlan->hash_lock); |
1474 | INIT_WORK(&vxlan->igmp_work, vxlan_igmp_work); | 1496 | INIT_WORK(&vxlan->igmp_join, vxlan_igmp_join); |
1497 | INIT_WORK(&vxlan->igmp_leave, vxlan_igmp_leave); | ||
1475 | INIT_WORK(&vxlan->sock_work, vxlan_sock_work); | 1498 | INIT_WORK(&vxlan->sock_work, vxlan_sock_work); |
1476 | 1499 | ||
1477 | init_timer_deferrable(&vxlan->age_timer); | 1500 | init_timer_deferrable(&vxlan->age_timer); |
@@ -1770,8 +1793,6 @@ static void vxlan_dellink(struct net_device *dev, struct list_head *head) | |||
1770 | struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); | 1793 | struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); |
1771 | struct vxlan_dev *vxlan = netdev_priv(dev); | 1794 | struct vxlan_dev *vxlan = netdev_priv(dev); |
1772 | 1795 | ||
1773 | flush_workqueue(vxlan_wq); | ||
1774 | |||
1775 | spin_lock(&vn->sock_lock); | 1796 | spin_lock(&vn->sock_lock); |
1776 | hlist_del_rcu(&vxlan->hlist); | 1797 | hlist_del_rcu(&vxlan->hlist); |
1777 | spin_unlock(&vn->sock_lock); | 1798 | spin_unlock(&vn->sock_lock); |
@@ -1878,10 +1899,12 @@ static __net_exit void vxlan_exit_net(struct net *net) | |||
1878 | { | 1899 | { |
1879 | struct vxlan_net *vn = net_generic(net, vxlan_net_id); | 1900 | struct vxlan_net *vn = net_generic(net, vxlan_net_id); |
1880 | struct vxlan_dev *vxlan; | 1901 | struct vxlan_dev *vxlan; |
1902 | LIST_HEAD(list); | ||
1881 | 1903 | ||
1882 | rtnl_lock(); | 1904 | rtnl_lock(); |
1883 | list_for_each_entry(vxlan, &vn->vxlan_list, next) | 1905 | list_for_each_entry(vxlan, &vn->vxlan_list, next) |
1884 | dev_close(vxlan->dev); | 1906 | unregister_netdevice_queue(vxlan->dev, &list); |
1907 | unregister_netdevice_many(&list); | ||
1885 | rtnl_unlock(); | 1908 | rtnl_unlock(); |
1886 | } | 1909 | } |
1887 | 1910 | ||