Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/infiniband/hw/mlx5/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 133 kB image not shown  

Quelle  main.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
 * Copyright (c) 2013-2020, Mellanox Technologies inc. All rights reserved.
 * Copyright (c) 2020, Intel Corporation. All rights reserved.
 */


#include <linux/debugfs.h>
#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/bitmap.h>
#include <linux/log2.h>
#include <linux/sched.h>
#include <linux/sched/mm.h>
#include <linux/sched/task.h>
#include <linux/delay.h>
#include <rdma/ib_user_verbs.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_cache.h>
#include <linux/mlx5/port.h>
#include <linux/mlx5/vport.h>
#include <linux/mlx5/fs.h>
#include <linux/mlx5/eswitch.h>
#include <linux/mlx5/driver.h>
#include <linux/list.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_umem_odp.h>
#include <rdma/lag.h>
#include <linux/in.h>
#include <linux/etherdevice.h>
#include "mlx5_ib.h"
#include "ib_rep.h"
#include "cmd.h"
#include "devx.h"
#include "dm.h"
#include "fs.h"
#include "srq.h"
#include "qp.h"
#include "wr.h"
#include "restrack.h"
#include "counters.h"
#include "umr.h"
#include <rdma/uverbs_std_types.h>
#include <rdma/uverbs_ioctl.h>
#include <rdma/mlx5_user_ioctl_verbs.h>
#include <rdma/mlx5_user_ioctl_cmds.h>
#include <rdma/ib_ucaps.h>
#include "macsec.h"
#include "data_direct.h"
#include "dmah.h"

#define UVERBS_MODULE_NAME mlx5_ib
#include <rdma/uverbs_named_ioctl.h>

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

struct mlx5_ib_event_work {
 struct work_struct work;
 union {
  struct mlx5_ib_dev       *dev;
  struct mlx5_ib_multiport_info *mpi;
 };
 bool   is_slave;
 unsigned int  event;
 void   *param;
};

enum {
 MLX5_ATOMIC_SIZE_QP_8BYTES = 1 << 3,
};

static struct workqueue_struct *mlx5_ib_event_wq;
static LIST_HEAD(mlx5_ib_unaffiliated_port_list);
static LIST_HEAD(mlx5_ib_dev_list);
/*
 * This mutex should be held when accessing either of the above lists
 */

static DEFINE_MUTEX(mlx5_ib_multiport_mutex);

struct mlx5_ib_dev *mlx5_ib_get_ibdev_from_mpi(struct mlx5_ib_multiport_info *mpi)
{
 struct mlx5_ib_dev *dev;

 mutex_lock(&mlx5_ib_multiport_mutex);
 dev = mpi->ibdev;
 mutex_unlock(&mlx5_ib_multiport_mutex);
 return dev;
}

static enum rdma_link_layer
mlx5_port_type_cap_to_rdma_ll(int port_type_cap)
{
 switch (port_type_cap) {
 case MLX5_CAP_PORT_TYPE_IB:
  return IB_LINK_LAYER_INFINIBAND;
 case MLX5_CAP_PORT_TYPE_ETH:
  return IB_LINK_LAYER_ETHERNET;
 default:
  return IB_LINK_LAYER_UNSPECIFIED;
 }
}

static enum rdma_link_layer
mlx5_ib_port_link_layer(struct ib_device *device, u32 port_num)
{
 struct mlx5_ib_dev *dev = to_mdev(device);
 int port_type_cap = MLX5_CAP_GEN(dev->mdev, port_type);

 return mlx5_port_type_cap_to_rdma_ll(port_type_cap);
}

static int get_port_state(struct ib_device *ibdev,
     u32 port_num,
     enum ib_port_state *state)
{
 struct ib_port_attr attr;
 int ret;

 memset(&attr, 0, sizeof(attr));
 ret = ibdev->ops.query_port(ibdev, port_num, &attr);
 if (!ret)
  *state = attr.state;
 return ret;
}

static struct mlx5_roce *mlx5_get_rep_roce(struct mlx5_ib_dev *dev,
        struct net_device *ndev,
        struct net_device *upper,
        u32 *port_num)
{
 struct net_device *rep_ndev;
 struct mlx5_ib_port *port;
 int i;

 for (i = 0; i < dev->num_ports; i++) {
  port  = &dev->port[i];
  if (!port->rep)
   continue;

  if (upper == ndev && port->rep->vport == MLX5_VPORT_UPLINK) {
   *port_num = i + 1;
   return &port->roce;
  }

  if (upper && port->rep->vport == MLX5_VPORT_UPLINK)
   continue;
  rep_ndev = ib_device_get_netdev(&dev->ib_dev, i + 1);
  if (rep_ndev && rep_ndev == ndev) {
   dev_put(rep_ndev);
   *port_num = i + 1;
   return &port->roce;
  }

  dev_put(rep_ndev);
 }

 return NULL;
}

static bool mlx5_netdev_send_event(struct mlx5_ib_dev *dev,
       struct net_device *ndev,
       struct net_device *upper,
       struct net_device *ib_ndev)
{
 if (!dev->ib_active)
  return false;

 /* Event is about our upper device */
 if (upper == ndev)
  return true;

 /* RDMA device is not in lag and not in switchdev */
 if (!dev->is_rep && !upper && ndev == ib_ndev)
  return true;

 /* RDMA devie is in switchdev */
 if (dev->is_rep && ndev == ib_ndev)
  return true;

 return false;
}

static struct net_device *mlx5_ib_get_rep_uplink_netdev(struct mlx5_ib_dev *ibdev)
{
 struct mlx5_ib_port *port;
 int i;

 for (i = 0; i < ibdev->num_ports; i++) {
  port = &ibdev->port[i];
  if (port->rep && port->rep->vport == MLX5_VPORT_UPLINK) {
   return ib_device_get_netdev(&ibdev->ib_dev, i + 1);
  }
 }

 return NULL;
}

static int mlx5_netdev_event(struct notifier_block *this,
        unsigned long event, void *ptr)
{
 struct mlx5_roce *roce = container_of(thisstruct mlx5_roce, nb);
 struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
 u32 port_num = roce->native_port_num;
 struct net_device *ib_ndev = NULL;
 struct mlx5_core_dev *mdev;
 struct mlx5_ib_dev *ibdev;

 ibdev = roce->dev;
 mdev = mlx5_ib_get_native_port_mdev(ibdev, port_num, NULL);
 if (!mdev)
  return NOTIFY_DONE;

 switch (event) {
 case NETDEV_REGISTER:
  /* Should already be registered during the load */
  if (ibdev->is_rep)
   break;

  ib_ndev = ib_device_get_netdev(&ibdev->ib_dev, port_num);
  /* Exit if already registered */
  if (ib_ndev)
   goto put_ndev;

  if (ndev->dev.parent == mdev->device)
   ib_device_set_netdev(&ibdev->ib_dev, ndev, port_num);
  break;

 case NETDEV_UNREGISTER:
  /* In case of reps, ib device goes away before the netdevs */
  if (ibdev->is_rep)
   break;
  ib_ndev = ib_device_get_netdev(&ibdev->ib_dev, port_num);
  if (ib_ndev == ndev)
   ib_device_set_netdev(&ibdev->ib_dev, NULL, port_num);
  goto put_ndev;

 case NETDEV_CHANGE:
 case NETDEV_UP:
 case NETDEV_DOWN: {
  struct net_device *upper = NULL;

  if (!netif_is_lag_master(ndev) && !netif_is_lag_port(ndev) &&
      !mlx5_core_mp_enabled(mdev))
   return NOTIFY_DONE;

  if (mlx5_lag_is_roce(mdev) || mlx5_lag_is_sriov(mdev)) {
   struct net_device *lag_ndev;

   if(mlx5_lag_is_roce(mdev))
    lag_ndev = ib_device_get_netdev(&ibdev->ib_dev, 1);
   else /* sriov lag */
    lag_ndev = mlx5_ib_get_rep_uplink_netdev(ibdev);

   if (lag_ndev) {
    upper = netdev_master_upper_dev_get(lag_ndev);
    dev_put(lag_ndev);
   } else {
    goto done;
   }
  }

  if (ibdev->is_rep)
   roce = mlx5_get_rep_roce(ibdev, ndev, upper, &port_num);
  if (!roce)
   return NOTIFY_DONE;

  ib_ndev = ib_device_get_netdev(&ibdev->ib_dev, port_num);

  if (mlx5_netdev_send_event(ibdev, ndev, upper, ib_ndev)) {
   struct ib_event ibev = { };
   enum ib_port_state port_state;

   if (get_port_state(&ibdev->ib_dev, port_num,
        &port_state))
    goto put_ndev;

   if (roce->last_port_state == port_state)
    goto put_ndev;

   roce->last_port_state = port_state;
   ibev.device = &ibdev->ib_dev;
   if (port_state == IB_PORT_DOWN)
    ibev.event = IB_EVENT_PORT_ERR;
   else if (port_state == IB_PORT_ACTIVE)
    ibev.event = IB_EVENT_PORT_ACTIVE;
   else
    goto put_ndev;

   ibev.element.port_num = port_num;
   ib_dispatch_event(&ibev);
  }
  break;
 }

 default:
  break;
 }
put_ndev:
 dev_put(ib_ndev);
done:
 mlx5_ib_put_native_port_mdev(ibdev, port_num);
 return NOTIFY_DONE;
}

struct mlx5_core_dev *mlx5_ib_get_native_port_mdev(struct mlx5_ib_dev *ibdev,
         u32 ib_port_num,
         u32 *native_port_num)
{
 enum rdma_link_layer ll = mlx5_ib_port_link_layer(&ibdev->ib_dev,
         ib_port_num);
 struct mlx5_core_dev *mdev = NULL;
 struct mlx5_ib_multiport_info *mpi;
 struct mlx5_ib_port *port;

 if (ibdev->ib_dev.type == RDMA_DEVICE_TYPE_SMI) {
  if (native_port_num)
   *native_port_num = smi_to_native_portnum(ibdev,
         ib_port_num);
  return ibdev->mdev;

 }

 if (!mlx5_core_mp_enabled(ibdev->mdev) ||
     ll != IB_LINK_LAYER_ETHERNET) {
  if (native_port_num)
   *native_port_num = ib_port_num;
  return ibdev->mdev;
 }

 if (native_port_num)
  *native_port_num = 1;

 port = &ibdev->port[ib_port_num - 1];
 spin_lock(&port->mp.mpi_lock);
 mpi = ibdev->port[ib_port_num - 1].mp.mpi;
 if (mpi && !mpi->unaffiliate) {
  mdev = mpi->mdev;
  /* If it's the master no need to refcount, it'll exist
 * as long as the ib_dev exists.
 */

  if (!mpi->is_master)
   mpi->mdev_refcnt++;
 }
 spin_unlock(&port->mp.mpi_lock);

 return mdev;
}

void mlx5_ib_put_native_port_mdev(struct mlx5_ib_dev *ibdev, u32 port_num)
{
 enum rdma_link_layer ll = mlx5_ib_port_link_layer(&ibdev->ib_dev,
         port_num);
 struct mlx5_ib_multiport_info *mpi;
 struct mlx5_ib_port *port;

 if (!mlx5_core_mp_enabled(ibdev->mdev) || ll != IB_LINK_LAYER_ETHERNET)
  return;

 port = &ibdev->port[port_num - 1];

 spin_lock(&port->mp.mpi_lock);
 mpi = ibdev->port[port_num - 1].mp.mpi;
 if (mpi->is_master)
  goto out;

 mpi->mdev_refcnt--;
 if (mpi->unaffiliate)
  complete(&mpi->unref_comp);
out:
 spin_unlock(&port->mp.mpi_lock);
}

static int translate_eth_legacy_proto_oper(u32 eth_proto_oper,
        u16 *active_speed, u8 *active_width)
{
 switch (eth_proto_oper) {
 case MLX5E_PROT_MASK(MLX5E_1000BASE_CX_SGMII):
 case MLX5E_PROT_MASK(MLX5E_1000BASE_KX):
 case MLX5E_PROT_MASK(MLX5E_100BASE_TX):
 case MLX5E_PROT_MASK(MLX5E_1000BASE_T):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_SDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_10GBASE_T):
 case MLX5E_PROT_MASK(MLX5E_10GBASE_CX4):
 case MLX5E_PROT_MASK(MLX5E_10GBASE_KX4):
 case MLX5E_PROT_MASK(MLX5E_10GBASE_KR):
 case MLX5E_PROT_MASK(MLX5E_10GBASE_CR):
 case MLX5E_PROT_MASK(MLX5E_10GBASE_SR):
 case MLX5E_PROT_MASK(MLX5E_10GBASE_ER):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_QDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_25GBASE_CR):
 case MLX5E_PROT_MASK(MLX5E_25GBASE_KR):
 case MLX5E_PROT_MASK(MLX5E_25GBASE_SR):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_EDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_40GBASE_CR4):
 case MLX5E_PROT_MASK(MLX5E_40GBASE_KR4):
 case MLX5E_PROT_MASK(MLX5E_40GBASE_SR4):
 case MLX5E_PROT_MASK(MLX5E_40GBASE_LR4):
  *active_width = IB_WIDTH_4X;
  *active_speed = IB_SPEED_QDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_50GBASE_CR2):
 case MLX5E_PROT_MASK(MLX5E_50GBASE_KR2):
 case MLX5E_PROT_MASK(MLX5E_50GBASE_SR2):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_HDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_56GBASE_R4):
  *active_width = IB_WIDTH_4X;
  *active_speed = IB_SPEED_FDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_100GBASE_CR4):
 case MLX5E_PROT_MASK(MLX5E_100GBASE_SR4):
 case MLX5E_PROT_MASK(MLX5E_100GBASE_KR4):
 case MLX5E_PROT_MASK(MLX5E_100GBASE_LR4):
  *active_width = IB_WIDTH_4X;
  *active_speed = IB_SPEED_EDR;
  break;
 default:
  return -EINVAL;
 }

 return 0;
}

static int translate_eth_ext_proto_oper(u32 eth_proto_oper, u16 *active_speed,
     u8 *active_width)
{
 switch (eth_proto_oper) {
 case MLX5E_PROT_MASK(MLX5E_SGMII_100M):
 case MLX5E_PROT_MASK(MLX5E_1000BASE_X_SGMII):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_SDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_5GBASE_R):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_DDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_10GBASE_XFI_XAUI_1):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_QDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_40GBASE_XLAUI_4_XLPPI_4):
  *active_width = IB_WIDTH_4X;
  *active_speed = IB_SPEED_QDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_25GAUI_1_25GBASE_CR_KR):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_EDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_50GAUI_2_LAUI_2_50GBASE_CR2_KR2):
  *active_width = IB_WIDTH_2X;
  *active_speed = IB_SPEED_EDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_50GAUI_1_LAUI_1_50GBASE_CR_KR):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_HDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_CAUI_4_100GBASE_CR4_KR4):
  *active_width = IB_WIDTH_4X;
  *active_speed = IB_SPEED_EDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_100GAUI_2_100GBASE_CR2_KR2):
  *active_width = IB_WIDTH_2X;
  *active_speed = IB_SPEED_HDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_100GAUI_1_100GBASE_CR_KR):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_NDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_200GAUI_4_200GBASE_CR4_KR4):
  *active_width = IB_WIDTH_4X;
  *active_speed = IB_SPEED_HDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_200GAUI_2_200GBASE_CR2_KR2):
  *active_width = IB_WIDTH_2X;
  *active_speed = IB_SPEED_NDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_200GAUI_1_200GBASE_CR1_KR1):
  *active_width = IB_WIDTH_1X;
  *active_speed = IB_SPEED_XDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_400GAUI_8_400GBASE_CR8):
  *active_width = IB_WIDTH_8X;
  *active_speed = IB_SPEED_HDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_400GAUI_4_400GBASE_CR4_KR4):
  *active_width = IB_WIDTH_4X;
  *active_speed = IB_SPEED_NDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_400GAUI_2_400GBASE_CR2_KR2):
  *active_width = IB_WIDTH_2X;
  *active_speed = IB_SPEED_XDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_800GAUI_8_800GBASE_CR8_KR8):
  *active_width = IB_WIDTH_8X;
  *active_speed = IB_SPEED_NDR;
  break;
 case MLX5E_PROT_MASK(MLX5E_800GAUI_4_800GBASE_CR4_KR4):
  *active_width = IB_WIDTH_4X;
  *active_speed = IB_SPEED_XDR;
  break;
 default:
  return -EINVAL;
 }

 return 0;
}

static int translate_eth_proto_oper(u32 eth_proto_oper, u16 *active_speed,
        u8 *active_width, bool ext)
{
 return ext ?
  translate_eth_ext_proto_oper(eth_proto_oper, active_speed,
          active_width) :
  translate_eth_legacy_proto_oper(eth_proto_oper, active_speed,
      active_width);
}

static int mlx5_query_port_roce(struct ib_device *device, u32 port_num,
    struct ib_port_attr *props)
{
 struct mlx5_ib_dev *dev = to_mdev(device);
 u32 out[MLX5_ST_SZ_DW(ptys_reg)] = {0};
 struct mlx5_core_dev *mdev;
 struct net_device *ndev, *upper;
 enum ib_mtu ndev_ib_mtu;
 bool put_mdev = true;
 u32 eth_prot_oper;
 u32 mdev_port_num;
 bool ext;
 int err;

 mdev = mlx5_ib_get_native_port_mdev(dev, port_num, &mdev_port_num);
 if (!mdev) {
  /* This means the port isn't affiliated yet. Get the
 * info for the master port instead.
 */

  put_mdev = false;
  mdev = dev->mdev;
  mdev_port_num = 1;
  port_num = 1;
 }

 /* Possible bad flows are checked before filling out props so in case
 * of an error it will still be zeroed out.
 * Use native port in case of reps
 */

 if (dev->is_rep)
  err = mlx5_query_port_ptys(mdev, out, sizeof(out), MLX5_PTYS_EN,
        1, 0);
 else
  err = mlx5_query_port_ptys(mdev, out, sizeof(out), MLX5_PTYS_EN,
        mdev_port_num, 0);
 if (err)
  goto out;
 ext = !!MLX5_GET_ETH_PROTO(ptys_reg, out, true, eth_proto_capability);
 eth_prot_oper = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, eth_proto_oper);

 props->active_width     = IB_WIDTH_4X;
 props->active_speed     = IB_SPEED_QDR;

 translate_eth_proto_oper(eth_prot_oper, &props->active_speed,
     &props->active_width, ext);

 if (!dev->is_rep && dev->mdev->roce.roce_en) {
  u16 qkey_viol_cntr;

  props->port_cap_flags |= IB_PORT_CM_SUP;
  props->ip_gids = true;
  props->gid_tbl_len = MLX5_CAP_ROCE(dev->mdev,
         roce_address_table_size);
  mlx5_query_nic_vport_qkey_viol_cntr(mdev, &qkey_viol_cntr);
  props->qkey_viol_cntr = qkey_viol_cntr;
 }
 props->max_mtu          = IB_MTU_4096;
 props->max_msg_sz       = 1 << MLX5_CAP_GEN(dev->mdev, log_max_msg);
 props->pkey_tbl_len     = 1;
 props->state            = IB_PORT_DOWN;
 props->phys_state       = IB_PORT_PHYS_STATE_DISABLED;

 /* If this is a stub query for an unaffiliated port stop here */
 if (!put_mdev)
  goto out;

 ndev = ib_device_get_netdev(device, port_num);
 if (!ndev)
  goto out;

 if (mlx5_lag_is_roce(mdev) || mlx5_lag_is_sriov(mdev)) {
  rcu_read_lock();
  upper = netdev_master_upper_dev_get_rcu(ndev);
  if (upper) {
   dev_put(ndev);
   ndev = upper;
   dev_hold(ndev);
  }
  rcu_read_unlock();
 }

 if (netif_running(ndev) && netif_carrier_ok(ndev)) {
  props->state      = IB_PORT_ACTIVE;
  props->phys_state = IB_PORT_PHYS_STATE_LINK_UP;
 }

 ndev_ib_mtu = iboe_get_mtu(ndev->mtu);

 dev_put(ndev);

 props->active_mtu = min(props->max_mtu, ndev_ib_mtu);
out:
 if (put_mdev)
  mlx5_ib_put_native_port_mdev(dev, port_num);
 return err;
}

int set_roce_addr(struct mlx5_ib_dev *dev, u32 port_num,
    unsigned int index, const union ib_gid *gid,
    const struct ib_gid_attr *attr)
{
 enum ib_gid_type gid_type;
 u16 vlan_id = 0xffff;
 u8 roce_version = 0;
 u8 roce_l3_type = 0;
 u8 mac[ETH_ALEN];
 int ret;

 gid_type = attr->gid_type;
 if (gid) {
  ret = rdma_read_gid_l2_fields(attr, &vlan_id, &mac[0]);
  if (ret)
   return ret;
 }

 switch (gid_type) {
 case IB_GID_TYPE_ROCE:
  roce_version = MLX5_ROCE_VERSION_1;
  break;
 case IB_GID_TYPE_ROCE_UDP_ENCAP:
  roce_version = MLX5_ROCE_VERSION_2;
  if (gid && ipv6_addr_v4mapped((void *)gid))
   roce_l3_type = MLX5_ROCE_L3_TYPE_IPV4;
  else
   roce_l3_type = MLX5_ROCE_L3_TYPE_IPV6;
  break;

 default:
  mlx5_ib_warn(dev, "Unexpected GID type %u\n", gid_type);
 }

 return mlx5_core_roce_gid_set(dev->mdev, index, roce_version,
          roce_l3_type, gid->raw, mac,
          vlan_id < VLAN_CFI_MASK, vlan_id,
          port_num);
}

static int mlx5_ib_add_gid(const struct ib_gid_attr *attr,
      __always_unused void **context)
{
 int ret;

 ret = mlx5r_add_gid_macsec_operations(attr);
 if (ret)
  return ret;

 return set_roce_addr(to_mdev(attr->device), attr->port_num,
        attr->index, &attr->gid, attr);
}

static int mlx5_ib_del_gid(const struct ib_gid_attr *attr,
      __always_unused void **context)
{
 int ret;

 ret = set_roce_addr(to_mdev(attr->device), attr->port_num,
       attr->index, NULL, attr);
 if (ret)
  return ret;

 mlx5r_del_gid_macsec_operations(attr);
 return 0;
}

__be16 mlx5_get_roce_udp_sport_min(const struct mlx5_ib_dev *dev,
       const struct ib_gid_attr *attr)
{
 if (attr->gid_type != IB_GID_TYPE_ROCE_UDP_ENCAP)
  return 0;

 return cpu_to_be16(MLX5_CAP_ROCE(dev->mdev, r_roce_min_src_udp_port));
}

static int mlx5_use_mad_ifc(struct mlx5_ib_dev *dev)
{
 if (MLX5_CAP_GEN(dev->mdev, port_type) == MLX5_CAP_PORT_TYPE_IB)
  return !MLX5_CAP_GEN(dev->mdev, ib_virt);
 return 0;
}

enum {
 MLX5_VPORT_ACCESS_METHOD_MAD,
 MLX5_VPORT_ACCESS_METHOD_HCA,
 MLX5_VPORT_ACCESS_METHOD_NIC,
};

static int mlx5_get_vport_access_method(struct ib_device *ibdev)
{
 if (mlx5_use_mad_ifc(to_mdev(ibdev)))
  return MLX5_VPORT_ACCESS_METHOD_MAD;

 if (mlx5_ib_port_link_layer(ibdev, 1) ==
     IB_LINK_LAYER_ETHERNET)
  return MLX5_VPORT_ACCESS_METHOD_NIC;

 return MLX5_VPORT_ACCESS_METHOD_HCA;
}

static void get_atomic_caps(struct mlx5_ib_dev *dev,
       u8 atomic_size_qp,
       struct ib_device_attr *props)
{
 u8 tmp;
 u8 atomic_operations = MLX5_CAP_ATOMIC(dev->mdev, atomic_operations);
 u8 atomic_req_8B_endianness_mode =
  MLX5_CAP_ATOMIC(dev->mdev, atomic_req_8B_endianness_mode);

 /* Check if HW supports 8 bytes standard atomic operations and capable
 * of host endianness respond
 */

 tmp = MLX5_ATOMIC_OPS_CMP_SWAP | MLX5_ATOMIC_OPS_FETCH_ADD;
 if (((atomic_operations & tmp) == tmp) &&
     (atomic_size_qp & MLX5_ATOMIC_SIZE_QP_8BYTES) &&
     (atomic_req_8B_endianness_mode)) {
  props->atomic_cap = IB_ATOMIC_HCA;
 } else {
  props->atomic_cap = IB_ATOMIC_NONE;
 }
}

static void get_atomic_caps_qp(struct mlx5_ib_dev *dev,
          struct ib_device_attr *props)
{
 u8 atomic_size_qp = MLX5_CAP_ATOMIC(dev->mdev, atomic_size_qp);

 get_atomic_caps(dev, atomic_size_qp, props);
}

static int mlx5_query_system_image_guid(struct ib_device *ibdev,
     __be64 *sys_image_guid)
{
 struct mlx5_ib_dev *dev = to_mdev(ibdev);
 struct mlx5_core_dev *mdev = dev->mdev;
 u64 tmp;
 int err;

 switch (mlx5_get_vport_access_method(ibdev)) {
 case MLX5_VPORT_ACCESS_METHOD_MAD:
  return mlx5_query_mad_ifc_system_image_guid(ibdev,
           sys_image_guid);

 case MLX5_VPORT_ACCESS_METHOD_HCA:
  err = mlx5_query_hca_vport_system_image_guid(mdev, &tmp);
  break;

 case MLX5_VPORT_ACCESS_METHOD_NIC:
  err = mlx5_query_nic_vport_system_image_guid(mdev, &tmp);
  break;

 default:
  return -EINVAL;
 }

 if (!err)
  *sys_image_guid = cpu_to_be64(tmp);

 return err;

}

static int mlx5_query_max_pkeys(struct ib_device *ibdev,
    u16 *max_pkeys)
{
 struct mlx5_ib_dev *dev = to_mdev(ibdev);
 struct mlx5_core_dev *mdev = dev->mdev;

 switch (mlx5_get_vport_access_method(ibdev)) {
 case MLX5_VPORT_ACCESS_METHOD_MAD:
  return mlx5_query_mad_ifc_max_pkeys(ibdev, max_pkeys);

 case MLX5_VPORT_ACCESS_METHOD_HCA:
 case MLX5_VPORT_ACCESS_METHOD_NIC:
  *max_pkeys = mlx5_to_sw_pkey_sz(MLX5_CAP_GEN(mdev,
      pkey_table_size));
  return 0;

 default:
  return -EINVAL;
 }
}

static int mlx5_query_vendor_id(struct ib_device *ibdev,
    u32 *vendor_id)
{
 struct mlx5_ib_dev *dev = to_mdev(ibdev);

 switch (mlx5_get_vport_access_method(ibdev)) {
 case MLX5_VPORT_ACCESS_METHOD_MAD:
  return mlx5_query_mad_ifc_vendor_id(ibdev, vendor_id);

 case MLX5_VPORT_ACCESS_METHOD_HCA:
 case MLX5_VPORT_ACCESS_METHOD_NIC:
  return mlx5_core_query_vendor_id(dev->mdev, vendor_id);

 default:
  return -EINVAL;
 }
}

static int mlx5_query_node_guid(struct mlx5_ib_dev *dev,
    __be64 *node_guid)
{
 u64 tmp;
 int err;

 switch (mlx5_get_vport_access_method(&dev->ib_dev)) {
 case MLX5_VPORT_ACCESS_METHOD_MAD:
  return mlx5_query_mad_ifc_node_guid(dev, node_guid);

 case MLX5_VPORT_ACCESS_METHOD_HCA:
  err = mlx5_query_hca_vport_node_guid(dev->mdev, &tmp);
  break;

 case MLX5_VPORT_ACCESS_METHOD_NIC:
  err = mlx5_query_nic_vport_node_guid(dev->mdev, &tmp);
  break;

 default:
  return -EINVAL;
 }

 if (!err)
  *node_guid = cpu_to_be64(tmp);

 return err;
}

struct mlx5_reg_node_desc {
 u8 desc[IB_DEVICE_NODE_DESC_MAX];
};

static int mlx5_query_node_desc(struct mlx5_ib_dev *dev, char *node_desc)
{
 struct mlx5_reg_node_desc in;

 if (mlx5_use_mad_ifc(dev))
  return mlx5_query_mad_ifc_node_desc(dev, node_desc);

 memset(&in, 0, sizeof(in));

 return mlx5_core_access_reg(dev->mdev, &in, sizeof(in), node_desc,
        sizeof(struct mlx5_reg_node_desc),
        MLX5_REG_NODE_DESC, 0, 0);
}

static void fill_esw_mgr_reg_c0(struct mlx5_core_dev *mdev,
    struct mlx5_ib_query_device_resp *resp)
{
 struct mlx5_eswitch *esw = mdev->priv.eswitch;
 u16 vport = mlx5_eswitch_manager_vport(mdev);

 resp->reg_c0.value = mlx5_eswitch_get_vport_metadata_for_match(esw,
              vport);
 resp->reg_c0.mask = mlx5_eswitch_get_vport_metadata_mask();
}

/*
 * Calculate maximum SQ overhead across all QP types.
 * Other QP types (REG_UMR, UC, RC, UD/SMI/GSI, XRC_TGT)
 * have smaller overhead than the types calculated below,
 * so they are implicitly included.
 */

static u32 mlx5_ib_calc_max_sq_overhead(void)
{
 u32 max_overhead_xrc, overhead_ud_lso, a, b;

 /* XRC_INI */
 max_overhead_xrc = sizeof(struct mlx5_wqe_xrc_seg);
 max_overhead_xrc += sizeof(struct mlx5_wqe_ctrl_seg);
 a = sizeof(struct mlx5_wqe_atomic_seg) +
     sizeof(struct mlx5_wqe_raddr_seg);
 b = sizeof(struct mlx5_wqe_umr_ctrl_seg) +
     sizeof(struct mlx5_mkey_seg) +
     MLX5_IB_SQ_UMR_INLINE_THRESHOLD / MLX5_IB_UMR_OCTOWORD;
 max_overhead_xrc += max(a, b);

 /* UD with LSO */
 overhead_ud_lso = sizeof(struct mlx5_wqe_ctrl_seg);
 overhead_ud_lso += sizeof(struct mlx5_wqe_eth_pad);
 overhead_ud_lso += sizeof(struct mlx5_wqe_eth_seg);
 overhead_ud_lso += sizeof(struct mlx5_wqe_datagram_seg);

 return max(max_overhead_xrc, overhead_ud_lso);
}

static u32 mlx5_ib_calc_max_qp_wr(struct mlx5_ib_dev *dev)
{
 struct mlx5_core_dev *mdev = dev->mdev;
 u32 max_wqe_bb_units = 1 << MLX5_CAP_GEN(mdev, log_max_qp_sz);
 u32 max_wqe_size;
 /* max QP overhead + 1 SGE, no inline, no special features */
 max_wqe_size = mlx5_ib_calc_max_sq_overhead() +
         sizeof(struct mlx5_wqe_data_seg);

 max_wqe_size = roundup_pow_of_two(max_wqe_size);

 max_wqe_size = ALIGN(max_wqe_size, MLX5_SEND_WQE_BB);

 return (max_wqe_bb_units * MLX5_SEND_WQE_BB) / max_wqe_size;
}

static int mlx5_ib_query_device(struct ib_device *ibdev,
    struct ib_device_attr *props,
    struct ib_udata *uhw)
{
 size_t uhw_outlen = (uhw) ? uhw->outlen : 0;
 struct mlx5_ib_dev *dev = to_mdev(ibdev);
 struct mlx5_core_dev *mdev = dev->mdev;
 int err = -ENOMEM;
 int max_sq_desc;
 int max_rq_sg;
 int max_sq_sg;
 u64 min_page_size = 1ull << MLX5_CAP_GEN(mdev, log_pg_sz);
 bool raw_support = !mlx5_core_mp_enabled(mdev);
 struct mlx5_ib_query_device_resp resp = {};
 size_t resp_len;
 u64 max_tso;

 resp_len = sizeof(resp.comp_mask) + sizeof(resp.response_length);
 if (uhw_outlen && uhw_outlen < resp_len)
  return -EINVAL;

 resp.response_length = resp_len;

 if (uhw && uhw->inlen && !ib_is_udata_cleared(uhw, 0, uhw->inlen))
  return -EINVAL;

 memset(props, 0, sizeof(*props));
 err = mlx5_query_system_image_guid(ibdev,
        &props->sys_image_guid);
 if (err)
  return err;

 props->max_pkeys = dev->pkey_table_len;

 err = mlx5_query_vendor_id(ibdev, &props->vendor_id);
 if (err)
  return err;

 props->fw_ver = ((u64)fw_rev_maj(dev->mdev) << 32) |
  (fw_rev_min(dev->mdev) << 16) |
  fw_rev_sub(dev->mdev);
 props->device_cap_flags    = IB_DEVICE_CHANGE_PHY_PORT |
  IB_DEVICE_PORT_ACTIVE_EVENT  |
  IB_DEVICE_SYS_IMAGE_GUID  |
  IB_DEVICE_RC_RNR_NAK_GEN;

 if (MLX5_CAP_GEN(mdev, pkv))
  props->device_cap_flags |= IB_DEVICE_BAD_PKEY_CNTR;
 if (MLX5_CAP_GEN(mdev, qkv))
  props->device_cap_flags |= IB_DEVICE_BAD_QKEY_CNTR;
 if (MLX5_CAP_GEN(mdev, apm))
  props->device_cap_flags |= IB_DEVICE_AUTO_PATH_MIG;
 if (MLX5_CAP_GEN(mdev, xrc))
  props->device_cap_flags |= IB_DEVICE_XRC;
 if (MLX5_CAP_GEN(mdev, imaicl)) {
  props->device_cap_flags |= IB_DEVICE_MEM_WINDOW |
        IB_DEVICE_MEM_WINDOW_TYPE_2B;
  props->max_mw = 1 << MLX5_CAP_GEN(mdev, log_max_mkey);
  /* We support 'Gappy' memory registration too */
  props->kernel_cap_flags |= IBK_SG_GAPS_REG;
 }
 /* IB_WR_REG_MR always requires changing the entity size with UMR */
 if (!MLX5_CAP_GEN(dev->mdev, umr_modify_entity_size_disabled))
  props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS;
 if (MLX5_CAP_GEN(mdev, sho)) {
  props->kernel_cap_flags |= IBK_INTEGRITY_HANDOVER;
  /* At this stage no support for signature handover */
  props->sig_prot_cap = IB_PROT_T10DIF_TYPE_1 |
          IB_PROT_T10DIF_TYPE_2 |
          IB_PROT_T10DIF_TYPE_3;
  props->sig_guard_cap = IB_GUARD_T10DIF_CRC |
           IB_GUARD_T10DIF_CSUM;
 }
 if (MLX5_CAP_GEN(mdev, block_lb_mc))
  props->kernel_cap_flags |= IBK_BLOCK_MULTICAST_LOOPBACK;

 if (MLX5_CAP_GEN(dev->mdev, eth_net_offloads) && raw_support) {
  if (MLX5_CAP_ETH(mdev, csum_cap)) {
   /* Legacy bit to support old userspace libraries */
   props->device_cap_flags |= IB_DEVICE_RAW_IP_CSUM;
   props->raw_packet_caps |= IB_RAW_PACKET_CAP_IP_CSUM;
  }

  if (MLX5_CAP_ETH(dev->mdev, vlan_cap))
   props->raw_packet_caps |=
    IB_RAW_PACKET_CAP_CVLAN_STRIPPING;

  if (offsetofend(typeof(resp), tso_caps) <= uhw_outlen) {
   max_tso = MLX5_CAP_ETH(mdev, max_lso_cap);
   if (max_tso) {
    resp.tso_caps.max_tso = 1 << max_tso;
    resp.tso_caps.supported_qpts |=
     1 << IB_QPT_RAW_PACKET;
    resp.response_length += sizeof(resp.tso_caps);
   }
  }

  if (offsetofend(typeof(resp), rss_caps) <= uhw_outlen) {
   resp.rss_caps.rx_hash_function =
      MLX5_RX_HASH_FUNC_TOEPLITZ;
   resp.rss_caps.rx_hash_fields_mask =
      MLX5_RX_HASH_SRC_IPV4 |
      MLX5_RX_HASH_DST_IPV4 |
      MLX5_RX_HASH_SRC_IPV6 |
      MLX5_RX_HASH_DST_IPV6 |
      MLX5_RX_HASH_SRC_PORT_TCP |
      MLX5_RX_HASH_DST_PORT_TCP |
      MLX5_RX_HASH_SRC_PORT_UDP |
      MLX5_RX_HASH_DST_PORT_UDP |
      MLX5_RX_HASH_INNER;
   resp.response_length += sizeof(resp.rss_caps);
  }
 } else {
  if (offsetofend(typeof(resp), tso_caps) <= uhw_outlen)
   resp.response_length += sizeof(resp.tso_caps);
  if (offsetofend(typeof(resp), rss_caps) <= uhw_outlen)
   resp.response_length += sizeof(resp.rss_caps);
 }

 if (MLX5_CAP_GEN(mdev, ipoib_basic_offloads)) {
  props->device_cap_flags |= IB_DEVICE_UD_IP_CSUM;
  props->kernel_cap_flags |= IBK_UD_TSO;
 }

 if (MLX5_CAP_GEN(dev->mdev, rq_delay_drop) &&
     MLX5_CAP_GEN(dev->mdev, general_notification_event) &&
     raw_support)
  props->raw_packet_caps |= IB_RAW_PACKET_CAP_DELAY_DROP;

 if (MLX5_CAP_GEN(mdev, ipoib_enhanced_offloads) &&
     MLX5_CAP_IPOIB_ENHANCED(mdev, csum_cap))
  props->device_cap_flags |= IB_DEVICE_UD_IP_CSUM;

 if (MLX5_CAP_GEN(dev->mdev, eth_net_offloads) &&
     MLX5_CAP_ETH(dev->mdev, scatter_fcs) &&
     raw_support) {
  /* Legacy bit to support old userspace libraries */
  props->device_cap_flags |= IB_DEVICE_RAW_SCATTER_FCS;
  props->raw_packet_caps |= IB_RAW_PACKET_CAP_SCATTER_FCS;
 }

 if (MLX5_CAP_DEV_MEM(mdev, memic)) {
  props->max_dm_size =
   MLX5_CAP_DEV_MEM(mdev, max_memic_size);
 }

 if (mlx5_get_flow_namespace(dev->mdev, MLX5_FLOW_NAMESPACE_BYPASS))
  props->device_cap_flags |= IB_DEVICE_MANAGED_FLOW_STEERING;

 if (MLX5_CAP_GEN(mdev, end_pad))
  props->device_cap_flags |= IB_DEVICE_PCI_WRITE_END_PADDING;

 props->vendor_part_id    = mdev->pdev->device;
 props->hw_ver     = mdev->pdev->revision;

 props->max_mr_size    = ~0ull;
 props->page_size_cap    = ~(min_page_size - 1);
 props->max_qp     = 1 << MLX5_CAP_GEN(mdev, log_max_qp);
 props->max_qp_wr = mlx5_ib_calc_max_qp_wr(dev);
 max_rq_sg =  MLX5_CAP_GEN(mdev, max_wqe_sz_rq) /
       sizeof(struct mlx5_wqe_data_seg);
 max_sq_desc = min_t(int, MLX5_CAP_GEN(mdev, max_wqe_sz_sq), 512);
 max_sq_sg = (max_sq_desc - sizeof(struct mlx5_wqe_ctrl_seg) -
       sizeof(struct mlx5_wqe_raddr_seg)) /
  sizeof(struct mlx5_wqe_data_seg);
 props->max_send_sge = max_sq_sg;
 props->max_recv_sge = max_rq_sg;
 props->max_sge_rd    = MLX5_MAX_SGE_RD;
 props->max_cq     = 1 << MLX5_CAP_GEN(mdev, log_max_cq);
 props->max_cqe = (1 << MLX5_CAP_GEN(mdev, log_max_cq_sz)) - 1;
 props->max_mr     = 1 << MLX5_CAP_GEN(mdev, log_max_mkey);
 props->max_pd     = 1 << MLX5_CAP_GEN(mdev, log_max_pd);
 props->max_qp_rd_atom    = 1 << MLX5_CAP_GEN(mdev, log_max_ra_req_qp);
 props->max_qp_init_rd_atom = 1 << MLX5_CAP_GEN(mdev, log_max_ra_res_qp);
 props->max_srq     = 1 << MLX5_CAP_GEN(mdev, log_max_srq);
 props->max_srq_wr = (1 << MLX5_CAP_GEN(mdev, log_max_srq_sz)) - 1;
 props->local_ca_ack_delay  = MLX5_CAP_GEN(mdev, local_ca_ack_delay);
 props->max_res_rd_atom    = props->max_qp_rd_atom * props->max_qp;
 props->max_srq_sge    = max_rq_sg - 1;
 props->max_fast_reg_page_list_len =
  1 << MLX5_CAP_GEN(mdev, log_max_klm_list_size);
 props->max_pi_fast_reg_page_list_len =
  props->max_fast_reg_page_list_len / 2;
 props->max_sgl_rd =
  MLX5_CAP_GEN(mdev, max_sgl_for_optimized_performance);
 get_atomic_caps_qp(dev, props);
 props->masked_atomic_cap   = IB_ATOMIC_NONE;
 props->max_mcast_grp    = 1 << MLX5_CAP_GEN(mdev, log_max_mcg);
 props->max_mcast_qp_attach = MLX5_CAP_GEN(mdev, max_qp_mcg);
 props->max_total_mcast_qp_attach = props->max_mcast_qp_attach *
        props->max_mcast_grp;
 props->max_ah = INT_MAX;
 props->hca_core_clock = MLX5_CAP_GEN(mdev, device_frequency_khz);
 props->timestamp_mask = 0x7FFFFFFFFFFFFFFFULL;

