Commit e3dd7627 authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-fw_load_policy'

Ido Schimmel says:

====================
mlxsw: Add 'fw_load_policy' devlink parameter

Shalom says:

Currently, drivers do not have the ability to control the firmware
loading policy and they always use their own fixed policy. This prevents
drivers from running the device with a different firmware version for
testing and/or debugging purposes. For example, testing a firmware bug
fix.

For these situations, the new devlink generic parameter,
'fw_load_policy', gives the ability to control this option and allows
drivers to run with a different firmware version than required by the
driver.

Patch #1 adds the new parameter to devlink. The other two patches, #2
and #3, add support for this parameter in the mlxsw driver.

Example:
  # Query the devlink parameters supported by the device
    $ devlink dev param show
    pci/0000:03:00.0:
      name fw_load_policy type generic
        values:
          cmode driverinit value driver

  # Flash new firmware using ethtool
    $ ethtool -f swp1 mellanox/mlxsw_spectrum-13.1703.4.mfa2

  # Toggle parameter
    $ devlink dev param set pci/0000:03:00.0 name fw_load_policy value flash cmode driverinit

  # devlink reset
    $ devlink dev reload pci/0000:03:00.0

  # Query firmware version to show changes took affect
    $ ethtool -i swp1
    driver: mlxsw_spectrum
    version: 1.0
    firmware-version: 13.1703.4
    expansion-rom-version:
    bus-info: 0000:03:00.0
    supports-statistics: yes
    supports-test: no
    supports-eeprom-access: no
    supports-register-dump: no
    supports-priv-flags: no

iproute2 patches available here:
https://github.com/tshalom/iproute2-next

