aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHannes Frederic Sowa <hannes@stressinduktion.org>2016-04-08 16:55:01 -0400
committerDavid S. Miller <davem@davemloft.net>2016-04-16 18:23:01 -0400
commit0412bd931f5f94d1054e958415c4a945d8ee62f4 (patch)
treee7fe97a778ac5b9ce73d5e8becd5192d5924a734
parentf48256efededaa87f475c0d6330d83f853cb064a (diff)
vxlan: synchronously and race-free destruction of vxlan sockets
Due to the fact that the udp socket is destructed asynchronously in a work queue, we have some nondeterministic behavior during shutdown of vxlan tunnels and creating new ones. Fix this by keeping the destruction process synchronous in regards to the user space process so IFF_UP can be reliably set. udp_tunnel_sock_release destroys vs->sock->sk if reference counter indicates so. We expect to have the same lifetime of vxlan_sock and vxlan_sock->sock->sk even in fast paths with only rcu locks held. So only destruct the whole socket after we can be sure it cannot be found by searching vxlan_net->sock_list. Cc: Eric Dumazet <eric.dumazet@gmail.com> Cc: Jiri Benc <jbenc@redhat.com> Cc: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/vxlan.c20
-rw-r--r--include/net/vxlan.h2
2 files changed, 3 insertions, 19 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 7f697a3f00a4..19383371a27d 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -98,7 +98,6 @@ struct vxlan_fdb {
98 98
99/* salt for hash table */ 99/* salt for hash table */
100static u32 vxlan_salt __read_mostly; 100static u32 vxlan_salt __read_mostly;
101static struct workqueue_struct *vxlan_wq;
102 101
103static inline bool vxlan_collect_metadata(struct vxlan_sock *vs) 102static inline bool vxlan_collect_metadata(struct vxlan_sock *vs)
104{ 103{
@@ -1053,7 +1052,9 @@ static void __vxlan_sock_release(struct vxlan_sock *vs)
1053 vxlan_notify_del_rx_port(vs); 1052 vxlan_notify_del_rx_port(vs);
1054 spin_unlock(&vn->sock_lock); 1053 spin_unlock(&vn->sock_lock);
1055 1054
1056 queue_work(vxlan_wq, &vs->del_work); 1055 synchronize_net();
1056 udp_tunnel_sock_release(vs->sock);
1057 kfree(vs);
1057} 1058}
1058 1059
1059static void vxlan_sock_release(struct vxlan_dev *vxlan) 1060static void vxlan_sock_release(struct vxlan_dev *vxlan)
@@ -2674,13 +2675,6 @@ static const struct ethtool_ops vxlan_ethtool_ops = {
2674 .get_link = ethtool_op_get_link, 2675 .get_link = ethtool_op_get_link,
2675}; 2676};
2676 2677
2677static void vxlan_del_work(struct work_struct *work)
2678{
2679 struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work);
2680 udp_tunnel_sock_release(vs->sock);
2681 kfree_rcu(vs, rcu);
2682}
2683
2684static struct socket *vxlan_create_sock(struct net *net, bool ipv6, 2678static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
2685 __be16 port, u32 flags) 2679 __be16 port, u32 flags)
2686{ 2680{
@@ -2726,8 +2720,6 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
2726 for (h = 0; h < VNI_HASH_SIZE; ++h) 2720 for (h = 0; h < VNI_HASH_SIZE; ++h)
2727 INIT_HLIST_HEAD(&vs->vni_list[h]); 2721 INIT_HLIST_HEAD(&vs->vni_list[h]);
2728 2722
2729 INIT_WORK(&vs->del_work, vxlan_del_work);
2730
2731 sock = vxlan_create_sock(net, ipv6, port, flags); 2723 sock = vxlan_create_sock(net, ipv6, port, flags);
2732 if (IS_ERR(sock)) { 2724 if (IS_ERR(sock)) {
2733 pr_info("Cannot bind port %d, err=%ld\n", ntohs(port), 2725 pr_info("Cannot bind port %d, err=%ld\n", ntohs(port),
@@ -3346,10 +3338,6 @@ static int __init vxlan_init_module(void)
3346{ 3338{
3347 int rc; 3339 int rc;
3348 3340
3349 vxlan_wq = alloc_workqueue("vxlan", 0, 0);
3350 if (!vxlan_wq)
3351 return -ENOMEM;
3352
3353 get_random_bytes(&vxlan_salt, sizeof(vxlan_salt)); 3341 get_random_bytes(&vxlan_salt, sizeof(vxlan_salt));
3354 3342
3355 rc = register_pernet_subsys(&vxlan_net_ops); 3343 rc = register_pernet_subsys(&vxlan_net_ops);
@@ -3370,7 +3358,6 @@ out3:
3370out2: 3358out2:
3371 unregister_pernet_subsys(&vxlan_net_ops); 3359 unregister_pernet_subsys(&vxlan_net_ops);
3372out1: 3360out1:
3373 destroy_workqueue(vxlan_wq);
3374 return rc; 3361 return rc;
3375} 3362}
3376late_initcall(vxlan_init_module); 3363late_initcall(vxlan_init_module);
@@ -3379,7 +3366,6 @@ static void __exit vxlan_cleanup_module(void)
3379{ 3366{
3380 rtnl_link_unregister(&vxlan_link_ops); 3367 rtnl_link_unregister(&vxlan_link_ops);
3381 unregister_netdevice_notifier(&vxlan_notifier_block); 3368 unregister_netdevice_notifier(&vxlan_notifier_block);
3382 destroy_workqueue(vxlan_wq);
3383 unregister_pernet_subsys(&vxlan_net_ops); 3369 unregister_pernet_subsys(&vxlan_net_ops);
3384 /* rcu_barrier() is called by netns */ 3370 /* rcu_barrier() is called by netns */
3385} 3371}
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index 2f168f0ea32c..d442eb3129cd 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -184,9 +184,7 @@ struct vxlan_metadata {
184/* per UDP socket information */ 184/* per UDP socket information */
185struct vxlan_sock { 185struct vxlan_sock {
186 struct hlist_node hlist; 186 struct hlist_node hlist;
187 struct work_struct del_work;
188 struct socket *sock; 187 struct socket *sock;
189 struct rcu_head rcu;
190 struct hlist_head vni_list[VNI_HASH_SIZE]; 188 struct hlist_head vni_list[VNI_HASH_SIZE];
191 atomic_t refcnt; 189 atomic_t refcnt;
192 u32 flags; 190 u32 flags;