Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/ethernet/mellanox/mlx5/core/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 60 kB image not shown  

Quelle  main.c   Sprache: C

 
/*
 * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */


#include <linux/highmem.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/cq.h>
#include <linux/mlx5/qp.h>
#include <linux/debugfs.h>
#include <linux/kmod.h>
#include <linux/mlx5/mlx5_ifc.h>
#include <linux/mlx5/vport.h>
#include <linux/version.h>
#include <net/devlink.h>
#include "mlx5_core.h"
#include "lib/eq.h"
#include "fs_core.h"
#include "lib/mpfs.h"
#include "eswitch.h"
#include "devlink.h"
#include "fw_reset.h"
#include "lib/mlx5.h"
#include "lib/tout.h"
#include "fpga/core.h"
#include "en_accel/ipsec.h"
#include "lib/clock.h"
#include "lib/vxlan.h"
#include "lib/geneve.h"
#include "lib/devcom.h"
#include "lib/pci_vsc.h"
#include "diag/fw_tracer.h"
#include "ecpf.h"
#include "lib/hv_vhca.h"
#include "diag/rsc_dump.h"
#include "sf/vhca_event.h"
#include "sf/dev/dev.h"
#include "sf/sf.h"
#include "mlx5_irq.h"
#include "hwmon.h"
#include "lag/lag.h"

MODULE_AUTHOR("Eli Cohen ");
MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) core driver");
MODULE_LICENSE("Dual BSD/GPL");

unsigned int mlx5_core_debug_mask;
module_param_named(debug_mask, mlx5_core_debug_mask, uint, 0644);
MODULE_PARM_DESC(debug_mask, "debug mask: 1 = dump cmd data, 2 = dump cmd exec time, 3 = both. Default=0");

static unsigned int prof_sel = MLX5_DEFAULT_PROF;
module_param_named(prof_sel, prof_sel, uint, 0444);
MODULE_PARM_DESC(prof_sel, "profile selector. Valid range 0 - 2");

static u32 sw_owner_id[4];
#define MAX_SW_VHCA_ID (BIT(__mlx5_bit_sz(cmd_hca_cap_2, sw_vhca_id)) - 1)
static DEFINE_IDA(sw_vhca_ida);

enum {
 MLX5_ATOMIC_REQ_MODE_BE = 0x0,
 MLX5_ATOMIC_REQ_MODE_HOST_ENDIANNESS = 0x1,
};

#define LOG_MAX_SUPPORTED_QPS 0xff

static struct mlx5_profile profile[] = {
 [0] = {
  .mask           = 0,
  .num_cmd_caches = MLX5_NUM_COMMAND_CACHES,
 },
 [1] = {
  .mask  = MLX5_PROF_MASK_QP_SIZE,
  .log_max_qp = 12,
  .num_cmd_caches = MLX5_NUM_COMMAND_CACHES,

 },
 [2] = {
  .mask  = MLX5_PROF_MASK_QP_SIZE |
      MLX5_PROF_MASK_MR_CACHE,
  .log_max_qp = LOG_MAX_SUPPORTED_QPS,
  .num_cmd_caches = MLX5_NUM_COMMAND_CACHES,
  .mr_cache[0] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[1] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[2] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[3] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[4] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[5] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[6] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[7] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[8] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[9] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[10] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[11] = {
   .size = 500,
   .limit = 250
  },
  .mr_cache[12] = {
   .size = 64,
   .limit = 32
  },
  .mr_cache[13] = {
   .size = 32,
   .limit = 16
  },
  .mr_cache[14] = {
   .size = 16,
   .limit = 8
  },
  .mr_cache[15] = {
   .size = 8,
   .limit = 4
  },
 },
 [3] = {
  .mask  = MLX5_PROF_MASK_QP_SIZE,
  .log_max_qp = LOG_MAX_SUPPORTED_QPS,
  .num_cmd_caches = 0,
 },
};

static int wait_fw_init(struct mlx5_core_dev *dev, u32 max_wait_mili,
   u32 warn_time_mili, const char *init_state)
{
 unsigned long warn = jiffies + msecs_to_jiffies(warn_time_mili);
 unsigned long end = jiffies + msecs_to_jiffies(max_wait_mili);
 u32 fw_initializing;

 do {
  fw_initializing = ioread32be(&dev->iseg->initializing);
  if (!(fw_initializing >> 31))
   break;
  if (time_after(jiffies, end)) {
   mlx5_core_err(dev, "Firmware over %u MS in %s state, aborting\n",
          max_wait_mili, init_state);
   return -ETIMEDOUT;
  }
  if (test_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state)) {
   mlx5_core_warn(dev, "device is being removed, stop waiting for FW %s\n",
           init_state);
   return -ENODEV;
  }
  if (warn_time_mili && time_after(jiffies, warn)) {
   mlx5_core_warn(dev, "Waiting for FW %s, timeout abort in %ds (0x%x)\n",
           init_state, jiffies_to_msecs(end - warn) / 1000,
           fw_initializing);
   warn = jiffies + msecs_to_jiffies(warn_time_mili);
  }
  msleep(mlx5_tout_ms(dev, FW_PRE_INIT_WAIT));
 } while (true);

 return 0;
}

static void mlx5_set_driver_version(struct mlx5_core_dev *dev)
{
 int driver_ver_sz = MLX5_FLD_SZ_BYTES(set_driver_version_in,
           driver_version);
 u8 in[MLX5_ST_SZ_BYTES(set_driver_version_in)] = {};
 char *string;

 if (!MLX5_CAP_GEN(dev, driver_version))
  return;

 string = MLX5_ADDR_OF(set_driver_version_in, in, driver_version);

 snprintf(string, driver_ver_sz, "Linux,%s,%u.%u.%u",
   KBUILD_MODNAME, LINUX_VERSION_MAJOR,
   LINUX_VERSION_PATCHLEVEL, LINUX_VERSION_SUBLEVEL);

 /*Send the command*/
 MLX5_SET(set_driver_version_in, in, opcode,
   MLX5_CMD_OP_SET_DRIVER_VERSION);

 mlx5_cmd_exec_in(dev, set_driver_version, in);
}

static int set_dma_caps(struct pci_dev *pdev)
{
 int err;

 err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
 if (err) {
  dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask\n");
  err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
  if (err) {
   dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting\n");
   return err;
  }
 }

 dma_set_max_seg_size(&pdev->dev, 2u * 1024 * 1024 * 1024);
 return err;
}

static int mlx5_pci_enable_device(struct mlx5_core_dev *dev)
{
 struct pci_dev *pdev = dev->pdev;
 int err = 0;

 mutex_lock(&dev->pci_status_mutex);
 if (dev->pci_status == MLX5_PCI_STATUS_DISABLED) {
  err = pci_enable_device(pdev);
  if (!err)
   dev->pci_status = MLX5_PCI_STATUS_ENABLED;
 }
 mutex_unlock(&dev->pci_status_mutex);

 return err;
}

static void mlx5_pci_disable_device(struct mlx5_core_dev *dev)
{
 struct pci_dev *pdev = dev->pdev;

 mutex_lock(&dev->pci_status_mutex);
 if (dev->pci_status == MLX5_PCI_STATUS_ENABLED) {
  pci_disable_device(pdev);
  dev->pci_status = MLX5_PCI_STATUS_DISABLED;
 }
 mutex_unlock(&dev->pci_status_mutex);
}

static int request_bar(struct pci_dev *pdev)
{
 int err = 0;

 if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
  dev_err(&pdev->dev, "Missing registers BAR, aborting\n");
  return -ENODEV;
 }

 err = pci_request_regions(pdev, KBUILD_MODNAME);
 if (err)
  dev_err(&pdev->dev, "Couldn't get PCI resources, aborting\n");

 return err;
}

static void release_bar(struct pci_dev *pdev)
{
 pci_release_regions(pdev);
}

struct mlx5_reg_host_endianness {
 u8 he;
 u8      rsvd[15];
};

static u16 to_fw_pkey_sz(struct mlx5_core_dev *dev, u32 size)
{
 switch (size) {
 case 128:
  return 0;
 case 256:
  return 1;
 case 512:
  return 2;
 case 1024:
  return 3;
 case 2048:
  return 4;
 case 4096:
  return 5;
 default:
  mlx5_core_warn(dev, "invalid pkey table size %d\n", size);
  return 0;
 }
}

void mlx5_core_uplink_netdev_set(struct mlx5_core_dev *dev, struct net_device *netdev)
{
 mutex_lock(&dev->mlx5e_res.uplink_netdev_lock);
 dev->mlx5e_res.uplink_netdev = netdev;
 mlx5_blocking_notifier_call_chain(dev, MLX5_DRIVER_EVENT_UPLINK_NETDEV,
       netdev);
 mutex_unlock(&dev->mlx5e_res.uplink_netdev_lock);
}

void mlx5_core_uplink_netdev_event_replay(struct mlx5_core_dev *dev)
{
 mutex_lock(&dev->mlx5e_res.uplink_netdev_lock);
 mlx5_blocking_notifier_call_chain(dev, MLX5_DRIVER_EVENT_UPLINK_NETDEV,
       dev->mlx5e_res.uplink_netdev);
 mutex_unlock(&dev->mlx5e_res.uplink_netdev_lock);
}
EXPORT_SYMBOL(mlx5_core_uplink_netdev_event_replay);

void mlx5_core_mp_event_replay(struct mlx5_core_dev *dev, u32 event, void *data)
{
 mlx5_blocking_notifier_call_chain(dev, event, data);
}
EXPORT_SYMBOL(mlx5_core_mp_event_replay);

int mlx5_core_get_caps_mode(struct mlx5_core_dev *dev, enum mlx5_cap_type cap_type,
       enum mlx5_cap_mode cap_mode)
{
 u8 in[MLX5_ST_SZ_BYTES(query_hca_cap_in)];
 int out_sz = MLX5_ST_SZ_BYTES(query_hca_cap_out);
 void *out, *hca_caps;
 u16 opmod = (cap_type << 1) | (cap_mode & 0x01);
 int err;

 if (WARN_ON(!dev->caps.hca[cap_type]))
  /* this cap_type must be added to mlx5_hca_caps_alloc() */
  return -EINVAL;

 memset(in, 0, sizeof(in));
 out = kzalloc(out_sz, GFP_KERNEL);
 if (!out)
  return -ENOMEM;

 MLX5_SET(query_hca_cap_in, in, opcode, MLX5_CMD_OP_QUERY_HCA_CAP);
 MLX5_SET(query_hca_cap_in, in, op_mod, opmod);
 err = mlx5_cmd_exec_inout(dev, query_hca_cap, in, out);
 if (err) {
  mlx5_core_warn(dev,
          "QUERY_HCA_CAP : type(%x) opmode(%x) Failed(%d)\n",
          cap_type, cap_mode, err);
  goto query_ex;
 }

 hca_caps =  MLX5_ADDR_OF(query_hca_cap_out, out, capability);

 switch (cap_mode) {
 case HCA_CAP_OPMOD_GET_MAX:
  memcpy(dev->caps.hca[cap_type]->max, hca_caps,
         MLX5_UN_SZ_BYTES(hca_cap_union));
  break;
 case HCA_CAP_OPMOD_GET_CUR:
  memcpy(dev->caps.hca[cap_type]->cur, hca_caps,
         MLX5_UN_SZ_BYTES(hca_cap_union));
  break;
 default:
  mlx5_core_warn(dev,
          "Tried to query dev cap type(%x) with wrong opmode(%x)\n",
          cap_type, cap_mode);
  err = -EINVAL;
  break;
 }
query_ex:
 kfree(out);
 return err;
}

int mlx5_core_get_caps(struct mlx5_core_dev *dev, enum mlx5_cap_type cap_type)
{
 int ret;

 ret = mlx5_core_get_caps_mode(dev, cap_type, HCA_CAP_OPMOD_GET_CUR);
 if (ret)
  return ret;
 return mlx5_core_get_caps_mode(dev, cap_type, HCA_CAP_OPMOD_GET_MAX);
}

static int set_caps(struct mlx5_core_dev *dev, void *in, int opmod)
{
 MLX5_SET(set_hca_cap_in, in, opcode, MLX5_CMD_OP_SET_HCA_CAP);
 MLX5_SET(set_hca_cap_in, in, op_mod, opmod << 1);
 return mlx5_cmd_exec_in(dev, set_hca_cap, in);
}

static int handle_hca_cap_atomic(struct mlx5_core_dev *dev, void *set_ctx)
{
 void *set_hca_cap;
 int req_endianness;
 int err;

 if (!MLX5_CAP_GEN(dev, atomic))
  return 0;

 err = mlx5_core_get_caps(dev, MLX5_CAP_ATOMIC);
 if (err)
  return err;

 req_endianness =
  MLX5_CAP_ATOMIC(dev,
    supported_atomic_req_8B_endianness_mode_1);

 if (req_endianness != MLX5_ATOMIC_REQ_MODE_HOST_ENDIANNESS)
  return 0;

 set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx, capability);

 /* Set requestor to host endianness */
 MLX5_SET(atomic_caps, set_hca_cap, atomic_req_8B_endianness_mode,
   MLX5_ATOMIC_REQ_MODE_HOST_ENDIANNESS);

 return set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_ATOMIC);
}

static int handle_hca_cap_odp(struct mlx5_core_dev *dev, void *set_ctx)
{
 bool do_set = false, mem_page_fault = false;
 void *set_hca_cap;
 int err;

 if (!IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING) ||
     !MLX5_CAP_GEN(dev, pg))
  return 0;

 err = mlx5_core_get_caps(dev, MLX5_CAP_ODP);
 if (err)
  return err;

 set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx, capability);
 memcpy(set_hca_cap, dev->caps.hca[MLX5_CAP_ODP]->cur,
        MLX5_ST_SZ_BYTES(odp_cap));

 /* For best performance, enable memory scheme ODP only when
 * it has page prefetch enabled.
 */

 if (MLX5_CAP_ODP_MAX(dev, mem_page_fault) &&
     MLX5_CAP_ODP_MAX(dev, memory_page_fault_scheme_cap.page_prefetch)) {
  mem_page_fault = true;
  do_set = true;
  MLX5_SET(odp_cap, set_hca_cap, mem_page_fault, mem_page_fault);
  goto set;
 }

#define ODP_CAP_SET_MAX(dev, field)                                            \
 do {                                                                   \
  u32 _res = MLX5_CAP_ODP_MAX(dev, field);                       \
  if (_res) {                                                    \
   do_set = true;                                         \
   MLX5_SET(odp_cap, set_hca_cap, field, _res);           \
  }                                                              \
 } while (0)

 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.ud_odp_caps.srq_receive);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.rc_odp_caps.srq_receive);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.xrc_odp_caps.srq_receive);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.xrc_odp_caps.send);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.xrc_odp_caps.receive);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.xrc_odp_caps.write);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.xrc_odp_caps.read);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.xrc_odp_caps.atomic);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.dc_odp_caps.srq_receive);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.dc_odp_caps.send);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.dc_odp_caps.receive);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.dc_odp_caps.write);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.dc_odp_caps.read);
 ODP_CAP_SET_MAX(dev, transport_page_fault_scheme_cap.dc_odp_caps.atomic);

set:
 if (do_set)
  err = set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_ODP);

 mlx5_core_dbg(dev, "Using ODP %s scheme\n",
        mem_page_fault ? "memory" : "transport");
 return err;
}

static int max_uc_list_get_devlink_param(struct mlx5_core_dev *dev)
{
 struct devlink *devlink = priv_to_devlink(dev);
 union devlink_param_value val;
 int err;

 err = devl_param_driverinit_value_get(devlink,
           DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
           &val);
 if (!err)
  return val.vu32;
 mlx5_core_dbg(dev, "Failed to get param. err = %d\n", err);
 return err;
}

bool mlx5_is_roce_on(struct mlx5_core_dev *dev)
{
 struct devlink *devlink = priv_to_devlink(dev);
 union devlink_param_value val;
 int err;

 err = devl_param_driverinit_value_get(devlink,
           DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE,
           &val);

 if (!err)
  return val.vbool;

 mlx5_core_dbg(dev, "Failed to get param. err = %d\n", err);
 return MLX5_CAP_GEN(dev, roce);
}
EXPORT_SYMBOL(mlx5_is_roce_on);

static int handle_hca_cap_2(struct mlx5_core_dev *dev, void *set_ctx)
{
 void *set_hca_cap;
 int err;

 if (!MLX5_CAP_GEN_MAX(dev, hca_cap_2))
  return 0;

 err = mlx5_core_get_caps(dev, MLX5_CAP_GENERAL_2);
 if (err)
  return err;

 if (!MLX5_CAP_GEN_2_MAX(dev, sw_vhca_id_valid) ||
     !(dev->priv.sw_vhca_id > 0))
  return 0;

 set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx,
       capability);
 memcpy(set_hca_cap, dev->caps.hca[MLX5_CAP_GENERAL_2]->cur,
        MLX5_ST_SZ_BYTES(cmd_hca_cap_2));
 MLX5_SET(cmd_hca_cap_2, set_hca_cap, sw_vhca_id_valid, 1);

 return set_caps(dev, set_ctx, MLX5_CAP_GENERAL_2);
}

static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx)
{
 struct mlx5_profile *prof = &dev->profile;
 void *set_hca_cap;
 int max_uc_list;
 int err;

 err = mlx5_core_get_caps(dev, MLX5_CAP_GENERAL);
 if (err)
  return err;

 set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx,
       capability);
 memcpy(set_hca_cap, dev->caps.hca[MLX5_CAP_GENERAL]->cur,
        MLX5_ST_SZ_BYTES(cmd_hca_cap));

 mlx5_core_dbg(dev, "Current Pkey table size %d Setting new size %d\n",
        mlx5_to_sw_pkey_sz(MLX5_CAP_GEN(dev, pkey_table_size)),
        128);
 /* we limit the size of the pkey table to 128 entries for now */
 MLX5_SET(cmd_hca_cap, set_hca_cap, pkey_table_size,
   to_fw_pkey_sz(dev, 128));

 /* Check log_max_qp from HCA caps to set in current profile */
 if (prof->log_max_qp == LOG_MAX_SUPPORTED_QPS) {
  prof->log_max_qp = min_t(u8, 18, MLX5_CAP_GEN_MAX(dev, log_max_qp));
 } else if (MLX5_CAP_GEN_MAX(dev, log_max_qp) < prof->log_max_qp) {
  mlx5_core_warn(dev, "log_max_qp value in current profile is %d, changing it to HCA capability limit (%d)\n",
          prof->log_max_qp,
          MLX5_CAP_GEN_MAX(dev, log_max_qp));
  prof->log_max_qp = MLX5_CAP_GEN_MAX(dev, log_max_qp);
 }
 if (prof->mask & MLX5_PROF_MASK_QP_SIZE)
  MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_qp,
    prof->log_max_qp);

 /* disable cmdif checksum */
 MLX5_SET(cmd_hca_cap, set_hca_cap, cmdif_checksum, 0);

 /* Enable 4K UAR only when HCA supports it and page size is bigger
 * than 4K.
 */

 if (MLX5_CAP_GEN_MAX(dev, uar_4k) && PAGE_SIZE > 4096)
  MLX5_SET(cmd_hca_cap, set_hca_cap, uar_4k, 1);

 MLX5_SET(cmd_hca_cap, set_hca_cap, log_uar_page_sz, PAGE_SHIFT - 12);

 if (MLX5_CAP_GEN_MAX(dev, cache_line_128byte))
  MLX5_SET(cmd_hca_cap,
    set_hca_cap,
    cache_line_128byte,
    cache_line_size() >= 128 ? 1 : 0);

 if (MLX5_CAP_GEN_MAX(dev, dct))
  MLX5_SET(cmd_hca_cap, set_hca_cap, dct, 1);

 if (MLX5_CAP_GEN_MAX(dev, pci_sync_for_fw_update_event))
  MLX5_SET(cmd_hca_cap, set_hca_cap, pci_sync_for_fw_update_event, 1);
 if (MLX5_CAP_GEN_MAX(dev, pci_sync_for_fw_update_with_driver_unload))
  MLX5_SET(cmd_hca_cap, set_hca_cap,
    pci_sync_for_fw_update_with_driver_unload, 1);
 if (MLX5_CAP_GEN_MAX(dev, pcie_reset_using_hotreset_method))
  MLX5_SET(cmd_hca_cap, set_hca_cap,
    pcie_reset_using_hotreset_method, 1);

 if (MLX5_CAP_GEN_MAX(dev, num_vhca_ports))
  MLX5_SET(cmd_hca_cap,
    set_hca_cap,
    num_vhca_ports,
    MLX5_CAP_GEN_MAX(dev, num_vhca_ports));

 if (MLX5_CAP_GEN_MAX(dev, release_all_pages))
  MLX5_SET(cmd_hca_cap, set_hca_cap, release_all_pages, 1);

 if (MLX5_CAP_GEN_MAX(dev, mkey_by_name))
  MLX5_SET(cmd_hca_cap, set_hca_cap, mkey_by_name, 1);

 mlx5_vhca_state_cap_handle(dev, set_hca_cap);

 if (MLX5_CAP_GEN_MAX(dev, num_total_dynamic_vf_msix))
  MLX5_SET(cmd_hca_cap, set_hca_cap, num_total_dynamic_vf_msix,
    MLX5_CAP_GEN_MAX(dev, num_total_dynamic_vf_msix));

 if (MLX5_CAP_GEN(dev, roce_rw_supported) && MLX5_CAP_GEN_MAX(dev, roce))
  MLX5_SET(cmd_hca_cap, set_hca_cap, roce,
    mlx5_is_roce_on(dev));

 max_uc_list = max_uc_list_get_devlink_param(dev);
 if (max_uc_list > 0)
  MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_current_uc_list,
    ilog2(max_uc_list));

 /* enable absolute native port num */
 if (MLX5_CAP_GEN_MAX(dev, abs_native_port_num))
  MLX5_SET(cmd_hca_cap, set_hca_cap, abs_native_port_num, 1);

 return set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE);
}

/* Cached MLX5_CAP_GEN(dev, roce) can be out of sync this early in the
 * boot process.
 * In case RoCE cap is writable in FW and user/devlink requested to change the
 * cap, we are yet to query the final state of the above cap.
 * Hence, the need for this function.
 *
 * Returns
 * True:
 * 1) RoCE cap is read only in FW and already disabled
 * OR:
 * 2) RoCE cap is writable in FW and user/devlink requested it off.
 *
 * In any other case, return False.
 */

static bool is_roce_fw_disabled(struct mlx5_core_dev *dev)
{
 return (MLX5_CAP_GEN(dev, roce_rw_supported) && !mlx5_is_roce_on(dev)) ||
  (!MLX5_CAP_GEN(dev, roce_rw_supported) && !MLX5_CAP_GEN(dev, roce));
}

static int handle_hca_cap_roce(struct mlx5_core_dev *dev, void *set_ctx)
{
 void *set_hca_cap;
 int err;

 if (is_roce_fw_disabled(dev))
  return 0;

 err = mlx5_core_get_caps(dev, MLX5_CAP_ROCE);
 if (err)
  return err;

 if (MLX5_CAP_ROCE(dev, sw_r_roce_src_udp_port) ||
     !MLX5_CAP_ROCE_MAX(dev, sw_r_roce_src_udp_port))
  return 0;

 set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx, capability);
 memcpy(set_hca_cap, dev->caps.hca[MLX5_CAP_ROCE]->cur,
        MLX5_ST_SZ_BYTES(roce_cap));
 MLX5_SET(roce_cap, set_hca_cap, sw_r_roce_src_udp_port, 1);

 if (MLX5_CAP_ROCE_MAX(dev, qp_ooo_transmit_default))
  MLX5_SET(roce_cap, set_hca_cap, qp_ooo_transmit_default, 1);

 err = set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_ROCE);
 return err;
}

static int handle_hca_cap_port_selection(struct mlx5_core_dev *dev,
      void *set_ctx)
{
 void *set_hca_cap;
 int err;

 if (!MLX5_CAP_GEN(dev, port_selection_cap))
  return 0;

 err = mlx5_core_get_caps(dev, MLX5_CAP_PORT_SELECTION);
 if (err)
  return err;

 if (MLX5_CAP_PORT_SELECTION(dev, port_select_flow_table_bypass) ||
     !MLX5_CAP_PORT_SELECTION_MAX(dev, port_select_flow_table_bypass))
  return 0;

 set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx, capability);
 memcpy(set_hca_cap, dev->caps.hca[MLX5_CAP_PORT_SELECTION]->cur,
        MLX5_ST_SZ_BYTES(port_selection_cap));
 MLX5_SET(port_selection_cap, set_hca_cap, port_select_flow_table_bypass, 1);

 err = set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_PORT_SELECTION);

 return err;
}

static int set_hca_cap(struct mlx5_core_dev *dev)
{
 int set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in);
 void *set_ctx;
 int err;

 set_ctx = kzalloc(set_sz, GFP_KERNEL);
 if (!set_ctx)
  return -ENOMEM;

 err = handle_hca_cap(dev, set_ctx);
 if (err) {
  mlx5_core_err(dev, "handle_hca_cap failed\n");
  goto out;
 }

 memset(set_ctx, 0, set_sz);
 err = handle_hca_cap_atomic(dev, set_ctx);
 if (err) {
  mlx5_core_err(dev, "handle_hca_cap_atomic failed\n");
  goto out;
 }

 memset(set_ctx, 0, set_sz);
 err = handle_hca_cap_odp(dev, set_ctx);
 if (err) {
  mlx5_core_err(dev, "handle_hca_cap_odp failed\n");
  goto out;
 }

 memset(set_ctx, 0, set_sz);
 err = handle_hca_cap_roce(dev, set_ctx);
 if (err) {
  mlx5_core_err(dev, "handle_hca_cap_roce failed\n");
  goto out;
 }

 memset(set_ctx, 0, set_sz);
 err = handle_hca_cap_2(dev, set_ctx);
 if (err) {
  mlx5_core_err(dev, "handle_hca_cap_2 failed\n");
  goto out;
 }

 memset(set_ctx, 0, set_sz);
 err = handle_hca_cap_port_selection(dev, set_ctx);
 if (err) {
  mlx5_core_err(dev, "handle_hca_cap_port_selection failed\n");
  goto out;
 }

out:
 kfree(set_ctx);
 return err;
}

static int set_hca_ctrl(struct mlx5_core_dev *dev)
{
 struct mlx5_reg_host_endianness he_in;
 struct mlx5_reg_host_endianness he_out;
 int err;

 if (!mlx5_core_is_pf(dev))
  return 0;

 memset(&he_in, 0, sizeof(he_in));
 he_in.he = MLX5_SET_HOST_ENDIANNESS;
 err = mlx5_core_access_reg(dev, &he_in,  sizeof(he_in),
     &he_out, sizeof(he_out),
     MLX5_REG_HOST_ENDIANNESS, 0, 1);
 return err;
}

static int mlx5_core_set_hca_defaults(struct mlx5_core_dev *dev)
{
 int ret = 0;

 /* Disable local_lb by default */
 if (MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_ETH)
  ret = mlx5_nic_vport_update_local_lb(dev, false);

 return ret;
}

int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id)
{
 u32 in[MLX5_ST_SZ_DW(enable_hca_in)] = {};

 MLX5_SET(enable_hca_in, in, opcode, MLX5_CMD_OP_ENABLE_HCA);
 MLX5_SET(enable_hca_in, in, function_id, func_id);
 MLX5_SET(enable_hca_in, in, embedded_cpu_function,
   dev->caps.embedded_cpu);
 return mlx5_cmd_exec_in(dev, enable_hca, in);
}

int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id)
{
 u32 in[MLX5_ST_SZ_DW(disable_hca_in)] = {};

 MLX5_SET(disable_hca_in, in, opcode, MLX5_CMD_OP_DISABLE_HCA);
 MLX5_SET(disable_hca_in, in, function_id, func_id);
 MLX5_SET(enable_hca_in, in, embedded_cpu_function,
   dev->caps.embedded_cpu);
 return mlx5_cmd_exec_in(dev, disable_hca, in);
}

static int mlx5_core_set_issi(struct mlx5_core_dev *dev)
{
 u32 query_out[MLX5_ST_SZ_DW(query_issi_out)] = {};
 u32 query_in[MLX5_ST_SZ_DW(query_issi_in)] = {};
 u32 sup_issi;
 int err;

 MLX5_SET(query_issi_in, query_in, opcode, MLX5_CMD_OP_QUERY_ISSI);
 err = mlx5_cmd_exec_inout(dev, query_issi, query_in, query_out);
 if (err) {
  u32 syndrome = MLX5_GET(query_issi_out, query_out, syndrome);
  u8 status = MLX5_GET(query_issi_out, query_out, status);

  if (!status || syndrome == MLX5_DRIVER_SYND) {
   mlx5_core_err(dev, "Failed to query ISSI err(%d) status(%d) synd(%d)\n",
          err, status, syndrome);
   return err;
  }

  mlx5_core_warn(dev, "Query ISSI is not supported by FW, ISSI is 0\n");
  dev->issi = 0;
  return 0;
 }

 sup_issi = MLX5_GET(query_issi_out, query_out, supported_issi_dw0);

 if (sup_issi & (1 << 1)) {
  u32 set_in[MLX5_ST_SZ_DW(set_issi_in)] = {};

  MLX5_SET(set_issi_in, set_in, opcode, MLX5_CMD_OP_SET_ISSI);
  MLX5_SET(set_issi_in, set_in, current_issi, 1);
  err = mlx5_cmd_exec_in(dev, set_issi, set_in);
  if (err) {
   mlx5_core_err(dev, "Failed to set ISSI to 1 err(%d)\n",
          err);
   return err;
  }

  dev->issi = 1;

  return 0;
 } else if (sup_issi & (1 << 0) || !sup_issi) {
  return 0;
 }

 return -EOPNOTSUPP;
}

static int mlx5_pci_init(struct mlx5_core_dev *dev, struct pci_dev *pdev,
    const struct pci_device_id *id)
{
 int err = 0;

 mutex_init(&dev->pci_status_mutex);
 pci_set_drvdata(dev->pdev, dev);

 dev->bar_addr = pci_resource_start(pdev, 0);

 err = mlx5_pci_enable_device(dev);
 if (err) {
  mlx5_core_err(dev, "Cannot enable PCI device, aborting\n");
  return err;
 }

 err = request_bar(pdev);
 if (err) {
  mlx5_core_err(dev, "error requesting BARs, aborting\n");
  goto err_disable;
 }

 pci_set_master(pdev);

 err = set_dma_caps(pdev);
 if (err) {
  mlx5_core_err(dev, "Failed setting DMA capabilities mask, aborting\n");
  goto err_clr_master;
 }

 if (pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP32) &&
     pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP64) &&
     pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP128))
  mlx5_core_dbg(dev, "Enabling pci atomics failed\n");

 dev->iseg_base = dev->bar_addr;
 dev->iseg = ioremap(dev->iseg_base, sizeof(*dev->iseg));
 if (!dev->iseg) {
  err = -ENOMEM;
  mlx5_core_err(dev, "Failed mapping initialization segment, aborting\n");
  goto err_clr_master;
 }

 mlx5_pci_vsc_init(dev);

 pci_enable_ptm(pdev, NULL);

 return 0;

err_clr_master:
 release_bar(dev->pdev);
err_disable:
 mlx5_pci_disable_device(dev);
 return err;
}

static void mlx5_pci_close(struct mlx5_core_dev *dev)
{
 /* health work might still be active, and it needs pci bar in
 * order to know the NIC state. Therefore, drain the health WQ
 * before removing the pci bars
 */

 mlx5_drain_health_wq(dev);
 pci_disable_ptm(dev->pdev);
 iounmap(dev->iseg);
 release_bar(dev->pdev);
 mlx5_pci_disable_device(dev);
}

static void mlx5_register_hca_devcom_comp(struct mlx5_core_dev *dev)
{
 /* This component is use to sync adding core_dev to lag_dev and to sync
 * changes of mlx5_adev_devices between LAG layer and other layers.
 */

 if (!mlx5_lag_is_supported(dev))
  return;

 dev->priv.hca_devcom_comp =
  mlx5_devcom_register_component(dev->priv.devc, MLX5_DEVCOM_HCA_PORTS,
            mlx5_query_nic_system_image_guid(dev),
            NULL, dev);
 if (IS_ERR(dev->priv.hca_devcom_comp))
  mlx5_core_err(dev, "Failed to register devcom HCA component\n");
}

static void mlx5_unregister_hca_devcom_comp(struct mlx5_core_dev *dev)
{
 mlx5_devcom_unregister_component(dev->priv.hca_devcom_comp);
}

static int mlx5_init_once(struct mlx5_core_dev *dev)
{
 int err;

 dev->priv.devc = mlx5_devcom_register_device(dev);
 if (IS_ERR(dev->priv.devc))
  mlx5_core_warn(dev, "failed to register devcom device %ld\n",
          PTR_ERR(dev->priv.devc));
 mlx5_register_hca_devcom_comp(dev);

 err = mlx5_query_board_id(dev);
 if (err) {
  mlx5_core_err(dev, "query board id failed\n");
  goto err_devcom;
 }

 err = mlx5_irq_table_init(dev);
 if (err) {
  mlx5_core_err(dev, "failed to initialize irq table\n");
  goto err_devcom;
 }

 err = mlx5_eq_table_init(dev);
 if (err) {
  mlx5_core_err(dev, "failed to initialize eq\n");
  goto err_irq_cleanup;
 }

 err = mlx5_events_init(dev);
 if (err) {
  mlx5_core_err(dev, "failed to initialize events\n");
  goto err_eq_cleanup;
 }

 err = mlx5_fw_reset_init(dev);
 if (err) {
  mlx5_core_err(dev, "failed to initialize fw reset events\n");
  goto err_events_cleanup;
 }

 mlx5_cq_debugfs_init(dev);

 mlx5_init_reserved_gids(dev);

 err = mlx5_init_clock(dev);
 if (err) {
  mlx5_core_err(dev, "failed to initialize hardware clock\n");
  goto err_tables_cleanup;
 }

 dev->vxlan = mlx5_vxlan_create(dev);
 dev->geneve = mlx5_geneve_create(dev);

 err = mlx5_init_rl_table(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init rate limiting\n");
  goto err_clock_cleanup;
 }

 err = mlx5_mpfs_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init l2 table %d\n", err);
  goto err_rl_cleanup;
 }

 err = mlx5_sriov_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init sriov %d\n", err);
  goto err_mpfs_cleanup;
 }

 err = mlx5_eswitch_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init eswitch %d\n", err);
  goto err_sriov_cleanup;
 }

 err = mlx5_fpga_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init fpga device %d\n", err);
  goto err_eswitch_cleanup;
 }

 err = mlx5_vhca_event_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init vhca event notifier %d\n", err);
  goto err_fpga_cleanup;
 }

 err = mlx5_sf_hw_table_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init SF HW table %d\n", err);
  goto err_sf_hw_table_cleanup;
 }

 err = mlx5_sf_table_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init SF table %d\n", err);
  goto err_sf_table_cleanup;
 }

 err = mlx5_fs_core_alloc(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to alloc flow steering\n");
  goto err_fs;
 }

 dev->dm = mlx5_dm_create(dev);
 dev->st = mlx5_st_create(dev);
 dev->tracer = mlx5_fw_tracer_create(dev);
 dev->hv_vhca = mlx5_hv_vhca_create(dev);
 dev->rsc_dump = mlx5_rsc_dump_create(dev);

 return 0;

err_fs:
 mlx5_sf_table_cleanup(dev);
err_sf_table_cleanup:
 mlx5_sf_hw_table_cleanup(dev);
err_sf_hw_table_cleanup:
 mlx5_vhca_event_cleanup(dev);
err_fpga_cleanup:
 mlx5_fpga_cleanup(dev);
err_eswitch_cleanup:
 mlx5_eswitch_cleanup(dev->priv.eswitch);
err_sriov_cleanup:
 mlx5_sriov_cleanup(dev);
err_mpfs_cleanup:
 mlx5_mpfs_cleanup(dev);
err_rl_cleanup:
 mlx5_cleanup_rl_table(dev);
err_clock_cleanup:
 mlx5_geneve_destroy(dev->geneve);
 mlx5_vxlan_destroy(dev->vxlan);
 mlx5_cleanup_clock(dev);
err_tables_cleanup:
 mlx5_cleanup_reserved_gids(dev);
 mlx5_cq_debugfs_cleanup(dev);
 mlx5_fw_reset_cleanup(dev);
err_events_cleanup:
 mlx5_events_cleanup(dev);
err_eq_cleanup:
 mlx5_eq_table_cleanup(dev);
err_irq_cleanup:
 mlx5_irq_table_cleanup(dev);
err_devcom:
 mlx5_unregister_hca_devcom_comp(dev);
 mlx5_devcom_unregister_device(dev->priv.devc);

 return err;
}

static void mlx5_cleanup_once(struct mlx5_core_dev *dev)
{
 mlx5_rsc_dump_destroy(dev);
 mlx5_hv_vhca_destroy(dev->hv_vhca);
 mlx5_fw_tracer_destroy(dev->tracer);
 mlx5_st_destroy(dev);
 mlx5_dm_cleanup(dev);
 mlx5_fs_core_free(dev);
 mlx5_sf_table_cleanup(dev);
 mlx5_sf_hw_table_cleanup(dev);
 mlx5_vhca_event_cleanup(dev);
 mlx5_fpga_cleanup(dev);
 mlx5_eswitch_cleanup(dev->priv.eswitch);
 mlx5_sriov_cleanup(dev);
 mlx5_mpfs_cleanup(dev);
 mlx5_cleanup_rl_table(dev);
 mlx5_geneve_destroy(dev->geneve);
 mlx5_vxlan_destroy(dev->vxlan);
 mlx5_cleanup_clock(dev);
 mlx5_cleanup_reserved_gids(dev);
 mlx5_cq_debugfs_cleanup(dev);
 mlx5_fw_reset_cleanup(dev);
 mlx5_events_cleanup(dev);
 mlx5_eq_table_cleanup(dev);
 mlx5_irq_table_cleanup(dev);
 mlx5_unregister_hca_devcom_comp(dev);
 mlx5_devcom_unregister_device(dev->priv.devc);
}

static int mlx5_function_enable(struct mlx5_core_dev *dev, bool boot, u64 timeout)
{
 int err;

 mlx5_core_info(dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev),
         fw_rev_min(dev), fw_rev_sub(dev));

 /* Only PFs hold the relevant PCIe information for this query */
 if (mlx5_core_is_pf(dev))
  pcie_print_link_status(dev->pdev);

 /* wait for firmware to accept initialization segments configurations
 */

 err = wait_fw_init(dev, timeout,
      mlx5_tout_ms(dev, FW_PRE_INIT_WARN_MESSAGE_INTERVAL),
      "pre-initializing");
 if (err)
  return err;

 err = mlx5_cmd_enable(dev);
 if (err) {
  mlx5_core_err(dev, "Failed initializing command interface, aborting\n");
  return err;
 }

 mlx5_tout_query_iseg(dev);

 err = wait_fw_init(dev, mlx5_tout_ms(dev, FW_INIT), 0, "initializing");
 if (err)
  goto err_cmd_cleanup;

 dev->caps.embedded_cpu = mlx5_read_embedded_cpu(dev);
 mlx5_cmd_set_state(dev, MLX5_CMDIF_STATE_UP);

 err = mlx5_core_enable_hca(dev, 0);
 if (err) {
  mlx5_core_err(dev, "enable hca failed\n");
  goto err_cmd_cleanup;
 }

 mlx5_start_health_poll(dev);

 err = mlx5_core_set_issi(dev);
 if (err) {
  mlx5_core_err(dev, "failed to set issi\n");
  goto stop_health_poll;
 }

 err = mlx5_satisfy_startup_pages(dev, 1);
 if (err) {
  mlx5_core_err(dev, "failed to allocate boot pages\n");
  goto stop_health_poll;
 }

 err = mlx5_tout_query_dtor(dev);
 if (err) {
  mlx5_core_err(dev, "failed to read dtor\n");
  goto reclaim_boot_pages;
 }

 return 0;

reclaim_boot_pages:
 mlx5_reclaim_startup_pages(dev);
stop_health_poll:
 mlx5_stop_health_poll(dev, boot);
 mlx5_core_disable_hca(dev, 0);
err_cmd_cleanup:
 mlx5_cmd_set_state(dev, MLX5_CMDIF_STATE_DOWN);
 mlx5_cmd_disable(dev);

 return err;
}

static void mlx5_function_disable(struct mlx5_core_dev *dev, bool boot)
{
 mlx5_reclaim_startup_pages(dev);
 mlx5_stop_health_poll(dev, boot);
 mlx5_core_disable_hca(dev, 0);
 mlx5_cmd_set_state(dev, MLX5_CMDIF_STATE_DOWN);
 mlx5_cmd_disable(dev);
}

static int mlx5_function_open(struct mlx5_core_dev *dev)
{
 int err;

 err = set_hca_ctrl(dev);
 if (err) {
  mlx5_core_err(dev, "set_hca_ctrl failed\n");
  return err;
 }

 err = set_hca_cap(dev);
 if (err) {
  mlx5_core_err(dev, "set_hca_cap failed\n");
  return err;
 }

 err = mlx5_satisfy_startup_pages(dev, 0);
 if (err) {
  mlx5_core_err(dev, "failed to allocate init pages\n");
  return err;
 }

 err = mlx5_cmd_init_hca(dev, sw_owner_id);
 if (err) {
  mlx5_core_err(dev, "init hca failed\n");
  return err;
 }

 mlx5_set_driver_version(dev);

 err = mlx5_query_hca_caps(dev);
 if (err) {
  mlx5_core_err(dev, "query hca failed\n");
  return err;
 }
 mlx5_start_health_fw_log_up(dev);
 return 0;
}

static int mlx5_function_close(struct mlx5_core_dev *dev)
{
 int err;

 err = mlx5_cmd_teardown_hca(dev);
 if (err) {
  mlx5_core_err(dev, "tear_down_hca failed, skip cleanup\n");
  return err;
 }

 return 0;
}

static int mlx5_function_setup(struct mlx5_core_dev *dev, bool boot, u64 timeout)
{
 int err;

 err = mlx5_function_enable(dev, boot, timeout);
 if (err)
  return err;

 err = mlx5_function_open(dev);
 if (err)
  mlx5_function_disable(dev, boot);
 return err;
}

static int mlx5_function_teardown(struct mlx5_core_dev *dev, bool boot)
{
 int err = mlx5_function_close(dev);

 if (!err)
  mlx5_function_disable(dev, boot);
 else
  mlx5_stop_health_poll(dev, boot);

 return err;
}

static int mlx5_load(struct mlx5_core_dev *dev)
{
 int err;

 err = mlx5_alloc_bfreg(dev, &dev->priv.bfreg, falsefalse);
 if (err) {
  mlx5_core_err(dev, "Failed allocating bfreg, %d\n", err);
  return err;
 }

 mlx5_events_start(dev);
 mlx5_pagealloc_start(dev);

 err = mlx5_irq_table_create(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to alloc IRQs\n");
  goto err_irq_table;
 }

 err = mlx5_eq_table_create(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to create EQs\n");
  goto err_eq_table;
 }

 mlx5_clock_load(dev);

 err = mlx5_fw_tracer_init(dev->tracer);
 if (err) {
  mlx5_core_err(dev, "Failed to init FW tracer %d\n", err);
  mlx5_fw_tracer_destroy(dev->tracer);
  dev->tracer = NULL;
 }

 mlx5_fw_reset_events_start(dev);
 mlx5_hv_vhca_init(dev->hv_vhca);

 err = mlx5_rsc_dump_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init Resource dump %d\n", err);
  mlx5_rsc_dump_destroy(dev);
  dev->rsc_dump = NULL;
 }

 err = mlx5_fpga_device_start(dev);
 if (err) {
  mlx5_core_err(dev, "fpga device start failed %d\n", err);
  goto err_fpga_start;
 }

 err = mlx5_fs_core_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init flow steering\n");
  goto err_fs;
 }

 err = mlx5_core_set_hca_defaults(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to set hca defaults\n");
  goto err_set_hca;
 }

 mlx5_vhca_event_start(dev);

 err = mlx5_sf_hw_table_create(dev);
 if (err) {
  mlx5_core_err(dev, "sf table create failed %d\n", err);
  goto err_vhca;
 }

 err = mlx5_ec_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed to init embedded CPU\n");
  goto err_ec;
 }

 mlx5_lag_add_mdev(dev);
 err = mlx5_sriov_attach(dev);
 if (err) {
  mlx5_core_err(dev, "sriov init failed %d\n", err);
  goto err_sriov;
 }

 mlx5_sf_dev_table_create(dev);

 err = mlx5_devlink_traps_register(priv_to_devlink(dev));
 if (err)
  goto err_traps_reg;

 return 0;

err_traps_reg:
 mlx5_sf_dev_table_destroy(dev);
 mlx5_sriov_detach(dev);
err_sriov:
 mlx5_lag_remove_mdev(dev);
 mlx5_ec_cleanup(dev);
err_ec:
 mlx5_sf_hw_table_destroy(dev);
err_vhca:
 mlx5_vhca_event_stop(dev);
err_set_hca:
 mlx5_fs_core_cleanup(dev);
err_fs:
 mlx5_fpga_device_stop(dev);
err_fpga_start:
 mlx5_rsc_dump_cleanup(dev);
 mlx5_hv_vhca_cleanup(dev->hv_vhca);
 mlx5_fw_reset_events_stop(dev);
 mlx5_fw_tracer_cleanup(dev->tracer);
 mlx5_clock_unload(dev);
 mlx5_eq_table_destroy(dev);
err_eq_table:
 mlx5_irq_table_destroy(dev);
err_irq_table:
 mlx5_pagealloc_stop(dev);
 mlx5_events_stop(dev);
 mlx5_free_bfreg(dev, &dev->priv.bfreg);
 return err;
}

static void mlx5_unload(struct mlx5_core_dev *dev)
{
 mlx5_eswitch_disable(dev->priv.eswitch);
 mlx5_devlink_traps_unregister(priv_to_devlink(dev));
 mlx5_sf_dev_table_destroy(dev);
 mlx5_sriov_detach(dev);
 mlx5_lag_remove_mdev(dev);
 mlx5_ec_cleanup(dev);
 mlx5_sf_hw_table_destroy(dev);
 mlx5_vhca_event_stop(dev);
 mlx5_fs_core_cleanup(dev);
 mlx5_fpga_device_stop(dev);
 mlx5_rsc_dump_cleanup(dev);
 mlx5_hv_vhca_cleanup(dev->hv_vhca);
 mlx5_fw_reset_events_stop(dev);
 mlx5_fw_tracer_cleanup(dev->tracer);
 mlx5_clock_unload(dev);
 mlx5_eq_table_destroy(dev);
 mlx5_irq_table_destroy(dev);
 mlx5_pagealloc_stop(dev);
 mlx5_events_stop(dev);
 mlx5_free_bfreg(dev, &dev->priv.bfreg);
}

int mlx5_init_one_devl_locked(struct mlx5_core_dev *dev)
{
 bool light_probe = mlx5_dev_is_lightweight(dev);
 int err = 0;

 mutex_lock(&dev->intf_state_mutex);
 dev->state = MLX5_DEVICE_STATE_UP;

 err = mlx5_function_setup(dev, true, mlx5_tout_ms(dev, FW_PRE_INIT_TIMEOUT));
 if (err)
  goto err_function;

 err = mlx5_init_once(dev);
 if (err) {
  mlx5_core_err(dev, "sw objs init failed\n");
  goto function_teardown;
 }

 /* In case of light_probe, mlx5_devlink is already registered.
 * Hence, don't register devlink again.
 */

 if (!light_probe) {
  err = mlx5_devlink_params_register(priv_to_devlink(dev));
  if (err)
   goto err_devlink_params_reg;
 }

 err = mlx5_load(dev);
 if (err)
  goto err_load;

 set_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state);

 err = mlx5_register_device(dev);
 if (err)
  goto err_register;

 err = mlx5_crdump_enable(dev);
 if (err)
  mlx5_core_err(dev, "mlx5_crdump_enable failed with error code %d\n", err);

 err = mlx5_hwmon_dev_register(dev);
 if (err)
  mlx5_core_err(dev, "mlx5_hwmon_dev_register failed with error code %d\n", err);

 mutex_unlock(&dev->intf_state_mutex);
 return 0;

err_register:
 clear_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state);
 mlx5_unload(dev);
err_load:
 if (!light_probe)
  mlx5_devlink_params_unregister(priv_to_devlink(dev));
err_devlink_params_reg:
 mlx5_cleanup_once(dev);
function_teardown:
 mlx5_function_teardown(dev, true);
err_function:
 dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
 mutex_unlock(&dev->intf_state_mutex);
 return err;
}

int mlx5_init_one(struct mlx5_core_dev *dev)
{
 struct devlink *devlink = priv_to_devlink(dev);
 int err;

 devl_lock(devlink);
 devl_register(devlink);
 err = mlx5_init_one_devl_locked(dev);
 if (err)
  devl_unregister(devlink);
 devl_unlock(devlink);
 return err;
}

void mlx5_uninit_one(struct mlx5_core_dev *dev)
{
 struct devlink *devlink = priv_to_devlink(dev);

 devl_lock(devlink);
 mutex_lock(&dev->intf_state_mutex);

 mlx5_hwmon_dev_unregister(dev);
 mlx5_crdump_disable(dev);
 mlx5_unregister_device(dev);

 if (!test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) {
  mlx5_core_warn(dev, "%s: interface is down, NOP\n",
          __func__);
  mlx5_devlink_params_unregister(priv_to_devlink(dev));
  mlx5_cleanup_once(dev);
  goto out;
 }

 clear_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state);
 mlx5_unload(dev);
 mlx5_devlink_params_unregister(priv_to_devlink(dev));
 mlx5_cleanup_once(dev);
 mlx5_function_teardown(dev, true);
out:
 mutex_unlock(&dev->intf_state_mutex);
 devl_unregister(devlink);
 devl_unlock(devlink);
}

int mlx5_load_one_devl_locked(struct mlx5_core_dev *dev, bool recovery)
{
 int err = 0;
 u64 timeout;

 devl_assert_locked(priv_to_devlink(dev));
 mutex_lock(&dev->intf_state_mutex);
 if (test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) {
  mlx5_core_warn(dev, "interface is up, NOP\n");
  goto out;
 }
 /* remove any previous indication of internal error */
 dev->state = MLX5_DEVICE_STATE_UP;

 if (recovery)
  timeout = mlx5_tout_ms(dev, FW_PRE_INIT_ON_RECOVERY_TIMEOUT);
 else
  timeout = mlx5_tout_ms(dev, FW_PRE_INIT_TIMEOUT);
 err = mlx5_function_setup(dev, false, timeout);
 if (err)
  goto err_function;

 err = mlx5_load(dev);
 if (err)
  goto err_load;

 set_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state);

 err = mlx5_attach_device(dev);
 if (err)
  goto err_attach;

 mutex_unlock(&dev->intf_state_mutex);
 return 0;

err_attach:
 clear_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state);
 mlx5_unload(dev);
err_load:
 mlx5_function_teardown(dev, false);
err_function:
 dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
out:
 mutex_unlock(&dev->intf_state_mutex);
 return err;
}

int mlx5_load_one(struct mlx5_core_dev *dev, bool recovery)
{
 struct devlink *devlink = priv_to_devlink(dev);
 int ret;

 devl_lock(devlink);
 ret = mlx5_load_one_devl_locked(dev, recovery);
 devl_unlock(devlink);
 return ret;
}

void mlx5_unload_one_devl_locked(struct mlx5_core_dev *dev, bool suspend)
{
 devl_assert_locked(priv_to_devlink(dev));
 mutex_lock(&dev->intf_state_mutex);

 mlx5_detach_device(dev, suspend);

 if (!test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) {
  mlx5_core_warn(dev, "%s: interface is down, NOP\n",
          __func__);
  goto out;
 }

 clear_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state);
 mlx5_unload(dev);
 mlx5_function_teardown(dev, false);
out:
 mutex_unlock(&dev->intf_state_mutex);
}

void mlx5_unload_one(struct mlx5_core_dev *dev, bool suspend)
{
 struct devlink *devlink = priv_to_devlink(dev);

 devl_lock(devlink);
 mlx5_unload_one_devl_locked(dev, suspend);
 devl_unlock(devlink);
}

/* In case of light probe, we don't need a full query of hca_caps, but only the bellow caps.
 * A full query of hca_caps will be done when the device will reload.
 */

static int mlx5_query_hca_caps_light(struct mlx5_core_dev *dev)
{
 int err;

 err = mlx5_core_get_caps(dev, MLX5_CAP_GENERAL);
 if (err)
  return err;

 if (MLX5_CAP_GEN(dev, eth_net_offloads)) {
  err = mlx5_core_get_caps_mode(dev, MLX5_CAP_ETHERNET_OFFLOADS,
           HCA_CAP_OPMOD_GET_CUR);
  if (err)
   return err;
 }

 if (MLX5_CAP_GEN(dev, nic_flow_table) ||
     MLX5_CAP_GEN(dev, ipoib_enhanced_offloads)) {
  err = mlx5_core_get_caps_mode(dev, MLX5_CAP_FLOW_TABLE,
           HCA_CAP_OPMOD_GET_CUR);
  if (err)
   return err;
 }

 if (MLX5_CAP_GEN_64(dev, general_obj_types) &
  MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q) {
  err = mlx5_core_get_caps_mode(dev, MLX5_CAP_VDPA_EMULATION,
           HCA_CAP_OPMOD_GET_CUR);
  if (err)
   return err;
 }

 return 0;
}

int mlx5_init_one_light(struct mlx5_core_dev *dev)
{
 struct devlink *devlink = priv_to_devlink(dev);
 int err;

 devl_lock(devlink);
 devl_register(devlink);
 dev->state = MLX5_DEVICE_STATE_UP;
 err = mlx5_function_enable(dev, true, mlx5_tout_ms(dev, FW_PRE_INIT_TIMEOUT));
 if (err) {
  mlx5_core_warn(dev, "mlx5_function_enable err=%d\n", err);
  goto out;
 }

 err = mlx5_query_hca_caps_light(dev);
 if (err) {
  mlx5_core_warn(dev, "mlx5_query_hca_caps_light err=%d\n", err);
  goto query_hca_caps_err;
 }

 err = mlx5_devlink_params_register(priv_to_devlink(dev));
 if (err) {
  mlx5_core_warn(dev, "mlx5_devlink_param_reg err = %d\n", err);
  goto query_hca_caps_err;
 }

 devl_unlock(devlink);
 return 0;

query_hca_caps_err:
 mlx5_function_disable(dev, true);
out:
 dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
 devl_unregister(devlink);
 devl_unlock(devlink);
 return err;
}

void mlx5_uninit_one_light(struct mlx5_core_dev *dev)
{
 struct devlink *devlink = priv_to_devlink(dev);

 devl_lock(devlink);
 mlx5_devlink_params_unregister(priv_to_devlink(dev));
 devl_unregister(devlink);
 devl_unlock(devlink);
 if (dev->state != MLX5_DEVICE_STATE_UP)
  return;
 mlx5_function_disable(dev, true);
}

/* xxx_light() function are used in order to configure the device without full
 * init (light init). e.g.: There isn't a point in reload a device to light state.
 * Hence, mlx5_load_one_light() isn't needed.
 */


void mlx5_unload_one_light(struct mlx5_core_dev *dev)
{
 if (dev->state != MLX5_DEVICE_STATE_UP)
  return;
 mlx5_function_disable(dev, false);
 dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
}

static const int types[] = {
 MLX5_CAP_GENERAL,
 MLX5_CAP_GENERAL_2,
 MLX5_CAP_ETHERNET_OFFLOADS,
 MLX5_CAP_IPOIB_ENHANCED_OFFLOADS,
 MLX5_CAP_ODP,
 MLX5_CAP_ATOMIC,
 MLX5_CAP_ROCE,
 MLX5_CAP_IPOIB_OFFLOADS,
 MLX5_CAP_FLOW_TABLE,
 MLX5_CAP_ESWITCH_FLOW_TABLE,
 MLX5_CAP_ESWITCH,
 MLX5_CAP_QOS,
 MLX5_CAP_DEBUG,
 MLX5_CAP_DEV_MEM,
 MLX5_CAP_DEV_EVENT,
 MLX5_CAP_TLS,
 MLX5_CAP_VDPA_EMULATION,
 MLX5_CAP_IPSEC,
 MLX5_CAP_PORT_SELECTION,
 MLX5_CAP_MACSEC,
 MLX5_CAP_ADV_VIRTUALIZATION,
 MLX5_CAP_CRYPTO,
 MLX5_CAP_SHAMPO,
 MLX5_CAP_ADV_RDMA,
};

static void mlx5_hca_caps_free(struct mlx5_core_dev *dev)
{
 int type;
 int i;

 for (i = 0; i < ARRAY_SIZE(types); i++) {
  type = types[i];
  kfree(dev->caps.hca[type]);
 }
}

static int mlx5_hca_caps_alloc(struct mlx5_core_dev *dev)
{
 struct mlx5_hca_cap *cap;
 int type;
 int i;

 for (i = 0; i < ARRAY_SIZE(types); i++) {
  cap = kzalloc(sizeof(*cap), GFP_KERNEL);
  if (!cap)
   goto err;
  type = types[i];
  dev->caps.hca[type] = cap;
 }

 return 0;

err:
 mlx5_hca_caps_free(dev);
 return -ENOMEM;
}

static int vhca_id_show(struct seq_file *file, void *priv)
{
 struct mlx5_core_dev *dev = file->private;

 seq_printf(file, "0x%x\n", MLX5_CAP_GEN(dev, vhca_id));
 return 0;
}

DEFINE_SHOW_ATTRIBUTE(vhca_id);

int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
{
 struct mlx5_priv *priv = &dev->priv;
 int err;

 memcpy(&dev->profile, &profile[profile_idx], sizeof(dev->profile));
 lockdep_register_key(&dev->lock_key);
 mutex_init(&dev->intf_state_mutex);
 lockdep_set_class(&dev->intf_state_mutex, &dev->lock_key);
 mutex_init(&dev->mlx5e_res.uplink_netdev_lock);
 mutex_init(&dev->wc_state_lock);

 mutex_init(&priv->bfregs.reg_head.lock);
 mutex_init(&priv->bfregs.wc_head.lock);
 INIT_LIST_HEAD(&priv->bfregs.reg_head.list);
 INIT_LIST_HEAD(&priv->bfregs.wc_head.list);

 mutex_init(&priv->alloc_mutex);
 mutex_init(&priv->pgdir_mutex);
 INIT_LIST_HEAD(&priv->pgdir_list);

 priv->numa_node = dev_to_node(mlx5_core_dma_dev(dev));
 priv->dbg.dbg_root = debugfs_create_dir(dev_name(dev->device),
      mlx5_debugfs_root);
 debugfs_create_file("vhca_id", 0400, priv->dbg.dbg_root, dev, &vhca_id_fops);
 INIT_LIST_HEAD(&priv->traps);

 err = mlx5_cmd_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed initializing cmdif SW structs, aborting\n");
  goto err_cmd_init;
 }

 err = mlx5_tout_init(dev);
 if (err) {
  mlx5_core_err(dev, "Failed initializing timeouts, aborting\n");
  goto err_timeout_init;
 }

 err = mlx5_health_init(dev);
 if (err)
  goto err_health_init;

 err = mlx5_pagealloc_init(dev);
 if (err)
  goto err_pagealloc_init;

 err = mlx5_adev_init(dev);
 if (err)
  goto err_adev_init;

 err = mlx5_hca_caps_alloc(dev);
 if (err)
  goto err_hca_caps;

 /* The conjunction of sw_vhca_id with sw_owner_id will be a global
 * unique id per function which uses mlx5_core.
 * Those values are supplied to FW as part of the init HCA command to
 * be used by both driver and FW when it's applicable.
 */

 dev->priv.sw_vhca_id = ida_alloc_range(&sw_vhca_ida, 1,
            MAX_SW_VHCA_ID,
            GFP_KERNEL);
 if (dev->priv.sw_vhca_id < 0)
  mlx5_core_err(dev, "failed to allocate sw_vhca_id, err=%d\n",
         dev->priv.sw_vhca_id);

 return 0;

err_hca_caps:
 mlx5_adev_cleanup(dev);
err_adev_init:
 mlx5_pagealloc_cleanup(dev);
err_pagealloc_init:
 mlx5_health_cleanup(dev);
err_health_init:
 mlx5_tout_cleanup(dev);
err_timeout_init:
 mlx5_cmd_cleanup(dev);
err_cmd_init:
 debugfs_remove(dev->priv.dbg.dbg_root);
 mutex_destroy(&priv->pgdir_mutex);
 mutex_destroy(&priv->alloc_mutex);
 mutex_destroy(&priv->bfregs.wc_head.lock);
 mutex_destroy(&priv->bfregs.reg_head.lock);
 mutex_destroy(&dev->intf_state_mutex);
 lockdep_unregister_key(&dev->lock_key);
 return err;
}

void mlx5_mdev_uninit(struct mlx5_core_dev *dev)
{
 struct mlx5_priv *priv = &dev->priv;

 if (priv->sw_vhca_id > 0)
  ida_free(&sw_vhca_ida, dev->priv.sw_vhca_id);

 mlx5_hca_caps_free(dev);
 mlx5_adev_cleanup(dev);
 mlx5_pagealloc_cleanup(dev);
 mlx5_health_cleanup(dev);
 mlx5_tout_cleanup(dev);
 mlx5_cmd_cleanup(dev);
 debugfs_remove_recursive(dev->priv.dbg.dbg_root);
 mutex_destroy(&priv->pgdir_mutex);
 mutex_destroy(&priv->alloc_mutex);
 mutex_destroy(&priv->bfregs.wc_head.lock);
 mutex_destroy(&priv->bfregs.reg_head.lock);
 mutex_destroy(&dev->wc_state_lock);
 mutex_destroy(&dev->mlx5e_res.uplink_netdev_lock);
 mutex_destroy(&dev->intf_state_mutex);
 lockdep_unregister_key(&dev->lock_key);
}

static int probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
 struct mlx5_core_dev *dev;
 struct devlink *devlink;
 int err;

 devlink = mlx5_devlink_alloc(&pdev->dev);
 if (!devlink) {
  dev_err(&pdev->dev, "devlink alloc failed\n");
  return -ENOMEM;
 }

 dev = devlink_priv(devlink);
 dev->device = &pdev->dev;
 dev->pdev = pdev;

 dev->coredev_type = id->driver_data & MLX5_PCI_DEV_IS_VF ?
    MLX5_COREDEV_VF : MLX5_COREDEV_PF;

 dev->priv.adev_idx = mlx5_adev_idx_alloc();
 if (dev->priv.adev_idx < 0) {
  err = dev->priv.adev_idx;
  goto adev_init_err;
 }

 err = mlx5_mdev_init(dev, prof_sel);
 if (err)
  goto mdev_init_err;

 err = mlx5_pci_init(dev, pdev, id);
 if (err) {
  mlx5_core_err(dev, "mlx5_pci_init failed with error code %d\n",
         err);
  goto pci_init_err;
 }

 err = mlx5_init_one(dev);
 if (err) {
  mlx5_core_err(dev, "mlx5_init_one failed with error code %d\n",
         err);
  goto err_init_one;
 }

 pci_save_state(pdev);
 return 0;

err_init_one:
 mlx5_pci_close(dev);
pci_init_err:
 mlx5_mdev_uninit(dev);
mdev_init_err:
 mlx5_adev_idx_free(dev->priv.adev_idx);
adev_init_err:
 mlx5_devlink_free(devlink);

 return err;
}

static void remove_one(struct pci_dev *pdev)
{
 struct mlx5_core_dev *dev  = pci_get_drvdata(pdev);
 struct devlink *devlink = priv_to_devlink(dev);

 set_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state);
 mlx5_drain_fw_reset(dev);
 mlx5_drain_health_wq(dev);
 mlx5_sriov_disable(pdev, false);
 mlx5_uninit_one(dev);
 mlx5_pci_close(dev);
 mlx5_mdev_uninit(dev);
 mlx5_adev_idx_free(dev->priv.adev_idx);
 mlx5_devlink_free(devlink);
}

#define mlx5_pci_trace(dev, fmt, ...) ({ \
 struct mlx5_core_dev *__dev = (dev); \
 mlx5_core_info(__dev, "%s Device state = %d health sensors: %d pci_status: %d. " fmt, \
         __func__, __dev->state, mlx5_health_check_fatal_sensors(__dev), \
         __dev->pci_status, ##__VA_ARGS__); \
})

static const char *result2str(enum pci_ers_result result)
{
 return  result == PCI_ERS_RESULT_NEED_RESET ? "need reset" :
  result == PCI_ERS_RESULT_DISCONNECT ? "disconnect" :
  result == PCI_ERS_RESULT_RECOVERED  ? "recovered" :
  "unknown";
}

static pci_ers_result_t mlx5_pci_err_detected(struct pci_dev *pdev,
           pci_channel_state_t state)
{
 struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
 enum pci_ers_result res;

 mlx5_pci_trace(dev, "Enter, pci channel state = %d\n", state);

 mlx5_enter_error_state(dev, false);
 mlx5_error_sw_reset(dev);
 mlx5_unload_one(dev, false);
 mlx5_drain_health_wq(dev);
 mlx5_pci_disable_device(dev);

 res = state == pci_channel_io_perm_failure ?
  PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_NEED_RESET;

 mlx5_core_info(dev, "%s Device state = %d pci_status: %d. Exit, result = %d, %s\n",
         __func__, dev->state, dev->pci_status, res, result2str(res));
 return res;
}

/* wait for the device to show vital signs by waiting
 * for the health counter to start counting.
 */

static int wait_vital(struct pci_dev *pdev)
{
 struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
 struct mlx5_core_health *health = &dev->priv.health;
 const int niter = 100;
 u32 last_count = 0;
 u32 count;
 int i;

 for (i = 0; i < niter; i++) {
  count = ioread32be(health->health_counter);
  if (count && count != 0xffffffff) {
   if (last_count && last_count != count) {
    mlx5_core_info(dev,
            "wait vital counter value 0x%x after %d iterations\n",
            count, i);
    return 0;
   }
   last_count = count;
  }
  msleep(50);
 }

 return -ETIMEDOUT;
}

static pci_ers_result_t mlx5_pci_slot_reset(struct pci_dev *pdev)
{
 enum pci_ers_result res = PCI_ERS_RESULT_DISCONNECT;
 struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
 int err;

 mlx5_core_info(dev, "%s Device state = %d pci_status: %d. Enter\n",
         __func__, dev->state, dev->pci_status);

 err = mlx5_pci_enable_device(dev);
 if (err) {
  mlx5_core_err(dev, "%s: mlx5_pci_enable_device failed with error code: %d\n",
         __func__, err);
  goto out;
 }

 pci_set_master(pdev);
 pci_restore_state(pdev);
 pci_save_state(pdev);

 err = wait_vital(pdev);
 if (err) {
  mlx5_core_err(dev, "%s: wait vital failed with error code: %d\n",
         __func__, err);
  goto out;
 }

 res = PCI_ERS_RESULT_RECOVERED;
out:
 mlx5_core_info(dev, "%s Device state = %d pci_status: %d. Exit, err = %d, result = %d, %s\n",
         __func__, dev->state, dev->pci_status, err, res, result2str(res));
 return res;
}

static void mlx5_pci_resume(struct pci_dev *pdev)
{
 struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
 int err;

 mlx5_pci_trace(dev, "Enter, loading driver..\n");

 err = mlx5_load_one(dev, false);

 if (!err)
  devlink_health_reporter_state_update(dev->priv.health.fw_fatal_reporter,
           DEVLINK_HEALTH_REPORTER_STATE_HEALTHY);

 mlx5_pci_trace(dev, "Done, err = %d, device %s\n", err,
         !err ? "recovered" : "Failed");
}

