Commit 12554574 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'hinic-add-some-error-messages-for-debug'



Luo bin says:

====================
hinic: add some error messages for debug

patch #1: support to handle hw abnormal event
patch #2: improve the error messages when functions return failure and
	  dump relevant registers in some exception handling processes
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents aff75431 90f86b8a
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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
+283 −3
Original line number Diff line number Diff line
@@ -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;
	}
}
+6 −2
Original line number Diff line number Diff line
@@ -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
@@ -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 −0
Original line number Diff line number Diff line
@@ -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 |
@@ -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,
+25 −2
Original line number Diff line number Diff line
@@ -112,6 +112,26 @@ static u32 get_hw_cons_idx(struct hinic_api_cmd_chain *chain)
	return HINIC_API_CMD_STATUS_GET(val, CONS_IDX);
}

static void dump_api_chain_reg(struct hinic_api_cmd_chain *chain)
{
	u32 addr, val;

	addr = HINIC_CSR_API_CMD_STATUS_ADDR(chain->chain_type);
	val  = hinic_hwif_read_reg(chain->hwif, addr);

	dev_err(&chain->hwif->pdev->dev, "Chain type: 0x%x, cpld error: 0x%x, check error: 0x%x, current fsm: 0x%x\n",
		chain->chain_type, HINIC_API_CMD_STATUS_GET(val, CPLD_ERR),
		HINIC_API_CMD_STATUS_GET(val, CHKSUM_ERR),
		HINIC_API_CMD_STATUS_GET(val, FSM));

	dev_err(&chain->hwif->pdev->dev, "Chain hw current ci: 0x%x\n",
		HINIC_API_CMD_STATUS_GET(val, CONS_IDX));

	addr = HINIC_CSR_API_CMD_CHAIN_PI_ADDR(chain->chain_type);
	val  = hinic_hwif_read_reg(chain->hwif, addr);
	dev_err(&chain->hwif->pdev->dev, "Chain hw current pi: 0x%x\n", val);
}

/**
 * chain_busy - check if the chain is still processing last requests
 * @chain: chain to check
@@ -131,8 +151,10 @@ static int chain_busy(struct hinic_api_cmd_chain *chain)

		/* check for a space for a new command */
		if (chain->cons_idx == MASKED_IDX(chain, prod_idx + 1)) {
			dev_err(&pdev->dev, "API CMD chain %d is busy\n",
				chain->chain_type);
			dev_err(&pdev->dev, "API CMD chain %d is busy, cons_idx: %d, prod_idx: %d\n",
				chain->chain_type, chain->cons_idx,
				chain->prod_idx);
			dump_api_chain_reg(chain);
			return -EBUSY;
		}
		break;
@@ -332,6 +354,7 @@ static int wait_for_api_cmd_completion(struct hinic_api_cmd_chain *chain)
		err = wait_for_status_poll(chain);
		if (err) {
			dev_err(&pdev->dev, "API CMD Poll status timeout\n");
			dump_api_chain_reg(chain);
			break;
		}
		break;
Loading