From fedd3b7b93302c7789bd3eeb190653cfb0fe7645 Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 16 Feb 2011 12:39:24 -0500 Subject: [SCSI] lpfc 8.3.21: Critical Errors and Bug Fixes Critical Errors: - Correctly handle non-zero return lpfc_workq_post_event and return ENOMEM - Save the irq level when locking the host_lock in lpfc_findnode_did Bug Fixes: - Adjust payload_length and request_length for sli4_config mailbox commands. - Add the freed sgl/XRI to the tail of the list rather than to the head. - Set the FC_VPORT_NEEDS_INIT_VPI on vport deletes and check it before issuing a fdisc on an els retry. - Only call lpfc_hba_init_link() if phba->cfg_suppress_link_up is LPFC_INITIALIZE_LINK. - Add support for SLI-4 Performance Hints Signed-off-by: Alex Iannicelli Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_els.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/lpfc/lpfc_els.c') diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index c62d567cc845..58e1a55d97f9 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -2745,7 +2745,8 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp) } break; case ELS_CMD_FDISC: - lpfc_issue_els_fdisc(vport, ndlp, retry); + if (!(vport->fc_flag & FC_VPORT_NEEDS_INIT_VPI)) + lpfc_issue_els_fdisc(vport, ndlp, retry); break; } return; -- cgit v1.2.3 From 1151e3ec15c32021a8a12a123459ab5e41692898 Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 16 Feb 2011 12:39:35 -0500 Subject: [SCSI] lpfc 8.3.21: RRQ Implementation fixes RRQ Implementation fixes - Added checks to prevent a call to findnode_did in clr_active_rrq - Added the del_sync_timer call for the rrq_tmr to the stop_hba_timers routine. - Added a check in __lpfc_set_active_rrq for the driver unloading to prevent adding an rrq when the driver is being removed. - Add code to scsi_iocb_cmpl to check for the remote stop and add the rrq. - Added the same check to els retry. - Added code to compare the source did in the els rrq to the vports did and chose the right exchange ID. - Initialize the start_cmd pointer to indicate when we have looped through all of the scsi buffers. - Remove the need for the lock around the clearing of the active bit in the rrq. - Added code to clean the els and fcp xri aborted list and remove the all of the RRQs for a deleted vport. Signed-off-by: Alex Iannicelli Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_crtn.h | 2 +- drivers/scsi/lpfc/lpfc_els.c | 52 +++++++++-- drivers/scsi/lpfc/lpfc_hbadisc.c | 6 +- drivers/scsi/lpfc/lpfc_init.c | 13 +-- drivers/scsi/lpfc/lpfc_scsi.c | 77 +++++++++++----- drivers/scsi/lpfc/lpfc_sli.c | 191 ++++++++++++++++++--------------------- drivers/scsi/lpfc/lpfc_sli4.h | 2 + 7 files changed, 205 insertions(+), 138 deletions(-) (limited to 'drivers/scsi/lpfc/lpfc_els.c') diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 5b4513792a89..34fbc1820c40 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -423,6 +423,6 @@ int lpfc_send_rrq(struct lpfc_hba *, struct lpfc_node_rrq *); int lpfc_set_rrq_active(struct lpfc_hba *, struct lpfc_nodelist *, uint16_t, uint16_t, uint16_t); void lpfc_cleanup_wt_rrqs(struct lpfc_hba *); -void lpfc_cleanup_vports_rrqs(struct lpfc_vport *); +void lpfc_cleanup_vports_rrqs(struct lpfc_vport *, struct lpfc_nodelist *); struct lpfc_node_rrq *lpfc_get_active_rrq(struct lpfc_vport *, uint16_t, uint32_t); diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 58e1a55d97f9..c6d01f63f557 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -2816,9 +2816,17 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, switch (irsp->ulpStatus) { case IOSTAT_FCP_RSP_ERROR: + break; case IOSTAT_REMOTE_STOP: + if (phba->sli_rev == LPFC_SLI_REV4) { + /* This IO was aborted by the target, we don't + * know the rxid and because we did not send the + * ABTS we cannot generate and RRQ. + */ + lpfc_set_rrq_active(phba, ndlp, + cmdiocb->sli4_xritag, 0, 0); + } break; - case IOSTAT_LOCAL_REJECT: switch ((irsp->un.ulpWord[4] & 0xff)) { case IOERR_LOOP_OPEN_FAILURE: @@ -4014,28 +4022,34 @@ lpfc_els_clear_rrq(struct lpfc_vport *vport, uint8_t *pcmd; struct RRQ *rrq; uint16_t rxid; + uint16_t xri; struct lpfc_node_rrq *prrq; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) iocb->context2)->virt); pcmd += sizeof(uint32_t); rrq = (struct RRQ *)pcmd; - rxid = bf_get(rrq_oxid, rrq); + rrq->rrq_exchg = be32_to_cpu(rrq->rrq_exchg); + rxid = be16_to_cpu(bf_get(rrq_rxid, rrq)); lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "2883 Clear RRQ for SID:x%x OXID:x%x RXID:x%x" " x%x x%x\n", - bf_get(rrq_did, rrq), - bf_get(rrq_oxid, rrq), + be32_to_cpu(bf_get(rrq_did, rrq)), + be16_to_cpu(bf_get(rrq_oxid, rrq)), rxid, iocb->iotag, iocb->iocb.ulpContext); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, "Clear RRQ: did:x%x flg:x%x exchg:x%.08x", ndlp->nlp_DID, ndlp->nlp_flag, rrq->rrq_exchg); - prrq = lpfc_get_active_rrq(vport, rxid, ndlp->nlp_DID); + if (vport->fc_myDID == be32_to_cpu(bf_get(rrq_did, rrq))) + xri = be16_to_cpu(bf_get(rrq_oxid, rrq)); + else + xri = rxid; + prrq = lpfc_get_active_rrq(vport, xri, ndlp->nlp_DID); if (prrq) - lpfc_clr_rrq_active(phba, rxid, prrq); + lpfc_clr_rrq_active(phba, xri, prrq); return; } @@ -7582,6 +7596,32 @@ void lpfc_fabric_abort_hba(struct lpfc_hba *phba) IOERR_SLI_ABORTED); } +/** + * lpfc_sli4_vport_delete_els_xri_aborted -Remove all ndlp references for vport + * @vport: pointer to lpfc vport data structure. + * + * This routine is invoked by the vport cleanup for deletions and the cleanup + * for an ndlp on removal. + **/ +void +lpfc_sli4_vport_delete_els_xri_aborted(struct lpfc_vport *vport) +{ + struct lpfc_hba *phba = vport->phba; + struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL; + unsigned long iflag = 0; + + spin_lock_irqsave(&phba->hbalock, iflag); + spin_lock(&phba->sli4_hba.abts_sgl_list_lock); + list_for_each_entry_safe(sglq_entry, sglq_next, + &phba->sli4_hba.lpfc_abts_els_sgl_list, list) { + if (sglq_entry->ndlp && sglq_entry->ndlp->vport == vport) + sglq_entry->ndlp = NULL; + } + spin_unlock(&phba->sli4_hba.abts_sgl_list_lock); + spin_unlock_irqrestore(&phba->hbalock, iflag); + return; +} + /** * lpfc_sli4_els_xri_aborted - Slow-path process of els xri abort * @phba: pointer to lpfc hba data structure. diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 1f9c7f19c31f..63300be2e45b 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -3160,7 +3160,7 @@ lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) spin_unlock_irq(shost->host_lock); vport->unreg_vpi_cmpl = VPORT_OK; mempool_free(pmb, phba->mbox_mem_pool); - lpfc_cleanup_vports_rrqs(vport); + lpfc_cleanup_vports_rrqs(vport, NULL); /* * This shost reference might have been taken at the beginning of * lpfc_vport_delete() @@ -3900,6 +3900,8 @@ lpfc_drop_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) return; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE); + if (vport->phba->sli_rev == LPFC_SLI_REV4) + lpfc_cleanup_vports_rrqs(vport, ndlp); lpfc_nlp_put(ndlp); return; } @@ -4289,7 +4291,7 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) list_del_init(&ndlp->els_retry_evt.evt_listp); list_del_init(&ndlp->dev_loss_evt.evt_listp); - + lpfc_cleanup_vports_rrqs(vport, ndlp); lpfc_unreg_rpi(vport, ndlp); return 0; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 0b5f76caa6e4..32cd138f6543 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -945,17 +945,13 @@ static void lpfc_rrq_timeout(unsigned long ptr) { struct lpfc_hba *phba; - uint32_t tmo_posted; unsigned long iflag; phba = (struct lpfc_hba *)ptr; spin_lock_irqsave(&phba->pport->work_port_lock, iflag); - tmo_posted = phba->hba_flag & HBA_RRQ_ACTIVE; - if (!tmo_posted) - phba->hba_flag |= HBA_RRQ_ACTIVE; + phba->hba_flag |= HBA_RRQ_ACTIVE; spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag); - if (!tmo_posted) - lpfc_worker_wake_up(phba); + lpfc_worker_wake_up(phba); } /** @@ -2280,6 +2276,7 @@ lpfc_cleanup(struct lpfc_vport *vport) /* Wait for any activity on ndlps to settle */ msleep(10); } + lpfc_cleanup_vports_rrqs(vport, NULL); } /** @@ -2355,6 +2352,10 @@ lpfc_stop_hba_timers(struct lpfc_hba *phba) del_timer_sync(&phba->fabric_block_timer); del_timer_sync(&phba->eratt_poll); del_timer_sync(&phba->hb_tmofunc); + if (phba->sli_rev == LPFC_SLI_REV4) { + del_timer_sync(&phba->rrq_tmr); + phba->hba_flag &= ~HBA_RRQ_ACTIVE; + } phba->hb_outstanding = 0; switch (phba->pci_dev_grp) { diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index af5498d735e2..d482dfc951f3 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -608,6 +608,32 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) return bcnt; } +/** + * lpfc_sli4_vport_delete_fcp_xri_aborted -Remove all ndlp references for vport + * @vport: pointer to lpfc vport data structure. + * + * This routine is invoked by the vport cleanup for deletions and the cleanup + * for an ndlp on removal. + **/ +void +lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport) +{ + struct lpfc_hba *phba = vport->phba; + struct lpfc_scsi_buf *psb, *next_psb; + unsigned long iflag = 0; + + spin_lock_irqsave(&phba->hbalock, iflag); + spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock); + list_for_each_entry_safe(psb, next_psb, + &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) { + if (psb->rdata && psb->rdata->pnode + && psb->rdata->pnode->vport == vport) + psb->rdata = NULL; + } + spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock); + spin_unlock_irqrestore(&phba->hbalock, iflag); +} + /** * lpfc_sli4_fcp_xri_aborted - Fast-path process of fcp xri abort * @phba: pointer to lpfc hba data structure. @@ -640,7 +666,11 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, psb->status = IOSTAT_SUCCESS; spin_unlock( &phba->sli4_hba.abts_scsi_buf_list_lock); - ndlp = psb->rdata->pnode; + if (psb->rdata && psb->rdata->pnode) + ndlp = psb->rdata->pnode; + else + ndlp = NULL; + rrq_empty = list_empty(&phba->active_rrq_list); spin_unlock_irqrestore(&phba->hbalock, iflag); if (ndlp) @@ -964,36 +994,29 @@ lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) static struct lpfc_scsi_buf* lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { - struct lpfc_scsi_buf *lpfc_cmd = NULL; - struct lpfc_scsi_buf *start_lpfc_cmd = NULL; - struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list; + struct lpfc_scsi_buf *lpfc_cmd ; unsigned long iflag = 0; int found = 0; spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag); - list_remove_head(scsi_buf_list, lpfc_cmd, struct lpfc_scsi_buf, list); - spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag); - while (!found && lpfc_cmd) { + list_for_each_entry(lpfc_cmd, &phba->lpfc_scsi_buf_list, + list) { if (lpfc_test_rrq_active(phba, ndlp, - lpfc_cmd->cur_iocbq.sli4_xritag)) { - lpfc_release_scsi_buf_s4(phba, lpfc_cmd); - spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag); - list_remove_head(scsi_buf_list, lpfc_cmd, - struct lpfc_scsi_buf, list); - spin_unlock_irqrestore(&phba->scsi_buf_list_lock, - iflag); - if (lpfc_cmd == start_lpfc_cmd) { - lpfc_cmd = NULL; - break; - } else - continue; - } + lpfc_cmd->cur_iocbq.sli4_xritag)) + continue; + list_del(&lpfc_cmd->list); found = 1; lpfc_cmd->seg_cnt = 0; lpfc_cmd->nonsg_phys = 0; lpfc_cmd->prot_seg_cnt = 0; + break; } - return lpfc_cmd; + spin_unlock_irqrestore(&phba->scsi_buf_list_lock, + iflag); + if (!found) + return NULL; + else + return lpfc_cmd; } /** * lpfc_get_scsi_buf - Get a scsi buffer from lpfc_scsi_buf_list of the HBA @@ -2484,6 +2507,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, lpfc_worker_wake_up(phba); break; case IOSTAT_LOCAL_REJECT: + case IOSTAT_REMOTE_STOP: if (lpfc_cmd->result == IOERR_INVALID_RPI || lpfc_cmd->result == IOERR_NO_RESOURCES || lpfc_cmd->result == IOERR_ABORT_REQUESTED || @@ -2510,6 +2534,17 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, "on unprotected cmd\n"); } } + if ((lpfc_cmd->status == IOSTAT_REMOTE_STOP) + && (phba->sli_rev == LPFC_SLI_REV4) + && (pnode && NLP_CHK_NODE_ACT(pnode))) { + /* This IO was aborted by the target, we don't + * know the rxid and because we did not send the + * ABTS we cannot generate and RRQ. + */ + lpfc_set_rrq_active(phba, pnode, + lpfc_cmd->cur_iocbq.sli4_xritag, + 0, 0); + } /* else: fall through */ default: diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index ed8f048dfb53..b85c40e3bf55 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -535,15 +535,35 @@ __lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, uint16_t adj_xri; struct lpfc_node_rrq *rrq; int empty; + uint32_t did = 0; + + + if (!ndlp) + return -EINVAL; + + if (!phba->cfg_enable_rrq) + return -EINVAL; + + if (phba->pport->load_flag & FC_UNLOADING) { + phba->hba_flag &= ~HBA_RRQ_ACTIVE; + goto out; + } + did = ndlp->nlp_DID; /* * set the active bit even if there is no mem available. */ adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base; - if (!ndlp) - return -EINVAL; + + if (NLP_CHK_FREE_REQ(ndlp)) + goto out; + + if (ndlp->vport && (ndlp->vport->load_flag & FC_UNLOADING)) + goto out; + if (test_and_set_bit(adj_xri, ndlp->active_rrqs.xri_bitmap)) - return -EINVAL; + goto out; + rrq = mempool_alloc(phba->rrq_pool, GFP_KERNEL); if (rrq) { rrq->send_rrq = send_rrq; @@ -554,14 +574,7 @@ __lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, rrq->vport = ndlp->vport; rrq->rxid = rxid; empty = list_empty(&phba->active_rrq_list); - if (phba->cfg_enable_rrq && send_rrq) - /* - * We need the xri before we can add this to the - * phba active rrq list. - */ - rrq->send_rrq = send_rrq; - else - rrq->send_rrq = 0; + rrq->send_rrq = send_rrq; list_add_tail(&rrq->list, &phba->active_rrq_list); if (!(phba->hba_flag & HBA_RRQ_ACTIVE)) { phba->hba_flag |= HBA_RRQ_ACTIVE; @@ -570,40 +583,49 @@ __lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, } return 0; } - return -ENOMEM; +out: + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "2921 Can't set rrq active xri:0x%x rxid:0x%x" + " DID:0x%x Send:%d\n", + xritag, rxid, did, send_rrq); + return -EINVAL; } /** - * __lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap. + * lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap. * @phba: Pointer to HBA context object. * @xritag: xri used in this exchange. * @rrq: The RRQ to be cleared. * - * This function is called with hbalock held. This function **/ -static void -__lpfc_clr_rrq_active(struct lpfc_hba *phba, - uint16_t xritag, - struct lpfc_node_rrq *rrq) +void +lpfc_clr_rrq_active(struct lpfc_hba *phba, + uint16_t xritag, + struct lpfc_node_rrq *rrq) { uint16_t adj_xri; - struct lpfc_nodelist *ndlp; + struct lpfc_nodelist *ndlp = NULL; - ndlp = lpfc_findnode_did(rrq->vport, rrq->nlp_DID); + if ((rrq->vport) && NLP_CHK_NODE_ACT(rrq->ndlp)) + ndlp = lpfc_findnode_did(rrq->vport, rrq->nlp_DID); /* The target DID could have been swapped (cable swap) * we should use the ndlp from the findnode if it is * available. */ - if (!ndlp) + if ((!ndlp) && rrq->ndlp) ndlp = rrq->ndlp; + if (!ndlp) + goto out; + adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base; if (test_and_clear_bit(adj_xri, ndlp->active_rrqs.xri_bitmap)) { rrq->send_rrq = 0; rrq->xritag = 0; rrq->rrq_stop_time = 0; } +out: mempool_free(rrq, phba->rrq_pool); } @@ -628,34 +650,34 @@ lpfc_handle_rrq_active(struct lpfc_hba *phba) struct lpfc_node_rrq *nextrrq; unsigned long next_time; unsigned long iflags; + LIST_HEAD(send_rrq); spin_lock_irqsave(&phba->hbalock, iflags); phba->hba_flag &= ~HBA_RRQ_ACTIVE; next_time = jiffies + HZ * (phba->fc_ratov + 1); list_for_each_entry_safe(rrq, nextrrq, - &phba->active_rrq_list, list) { - if (time_after(jiffies, rrq->rrq_stop_time)) { - list_del(&rrq->list); - if (!rrq->send_rrq) - /* this call will free the rrq */ - __lpfc_clr_rrq_active(phba, rrq->xritag, rrq); - else { - /* if we send the rrq then the completion handler - * will clear the bit in the xribitmap. - */ - spin_unlock_irqrestore(&phba->hbalock, iflags); - if (lpfc_send_rrq(phba, rrq)) { - lpfc_clr_rrq_active(phba, rrq->xritag, - rrq); - } - spin_lock_irqsave(&phba->hbalock, iflags); - } - } else if (time_before(rrq->rrq_stop_time, next_time)) + &phba->active_rrq_list, list) { + if (time_after(jiffies, rrq->rrq_stop_time)) + list_move(&rrq->list, &send_rrq); + else if (time_before(rrq->rrq_stop_time, next_time)) next_time = rrq->rrq_stop_time; } spin_unlock_irqrestore(&phba->hbalock, iflags); if (!list_empty(&phba->active_rrq_list)) mod_timer(&phba->rrq_tmr, next_time); + list_for_each_entry_safe(rrq, nextrrq, &send_rrq, list) { + list_del(&rrq->list); + if (!rrq->send_rrq) + /* this call will free the rrq */ + lpfc_clr_rrq_active(phba, rrq->xritag, rrq); + else if (lpfc_send_rrq(phba, rrq)) { + /* if we send the rrq then the completion handler + * will clear the bit in the xribitmap. + */ + lpfc_clr_rrq_active(phba, rrq->xritag, + rrq); + } + } } /** @@ -693,29 +715,37 @@ lpfc_get_active_rrq(struct lpfc_vport *vport, uint16_t xri, uint32_t did) /** * lpfc_cleanup_vports_rrqs - Remove and clear the active RRQ for this vport. * @vport: Pointer to vport context object. - * - * Remove all active RRQs for this vport from the phba->active_rrq_list and - * clear the rrq. + * @ndlp: Pointer to the lpfc_node_list structure. + * If ndlp is NULL Remove all active RRQs for this vport from the + * phba->active_rrq_list and clear the rrq. + * If ndlp is not NULL then only remove rrqs for this vport & this ndlp. **/ void -lpfc_cleanup_vports_rrqs(struct lpfc_vport *vport) +lpfc_cleanup_vports_rrqs(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) { struct lpfc_hba *phba = vport->phba; struct lpfc_node_rrq *rrq; struct lpfc_node_rrq *nextrrq; unsigned long iflags; + LIST_HEAD(rrq_list); if (phba->sli_rev != LPFC_SLI_REV4) return; - spin_lock_irqsave(&phba->hbalock, iflags); - list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) { - if (rrq->vport == vport) { - list_del(&rrq->list); - __lpfc_clr_rrq_active(phba, rrq->xritag, rrq); - } + if (!ndlp) { + lpfc_sli4_vport_delete_els_xri_aborted(vport); + lpfc_sli4_vport_delete_fcp_xri_aborted(vport); } + spin_lock_irqsave(&phba->hbalock, iflags); + list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) + if ((rrq->vport == vport) && (!ndlp || rrq->ndlp == ndlp)) + list_move(&rrq->list, &rrq_list); spin_unlock_irqrestore(&phba->hbalock, iflags); + + list_for_each_entry_safe(rrq, nextrrq, &rrq_list, list) { + list_del(&rrq->list); + lpfc_clr_rrq_active(phba, rrq->xritag, rrq); + } } /** @@ -733,24 +763,27 @@ lpfc_cleanup_wt_rrqs(struct lpfc_hba *phba) struct lpfc_node_rrq *nextrrq; unsigned long next_time; unsigned long iflags; + LIST_HEAD(rrq_list); if (phba->sli_rev != LPFC_SLI_REV4) return; spin_lock_irqsave(&phba->hbalock, iflags); phba->hba_flag &= ~HBA_RRQ_ACTIVE; next_time = jiffies + HZ * (phba->fc_ratov * 2); - list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) { + list_splice_init(&phba->active_rrq_list, &rrq_list); + spin_unlock_irqrestore(&phba->hbalock, iflags); + + list_for_each_entry_safe(rrq, nextrrq, &rrq_list, list) { list_del(&rrq->list); - __lpfc_clr_rrq_active(phba, rrq->xritag, rrq); + lpfc_clr_rrq_active(phba, rrq->xritag, rrq); } - spin_unlock_irqrestore(&phba->hbalock, iflags); if (!list_empty(&phba->active_rrq_list)) mod_timer(&phba->rrq_tmr, next_time); } /** - * __lpfc_test_rrq_active - Test RRQ bit in xri_bitmap. + * lpfc_test_rrq_active - Test RRQ bit in xri_bitmap. * @phba: Pointer to HBA context object. * @ndlp: Targets nodelist pointer for this exchange. * @xritag the xri in the bitmap to test. @@ -759,8 +792,8 @@ lpfc_cleanup_wt_rrqs(struct lpfc_hba *phba) * returns 0 = rrq not active for this xri * 1 = rrq is valid for this xri. **/ -static int -__lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, +int +lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, uint16_t xritag) { uint16_t adj_xri; @@ -802,52 +835,6 @@ lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, return ret; } -/** - * lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap. - * @phba: Pointer to HBA context object. - * @xritag: xri used in this exchange. - * @rrq: The RRQ to be cleared. - * - * This function is takes the hbalock. - **/ -void -lpfc_clr_rrq_active(struct lpfc_hba *phba, - uint16_t xritag, - struct lpfc_node_rrq *rrq) -{ - unsigned long iflags; - - spin_lock_irqsave(&phba->hbalock, iflags); - __lpfc_clr_rrq_active(phba, xritag, rrq); - spin_unlock_irqrestore(&phba->hbalock, iflags); - return; -} - - - -/** - * lpfc_test_rrq_active - Test RRQ bit in xri_bitmap. - * @phba: Pointer to HBA context object. - * @ndlp: Targets nodelist pointer for this exchange. - * @xritag the xri in the bitmap to test. - * - * This function takes the hbalock. - * returns 0 = rrq not active for this xri - * 1 = rrq is valid for this xri. - **/ -int -lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, - uint16_t xritag) -{ - int ret; - unsigned long iflags; - - spin_lock_irqsave(&phba->hbalock, iflags); - ret = __lpfc_test_rrq_active(phba, ndlp, xritag); - spin_unlock_irqrestore(&phba->hbalock, iflags); - return ret; -} - /** * __lpfc_sli_get_sglq - Allocates an iocb object from sgl pool * @phba: Pointer to HBA context object. @@ -885,7 +872,7 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq) return NULL; adj_xri = sglq->sli4_xritag - phba->sli4_hba.max_cfg_param.xri_base; - if (__lpfc_test_rrq_active(phba, ndlp, sglq->sli4_xritag)) { + if (lpfc_test_rrq_active(phba, ndlp, sglq->sli4_xritag)) { /* This xri has an rrq outstanding for this DID. * put it back in the list and get another xri. */ diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index aea4aa012270..435a09d331ee 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -566,6 +566,8 @@ void lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *, struct sli4_wcqe_xri_aborted *); void lpfc_sli4_els_xri_aborted(struct lpfc_hba *, struct sli4_wcqe_xri_aborted *); +void lpfc_sli4_vport_delete_els_xri_aborted(struct lpfc_vport *); +void lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *); int lpfc_sli4_brdreset(struct lpfc_hba *); int lpfc_sli4_add_fcf_record(struct lpfc_hba *, struct fcf_record *); void lpfc_sli_remove_dflt_fcf(struct lpfc_hba *); -- cgit v1.2.3 From 924941444b481fc862b2de5e1dd7692ca85274d7 Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 16 Feb 2011 12:39:44 -0500 Subject: [SCSI] lpfc 8.3.21: FC Discovery changes FC Discovery changes - Treat received PLOGI while logged in as a relogin (unregister and reregister). - Added a timer to delay Nport discovery when clean bit is cleared and Fabric portname/nodename/FCID is changed. - Invalidate Port's DID when receiving PLOGI from p2p port with CONFIG_PORT mailbox command. Signed-off-by: Alex Iannicelli Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 5 ++ drivers/scsi/lpfc/lpfc_attr.c | 21 ++++++- drivers/scsi/lpfc/lpfc_crtn.h | 3 + drivers/scsi/lpfc/lpfc_ct.c | 49 +++++++++++++++ drivers/scsi/lpfc/lpfc_els.c | 118 ++++++++++++++++++++++++++++++++++++- drivers/scsi/lpfc/lpfc_hbadisc.c | 7 +++ drivers/scsi/lpfc/lpfc_hw.h | 6 ++ drivers/scsi/lpfc/lpfc_init.c | 6 ++ drivers/scsi/lpfc/lpfc_nportdisc.c | 11 +++- 9 files changed, 219 insertions(+), 7 deletions(-) (limited to 'drivers/scsi/lpfc/lpfc_els.c') diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index b388e436c687..e2a763426a7c 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -325,6 +325,7 @@ struct lpfc_vport { #define FC_VPORT_CVL_RCVD 0x400000 /* VLink failed due to CVL */ #define FC_VFI_REGISTERED 0x800000 /* VFI is registered */ #define FC_FDISC_COMPLETED 0x1000000/* FDISC completed */ +#define FC_DISC_DELAYED 0x2000000/* Delay NPort discovery */ uint32_t ct_flags; #define FC_CT_RFF_ID 0x1 /* RFF_ID accepted by switch */ @@ -348,6 +349,8 @@ struct lpfc_vport { uint32_t fc_myDID; /* fibre channel S_ID */ uint32_t fc_prevDID; /* previous fibre channel S_ID */ + struct lpfc_name fabric_portname; + struct lpfc_name fabric_nodename; int32_t stopped; /* HBA has not been restarted since last ERATT */ uint8_t fc_linkspeed; /* Link speed after last READ_LA */ @@ -372,6 +375,7 @@ struct lpfc_vport { #define WORKER_DISC_TMO 0x1 /* vport: Discovery timeout */ #define WORKER_ELS_TMO 0x2 /* vport: ELS timeout */ #define WORKER_FDMI_TMO 0x4 /* vport: FDMI timeout */ +#define WORKER_DELAYED_DISC_TMO 0x8 /* vport: delayed discovery */ #define WORKER_MBOX_TMO 0x100 /* hba: MBOX timeout */ #define WORKER_HB_TMO 0x200 /* hba: Heart beat timeout */ @@ -382,6 +386,7 @@ struct lpfc_vport { struct timer_list fc_fdmitmo; struct timer_list els_tmofunc; + struct timer_list delayed_disc_tmo; int unreg_vpi_cmpl; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 745774c5a4be..a57f6c0cbbba 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.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-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -3365,6 +3365,25 @@ unsigned char lpfc_prot_guard = SHOST_DIX_GUARD_IP; module_param(lpfc_prot_guard, byte, 0); MODULE_PARM_DESC(lpfc_prot_guard, "host protection guard type"); +/* + * Delay initial NPort discovery when Clean Address bit is cleared in + * FLOGI/FDISC accept and FCID/Fabric name/Fabric portname is changed. + * This parameter can have value 0 or 1. + * When this parameter is set to 0, no delay is added to the initial + * discovery. + * When this parameter is set to non-zero value, initial Nport discovery is + * delayed by ra_tov seconds when Clean Address bit is cleared in FLOGI/FDISC + * accept and FCID/Fabric name/Fabric portname is changed. + * Driver always delay Nport discovery for subsequent FLOGI/FDISC completion + * when Clean Address bit is cleared in FLOGI/FDISC + * accept and FCID/Fabric name/Fabric portname is changed. + * Default value is 0. + */ +int lpfc_delay_discovery; +module_param(lpfc_delay_discovery, int, 0); +MODULE_PARM_DESC(lpfc_delay_discovery, + "Delay NPort discovery when Clean Address bit is cleared. " + "Allowed values: 0,1."); /* * lpfc_sg_seg_cnt - Initial Maximum DMA Segment Count diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 34fbc1820c40..3d40023f4804 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -167,6 +167,8 @@ int lpfc_ns_cmd(struct lpfc_vport *, int, uint8_t, uint32_t); int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int); void lpfc_fdmi_tmo(unsigned long); void lpfc_fdmi_timeout_handler(struct lpfc_vport *); +void lpfc_delayed_disc_tmo(unsigned long); +void lpfc_delayed_disc_timeout_handler(struct lpfc_vport *); int lpfc_config_port_prep(struct lpfc_hba *); int lpfc_config_port_post(struct lpfc_hba *); @@ -341,6 +343,7 @@ extern struct fc_function_template lpfc_transport_functions; extern struct fc_function_template lpfc_vport_transport_functions; extern int lpfc_sli_mode; extern int lpfc_enable_npiv; +extern int lpfc_delay_discovery; int lpfc_vport_symbolic_node_name(struct lpfc_vport *, char *, size_t); int lpfc_vport_symbolic_port_name(struct lpfc_vport *, char *, size_t); diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index c004fa9a681e..d9edfd90d7ff 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1738,6 +1738,55 @@ fdmi_cmd_exit: return 1; } +/** + * lpfc_delayed_disc_tmo - Timeout handler for delayed discovery timer. + * @ptr - Context object of the timer. + * + * This function set the WORKER_DELAYED_DISC_TMO flag and wake up + * the worker thread. + **/ +void +lpfc_delayed_disc_tmo(unsigned long ptr) +{ + struct lpfc_vport *vport = (struct lpfc_vport *)ptr; + struct lpfc_hba *phba = vport->phba; + uint32_t tmo_posted; + unsigned long iflag; + + spin_lock_irqsave(&vport->work_port_lock, iflag); + tmo_posted = vport->work_port_events & WORKER_DELAYED_DISC_TMO; + if (!tmo_posted) + vport->work_port_events |= WORKER_DELAYED_DISC_TMO; + spin_unlock_irqrestore(&vport->work_port_lock, iflag); + + if (!tmo_posted) + lpfc_worker_wake_up(phba); + return; +} + +/** + * lpfc_delayed_disc_timeout_handler - Function called by worker thread to + * handle delayed discovery. + * @vport: pointer to a host virtual N_Port data structure. + * + * This function start nport discovery of the vport. + **/ +void +lpfc_delayed_disc_timeout_handler(struct lpfc_vport *vport) +{ + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + + spin_lock_irq(shost->host_lock); + if (!(vport->fc_flag & FC_DISC_DELAYED)) { + spin_unlock_irq(shost->host_lock); + return; + } + vport->fc_flag &= ~FC_DISC_DELAYED; + spin_unlock_irq(shost->host_lock); + + lpfc_do_scr_ns_plogi(vport->phba, vport); +} + void lpfc_fdmi_tmo(unsigned long ptr) { diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index c6d01f63f557..8e28edf9801e 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.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-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -484,6 +484,59 @@ fail: return rc; } +/** + * lpfc_check_clean_addr_bit - Check whether assigned FCID is clean. + * @vport: pointer to a host virtual N_Port data structure. + * @sp: pointer to service parameter data structure. + * + * This routine is called from FLOGI/FDISC completion handler functions. + * lpfc_check_clean_addr_bit return 1 when FCID/Fabric portname/ Fabric + * node nodename is changed in the completion service parameter else return + * 0. This function also set flag in the vport data structure to delay + * NP_Port discovery after the FLOGI/FDISC completion if Clean address bit + * in FLOGI/FDISC response is cleared and FCID/Fabric portname/ Fabric + * node nodename is changed in the completion service parameter. + * + * Return code + * 0 - FCID and Fabric Nodename and Fabric portname is not changed. + * 1 - FCID or Fabric Nodename or Fabric portname is changed. + * + **/ +static uint8_t +lpfc_check_clean_addr_bit(struct lpfc_vport *vport, + struct serv_parm *sp) +{ + uint8_t fabric_param_changed = 0; + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + + if ((vport->fc_prevDID != vport->fc_myDID) || + memcmp(&vport->fabric_portname, &sp->portName, + sizeof(struct lpfc_name)) || + memcmp(&vport->fabric_nodename, &sp->nodeName, + sizeof(struct lpfc_name))) + fabric_param_changed = 1; + + /* + * Word 1 Bit 31 in common service parameter is overloaded. + * Word 1 Bit 31 in FLOGI request is multiple NPort request + * Word 1 Bit 31 in FLOGI response is clean address bit + * + * If fabric parameter is changed and clean address bit is + * cleared delay nport discovery if + * - vport->fc_prevDID != 0 (not initial discovery) OR + * - lpfc_delay_discovery module parameter is set. + */ + if (fabric_param_changed && !sp->cmn.clean_address_bit && + (vport->fc_prevDID || lpfc_delay_discovery)) { + spin_lock_irq(shost->host_lock); + vport->fc_flag |= FC_DISC_DELAYED; + spin_unlock_irq(shost->host_lock); + } + + return fabric_param_changed; +} + + /** * lpfc_cmpl_els_flogi_fabric - Completion function for flogi to a fabric port * @vport: pointer to a host virtual N_Port data structure. @@ -512,6 +565,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_hba *phba = vport->phba; struct lpfc_nodelist *np; struct lpfc_nodelist *next_np; + uint8_t fabric_param_changed; spin_lock_irq(shost->host_lock); vport->fc_flag |= FC_FABRIC; @@ -544,6 +598,12 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_class_sup |= FC_COS_CLASS4; ndlp->nlp_maxframe = ((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) | sp->cmn.bbRcvSizeLsb; + + fabric_param_changed = lpfc_check_clean_addr_bit(vport, sp); + memcpy(&vport->fabric_portname, &sp->portName, + sizeof(struct lpfc_name)); + memcpy(&vport->fabric_nodename, &sp->nodeName, + sizeof(struct lpfc_name)); memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm)); if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) { @@ -565,7 +625,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } } - if ((vport->fc_prevDID != vport->fc_myDID) && + if (fabric_param_changed && !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) { /* If our NportID changed, we need to ensure all @@ -2203,6 +2263,7 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct Scsi_Host *shost = lpfc_shost_from_vport(vport); IOCB_t *irsp; struct lpfc_sli *psli; + struct lpfcMboxq *mbox; psli = &phba->sli; /* we pass cmdiocb to state machine which needs rspiocb as well */ @@ -2260,6 +2321,21 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *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 */ + if ((vport->fc_flag & FC_PT2PT) && + !(vport->fc_flag & FC_PT2PT_PLOGI)) { + phba->pport->fc_myDID = 0; + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (mbox) { + lpfc_config_link(phba, mbox); + mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + mbox->vport = vport; + if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) == + MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + } + } + } return; } @@ -6181,6 +6257,11 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, if (vport->load_flag & FC_UNLOADING) goto dropit; + /* If NPort discovery is delayed drop incoming ELS */ + if ((vport->fc_flag & FC_DISC_DELAYED) && + (cmd != ELS_CMD_PLOGI)) + goto dropit; + ndlp = lpfc_findnode_did(vport, did); if (!ndlp) { /* Cannot find existing Fabric ndlp, so allocate a new one */ @@ -6233,6 +6314,12 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp); lpfc_send_els_event(vport, ndlp, payload); + + /* If Nport discovery is delayed, reject PLOGIs */ + if (vport->fc_flag & FC_DISC_DELAYED) { + rjt_err = LSRJT_UNABLE_TPC; + break; + } if (vport->port_state < LPFC_DISC_AUTH) { if (!(phba->pport->fc_flag & FC_PT2PT) || (phba->pport->fc_flag & FC_PT2PT_PLOGI)) { @@ -6611,6 +6698,21 @@ void lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport) { struct lpfc_nodelist *ndlp, *ndlp_fdmi; + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + + /* + * If lpfc_delay_discovery parameter is set and the clean address + * bit is cleared and fc fabric parameters chenged, delay FC NPort + * discovery. + */ + spin_lock_irq(shost->host_lock); + if (vport->fc_flag & FC_DISC_DELAYED) { + spin_unlock_irq(shost->host_lock); + mod_timer(&vport->delayed_disc_tmo, + jiffies + HZ * phba->fc_ratov); + return; + } + spin_unlock_irq(shost->host_lock); ndlp = lpfc_findnode_did(vport, NameServer_DID); if (!ndlp) { @@ -6953,6 +7055,9 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct lpfc_nodelist *next_np; IOCB_t *irsp = &rspiocb->iocb; struct lpfc_iocbq *piocb; + struct lpfc_dmabuf *pcmd = cmdiocb->context2, *prsp; + struct serv_parm *sp; + uint8_t fabric_param_changed; lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0123 FDISC completes. x%x/x%x prevDID: x%x\n", @@ -6996,7 +7101,14 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, vport->fc_myDID = irsp->un.ulpWord[4] & Mask_DID; lpfc_vport_set_state(vport, FC_VPORT_ACTIVE); - if ((vport->fc_prevDID != vport->fc_myDID) && + prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list); + sp = prsp->virt + sizeof(uint32_t); + fabric_param_changed = lpfc_check_clean_addr_bit(vport, sp); + memcpy(&vport->fabric_portname, &sp->portName, + sizeof(struct lpfc_name)); + memcpy(&vport->fabric_nodename, &sp->nodeName, + sizeof(struct lpfc_name)); + if (fabric_param_changed && !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) { /* If our NportID changed, we need to ensure all * remaining NPORTs get unreg_login'ed so we can diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 63300be2e45b..154c715fb3af 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -658,6 +658,8 @@ lpfc_work_done(struct lpfc_hba *phba) lpfc_ramp_down_queue_handler(phba); if (work_port_events & WORKER_RAMP_UP_QUEUE) lpfc_ramp_up_queue_handler(phba); + if (work_port_events & WORKER_DELAYED_DISC_TMO) + lpfc_delayed_disc_timeout_handler(vport); } lpfc_destroy_vport_work_array(phba, vports); @@ -838,6 +840,11 @@ lpfc_linkdown_port(struct lpfc_vport *vport) lpfc_port_link_failure(vport); + /* Stop delayed Nport discovery */ + spin_lock_irq(shost->host_lock); + vport->fc_flag &= ~FC_DISC_DELAYED; + spin_unlock_irq(shost->host_lock); + del_timer_sync(&vport->delayed_disc_tmo); } int diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 96ed3ba6ba95..5a4a196baa90 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -341,6 +341,12 @@ struct csp { uint8_t bbCreditMsb; uint8_t bbCreditlsb; /* FC Word 0, byte 3 */ +/* + * Word 1 Bit 31 in common service parameter is overloaded. + * Word 1 Bit 31 in FLOGI request is multiple NPort request + * Word 1 Bit 31 in FLOGI response is clean address bit + */ +#define clean_address_bit request_multiple_Nport /* Word 1, bit 31 */ #ifdef __BIG_ENDIAN_BITFIELD uint16_t request_multiple_Nport:1; /* FC Word 1, bit 31 */ uint16_t randomOffset:1; /* FC Word 1, bit 30 */ diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 32cd138f6543..5e84d2a6ada6 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -2292,6 +2292,7 @@ lpfc_stop_vport_timers(struct lpfc_vport *vport) { del_timer_sync(&vport->els_tmofunc); del_timer_sync(&vport->fc_fdmitmo); + del_timer_sync(&vport->delayed_disc_tmo); lpfc_can_disctmo(vport); return; } @@ -2733,6 +2734,11 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) init_timer(&vport->els_tmofunc); vport->els_tmofunc.function = lpfc_els_timeout; vport->els_tmofunc.data = (unsigned long)vport; + + init_timer(&vport->delayed_disc_tmo); + vport->delayed_disc_tmo.function = lpfc_delayed_disc_tmo; + vport->delayed_disc_tmo.data = (unsigned long)vport; + error = scsi_add_host_with_dma(shost, dev, &phba->pcidev->dev); if (error) goto out_put_shost; diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index d85a7423a694..52b35159fc35 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -350,7 +350,11 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, 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 */ + /* + * Need to unreg_login if we are already in one of these states and + * change to NPR state. This will block the port until after the ACC + * completes and the reg_login is issued and completed. + */ switch (ndlp->nlp_state) { case NLP_STE_NPR_NODE: if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) @@ -359,8 +363,9 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, 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_unreg_rpi(vport, ndlp); + ndlp->nlp_prev_state = ndlp->nlp_state; + lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); } if ((vport->fc_flag & FC_PT2PT) && -- cgit v1.2.3