diff options
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_nportdisc.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_nportdisc.c | 87 |
1 files changed, 81 insertions, 6 deletions
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index e8bb0055994..15ca2a9a0cd 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2009 Emulex. All rights reserved. * + * Copyright (C) 2004-2012 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -48,6 +48,10 @@ static int lpfc_check_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_name *nn, struct lpfc_name *pn) { + /* First, we MUST have a RPI registered */ + if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED)) + return 0; + /* Compare the ADISC rsp WWNN / WWPN matches our internal node * table entry for that node. */ @@ -385,6 +389,10 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (!mbox) goto out; + /* Registering an existing RPI behaves differently for SLI3 vs SLI4 */ + if (phba->sli_rev == LPFC_SLI_REV4) + lpfc_unreg_rpi(vport, ndlp); + rc = lpfc_reg_rpi(phba, vport->vpi, icmd->un.rcvels.remoteID, (uint8_t *) sp, mbox, ndlp->nlp_rpi); if (rc) { @@ -432,11 +440,15 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, spin_unlock_irq(shost->host_lock); stat.un.b.lsRjtRsnCode = LSRJT_INVALID_CMD; stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE; - lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, + rc = lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, mbox); + if (rc) + mempool_free(mbox, phba->mbox_mem_pool); return 1; } - lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox); + rc = lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox); + if (rc) + mempool_free(mbox, phba->mbox_mem_pool); return 1; out: stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; @@ -445,11 +457,43 @@ out: return 0; } +/** + * lpfc_mbx_cmpl_resume_rpi - Resume RPI completion routine + * @phba: pointer to lpfc hba data structure. + * @mboxq: pointer to mailbox object + * + * This routine is invoked to issue a completion to a rcv'ed + * ADISC or PDISC after the paused RPI has been resumed. + **/ +static void +lpfc_mbx_cmpl_resume_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) +{ + struct lpfc_vport *vport; + struct lpfc_iocbq *elsiocb; + struct lpfc_nodelist *ndlp; + uint32_t cmd; + + elsiocb = (struct lpfc_iocbq *)mboxq->context1; + ndlp = (struct lpfc_nodelist *) mboxq->context2; + vport = mboxq->vport; + cmd = elsiocb->drvrTimeout; + + if (cmd == ELS_CMD_ADISC) { + lpfc_els_rsp_adisc_acc(vport, elsiocb, ndlp); + } else { + lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, elsiocb, + ndlp, NULL); + } + kfree(elsiocb); + mempool_free(mboxq, phba->mbox_mem_pool); +} + static int lpfc_rcv_padisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_iocbq *cmdiocb) { struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + struct lpfc_iocbq *elsiocb; struct lpfc_dmabuf *pcmd; struct serv_parm *sp; struct lpfc_name *pnn, *ppn; @@ -475,12 +519,43 @@ lpfc_rcv_padisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, icmd = &cmdiocb->iocb; if (icmd->ulpStatus == 0 && lpfc_check_adisc(vport, ndlp, pnn, ppn)) { + + /* + * As soon as we send ACC, the remote NPort can + * start sending us data. Thus, for SLI4 we must + * resume the RPI before the ACC goes out. + */ + if (vport->phba->sli_rev == LPFC_SLI_REV4) { + elsiocb = kmalloc(sizeof(struct lpfc_iocbq), + GFP_KERNEL); + if (elsiocb) { + + /* Save info from cmd IOCB used in rsp */ + memcpy((uint8_t *)elsiocb, (uint8_t *)cmdiocb, + sizeof(struct lpfc_iocbq)); + + /* Save the ELS cmd */ + elsiocb->drvrTimeout = cmd; + + lpfc_sli4_resume_rpi(ndlp, + lpfc_mbx_cmpl_resume_rpi, elsiocb); + goto out; + } + } + if (cmd == ELS_CMD_ADISC) { lpfc_els_rsp_adisc_acc(vport, cmdiocb, ndlp); } else { - lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, - NULL); + lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, + ndlp, NULL); } +out: + /* If we are authenticated, move to the proper state */ + if (ndlp->nlp_type & NLP_FCP_TARGET) + lpfc_nlp_set_state(vport, ndlp, NLP_STE_MAPPED_NODE); + else + lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); + return 1; } /* Reject this request because invalid parameters */ @@ -1229,7 +1304,7 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport, } if (phba->sli_rev == LPFC_SLI_REV4) { - rc = lpfc_sli4_resume_rpi(ndlp); + rc = lpfc_sli4_resume_rpi(ndlp, NULL, NULL); if (rc) { /* Stay in state and retry. */ ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE; |