net/mlx5e: Fix concurrency issues between config flow and XSK
After disabling resources necessary for XSK (the XDP program, channels, XSK queues), use synchronize_rcu to wait until the XSK wakeup function finishes, before freeing the resources. Suspend XSK wakeups during switching channels. If the XDP program is being removed, synchronize_rcu before closing the old channels to allow XSK wakeup to complete. Signed-off-by: Maxim Mikityanskiy <maximmi@mellanox.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Link: https://lore.kernel.org/bpf/20191217162023.16011-3-maximmi@mellanox.com
This commit is contained in:

committed by
Daniel Borkmann

parent
0687068208
commit
9cf88808ad
@@ -760,7 +760,7 @@ enum {
|
|||||||
MLX5E_STATE_OPENED,
|
MLX5E_STATE_OPENED,
|
||||||
MLX5E_STATE_DESTROYING,
|
MLX5E_STATE_DESTROYING,
|
||||||
MLX5E_STATE_XDP_TX_ENABLED,
|
MLX5E_STATE_XDP_TX_ENABLED,
|
||||||
MLX5E_STATE_XDP_OPEN,
|
MLX5E_STATE_XDP_ACTIVE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mlx5e_rqt {
|
struct mlx5e_rqt {
|
||||||
|
@@ -75,12 +75,18 @@ int mlx5e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames,
|
|||||||
static inline void mlx5e_xdp_tx_enable(struct mlx5e_priv *priv)
|
static inline void mlx5e_xdp_tx_enable(struct mlx5e_priv *priv)
|
||||||
{
|
{
|
||||||
set_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state);
|
set_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state);
|
||||||
|
|
||||||
|
if (priv->channels.params.xdp_prog)
|
||||||
|
set_bit(MLX5E_STATE_XDP_ACTIVE, &priv->state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void mlx5e_xdp_tx_disable(struct mlx5e_priv *priv)
|
static inline void mlx5e_xdp_tx_disable(struct mlx5e_priv *priv)
|
||||||
{
|
{
|
||||||
|
if (priv->channels.params.xdp_prog)
|
||||||
|
clear_bit(MLX5E_STATE_XDP_ACTIVE, &priv->state);
|
||||||
|
|
||||||
clear_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state);
|
clear_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state);
|
||||||
/* let other device's napi(s) see our new state */
|
/* Let other device's napi(s) and XSK wakeups see our new state. */
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,19 +95,9 @@ static inline bool mlx5e_xdp_tx_is_enabled(struct mlx5e_priv *priv)
|
|||||||
return test_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state);
|
return test_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void mlx5e_xdp_set_open(struct mlx5e_priv *priv)
|
static inline bool mlx5e_xdp_is_active(struct mlx5e_priv *priv)
|
||||||
{
|
{
|
||||||
set_bit(MLX5E_STATE_XDP_OPEN, &priv->state);
|
return test_bit(MLX5E_STATE_XDP_ACTIVE, &priv->state);
|
||||||
}
|
|
||||||
|
|
||||||
static inline void mlx5e_xdp_set_closed(struct mlx5e_priv *priv)
|
|
||||||
{
|
|
||||||
clear_bit(MLX5E_STATE_XDP_OPEN, &priv->state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool mlx5e_xdp_is_open(struct mlx5e_priv *priv)
|
|
||||||
{
|
|
||||||
return test_bit(MLX5E_STATE_XDP_OPEN, &priv->state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void mlx5e_xmit_xdp_doorbell(struct mlx5e_xdpsq *sq)
|
static inline void mlx5e_xmit_xdp_doorbell(struct mlx5e_xdpsq *sq)
|
||||||
|
@@ -144,6 +144,7 @@ void mlx5e_close_xsk(struct mlx5e_channel *c)
|
|||||||
{
|
{
|
||||||
clear_bit(MLX5E_CHANNEL_STATE_XSK, c->state);
|
clear_bit(MLX5E_CHANNEL_STATE_XSK, c->state);
|
||||||
napi_synchronize(&c->napi);
|
napi_synchronize(&c->napi);
|
||||||
|
synchronize_rcu(); /* Sync with the XSK wakeup. */
|
||||||
|
|
||||||
mlx5e_close_rq(&c->xskrq);
|
mlx5e_close_rq(&c->xskrq);
|
||||||
mlx5e_close_cq(&c->xskrq.cq);
|
mlx5e_close_cq(&c->xskrq.cq);
|
||||||
|
@@ -14,7 +14,7 @@ int mlx5e_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags)
|
|||||||
struct mlx5e_channel *c;
|
struct mlx5e_channel *c;
|
||||||
u16 ix;
|
u16 ix;
|
||||||
|
|
||||||
if (unlikely(!mlx5e_xdp_is_open(priv)))
|
if (unlikely(!mlx5e_xdp_is_active(priv)))
|
||||||
return -ENETDOWN;
|
return -ENETDOWN;
|
||||||
|
|
||||||
if (unlikely(!mlx5e_qid_get_ch_if_in_group(params, qid, MLX5E_RQ_GROUP_XSK, &ix)))
|
if (unlikely(!mlx5e_qid_get_ch_if_in_group(params, qid, MLX5E_RQ_GROUP_XSK, &ix)))
|
||||||
|
@@ -3000,12 +3000,9 @@ void mlx5e_timestamp_init(struct mlx5e_priv *priv)
|
|||||||
int mlx5e_open_locked(struct net_device *netdev)
|
int mlx5e_open_locked(struct net_device *netdev)
|
||||||
{
|
{
|
||||||
struct mlx5e_priv *priv = netdev_priv(netdev);
|
struct mlx5e_priv *priv = netdev_priv(netdev);
|
||||||
bool is_xdp = priv->channels.params.xdp_prog;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
set_bit(MLX5E_STATE_OPENED, &priv->state);
|
set_bit(MLX5E_STATE_OPENED, &priv->state);
|
||||||
if (is_xdp)
|
|
||||||
mlx5e_xdp_set_open(priv);
|
|
||||||
|
|
||||||
err = mlx5e_open_channels(priv, &priv->channels);
|
err = mlx5e_open_channels(priv, &priv->channels);
|
||||||
if (err)
|
if (err)
|
||||||
@@ -3020,8 +3017,6 @@ int mlx5e_open_locked(struct net_device *netdev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_clear_state_opened_flag:
|
err_clear_state_opened_flag:
|
||||||
if (is_xdp)
|
|
||||||
mlx5e_xdp_set_closed(priv);
|
|
||||||
clear_bit(MLX5E_STATE_OPENED, &priv->state);
|
clear_bit(MLX5E_STATE_OPENED, &priv->state);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -3053,8 +3048,6 @@ int mlx5e_close_locked(struct net_device *netdev)
|
|||||||
if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
|
if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (priv->channels.params.xdp_prog)
|
|
||||||
mlx5e_xdp_set_closed(priv);
|
|
||||||
clear_bit(MLX5E_STATE_OPENED, &priv->state);
|
clear_bit(MLX5E_STATE_OPENED, &priv->state);
|
||||||
|
|
||||||
netif_carrier_off(priv->netdev);
|
netif_carrier_off(priv->netdev);
|
||||||
@@ -4371,16 +4364,6 @@ static int mlx5e_xdp_allowed(struct mlx5e_priv *priv, struct bpf_prog *prog)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mlx5e_xdp_update_state(struct mlx5e_priv *priv)
|
|
||||||
{
|
|
||||||
if (priv->channels.params.xdp_prog)
|
|
||||||
mlx5e_xdp_set_open(priv);
|
|
||||||
else
|
|
||||||
mlx5e_xdp_set_closed(priv);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
|
static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
|
||||||
{
|
{
|
||||||
struct mlx5e_priv *priv = netdev_priv(netdev);
|
struct mlx5e_priv *priv = netdev_priv(netdev);
|
||||||
@@ -4415,7 +4398,7 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
|
|||||||
mlx5e_set_rq_type(priv->mdev, &new_channels.params);
|
mlx5e_set_rq_type(priv->mdev, &new_channels.params);
|
||||||
old_prog = priv->channels.params.xdp_prog;
|
old_prog = priv->channels.params.xdp_prog;
|
||||||
|
|
||||||
err = mlx5e_safe_switch_channels(priv, &new_channels, mlx5e_xdp_update_state);
|
err = mlx5e_safe_switch_channels(priv, &new_channels, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
} else {
|
} else {
|
||||||
|
Reference in New Issue
Block a user