diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 1986d4d0e886..8ec52498be3f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1070,13 +1070,17 @@ static void mlx5_eswitch_event_handler_register(struct mlx5_eswitch *esw) } } +static void mlx5_eswitch_invalidate_wq(struct mlx5_eswitch *esw) +{ + atomic_inc(&esw->generation); + wake_up_all(&esw->work_queue_wait); +} + static void mlx5_eswitch_event_handler_unregister(struct mlx5_eswitch *esw) { if (esw->mode == MLX5_ESWITCH_OFFLOADS && - mlx5_eswitch_is_funcs_handler(esw->dev)) { + mlx5_eswitch_is_funcs_handler(esw->dev)) mlx5_eq_notifier_unregister(esw->dev, &esw->esw_funcs.nb); - atomic_inc(&esw->generation); - } } static void mlx5_eswitch_clear_vf_vports_info(struct mlx5_eswitch *esw) @@ -1701,6 +1705,8 @@ int mlx5_eswitch_enable(struct mlx5_eswitch *esw, int num_vfs) if (toggle_lag) mlx5_lag_disable_change(esw->dev); + mlx5_eswitch_invalidate_wq(esw); + if (!mlx5_esw_is_fdb_created(esw)) { ret = mlx5_eswitch_enable_locked(esw, num_vfs); } else { @@ -1746,6 +1752,8 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw, bool clear_vf) esw->mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS", esw->esw_funcs.num_vfs, esw->esw_funcs.num_ec_vfs, esw->enabled_vports); + mlx5_eswitch_invalidate_wq(esw); + if (!mlx5_core_is_ecpf(esw->dev)) { mlx5_eswitch_unload_vf_vports(esw, esw->esw_funcs.num_vfs); if (clear_vf) @@ -1785,6 +1793,7 @@ void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw) mlx5_eq_notifier_unregister(esw->dev, &esw->nb); mlx5_eswitch_event_handler_unregister(esw); + mlx5_eswitch_invalidate_wq(esw); esw_info(esw->dev, "Disable: mode(%s), nvfs(%d), necvfs(%d), active vports(%d)\n", esw->mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS", @@ -2072,6 +2081,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) mutex_init(&esw->state_lock); init_rwsem(&esw->mode_lock); refcount_set(&esw->qos.refcnt, 0); + init_waitqueue_head(&esw->work_queue_wait); atomic_set(&esw->generation, 0); esw->enabled_vports = 0; @@ -2110,8 +2120,9 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) esw_info(esw->dev, "cleanup\n"); - mlx5_esw_qos_cleanup(esw); + mlx5_eswitch_invalidate_wq(esw); destroy_workqueue(esw->work_queue); + mlx5_esw_qos_cleanup(esw); WARN_ON(refcount_read(&esw->qos.refcnt)); mutex_destroy(&esw->state_lock); WARN_ON(!xa_empty(&esw->offloads.vhca_map)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index e3ab8a30c174..8f4c47975c58 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -385,6 +386,7 @@ struct mlx5_eswitch { */ struct rw_semaphore mode_lock; atomic64_t user_count; + wait_queue_head_t work_queue_wait; /* Protected with the E-Switch qos domain lock. */ struct { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 23af5a12dc07..9e2ae217c224 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -3694,21 +3694,38 @@ static void esw_wq_handler(struct work_struct *work) struct mlx5_host_work *host_work; struct mlx5_eswitch *esw; struct devlink *devlink; + int work_gen; host_work = container_of(work, struct mlx5_host_work, work); esw = host_work->esw; + work_gen = host_work->work_gen; devlink = priv_to_devlink(esw->dev); - devl_lock(devlink); + /* Do not block on devlink lock until stale work is filtered out. + * Teardown can invalidate the generation and then wait for this + * workqueue while holding devlink lock. + */ + for (;;) { + if (work_gen != atomic_read(&esw->generation)) + goto free; + + if (devl_trylock(devlink)) + break; + + wait_event_timeout(esw->work_queue_wait, + work_gen != atomic_read(&esw->generation), + msecs_to_jiffies(60)); + } /* Stale work from one or more mode changes ago. Bail out. */ - if (host_work->work_gen != atomic_read(&esw->generation)) + if (work_gen != atomic_read(&esw->generation)) goto unlock; host_work->func(esw); unlock: devl_unlock(devlink); +free: kfree(host_work); }