in hw/hfi1/mad.c [1309:1620]
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);
}