[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:
Bhanu Prakash Gollapudi 2011-05-16 16:45:24 -07:00 committed by James Bottomley
parent 4f788dce0b
commit c051ad2e57

View file

@ -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);
} }
/** /**