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

Quelle  mad.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
 * Copyright(c) 2015-2018 Intel Corporation.
 */


#include <linux/net.h>
#include <rdma/opa_addr.h>
#define OPA_NUM_PKEY_BLOCKS_PER_SMP (OPA_SMP_DR_DATA_SIZE \
   / (OPA_PARTITION_TABLE_BLK_SIZE * sizeof(u16)))

#include "hfi.h"
#include "mad.h"
#include "trace.h"
#include "qp.h"
#include "vnic.h"

/* the reset value from the FM is supposed to be 0xffff, handle both */
#define OPA_LINK_WIDTH_RESET_OLD 0x0fff
#define OPA_LINK_WIDTH_RESET 0xffff

struct trap_node {
 struct list_head list;
 struct opa_mad_notice_attr data;
 __be64 tid;
 int len;
 u32 retry;
 u8 in_use;
 u8 repress;
};

static int smp_length_check(u32 data_size, u32 request_len)
{
 if (unlikely(request_len < data_size))
  return -EINVAL;

 return 0;
}

static int reply(struct ib_mad_hdr *smp)
{
 /*
 * The verbs framework will handle the directed/LID route
 * packet changes.
 */

 smp->method = IB_MGMT_METHOD_GET_RESP;
 if (smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
  smp->status |= IB_SMP_DIRECTION;
 return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;
}

static inline void clear_opa_smp_data(struct opa_smp *smp)
{
 void *data = opa_get_smp_data(smp);
 size_t size = opa_get_smp_data_size(smp);

 memset(data, 0, size);
}

static u16 hfi1_lookup_pkey_value(struct hfi1_ibport *ibp, int pkey_idx)
{
 struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);

 if (pkey_idx < ARRAY_SIZE(ppd->pkeys))
  return ppd->pkeys[pkey_idx];

 return 0;
}

void hfi1_event_pkey_change(struct hfi1_devdata *dd, u32 port)
{
 struct ib_event event;

 event.event = IB_EVENT_PKEY_CHANGE;
 event.device = &dd->verbs_dev.rdi.ibdev;
 event.element.port_num = port;
 ib_dispatch_event(&event);
}

/*
 * If the port is down, clean up all pending traps.  We need to be careful
 * with the given trap, because it may be queued.
 */

static void cleanup_traps(struct hfi1_ibport *ibp, struct trap_node *trap)
{
 struct trap_node *node, *q;
 unsigned long flags;
 struct list_head trap_list;
 int i;

 for (i = 0; i < RVT_MAX_TRAP_LISTS; i++) {
  spin_lock_irqsave(&ibp->rvp.lock, flags);
  list_replace_init(&ibp->rvp.trap_lists[i].list, &trap_list);
  ibp->rvp.trap_lists[i].list_len = 0;
  spin_unlock_irqrestore(&ibp->rvp.lock, flags);

  /*
 * Remove all items from the list, freeing all the non-given
 * traps.
 */

  list_for_each_entry_safe(node, q, &trap_list, list) {
   list_del(&node->list);
   if (node != trap)
    kfree(node);
  }
 }

 /*
 * If this wasn't on one of the lists it would not be freed.  If it
 * was on the list, it is now safe to free.
 */

 kfree(trap);
}

static struct trap_node *check_and_add_trap(struct hfi1_ibport *ibp,
         struct trap_node *trap)
{
 struct trap_node *node;
 struct trap_list *trap_list;
 unsigned long flags;
 unsigned long timeout;
 int found = 0;
 unsigned int queue_id;
 static int trap_count;

 queue_id = trap->data.generic_type & 0x0F;
 if (queue_id >= RVT_MAX_TRAP_LISTS) {
  trap_count++;
  pr_err_ratelimited("hfi1: Invalid trap 0x%0x dropped. Total dropped: %d\n",
       trap->data.generic_type, trap_count);
  kfree(trap);
  return NULL;
 }

 /*
 * Since the retry (handle timeout) does not remove a trap request
 * from the list, all we have to do is compare the node.
 */

 spin_lock_irqsave(&ibp->rvp.lock, flags);
 trap_list = &ibp->rvp.trap_lists[queue_id];

 list_for_each_entry(node, &trap_list->list, list) {
  if (node == trap) {
   node->retry++;
   found = 1;
   break;
  }
 }

 /* If it is not on the list, add it, limited to RVT-MAX_TRAP_LEN. */
 if (!found) {
  if (trap_list->list_len < RVT_MAX_TRAP_LEN) {
   trap_list->list_len++;
   list_add_tail(&trap->list, &trap_list->list);
  } else {
   pr_warn_ratelimited("hfi1: Maximum trap limit reached for 0x%0x traps\n",
         trap->data.generic_type);
   kfree(trap);
  }
 }

 /*
 * Next check to see if there is a timer pending.  If not, set it up
 * and get the first trap from the list.
 */

 node = NULL;
 if (!timer_pending(&ibp->rvp.trap_timer)) {
  /*
 * o14-2
 * If the time out is set we have to wait until it expires
 * before the trap can be sent.
 * This should be > RVT_TRAP_TIMEOUT
 */

  timeout = (RVT_TRAP_TIMEOUT *
      (1UL << ibp->rvp.subnet_timeout)) / 1000;
  mod_timer(&ibp->rvp.trap_timer,
     jiffies + usecs_to_jiffies(timeout));
  node = list_first_entry(&trap_list->list, struct trap_node,
     list);
  node->in_use = 1;
 }
 spin_unlock_irqrestore(&ibp->rvp.lock, flags);

 return node;
}

static void subn_handle_opa_trap_repress(struct hfi1_ibport *ibp,
      struct opa_smp *smp)
{
 struct trap_list *trap_list;
 struct trap_node *trap;
 unsigned long flags;
 int i;

 if (smp->attr_id != IB_SMP_ATTR_NOTICE)
  return;

 spin_lock_irqsave(&ibp->rvp.lock, flags);
 for (i = 0; i < RVT_MAX_TRAP_LISTS; i++) {
  trap_list = &ibp->rvp.trap_lists[i];
  trap = list_first_entry_or_null(&trap_list->list,
      struct trap_node, list);
  if (trap && trap->tid == smp->tid) {
   if (trap->in_use) {
    trap->repress = 1;
   } else {
    trap_list->list_len--;
    list_del(&trap->list);
    kfree(trap);
   }
   break;
  }
 }
 spin_unlock_irqrestore(&ibp->rvp.lock, flags);
}

static void hfi1_update_sm_ah_attr(struct hfi1_ibport *ibp,
       struct rdma_ah_attr *attr, u32 dlid)
{
 rdma_ah_set_dlid(attr, dlid);
 rdma_ah_set_port_num(attr, ppd_from_ibp(ibp)->port);
 if (dlid >= be16_to_cpu(IB_MULTICAST_LID_BASE)) {
  struct ib_global_route *grh = rdma_ah_retrieve_grh(attr);

  rdma_ah_set_ah_flags(attr, IB_AH_GRH);
  grh->sgid_index = 0;
  grh->hop_limit = 1;
  grh->dgid.global.subnet_prefix =
   ibp->rvp.gid_prefix;
  grh->dgid.global.interface_id = OPA_MAKE_ID(dlid);
 }
}

static int hfi1_modify_qp0_ah(struct hfi1_ibport *ibp,
         struct rvt_ah *ah, u32 dlid)
{
 struct rdma_ah_attr attr;
 struct rvt_qp *qp0;
 int ret = -EINVAL;

 memset(&attr, 0, sizeof(attr));
 attr.type = ah->ibah.type;
 hfi1_update_sm_ah_attr(ibp, &attr, dlid);
 rcu_read_lock();
 qp0 = rcu_dereference(ibp->rvp.qp[0]);
 if (qp0)
  ret = rdma_modify_ah(&ah->ibah, &attr);
 rcu_read_unlock();
 return ret;
}

static struct ib_ah *hfi1_create_qp0_ah(struct hfi1_ibport *ibp, u32 dlid)
{
 struct rdma_ah_attr attr;
 struct ib_ah *ah = ERR_PTR(-EINVAL);
 struct rvt_qp *qp0;
 struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
 struct hfi1_devdata *dd = dd_from_ppd(ppd);
 u32 port_num = ppd->port;

 memset(&attr, 0, sizeof(attr));
 attr.type = rdma_ah_find_type(&dd->verbs_dev.rdi.ibdev, port_num);
 hfi1_update_sm_ah_attr(ibp, &attr, dlid);
 rcu_read_lock();
 qp0 = rcu_dereference(ibp->rvp.qp[0]);
 if (qp0)
  ah = rdma_create_ah(qp0->ibqp.pd, &attr, 0);
 rcu_read_unlock();
 return ah;
}

static void send_trap(struct hfi1_ibport *ibp, struct trap_node *trap)
{
 struct ib_mad_send_buf *send_buf;
 struct ib_mad_agent *agent;
 struct opa_smp *smp;
 unsigned long flags;
 int pkey_idx;
 u32 qpn = ppd_from_ibp(ibp)->sm_trap_qp;

 agent = ibp->rvp.send_agent;
 if (!agent) {
  cleanup_traps(ibp, trap);
  return;
 }

 /* o14-3.2.1 */
 if (driver_lstate(ppd_from_ibp(ibp)) != IB_PORT_ACTIVE) {
  cleanup_traps(ibp, trap);
  return;
 }

 /* Add the trap to the list if necessary and see if we can send it */
 trap = check_and_add_trap(ibp, trap);
 if (!trap)
  return;

 pkey_idx = hfi1_lookup_pkey_idx(ibp, LIM_MGMT_P_KEY);
 if (pkey_idx < 0) {
  pr_warn("%s: failed to find limited mgmt pkey, defaulting 0x%x\n",
   __func__, hfi1_get_pkey(ibp, 1));
  pkey_idx = 1;
 }

 send_buf = ib_create_send_mad(agent, qpn, pkey_idx, 0,
          IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
          GFP_ATOMIC, IB_MGMT_BASE_VERSION);
 if (IS_ERR(send_buf))
  return;

 smp = send_buf->mad;
 smp->base_version = OPA_MGMT_BASE_VERSION;
 smp->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
 smp->class_version = OPA_SM_CLASS_VERSION;
 smp->method = IB_MGMT_METHOD_TRAP;

 /* Only update the transaction ID for new traps (o13-5). */
 if (trap->tid == 0) {
  ibp->rvp.tid++;
  /* make sure that tid != 0 */
  if (ibp->rvp.tid == 0)
   ibp->rvp.tid++;
  trap->tid = cpu_to_be64(ibp->rvp.tid);
 }
 smp->tid = trap->tid;

 smp->attr_id = IB_SMP_ATTR_NOTICE;
 /* o14-1: smp->mkey = 0; */

 memcpy(smp->route.lid.data, &trap->data, trap->len);

 spin_lock_irqsave(&ibp->rvp.lock, flags);
 if (!ibp->rvp.sm_ah) {
  if (ibp->rvp.sm_lid != be16_to_cpu(IB_LID_PERMISSIVE)) {
   struct ib_ah *ah;

   ah = hfi1_create_qp0_ah(ibp, ibp->rvp.sm_lid);
   if (IS_ERR(ah)) {
    spin_unlock_irqrestore(&ibp->rvp.lock, flags);
    return;
   }
   send_buf->ah = ah;
   ibp->rvp.sm_ah = ibah_to_rvtah(ah);
  } else {
   spin_unlock_irqrestore(&ibp->rvp.lock, flags);
   return;
  }
 } else {
  send_buf->ah = &ibp->rvp.sm_ah->ibah;
 }

 /*
 * If the trap was repressed while things were getting set up, don't
 * bother sending it. This could happen for a retry.
 */

 if (trap->repress) {
  list_del(&trap->list);
  spin_unlock_irqrestore(&ibp->rvp.lock, flags);
  kfree(trap);
  ib_free_send_mad(send_buf);
  return;
 }

 trap->in_use = 0;
 spin_unlock_irqrestore(&ibp->rvp.lock, flags);

 if (ib_post_send_mad(send_buf, NULL))
  ib_free_send_mad(send_buf);
}

