Commit f8921d73 authored by Dirk van der Merwe's avatar Dirk van der Merwe Committed by David S. Miller

nfp: honor FW reset and loading policies

The firmware reset and loading policies can be controlled with the
combination of three hwinfo keys, 'abi_drv_reset', 'abi_drv_load_ifc'
and 'app_fw_from_flash'.

'app_fw_from_flash' defines which firmware should take precedence,
'Disk', 'Flash' or the 'Preferred' firmware. When 'Preferred'
is selected, the management firmware makes the decision on which
firmware will be loaded by comparing versions of the flash firmware
and the host supplied firmware.

'abi_drv_reset' defines when the driver should reset the firmware when
the driver is probed, either 'Disk' if firmware was found on disk,
'Always' reset or 'Never' reset. Note that the device is always reset
on driver unload if firmware was loaded when the driver was probed.

'abi_drv_load_ifc' defines a list of PF devices allowed to load FW on
the device.

Furthermore, we limit the cases to where the driver will unload firmware
again when the driver is removed to only when firmware was loaded by the
driver and only if this particular device was the only one that could
have loaded firmware. This is needed to avoid firmware being removed
while in use on multi-host platforms.
Signed-off-by: default avatarDirk van der Merwe <dirk.vandermerwe@netronome.com>
Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: default avatarSimon Horman <simon.horman@netronome.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e69e9db9
...@@ -352,7 +352,7 @@ nfp_net_fw_request(struct pci_dev *pdev, struct nfp_pf *pf, const char *name) ...@@ -352,7 +352,7 @@ nfp_net_fw_request(struct pci_dev *pdev, struct nfp_pf *pf, const char *name)
err = request_firmware_direct(&fw, name, &pdev->dev); err = request_firmware_direct(&fw, name, &pdev->dev);
nfp_info(pf->cpp, " %s: %s\n", nfp_info(pf->cpp, " %s: %s\n",
name, err ? "not found" : "found, loading..."); name, err ? "not found" : "found");
if (err) if (err)
return NULL; return NULL;
...@@ -430,6 +430,33 @@ nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf) ...@@ -430,6 +430,33 @@ nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf)
return nfp_net_fw_request(pdev, pf, fw_name); return nfp_net_fw_request(pdev, pf, fw_name);
} }
static int
nfp_get_fw_policy_value(struct pci_dev *pdev, struct nfp_nsp *nsp,
const char *key, const char *default_val, int max_val,
int *value)
{
char hwinfo[64];
long hi_val;
int err;
snprintf(hwinfo, sizeof(hwinfo), key);
err = nfp_nsp_hwinfo_lookup_optional(nsp, hwinfo, sizeof(hwinfo),
default_val);
if (err)
return err;
err = kstrtol(hwinfo, 0, &hi_val);
if (err || hi_val < 0 || hi_val > max_val) {
dev_warn(&pdev->dev,
"Invalid value '%s' from '%s', ignoring\n",
hwinfo, key);
err = kstrtol(default_val, 0, &hi_val);
}
*value = hi_val;
return err;
}
/** /**
* nfp_net_fw_load() - Load the firmware image * nfp_net_fw_load() - Load the firmware image
* @pdev: PCI Device structure * @pdev: PCI Device structure
...@@ -441,44 +468,107 @@ nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf) ...@@ -441,44 +468,107 @@ nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf)
static int static int
nfp_fw_load(struct pci_dev *pdev, struct nfp_pf *pf, struct nfp_nsp *nsp) nfp_fw_load(struct pci_dev *pdev, struct nfp_pf *pf, struct nfp_nsp *nsp)
{ {
const struct firmware *fw; bool do_reset, fw_loaded = false;
const struct firmware *fw = NULL;
int err, reset, policy, ifcs = 0;
char *token, *ptr;
char hwinfo[64];
u16 interface; u16 interface;
int err;
snprintf(hwinfo, sizeof(hwinfo), "abi_drv_load_ifc");
err = nfp_nsp_hwinfo_lookup_optional(nsp, hwinfo, sizeof(hwinfo),
NFP_NSP_DRV_LOAD_IFC_DEFAULT);
if (err)
return err;
interface = nfp_cpp_interface(pf->cpp); interface = nfp_cpp_interface(pf->cpp);
if (NFP_CPP_INTERFACE_UNIT_of(interface) != 0) { ptr = hwinfo;
/* Only Unit 0 should reset or load firmware */ while ((token = strsep(&ptr, ","))) {
unsigned long interface_hi;
err = kstrtoul(token, 0, &interface_hi);
if (err) {
dev_err(&pdev->dev,
"Failed to parse interface '%s': %d\n",
token, err);
return err;
}
ifcs++;
if (interface == interface_hi)
break;
}
if (!token) {
dev_info(&pdev->dev, "Firmware will be loaded by partner\n"); dev_info(&pdev->dev, "Firmware will be loaded by partner\n");
return 0; return 0;
} }
err = nfp_get_fw_policy_value(pdev, nsp, "abi_drv_reset",
NFP_NSP_DRV_RESET_DEFAULT,
NFP_NSP_DRV_RESET_NEVER, &reset);
if (err)
return err;
err = nfp_get_fw_policy_value(pdev, nsp, "app_fw_from_flash",
NFP_NSP_APP_FW_LOAD_DEFAULT,
NFP_NSP_APP_FW_LOAD_PREF, &policy);
if (err)
return err;
fw = nfp_net_fw_find(pdev, pf); fw = nfp_net_fw_find(pdev, pf);
if (!fw) { do_reset = reset == NFP_NSP_DRV_RESET_ALWAYS ||
if (nfp_nsp_has_stored_fw_load(nsp)) (fw && reset == NFP_NSP_DRV_RESET_DISK);
nfp_nsp_load_stored_fw(nsp);
return 0; if (do_reset) {
dev_info(&pdev->dev, "Soft-resetting the NFP\n");
err = nfp_nsp_device_soft_reset(nsp);
if (err < 0) {
dev_err(&pdev->dev,
"Failed to soft reset the NFP: %d\n", err);
goto exit_release_fw;
}
} }
dev_info(&pdev->dev, "Soft-reset, loading FW image\n"); if (fw && policy != NFP_NSP_APP_FW_LOAD_FLASH) {
err = nfp_nsp_device_soft_reset(nsp); if (nfp_nsp_has_fw_loaded(nsp) && nfp_nsp_fw_loaded(nsp))
if (err < 0) { goto exit_release_fw;
dev_err(&pdev->dev, "Failed to soft reset the NFP: %d\n",
err);
goto exit_release_fw;
}
err = nfp_nsp_load_fw(nsp, fw); err = nfp_nsp_load_fw(nsp, fw);
if (err < 0) { if (err < 0) {
dev_err(&pdev->dev, "FW loading failed: %d\n", err); dev_err(&pdev->dev, "FW loading failed: %d\n",
goto exit_release_fw; err);
goto exit_release_fw;
}
dev_info(&pdev->dev, "Finished loading FW image\n");
fw_loaded = true;
} else if (policy != NFP_NSP_APP_FW_LOAD_DISK &&
nfp_nsp_has_stored_fw_load(nsp)) {
/* Don't propagate this error to stick with legacy driver
* behavior, failure will be detected later during init.
*/
if (!nfp_nsp_load_stored_fw(nsp))
dev_info(&pdev->dev, "Finished loading stored FW image\n");
/* Don't flag the fw_loaded in this case since other devices
* may reuse the firmware when configured this way
*/
} else {
dev_warn(&pdev->dev, "Didn't load firmware, please update flash or reconfigure card\n");
} }
dev_info(&pdev->dev, "Finished loading FW image\n");
exit_release_fw: exit_release_fw:
release_firmware(fw); release_firmware(fw);
return err < 0 ? err : 1; /* We don't want to unload firmware when other devices may still be
* dependent on it, which could be the case if there are multiple
* devices that could load firmware.
*/
if (fw_loaded && ifcs == 1)
pf->unload_fw_on_remove = true;
return err < 0 ? err : fw_loaded;
} }
static void static void
...@@ -704,7 +794,7 @@ static int nfp_pci_probe(struct pci_dev *pdev, ...@@ -704,7 +794,7 @@ static int nfp_pci_probe(struct pci_dev *pdev,
err_fw_unload: err_fw_unload:
kfree(pf->rtbl); kfree(pf->rtbl);
nfp_mip_close(pf->mip); nfp_mip_close(pf->mip);
if (pf->fw_loaded) if (pf->unload_fw_on_remove)
nfp_fw_unload(pf); nfp_fw_unload(pf);
kfree(pf->eth_tbl); kfree(pf->eth_tbl);
kfree(pf->nspi); kfree(pf->nspi);
...@@ -744,7 +834,7 @@ static void __nfp_pci_shutdown(struct pci_dev *pdev, bool unload_fw) ...@@ -744,7 +834,7 @@ static void __nfp_pci_shutdown(struct pci_dev *pdev, bool unload_fw)
vfree(pf->dumpspec); vfree(pf->dumpspec);
kfree(pf->rtbl); kfree(pf->rtbl);
nfp_mip_close(pf->mip); nfp_mip_close(pf->mip);
if (unload_fw && pf->fw_loaded) if (unload_fw && pf->unload_fw_on_remove)
nfp_fw_unload(pf); nfp_fw_unload(pf);
destroy_workqueue(pf->wq); destroy_workqueue(pf->wq);
......
...@@ -64,6 +64,7 @@ struct nfp_dumpspec { ...@@ -64,6 +64,7 @@ struct nfp_dumpspec {
* @limit_vfs: Number of VFs supported by firmware (~0 for PCI limit) * @limit_vfs: Number of VFs supported by firmware (~0 for PCI limit)
* @num_vfs: Number of SR-IOV VFs enabled * @num_vfs: Number of SR-IOV VFs enabled
* @fw_loaded: Is the firmware loaded? * @fw_loaded: Is the firmware loaded?
* @unload_fw_on_remove:Do we need to unload firmware on driver removal?
* @ctrl_vnic: Pointer to the control vNIC if available * @ctrl_vnic: Pointer to the control vNIC if available
* @mip: MIP handle * @mip: MIP handle
* @rtbl: RTsym table * @rtbl: RTsym table
...@@ -110,6 +111,7 @@ struct nfp_pf { ...@@ -110,6 +111,7 @@ struct nfp_pf {
unsigned int num_vfs; unsigned int num_vfs;
bool fw_loaded; bool fw_loaded;
bool unload_fw_on_remove;
struct nfp_net *ctrl_vnic; struct nfp_net *ctrl_vnic;
......
...@@ -102,6 +102,21 @@ enum nfp_eth_fec { ...@@ -102,6 +102,21 @@ enum nfp_eth_fec {
#define NFP_FEC_REED_SOLOMON BIT(NFP_FEC_REED_SOLOMON_BIT) #define NFP_FEC_REED_SOLOMON BIT(NFP_FEC_REED_SOLOMON_BIT)
#define NFP_FEC_DISABLED BIT(NFP_FEC_DISABLED_BIT) #define NFP_FEC_DISABLED BIT(NFP_FEC_DISABLED_BIT)
/* Defines the valid values of the 'abi_drv_reset' hwinfo key */
#define NFP_NSP_DRV_RESET_DISK 0
#define NFP_NSP_DRV_RESET_ALWAYS 1
#define NFP_NSP_DRV_RESET_NEVER 2
#define NFP_NSP_DRV_RESET_DEFAULT "0"
/* Defines the valid values of the 'app_fw_from_flash' hwinfo key */
#define NFP_NSP_APP_FW_LOAD_DISK 0
#define NFP_NSP_APP_FW_LOAD_FLASH 1
#define NFP_NSP_APP_FW_LOAD_PREF 2
#define NFP_NSP_APP_FW_LOAD_DEFAULT "2"
/* Define the default value for the 'abi_drv_load_ifc' key */
#define NFP_NSP_DRV_LOAD_IFC_DEFAULT "0x10ff"
/** /**
* struct nfp_eth_table - ETH table information * struct nfp_eth_table - ETH table information
* @count: number of table entries * @count: number of table entries
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment