aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/lpfc/lpfc_els.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_els.c')
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c186
1 files changed, 151 insertions, 35 deletions
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index d54ae1999797..cfe533bc9790 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -962,7 +962,8 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
if ((phba->fcoe_cvl_eventtag_attn ==
phba->fcoe_cvl_eventtag) &&
(irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
- (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))
+ ((irsp->un.ulpWord[4] & IOERR_PARAM_MASK) ==
+ IOERR_SLI_ABORTED))
goto stop_rr_fcf_flogi;
else
phba->fcoe_cvl_eventtag_attn =
@@ -1108,8 +1109,10 @@ flogifail:
/* Start discovery */
lpfc_disc_start(vport);
} else if (((irsp->ulpStatus != IOSTAT_LOCAL_REJECT) ||
- ((irsp->un.ulpWord[4] != IOERR_SLI_ABORTED) &&
- (irsp->un.ulpWord[4] != IOERR_SLI_DOWN))) &&
+ (((irsp->un.ulpWord[4] & IOERR_PARAM_MASK) !=
+ IOERR_SLI_ABORTED) &&
+ ((irsp->un.ulpWord[4] & IOERR_PARAM_MASK) !=
+ IOERR_SLI_DOWN))) &&
(phba->link_state != LPFC_CLEAR_LA)) {
/* If FLOGI failed enable link interrupt. */
lpfc_issue_clear_la(phba, vport);
@@ -1476,6 +1479,10 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
return ndlp;
memset(&rrq.xri_bitmap, 0, sizeof(new_ndlp->active_rrqs.xri_bitmap));
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3178 PLOGI confirm: ndlp %p x%x: new_ndlp %p\n",
+ ndlp, ndlp->nlp_DID, new_ndlp);
+
if (!new_ndlp) {
rc = memcmp(&ndlp->nlp_portname, name,
sizeof(struct lpfc_name));
@@ -1527,6 +1534,9 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
/* The new_ndlp is replacing ndlp totally, so we need
* to put ndlp on UNUSED list and try to free it.
*/
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3179 PLOGI confirm NEW: %x %x\n",
+ new_ndlp->nlp_DID, keepDID);
/* Fix up the rport accordingly */
rport = ndlp->rport;
@@ -1559,23 +1569,34 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
lpfc_drop_node(vport, ndlp);
}
else {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3180 PLOGI confirm SWAP: %x %x\n",
+ new_ndlp->nlp_DID, keepDID);
+
lpfc_unreg_rpi(vport, ndlp);
+
/* Two ndlps cannot have the same did */
ndlp->nlp_DID = keepDID;
if (phba->sli_rev == LPFC_SLI_REV4)
memcpy(&ndlp->active_rrqs.xri_bitmap,
&rrq.xri_bitmap,
sizeof(ndlp->active_rrqs.xri_bitmap));
+
/* Since we are swapping the ndlp passed in with the new one
- * and the did has already been swapped, copy over the
- * state and names.
+ * and the did has already been swapped, copy over state.
+ * The new WWNs are already in new_ndlp since thats what
+ * we looked it up by in the begining of this routine.
*/
- memcpy(&new_ndlp->nlp_portname, &ndlp->nlp_portname,
- sizeof(struct lpfc_name));
- memcpy(&new_ndlp->nlp_nodename, &ndlp->nlp_nodename,
- sizeof(struct lpfc_name));
new_ndlp->nlp_state = ndlp->nlp_state;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+
+ /* Since we are switching over to the new_ndlp, the old
+ * ndlp should be put in the NPR state, unless we have
+ * already started re-discovery on it.
+ */
+ if ((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) ||
+ (ndlp->nlp_state == NLP_STE_MAPPED_NODE))
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+
/* Fix up the rport accordingly */
rport = ndlp->rport;
if (rport) {
@@ -2367,6 +2388,8 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
IOCB_t *irsp;
struct lpfc_sli *psli;
struct lpfcMboxq *mbox;
+ unsigned long flags;
+ uint32_t skip_recovery = 0;
psli = &phba->sli;
/* we pass cmdiocb to state machine which needs rspiocb as well */
@@ -2381,47 +2404,52 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
"LOGO cmpl: status:x%x/x%x did:x%x",
irsp->ulpStatus, irsp->un.ulpWord[4],
ndlp->nlp_DID);
+
/* LOGO completes to NPort <nlp_DID> */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"0105 LOGO completes to NPort x%x "
"Data: x%x x%x x%x x%x\n",
ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4],
irsp->ulpTimeout, vport->num_disc_nodes);
- /* Check to see if link went down during discovery */
- if (lpfc_els_chk_latt(vport))
+
+ if (lpfc_els_chk_latt(vport)) {
+ skip_recovery = 1;
goto out;
+ }
+ /* Check to see if link went down during discovery */
if (ndlp->nlp_flag & NLP_TARGET_REMOVE) {
/* NLP_EVT_DEVICE_RM should unregister the RPI
* which should abort all outstanding IOs.
*/
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
NLP_EVT_DEVICE_RM);
+ skip_recovery = 1;
goto out;
}
if (irsp->ulpStatus) {
/* Check for retry */
- if (lpfc_els_retry(phba, cmdiocb, rspiocb))
+ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
/* ELS command is being retried */
+ skip_recovery = 1;
goto out;
+ }
/* LOGO failed */
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"2756 LOGO failure DID:%06X Status:x%x/x%x\n",
ndlp->nlp_DID, irsp->ulpStatus,
irsp->un.ulpWord[4]);
/* Do not call DSM for lpfc_els_abort'ed ELS cmds */
- if (lpfc_error_lost_link(irsp))
+ if (lpfc_error_lost_link(irsp)) {
+ skip_recovery = 1;
goto out;
- else
- lpfc_disc_state_machine(vport, ndlp, cmdiocb,
- NLP_EVT_CMPL_LOGO);
- } else
- /* Good status, call state machine.
- * This will unregister the rpi if needed.
- */
- lpfc_disc_state_machine(vport, ndlp, cmdiocb,
- NLP_EVT_CMPL_LOGO);
+ }
+ }
+
+ /* Call state machine. This will unregister the rpi if needed. */
+ lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO);
+
out:
lpfc_els_free_iocb(phba, cmdiocb);
/* If we are in pt2pt mode, we could rcv new S_ID on PLOGI */
@@ -2436,9 +2464,30 @@ out:
if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) ==
MBX_NOT_FINISHED) {
mempool_free(mbox, phba->mbox_mem_pool);
+ skip_recovery = 1;
}
}
}
+
+ /*
+ * If the node is a target, the handling attempts to recover the port.
+ * For any other port type, the rpi is unregistered as an implicit
+ * LOGO.
+ */
+ if ((ndlp->nlp_type & NLP_FCP_TARGET) && (skip_recovery == 0)) {
+ lpfc_cancel_retry_delay_tmo(vport, ndlp);
+ spin_lock_irqsave(shost->host_lock, flags);
+ ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3187 LOGO completes to NPort x%x: Start "
+ "Recovery Data: x%x x%x x%x x%x\n",
+ ndlp->nlp_DID, irsp->ulpStatus,
+ irsp->un.ulpWord[4], irsp->ulpTimeout,
+ vport->num_disc_nodes);
+ lpfc_disc_start(vport);
+ }
return;
}
@@ -2501,10 +2550,27 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
"Issue LOGO: did:x%x",
ndlp->nlp_DID, 0, 0);
+ /*
+ * If we are issuing a LOGO, we may try to recover the remote NPort
+ * by issuing a PLOGI later. Even though we issue ELS cmds by the
+ * VPI, if we have a valid RPI, and that RPI gets unreg'ed while
+ * that ELS command is in-flight, the HBA returns a IOERR_INVALID_RPI
+ * for that ELS cmd. To avoid this situation, lets get rid of the
+ * RPI right now, before any ELS cmds are sent.
+ */
+ spin_lock_irq(shost->host_lock);
+ ndlp->nlp_flag |= NLP_ISSUE_LOGO;
+ spin_unlock_irq(shost->host_lock);
+ if (lpfc_unreg_rpi(vport, ndlp)) {
+ lpfc_els_free_iocb(phba, elsiocb);
+ return 0;
+ }
+
phba->fc_stat.elsXmitLOGO++;
elsiocb->iocb_cmpl = lpfc_cmpl_els_logo;
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_LOGO_SND;
+ ndlp->nlp_flag &= ~NLP_ISSUE_LOGO;
spin_unlock_irq(shost->host_lock);
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
@@ -2920,7 +2986,7 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
case ELS_CMD_LOGO:
if (!lpfc_issue_els_logo(vport, ndlp, retry)) {
ndlp->nlp_prev_state = ndlp->nlp_state;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_LOGO_ISSUE);
}
break;
case ELS_CMD_FDISC:
@@ -3007,7 +3073,7 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
}
break;
case IOSTAT_LOCAL_REJECT:
- switch ((irsp->un.ulpWord[4] & 0xff)) {
+ switch ((irsp->un.ulpWord[4] & IOERR_PARAM_MASK)) {
case IOERR_LOOP_OPEN_FAILURE:
if (cmd == ELS_CMD_FLOGI) {
if (PCI_DEVICE_ID_HORNET ==
@@ -3214,7 +3280,8 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
if (((cmd == ELS_CMD_PLOGI) || (cmd == ELS_CMD_ADISC)) &&
((irsp->ulpStatus != IOSTAT_LOCAL_REJECT) ||
- ((irsp->un.ulpWord[4] & 0xff) != IOERR_NO_RESOURCES))) {
+ ((irsp->un.ulpWord[4] & IOERR_PARAM_MASK) !=
+ IOERR_NO_RESOURCES))) {
/* Don't reset timer for no resources */
/* If discovery / RSCN timer is running, reset it */
@@ -3273,7 +3340,7 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
return 1;
case ELS_CMD_LOGO:
ndlp->nlp_prev_state = ndlp->nlp_state;
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_LOGO_ISSUE);
lpfc_issue_els_logo(vport, ndlp, cmdiocb->retry);
return 1;
}
@@ -3533,13 +3600,17 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
mempool_free(pmb, phba->mbox_mem_pool);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
- lpfc_nlp_put(ndlp);
- /* This is the end of the default RPI cleanup logic for this
- * ndlp. If no other discovery threads are using this ndlp.
- * we should free all resources associated with it.
- */
- lpfc_nlp_not_used(ndlp);
+ if (ndlp) {
+ if (NLP_CHK_NODE_ACT(ndlp)) {
+ lpfc_nlp_put(ndlp);
+ /* This is the end of the default RPI cleanup logic for
+ * this ndlp. If no other discovery threads are using
+ * this ndlp, free all resources associated with it.
+ */
+ lpfc_nlp_not_used(ndlp);
+ } else {
+ lpfc_drop_node(ndlp->vport, ndlp);
+ }
}
return;
@@ -6803,7 +6874,8 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (icmd->ulpStatus == IOSTAT_NEED_BUFFER) {
lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
} else if (icmd->ulpStatus == IOSTAT_LOCAL_REJECT &&
- (icmd->un.ulpWord[4] & 0xff) == IOERR_RCV_BUFFER_WAITING) {
+ (icmd->un.ulpWord[4] & IOERR_PARAM_MASK) ==
+ IOERR_RCV_BUFFER_WAITING) {
phba->fc_stat.NoRcvBuf++;
/* Not enough posted buffers; Try posting more buffers */
if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
@@ -7985,3 +8057,47 @@ lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba,
spin_unlock_irqrestore(&phba->hbalock, iflag);
return;
}
+
+/* lpfc_sli_abts_recover_port - Recover a port that failed a BLS_ABORT req.
+ * @vport: pointer to virtual port object.
+ * @ndlp: nodelist pointer for the impacted node.
+ *
+ * The driver calls this routine in response to an SLI4 XRI ABORT CQE
+ * or an SLI3 ASYNC_STATUS_CN event from the port. For either event,
+ * the driver is required to send a LOGO to the remote node before it
+ * attempts to recover its login to the remote node.
+ */
+void
+lpfc_sli_abts_recover_port(struct lpfc_vport *vport,
+ struct lpfc_nodelist *ndlp)
+{
+ struct Scsi_Host *shost;
+ struct lpfc_hba *phba;
+ unsigned long flags = 0;
+
+ shost = lpfc_shost_from_vport(vport);
+ phba = vport->phba;
+ if (ndlp->nlp_state != NLP_STE_MAPPED_NODE) {
+ lpfc_printf_log(phba, KERN_INFO,
+ LOG_SLI, "3093 No rport recovery needed. "
+ "rport in state 0x%x\n", ndlp->nlp_state);
+ return;
+ }
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3094 Start rport recovery on shost id 0x%x "
+ "fc_id 0x%06x vpi 0x%x rpi 0x%x state 0x%x "
+ "flags 0x%x\n",
+ shost->host_no, ndlp->nlp_DID,
+ vport->vpi, ndlp->nlp_rpi, ndlp->nlp_state,
+ ndlp->nlp_flag);
+ /*
+ * The rport is not responding. Remove the FCP-2 flag to prevent
+ * an ADISC in the follow-up recovery code.
+ */
+ spin_lock_irqsave(shost->host_lock, flags);
+ ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ lpfc_issue_els_logo(vport, ndlp, 0);
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_LOGO_ISSUE);
+}
+