dect
/
linux-2.6
Archived
13
0
Fork 0

[SCSI] lpfc 8.2.3 : FC Discovery Fixes

FC Discovery Fixes:
- Fix up lpfc_drop_node() vs lpfc_nlp_not_used() usage
- Clear ADISC flag when unregistering RPI and REMOVE ndlps if in recovery.
- Fix usage of UNUSED list and ndlps
- Fix PLOGI race conditions
- Reset link if NameServer PLOGI errors occur
- Synchronize GID_FT queries with PLOGI receptions

Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
James Smart 2007-10-27 13:37:43 -04:00 committed by James Bottomley
parent 98c9ea5c02
commit 87af33fe5f
7 changed files with 300 additions and 204 deletions

View File

@ -45,6 +45,7 @@ void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *, uint32_t);
void lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove);
int lpfc_linkdown(struct lpfc_hba *);
void lpfc_port_link_failure(struct lpfc_vport *);
void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
@ -74,6 +75,7 @@ void lpfc_disc_list_loopmap(struct lpfc_vport *);
void lpfc_disc_start(struct lpfc_vport *);
void lpfc_disc_flush_list(struct lpfc_vport *);
void lpfc_cleanup_discovery_resources(struct lpfc_vport *);
void lpfc_cleanup(struct lpfc_vport *);
void lpfc_disc_timeout(unsigned long);
struct lpfc_nodelist *__lpfc_findnode_rpi(struct lpfc_vport *, uint16_t);
@ -91,6 +93,8 @@ void lpfc_do_scr_ns_plogi(struct lpfc_hba *, struct lpfc_vport *);
int lpfc_check_sparm(struct lpfc_vport *, struct lpfc_nodelist *,
struct serv_parm *, uint32_t);
int lpfc_els_abort(struct lpfc_hba *, struct lpfc_nodelist *);
void lpfc_more_plogi(struct lpfc_vport *);
void lpfc_end_rscn(struct lpfc_vport *);
int lpfc_els_chk_latt(struct lpfc_vport *);
int lpfc_els_abort_flogi(struct lpfc_hba *);
int lpfc_initial_flogi(struct lpfc_vport *);

View File

