net/mlx5: Add Fast teardown support
authorFeras Daoud <ferasda@mellanox.com>
Thu, 9 Aug 2018 06:55:21 +0000 (09:55 +0300)
committerSaeed Mahameed <saeedm@mellanox.com>
Wed, 3 Oct 2018 23:18:00 +0000 (16:18 -0700)
Today mlx5 devices support two teardown modes:
1- Regular teardown
2- Force teardown

This change introduces the enhanced version of the "Force teardown" that
allows SW to perform teardown in a faster way without the need to reclaim
all the pages.

Fast teardown provides the following advantages:
1- Fix a FW race condition that could cause command timeout
2- Avoid moving to polling mode
3- Close the vport to prevent PCI ACK to be sent without been scatter
to memory

Signed-off-by: Feras Daoud <ferasda@mellanox.com>
Reviewed-by: Majd Dibbiny <majd@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
drivers/net/ethernet/mellanox/mlx5/core/fw.c
drivers/net/ethernet/mellanox/mlx5/core/health.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
include/linux/mlx5/device.h
include/linux/mlx5/mlx5_ifc.h

index 41ad24f0de2cf9d171e586df3b9d167515d3cb03..1ab6f7e3bec6268d542e8adecccca38f295edc16 100644 (file)
@@ -250,7 +250,7 @@ int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev)
        if (ret)
                return ret;
 
-       force_state = MLX5_GET(teardown_hca_out, out, force_state);
+       force_state = MLX5_GET(teardown_hca_out, out, state);
        if (force_state == MLX5_TEARDOWN_HCA_OUT_FORCE_STATE_FAIL) {
                mlx5_core_warn(dev, "teardown with force mode failed, doing normal teardown\n");
                return -EIO;
@@ -259,6 +259,54 @@ int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev)
        return 0;
 }
 
+#define MLX5_FAST_TEARDOWN_WAIT_MS   3000
+int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev)
+{
+       unsigned long end, delay_ms = MLX5_FAST_TEARDOWN_WAIT_MS;
+       u32 out[MLX5_ST_SZ_DW(teardown_hca_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(teardown_hca_in)] = {0};
+       int state;
+       int ret;
+
+       if (!MLX5_CAP_GEN(dev, fast_teardown)) {
+               mlx5_core_dbg(dev, "fast teardown is not supported in the firmware\n");
+               return -EOPNOTSUPP;
+       }
+
+       MLX5_SET(teardown_hca_in, in, opcode, MLX5_CMD_OP_TEARDOWN_HCA);
+       MLX5_SET(teardown_hca_in, in, profile,
+                MLX5_TEARDOWN_HCA_IN_PROFILE_PREPARE_FAST_TEARDOWN);
+
+       ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       if (ret)
+               return ret;
+
+       state = MLX5_GET(teardown_hca_out, out, state);
+       if (state == MLX5_TEARDOWN_HCA_OUT_FORCE_STATE_FAIL) {
+               mlx5_core_warn(dev, "teardown with fast mode failed\n");
+               return -EIO;
+       }
+
+       mlx5_set_nic_state(dev, MLX5_NIC_IFC_DISABLED);
+
+       /* Loop until device state turns to disable */
+       end = jiffies + msecs_to_jiffies(delay_ms);
+       do {
+               if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
+                       break;
+
+               cond_resched();
+       } while (!time_after(jiffies, end));
+
+       if (mlx5_get_nic_state(dev) != MLX5_NIC_IFC_DISABLED) {
+               dev_err(&dev->pdev->dev, "NIC IFC still %d after %lums.\n",
+                       mlx5_get_nic_state(dev), delay_ms);
+               return -EIO;
+       }
+
+       return 0;
+}
+
 enum mlxsw_reg_mcc_instruction {
        MLX5_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE = 0x01,
        MLX5_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE = 0x02,
index 9f39aeca863f321fe169fb3d69ef1cff98bbaf18..43118de8ee99a19b29ed687c84b45b043f1cab7f 100644 (file)
@@ -58,23 +58,26 @@ enum {
        MLX5_HEALTH_SYNDR_HIGH_TEMP             = 0x10
 };
 
-enum {
-       MLX5_NIC_IFC_FULL               = 0,
-       MLX5_NIC_IFC_DISABLED           = 1,
-       MLX5_NIC_IFC_NO_DRAM_NIC        = 2,
-       MLX5_NIC_IFC_INVALID            = 3
-};
-
 enum {
        MLX5_DROP_NEW_HEALTH_WORK,
        MLX5_DROP_NEW_RECOVERY_WORK,
 };
 
-static u8 get_nic_state(struct mlx5_core_dev *dev)
+u8 mlx5_get_nic_state(struct mlx5_core_dev *dev)
 {
        return (ioread32be(&dev->iseg->cmdq_addr_l_sz) >> 8) & 3;
 }
 
+void mlx5_set_nic_state(struct mlx5_core_dev *dev, u8 state)
+{
+       u32 cur_cmdq_addr_l_sz;
+
+       cur_cmdq_addr_l_sz = ioread32be(&dev->iseg->cmdq_addr_l_sz);
+       iowrite32be((cur_cmdq_addr_l_sz & 0xFFFFF000) |
+                   state << MLX5_NIC_IFC_OFFSET,
+                   &dev->iseg->cmdq_addr_l_sz);
+}
+
 static void trigger_cmd_completions(struct mlx5_core_dev *dev)
 {
        unsigned long flags;
@@ -103,7 +106,7 @@ static int in_fatal(struct mlx5_core_dev *dev)
        struct mlx5_core_health *health = &dev->priv.health;
        struct health_buffer __iomem *h = health->health;
 
-       if (get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
+       if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
                return 1;
 
        if (ioread32be(&h->fw_ver) == 0xffffffff)
@@ -133,7 +136,7 @@ unlock:
 
 static void mlx5_handle_bad_state(struct mlx5_core_dev *dev)
 {
-       u8 nic_interface = get_nic_state(dev);
+       u8 nic_interface = mlx5_get_nic_state(dev);
 
        switch (nic_interface) {
        case MLX5_NIC_IFC_FULL:
@@ -168,7 +171,7 @@ static void health_recover(struct work_struct *work)
        priv = container_of(health, struct mlx5_priv, health);
        dev = container_of(priv, struct mlx5_core_dev, priv);
 
-       nic_state = get_nic_state(dev);
+       nic_state = mlx5_get_nic_state(dev);
        if (nic_state == MLX5_NIC_IFC_INVALID) {
                dev_err(&dev->pdev->dev, "health recovery flow aborted since the nic state is invalid\n");
                return;
index b5e9f664fc66758d5642b18e2396503baf351415..28132c7dc05f252c6287a3fa6a8a37415de4872c 100644 (file)
@@ -1594,12 +1594,17 @@ static const struct pci_error_handlers mlx5_err_handler = {
 
 static int mlx5_try_fast_unload(struct mlx5_core_dev *dev)
 {
-       int ret;
+       bool fast_teardown = false, force_teardown = false;
+       int ret = 1;
+
+       fast_teardown = MLX5_CAP_GEN(dev, fast_teardown);
+       force_teardown = MLX5_CAP_GEN(dev, force_teardown);
+
+       mlx5_core_dbg(dev, "force teardown firmware support=%d\n", force_teardown);
+       mlx5_core_dbg(dev, "fast teardown firmware support=%d\n", fast_teardown);
 
-       if (!MLX5_CAP_GEN(dev, force_teardown)) {
-               mlx5_core_dbg(dev, "force teardown is not supported in the firmware\n");
+       if (!fast_teardown && !force_teardown)
                return -EOPNOTSUPP;
-       }
 
        if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
                mlx5_core_dbg(dev, "Device in internal error state, giving up\n");
@@ -1612,13 +1617,19 @@ static int mlx5_try_fast_unload(struct mlx5_core_dev *dev)
        mlx5_drain_health_wq(dev);
        mlx5_stop_health_poll(dev, false);
 
+       ret = mlx5_cmd_fast_teardown_hca(dev);
+       if (!ret)
+               goto succeed;
+
        ret = mlx5_cmd_force_teardown_hca(dev);
-       if (ret) {
-               mlx5_core_dbg(dev, "Firmware couldn't do fast unload error: %d\n", ret);
-               mlx5_start_health_poll(dev);
-               return ret;
-       }
+       if (!ret)
+               goto succeed;
+
+       mlx5_core_dbg(dev, "Firmware couldn't do fast unload error: %d\n", ret);
+       mlx5_start_health_poll(dev);
+       return ret;
 
+succeed:
        mlx5_enter_error_state(dev, true);
 
        /* Some platforms requiring freeing the IRQ's in the shutdown
index b4134fa0bba36a3a4545dc65af2cec86b0984f0d..cc298527baf1687d3bafb8f5b345b9c6a8ad1aa9 100644 (file)
@@ -95,6 +95,8 @@ int mlx5_query_board_id(struct mlx5_core_dev *dev);
 int mlx5_cmd_init_hca(struct mlx5_core_dev *dev, uint32_t *sw_owner_id);
 int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev);
 int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev);
+int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev);
+
 void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
                     unsigned long param);
 void mlx5_core_page_fault(struct mlx5_core_dev *dev,
@@ -214,4 +216,14 @@ int mlx5_lag_allow(struct mlx5_core_dev *dev);
 int mlx5_lag_forbid(struct mlx5_core_dev *dev);
 
 void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol);
+
+enum {
+       MLX5_NIC_IFC_FULL               = 0,
+       MLX5_NIC_IFC_DISABLED           = 1,
+       MLX5_NIC_IFC_NO_DRAM_NIC        = 2,
+       MLX5_NIC_IFC_INVALID            = 3
+};
+
+u8 mlx5_get_nic_state(struct mlx5_core_dev *dev);
+void mlx5_set_nic_state(struct mlx5_core_dev *dev, u8 state);
 #endif /* __MLX5_CORE_H__ */
index 11fa4e66afc5d5cc983ebf40522f79595031ea58..e9b502d5bcc10f19abbb1f0c75d53884e79ad595 100644 (file)
@@ -504,6 +504,10 @@ struct health_buffer {
        __be16          ext_synd;
 };
 
+enum mlx5_cmd_addr_l_sz_offset {
+       MLX5_NIC_IFC_OFFSET = 8,
+};
+
 struct mlx5_init_seg {
        __be32                  fw_rev;
        __be32                  cmdif_rev_fw_sub;
index f043d65b9bac2d65b0b8c8053e22a9051d03769f..6e8a882052b1973ed845098179941e4e94a35732 100644 (file)
@@ -896,7 +896,8 @@ struct mlx5_ifc_cmd_hca_cap_bits {
        u8         log_max_mkey[0x6];
        u8         reserved_at_f0[0x8];
        u8         dump_fill_mkey[0x1];
-       u8         reserved_at_f9[0x3];
+       u8         reserved_at_f9[0x2];
+       u8         fast_teardown[0x1];
        u8         log_max_eq[0x4];
 
        u8         max_indirection[0x8];
@@ -3352,12 +3353,13 @@ struct mlx5_ifc_teardown_hca_out_bits {
 
        u8         reserved_at_40[0x3f];
 
-       u8         force_state[0x1];
+       u8         state[0x1];
 };
 
 enum {
        MLX5_TEARDOWN_HCA_IN_PROFILE_GRACEFUL_CLOSE  = 0x0,
        MLX5_TEARDOWN_HCA_IN_PROFILE_FORCE_CLOSE     = 0x1,
+       MLX5_TEARDOWN_HCA_IN_PROFILE_PREPARE_FAST_TEARDOWN = 0x2,
 };
 
 struct mlx5_ifc_teardown_hca_in_bits {