--- zzzz-none-000/linux-3.10.107/drivers/scsi/lpfc/lpfc_nportdisc.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/scsi/lpfc/lpfc_nportdisc.c 2021-02-04 17:41:59.000000000 +0000 @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2012 Emulex. All rights reserved. * + * Copyright (C) 2004-2015 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -203,8 +203,6 @@ int lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { - LIST_HEAD(completions); - LIST_HEAD(txcmplq_completions); LIST_HEAD(abort_list); struct lpfc_sli *psli = &phba->sli; struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; @@ -216,32 +214,27 @@ "Data: x%x x%x x%x\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); - + /* Clean up all fabric IOs first.*/ lpfc_fabric_abort_nport(ndlp); - /* First check the txq */ + /* + * Lock the ELS ring txcmplq for SLI3/SLI4 and build a local list + * of all ELS IOs that need an ABTS. The IOs need to stay on the + * txcmplq so that the abort operation completes them successfully. + */ spin_lock_irq(&phba->hbalock); - list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { - /* Check to see if iocb matches the nport we are looking for */ - if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) { - /* It matches, so deque and call compl with anp error */ - list_move_tail(&iocb->list, &completions); - } - } - - /* Next check the txcmplq */ - list_splice_init(&pring->txcmplq, &txcmplq_completions); - spin_unlock_irq(&phba->hbalock); - - list_for_each_entry_safe(iocb, next_iocb, &txcmplq_completions, list) { - /* Check to see if iocb matches the nport we are looking for */ + if (phba->sli_rev == LPFC_SLI_REV4) + spin_lock(&pring->ring_lock); + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + /* Add to abort_list on on NDLP match. */ if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) list_add_tail(&iocb->dlist, &abort_list); } - spin_lock_irq(&phba->hbalock); - list_splice(&txcmplq_completions, &pring->txcmplq); + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring->ring_lock); spin_unlock_irq(&phba->hbalock); + /* Abort the targeted IOs and remove them from the abort list. */ list_for_each_entry_safe(iocb, next_iocb, &abort_list, dlist) { spin_lock_irq(&phba->hbalock); list_del_init(&iocb->dlist); @@ -249,9 +242,28 @@ spin_unlock_irq(&phba->hbalock); } + INIT_LIST_HEAD(&abort_list); + + /* Now process the txq */ + spin_lock_irq(&phba->hbalock); + if (phba->sli_rev == LPFC_SLI_REV4) + spin_lock(&pring->ring_lock); + + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + /* Check to see if iocb matches the nport we are looking for */ + if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) { + list_del_init(&iocb->list); + list_add_tail(&iocb->list, &abort_list); + } + } + + if (phba->sli_rev == LPFC_SLI_REV4) + spin_unlock(&pring->ring_lock); + spin_unlock_irq(&phba->hbalock); + /* Cancel all the IOCBs from the completions list */ - lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, - IOERR_SLI_ABORTED); + lpfc_sli_cancel_iocbs(phba, &abort_list, + IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED); lpfc_cancel_retry_delay_tmo(phba->pport, ndlp); return 0; @@ -264,41 +276,16 @@ struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; struct lpfc_dmabuf *pcmd; + uint64_t nlp_portwwn = 0; uint32_t *lp; IOCB_t *icmd; struct serv_parm *sp; + uint32_t ed_tov; LPFC_MBOXQ_t *mbox; struct ls_rjt stat; int rc; memset(&stat, 0, sizeof (struct ls_rjt)); - if (vport->port_state <= LPFC_FDISC) { - /* Before responding to PLOGI, check for pt2pt mode. - * If we are pt2pt, with an outstanding FLOGI, abort - * the FLOGI and resend it first. - */ - if (vport->fc_flag & FC_PT2PT) { - lpfc_els_abort_flogi(phba); - if (!(vport->fc_flag & FC_PT2PT_PLOGI)) { - /* If the other side is supposed to initiate - * the PLOGI anyway, just ACC it now and - * move on with discovery. - */ - phba->fc_edtov = FF_DEF_EDTOV; - phba->fc_ratov = FF_DEF_RATOV; - /* Start discovery - this should just do - CLEAR_LA */ - lpfc_disc_start(vport); - } else - lpfc_initial_flogi(vport); - } else { - stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY; - stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE; - lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, - ndlp, NULL); - return 0; - } - } pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; lp = (uint32_t *) pcmd->virt; sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); @@ -320,6 +307,8 @@ NULL); return 0; } + + nlp_portwwn = wwn_to_u64(ndlp->nlp_portname.u.wwn); if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3, 0) == 0)) { /* Reject this request because invalid parameters */ stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; @@ -355,7 +344,7 @@ ndlp->nlp_maxframe = ((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) | sp->cmn.bbRcvSizeLsb; - /* no need to reg_login if we are already in one of these states */ + /* if already logged in, do implicit logout */ switch (ndlp->nlp_state) { case NLP_STE_NPR_NODE: if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) @@ -364,37 +353,71 @@ case NLP_STE_PRLI_ISSUE: case NLP_STE_UNMAPPED_NODE: case NLP_STE_MAPPED_NODE: - lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL); - return 1; + /* lpfc_plogi_confirm_nport skips fabric did, handle it here */ + if (!(ndlp->nlp_type & NLP_FABRIC)) { + lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, + ndlp, NULL); + return 1; + } + if (nlp_portwwn != 0 && + nlp_portwwn != wwn_to_u64(sp->portName.u.wwn)) + lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, + "0143 PLOGI recv'd from DID: x%x " + "WWPN changed: old %llx new %llx\n", + ndlp->nlp_DID, + (unsigned long long)nlp_portwwn, + (unsigned long long) + wwn_to_u64(sp->portName.u.wwn)); + + ndlp->nlp_prev_state = ndlp->nlp_state; + /* rport needs to be unregistered first */ + lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); + break; } /* Check for Nport to NPort pt2pt protocol */ if ((vport->fc_flag & FC_PT2PT) && !(vport->fc_flag & FC_PT2PT_PLOGI)) { - /* rcv'ed PLOGI decides what our NPortId will be */ vport->fc_myDID = icmd->un.rcvels.parmRo; - mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); - if (mbox == NULL) - goto out; - 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); - if (rc == MBX_NOT_FINISHED) { - mempool_free(mbox, phba->mbox_mem_pool); - goto out; + + ed_tov = be32_to_cpu(sp->cmn.e_d_tov); + if (sp->cmn.edtovResolution) { + /* E_D_TOV ticks are in nanoseconds */ + ed_tov = (phba->fc_edtov + 999999) / 1000000; } + /* - * For SLI4, the VFI/VPI are registered AFTER the - * Nport with the higher WWPN sends us a PLOGI with - * our assigned NPortId. + * For pt-to-pt, use the larger EDTOV + * RATOV = 2 * EDTOV */ + if (ed_tov > phba->fc_edtov) + phba->fc_edtov = ed_tov; + phba->fc_ratov = (2 * phba->fc_edtov) / 1000; + + memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm)); + + /* Issue config_link / reg_vfi to account for updated TOV's */ + if (phba->sli_rev == LPFC_SLI_REV4) lpfc_issue_reg_vfi(vport); + else { + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (mbox == NULL) + goto out; + 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); + if (rc == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto out; + } + } lpfc_can_disctmo(vport); } + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) goto out; @@ -628,7 +651,13 @@ lpfc_destroy_vport_work_array(phba, vports); } - if (active_vlink_present) { + /* + * Don't re-instantiate if vport is marked for deletion. + * If we are here first then vport_delete is going to wait + * for discovery to complete. + */ + if (!(vport->load_flag & FC_UNLOADING) && + active_vlink_present) { /* * If there are other active VLinks present, * re-instantiate the Vlink using FDISC. @@ -690,11 +719,15 @@ ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; + ndlp->nlp_flag &= ~NLP_FIRSTBURST; if (npr->prliType == PRLI_FCP_TYPE) { if (npr->initiatorFunc) ndlp->nlp_type |= NLP_FCP_INITIATOR; - if (npr->targetFunc) + if (npr->targetFunc) { ndlp->nlp_type |= NLP_FCP_TARGET; + if (npr->writeXferRdyDis) + ndlp->nlp_flag |= NLP_FIRSTBURST; + } if (npr->Retry) ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; } @@ -777,7 +810,6 @@ { struct lpfc_hba *phba; LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg; - MAILBOX_t *mb; uint16_t rpi; phba = vport->phba; @@ -785,7 +817,6 @@ if (!(phba->pport->load_flag & FC_UNLOADING) && (evt == NLP_EVT_CMPL_REG_LOGIN) && (!pmb->u.mb.mbxStatus)) { - mb = &pmb->u.mb; rpi = pmb->u.mb.un.varWords[0]; lpfc_release_rpi(phba, vport, rpi); } @@ -997,7 +1028,9 @@ uint32_t *lp; IOCB_t *irsp; struct serv_parm *sp; + uint32_t ed_tov; LPFC_MBOXQ_t *mbox; + int rc; cmdiocb = (struct lpfc_iocbq *) arg; rspiocb = cmdiocb->context_un.rsp_iocb; @@ -1015,6 +1048,8 @@ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list); + if (!prsp) + goto out; lp = (uint32_t *) prsp->virt; sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); @@ -1051,18 +1086,63 @@ ndlp->nlp_maxframe = ((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) | sp->cmn.bbRcvSizeLsb; + if ((vport->fc_flag & FC_PT2PT) && + (vport->fc_flag & FC_PT2PT_PLOGI)) { + ed_tov = be32_to_cpu(sp->cmn.e_d_tov); + if (sp->cmn.edtovResolution) { + /* E_D_TOV ticks are in nanoseconds */ + ed_tov = (phba->fc_edtov + 999999) / 1000000; + } + + /* + * Use the larger EDTOV + * RATOV = 2 * EDTOV for pt-to-pt + */ + if (ed_tov > phba->fc_edtov) + phba->fc_edtov = ed_tov; + phba->fc_ratov = (2 * phba->fc_edtov) / 1000; + + memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm)); + + /* Issue config_link / reg_vfi to account for updated TOV's */ + if (phba->sli_rev == LPFC_SLI_REV4) { + lpfc_issue_reg_vfi(vport); + } else { + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, + "0133 PLOGI: no memory " + "for config_link " + "Data: x%x x%x x%x x%x\n", + ndlp->nlp_DID, ndlp->nlp_state, + ndlp->nlp_flag, ndlp->nlp_rpi); + goto out; + } + + 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); + if (rc == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto out; + } + } + } + + lpfc_unreg_rpi(vport, ndlp); + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) { lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, - "0133 PLOGI: no memory for reg_login " - "Data: x%x x%x x%x x%x\n", - ndlp->nlp_DID, ndlp->nlp_state, - ndlp->nlp_flag, ndlp->nlp_rpi); + "0018 PLOGI: no memory for reg_login " + "Data: x%x x%x x%x x%x\n", + ndlp->nlp_DID, ndlp->nlp_state, + ndlp->nlp_flag, ndlp->nlp_rpi); goto out; } - lpfc_unreg_rpi(vport, ndlp); - if (lpfc_reg_rpi(phba, vport->vpi, irsp->un.elsreq64.remoteID, (uint8_t *) sp, mbox, ndlp->nlp_rpi) == 0) { switch (ndlp->nlp_DID) { @@ -1676,12 +1756,16 @@ /* Check out PRLI rsp */ ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; + ndlp->nlp_flag &= ~NLP_FIRSTBURST; if ((npr->acceptRspCode == PRLI_REQ_EXECUTED) && (npr->prliType == PRLI_FCP_TYPE)) { if (npr->initiatorFunc) ndlp->nlp_type |= NLP_FCP_INITIATOR; - if (npr->targetFunc) + if (npr->targetFunc) { ndlp->nlp_type |= NLP_FCP_TARGET; + if (npr->writeXferRdyDis) + ndlp->nlp_flag |= NLP_FIRSTBURST; + } if (npr->Retry) ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; } @@ -1825,7 +1909,7 @@ struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)arg; spin_lock_irq(shost->host_lock); - ndlp->nlp_flag &= NLP_LOGO_ACC; + ndlp->nlp_flag |= NLP_LOGO_ACC; spin_unlock_irq(shost->host_lock); lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL); return ndlp->nlp_state; @@ -2252,6 +2336,9 @@ if (vport->phba->sli_rev < LPFC_SLI_REV4) ndlp->nlp_rpi = mb->un.varWords[0]; ndlp->nlp_flag |= NLP_RPI_REGISTERED; + if (ndlp->nlp_flag & NLP_LOGO_ACC) { + lpfc_unreg_rpi(vport, ndlp); + } } else { if (ndlp->nlp_flag & NLP_NODEV_REMOVE) { lpfc_drop_node(vport, ndlp);