diff options
author | Luo bin <luobin9@huawei.com> | 2020-07-25 15:11:18 +0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2020-07-28 17:22:02 -0700 |
commit | c15850c709eb5c7771785c9174e4908d1806a0f0 (patch) | |
tree | e821faf2326a2301e6d7ba7005732b37069dbe25 /drivers/net/ethernet/huawei/hinic/hinic_devlink.c | |
parent | aff7543126df2006f9eb44859b11c8615de6967d (diff) | |
download | linux-c15850c709eb5c7771785c9174e4908d1806a0f0.tar.gz linux-c15850c709eb5c7771785c9174e4908d1806a0f0.tar.bz2 linux-c15850c709eb5c7771785c9174e4908d1806a0f0.zip |
hinic: add support to handle hw abnormal event
add support to handle hw abnormal event such as hardware failure,
cable unplugged,link error
Signed-off-by: Luo bin <luobin9@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/huawei/hinic/hinic_devlink.c')
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_devlink.c | 286 |
1 files changed, 283 insertions, 3 deletions
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_devlink.c b/drivers/net/ethernet/huawei/hinic/hinic_devlink.c index a40a10ac1ee9..c6adc776f3c8 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_devlink.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_devlink.c @@ -16,9 +16,9 @@ #include <net/devlink.h> #include <linux/firmware.h> -#include "hinic_dev.h" #include "hinic_port.h" #include "hinic_devlink.h" +#include "hinic_hw_dev.h" static bool check_image_valid(struct hinic_devlink_priv *priv, const u8 *buf, u32 image_size, struct host_image_st *host_image) @@ -317,12 +317,292 @@ void hinic_devlink_free(struct devlink *devlink) devlink_free(devlink); } -int hinic_devlink_register(struct devlink *devlink, struct device *dev) +int hinic_devlink_register(struct hinic_devlink_priv *priv, struct device *dev) { + struct devlink *devlink = priv_to_devlink(priv); + return devlink_register(devlink, dev); } -void hinic_devlink_unregister(struct devlink *devlink) +void hinic_devlink_unregister(struct hinic_devlink_priv *priv) { + struct devlink *devlink = priv_to_devlink(priv); + devlink_unregister(devlink); } + +static int chip_fault_show(struct devlink_fmsg *fmsg, + struct hinic_fault_event *event) +{ + char fault_level[FAULT_TYPE_MAX][FAULT_SHOW_STR_LEN + 1] = { + "fatal", "reset", "flr", "general", "suggestion"}; + char level_str[FAULT_SHOW_STR_LEN + 1] = {0}; + u8 level; + int err; + + level = event->event.chip.err_level; + if (level < FAULT_LEVEL_MAX) + strncpy(level_str, fault_level[level], strlen(fault_level[level])); + else + strncpy(level_str, "Unknown", strlen("Unknown")); + + if (level == FAULT_LEVEL_SERIOUS_FLR) { + err = devlink_fmsg_u32_pair_put(fmsg, "Function level err func_id", + (u32)event->event.chip.func_id); + if (err) + return err; + } + + err = devlink_fmsg_u8_pair_put(fmsg, "module_id", event->event.chip.node_id); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "err_type", (u32)event->event.chip.err_type); + if (err) + return err; + + err = devlink_fmsg_string_pair_put(fmsg, "err_level", level_str); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_addr", + event->event.chip.err_csr_addr); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_value", + event->event.chip.err_csr_value); + if (err) + return err; + + return 0; +} + +static int fault_report_show(struct devlink_fmsg *fmsg, + struct hinic_fault_event *event) +{ + char fault_type[FAULT_TYPE_MAX][FAULT_SHOW_STR_LEN + 1] = { + "chip", "ucode", "mem rd timeout", "mem wr timeout", + "reg rd timeout", "reg wr timeout", "phy fault"}; + char type_str[FAULT_SHOW_STR_LEN + 1] = {0}; + int err; + + if (event->type < FAULT_TYPE_MAX) + strncpy(type_str, fault_type[event->type], strlen(fault_type[event->type])); + else + strncpy(type_str, "Unknown", strlen("Unknown")); + + err = devlink_fmsg_string_pair_put(fmsg, "Fault type", type_str); + if (err) + return err; + + err = devlink_fmsg_binary_pair_put(fmsg, "Fault raw data", + event->event.val, sizeof(event->event.val)); + if (err) + return err; + + switch (event->type) { + case FAULT_TYPE_CHIP: + err = chip_fault_show(fmsg, event); + if (err) + return err; + break; + case FAULT_TYPE_UCODE: + err = devlink_fmsg_u8_pair_put(fmsg, "Cause_id", event->event.ucode.cause_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "core_id", event->event.ucode.core_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "c_id", event->event.ucode.c_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "epc", event->event.ucode.epc); + if (err) + return err; + break; + case FAULT_TYPE_MEM_RD_TIMEOUT: + case FAULT_TYPE_MEM_WR_TIMEOUT: + err = devlink_fmsg_u32_pair_put(fmsg, "Err_csr_ctrl", + event->event.mem_timeout.err_csr_ctrl); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_data", + event->event.mem_timeout.err_csr_data); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "ctrl_tab", + event->event.mem_timeout.ctrl_tab); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "mem_index", + event->event.mem_timeout.mem_index); + if (err) + return err; + break; + case FAULT_TYPE_REG_RD_TIMEOUT: + case FAULT_TYPE_REG_WR_TIMEOUT: + err = devlink_fmsg_u32_pair_put(fmsg, "Err_csr", event->event.reg_timeout.err_csr); + if (err) + return err; + break; + case FAULT_TYPE_PHY_FAULT: + err = devlink_fmsg_u8_pair_put(fmsg, "Op_type", event->event.phy_fault.op_type); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "port_id", event->event.phy_fault.port_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "dev_ad", event->event.phy_fault.dev_ad); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "csr_addr", event->event.phy_fault.csr_addr); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "op_data", event->event.phy_fault.op_data); + if (err) + return err; + break; + default: + break; + } + + return 0; +} + +static int hinic_hw_reporter_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *priv_ctx, + struct netlink_ext_ack *extack) +{ + if (priv_ctx) + return fault_report_show(fmsg, priv_ctx); + + return 0; +} + +static int mgmt_watchdog_report_show(struct devlink_fmsg *fmsg, + struct hinic_mgmt_watchdog_info *watchdog_info) +{ + int err; + + err = devlink_fmsg_u32_pair_put(fmsg, "Mgmt deadloop time_h", watchdog_info->curr_time_h); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "time_l", watchdog_info->curr_time_l); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "task_id", watchdog_info->task_id); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "sp", watchdog_info->sp); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "stack_current_used", watchdog_info->curr_used); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "peak_used", watchdog_info->peak_used); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "\n Overflow_flag", watchdog_info->is_overflow); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "stack_top", watchdog_info->stack_top); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "stack_bottom", watchdog_info->stack_bottom); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "mgmt_pc", watchdog_info->pc); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "lr", watchdog_info->lr); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "cpsr", watchdog_info->cpsr); + if (err) + return err; + + err = devlink_fmsg_binary_pair_put(fmsg, "Mgmt register info", + watchdog_info->reg, sizeof(watchdog_info->reg)); + if (err) + return err; + + err = devlink_fmsg_binary_pair_put(fmsg, "Mgmt dump stack(start from sp)", + watchdog_info->data, sizeof(watchdog_info->data)); + if (err) + return err; + + return 0; +} + +static int hinic_fw_reporter_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *priv_ctx, + struct netlink_ext_ack *extack) +{ + if (priv_ctx) + return mgmt_watchdog_report_show(fmsg, priv_ctx); + + return 0; +} + +static const struct devlink_health_reporter_ops hinic_hw_fault_reporter_ops = { + .name = "hw", + .dump = hinic_hw_reporter_dump, +}; + +static const struct devlink_health_reporter_ops hinic_fw_fault_reporter_ops = { + .name = "fw", + .dump = hinic_fw_reporter_dump, +}; + +int hinic_health_reporters_create(struct hinic_devlink_priv *priv) +{ + struct devlink *devlink = priv_to_devlink(priv); + + priv->hw_fault_reporter = + devlink_health_reporter_create(devlink, &hinic_hw_fault_reporter_ops, + 0, priv); + if (IS_ERR(priv->hw_fault_reporter)) { + dev_warn(&priv->hwdev->hwif->pdev->dev, "Failed to create hw fault reporter, err: %ld\n", + PTR_ERR(priv->hw_fault_reporter)); + return PTR_ERR(priv->hw_fault_reporter); + } + + priv->fw_fault_reporter = + devlink_health_reporter_create(devlink, &hinic_fw_fault_reporter_ops, + 0, priv); + if (IS_ERR(priv->fw_fault_reporter)) { + dev_warn(&priv->hwdev->hwif->pdev->dev, "Failed to create fw fault reporter, err: %ld\n", + PTR_ERR(priv->fw_fault_reporter)); + devlink_health_reporter_destroy(priv->hw_fault_reporter); + priv->hw_fault_reporter = NULL; + return PTR_ERR(priv->fw_fault_reporter); + } + + return 0; +} + +void hinic_health_reporters_destroy(struct hinic_devlink_priv *priv) +{ + if (!IS_ERR_OR_NULL(priv->fw_fault_reporter)) { + devlink_health_reporter_destroy(priv->fw_fault_reporter); + priv->fw_fault_reporter = NULL; + } + + if (!IS_ERR_OR_NULL(priv->hw_fault_reporter)) { + devlink_health_reporter_destroy(priv->hw_fault_reporter); + priv->hw_fault_reporter = NULL; + } +} |