net/mlx5e: Use workqueue for vxlan ops

The vxlan add/delete port NDOs are called under rcu lock.
The current mlx5e implementation can potentially block in these
calls, which is not allowed.  Move to using the mlx5e workqueue
to handle these NDOs.

Fixes: b3f63c3d5e ('net/mlx5e: Add netdev support for VXLAN tunneling')
Signed-off-by: Matthew Finlay <matt@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Matthew Finlay 2016-05-01 22:59:57 +03:00 committed by David S. Miller
parent 7bb2975599
commit d8cf2dda3d
3 changed files with 49 additions and 16 deletions

View File

@ -2157,7 +2157,7 @@ static void mlx5e_add_vxlan_port(struct net_device *netdev,
if (!mlx5e_vxlan_allowed(priv->mdev)) if (!mlx5e_vxlan_allowed(priv->mdev))
return; return;
mlx5e_vxlan_add_port(priv, be16_to_cpu(port)); mlx5e_vxlan_queue_work(priv, sa_family, be16_to_cpu(port), 1);
} }
static void mlx5e_del_vxlan_port(struct net_device *netdev, static void mlx5e_del_vxlan_port(struct net_device *netdev,
@ -2168,7 +2168,7 @@ static void mlx5e_del_vxlan_port(struct net_device *netdev,
if (!mlx5e_vxlan_allowed(priv->mdev)) if (!mlx5e_vxlan_allowed(priv->mdev))
return; return;
mlx5e_vxlan_del_port(priv, be16_to_cpu(port)); mlx5e_vxlan_queue_work(priv, sa_family, be16_to_cpu(port), 0);
} }
static netdev_features_t mlx5e_vxlan_features_check(struct mlx5e_priv *priv, static netdev_features_t mlx5e_vxlan_features_check(struct mlx5e_priv *priv,

View File

@ -95,21 +95,22 @@ struct mlx5e_vxlan *mlx5e_vxlan_lookup_port(struct mlx5e_priv *priv, u16 port)
return vxlan; return vxlan;
} }
int mlx5e_vxlan_add_port(struct mlx5e_priv *priv, u16 port) static void mlx5e_vxlan_add_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; struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
u16 port = vxlan_work->port;
struct mlx5e_vxlan *vxlan; struct mlx5e_vxlan *vxlan;
int err; int err;
err = mlx5e_vxlan_core_add_port_cmd(priv->mdev, port); if (mlx5e_vxlan_core_add_port_cmd(priv->mdev, port))
if (err) goto free_work;
return err;
vxlan = kzalloc(sizeof(*vxlan), GFP_KERNEL); vxlan = kzalloc(sizeof(*vxlan), GFP_KERNEL);
if (!vxlan) { if (!vxlan)
err = -ENOMEM;
goto err_delete_port; goto err_delete_port;
}
vxlan->udp_port = port; vxlan->udp_port = port;
@ -119,13 +120,14 @@ int mlx5e_vxlan_add_port(struct mlx5e_priv *priv, u16 port)
if (err) if (err)
goto err_free; goto err_free;
return 0; goto free_work;
err_free: err_free:
kfree(vxlan); kfree(vxlan);
err_delete_port: err_delete_port:
mlx5e_vxlan_core_del_port_cmd(priv->mdev, port); mlx5e_vxlan_core_del_port_cmd(priv->mdev, port);
return err; free_work:
kfree(vxlan_work);
} }
static void __mlx5e_vxlan_core_del_port(struct mlx5e_priv *priv, u16 port) static void __mlx5e_vxlan_core_del_port(struct mlx5e_priv *priv, u16 port)
@ -145,12 +147,36 @@ static void __mlx5e_vxlan_core_del_port(struct mlx5e_priv *priv, u16 port)
kfree(vxlan); kfree(vxlan);
} }
void mlx5e_vxlan_del_port(struct mlx5e_priv *priv, u16 port) static void mlx5e_vxlan_del_port(struct work_struct *work)
{ {
if (!mlx5e_vxlan_lookup_port(priv, port)) struct mlx5e_vxlan_work *vxlan_work =
return; container_of(work, struct mlx5e_vxlan_work, work);
struct mlx5e_priv *priv = vxlan_work->priv;
u16 port = vxlan_work->port;
__mlx5e_vxlan_core_del_port(priv, port); __mlx5e_vxlan_core_del_port(priv, port);
kfree(vxlan_work);
}
void mlx5e_vxlan_queue_work(struct mlx5e_priv *priv, sa_family_t sa_family,
u16 port, int add)
{
struct mlx5e_vxlan_work *vxlan_work;
vxlan_work = kmalloc(sizeof(*vxlan_work), GFP_ATOMIC);
if (!vxlan_work)
return;
if (add)
INIT_WORK(&vxlan_work->work, mlx5e_vxlan_add_port);
else
INIT_WORK(&vxlan_work->work, mlx5e_vxlan_del_port);
vxlan_work->priv = priv;
vxlan_work->port = port;
vxlan_work->sa_family = sa_family;
queue_work(priv->wq, &vxlan_work->work);
} }
void mlx5e_vxlan_cleanup(struct mlx5e_priv *priv) void mlx5e_vxlan_cleanup(struct mlx5e_priv *priv)

View File

@ -39,6 +39,13 @@ struct mlx5e_vxlan {
u16 udp_port; u16 udp_port;
}; };
struct mlx5e_vxlan_work {
struct work_struct work;
struct mlx5e_priv *priv;
sa_family_t sa_family;
u16 port;
};
static inline bool mlx5e_vxlan_allowed(struct mlx5_core_dev *mdev) static inline bool mlx5e_vxlan_allowed(struct mlx5_core_dev *mdev)
{ {
return (MLX5_CAP_ETH(mdev, tunnel_stateless_vxlan) && return (MLX5_CAP_ETH(mdev, tunnel_stateless_vxlan) &&
@ -46,8 +53,8 @@ static inline bool mlx5e_vxlan_allowed(struct mlx5_core_dev *mdev)
} }
void mlx5e_vxlan_init(struct mlx5e_priv *priv); void mlx5e_vxlan_init(struct mlx5e_priv *priv);
int mlx5e_vxlan_add_port(struct mlx5e_priv *priv, u16 port); void mlx5e_vxlan_queue_work(struct mlx5e_priv *priv, sa_family_t sa_family,
void mlx5e_vxlan_del_port(struct mlx5e_priv *priv, u16 port); u16 port, int add);
struct mlx5e_vxlan *mlx5e_vxlan_lookup_port(struct mlx5e_priv *priv, u16 port); struct mlx5e_vxlan *mlx5e_vxlan_lookup_port(struct mlx5e_priv *priv, u16 port);
void mlx5e_vxlan_cleanup(struct mlx5e_priv *priv); void mlx5e_vxlan_cleanup(struct mlx5e_priv *priv);