Commit 0412bd93 authored by Hannes Frederic Sowa's avatar Hannes Frederic Sowa Committed by David S. Miller

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: default avatarHannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f48256ef
...@@ -98,7 +98,6 @@ struct vxlan_fdb { ...@@ -98,7 +98,6 @@ struct vxlan_fdb {
/* salt for hash table */ /* salt for hash table */
static u32 vxlan_salt __read_mostly; static u32 vxlan_salt __read_mostly;
static struct workqueue_struct *vxlan_wq;
static inline bool vxlan_collect_metadata(struct vxlan_sock *vs) static inline bool vxlan_collect_metadata(struct vxlan_sock *vs)
{ {
...@@ -1053,7 +1052,9 @@ static void __vxlan_sock_release(struct vxlan_sock *vs) ...@@ -1053,7 +1052,9 @@ static void __vxlan_sock_release(struct vxlan_sock *vs)
vxlan_notify_del_rx_port(vs); vxlan_notify_del_rx_port(vs);
spin_unlock(&vn->sock_lock); spin_unlock(&vn->sock_lock);
queue_work(vxlan_wq, &vs->del_work); synchronize_net();
udp_tunnel_sock_release(vs->sock);
kfree(vs);
} }
static void vxlan_sock_release(struct vxlan_dev *vxlan) static void vxlan_sock_release(struct vxlan_dev *vxlan)
...@@ -2674,13 +2675,6 @@ static const struct ethtool_ops vxlan_ethtool_ops = { ...@@ -2674,13 +2675,6 @@ static const struct ethtool_ops vxlan_ethtool_ops = {
.get_link = ethtool_op_get_link, .get_link = ethtool_op_get_link,
}; };
static void vxlan_del_work(struct work_struct *work)
{
struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work);
udp_tunnel_sock_release(vs->sock);
kfree_rcu(vs, rcu);
}
static struct socket *vxlan_create_sock(struct net *net, bool ipv6, static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
__be16 port, u32 flags) __be16 port, u32 flags)
{ {
...@@ -2726,8 +2720,6 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6, ...@@ -2726,8 +2720,6 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
for (h = 0; h < VNI_HASH_SIZE; ++h) for (h = 0; h < VNI_HASH_SIZE; ++h)
INIT_HLIST_HEAD(&vs->vni_list[h]); INIT_HLIST_HEAD(&vs->vni_list[h]);
INIT_WORK(&vs->del_work, vxlan_del_work);
sock = vxlan_create_sock(net, ipv6, port, flags); sock = vxlan_create_sock(net, ipv6, port, flags);
if (IS_ERR(sock)) { if (IS_ERR(sock)) {
pr_info("Cannot bind port %d, err=%ld\n", ntohs(port), pr_info("Cannot bind port %d, err=%ld\n", ntohs(port),
...@@ -3346,10 +3338,6 @@ static int __init vxlan_init_module(void) ...@@ -3346,10 +3338,6 @@ static int __init vxlan_init_module(void)
{ {
int rc; int rc;
vxlan_wq = alloc_workqueue("vxlan", 0, 0);
if (!vxlan_wq)
return -ENOMEM;
get_random_bytes(&vxlan_salt, sizeof(vxlan_salt)); get_random_bytes(&vxlan_salt, sizeof(vxlan_salt));
rc = register_pernet_subsys(&vxlan_net_ops); rc = register_pernet_subsys(&vxlan_net_ops);
...@@ -3370,7 +3358,6 @@ static int __init vxlan_init_module(void) ...@@ -3370,7 +3358,6 @@ static int __init vxlan_init_module(void)
out2: out2:
unregister_pernet_subsys(&vxlan_net_ops); unregister_pernet_subsys(&vxlan_net_ops);
out1: out1:
destroy_workqueue(vxlan_wq);
return rc; return rc;
} }
late_initcall(vxlan_init_module); late_initcall(vxlan_init_module);
...@@ -3379,7 +3366,6 @@ static void __exit vxlan_cleanup_module(void) ...@@ -3379,7 +3366,6 @@ static void __exit vxlan_cleanup_module(void)
{ {
rtnl_link_unregister(&vxlan_link_ops); rtnl_link_unregister(&vxlan_link_ops);
unregister_netdevice_notifier(&vxlan_notifier_block); unregister_netdevice_notifier(&vxlan_notifier_block);
destroy_workqueue(vxlan_wq);
unregister_pernet_subsys(&vxlan_net_ops); unregister_pernet_subsys(&vxlan_net_ops);
/* rcu_barrier() is called by netns */ /* rcu_barrier() is called by netns */
} }
......
...@@ -184,9 +184,7 @@ struct vxlan_metadata { ...@@ -184,9 +184,7 @@ struct vxlan_metadata {
/* per UDP socket information */ /* per UDP socket information */
struct vxlan_sock { struct vxlan_sock {
struct hlist_node hlist; struct hlist_node hlist;
struct work_struct del_work;
struct socket *sock; struct socket *sock;
struct rcu_head rcu;
struct hlist_head vni_list[VNI_HASH_SIZE]; struct hlist_head vni_list[VNI_HASH_SIZE];
atomic_t refcnt; atomic_t refcnt;
u32 flags; u32 flags;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment