IB/hfi1: Add 16B UD support
[linux-2.6-microblaze.git] / drivers / infiniband / hw / hfi1 / mad.c
index 5977673..1509bc6 100644 (file)
@@ -46,6 +46,7 @@
  */
 
 #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)))
 
 #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)
 {
        /*
@@ -89,28 +108,156 @@ void hfi1_event_pkey_change(struct hfi1_devdata *dd, u8 port)
        ib_dispatch_event(&event);
 }
 
-static void send_trap(struct hfi1_ibport *ibp, void *data, unsigned len)
+/*
+ * 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;
+
+       /*
+        * 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[trap->data.generic_type & 0x0F];
+
+       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: Maximim 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 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;
-       int ret;
        unsigned long flags;
-       unsigned long timeout;
        int pkey_idx;
        u32 qpn = ppd_from_ibp(ibp)->sm_trap_qp;
 
        agent = ibp->rvp.send_agent;
-       if (!agent)
+       if (!agent) {
+               cleanup_traps(ibp, trap);
                return;
+       }
 
        /* o14-3.2.1 */
-       if (ppd_from_ibp(ibp)->lstate != IB_PORT_ACTIVE)
+       if (driver_lstate(ppd_from_ibp(ibp)) != IB_PORT_ACTIVE) {
+               cleanup_traps(ibp, trap);
                return;
+       }
 
-       /* o14-2 */
-       if (ibp->rvp.trap_timeout && time_before(jiffies,
-                                                ibp->rvp.trap_timeout))
+       /* 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);
@@ -131,11 +278,21 @@ static void send_trap(struct hfi1_ibport *ibp, void *data, unsigned len)
        smp->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
        smp->class_version = OPA_SM_CLASS_VERSION;
        smp->method = IB_MGMT_METHOD_TRAP;
-       ibp->rvp.tid++;
-       smp->tid = cpu_to_be64(ibp->rvp.tid);
+
+       /* 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, data, len);
+
+       memcpy(smp->route.lid.data, &trap->data, trap->len);
 
        spin_lock_irqsave(&ibp->rvp.lock, flags);
        if (!ibp->rvp.sm_ah) {
@@ -144,65 +301,101 @@ static void send_trap(struct hfi1_ibport *ibp, void *data, unsigned len)
 
                        ah = hfi1_create_qp0_ah(ibp, ibp->rvp.sm_lid);
                        if (IS_ERR(ah)) {
-                               ret = PTR_ERR(ah);
-                       } else {
-                               send_buf->ah = ah;
-                               ibp->rvp.sm_ah = ibah_to_rvtah(ah);
-                               ret = 0;
+                               spin_unlock_irqrestore(&ibp->rvp.lock, flags);
+                               return;
                        }
+                       send_buf->ah = ah;
+                       ibp->rvp.sm_ah = ibah_to_rvtah(ah);
                } else {
-                       ret = -EINVAL;
+                       spin_unlock_irqrestore(&ibp->rvp.lock, flags);
+                       return;
                }
        } else {
                send_buf->ah = &ibp->rvp.sm_ah->ibah;
-               ret = 0;
        }
+
+       /*
+        * 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 (!ret)
-               ret = ib_post_send_mad(send_buf, NULL);
-       if (!ret) {
-               /* 4.096 usec. */
-               timeout = (4096 * (1UL << ibp->rvp.subnet_timeout)) / 1000;
-               ibp->rvp.trap_timeout = jiffies + usecs_to_jiffies(timeout);
-       } else {
+       if (ib_post_send_mad(send_buf, NULL))
                ib_free_send_mad(send_buf);
-               ibp->rvp.trap_timeout = 0;
+}
+
+void hfi1_handle_trap_timer(unsigned long data)
+{
+       struct hfi1_ibport *ibp = (struct hfi1_ibport *)data;
+       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 [PQ]_Key trap (ch. 14.3.8).
+ * Send a bad P_Key trap (ch. 14.3.8).
  */
-void hfi1_bad_pqkey(struct hfi1_ibport *ibp, __be16 trap_num, u32 key, u32 sl,
-                   u32 qp1, u32 qp2, u16 lid1, u16 lid2)
+void hfi1_bad_pkey(struct hfi1_ibport *ibp, u32 key, u32 sl,
+                  u32 qp1, u32 qp2, u32 lid1, u32 lid2)
 {
-       struct opa_mad_notice_attr data;
+       struct trap_node *trap;
        u32 lid = ppd_from_ibp(ibp)->lid;
-       u32 _lid1 = lid1;
-       u32 _lid2 = lid2;
-
-       memset(&data, 0, sizeof(data));
 
-       if (trap_num == OPA_TRAP_BAD_P_KEY)
-               ibp->rvp.pkey_violations++;
-       else
-               ibp->rvp.qkey_violations++;
        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 */
-       data.generic_type = IB_NOTICE_TYPE_SECURITY;
-       data.prod_type_lsb = IB_NOTICE_PROD_CA;
-       data.trap_num = trap_num;
-       data.issuer_lid = cpu_to_be32(lid);
-       data.ntc_257_258.lid1 = cpu_to_be32(_lid1);
-       data.ntc_257_258.lid2 = cpu_to_be32(_lid2);
-       data.ntc_257_258.key = cpu_to_be32(key);
-       data.ntc_257_258.sl = sl << 3;
-       data.ntc_257_258.qp1 = cpu_to_be32(qp1);
-       data.ntc_257_258.qp2 = cpu_to_be32(qp2);
-
-       send_trap(ibp, &data, sizeof(data));
+       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);
 }
 
 /*
@@ -211,34 +404,36 @@ void hfi1_bad_pqkey(struct hfi1_ibport *ibp, __be16 trap_num, u32 key, u32 sl,
 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 opa_mad_notice_attr data;
+       struct trap_node *trap;
        u32 lid = ppd_from_ibp(ibp)->lid;
 
-       memset(&data, 0, sizeof(data));
+       trap = create_trap_node(IB_NOTICE_TYPE_SECURITY, OPA_TRAP_BAD_M_KEY,
+                               lid);
+       if (!trap)
+               return;
+
        /* Send violation trap */
