net/mlx5: E-Switch, Protect changing mode while adding rules
authorRoi Dayan <roid@nvidia.com>
Wed, 16 Sep 2020 07:11:47 +0000 (10:11 +0300)
committerSaeed Mahameed <saeedm@nvidia.com>
Tue, 16 Mar 2021 23:48:43 +0000 (16:48 -0700)
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: Roi Dayan <roid@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
Signed-off-by: Saeed Mahameed <saeedm@nvidia.com>
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c

index b3bf7bb7b97e7fefa8fb3b3e0bb2a4220ecc7258..730f33ada90a1aaa90aa4da2f2415a4fe8e72f9b 100644 (file)
@@ -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 @@ rcu_unlock:
        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:
index ddee2aefe8b9b0a6f7b91756abc4be13456b18c6..6b260dacf853f1b71f6f956794dabf509e41e353 100644 (file)
@@ -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);
+}
index b149d1d2c1509c2e934d3faf8d453f367b909b11..56d85cedb9bda10d737c0451f3925f12d56fc7be 100644 (file)
@@ -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; }
index 5e2712521fec66fcfb96506c96c8a8f4b0c33c72..8e7a702e23a3a659a1ea39b65ac30f40a01563b8 100644 (file)
@@ -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;
 }