static const struct pci_error_handlers mlx5_err_handler = {
 .error_detected = mlx5_pci_err_detected,
 .slot_reset = mlx5_pci_slot_reset,
 .resume  = mlx5_pci_resume
};

static int mlx5_try_fast_unload(struct mlx5_core_dev *dev)
{
 bool fast_teardown = false, force_teardown = false;
 int ret = 1;

 fast_teardown = MLX5_CAP_GEN(dev, fast_teardown);
 force_teardown = MLX5_CAP_GEN(dev, force_teardown);

 mlx5_core_dbg(dev, "force teardown firmware support=%d\n", force_teardown);
 mlx5_core_dbg(dev, "fast teardown firmware support=%d\n", fast_teardown);

 if (!fast_teardown && !force_teardown)
  return -EOPNOTSUPP;

 if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
  mlx5_core_dbg(dev, "Device in internal error state, giving up\n");
  return -EAGAIN;
 }

 /* Panic tear down fw command will stop the PCI bus communication
 * with the HCA, so the health poll is no longer needed.
 */

 mlx5_stop_health_poll(dev, false);

 ret = mlx5_cmd_fast_teardown_hca(dev);
 if (!ret)
  goto succeed;

 ret = mlx5_cmd_force_teardown_hca(dev);
 if (!ret)
  goto succeed;

 mlx5_core_dbg(dev, "Firmware couldn't do fast unload error: %d\n", ret);
 mlx5_start_health_poll(dev);
 return ret;

succeed:
 mlx5_enter_error_state(dev, true);

 /* Some platforms requiring freeing the IRQ's in the shutdown
 * flow. If they aren't freed they can't be allocated after
 * kexec. There is no need to cleanup the mlx5_core software
 * contexts.
 */

 mlx5_core_eq_free_irqs(dev);

 return 0;
}

static void shutdown(struct pci_dev *pdev)
{
 struct mlx5_core_dev *dev  = pci_get_drvdata(pdev);
 int err;

 mlx5_core_info(dev, "Shutdown was called\n");
 set_bit(MLX5_BREAK_FW_WAIT, &dev->intf_state);
 mlx5_drain_health_wq(dev);
 err = mlx5_try_fast_unload(dev);
 if (err)
  mlx5_unload_one(dev, false);
 mlx5_pci_disable_device(dev);
}

static int mlx5_suspend(struct pci_dev *pdev, pm_message_t state)
{
 struct mlx5_core_dev *dev = pci_get_drvdata(pdev);

 mlx5_unload_one(dev, true);

 return 0;
}

static int mlx5_resume(struct pci_dev *pdev)
{
 struct mlx5_core_dev *dev = pci_get_drvdata(pdev);

 return mlx5_load_one(dev, false);
}

static const struct pci_device_id mlx5_core_pci_table[] = {
 { PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_CONNECTIB) },
 { PCI_VDEVICE(MELLANOX, 0x1012), MLX5_PCI_DEV_IS_VF}, /* Connect-IB VF */
 { PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_CONNECTX4) },
 { PCI_VDEVICE(MELLANOX, 0x1014), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4 VF */
 { PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_CONNECTX4_LX) },
 { PCI_VDEVICE(MELLANOX, 0x1016), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4LX VF */
 { PCI_VDEVICE(MELLANOX, 0x1017) },   /* ConnectX-5, PCIe 3.0 */
 { PCI_VDEVICE(MELLANOX, 0x1018), MLX5_PCI_DEV_IS_VF}, /* ConnectX-5 VF */
 { PCI_VDEVICE(MELLANOX, 0x1019) },   /* ConnectX-5 Ex */
 { PCI_VDEVICE(MELLANOX, 0x101a), MLX5_PCI_DEV_IS_VF}, /* ConnectX-5 Ex VF */
 { PCI_VDEVICE(MELLANOX, 0x101b) },   /* ConnectX-6 */
 { PCI_VDEVICE(MELLANOX, 0x101c), MLX5_PCI_DEV_IS_VF}, /* ConnectX-6 VF */
 { PCI_VDEVICE(MELLANOX, 0x101d) },   /* ConnectX-6 Dx */
 { PCI_VDEVICE(MELLANOX, 0x101e), MLX5_PCI_DEV_IS_VF}, /* ConnectX Family mlx5Gen Virtual Function */
 { PCI_VDEVICE(MELLANOX, 0x101f) },   /* ConnectX-6 LX */
 { PCI_VDEVICE(MELLANOX, 0x1021) },   /* ConnectX-7 */
 { PCI_VDEVICE(MELLANOX, 0x1023) },   /* ConnectX-8 */
 { PCI_VDEVICE(MELLANOX, 0x1025) },   /* ConnectX-9 */
 { PCI_VDEVICE(MELLANOX, 0x1027) },   /* ConnectX-10 */
 { PCI_VDEVICE(MELLANOX, 0xa2d2) },   /* BlueField integrated ConnectX-5 network controller */
 { PCI_VDEVICE(MELLANOX, 0xa2d3), MLX5_PCI_DEV_IS_VF}, /* BlueField integrated ConnectX-5 network controller VF */
 { PCI_VDEVICE(MELLANOX, 0xa2d6) },   /* BlueField-2 integrated ConnectX-6 Dx network controller */
 { PCI_VDEVICE(MELLANOX, 0xa2dc) },   /* BlueField-3 integrated ConnectX-7 network controller */
 { PCI_VDEVICE(MELLANOX, 0xa2df) },   /* BlueField-4 integrated ConnectX-8 network controller */
 { 0, }
};

MODULE_DEVICE_TABLE(pci, mlx5_core_pci_table);

void mlx5_disable_device(struct mlx5_core_dev *dev)
{
 mlx5_error_sw_reset(dev);
 mlx5_unload_one_devl_locked(dev, false);
}

int mlx5_recover_device(struct mlx5_core_dev *dev)
{
 if (!mlx5_core_is_sf(dev)) {
  mlx5_pci_disable_device(dev);
  if (mlx5_pci_slot_reset(dev->pdev) != PCI_ERS_RESULT_RECOVERED)
   return -EIO;
 }

 return mlx5_load_one_devl_locked(dev, true);
}

static struct pci_driver mlx5_core_driver = {
 .name           = KBUILD_MODNAME,
 .id_table       = mlx5_core_pci_table,
 .probe          = probe_one,
 .remove         = remove_one,
 .suspend        = mlx5_suspend,
 .resume         = mlx5_resume,
 .shutdown = shutdown,
 .err_handler = &mlx5_err_handler,
 .sriov_configure   = mlx5_core_sriov_configure,
 .sriov_get_vf_total_msix = mlx5_sriov_get_vf_total_msix,
 .sriov_set_msix_vec_count = mlx5_core_sriov_set_msix_vec_count,
};

/**
 * mlx5_vf_get_core_dev - Get the mlx5 core device from a given VF PCI device if
 *                     mlx5_core is its driver.
 * @pdev: The associated PCI device.
 *
 * Upon return the interface state lock stay held to let caller uses it safely.
 * Caller must ensure to use the returned mlx5 device for a narrow window
 * and put it back with mlx5_vf_put_core_dev() immediately once usage was over.
 *
 * Return: Pointer to the associated mlx5_core_dev or NULL.
 */

struct mlx5_core_dev *mlx5_vf_get_core_dev(struct pci_dev *pdev)
{
 struct mlx5_core_dev *mdev;

 mdev = pci_iov_get_pf_drvdata(pdev, &mlx5_core_driver);
 if (IS_ERR(mdev))
  return NULL;

 mutex_lock(&mdev->intf_state_mutex);
 if (!test_bit(MLX5_INTERFACE_STATE_UP, &mdev->intf_state)) {
  mutex_unlock(&mdev->intf_state_mutex);
  return NULL;
 }

 return mdev;
}
EXPORT_SYMBOL(mlx5_vf_get_core_dev);

/**
 * mlx5_vf_put_core_dev - Put the mlx5 core device back.
 * @mdev: The mlx5 core device.
 *
 * Upon return the interface state lock is unlocked and caller should not
 * access the mdev any more.
 */

void mlx5_vf_put_core_dev(struct mlx5_core_dev *mdev)
{
 mutex_unlock(&mdev->intf_state_mutex);
}
EXPORT_SYMBOL(mlx5_vf_put_core_dev);

static void mlx5_core_verify_params(void)
{
 if (prof_sel >= ARRAY_SIZE(profile)) {
  pr_warn("mlx5_core: WARNING: Invalid module parameter prof_sel %d, valid range 0-%zu, changing back to default(%d)\n",
   prof_sel,
   ARRAY_SIZE(profile) - 1,
   MLX5_DEFAULT_PROF);
  prof_sel = MLX5_DEFAULT_PROF;
 }
}

static int __init mlx5_init(void)
{
 int err;

 WARN_ONCE(strcmp(MLX5_ADEV_NAME, KBUILD_MODNAME),
    "mlx5_core name not in sync with kernel module name");

 get_random_bytes(&sw_owner_id, sizeof(sw_owner_id));

 mlx5_core_verify_params();
 mlx5_register_debugfs();

 err = mlx5e_init();
 if (err)
  goto err_debug;

 err = mlx5_sf_driver_register();
 if (err)
  goto err_sf;

 err = pci_register_driver(&mlx5_core_driver);
 if (err)
  goto err_pci;

 return 0;

err_pci:
 mlx5_sf_driver_unregister();
err_sf:
 mlx5e_cleanup();
err_debug:
 mlx5_unregister_debugfs();
 return err;
}

static void __exit mlx5_cleanup(void)
{
 pci_unregister_driver(&mlx5_core_driver);
 mlx5_sf_driver_unregister();
 mlx5e_cleanup();
 mlx5_unregister_debugfs();
}

module_init(mlx5_init);
module_exit(mlx5_cleanup);

Messung V0.5
C=98 H=89 G=93

¤ Dauer der Verarbeitung: 0.40 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.