-       data.generic_type = IB_NOTICE_TYPE_SECURITY;
-       data.prod_type_lsb = IB_NOTICE_PROD_CA;
-       data.trap_num = OPA_TRAP_BAD_M_KEY;
-       data.issuer_lid = cpu_to_be32(lid);
-       data.ntc_256.lid = data.issuer_lid;
-       data.ntc_256.method = mad->method;
-       data.ntc_256.attr_id = mad->attr_id;
-       data.ntc_256.attr_mod = mad->attr_mod;
-       data.ntc_256.mkey = mkey;
+       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) {
-               data.ntc_256.dr_slid = dr_slid;
-               data.ntc_256.dr_trunc_hop = IB_NOTICE_TRAP_DR_NOTICE;
-               if (hop_cnt > ARRAY_SIZE(data.ntc_256.dr_rtn_path)) {
-                       data.ntc_256.dr_trunc_hop |=
+               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(data.ntc_256.dr_rtn_path);
+                       hop_cnt = ARRAY_SIZE(trap->data.ntc_256.dr_rtn_path);
                }
-               data.ntc_256.dr_trunc_hop |= hop_cnt;
-               memcpy(data.ntc_256.dr_rtn_path, return_path,
+               trap->data.ntc_256.dr_trunc_hop |= hop_cnt;
+               memcpy(trap->data.ntc_256.dr_rtn_path, return_path,
                       hop_cnt);
        }
 
-       send_trap(ibp, &data, sizeof(data));
+       trap->len = sizeof(trap->data);
+
+       send_trap(ibp, trap);
 }
 
 /*
@@ -246,22 +441,24 @@ static void bad_mkey(struct hfi1_ibport *ibp, struct ib_mad_hdr *mad,
  */
 void hfi1_cap_mask_chg(struct rvt_dev_info *rdi, u8 port_num)
 {
-       struct opa_mad_notice_attr data;
+       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;
 
-       memset(&data, 0, sizeof(data));
+       trap = create_trap_node(IB_NOTICE_TYPE_INFO,
+                               OPA_TRAP_CHANGE_CAPABILITY,
+                               lid);
+       if (!trap)
+               return;
 
-       data.generic_type = IB_NOTICE_TYPE_INFO;
-       data.prod_type_lsb = IB_NOTICE_PROD_CA;
-       data.trap_num = OPA_TRAP_CHANGE_CAPABILITY;
-       data.issuer_lid = cpu_to_be32(lid);
-       data.ntc_144.lid = data.issuer_lid;
-       data.ntc_144.new_cap_mask = cpu_to_be32(ibp->rvp.port_cap_flags);
+       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);
 
