[SCSI] lpfc: NPIV: add SLI-3 interface

NPIV support is only available via new adapter interface extensions,
termed SLI-3. This interface changes some of the basic behaviors such
as command and response ring element sizes and data structures, as
well as a change in buffer posting.  Note: the new firmware extensions
are found only on our mid-range and enterprise 4Gig adapters - so NPIV
support is available only on these newer adapters. The latest firmware
can be downloaded from the Emulex support page.

Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 0af33be..d48247b 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -45,9 +45,7 @@
 {
 	struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
 	struct lpfc_hba  *phba = vport->phba;
-	LPFC_MBOXQ_t *mbox;
 	uint32_t ha_copy;
-	int rc;
 
 	if (vport->port_state >= LPFC_VPORT_READY ||
 	    phba->link_state == LPFC_LINK_DOWN)
@@ -76,20 +74,7 @@
 	spin_unlock_irq(shost->host_lock);
 
 	if (phba->link_state != LPFC_CLEAR_LA) {
-		if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) {
-			phba->link_state = LPFC_CLEAR_LA;
-			lpfc_clear_la(phba, mbox);
-			mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
-			mbox->vport = vport;
-			printk(KERN_ERR "%s (%d): do clear_la\n",
-			       __FUNCTION__, __LINE__);
-			rc = lpfc_sli_issue_mbox(phba, mbox,
-						 (MBX_NOWAIT | MBX_STOP_IOCB));
-			if (rc == MBX_NOT_FINISHED) {
-				mempool_free(mbox, phba->mbox_mem_pool);
-				phba->link_state = LPFC_HBA_ERROR;
-			}
-		}
+		lpfc_issue_clear_la(phba, vport);
 	}
 
 	return 1;
@@ -153,8 +138,8 @@
 	/* Allocate buffer for Buffer ptr list */
 	pbuflist = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
 	if (pbuflist)
-	    pbuflist->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
-					     &pbuflist->phys);
+		pbuflist->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
+						 &pbuflist->phys);
 	if (pbuflist == 0 || pbuflist->virt == 0) {
 		lpfc_sli_release_iocbq(phba, elsiocb);
 		lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);
@@ -289,6 +274,7 @@
 	vport->port_state = LPFC_FABRIC_CFG_LINK;
 	lpfc_config_link(phba, mbox);
 	mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+	mbox->vport = vport;
 
 	rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT | MBX_STOP_IOCB);
 	if (rc == MBX_NOT_FINISHED)
@@ -364,6 +350,7 @@
 		lpfc_config_link(phba, mbox);
 
 		mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+		mbox->vport = vport;
 		rc = lpfc_sli_issue_mbox(phba, mbox,
 				MBX_NOWAIT | MBX_STOP_IOCB);
 		if (rc == MBX_NOT_FINISHED) {
@@ -714,8 +701,10 @@
 
 	irsp = &rspiocb->iocb;
 	ndlp = lpfc_findnode_did(vport, irsp->un.elsreq64.remoteID);
-	if (!ndlp)
+
+	if (!ndlp) {
 		goto out;
+	}
 
 	/* Since ndlp can be freed in the disc state machine, note if this node
 	 * is being used during discovery.
@@ -1110,9 +1099,8 @@
 			/* If we get here, there is nothing left to wait for */
 			if (vport->port_state < LPFC_VPORT_READY &&
 			    phba->link_state != LPFC_CLEAR_LA) {
-				if (vport->port_type == LPFC_PHYSICAL_PORT) {
+				if (vport->port_type == LPFC_PHYSICAL_PORT)
 					lpfc_issue_clear_la(phba, vport);
-				}
 			} else {
 				lpfc_rscn_disc(vport);
 			}
@@ -1420,6 +1408,27 @@
 	return 0;
 }
 
+static void
+lpfc_end_rscn(struct lpfc_vport *vport)
+{
+	struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+
+	if (vport->fc_flag & FC_RSCN_MODE) {
+		/*
+		 * Check to see if more RSCNs came in while we were
+		 * processing this one.
+		 */
+		if (vport->fc_rscn_id_cnt ||
+		    (vport->fc_flag & FC_RSCN_DISCOVERY) != 0)
+			lpfc_els_handle_rscn(vport);
+		else {
+			spin_lock_irq(shost->host_lock);
+			vport->fc_flag &= ~FC_RSCN_MODE;
+			spin_unlock_irq(shost->host_lock);
+		}
+	}
+}
+
 void
 lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp)
 {
@@ -1449,24 +1458,7 @@
 				vport->fc_flag &= ~FC_NDISC_ACTIVE;
 				spin_unlock_irq(shost->host_lock);
 				lpfc_can_disctmo(vport);
-				if (vport->fc_flag & FC_RSCN_MODE) {
-					/*
-					 * Check to see if more RSCNs
-					 * came in while we were
-					 * processing this one.
-					 */
-					if (!vport->fc_rscn_id_cnt &&
-					    !(vport->fc_flag &
-					      FC_RSCN_DISCOVERY)) {
-						spin_lock_irq(shost->host_lock);
-						vport->fc_flag &= ~FC_RSCN_MODE;
-						spin_unlock_irq(
-							shost->host_lock);
-					}
-					else {
-						lpfc_els_handle_rscn(vport);
-					}
-				}
+				lpfc_end_rscn(vport);
 			}
 		}
 	}
@@ -1689,6 +1681,9 @@
 		retry = 0;
 	}
 
+	if ((vport->load_flag & FC_UNLOADING) != 0)
+		retry = 0;
+
 	if (retry) {
 
 		/* Retry ELS command <elsCmd> to remote NPORT <did> */
@@ -2141,9 +2136,7 @@
 
 	cmdsize = sizeof (uint32_t) + sizeof (PRLI);
 	elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp,
-				     ndlp->nlp_DID,
-				     (ELS_CMD_ACC |
-				      (ELS_CMD_PRLI & ~ELS_RSP_MASK)));
+	     ndlp->nlp_DID, (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK)));
 	if (!elsiocb)
 		return 1;
 
@@ -2361,8 +2354,12 @@
 
 	for (i = 0; i < vport->fc_rscn_id_cnt; i++) {
 		mp = vport->fc_rscn_id_list[i];
-		lpfc_mbuf_free(phba, mp->virt, mp->phys);
-		kfree(mp);
+		if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
+			lpfc_sli_hbqbuf_free(phba, mp->virt, mp->phys);
+		else {
+			lpfc_mbuf_free(phba, mp->virt, mp->phys);
+			kfree(mp);
+		}
 		vport->fc_rscn_id_list[i] = NULL;
 	}
 	spin_lock_irq(shost->host_lock);
@@ -2486,9 +2483,7 @@
 	cmd &= ELS_CMD_MASK;
 
 	/* RSCN received */
-	lpfc_printf_log(phba,
-			KERN_INFO,
-			LOG_DISCOVERY,
+	lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
 			"%d:0214 RSCN received Data: x%x x%x x%x x%x\n",
 			phba->brd_no, vport->fc_flag, payload_len, *lp,
 			vport->fc_rscn_id_cnt);
@@ -2581,9 +2576,7 @@
 	lpfc_set_disctmo(vport);
 
 	/* RSCN processed */
-	lpfc_printf_log(phba,
-			KERN_INFO,
-			LOG_DISCOVERY,
+	lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
 			"%d:0215 RSCN processed Data: x%x x%x x%x x%x\n",
 			phba->brd_no,
 			vport->fc_flag, 0, vport->fc_rscn_id_cnt,
@@ -2683,6 +2676,7 @@
 				       phba->cfg_link_speed);
 			mbox->mb.un.varInitLnk.lipsr_AL_PA = 0;
 			mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+			mbox->vport = vport;
 			rc = lpfc_sli_issue_mbox
 				(phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB));
 			lpfc_set_loopback_flag(phba);
@@ -2837,10 +2831,8 @@
 
 	elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
 	phba->fc_stat.elsXmitACC++;
-
-	if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+	if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR)
 		lpfc_els_free_iocb(phba, elsiocb);
-	}
 	return;
 }
 
@@ -3015,9 +3007,7 @@
 	fp = (FARP *) lp;
 
 	/* FARP-REQ received from DID <did> */
-	lpfc_printf_log(phba,
-			 KERN_INFO,
-			 LOG_ELS,
+	lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
 			 "%d:0601 FARP-REQ received from DID x%x\n",
 			 phba->brd_no, did);
 
@@ -3077,12 +3067,9 @@
 
 	cmd = *lp++;
 	/* FARP-RSP received from DID <did> */
