diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 123c96716a54..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->esw_funcs.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,8 @@ 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; esw->offloads.inline_mode = MLX5_INLINE_MODE_NONE; @@ -2109,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 5128f5020dae..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 @@ -336,11 +337,11 @@ struct mlx5_host_work { struct work_struct work; struct mlx5_eswitch *esw; int work_gen; + void (*func)(struct mlx5_eswitch *esw); }; struct mlx5_esw_functions { struct mlx5_nb nb; - atomic_t generation; bool host_funcs_disabled; u16 num_vfs; u16 num_ec_vfs; @@ -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 { @@ -410,6 +412,7 @@ struct mlx5_eswitch { struct mlx5_devcom_comp_dev *devcom; u16 enabled_ipsec_vf_count; bool eswitch_operation_in_progress; + atomic_t generation; }; void esw_offloads_disable(struct mlx5_eswitch *esw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index a078d06f4567..9e2ae217c224 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -3655,20 +3655,15 @@ static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw) mutex_destroy(&esw->fdb_table.offloads.vports.lock); } -static void -esw_vfs_changed_event_handler(struct mlx5_eswitch *esw, int work_gen, - const u32 *out) +static void esw_vfs_changed_event_handler(struct mlx5_eswitch *esw) { - struct devlink *devlink; bool host_pf_disabled; u16 new_num_vfs; + const u32 *out; - devlink = priv_to_devlink(esw->dev); - devl_lock(devlink); - - /* Stale work from one or more mode changes ago. Bail out. */ - if (work_gen != atomic_read(&esw->esw_funcs.generation)) - goto unlock; + out = mlx5_esw_query_functions(esw->dev); + if (IS_ERR(out)) + return; new_num_vfs = MLX5_GET(query_esw_functions_out, out, host_params_context.host_num_of_vfs); @@ -3676,7 +3671,7 @@ esw_vfs_changed_event_handler(struct mlx5_eswitch *esw, int work_gen, host_params_context.host_pf_disabled); if (new_num_vfs == esw->esw_funcs.num_vfs || host_pf_disabled) - goto unlock; + goto free; /* Number of VFs can only change from "0 to x" or "x to 0". */ if (esw->esw_funcs.num_vfs > 0) { @@ -3686,53 +3681,86 @@ esw_vfs_changed_event_handler(struct mlx5_eswitch *esw, int work_gen, err = mlx5_eswitch_load_vf_vports(esw, new_num_vfs, MLX5_VPORT_UC_ADDR_CHANGE); - if (err) { - devl_unlock(devlink); - return; - } + if (err) + goto free; } esw->esw_funcs.num_vfs = new_num_vfs; -unlock: - devl_unlock(devlink); +free: + kvfree(out); } -static void esw_functions_changed_event_handler(struct work_struct *work) +static void esw_wq_handler(struct work_struct *work) { struct mlx5_host_work *host_work; struct mlx5_eswitch *esw; - const u32 *out; + 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); - out = mlx5_esw_query_functions(esw->dev); - if (IS_ERR(out)) - goto out; + /* 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; - esw_vfs_changed_event_handler(esw, host_work->work_gen, out); - kvfree(out); -out: + 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 (work_gen != atomic_read(&esw->generation)) + goto unlock; + + host_work->func(esw); + +unlock: + devl_unlock(devlink); +free: kfree(host_work); } -int mlx5_esw_funcs_changed_handler(struct notifier_block *nb, unsigned long type, void *data) +static int mlx5_esw_add_work(struct mlx5_eswitch *esw, + void (*func)(struct mlx5_eswitch *esw)) { - struct mlx5_esw_functions *esw_funcs; struct mlx5_host_work *host_work; - struct mlx5_eswitch *esw; host_work = kzalloc_obj(*host_work, GFP_ATOMIC); if (!host_work) - return NOTIFY_DONE; + return -ENOMEM; + + host_work->esw = esw; + host_work->work_gen = atomic_read(&esw->generation); + + host_work->func = func; + INIT_WORK(&host_work->work, esw_wq_handler); + queue_work(esw->work_queue, &host_work->work); + + return 0; +} + +int mlx5_esw_funcs_changed_handler(struct notifier_block *nb, + unsigned long type, void *data) +{ + struct mlx5_esw_functions *esw_funcs; + struct mlx5_eswitch *esw; + int ret; esw_funcs = mlx5_nb_cof(nb, struct mlx5_esw_functions, nb); esw = container_of(esw_funcs, struct mlx5_eswitch, esw_funcs); - host_work->esw = esw; - host_work->work_gen = atomic_read(&esw_funcs->generation); - - INIT_WORK(&host_work->work, esw_functions_changed_event_handler); - queue_work(esw->work_queue, &host_work->work); + ret = mlx5_esw_add_work(esw, esw_vfs_changed_event_handler); + if (ret) + return NOTIFY_DONE; return NOTIFY_OK; }