@ -103,7 +103,6 @@ struct lpfc_nodelist {
#define NLP_RM_DFLT_RPI 0x4000000 /* need to remove leftover dflt RPI */
#define NLP_NODEV_REMOVE 0x8000000 /* Defer removal till discovery ends */
#define NLP_TARGET_REMOVE 0x10000000 /* Target remove in process */
#define NLP_DELAYED_RM 0x20000000 /* Defer UNUSED List removal */
/* There are 4 different double linked lists nodelist entries can reside on.
* The Port Login (PLOGI) list and Address Discovery (ADISC) list are used

View File

@ -575,8 +575,13 @@ 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))) &&
(phba->link_state != LPFC_CLEAR_LA)) {
/* If FLOGI failed enable link interrupt. */
lpfc_issue_clear_la(phba, vport);
}
out:
lpfc_els_free_iocb(phba, cmdiocb);
}
@ -711,13 +716,8 @@ lpfc_initial_flogi(struct lpfc_vport *vport)
lpfc_nlp_init(vport, ndlp, Fabric_DID);
} else {
lpfc_dequeue_node(vport, ndlp);
/* If we go thru this path, Fabric_DID ndlp is in the process
* of being removed. We need to bump the reference count by 1
* so it stays around all through this link up period.
*/
lpfc_nlp_get(ndlp);
}
if (lpfc_issue_els_flogi(vport, ndlp, 0)) {
lpfc_nlp_put(ndlp);
}
@ -746,7 +746,8 @@ lpfc_initial_fdisc(struct lpfc_vport *vport)
}
return 1;
}
static void
void
lpfc_more_plogi(struct lpfc_vport *vport)
{
int sentplogi;
@ -813,8 +814,12 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
lpfc_nlp_set_state(vport, new_ndlp, ndlp->nlp_state);
/* Move this back to NPR state */
if (memcmp(&ndlp->nlp_portname, name, sizeof(struct lpfc_name)) == 0)
if (memcmp(&ndlp->nlp_portname, name, sizeof(struct lpfc_name)) == 0) {
/* The new_ndlp is replacing ndlp totally, so we need
* to put ndlp on UNUSED list and try to free it.
*/
lpfc_drop_node(vport, ndlp);
}
else {
lpfc_unreg_rpi(vport, ndlp);
ndlp->nlp_DID = 0; /* Two ndlps cannot have the same did */
@ -823,6 +828,27 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
return new_ndlp;
}
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);
}
}
}
static void
lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_iocbq *rspiocb)
@ -893,13 +919,6 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
goto out;
}
/* PLOGI failed */
if (ndlp->nlp_DID == NameServer_DID) {
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"0250 Nameserver login error: "
"0x%x / 0x%x\n",
irsp->ulpStatus, irsp->un.ulpWord[4]);
}
/* Do not call DSM for lpfc_els_abort'ed ELS cmds */
if (lpfc_error_lost_link(irsp)) {
rc = NLP_STE_FREED_NODE;
@ -927,20 +946,7 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
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 == 0) &&
(!(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);
}
}
@ -1160,8 +1166,6 @@ lpfc_more_adisc(struct lpfc_vport *vport)
static void
lpfc_rscn_disc(struct lpfc_vport *vport)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
lpfc_can_disctmo(vport);
/* RSCN discovery */
@ -1170,19 +1174,7 @@ lpfc_rscn_disc(struct lpfc_vport *vport)
if (lpfc_els_disc_plogi(vport))
return;
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 == 0) &&
(!(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);
}
static void
@ -1632,27 +1624,6 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
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)
{
@ -2068,6 +2039,32 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
return 0;
}
int
lpfc_els_free_data(struct lpfc_hba *phba, struct lpfc_dmabuf *buf_ptr1)
{
struct lpfc_dmabuf *buf_ptr;
/* Free the response before processing the command. */
if (!list_empty(&buf_ptr1->list)) {
list_remove_head(&buf_ptr1->list, buf_ptr,
struct lpfc_dmabuf,
list);
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
kfree(buf_ptr);
}
lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys);
kfree(buf_ptr1);
return 0;
}
int
lpfc_els_free_bpl(struct lpfc_hba *phba, struct lpfc_dmabuf *buf_ptr)
{
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
kfree(buf_ptr);
return 0;
}
int
lpfc_els_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *elsiocb)
{
@ -2080,22 +2077,12 @@ lpfc_els_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *elsiocb)
/* context2 = cmd, context2->next = rsp, context3 = bpl */
if (elsiocb->context2) {
buf_ptr1 = (struct lpfc_dmabuf *) elsiocb->context2;
/* Free the response before processing the command. */
if (!list_empty(&buf_ptr1->list)) {
list_remove_head(&buf_ptr1->list, buf_ptr,
struct lpfc_dmabuf,
list);
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
kfree(buf_ptr);
}
lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys);
kfree(buf_ptr1);
lpfc_els_free_data(phba, buf_ptr1);
}
if (elsiocb->context3) {
buf_ptr = (struct lpfc_dmabuf *) elsiocb->context3;
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
kfree(buf_ptr);
lpfc_els_free_bpl(phba, buf_ptr);
}
lpfc_sli_release_iocbq(phba, elsiocb);
return 0;
@ -2119,15 +2106,15 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
"Data: x%x x%x x%x\n",
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
ndlp->nlp_rpi);
switch (ndlp->nlp_state) {
case NLP_STE_UNUSED_NODE: /* node is just allocated */
lpfc_drop_node(vport, ndlp);
break;
case NLP_STE_NPR_NODE: /* NPort Recovery mode */
lpfc_unreg_rpi(vport, ndlp);
break;
default:
break;
if (ndlp->nlp_state == NLP_STE_NPR_NODE) {
/* NPort Recovery mode or node is just allocated */
if (!lpfc_nlp_not_used(ndlp)) {
/* If the ndlp is being used by another discovery
* thread, just unregister the RPI.
*/
lpfc_unreg_rpi(vport, ndlp);
}
}
lpfc_els_free_iocb(phba, cmdiocb);
return;
@ -2160,15 +2147,27 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
struct lpfc_vport *vport = ndlp ? ndlp->vport : NULL;
struct Scsi_Host *shost = vport ? lpfc_shost_from_vport(vport) : NULL;
IOCB_t *irsp;
IOCB_t *irsp;
uint8_t *pcmd;
LPFC_MBOXQ_t *mbox = NULL;
struct lpfc_dmabuf *mp = NULL;
uint32_t ls_rjt = 0;
irsp = &rspiocb->iocb;
if (cmdiocb->context_un.mbox)
mbox = cmdiocb->context_un.mbox;
/* First determine if this is a LS_RJT cmpl */
pcmd = (uint8_t *) (((struct lpfc_dmabuf *) cmdiocb->context2)->virt);
if (*((uint32_t *) (pcmd)) == ELS_CMD_LS_RJT) {
/* A LS_RJT associated with Default RPI cleanup
* has its own seperate code path.
*/
if (!(ndlp->nlp_flag & NLP_RM_DFLT_RPI))
ls_rjt = 1;
}
/* Check to see if link went down during discovery */
if (!ndlp || lpfc_els_chk_latt(vport)) {
if (mbox) {
@ -2247,7 +2246,16 @@ out:
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~(NLP_ACC_REGLOGIN | NLP_RM_DFLT_RPI);
spin_unlock_irq(shost->host_lock);
/* If the node is not being used by another discovery thread,
* and we are sending a reject, we are done with it.
* Release driver reference count here and free associated
* resources.
*/
if (ls_rjt)
lpfc_nlp_not_used(ndlp);
}
lpfc_els_free_iocb(phba, cmdiocb);
return;
}
@ -2418,18 +2426,6 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError,
elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
rc = lpfc_sli_issue_iocb(phba, pring, elsiocb, 0);
/* If the node is in the UNUSED state, and we are sending
* a reject, we are done with it. Release driver reference
* count here. The outstanding els will release its reference on
* completion, as long as the ndlp stays in the UNUSED list,
* and the node can be freed then.
*/
if ((ndlp->nlp_state == NLP_STE_UNUSED_NODE) &&
!(ndlp->nlp_flag & NLP_DELAYED_RM)) {
ndlp->nlp_flag |= NLP_DELAYED_RM;
lpfc_nlp_put(ndlp);
}
if (rc == IOCB_ERROR) {
lpfc_els_free_iocb(phba, elsiocb);
return 1;
@ -2715,7 +2711,10 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport)
}
}
}
if (sentplogi == 0) {
if (sentplogi) {
lpfc_set_disctmo(vport);
}
else {
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_NLP_MORE;
spin_unlock_irq(shost->host_lock);
@ -3533,6 +3532,7 @@ lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
* other NLP_FABRIC logins
*/
lpfc_drop_node(vport, ndlp);
} else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) {
/* Fail outstanding I/O now since this
* device is marked for PLOGI
@ -3781,6 +3781,7 @@ static void
lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_vport *vport, struct lpfc_iocbq *elsiocb)
{
struct Scsi_Host *shost;
struct lpfc_nodelist *ndlp;
struct ls_rjt stat;
uint32_t *payload;
@ -3826,6 +3827,14 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
ndlp->nlp_type |= NLP_FABRIC;
}
}
else {
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) {
/* This is simular to the new node path */
lpfc_nlp_get(ndlp);
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
newnode = 1;
}
}
phba->fc_stat.elsRcvFrame++;
if (elsiocb->context1)
@ -3853,6 +3862,12 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
rjt_err = LSRJT_UNABLE_TPC;
break;
}
shost = lpfc_shost_from_vport(vport);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_TARGET_REMOVE;
spin_unlock_irq(shost->host_lock);
lpfc_disc_state_machine(vport, ndlp, elsiocb,
NLP_EVT_RCV_PLOGI);
@ -3864,7 +3879,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
phba->fc_stat.elsRcvFLOGI++;
lpfc_els_rcv_flogi(vport, elsiocb, ndlp);
if (newnode && (!(ndlp->nlp_flag & NLP_DELAYED_RM)))
if (newnode)
lpfc_nlp_put(ndlp);
break;
case ELS_CMD_LOGO:
@ -3894,7 +3909,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
case ELS_CMD_RSCN:
phba->fc_stat.elsRcvRSCN++;
lpfc_els_rcv_rscn(vport, elsiocb, ndlp);
if (newnode && (!(ndlp->nlp_flag & NLP_DELAYED_RM)))
if (newnode)
lpfc_nlp_put(ndlp);
break;
case ELS_CMD_ADISC:
@ -3966,7 +3981,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
phba->fc_stat.elsRcvLIRR++;
lpfc_els_rcv_lirr(vport, elsiocb, ndlp);
if (newnode && (!(ndlp->nlp_flag & NLP_DELAYED_RM)))
if (newnode)
lpfc_nlp_put(ndlp);
break;
case ELS_CMD_RPS:
@ -3976,7 +3991,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
phba->fc_stat.elsRcvRPS++;
lpfc_els_rcv_rps(vport, elsiocb, ndlp);
if (newnode && (!(ndlp->nlp_flag & NLP_DELAYED_RM)))
if (newnode)
lpfc_nlp_put(ndlp);
break;
case ELS_CMD_RPL:
@ -3986,7 +4001,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
phba->fc_stat.elsRcvRPL++;
lpfc_els_rcv_rpl(vport, elsiocb, ndlp);
if (newnode && (!(ndlp->nlp_flag & NLP_DELAYED_RM)))
if (newnode)
lpfc_nlp_put(ndlp);
break;
case ELS_CMD_RNID:
@ -3996,7 +4011,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
phba->fc_stat.elsRcvRNID++;
lpfc_els_rcv_rnid(vport, elsiocb, ndlp);
if (newnode && (!(ndlp->nlp_flag & NLP_DELAYED_RM)))
if (newnode)
lpfc_nlp_put(ndlp);
break;
default:
@ -4011,7 +4026,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"0115 Unknown ELS command x%x "
"received from NPORT x%x\n", cmd, did);
if (newnode && (!(ndlp->nlp_flag & NLP_DELAYED_RM)))
if (newnode)
lpfc_nlp_put(ndlp);
break;
}

