[PATCH 1/8] libfcoe: Incorrect CVL handling for NPIV ports

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Bhanu Prakash Gollapudi <bprakash@xxxxxxxxxxxx>

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@xxxxxxxxxxxx>
Signed-off-by: Robert Love <robert.w.love@xxxxxxxxx>
---
 drivers/scsi/fcoe/fcoe_ctlr.c |  133 ++++++++++++++++++++++++++++-------------
 1 files changed, 90 insertions(+), 43 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index 229e4af..c74c4b8 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -1173,7 +1173,9 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
 	struct fc_lport *lport = fip->lp;
 	struct fc_lport *vn_port = NULL;
 	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");
 
@@ -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.
 	 */
-	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;
 	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)) {
 		dlen = desc->fip_dlen * FIP_BPW;
 		if (dlen > rlen)
-			return;
+			goto err;
 		/* Drop CVL if there are duplicate critical descriptors */
 		if ((desc->fip_dtype < 32) &&
+		    (desc->fip_dtype != FIP_DT_VN_ID) &&
 		    !(desc_mask & 1U << desc->fip_dtype)) {
 			LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
 					"Descriptors in FIP CVL\n");
-			return;
+			goto err;
 		}
 		switch (desc->fip_dtype) {
 		case FIP_DT_MAC:
 			mp = (struct fip_mac_desc *)desc;
 			if (dlen < sizeof(*mp))
-				return;
+				goto err;
 			if (compare_ether_addr(mp->fd_mac, fcf->fcf_mac))
-				return;
+				goto err;
 			desc_mask &= ~BIT(FIP_DT_MAC);
 			break;
 		case FIP_DT_NAME:
 			wp = (struct fip_wwn_desc *)desc;
 			if (dlen < sizeof(*wp))
-				return;
+				goto err;
 			if (get_unaligned_be64(&wp->fd_wwn) != fcf->switch_name)
-				return;
+				goto err;
 			desc_mask &= ~BIT(FIP_DT_NAME);
 			break;
 		case FIP_DT_VN_ID:
 			vp = (struct fip_vn_desc *)desc;
 			if (dlen < sizeof(*vp))
-				return;
-			if (compare_ether_addr(vp->fd_mac,
-					       fip->get_src_addr(lport)) == 0 &&
-			    get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn &&
-			    ntoh24(vp->fd_fc_id) == lport->port_id) {
-				desc_mask &= ~BIT(FIP_DT_VN_ID);
-				break;
+				goto err;
+			vlink_desc_arr[num_vlink_desc++] = vp;
+			vn_port = fc_vport_id_lookup(lport,
+						      ntoh24(vp->fd_fc_id));
+			if (vn_port && (vn_port == lport)) {
+				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);
 			}
-			/* 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;
 		default:
 			/* standard says ignore unknown descriptors >= 128 */
 			if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
-				return;
+				goto err;
 			break;
 		}
 		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.
 	 */
-	if (desc_mask) {
+	if (desc_mask)
 		LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n",
 				desc_mask);
+	else if (!num_vlink_desc) {
+		LIBFCOE_FIP_DBG(fip, "CVL: no Vx_Port descriptor found\n");
+		/*
+		 * 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);
+		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);
+
+		fc_lport_reset(fip->lp);
+		fcoe_ctlr_solicit(fip, NULL);
 	} else {
-		LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n");
+		int i;
 
-		if (is_vn_port)
-			fc_lport_reset(vn_port);
-		else {
-			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);
+		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);
 			fcoe_ctlr_solicit(fip, NULL);
 		}
 	}
+
+err:
+	kfree(vlink_desc_arr);
 }
 
 /**

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux