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

Merge branch 'bnxt_en-updates'



Michael Chan says:

====================
bnxt_en: Updates.

This patch series contains mainly NVRAM related features.  More
NVRAM error checking and logging are added when installing firmware
packages.  A new devlink hw health report is now added to report
and diagnose NVRAM issues.  Other miscellaneous patches include
reporting correctly cards that don't support link pause, adding
an internal unknown link state, and avoiding unnecessary link
toggle during firmware reset.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2057b8b7 22f5dba5
Loading
Loading
Loading
Loading
+43 −18
Original line number Diff line number Diff line
@@ -2061,6 +2061,22 @@ static void bnxt_event_error_report(struct bnxt *bp, u32 data1, u32 data2)
	case ASYNC_EVENT_CMPL_ERROR_REPORT_BASE_EVENT_DATA1_ERROR_TYPE_DOORBELL_DROP_THRESHOLD:
		netdev_warn(bp->dev, "One or more MMIO doorbells dropped by the device!\n");
		break;
	case ASYNC_EVENT_CMPL_ERROR_REPORT_BASE_EVENT_DATA1_ERROR_TYPE_NVM: {
		struct bnxt_hw_health *hw_health = &bp->hw_health;

		hw_health->nvm_err_address = EVENT_DATA2_NVM_ERR_ADDR(data2);
		if (EVENT_DATA1_NVM_ERR_TYPE_WRITE(data1)) {
			hw_health->synd = BNXT_HW_STATUS_NVM_WRITE_ERR;
			hw_health->nvm_write_errors++;
		} else if (EVENT_DATA1_NVM_ERR_TYPE_ERASE(data1)) {
			hw_health->synd = BNXT_HW_STATUS_NVM_ERASE_ERR;
			hw_health->nvm_erase_errors++;
		} else {
			hw_health->synd = BNXT_HW_STATUS_NVM_UNKNOWN_ERR;
		}
		set_bit(BNXT_FW_NVM_ERR_SP_EVENT, &bp->sp_event);
		break;
	}
	default:
		netdev_err(bp->dev, "FW reported unknown error type %u\n",
			   err_type);
@@ -9300,7 +9316,7 @@ void bnxt_tx_enable(struct bnxt *bp)
	/* Make sure napi polls see @dev_state change */
	synchronize_net();
	netif_tx_wake_all_queues(bp->dev);
	if (bp->link_info.link_up)
	if (BNXT_LINK_IS_UP(bp))
		netif_carrier_on(bp->dev);
}

@@ -9330,7 +9346,7 @@ static char *bnxt_report_fec(struct bnxt_link_info *link_info)