View File

@ -157,6 +157,8 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
struct lpfc_vport *vport;
struct lpfc_hba *phba;
uint8_t *name;
int put_node;
int put_rport;
int warn_on = 0;
rport = ndlp->rport;
@ -178,9 +180,6 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
return;
if (ndlp->nlp_type & NLP_FABRIC) {
int put_node;
int put_rport;
/* We will clean up these Nodes in linkup */
put_node = rdata->pnode != NULL;
put_rport = ndlp->rport != NULL;
@ -222,23 +221,20 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
ndlp->nlp_state, ndlp->nlp_rpi);
}
put_node = rdata->pnode != NULL;
put_rport = ndlp->rport != NULL;
rdata->pnode = NULL;
ndlp->rport = NULL;
if (put_node)
lpfc_nlp_put(ndlp);
if (put_rport)
put_device(&rport->dev);
if (!(vport->load_flag & FC_UNLOADING) &&
!(ndlp->nlp_flag & NLP_DELAY_TMO) &&
!(ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
(ndlp->nlp_state != NLP_STE_UNMAPPED_NODE))
(ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)) {
lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
else {
int put_node;
int put_rport;
put_node = rdata->pnode != NULL;
put_rport = ndlp->rport != NULL;
rdata->pnode = NULL;
ndlp->rport = NULL;
if (put_node)
lpfc_nlp_put(ndlp);
if (put_rport)
put_device(&rport->dev);
}
}
@ -546,11 +542,9 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove)
}
}
static void
void
lpfc_port_link_failure(struct lpfc_vport *vport)
{
struct lpfc_nodelist *ndlp, *next_ndlp;
/* Cleanup any outstanding RSCN activity */
lpfc_els_flush_rscn(vport);
@ -559,11 +553,6 @@ lpfc_port_link_failure(struct lpfc_vport *vport)
lpfc_cleanup_rpis(vport, 0);
/* free any ndlp's on unused list */
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp)
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
lpfc_drop_node(vport, ndlp);
/* Turn off discovery timer if its running */
lpfc_can_disctmo(vport);
}
@ -670,7 +659,6 @@ static void
lpfc_linkup_port(struct lpfc_vport *vport)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_nodelist *ndlp, *next_ndlp;
struct lpfc_hba *phba = vport->phba;
if ((vport->load_flag & FC_UNLOADING) != 0)
@ -697,11 +685,6 @@ lpfc_linkup_port(struct lpfc_vport *vport)
if (vport->fc_flag & FC_LBIT)
lpfc_linkup_cleanup_nodes(vport);
/* free any ndlp's in unused state */
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes,
nlp_listp)
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
lpfc_drop_node(vport, ndlp);
}
static int
@ -1345,7 +1328,9 @@ out:
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
mempool_free(pmb, phba->mbox_mem_pool);
lpfc_drop_node(vport, ndlp);
/* If no other thread is using the ndlp, free it */
lpfc_nlp_not_used(ndlp);
if (phba->fc_topology == TOPOLOGY_LOOP) {
/*
@ -1605,16 +1590,6 @@ lpfc_nlp_set_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp->nlp_type &= ~NLP_FC_NODE;
}
if ((old_state == NLP_STE_UNUSED_NODE) &&
(state != NLP_STE_UNUSED_NODE) &&
(ndlp->nlp_flag & NLP_DELAYED_RM)) {
/* We are using the ndlp after all, so reverse
* the delayed removal of it.
*/
ndlp->nlp_flag &= ~NLP_DELAYED_RM;
lpfc_nlp_get(ndlp);
}
if (list_empty(&ndlp->nlp_listp)) {
spin_lock_irq(shost->host_lock);
list_add_tail(&ndlp->nlp_listp, &vport->fc_nodes);
@ -1646,9 +1621,16 @@ lpfc_dequeue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
void
lpfc_drop_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
/*
* Use of lpfc_drop_node and UNUSED list. lpfc_drop_node should
* be used if we wish to issue the "last" lpfc_nlp_put() to remove
* the ndlp from the vport. The ndlp resides on the UNUSED list
* until ALL other outstanding threads have completed. Thus, if a
* ndlp is on the UNUSED list already, we should never do another
* lpfc_drop_node() on it.
*/
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE);
if (!(ndlp->nlp_flag & NLP_DELAYED_RM))
lpfc_nlp_put(ndlp);
lpfc_nlp_put(ndlp);
return;
}
@ -2116,6 +2098,12 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did)
}
if (vport->fc_flag & FC_RSCN_MODE) {
if (lpfc_rscn_payload_check(vport, did)) {
/* If we've already recieved a PLOGI from this NPort
* we don't need to try to discover it again.
*/
if (ndlp->nlp_flag & NLP_RCV_PLOGI)
return NULL;
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock);
@ -2128,8 +2116,13 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did)
} else
ndlp = NULL;
} else {
/* If we've already recieved a PLOGI from this NPort,
* or we are already in the process of discovery on it,
* we don't need to try to discover it again.
*/
if (ndlp->nlp_state == NLP_STE_ADISC_ISSUE ||
ndlp->nlp_state == NLP_STE_PLOGI_ISSUE)
ndlp->nlp_state == NLP_STE_PLOGI_ISSUE ||
ndlp->nlp_flag & NLP_RCV_PLOGI)
return NULL;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
spin_lock_irq(shost->host_lock);
@ -2497,6 +2490,7 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport)
if (ndlp->nlp_type & NLP_FABRIC) {
/* Clean up the ndlp on Fabric connections */
lpfc_drop_node(vport, ndlp);
} else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) {
/* Fail outstanding IO now since device
* is marked for PLOGI.
@ -2515,7 +2509,7 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport)
/* Initial FLOGI timeout */
lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
"0222 Initial %s timeout\n",
vport->vpi ? "FLOGI" : "FDISC");
vport->vpi ? "FDISC" : "FLOGI");
/* Assume no Fabric and go on with discovery.
* Check for outstanding ELS FLOGI to abort.
@ -2537,10 +2531,10 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport)
/* Next look for NameServer ndlp */
ndlp = lpfc_findnode_did(vport, NameServer_DID);
if (ndlp)
lpfc_nlp_put(ndlp);
/* Start discovery */
lpfc_disc_start(vport);
break;
lpfc_els_abort(phba, ndlp);
/* ReStart discovery */
goto restart_disc;
case LPFC_NS_QRY:
/* Check for wait for NameServer Rsp timeout */
@ -2559,6 +2553,7 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport)
}
vport->fc_ns_retry = 0;
restart_disc:
/*
* Discovery is over.
* set port_state to PORT_READY if SLI2.
@ -2731,8 +2726,7 @@ __lpfc_find_node(struct lpfc_vport *vport, node_filter filter, void *param)
struct lpfc_nodelist *ndlp;
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
if (ndlp->nlp_state != NLP_STE_UNUSED_NODE &&
filter(ndlp, param))
if (filter(ndlp, param))
return ndlp;
}
return NULL;

View File

@ -1334,15 +1334,35 @@ lpfc_hba_init(struct lpfc_hba *phba, uint32_t *hbainit)
kfree(HashWorking);
}
static void
void
lpfc_cleanup(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp, *next_ndlp;
/* clean up phba - lpfc specific */
lpfc_can_disctmo(vport);
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp)
lpfc_nlp_put(ndlp);
if (phba->link_state > LPFC_LINK_DOWN)
lpfc_port_link_failure(vport);
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
if (ndlp->nlp_type & NLP_FABRIC)
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RECOVERY);
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RM);
}
/* At this point, ALL ndlp's should be gone */
while (!list_empty(&vport->fc_nodes)) {
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes,
nlp_listp) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
"0233 Nodelist x%x not free: %d\n",
ndlp->nlp_DID,
atomic_read(&ndlp->kref.refcount));
lpfc_drop_node(vport, ndlp);
}
}
return;
}
@ -1463,6 +1483,8 @@ lpfc_offline_prep(struct lpfc_hba * phba)
{
struct lpfc_vport *vport = phba->pport;
struct lpfc_nodelist *ndlp, *next_ndlp;
struct lpfc_vport **vports;
int i;
if (vport->fc_flag & FC_OFFLINE_MODE)
return;
@ -1471,10 +1493,32 @@ lpfc_offline_prep(struct lpfc_hba * phba)
lpfc_linkdown(phba);
/* Issue an unreg_login to all nodes */
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp)
if (ndlp->nlp_state != NLP_STE_UNUSED_NODE)
lpfc_unreg_rpi(vport, ndlp);
/* Issue an unreg_login to all nodes on all vports */
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL) {
for(i = 0; i < LPFC_MAX_VPORTS && vports[i] != NULL; i++) {
struct Scsi_Host *shost;
shost = lpfc_shost_from_vport(vports[i]);
list_for_each_entry_safe(ndlp, next_ndlp,
&vports[i]->fc_nodes,
nlp_listp) {
if (ndlp->nlp_state == NLP_STE_UNUSED_NODE)
continue;
if (ndlp->nlp_type & NLP_FABRIC) {
lpfc_disc_state_machine(vports[i], ndlp,
NULL, NLP_EVT_DEVICE_RECOVERY);
lpfc_disc_state_machine(vports[i], ndlp,
NULL, NLP_EVT_DEVICE_RM);
}
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NPR_ADISC;
spin_unlock_irq(shost->host_lock);
lpfc_unreg_rpi(vports[i], ndlp);
}
}
}
lpfc_destroy_vport_work_array(vports);
lpfc_sli_flush_mbox_queue(phba);
}
@ -1508,7 +1552,6 @@ lpfc_offline(struct lpfc_hba *phba)
if (vports != NULL)
for(i = 0; i < LPFC_MAX_VPORTS && vports[i] != NULL; i++) {
shost = lpfc_shost_from_vport(vports[i]);
lpfc_cleanup(vports[i]);
spin_lock_irq(shost->host_lock);
vports[i]->work_port_events = 0;
vports[i]->fc_flag |= FC_OFFLINE_MODE;
@ -2061,6 +2104,8 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
fc_remove_host(shost);
scsi_remove_host(shost);
lpfc_cleanup(vport);
/*
* Bring down the SLI Layer. This step disable all interrupts,
* clears the rings, discards all mailbox commands, and resets
@ -2075,7 +2120,6 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
spin_unlock_irq(&phba->hbalock);
lpfc_debugfs_terminate(vport);
lpfc_cleanup(vport);
kthread_stop(phba->worker_thread);

View File

@ -406,6 +406,41 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp, mbox);
return 1;
}
/* If the remote NPort logs into us, before we can initiate
* discovery to them, cleanup the NPort from discovery accordingly.
*/
if (ndlp->nlp_state == NLP_STE_NPR_NODE) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_DELAY_TMO;
spin_unlock_irq(shost->host_lock);
del_timer_sync(&ndlp->nlp_delayfunc);
ndlp->nlp_last_elscmd = 0;
if (!list_empty(&ndlp->els_retry_evt.evt_listp))
list_del_init(&ndlp->els_retry_evt.evt_listp);
if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock);
if (vport->num_disc_nodes) {
/* Check to see if there are more
* PLOGIs to be sent
*/
lpfc_more_plogi(vport);
if (vport->num_disc_nodes == 0) {
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~FC_NDISC_ACTIVE;
spin_unlock_irq(shost->host_lock);
lpfc_can_disctmo(vport);
lpfc_end_rscn(vport);
}
}
}
}
lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox);
return 1;
@ -500,12 +535,9 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
spin_unlock_irq(shost->host_lock);
ndlp->nlp_last_elscmd = ELS_CMD_PLOGI;
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
} else {
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE);
}
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NPR_ADISC;
@ -593,6 +625,25 @@ lpfc_disc_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
return ndlp->nlp_state;
}
static uint32_t
lpfc_cmpl_plogi_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
/* This transition is only legal if we previously
* rcv'ed a PLOGI. Since we don't want 2 discovery threads
* working on the same NPortID, do nothing for this thread
* to stop it.
*/
if (!(ndlp->nlp_flag & NLP_RCV_PLOGI)) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
"0253 Illegal State Transition: node x%x "
"event x%x, state x%x Data: x%x x%x\n",
ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi,
ndlp->nlp_flag);
}
return ndlp->nlp_state;
}
/* Start of Discovery State Machine routines */
static uint32_t
@ -604,11 +655,8 @@ lpfc_rcv_plogi_unused_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
cmdiocb = (struct lpfc_iocbq *) arg;
if (lpfc_rcv_plogi(vport, ndlp, cmdiocb)) {
ndlp->nlp_prev_state = NLP_STE_UNUSED_NODE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE);
return ndlp->nlp_state;
}
lpfc_drop_node(vport, ndlp);
return NLP_STE_FREED_NODE;
}
@ -617,7 +665,6 @@ lpfc_rcv_els_unused_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
lpfc_issue_els_logo(vport, ndlp, 0);
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE);
return ndlp->nlp_state;
}
@ -632,7 +679,6 @@ lpfc_rcv_logo_unused_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
ndlp->nlp_flag |= NLP_LOGO_ACC;
spin_unlock_irq(shost->host_lock);
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE);
return ndlp->nlp_state;
}
@ -641,7 +687,6 @@ static uint32_t
lpfc_cmpl_logo_unused_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
lpfc_drop_node(vport, ndlp);
return NLP_STE_FREED_NODE;
}
@ -649,7 +694,6 @@ static uint32_t
lpfc_device_rm_unused_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
void *arg, uint32_t evt)
{
lpfc_drop_node(vport, ndlp);
return NLP_STE_FREED_NODE;
}
@ -864,7 +908,7 @@ out:
/* Free this node since the driver cannot login or has the wrong
sparm */
lpfc_drop_node(vport, ndlp);
lpfc_nlp_not_used(ndlp);
return NLP_STE_FREED_NODE;
}
@ -1195,8 +1239,8 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport,
* retry discovery.
*/
if (mb->mbxStatus == MBXERR_RPI_FULL) {
ndlp->nlp_prev_state = NLP_STE_UNUSED_NODE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE);
ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
return ndlp->nlp_state;
}
@ -1376,7 +1420,7 @@ out:
lpfc_issue_els_logo(vport, ndlp, 0);
ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE);
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
return ndlp->nlp_state;
}
@ -1751,7 +1795,7 @@ lpfc_cmpl_plogi_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
irsp = &rspiocb->iocb;
if (irsp->ulpStatus) {
lpfc_drop_node(vport, ndlp);
lpfc_nlp_not_used(ndlp);
return NLP_STE_FREED_NODE;
}
return ndlp->nlp_state;
@ -1966,7 +2010,7 @@ static uint32_t (*lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT])
lpfc_rcv_padisc_reglogin_issue, /* RCV_ADISC */
lpfc_rcv_padisc_reglogin_issue, /* RCV_PDISC */
lpfc_rcv_prlo_reglogin_issue, /* RCV_PRLO */
lpfc_disc_illegal, /* CMPL_PLOGI */
lpfc_cmpl_plogi_illegal, /* CMPL_PLOGI */
lpfc_disc_illegal, /* CMPL_PRLI */
lpfc_disc_illegal, /* CMPL_LOGO */
lpfc_disc_illegal, /* CMPL_ADISC */
@ -1980,7 +2024,7 @@ static uint32_t (*lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT])
lpfc_rcv_padisc_prli_issue, /* RCV_ADISC */
lpfc_rcv_padisc_prli_issue, /* RCV_PDISC */
lpfc_rcv_prlo_prli_issue, /* RCV_PRLO */
lpfc_disc_illegal, /* CMPL_PLOGI */
lpfc_cmpl_plogi_illegal, /* CMPL_PLOGI */
lpfc_cmpl_prli_prli_issue, /* CMPL_PRLI */
lpfc_disc_illegal, /* CMPL_LOGO */
lpfc_disc_illegal, /* CMPL_ADISC */