 if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) {
  if (dev->odp_caps.general_caps & IB_ODP_SUPPORT)
   props->kernel_cap_flags |= IBK_ON_DEMAND_PAGING;
  props->odp_caps = dev->odp_caps;
  if (!uhw) {
   /* ODP for kernel QPs is not implemented for receive
 * WQEs and SRQ WQEs
 */

   props->odp_caps.per_transport_caps.rc_odp_caps &=
    ~(IB_ODP_SUPPORT_READ |
      IB_ODP_SUPPORT_SRQ_RECV);
   props->odp_caps.per_transport_caps.uc_odp_caps &=
    ~(IB_ODP_SUPPORT_READ |
      IB_ODP_SUPPORT_SRQ_RECV);
   props->odp_caps.per_transport_caps.ud_odp_caps &=
    ~(IB_ODP_SUPPORT_READ |
      IB_ODP_SUPPORT_SRQ_RECV);
   props->odp_caps.per_transport_caps.xrc_odp_caps &=
    ~(IB_ODP_SUPPORT_READ |
      IB_ODP_SUPPORT_SRQ_RECV);
  }
 }

 if (mlx5_core_is_vf(mdev))
  props->kernel_cap_flags |= IBK_VIRTUAL_FUNCTION;

 if (mlx5_ib_port_link_layer(ibdev, 1) ==
     IB_LINK_LAYER_ETHERNET && raw_support) {
  props->rss_caps.max_rwq_indirection_tables =
   1 << MLX5_CAP_GEN(dev->mdev, log_max_rqt);
  props->rss_caps.max_rwq_indirection_table_size =
   1 << MLX5_CAP_GEN(dev->mdev, log_max_rqt_size);
  props->rss_caps.supported_qpts = 1 << IB_QPT_RAW_PACKET;
  props->max_wq_type_rq =
   1 << MLX5_CAP_GEN(dev->mdev, log_max_rq);
 }

 if (MLX5_CAP_GEN(mdev, tag_matching)) {
  props->tm_caps.max_num_tags =
   (1 << MLX5_CAP_GEN(mdev, log_tag_matching_list_sz)) - 1;
  props->tm_caps.max_ops =
   1 << MLX5_CAP_GEN(mdev, log_max_qp_sz);
  props->tm_caps.max_sge = MLX5_TM_MAX_SGE;
 }

 if (MLX5_CAP_GEN(mdev, tag_matching) &&
     MLX5_CAP_GEN(mdev, rndv_offload_rc)) {
  props->tm_caps.flags = IB_TM_CAP_RNDV_RC;
  props->tm_caps.max_rndv_hdr_size = MLX5_TM_MAX_RNDV_MSG_SIZE;
 }

 if (MLX5_CAP_GEN(dev->mdev, cq_moderation)) {
  props->cq_caps.max_cq_moderation_count =
      MLX5_MAX_CQ_COUNT;
  props->cq_caps.max_cq_moderation_period =
      MLX5_MAX_CQ_PERIOD;
 }

 if (offsetofend(typeof(resp), cqe_comp_caps) <= uhw_outlen) {
  resp.response_length += sizeof(resp.cqe_comp_caps);

  if (MLX5_CAP_GEN(dev->mdev, cqe_compression)) {
   resp.cqe_comp_caps.max_num =
    MLX5_CAP_GEN(dev->mdev,
          cqe_compression_max_num);

   resp.cqe_comp_caps.supported_format =
    MLX5_IB_CQE_RES_FORMAT_HASH |
    MLX5_IB_CQE_RES_FORMAT_CSUM;

   if (MLX5_CAP_GEN(dev->mdev, mini_cqe_resp_stride_index))
    resp.cqe_comp_caps.supported_format |=
     MLX5_IB_CQE_RES_FORMAT_CSUM_STRIDX;
  }
 }

 if (offsetofend(typeof(resp), packet_pacing_caps) <= uhw_outlen &&
     raw_support) {
  if (MLX5_CAP_QOS(mdev, packet_pacing) &&
      MLX5_CAP_GEN(mdev, qos)) {
   resp.packet_pacing_caps.qp_rate_limit_max =
    MLX5_CAP_QOS(mdev, packet_pacing_max_rate);
   resp.packet_pacing_caps.qp_rate_limit_min =
    MLX5_CAP_QOS(mdev, packet_pacing_min_rate);
   resp.packet_pacing_caps.supported_qpts |=
    1 << IB_QPT_RAW_PACKET;
   if (MLX5_CAP_QOS(mdev, packet_pacing_burst_bound) &&
       MLX5_CAP_QOS(mdev, packet_pacing_typical_size))
    resp.packet_pacing_caps.cap_flags |=
     MLX5_IB_PP_SUPPORT_BURST;
  }
  resp.response_length += sizeof(resp.packet_pacing_caps);
 }

 if (offsetofend(typeof(resp), mlx5_ib_support_multi_pkt_send_wqes) <=
     uhw_outlen) {
  if (MLX5_CAP_ETH(mdev, multi_pkt_send_wqe))
   resp.mlx5_ib_support_multi_pkt_send_wqes =
    MLX5_IB_ALLOW_MPW;

  if (MLX5_CAP_ETH(mdev, enhanced_multi_pkt_send_wqe))
   resp.mlx5_ib_support_multi_pkt_send_wqes |=
    MLX5_IB_SUPPORT_EMPW;

  resp.response_length +=
   sizeof(resp.mlx5_ib_support_multi_pkt_send_wqes);
 }

 if (offsetofend(typeof(resp), flags) <= uhw_outlen) {
  resp.response_length += sizeof(resp.flags);

  if (MLX5_CAP_GEN(mdev, cqe_compression_128))
   resp.flags |=
    MLX5_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_COMP;

  if (MLX5_CAP_GEN(mdev, cqe_128_always))
   resp.flags |= MLX5_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_PAD;
  if (MLX5_CAP_GEN(mdev, qp_packet_based))
   resp.flags |=
    MLX5_IB_QUERY_DEV_RESP_PACKET_BASED_CREDIT_MODE;

  resp.flags |= MLX5_IB_QUERY_DEV_RESP_FLAGS_SCAT2CQE_DCT;

  if (MLX5_CAP_GEN_2(mdev, dp_ordering_force) &&
      (MLX5_CAP_GEN(mdev, dp_ordering_ooo_all_xrc) ||
      MLX5_CAP_GEN(mdev, dp_ordering_ooo_all_dc) ||
      MLX5_CAP_GEN(mdev, dp_ordering_ooo_all_rc) ||
      MLX5_CAP_GEN(mdev, dp_ordering_ooo_all_ud) ||
      MLX5_CAP_GEN(mdev, dp_ordering_ooo_all_uc)))
   resp.flags |= MLX5_IB_QUERY_DEV_RESP_FLAGS_OOO_DP;
 }

 if (offsetofend(typeof(resp), sw_parsing_caps) <= uhw_outlen) {
  resp.response_length += sizeof(resp.sw_parsing_caps);
  if (MLX5_CAP_ETH(mdev, swp)) {
   resp.sw_parsing_caps.sw_parsing_offloads |=
    MLX5_IB_SW_PARSING;

   if (MLX5_CAP_ETH(mdev, swp_csum))
    resp.sw_parsing_caps.sw_parsing_offloads |=
     MLX5_IB_SW_PARSING_CSUM;

   if (MLX5_CAP_ETH(mdev, swp_lso))
    resp.sw_parsing_caps.sw_parsing_offloads |=
     MLX5_IB_SW_PARSING_LSO;

   if (resp.sw_parsing_caps.sw_parsing_offloads)
    resp.sw_parsing_caps.supported_qpts =
     BIT(IB_QPT_RAW_PACKET);
  }
 }

 if (offsetofend(typeof(resp), striding_rq_caps) <= uhw_outlen &&
     raw_support) {
  resp.response_length += sizeof(resp.striding_rq_caps);
  if (MLX5_CAP_GEN(mdev, striding_rq)) {
   resp.striding_rq_caps.min_single_stride_log_num_of_bytes =
    MLX5_MIN_SINGLE_STRIDE_LOG_NUM_BYTES;
   resp.striding_rq_caps.max_single_stride_log_num_of_bytes =
    MLX5_MAX_SINGLE_STRIDE_LOG_NUM_BYTES;
   if (MLX5_CAP_GEN(dev->mdev, ext_stride_num_range))
    resp.striding_rq_caps
     .min_single_wqe_log_num_of_strides =
     MLX5_EXT_MIN_SINGLE_WQE_LOG_NUM_STRIDES;
   else
    resp.striding_rq_caps
     .min_single_wqe_log_num_of_strides =
     MLX5_MIN_SINGLE_WQE_LOG_NUM_STRIDES;
   resp.striding_rq_caps.max_single_wqe_log_num_of_strides =
    MLX5_MAX_SINGLE_WQE_LOG_NUM_STRIDES;
   resp.striding_rq_caps.supported_qpts =
    BIT(IB_QPT_RAW_PACKET);
  }
 }

 if (offsetofend(typeof(resp), tunnel_offloads_caps) <= uhw_outlen) {
  resp.response_length += sizeof(resp.tunnel_offloads_caps);
  if (MLX5_CAP_ETH(mdev, tunnel_stateless_vxlan))
   resp.tunnel_offloads_caps |=
    MLX5_IB_TUNNELED_OFFLOADS_VXLAN;
  if (MLX5_CAP_ETH(mdev, tunnel_stateless_geneve_rx))
   resp.tunnel_offloads_caps |=
    MLX5_IB_TUNNELED_OFFLOADS_GENEVE;
  if (MLX5_CAP_ETH(mdev, tunnel_stateless_gre))
   resp.tunnel_offloads_caps |=
    MLX5_IB_TUNNELED_OFFLOADS_GRE;
  if (MLX5_CAP_ETH(mdev, tunnel_stateless_mpls_over_gre))
   resp.tunnel_offloads_caps |=
    MLX5_IB_TUNNELED_OFFLOADS_MPLS_GRE;
  if (MLX5_CAP_ETH(mdev, tunnel_stateless_mpls_over_udp))
   resp.tunnel_offloads_caps |=
    MLX5_IB_TUNNELED_OFFLOADS_MPLS_UDP;
 }

 if (offsetofend(typeof(resp), dci_streams_caps) <= uhw_outlen) {
  resp.response_length += sizeof(resp.dci_streams_caps);

  resp.dci_streams_caps.max_log_num_concurent =
   MLX5_CAP_GEN(mdev, log_max_dci_stream_channels);

  resp.dci_streams_caps.max_log_num_errored =
   MLX5_CAP_GEN(mdev, log_max_dci_errored_streams);
 }

 if (offsetofend(typeof(resp), reserved) <= uhw_outlen)
  resp.response_length += sizeof(resp.reserved);

 if (offsetofend(typeof(resp), reg_c0) <= uhw_outlen) {
  struct mlx5_eswitch *esw = mdev->priv.eswitch;

  resp.response_length += sizeof(resp.reg_c0);

  if (mlx5_eswitch_mode(mdev) == MLX5_ESWITCH_OFFLOADS &&
      mlx5_eswitch_vport_match_metadata_enabled(esw))
   fill_esw_mgr_reg_c0(mdev, &resp);
 }

 if (uhw_outlen) {
  err = ib_copy_to_udata(uhw, &resp, resp.response_length);

  if (err)
   return err;
 }

 return 0;
}

static void translate_active_width(struct ib_device *ibdev, u16 active_width,
       u8 *ib_width)
{
 struct mlx5_ib_dev *dev = to_mdev(ibdev);

 if (active_width & MLX5_PTYS_WIDTH_1X)
  *ib_width = IB_WIDTH_1X;
 else if (active_width & MLX5_PTYS_WIDTH_2X)
  *ib_width = IB_WIDTH_2X;
 else if (active_width & MLX5_PTYS_WIDTH_4X)
  *ib_width = IB_WIDTH_4X;
 else if (active_width & MLX5_PTYS_WIDTH_8X)
  *ib_width = IB_WIDTH_8X;
 else if (active_width & MLX5_PTYS_WIDTH_12X)
  *ib_width = IB_WIDTH_12X;
 else {
  mlx5_ib_dbg(dev, "Invalid active_width %d, setting width to default value: 4x\n",
       active_width);
  *ib_width = IB_WIDTH_4X;
 }

 return;
}

static int mlx5_mtu_to_ib_mtu(int mtu)
{
 switch (mtu) {
 case 256: return 1;
 case 512: return 2;
 case 1024: return 3;
 case 2048: return 4;
 case 4096: return 5;
 default:
  pr_warn("invalid mtu\n");
  return -1;
 }
}

enum ib_max_vl_num {
 __IB_MAX_VL_0  = 1,
 __IB_MAX_VL_0_1  = 2,
 __IB_MAX_VL_0_3  = 3,
 __IB_MAX_VL_0_7  = 4,
 __IB_MAX_VL_0_14 = 5,
};

enum mlx5_vl_hw_cap {
 MLX5_VL_HW_0 = 1,
 MLX5_VL_HW_0_1 = 2,
 MLX5_VL_HW_0_2 = 3,
 MLX5_VL_HW_0_3 = 4,
 MLX5_VL_HW_0_4 = 5,
 MLX5_VL_HW_0_5 = 6,
 MLX5_VL_HW_0_6 = 7,
 MLX5_VL_HW_0_7 = 8,
 MLX5_VL_HW_0_14 = 15
};

static int translate_max_vl_num(struct ib_device *ibdev, u8 vl_hw_cap,
    u8 *max_vl_num)
{
 switch (vl_hw_cap) {
 case MLX5_VL_HW_0:
  *max_vl_num = __IB_MAX_VL_0;
  break;
 case MLX5_VL_HW_0_1:
  *max_vl_num = __IB_MAX_VL_0_1;
  break;
 case MLX5_VL_HW_0_3:
  *max_vl_num = __IB_MAX_VL_0_3;
  break;
 case MLX5_VL_HW_0_7:
  *max_vl_num = __IB_MAX_VL_0_7;
  break;
 case MLX5_VL_HW_0_14:
  *max_vl_num = __IB_MAX_VL_0_14;
  break;

 default:
  return -EINVAL;
 }

 return 0;
}

static int mlx5_query_hca_port(struct ib_device *ibdev, u32 port,
          struct ib_port_attr *props)
{
 struct mlx5_ib_dev *dev = to_mdev(ibdev);
 struct mlx5_core_dev *mdev = dev->mdev;
 struct mlx5_hca_vport_context *rep;
 u8 vl_hw_cap, plane_index = 0;
 u16 max_mtu;
 u16 oper_mtu;
 int err;
 u16 ib_link_width_oper;

 rep = kzalloc(sizeof(*rep), GFP_KERNEL);
 if (!rep) {
  err = -ENOMEM;
  goto out;
 }

 /* props being zeroed by the caller, avoid zeroing it here */

 if (ibdev->type == RDMA_DEVICE_TYPE_SMI) {
  plane_index = port;
  port = smi_to_native_portnum(dev, port);
 }

 err = mlx5_query_hca_vport_context(mdev, 0, port, 0, rep);
 if (err)
  goto out;

 props->lid  = rep->lid;
 props->lmc  = rep->lmc;
 props->sm_lid  = rep->sm_lid;
 props->sm_sl  = rep->sm_sl;
 props->state  = rep->vport_state;
 props->phys_state = rep->port_physical_state;

 props->port_cap_flags = rep->cap_mask1;
 if (dev->num_plane) {
  props->port_cap_flags |= IB_PORT_SM_DISABLED;
  props->port_cap_flags &= ~IB_PORT_SM;
 } else if (ibdev->type == RDMA_DEVICE_TYPE_SMI)
  props->port_cap_flags &= ~IB_PORT_CM_SUP;

 props->gid_tbl_len = mlx5_get_gid_table_len(MLX5_CAP_GEN(mdev, gid_table_size));
 props->max_msg_sz = 1 << MLX5_CAP_GEN(mdev, log_max_msg);
 props->pkey_tbl_len = mlx5_to_sw_pkey_sz(MLX5_CAP_GEN(mdev, pkey_table_size));
 props->bad_pkey_cntr = rep->pkey_violation_counter;
 props->qkey_viol_cntr = rep->qkey_violation_counter;
 props->subnet_timeout = rep->subnet_timeout;
 props->init_type_reply = rep->init_type_reply;

 if (props->port_cap_flags & IB_PORT_CAP_MASK2_SUP)
  props->port_cap_flags2 = rep->cap_mask2;

 err = mlx5_query_ib_port_oper(mdev, &ib_link_width_oper,
          &props->active_speed, port, plane_index);
 if (err)
  goto out;

 translate_active_width(ibdev, ib_link_width_oper, &props->active_width);

 mlx5_query_port_max_mtu(mdev, &max_mtu, port);

 props->max_mtu = mlx5_mtu_to_ib_mtu(max_mtu);

 mlx5_query_port_oper_mtu(mdev, &oper_mtu, port);

 props->active_mtu = mlx5_mtu_to_ib_mtu(oper_mtu);

 err = mlx5_query_port_vl_hw_cap(mdev, &vl_hw_cap, port);
 if (err)
  goto out;

 err = translate_max_vl_num(ibdev, vl_hw_cap,
       &props->max_vl_num);
out:
 kfree(rep);
 return err;
}

int mlx5_ib_query_port(struct ib_device *ibdev, u32 port,
         struct ib_port_attr *props)
{
 unsigned int count;
 int ret;

 switch (mlx5_get_vport_access_method(ibdev)) {
 case MLX5_VPORT_ACCESS_METHOD_MAD:
  ret = mlx5_query_mad_ifc_port(ibdev, port, props);
  break;

 case MLX5_VPORT_ACCESS_METHOD_HCA:
  ret = mlx5_query_hca_port(ibdev, port, props);
  break;

 case MLX5_VPORT_ACCESS_METHOD_NIC:
  ret = mlx5_query_port_roce(ibdev, port, props);
  break;

 default:
  ret = -EINVAL;
 }

 if (!ret && props) {
  struct mlx5_ib_dev *dev = to_mdev(ibdev);
  struct mlx5_core_dev *mdev;
  bool put_mdev = true;

  mdev = mlx5_ib_get_native_port_mdev(dev, port, NULL);
  if (!mdev) {
   /* If the port isn't affiliated yet query the master.
 * The master and slave will have the same values.
 */

   mdev = dev->mdev;
   port = 1;
   put_mdev = false;
  }
  count = mlx5_core_reserved_gids_count(mdev);
  if (put_mdev)
   mlx5_ib_put_native_port_mdev(dev, port);
  props->gid_tbl_len -= count;
 }
 return ret;
}

static int mlx5_ib_rep_query_port(struct ib_device *ibdev, u32 port,
      struct ib_port_attr *props)
{
 return mlx5_query_port_roce(ibdev, port, props);
}

static int mlx5_ib_rep_query_pkey(struct ib_device *ibdev, u32 port, u16 index,
      u16 *pkey)
{
 /* Default special Pkey for representor device port as per the
 * IB specification 1.3 section 10.9.1.2.
 */

 *pkey = 0xffff;
 return 0;
}

static int mlx5_ib_query_gid(struct ib_device *ibdev, u32 port, int index,
        union ib_gid *gid)
{
 struct mlx5_ib_dev *dev = to_mdev(ibdev);
 struct mlx5_core_dev *mdev = dev->mdev;

 switch (mlx5_get_vport_access_method(ibdev)) {
 case MLX5_VPORT_ACCESS_METHOD_MAD:
  return mlx5_query_mad_ifc_gids(ibdev, port, index, gid);

 case MLX5_VPORT_ACCESS_METHOD_HCA:
  return mlx5_query_hca_vport_gid(mdev, 0, port, 0, index, gid);

 default:
  return -EINVAL;
 }

}

static int mlx5_query_hca_nic_pkey(struct ib_device *ibdev, u32 port,
       u16 index, u16 *pkey)
{
 struct mlx5_ib_dev *dev = to_mdev(ibdev);
 struct mlx5_core_dev *mdev;
 bool put_mdev = true;
 u32 mdev_port_num;
 int err;

 mdev = mlx5_ib_get_native_port_mdev(dev, port, &mdev_port_num);
 if (!mdev) {
  /* The port isn't affiliated yet, get the PKey from the master
 * port. For RoCE the PKey tables will be the same.
 */

  put_mdev = false;
  mdev = dev->mdev;
  mdev_port_num = 1;
 }

 err = mlx5_query_hca_vport_pkey(mdev, 0, mdev_port_num, 0,
     index, pkey);
 if (put_mdev)
  mlx5_ib_put_native_port_mdev(dev, port);

 return err;
}

static int mlx5_ib_query_pkey(struct ib_device *ibdev, u32 port, u16 index,
         u16 *pkey)
{
 switch (mlx5_get_vport_access_method(ibdev)) {
 case MLX5_VPORT_ACCESS_METHOD_MAD:
  return mlx5_query_mad_ifc_pkey(ibdev, port, index, pkey);

 case MLX5_VPORT_ACCESS_METHOD_HCA:
 case MLX5_VPORT_ACCESS_METHOD_NIC:
  return mlx5_query_hca_nic_pkey(ibdev, port, index, pkey);
 default:
  return -EINVAL;
 }
}

static int mlx5_ib_modify_device(struct ib_device *ibdev, int mask,
     struct ib_device_modify *props)
{
 struct mlx5_ib_dev *dev = to_mdev(ibdev);
 struct mlx5_reg_node_desc in;
 struct mlx5_reg_node_desc out;
 int err;

 if (mask & ~IB_DEVICE_MODIFY_NODE_DESC)
  return -EOPNOTSUPP;

 if (!(mask & IB_DEVICE_MODIFY_NODE_DESC))
  return 0;

 /*
 * If possible, pass node desc to FW, so it can generate
 * a 144 trap.  If cmd fails, just ignore.
 */

 memcpy(&in, props->node_desc, IB_DEVICE_NODE_DESC_MAX);
 err = mlx5_core_access_reg(dev->mdev, &in, sizeof(in), &out,
       sizeof(out), MLX5_REG_NODE_DESC, 0, 1);
 if (err)
  return err;

 memcpy(ibdev->node_desc, props->node_desc, IB_DEVICE_NODE_DESC_MAX);

 return err;
}

static int set_port_caps_atomic(struct mlx5_ib_dev *dev, u32 port_num, u32 mask,
    u32 value)
{
 struct mlx5_hca_vport_context ctx = {};
 struct mlx5_core_dev *mdev;
 u32 mdev_port_num;
 int err;

 mdev = mlx5_ib_get_native_port_mdev(dev, port_num, &mdev_port_num);
 if (!mdev)
  return -ENODEV;

 err = mlx5_query_hca_vport_context(mdev, 0, mdev_port_num, 0, &ctx);
 if (err)
  goto out;

 if (~ctx.cap_mask1_perm & mask) {
  mlx5_ib_warn(dev, "trying to change bitmask 0x%X but change supported 0x%X\n",
        mask, ctx.cap_mask1_perm);
  err = -EINVAL;
  goto out;
 }

 ctx.cap_mask1 = value;
 ctx.cap_mask1_perm = mask;
 err = mlx5_core_modify_hca_vport_context(mdev, 0, mdev_port_num,
       0, &ctx);

out:
 mlx5_ib_put_native_port_mdev(dev, port_num);

 return err;
}

static int mlx5_ib_modify_port(struct ib_device *ibdev, u32 port, int mask,
          struct ib_port_modify *props)
{
 struct mlx5_ib_dev *dev = to_mdev(ibdev);
 struct ib_port_attr attr;
 u32 tmp;
 int err;
 u32 change_mask;
 u32 value;
 bool is_ib = (mlx5_ib_port_link_layer(ibdev, port) ==
        IB_LINK_LAYER_INFINIBAND);

 /* CM layer calls ib_modify_port() regardless of the link layer. For
 * Ethernet ports, qkey violation and Port capabilities are meaningless.
 */

 if (!is_ib)
  return 0;

 if (MLX5_CAP_GEN(dev->mdev, ib_virt) && is_ib) {
  change_mask = props->clr_port_cap_mask | props->set_port_cap_mask;
  value = ~props->clr_port_cap_mask | props->set_port_cap_mask;
  return set_port_caps_atomic(dev, port, change_mask, value);
 }

 mutex_lock(&dev->cap_mask_mutex);

 err = ib_query_port(ibdev, port, &attr);
 if (err)
  goto out;

 tmp = (attr.port_cap_flags | props->set_port_cap_mask) &
  ~props->clr_port_cap_mask;

 err = mlx5_set_port_caps(dev->mdev, port, tmp);

out:
 mutex_unlock(&dev->cap_mask_mutex);
 return err;
}

static void print_lib_caps(struct mlx5_ib_dev *dev, u64 caps)
{
 mlx5_ib_dbg(dev, "MLX5_LIB_CAP_4K_UAR = %s\n",
      caps & MLX5_LIB_CAP_4K_UAR ? "y" : "n");
}

static u16 calc_dynamic_bfregs(int uars_per_sys_page)
{
 /* Large page with non 4k uar support might limit the dynamic size */
 if (uars_per_sys_page == 1  && PAGE_SIZE > 4096)
  return MLX5_MIN_DYN_BFREGS;

 return MLX5_MAX_DYN_BFREGS;
}

static int calc_total_bfregs(struct mlx5_ib_dev *dev, bool lib_uar_4k,
        struct mlx5_ib_alloc_ucontext_req_v2 *req,
        struct mlx5_bfreg_info *bfregi)
{
 int uars_per_sys_page;
 int bfregs_per_sys_page;
 int ref_bfregs = req->total_num_bfregs;

 if (req->total_num_bfregs == 0)
  return -EINVAL;

 BUILD_BUG_ON(MLX5_MAX_BFREGS % MLX5_NON_FP_BFREGS_IN_PAGE);
 BUILD_BUG_ON(MLX5_MAX_BFREGS < MLX5_NON_FP_BFREGS_IN_PAGE);

 if (req->total_num_bfregs > MLX5_MAX_BFREGS)
  return -ENOMEM;

 uars_per_sys_page = get_uars_per_sys_page(dev, lib_uar_4k);
 bfregs_per_sys_page = uars_per_sys_page * MLX5_NON_FP_BFREGS_PER_UAR;
 /* This holds the required static allocation asked by the user */
 req->total_num_bfregs = ALIGN(req->total_num_bfregs, bfregs_per_sys_page);
 if (req->num_low_latency_bfregs > req->total_num_bfregs - 1)
  return -EINVAL;

 bfregi->num_static_sys_pages = req->total_num_bfregs / bfregs_per_sys_page;
 bfregi->num_dyn_bfregs = ALIGN(calc_dynamic_bfregs(uars_per_sys_page), bfregs_per_sys_page);
 bfregi->total_num_bfregs = req->total_num_bfregs + bfregi->num_dyn_bfregs;
 bfregi->num_sys_pages = bfregi->total_num_bfregs / bfregs_per_sys_page;

 mlx5_ib_dbg(dev, "uar_4k: fw support %s, lib support %s, user requested %d bfregs, allocated %d, total bfregs %d, using %d sys pages\n",
      MLX5_CAP_GEN(dev->mdev, uar_4k) ? "yes" : "no",
      lib_uar_4k ? "yes" : "no", ref_bfregs,
      req->total_num_bfregs, bfregi->total_num_bfregs,
      bfregi->num_sys_pages);

 return 0;
}

static int allocate_uars(struct mlx5_ib_dev *dev, struct mlx5_ib_ucontext *context)
{
 struct mlx5_bfreg_info *bfregi;
 int err;
 int i;

 bfregi = &context->bfregi;
 for (i = 0; i < bfregi->num_static_sys_pages; i++) {
  err = mlx5_cmd_uar_alloc(dev->mdev, &bfregi->sys_pages[i],
      context->devx_uid);
  if (err)
   goto error;

  mlx5_ib_dbg(dev, "allocated uar %d\n", bfregi->sys_pages[i]);
 }

 for (i = bfregi->num_static_sys_pages; i < bfregi->num_sys_pages; i++)
  bfregi->sys_pages[i] = MLX5_IB_INVALID_UAR_INDEX;

 return 0;

error:
 for (--i; i >= 0; i--)
  if (mlx5_cmd_uar_dealloc(dev->mdev, bfregi->sys_pages[i],
      context->devx_uid))
   mlx5_ib_warn(dev, "failed to free uar %d\n", i);

 return err;
}

static void deallocate_uars(struct mlx5_ib_dev *dev,
       struct mlx5_ib_ucontext *context)
{
 struct mlx5_bfreg_info *bfregi;
 int i;

 bfregi = &context->bfregi;
 for (i = 0; i < bfregi->num_sys_pages; i++)
  if (i < bfregi->num_static_sys_pages ||
      bfregi->sys_pages[i] != MLX5_IB_INVALID_UAR_INDEX)
   mlx5_cmd_uar_dealloc(dev->mdev, bfregi->sys_pages[i],
          context->devx_uid);
}

static int mlx5_ib_enable_lb_mp(struct mlx5_core_dev *master,
    struct mlx5_core_dev *slave,
    struct mlx5_ib_lb_state *lb_state)
{
 int err;

 err = mlx5_nic_vport_update_local_lb(master, true);
 if (err)
  return err;

 err = mlx5_nic_vport_update_local_lb(slave, true);
 if (err)
  goto out;

 lb_state->force_enable = true;
 return 0;

out:
 mlx5_nic_vport_update_local_lb(master, false);
 return err;
}

static void mlx5_ib_disable_lb_mp(struct mlx5_core_dev *master,
      struct mlx5_core_dev *slave,
      struct mlx5_ib_lb_state *lb_state)
{
 mlx5_nic_vport_update_local_lb(slave, false);
 mlx5_nic_vport_update_local_lb(master, false);

 lb_state->force_enable = false;
}

int mlx5_ib_enable_lb(struct mlx5_ib_dev *dev, bool td, bool qp)
{
 int err = 0;

 if (dev->lb.force_enable)
  return 0;

 mutex_lock(&dev->lb.mutex);
 if (td)
  dev->lb.user_td++;
 if (qp)
  dev->lb.qps++;

 if (dev->lb.user_td == 2 ||
     dev->lb.qps == 1) {
  if (!dev->lb.enabled) {
   err = mlx5_nic_vport_update_local_lb(dev->mdev, true);
   dev->lb.enabled = true;
  }
 }

 mutex_unlock(&dev->lb.mutex);

 return err;
}

void mlx5_ib_disable_lb(struct mlx5_ib_dev *dev, bool td, bool qp)
{
 if (dev->lb.force_enable)
  return;

 mutex_lock(&dev->lb.mutex);
 if (td)
  dev->lb.user_td--;
 if (qp)
  dev->lb.qps--;

 if (dev->lb.user_td == 1 &&
     dev->lb.qps == 0) {
  if (dev->lb.enabled) {
   mlx5_nic_vport_update_local_lb(dev->mdev, false);
   dev->lb.enabled = false;
  }
 }

 mutex_unlock(&dev->lb.mutex);
}

static int mlx5_ib_alloc_transport_domain(struct mlx5_ib_dev *dev, u32 *tdn,
       u16 uid)
{
 int err;

 if (!MLX5_CAP_GEN(dev->mdev, log_max_transport_domain))
  return 0;

 err = mlx5_cmd_alloc_transport_domain(dev->mdev, tdn, uid);
 if (err)
  return err;

 if ((MLX5_CAP_GEN(dev->mdev, port_type) != MLX5_CAP_PORT_TYPE_ETH) ||
     (!MLX5_CAP_GEN(dev->mdev, disable_local_lb_uc) &&
      !MLX5_CAP_GEN(dev->mdev, disable_local_lb_mc)))
  return err;

 return mlx5_ib_enable_lb(dev, truefalse);
}

static void mlx5_ib_dealloc_transport_domain(struct mlx5_ib_dev *dev, u32 tdn,
          u16 uid)
{
 if (!MLX5_CAP_GEN(dev->mdev, log_max_transport_domain))
  return;

 mlx5_cmd_dealloc_transport_domain(dev->mdev, tdn, uid);

 if ((MLX5_CAP_GEN(dev->mdev, port_type) != MLX5_CAP_PORT_TYPE_ETH) ||
     (!MLX5_CAP_GEN(dev->mdev, disable_local_lb_uc) &&
      !MLX5_CAP_GEN(dev->mdev, disable_local_lb_mc)))
  return;

 mlx5_ib_disable_lb(dev, truefalse);
}

static int set_ucontext_resp(struct ib_ucontext *uctx,
        struct mlx5_ib_alloc_ucontext_resp *resp)
{
 struct ib_device *ibdev = uctx->device;
 struct mlx5_ib_dev *dev = to_mdev(ibdev);
 struct mlx5_ib_ucontext *context = to_mucontext(uctx);
 struct mlx5_bfreg_info *bfregi = &context->bfregi;

 if (MLX5_CAP_GEN(dev->mdev, dump_fill_mkey)) {
  resp->dump_fill_mkey = dev->mkeys.dump_fill_mkey;
  resp->comp_mask |=
   MLX5_IB_ALLOC_UCONTEXT_RESP_MASK_DUMP_FILL_MKEY;
 }

 resp->qp_tab_size = 1 << MLX5_CAP_GEN(dev->mdev, log_max_qp);
 if (mlx5_wc_support_get(dev->mdev))
  resp->bf_reg_size = 1 << MLX5_CAP_GEN(dev->mdev,
            log_bf_reg_size);
 resp->cache_line_size = cache_line_size();
 resp->max_sq_desc_sz = MLX5_CAP_GEN(dev->mdev, max_wqe_sz_sq);
 resp->max_rq_desc_sz = MLX5_CAP_GEN(dev->mdev, max_wqe_sz_rq);
 resp->max_send_wqebb = 1 << MLX5_CAP_GEN(dev->mdev, log_max_qp_sz);
 resp->max_recv_wr = 1 << MLX5_CAP_GEN(dev->mdev, log_max_qp_sz);
 resp->max_srq_recv_wr = 1 << MLX5_CAP_GEN(dev->mdev, log_max_srq_sz);
 resp->cqe_version = context->cqe_version;
 resp->log_uar_size = MLX5_CAP_GEN(dev->mdev, uar_4k) ?
    MLX5_ADAPTER_PAGE_SHIFT : PAGE_SHIFT;
 resp->num_uars_per_page = MLX5_CAP_GEN(dev->mdev, uar_4k) ?
     MLX5_CAP_GEN(dev->mdev,
           num_of_uars_per_page) : 1;
 resp->tot_bfregs = bfregi->lib_uar_dyn ? 0 :
   bfregi->total_num_bfregs - bfregi->num_dyn_bfregs;
 resp->num_ports = dev->num_ports;
 resp->cmds_supp_uhw |= MLX5_USER_CMDS_SUPP_UHW_QUERY_DEVICE |
          MLX5_USER_CMDS_SUPP_UHW_CREATE_AH;

 if (mlx5_ib_port_link_layer(ibdev, 1) == IB_LINK_LAYER_ETHERNET) {
  mlx5_query_min_inline(dev->mdev, &resp->eth_min_inline);
  resp->eth_min_inline++;
 }

 if (dev->mdev->clock_info)
  resp->clock_info_versions = BIT(MLX5_IB_CLOCK_INFO_V1);

 /*
 * We don't want to expose information from the PCI bar that is located
 * after 4096 bytes, so if the arch only supports larger pages, let's
 * pretend we don't support reading the HCA's core clock. This is also
 * forced by mmap function.
 */

 if (PAGE_SIZE <= 4096) {
  resp->comp_mask |=
   MLX5_IB_ALLOC_UCONTEXT_RESP_MASK_CORE_CLOCK_OFFSET;
  resp->hca_core_clock_offset =
   offsetof(struct mlx5_init_seg,
     internal_timer_h) % PAGE_SIZE;
 }

 if (MLX5_CAP_GEN(dev->mdev, ece_support))
  resp->comp_mask |= MLX5_IB_ALLOC_UCONTEXT_RESP_MASK_ECE;

 if (rt_supported(MLX5_CAP_GEN(dev->mdev, sq_ts_format)) &&
     rt_supported(MLX5_CAP_GEN(dev->mdev, rq_ts_format)) &&
     rt_supported(MLX5_CAP_ROCE(dev->mdev, qp_ts_format)))
  resp->comp_mask |=
   MLX5_IB_ALLOC_UCONTEXT_RESP_MASK_REAL_TIME_TS;

 resp->num_dyn_bfregs = bfregi->num_dyn_bfregs;

 if (MLX5_CAP_GEN(dev->mdev, drain_sigerr))
  resp->comp_mask |= MLX5_IB_ALLOC_UCONTEXT_RESP_MASK_SQD2RTS;

 resp->comp_mask |=
  MLX5_IB_ALLOC_UCONTEXT_RESP_MASK_MKEY_UPDATE_TAG;

 return 0;
}

static bool uctx_rdma_ctrl_is_enabled(u64 enabled_caps)
{
 return UCAP_ENABLED(enabled_caps, RDMA_UCAP_MLX5_CTRL_LOCAL) ||
        UCAP_ENABLED(enabled_caps, RDMA_UCAP_MLX5_CTRL_OTHER_VHCA);
}

static int mlx5_ib_alloc_ucontext(struct ib_ucontext *uctx,
      struct ib_udata *udata)
{
 struct ib_device *ibdev = uctx->device;
 struct mlx5_ib_dev *dev = to_mdev(ibdev);
 struct mlx5_ib_alloc_ucontext_req_v2 req = {};
 struct mlx5_ib_alloc_ucontext_resp resp = {};
 struct mlx5_ib_ucontext *context = to_mucontext(uctx);
 struct mlx5_bfreg_info *bfregi;
 int ver;
 int err;
 size_t min_req_v2 = offsetof(struct mlx5_ib_alloc_ucontext_req_v2,
         max_cqe_version);
 bool lib_uar_4k;
 bool lib_uar_dyn;

 if (!dev->ib_active)
  return -EAGAIN;

 if (udata->inlen == sizeof(struct mlx5_ib_alloc_ucontext_req))
  ver = 0;
 else if (udata->inlen >= min_req_v2)
  ver = 2;
 else
  return -EINVAL;

 err = ib_copy_from_udata(&req, udata, min(udata->inlen, sizeof(req)));
 if (err)
  return err;

 if (req.flags & ~MLX5_IB_ALLOC_UCTX_DEVX)
  return -EOPNOTSUPP;

 if (req.comp_mask || req.reserved0 || req.reserved1 || req.reserved2)
  return -EOPNOTSUPP;