-       send_trap(ibp, &data, sizeof(data));
+       trap->len = sizeof(trap->data);
+       send_trap(ibp, trap);
 }
 
 /*
@@ -269,19 +466,19 @@ void hfi1_cap_mask_chg(struct rvt_dev_info *rdi, u8 port_num)
  */
 void hfi1_sys_guid_chg(struct hfi1_ibport *ibp)
 {
-       struct opa_mad_notice_attr data;
+       struct trap_node *trap;
        u32 lid = ppd_from_ibp(ibp)->lid;
 
-       memset(&data, 0, sizeof(data));
+       trap = create_trap_node(IB_NOTICE_TYPE_INFO, OPA_TRAP_CHANGE_SYSGUID,
+                               lid);
+       if (!trap)
+               return;
 
-       data.generic_type = IB_NOTICE_TYPE_INFO;
-       data.prod_type_lsb = IB_NOTICE_PROD_CA;
-       data.trap_num = OPA_TRAP_CHANGE_SYSGUID;
-       data.issuer_lid = cpu_to_be32(lid);
-       data.ntc_145.new_sys_guid = ib_hfi1_sys_image_guid;
-       data.ntc_145.lid = data.issuer_lid;
+       trap->data.ntc_145.new_sys_guid = ib_hfi1_sys_image_guid;
+       trap->data.ntc_145.lid = trap->data.issuer_lid;
 
-       send_trap(ibp, &data, sizeof(data));
+       trap->len = sizeof(trap->data);
+       send_trap(ibp, trap);
 }
 
 /*
@@ -289,29 +486,30 @@ void hfi1_sys_guid_chg(struct hfi1_ibport *ibp)
  */
 void hfi1_node_desc_chg(struct hfi1_ibport *ibp)
 {
-       struct opa_mad_notice_attr data;
+       struct trap_node *trap;
        u32 lid = ppd_from_ibp(ibp)->lid;
 
-       memset(&data, 0, sizeof(data));
+       trap = create_trap_node(IB_NOTICE_TYPE_INFO,
+                               OPA_TRAP_CHANGE_CAPABILITY,
+                               lid);
+       if (!trap)
+               return;
 
-       data.generic_type = IB_NOTICE_TYPE_INFO;
-       data.prod_type_lsb = IB_NOTICE_PROD_CA;
-       data.trap_num = OPA_TRAP_CHANGE_CAPABILITY;
-       data.issuer_lid = cpu_to_be32(lid);
-       data.ntc_144.lid = data.issuer_lid;
-       data.ntc_144.change_flags =
+       trap->data.ntc_144.lid = trap->data.issuer_lid;
+       trap->data.ntc_144.change_flags =
                cpu_to_be16(OPA_NOTICE_TRAP_NODE_DESC_CHG);
 
-       send_trap(ibp, &data, sizeof(data));
+       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,
-                                  u8 port, u32 *resp_len)
+                                  u8 port, u32 *resp_len, u32 max_len)
 {
        struct opa_node_description *nd;
 
-       if (am) {
+       if (am || smp_length_check(sizeof(*nd), max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -328,7 +526,7 @@ static int __subn_get_opa_nodedesc(struct opa_smp *smp, u32 am,
 
 static int __subn_get_opa_nodeinfo(struct opa_smp *smp, u32 am, u8 *data,
                                   struct ib_device *ibdev, u8 port,
-                                  u32 *resp_len)
+                                  u32 *resp_len, u32 max_len)
 {
        struct opa_node_info *ni;
        struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
@@ -338,6 +536,7 @@ static int __subn_get_opa_nodeinfo(struct opa_smp *smp, u32 am, u8 *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);
@@ -519,7 +718,7 @@ void read_ltp_rtt(struct hfi1_devdata *dd)
 
 static int __subn_get_opa_portinfo(struct opa_smp *smp, u32 am, u8 *data,
                                   struct ib_device *ibdev, u8 port,
-                                  u32 *resp_len)
+                                  u32 *resp_len, u32 max_len)
 {
        int i;
        struct hfi1_devdata *dd;
@@ -535,7 +734,7 @@ static int __subn_get_opa_portinfo(struct opa_smp *smp, u32 am, u8 *data,
        u32 buffer_units;
        u64 tmp = 0;
 
-       if (num_ports != 1) {
+       if (num_ports != 1 || smp_length_check(sizeof(*pi), max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -605,7 +804,7 @@ static int __subn_get_opa_portinfo(struct opa_smp *smp, u32 am, u8 *data,
                ppd->offline_disabled_reason;
 
        pi->port_states.portphysstate_portstate =
-               (hfi1_ibphys_portstate(ppd) << 4) | state;
+               (driver_pstate(ppd) << 4) | state;
 
        pi->mkeyprotect_lmc = (ibp->rvp.mkeyprot << 6) | ppd->lmc;
 
@@ -704,13 +903,9 @@ static int __subn_get_opa_portinfo(struct opa_smp *smp, u32 am, u8 *data,
        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(OPA_CAP_MASK3_IsSharedSpaceSupported |
-                                      OPA_CAP_MASK3_IsEthOnFabricSupported);
-       /* Driver does not support mcast/collective configuration */
-       pi->opa_cap_mask &=
-               cpu_to_be16(~OPA_CAP_MASK3_IsAddrRangeConfigSupported);
-       pi->collectivemask_multicastmask = ((HFI1_COLLECTIVE_NR & 0x7)
-                                           << 3 | (HFI1_MCAST_NR & 0x7));
+       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;
@@ -748,7 +943,7 @@ static int get_pkeys(struct hfi1_devdata *dd, u8 port, u16 *pkeys)
 
 static int __subn_get_opa_pkeytable(struct opa_smp *smp, u32 am, u8 *data,
                                    struct ib_device *ibdev, u8 port,
-                                   u32 *resp_len)
+                                   u32 *resp_len, u32 max_len)
 {
        struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
        u32 n_blocks_req = OPA_AM_NBLK(am);
@@ -771,6 +966,11 @@ static int __subn_get_opa_pkeytable(struct opa_smp *smp, u32 am, u8 *data,
 
        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; "
@@ -916,7 +1116,7 @@ static int port_states_transition_allowed(struct hfi1_pportdata *ppd,
                                          u32 logical_new, u32 physical_new)
 {
        u32 physical_old = driver_physical_state(ppd);
-       u32 logical_old = driver_logical_state(ppd);
+       u32 logical_old = driver_lstate(ppd);
        int ret, logical_allowed, physical_allowed;
 
        ret = logical_transition_allowed(logical_old, logical_new);
@@ -1074,7 +1274,7 @@ static int set_port_states(struct hfi1_pportdata *ppd, struct opa_smp *smp,
  */
 static int __subn_set_opa_portinfo(struct opa_smp *smp, u32 am, u8 *data,
                                   struct ib_device *ibdev, u8 port,
-                                  u32 *resp_len)
+                                  u32 *resp_len, u32 max_len)
 {
        struct opa_port_info *pi = (struct opa_port_info *)data;
        struct ib_event event;
@@ -1095,7 +1295,8 @@ static int __subn_set_opa_portinfo(struct opa_smp *smp, u32 am, u8 *data,
        int ret, i, invalid = 0, call_set_mtu = 0;
        int call_link_downgrade_policy = 0;
 
-       if (num_ports != 1) {
+       if (num_ports != 1 ||
+           smp_length_check(sizeof(*pi), max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1346,7 +1547,8 @@ static int __subn_set_opa_portinfo(struct opa_smp *smp, u32 am, u8 *data,
        if (ret)
                return ret;
 
-       ret = __subn_get_opa_portinfo(smp, am, data, ibdev, port, resp_len);
+       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;
@@ -1363,7 +1565,8 @@ static int __subn_set_opa_portinfo(struct opa_smp *smp, u32 am, u8 *data,
        return ret;
 
 get_only:
-       return __subn_get_opa_portinfo(smp, am, data, ibdev, port, resp_len);
+       return __subn_get_opa_portinfo(smp, am, data, ibdev, port, resp_len,
+                                      max_len);
 }
 
 /**
@@ -1424,7 +1627,7 @@ static int set_pkeys(struct hfi1_devdata *dd, u8 port, u16 *pkeys)
 
 static int __subn_set_opa_pkeytable(struct opa_smp *smp, u32 am, u8 *data,
                                    struct ib_device *ibdev, u8 port,
-                                   u32 *resp_len)
+                                   u32 *resp_len, u32 max_len)
 {
        struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
        u32 n_blocks_sent = OPA_AM_NBLK(am);
@@ -1434,6 +1637,7 @@ static int __subn_set_opa_pkeytable(struct opa_smp *smp, u32 am, u8 *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 = %d; B = 0x%x; N = 0x%x\n",
@@ -1444,6 +1648,13 @@ static int __subn_set_opa_pkeytable(struct opa_smp *smp, u32 am, u8 *data,
 
        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",
@@ -1461,7 +1672,8 @@ static int __subn_set_opa_pkeytable(struct opa_smp *smp, u32 am, u8 *data,
                return reply((struct ib_mad_hdr *)smp);
        }
 
-       return __subn_get_opa_pkeytable(smp, am, data, ibdev, port, resp_len);
+       return __subn_get_opa_pkeytable(smp, am, data, ibdev, port, resp_len,
+                                       max_len);
 }
 
 #define ILLEGAL_VL 12
@@ -1522,14 +1734,14 @@ static int get_sc2vlt_tables(struct hfi1_devdata *dd, void *data)
 
 static int __subn_get_opa_sl_to_sc(struct opa_smp *smp, u32 am, u8 *data,
                                   struct ib_device *ibdev, u8 port,
-                                  u32 *resp_len)
+                                  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) {
+       if (am || smp_length_check(size, max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1545,14 +1757,15 @@ static int __subn_get_opa_sl_to_sc(struct opa_smp *smp, u32 am, u8 *data,
 
 static int __subn_set_opa_sl_to_sc(struct opa_smp *smp, u32 am, u8 *data,
                                   struct ib_device *ibdev, u8 port,
-                                  u32 *resp_len)
+                                  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) {
+       if (am || smp_length_check(size, max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1567,19 +1780,20 @@ static int __subn_set_opa_sl_to_sc(struct opa_smp *smp, u32 am, u8 *data,
                }
        }
 
-       return __subn_get_opa_sl_to_sc(smp, am, data, ibdev, port, resp_len);
+       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, u8 port,
-                                  u32 *resp_len)
+                                  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) {
+       if (am || smp_length_check(size, max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1595,13 +1809,14 @@ static int __subn_get_opa_sc_to_sl(struct opa_smp *smp, u32 am, u8 *data,
 
 static int __subn_set_opa_sc_to_sl(struct opa_smp *smp, u32 am, u8 *data,
                                   struct ib_device *ibdev, u8 port,
-                                  u32 *resp_len)
+                                  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) {
+       if (am || smp_length_check(size, max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1609,19 +1824,20 @@ static int __subn_set_opa_sc_to_sl(struct opa_smp *smp, u32 am, u8 *data,
        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);
+       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, u8 port,
-                                   u32 *resp_len)
+                                   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) {
+       if (n_blocks != 1 || smp_length_check(size, max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1636,7 +1852,7 @@ static int __subn_get_opa_sc_to_vlt(struct opa_smp *smp, u32 am, u8 *data,
 
 static int __subn_set_opa_sc_to_vlt(struct opa_smp *smp, u32 am, u8 *data,
                                    struct ib_device *ibdev, u8 port,
-                                   u32 *resp_len)
+                                   u32 *resp_len, u32 max_len)
 {
        u32 n_blocks = OPA_AM_NBLK(am);
        int async_update = OPA_AM_ASYNC(am);
@@ -1644,8 +1860,15 @@ static int __subn_set_opa_sc_to_vlt(struct opa_smp *smp, u32 am, u8 *data,
        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) {
+       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);
        }
@@ -1665,27 +1888,28 @@ static int __subn_set_opa_sc_to_vlt(struct opa_smp *smp, u32 am, u8 *data,
 
        set_sc2vlt_tables(dd, vp);
 
-       return __subn_get_opa_sc_to_vlt(smp, am, data, ibdev, port, resp_len);
+       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, u8 port,
-                                    u32 *resp_len)
+                                    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;
+       int size = sizeof(struct sc2vlnt);
 
-       if (n_blocks != 1) {
+       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);
 
-       size = fm_get_table(ppd, FM_TBL_SC2VLNT, vp);
+       fm_get_table(ppd, FM_TBL_SC2VLNT, vp);
 
        if (resp_len)
                *resp_len += size;
@@ -1695,15 +1919,16 @@ static int __subn_get_opa_sc_to_vlnt(struct opa_smp *smp, u32 am, u8 *data,
 
 static int __subn_set_opa_sc_to_vlnt(struct opa_smp *smp, u32 am, u8 *data,
                                     struct ib_device *ibdev, u8 port,
-                                    u32 *resp_len)
+                                    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) {
+       if (n_blocks != 1 || smp_length_check(size, max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1721,12 +1946,12 @@ static int __subn_set_opa_sc_to_vlnt(struct opa_smp *smp, u32 am, u8 *data,
        fm_set_table(ppd, FM_TBL_SC2VLNT, vp);
 
        return __subn_get_opa_sc_to_vlnt(smp, am, data, ibdev, port,
-                                        resp_len);
+                                        resp_len, max_len);
 }
 
 static int __subn_get_opa_psi(struct opa_smp *smp, u32 am, u8 *data,
                              struct ib_device *ibdev, u8 port,
-                             u32 *resp_len)
+                             u32 *resp_len, u32 max_len)
 {
        u32 nports = OPA_AM_NPORT(am);
        u32 start_of_sm_config = OPA_AM_START_SM_CFG(am);
@@ -1735,7 +1960,7 @@ static int __subn_get_opa_psi(struct opa_smp *smp, u32 am, u8 *data,
        struct hfi1_pportdata *ppd;
        struct opa_port_state_info *psi = (struct opa_port_state_info *)data;
 
-       if (nports != 1) {
+       if (nports != 1 || smp_length_check(sizeof(*psi), max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1755,7 +1980,7 @@ static int __subn_get_opa_psi(struct opa_smp *smp, u32 am, u8 *data,
                ppd->offline_disabled_reason;
 
        psi->port_states.portphysstate_portstate =
-               (hfi1_ibphys_portstate(ppd) << 4) | (lstate & 0xf);
+               (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 =
@@ -1768,7 +1993,7 @@ static int __subn_get_opa_psi(struct opa_smp *smp, u32 am, u8 *data,
 
 static int __subn_set_opa_psi(struct opa_smp *smp, u32 am, u8 *data,
                              struct ib_device *ibdev, u8 port,
-                             u32 *resp_len)
+                             u32 *resp_len, u32 max_len)
 {
        u32 nports = OPA_AM_NPORT(am);
        u32 start_of_sm_config = OPA_AM_START_SM_CFG(am);
@@ -1779,7 +2004,7 @@ static int __subn_set_opa_psi(struct opa_smp *smp, u32 am, u8 *data,
        struct opa_port_state_info *psi = (struct opa_port_state_info *)data;
        int ret, invalid = 0;
 
-       if (nports != 1) {
+       if (nports != 1 || smp_length_check(sizeof(*psi), max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1809,19 +2034,21 @@ static int __subn_set_opa_psi(struct opa_smp *smp, u32 am, u8 *data,
        if (invalid)
                smp->status |= IB_SMP_INVALID_FIELD;
 
-       return __subn_get_opa_psi(smp, am, data, ibdev, port, resp_len);
+       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, u8 port,
-                                    u32 *resp_len)
+                                    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) {
+       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);
        }
@@ -1864,21 +2091,22 @@ static int __subn_get_opa_cable_info(struct opa_smp *smp, u32 am, u8 *data,
 }
 
 static int __subn_get_opa_bct(struct opa_smp *smp, u32 am, u8 *data,
-                             struct ib_device *ibdev, u8 port, u32 *resp_len)
+                             struct ib_device *ibdev, u8 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;
+       int size = sizeof(struct buffer_control);
 
-       if (num_ports != 1) {
+       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);
-       size = fm_get_table(ppd, FM_TBL_BUFFER_CONTROL, p);
+       fm_get_table(ppd, FM_TBL_BUFFER_CONTROL, p);
        trace_bct_get(dd, p);
        if (resp_len)
                *resp_len += size;
@@ -1887,14 +2115,15 @@ static int __subn_get_opa_bct(struct opa_smp *smp, u32 am, u8 *data,
 }
 
 static int __subn_set_opa_bct(struct opa_smp *smp, u32 am, u8 *data,
-                             struct ib_device *ibdev, u8 port, u32 *resp_len)
+                             struct ib_device *ibdev, u8 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) {
+       if (num_ports != 1 || smp_length_check(sizeof(*p), max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1905,41 +2134,43 @@ static int __subn_set_opa_bct(struct opa_smp *smp, u32 am, u8 *data,
                return reply((struct ib_mad_hdr *)smp);
        }
 
-       return __subn_get_opa_bct(smp, am, data, ibdev, port, resp_len);
+       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, u8 port,
-                                u32 *resp_len)
+                                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 = 0;
+       int size = 256;
 
-       if (num_ports != 1) {
+       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:
-               size = fm_get_table(ppd, FM_TBL_VL_LOW_ARB, p);
+               fm_get_table(ppd, FM_TBL_VL_LOW_ARB, p);
                break;
        case OPA_VLARB_HIGH_ELEMENTS:
-               size = fm_get_table(ppd, FM_TBL_VL_HIGH_ARB, p);
+               fm_get_table(ppd, FM_TBL_VL_HIGH_ARB, p);
                break;
        case OPA_VLARB_PREEMPT_ELEMENTS:
-               size = fm_get_table(ppd, FM_TBL_VL_PREEMPT_ELEMS, p);
+               fm_get_table(ppd, FM_TBL_VL_PREEMPT_ELEMS, p);
                break;
        case OPA_VLARB_PREEMPT_MATRIX:
-               size = fm_get_table(ppd, FM_TBL_VL_PREEMPT_MATRIX, p);
+               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;
        }
 
@@ -1951,14 +2182,15 @@ static int __subn_get_opa_vl_arb(struct opa_smp *smp, u32 am, u8 *data,
 
 static int __subn_set_opa_vl_arb(struct opa_smp *smp, u32 am, u8 *data,
                                 struct ib_device *ibdev, u8 port,
-                                u32 *resp_len)
+                                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) {
+       if (num_ports != 1 || smp_length_check(size, max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -1986,7 +2218,8 @@ static int __subn_set_opa_vl_arb(struct opa_smp *smp, u32 am, u8 *data,
                break;
        }
 
-       return __subn_get_opa_vl_arb(smp, am, data, ibdev, port, resp_len);
+       return __subn_get_opa_vl_arb(smp, am, data, ibdev, port, resp_len,
+                                    max_len);
 }
 
 struct opa_pma_mad {
@@ -3282,13 +3515,18 @@ struct opa_congestion_info_attr {
 
 static int __subn_get_opa_cong_info(struct opa_smp *smp, u32 am, u8 *data,
                                    struct ib_device *ibdev, u8 port,
-                                   u32 *resp_len)
+                                   u32 *resp_len, u32 max_len)
 {
        struct opa_congestion_info_attr *p =
                (struct opa_congestion_info_attr *)data;
        struct hfi1_ibport *ibp = to_iport(ibdev, port);
        struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
 
+       if (smp_length_check(sizeof(*p), max_len)) {
+               smp->status |= IB_SMP_INVALID_FIELD;
+               return reply((struct ib_mad_hdr *)smp);
+       }
+
        p->congestion_info = 0;
        p->control_table_cap = ppd->cc_max_table_entries;
        p->congestion_log_length = OPA_CONG_LOG_ELEMS;
@@ -3301,7 +3539,7 @@ static int __subn_get_opa_cong_info(struct opa_smp *smp, u32 am, u8 *data,
 
 static int __subn_get_opa_cong_setting(struct opa_smp *smp, u32 am,
                                       u8 *data, struct ib_device *ibdev,
-                                      u8 port, u32 *resp_len)
+                                      u8 port, u32 *resp_len, u32 max_len)
 {
        int i;
        struct opa_congestion_setting_attr *p =
@@ -3311,6 +3549,11 @@ static int __subn_get_opa_cong_setting(struct opa_smp *smp, u32 am,
        struct opa_congestion_setting_entry_shadow *entries;
        struct cc_state *cc_state;
 
+       if (smp_length_check(sizeof(*p), max_len)) {
+               smp->status |= IB_SMP_INVALID_FIELD;
+               return reply((struct ib_mad_hdr *)smp);
+       }
+
        rcu_read_lock();
 
        cc_state = get_cc_state(ppd);
@@ -3385,7 +3628,7 @@ static void apply_cc_state(struct hfi1_pportdata *ppd)
 
 static int __subn_set_opa_cong_setting(struct opa_smp *smp, u32 am, u8 *data,
                                       struct ib_device *ibdev, u8 port,
-                                      u32 *resp_len)
+                                      u32 *resp_len, u32 max_len)
 {
        struct opa_congestion_setting_attr *p =
                (struct opa_congestion_setting_attr *)data;
@@ -3394,6 +3637,11 @@ static int __subn_set_opa_cong_setting(struct opa_smp *smp, u32 am, u8 *data,
        struct opa_congestion_setting_entry_shadow *entries;
        int i;
 
+       if (smp_length_check(sizeof(*p), max_len)) {
+               smp->status |= IB_SMP_INVALID_FIELD;
+               return reply((struct ib_mad_hdr *)smp);
+       }
+
        /*
         * Save details from packet into the ppd.  Hold the cc_state_lock so
         * our information is consistent with anyone trying to apply the state.
@@ -3415,12 +3663,12 @@ static int __subn_set_opa_cong_setting(struct opa_smp *smp, u32 am, u8 *data,
        apply_cc_state(ppd);
 
        return __subn_get_opa_cong_setting(smp, am, data, ibdev, port,
-                                          resp_len);
+                                          resp_len, max_len);
 }
 
 static int __subn_get_opa_hfi1_cong_log(struct opa_smp *smp, u32 am,
                                        u8 *data, struct ib_device *ibdev,
-                                       u8 port, u32 *resp_len)
+                                       u8 port, u32 *resp_len, u32 max_len)
 {
        struct hfi1_ibport *ibp = to_iport(ibdev, port);
        struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
@@ -3428,7 +3676,7 @@ static int __subn_get_opa_hfi1_cong_log(struct opa_smp *smp, u32 am,
        s64 ts;
        int i;
 
-       if (am != 0) {
+       if (am || smp_length_check(sizeof(*cong_log), max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -3486,7 +3734,7 @@ static int __subn_get_opa_hfi1_cong_log(struct opa_smp *smp, u32 am,
 
 static int __subn_get_opa_cc_table(struct opa_smp *smp, u32 am, u8 *data,
                                   struct ib_device *ibdev, u8 port,
-                                  u32 *resp_len)
+                                  u32 *resp_len, u32 max_len)
 {
        struct ib_cc_table_attr *cc_table_attr =
                (struct ib_cc_table_attr *)data;
@@ -3498,9 +3746,10 @@ static int __subn_get_opa_cc_table(struct opa_smp *smp, u32 am, u8 *data,
        int i, j;
        u32 sentry, eentry;
        struct cc_state *cc_state;
+       u32 size = sizeof(u16) * (IB_CCT_ENTRIES * n_blocks + 1);
 
        /* sanity check n_blocks, start_block */
-       if (n_blocks == 0 ||
+       if (n_blocks == 0 || smp_length_check(size, max_len) ||
            start_block + n_blocks > ppd->cc_max_table_entries) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
@@ -3530,14 +3779,14 @@ static int __subn_get_opa_cc_table(struct opa_smp *smp, u32 am, u8 *data,
        rcu_read_unlock();
 
        if (resp_len)
-               *resp_len += sizeof(u16) * (IB_CCT_ENTRIES * n_blocks + 1);
+               *resp_len += size;
 
        return reply((struct ib_mad_hdr *)smp);
 }
 
 static int __subn_set_opa_cc_table(struct opa_smp *smp, u32 am, u8 *data,
                                   struct ib_device *ibdev, u8 port,
-                                  u32 *resp_len)
+                                  u32 *resp_len, u32 max_len)
 {
        struct ib_cc_table_attr *p = (struct ib_cc_table_attr *)data;
        struct hfi1_ibport *ibp = to_iport(ibdev, port);
@@ -3548,9 +3797,10 @@ static int __subn_set_opa_cc_table(struct opa_smp *smp, u32 am, u8 *data,
        int i, j;
        u32 sentry, eentry;
        u16 ccti_limit;
+       u32 size = sizeof(u16) * (IB_CCT_ENTRIES * n_blocks + 1);
 
        /* sanity check n_blocks, start_block */
-       if (n_blocks == 0 ||
+       if (n_blocks == 0 || smp_length_check(size, max_len) ||
            start_block + n_blocks > ppd->cc_max_table_entries) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
@@ -3581,7 +3831,8 @@ static int __subn_set_opa_cc_table(struct opa_smp *smp, u32 am, u8 *data,
        /* now apply the information */
        apply_cc_state(ppd);
 
-       return __subn_get_opa_cc_table(smp, am, data, ibdev, port, resp_len);
+       return __subn_get_opa_cc_table(smp, am, data, ibdev, port, resp_len,
+                                      max_len);
 }
 
 struct opa_led_info {
@@ -3594,7 +3845,7 @@ struct opa_led_info {
 
 static int __subn_get_opa_led_info(struct opa_smp *smp, u32 am, u8 *data,
                                   struct ib_device *ibdev, u8 port,
-                                  u32 *resp_len)
+                                  u32 *resp_len, u32 max_len)
 {
        struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
        struct hfi1_pportdata *ppd = dd->pport;
@@ -3602,7 +3853,7 @@ static int __subn_get_opa_led_info(struct opa_smp *smp, u32 am, u8 *data,
        u32 nport = OPA_AM_NPORT(am);
        u32 is_beaconing_active;
 
-       if (nport != 1) {
+       if (nport != 1 || smp_length_check(sizeof(*p), max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -3624,14 +3875,14 @@ static int __subn_get_opa_led_info(struct opa_smp *smp, u32 am, u8 *data,
 
 static int __subn_set_opa_led_info(struct opa_smp *smp, u32 am, u8 *data,
                                   struct ib_device *ibdev, u8 port,
-                                  u32 *resp_len)
+                                  u32 *resp_len, u32 max_len)
 {
        struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
        struct opa_led_info *p = (struct opa_led_info *)data;
        u32 nport = OPA_AM_NPORT(am);
        int on = !!(be32_to_cpu(p->rsvd_led_mask) & OPA_LED_MASK);
 
-       if (nport != 1) {
+       if (nport != 1 || smp_length_check(sizeof(*p), max_len)) {
                smp->status |= IB_SMP_INVALID_FIELD;
                return reply((struct ib_mad_hdr *)smp);
        }
@@ -3641,12 +3892,13 @@ static int __subn_set_opa_led_info(struct opa_smp *smp, u32 am, u8 *data,
        else
                shutdown_led_override(dd->pport);
 
-       return __subn_get_opa_led_info(smp, am, data, ibdev, port, resp_len);
+       return __subn_get_opa_led_info(smp, am, data, ibdev, port, resp_len,
+                                      max_len);
 }
 
 static int subn_get_opa_sma(__be16 attr_id, struct opa_smp *smp, u32 am,
                            u8 *data, struct ib_device *ibdev, u8 port,
-                           u32 *resp_len)
+                           u32 *resp_len, u32 max_len)
 {
        int ret;
        struct hfi1_ibport *ibp = to_iport(ibdev, port);
@@ -3654,71 +3906,71 @@ static int subn_get_opa_sma(__be16 attr_id, struct opa_smp *smp, u32 am,
        switch (attr_id) {
        case IB_SMP_ATTR_NODE_DESC:
                ret = __subn_get_opa_nodedesc(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case IB_SMP_ATTR_NODE_INFO:
                ret = __subn_get_opa_nodeinfo(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case IB_SMP_ATTR_PORT_INFO:
                ret = __subn_get_opa_portinfo(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case IB_SMP_ATTR_PKEY_TABLE:
                ret = __subn_get_opa_pkeytable(smp, am, data, ibdev, port,
-                                              resp_len);
+                                              resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_SL_TO_SC_MAP:
                ret = __subn_get_opa_sl_to_sc(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_SC_TO_SL_MAP:
                ret = __subn_get_opa_sc_to_sl(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_SC_TO_VLT_MAP:
                ret = __subn_get_opa_sc_to_vlt(smp, am, data, ibdev, port,
-                                              resp_len);
+                                              resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_SC_TO_VLNT_MAP:
                ret = __subn_get_opa_sc_to_vlnt(smp, am, data, ibdev, port,
-                                               resp_len);
+                                               resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_PORT_STATE_INFO:
                ret = __subn_get_opa_psi(smp, am, data, ibdev, port,
-                                        resp_len);
+                                        resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_BUFFER_CONTROL_TABLE:
                ret = __subn_get_opa_bct(smp, am, data, ibdev, port,
-                                        resp_len);
+                                        resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_CABLE_INFO:
                ret = __subn_get_opa_cable_info(smp, am, data, ibdev, port,
-                                               resp_len);
+                                               resp_len, max_len);
                break;
        case IB_SMP_ATTR_VL_ARB_TABLE:
                ret = __subn_get_opa_vl_arb(smp, am, data, ibdev, port,
-                                           resp_len);
+                                           resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_CONGESTION_INFO:
                ret = __subn_get_opa_cong_info(smp, am, data, ibdev, port,
-                                              resp_len);
+                                              resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_HFI_CONGESTION_SETTING:
                ret = __subn_get_opa_cong_setting(smp, am, data, ibdev,
-                                                 port, resp_len);
+                                                 port, resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_HFI_CONGESTION_LOG:
                ret = __subn_get_opa_hfi1_cong_log(smp, am, data, ibdev,
-                                                  port, resp_len);
+                                                  port, resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_CONGESTION_CONTROL_TABLE:
                ret = __subn_get_opa_cc_table(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case IB_SMP_ATTR_LED_INFO:
                ret = __subn_get_opa_led_info(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case IB_SMP_ATTR_SM_INFO:
                if (ibp->rvp.port_cap_flags & IB_PORT_SM_DISABLED)
@@ -3736,7 +3988,7 @@ static int subn_get_opa_sma(__be16 attr_id, struct opa_smp *smp, u32 am,
 
 static int subn_set_opa_sma(__be16 attr_id, struct opa_smp *smp, u32 am,
                            u8 *data, struct ib_device *ibdev, u8 port,
-                           u32 *resp_len)
+                           u32 *resp_len, u32 max_len)
 {
        int ret;
        struct hfi1_ibport *ibp = to_iport(ibdev, port);
@@ -3744,51 +3996,51 @@ static int subn_set_opa_sma(__be16 attr_id, struct opa_smp *smp, u32 am,
        switch (attr_id) {
        case IB_SMP_ATTR_PORT_INFO:
                ret = __subn_set_opa_portinfo(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case IB_SMP_ATTR_PKEY_TABLE:
                ret = __subn_set_opa_pkeytable(smp, am, data, ibdev, port,
-                                              resp_len);
+                                              resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_SL_TO_SC_MAP:
                ret = __subn_set_opa_sl_to_sc(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_SC_TO_SL_MAP:
                ret = __subn_set_opa_sc_to_sl(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_SC_TO_VLT_MAP:
                ret = __subn_set_opa_sc_to_vlt(smp, am, data, ibdev, port,
-                                              resp_len);
+                                              resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_SC_TO_VLNT_MAP:
                ret = __subn_set_opa_sc_to_vlnt(smp, am, data, ibdev, port,
-                                               resp_len);
+                                               resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_PORT_STATE_INFO:
                ret = __subn_set_opa_psi(smp, am, data, ibdev, port,
-                                        resp_len);
+                                        resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_BUFFER_CONTROL_TABLE:
                ret = __subn_set_opa_bct(smp, am, data, ibdev, port,
-                                        resp_len);
+                                        resp_len, max_len);
                break;
        case IB_SMP_ATTR_VL_ARB_TABLE:
                ret = __subn_set_opa_vl_arb(smp, am, data, ibdev, port,
-                                           resp_len);
+                                           resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_HFI_CONGESTION_SETTING:
                ret = __subn_set_opa_cong_setting(smp, am, data, ibdev,
-                                                 port, resp_len);
+                                                 port, resp_len, max_len);
                break;
        case OPA_ATTRIB_ID_CONGESTION_CONTROL_TABLE:
                ret = __subn_set_opa_cc_table(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case IB_SMP_ATTR_LED_INFO:
                ret = __subn_set_opa_led_info(smp, am, data, ibdev, port,
-                                             resp_len);
+                                             resp_len, max_len);
                break;
        case IB_SMP_ATTR_SM_INFO:
                if (ibp->rvp.port_cap_flags & IB_PORT_SM_DISABLED)
@@ -3844,7 +4096,10 @@ static int subn_get_opa_aggregate(struct opa_smp *smp,
                memset(next_smp + sizeof(*agg), 0, agg_data_len);
 
                (void)subn_get_opa_sma(agg->attr_id, smp, am, agg->data,
-                                       ibdev, port, NULL);
+                                      ibdev, port, NULL, (u32)agg_data_len);
+
+               if (smp->status & IB_SMP_INVALID_FIELD)
+                       break;
                if (smp->status & ~IB_SMP_DIRECTION) {
                        set_aggr_error(agg);
                        return reply((struct ib_mad_hdr *)smp);
@@ -3887,7 +4142,9 @@ static int subn_set_opa_aggregate(struct opa_smp *smp,
                }
 
                (void)subn_set_opa_sma(agg->attr_id, smp, am, agg->data,
-                                       ibdev, port, NULL);
+                                      ibdev, port, NULL, (u32)agg_data_len);
+               if (smp->status & IB_SMP_INVALID_FIELD)
+                       break;
                if (smp->status & ~IB_SMP_DIRECTION) {
                        set_aggr_error(agg);
                        return reply((struct ib_mad_hdr *)smp);
@@ -3958,7 +4215,7 @@ static int opa_local_smp_check(struct hfi1_ibport *ibp,
                               const struct ib_wc *in_wc)
 {
        struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
-       u16 slid = in_wc->slid;
+       u16 slid = ib_lid_cpu16(in_wc->slid);
        u16 pkey;
 
        if (in_wc->pkey_index >= ARRAY_SIZE(ppd->pkeys))
@@ -3997,12 +4254,13 @@ static int process_subn_opa(struct ib_device *ibdev, int mad_flags,
        struct opa_smp *smp = (struct opa_smp *)out_mad;
        struct hfi1_ibport *ibp = to_iport(ibdev, port);
        u8 *data;
-       u32 am;
+       u32 am, data_size;
        __be16 attr_id;
        int ret;
 
        *out_mad = *in_mad;
        data = opa_get_smp_data(smp);
+       data_size = (u32)opa_get_smp_data_size(smp);
 
        am = be32_to_cpu(smp->attr_mod);
        attr_id = smp->attr_id;
@@ -4046,7 +4304,8 @@ static int process_subn_opa(struct ib_device *ibdev, int mad_flags,
                default:
                        clear_opa_smp_data(smp);
                        ret = subn_get_opa_sma(attr_id, smp, am, data,
-                                              ibdev, port, resp_len);
+                                              ibdev, port, resp_len,
+                                              data_size);
                        break;
                case OPA_ATTRIB_ID_AGGREGATE:
                        ret = subn_get_opa_aggregate(smp, ibdev, port,
@@ -4058,7 +4317,8 @@ static int process_subn_opa(struct ib_device *ibdev, int mad_flags,
                switch (attr_id) {
                default:
                        ret = subn_set_opa_sma(attr_id, smp, am, data,
-                                              ibdev, port, resp_len);
+                                              ibdev, port, resp_len,
+                                              data_size);
                        break;
                case OPA_ATTRIB_ID_AGGREGATE:
                        ret = subn_set_opa_aggregate(smp, ibdev, port,
@@ -4077,6 +4337,11 @@ static int process_subn_opa(struct ib_device *ibdev, int mad_flags,
                 */
                ret = IB_MAD_RESULT_SUCCESS;
                break;
+       case IB_MGMT_METHOD_TRAP_REPRESS:
+               subn_handle_opa_trap_repress(ibp, smp);
+               /* Always successful */
+               ret = IB_MAD_RESULT_SUCCESS;
+               break;
        default:
                smp->status |= IB_SMP_UNSUP_METHOD;
                ret = reply((struct ib_mad_hdr *)smp);