void hfi1_handle_trap_timer(struct timer_list *t)
{
 struct hfi1_ibport *ibp = timer_container_of(ibp, t, rvp.trap_timer);
 struct trap_node *trap = NULL;
 unsigned long flags;
 int i;

 /* Find the trap with the highest priority */
 spin_lock_irqsave(&ibp->rvp.lock, flags);
 for (i = 0; !trap && i < RVT_MAX_TRAP_LISTS; i++) {
  trap = list_first_entry_or_null(&ibp->rvp.trap_lists[i].list,
      struct trap_node, list);
 }
 spin_unlock_irqrestore(&ibp->rvp.lock, flags);

 if (trap)
  send_trap(ibp, trap);
}

static struct trap_node *create_trap_node(u8 type, __be16 trap_num, u32 lid)
{
 struct trap_node *trap;

 trap = kzalloc(sizeof(*trap), GFP_ATOMIC);
 if (!trap)
  return NULL;

 INIT_LIST_HEAD(&trap->list);
 trap->data.generic_type = type;
 trap->data.prod_type_lsb = IB_NOTICE_PROD_CA;
 trap->data.trap_num = trap_num;
 trap->data.issuer_lid = cpu_to_be32(lid);

 return trap;
}

/*
 * Send a bad P_Key trap (ch. 14.3.8).
 */

void hfi1_bad_pkey(struct hfi1_ibport *ibp, u32 key, u32 sl,
     u32 qp1, u32 qp2, u32 lid1, u32 lid2)
{
 struct trap_node *trap;
 u32 lid = ppd_from_ibp(ibp)->lid;

 ibp->rvp.n_pkt_drops++;
 ibp->rvp.pkey_violations++;

 trap = create_trap_node(IB_NOTICE_TYPE_SECURITY, OPA_TRAP_BAD_P_KEY,
    lid);
 if (!trap)
  return;

 /* Send violation trap */
 trap->data.ntc_257_258.lid1 = cpu_to_be32(lid1);
 trap->data.ntc_257_258.lid2 = cpu_to_be32(lid2);
 trap->data.ntc_257_258.key = cpu_to_be32(key);
 trap->data.ntc_257_258.sl = sl << 3;
 trap->data.ntc_257_258.qp1 = cpu_to_be32(qp1);
 trap->data.ntc_257_258.qp2 = cpu_to_be32(qp2);

 trap->len = sizeof(trap->data);
 send_trap(ibp, trap);
}

/*
 * Send a bad M_Key trap (ch. 14.3.9).
 */

static void bad_mkey(struct hfi1_ibport *ibp, struct ib_mad_hdr *mad,
       __be64 mkey, __be32 dr_slid, u8 return_path[], u8 hop_cnt)
{
 struct trap_node *trap;
 u32 lid = ppd_from_ibp(ibp)->lid;

 trap = create_trap_node(IB_NOTICE_TYPE_SECURITY, OPA_TRAP_BAD_M_KEY,
    lid);
 if (!trap)
  return;

 /* Send violation trap */
 trap->data.ntc_256.lid = trap->data.issuer_lid;
 trap->data.ntc_256.method = mad->method;
 trap->data.ntc_256.attr_id = mad->attr_id;
 trap->data.ntc_256.attr_mod = mad->attr_mod;
 trap->data.ntc_256.mkey = mkey;
 if (mad->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
  trap->data.ntc_256.dr_slid = dr_slid;
  trap->data.ntc_256.dr_trunc_hop = IB_NOTICE_TRAP_DR_NOTICE;
  if (hop_cnt > ARRAY_SIZE(trap->data.ntc_256.dr_rtn_path)) {
   trap->data.ntc_256.dr_trunc_hop |=
    IB_NOTICE_TRAP_DR_TRUNC;
   hop_cnt = ARRAY_SIZE(trap->data.ntc_256.dr_rtn_path);
  }
  trap->data.ntc_256.dr_trunc_hop |= hop_cnt;
  memcpy(trap->data.ntc_256.dr_rtn_path, return_path,
         hop_cnt);
 }

 trap->len = sizeof(trap->data);

 send_trap(ibp, trap);
}

/*
 * Send a Port Capability Mask Changed trap (ch. 14.3.11).
 */

void hfi1_cap_mask_chg(struct rvt_dev_info *rdi, u32 port_num)
{
 struct trap_node *trap;
 struct hfi1_ibdev *verbs_dev = dev_from_rdi(rdi);
 struct hfi1_devdata *dd = dd_from_dev(verbs_dev);
 struct hfi1_ibport *ibp = &dd->pport[port_num - 1].ibport_data;
 u32 lid = ppd_from_ibp(ibp)->lid;

 trap = create_trap_node(IB_NOTICE_TYPE_INFO,
    OPA_TRAP_CHANGE_CAPABILITY,
    lid);
 if (!trap)
  return;

 trap->data.ntc_144.lid = trap->data.issuer_lid;
 trap->data.ntc_144.new_cap_mask = cpu_to_be32(ibp->rvp.port_cap_flags);
 trap->data.ntc_144.cap_mask3 = cpu_to_be16(ibp->rvp.port_cap3_flags);

 trap->len = sizeof(trap->data);
 send_trap(ibp, trap);
}

/*
 * Send a System Image GUID Changed trap (ch. 14.3.12).
 */

void hfi1_sys_guid_chg(struct hfi1_ibport *ibp)
{
 struct trap_node *trap;
 u32 lid = ppd_from_ibp(ibp)->lid;

 trap = create_trap_node(IB_NOTICE_TYPE_INFO, OPA_TRAP_CHANGE_SYSGUID,
    lid);
 if (!trap)
  return;

 trap->data.ntc_145.new_sys_guid = ib_hfi1_sys_image_guid;
 trap->data.ntc_145.lid = trap->data.issuer_lid;

 trap->len = sizeof(trap->data);
 send_trap(ibp, trap);
}

/*
 * Send a Node Description Changed trap (ch. 14.3.13).
 */

void hfi1_node_desc_chg(struct hfi1_ibport *ibp)
{
 struct trap_node *trap;
 u32 lid = ppd_from_ibp(ibp)->lid;

 trap = create_trap_node(IB_NOTICE_TYPE_INFO,
    OPA_TRAP_CHANGE_CAPABILITY,
    lid);
 if (!trap)
  return;

 trap->data.ntc_144.lid = trap->data.issuer_lid;
 trap->data.ntc_144.change_flags =
  cpu_to_be16(OPA_NOTICE_TRAP_NODE_DESC_CHG);

 trap->len = sizeof(trap->data);
 send_trap(ibp, trap);
}

static int __subn_get_opa_nodedesc(struct opa_smp *smp, u32 am,
       u8 *data, struct ib_device *ibdev,
       u32 port, u32 *resp_len, u32 max_len)
{
 struct opa_node_description *nd;

 if (am || smp_length_check(sizeof(*nd), max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 nd = (struct opa_node_description *)data;

 memcpy(nd->data, ibdev->node_desc, sizeof(nd->data));

 if (resp_len)
  *resp_len += sizeof(*nd);

 return reply((struct ib_mad_hdr *)smp);
}

static int __subn_get_opa_nodeinfo(struct opa_smp *smp, u32 am, u8 *data,
       struct ib_device *ibdev, u32 port,
       u32 *resp_len, u32 max_len)
{
 struct opa_node_info *ni;
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 u32 pidx = port - 1; /* IB number port from 1, hw from 0 */

 ni = (struct opa_node_info *)data;

 /* GUID 0 is illegal */
 if (am || pidx >= dd->num_pports || ibdev->node_guid == 0 ||
     smp_length_check(sizeof(*ni), max_len) ||
     get_sguid(to_iport(ibdev, port), HFI1_PORT_GUID_INDEX) == 0) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 ni->port_guid = get_sguid(to_iport(ibdev, port), HFI1_PORT_GUID_INDEX);
 ni->base_version = OPA_MGMT_BASE_VERSION;
 ni->class_version = OPA_SM_CLASS_VERSION;
 ni->node_type = 1;     /* channel adapter */
 ni->num_ports = ibdev->phys_port_cnt;
 /* This is already in network order */
 ni->system_image_guid = ib_hfi1_sys_image_guid;
 ni->node_guid = ibdev->node_guid;
 ni->partition_cap = cpu_to_be16(hfi1_get_npkeys(dd));
 ni->device_id = cpu_to_be16(dd->pcidev->device);
 ni->revision = cpu_to_be32(dd->minrev);
 ni->local_port_num = port;
 ni->vendor_id[0] = dd->oui1;
 ni->vendor_id[1] = dd->oui2;
 ni->vendor_id[2] = dd->oui3;

 if (resp_len)
  *resp_len += sizeof(*ni);

 return reply((struct ib_mad_hdr *)smp);
}

static int subn_get_nodeinfo(struct ib_smp *smp, struct ib_device *ibdev,
        u32 port)
{
 struct ib_node_info *nip = (struct ib_node_info *)&smp->data;
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 u32 pidx = port - 1; /* IB number port from 1, hw from 0 */

 /* GUID 0 is illegal */
 if (smp->attr_mod || pidx >= dd->num_pports ||
     ibdev->node_guid == 0 ||
     get_sguid(to_iport(ibdev, port), HFI1_PORT_GUID_INDEX) == 0) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 nip->port_guid = get_sguid(to_iport(ibdev, port), HFI1_PORT_GUID_INDEX);
 nip->base_version = OPA_MGMT_BASE_VERSION;
 nip->class_version = OPA_SM_CLASS_VERSION;
 nip->node_type = 1;     /* channel adapter */
 nip->num_ports = ibdev->phys_port_cnt;
 /* This is already in network order */
 nip->sys_guid = ib_hfi1_sys_image_guid;
 nip->node_guid = ibdev->node_guid;
 nip->partition_cap = cpu_to_be16(hfi1_get_npkeys(dd));
 nip->device_id = cpu_to_be16(dd->pcidev->device);
 nip->revision = cpu_to_be32(dd->minrev);
 nip->local_port_num = port;
 nip->vendor_id[0] = dd->oui1;
 nip->vendor_id[1] = dd->oui2;
 nip->vendor_id[2] = dd->oui3;

 return reply((struct ib_mad_hdr *)smp);
}

static void set_link_width_enabled(struct hfi1_pportdata *ppd, u32 w)
{
 (void)hfi1_set_ib_cfg(ppd, HFI1_IB_CFG_LWID_ENB, w);
}

static void set_link_width_downgrade_enabled(struct hfi1_pportdata *ppd, u32 w)
{
 (void)hfi1_set_ib_cfg(ppd, HFI1_IB_CFG_LWID_DG_ENB, w);
}

static void set_link_speed_enabled(struct hfi1_pportdata *ppd, u32 s)
{
 (void)hfi1_set_ib_cfg(ppd, HFI1_IB_CFG_SPD_ENB, s);
}

static int check_mkey(struct hfi1_ibport *ibp, struct ib_mad_hdr *mad,
        int mad_flags, __be64 mkey, __be32 dr_slid,
        u8 return_path[], u8 hop_cnt)
{
 int valid_mkey = 0;
 int ret = 0;

 /* Is the mkey in the process of expiring? */
 if (ibp->rvp.mkey_lease_timeout &&
     time_after_eq(jiffies, ibp->rvp.mkey_lease_timeout)) {
  /* Clear timeout and mkey protection field. */
  ibp->rvp.mkey_lease_timeout = 0;
  ibp->rvp.mkeyprot = 0;
 }

 if ((mad_flags & IB_MAD_IGNORE_MKEY) ||  ibp->rvp.mkey == 0 ||
     ibp->rvp.mkey == mkey)
  valid_mkey = 1;

 /* Unset lease timeout on any valid Get/Set/TrapRepress */
 if (valid_mkey && ibp->rvp.mkey_lease_timeout &&
     (mad->method == IB_MGMT_METHOD_GET ||
      mad->method == IB_MGMT_METHOD_SET ||
      mad->method == IB_MGMT_METHOD_TRAP_REPRESS))
  ibp->rvp.mkey_lease_timeout = 0;

 if (!valid_mkey) {
  switch (mad->method) {
  case IB_MGMT_METHOD_GET:
   /* Bad mkey not a violation below level 2 */
   if (ibp->rvp.mkeyprot < 2)
    break;
   fallthrough;
  case IB_MGMT_METHOD_SET:
  case IB_MGMT_METHOD_TRAP_REPRESS:
   if (ibp->rvp.mkey_violations != 0xFFFF)
    ++ibp->rvp.mkey_violations;
   if (!ibp->rvp.mkey_lease_timeout &&
       ibp->rvp.mkey_lease_period)
    ibp->rvp.mkey_lease_timeout = jiffies +
     ibp->rvp.mkey_lease_period * HZ;
   /* Generate a trap notice. */
   bad_mkey(ibp, mad, mkey, dr_slid, return_path,
     hop_cnt);
   ret = 1;
  }
 }

 return ret;
}

/*
 * The SMA caches reads from LCB registers in case the LCB is unavailable.
 * (The LCB is unavailable in certain link states, for example.)
 */

struct lcb_datum {
 u32 off;
 u64 val;
};

static struct lcb_datum lcb_cache[] = {
 { DC_LCB_STS_ROUND_TRIP_LTP_CNT, 0 },
};

static int write_lcb_cache(u32 off, u64 val)
{
 int i;

 for (i = 0; i < ARRAY_SIZE(lcb_cache); i++) {
  if (lcb_cache[i].off == off) {
   lcb_cache[i].val = val;
   return 0;
  }
 }

 pr_warn("%s bad offset 0x%x\n", __func__, off);
 return -1;
}

static int read_lcb_cache(u32 off, u64 *val)
{
 int i;

 for (i = 0; i < ARRAY_SIZE(lcb_cache); i++) {
  if (lcb_cache[i].off == off) {
   *val = lcb_cache[i].val;
   return 0;
  }
 }

 pr_warn("%s bad offset 0x%x\n", __func__, off);
 return -1;
}

void read_ltp_rtt(struct hfi1_devdata *dd)
{
 u64 reg;

 if (read_lcb_csr(dd, DC_LCB_STS_ROUND_TRIP_LTP_CNT, ®))
  dd_dev_err(dd, "%s: unable to read LTP RTT\n", __func__);
 else
  write_lcb_cache(DC_LCB_STS_ROUND_TRIP_LTP_CNT, reg);
}

static int __subn_get_opa_portinfo(struct opa_smp *smp, u32 am, u8 *data,
       struct ib_device *ibdev, u32 port,
       u32 *resp_len, u32 max_len)
{
 int i;
 struct hfi1_devdata *dd;
 struct hfi1_pportdata *ppd;
 struct hfi1_ibport *ibp;
 struct opa_port_info *pi = (struct opa_port_info *)data;
 u8 mtu;
 u8 credit_rate;
 u8 is_beaconing_active;
 u32 state;
 u32 num_ports = OPA_AM_NPORT(am);
 u32 start_of_sm_config = OPA_AM_START_SM_CFG(am);
 u32 buffer_units;
 u64 tmp = 0;

 if (num_ports != 1 || smp_length_check(sizeof(*pi), max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 dd = dd_from_ibdev(ibdev);
 /* IB numbers ports from 1, hw from 0 */
 ppd = dd->pport + (port - 1);
 ibp = &ppd->ibport_data;

 if (ppd->vls_supported / 2 > ARRAY_SIZE(pi->neigh_mtu.pvlx_to_mtu) ||
     ppd->vls_supported > ARRAY_SIZE(dd->vld)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 pi->lid = cpu_to_be32(ppd->lid);

 /* Only return the mkey if the protection field allows it. */
 if (!(smp->method == IB_MGMT_METHOD_GET &&
       ibp->rvp.mkey != smp->mkey &&
       ibp->rvp.mkeyprot == 1))
  pi->mkey = ibp->rvp.mkey;

 pi->subnet_prefix = ibp->rvp.gid_prefix;
 pi->sm_lid = cpu_to_be32(ibp->rvp.sm_lid);
 pi->ib_cap_mask = cpu_to_be32(ibp->rvp.port_cap_flags);
 pi->mkey_lease_period = cpu_to_be16(ibp->rvp.mkey_lease_period);
 pi->sm_trap_qp = cpu_to_be32(ppd->sm_trap_qp);
 pi->sa_qp = cpu_to_be32(ppd->sa_qp);

 pi->link_width.enabled = cpu_to_be16(ppd->link_width_enabled);
 pi->link_width.supported = cpu_to_be16(ppd->link_width_supported);
 pi->link_width.active = cpu_to_be16(ppd->link_width_active);

 pi->link_width_downgrade.supported =
   cpu_to_be16(ppd->link_width_downgrade_supported);
 pi->link_width_downgrade.enabled =
   cpu_to_be16(ppd->link_width_downgrade_enabled);
 pi->link_width_downgrade.tx_active =
   cpu_to_be16(ppd->link_width_downgrade_tx_active);
 pi->link_width_downgrade.rx_active =
   cpu_to_be16(ppd->link_width_downgrade_rx_active);

 pi->link_speed.supported = cpu_to_be16(ppd->link_speed_supported);
 pi->link_speed.active = cpu_to_be16(ppd->link_speed_active);
 pi->link_speed.enabled = cpu_to_be16(ppd->link_speed_enabled);

 state = driver_lstate(ppd);

 if (start_of_sm_config && (state == IB_PORT_INIT))
  ppd->is_sm_config_started = 1;

 pi->port_phys_conf = (ppd->port_type & 0xf);

 pi->port_states.ledenable_offlinereason = ppd->neighbor_normal << 4;
 pi->port_states.ledenable_offlinereason |=
  ppd->is_sm_config_started << 5;
 /*
 * This pairs with the memory barrier in hfi1_start_led_override to
 * ensure that we read the correct state of LED beaconing represented
 * by led_override_timer_active
 */

 smp_rmb();
 is_beaconing_active = !!atomic_read(&ppd->led_override_timer_active);
 pi->port_states.ledenable_offlinereason |= is_beaconing_active << 6;
 pi->port_states.ledenable_offlinereason |=
  ppd->offline_disabled_reason;

 pi->port_states.portphysstate_portstate =
  (driver_pstate(ppd) << 4) | state;

 pi->mkeyprotect_lmc = (ibp->rvp.mkeyprot << 6) | ppd->lmc;

 memset(pi->neigh_mtu.pvlx_to_mtu, 0, sizeof(pi->neigh_mtu.pvlx_to_mtu));
 for (i = 0; i < ppd->vls_supported; i++) {
  mtu = mtu_to_enum(dd->vld[i].mtu, HFI1_DEFAULT_ACTIVE_MTU);
  if ((i % 2) == 0)
   pi->neigh_mtu.pvlx_to_mtu[i / 2] |= (mtu << 4);
  else
   pi->neigh_mtu.pvlx_to_mtu[i / 2] |= mtu;
 }
 /* don't forget VL 15 */
 mtu = mtu_to_enum(dd->vld[15].mtu, 2048);
 pi->neigh_mtu.pvlx_to_mtu[15 / 2] |= mtu;
 pi->smsl = ibp->rvp.sm_sl & OPA_PI_MASK_SMSL;
 pi->operational_vls = hfi1_get_ib_cfg(ppd, HFI1_IB_CFG_OP_VLS);
 pi->partenforce_filterraw |=
  (ppd->linkinit_reason & OPA_PI_MASK_LINKINIT_REASON);
 if (ppd->part_enforce & HFI1_PART_ENFORCE_IN)
  pi->partenforce_filterraw |= OPA_PI_MASK_PARTITION_ENFORCE_IN;
 if (ppd->part_enforce & HFI1_PART_ENFORCE_OUT)
  pi->partenforce_filterraw |= OPA_PI_MASK_PARTITION_ENFORCE_OUT;
 pi->mkey_violations = cpu_to_be16(ibp->rvp.mkey_violations);
 /* P_KeyViolations are counted by hardware. */
 pi->pkey_violations = cpu_to_be16(ibp->rvp.pkey_violations);
 pi->qkey_violations = cpu_to_be16(ibp->rvp.qkey_violations);

 pi->vl.cap = ppd->vls_supported;
 pi->vl.high_limit = cpu_to_be16(ibp->rvp.vl_high_limit);
 pi->vl.arb_high_cap = (u8)hfi1_get_ib_cfg(ppd, HFI1_IB_CFG_VL_HIGH_CAP);
 pi->vl.arb_low_cap = (u8)hfi1_get_ib_cfg(ppd, HFI1_IB_CFG_VL_LOW_CAP);

 pi->clientrereg_subnettimeout = ibp->rvp.subnet_timeout;

 pi->port_link_mode  = cpu_to_be16(OPA_PORT_LINK_MODE_OPA << 10 |
       OPA_PORT_LINK_MODE_OPA << 5 |
       OPA_PORT_LINK_MODE_OPA);

 pi->port_ltp_crc_mode = cpu_to_be16(ppd->port_ltp_crc_mode);

 pi->port_mode = cpu_to_be16(
    ppd->is_active_optimize_enabled ?
     OPA_PI_MASK_PORT_ACTIVE_OPTOMIZE : 0);

 pi->port_packet_format.supported =
  cpu_to_be16(OPA_PORT_PACKET_FORMAT_9B |
       OPA_PORT_PACKET_FORMAT_16B);
 pi->port_packet_format.enabled =
  cpu_to_be16(OPA_PORT_PACKET_FORMAT_9B |
       OPA_PORT_PACKET_FORMAT_16B);

 /* flit_control.interleave is (OPA V1, version .76):
 * bits use
 * ---- ---
 * 2 res
 * 2 DistanceSupported
 * 2 DistanceEnabled
 * 5 MaxNextLevelTxEnabled
 * 5 MaxNestLevelRxSupported
 *
 * HFI supports only "distance mode 1" (see OPA V1, version .76,
 * section 9.6.2), so set DistanceSupported, DistanceEnabled
 * to 0x1.
 */

 pi->flit_control.interleave = cpu_to_be16(0x1400);

 pi->link_down_reason = ppd->local_link_down_reason.sma;
 pi->neigh_link_down_reason = ppd->neigh_link_down_reason.sma;
 pi->port_error_action = cpu_to_be32(ppd->port_error_action);
 pi->mtucap = mtu_to_enum(hfi1_max_mtu, IB_MTU_4096);

 /* 32.768 usec. response time (guessing) */
 pi->resptimevalue = 3;

 pi->local_port_num = port;

 /* buffer info for FM */
 pi->overall_buffer_space = cpu_to_be16(dd->link_credits);

 pi->neigh_node_guid = cpu_to_be64(ppd->neighbor_guid);
 pi->neigh_port_num = ppd->neighbor_port_number;
 pi->port_neigh_mode =
  (ppd->neighbor_type & OPA_PI_MASK_NEIGH_NODE_TYPE) |
  (ppd->mgmt_allowed ? OPA_PI_MASK_NEIGH_MGMT_ALLOWED : 0) |
  (ppd->neighbor_fm_security ?
   OPA_PI_MASK_NEIGH_FW_AUTH_BYPASS : 0);

 /* HFIs shall always return VL15 credits to their
 * neighbor in a timely manner, without any credit return pacing.
 */

 credit_rate = 0;
 buffer_units  = (dd->vau) & OPA_PI_MASK_BUF_UNIT_BUF_ALLOC;
 buffer_units |= (dd->vcu << 3) & OPA_PI_MASK_BUF_UNIT_CREDIT_ACK;
 buffer_units |= (credit_rate << 6) &
    OPA_PI_MASK_BUF_UNIT_VL15_CREDIT_RATE;
 buffer_units |= (dd->vl15_init << 11) & OPA_PI_MASK_BUF_UNIT_VL15_INIT;
 pi->buffer_units = cpu_to_be32(buffer_units);

 pi->opa_cap_mask = cpu_to_be16(ibp->rvp.port_cap3_flags);
 pi->collectivemask_multicastmask = ((OPA_COLLECTIVE_NR & 0x7)
         << 3 | (OPA_MCAST_NR & 0x7));

 /* HFI supports a replay buffer 128 LTPs in size */
 pi->replay_depth.buffer = 0x80;
 /* read the cached value of DC_LCB_STS_ROUND_TRIP_LTP_CNT */
 read_lcb_cache(DC_LCB_STS_ROUND_TRIP_LTP_CNT, &tmp);

 /*
 * this counter is 16 bits wide, but the replay_depth.wire
 * variable is only 8 bits
 */

 if (tmp > 0xff)
  tmp = 0xff;
 pi->replay_depth.wire = tmp;

 if (resp_len)
  *resp_len += sizeof(struct opa_port_info);

 return reply((struct ib_mad_hdr *)smp);
}

/**
 * get_pkeys - return the PKEY table
 * @dd: the hfi1_ib device
 * @port: the IB port number
 * @pkeys: the pkey table is placed here
 */

static int get_pkeys(struct hfi1_devdata *dd, u32 port, u16 *pkeys)
{
 struct hfi1_pportdata *ppd = dd->pport + port - 1;

 memcpy(pkeys, ppd->pkeys, sizeof(ppd->pkeys));

 return 0;
}

static int __subn_get_opa_pkeytable(struct opa_smp *smp, u32 am, u8 *data,
        struct ib_device *ibdev, u32 port,
        u32 *resp_len, u32 max_len)
{
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 u32 n_blocks_req = OPA_AM_NBLK(am);
 u32 start_block = am & 0x7ff;
 __be16 *p;
 u16 *q;
 int i;
 u16 n_blocks_avail;
 unsigned npkeys = hfi1_get_npkeys(dd);
 size_t size;

 if (n_blocks_req == 0) {
  pr_warn("OPA Get PKey AM Invalid : P = %d; B = 0x%x; N = 0x%x\n",
   port, start_block, n_blocks_req);
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 n_blocks_avail = (u16)(npkeys / OPA_PARTITION_TABLE_BLK_SIZE) + 1;

 size = (n_blocks_req * OPA_PARTITION_TABLE_BLK_SIZE) * sizeof(u16);

 if (smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 if (start_block + n_blocks_req > n_blocks_avail ||
     n_blocks_req > OPA_NUM_PKEY_BLOCKS_PER_SMP) {
  pr_warn("OPA Get PKey AM Invalid : s 0x%x; req 0x%x; "
   "avail 0x%x; blk/smp 0x%lx\n",
   start_block, n_blocks_req, n_blocks_avail,
   OPA_NUM_PKEY_BLOCKS_PER_SMP);
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 p = (__be16 *)data;
 q = (u16 *)data;
 /* get the real pkeys if we are requesting the first block */
 if (start_block == 0) {
  get_pkeys(dd, port, q);
  for (i = 0; i < npkeys; i++)
   p[i] = cpu_to_be16(q[i]);
  if (resp_len)
   *resp_len += size;
 } else {
  smp->status |= IB_SMP_INVALID_FIELD;
 }
 return reply((struct ib_mad_hdr *)smp);
}

enum {
 HFI_TRANSITION_DISALLOWED,
 HFI_TRANSITION_IGNORED,
 HFI_TRANSITION_ALLOWED,
 HFI_TRANSITION_UNDEFINED,
};

/*
 * Use shortened names to improve readability of
 * {logical,physical}_state_transitions
 */

enum {
 __D = HFI_TRANSITION_DISALLOWED,
 __I = HFI_TRANSITION_IGNORED,
 __A = HFI_TRANSITION_ALLOWED,
 __U = HFI_TRANSITION_UNDEFINED,
};

/*
 * IB_PORTPHYSSTATE_POLLING (2) through OPA_PORTPHYSSTATE_MAX (11) are
 * represented in physical_state_transitions.
 */

#define __N_PHYSTATES (OPA_PORTPHYSSTATE_MAX - IB_PORTPHYSSTATE_POLLING + 1)

/*
 * Within physical_state_transitions, rows represent "old" states,
 * columns "new" states, and physical_state_transitions.allowed[old][new]
 * indicates if the transition from old state to new state is legal (see
 * OPAg1v1, Table 6-4).
 */

static const struct {
 u8 allowed[__N_PHYSTATES][__N_PHYSTATES];
} physical_state_transitions = {
 {
  /* 2    3    4    5    6    7    8    9   10   11 */
 /* 2 */ { __A, __A, __D, __D, __D, __D, __D, __D, __D, __D },
 /* 3 */ { __A, __I, __D, __D, __D, __D, __D, __D, __D, __A },
 /* 4 */ { __U, __U, __U, __U, __U, __U, __U, __U, __U, __U },
 /* 5 */ { __A, __A, __D, __I, __D, __D, __D, __D, __D, __D },
 /* 6 */ { __U, __U, __U, __U, __U, __U, __U, __U, __U, __U },
 /* 7 */ { __D, __A, __D, __D, __D, __I, __D, __D, __D, __D },
 /* 8 */ { __U, __U, __U, __U, __U, __U, __U, __U, __U, __U },
 /* 9 */ { __I, __A, __D, __D, __D, __D, __D, __I, __D, __D },
 /*10 */ { __U, __U, __U, __U, __U, __U, __U, __U, __U, __U },
 /*11 */ { __D, __A, __D, __D, __D, __D, __D, __D, __D, __I },
 }
};

/*
 * IB_PORT_DOWN (1) through IB_PORT_ACTIVE_DEFER (5) are represented
 * logical_state_transitions
 */


#define __N_LOGICAL_STATES (IB_PORT_ACTIVE_DEFER - IB_PORT_DOWN + 1)

/*
 * Within logical_state_transitions rows represent "old" states,
 * columns "new" states, and logical_state_transitions.allowed[old][new]
 * indicates if the transition from old state to new state is legal (see
 * OPAg1v1, Table 9-12).
 */

static const struct {
 u8 allowed[__N_LOGICAL_STATES][__N_LOGICAL_STATES];
} logical_state_transitions = {
 {
  /* 1    2    3    4    5 */
 /* 1 */ { __I, __D, __D, __D, __U},
 /* 2 */ { __D, __I, __A, __D, __U},
 /* 3 */ { __D, __D, __I, __A, __U},
 /* 4 */ { __D, __D, __I, __I, __U},
 /* 5 */ { __U, __U, __U, __U, __U},
 }
};

static int logical_transition_allowed(int old, int new)
{
 if (old < IB_PORT_NOP || old > IB_PORT_ACTIVE_DEFER ||
     new < IB_PORT_NOP || new > IB_PORT_ACTIVE_DEFER) {
  pr_warn("invalid logical state(s) (old %d new %d)\n",
   old, new);
  return HFI_TRANSITION_UNDEFINED;
 }

 if (new == IB_PORT_NOP)
  return HFI_TRANSITION_ALLOWED; /* always allowed */

 /* adjust states for indexing into logical_state_transitions */
 old -= IB_PORT_DOWN;
 new -= IB_PORT_DOWN;

 if (old < 0 || new < 0)
  return HFI_TRANSITION_UNDEFINED;
 return logical_state_transitions.allowed[old][new];
}

static int physical_transition_allowed(int old, int new)
{
 if (old < IB_PORTPHYSSTATE_NOP || old > OPA_PORTPHYSSTATE_MAX ||
     new < IB_PORTPHYSSTATE_NOP || new > OPA_PORTPHYSSTATE_MAX) {
  pr_warn("invalid physical state(s) (old %d new %d)\n",
   old, new);
  return HFI_TRANSITION_UNDEFINED;
 }

 if (new == IB_PORTPHYSSTATE_NOP)
  return HFI_TRANSITION_ALLOWED; /* always allowed */

 /* adjust states for indexing into physical_state_transitions */
 old -= IB_PORTPHYSSTATE_POLLING;
 new -= IB_PORTPHYSSTATE_POLLING;

 if (old < 0 || new < 0)
  return HFI_TRANSITION_UNDEFINED;
 return physical_state_transitions.allowed[old][new];
}

static int port_states_transition_allowed(struct hfi1_pportdata *ppd,
       u32 logical_new, u32 physical_new)
{
 u32 physical_old = driver_pstate(ppd);
 u32 logical_old = driver_lstate(ppd);
 int ret, logical_allowed, physical_allowed;

 ret = logical_transition_allowed(logical_old, logical_new);
 logical_allowed = ret;

 if (ret == HFI_TRANSITION_DISALLOWED ||
     ret == HFI_TRANSITION_UNDEFINED) {
  pr_warn("invalid logical state transition %s -> %s\n",
   ib_port_state_to_str(logical_old),
   ib_port_state_to_str(logical_new));
  return ret;
 }

 ret = physical_transition_allowed(physical_old, physical_new);
 physical_allowed = ret;

 if (ret == HFI_TRANSITION_DISALLOWED ||
     ret == HFI_TRANSITION_UNDEFINED) {
  pr_warn("invalid physical state transition %s -> %s\n",
   opa_pstate_name(physical_old),
   opa_pstate_name(physical_new));
  return ret;
 }

 if (logical_allowed == HFI_TRANSITION_IGNORED &&
     physical_allowed == HFI_TRANSITION_IGNORED)
  return HFI_TRANSITION_IGNORED;

 /*
 * A change request of Physical Port State from
 * 'Offline' to 'Polling' should be ignored.
 */

 if ((physical_old == OPA_PORTPHYSSTATE_OFFLINE) &&
     (physical_new == IB_PORTPHYSSTATE_POLLING))
  return HFI_TRANSITION_IGNORED;

 /*
 * Either physical_allowed or logical_allowed is
 * HFI_TRANSITION_ALLOWED.
 */

 return HFI_TRANSITION_ALLOWED;
}

static int set_port_states(struct hfi1_pportdata *ppd, struct opa_smp *smp,
      u32 logical_state, u32 phys_state, int local_mad)
{
 struct hfi1_devdata *dd = ppd->dd;
 u32 link_state;
 int ret;

 ret = port_states_transition_allowed(ppd, logical_state, phys_state);
 if (ret == HFI_TRANSITION_DISALLOWED ||
     ret == HFI_TRANSITION_UNDEFINED) {
  /* error message emitted above */
  smp->status |= IB_SMP_INVALID_FIELD;
  return 0;
 }

 if (ret == HFI_TRANSITION_IGNORED)
  return 0;

 if ((phys_state != IB_PORTPHYSSTATE_NOP) &&
     !(logical_state == IB_PORT_DOWN ||
       logical_state == IB_PORT_NOP)){
  pr_warn("SubnSet(OPA_PortInfo) port state invalid: logical_state 0x%x physical_state 0x%x\n",
   logical_state, phys_state);
  smp->status |= IB_SMP_INVALID_FIELD;
 }

 /*
 * Logical state changes are summarized in OPAv1g1 spec.,
 * Table 9-12; physical state changes are summarized in
 * OPAv1g1 spec., Table 6.4.
 */

 switch (logical_state) {
 case IB_PORT_NOP:
  if (phys_state == IB_PORTPHYSSTATE_NOP)
   break;
  fallthrough;
 case IB_PORT_DOWN:
  if (phys_state == IB_PORTPHYSSTATE_NOP) {
   link_state = HLS_DN_DOWNDEF;
  } else if (phys_state == IB_PORTPHYSSTATE_POLLING) {
   link_state = HLS_DN_POLL;
   set_link_down_reason(ppd, OPA_LINKDOWN_REASON_FM_BOUNCE,
          0, OPA_LINKDOWN_REASON_FM_BOUNCE);
  } else if (phys_state == IB_PORTPHYSSTATE_DISABLED) {
   link_state = HLS_DN_DISABLE;
  } else {
   pr_warn("SubnSet(OPA_PortInfo) invalid physical state 0x%x\n",
    phys_state);
   smp->status |= IB_SMP_INVALID_FIELD;
   break;
  }

  if ((link_state == HLS_DN_POLL ||
       link_state == HLS_DN_DOWNDEF)) {
   /*
 * Going to poll.  No matter what the current state,
 * always move offline first, then tune and start the
 * link.  This correctly handles a FM link bounce and
 * a link enable.  Going offline is a no-op if already
 * offline.
 */

   set_link_state(ppd, HLS_DN_OFFLINE);
   start_link(ppd);
  } else {
   set_link_state(ppd, link_state);
  }
  if (link_state == HLS_DN_DISABLE &&
      (ppd->offline_disabled_reason >
       HFI1_ODR_MASK(OPA_LINKDOWN_REASON_SMA_DISABLED) ||
       ppd->offline_disabled_reason ==
       HFI1_ODR_MASK(OPA_LINKDOWN_REASON_NONE)))
   ppd->offline_disabled_reason =
   HFI1_ODR_MASK(OPA_LINKDOWN_REASON_SMA_DISABLED);
  /*
 * Don't send a reply if the response would be sent
 * through the disabled port.
 */

  if (link_state == HLS_DN_DISABLE && !local_mad)
   return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
  break;
 case IB_PORT_ARMED:
  ret = set_link_state(ppd, HLS_UP_ARMED);
  if (!ret)
   send_idle_sma(dd, SMA_IDLE_ARM);
  break;
 case IB_PORT_ACTIVE:
  if (ppd->neighbor_normal) {
   ret = set_link_state(ppd, HLS_UP_ACTIVE);
   if (ret == 0)
    send_idle_sma(dd, SMA_IDLE_ACTIVE);
  } else {
   pr_warn("SubnSet(OPA_PortInfo) Cannot move to Active with NeighborNormal 0\n");
   smp->status |= IB_SMP_INVALID_FIELD;
  }
  break;
 default:
  pr_warn("SubnSet(OPA_PortInfo) invalid logical state 0x%x\n",
   logical_state);
  smp->status |= IB_SMP_INVALID_FIELD;
 }

 return 0;
}

/*
 * subn_set_opa_portinfo - set port information
 * @smp: the incoming SM packet
 * @ibdev: the infiniband device
 * @port: the port on the device
 *
 */

static int __subn_set_opa_portinfo(struct opa_smp *smp, u32 am, u8 *data,
       struct ib_device *ibdev, u32 port,
       u32 *resp_len, u32 max_len, int local_mad)
{
 struct opa_port_info *pi = (struct opa_port_info *)data;
 struct ib_event event;
 struct hfi1_devdata *dd;
 struct hfi1_pportdata *ppd;
 struct hfi1_ibport *ibp;
 u8 clientrereg;
 unsigned long flags;
 u32 smlid;
 u32 lid;
 u8 ls_old, ls_new, ps_new;
 u8 vls;
 u8 msl;
 u8 crc_enabled;
 u16 lse, lwe, mtu;
 u32 num_ports = OPA_AM_NPORT(am);
 u32 start_of_sm_config = OPA_AM_START_SM_CFG(am);
 int ret, i, invalid = 0, call_set_mtu = 0;
 int call_link_downgrade_policy = 0;

 if (num_ports != 1 ||
     smp_length_check(sizeof(*pi), max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 lid = be32_to_cpu(pi->lid);
 if (lid & 0xFF000000) {
  pr_warn("OPA_PortInfo lid out of range: %X\n", lid);
  smp->status |= IB_SMP_INVALID_FIELD;
  goto get_only;
 }


 smlid = be32_to_cpu(pi->sm_lid);
 if (smlid & 0xFF000000) {
  pr_warn("OPA_PortInfo SM lid out of range: %X\n", smlid);
  smp->status |= IB_SMP_INVALID_FIELD;
  goto get_only;
 }

 clientrereg = (pi->clientrereg_subnettimeout &
   OPA_PI_MASK_CLIENT_REREGISTER);

 dd = dd_from_ibdev(ibdev);
 /* IB numbers ports from 1, hw from 0 */
 ppd = dd->pport + (port - 1);
 ibp = &ppd->ibport_data;
 event.device = ibdev;
 event.element.port_num = port;

 ls_old = driver_lstate(ppd);

 ibp->rvp.mkey = pi->mkey;
 if (ibp->rvp.gid_prefix != pi->subnet_prefix) {
  ibp->rvp.gid_prefix = pi->subnet_prefix;
  event.event = IB_EVENT_GID_CHANGE;
  ib_dispatch_event(&event);
 }
 ibp->rvp.mkey_lease_period = be16_to_cpu(pi->mkey_lease_period);

 /* Must be a valid unicast LID address. */
 if ((lid == 0 && ls_old > IB_PORT_INIT) ||
      (hfi1_is_16B_mcast(lid))) {
  smp->status |= IB_SMP_INVALID_FIELD;
  pr_warn("SubnSet(OPA_PortInfo) lid invalid 0x%x\n",
   lid);
 } else if (ppd->lid != lid ||
   ppd->lmc != (pi->mkeyprotect_lmc & OPA_PI_MASK_LMC)) {
  if (ppd->lid != lid)
   hfi1_set_uevent_bits(ppd, _HFI1_EVENT_LID_CHANGE_BIT);
  if (ppd->lmc != (pi->mkeyprotect_lmc & OPA_PI_MASK_LMC))
   hfi1_set_uevent_bits(ppd, _HFI1_EVENT_LMC_CHANGE_BIT);
  hfi1_set_lid(ppd, lid, pi->mkeyprotect_lmc & OPA_PI_MASK_LMC);
  event.event = IB_EVENT_LID_CHANGE;
  ib_dispatch_event(&event);

  if (HFI1_PORT_GUID_INDEX + 1 < HFI1_GUIDS_PER_PORT) {
   /* Manufacture GID from LID to support extended
 * addresses
 */

   ppd->guids[HFI1_PORT_GUID_INDEX + 1] =
    be64_to_cpu(OPA_MAKE_ID(lid));
   event.event = IB_EVENT_GID_CHANGE;
   ib_dispatch_event(&event);
  }
 }

 msl = pi->smsl & OPA_PI_MASK_SMSL;
 if (pi->partenforce_filterraw & OPA_PI_MASK_LINKINIT_REASON)
  ppd->linkinit_reason =
   (pi->partenforce_filterraw &
    OPA_PI_MASK_LINKINIT_REASON);

 /* Must be a valid unicast LID address. */
 if ((smlid == 0 && ls_old > IB_PORT_INIT) ||
      (hfi1_is_16B_mcast(smlid))) {
  smp->status |= IB_SMP_INVALID_FIELD;
  pr_warn("SubnSet(OPA_PortInfo) smlid invalid 0x%x\n", smlid);
 } else if (smlid != ibp->rvp.sm_lid || msl != ibp->rvp.sm_sl) {
  pr_warn("SubnSet(OPA_PortInfo) smlid 0x%x\n", smlid);
  spin_lock_irqsave(&ibp->rvp.lock, flags);
  if (ibp->rvp.sm_ah) {
   if (smlid != ibp->rvp.sm_lid)
    hfi1_modify_qp0_ah(ibp, ibp->rvp.sm_ah, smlid);
   if (msl != ibp->rvp.sm_sl)
    rdma_ah_set_sl(&ibp->rvp.sm_ah->attr, msl);
  }
  spin_unlock_irqrestore(&ibp->rvp.lock, flags);
  if (smlid != ibp->rvp.sm_lid)
   ibp->rvp.sm_lid = smlid;
  if (msl != ibp->rvp.sm_sl)
   ibp->rvp.sm_sl = msl;
  event.event = IB_EVENT_SM_CHANGE;
  ib_dispatch_event(&event);
 }

 if (pi->link_down_reason == 0) {
  ppd->local_link_down_reason.sma = 0;
  ppd->local_link_down_reason.latest = 0;
 }

 if (pi->neigh_link_down_reason == 0) {
  ppd->neigh_link_down_reason.sma = 0;
  ppd->neigh_link_down_reason.latest = 0;
 }

 ppd->sm_trap_qp = be32_to_cpu(pi->sm_trap_qp);
 ppd->sa_qp = be32_to_cpu(pi->sa_qp);

 ppd->port_error_action = be32_to_cpu(pi->port_error_action);
 lwe = be16_to_cpu(pi->link_width.enabled);
 if (lwe) {
  if (lwe == OPA_LINK_WIDTH_RESET ||
      lwe == OPA_LINK_WIDTH_RESET_OLD)
   set_link_width_enabled(ppd, ppd->link_width_supported);
  else if ((lwe & ~ppd->link_width_supported) == 0)
   set_link_width_enabled(ppd, lwe);
  else
   smp->status |= IB_SMP_INVALID_FIELD;
 }
 lwe = be16_to_cpu(pi->link_width_downgrade.enabled);
 /* LWD.E is always applied - 0 means "disabled" */
 if (lwe == OPA_LINK_WIDTH_RESET ||
     lwe == OPA_LINK_WIDTH_RESET_OLD) {
  set_link_width_downgrade_enabled(ppd,
       ppd->
       link_width_downgrade_supported
       );
 } else if ((lwe & ~ppd->link_width_downgrade_supported) == 0) {
  /* only set and apply if something changed */
  if (lwe != ppd->link_width_downgrade_enabled) {
   set_link_width_downgrade_enabled(ppd, lwe);
   call_link_downgrade_policy = 1;
  }
 } else {
  smp->status |= IB_SMP_INVALID_FIELD;
 }
 lse = be16_to_cpu(pi->link_speed.enabled);
 if (lse) {
  if (lse & be16_to_cpu(pi->link_speed.supported))
   set_link_speed_enabled(ppd, lse);
  else
   smp->status |= IB_SMP_INVALID_FIELD;
 }

 ibp->rvp.mkeyprot =
  (pi->mkeyprotect_lmc & OPA_PI_MASK_MKEY_PROT_BIT) >> 6;
 ibp->rvp.vl_high_limit = be16_to_cpu(pi->vl.high_limit) & 0xFF;
 (void)hfi1_set_ib_cfg(ppd, HFI1_IB_CFG_VL_HIGH_LIMIT,
        ibp->rvp.vl_high_limit);

 if (ppd->vls_supported / 2 > ARRAY_SIZE(pi->neigh_mtu.pvlx_to_mtu) ||
     ppd->vls_supported > ARRAY_SIZE(dd->vld)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }
 for (i = 0; i < ppd->vls_supported; i++) {
  if ((i % 2) == 0)
   mtu = enum_to_mtu((pi->neigh_mtu.pvlx_to_mtu[i / 2] >>
        4) & 0xF);
  else
   mtu = enum_to_mtu(pi->neigh_mtu.pvlx_to_mtu[i / 2] &
       0xF);
  if (mtu == 0xffff) {
   pr_warn("SubnSet(OPA_PortInfo) mtu invalid %d (0x%x)\n",
    mtu,
    (pi->neigh_mtu.pvlx_to_mtu[0] >> 4) & 0xF);
   smp->status |= IB_SMP_INVALID_FIELD;
   mtu = hfi1_max_mtu; /* use a valid MTU */
  }
  if (dd->vld[i].mtu != mtu) {
   dd_dev_info(dd,
        "MTU change on vl %d from %d to %d\n",
        i, dd->vld[i].mtu, mtu);
   dd->vld[i].mtu = mtu;
   call_set_mtu++;
  }
 }
 /* As per OPAV1 spec: VL15 must support and be configured
 * for operation with a 2048 or larger MTU.
 */

 mtu = enum_to_mtu(pi->neigh_mtu.pvlx_to_mtu[15 / 2] & 0xF);
 if (mtu < 2048 || mtu == 0xffff)
  mtu = 2048;
 if (dd->vld[15].mtu != mtu) {
  dd_dev_info(dd,
       "MTU change on vl 15 from %d to %d\n",
       dd->vld[15].mtu, mtu);
  dd->vld[15].mtu = mtu;
  call_set_mtu++;
 }
 if (call_set_mtu)
  set_mtu(ppd);

 /* Set operational VLs */
 vls = pi->operational_vls & OPA_PI_MASK_OPERATIONAL_VL;
 if (vls) {
  if (vls > ppd->vls_supported) {
   pr_warn("SubnSet(OPA_PortInfo) VL's supported invalid %d\n",
    pi->operational_vls);
   smp->status |= IB_SMP_INVALID_FIELD;
  } else {
   if (hfi1_set_ib_cfg(ppd, HFI1_IB_CFG_OP_VLS,
         vls) == -EINVAL)
    smp->status |= IB_SMP_INVALID_FIELD;
  }
 }

 if (pi->mkey_violations == 0)
  ibp->rvp.mkey_violations = 0;

 if (pi->pkey_violations == 0)
  ibp->rvp.pkey_violations = 0;

 if (pi->qkey_violations == 0)
  ibp->rvp.qkey_violations = 0;

 ibp->rvp.subnet_timeout =
  pi->clientrereg_subnettimeout & OPA_PI_MASK_SUBNET_TIMEOUT;

 crc_enabled = be16_to_cpu(pi->port_ltp_crc_mode);
 crc_enabled >>= 4;
 crc_enabled &= 0xf;

 if (crc_enabled != 0)
  ppd->port_crc_mode_enabled = port_ltp_to_cap(crc_enabled);

 ppd->is_active_optimize_enabled =
   !!(be16_to_cpu(pi->port_mode)
     & OPA_PI_MASK_PORT_ACTIVE_OPTOMIZE);

 ls_new = pi->port_states.portphysstate_portstate &
   OPA_PI_MASK_PORT_STATE;
 ps_new = (pi->port_states.portphysstate_portstate &
   OPA_PI_MASK_PORT_PHYSICAL_STATE) >> 4;

 if (ls_old == IB_PORT_INIT) {
  if (start_of_sm_config) {
   if (ls_new == ls_old || (ls_new == IB_PORT_ARMED))
    ppd->is_sm_config_started = 1;
  } else if (ls_new == IB_PORT_ARMED) {
   if (ppd->is_sm_config_started == 0) {
    invalid = 1;
    smp->status |= IB_SMP_INVALID_FIELD;
   }
  }
 }

 /* Handle CLIENT_REREGISTER event b/c SM asked us for it */
 if (clientrereg) {
  event.event = IB_EVENT_CLIENT_REREGISTER;
  ib_dispatch_event(&event);
 }

 /*
 * Do the port state change now that the other link parameters
 * have been set.
 * Changing the port physical state only makes sense if the link
 * is down or is being set to down.
 */


 if (!invalid) {
  ret = set_port_states(ppd, smp, ls_new, ps_new, local_mad);
  if (ret)
   return ret;
 }

 ret = __subn_get_opa_portinfo(smp, am, data, ibdev, port, resp_len,
          max_len);

 /* restore re-reg bit per o14-12.2.1 */
 pi->clientrereg_subnettimeout |= clientrereg;

 /*
 * Apply the new link downgrade policy.  This may result in a link
 * bounce.  Do this after everything else so things are settled.
 * Possible problem: if setting the port state above fails, then
 * the policy change is not applied.
 */

 if (call_link_downgrade_policy)
  apply_link_downgrade_policy(ppd, 0);

 return ret;

get_only:
 return __subn_get_opa_portinfo(smp, am, data, ibdev, port, resp_len,
           max_len);
}

/**
 * set_pkeys - set the PKEY table for ctxt 0
 * @dd: the hfi1_ib device
 * @port: the IB port number
 * @pkeys: the PKEY table
 */

static int set_pkeys(struct hfi1_devdata *dd, u32 port, u16 *pkeys)
{
 struct hfi1_pportdata *ppd;
 int i;
 int changed = 0;
 int update_includes_mgmt_partition = 0;

 /*
 * IB port one/two always maps to context zero/one,
 * always a kernel context, no locking needed
 * If we get here with ppd setup, no need to check
 * that rcd is valid.
 */

 ppd = dd->pport + (port - 1);
 /*
 * If the update does not include the management pkey, don't do it.
 */

 for (i = 0; i < ARRAY_SIZE(ppd->pkeys); i++) {
  if (pkeys[i] == LIM_MGMT_P_KEY) {
   update_includes_mgmt_partition = 1;
   break;
  }
 }

 if (!update_includes_mgmt_partition)
  return 1;

 for (i = 0; i < ARRAY_SIZE(ppd->pkeys); i++) {
  u16 key = pkeys[i];
  u16 okey = ppd->pkeys[i];

  if (key == okey)
   continue;
  /*
 * The SM gives us the complete PKey table. We have
 * to ensure that we put the PKeys in the matching
 * slots.
 */

  ppd->pkeys[i] = key;
  changed = 1;
 }

 if (changed) {
  (void)hfi1_set_ib_cfg(ppd, HFI1_IB_CFG_PKEYS, 0);
  hfi1_event_pkey_change(dd, port);
 }

 return 0;
}

static int __subn_set_opa_pkeytable(struct opa_smp *smp, u32 am, u8 *data,
        struct ib_device *ibdev, u32 port,
        u32 *resp_len, u32 max_len)
{
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 u32 n_blocks_sent = OPA_AM_NBLK(am);
 u32 start_block = am & 0x7ff;
 u16 *p = (u16 *)data;
 __be16 *q = (__be16 *)data;
 int i;
 u16 n_blocks_avail;
 unsigned npkeys = hfi1_get_npkeys(dd);
 u32 size = 0;

 if (n_blocks_sent == 0) {
  pr_warn("OPA Get PKey AM Invalid : P = %u; B = 0x%x; N = 0x%x\n",
   port, start_block, n_blocks_sent);
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 n_blocks_avail = (u16)(npkeys / OPA_PARTITION_TABLE_BLK_SIZE) + 1;

 size = sizeof(u16) * (n_blocks_sent * OPA_PARTITION_TABLE_BLK_SIZE);

 if (smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 if (start_block + n_blocks_sent > n_blocks_avail ||
     n_blocks_sent > OPA_NUM_PKEY_BLOCKS_PER_SMP) {
  pr_warn("OPA Set PKey AM Invalid : s 0x%x; req 0x%x; avail 0x%x; blk/smp 0x%lx\n",
   start_block, n_blocks_sent, n_blocks_avail,
   OPA_NUM_PKEY_BLOCKS_PER_SMP);
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 for (i = 0; i < n_blocks_sent * OPA_PARTITION_TABLE_BLK_SIZE; i++)
  p[i] = be16_to_cpu(q[i]);

 if (start_block == 0 && set_pkeys(dd, port, p) != 0) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 return __subn_get_opa_pkeytable(smp, am, data, ibdev, port, resp_len,
     max_len);
}

#define ILLEGAL_VL 12
/*
 * filter_sc2vlt changes mappings to VL15 to ILLEGAL_VL (except
 * for SC15, which must map to VL15). If we don't remap things this
 * way it is possible for VL15 counters to increment when we try to
 * send on a SC which is mapped to an invalid VL.
 * When getting the table convert ILLEGAL_VL back to VL15.
 */

static void filter_sc2vlt(void *data, bool set)
{
 int i;
 u8 *pd = data;

 for (i = 0; i < OPA_MAX_SCS; i++) {
  if (i == 15)
   continue;

  if (set) {
   if ((pd[i] & 0x1f) == 0xf)
    pd[i] = ILLEGAL_VL;
  } else {
   if ((pd[i] & 0x1f) == ILLEGAL_VL)
    pd[i] = 0xf;
  }
 }
}

static int set_sc2vlt_tables(struct hfi1_devdata *dd, void *data)
{
 u64 *val = data;

 filter_sc2vlt(data, true);

 write_csr(dd, SEND_SC2VLT0, *val++);
 write_csr(dd, SEND_SC2VLT1, *val++);
 write_csr(dd, SEND_SC2VLT2, *val++);
 write_csr(dd, SEND_SC2VLT3, *val++);
 write_seqlock_irq(&dd->sc2vl_lock);
 memcpy(dd->sc2vl, data, sizeof(dd->sc2vl));
 write_sequnlock_irq(&dd->sc2vl_lock);
 return 0;
}

static int get_sc2vlt_tables(struct hfi1_devdata *dd, void *data)
{
 u64 *val = (u64 *)data;

 *val++ = read_csr(dd, SEND_SC2VLT0);
 *val++ = read_csr(dd, SEND_SC2VLT1);
 *val++ = read_csr(dd, SEND_SC2VLT2);
 *val++ = read_csr(dd, SEND_SC2VLT3);

 filter_sc2vlt((u64 *)data, false);
 return 0;
}

static int __subn_get_opa_sl_to_sc(struct opa_smp *smp, u32 am, u8 *data,
       struct ib_device *ibdev, u32 port,
       u32 *resp_len, u32 max_len)
{
 struct hfi1_ibport *ibp = to_iport(ibdev, port);
 u8 *p = data;
 size_t size = ARRAY_SIZE(ibp->sl_to_sc); /* == 32 */
 unsigned i;

 if (am || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 for (i = 0; i < ARRAY_SIZE(ibp->sl_to_sc); i++)
  *p++ = ibp->sl_to_sc[i];

 if (resp_len)
  *resp_len += size;

 return reply((struct ib_mad_hdr *)smp);
}

static int __subn_set_opa_sl_to_sc(struct opa_smp *smp, u32 am, u8 *data,
       struct ib_device *ibdev, u32 port,
       u32 *resp_len, u32 max_len)
{
 struct hfi1_ibport *ibp = to_iport(ibdev, port);
 u8 *p = data;
 size_t size = ARRAY_SIZE(ibp->sl_to_sc);
 int i;
 u8 sc;

 if (am || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 for (i = 0; i <  ARRAY_SIZE(ibp->sl_to_sc); i++) {
  sc = *p++;
  if (ibp->sl_to_sc[i] != sc) {
   ibp->sl_to_sc[i] = sc;

   /* Put all stale qps into error state */
   hfi1_error_port_qps(ibp, i);
  }
 }

 return __subn_get_opa_sl_to_sc(smp, am, data, ibdev, port, resp_len,
           max_len);
}

static int __subn_get_opa_sc_to_sl(struct opa_smp *smp, u32 am, u8 *data,
       struct ib_device *ibdev, u32 port,
       u32 *resp_len, u32 max_len)
{
 struct hfi1_ibport *ibp = to_iport(ibdev, port);
 u8 *p = data;
 size_t size = ARRAY_SIZE(ibp->sc_to_sl); /* == 32 */
 unsigned i;

 if (am || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 for (i = 0; i < ARRAY_SIZE(ibp->sc_to_sl); i++)
  *p++ = ibp->sc_to_sl[i];

 if (resp_len)
  *resp_len += size;

 return reply((struct ib_mad_hdr *)smp);
}

static int __subn_set_opa_sc_to_sl(struct opa_smp *smp, u32 am, u8 *data,
       struct ib_device *ibdev, u32 port,
       u32 *resp_len, u32 max_len)
{
 struct hfi1_ibport *ibp = to_iport(ibdev, port);
 size_t size = ARRAY_SIZE(ibp->sc_to_sl);
 u8 *p = data;
 int i;

 if (am || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 for (i = 0; i < ARRAY_SIZE(ibp->sc_to_sl); i++)
  ibp->sc_to_sl[i] = *p++;

 return __subn_get_opa_sc_to_sl(smp, am, data, ibdev, port, resp_len,
           max_len);
}

static int __subn_get_opa_sc_to_vlt(struct opa_smp *smp, u32 am, u8 *data,
        struct ib_device *ibdev, u32 port,
        u32 *resp_len, u32 max_len)
{
 u32 n_blocks = OPA_AM_NBLK(am);
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 void *vp = (void *)data;
 size_t size = 4 * sizeof(u64);

 if (n_blocks != 1 || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 get_sc2vlt_tables(dd, vp);

 if (resp_len)
  *resp_len += size;

 return reply((struct ib_mad_hdr *)smp);
}

static int __subn_set_opa_sc_to_vlt(struct opa_smp *smp, u32 am, u8 *data,
        struct ib_device *ibdev, u32 port,
        u32 *resp_len, u32 max_len)
{
 u32 n_blocks = OPA_AM_NBLK(am);
 int async_update = OPA_AM_ASYNC(am);
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 void *vp = (void *)data;
 struct hfi1_pportdata *ppd;
 int lstate;
 /*
 * set_sc2vlt_tables writes the information contained in *data
 * to four 64-bit registers SendSC2VLt[0-3]. We need to make
 * sure *max_len is not greater than the total size of the four
 * SendSC2VLt[0-3] registers.
 */

 size_t size = 4 * sizeof(u64);

 if (n_blocks != 1 || async_update || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 /* IB numbers ports from 1, hw from 0 */
 ppd = dd->pport + (port - 1);
 lstate = driver_lstate(ppd);
 /*
 * it's known that async_update is 0 by this point, but include
 * the explicit check for clarity
 */

 if (!async_update &&
     (lstate == IB_PORT_ARMED || lstate == IB_PORT_ACTIVE)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 set_sc2vlt_tables(dd, vp);

 return __subn_get_opa_sc_to_vlt(smp, am, data, ibdev, port, resp_len,
     max_len);
}

static int __subn_get_opa_sc_to_vlnt(struct opa_smp *smp, u32 am, u8 *data,
         struct ib_device *ibdev, u32 port,
         u32 *resp_len, u32 max_len)
{
 u32 n_blocks = OPA_AM_NPORT(am);
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 struct hfi1_pportdata *ppd;
 void *vp = (void *)data;
 int size = sizeof(struct sc2vlnt);

 if (n_blocks != 1 || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 ppd = dd->pport + (port - 1);

 fm_get_table(ppd, FM_TBL_SC2VLNT, vp);

 if (resp_len)
  *resp_len += size;

 return reply((struct ib_mad_hdr *)smp);
}

static int __subn_set_opa_sc_to_vlnt(struct opa_smp *smp, u32 am, u8 *data,
         struct ib_device *ibdev, u32 port,
         u32 *resp_len, u32 max_len)
{
 u32 n_blocks = OPA_AM_NPORT(am);
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 struct hfi1_pportdata *ppd;
 void *vp = (void *)data;
 int lstate;
 int size = sizeof(struct sc2vlnt);

 if (n_blocks != 1 || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 /* IB numbers ports from 1, hw from 0 */
 ppd = dd->pport + (port - 1);
 lstate = driver_lstate(ppd);
 if (lstate == IB_PORT_ARMED || lstate == IB_PORT_ACTIVE) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 ppd = dd->pport + (port - 1);

 fm_set_table(ppd, FM_TBL_SC2VLNT, vp);

 return __subn_get_opa_sc_to_vlnt(smp, am, data, ibdev, port,
      resp_len, max_len);
}

static int __subn_get_opa_psi(struct opa_smp *smp, u32 am, u8 *data,
         struct ib_device *ibdev, u32 port,
         u32 *resp_len, u32 max_len)
{
 u32 nports = OPA_AM_NPORT(am);
 u32 start_of_sm_config = OPA_AM_START_SM_CFG(am);
 u32 lstate;
 struct hfi1_ibport *ibp;
 struct hfi1_pportdata *ppd;
 struct opa_port_state_info *psi = (struct opa_port_state_info *)data;

 if (nports != 1 || smp_length_check(sizeof(*psi), max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 ibp = to_iport(ibdev, port);
 ppd = ppd_from_ibp(ibp);

 lstate = driver_lstate(ppd);

 if (start_of_sm_config && (lstate == IB_PORT_INIT))
  ppd->is_sm_config_started = 1;

 psi->port_states.ledenable_offlinereason = ppd->neighbor_normal << 4;
 psi->port_states.ledenable_offlinereason |=
  ppd->is_sm_config_started << 5;
 psi->port_states.ledenable_offlinereason |=
  ppd->offline_disabled_reason;

 psi->port_states.portphysstate_portstate =
  (driver_pstate(ppd) << 4) | (lstate & 0xf);
 psi->link_width_downgrade_tx_active =
  cpu_to_be16(ppd->link_width_downgrade_tx_active);
 psi->link_width_downgrade_rx_active =
  cpu_to_be16(ppd->link_width_downgrade_rx_active);
 if (resp_len)
  *resp_len += sizeof(struct opa_port_state_info);

 return reply((struct ib_mad_hdr *)smp);
}

static int __subn_set_opa_psi(struct opa_smp *smp, u32 am, u8 *data,
         struct ib_device *ibdev, u32 port,
         u32 *resp_len, u32 max_len, int local_mad)
{
 u32 nports = OPA_AM_NPORT(am);
 u32 start_of_sm_config = OPA_AM_START_SM_CFG(am);
 u32 ls_old;
 u8 ls_new, ps_new;
 struct hfi1_ibport *ibp;
 struct hfi1_pportdata *ppd;
 struct opa_port_state_info *psi = (struct opa_port_state_info *)data;
 int ret, invalid = 0;

 if (nports != 1 || smp_length_check(sizeof(*psi), max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 ibp = to_iport(ibdev, port);
 ppd = ppd_from_ibp(ibp);

 ls_old = driver_lstate(ppd);

 ls_new = port_states_to_logical_state(&psi->port_states);
 ps_new = port_states_to_phys_state(&psi->port_states);

 if (ls_old == IB_PORT_INIT) {
  if (start_of_sm_config) {
   if (ls_new == ls_old || (ls_new == IB_PORT_ARMED))
    ppd->is_sm_config_started = 1;
  } else if (ls_new == IB_PORT_ARMED) {
   if (ppd->is_sm_config_started == 0) {
    invalid = 1;
    smp->status |= IB_SMP_INVALID_FIELD;
   }
  }
 }

 if (!invalid) {
  ret = set_port_states(ppd, smp, ls_new, ps_new, local_mad);
  if (ret)
   return ret;
 }

 return __subn_get_opa_psi(smp, am, data, ibdev, port, resp_len,
      max_len);
}

static int __subn_get_opa_cable_info(struct opa_smp *smp, u32 am, u8 *data,
         struct ib_device *ibdev, u32 port,
         u32 *resp_len, u32 max_len)
{
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 u32 addr = OPA_AM_CI_ADDR(am);
 u32 len = OPA_AM_CI_LEN(am) + 1;
 int ret;

 if (dd->pport->port_type != PORT_TYPE_QSFP ||
     smp_length_check(len, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

#define __CI_PAGE_SIZE BIT(7) /* 128 bytes */
#define __CI_PAGE_MASK ~(__CI_PAGE_SIZE - 1)
#define __CI_PAGE_NUM(a) ((a) & __CI_PAGE_MASK)

 /*
 * check that addr is within spec, and
 * addr and (addr + len - 1) are on the same "page"
 */

 if (addr >= 4096 ||
     (__CI_PAGE_NUM(addr) != __CI_PAGE_NUM(addr + len - 1))) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 ret = get_cable_info(dd, port, addr, len, data);

 if (ret == -ENODEV) {
  smp->status |= IB_SMP_UNSUP_METH_ATTR;
  return reply((struct ib_mad_hdr *)smp);
 }

 /* The address range for the CableInfo SMA query is wider than the
 * memory available on the QSFP cable. We want to return a valid
 * response, albeit zeroed out, for address ranges beyond available
 * memory but that are within the CableInfo query spec
 */

 if (ret < 0 && ret != -ERANGE) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 if (resp_len)
  *resp_len += len;

 return reply((struct ib_mad_hdr *)smp);
}

static int __subn_get_opa_bct(struct opa_smp *smp, u32 am, u8 *data,
         struct ib_device *ibdev, u32 port, u32 *resp_len,
         u32 max_len)
{
 u32 num_ports = OPA_AM_NPORT(am);
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 struct hfi1_pportdata *ppd;
 struct buffer_control *p = (struct buffer_control *)data;
 int size = sizeof(struct buffer_control);

 if (num_ports != 1 || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 ppd = dd->pport + (port - 1);
 fm_get_table(ppd, FM_TBL_BUFFER_CONTROL, p);
 trace_bct_get(dd, p);
 if (resp_len)
  *resp_len += size;

 return reply((struct ib_mad_hdr *)smp);
}

static int __subn_set_opa_bct(struct opa_smp *smp, u32 am, u8 *data,
         struct ib_device *ibdev, u32 port, u32 *resp_len,
         u32 max_len)
{
 u32 num_ports = OPA_AM_NPORT(am);
 struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
 struct hfi1_pportdata *ppd;
 struct buffer_control *p = (struct buffer_control *)data;

 if (num_ports != 1 || smp_length_check(sizeof(*p), max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }
 ppd = dd->pport + (port - 1);
 trace_bct_set(dd, p);
 if (fm_set_table(ppd, FM_TBL_BUFFER_CONTROL, p) < 0) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 return __subn_get_opa_bct(smp, am, data, ibdev, port, resp_len,
      max_len);
}

static int __subn_get_opa_vl_arb(struct opa_smp *smp, u32 am, u8 *data,
     struct ib_device *ibdev, u32 port,
     u32 *resp_len, u32 max_len)
{
 struct hfi1_pportdata *ppd = ppd_from_ibp(to_iport(ibdev, port));
 u32 num_ports = OPA_AM_NPORT(am);
 u8 section = (am & 0x00ff0000) >> 16;
 u8 *p = data;
 int size = 256;

 if (num_ports != 1 || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 switch (section) {
 case OPA_VLARB_LOW_ELEMENTS:
  fm_get_table(ppd, FM_TBL_VL_LOW_ARB, p);
  break;
 case OPA_VLARB_HIGH_ELEMENTS:
  fm_get_table(ppd, FM_TBL_VL_HIGH_ARB, p);
  break;
 case OPA_VLARB_PREEMPT_ELEMENTS:
  fm_get_table(ppd, FM_TBL_VL_PREEMPT_ELEMS, p);
  break;
 case OPA_VLARB_PREEMPT_MATRIX:
  fm_get_table(ppd, FM_TBL_VL_PREEMPT_MATRIX, p);
  break;
 default:
  pr_warn("OPA SubnGet(VL Arb) AM Invalid : 0x%x\n",
   be32_to_cpu(smp->attr_mod));
  smp->status |= IB_SMP_INVALID_FIELD;
  size = 0;
  break;
 }

 if (size > 0 && resp_len)
  *resp_len += size;

 return reply((struct ib_mad_hdr *)smp);
}

static int __subn_set_opa_vl_arb(struct opa_smp *smp, u32 am, u8 *data,
     struct ib_device *ibdev, u32 port,
     u32 *resp_len, u32 max_len)
{
 struct hfi1_pportdata *ppd = ppd_from_ibp(to_iport(ibdev, port));
 u32 num_ports = OPA_AM_NPORT(am);
 u8 section = (am & 0x00ff0000) >> 16;
 u8 *p = data;
 int size = 256;

 if (num_ports != 1 || smp_length_check(size, max_len)) {
  smp->status |= IB_SMP_INVALID_FIELD;
  return reply((struct ib_mad_hdr *)smp);
 }

 switch (section) {
 case OPA_VLARB_LOW_ELEMENTS:
  (void)fm_set_table(ppd, FM_TBL_VL_LOW_ARB, p);
  break;
 case OPA_VLARB_HIGH_ELEMENTS:
  (void)fm_set_table(ppd, FM_TBL_VL_HIGH_ARB, p);
  break;
 /*
 * neither OPA_VLARB_PREEMPT_ELEMENTS, or OPA_VLARB_PREEMPT_MATRIX
 * can be changed from the default values
 */

 case OPA_VLARB_PREEMPT_ELEMENTS:
 case OPA_VLARB_PREEMPT_MATRIX:
  smp->status |= IB_SMP_UNSUP_METH_ATTR;
  break;
 default:
  pr_warn("OPA SubnSet(VL Arb) AM Invalid : 0x%x\n",
   be32_to_cpu(smp->attr_mod));
  smp->status |= IB_SMP_INVALID_FIELD;
  break;
 }

 return __subn_get_opa_vl_arb(smp, am, data, ibdev, port, resp_len,
         max_len);
}

struct opa_pma_mad {
 struct ib_mad_hdr mad_hdr;
 u8 data[2024];
} __packed;

struct opa_port_status_req {
 __u8 port_num;
 __u8 reserved[3];
 __be32 vl_select_mask;
};

#define VL_MASK_ALL  0x00000000000080ffUL

struct opa_port_status_rsp {
 __u8 port_num;
 __u8 reserved[3];
 __be32  vl_select_mask;

 /* Data counters */
 __be64 port_xmit_data;
 __be64 port_rcv_data;
 __be64 port_xmit_pkts;
 __be64 port_rcv_pkts;
 __be64 port_multicast_xmit_pkts;
 __be64 port_multicast_rcv_pkts;
 __be64 port_xmit_wait;
 __be64 sw_port_congestion;
 __be64 port_rcv_fecn;
 __be64 port_rcv_becn;
 __be64 port_xmit_time_cong;
 __be64 port_xmit_wasted_bw;
 __be64 port_xmit_wait_data;
 __be64 port_rcv_bubble;
 __be64 port_mark_fecn;
 /* Error counters */
 __be64 port_rcv_constraint_errors;
 __be64 port_rcv_switch_relay_errors;
 __be64 port_xmit_discards;
 __be64 port_xmit_constraint_errors;
 __be64 port_rcv_remote_physical_errors;
 __be64 local_link_integrity_errors;
 __be64 port_rcv_errors;
 __be64 excessive_buffer_overruns;
 __be64 fm_config_errors;
 __be32 link_error_recovery;
 __be32 link_downed;
 u8 uncorrectable_errors;

 u8 link_quality_indicator; /* 5res, 3bit */
 u8 res2[6];
 struct _vls_pctrs {
  /* per-VL Data counters */
  __be64 port_vl_xmit_data;
  __be64 port_vl_rcv_data;
  __be64 port_vl_xmit_pkts;
  __be64 port_vl_rcv_pkts;
  __be64 port_vl_xmit_wait;
  __be64 sw_port_vl_congestion;
  __be64 port_vl_rcv_fecn;
  __be64 port_vl_rcv_becn;
  __be64 port_xmit_time_cong;
  __be64 port_vl_xmit_wasted_bw;
  __be64 port_vl_xmit_wait_data;
  __be64 port_vl_rcv_bubble;
  __be64 port_vl_mark_fecn;
  __be64 port_vl_xmit_discards;
 } vls[]; /* real array size defined by # bits set in vl_select_mask */
};

enum counter_selects {
 CS_PORT_XMIT_DATA   = (1 << 31),
 CS_PORT_RCV_DATA   = (1 << 30),
 CS_PORT_XMIT_PKTS   = (1 << 29),
 CS_PORT_RCV_PKTS   = (1 << 28),
 CS_PORT_MCAST_XMIT_PKTS   = (1 << 27),
 CS_PORT_MCAST_RCV_PKTS   = (1 << 26),
--> --------------------

--> maximum size reached

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

Messung V0.5
C=97 H=97 G=96

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