void bnxt_report_link(struct bnxt *bp)
{
	if (bp->link_info.link_up) {
	if (BNXT_LINK_IS_UP(bp)) {
		const char *signal = "";
		const char *flow_ctrl;
		const char *duplex;
@@ -9416,7 +9432,7 @@ static int bnxt_hwrm_phy_qcaps(struct bnxt *bp)
	if (rc)
		goto hwrm_phy_qcaps_exit;

	bp->phy_flags = resp->flags;
	bp->phy_flags = resp->flags | (le16_to_cpu(resp->flags2) << 8);
	if (resp->flags & PORT_PHY_QCAPS_RESP_FLAGS_EEE_SUPPORTED) {
		struct ethtool_eee *eee = &bp->eee;
		u16 fw_speeds = le16_to_cpu(resp->supported_speeds_eee_mode);
@@ -9466,7 +9482,7 @@ int bnxt_update_link(struct bnxt *bp, bool chng_link_state)
	struct bnxt_link_info *link_info = &bp->link_info;
	struct hwrm_port_phy_qcfg_output *resp;
	struct hwrm_port_phy_qcfg_input *req;
	u8 link_up = link_info->link_up;
	u8 link_state = link_info->link_state;
	bool support_changed = false;
	int rc;

@@ -9567,14 +9583,14 @@ int bnxt_update_link(struct bnxt *bp, bool chng_link_state)
	/* TODO: need to add more logic to report VF link */
	if (chng_link_state) {
		if (link_info->phy_link_status == BNXT_LINK_LINK)
			link_info->link_up = 1;
			link_info->link_state = BNXT_LINK_STATE_UP;
		else
			link_info->link_up = 0;
		if (link_up != link_info->link_up)
			link_info->link_state = BNXT_LINK_STATE_DOWN;
		if (link_state != link_info->link_state)
			bnxt_report_link(bp);
	} else {
		/* alwasy link down if not require to update link state */
		link_info->link_up = 0;
		/* always link down if not require to update link state */
		link_info->link_state = BNXT_LINK_STATE_DOWN;
	}
	hwrm_req_drop(bp, req);

@@ -9774,7 +9790,18 @@ static int bnxt_hwrm_shutdown_link(struct bnxt *bp)
		return rc;

	req->flags = cpu_to_le32(PORT_PHY_CFG_REQ_FLAGS_FORCE_LINK_DWN);
	return hwrm_req_send(bp, req);
	rc = hwrm_req_send(bp, req);
	if (!rc) {
		mutex_lock(&bp->link_lock);
		/* Device is not obliged link down in certain scenarios, even
		 * when forced. Setting the state unknown is consistent with
		 * driver startup and will force link state to be reported
		 * during subsequent open based on PORT_PHY_QCFG.
		 */
		bp->link_info.link_state = BNXT_LINK_STATE_UNKNOWN;
		mutex_unlock(&bp->link_lock);
	}
	return rc;
}

static int bnxt_fw_reset_via_optee(struct bnxt *bp)
@@ -10205,7 +10232,7 @@ static int bnxt_update_phy_setting(struct bnxt *bp)
	/* The last close may have shutdown the link, so need to call
	 * PHY_CFG to bring it back up.
	 */
	if (!bp->link_info.link_up)
	if (!BNXT_LINK_IS_UP(bp))
		update_link = true;

	if (!bnxt_eee_config_ok(bp))
@@ -11437,7 +11464,7 @@ static void bnxt_timer(struct timer_list *t)
	if (bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY)
		bnxt_fw_health_check(bp);

	if (bp->link_info.link_up && bp->stats_coal_ticks) {
	if (BNXT_LINK_IS_UP(bp) && bp->stats_coal_ticks) {
		set_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event);
		bnxt_queue_sp_work(bp);
	}
@@ -11876,6 +11903,9 @@ static void bnxt_sp_task(struct work_struct *work)
	if (test_and_clear_bit(BNXT_FW_ECHO_REQUEST_SP_EVENT, &bp->sp_event))
		bnxt_fw_echo_reply(bp);

	if (test_and_clear_bit(BNXT_FW_NVM_ERR_SP_EVENT, &bp->sp_event))
		bnxt_devlink_health_hw_report(bp);

	/* These functions below will clear BNXT_STATE_IN_SP_TASK.  They
	 * must be the last functions to be called before exiting.
	 */
@@ -12138,11 +12168,6 @@ int bnxt_fw_init_one(struct bnxt *bp)
	if (rc)
		return rc;

	/* In case fw capabilities have changed, destroy the unneeded
	 * reporters and create newly capable ones.
	 */
	bnxt_dl_fw_reporters_destroy(bp, false);
	bnxt_dl_fw_reporters_create(bp);
	bnxt_fw_init_one_p3(bp);
	return 0;
}
@@ -12971,7 +12996,7 @@ static void bnxt_remove_one(struct pci_dev *pdev)
	cancel_delayed_work_sync(&bp->fw_reset_task);
	bp->sp_event = 0;

	bnxt_dl_fw_reporters_destroy(bp, true);
	bnxt_dl_fw_reporters_destroy(bp);
	bnxt_dl_unregister(bp);
	bnxt_shutdown_tc(bp);

+54 −3
Original line number Diff line number Diff line
@@ -516,6 +516,21 @@ struct rx_tpa_end_cmp_ext {
	  ASYNC_EVENT_CMPL_ERROR_REPORT_INVALID_SIGNAL_EVENT_DATA2_PIN_ID_MASK) >>\
	 ASYNC_EVENT_CMPL_ERROR_REPORT_INVALID_SIGNAL_EVENT_DATA2_PIN_ID_SFT)

#define EVENT_DATA2_NVM_ERR_ADDR(data2)					\
	(((data2) &							\
	  ASYNC_EVENT_CMPL_ERROR_REPORT_NVM_EVENT_DATA2_ERR_ADDR_MASK) >>\
	 ASYNC_EVENT_CMPL_ERROR_REPORT_NVM_EVENT_DATA2_ERR_ADDR_SFT)

#define EVENT_DATA1_NVM_ERR_TYPE_WRITE(data1)				\
	(((data1) &							\
	  ASYNC_EVENT_CMPL_ERROR_REPORT_NVM_EVENT_DATA1_NVM_ERR_TYPE_MASK) ==\
	 ASYNC_EVENT_CMPL_ERROR_REPORT_NVM_EVENT_DATA1_NVM_ERR_TYPE_WRITE)

#define EVENT_DATA1_NVM_ERR_TYPE_ERASE(data1)				\
	(((data1) &							\
	  ASYNC_EVENT_CMPL_ERROR_REPORT_NVM_EVENT_DATA1_NVM_ERR_TYPE_MASK) ==\
	 ASYNC_EVENT_CMPL_ERROR_REPORT_NVM_EVENT_DATA1_NVM_ERR_TYPE_ERASE)

struct nqe_cn {
	__le16	type;
	#define NQ_CN_TYPE_MASK           0x3fUL
@@ -1175,7 +1190,11 @@ struct bnxt_link_info {
#define BNXT_PHY_STATE_ENABLED		0
#define BNXT_PHY_STATE_DISABLED		1

	u8			link_up;
	u8			link_state;
#define BNXT_LINK_STATE_UNKNOWN	0
#define BNXT_LINK_STATE_DOWN	1
#define BNXT_LINK_STATE_UP	2
#define BNXT_LINK_IS_UP(bp)	((bp)->link_info.link_state == BNXT_LINK_STATE_UP)
	u8			duplex;
#define BNXT_LINK_DUPLEX_HALF	PORT_PHY_QCFG_RESP_DUPLEX_STATE_HALF
#define BNXT_LINK_DUPLEX_FULL	PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL
@@ -1524,6 +1543,33 @@ struct bnxt_ctx_mem_info {
	struct bnxt_mem_init	mem_init[BNXT_CTX_MEM_INIT_MAX];
};

enum bnxt_hw_err {
	BNXT_HW_STATUS_HEALTHY			= 0x0,
	BNXT_HW_STATUS_NVM_WRITE_ERR		= 0x1,
	BNXT_HW_STATUS_NVM_ERASE_ERR		= 0x2,
	BNXT_HW_STATUS_NVM_UNKNOWN_ERR		= 0x3,
	BNXT_HW_STATUS_NVM_TEST_VPD_ENT_ERR	= 0x4,
	BNXT_HW_STATUS_NVM_TEST_VPD_READ_ERR	= 0x5,
	BNXT_HW_STATUS_NVM_TEST_VPD_WRITE_ERR	= 0x6,
	BNXT_HW_STATUS_NVM_TEST_INCMPL_ERR	= 0x7,
};

struct bnxt_hw_health {
	u32 nvm_err_address;
	u32 nvm_write_errors;
	u32 nvm_erase_errors;
	u32 nvm_test_vpd_ent_errors;
	u32 nvm_test_vpd_read_errors;
	u32 nvm_test_vpd_write_errors;
	u32 nvm_test_incmpl_errors;
	u8 synd;
	/* max a test in a day if previous test was successful */
#define HW_RETEST_MIN_TIME	(1000 * 3600 * 24)
	u8 nvm_test_result;
	unsigned long nvm_test_timestamp;
	struct devlink_health_reporter *hw_reporter;
};

enum bnxt_health_severity {
	SEVERITY_NORMAL = 0,
	SEVERITY_WARNING,
@@ -2041,6 +2087,7 @@ struct bnxt {
#define BNXT_FW_EXCEPTION_SP_EVENT	19
#define BNXT_LINK_CFG_CHANGE_SP_EVENT	21
#define BNXT_FW_ECHO_REQUEST_SP_EVENT	23
#define BNXT_FW_NVM_ERR_SP_EVENT	25

	struct delayed_work	fw_reset_task;
	int			fw_reset_state;
@@ -2100,8 +2147,8 @@ struct bnxt {
	u32			lpi_tmr_lo;
	u32			lpi_tmr_hi;

	/* copied from flags in hwrm_port_phy_qcaps_output */
	u8			phy_flags;
	/* copied from flags and flags2 in hwrm_port_phy_qcaps_output */
	u32			phy_flags;
#define BNXT_PHY_FL_EEE_CAP		PORT_PHY_QCAPS_RESP_FLAGS_EEE_SUPPORTED
#define BNXT_PHY_FL_EXT_LPBK		PORT_PHY_QCAPS_RESP_FLAGS_EXTERNAL_LPBK_SUPPORTED
#define BNXT_PHY_FL_AN_PHY_LPBK		PORT_PHY_QCAPS_RESP_FLAGS_AUTONEG_LPBK_SUPPORTED
@@ -2110,6 +2157,8 @@ struct bnxt {
#define BNXT_PHY_FL_NO_PHY_LPBK		PORT_PHY_QCAPS_RESP_FLAGS_LOCAL_LPBK_NOT_SUPPORTED
#define BNXT_PHY_FL_FW_MANAGED_LKDN	PORT_PHY_QCAPS_RESP_FLAGS_FW_MANAGED_LINK_DOWN
#define BNXT_PHY_FL_NO_FCS		PORT_PHY_QCAPS_RESP_FLAGS_NO_FCS
#define BNXT_PHY_FL_NO_PAUSE		(PORT_PHY_QCAPS_RESP_FLAGS2_PAUSE_UNSUPPORTED << 8)
#define BNXT_PHY_FL_NO_PFC		(PORT_PHY_QCAPS_RESP_FLAGS2_PFC_UNSUPPORTED << 8)

	u8			num_tests;
	struct bnxt_test_info	*test_info;
@@ -2139,6 +2188,8 @@ struct bnxt {
	struct dentry		*debugfs_pdev;
	struct device		*hwmon_dev;
	enum board_idx		board_idx;

	struct bnxt_hw_health	hw_health;
};

#define BNXT_NUM_RX_RING_STATS			8
+2 −1
Original line number Diff line number Diff line
@@ -627,7 +627,8 @@ static int bnxt_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc)
	int rc;

	if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) ||
	    !(bp->dcbx_cap & DCB_CAP_DCBX_HOST))
	    !(bp->dcbx_cap & DCB_CAP_DCBX_HOST) ||
	    (bp->phy_flags & BNXT_PHY_FL_NO_PAUSE))
		return -EINVAL;

	if (!my_pfc) {
+172 −19
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include "bnxt_ulp.h"
#include "bnxt_ptp.h"
#include "bnxt_coredump.h"
#include "bnxt_nvm_defs.h"	/* NVRAM content constant and structure defs */

static void __bnxt_fw_recover(struct bnxt *bp)
{
@@ -241,37 +242,189 @@ static const struct devlink_health_reporter_ops bnxt_dl_fw_reporter_ops = {
	.recover = bnxt_fw_recover,
};

void bnxt_dl_fw_reporters_create(struct bnxt *bp)
static int bnxt_hw_recover(struct devlink_health_reporter *reporter,
			   void *priv_ctx,
			   struct netlink_ext_ack *extack)
{
	struct bnxt_fw_health *health = bp->fw_health;
	struct bnxt *bp = devlink_health_reporter_priv(reporter);
	struct bnxt_hw_health *hw_health = &bp->hw_health;

	if (!health || health->fw_reporter)
		return;
	hw_health->synd = BNXT_HW_STATUS_HEALTHY;
	return 0;
}

	health->fw_reporter =
		devlink_health_reporter_create(bp->dl, &bnxt_dl_fw_reporter_ops,
					       0, bp);
	if (IS_ERR(health->fw_reporter)) {
		netdev_warn(bp->dev, "Failed to create FW health reporter, rc = %ld\n",
			    PTR_ERR(health->fw_reporter));
		health->fw_reporter = NULL;
		bp->fw_cap &= ~BNXT_FW_CAP_ERROR_RECOVERY;
static const char *hw_err_str(u8 synd)
{
	switch (synd) {
	case BNXT_HW_STATUS_HEALTHY:
		return "healthy";
	case BNXT_HW_STATUS_NVM_WRITE_ERR:
		return "nvm write error";
	case BNXT_HW_STATUS_NVM_ERASE_ERR:
		return "nvm erase error";
	case BNXT_HW_STATUS_NVM_UNKNOWN_ERR:
		return "unrecognized nvm error";
	case BNXT_HW_STATUS_NVM_TEST_VPD_ENT_ERR:
		return "nvm test vpd entry error";
	case BNXT_HW_STATUS_NVM_TEST_VPD_READ_ERR:
		return "nvm test vpd read error";
	case BNXT_HW_STATUS_NVM_TEST_VPD_WRITE_ERR:
		return "nvm test vpd write error";
	case BNXT_HW_STATUS_NVM_TEST_INCMPL_ERR:
		return "nvm test incomplete error";
	default:
		return "unknown hw error";
	}
}

void bnxt_dl_fw_reporters_destroy(struct bnxt *bp, bool all)
static void bnxt_nvm_test(struct bnxt *bp)
{
	struct bnxt_fw_health *health = bp->fw_health;
	struct bnxt_hw_health *h = &bp->hw_health;
	u32 datalen;
	u16 index;
	u8 *buf;

	if (!h->nvm_test_result) {
		if (!h->nvm_test_timestamp ||
		    time_after(jiffies, h->nvm_test_timestamp +
					msecs_to_jiffies(HW_RETEST_MIN_TIME)))
			h->nvm_test_timestamp = jiffies;
		else
			return;
	}

	if (!health)
	if (bnxt_find_nvram_item(bp->dev, BNX_DIR_TYPE_VPD,
				 BNX_DIR_ORDINAL_FIRST, BNX_DIR_EXT_NONE,
				 &index, NULL, &datalen) || !datalen) {
		h->nvm_test_result = BNXT_HW_STATUS_NVM_TEST_VPD_ENT_ERR;
		h->nvm_test_vpd_ent_errors++;
		return;
	}

	if ((bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY) && !all)
	buf = kzalloc(datalen, GFP_KERNEL);
	if (!buf) {
		h->nvm_test_result = BNXT_HW_STATUS_NVM_TEST_INCMPL_ERR;
		h->nvm_test_incmpl_errors++;
		return;
	}

	if (bnxt_get_nvram_item(bp->dev, index, 0, datalen, buf)) {
		h->nvm_test_result = BNXT_HW_STATUS_NVM_TEST_VPD_READ_ERR;
		h->nvm_test_vpd_read_errors++;
		goto err;
	}

	if (bnxt_flash_nvram(bp->dev, BNX_DIR_TYPE_VPD, BNX_DIR_ORDINAL_FIRST,
			     BNX_DIR_EXT_NONE, 0, 0, buf, datalen)) {
		h->nvm_test_result = BNXT_HW_STATUS_NVM_TEST_VPD_WRITE_ERR;
		h->nvm_test_vpd_write_errors++;
	}

err:
	kfree(buf);
}

static int bnxt_hw_diagnose(struct devlink_health_reporter *reporter,
			    struct devlink_fmsg *fmsg,
			    struct netlink_ext_ack *extack)
{
	struct bnxt *bp = devlink_health_reporter_priv(reporter);
	struct bnxt_hw_health *h = &bp->hw_health;
	u8 synd = h->synd;
	int rc;

	bnxt_nvm_test(bp);
	if (h->nvm_test_result) {
		synd = h->nvm_test_result;
		devlink_health_report(h->hw_reporter, hw_err_str(synd), NULL);
	}

	rc = devlink_fmsg_string_pair_put(fmsg, "Status", hw_err_str(synd));
	if (rc)
		return rc;
	rc = devlink_fmsg_u32_pair_put(fmsg, "nvm_write_errors", h->nvm_write_errors);
	if (rc)
		return rc;
	rc = devlink_fmsg_u32_pair_put(fmsg, "nvm_erase_errors", h->nvm_erase_errors);
	if (rc)
		return rc;
	rc = devlink_fmsg_u32_pair_put(fmsg, "nvm_test_vpd_ent_errors",
				       h->nvm_test_vpd_ent_errors);
	if (rc)
		return rc;
	rc = devlink_fmsg_u32_pair_put(fmsg, "nvm_test_vpd_read_errors",
				       h->nvm_test_vpd_read_errors);
	if (rc)
		return rc;
	rc = devlink_fmsg_u32_pair_put(fmsg, "nvm_test_vpd_write_errors",
				       h->nvm_test_vpd_write_errors);
	if (rc)
		return rc;
	rc = devlink_fmsg_u32_pair_put(fmsg, "nvm_test_incomplete_errors",
				       h->nvm_test_incmpl_errors);
	if (rc)
		return rc;

	return 0;
}

void bnxt_devlink_health_hw_report(struct bnxt *bp)
{
	struct bnxt_hw_health *hw_health = &bp->hw_health;

	netdev_warn(bp->dev, "%s reported at address 0x%x\n", hw_err_str(hw_health->synd),
		    hw_health->nvm_err_address);

	devlink_health_report(hw_health->hw_reporter, hw_err_str(hw_health->synd), NULL);
}

static const struct devlink_health_reporter_ops bnxt_dl_hw_reporter_ops = {
	.name = "hw",
	.diagnose = bnxt_hw_diagnose,
	.recover = bnxt_hw_recover,
};

static struct devlink_health_reporter *
__bnxt_dl_reporter_create(struct bnxt *bp,
			  const struct devlink_health_reporter_ops *ops)
{
	struct devlink_health_reporter *reporter;

	reporter = devlink_health_reporter_create(bp->dl, ops, 0, bp);
	if (IS_ERR(reporter)) {
		netdev_warn(bp->dev, "Failed to create %s health reporter, rc = %ld\n",
			    ops->name, PTR_ERR(reporter));
		return NULL;
	}

	return reporter;
}

void bnxt_dl_fw_reporters_create(struct bnxt *bp)
{
	struct bnxt_fw_health *fw_health = bp->fw_health;
	struct bnxt_hw_health *hw_health = &bp->hw_health;

	if (!hw_health->hw_reporter)
		hw_health->hw_reporter = __bnxt_dl_reporter_create(bp, &bnxt_dl_hw_reporter_ops);

	if (fw_health && !fw_health->fw_reporter)
		fw_health->fw_reporter = __bnxt_dl_reporter_create(bp, &bnxt_dl_fw_reporter_ops);
}

void bnxt_dl_fw_reporters_destroy(struct bnxt *bp)
{
	struct bnxt_fw_health *fw_health = bp->fw_health;
	struct bnxt_hw_health *hw_health = &bp->hw_health;

	if (hw_health->hw_reporter) {
		devlink_health_reporter_destroy(hw_health->hw_reporter);
		hw_health->hw_reporter = NULL;
	}

	if (health->fw_reporter) {
		devlink_health_reporter_destroy(health->fw_reporter);
		health->fw_reporter = NULL;
	if (fw_health && fw_health->fw_reporter) {
		devlink_health_reporter_destroy(fw_health->fw_reporter);
		fw_health->fw_reporter = NULL;
	}
}

+2 −1
Original line number Diff line number Diff line
@@ -74,8 +74,9 @@ enum bnxt_dl_version_type {
void bnxt_devlink_health_fw_report(struct bnxt *bp);
void bnxt_dl_health_fw_status_update(struct bnxt *bp, bool healthy);
void bnxt_dl_health_fw_recovery_done(struct bnxt *bp);
void bnxt_devlink_health_hw_report(struct bnxt *bp);
void bnxt_dl_fw_reporters_create(struct bnxt *bp);
void bnxt_dl_fw_reporters_destroy(struct bnxt *bp, bool all);
void bnxt_dl_fw_reporters_destroy(struct bnxt *bp);
int bnxt_dl_register(struct bnxt *bp);
void bnxt_dl_unregister(struct bnxt *bp);

Loading