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 <mo...@mellanox.com>
---
 .../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

Reply via email to