| From 7ba844cc4c7595f57cc68762e4e46fbabf728115 Mon Sep 17 00:00:00 2001 |
| From: Gal Pressman <galp@mellanox.com> |
| Date: Sun, 3 Dec 2017 13:58:50 +0200 |
| Subject: [PATCH] net/mlx5e: Add refcount to VXLAN structure |
| |
| commit 23f4cc2cd9ed92570647220aca60d0197d8c1fa9 upstream. |
| |
| A refcount mechanism must be implemented in order to prevent unwanted |
| scenarios such as: |
| - Open an IPv4 VXLAN interface |
| - Open an IPv6 VXLAN interface (different socket) |
| - Remove one of the interfaces |
| |
| With current implementation, the UDP port will be removed from our VXLAN |
| database and turn off the offloads for the other interface, which is |
| still active. |
| The reference count mechanism will only allow UDP port removals once all |
| consumers are gone. |
| |
| Fixes: b3f63c3d5e2c ("net/mlx5e: Add netdev support for VXLAN tunneling") |
| Signed-off-by: Gal Pressman <galp@mellanox.com> |
| Signed-off-by: Saeed Mahameed <saeedm@mellanox.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c |
| index f8238275759f..25f782344667 100644 |
| --- a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c |
| +++ b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c |
| @@ -88,8 +88,11 @@ static void mlx5e_vxlan_add_port(struct work_struct *work) |
| struct mlx5e_vxlan *vxlan; |
| int err; |
| |
| - if (mlx5e_vxlan_lookup_port(priv, port)) |
| + vxlan = mlx5e_vxlan_lookup_port(priv, port); |
| + if (vxlan) { |
| + atomic_inc(&vxlan->refcount); |
| goto free_work; |
| + } |
| |
| if (mlx5e_vxlan_core_add_port_cmd(priv->mdev, port)) |
| goto free_work; |
| @@ -99,6 +102,7 @@ static void mlx5e_vxlan_add_port(struct work_struct *work) |
| goto err_delete_port; |
| |
| vxlan->udp_port = port; |
| + atomic_set(&vxlan->refcount, 1); |
| |
| spin_lock_bh(&vxlan_db->lock); |
| err = radix_tree_insert(&vxlan_db->tree, vxlan->udp_port, vxlan); |
| @@ -116,32 +120,33 @@ static void mlx5e_vxlan_add_port(struct work_struct *work) |
| kfree(vxlan_work); |
| } |
| |
| -static void __mlx5e_vxlan_core_del_port(struct mlx5e_priv *priv, u16 port) |
| +static void mlx5e_vxlan_del_port(struct work_struct *work) |
| { |
| + struct mlx5e_vxlan_work *vxlan_work = |
| + container_of(work, struct mlx5e_vxlan_work, work); |
| + struct mlx5e_priv *priv = vxlan_work->priv; |
| struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan; |
| + u16 port = vxlan_work->port; |
| struct mlx5e_vxlan *vxlan; |
| + bool remove = false; |
| |
| spin_lock_bh(&vxlan_db->lock); |
| - vxlan = radix_tree_delete(&vxlan_db->tree, port); |
| - spin_unlock_bh(&vxlan_db->lock); |
| - |
| + vxlan = radix_tree_lookup(&vxlan_db->tree, port); |
| if (!vxlan) |
| - return; |
| - |
| - mlx5e_vxlan_core_del_port_cmd(priv->mdev, vxlan->udp_port); |
| - |
| - kfree(vxlan); |
| -} |
| + goto out_unlock; |
| |
| -static void mlx5e_vxlan_del_port(struct work_struct *work) |
| -{ |
| - struct mlx5e_vxlan_work *vxlan_work = |
| - container_of(work, struct mlx5e_vxlan_work, work); |
| - struct mlx5e_priv *priv = vxlan_work->priv; |
| - u16 port = vxlan_work->port; |
| + if (atomic_dec_and_test(&vxlan->refcount)) { |
| + radix_tree_delete(&vxlan_db->tree, port); |
| + remove = true; |
| + } |
| |
| - __mlx5e_vxlan_core_del_port(priv, port); |
| +out_unlock: |
| + spin_unlock_bh(&vxlan_db->lock); |
| |
| + if (remove) { |
| + mlx5e_vxlan_core_del_port_cmd(priv->mdev, port); |
| + kfree(vxlan); |
| + } |
| kfree(vxlan_work); |
| } |
| |
| @@ -171,12 +176,11 @@ void mlx5e_vxlan_cleanup(struct mlx5e_priv *priv) |
| struct mlx5e_vxlan *vxlan; |
| unsigned int port = 0; |
| |
| - spin_lock_bh(&vxlan_db->lock); |
| + /* Lockless since we are the only radix-tree consumers, wq is disabled */ |
| while (radix_tree_gang_lookup(&vxlan_db->tree, (void **)&vxlan, port, 1)) { |
| port = vxlan->udp_port; |
| - spin_unlock_bh(&vxlan_db->lock); |
| - __mlx5e_vxlan_core_del_port(priv, (u16)port); |
| - spin_lock_bh(&vxlan_db->lock); |
| + radix_tree_delete(&vxlan_db->tree, port); |
| + mlx5e_vxlan_core_del_port_cmd(priv->mdev, port); |
| + kfree(vxlan); |
| } |
| - spin_unlock_bh(&vxlan_db->lock); |
| } |
| diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h |
| index 5def12c048e3..5ef6ae7d568a 100644 |
| --- a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h |
| +++ b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h |
| @@ -36,6 +36,7 @@ |
| #include "en.h" |
| |
| struct mlx5e_vxlan { |
| + atomic_t refcount; |
| u16 udp_port; |
| }; |
| |
| -- |
| 2.15.0 |
| |