linux/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c

1246 lines
33 KiB
C
Raw Normal View History

/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2017 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
#include <net/devlink.h>
#include "bnxt_hsi.h"
#include "bnxt.h"
#include "bnxt_hwrm.h"
#include "bnxt_vfr.h"
#include "bnxt_devlink.h"
#include "bnxt_ethtool.h"
#include "bnxt_ulp.h"
#include "bnxt_ptp.h"
#include "bnxt_coredump.h"
static void __bnxt_fw_recover(struct bnxt *bp)
{
if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state) ||
test_bit(BNXT_STATE_FW_NON_FATAL_COND, &bp->state))
bnxt_fw_reset(bp);
else
bnxt_fw_exception(bp);
}
static int
bnxt_dl_flash_update(struct devlink *dl,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = bnxt_get_bp_from_dl(dl);
int rc;
if (!BNXT_PF(bp)) {
NL_SET_ERR_MSG_MOD(extack,
"flash update not supported from a VF");
return -EPERM;
}
devlink: check flash_update parameter support in net core When implementing .flash_update, drivers which do not support per-component update are manually checking the component parameter to verify that it is NULL. Without this check, the driver might accept an update request with a component specified even though it will not honor such a request. Instead of having each driver check this, move the logic into net/core/devlink.c, and use a new `supported_flash_update_params` field in the devlink_ops. Drivers which will support per-component update must now specify this by setting DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT in the supported_flash_update_params in their devlink_ops. This helps ensure that drivers do not forget to check for a NULL component if they do not support per-component update. This also enables a slightly better error message by enabling the core stack to set the netlink bad attribute message to indicate precisely the unsupported attribute in the message. Going forward, any new additional parameter to flash update will require a bit in the supported_flash_update_params bitfield. Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Reviewed-by: Jakub Kicinski <kuba@kernel.org> Cc: Jiri Pirko <jiri@mellanox.com> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Michael Chan <michael.chan@broadcom.com> Cc: Bin Luo <luobin9@huawei.com> Cc: Saeed Mahameed <saeedm@mellanox.com> Cc: Leon Romanovsky <leon@kernel.org> Cc: Ido Schimmel <idosch@mellanox.com> Cc: Danielle Ratson <danieller@mellanox.com> Cc: Shannon Nelson <snelson@pensando.io> Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-25 20:46:05 +00:00
devlink_flash_update_status_notify(dl, "Preparing to flash", NULL, 0, 0);
rc = bnxt_flash_package_from_fw_obj(bp->dev, params->fw, 0);
if (!rc)
devlink: check flash_update parameter support in net core When implementing .flash_update, drivers which do not support per-component update are manually checking the component parameter to verify that it is NULL. Without this check, the driver might accept an update request with a component specified even though it will not honor such a request. Instead of having each driver check this, move the logic into net/core/devlink.c, and use a new `supported_flash_update_params` field in the devlink_ops. Drivers which will support per-component update must now specify this by setting DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT in the supported_flash_update_params in their devlink_ops. This helps ensure that drivers do not forget to check for a NULL component if they do not support per-component update. This also enables a slightly better error message by enabling the core stack to set the netlink bad attribute message to indicate precisely the unsupported attribute in the message. Going forward, any new additional parameter to flash update will require a bit in the supported_flash_update_params bitfield. Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Reviewed-by: Jakub Kicinski <kuba@kernel.org> Cc: Jiri Pirko <jiri@mellanox.com> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Michael Chan <michael.chan@broadcom.com> Cc: Bin Luo <luobin9@huawei.com> Cc: Saeed Mahameed <saeedm@mellanox.com> Cc: Leon Romanovsky <leon@kernel.org> Cc: Ido Schimmel <idosch@mellanox.com> Cc: Danielle Ratson <danieller@mellanox.com> Cc: Shannon Nelson <snelson@pensando.io> Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-25 20:46:05 +00:00
devlink_flash_update_status_notify(dl, "Flashing done", NULL, 0, 0);
else
devlink: check flash_update parameter support in net core When implementing .flash_update, drivers which do not support per-component update are manually checking the component parameter to verify that it is NULL. Without this check, the driver might accept an update request with a component specified even though it will not honor such a request. Instead of having each driver check this, move the logic into net/core/devlink.c, and use a new `supported_flash_update_params` field in the devlink_ops. Drivers which will support per-component update must now specify this by setting DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT in the supported_flash_update_params in their devlink_ops. This helps ensure that drivers do not forget to check for a NULL component if they do not support per-component update. This also enables a slightly better error message by enabling the core stack to set the netlink bad attribute message to indicate precisely the unsupported attribute in the message. Going forward, any new additional parameter to flash update will require a bit in the supported_flash_update_params bitfield. Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Reviewed-by: Jakub Kicinski <kuba@kernel.org> Cc: Jiri Pirko <jiri@mellanox.com> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Michael Chan <michael.chan@broadcom.com> Cc: Bin Luo <luobin9@huawei.com> Cc: Saeed Mahameed <saeedm@mellanox.com> Cc: Leon Romanovsky <leon@kernel.org> Cc: Ido Schimmel <idosch@mellanox.com> Cc: Danielle Ratson <danieller@mellanox.com> Cc: Shannon Nelson <snelson@pensando.io> Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-25 20:46:05 +00:00
devlink_flash_update_status_notify(dl, "Flashing failed", NULL, 0, 0);
return rc;
}
static int bnxt_hwrm_remote_dev_reset_set(struct bnxt *bp, bool remote_reset)
{
struct hwrm_func_cfg_input *req;
int rc;
if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET_IF)
return -EOPNOTSUPP;
rc = hwrm_req_init(bp, req, HWRM_FUNC_CFG);
if (rc)
return rc;
req->fid = cpu_to_le16(0xffff);
req->enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_HOT_RESET_IF_SUPPORT);
if (remote_reset)
req->flags = cpu_to_le32(FUNC_CFG_REQ_FLAGS_HOT_RESET_IF_EN_DIS);
return hwrm_req_send(bp, req);
}
static char *bnxt_health_severity_str(enum bnxt_health_severity severity)
{
switch (severity) {
case SEVERITY_NORMAL: return "normal";
case SEVERITY_WARNING: return "warning";
case SEVERITY_RECOVERABLE: return "recoverable";
case SEVERITY_FATAL: return "fatal";
default: return "unknown";
}
}
static char *bnxt_health_remedy_str(enum bnxt_health_remedy remedy)
{
switch (remedy) {
case REMEDY_DEVLINK_RECOVER: return "devlink recover";
case REMEDY_POWER_CYCLE_DEVICE: return "device power cycle";
case REMEDY_POWER_CYCLE_HOST: return "host power cycle";
case REMEDY_FW_UPDATE: return "update firmware";
case REMEDY_HW_REPLACE: return "replace hardware";
default: return "unknown";
}
}
static int bnxt_fw_diagnose(struct devlink_health_reporter *reporter,
struct devlink_fmsg *fmsg,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = devlink_health_reporter_priv(reporter);
struct bnxt_fw_health *h = bp->fw_health;
u32 fw_status, fw_resets;
int rc;
if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state))
return devlink_fmsg_string_pair_put(fmsg, "Status", "recovering");
if (!h->status_reliable)
return devlink_fmsg_string_pair_put(fmsg, "Status", "unknown");
mutex_lock(&h->lock);
fw_status = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG);
if (BNXT_FW_IS_BOOTING(fw_status)) {
rc = devlink_fmsg_string_pair_put(fmsg, "Status", "initializing");
if (rc)
goto unlock;
} else if (h->severity || fw_status != BNXT_FW_STATUS_HEALTHY) {
if (!h->severity) {
h->severity = SEVERITY_FATAL;
h->remedy = REMEDY_POWER_CYCLE_DEVICE;
h->diagnoses++;
devlink_health_report(h->fw_reporter,
"FW error diagnosed", h);
}
rc = devlink_fmsg_string_pair_put(fmsg, "Status", "error");
if (rc)
goto unlock;
rc = devlink_fmsg_u32_pair_put(fmsg, "Syndrome", fw_status);
if (rc)
goto unlock;
} else {
rc = devlink_fmsg_string_pair_put(fmsg, "Status", "healthy");
if (rc)
goto unlock;
}
rc = devlink_fmsg_string_pair_put(fmsg, "Severity",
bnxt_health_severity_str(h->severity));
if (rc)
goto unlock;
if (h->severity) {
rc = devlink_fmsg_string_pair_put(fmsg, "Remedy",
bnxt_health_remedy_str(h->remedy));
if (rc)
goto unlock;
if (h->remedy == REMEDY_DEVLINK_RECOVER) {
rc = devlink_fmsg_string_pair_put(fmsg, "Impact",
"traffic+ntuple_cfg");
if (rc)
goto unlock;
}
}
unlock:
mutex_unlock(&h->lock);
if (rc || !h->resets_reliable)
return rc;
fw_resets = bnxt_fw_health_readl(bp, BNXT_FW_RESET_CNT_REG);
rc = devlink_fmsg_u32_pair_put(fmsg, "Resets", fw_resets);
if (rc)
return rc;
rc = devlink_fmsg_u32_pair_put(fmsg, "Arrests", h->arrests);
if (rc)
return rc;
rc = devlink_fmsg_u32_pair_put(fmsg, "Survivals", h->survivals);
if (rc)
return rc;
rc = devlink_fmsg_u32_pair_put(fmsg, "Discoveries", h->discoveries);
if (rc)
return rc;
rc = devlink_fmsg_u32_pair_put(fmsg, "Fatalities", h->fatalities);
if (rc)
return rc;
return devlink_fmsg_u32_pair_put(fmsg, "Diagnoses", h->diagnoses);
}
static int bnxt_fw_dump(struct devlink_health_reporter *reporter,
struct devlink_fmsg *fmsg, void *priv_ctx,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = devlink_health_reporter_priv(reporter);
u32 dump_len;
void *data;
int rc;
/* TODO: no firmware dump support in devlink_health_report() context */
if (priv_ctx)
return -EOPNOTSUPP;
dump_len = bnxt_get_coredump_length(bp, BNXT_DUMP_LIVE);
if (!dump_len)
return -EIO;
data = vmalloc(dump_len);
if (!data)
return -ENOMEM;
rc = bnxt_get_coredump(bp, BNXT_DUMP_LIVE, data, &dump_len);
if (!rc) {
rc = devlink_fmsg_pair_nest_start(fmsg, "core");
if (rc)
goto exit;
rc = devlink_fmsg_binary_pair_put(fmsg, "data", data, dump_len);
if (rc)
goto exit;
rc = devlink_fmsg_u32_pair_put(fmsg, "size", dump_len);
if (rc)
goto exit;
rc = devlink_fmsg_pair_nest_end(fmsg);
}
exit:
vfree(data);
return rc;
}
static int bnxt_fw_recover(struct devlink_health_reporter *reporter,
void *priv_ctx,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = devlink_health_reporter_priv(reporter);
if (bp->fw_health->severity == SEVERITY_FATAL)
return -ENODEV;
set_bit(BNXT_STATE_RECOVER, &bp->state);
__bnxt_fw_recover(bp);
return -EINPROGRESS;
}
static const struct devlink_health_reporter_ops bnxt_dl_fw_reporter_ops = {
.name = "fw",
.diagnose = bnxt_fw_diagnose,
.dump = bnxt_fw_dump,
.recover = bnxt_fw_recover,
};
void bnxt_dl_fw_reporters_create(struct bnxt *bp)
{
struct bnxt_fw_health *health = bp->fw_health;
if (!health || health->fw_reporter)
return;
health->fw_reporter =
devlink_health_reporter_create(bp->dl, &bnxt_dl_fw_reporter_ops,
0, bp);
if (IS_ERR(health->fw_reporter)) {
netdev_warn(bp->dev, "Failed to create FW health reporter, rc = %ld\n",
PTR_ERR(health->fw_reporter));
health->fw_reporter = NULL;
bp->fw_cap &= ~BNXT_FW_CAP_ERROR_RECOVERY;
}
}
void bnxt_dl_fw_reporters_destroy(struct bnxt *bp, bool all)
{
struct bnxt_fw_health *health = bp->fw_health;
if (!health)
return;
if ((bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY) && !all)
return;
if (health->fw_reporter) {
devlink_health_reporter_destroy(health->fw_reporter);
health->fw_reporter = NULL;
}
}
void bnxt_devlink_health_fw_report(struct bnxt *bp)
{
struct bnxt_fw_health *fw_health = bp->fw_health;
int rc;
if (!fw_health)
return;
if (!fw_health->fw_reporter) {
__bnxt_fw_recover(bp);
return;
}
mutex_lock(&fw_health->lock);
fw_health->severity = SEVERITY_RECOVERABLE;
fw_health->remedy = REMEDY_DEVLINK_RECOVER;
mutex_unlock(&fw_health->lock);
rc = devlink_health_report(fw_health->fw_reporter, "FW error reported",
fw_health);
if (rc == -ECANCELED)
__bnxt_fw_recover(bp);
}
void bnxt_dl_health_fw_status_update(struct bnxt *bp, bool healthy)
{
struct bnxt_fw_health *fw_health = bp->fw_health;
u8 state;
mutex_lock(&fw_health->lock);
if (healthy) {
fw_health->severity = SEVERITY_NORMAL;
state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
} else {
fw_health->severity = SEVERITY_FATAL;
fw_health->remedy = REMEDY_POWER_CYCLE_DEVICE;
state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
}
mutex_unlock(&fw_health->lock);
devlink_health_reporter_state_update(fw_health->fw_reporter, state);
}
void bnxt_dl_health_fw_recovery_done(struct bnxt *bp)
{
struct bnxt_dl *dl = devlink_priv(bp->dl);
devlink_health_reporter_recovery_done(bp->fw_health->fw_reporter);
bnxt_hwrm_remote_dev_reset_set(bp, dl->remote_reset);
}
static int bnxt_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
struct netlink_ext_ack *extack);
static void
bnxt_dl_livepatch_report_err(struct bnxt *bp, struct netlink_ext_ack *extack,
struct hwrm_fw_livepatch_output *resp)
{
int err = ((struct hwrm_err_output *)resp)->cmd_err;
switch (err) {
case FW_LIVEPATCH_CMD_ERR_CODE_INVALID_OPCODE:
netdev_err(bp->dev, "Illegal live patch opcode");
NL_SET_ERR_MSG_MOD(extack, "Invalid opcode");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_NOT_SUPPORTED:
NL_SET_ERR_MSG_MOD(extack, "Live patch operation not supported");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_NOT_INSTALLED:
NL_SET_ERR_MSG_MOD(extack, "Live patch not found");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_NOT_PATCHED:
NL_SET_ERR_MSG_MOD(extack,
"Live patch deactivation failed. Firmware not patched.");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_AUTH_FAIL:
NL_SET_ERR_MSG_MOD(extack, "Live patch not authenticated");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_INVALID_HEADER:
NL_SET_ERR_MSG_MOD(extack, "Incompatible live patch");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_INVALID_SIZE:
NL_SET_ERR_MSG_MOD(extack, "Live patch has invalid size");
break;
case FW_LIVEPATCH_CMD_ERR_CODE_ALREADY_PATCHED:
NL_SET_ERR_MSG_MOD(extack, "Live patch already applied");
break;
default:
netdev_err(bp->dev, "Unexpected live patch error: %d\n", err);
NL_SET_ERR_MSG_MOD(extack, "Failed to activate live patch");
break;
}
}
static int
bnxt_dl_livepatch_activate(struct bnxt *bp, struct netlink_ext_ack *extack)
{
struct hwrm_fw_livepatch_query_output *query_resp;
struct hwrm_fw_livepatch_query_input *query_req;
struct hwrm_fw_livepatch_output *patch_resp;
struct hwrm_fw_livepatch_input *patch_req;
u32 installed = 0;
u16 flags;
u8 target;
int rc;
if (~bp->fw_cap & BNXT_FW_CAP_LIVEPATCH) {
NL_SET_ERR_MSG_MOD(extack, "Device does not support live patch");
return -EOPNOTSUPP;
}
rc = hwrm_req_init(bp, query_req, HWRM_FW_LIVEPATCH_QUERY);
if (rc)
return rc;
query_resp = hwrm_req_hold(bp, query_req);
rc = hwrm_req_init(bp, patch_req, HWRM_FW_LIVEPATCH);
if (rc) {
hwrm_req_drop(bp, query_req);
return rc;
}
patch_req->opcode = FW_LIVEPATCH_REQ_OPCODE_ACTIVATE;
patch_req->loadtype = FW_LIVEPATCH_REQ_LOADTYPE_NVM_INSTALL;
patch_resp = hwrm_req_hold(bp, patch_req);
for (target = 1; target <= FW_LIVEPATCH_REQ_FW_TARGET_LAST; target++) {
query_req->fw_target = target;
rc = hwrm_req_send(bp, query_req);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to query packages");
break;
}
flags = le16_to_cpu(query_resp->status_flags);
if (~flags & FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_INSTALL)
continue;
if ((flags & FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_ACTIVE) &&
!strncmp(query_resp->active_ver, query_resp->install_ver,
sizeof(query_resp->active_ver)))
continue;
patch_req->fw_target = target;
rc = hwrm_req_send(bp, patch_req);
if (rc) {
bnxt_dl_livepatch_report_err(bp, extack, patch_resp);
break;
}
installed++;
}
if (!rc && !installed) {
NL_SET_ERR_MSG_MOD(extack, "No live patches found");
rc = -ENOENT;
}
hwrm_req_drop(bp, query_req);
hwrm_req_drop(bp, patch_req);
return rc;
}
static int bnxt_dl_reload_down(struct devlink *dl, bool netns_change,
enum devlink_reload_action action,
enum devlink_reload_limit limit,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = bnxt_get_bp_from_dl(dl);
int rc = 0;
switch (action) {
case DEVLINK_RELOAD_ACTION_DRIVER_REINIT: {
rtnl_lock();
if (bnxt_sriov_cfg(bp)) {
NL_SET_ERR_MSG_MOD(extack,
"reload is unsupported while VFs are allocated or being configured");
rtnl_unlock();
return -EOPNOTSUPP;
}
if (bp->dev->reg_state == NETREG_UNREGISTERED) {
rtnl_unlock();
return -ENODEV;
}
bnxt_ulp_stop(bp);
if (netif_running(bp->dev)) {
rc = bnxt_close_nic(bp, true, true);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to close");
dev_close(bp->dev);
rtnl_unlock();
break;
}
}
bnxt_vf_reps_free(bp);
rc = bnxt_hwrm_func_drv_unrgtr(bp);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to deregister");
if (netif_running(bp->dev))
dev_close(bp->dev);
rtnl_unlock();
break;
}
bnxt_cancel_reservations(bp, false);
bnxt_free_ctx_mem(bp);
kfree(bp->ctx);
bp->ctx = NULL;
break;
}
case DEVLINK_RELOAD_ACTION_FW_ACTIVATE: {
if (limit == DEVLINK_RELOAD_LIMIT_NO_RESET)
return bnxt_dl_livepatch_activate(bp, extack);
if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET) {
NL_SET_ERR_MSG_MOD(extack, "Device not capable, requires reboot");
return -EOPNOTSUPP;
}
if (!bnxt_hwrm_reset_permitted(bp)) {
NL_SET_ERR_MSG_MOD(extack,
"Reset denied by firmware, it may be inhibited by remote driver");
return -EPERM;
}
rtnl_lock();
if (bp->dev->reg_state == NETREG_UNREGISTERED) {
rtnl_unlock();
return -ENODEV;
}
if (netif_running(bp->dev))
set_bit(BNXT_STATE_FW_ACTIVATE, &bp->state);
rc = bnxt_hwrm_firmware_reset(bp->dev,
FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIP,
FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP,
FW_RESET_REQ_FLAGS_RESET_GRACEFUL |
FW_RESET_REQ_FLAGS_FW_ACTIVATION);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Failed to activate firmware");
clear_bit(BNXT_STATE_FW_ACTIVATE, &bp->state);
rtnl_unlock();
}
break;
}
default:
rc = -EOPNOTSUPP;
}
return rc;
}
static int bnxt_dl_reload_up(struct devlink *dl, enum devlink_reload_action action,
enum devlink_reload_limit limit, u32 *actions_performed,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = bnxt_get_bp_from_dl(dl);
int rc = 0;
*actions_performed = 0;
switch (action) {
case DEVLINK_RELOAD_ACTION_DRIVER_REINIT: {
bnxt_fw_init_one(bp);
bnxt_vf_reps_alloc(bp);
if (netif_running(bp->dev))
rc = bnxt_open_nic(bp, true, true);
bnxt_ulp_start(bp, rc);
if (!rc) {
bnxt_reenable_sriov(bp);
bnxt_ptp_reapply_pps(bp);
}
break;
}
case DEVLINK_RELOAD_ACTION_FW_ACTIVATE: {
unsigned long start = jiffies;
unsigned long timeout = start + BNXT_DFLT_FW_RST_MAX_DSECS * HZ / 10;
if (limit == DEVLINK_RELOAD_LIMIT_NO_RESET)
break;
if (bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY)
timeout = start + bp->fw_health->normal_func_wait_dsecs * HZ / 10;
if (!netif_running(bp->dev))
NL_SET_ERR_MSG_MOD(extack,
"Device is closed, not waiting for reset notice that will never come");
rtnl_unlock();
while (test_bit(BNXT_STATE_FW_ACTIVATE, &bp->state)) {
if (time_after(jiffies, timeout)) {
NL_SET_ERR_MSG_MOD(extack, "Activation incomplete");
rc = -ETIMEDOUT;
break;
}
if (test_bit(BNXT_STATE_ABORT_ERR, &bp->state)) {
NL_SET_ERR_MSG_MOD(extack, "Activation aborted");
rc = -ENODEV;
break;
}
msleep(50);
}
rtnl_lock();
if (!rc)
*actions_performed |= BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT);
clear_bit(BNXT_STATE_FW_ACTIVATE, &bp->state);
break;
}
default:
return -EOPNOTSUPP;
}
if (!rc) {
bnxt_print_device_info(bp);
if (netif_running(bp->dev)) {
mutex_lock(&bp->link_lock);
bnxt_report_link(bp);
mutex_unlock(&bp->link_lock);
}
*actions_performed |= BIT(action);
} else if (netif_running(bp->dev)) {
dev_close(bp->dev);
}
rtnl_unlock();
return rc;
}
static const struct devlink_ops bnxt_dl_ops = {
#ifdef CONFIG_BNXT_SRIOV
.eswitch_mode_set = bnxt_dl_eswitch_mode_set,
.eswitch_mode_get = bnxt_dl_eswitch_mode_get,
#endif /* CONFIG_BNXT_SRIOV */
.info_get = bnxt_dl_info_get,
.flash_update = bnxt_dl_flash_update,
.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) |
BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE),
.reload_limits = BIT(DEVLINK_RELOAD_LIMIT_NO_RESET),
.reload_down = bnxt_dl_reload_down,
.reload_up = bnxt_dl_reload_up,
};
static const struct devlink_ops bnxt_vf_dl_ops;
enum bnxt_dl_param_id {
BNXT_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK,
};
static const struct bnxt_dl_nvm_param nvm_params[] = {
{DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV, NVM_OFF_ENABLE_SRIOV,
BNXT_NVM_SHARED_CFG, 1, 1},
{DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI, NVM_OFF_IGNORE_ARI,
BNXT_NVM_SHARED_CFG, 1, 1},
{DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
NVM_OFF_MSIX_VEC_PER_PF_MAX, BNXT_NVM_SHARED_CFG, 10, 4},
{DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
NVM_OFF_MSIX_VEC_PER_PF_MIN, BNXT_NVM_SHARED_CFG, 7, 4},
{BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK, NVM_OFF_DIS_GRE_VER_CHECK,
BNXT_NVM_SHARED_CFG, 1, 1},
};
union bnxt_nvm_data {
u8 val8;
__le32 val32;
};
static void bnxt_copy_to_nvm_data(union bnxt_nvm_data *dst,
union devlink_param_value *src,
int nvm_num_bits, int dl_num_bytes)
{
u32 val32 = 0;
if (nvm_num_bits == 1) {
dst->val8 = src->vbool;
return;
}
if (dl_num_bytes == 4)
val32 = src->vu32;
else if (dl_num_bytes == 2)
val32 = (u32)src->vu16;
else if (dl_num_bytes == 1)
val32 = (u32)src->vu8;
dst->val32 = cpu_to_le32(val32);
}
static void bnxt_copy_from_nvm_data(union devlink_param_value *dst,
union bnxt_nvm_data *src,
int nvm_num_bits, int dl_num_bytes)
{
u32 val32;
if (nvm_num_bits == 1) {
dst->vbool = src->val8;
return;
}
val32 = le32_to_cpu(src->val32);
if (dl_num_bytes == 4)
dst->vu32 = val32;
else if (dl_num_bytes == 2)
dst->vu16 = (u16)val32;
else if (dl_num_bytes == 1)
dst->vu8 = (u8)val32;
}
static int bnxt_hwrm_get_nvm_cfg_ver(struct bnxt *bp, u32 *nvm_cfg_ver)
{
struct hwrm_nvm_get_variable_input *req;
u16 bytes = BNXT_NVM_CFG_VER_BYTES;
u16 bits = BNXT_NVM_CFG_VER_BITS;
union devlink_param_value ver;
union bnxt_nvm_data *data;
dma_addr_t data_dma_addr;
int rc, i = 2;
u16 dim = 1;
rc = hwrm_req_init(bp, req, HWRM_NVM_GET_VARIABLE);
if (rc)
return rc;
data = hwrm_req_dma_slice(bp, req, sizeof(*data), &data_dma_addr);
if (!data) {
rc = -ENOMEM;
goto exit;
}
/* earlier devices present as an array of raw bytes */
if (!BNXT_CHIP_P5(bp)) {
dim = 0;
i = 0;
bits *= 3; /* array of 3 version components */
bytes *= 4; /* copy whole word */
}
hwrm_req_hold(bp, req);
req->dest_data_addr = cpu_to_le64(data_dma_addr);
req->data_len = cpu_to_le16(bits);
req->option_num = cpu_to_le16(NVM_OFF_NVM_CFG_VER);
req->dimensions = cpu_to_le16(dim);
while (i >= 0) {
req->index_0 = cpu_to_le16(i--);
rc = hwrm_req_send_silent(bp, req);
if (rc)
goto exit;
bnxt_copy_from_nvm_data(&ver, data, bits, bytes);
if (BNXT_CHIP_P5(bp)) {
*nvm_cfg_ver <<= 8;
*nvm_cfg_ver |= ver.vu8;
} else {
*nvm_cfg_ver = ver.vu32;
}
}
exit:
hwrm_req_drop(bp, req);
return rc;
}
static int bnxt_dl_info_put(struct bnxt *bp, struct devlink_info_req *req,
enum bnxt_dl_version_type type, const char *key,
char *buf)
{
if (!strlen(buf))
return 0;
if ((bp->flags & BNXT_FLAG_CHIP_P5) &&
(!strcmp(key, DEVLINK_INFO_VERSION_GENERIC_FW_NCSI) ||
!strcmp(key, DEVLINK_INFO_VERSION_GENERIC_FW_ROCE)))
return 0;
switch (type) {
case BNXT_VERSION_FIXED:
return devlink_info_version_fixed_put(req, key, buf);
case BNXT_VERSION_RUNNING:
return devlink_info_version_running_put(req, key, buf);
case BNXT_VERSION_STORED:
return devlink_info_version_stored_put(req, key, buf);
}
return 0;
}
#define BNXT_FW_SRT_PATCH "fw.srt.patch"
#define BNXT_FW_CRT_PATCH "fw.crt.patch"
static int bnxt_dl_livepatch_info_put(struct bnxt *bp,
struct devlink_info_req *req,
const char *key)
{
struct hwrm_fw_livepatch_query_input *query;
struct hwrm_fw_livepatch_query_output *resp;
u16 flags;
int rc;
if (~bp->fw_cap & BNXT_FW_CAP_LIVEPATCH)
return 0;
rc = hwrm_req_init(bp, query, HWRM_FW_LIVEPATCH_QUERY);
if (rc)
return rc;
if (!strcmp(key, BNXT_FW_SRT_PATCH))
query->fw_target = FW_LIVEPATCH_QUERY_REQ_FW_TARGET_SECURE_FW;
else if (!strcmp(key, BNXT_FW_CRT_PATCH))
query->fw_target = FW_LIVEPATCH_QUERY_REQ_FW_TARGET_COMMON_FW;
else
goto exit;
resp = hwrm_req_hold(bp, query);
rc = hwrm_req_send(bp, query);
if (rc)
goto exit;
flags = le16_to_cpu(resp->status_flags);
if (flags & FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_ACTIVE) {
resp->active_ver[sizeof(resp->active_ver) - 1] = '\0';
rc = devlink_info_version_running_put(req, key, resp->active_ver);
if (rc)
goto exit;
}
if (flags & FW_LIVEPATCH_QUERY_RESP_STATUS_FLAGS_INSTALL) {
resp->install_ver[sizeof(resp->install_ver) - 1] = '\0';
rc = devlink_info_version_stored_put(req, key, resp->install_ver);
if (rc)
goto exit;
}
exit:
hwrm_req_drop(bp, query);
return rc;
}
#define HWRM_FW_VER_STR_LEN 16
static int bnxt_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct hwrm_nvm_get_dev_info_output nvm_dev_info;
struct bnxt *bp = bnxt_get_bp_from_dl(dl);
struct hwrm_ver_get_output *ver_resp;
char mgmt_ver[FW_VER_STR_LEN];
char roce_ver[FW_VER_STR_LEN];
char ncsi_ver[FW_VER_STR_LEN];
char buf[32];
u32 ver = 0;
int rc;
rc = devlink_info_driver_name_put(req, DRV_MODULE_NAME);
if (rc)
return rc;
if (BNXT_PF(bp) && (bp->flags & BNXT_FLAG_DSN_VALID)) {
sprintf(buf, "%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X",
bp->dsn[7], bp->dsn[6], bp->dsn[5], bp->dsn[4],
bp->dsn[3], bp->dsn[2], bp->dsn[1], bp->dsn[0]);
rc = devlink_info_serial_number_put(req, buf);
if (rc)
return rc;
}
if (strlen(bp->board_serialno)) {
rc = devlink_info_board_serial_number_put(req, bp->board_serialno);
if (rc)
return rc;
}
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_FIXED,
DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,
bp->board_partno);
if (rc)
return rc;
sprintf(buf, "%X", bp->chip_num);
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_FIXED,
DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, buf);
if (rc)
return rc;
ver_resp = &bp->ver_resp;
sprintf(buf, "%c%d", 'A' + ver_resp->chip_rev, ver_resp->chip_metal);
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_FIXED,
DEVLINK_INFO_VERSION_GENERIC_ASIC_REV, buf);
if (rc)
return rc;
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW_PSID,
bp->nvm_cfg_ver);
if (rc)
return rc;
buf[0] = 0;
strncat(buf, ver_resp->active_pkg_name, HWRM_FW_VER_STR_LEN);
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW, buf);
if (rc)
return rc;
if (BNXT_PF(bp) && !bnxt_hwrm_get_nvm_cfg_ver(bp, &ver)) {
sprintf(buf, "%d.%d.%d", (ver >> 16) & 0xff, (ver >> 8) & 0xff,
ver & 0xff);
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW_PSID,
buf);
if (rc)
return rc;
}
if (ver_resp->flags & VER_GET_RESP_FLAGS_EXT_VER_AVAIL) {
snprintf(mgmt_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->hwrm_fw_major, ver_resp->hwrm_fw_minor,
ver_resp->hwrm_fw_build, ver_resp->hwrm_fw_patch);
snprintf(ncsi_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->mgmt_fw_major, ver_resp->mgmt_fw_minor,
ver_resp->mgmt_fw_build, ver_resp->mgmt_fw_patch);
snprintf(roce_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->roce_fw_major, ver_resp->roce_fw_minor,
ver_resp->roce_fw_build, ver_resp->roce_fw_patch);
} else {
snprintf(mgmt_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->hwrm_fw_maj_8b, ver_resp->hwrm_fw_min_8b,
ver_resp->hwrm_fw_bld_8b, ver_resp->hwrm_fw_rsvd_8b);
snprintf(ncsi_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->mgmt_fw_maj_8b, ver_resp->mgmt_fw_min_8b,
ver_resp->mgmt_fw_bld_8b, ver_resp->mgmt_fw_rsvd_8b);
snprintf(roce_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
ver_resp->roce_fw_maj_8b, ver_resp->roce_fw_min_8b,
ver_resp->roce_fw_bld_8b, ver_resp->roce_fw_rsvd_8b);
}
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, mgmt_ver);
if (rc)
return rc;
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT_API,
bp->hwrm_ver_supp);
if (rc)
return rc;
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW_NCSI, ncsi_ver);
if (rc)
return rc;
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_RUNNING,
DEVLINK_INFO_VERSION_GENERIC_FW_ROCE, roce_ver);
if (rc)
return rc;
rc = bnxt_hwrm_nvm_get_dev_info(bp, &nvm_dev_info);
if (rc ||
!(nvm_dev_info.flags & NVM_GET_DEV_INFO_RESP_FLAGS_FW_VER_VALID)) {
if (!bnxt_get_pkginfo(bp->dev, buf, sizeof(buf)))
return bnxt_dl_info_put(bp, req, BNXT_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW,
buf);
return 0;
}
buf[0] = 0;
strncat(buf, nvm_dev_info.pkg_name, HWRM_FW_VER_STR_LEN);
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW, buf);
if (rc)
return rc;
snprintf(mgmt_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
nvm_dev_info.hwrm_fw_major, nvm_dev_info.hwrm_fw_minor,
nvm_dev_info.hwrm_fw_build, nvm_dev_info.hwrm_fw_patch);
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, mgmt_ver);
if (rc)
return rc;
snprintf(ncsi_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
nvm_dev_info.mgmt_fw_major, nvm_dev_info.mgmt_fw_minor,
nvm_dev_info.mgmt_fw_build, nvm_dev_info.mgmt_fw_patch);
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW_NCSI, ncsi_ver);
if (rc)
return rc;
snprintf(roce_ver, FW_VER_STR_LEN, "%d.%d.%d.%d",
nvm_dev_info.roce_fw_major, nvm_dev_info.roce_fw_minor,
nvm_dev_info.roce_fw_build, nvm_dev_info.roce_fw_patch);
rc = bnxt_dl_info_put(bp, req, BNXT_VERSION_STORED,
DEVLINK_INFO_VERSION_GENERIC_FW_ROCE, roce_ver);
if (rc)
return rc;
rc = bnxt_dl_livepatch_info_put(bp, req, BNXT_FW_SRT_PATCH);
if (rc)
return rc;
return bnxt_dl_livepatch_info_put(bp, req, BNXT_FW_CRT_PATCH);
}
static int bnxt_hwrm_nvm_req(struct bnxt *bp, u32 param_id, void *msg,
union devlink_param_value *val)
{
struct hwrm_nvm_get_variable_input *req = msg;
struct bnxt_dl_nvm_param nvm_param;
struct hwrm_err_output *resp;
union bnxt_nvm_data *data;
dma_addr_t data_dma_addr;
int idx = 0, rc, i;
/* Get/Set NVM CFG parameter is supported only on PFs */
if (BNXT_VF(bp)) {
hwrm_req_drop(bp, req);
return -EPERM;
}
for (i = 0; i < ARRAY_SIZE(nvm_params); i++) {
if (nvm_params[i].id == param_id) {
nvm_param = nvm_params[i];
break;
}
}
if (i == ARRAY_SIZE(nvm_params)) {
hwrm_req_drop(bp, req);
return -EOPNOTSUPP;
}
if (nvm_param.dir_type == BNXT_NVM_PORT_CFG)
idx = bp->pf.port_id;
else if (nvm_param.dir_type == BNXT_NVM_FUNC_CFG)
idx = bp->pf.fw_fid - BNXT_FIRST_PF_FID;
data = hwrm_req_dma_slice(bp, req, sizeof(*data), &data_dma_addr);
if (!data) {
hwrm_req_drop(bp, req);
return -ENOMEM;
}
req->dest_data_addr = cpu_to_le64(data_dma_addr);
req->data_len = cpu_to_le16(nvm_param.nvm_num_bits);
req->option_num = cpu_to_le16(nvm_param.offset);
req->index_0 = cpu_to_le16(idx);
if (idx)
req->dimensions = cpu_to_le16(1);
resp = hwrm_req_hold(bp, req);
if (req->req_type == cpu_to_le16(HWRM_NVM_SET_VARIABLE)) {
bnxt_copy_to_nvm_data(data, val, nvm_param.nvm_num_bits,
nvm_param.dl_num_bytes);
rc = hwrm_req_send(bp, msg);
} else {
rc = hwrm_req_send_silent(bp, msg);
if (!rc) {
bnxt_copy_from_nvm_data(val, data,
nvm_param.nvm_num_bits,
nvm_param.dl_num_bytes);
} else {
if (resp->cmd_err ==
NVM_GET_VARIABLE_CMD_ERR_CODE_VAR_NOT_EXIST)
rc = -EOPNOTSUPP;
}
}
hwrm_req_drop(bp, req);
if (rc == -EACCES)
netdev_err(bp->dev, "PF does not have admin privileges to modify NVM config\n");
return rc;
}
static int bnxt_dl_nvm_param_get(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct bnxt *bp = bnxt_get_bp_from_dl(dl);
struct hwrm_nvm_get_variable_input *req;
int rc;
rc = hwrm_req_init(bp, req, HWRM_NVM_GET_VARIABLE);
if (rc)
return rc;
rc = bnxt_hwrm_nvm_req(bp, id, req, &ctx->val);
if (!rc && id == BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK)
ctx->val.vbool = !ctx->val.vbool;
return rc;
}
static int bnxt_dl_nvm_param_set(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct bnxt *bp = bnxt_get_bp_from_dl(dl);
struct hwrm_nvm_set_variable_input *req;
int rc;
rc = hwrm_req_init(bp, req, HWRM_NVM_SET_VARIABLE);
if (rc)
return rc;
if (id == BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK)
ctx->val.vbool = !ctx->val.vbool;
return bnxt_hwrm_nvm_req(bp, id, req, &ctx->val);
}
static int bnxt_dl_msix_validate(struct devlink *dl, u32 id,
union devlink_param_value val,
struct netlink_ext_ack *extack)
{
int max_val = -1;
if (id == DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX)
max_val = BNXT_MSIX_VEC_MAX;
if (id == DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN)
max_val = BNXT_MSIX_VEC_MIN_MAX;
if (val.vu32 > max_val) {
NL_SET_ERR_MSG_MOD(extack, "MSIX value is exceeding the range");
return -EINVAL;
}
return 0;
}
static int bnxt_remote_dev_reset_get(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct bnxt *bp = bnxt_get_bp_from_dl(dl);
if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET_IF)
return -EOPNOTSUPP;
ctx->val.vbool = bnxt_dl_get_remote_reset(dl);
return 0;
}
static int bnxt_remote_dev_reset_set(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct bnxt *bp = bnxt_get_bp_from_dl(dl);
int rc;
rc = bnxt_hwrm_remote_dev_reset_set(bp, ctx->val.vbool);
if (rc)
return rc;
bnxt_dl_set_remote_reset(dl, ctx->val.vbool);
return rc;
}
static const struct devlink_param bnxt_dl_params[] = {
DEVLINK_PARAM_GENERIC(ENABLE_SRIOV,
BIT(DEVLINK_PARAM_CMODE_PERMANENT),
bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
NULL),
DEVLINK_PARAM_GENERIC(IGNORE_ARI,
BIT(DEVLINK_PARAM_CMODE_PERMANENT),
bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
NULL),
DEVLINK_PARAM_GENERIC(MSIX_VEC_PER_PF_MAX,
BIT(DEVLINK_PARAM_CMODE_PERMANENT),
bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
bnxt_dl_msix_validate),
DEVLINK_PARAM_GENERIC(MSIX_VEC_PER_PF_MIN,
BIT(DEVLINK_PARAM_CMODE_PERMANENT),
bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
bnxt_dl_msix_validate),
DEVLINK_PARAM_DRIVER(BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK,
"gre_ver_check", DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_PERMANENT),
bnxt_dl_nvm_param_get, bnxt_dl_nvm_param_set,
NULL),
/* keep REMOTE_DEV_RESET last, it is excluded based on caps */
DEVLINK_PARAM_GENERIC(ENABLE_REMOTE_DEV_RESET,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
bnxt_remote_dev_reset_get,
bnxt_remote_dev_reset_set, NULL),
};
static int bnxt_dl_params_register(struct bnxt *bp)
{
int num_params = ARRAY_SIZE(bnxt_dl_params);
int rc;
if (bp->hwrm_spec_code < 0x10600)
return 0;
if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET_IF)
num_params--;
rc = devlink_params_register(bp->dl, bnxt_dl_params, num_params);
if (rc)
netdev_warn(bp->dev, "devlink_params_register failed. rc=%d\n",
rc);
return rc;
}
static void bnxt_dl_params_unregister(struct bnxt *bp)
{
int num_params = ARRAY_SIZE(bnxt_dl_params);
if (bp->hwrm_spec_code < 0x10600)
return;
if (~bp->fw_cap & BNXT_FW_CAP_HOT_RESET_IF)
num_params--;
devlink_params_unregister(bp->dl, bnxt_dl_params, num_params);
}
int bnxt_dl_register(struct bnxt *bp)
{
const struct devlink_ops *devlink_ops;
struct devlink_port_attrs attrs = {};
struct bnxt_dl *bp_dl;
struct devlink *dl;
int rc;
if (BNXT_PF(bp))
devlink_ops = &bnxt_dl_ops;
else
devlink_ops = &bnxt_vf_dl_ops;
dl = devlink_alloc(devlink_ops, sizeof(struct bnxt_dl), &bp->pdev->dev);
if (!dl) {
netdev_warn(bp->dev, "devlink_alloc failed\n");
return -ENOMEM;
}
bp->dl = dl;
bp_dl = devlink_priv(dl);
bp_dl->bp = bp;
bnxt_dl_set_remote_reset(dl, true);
/* Add switchdev eswitch mode setting, if SRIOV supported */
if (pci_find_ext_capability(bp->pdev, PCI_EXT_CAP_ID_SRIOV) &&
bp->hwrm_spec_code > 0x10803)
bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY;
if (!BNXT_PF(bp))
goto out;
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = bp->pf.port_id;
memcpy(attrs.switch_id.id, bp->dsn, sizeof(bp->dsn));
attrs.switch_id.id_len = sizeof(bp->dsn);
devlink_port_attrs_set(&bp->dl_port, &attrs);
rc = devlink_port_register(dl, &bp->dl_port, bp->pf.port_id);
if (rc) {
netdev_err(bp->dev, "devlink_port_register failed\n");
goto err_dl_free;
}
rc = bnxt_dl_params_register(bp);
if (rc)
goto err_dl_port_unreg;
out:
devlink_register(dl);
return 0;
err_dl_port_unreg:
devlink_port_unregister(&bp->dl_port);
err_dl_free:
devlink_free(dl);
return rc;
}
void bnxt_dl_unregister(struct bnxt *bp)
{
struct devlink *dl = bp->dl;
devlink_unregister(dl);
if (BNXT_PF(bp)) {
bnxt_dl_params_unregister(bp);
devlink_port_unregister(&bp->dl_port);
}
devlink_free(dl);
}