v2:
* Change 'fw_version_check' to 'fw_load_policy' with values 'driver' and
  'flash' (Jakub)
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6915bf3b 064501c5
...@@ -40,3 +40,12 @@ msix_vec_per_pf_min [DEVICE, GENERIC] ...@@ -40,3 +40,12 @@ msix_vec_per_pf_min [DEVICE, GENERIC]
for the device initialization. Value is same across all for the device initialization. Value is same across all
physical functions (PFs) in the device. physical functions (PFs) in the device.
Type: u32 Type: u32
fw_load_policy [DEVICE, GENERIC]
Controls the device's firmware loading policy.
Valid values:
* DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER (0)
Load firmware version preferred by the driver.
* DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH (1)
Load firmware currently stored in flash.
Type: u8
...@@ -965,10 +965,11 @@ static const struct devlink_ops mlxsw_devlink_ops = { ...@@ -965,10 +965,11 @@ static const struct devlink_ops mlxsw_devlink_ops = {
.sb_occ_tc_port_bind_get = mlxsw_devlink_sb_occ_tc_port_bind_get, .sb_occ_tc_port_bind_get = mlxsw_devlink_sb_occ_tc_port_bind_get,
}; };
int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, static int
const struct mlxsw_bus *mlxsw_bus, __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
void *bus_priv, bool reload, const struct mlxsw_bus *mlxsw_bus,
struct devlink *devlink) void *bus_priv, bool reload,
struct devlink *devlink)
{ {
const char *device_kind = mlxsw_bus_info->device_kind; const char *device_kind = mlxsw_bus_info->device_kind;
struct mlxsw_core *mlxsw_core; struct mlxsw_core *mlxsw_core;
...@@ -1035,6 +1036,12 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, ...@@ -1035,6 +1036,12 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
goto err_devlink_register; goto err_devlink_register;
} }
if (mlxsw_driver->params_register && !reload) {
err = mlxsw_driver->params_register(mlxsw_core);
if (err)
goto err_register_params;
}
err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon); err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon);
if (err) if (err)
goto err_hwmon_init; goto err_hwmon_init;
...@@ -1057,6 +1064,9 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, ...@@ -1057,6 +1064,9 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
err_thermal_init: err_thermal_init:
mlxsw_hwmon_fini(mlxsw_core->hwmon); mlxsw_hwmon_fini(mlxsw_core->hwmon);
err_hwmon_init: err_hwmon_init:
if (mlxsw_driver->params_unregister && !reload)
mlxsw_driver->params_unregister(mlxsw_core);
err_register_params:
if (!reload) if (!reload)
devlink_unregister(devlink); devlink_unregister(devlink);
err_devlink_register: err_devlink_register:
...@@ -1076,6 +1086,29 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, ...@@ -1076,6 +1086,29 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
err_devlink_alloc: err_devlink_alloc:
return err; return err;
} }
int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
const struct mlxsw_bus *mlxsw_bus,
void *bus_priv, bool reload,
struct devlink *devlink)
{
bool called_again = false;
int err;
again:
err = __mlxsw_core_bus_device_register(mlxsw_bus_info, mlxsw_bus,
bus_priv, reload, devlink);
/* -EAGAIN is returned in case the FW was updated. FW needs
* a reset, so lets try to call __mlxsw_core_bus_device_register()
* again.
*/
if (err == -EAGAIN && !called_again) {
called_again = true;
goto again;
}
return err;
}
EXPORT_SYMBOL(mlxsw_core_bus_device_register); EXPORT_SYMBOL(mlxsw_core_bus_device_register);
void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
...@@ -1097,6 +1130,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, ...@@ -1097,6 +1130,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
mlxsw_core->driver->fini(mlxsw_core); mlxsw_core->driver->fini(mlxsw_core);
mlxsw_thermal_fini(mlxsw_core->thermal); mlxsw_thermal_fini(mlxsw_core->thermal);
mlxsw_hwmon_fini(mlxsw_core->hwmon); mlxsw_hwmon_fini(mlxsw_core->hwmon);
if (mlxsw_core->driver->params_unregister && !reload)
mlxsw_core->driver->params_unregister(mlxsw_core);
if (!reload) if (!reload)
devlink_unregister(devlink); devlink_unregister(devlink);
mlxsw_emad_fini(mlxsw_core); mlxsw_emad_fini(mlxsw_core);
...@@ -1109,6 +1144,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, ...@@ -1109,6 +1144,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
return; return;
reload_fail_deinit: reload_fail_deinit:
if (mlxsw_core->driver->params_unregister)
mlxsw_core->driver->params_unregister(mlxsw_core);
devlink_unregister(devlink); devlink_unregister(devlink);
devlink_resources_unregister(devlink, NULL); devlink_resources_unregister(devlink, NULL);
devlink_free(devlink); devlink_free(devlink);
......
...@@ -282,6 +282,8 @@ struct mlxsw_driver { ...@@ -282,6 +282,8 @@ struct mlxsw_driver {
const struct mlxsw_config_profile *profile, const struct mlxsw_config_profile *profile,
u64 *p_single_size, u64 *p_double_size, u64 *p_single_size, u64 *p_double_size,
u64 *p_linear_size); u64 *p_linear_size);
int (*params_register)(struct mlxsw_core *mlxsw_core);
void (*params_unregister)(struct mlxsw_core *mlxsw_core);
u8 txhdr_len; u8 txhdr_len;
const struct mlxsw_config_profile *profile; const struct mlxsw_config_profile *profile;
bool res_query_enabled; bool res_query_enabled;
......
...@@ -1720,7 +1720,6 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1720,7 +1720,6 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ {
const char *driver_name = pdev->driver->name; const char *driver_name = pdev->driver->name;
struct mlxsw_pci *mlxsw_pci; struct mlxsw_pci *mlxsw_pci;
bool called_again = false;
int err; int err;
mlxsw_pci = kzalloc(sizeof(*mlxsw_pci), GFP_KERNEL); mlxsw_pci = kzalloc(sizeof(*mlxsw_pci), GFP_KERNEL);
...@@ -1777,18 +1776,10 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1777,18 +1776,10 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mlxsw_pci->bus_info.dev = &pdev->dev; mlxsw_pci->bus_info.dev = &pdev->dev;
mlxsw_pci->id = id; mlxsw_pci->id = id;
again:
err = mlxsw_core_bus_device_register(&mlxsw_pci->bus_info, err = mlxsw_core_bus_device_register(&mlxsw_pci->bus_info,
&mlxsw_pci_bus, mlxsw_pci, false, &mlxsw_pci_bus, mlxsw_pci, false,
NULL); NULL);
/* -EAGAIN is returned in case the FW was updated. FW needs if (err) {
* a reset, so lets try to call mlxsw_core_bus_device_register()
* again.
*/
if (err == -EAGAIN && !called_again) {
called_again = true;
goto again;
} else if (err) {
dev_err(&pdev->dev, "cannot register bus device\n"); dev_err(&pdev->dev, "cannot register bus device\n");
goto err_bus_device_register; goto err_bus_device_register;
} }
......
...@@ -318,6 +318,7 @@ static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp) ...@@ -318,6 +318,7 @@ static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp)
const struct mlxsw_fw_rev *rev = &mlxsw_sp->bus_info->fw_rev; const struct mlxsw_fw_rev *rev = &mlxsw_sp->bus_info->fw_rev;
const struct mlxsw_fw_rev *req_rev = mlxsw_sp->req_rev; const struct mlxsw_fw_rev *req_rev = mlxsw_sp->req_rev;
const char *fw_filename = mlxsw_sp->fw_filename; const char *fw_filename = mlxsw_sp->fw_filename;
union devlink_param_value value;
const struct firmware *firmware; const struct firmware *firmware;
int err; int err;
...@@ -325,6 +326,15 @@ static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp) ...@@ -325,6 +326,15 @@ static int mlxsw_sp_fw_rev_validate(struct mlxsw_sp *mlxsw_sp)
if (!req_rev || !fw_filename) if (!req_rev || !fw_filename)
return 0; return 0;
/* Don't check if devlink 'fw_load_policy' param is 'flash' */
err = devlink_param_driverinit_value_get(priv_to_devlink(mlxsw_sp->core),
DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
&value);
if (err)
return err;
if (value.vu8 == DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH)
return 0;
/* Validate driver & FW are compatible */ /* Validate driver & FW are compatible */
if (rev->major != req_rev->major) { if (rev->major != req_rev->major) {
WARN(1, "Mismatch in major FW version [%d:%d] is never expected; Please contact support\n", WARN(1, "Mismatch in major FW version [%d:%d] is never expected; Please contact support\n",
...@@ -4328,6 +4338,52 @@ static int mlxsw_sp_kvd_sizes_get(struct mlxsw_core *mlxsw_core, ...@@ -4328,6 +4338,52 @@ static int mlxsw_sp_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
return 0; return 0;
} }
static int
mlxsw_sp_devlink_param_fw_load_policy_validate(struct devlink *devlink, u32 id,
union devlink_param_value val,
struct netlink_ext_ack *extack)
{
if ((val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER) &&
(val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH)) {
NL_SET_ERR_MSG_MOD(extack, "'fw_load_policy' must be 'driver' or 'flash'");
return -EINVAL;
}
return 0;
}
static const struct devlink_param mlxsw_sp_devlink_params[] = {
DEVLINK_PARAM_GENERIC(FW_LOAD_POLICY,
BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
NULL, NULL,
mlxsw_sp_devlink_param_fw_load_policy_validate),
};
static int mlxsw_sp_params_register(struct mlxsw_core *mlxsw_core)
{
struct devlink *devlink = priv_to_devlink(mlxsw_core);
union devlink_param_value value;
int err;
err = devlink_params_register(devlink, mlxsw_sp_devlink_params,
ARRAY_SIZE(mlxsw_sp_devlink_params));
if (err)
return err;
value.vu8 = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER;
devlink_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
value);
return 0;
}
static void mlxsw_sp_params_unregister(struct mlxsw_core *mlxsw_core)
{
devlink_params_unregister(priv_to_devlink(mlxsw_core),
mlxsw_sp_devlink_params,
ARRAY_SIZE(mlxsw_sp_devlink_params));
}
static struct mlxsw_driver mlxsw_sp1_driver = { static struct mlxsw_driver mlxsw_sp1_driver = {
.kind = mlxsw_sp1_driver_name, .kind = mlxsw_sp1_driver_name,
.priv_size = sizeof(struct mlxsw_sp), .priv_size = sizeof(struct mlxsw_sp),
...@@ -4349,6 +4405,8 @@ static struct mlxsw_driver mlxsw_sp1_driver = { ...@@ -4349,6 +4405,8 @@ static struct mlxsw_driver mlxsw_sp1_driver = {
.txhdr_construct = mlxsw_sp_txhdr_construct, .txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp1_resources_register, .resources_register = mlxsw_sp1_resources_register,
.kvd_sizes_get = mlxsw_sp_kvd_sizes_get, .kvd_sizes_get = mlxsw_sp_kvd_sizes_get,
.params_register = mlxsw_sp_params_register,
.params_unregister = mlxsw_sp_params_unregister,
.txhdr_len = MLXSW_TXHDR_LEN, .txhdr_len = MLXSW_TXHDR_LEN,
.profile = &mlxsw_sp1_config_profile, .profile = &mlxsw_sp1_config_profile,
.res_query_enabled = true, .res_query_enabled = true,
...@@ -4374,6 +4432,8 @@ static struct mlxsw_driver mlxsw_sp2_driver = { ...@@ -4374,6 +4432,8 @@ static struct mlxsw_driver mlxsw_sp2_driver = {
.sb_occ_tc_port_bind_get = mlxsw_sp_sb_occ_tc_port_bind_get, .sb_occ_tc_port_bind_get = mlxsw_sp_sb_occ_tc_port_bind_get,
.txhdr_construct = mlxsw_sp_txhdr_construct, .txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp2_resources_register, .resources_register = mlxsw_sp2_resources_register,
.params_register = mlxsw_sp_params_register,
.params_unregister = mlxsw_sp_params_unregister,
.txhdr_len = MLXSW_TXHDR_LEN, .txhdr_len = MLXSW_TXHDR_LEN,
.profile = &mlxsw_sp2_config_profile, .profile = &mlxsw_sp2_config_profile,
.res_query_enabled = true, .res_query_enabled = true,
......
...@@ -365,6 +365,7 @@ enum devlink_param_generic_id { ...@@ -365,6 +365,7 @@ enum devlink_param_generic_id {
DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI, DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI,
DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX, DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN, DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
/* add new param generic ids above here*/ /* add new param generic ids above here*/
__DEVLINK_PARAM_GENERIC_ID_MAX, __DEVLINK_PARAM_GENERIC_ID_MAX,
...@@ -392,6 +393,9 @@ enum devlink_param_generic_id { ...@@ -392,6 +393,9 @@ enum devlink_param_generic_id {
#define DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME "msix_vec_per_pf_min" #define DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME "msix_vec_per_pf_min"
#define DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE DEVLINK_PARAM_TYPE_U32 #define DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE DEVLINK_PARAM_TYPE_U32
#define DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME "fw_load_policy"
#define DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE DEVLINK_PARAM_TYPE_U8
#define DEVLINK_PARAM_GENERIC(_id, _cmodes, _get, _set, _validate) \ #define DEVLINK_PARAM_GENERIC(_id, _cmodes, _get, _set, _validate) \
{ \ { \
.id = DEVLINK_PARAM_GENERIC_ID_##_id, \ .id = DEVLINK_PARAM_GENERIC_ID_##_id, \
......
...@@ -163,6 +163,11 @@ enum devlink_param_cmode { ...@@ -163,6 +163,11 @@ enum devlink_param_cmode {
DEVLINK_PARAM_CMODE_MAX = __DEVLINK_PARAM_CMODE_MAX - 1 DEVLINK_PARAM_CMODE_MAX = __DEVLINK_PARAM_CMODE_MAX - 1
}; };
enum devlink_param_fw_load_policy_value {
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
};
enum devlink_attr { enum devlink_attr {
/* don't change the order or add anything between, this is ABI! */ /* don't change the order or add anything between, this is ABI! */
DEVLINK_ATTR_UNSPEC, DEVLINK_ATTR_UNSPEC,
......
...@@ -2692,6 +2692,11 @@ static const struct devlink_param devlink_param_generic[] = { ...@@ -2692,6 +2692,11 @@ static const struct devlink_param devlink_param_generic[] = {
.name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME, .name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME,
.type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE, .type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE,
}, },
{
.id = DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
.name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME,
.type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE,
},
}; };
static int devlink_param_generic_verify(const struct devlink_param *param) static int devlink_param_generic_verify(const struct devlink_param *param)
......
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