[SCSI] libfcoe: Incorrect CVL handling for NPIV ports
Host doesnt handle CVL to NPIV instantiated ports correctly. - As per FC-BB-5 Rev 2 CVLs with no VN_Port descriptors shall be treated as implicit logout of ALL vn_ports. - CVL for NPIV ports should be handled before physical port even if descriptor for physical port appears before NPIV ports Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com> Signed-off-by: Robert Love <robert.w.love@intel.com> Signed-off-by: James Bottomley <jbottomley@parallels.com>
This commit is contained in:
parent
4f788dce0b
commit
c051ad2e57
@ -1173,7 +1173,9 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
|
|||||||
struct fc_lport *lport = fip->lp;
|
struct fc_lport *lport = fip->lp;
|
||||||
struct fc_lport *vn_port = NULL;
|
struct fc_lport *vn_port = NULL;
|
||||||
u32 desc_mask;
|
u32 desc_mask;
|
||||||
int is_vn_port = 0;
|
int num_vlink_desc;
|
||||||
|
int reset_phys_port = 0;
|
||||||
|
struct fip_vn_desc **vlink_desc_arr = NULL;
|
||||||
|
|
||||||
LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n");
|
LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n");
|
||||||
|
|
||||||
@ -1183,70 +1185,73 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
|
|||||||
/*
|
/*
|
||||||
* mask of required descriptors. Validating each one clears its bit.
|
* mask of required descriptors. Validating each one clears its bit.
|
||||||
*/
|
*/
|
||||||
desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) | BIT(FIP_DT_VN_ID);
|
desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME);
|
||||||
|
|
||||||
rlen = ntohs(fh->fip_dl_len) * FIP_BPW;
|
rlen = ntohs(fh->fip_dl_len) * FIP_BPW;
|
||||||
desc = (struct fip_desc *)(fh + 1);
|
desc = (struct fip_desc *)(fh + 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Actually need to subtract 'sizeof(*mp) - sizeof(*wp)' from 'rlen'
|
||||||
|
* before determining max Vx_Port descriptor but a buggy FCF could have
|
||||||
|
* omited either or both MAC Address and Name Identifier descriptors
|
||||||
|
*/
|
||||||
|
num_vlink_desc = rlen / sizeof(*vp);
|
||||||
|
if (num_vlink_desc)
|
||||||
|
vlink_desc_arr = kmalloc(sizeof(vp) * num_vlink_desc,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
if (!vlink_desc_arr)
|
||||||
|
return;
|
||||||
|
num_vlink_desc = 0;
|
||||||
|
|
||||||
while (rlen >= sizeof(*desc)) {
|
while (rlen >= sizeof(*desc)) {
|
||||||
dlen = desc->fip_dlen * FIP_BPW;
|
dlen = desc->fip_dlen * FIP_BPW;
|
||||||
if (dlen > rlen)
|
if (dlen > rlen)
|
||||||
return;
|
goto err;
|
||||||
/* Drop CVL if there are duplicate critical descriptors */
|
/* Drop CVL if there are duplicate critical descriptors */
|
||||||
if ((desc->fip_dtype < 32) &&
|
if ((desc->fip_dtype < 32) &&
|
||||||
|
(desc->fip_dtype != FIP_DT_VN_ID) &&
|
||||||
!(desc_mask & 1U << desc->fip_dtype)) {
|
!(desc_mask & 1U << desc->fip_dtype)) {
|
||||||
LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
|
LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
|
||||||
"Descriptors in FIP CVL\n");
|
"Descriptors in FIP CVL\n");
|
||||||
return;
|
goto err;
|
||||||
}
|
}
|
||||||
switch (desc->fip_dtype) {
|
switch (desc->fip_dtype) {
|
||||||
case FIP_DT_MAC:
|
case FIP_DT_MAC:
|
||||||
mp = (struct fip_mac_desc *)desc;
|
mp = (struct fip_mac_desc *)desc;
|
||||||
if (dlen < sizeof(*mp))
|
if (dlen < sizeof(*mp))
|
||||||
return;
|
goto err;
|
||||||
if (compare_ether_addr(mp->fd_mac, fcf->fcf_mac))
|
if (compare_ether_addr(mp->fd_mac, fcf->fcf_mac))
|
||||||
return;
|
goto err;
|
||||||
desc_mask &= ~BIT(FIP_DT_MAC);
|
desc_mask &= ~BIT(FIP_DT_MAC);
|
||||||
break;
|
break;
|
||||||
case FIP_DT_NAME:
|
case FIP_DT_NAME:
|
||||||
wp = (struct fip_wwn_desc *)desc;
|
wp = (struct fip_wwn_desc *)desc;
|
||||||
if (dlen < sizeof(*wp))
|
if (dlen < sizeof(*wp))
|
||||||
return;
|
goto err;
|
||||||
if (get_unaligned_be64(&wp->fd_wwn) != fcf->switch_name)
|
if (get_unaligned_be64(&wp->fd_wwn) != fcf->switch_name)
|
||||||
return;
|
goto err;
|
||||||
desc_mask &= ~BIT(FIP_DT_NAME);
|
desc_mask &= ~BIT(FIP_DT_NAME);
|
||||||
break;
|
break;
|
||||||
case FIP_DT_VN_ID:
|
case FIP_DT_VN_ID:
|
||||||
vp = (struct fip_vn_desc *)desc;
|
vp = (struct fip_vn_desc *)desc;
|
||||||
if (dlen < sizeof(*vp))
|
if (dlen < sizeof(*vp))
|
||||||
return;
|
goto err;
|
||||||
if (compare_ether_addr(vp->fd_mac,
|
vlink_desc_arr[num_vlink_desc++] = vp;
|
||||||
fip->get_src_addr(lport)) == 0 &&
|
vn_port = fc_vport_id_lookup(lport,
|
||||||
get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn &&
|
ntoh24(vp->fd_fc_id));
|
||||||
ntoh24(vp->fd_fc_id) == lport->port_id) {
|
if (vn_port && (vn_port == lport)) {
|
||||||
desc_mask &= ~BIT(FIP_DT_VN_ID);
|
mutex_lock(&fip->ctlr_mutex);
|
||||||
break;
|
per_cpu_ptr(lport->dev_stats,
|
||||||
|
get_cpu())->VLinkFailureCount++;
|
||||||
|
put_cpu();
|
||||||
|
fcoe_ctlr_reset(fip);
|
||||||
|
mutex_unlock(&fip->ctlr_mutex);
|
||||||
}
|
}
|
||||||
/* check if clr_vlink is for NPIV port */
|
|
||||||
mutex_lock(&lport->lp_mutex);
|
|
||||||
list_for_each_entry(vn_port, &lport->vports, list) {
|
|
||||||
if (compare_ether_addr(vp->fd_mac,
|
|
||||||
fip->get_src_addr(vn_port)) == 0 &&
|
|
||||||
(get_unaligned_be64(&vp->fd_wwpn)
|
|
||||||
== vn_port->wwpn) &&
|
|
||||||
(ntoh24(vp->fd_fc_id) ==
|
|
||||||
fc_host_port_id(vn_port->host))) {
|
|
||||||
desc_mask &= ~BIT(FIP_DT_VN_ID);
|
|
||||||
is_vn_port = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_unlock(&lport->lp_mutex);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* standard says ignore unknown descriptors >= 128 */
|
/* standard says ignore unknown descriptors >= 128 */
|
||||||
if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
|
if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
|
||||||
return;
|
goto err;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
desc = (struct fip_desc *)((char *)desc + dlen);
|
desc = (struct fip_desc *)((char *)desc + dlen);
|
||||||
@ -1256,26 +1261,68 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
|
|||||||
/*
|
/*
|
||||||
* reset only if all required descriptors were present and valid.
|
* reset only if all required descriptors were present and valid.
|
||||||
*/
|
*/
|
||||||
if (desc_mask) {
|
if (desc_mask)
|
||||||
LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n",
|
LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n",
|
||||||
desc_mask);
|
desc_mask);
|
||||||
} else {
|
else if (!num_vlink_desc) {
|
||||||
LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n");
|
LIBFCOE_FIP_DBG(fip, "CVL: no Vx_Port descriptor found\n");
|
||||||
|
/*
|
||||||
if (is_vn_port)
|
* No Vx_Port description. Clear all NPIV ports,
|
||||||
|
* followed by physical port
|
||||||
|
*/
|
||||||
|
mutex_lock(&lport->lp_mutex);
|
||||||
|
list_for_each_entry(vn_port, &lport->vports, list)
|
||||||
fc_lport_reset(vn_port);
|
fc_lport_reset(vn_port);
|
||||||
else {
|
mutex_unlock(&lport->lp_mutex);
|
||||||
mutex_lock(&fip->ctlr_mutex);
|
|
||||||
per_cpu_ptr(lport->dev_stats,
|
|
||||||
get_cpu())->VLinkFailureCount++;
|
|
||||||
put_cpu();
|
|
||||||
fcoe_ctlr_reset(fip);
|
|
||||||
mutex_unlock(&fip->ctlr_mutex);
|
|
||||||
|
|
||||||
|
mutex_lock(&fip->ctlr_mutex);
|
||||||
|
per_cpu_ptr(lport->dev_stats,
|
||||||
|
get_cpu())->VLinkFailureCount++;
|
||||||
|
put_cpu();
|
||||||
|
fcoe_ctlr_reset(fip);
|
||||||
|
mutex_unlock(&fip->ctlr_mutex);
|
||||||
|
|
||||||
|
fc_lport_reset(fip->lp);
|
||||||
|
fcoe_ctlr_solicit(fip, NULL);
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n");
|
||||||
|
for (i = 0; i < num_vlink_desc; i++) {
|
||||||
|
vp = vlink_desc_arr[i];
|
||||||
|
vn_port = fc_vport_id_lookup(lport,
|
||||||
|
ntoh24(vp->fd_fc_id));
|
||||||
|
if (!vn_port)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 'port_id' is already validated, check MAC address and
|
||||||
|
* wwpn
|
||||||
|
*/
|
||||||
|
if (compare_ether_addr(fip->get_src_addr(vn_port),
|
||||||
|
vp->fd_mac) != 0 ||
|
||||||
|
get_unaligned_be64(&vp->fd_wwpn) !=
|
||||||
|
vn_port->wwpn)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (vn_port == lport)
|
||||||
|
/*
|
||||||
|
* Physical port, defer processing till all
|
||||||
|
* listed NPIV ports are cleared
|
||||||
|
*/
|
||||||
|
reset_phys_port = 1;
|
||||||
|
else /* NPIV port */
|
||||||
|
fc_lport_reset(vn_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reset_phys_port) {
|
||||||
fc_lport_reset(fip->lp);
|
fc_lport_reset(fip->lp);
|
||||||
fcoe_ctlr_solicit(fip, NULL);
|
fcoe_ctlr_solicit(fip, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
kfree(vlink_desc_arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user