Commit 7dc84de9 authored by Roi Dayan's avatar Roi Dayan Committed by Saeed Mahameed
Browse files

net/mlx5: E-Switch, Protect changing mode while adding rules



We re-use the native NIC port net device instance for the Uplink
representor, a driver currently cannot unbind TC setup callback
actively, hence protect changing E-Switch mode while adding rules.

Signed-off-by: default avatarRoi Dayan <roid@nvidia.com>
Reviewed-by: default avatarParav Pandit <parav@nvidia.com>
Signed-off-by: default avatarSaeed Mahameed <saeedm@nvidia.com>
parent c55479d0
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -4323,6 +4323,11 @@ int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,
	struct mlx5e_tc_flow *flow;
	int err = 0;

	if (!mlx5_esw_hold(priv->mdev))
		return -EAGAIN;

	mlx5_esw_get(priv->mdev);

	rcu_read_lock();
	flow = rhashtable_lookup(tc_ht, &f->cookie, tc_ht_params);
	if (flow) {
@@ -4360,11 +4365,14 @@ int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,
	if (err)
		goto err_free;

	mlx5_esw_release(priv->mdev);
	return 0;

err_free:
	mlx5e_flow_put(priv, flow);
out:
	mlx5_esw_put(priv->mdev);
	mlx5_esw_release(priv->mdev);
	return err;
}

@@ -4404,6 +4412,7 @@ int mlx5e_delete_flower(struct net_device *dev, struct mlx5e_priv *priv,
	trace_mlx5e_delete_flower(f);
	mlx5e_flow_put(priv, flow);

	mlx5_esw_put(priv->mdev);
	return 0;

errout:
+93 −0
Original line number Diff line number Diff line
@@ -435,6 +435,7 @@ static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw)
	esw->fdb_table.legacy.addr_grp = NULL;
	esw->fdb_table.legacy.allmulti_grp = NULL;
	esw->fdb_table.legacy.promisc_grp = NULL;
	atomic64_set(&esw->user_count, 0);
}

static int esw_create_legacy_table(struct mlx5_eswitch *esw)
@@ -442,6 +443,7 @@ static int esw_create_legacy_table(struct mlx5_eswitch *esw)
	int err;

	memset(&esw->fdb_table.legacy, 0, sizeof(struct legacy_fdb));
	atomic64_set(&esw->user_count, 0);

	err = esw_create_legacy_vepa_table(esw);
	if (err)