View File

@ -445,7 +445,6 @@ int
lpfc_vport_delete(struct fc_vport *fc_vport)
{
struct lpfc_nodelist *ndlp = NULL;
struct lpfc_nodelist *next_ndlp;
struct Scsi_Host *shost = (struct Scsi_Host *) fc_vport->shost;
struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
struct lpfc_hba *phba = vport->phba;
@ -531,23 +530,20 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
}
skip_logo:
lpfc_cleanup(vport);
lpfc_sli_host_down(vport);
list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) {
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RECOVERY);
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RM);
}
lpfc_stop_vport_timers(vport);
lpfc_unreg_all_rpis(vport);
lpfc_unreg_default_rpis(vport);
/*
* Completion of unreg_vpi (lpfc_mbx_cmpl_unreg_vpi) does the
* scsi_host_put() to release the vport.
*/
lpfc_mbx_unreg_vpi(vport);
if (!(phba->pport->load_flag & FC_UNLOADING)) {
lpfc_unreg_default_rpis(vport);
/*
* Completion of unreg_vpi (lpfc_mbx_cmpl_unreg_vpi)
* does the scsi_host_put() to release the vport.
*/
lpfc_mbx_unreg_vpi(vport);
}
lpfc_free_vpi(phba, vport->vpi);
vport->work_port_events = 0;