net/mlx5e: Make IPsec offload work together with eswitch and TC
authorJianbo Liu <jianbol@nvidia.com>
Mon, 31 Jul 2023 11:28:21 +0000 (14:28 +0300)
committerJakub Kicinski <kuba@kernel.org>
Thu, 3 Aug 2023 01:37:30 +0000 (18:37 -0700)
The eswitch mode is not allowed to change if there are any IPsec rules.
Besides, by using mlx5_esw_try_lock() to get eswitch mode lock, IPsec
rules are not allowed to be offloaded if there are any TC rules.

Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Link: https://lore.kernel.org/r/e442b512b21a931fbdfb87d57ae428c37badd58a.1690802064.git.leon@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c
drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c

index 36704c9fce33d479106a36f3e5faa4f49aae5d6e..7eb926b527daa1d040797a561c32bb435c537089 100644 (file)
@@ -254,8 +254,6 @@ static void rx_destroy(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec,
        mlx5_del_flow_rules(rx->sa.rule);
        mlx5_destroy_flow_group(rx->sa.group);
        mlx5_destroy_flow_table(rx->ft.sa);
-       if (rx->allow_tunnel_mode)
-               mlx5_eswitch_unblock_encap(mdev);
        if (rx == ipsec->rx_esw) {
                mlx5_esw_ipsec_rx_status_destroy(ipsec, rx);
        } else {
@@ -359,8 +357,6 @@ static int rx_create(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec,
                goto err_add;
 
        /* Create FT */
-       if (mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_TUNNEL)
-               rx->allow_tunnel_mode = mlx5_eswitch_block_encap(mdev);
        if (rx->allow_tunnel_mode)
                flags = MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT;
        ft = ipsec_ft_create(attr.ns, attr.sa_level, attr.prio, 2, flags);
@@ -415,8 +411,6 @@ err_pol_ft:
 err_fs:
        mlx5_destroy_flow_table(rx->ft.sa);
 err_fs_ft:
-       if (rx->allow_tunnel_mode)
-               mlx5_eswitch_unblock_encap(mdev);
        mlx5_del_flow_rules(rx->status.rule);
        mlx5_modify_header_dealloc(mdev, rx->status.modify_hdr);
 err_add:
@@ -434,13 +428,26 @@ static int rx_get(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec,
        if (rx->ft.refcnt)
                goto skip;
 
+       if (mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_TUNNEL)
+               rx->allow_tunnel_mode = mlx5_eswitch_block_encap(mdev);
+
+       err = mlx5_eswitch_block_mode_trylock(mdev);
+       if (err)
+               goto err_out;
+
        err = rx_create(mdev, ipsec, rx, family);
+       mlx5_eswitch_block_mode_unlock(mdev, err);
        if (err)
-               return err;
+               goto err_out;
 
 skip:
        rx->ft.refcnt++;
        return 0;
+
+err_out:
+       if (rx->allow_tunnel_mode)
+               mlx5_eswitch_unblock_encap(mdev);
+       return err;
 }
 
 static void rx_put(struct mlx5e_ipsec *ipsec, struct mlx5e_ipsec_rx *rx,
@@ -449,7 +456,12 @@ static void rx_put(struct mlx5e_ipsec *ipsec, struct mlx5e_ipsec_rx *rx,
        if (--rx->ft.refcnt)
                return;
 
+       mlx5_eswitch_unblock_mode_lock(ipsec->mdev);
        rx_destroy(ipsec->mdev, ipsec, rx, family);
+       mlx5_eswitch_unblock_mode_unlock(ipsec->mdev);
+
+       if (rx->allow_tunnel_mode)
+               mlx5_eswitch_unblock_encap(ipsec->mdev);
 }
 
 static struct mlx5e_ipsec_rx *rx_ft_get(struct mlx5_core_dev *mdev,
@@ -569,8 +581,6 @@ static void tx_destroy(struct mlx5e_ipsec *ipsec, struct mlx5e_ipsec_tx *tx,
                mlx5_destroy_flow_group(tx->sa.group);
        }
        mlx5_destroy_flow_table(tx->ft.sa);
-       if (tx->allow_tunnel_mode)
-               mlx5_eswitch_unblock_encap(ipsec->mdev);
        mlx5_del_flow_rules(tx->status.rule);
        mlx5_destroy_flow_table(tx->ft.status);
 }
@@ -611,8 +621,6 @@ static int tx_create(struct mlx5e_ipsec *ipsec, struct mlx5e_ipsec_tx *tx,
        if (err)
                goto err_status_rule;
 
-       if (mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_TUNNEL)
-               tx->allow_tunnel_mode = mlx5_eswitch_block_encap(mdev);
        if (tx->allow_tunnel_mode)
                flags = MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT;
        ft = ipsec_ft_create(tx->ns, attr.sa_level, attr.prio, 4, flags);
@@ -679,8 +687,6 @@ err_pol_ft:
 err_sa_miss:
        mlx5_destroy_flow_table(tx->ft.sa);
 err_sa_ft:
-       if (tx->allow_tunnel_mode)
-               mlx5_eswitch_unblock_encap(mdev);
        mlx5_del_flow_rules(tx->status.rule);
 err_status_rule:
        mlx5_destroy_flow_table(tx->ft.status);
@@ -714,16 +720,32 @@ static int tx_get(struct mlx5_core_dev *mdev, struct mlx5e_ipsec *ipsec,
        if (tx->ft.refcnt)
                goto skip;
 
-       err = tx_create(ipsec, tx, ipsec->roce);
+       if (mlx5_ipsec_device_caps(mdev) & MLX5_IPSEC_CAP_TUNNEL)
+               tx->allow_tunnel_mode = mlx5_eswitch_block_encap(mdev);
+
+       err = mlx5_eswitch_block_mode_trylock(mdev);
        if (err)
-               return err;
+               goto err_out;
+
+       err = tx_create(ipsec, tx, ipsec->roce);
+       if (err) {
+               mlx5_eswitch_block_mode_unlock(mdev, err);
+               goto err_out;
+       }
 
        if (tx == ipsec->tx_esw)
                ipsec_esw_tx_ft_policy_set(mdev, tx->ft.pol);
 
+       mlx5_eswitch_block_mode_unlock(mdev, err);
+
 skip:
        tx->ft.refcnt++;
        return 0;
+
+err_out:
+       if (tx->allow_tunnel_mode)
+               mlx5_eswitch_unblock_encap(mdev);
+       return err;
 }
 
 static void tx_put(struct mlx5e_ipsec *ipsec, struct mlx5e_ipsec_tx *tx)
@@ -731,10 +753,17 @@ static void tx_put(struct mlx5e_ipsec *ipsec, struct mlx5e_ipsec_tx *tx)
        if (--tx->ft.refcnt)
                return;
 
+       mlx5_eswitch_unblock_mode_lock(ipsec->mdev);
+
        if (tx == ipsec->tx_esw)
                ipsec_esw_tx_ft_policy_set(ipsec->mdev, NULL);
 
        tx_destroy(ipsec, tx, ipsec->roce);
+
+       mlx5_eswitch_unblock_mode_unlock(ipsec->mdev);
+
+       if (tx->allow_tunnel_mode)
+               mlx5_eswitch_unblock_encap(ipsec->mdev);
 }
 
 static struct mlx5_flow_table *tx_ft_get_policy(struct mlx5_core_dev *mdev,
index c7b5faae20a7c05893761cbebe83e3aa5c7e62e3..f4b52ab1ff0783f136e0ab4ac6e40b42aa5e2e08 100644 (file)
@@ -270,6 +270,7 @@ struct mlx5_esw_offload {
        u8 inline_mode;
        atomic64_t num_flows;
        u64 num_block_encap;
+       u64 num_block_mode;
        enum devlink_eswitch_encap_mode encap;
        struct ida vport_metadata_ida;
        unsigned int host_number; /* ECPF supports one external host */
@@ -784,6 +785,11 @@ int mlx5_eswitch_reload_reps(struct mlx5_eswitch *esw);
 bool mlx5_eswitch_block_encap(struct mlx5_core_dev *dev);
 void mlx5_eswitch_unblock_encap(struct mlx5_core_dev *dev);
 
+int mlx5_eswitch_block_mode_trylock(struct mlx5_core_dev *dev);
+void mlx5_eswitch_block_mode_unlock(struct mlx5_core_dev *dev, int err);
+void mlx5_eswitch_unblock_mode_lock(struct mlx5_core_dev *dev);
+void mlx5_eswitch_unblock_mode_unlock(struct mlx5_core_dev *dev);
+
 static inline int mlx5_eswitch_num_vfs(struct mlx5_eswitch *esw)
 {
        if (mlx5_esw_allowed(esw))
@@ -863,6 +869,14 @@ static inline bool mlx5_eswitch_block_encap(struct mlx5_core_dev *dev)
 static inline void mlx5_eswitch_unblock_encap(struct mlx5_core_dev *dev)
 {
 }
+
+static inline int mlx5_eswitch_block_mode_trylock(struct mlx5_core_dev *dev) { return 0; }
+
+static inline void mlx5_eswitch_block_mode_unlock(struct mlx5_core_dev *dev, int err) {}
+
+static inline void mlx5_eswitch_unblock_mode_lock(struct mlx5_core_dev *dev) {}
+
+static inline void mlx5_eswitch_unblock_mode_unlock(struct mlx5_core_dev *dev) {}
 #endif /* CONFIG_MLX5_ESWITCH */
 
 #endif /* __MLX5_ESWITCH_H__ */
index dc22a7f959e3fb636dfaa909d90897719b9e9e78..6a7e1955eddf2eb88d888512141d22452ecc5ff8 100644 (file)
@@ -3518,6 +3518,69 @@ static bool esw_offloads_devlink_ns_eq_netdev_ns(struct devlink *devlink)
        return net_eq(devl_net, netdev_net);
 }
 
+int mlx5_eswitch_block_mode_trylock(struct mlx5_core_dev *dev)
+{
+       struct devlink *devlink = priv_to_devlink(dev);
+       struct mlx5_eswitch *esw;
+       int err;
+
+       devl_lock(devlink);
+       esw = mlx5_devlink_eswitch_get(devlink);
+       if (IS_ERR(esw)) {
+               /* Failure means no eswitch => not possible to change eswitch mode */
+               devl_unlock(devlink);
+               return 0;
+       }
+
+       err = mlx5_esw_try_lock(esw);
+       if (err < 0) {
+               devl_unlock(devlink);
+               return err;
+       }
+
+       return 0;
+}
+
+void mlx5_eswitch_block_mode_unlock(struct mlx5_core_dev *dev, int err)
+{
+       struct devlink *devlink = priv_to_devlink(dev);
+       struct mlx5_eswitch *esw;
+
+       esw = mlx5_devlink_eswitch_get(devlink);
+       if (IS_ERR(esw))
+               return;
+
+       if (!err)
+               esw->offloads.num_block_mode++;
+       mlx5_esw_unlock(esw);
+       devl_unlock(devlink);
+}
+
+void mlx5_eswitch_unblock_mode_lock(struct mlx5_core_dev *dev)
+{
+       struct devlink *devlink = priv_to_devlink(dev);
+       struct mlx5_eswitch *esw;
+
+       esw = mlx5_devlink_eswitch_get(devlink);
+       if (IS_ERR(esw))
+               return;
+
+       down_write(&esw->mode_lock);
+}
+
+void mlx5_eswitch_unblock_mode_unlock(struct mlx5_core_dev *dev)
+{
+       struct devlink *devlink = priv_to_devlink(dev);
+       struct mlx5_eswitch *esw;
+
+       esw = mlx5_devlink_eswitch_get(devlink);
+       if (IS_ERR(esw))
+               return;
+
+       esw->offloads.num_block_mode--;
+       up_write(&esw->mode_lock);
+}
+
 int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
                                  struct netlink_ext_ack *extack)
 {
@@ -3551,6 +3614,13 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
        if (cur_mlx5_mode == mlx5_mode)
                goto unlock;
 
+       if (esw->offloads.num_block_mode) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Can't change eswitch mode when IPsec SA and/or policies are configured");
+               err = -EOPNOTSUPP;
+               goto unlock;
+       }
+
        mlx5_eswitch_disable_locked(esw);
        if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV) {
                if (mlx5_devlink_trap_get_num_active(esw->dev)) {