Add support for devlink reload level fw_reset which does firmware reset
and driver entities re-instantiation. Once this reload command is
executed the driver initiates fw sync reset flow, where the firmware
synchronizes all PFs on coming reset and driver's entities
re-instantiation.
Signed-off-by: Moshe Shemesh <[email protected]>
---
.../net/ethernet/mellanox/mlx5/core/devlink.c | 53 +++++++++++++++++--
.../ethernet/mellanox/mlx5/core/fw_reset.c | 14 +++++
.../ethernet/mellanox/mlx5/core/fw_reset.h | 1 +
3 files changed, 64 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
index 5424e31a0f45..905d55cab4c3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
@@ -4,6 +4,7 @@
#include <devlink.h>
#include "mlx5_core.h"
+#include "fw_reset.h"
#include "fs_core.h"
#include "eswitch.h"
@@ -88,13 +89,48 @@ mlx5_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
return 0;
}
+static int mlx5_devlink_trigger_fw_reset(struct devlink *devlink, struct netlink_ext_ack *extack)
+{
+ struct mlx5_core_dev *dev = devlink_priv(devlink);
+ u8 reset_level, reset_type, net_port_alive;
+ int err;
+
+ err = mlx5_reg_mfrl_query(dev, &reset_level, &reset_type);
+ if (err)
+ return err;
+ if (!(reset_level & MLX5_MFRL_REG_RESET_LEVEL3)) {
+ NL_SET_ERR_MSG_MOD(extack, "FW reset requires reboot");
+ return -EINVAL;
+ }
+
+ net_port_alive = !!(reset_type & MLX5_MFRL_REG_RESET_TYPE_NET_PORT_ALIVE);
+ err = mlx5_fw_set_reset_sync(dev, net_port_alive);
+ if (err)
+ goto out;
+
+ err = mlx5_fw_wait_fw_reset_done(dev);
+out:
+ if (err)
+ NL_SET_ERR_MSG_MOD(extack, "FW reset command failed");
+ return err;
+}
+
static int mlx5_devlink_reload_down(struct devlink *devlink, bool netns_change,
enum devlink_reload_level level, struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
- mlx5_unload_one(dev, false);
- return 0;
+ switch (level) {
+ case DEVLINK_RELOAD_LEVEL_DRIVER:
+ mlx5_unload_one(dev, false);
+ return 0;
+ case DEVLINK_RELOAD_LEVEL_FW_RESET:
+ return mlx5_devlink_trigger_fw_reset(devlink, extack);
+ default:
+ /* Unsupported level should not get to this function */
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
}
static int mlx5_devlink_reload_up(struct devlink *devlink, enum devlink_reload_level level,
@@ -102,7 +138,15 @@ static int mlx5_devlink_reload_up(struct devlink *devlink, enum devlink_reload_l
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
- return mlx5_load_one(dev, false);
+ switch (level) {
+ case DEVLINK_RELOAD_LEVEL_DRIVER:
+ case DEVLINK_RELOAD_LEVEL_FW_RESET:
+ return mlx5_load_one(dev, false);
+ default:
+ /* Unsupported level should not get to this function */
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
}
static const struct devlink_ops mlx5_devlink_ops = {
@@ -118,7 +162,8 @@ static const struct devlink_ops mlx5_devlink_ops = {
#endif
.flash_update = mlx5_devlink_flash_update,
.info_get = mlx5_devlink_info_get,
- .supported_reload_levels = BIT(DEVLINK_RELOAD_LEVEL_DRIVER),
+ .supported_reload_levels = BIT(DEVLINK_RELOAD_LEVEL_DRIVER) |
+ BIT(DEVLINK_RELOAD_LEVEL_FW_RESET),
.default_reload_level = DEVLINK_RELOAD_LEVEL_DRIVER,
.reload_down = mlx5_devlink_reload_down,
.reload_up = mlx5_devlink_reload_up,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
index e665080e9a4e..f95df226b915 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
@@ -239,6 +239,20 @@ static int fw_reset_event_notifier(struct notifier_block *nb, unsigned long acti
return NOTIFY_OK;
}
+#define MLX5_FW_RESET_TIMEOUT_MSEC 5000
+int mlx5_fw_wait_fw_reset_done(struct mlx5_core_dev *dev)
+{
+ unsigned long timeout = msecs_to_jiffies(MLX5_FW_RESET_TIMEOUT_MSEC);
+ struct mlx5_fw_reset *fw_reset = dev->priv.fw_reset;
+
+ if (!wait_for_completion_timeout(&fw_reset->done, timeout)) {
+ mlx5_core_warn(dev, "FW sync reset timeout after %d seconds\n",
+ MLX5_FW_RESET_TIMEOUT_MSEC / 1000);
+ return -ETIMEDOUT;
+ }
+ return fw_reset->ret;
+}
+
int mlx5_fw_reset_events_init(struct mlx5_core_dev *dev)
{
struct mlx5_fw_reset *fw_reset = kzalloc(sizeof(*fw_reset), GFP_KERNEL);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h
index 278f538ea92a..d7ee951a2258 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.h
@@ -10,6 +10,7 @@ int mlx5_reg_mfrl_query(struct mlx5_core_dev *dev, u8 *reset_level, u8 *reset_ty
int mlx5_fw_set_reset_sync(struct mlx5_core_dev *dev, u8 reset_type_sel);
int mlx5_fw_set_live_patch(struct mlx5_core_dev *dev);
+int mlx5_fw_wait_fw_reset_done(struct mlx5_core_dev *dev);
int mlx5_fw_reset_events_init(struct mlx5_core_dev *dev);
void mlx5_fw_reset_events_cleanup(struct mlx5_core_dev *dev);
--
2.17.1