@@ -2581,3 +2583,94 @@ void mlx5_esw_event_notifier_unregister(struct mlx5_eswitch *esw, struct notifie
{
	blocking_notifier_chain_unregister(&esw->n_head, nb);
}

/**
 * mlx5_esw_hold() - Try to take a read lock on esw mode lock.
 * @mdev: mlx5 core device.
 *
 * Should be called by esw resources callers.
 *
 * Return: true on success or false.
 */
bool mlx5_esw_hold(struct mlx5_core_dev *mdev)
{
	struct mlx5_eswitch *esw = mdev->priv.eswitch;

	/* e.g. VF doesn't have eswitch so nothing to do */
	if (!ESW_ALLOWED(esw))
		return true;

	if (down_read_trylock(&esw->mode_lock) != 0)
		return true;

	return false;
}

/**
 * mlx5_esw_release() - Release a read lock on esw mode lock.
 * @mdev: mlx5 core device.
 */
void mlx5_esw_release(struct mlx5_core_dev *mdev)
{
	struct mlx5_eswitch *esw = mdev->priv.eswitch;

	if (ESW_ALLOWED(esw))
		up_read(&esw->mode_lock);
}

/**
 * mlx5_esw_get() - Increase esw user count.
 * @mdev: mlx5 core device.
 */
void mlx5_esw_get(struct mlx5_core_dev *mdev)
{
	struct mlx5_eswitch *esw = mdev->priv.eswitch;

	if (ESW_ALLOWED(esw))
		atomic64_inc(&esw->user_count);
}

/**
 * mlx5_esw_put() - Decrease esw user count.
 * @mdev: mlx5 core device.
 */
void mlx5_esw_put(struct mlx5_core_dev *mdev)
{
	struct mlx5_eswitch *esw = mdev->priv.eswitch;

	if (ESW_ALLOWED(esw))
		atomic64_dec_if_positive(&esw->user_count);
}

/**
 * mlx5_esw_try_lock() - Take a write lock on esw mode lock.
 * @esw: eswitch device.
 *
 * Should be called by esw mode change routine.
 *
 * Return:
 * * 0       - esw mode if successfully locked and refcount is 0.
 * * -EBUSY  - refcount is not 0.
 * * -EINVAL - In the middle of switching mode or lock is already held.
 */
int mlx5_esw_try_lock(struct mlx5_eswitch *esw)
{
	if (down_write_trylock(&esw->mode_lock) == 0)
		return -EINVAL;

	if (atomic64_read(&esw->user_count) > 0) {
		up_write(&esw->mode_lock);
		return -EBUSY;
	}

	return esw->mode;
}

/**
 * mlx5_esw_unlock() - Release write lock on esw mode lock
 * @esw: eswitch device.
 */
void mlx5_esw_unlock(struct mlx5_eswitch *esw)
{
	up_write(&esw->mode_lock);
}
+9 −0
Original line number Diff line number Diff line
@@ -272,6 +272,7 @@ struct mlx5_eswitch {
	 * user commands, i.e. sriov state change, devlink commands.
	 */
	struct rw_semaphore mode_lock;
	atomic64_t user_count;

	struct {
		bool            enabled;
@@ -761,6 +762,14 @@ struct mlx5_esw_event_info {

int mlx5_esw_event_notifier_register(struct mlx5_eswitch *esw, struct notifier_block *n);
void mlx5_esw_event_notifier_unregister(struct mlx5_eswitch *esw, struct notifier_block *n);

bool mlx5_esw_hold(struct mlx5_core_dev *dev);
void mlx5_esw_release(struct mlx5_core_dev *dev);
void mlx5_esw_get(struct mlx5_core_dev *dev);
void mlx5_esw_put(struct mlx5_core_dev *dev);
int mlx5_esw_try_lock(struct mlx5_eswitch *esw);
void mlx5_esw_unlock(struct mlx5_eswitch *esw);

#else  /* CONFIG_MLX5_ESWITCH */
/* eswitch API stubs */
static inline int  mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; }
+11 −3
Original line number Diff line number Diff line
@@ -1854,6 +1854,7 @@ static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw)
	/* Holds true only as long as DMFS is the default */
	mlx5_flow_namespace_set_mode(esw->fdb_table.offloads.ns,
				     MLX5_FLOW_STEERING_MODE_DMFS);
	atomic64_set(&esw->user_count, 0);
}

static int esw_create_offloads_table(struct mlx5_eswitch *esw)
@@ -2584,6 +2585,7 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw)
	memset(&esw->fdb_table.offloads, 0, sizeof(struct offloads_fdb));
	mutex_init(&esw->fdb_table.offloads.vports.lock);
	hash_init(esw->fdb_table.offloads.vports.table);
	atomic64_set(&esw->user_count, 0);

	indir = mlx5_esw_indir_table_init();
	if (IS_ERR(indir)) {
@@ -2925,8 +2927,14 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
	if (esw_mode_from_devlink(mode, &mlx5_mode))
		return -EINVAL;

	down_write(&esw->mode_lock);
	cur_mlx5_mode = esw->mode;
	err = mlx5_esw_try_lock(esw);
	if (err < 0) {
		NL_SET_ERR_MSG_MOD(extack, "Can't change mode, E-Switch is busy");
		return err;
	}
	cur_mlx5_mode = err;
	err = 0;

	if (cur_mlx5_mode == mlx5_mode)
		goto unlock;

@@ -2938,7 +2946,7 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
		err = -EINVAL;

unlock:
	up_write(&esw->mode_lock);
	mlx5_esw_unlock(esw);
	return err;
}