Skip to content

Commit

Permalink
hinic: add support to handle hw abnormal event
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Luo bin authored and David S. Miller committed Jul 29, 2020
1 parent aff7543 commit c15850c
Show file tree
Hide file tree
Showing 10 changed files with 704 additions and 36 deletions.
4 changes: 4 additions & 0 deletions drivers/net/ethernet/huawei/hinic/hinic_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,14 @@ struct hinic_dev {
int lb_pkt_len;
u8 *lb_test_rx_buf;
struct devlink *devlink;
bool cable_unplugged;
bool module_unrecognized;
};

struct hinic_devlink_priv {
struct hinic_hwdev *hwdev;
struct devlink_health_reporter *hw_fault_reporter;
struct devlink_health_reporter *fw_fault_reporter;
};

#endif
286 changes: 283 additions & 3 deletions drivers/net/ethernet/huawei/hinic/hinic_devlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
}
8 changes: 6 additions & 2 deletions drivers/net/ethernet/huawei/hinic/hinic_devlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define __HINIC_DEVLINK_H__

#include <net/devlink.h>
#include "hinic_dev.h"

#define MAX_FW_TYPE_NUM 30
#define HINIC_MAGIC_NUM 0x18221100
Expand Down Expand Up @@ -109,7 +110,10 @@ struct host_image_st {

struct devlink *hinic_devlink_alloc(void);
void hinic_devlink_free(struct devlink *devlink);
int hinic_devlink_register(struct devlink *devlink, struct device *dev);
void hinic_devlink_unregister(struct devlink *devlink);
int hinic_devlink_register(struct hinic_devlink_priv *priv, struct device *dev);
void hinic_devlink_unregister(struct hinic_devlink_priv *priv);

int hinic_health_reporters_create(struct hinic_devlink_priv *priv);
void hinic_health_reporters_destroy(struct hinic_devlink_priv *priv);

#endif /* __HINIC_DEVLINK_H__ */
20 changes: 20 additions & 0 deletions drivers/net/ethernet/huawei/hinic/hinic_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,25 @@ static int hinic_get_module_eeprom(struct net_device *netdev,
return 0;
}

static int
hinic_get_link_ext_state(struct net_device *netdev,
struct ethtool_link_ext_state_info *link_ext_state_info)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);

if (netif_carrier_ok(netdev))
return -ENODATA;

if (nic_dev->cable_unplugged)
link_ext_state_info->link_ext_state =
ETHTOOL_LINK_EXT_STATE_NO_CABLE;
else if (nic_dev->module_unrecognized)
link_ext_state_info->link_ext_state =
ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH;

return 0;
}

static const struct ethtool_ops hinic_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
ETHTOOL_COALESCE_RX_MAX_FRAMES |
Expand All @@ -1776,6 +1795,7 @@ static const struct ethtool_ops hinic_ethtool_ops = {
.set_link_ksettings = hinic_set_link_ksettings,
.get_drvinfo = hinic_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_link_ext_state = hinic_get_link_ext_state,
.get_ringparam = hinic_get_ringparam,
.set_ringparam = hinic_set_ringparam,
.get_coalesce = hinic_get_coalesce,
Expand Down
Loading

0 comments on commit c15850c

Please sign in to comment.