2094 lines · 58.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* Copyright (c) 2020, Intel Corporation. */ #include <linux/vmalloc.h> #include "ice.h"#include "ice_lib.h"#include "devlink.h"#include "port.h"#include "ice_eswitch.h"#include "ice_fw_update.h"#include "ice_dcb_lib.h"#include "ice_sf_eth.h" /* context for devlink info version reporting */struct ice_info_ctx {	char buf[128];	struct ice_orom_info pending_orom;	struct ice_nvm_info pending_nvm;	struct ice_netlist_info pending_netlist;	struct ice_hw_dev_caps dev_caps;}; /* The following functions are used to format specific strings for various * devlink info versions. The ctx parameter is used to provide the storage * buffer, as well as any ancillary information calculated when the info * request was made. * * If a version does not exist, for example when attempting to get the * inactive version of flash when there is no pending update, the function * should leave the buffer in the ctx structure empty. */ static void ice_info_get_dsn(struct ice_pf *pf, struct ice_info_ctx *ctx){	u8 dsn[8]; 	/* Copy the DSN into an array in Big Endian format */	put_unaligned_be64(pci_get_dsn(pf->pdev), dsn); 	snprintf(ctx->buf, sizeof(ctx->buf), "%8phD", dsn);} static void ice_info_pba(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_hw *hw = &pf->hw;	int status; 	status = ice_read_pba_string(hw, (u8 *)ctx->buf, sizeof(ctx->buf));	if (status)		/* We failed to locate the PBA, so just skip this entry */		dev_dbg(ice_pf_to_dev(pf), "Failed to read Product Board Assembly string, status %d\n",			status);} static void ice_info_fw_mgmt(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_hw *hw = &pf->hw; 	snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u",		 hw->fw_maj_ver, hw->fw_min_ver, hw->fw_patch);} static void ice_info_fw_api(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_hw *hw = &pf->hw; 	snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", hw->api_maj_ver,		 hw->api_min_ver, hw->api_patch);} static void ice_info_fw_build(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_hw *hw = &pf->hw; 	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", hw->fw_build);} static void ice_info_orom_ver(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_orom_info *orom = &pf->hw.flash.orom; 	snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u",		 orom->major, orom->build, orom->patch);} static voidice_info_pending_orom_ver(struct ice_pf __always_unused *pf,			  struct ice_info_ctx *ctx){	struct ice_orom_info *orom = &ctx->pending_orom; 	if (ctx->dev_caps.common_cap.nvm_update_pending_orom)		snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u",			 orom->major, orom->build, orom->patch);} static void ice_info_nvm_ver(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_nvm_info *nvm = &pf->hw.flash.nvm; 	snprintf(ctx->buf, sizeof(ctx->buf), "%x.%02x", nvm->major, nvm->minor);} static voidice_info_pending_nvm_ver(struct ice_pf __always_unused *pf,			 struct ice_info_ctx *ctx){	struct ice_nvm_info *nvm = &ctx->pending_nvm; 	if (ctx->dev_caps.common_cap.nvm_update_pending_nvm)		snprintf(ctx->buf, sizeof(ctx->buf), "%x.%02x",			 nvm->major, nvm->minor);} static void ice_info_eetrack(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_nvm_info *nvm = &pf->hw.flash.nvm; 	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", nvm->eetrack);} static voidice_info_pending_eetrack(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_nvm_info *nvm = &ctx->pending_nvm; 	if (ctx->dev_caps.common_cap.nvm_update_pending_nvm)		snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", nvm->eetrack);} static void ice_info_ddp_pkg_name(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_hw *hw = &pf->hw; 	snprintf(ctx->buf, sizeof(ctx->buf), "%s", hw->active_pkg_name);} static voidice_info_ddp_pkg_version(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_pkg_ver *pkg = &pf->hw.active_pkg_ver; 	snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u.%u",		 pkg->major, pkg->minor, pkg->update, pkg->draft);} static voidice_info_ddp_pkg_bundle_id(struct ice_pf *pf, struct ice_info_ctx *ctx){	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", pf->hw.active_track_id);} static void ice_info_netlist_ver(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_netlist_info *netlist = &pf->hw.flash.netlist; 	/* The netlist version fields are BCD formatted */	snprintf(ctx->buf, sizeof(ctx->buf), "%x.%x.%x-%x.%x.%x",		 netlist->major, netlist->minor,		 netlist->type >> 16, netlist->type & 0xFFFF,		 netlist->rev, netlist->cust_ver);} static void ice_info_netlist_build(struct ice_pf *pf, struct ice_info_ctx *ctx){	struct ice_netlist_info *netlist = &pf->hw.flash.netlist; 	snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", netlist->hash);} static voidice_info_pending_netlist_ver(struct ice_pf __always_unused *pf,			     struct ice_info_ctx *ctx){	struct ice_netlist_info *netlist = &ctx->pending_netlist; 	/* The netlist version fields are BCD formatted */	if (ctx->dev_caps.common_cap.nvm_update_pending_netlist)		snprintf(ctx->buf, sizeof(ctx->buf), "%x.%x.%x-%x.%x.%x",			 netlist->major, netlist->minor,			 netlist->type >> 16, netlist->type & 0xFFFF,			 netlist->rev, netlist->cust_ver);} static voidice_info_pending_netlist_build(struct ice_pf __always_unused *pf,			       struct ice_info_ctx *ctx){	struct ice_netlist_info *netlist = &ctx->pending_netlist; 	if (ctx->dev_caps.common_cap.nvm_update_pending_netlist)		snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", netlist->hash);} static void ice_info_cgu_fw_build(struct ice_pf *pf, struct ice_info_ctx *ctx){	u32 id, cfg_ver, fw_ver; 	if (!ice_is_feature_supported(pf, ICE_F_CGU))		return;	if (ice_aq_get_cgu_info(&pf->hw, &id, &cfg_ver, &fw_ver))		return;	snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", id, cfg_ver, fw_ver);} static void ice_info_cgu_id(struct ice_pf *pf, struct ice_info_ctx *ctx){	if (!ice_is_feature_supported(pf, ICE_F_CGU))		return;	snprintf(ctx->buf, sizeof(ctx->buf), "%u", pf->hw.cgu_part_number);} #define fixed(key, getter) { ICE_VERSION_FIXED, key, getter, NULL }#define running(key, getter) { ICE_VERSION_RUNNING, key, getter, NULL }#define stored(key, getter, fallback) { ICE_VERSION_STORED, key, getter, fallback } /* The combined() macro inserts both the running entry as well as a stored * entry. The running entry will always report the version from the active * handler. The stored entry will first try the pending handler, and fallback * to the active handler if the pending function does not report a version. * The pending handler should check the status of a pending update for the * relevant flash component. It should only fill in the buffer in the case * where a valid pending version is available. This ensures that the related * stored and running versions remain in sync, and that stored versions are * correctly reported as expected. */#define combined(key, active, pending) \	running(key, active), \	stored(key, pending, active) enum ice_version_type {	ICE_VERSION_FIXED,	ICE_VERSION_RUNNING,	ICE_VERSION_STORED,}; static const struct ice_devlink_version {	enum ice_version_type type;	const char *key;	void (*getter)(struct ice_pf *pf, struct ice_info_ctx *ctx);	void (*fallback)(struct ice_pf *pf, struct ice_info_ctx *ctx);} ice_devlink_versions[] = {	fixed(DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, ice_info_pba),	running(DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, ice_info_fw_mgmt),	running("fw.mgmt.api", ice_info_fw_api),	running("fw.mgmt.build", ice_info_fw_build),	combined(DEVLINK_INFO_VERSION_GENERIC_FW_UNDI, ice_info_orom_ver, ice_info_pending_orom_ver),	combined("fw.psid.api", ice_info_nvm_ver, ice_info_pending_nvm_ver),	combined(DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID, ice_info_eetrack, ice_info_pending_eetrack),	running("fw.app.name", ice_info_ddp_pkg_name),	running(DEVLINK_INFO_VERSION_GENERIC_FW_APP, ice_info_ddp_pkg_version),	running("fw.app.bundle_id", ice_info_ddp_pkg_bundle_id),	combined("fw.netlist", ice_info_netlist_ver, ice_info_pending_netlist_ver),	combined("fw.netlist.build", ice_info_netlist_build, ice_info_pending_netlist_build),	fixed("cgu.id", ice_info_cgu_id),	running("fw.cgu", ice_info_cgu_fw_build),}; /** * ice_devlink_info_get - .info_get devlink handler * @devlink: devlink instance structure * @req: the devlink info request * @extack: extended netdev ack structure * * Callback for the devlink .info_get operation. Reports information about the * device. * * Return: zero on success or an error code on failure. */static int ice_devlink_info_get(struct devlink *devlink,				struct devlink_info_req *req,				struct netlink_ext_ack *extack){	struct ice_pf *pf = devlink_priv(devlink);	struct device *dev = ice_pf_to_dev(pf);	struct ice_hw *hw = &pf->hw;	struct ice_info_ctx *ctx;	size_t i;	int err; 	err = ice_wait_for_reset(pf, 10 * HZ);	if (err) {		NL_SET_ERR_MSG_MOD(extack, "Device is busy resetting");		return err;	} 	ctx = kzalloc_obj(*ctx);	if (!ctx)		return -ENOMEM; 	/* discover capabilities first */	err = ice_discover_dev_caps(hw, &ctx->dev_caps);	if (err) {		dev_dbg(dev, "Failed to discover device capabilities, status %d aq_err %s\n",			err, libie_aq_str(hw->adminq.sq_last_status));		NL_SET_ERR_MSG_MOD(extack, "Unable to discover device capabilities");		goto out_free_ctx;	} 	if (ctx->dev_caps.common_cap.nvm_update_pending_orom) {		err = ice_get_inactive_orom_ver(hw, &ctx->pending_orom);		if (err) {			dev_dbg(dev, "Unable to read inactive Option ROM version data, status %d aq_err %s\n",				err, libie_aq_str(hw->adminq.sq_last_status)); 			/* disable display of pending Option ROM */			ctx->dev_caps.common_cap.nvm_update_pending_orom = false;		}	} 	if (ctx->dev_caps.common_cap.nvm_update_pending_nvm) {		err = ice_get_inactive_nvm_ver(hw, &ctx->pending_nvm);		if (err) {			dev_dbg(dev, "Unable to read inactive NVM version data, status %d aq_err %s\n",				err, libie_aq_str(hw->adminq.sq_last_status)); 			/* disable display of pending Option ROM */			ctx->dev_caps.common_cap.nvm_update_pending_nvm = false;		}	} 	if (ctx->dev_caps.common_cap.nvm_update_pending_netlist) {		err = ice_get_inactive_netlist_ver(hw, &ctx->pending_netlist);		if (err) {			dev_dbg(dev, "Unable to read inactive Netlist version data, status %d aq_err %s\n",				err, libie_aq_str(hw->adminq.sq_last_status)); 			/* disable display of pending Option ROM */			ctx->dev_caps.common_cap.nvm_update_pending_netlist = false;		}	} 	ice_info_get_dsn(pf, ctx); 	err = devlink_info_serial_number_put(req, ctx->buf);	if (err) {		NL_SET_ERR_MSG_MOD(extack, "Unable to set serial number");		goto out_free_ctx;	} 	for (i = 0; i < ARRAY_SIZE(ice_devlink_versions); i++) {		enum ice_version_type type = ice_devlink_versions[i].type;		const char *key = ice_devlink_versions[i].key; 		memset(ctx->buf, 0, sizeof(ctx->buf)); 		ice_devlink_versions[i].getter(pf, ctx); 		/* If the default getter doesn't report a version, use the		 * fallback function. This is primarily useful in the case of		 * "stored" versions that want to report the same value as the		 * running version in the normal case of no pending update.		 */		if (ctx->buf[0] == '\0' && ice_devlink_versions[i].fallback)			ice_devlink_versions[i].fallback(pf, ctx); 		/* Do not report missing versions */		if (ctx->buf[0] == '\0')			continue; 		switch (type) {		case ICE_VERSION_FIXED:			err = devlink_info_version_fixed_put(req, key, ctx->buf);			if (err) {				NL_SET_ERR_MSG_MOD(extack, "Unable to set fixed version");				goto out_free_ctx;			}			break;		case ICE_VERSION_RUNNING:			err = devlink_info_version_running_put_ext(req, key,								   ctx->buf,								   DEVLINK_INFO_VERSION_TYPE_COMPONENT);			if (err) {				NL_SET_ERR_MSG_MOD(extack, "Unable to set running version");				goto out_free_ctx;			}			break;		case ICE_VERSION_STORED:			err = devlink_info_version_stored_put_ext(req, key,								  ctx->buf,								  DEVLINK_INFO_VERSION_TYPE_COMPONENT);			if (err) {				NL_SET_ERR_MSG_MOD(extack, "Unable to set stored version");				goto out_free_ctx;			}			break;		}	} out_free_ctx:	kfree(ctx);	return err;} /** * ice_devlink_reload_empr_start - Start EMP reset to activate new firmware * @pf: pointer to the pf instance * @extack: netlink extended ACK structure * * Allow user to activate new Embedded Management Processor firmware by * issuing device specific EMP reset. Called in response to * a DEVLINK_CMD_RELOAD with the DEVLINK_RELOAD_ACTION_FW_ACTIVATE. * * Note that teardown and rebuild of the driver state happens automatically as * part of an interrupt and watchdog task. This is because all physical * functions on the device must be able to reset when an EMP reset occurs from * any source. */static intice_devlink_reload_empr_start(struct ice_pf *pf,			      struct netlink_ext_ack *extack){	struct device *dev = ice_pf_to_dev(pf);	struct ice_hw *hw = &pf->hw;	u8 pending;	int err; 	err = ice_get_pending_updates(pf, &pending, extack);	if (err)		return err; 	/* pending is a bitmask of which flash banks have a pending update,	 * including the main NVM bank, the Option ROM bank, and the netlist	 * bank. If any of these bits are set, then there is a pending update	 * waiting to be activated.	 */	if (!pending) {		NL_SET_ERR_MSG_MOD(extack, "No pending firmware update");		return -ECANCELED;	} 	if (pf->fw_emp_reset_disabled) {		NL_SET_ERR_MSG_MOD(extack, "EMP reset is not available. To activate firmware, a reboot or power cycle is needed");		return -ECANCELED;	} 	dev_dbg(dev, "Issuing device EMP reset to activate firmware\n"); 	err = ice_aq_nvm_update_empr(hw);	if (err) {		dev_err(dev, "Failed to trigger EMP device reset to reload firmware, err %d aq_err %s\n",			err, libie_aq_str(hw->adminq.sq_last_status));		NL_SET_ERR_MSG_MOD(extack, "Failed to trigger EMP device reset to reload firmware");		return err;	} 	return 0;} /** * ice_devlink_reinit_down - unload given PF * @pf: pointer to the PF struct */static void ice_devlink_reinit_down(struct ice_pf *pf){	/* No need to take devl_lock, it's already taken by devlink API */	ice_unload(pf);	rtnl_lock();	ice_vsi_decfg(ice_get_main_vsi(pf));	rtnl_unlock();	ice_deinit_pf(pf);	ice_deinit_hw(&pf->hw);	ice_deinit_dev(pf);} /** * ice_devlink_reload_down - prepare for reload * @devlink: pointer to the devlink instance to reload * @netns_change: if true, the network namespace is changing * @action: the action to perform * @limit: limits on what reload should do, such as not resetting * @extack: netlink extended ACK structure */static intice_devlink_reload_down(struct devlink *devlink, bool netns_change,			enum devlink_reload_action action,			enum devlink_reload_limit limit,			struct netlink_ext_ack *extack){	struct ice_pf *pf = devlink_priv(devlink); 	switch (action) {	case DEVLINK_RELOAD_ACTION_DRIVER_REINIT:		if (ice_is_eswitch_mode_switchdev(pf)) {			NL_SET_ERR_MSG_MOD(extack,					   "Go to legacy mode before doing reinit");			return -EOPNOTSUPP;		}		if (ice_is_adq_active(pf)) {			NL_SET_ERR_MSG_MOD(extack,					   "Turn off ADQ before doing reinit");			return -EOPNOTSUPP;		}		if (ice_has_vfs(pf)) {			NL_SET_ERR_MSG_MOD(extack,					   "Remove all VFs before doing reinit");			return -EOPNOTSUPP;		}		ice_devlink_reinit_down(pf);

Lines 1–500 of 2094.