 req.total_num_bfregs = ALIGN(req.total_num_bfregs,
        MLX5_NON_FP_BFREGS_PER_UAR);
 if (req.num_low_latency_bfregs > req.total_num_bfregs - 1)
  return -EINVAL;

 if (req.flags & MLX5_IB_ALLOC_UCTX_DEVX) {
  err = mlx5_ib_devx_create(dev, true, uctx->enabled_caps);
  if (err < 0)
   goto out_ctx;
  context->devx_uid = err;

  if (uctx_rdma_ctrl_is_enabled(uctx->enabled_caps)) {
   err = mlx5_cmd_add_privileged_uid(dev->mdev,
         context->devx_uid);
   if (err)
    goto out_devx;
  }
 }

 lib_uar_4k = req.lib_caps & MLX5_LIB_CAP_4K_UAR;
 lib_uar_dyn = req.lib_caps & MLX5_LIB_CAP_DYN_UAR;
 bfregi = &context->bfregi;

 if (lib_uar_dyn) {
  bfregi->lib_uar_dyn = lib_uar_dyn;
  goto uar_done;
 }

 /* updates req->total_num_bfregs */
 err = calc_total_bfregs(dev, lib_uar_4k, &req, bfregi);
 if (err)
  goto out_ucap;

 mutex_init(&bfregi->lock);
 bfregi->lib_uar_4k = lib_uar_4k;
 bfregi->count = kcalloc(bfregi->total_num_bfregs, sizeof(*bfregi->count),
    GFP_KERNEL);
 if (!bfregi->count) {
  err = -ENOMEM;
  goto out_ucap;
 }

 bfregi->sys_pages = kcalloc(bfregi->num_sys_pages,
        sizeof(*bfregi->sys_pages),
        GFP_KERNEL);
 if (!bfregi->sys_pages) {
  err = -ENOMEM;
  goto out_count;
 }

 err = allocate_uars(dev, context);
 if (err)
  goto out_sys_pages;

uar_done:
 err = mlx5_ib_alloc_transport_domain(dev, &context->tdn,
          context->devx_uid);
 if (err)
  goto out_uars;

 INIT_LIST_HEAD(&context->db_page_list);
 mutex_init(&context->db_page_mutex);

 context->cqe_version = min_t(__u8,
     (__u8)MLX5_CAP_GEN(dev->mdev, cqe_version),
     req.max_cqe_version);

 err = set_ucontext_resp(uctx, &resp);
 if (err)
  goto out_mdev;

 resp.response_length = min(udata->outlen, sizeof(resp));
 err = ib_copy_to_udata(udata, &resp, resp.response_length);
 if (err)
  goto out_mdev;

 bfregi->ver = ver;
 bfregi->num_low_latency_bfregs = req.num_low_latency_bfregs;
 context->lib_caps = req.lib_caps;
 print_lib_caps(dev, context->lib_caps);

 if (mlx5_ib_lag_should_assign_affinity(dev)) {
  u32 port = mlx5_core_native_port_num(dev->mdev) - 1;

  atomic_set(&context->tx_port_affinity,
      atomic_add_return(
       1, &dev->port[port].roce.tx_port_affinity));
 }

 return 0;

out_mdev:
 mlx5_ib_dealloc_transport_domain(dev, context->tdn, context->devx_uid);

out_uars:
 deallocate_uars(dev, context);

out_sys_pages:
 kfree(bfregi->sys_pages);

out_count:
 kfree(bfregi->count);

out_ucap:
 if (req.flags & MLX5_IB_ALLOC_UCTX_DEVX &&
     uctx_rdma_ctrl_is_enabled(uctx->enabled_caps))
  mlx5_cmd_remove_privileged_uid(dev->mdev, context->devx_uid);

out_devx:
 if (req.flags & MLX5_IB_ALLOC_UCTX_DEVX)
  mlx5_ib_devx_destroy(dev, context->devx_uid);

out_ctx:
 return err;
}

static int mlx5_ib_query_ucontext(struct ib_ucontext *ibcontext,
      struct uverbs_attr_bundle *attrs)
{
 struct mlx5_ib_alloc_ucontext_resp uctx_resp = {};
 int ret;

 ret = set_ucontext_resp(ibcontext, &uctx_resp);
 if (ret)
  return ret;

 uctx_resp.response_length =
  min_t(size_t,
        uverbs_attr_get_len(attrs,
    MLX5_IB_ATTR_QUERY_CONTEXT_RESP_UCTX),
        sizeof(uctx_resp));

 ret = uverbs_copy_to_struct_or_zero(attrs,
     MLX5_IB_ATTR_QUERY_CONTEXT_RESP_UCTX,
     &uctx_resp,
     sizeof(uctx_resp));
 return ret;
}

static void mlx5_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
{
 struct mlx5_ib_ucontext *context = to_mucontext(ibcontext);
 struct mlx5_ib_dev *dev = to_mdev(ibcontext->device);
 struct mlx5_bfreg_info *bfregi;

 bfregi = &context->bfregi;
 mlx5_ib_dealloc_transport_domain(dev, context->tdn, context->devx_uid);

 deallocate_uars(dev, context);
 kfree(bfregi->sys_pages);
 kfree(bfregi->count);

 if (context->devx_uid) {
  if (uctx_rdma_ctrl_is_enabled(ibcontext->enabled_caps))
   mlx5_cmd_remove_privileged_uid(dev->mdev,
             context->devx_uid);
  mlx5_ib_devx_destroy(dev, context->devx_uid);
 }
}

static phys_addr_t uar_index2pfn(struct mlx5_ib_dev *dev,
     int uar_idx)
{
 int fw_uars_per_page;

 fw_uars_per_page = MLX5_CAP_GEN(dev->mdev, uar_4k) ? MLX5_UARS_IN_PAGE : 1;

 return (dev->mdev->bar_addr >> PAGE_SHIFT) + uar_idx / fw_uars_per_page;
}

static u64 uar_index2paddress(struct mlx5_ib_dev *dev,
     int uar_idx)
{
 unsigned int fw_uars_per_page;

 fw_uars_per_page = MLX5_CAP_GEN(dev->mdev, uar_4k) ?
    MLX5_UARS_IN_PAGE : 1;

 return (dev->mdev->bar_addr + (uar_idx / fw_uars_per_page) * PAGE_SIZE);
}

static int get_command(unsigned long offset)
{
 return (offset >> MLX5_IB_MMAP_CMD_SHIFT) & MLX5_IB_MMAP_CMD_MASK;
}

static int get_arg(unsigned long offset)
{
 return offset & ((1 << MLX5_IB_MMAP_CMD_SHIFT) - 1);
}

static int get_index(unsigned long offset)
{
 return get_arg(offset);
}

/* Index resides in an extra byte to enable larger values than 255 */
static int get_extended_index(unsigned long offset)
{
 return get_arg(offset) | ((offset >> 16) & 0xff) << 8;
}


static void mlx5_ib_disassociate_ucontext(struct ib_ucontext *ibcontext)
{
}

static inline char *mmap_cmd2str(enum mlx5_ib_mmap_cmd cmd)
{
 switch (cmd) {
 case MLX5_IB_MMAP_WC_PAGE:
  return "WC";
 case MLX5_IB_MMAP_REGULAR_PAGE:
  return "best effort WC";
 case MLX5_IB_MMAP_NC_PAGE:
  return "NC";
 case MLX5_IB_MMAP_DEVICE_MEM:
  return "Device Memory";
 default:
  return "Unknown";
 }
}

static int mlx5_ib_mmap_clock_info_page(struct mlx5_ib_dev *dev,
     struct vm_area_struct *vma,
     struct mlx5_ib_ucontext *context)
{
 if ((vma->vm_end - vma->vm_start != PAGE_SIZE) ||
     !(vma->vm_flags & VM_SHARED))
  return -EINVAL;

 if (get_index(vma->vm_pgoff) != MLX5_IB_CLOCK_INFO_V1)
  return -EOPNOTSUPP;

 if (vma->vm_flags & (VM_WRITE | VM_EXEC))
  return -EPERM;
 vm_flags_clear(vma, VM_MAYWRITE);

 if (!dev->mdev->clock_info)
  return -EOPNOTSUPP;

 return vm_insert_page(vma, vma->vm_start,
         virt_to_page(dev->mdev->clock_info));
}

static void mlx5_ib_mmap_free(struct rdma_user_mmap_entry *entry)
{
 struct mlx5_user_mmap_entry *mentry = to_mmmap(entry);
 struct mlx5_ib_dev *dev = to_mdev(entry->ucontext->device);
 struct mlx5_var_table *var_table = &dev->var_table;
 struct mlx5_ib_ucontext *context = to_mucontext(entry->ucontext);

 switch (mentry->mmap_flag) {
 case MLX5_IB_MMAP_TYPE_MEMIC:
 case MLX5_IB_MMAP_TYPE_MEMIC_OP:
  mlx5_ib_dm_mmap_free(dev, mentry);
  break;
 case MLX5_IB_MMAP_TYPE_VAR:
  mutex_lock(&var_table->bitmap_lock);
  clear_bit(mentry->page_idx, var_table->bitmap);
  mutex_unlock(&var_table->bitmap_lock);
  kfree(mentry);
  break;
 case MLX5_IB_MMAP_TYPE_UAR_WC:
 case MLX5_IB_MMAP_TYPE_UAR_NC:
  mlx5_cmd_uar_dealloc(dev->mdev, mentry->page_idx,
         context->devx_uid);
  kfree(mentry);
  break;
 default:
  WARN_ON(true);
 }
}

static int uar_mmap(struct mlx5_ib_dev *dev, enum mlx5_ib_mmap_cmd cmd,
      struct vm_area_struct *vma,
      struct mlx5_ib_ucontext *context)
{
 struct mlx5_bfreg_info *bfregi = &context->bfregi;
 int err;
 unsigned long idx;
 phys_addr_t pfn;
 pgprot_t prot;
 u32 bfreg_dyn_idx = 0;
 u32 uar_index;
 int dyn_uar = (cmd == MLX5_IB_MMAP_ALLOC_WC);
 int max_valid_idx = dyn_uar ? bfregi->num_sys_pages :
    bfregi->num_static_sys_pages;

 if (bfregi->lib_uar_dyn)
  return -EINVAL;

 if (vma->vm_end - vma->vm_start != PAGE_SIZE)
  return -EINVAL;

 if (dyn_uar)
  idx = get_extended_index(vma->vm_pgoff) + bfregi->num_static_sys_pages;
 else
  idx = get_index(vma->vm_pgoff);

 if (idx >= max_valid_idx) {
  mlx5_ib_warn(dev, "invalid uar index %lu, max=%d\n",
        idx, max_valid_idx);
  return -EINVAL;
 }

 switch (cmd) {
 case MLX5_IB_MMAP_WC_PAGE:
 case MLX5_IB_MMAP_ALLOC_WC:
 case MLX5_IB_MMAP_REGULAR_PAGE:
  /* For MLX5_IB_MMAP_REGULAR_PAGE do the best effort to get WC */
  prot = pgprot_writecombine(vma->vm_page_prot);
  break;
 case MLX5_IB_MMAP_NC_PAGE:
  prot = pgprot_noncached(vma->vm_page_prot);
  break;
 default:
  return -EINVAL;
 }

 if (dyn_uar) {
  int uars_per_page;

  uars_per_page = get_uars_per_sys_page(dev, bfregi->lib_uar_4k);
  bfreg_dyn_idx = idx * (uars_per_page * MLX5_NON_FP_BFREGS_PER_UAR);
  if (bfreg_dyn_idx >= bfregi->total_num_bfregs) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=99 H=94 G=96

¤ Dauer der Verarbeitung: 0.12 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.