-	lpfc_printf_log(phba,
-			 KERN_INFO,
-			 LOG_ELS,
+	lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
 			 "%d:0600 FARP-RSP received from DID x%x\n",
 			 phba->brd_no, did);
-
 	/* ACCEPT the Farp resp request */
 	lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
 
@@ -3102,8 +3089,9 @@
 	struct lpfc_hba *phba = vport->phba;
 
 	/* FAN received */
-	lpfc_printf_log(phba, KERN_INFO, LOG_ELS, "%d:0265 FAN received\n",
-								phba->brd_no);
+	lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+			"%d:0265 FAN received\n",
+			phba->brd_no);
 
 	icmd = &cmdiocb->iocb;
 	did = icmd->un.elsreq64.remoteID;
@@ -3332,79 +3320,40 @@
 	return;
 }
 
-void
-lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
-		     struct lpfc_iocbq *elsiocb)
+static void
+lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+		      struct lpfc_vport *vport, struct lpfc_dmabuf *mp,
+		      struct lpfc_iocbq *elsiocb)
 {
-	struct lpfc_sli *psli;
 	struct lpfc_nodelist *ndlp;
-	struct lpfc_dmabuf *mp = NULL;
-	uint32_t *lp;
-	IOCB_t *icmd;
 	struct ls_rjt stat;
+	uint32_t *lp;
 	uint32_t cmd, did, newnode, rjt_err = 0;
-	uint32_t drop_cmd = 0;	/* by default do NOT drop received cmd */
-	struct lpfc_vport *vport = NULL;
+	IOCB_t *icmd = &elsiocb->iocb;
 
-	psli = &phba->sli;
-	icmd = &elsiocb->iocb;
-
-	if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) &&
-		((icmd->un.ulpWord[4] & 0xff) == IOERR_RCV_BUFFER_WAITING)) {
-		phba->fc_stat.NoRcvBuf++;
-		/* Not enough posted buffers; Try posting more buffers */
-		lpfc_post_buffer(phba, pring, 0, 1);
-		return;
-	}
-
-	/* If there are no BDEs associated with this IOCB,
-	 * there is nothing to do.
-	 */
-	if (icmd->ulpBdeCount == 0)
-		return;
-
-		/* type of ELS cmd is first 32bit word in packet */
-	mp = lpfc_sli_ringpostbuf_get(phba, pring,
-				      getPaddr(icmd->un.cont64[0].addrHigh,
-					       icmd->un.cont64[0].addrLow));
-	if (mp == 0) {
-		drop_cmd = 1;
+	if (!vport || !mp)
 		goto dropit;
-	}
-
-	vport = phba->pport;
 
 	newnode = 0;
 	lp = (uint32_t *) mp->virt;
 	cmd = *lp++;
-	lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], 1, 1);
+	if ((phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) == 0)
+		lpfc_post_buffer(phba, pring, 1, 1);
 
-	if (icmd->ulpStatus) {
-		lpfc_mbuf_free(phba, mp->virt, mp->phys);
-		kfree(mp);
-		drop_cmd = 1;
+	if (icmd->ulpStatus)
 		goto dropit;
-	}
 
 	/* Check to see if link went down during discovery */
-	if (lpfc_els_chk_latt(vport)) {
-		lpfc_mbuf_free(phba, mp->virt, mp->phys);
-		kfree(mp);
-		drop_cmd = 1;
+	if (lpfc_els_chk_latt(vport))
 		goto dropit;
-	}
 
 	did = icmd->un.rcvels.remoteID;
 	ndlp = lpfc_findnode_did(vport, did);
 	if (!ndlp) {
 		/* Cannot find existing Fabric ndlp, so allocate a new one */
 		ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
-		if (!ndlp) {
-			lpfc_mbuf_free(phba, mp->virt, mp->phys);
-			kfree(mp);
-			drop_cmd = 1;
+		if (!ndlp)
 			goto dropit;
-		}
 
 		lpfc_nlp_init(vport, ndlp, did);
 		newnode = 1;
@@ -3428,7 +3377,7 @@
 	lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
 			"%d:0112 ELS command x%x received from NPORT x%x "
 			"Data: x%x\n", phba->brd_no, cmd, did,
-			 vport->port_state);
+			vport->port_state);
 
 	switch (cmd) {
 	case ELS_CMD_PLOGI:
@@ -3537,8 +3486,9 @@
 
 		/* Unknown ELS command <elsCmd> received from NPORT <did> */
 		lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
-				"%d:0115 Unknown ELS command x%x received from "
-				"NPORT x%x\n", phba->brd_no, cmd, did);
+				"%d:0115 Unknown ELS command x%x "
+				"received from NPORT x%x\n",
+				phba->brd_no, cmd, did);
 		if (newnode)
 			lpfc_drop_node(vport, ndlp);
 		break;
@@ -3553,20 +3503,89 @@
 		lpfc_els_rsp_reject(vport, stat.un.lsRjtError, elsiocb, ndlp);
 	}
 
+	return;
+
+dropit:
+	lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+			"%d:0111 Dropping received ELS cmd "
+			"Data: x%x x%x x%x\n",
+			phba->brd_no,
+			icmd->ulpStatus, icmd->un.ulpWord[4],
+			icmd->ulpTimeout);
+	phba->fc_stat.elsRcvDrop++;
+}
+
+
+void
+lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+		     struct lpfc_iocbq *elsiocb)
+{
+	struct lpfc_vport *vport = phba->pport;
+	struct lpfc_dmabuf *mp = NULL;
+	IOCB_t *icmd = &elsiocb->iocb;
+	struct hbq_dmabuf *sp = NULL;
+	dma_addr_t paddr;
+
+	if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+	    ((icmd->un.ulpWord[4] & 0xff) == IOERR_RCV_BUFFER_WAITING)) {
+		phba->fc_stat.NoRcvBuf++;
+		/* Not enough posted buffers; Try posting more buffers */
+		if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
+			lpfc_sli_hbqbuf_fill_hbq(phba);
+		else
+			lpfc_post_buffer(phba, pring, 0, 1);
+		return;
+	}
+
+	/* If there are no BDEs associated with this IOCB,
+	 * there is nothing to do.
+	 */
+	if (icmd->ulpBdeCount == 0)
+		return;
+
+	/* type of ELS cmd is first 32bit word in packet */
+	if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
+		paddr = getPaddr(icmd->un.cont64[0].addrHigh,
+				 icmd->un.cont64[0].addrLow);
+		sp = lpfc_sli_hbqbuf_find(phba, icmd->un.ulpWord[3]);
+		if (sp)
+			phba->hbq_buff_count--;
+		mp = sp ? &sp->dbuf : NULL;
+	} else {
+		paddr = getPaddr(icmd->un.cont64[0].addrHigh,
+				 icmd->un.cont64[0].addrLow);
+		mp = lpfc_sli_ringpostbuf_get(phba, pring, paddr);
+	}
+
+	lpfc_els_unsol_buffer(phba, pring, vport, mp, elsiocb);
+
 	lpfc_nlp_put(elsiocb->context1);
 	elsiocb->context1 = NULL;
 	if (elsiocb->context2) {
-		lpfc_mbuf_free(phba, mp->virt, mp->phys);
-		kfree(mp);
+		if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
+			lpfc_sli_free_hbq(phba, sp);
+		else {
+			lpfc_mbuf_free(phba, mp->virt, mp->phys);
+			kfree(mp);
+		}
 	}
-dropit:
-	/* check if need to drop received ELS cmd */
-	if (drop_cmd == 1) {
-		lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
-				"%d:0111 Dropping received ELS cmd "
-				"Data: x%x x%x x%x\n", phba->brd_no,
-				icmd->ulpStatus, icmd->un.ulpWord[4],
-				icmd->ulpTimeout);
-		phba->fc_stat.elsRcvDrop++;
+
+	/* RCV_ELS64_CX provide for 2 BDEs - process 2nd if included */
+	if ((phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) != 0 &&
+	    icmd->ulpBdeCount == 2) {
+		sp = lpfc_sli_hbqbuf_find(phba, icmd->un.ulpWord[15]);
+		if (sp)
+			phba->hbq_buff_count--;
+		mp = sp ? &sp->dbuf : NULL;
+		lpfc_els_unsol_buffer(phba, pring, vport, mp, elsiocb);
+		/* free mp if we are done with it */
+		if (elsiocb->context2) {
+			if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
+				lpfc_sli_free_hbq(phba, sp);
+			else {
+				lpfc_mbuf_free(phba, mp->virt, mp->phys);
+				kfree(mp);
+			}
+		}
 	}
 }