numam-dpdk/drivers/net/hns3/hns3_intr.c
Hongbo Zheng 80cd38e1b5 net/hns3: ignore function return on reset error path
There is a coverity defect related "Unchecked return value".

The internal static hns3_reset_err_handle function is reset error
process of hns3 PMD driver. If failure in reset process, it does not
mean that the network port is completely unavailable, so the command
interface between driver and firmware still needs to be initialized.
Regardless of whether the execution of the function named hns3_cmd_init
is successful or not, the next process after execution must be
continued, so there is no need to check the return value. If
hns3_cmd_init fails to execute, there will be corresponding log
information inside hns3_cmd_init.

This patch adds '(void)' Type conversion to avoid coverity warning.

Coverity issue: 349934
Fixes: 2790c6464725 ("net/hns3: support device reset")
Cc: stable@dpdk.org

Signed-off-by: Hongbo Zheng <zhenghongbo3@huawei.com>
Signed-off-by: Wei Hu (Xavier) <xavier.huwei@huawei.com>
2020-06-16 19:21:07 +02:00

1176 lines
33 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2018-2019 Hisilicon Limited.
*/
#include <stdbool.h>
#include <rte_atomic.h>
#include <rte_alarm.h>
#include <rte_cycles.h>
#include <rte_ethdev.h>
#include <rte_io.h>
#include <rte_malloc.h>
#include <rte_pci.h>
#include <rte_bus_pci.h>
#include "hns3_ethdev.h"
#include "hns3_logs.h"
#include "hns3_intr.h"
#include "hns3_regs.h"
#include "hns3_rxtx.h"
#define SWITCH_CONTEXT_US 10
/* offset in MSIX bd */
#define MAC_ERROR_OFFSET 1
#define PPP_PF_ERROR_OFFSET 2
#define PPU_PF_ERROR_OFFSET 3
#define RCB_ERROR_OFFSET 5
#define RCB_ERROR_STATUS_OFFSET 2
#define HNS3_CHECK_MERGE_CNT(val) \
do { \
if (val) \
hw->reset.stats.merge_cnt++; \
} while (0)
static const char *reset_string[HNS3_MAX_RESET] = {
"none", "vf_func", "vf_pf_func", "vf_full", "flr",
"vf_global", "pf_func", "global", "IMP",
};
const struct hns3_hw_error mac_afifo_tnl_int[] = {
{ .int_msk = BIT(0), .msg = "egu_cge_afifo_ecc_1bit_err",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = BIT(1), .msg = "egu_cge_afifo_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(2), .msg = "egu_lge_afifo_ecc_1bit_err",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = BIT(3), .msg = "egu_lge_afifo_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(4), .msg = "cge_igu_afifo_ecc_1bit_err",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = BIT(5), .msg = "cge_igu_afifo_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(6), .msg = "lge_igu_afifo_ecc_1bit_err",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = BIT(7), .msg = "lge_igu_afifo_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(8), .msg = "cge_igu_afifo_overflow_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(9), .msg = "lge_igu_afifo_overflow_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(10), .msg = "egu_cge_afifo_underrun_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(11), .msg = "egu_lge_afifo_underrun_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(12), .msg = "egu_ge_afifo_underrun_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(13), .msg = "ge_igu_afifo_overflow_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = 0, .msg = NULL,
.reset_level = HNS3_NONE_RESET}
};
const struct hns3_hw_error ppu_mpf_abnormal_int_st2[] = {
{ .int_msk = BIT(13), .msg = "rpu_rx_pkt_bit32_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(14), .msg = "rpu_rx_pkt_bit33_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(15), .msg = "rpu_rx_pkt_bit34_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(16), .msg = "rpu_rx_pkt_bit35_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(17), .msg = "rcb_tx_ring_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(18), .msg = "rcb_rx_ring_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(19), .msg = "rcb_tx_fbd_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(20), .msg = "rcb_rx_ebd_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(21), .msg = "rcb_tso_info_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(22), .msg = "rcb_tx_int_info_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(23), .msg = "rcb_rx_int_info_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(24), .msg = "tpu_tx_pkt_0_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(25), .msg = "tpu_tx_pkt_1_ecc_mbit_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(26), .msg = "rd_bus_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(27), .msg = "wr_bus_err",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(28), .msg = "reg_search_miss",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(29), .msg = "rx_q_search_miss",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = BIT(30), .msg = "ooo_ecc_err_detect",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = BIT(31), .msg = "ooo_ecc_err_multpl",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = 0, .msg = NULL,
.reset_level = HNS3_NONE_RESET}
};
const struct hns3_hw_error ssu_port_based_pf_int[] = {
{ .int_msk = BIT(0), .msg = "roc_pkt_without_key_port",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = BIT(9), .msg = "low_water_line_err_port",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = BIT(10), .msg = "hi_water_line_err_port",
.reset_level = HNS3_GLOBAL_RESET },
{ .int_msk = 0, .msg = NULL,
.reset_level = HNS3_NONE_RESET}
};
const struct hns3_hw_error ppp_pf_abnormal_int[] = {
{ .int_msk = BIT(0), .msg = "tx_vlan_tag_err",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = BIT(1), .msg = "rss_list_tc_unassigned_queue_err",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = 0, .msg = NULL,
.reset_level = HNS3_NONE_RESET}
};
const struct hns3_hw_error ppu_pf_abnormal_int[] = {
{ .int_msk = BIT(0), .msg = "over_8bd_no_fe",
.reset_level = HNS3_FUNC_RESET },
{ .int_msk = BIT(1), .msg = "tso_mss_cmp_min_err",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = BIT(2), .msg = "tso_mss_cmp_max_err",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = BIT(3), .msg = "tx_rd_fbd_poison",
.reset_level = HNS3_FUNC_RESET },
{ .int_msk = BIT(4), .msg = "rx_rd_ebd_poison",
.reset_level = HNS3_FUNC_RESET },
{ .int_msk = BIT(5), .msg = "buf_wait_timeout",
.reset_level = HNS3_NONE_RESET },
{ .int_msk = 0, .msg = NULL,
.reset_level = HNS3_NONE_RESET}
};
static int
config_ppp_err_intr(struct hns3_adapter *hns, uint32_t cmd, bool en)
{
struct hns3_hw *hw = &hns->hw;
struct hns3_cmd_desc desc[2];
int ret;
/* configure PPP error interrupts */
hns3_cmd_setup_basic_desc(&desc[0], cmd, false);
desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
hns3_cmd_setup_basic_desc(&desc[1], cmd, false);
if (cmd == HNS3_PPP_CMD0_INT_CMD) {
if (en) {
desc[0].data[0] =
rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT0_EN);
desc[0].data[1] =
rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT1_EN);
desc[0].data[4] =
rte_cpu_to_le_32(HNS3_PPP_PF_ERR_INT_EN);
}
desc[1].data[0] =
rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT0_EN_MASK);
desc[1].data[1] =
rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT1_EN_MASK);
desc[1].data[2] =
rte_cpu_to_le_32(HNS3_PPP_PF_ERR_INT_EN_MASK);
} else if (cmd == HNS3_PPP_CMD1_INT_CMD) {
if (en) {
desc[0].data[0] =
rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT2_EN);
desc[0].data[1] =
rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT3_EN);
}
desc[1].data[0] =
rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT2_EN_MASK);
desc[1].data[1] =
rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT3_EN_MASK);
}
ret = hns3_cmd_send(hw, &desc[0], 2);
if (ret)
hns3_err(hw, "fail to configure PPP error int: %d", ret);
return ret;
}
static int
enable_ppp_err_intr(struct hns3_adapter *hns, bool en)
{
int ret;
ret = config_ppp_err_intr(hns, HNS3_PPP_CMD0_INT_CMD, en);
if (ret)
return ret;
return config_ppp_err_intr(hns, HNS3_PPP_CMD1_INT_CMD, en);
}
static int
enable_ssu_err_intr(struct hns3_adapter *hns, bool en)
{
struct hns3_hw *hw = &hns->hw;
struct hns3_cmd_desc desc[2];
int ret;
/* configure SSU ecc error interrupts */
hns3_cmd_setup_basic_desc(&desc[0], HNS3_SSU_ECC_INT_CMD, false);
desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
hns3_cmd_setup_basic_desc(&desc[1], HNS3_SSU_ECC_INT_CMD, false);
if (en) {
desc[0].data[0] =
rte_cpu_to_le_32(HNS3_SSU_1BIT_ECC_ERR_INT_EN);
desc[0].data[1] =
rte_cpu_to_le_32(HNS3_SSU_MULTI_BIT_ECC_ERR_INT_EN);
desc[0].data[4] =
rte_cpu_to_le_32(HNS3_SSU_BIT32_ECC_ERR_INT_EN);
}
desc[1].data[0] = rte_cpu_to_le_32(HNS3_SSU_1BIT_ECC_ERR_INT_EN_MASK);
desc[1].data[1] =
rte_cpu_to_le_32(HNS3_SSU_MULTI_BIT_ECC_ERR_INT_EN_MASK);
desc[1].data[2] = rte_cpu_to_le_32(HNS3_SSU_BIT32_ECC_ERR_INT_EN_MASK);
ret = hns3_cmd_send(hw, &desc[0], 2);
if (ret) {
hns3_err(hw, "fail to configure SSU ECC error interrupt: %d",
ret);
return ret;
}
/* configure SSU common error interrupts */
hns3_cmd_setup_basic_desc(&desc[0], HNS3_SSU_COMMON_INT_CMD, false);
desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
hns3_cmd_setup_basic_desc(&desc[1], HNS3_SSU_COMMON_INT_CMD, false);
if (en) {
desc[0].data[0] = rte_cpu_to_le_32(HNS3_SSU_COMMON_INT_EN);
desc[0].data[1] =
rte_cpu_to_le_32(HNS3_SSU_PORT_BASED_ERR_INT_EN);
desc[0].data[2] =
rte_cpu_to_le_32(HNS3_SSU_FIFO_OVERFLOW_ERR_INT_EN);
}
desc[1].data[0] = rte_cpu_to_le_32(HNS3_SSU_COMMON_INT_EN_MASK |
HNS3_SSU_PORT_BASED_ERR_INT_EN_MASK);
desc[1].data[1] =
rte_cpu_to_le_32(HNS3_SSU_FIFO_OVERFLOW_ERR_INT_EN_MASK);
ret = hns3_cmd_send(hw, &desc[0], 2);
if (ret)
hns3_err(hw, "fail to configure SSU COMMON error intr: %d",
ret);
return ret;
}
static int
config_ppu_err_intrs(struct hns3_adapter *hns, uint32_t cmd, bool en)
{
struct hns3_hw *hw = &hns->hw;
struct hns3_cmd_desc desc[2];
int num = 1;
/* configure PPU error interrupts */
switch (cmd) {
case HNS3_PPU_MPF_ECC_INT_CMD:
hns3_cmd_setup_basic_desc(&desc[0], cmd, false);
desc[0].flag |= HNS3_CMD_FLAG_NEXT;
hns3_cmd_setup_basic_desc(&desc[1], cmd, false);
if (en) {
desc[0].data[0] = HNS3_PPU_MPF_ABNORMAL_INT0_EN;
desc[0].data[1] = HNS3_PPU_MPF_ABNORMAL_INT1_EN;
desc[1].data[3] = HNS3_PPU_MPF_ABNORMAL_INT3_EN;
desc[1].data[4] = HNS3_PPU_MPF_ABNORMAL_INT2_EN;
}
desc[1].data[0] = HNS3_PPU_MPF_ABNORMAL_INT0_EN_MASK;
desc[1].data[1] = HNS3_PPU_MPF_ABNORMAL_INT1_EN_MASK;
desc[1].data[2] = HNS3_PPU_MPF_ABNORMAL_INT2_EN_MASK;
desc[1].data[3] |= HNS3_PPU_MPF_ABNORMAL_INT3_EN_MASK;
num = 2;
break;
case HNS3_PPU_MPF_OTHER_INT_CMD:
hns3_cmd_setup_basic_desc(&desc[0], cmd, false);
if (en)
desc[0].data[0] = HNS3_PPU_MPF_ABNORMAL_INT2_EN2;
desc[0].data[2] = HNS3_PPU_MPF_ABNORMAL_INT2_EN2_MASK;
break;
case HNS3_PPU_PF_OTHER_INT_CMD:
hns3_cmd_setup_basic_desc(&desc[0], cmd, false);
if (en)
desc[0].data[0] = HNS3_PPU_PF_ABNORMAL_INT_EN;
desc[0].data[2] = HNS3_PPU_PF_ABNORMAL_INT_EN_MASK;
break;
default:
hns3_err(hw,
"Invalid cmd(%u) to configure PPU error interrupts.",
cmd);
return -EINVAL;
}
return hns3_cmd_send(hw, &desc[0], num);
}
static int
enable_ppu_err_intr(struct hns3_adapter *hns, bool en)
{
struct hns3_hw *hw = &hns->hw;
int ret;
ret = config_ppu_err_intrs(hns, HNS3_PPU_MPF_ECC_INT_CMD, en);
if (ret) {
hns3_err(hw, "fail to configure PPU MPF ECC error intr: %d",
ret);
return ret;
}
ret = config_ppu_err_intrs(hns, HNS3_PPU_MPF_OTHER_INT_CMD, en);
if (ret) {
hns3_err(hw, "fail to configure PPU MPF other intr: %d",
ret);
return ret;
}
ret = config_ppu_err_intrs(hns, HNS3_PPU_PF_OTHER_INT_CMD, en);
if (ret)
hns3_err(hw, "fail to configure PPU PF error interrupts: %d",
ret);
return ret;
}
static int
enable_mac_err_intr(struct hns3_adapter *hns, bool en)
{
struct hns3_hw *hw = &hns->hw;
struct hns3_cmd_desc desc;
int ret;
/* configure MAC common error interrupts */
hns3_cmd_setup_basic_desc(&desc, HNS3_MAC_COMMON_INT_EN, false);
if (en)
desc.data[0] = rte_cpu_to_le_32(HNS3_MAC_COMMON_ERR_INT_EN);
desc.data[1] = rte_cpu_to_le_32(HNS3_MAC_COMMON_ERR_INT_EN_MASK);
ret = hns3_cmd_send(hw, &desc, 1);
if (ret)
hns3_err(hw, "fail to configure MAC COMMON error intr: %d",
ret);
return ret;
}
static const struct hns3_hw_blk hw_blk[] = {
{
.name = "PPP",
.enable_err_intr = enable_ppp_err_intr,
},
{
.name = "SSU",
.enable_err_intr = enable_ssu_err_intr,
},
{
.name = "PPU",
.enable_err_intr = enable_ppu_err_intr,
},
{
.name = "MAC",
.enable_err_intr = enable_mac_err_intr,
},
{
.name = NULL,
.enable_err_intr = NULL,
}
};
int
hns3_enable_hw_error_intr(struct hns3_adapter *hns, bool en)
{
const struct hns3_hw_blk *module = hw_blk;
int ret = 0;
while (module->enable_err_intr) {
ret = module->enable_err_intr(hns, en);
if (ret)
return ret;
module++;
}
return ret;
}
static enum hns3_reset_level
hns3_find_highest_level(struct hns3_adapter *hns, const char *reg,
const struct hns3_hw_error *err, uint32_t err_sts)
{
enum hns3_reset_level reset_level = HNS3_FUNC_RESET;
struct hns3_hw *hw = &hns->hw;
bool need_reset = false;
while (err->msg) {
if (err->int_msk & err_sts) {
hns3_warn(hw, "%s %s found [error status=0x%x]",
reg, err->msg, err_sts);
if (err->reset_level != HNS3_NONE_RESET &&
err->reset_level >= reset_level) {
reset_level = err->reset_level;
need_reset = true;
}
}
err++;
}
if (need_reset)
return reset_level;
else
return HNS3_NONE_RESET;
}
static int
query_num_bds_in_msix(struct hns3_hw *hw, struct hns3_cmd_desc *desc_bd)
{
int ret;
hns3_cmd_setup_basic_desc(desc_bd, HNS3_QUERY_MSIX_INT_STS_BD_NUM,
true);
ret = hns3_cmd_send(hw, desc_bd, 1);
if (ret)
hns3_err(hw, "query num bds in msix failed: %d", ret);
return ret;
}
static int
query_all_mpf_msix_err(struct hns3_hw *hw, struct hns3_cmd_desc *desc,
uint32_t mpf_bd_num)
{
int ret;
hns3_cmd_setup_basic_desc(desc, HNS3_QUERY_CLEAR_ALL_MPF_MSIX_INT,
true);
desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
ret = hns3_cmd_send(hw, &desc[0], mpf_bd_num);
if (ret)
hns3_err(hw, "query all mpf msix err failed: %d", ret);
return ret;
}
static int
clear_all_mpf_msix_err(struct hns3_hw *hw, struct hns3_cmd_desc *desc,
uint32_t mpf_bd_num)
{
int ret;
hns3_cmd_reuse_desc(desc, false);
desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
ret = hns3_cmd_send(hw, desc, mpf_bd_num);
if (ret)
hns3_err(hw, "clear all mpf msix err failed: %d", ret);
return ret;
}
static int
query_all_pf_msix_err(struct hns3_hw *hw, struct hns3_cmd_desc *desc,
uint32_t pf_bd_num)
{
int ret;
hns3_cmd_setup_basic_desc(desc, HNS3_QUERY_CLEAR_ALL_PF_MSIX_INT, true);
desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
ret = hns3_cmd_send(hw, desc, pf_bd_num);
if (ret)
hns3_err(hw, "query all pf msix int cmd failed: %d", ret);
return ret;
}
static int
clear_all_pf_msix_err(struct hns3_hw *hw, struct hns3_cmd_desc *desc,
uint32_t pf_bd_num)
{
int ret;
hns3_cmd_reuse_desc(desc, false);
desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
ret = hns3_cmd_send(hw, desc, pf_bd_num);
if (ret)
hns3_err(hw, "clear all pf msix err failed: %d", ret);
return ret;
}
void
hns3_intr_unregister(const struct rte_intr_handle *hdl,
rte_intr_callback_fn cb_fn, void *cb_arg)
{
int retry_cnt = 0;
int ret;
do {
ret = rte_intr_callback_unregister(hdl, cb_fn, cb_arg);
if (ret >= 0) {
break;
} else if (ret != -EAGAIN) {
PMD_INIT_LOG(ERR, "Failed to unregister intr: %d", ret);
break;
}
rte_delay_ms(HNS3_INTR_UNREG_FAIL_DELAY_MS);
} while (retry_cnt++ < HNS3_INTR_UNREG_FAIL_RETRY_CNT);
}
void
hns3_handle_msix_error(struct hns3_adapter *hns, uint64_t *levels)
{
uint32_t mpf_bd_num, pf_bd_num, bd_num;
enum hns3_reset_level req_level;
struct hns3_hw *hw = &hns->hw;
struct hns3_pf *pf = &hns->pf;
struct hns3_cmd_desc desc_bd;
struct hns3_cmd_desc *desc;
uint32_t *desc_data;
uint32_t status;
int ret;
/* query the number of bds for the MSIx int status */
ret = query_num_bds_in_msix(hw, &desc_bd);
if (ret) {
hns3_err(hw, "fail to query msix int status bd num: %d", ret);
return;
}
mpf_bd_num = rte_le_to_cpu_32(desc_bd.data[0]);
pf_bd_num = rte_le_to_cpu_32(desc_bd.data[1]);
bd_num = max_t(uint32_t, mpf_bd_num, pf_bd_num);
if (bd_num < RCB_ERROR_OFFSET) {
hns3_err(hw, "bd_num is less than RCB_ERROR_OFFSET: %u",
bd_num);
return;
}
desc = rte_zmalloc(NULL, bd_num * sizeof(struct hns3_cmd_desc), 0);
if (desc == NULL) {
hns3_err(hw, "fail to zmalloc desc");
return;
}
/* query all main PF MSIx errors */
ret = query_all_mpf_msix_err(hw, &desc[0], mpf_bd_num);
if (ret) {
hns3_err(hw, "query all mpf msix int cmd failed: %d", ret);
goto out;
}
/* log MAC errors */
desc_data = (uint32_t *)&desc[MAC_ERROR_OFFSET];
status = rte_le_to_cpu_32(*desc_data);
if (status) {
req_level = hns3_find_highest_level(hns, "MAC_AFIFO_TNL_INT_R",
mac_afifo_tnl_int,
status);
hns3_atomic_set_bit(req_level, levels);
pf->abn_int_stats.mac_afifo_tnl_intr_cnt++;
}
/* log PPU(RCB) errors */
desc_data = (uint32_t *)&desc[RCB_ERROR_OFFSET];
status = rte_le_to_cpu_32(*(desc_data + RCB_ERROR_STATUS_OFFSET)) &
HNS3_PPU_MPF_INT_ST2_MSIX_MASK;
if (status) {
req_level = hns3_find_highest_level(hns,
"PPU_MPF_ABNORMAL_INT_ST2",
ppu_mpf_abnormal_int_st2,
status);
hns3_atomic_set_bit(req_level, levels);
pf->abn_int_stats.ppu_mpf_abnormal_intr_st2_cnt++;
}
/* clear all main PF MSIx errors */
ret = clear_all_mpf_msix_err(hw, desc, mpf_bd_num);
if (ret) {
hns3_err(hw, "clear all mpf msix int cmd failed: %d", ret);
goto out;
}
/* query all PF MSIx errors */
memset(desc, 0, bd_num * sizeof(struct hns3_cmd_desc));
ret = query_all_pf_msix_err(hw, &desc[0], pf_bd_num);
if (ret) {
hns3_err(hw, "query all pf msix int cmd failed (%d)", ret);
goto out;
}
/* log SSU PF errors */
status = rte_le_to_cpu_32(desc[0].data[0]) &
HNS3_SSU_PORT_INT_MSIX_MASK;
if (status) {
req_level = hns3_find_highest_level(hns,
"SSU_PORT_BASED_ERR_INT",
ssu_port_based_pf_int,
status);
hns3_atomic_set_bit(req_level, levels);
pf->abn_int_stats.ssu_port_based_pf_intr_cnt++;
}
/* log PPP PF errors */
desc_data = (uint32_t *)&desc[PPP_PF_ERROR_OFFSET];
status = rte_le_to_cpu_32(*desc_data);
if (status) {
req_level = hns3_find_highest_level(hns,
"PPP_PF_ABNORMAL_INT_ST0",
ppp_pf_abnormal_int,
status);
hns3_atomic_set_bit(req_level, levels);
pf->abn_int_stats.ppp_pf_abnormal_intr_cnt++;
}
/* log PPU(RCB) PF errors */
desc_data = (uint32_t *)&desc[PPU_PF_ERROR_OFFSET];
status = rte_le_to_cpu_32(*desc_data) & HNS3_PPU_PF_INT_MSIX_MASK;
if (status) {
req_level = hns3_find_highest_level(hns,
"PPU_PF_ABNORMAL_INT_ST",
ppu_pf_abnormal_int,
status);
hns3_atomic_set_bit(req_level, levels);
pf->abn_int_stats.ppu_pf_abnormal_intr_cnt++;
}
/* clear all PF MSIx errors */
ret = clear_all_pf_msix_err(hw, desc, pf_bd_num);
if (ret)
hns3_err(hw, "clear all pf msix int cmd failed: %d", ret);
out:
rte_free(desc);
}
int
hns3_reset_init(struct hns3_hw *hw)
{
rte_spinlock_init(&hw->lock);
hw->reset.level = HNS3_NONE_RESET;
hw->reset.stage = RESET_STAGE_NONE;
hw->reset.request = 0;
hw->reset.pending = 0;
rte_atomic16_init(&hw->reset.resetting);
rte_atomic16_init(&hw->reset.disable_cmd);
hw->reset.wait_data = rte_zmalloc("wait_data",
sizeof(struct hns3_wait_data), 0);
if (!hw->reset.wait_data) {
PMD_INIT_LOG(ERR, "Failed to allocate memory for wait_data");
return -ENOMEM;
}
return 0;
}
void
hns3_schedule_reset(struct hns3_adapter *hns)
{
struct hns3_hw *hw = &hns->hw;
/* Reschedule the reset process after successful initialization */
if (hw->adapter_state == HNS3_NIC_UNINITIALIZED) {
rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_PENDING);
return;
}
if (hw->adapter_state >= HNS3_NIC_CLOSED)
return;
/* Schedule restart alarm if it is not scheduled yet */
if (rte_atomic16_read(&hns->hw.reset.schedule) == SCHEDULE_REQUESTED)
return;
if (rte_atomic16_read(&hns->hw.reset.schedule) == SCHEDULE_DEFERRED)
rte_eal_alarm_cancel(hw->reset.ops->reset_service, hns);
rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_REQUESTED);
rte_eal_alarm_set(SWITCH_CONTEXT_US, hw->reset.ops->reset_service, hns);
}
void
hns3_schedule_delayed_reset(struct hns3_adapter *hns)
{
#define DEFERRED_SCHED_US (3 * MSEC_PER_SEC * USEC_PER_MSEC)
struct hns3_hw *hw = &hns->hw;
/* Do nothing if it is uninited or closed */
if (hw->adapter_state == HNS3_NIC_UNINITIALIZED ||
hw->adapter_state >= HNS3_NIC_CLOSED) {
return;
}
if (rte_atomic16_read(&hns->hw.reset.schedule) != SCHEDULE_NONE)
return;
rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_DEFERRED);
rte_eal_alarm_set(DEFERRED_SCHED_US, hw->reset.ops->reset_service, hns);
}
void
hns3_wait_callback(void *param)
{
struct hns3_wait_data *data = (struct hns3_wait_data *)param;
struct hns3_adapter *hns = data->hns;
struct hns3_hw *hw = &hns->hw;
uint64_t msec;
bool done;
data->count--;
if (data->check_completion) {
/*
* Check if the current time exceeds the deadline
* or a pending reset coming, or reset during close.
*/
msec = get_timeofday_ms();
if (msec > data->end_ms || is_reset_pending(hns) ||
hw->adapter_state == HNS3_NIC_CLOSING) {
done = false;
data->count = 0;
} else
done = data->check_completion(hw);
} else
done = true;
if (!done && data->count > 0) {
rte_eal_alarm_set(data->interval, hns3_wait_callback, data);
return;
}
if (done)
data->result = HNS3_WAIT_SUCCESS;
else {
hns3_err(hw, "%s wait timeout at stage %d",
reset_string[hw->reset.level], hw->reset.stage);
data->result = HNS3_WAIT_TIMEOUT;
}
hns3_schedule_reset(hns);
}
void
hns3_notify_reset_ready(struct hns3_hw *hw, bool enable)
{
uint32_t reg_val;
reg_val = hns3_read_dev(hw, HNS3_CMDQ_TX_DEPTH_REG);
if (enable)
reg_val |= HNS3_NIC_SW_RST_RDY;
else
reg_val &= ~HNS3_NIC_SW_RST_RDY;
hns3_write_dev(hw, HNS3_CMDQ_TX_DEPTH_REG, reg_val);
}
int
hns3_reset_req_hw_reset(struct hns3_adapter *hns)
{
struct hns3_hw *hw = &hns->hw;
if (hw->reset.wait_data->result == HNS3_WAIT_UNKNOWN) {
hw->reset.wait_data->hns = hns;
hw->reset.wait_data->check_completion = NULL;
hw->reset.wait_data->interval = HNS3_RESET_SYNC_US;
hw->reset.wait_data->count = 1;
hw->reset.wait_data->result = HNS3_WAIT_REQUEST;
rte_eal_alarm_set(hw->reset.wait_data->interval,
hns3_wait_callback, hw->reset.wait_data);
return -EAGAIN;
} else if (hw->reset.wait_data->result == HNS3_WAIT_REQUEST)
return -EAGAIN;
/* inform hardware that preparatory work is done */
hns3_notify_reset_ready(hw, true);
return 0;
}
static void
hns3_clear_reset_level(struct hns3_hw *hw, uint64_t *levels)
{
uint64_t merge_cnt = hw->reset.stats.merge_cnt;
int64_t tmp;
switch (hw->reset.level) {
case HNS3_IMP_RESET:
hns3_atomic_clear_bit(HNS3_IMP_RESET, levels);
tmp = hns3_test_and_clear_bit(HNS3_GLOBAL_RESET, levels);
HNS3_CHECK_MERGE_CNT(tmp);
tmp = hns3_test_and_clear_bit(HNS3_FUNC_RESET, levels);
HNS3_CHECK_MERGE_CNT(tmp);
break;
case HNS3_GLOBAL_RESET:
hns3_atomic_clear_bit(HNS3_GLOBAL_RESET, levels);
tmp = hns3_test_and_clear_bit(HNS3_FUNC_RESET, levels);
HNS3_CHECK_MERGE_CNT(tmp);
break;
case HNS3_FUNC_RESET:
hns3_atomic_clear_bit(HNS3_FUNC_RESET, levels);
break;
case HNS3_VF_RESET:
hns3_atomic_clear_bit(HNS3_VF_RESET, levels);
tmp = hns3_test_and_clear_bit(HNS3_VF_PF_FUNC_RESET, levels);
HNS3_CHECK_MERGE_CNT(tmp);
tmp = hns3_test_and_clear_bit(HNS3_VF_FUNC_RESET, levels);
HNS3_CHECK_MERGE_CNT(tmp);
break;
case HNS3_VF_FULL_RESET:
hns3_atomic_clear_bit(HNS3_VF_FULL_RESET, levels);
tmp = hns3_test_and_clear_bit(HNS3_VF_FUNC_RESET, levels);
HNS3_CHECK_MERGE_CNT(tmp);
break;
case HNS3_VF_PF_FUNC_RESET:
hns3_atomic_clear_bit(HNS3_VF_PF_FUNC_RESET, levels);
tmp = hns3_test_and_clear_bit(HNS3_VF_FUNC_RESET, levels);
HNS3_CHECK_MERGE_CNT(tmp);
break;
case HNS3_VF_FUNC_RESET:
hns3_atomic_clear_bit(HNS3_VF_FUNC_RESET, levels);
break;
case HNS3_FLR_RESET:
hns3_atomic_clear_bit(HNS3_FLR_RESET, levels);
break;
case HNS3_NONE_RESET:
default:
return;
};
if (merge_cnt != hw->reset.stats.merge_cnt)
hns3_warn(hw,
"No need to do low-level reset after %s reset. "
"merge cnt: %" PRIx64 " total merge cnt: %" PRIx64,
reset_string[hw->reset.level],
hw->reset.stats.merge_cnt - merge_cnt,
hw->reset.stats.merge_cnt);
}
static bool
hns3_reset_err_handle(struct hns3_adapter *hns)
{
#define MAX_RESET_FAIL_CNT 5
struct hns3_hw *hw = &hns->hw;
if (hw->adapter_state == HNS3_NIC_CLOSING)
goto reset_fail;
if (is_reset_pending(hns)) {
hw->reset.attempts = 0;
hw->reset.stats.fail_cnt++;
hns3_warn(hw, "%s reset fail because new Reset is pending "
"attempts:%" PRIx64,
reset_string[hw->reset.level],
hw->reset.stats.fail_cnt);
hw->reset.level = HNS3_NONE_RESET;
return true;
}
hw->reset.attempts++;
if (hw->reset.attempts < MAX_RESET_FAIL_CNT) {
hns3_atomic_set_bit(hw->reset.level, &hw->reset.pending);
hns3_warn(hw, "%s retry to reset attempts: %d",
reset_string[hw->reset.level],
hw->reset.attempts);
return true;
}
/*
* Failure to reset does not mean that the network port is
* completely unavailable, so cmd still needs to be initialized.
* Regardless of whether the execution is successful or not, the
* flow after execution must be continued.
*/
if (rte_atomic16_read(&hw->reset.disable_cmd))
(void)hns3_cmd_init(hw);
reset_fail:
hw->reset.attempts = 0;
hw->reset.stats.fail_cnt++;
hns3_warn(hw, "%s reset fail fail_cnt:%" PRIx64 " success_cnt:%" PRIx64
" global_cnt:%" PRIx64 " imp_cnt:%" PRIx64
" request_cnt:%" PRIx64 " exec_cnt:%" PRIx64
" merge_cnt:%" PRIx64 "adapter_state:%d",
reset_string[hw->reset.level], hw->reset.stats.fail_cnt,
hw->reset.stats.success_cnt, hw->reset.stats.global_cnt,
hw->reset.stats.imp_cnt, hw->reset.stats.request_cnt,
hw->reset.stats.exec_cnt, hw->reset.stats.merge_cnt,
hw->adapter_state);
/* IMP no longer waiting the ready flag */
hns3_notify_reset_ready(hw, true);
return false;
}
static int
hns3_reset_pre(struct hns3_adapter *hns)
{
struct hns3_hw *hw = &hns->hw;
struct timeval tv;
int ret;
if (hw->reset.stage == RESET_STAGE_NONE) {
rte_atomic16_set(&hns->hw.reset.resetting, 1);
hw->reset.stage = RESET_STAGE_DOWN;
ret = hw->reset.ops->stop_service(hns);
gettimeofday(&tv, NULL);
if (ret) {
hns3_warn(hw, "Reset step1 down fail=%d time=%ld.%.6ld",
ret, tv.tv_sec, tv.tv_usec);
return ret;
}
hns3_warn(hw, "Reset step1 down success time=%ld.%.6ld",
tv.tv_sec, tv.tv_usec);
hw->reset.stage = RESET_STAGE_PREWAIT;
}
if (hw->reset.stage == RESET_STAGE_PREWAIT) {
ret = hw->reset.ops->prepare_reset(hns);
gettimeofday(&tv, NULL);
if (ret) {
hns3_warn(hw,
"Reset step2 prepare wait fail=%d time=%ld.%.6ld",
ret, tv.tv_sec, tv.tv_usec);
return ret;
}
hns3_warn(hw, "Reset step2 prepare wait success time=%ld.%.6ld",
tv.tv_sec, tv.tv_usec);
hw->reset.stage = RESET_STAGE_REQ_HW_RESET;
hw->reset.wait_data->result = HNS3_WAIT_UNKNOWN;
}
return 0;
}
static int
hns3_reset_post(struct hns3_adapter *hns)
{
#define TIMEOUT_RETRIES_CNT 5
struct hns3_hw *hw = &hns->hw;
struct timeval tv_delta;
struct timeval tv;
int ret = 0;
if (hw->adapter_state == HNS3_NIC_CLOSING) {
hns3_warn(hw, "Don't do reset_post during closing, just uninit cmd");
hns3_cmd_uninit(hw);
return -EPERM;
}
if (hw->reset.stage == RESET_STAGE_DEV_INIT) {
rte_spinlock_lock(&hw->lock);
if (hw->reset.mbuf_deferred_free) {
hns3_dev_release_mbufs(hns);
hw->reset.mbuf_deferred_free = false;
}
ret = hw->reset.ops->reinit_dev(hns);
rte_spinlock_unlock(&hw->lock);
gettimeofday(&tv, NULL);
if (ret) {
hns3_warn(hw, "Reset step5 devinit fail=%d retries=%d",
ret, hw->reset.retries);
goto err;
}
hns3_warn(hw, "Reset step5 devinit success time=%ld.%.6ld",
tv.tv_sec, tv.tv_usec);
hw->reset.retries = 0;
hw->reset.stage = RESET_STAGE_RESTORE;
rte_eal_alarm_set(SWITCH_CONTEXT_US,
hw->reset.ops->reset_service, hns);
return -EAGAIN;
}
if (hw->reset.stage == RESET_STAGE_RESTORE) {
rte_spinlock_lock(&hw->lock);
ret = hw->reset.ops->restore_conf(hns);
rte_spinlock_unlock(&hw->lock);
gettimeofday(&tv, NULL);
if (ret) {
hns3_warn(hw,
"Reset step6 restore fail=%d retries=%d",
ret, hw->reset.retries);
goto err;
}
hns3_warn(hw, "Reset step6 restore success time=%ld.%.6ld",
tv.tv_sec, tv.tv_usec);
hw->reset.retries = 0;
hw->reset.stage = RESET_STAGE_DONE;
}
if (hw->reset.stage == RESET_STAGE_DONE) {
/* IMP will wait ready flag before reset */
hns3_notify_reset_ready(hw, false);
hns3_clear_reset_level(hw, &hw->reset.pending);
rte_atomic16_clear(&hns->hw.reset.resetting);
hw->reset.attempts = 0;
hw->reset.stats.success_cnt++;
hw->reset.stage = RESET_STAGE_NONE;
rte_spinlock_lock(&hw->lock);
hw->reset.ops->start_service(hns);
rte_spinlock_unlock(&hw->lock);
gettimeofday(&tv, NULL);
timersub(&tv, &hw->reset.start_time, &tv_delta);
hns3_warn(hw, "%s reset done fail_cnt:%" PRIx64
" success_cnt:%" PRIx64 " global_cnt:%" PRIx64
" imp_cnt:%" PRIx64 " request_cnt:%" PRIx64
" exec_cnt:%" PRIx64 " merge_cnt:%" PRIx64,
reset_string[hw->reset.level],
hw->reset.stats.fail_cnt, hw->reset.stats.success_cnt,
hw->reset.stats.global_cnt, hw->reset.stats.imp_cnt,
hw->reset.stats.request_cnt, hw->reset.stats.exec_cnt,
hw->reset.stats.merge_cnt);
hns3_warn(hw,
"%s reset done delta %ld ms time=%ld.%.6ld",
reset_string[hw->reset.level],
tv_delta.tv_sec * MSEC_PER_SEC +
tv_delta.tv_usec / USEC_PER_MSEC,
tv.tv_sec, tv.tv_usec);
hw->reset.level = HNS3_NONE_RESET;
}
return 0;
err:
if (ret == -ETIME) {
hw->reset.retries++;
if (hw->reset.retries < TIMEOUT_RETRIES_CNT) {
rte_eal_alarm_set(HNS3_RESET_SYNC_US,
hw->reset.ops->reset_service, hns);
return -EAGAIN;
}
}
hw->reset.retries = 0;
return -EIO;
}
/*
* There are three scenarios as follows:
* When the reset is not in progress, the reset process starts.
* During the reset process, if the reset level has not changed,
* the reset process continues; otherwise, the reset process is aborted.
* hw->reset.level new_level action
* HNS3_NONE_RESET HNS3_XXXX_RESET start reset
* HNS3_XXXX_RESET HNS3_XXXX_RESET continue reset
* HNS3_LOW_RESET HNS3_HIGH_RESET abort
*/
int
hns3_reset_process(struct hns3_adapter *hns, enum hns3_reset_level new_level)
{
struct hns3_hw *hw = &hns->hw;
struct timeval tv_delta;
struct timeval tv;
int ret;
if (hw->reset.level == HNS3_NONE_RESET) {
hw->reset.level = new_level;
hw->reset.stats.exec_cnt++;
gettimeofday(&hw->reset.start_time, NULL);
hns3_warn(hw, "Start %s reset time=%ld.%.6ld",
reset_string[hw->reset.level],
hw->reset.start_time.tv_sec,
hw->reset.start_time.tv_usec);
}
if (is_reset_pending(hns)) {
gettimeofday(&tv, NULL);
hns3_warn(hw,
"%s reset is aborted by high level time=%ld.%.6ld",
reset_string[hw->reset.level], tv.tv_sec, tv.tv_usec);
if (hw->reset.wait_data->result == HNS3_WAIT_REQUEST)
rte_eal_alarm_cancel(hns3_wait_callback,
hw->reset.wait_data);
ret = -EBUSY;
goto err;
}
ret = hns3_reset_pre(hns);
if (ret)
goto err;
if (hw->reset.stage == RESET_STAGE_REQ_HW_RESET) {
ret = hns3_reset_req_hw_reset(hns);
if (ret == -EAGAIN)
return ret;
gettimeofday(&tv, NULL);
hns3_warn(hw,
"Reset step3 request IMP reset success time=%ld.%.6ld",
tv.tv_sec, tv.tv_usec);
hw->reset.stage = RESET_STAGE_WAIT;
hw->reset.wait_data->result = HNS3_WAIT_UNKNOWN;
}
if (hw->reset.stage == RESET_STAGE_WAIT) {
ret = hw->reset.ops->wait_hardware_ready(hns);
if (ret)
goto retry;
gettimeofday(&tv, NULL);
hns3_warn(hw, "Reset step4 reset wait success time=%ld.%.6ld",
tv.tv_sec, tv.tv_usec);
hw->reset.stage = RESET_STAGE_DEV_INIT;
}
ret = hns3_reset_post(hns);
if (ret)
goto retry;
return 0;
retry:
if (ret == -EAGAIN)
return ret;
err:
hns3_clear_reset_level(hw, &hw->reset.pending);
if (hns3_reset_err_handle(hns)) {
hw->reset.stage = RESET_STAGE_PREWAIT;
hns3_schedule_reset(hns);
} else {
rte_spinlock_lock(&hw->lock);
if (hw->reset.mbuf_deferred_free) {
hns3_dev_release_mbufs(hns);
hw->reset.mbuf_deferred_free = false;
}
rte_spinlock_unlock(&hw->lock);
rte_atomic16_clear(&hns->hw.reset.resetting);
hw->reset.stage = RESET_STAGE_NONE;
gettimeofday(&tv, NULL);
timersub(&tv, &hw->reset.start_time, &tv_delta);
hns3_warn(hw, "%s reset fail delta %ld ms time=%ld.%.6ld",
reset_string[hw->reset.level],
tv_delta.tv_sec * MSEC_PER_SEC +
tv_delta.tv_usec / USEC_PER_MSEC,
tv.tv_sec, tv.tv_usec);
hw->reset.level = HNS3_NONE_RESET;
}
return -EIO;
}
/*
* The reset process can only be terminated after handshake with IMP(step3),
* so that IMP can complete the reset process normally.
*/
void
hns3_reset_abort(struct hns3_adapter *hns)
{
struct hns3_hw *hw = &hns->hw;
struct timeval tv;
int i;
for (i = 0; i < HNS3_QUIT_RESET_CNT; i++) {
if (hw->reset.level == HNS3_NONE_RESET)
break;
rte_delay_ms(HNS3_QUIT_RESET_DELAY_MS);
}
/* IMP no longer waiting the ready flag */
hns3_notify_reset_ready(hw, true);
rte_eal_alarm_cancel(hw->reset.ops->reset_service, hns);
rte_eal_alarm_cancel(hns3_wait_callback, hw->reset.wait_data);
if (hw->reset.level != HNS3_NONE_RESET) {
gettimeofday(&tv, NULL);
hns3_err(hw, "Failed to terminate reset: %s time=%ld.%.6ld",
reset_string[hw->reset.level], tv.tv_sec, tv.tv_usec);
}
}