X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2Flpfc%2Flpfc_els.c;h=283b7d824c34fae6693f5a2e71ae60e1008219b6;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=68d1b77e02560c7a764a16ebbfc4c0fcdd637c25;hpb=cee37fe97739d85991964371c1f3a745c00dd236;p=linux-2.6.git diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 68d1b77e0..283b7d824 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -1,31 +1,29 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * - * Enterprise Fibre Channel Host Bus Adapters. * - * Refer to the README file included with this package for * - * driver version and adapter support. * - * Copyright (C) 2004 Emulex Corporation. * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2004-2006 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * + * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details, a copy of which * - * can be found in the file COPYING included with this package. * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * *******************************************************************/ -/* - * $Id: lpfc_els.c 1.186 2005/04/13 14:26:55EDT sf_support Exp $ - */ - #include #include #include +#include #include #include #include @@ -94,19 +92,17 @@ lpfc_els_chk_latt(struct lpfc_hba * phba) } } - return (1); + return 1; } static struct lpfc_iocbq * -lpfc_prep_els_iocb(struct lpfc_hba * phba, - uint8_t expectRsp, - uint16_t cmdSize, - uint8_t retry, struct lpfc_nodelist * ndlp, uint32_t elscmd) +lpfc_prep_els_iocb(struct lpfc_hba * phba, uint8_t expectRsp, + uint16_t cmdSize, uint8_t retry, struct lpfc_nodelist * ndlp, + uint32_t did, uint32_t elscmd) { - struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list; struct lpfc_sli_ring *pring; - struct lpfc_iocbq *elsiocb = NULL; + struct lpfc_iocbq *elsiocb; struct lpfc_dmabuf *pcmd, *prsp, *pbuflist; struct ulp_bde64 *bpl; IOCB_t *icmd; @@ -116,15 +112,13 @@ lpfc_prep_els_iocb(struct lpfc_hba * phba, if (phba->hba_state < LPFC_LINK_UP) return NULL; - /* Allocate buffer for command iocb */ spin_lock_irq(phba->host->host_lock); - list_remove_head(lpfc_iocb_list, elsiocb, struct lpfc_iocbq, list); + elsiocb = lpfc_sli_get_iocbq(phba); spin_unlock_irq(phba->host->host_lock); if (elsiocb == NULL) return NULL; - memset(elsiocb, 0, sizeof (struct lpfc_iocbq)); icmd = &elsiocb->iocb; /* fill in BDEs for command */ @@ -132,10 +126,11 @@ lpfc_prep_els_iocb(struct lpfc_hba * phba, if (((pcmd = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL)) == 0) || ((pcmd->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(pcmd->phys))) == 0)) { - if (pcmd) - kfree(pcmd); + kfree(pcmd); - list_add_tail(&elsiocb->list, lpfc_iocb_list); + spin_lock_irq(phba->host->host_lock); + lpfc_sli_release_iocbq(phba, elsiocb); + spin_unlock_irq(phba->host->host_lock); return NULL; } @@ -148,11 +143,12 @@ lpfc_prep_els_iocb(struct lpfc_hba * phba, prsp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &prsp->phys); if (prsp == 0 || prsp->virt == 0) { - if (prsp) - kfree(prsp); + kfree(prsp); lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); kfree(pcmd); - list_add_tail(&elsiocb->list, lpfc_iocb_list); + spin_lock_irq(phba->host->host_lock); + lpfc_sli_release_iocbq(phba, elsiocb); + spin_unlock_irq(phba->host->host_lock); return NULL; } INIT_LIST_HEAD(&prsp->list); @@ -166,13 +162,14 @@ lpfc_prep_els_iocb(struct lpfc_hba * phba, pbuflist->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &pbuflist->phys); if (pbuflist == 0 || pbuflist->virt == 0) { - list_add_tail(&elsiocb->list, lpfc_iocb_list); + spin_lock_irq(phba->host->host_lock); + lpfc_sli_release_iocbq(phba, elsiocb); + spin_unlock_irq(phba->host->host_lock); lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); lpfc_mbuf_free(phba, prsp->virt, prsp->phys); kfree(pcmd); kfree(prsp); - if (pbuflist) - kfree(pbuflist); + kfree(pbuflist); return NULL; } @@ -183,7 +180,7 @@ lpfc_prep_els_iocb(struct lpfc_hba * phba, icmd->un.elsreq64.bdl.bdeFlags = BUFF_TYPE_BDL; if (expectRsp) { icmd->un.elsreq64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64)); - icmd->un.elsreq64.remoteID = ndlp->nlp_DID; /* DID */ + icmd->un.elsreq64.remoteID = did; /* DID */ icmd->ulpCommand = CMD_ELS_REQUEST64_CR; } else { icmd->un.elsreq64.bdl.bdeSize = sizeof (struct ulp_bde64); @@ -227,7 +224,7 @@ lpfc_prep_els_iocb(struct lpfc_hba * phba, "%d:0116 Xmit ELS command x%x to remote " "NPORT x%x Data: x%x x%x\n", phba->brd_no, elscmd, - ndlp->nlp_DID, icmd->ulpIoTag, phba->hba_state); + did, icmd->ulpIoTag, phba->hba_state); } else { /* Xmit ELS response to remote NPORT */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, @@ -237,7 +234,7 @@ lpfc_prep_els_iocb(struct lpfc_hba * phba, ndlp->nlp_DID, icmd->ulpIoTag, cmdSize); } - return (elsiocb); + return elsiocb; } @@ -305,10 +302,6 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, if (lpfc_reg_login(phba, Fabric_DID, (uint8_t *) sp, mbox, 0)) goto fail_free_mbox; - /* - * set_slim mailbox command needs to execute first, - * queue this command to be processed later. - */ mbox->mbox_cmpl = lpfc_mbx_cmpl_fabric_reg_login; mbox->context2 = ndlp; @@ -448,9 +441,10 @@ lpfc_cmpl_els_flogi(struct lpfc_hba * phba, lpfc_printf_log(phba, KERN_INFO, LOG_ELS, - "%d:0100 FLOGI failure Data: x%x x%x\n", + "%d:0100 FLOGI failure Data: x%x x%x x%x\n", phba->brd_no, - irsp->ulpStatus, irsp->un.ulpWord[4]); + irsp->ulpStatus, irsp->un.ulpWord[4], + irsp->ulpTimeout); goto flogifail; } @@ -517,10 +511,10 @@ lpfc_issue_els_flogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, pring = &phba->sli.ring[LPFC_ELS_RING]; cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm)); - if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, - ndlp, ELS_CMD_FLOGI)) == 0) { - return (1); - } + elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, ndlp, + ndlp->nlp_DID, ELS_CMD_FLOGI); + if (!elsiocb) + return 1; icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); @@ -554,9 +548,9 @@ lpfc_issue_els_flogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, spin_unlock_irq(phba->host->host_lock); if (rc == IOCB_ERROR) { lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } - return (0); + return 0; } int @@ -598,10 +592,8 @@ lpfc_els_abort_flogi(struct lpfc_hba * phba) spin_unlock_irq(phba->host->host_lock); (iocb->iocb_cmpl) (phba, iocb, iocb); spin_lock_irq(phba->host->host_lock); - } else { - list_add_tail(&iocb->list, - &phba->lpfc_iocb_list); - } + } else + lpfc_sli_release_iocbq(phba, iocb); } } } @@ -615,29 +607,21 @@ lpfc_initial_flogi(struct lpfc_hba * phba) { struct lpfc_nodelist *ndlp; - /* First look for Fabric ndlp on the unmapped list */ - - if ((ndlp = - lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, - Fabric_DID)) == 0) { + /* First look for the Fabric ndlp */ + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, Fabric_DID); + if (!ndlp) { /* Cannot find existing Fabric ndlp, so allocate a new one */ - if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL)) - == 0) { - return (0); - } + ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); + if (!ndlp) + return 0; lpfc_nlp_init(phba, ndlp, Fabric_DID); - } - else { - phba->fc_unmap_cnt--; - list_del(&ndlp->nlp_listp); - spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_LIST_MASK; - spin_unlock_irq(phba->host->host_lock); + } else { + lpfc_nlp_list(phba, ndlp, NLP_JUST_DQ); } if (lpfc_issue_els_flogi(phba, ndlp, 0)) { mempool_free( ndlp, phba->nlp_mem_pool); } - return (1); + return 1; } static void @@ -663,38 +647,90 @@ lpfc_more_plogi(struct lpfc_hba * phba) return; } +static struct lpfc_nodelist * +lpfc_plogi_confirm_nport(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist *ndlp) +{ + struct lpfc_nodelist *new_ndlp; + struct lpfc_dmabuf *pcmd, *prsp; + uint32_t *lp; + struct serv_parm *sp; + uint8_t name[sizeof (struct lpfc_name)]; + uint32_t rc; + + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + prsp = (struct lpfc_dmabuf *) pcmd->list.next; + lp = (uint32_t *) prsp->virt; + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + + /* Now we to find out if the NPort we are logging into, matches the WWPN + * we have for that ndlp. If not, we have some work to do. + */ + new_ndlp = lpfc_findnode_wwpn(phba, NLP_SEARCH_ALL, &sp->portName); + + memset(name, 0, sizeof (struct lpfc_name)); + rc = memcmp(&ndlp->nlp_portname, name, sizeof(struct lpfc_name)); + if (!rc || (new_ndlp == ndlp)) { + return ndlp; + } + + if (!new_ndlp) { + new_ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC); + if (!new_ndlp) + return ndlp; + + lpfc_nlp_init(phba, new_ndlp, ndlp->nlp_DID); + } + + lpfc_unreg_rpi(phba, new_ndlp); + new_ndlp->nlp_prev_state = ndlp->nlp_state; + new_ndlp->nlp_DID = ndlp->nlp_DID; + new_ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, new_ndlp, NLP_PLOGI_LIST); + + /* Move this back to NPR list */ + lpfc_unreg_rpi(phba, ndlp); + ndlp->nlp_DID = 0; /* Two ndlps cannot have the same did */ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + + return new_ndlp; +} + static void lpfc_cmpl_els_plogi(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb) { IOCB_t *irsp; - struct lpfc_sli *psli; struct lpfc_nodelist *ndlp; int disc, rc, did, type; - psli = &phba->sli; /* we pass cmdiocb to state machine which needs rspiocb as well */ cmdiocb->context_un.rsp_iocb = rspiocb; irsp = &rspiocb->iocb; - ndlp = (struct lpfc_nodelist *) cmdiocb->context1; - spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_PLOGI_SND; - spin_unlock_irq(phba->host->host_lock); + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, + irsp->un.elsreq64.remoteID); + if (!ndlp) + goto out; /* Since ndlp can be freed in the disc state machine, note if this node * is being used during discovery. */ disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + spin_lock_irq(phba->host->host_lock); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + spin_unlock_irq(phba->host->host_lock); rc = 0; /* PLOGI completes to NPort */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, "%d:0102 PLOGI completes to NPort x%x " - "Data: x%x x%x x%x x%x\n", + "Data: x%x x%x x%x x%x x%x\n", phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, - irsp->un.ulpWord[4], disc, phba->num_disc_nodes); + irsp->un.ulpWord[4], irsp->ulpTimeout, disc, + phba->num_disc_nodes); /* Check to see if link went down during discovery */ if (lpfc_els_chk_latt(phba)) { @@ -724,56 +760,43 @@ lpfc_cmpl_els_plogi(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_LINK_DOWN) || (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { - disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); - } - else { + rc = NLP_STE_FREED_NODE; + } else { rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_PLOGI); } } else { /* Good status, call state machine */ + ndlp = lpfc_plogi_confirm_nport(phba, cmdiocb, ndlp); rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_PLOGI); } - if (type & NLP_FABRIC) { - /* If we cannot login to Nameserver, kick off discovery now */ - if ((did == NameServer_DID) && (rc == NLP_STE_FREED_NODE)) { - lpfc_disc_start(phba); - } - goto out; - } - if (disc && phba->num_disc_nodes) { /* Check to see if there are more PLOGIs to be sent */ lpfc_more_plogi(phba); - } - if (rc != NLP_STE_FREED_NODE) { - spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - spin_unlock_irq(phba->host->host_lock); - } - - if (phba->num_disc_nodes == 0) { - if(disc) { + if (phba->num_disc_nodes == 0) { spin_lock_irq(phba->host->host_lock); phba->fc_flag &= ~FC_NDISC_ACTIVE; spin_unlock_irq(phba->host->host_lock); - } - lpfc_can_disctmo(phba); - if (phba->fc_flag & FC_RSCN_MODE) { - /* Check to see if more RSCNs came in while we were - * processing this one. - */ - if ((phba->fc_rscn_id_cnt == 0) && - (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { - spin_lock_irq(phba->host->host_lock); - phba->fc_flag &= ~FC_RSCN_MODE; - spin_unlock_irq(phba->host->host_lock); - } else { - lpfc_els_handle_rscn(phba); + + lpfc_can_disctmo(phba); + if (phba->fc_flag & FC_RSCN_MODE) { + /* + * Check to see if more RSCNs came in while + * we were processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + spin_lock_irq(phba->host->host_lock); + phba->fc_flag &= ~FC_RSCN_MODE; + spin_unlock_irq(phba->host->host_lock); + } else { + lpfc_els_handle_rscn(phba); + } } } } @@ -784,8 +807,7 @@ out: } int -lpfc_issue_els_plogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, - uint8_t retry) +lpfc_issue_els_plogi(struct lpfc_hba * phba, uint32_t did, uint8_t retry) { struct serv_parm *sp; IOCB_t *icmd; @@ -799,10 +821,10 @@ lpfc_issue_els_plogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm)); - if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, - ndlp, ELS_CMD_PLOGI)) == 0) { - return (1); - } + elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, 0, did, + ELS_CMD_PLOGI); + if (!elsiocb) + return 1; icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); @@ -822,15 +844,13 @@ lpfc_issue_els_plogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, phba->fc_stat.elsXmitPLOGI++; elsiocb->iocb_cmpl = lpfc_cmpl_els_plogi; spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag |= NLP_PLOGI_SND; if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { - ndlp->nlp_flag &= ~NLP_PLOGI_SND; spin_unlock_irq(phba->host->host_lock); lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } spin_unlock_irq(phba->host->host_lock); - return (0); + return 0; } static void @@ -854,9 +874,10 @@ lpfc_cmpl_els_prli(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, /* PRLI completes to NPort */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, "%d:0103 PRLI completes to NPort x%x " - "Data: x%x x%x x%x\n", + "Data: x%x x%x x%x x%x\n", phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, - irsp->un.ulpWord[4], phba->num_disc_nodes); + irsp->un.ulpWord[4], irsp->ulpTimeout, + phba->num_disc_nodes); phba->fc_prli_sent--; /* Check to see if link went down during discovery */ @@ -873,10 +894,10 @@ lpfc_cmpl_els_prli(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_LINK_DOWN) || (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { goto out; - } - else { + } else { lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_PRLI); } @@ -906,10 +927,10 @@ lpfc_issue_els_prli(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ cmdsize = (sizeof (uint32_t) + sizeof (PRLI)); - if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, - ndlp, ELS_CMD_PRLI)) == 0) { - return (1); - } + elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, ndlp, + ndlp->nlp_DID, ELS_CMD_PRLI); + if (!elsiocb) + return 1; icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); @@ -945,11 +966,11 @@ lpfc_issue_els_prli(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, ndlp->nlp_flag &= ~NLP_PRLI_SND; spin_unlock_irq(phba->host->host_lock); lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } spin_unlock_irq(phba->host->host_lock); phba->fc_prli_sent++; - return (0); + return 0; } static void @@ -1018,21 +1039,22 @@ lpfc_cmpl_els_adisc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, irsp = &(rspiocb->iocb); ndlp = (struct lpfc_nodelist *) cmdiocb->context1; - spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_ADISC_SND; - spin_unlock_irq(phba->host->host_lock); /* Since ndlp can be freed in the disc state machine, note if this node * is being used during discovery. */ disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + spin_lock_irq(phba->host->host_lock); + ndlp->nlp_flag &= ~(NLP_ADISC_SND | NLP_NPR_2B_DISC); + spin_unlock_irq(phba->host->host_lock); /* ADISC completes to NPort */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, "%d:0104 ADISC completes to NPort x%x " - "Data: x%x x%x x%x x%x\n", + "Data: x%x x%x x%x x%x x%x\n", phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, - irsp->un.ulpWord[4], disc, phba->num_disc_nodes); + irsp->un.ulpWord[4], irsp->ulpTimeout, disc, + phba->num_disc_nodes); /* Check to see if link went down during discovery */ if (lpfc_els_chk_latt(phba)) { @@ -1056,12 +1078,10 @@ lpfc_cmpl_els_adisc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, } /* ADISC failed */ /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ - if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && - ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || - (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { - disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); - } - else { + if ((irsp->ulpStatus != IOSTAT_LOCAL_REJECT) || + ((irsp->un.ulpWord[4] != IOERR_SLI_ABORTED) && + (irsp->un.ulpWord[4] != IOERR_LINK_DOWN) && + (irsp->un.ulpWord[4] != IOERR_SLI_DOWN))) { lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_ADISC); } @@ -1113,9 +1133,6 @@ lpfc_cmpl_els_adisc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, } } } - spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; - spin_unlock_irq(phba->host->host_lock); out: lpfc_els_free_iocb(phba, cmdiocb); return; @@ -1137,10 +1154,10 @@ lpfc_issue_els_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ cmdsize = (sizeof (uint32_t) + sizeof (ADISC)); - if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, - ndlp, ELS_CMD_ADISC)) == 0) { - return (1); - } + elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, ndlp, + ndlp->nlp_DID, ELS_CMD_ADISC); + if (!elsiocb) + return 1; icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); @@ -1164,10 +1181,10 @@ lpfc_issue_els_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, ndlp->nlp_flag &= ~NLP_ADISC_SND; spin_unlock_irq(phba->host->host_lock); lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } spin_unlock_irq(phba->host->host_lock); - return (0); + return 0; } static void @@ -1191,9 +1208,10 @@ lpfc_cmpl_els_logo(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, /* LOGO completes to NPort */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, "%d:0105 LOGO completes to NPort x%x " - "Data: x%x x%x x%x\n", + "Data: x%x x%x x%x x%x\n", phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, - irsp->un.ulpWord[4], phba->num_disc_nodes); + irsp->un.ulpWord[4], irsp->ulpTimeout, + phba->num_disc_nodes); /* Check to see if link went down during discovery */ if (lpfc_els_chk_latt(phba)) @@ -1209,20 +1227,18 @@ lpfc_cmpl_els_logo(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_LINK_DOWN) || (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { goto out; - } - else { + } else { lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO); } } else { - /* Good status, call state machine */ + /* Good status, call state machine. + * This will unregister the rpi if needed. + */ lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO); - - if (ndlp->nlp_flag & NLP_DELAY_TMO) { - lpfc_unreg_rpi(phba, ndlp); - } } out: @@ -1244,11 +1260,11 @@ lpfc_issue_els_logo(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, psli = &phba->sli; pring = &psli->ring[LPFC_ELS_RING]; - cmdsize = 2 * (sizeof (uint32_t) + sizeof (struct lpfc_name)); - if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, - ndlp, ELS_CMD_LOGO)) == 0) { - return (1); - } + cmdsize = (2 * sizeof (uint32_t)) + sizeof (struct lpfc_name); + elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, ndlp, + ndlp->nlp_DID, ELS_CMD_LOGO); + if (!elsiocb) + return 1; icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); @@ -1268,10 +1284,10 @@ lpfc_issue_els_logo(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, ndlp->nlp_flag &= ~NLP_LOGO_SND; spin_unlock_irq(phba->host->host_lock); lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } spin_unlock_irq(phba->host->host_lock); - return (0); + return 0; } static void @@ -1286,9 +1302,10 @@ lpfc_cmpl_els_cmd(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, lpfc_printf_log(phba, KERN_INFO, LOG_ELS, - "%d:0106 ELS cmd tag x%x completes Data: x%x x%x\n", + "%d:0106 ELS cmd tag x%x completes Data: x%x x%x x%x\n", phba->brd_no, - irsp->ulpIoTag, irsp->ulpStatus, irsp->un.ulpWord[4]); + irsp->ulpIoTag, irsp->ulpStatus, + irsp->un.ulpWord[4], irsp->ulpTimeout); /* Check to see if link went down during discovery */ lpfc_els_chk_latt(phba); @@ -1310,16 +1327,17 @@ lpfc_issue_els_scr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry) psli = &phba->sli; pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ cmdsize = (sizeof (uint32_t) + sizeof (SCR)); - if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL)) == 0) { - return (1); - } + ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); + if (!ndlp) + return 1; lpfc_nlp_init(phba, ndlp, nportid); - if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, - ndlp, ELS_CMD_SCR)) == 0) { + elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, ndlp, + ndlp->nlp_DID, ELS_CMD_SCR); + if (!elsiocb) { mempool_free( ndlp, phba->nlp_mem_pool); - return (1); + return 1; } icmd = &elsiocb->iocb; @@ -1339,11 +1357,11 @@ lpfc_issue_els_scr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry) spin_unlock_irq(phba->host->host_lock); mempool_free( ndlp, phba->nlp_mem_pool); lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } spin_unlock_irq(phba->host->host_lock); mempool_free( ndlp, phba->nlp_mem_pool); - return (0); + return 0; } static int @@ -1363,15 +1381,16 @@ lpfc_issue_els_farpr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry) psli = &phba->sli; pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ cmdsize = (sizeof (uint32_t) + sizeof (FARP)); - if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL)) == 0) { - return (1); - } + ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); + if (!ndlp) + return 1; lpfc_nlp_init(phba, ndlp, nportid); - if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, - ndlp, ELS_CMD_RNID)) == 0) { + elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, ndlp, + ndlp->nlp_DID, ELS_CMD_RNID); + if (!elsiocb) { mempool_free( ndlp, phba->nlp_mem_pool); - return (1); + return 1; } icmd = &elsiocb->iocb; @@ -1405,11 +1424,52 @@ lpfc_issue_els_farpr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry) spin_unlock_irq(phba->host->host_lock); mempool_free( ndlp, phba->nlp_mem_pool); lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } spin_unlock_irq(phba->host->host_lock); mempool_free( ndlp, phba->nlp_mem_pool); - return (0); + return 0; +} + +void +lpfc_cancel_retry_delay_tmo(struct lpfc_hba *phba, struct lpfc_nodelist * nlp) +{ + nlp->nlp_flag &= ~NLP_DELAY_TMO; + del_timer_sync(&nlp->nlp_delayfunc); + nlp->nlp_last_elscmd = 0; + + if (!list_empty(&nlp->els_retry_evt.evt_listp)) + list_del_init(&nlp->els_retry_evt.evt_listp); + + if (nlp->nlp_flag & NLP_NPR_2B_DISC) { + nlp->nlp_flag &= ~NLP_NPR_2B_DISC; + if (phba->num_disc_nodes) { + /* Check to see if there are more + * PLOGIs to be sent + */ + lpfc_more_plogi(phba); + + if (phba->num_disc_nodes == 0) { + phba->fc_flag &= ~FC_NDISC_ACTIVE; + lpfc_can_disctmo(phba); + if (phba->fc_flag & FC_RSCN_MODE) { + /* + * Check to see if more RSCNs + * came in while we were + * processing this one. + */ + if((phba->fc_rscn_id_cnt==0) && + !(phba->fc_flag & FC_RSCN_DISCOVERY)) { + phba->fc_flag &= ~FC_RSCN_MODE; + } + else { + lpfc_els_handle_rscn(phba); + } + } + } + } + } + return; } void @@ -1450,8 +1510,9 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp) phba = ndlp->nlp_phba; spin_lock_irq(phba->host->host_lock); - did = (uint32_t) (ndlp->nlp_DID); - cmd = (uint32_t) (ndlp->nlp_last_elscmd); + did = ndlp->nlp_DID; + cmd = ndlp->nlp_last_elscmd; + ndlp->nlp_last_elscmd = 0; if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) { spin_unlock_irq(phba->host->host_lock); @@ -1460,6 +1521,12 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp) ndlp->nlp_flag &= ~NLP_DELAY_TMO; spin_unlock_irq(phba->host->host_lock); + /* + * If a discovery event readded nlp_delayfunc after timer + * firing and before processing the timer, cancel the + * nlp_delayfunc. + */ + del_timer_sync(&ndlp->nlp_delayfunc); retry = ndlp->nlp_retry; switch (cmd) { @@ -1467,24 +1534,32 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp) lpfc_issue_els_flogi(phba, ndlp, retry); break; case ELS_CMD_PLOGI: - ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; - lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); - lpfc_issue_els_plogi(phba, ndlp, retry); + if(!lpfc_issue_els_plogi(phba, ndlp->nlp_DID, retry)) { + ndlp->nlp_prev_state = ndlp->nlp_state; + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + } break; case ELS_CMD_ADISC: - ndlp->nlp_state = NLP_STE_ADISC_ISSUE; - lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); - lpfc_issue_els_adisc(phba, ndlp, retry); + if (!lpfc_issue_els_adisc(phba, ndlp, retry)) { + ndlp->nlp_prev_state = ndlp->nlp_state; + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + } break; case ELS_CMD_PRLI: - ndlp->nlp_state = NLP_STE_PRLI_ISSUE; - lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); - lpfc_issue_els_prli(phba, ndlp, retry); + if (!lpfc_issue_els_prli(phba, ndlp, retry)) { + ndlp->nlp_prev_state = ndlp->nlp_state; + ndlp->nlp_state = NLP_STE_PRLI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); + } break; case ELS_CMD_LOGO: - ndlp->nlp_state = NLP_STE_NPR_NODE; - lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); - lpfc_issue_els_logo(phba, ndlp, retry); + if (!lpfc_issue_els_logo(phba, ndlp, retry)) { + ndlp->nlp_prev_state = ndlp->nlp_state; + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + } break; } return; @@ -1502,6 +1577,7 @@ lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, int retry, maxretry; int delay; uint32_t cmd; + uint32_t did; retry = 0; delay = 0; @@ -1510,6 +1586,7 @@ lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, ndlp = (struct lpfc_nodelist *) cmdiocb->context1; pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; cmd = 0; + /* Note: context2 may be 0 for internal driver abort * of delays ELS command. */ @@ -1519,6 +1596,16 @@ lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, cmd = *elscmd++; } + if(ndlp) + did = ndlp->nlp_DID; + else { + /* We should only hit this case for retrying PLOGI */ + did = irsp->un.elsreq64.remoteID; + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did); + if (!ndlp && (cmd != ELS_CMD_PLOGI)) + return 1; + } + switch (irsp->ulpStatus) { case IOSTAT_FCP_RSP_ERROR: case IOSTAT_REMOTE_STOP: @@ -1537,11 +1624,6 @@ lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, case IOERR_SEQUENCE_TIMEOUT: retry = 1; - if ((cmd == ELS_CMD_FLOGI) - && (phba->fc_topology != TOPOLOGY_LOOP)) { - delay = 1; - maxretry = 48; - } break; case IOERR_NO_RESOURCES: @@ -1612,9 +1694,8 @@ lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, break; } - if (ndlp->nlp_DID == FDMI_DID) { + if (did == FDMI_DID) retry = 1; - } if ((++cmdiocb->retry) >= maxretry) { phba->fc_stat.elsRetryExceeded++; @@ -1628,7 +1709,7 @@ lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, "%d:0107 Retry ELS command x%x to remote " "NPORT x%x Data: x%x x%x\n", phba->brd_no, - cmd, ndlp->nlp_DID, cmdiocb->retry, delay); + cmd, did, cmdiocb->retry, delay); if ((cmd == ELS_CMD_PLOGI) || (cmd == ELS_CMD_ADISC)) { /* If discovery / RSCN timer is running, reset it */ @@ -1639,54 +1720,61 @@ lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, } phba->fc_stat.elsXmitRetry++; - if (delay) { + if (ndlp && delay) { phba->fc_stat.elsDelayRetry++; ndlp->nlp_retry = cmdiocb->retry; mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); ndlp->nlp_flag |= NLP_DELAY_TMO; + ndlp->nlp_prev_state = ndlp->nlp_state; ndlp->nlp_state = NLP_STE_NPR_NODE; lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); ndlp->nlp_last_elscmd = cmd; - return (1); + return 1; } switch (cmd) { case ELS_CMD_FLOGI: lpfc_issue_els_flogi(phba, ndlp, cmdiocb->retry); - return (1); + return 1; case ELS_CMD_PLOGI: - ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; - lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); - lpfc_issue_els_plogi(phba, ndlp, cmdiocb->retry); - return (1); + if (ndlp) { + ndlp->nlp_prev_state = ndlp->nlp_state; + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + } + lpfc_issue_els_plogi(phba, did, cmdiocb->retry); + return 1; case ELS_CMD_ADISC: + ndlp->nlp_prev_state = ndlp->nlp_state; ndlp->nlp_state = NLP_STE_ADISC_ISSUE; lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); lpfc_issue_els_adisc(phba, ndlp, cmdiocb->retry); - return (1); + return 1; case ELS_CMD_PRLI: + ndlp->nlp_prev_state = ndlp->nlp_state; ndlp->nlp_state = NLP_STE_PRLI_ISSUE; lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); lpfc_issue_els_prli(phba, ndlp, cmdiocb->retry); - return (1); + return 1; case ELS_CMD_LOGO: + ndlp->nlp_prev_state = ndlp->nlp_state; ndlp->nlp_state = NLP_STE_NPR_NODE; lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); lpfc_issue_els_logo(phba, ndlp, cmdiocb->retry); - return (1); + return 1; } } /* No retry ELS command to remote NPORT */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, "%d:0108 No retry ELS command x%x to remote NPORT x%x " - "Data: x%x x%x\n", + "Data: x%x\n", phba->brd_no, - cmd, ndlp->nlp_DID, cmdiocb->retry, ndlp->nlp_flag); + cmd, did, cmdiocb->retry); - return (0); + return 0; } int @@ -1715,7 +1803,7 @@ lpfc_els_free_iocb(struct lpfc_hba * phba, struct lpfc_iocbq * elsiocb) kfree(buf_ptr); } spin_lock_irq(phba->host->host_lock); - list_add_tail(&elsiocb->list, &phba->lpfc_iocb_list); + lpfc_sli_release_iocbq(phba, elsiocb); spin_unlock_irq(phba->host->host_lock); return 0; } @@ -1735,10 +1823,6 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); - spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_LOGO_ACC; - spin_unlock_irq(phba->host->host_lock); - switch (ndlp->nlp_state) { case NLP_STE_UNUSED_NODE: /* node is just allocated */ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); @@ -1776,21 +1860,20 @@ lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, /* ELS response tag completes */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, "%d:0110 ELS response tag x%x completes " - "Data: x%x x%x x%x x%x x%x x%x\n", + "Data: x%x x%x x%x x%x x%x x%x x%x\n", phba->brd_no, cmdiocb->iocb.ulpIoTag, rspiocb->iocb.ulpStatus, - rspiocb->iocb.un.ulpWord[4], ndlp->nlp_DID, - ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + rspiocb->iocb.un.ulpWord[4], rspiocb->iocb.ulpTimeout, + ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); if (mbox) { if ((rspiocb->iocb.ulpStatus == 0) && (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) { - /* set_slim mailbox command needs to execute first, - * queue this command to be processed later. - */ lpfc_unreg_rpi(phba, ndlp); mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; mbox->context2 = ndlp; + ndlp->nlp_prev_state = ndlp->nlp_state; ndlp->nlp_state = NLP_STE_REG_LOGIN_ISSUE; lpfc_nlp_list(phba, ndlp, NLP_REGLOGIN_LIST); if (lpfc_sli_issue_mbox(phba, mbox, @@ -1805,6 +1888,7 @@ lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, mempool_free( mbox, phba->mbox_mem_pool); if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) { lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + ndlp = NULL; } } } @@ -1831,6 +1915,7 @@ lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, uint8_t *pcmd; uint16_t cmdsize; int rc; + ELS_PKT *els_pkt_ptr; psli = &phba->sli; pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ @@ -1839,10 +1924,11 @@ lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, switch (flag) { case ELS_CMD_ACC: cmdsize = sizeof (uint32_t); - if ((elsiocb = - lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, - ndlp, ELS_CMD_ACC)) == 0) { - return (1); + elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ndlp->nlp_DID, ELS_CMD_ACC); + if (!elsiocb) { + ndlp->nlp_flag &= ~NLP_LOGO_ACC; + return 1; } icmd = &elsiocb->iocb; icmd->ulpContext = oldcmd->ulpContext; /* Xri */ @@ -1852,11 +1938,11 @@ lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, break; case ELS_CMD_PLOGI: cmdsize = (sizeof (struct serv_parm) + sizeof (uint32_t)); - if ((elsiocb = - lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, - ndlp, ELS_CMD_ACC)) == 0) { - return (1); - } + elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ndlp->nlp_DID, ELS_CMD_ACC); + if (!elsiocb) + return 1; + icmd = &elsiocb->iocb; icmd->ulpContext = oldcmd->ulpContext; /* Xri */ pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); @@ -1868,8 +1954,25 @@ lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, pcmd += sizeof (uint32_t); memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); break; + case ELS_CMD_PRLO: + cmdsize = sizeof (uint32_t) + sizeof (PRLO); + elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ndlp->nlp_DID, ELS_CMD_PRLO); + if (!elsiocb) + return 1; + + icmd = &elsiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + memcpy(pcmd, ((struct lpfc_dmabuf *) oldiocb->context2)->virt, + sizeof (uint32_t) + sizeof (PRLO)); + *((uint32_t *) (pcmd)) = ELS_CMD_PRLO_ACC; + els_pkt_ptr = (ELS_PKT *) pcmd; + els_pkt_ptr->un.prlo.acceptRspCode = PRLO_REQ_EXECUTED; + break; default: - return (1); + return 1; } if (newnode) @@ -1885,6 +1988,9 @@ lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); if (ndlp->nlp_flag & NLP_LOGO_ACC) { + spin_lock_irq(phba->host->host_lock); + ndlp->nlp_flag &= ~NLP_LOGO_ACC; + spin_unlock_irq(phba->host->host_lock); elsiocb->iocb_cmpl = lpfc_cmpl_els_logo_acc; } else { elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; @@ -1896,9 +2002,9 @@ lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, spin_unlock_irq(phba->host->host_lock); if (rc == IOCB_ERROR) { lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } - return (0); + return 0; } int @@ -1918,10 +2024,10 @@ lpfc_els_rsp_reject(struct lpfc_hba * phba, uint32_t rejectError, pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ cmdsize = 2 * sizeof (uint32_t); - if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, - ndlp, ELS_CMD_LS_RJT)) == 0) { - return (1); - } + elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ndlp->nlp_DID, ELS_CMD_LS_RJT); + if (!elsiocb) + return 1; icmd = &elsiocb->iocb; oldcmd = &oldiocb->iocb; @@ -1948,9 +2054,9 @@ lpfc_els_rsp_reject(struct lpfc_hba * phba, uint32_t rejectError, spin_unlock_irq(phba->host->host_lock); if (rc == IOCB_ERROR) { lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } - return (0); + return 0; } int @@ -1971,10 +2077,10 @@ lpfc_els_rsp_adisc_acc(struct lpfc_hba * phba, pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ cmdsize = sizeof (uint32_t) + sizeof (ADISC); - if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, - ndlp, ELS_CMD_ACC)) == 0) { - return (1); - } + elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ndlp->nlp_DID, ELS_CMD_ACC); + if (!elsiocb) + return 1; /* Xmit ADISC ACC response tag */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, @@ -2006,9 +2112,9 @@ lpfc_els_rsp_adisc_acc(struct lpfc_hba * phba, spin_unlock_irq(phba->host->host_lock); if (rc == IOCB_ERROR) { lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } - return (0); + return 0; } int @@ -2030,13 +2136,10 @@ lpfc_els_rsp_prli_acc(struct lpfc_hba * phba, pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ cmdsize = sizeof (uint32_t) + sizeof (PRLI); - if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, - ndlp, - (ELS_CMD_ACC | - (ELS_CMD_PRLI & ~ELS_RSP_MASK)))) == - 0) { - return (1); - } + elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, ndlp, + ndlp->nlp_DID, (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK))); + if (!elsiocb) + return 1; /* Xmit PRLI ACC response tag */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, @@ -2086,9 +2189,9 @@ lpfc_els_rsp_prli_acc(struct lpfc_hba * phba, spin_unlock_irq(phba->host->host_lock); if (rc == IOCB_ERROR) { lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } - return (0); + return 0; } static int @@ -2114,10 +2217,10 @@ lpfc_els_rsp_rnid_acc(struct lpfc_hba * phba, if (format) cmdsize += sizeof (RNID_TOP_DISC); - if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, - ndlp, ELS_CMD_ACC)) == 0) { - return (1); - } + elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ndlp->nlp_DID, ELS_CMD_ACC); + if (!elsiocb) + return 1; /* Xmit RNID ACC response tag */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, @@ -2169,9 +2272,9 @@ lpfc_els_rsp_rnid_acc(struct lpfc_hba * phba, spin_unlock_irq(phba->host->host_lock); if (rc == IOCB_ERROR) { lpfc_els_free_iocb(phba, elsiocb); - return (1); + return 1; } - return (0); + return 0; } int @@ -2187,6 +2290,7 @@ lpfc_els_disc_adisc(struct lpfc_hba * phba) if (ndlp->nlp_flag & NLP_NPR_2B_DISC) { if (ndlp->nlp_flag & NLP_NPR_ADISC) { ndlp->nlp_flag &= ~NLP_NPR_ADISC; + ndlp->nlp_prev_state = ndlp->nlp_state; ndlp->nlp_state = NLP_STE_ADISC_ISSUE; lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); @@ -2208,7 +2312,7 @@ lpfc_els_disc_adisc(struct lpfc_hba * phba) phba->fc_flag &= ~FC_NLP_MORE; spin_unlock_irq(phba->host->host_lock); } - return(sentadisc); + return sentadisc; } int @@ -2224,9 +2328,10 @@ lpfc_els_disc_plogi(struct lpfc_hba * phba) if ((ndlp->nlp_flag & NLP_NPR_2B_DISC) && (!(ndlp->nlp_flag & NLP_DELAY_TMO))) { if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) { + ndlp->nlp_prev_state = ndlp->nlp_state; ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); - lpfc_issue_els_plogi(phba, ndlp, 0); + lpfc_issue_els_plogi(phba, ndlp->nlp_DID, 0); sentplogi++; phba->num_disc_nodes++; if (phba->num_disc_nodes >= @@ -2244,7 +2349,7 @@ lpfc_els_disc_plogi(struct lpfc_hba * phba) phba->fc_flag &= ~FC_NLP_MORE; spin_unlock_irq(phba->host->host_lock); } - return(sentplogi); + return sentplogi; } int @@ -2264,7 +2369,7 @@ lpfc_els_flush_rscn(struct lpfc_hba * phba) phba->fc_flag &= ~(FC_RSCN_MODE | FC_RSCN_DISCOVERY); spin_unlock_irq(phba->host->host_lock); lpfc_can_disctmo(phba); - return (0); + return 0; } int @@ -2285,7 +2390,7 @@ lpfc_rscn_payload_check(struct lpfc_hba * phba, uint32_t did) /* If we are doing a FULL RSCN rediscovery, match everything */ if (phba->fc_flag & FC_RSCN_DISCOVERY) { - return (did); + return did; } for (i = 0; i < phba->fc_rscn_id_cnt; i++) { @@ -2333,7 +2438,7 @@ lpfc_rscn_payload_check(struct lpfc_hba * phba, uint32_t did) } } } - return (match); + return match; } static int @@ -2365,17 +2470,15 @@ lpfc_rscn_recovery_check(struct lpfc_hba * phba) lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RECOVERY); - if (ndlp->nlp_flag & NLP_DELAY_TMO) { - ndlp->nlp_flag &= ~NLP_DELAY_TMO; - del_timer_sync(&ndlp->nlp_delayfunc); - if (!list_empty(&ndlp-> - els_retry_evt.evt_listp)) - list_del_init(&ndlp-> - els_retry_evt.evt_listp); - } + + /* Make sure NLP_DELAY_TMO is NOT running + * after a device recovery event. + */ + if (ndlp->nlp_flag & NLP_DELAY_TMO) + lpfc_cancel_retry_delay_tmo(phba, ndlp); } } - return (0); + return 0; } static int @@ -2408,10 +2511,10 @@ lpfc_els_rcv_rscn(struct lpfc_hba * phba, /* If we are about to begin discovery, just ACC the RSCN. * Discovery processing will satisfy it. */ - if (phba->hba_state < LPFC_NS_QRY) { + if (phba->hba_state <= LPFC_NS_QRY) { lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, newnode); - return (0); + return 0; } /* If we are already processing an RSCN, save the received @@ -2453,7 +2556,7 @@ lpfc_els_rcv_rscn(struct lpfc_hba * phba, /* send RECOVERY event for ALL nodes that match RSCN payload */ lpfc_rscn_recovery_check(phba); - return (0); + return 0; } phba->fc_flag |= FC_RSCN_MODE; @@ -2472,7 +2575,7 @@ lpfc_els_rcv_rscn(struct lpfc_hba * phba, /* send RECOVERY event for ALL nodes that match RSCN payload */ lpfc_rscn_recovery_check(phba); - return (lpfc_els_handle_rscn(phba)); + return lpfc_els_handle_rscn(phba); } int @@ -2494,40 +2597,41 @@ lpfc_els_handle_rscn(struct lpfc_hba * phba) /* To process RSCN, first compare RSCN data with NameServer */ phba->fc_ns_retry = 0; - if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, - NameServer_DID))) { + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, NameServer_DID); + if (ndlp) { /* Good ndlp, issue CT Request to NameServer */ if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == 0) { /* Wait for NameServer query cmpl before we can continue */ - return (1); + return 1; } } else { /* If login to NameServer does not exist, issue one */ /* Good status, issue PLOGI to NameServer */ - if ((ndlp = - lpfc_findnode_did(phba, NLP_SEARCH_ALL, NameServer_DID))) { + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, NameServer_DID); + if (ndlp) { /* Wait for NameServer login cmpl before we can continue */ - return (1); + return 1; } - if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL)) - == 0) { + ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); + if (!ndlp) { lpfc_els_flush_rscn(phba); - return (0); + return 0; } else { lpfc_nlp_init(phba, ndlp, NameServer_DID); ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_prev_state = ndlp->nlp_state; ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; - lpfc_issue_els_plogi(phba, ndlp, 0); + lpfc_issue_els_plogi(phba, NameServer_DID, 0); /* Wait for NameServer login cmpl before we can continue */ - return (1); + return 1; } } lpfc_els_flush_rscn(phba); - return (0); + return 0; } static int @@ -2561,7 +2665,7 @@ lpfc_els_rcv_flogi(struct lpfc_hba * phba, "%d:0113 An FLOGI ELS command x%x was received " "from DID x%x in Loop Mode\n", phba->brd_no, cmd, did); - return (1); + return 1; } did = Fabric_DID; @@ -2577,7 +2681,7 @@ lpfc_els_rcv_flogi(struct lpfc_hba * phba, if (!rc) { if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL)) == 0) { - return (1); + return 1; } lpfc_linkdown(phba); lpfc_init_link(phba, mbox, @@ -2590,9 +2694,8 @@ lpfc_els_rcv_flogi(struct lpfc_hba * phba, if (rc == MBX_NOT_FINISHED) { mempool_free( mbox, phba->mbox_mem_pool); } - return (1); - } - else if (rc > 0) { /* greater than */ + return 1; + } else if (rc > 0) { /* greater than */ spin_lock_irq(phba->host->host_lock); phba->fc_flag |= FC_PT2PT_PLOGI; spin_unlock_irq(phba->host->host_lock); @@ -2606,13 +2709,13 @@ lpfc_els_rcv_flogi(struct lpfc_hba * phba, stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; stat.un.b.vendorUnique = 0; lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); - return (1); + return 1; } /* Send back ACC */ lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, newnode); - return (0); + return 0; } static int @@ -2650,45 +2753,246 @@ lpfc_els_rcv_rnid(struct lpfc_hba * phba, stat.un.b.vendorUnique = 0; lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); } - return (0); + return 0; } static int -lpfc_els_rcv_rrq(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, +lpfc_els_rcv_lirr(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp) +{ + struct ls_rjt stat; + + /* For now, unconditionally reject this command */ + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + return 0; +} + +static void +lpfc_els_rsp_rps_acc(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + MAILBOX_t *mb; + IOCB_t *icmd; + RPS_RSP *rps_rsp; + uint8_t *pcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_nodelist *ndlp; + uint16_t xri, status; + uint32_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + xri = (uint16_t) ((unsigned long)(pmb->context1)); + pmb->context1 = 0; + pmb->context2 = 0; + + if (mb->mbxStatus) { + mempool_free( pmb, phba->mbox_mem_pool); + return; + } + + cmdsize = sizeof(RPS_RSP) + sizeof(uint32_t); + mempool_free( pmb, phba->mbox_mem_pool); + elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, lpfc_max_els_tries, ndlp, + ndlp->nlp_DID, ELS_CMD_ACC); + if (!elsiocb) + return; + + icmd = &elsiocb->iocb; + icmd->ulpContext = xri; + + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); /* Skip past command */ + rps_rsp = (RPS_RSP *)pcmd; + + if (phba->fc_topology != TOPOLOGY_LOOP) + status = 0x10; + else + status = 0x8; + if (phba->fc_flag & FC_FABRIC) + status |= 0x4; + + rps_rsp->rsvd1 = 0; + rps_rsp->portStatus = be16_to_cpu(status); + rps_rsp->linkFailureCnt = be32_to_cpu(mb->un.varRdLnk.linkFailureCnt); + rps_rsp->lossSyncCnt = be32_to_cpu(mb->un.varRdLnk.lossSyncCnt); + rps_rsp->lossSignalCnt = be32_to_cpu(mb->un.varRdLnk.lossSignalCnt); + rps_rsp->primSeqErrCnt = be32_to_cpu(mb->un.varRdLnk.primSeqErrCnt); + rps_rsp->invalidXmitWord = be32_to_cpu(mb->un.varRdLnk.invalidXmitWord); + rps_rsp->crcCnt = be32_to_cpu(mb->un.varRdLnk.crcCnt); + + /* Xmit ELS RPS ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0128 Xmit ELS RPS ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + phba->fc_stat.elsXmitACC++; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + } + return; +} + +static int +lpfc_els_rcv_rps(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp) { - struct lpfc_dmabuf *pcmd; uint32_t *lp; + uint8_t flag; + LPFC_MBOXQ_t *mbox; + struct lpfc_dmabuf *pcmd; + RPS *rps; + struct ls_rjt stat; + + if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) && + (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) { + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + } + + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + flag = (be32_to_cpu(*lp++) & 0xf); + rps = (RPS *) lp; + + if ((flag == 0) || + ((flag == 1) && (be32_to_cpu(rps->un.portNum) == 0)) || + ((flag == 2) && (memcmp(&rps->un.portName, &phba->fc_portname, + sizeof (struct lpfc_name)) == 0))) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_read_lnk_stat(phba, mbox); + mbox->context1 = + (void *)((unsigned long)cmdiocb->iocb.ulpContext); + mbox->context2 = ndlp; + mbox->mbox_cmpl = lpfc_els_rsp_rps_acc; + if (lpfc_sli_issue_mbox (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) != MBX_NOT_FINISHED) { + /* Mbox completion will send ELS Response */ + return 0; + } + mempool_free(mbox, phba->mbox_mem_pool); + } + } + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + return 0; +} + +static int +lpfc_els_rsp_rpl_acc(struct lpfc_hba * phba, uint16_t cmdsize, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ IOCB_t *icmd; + IOCB_t *oldcmd; + RPL_RSP rpl_rsp; + struct lpfc_iocbq *elsiocb; struct lpfc_sli_ring *pring; struct lpfc_sli *psli; - RRQ *rrq; - uint32_t cmd, did; + uint8_t *pcmd; psli = &phba->sli; - pring = &psli->ring[LPFC_FCP_RING]; - icmd = &cmdiocb->iocb; - did = icmd->un.elsreq64.remoteID; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ndlp->nlp_DID, ELS_CMD_ACC); + if (!elsiocb) + return 1; + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + + pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint16_t); + *((uint16_t *)(pcmd)) = be16_to_cpu(cmdsize); + pcmd += sizeof(uint16_t); + + /* Setup the RPL ACC payload */ + rpl_rsp.listLen = be32_to_cpu(1); + rpl_rsp.index = 0; + rpl_rsp.port_num_blk.portNum = 0; + rpl_rsp.port_num_blk.portID = be32_to_cpu(phba->fc_myDID); + memcpy(&rpl_rsp.port_num_blk.portName, &phba->fc_portname, + sizeof(struct lpfc_name)); + + memcpy(pcmd, &rpl_rsp, cmdsize - sizeof(uint32_t)); + + + /* Xmit ELS RPL ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0128 Xmit ELS RPL ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + + phba->fc_stat.elsXmitACC++; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return 1; + } + return 0; +} + +static int +lpfc_els_rcv_rpl(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + uint32_t maxsize; + uint16_t cmdsize; + RPL *rpl; + struct ls_rjt stat; + + if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) && + (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) { + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + } + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; lp = (uint32_t *) pcmd->virt; + rpl = (RPL *) (lp + 1); - cmd = *lp++; - rrq = (RRQ *) lp; + maxsize = be32_to_cpu(rpl->maxsize); - /* RRQ received */ - /* Get oxid / rxid from payload and abort it */ - spin_lock_irq(phba->host->host_lock); - if ((rrq->SID == be32_to_cpu(phba->fc_myDID))) { - lpfc_sli_abort_iocb(phba, pring, 0, 0, rrq->Oxid, - LPFC_CTX_CTX); + /* We support only one port */ + if ((rpl->index == 0) && + ((maxsize == 0) || + ((maxsize * sizeof(uint32_t)) >= sizeof(RPL_RSP)))) { + cmdsize = sizeof(uint32_t) + sizeof(RPL_RSP); } else { - lpfc_sli_abort_iocb(phba, pring, 0, 0, rrq->Rxid, - LPFC_CTX_CTX); + cmdsize = sizeof(uint32_t) + maxsize * sizeof(uint32_t); } - - spin_unlock_irq(phba->host->host_lock); - /* ACCEPT the rrq request */ - lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + lpfc_els_rsp_rpl_acc(phba, cmdsize, cmdiocb, ndlp); return 0; } @@ -2720,7 +3024,7 @@ lpfc_els_rcv_farp(struct lpfc_hba * phba, /* We will only support match on WWPN or WWNN */ if (fp->Mflags & ~(FARP_MATCH_NODE | FARP_MATCH_PORT)) { - return (0); + return 0; } cnt = 0; @@ -2743,9 +3047,10 @@ lpfc_els_rcv_farp(struct lpfc_hba * phba, (ndlp->nlp_state == NLP_STE_MAPPED_NODE)) { /* Log back into the node before sending the FARP. */ if (fp->Rflags & FARP_REQUEST_PLOGI) { + ndlp->nlp_prev_state = ndlp->nlp_state; ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); - lpfc_issue_els_plogi(phba, ndlp, 0); + lpfc_issue_els_plogi(phba, ndlp->nlp_DID, 0); } /* Send a FARP response to that node */ @@ -2754,7 +3059,7 @@ lpfc_els_rcv_farp(struct lpfc_hba * phba, } } } - return (0); + return 0; } static int @@ -2787,47 +3092,89 @@ lpfc_els_rcv_farpr(struct lpfc_hba * phba, static int lpfc_els_rcv_fan(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, - struct lpfc_nodelist * ndlp) + struct lpfc_nodelist * fan_ndlp) { struct lpfc_dmabuf *pcmd; uint32_t *lp; IOCB_t *icmd; - FAN *fp; uint32_t cmd, did; + FAN *fp; + struct lpfc_nodelist *ndlp, *next_ndlp; + + /* FAN received */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, "%d:265 FAN received\n", + phba->brd_no); icmd = &cmdiocb->iocb; did = icmd->un.elsreq64.remoteID; - pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; - lp = (uint32_t *) pcmd->virt; + pcmd = (struct lpfc_dmabuf *)cmdiocb->context2; + lp = (uint32_t *)pcmd->virt; cmd = *lp++; - fp = (FAN *) lp; + fp = (FAN *)lp; - /* FAN received */ - - /* ACCEPT the FAN request */ - lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + /* FAN received; Fan does not have a reply sequence */ if (phba->hba_state == LPFC_LOCAL_CFG_LINK) { - /* The discovery state machine needs to take a different - * action if this node has switched fabrics - */ - if ((memcmp(&fp->FportName, &phba->fc_fabparam.portName, - sizeof (struct lpfc_name)) != 0) - || - (memcmp(&fp->FnodeName, &phba->fc_fabparam.nodeName, - sizeof (struct lpfc_name)) != 0)) { - /* This node has switched fabrics. An FLOGI is required - * after the timeout + if ((memcmp(&phba->fc_fabparam.nodeName, &fp->FnodeName, + sizeof(struct lpfc_name)) != 0) || + (memcmp(&phba->fc_fabparam.portName, &fp->FportName, + sizeof(struct lpfc_name)) != 0)) { + /* + * This node has switched fabrics. FLOGI is required + * Clean up the old rpi's */ - return (0); + + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_npr_list, nlp_listp) { + + if (ndlp->nlp_type & NLP_FABRIC) { + /* + * Clean up old Fabric, Nameserver and + * other NLP_FABRIC logins + */ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) { + /* Fail outstanding I/O now since this + * device is marked for PLOGI + */ + lpfc_unreg_rpi(phba, ndlp); + } + } + + phba->hba_state = LPFC_FLOGI; + lpfc_set_disctmo(phba); + lpfc_initial_flogi(phba); + return 0; } + /* Discovery not needed, + * move the nodes to their original state. + */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { - /* Start discovery */ + switch (ndlp->nlp_prev_state) { + case NLP_STE_UNMAPPED_NODE: + ndlp->nlp_prev_state = NLP_STE_NPR_NODE; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + break; + + case NLP_STE_MAPPED_NODE: + ndlp->nlp_prev_state = NLP_STE_NPR_NODE; + ndlp->nlp_state = NLP_STE_MAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST); + break; + + default: + break; + } + } + + /* Start discovery - this should just do CLEAR_LA */ lpfc_disc_start(phba); } - - return (0); + return 0; } void @@ -2904,8 +3251,9 @@ lpfc_els_timeout_handler(struct lpfc_hba *phba) if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { struct lpfc_nodelist *ndlp; - + spin_unlock_irq(phba->host->host_lock); ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); + spin_lock_irq(phba->host->host_lock); remote_ID = ndlp->nlp_DID; if (cmd->un.elsreq64.bdl.ulpIoTag32) { lpfc_sli_issue_abort_iotag32(phba, @@ -2931,9 +3279,8 @@ lpfc_els_timeout_handler(struct lpfc_hba *phba) spin_unlock_irq(phba->host->host_lock); (piocb->iocb_cmpl) (phba, piocb, piocb); spin_lock_irq(phba->host->host_lock); - } else { - list_add_tail(&piocb->list, &phba->lpfc_iocb_list); - } + } else + lpfc_sli_release_iocbq(phba, piocb); } if (phba->sli.ring[LPFC_ELS_RING].txcmplq_cnt) { phba->els_tmofunc.expires = jiffies + HZ * timeout; @@ -2951,7 +3298,6 @@ lpfc_els_flush_cmd(struct lpfc_hba * phba) struct lpfc_dmabuf *pcmd; uint32_t *elscmd; uint32_t els_command; - uint32_t remote_ID; pring = &phba->sli.ring[LPFC_ELS_RING]; spin_lock_irq(phba->host->host_lock); @@ -2974,18 +3320,6 @@ lpfc_els_flush_cmd(struct lpfc_hba * phba) elscmd = (uint32_t *) (pcmd->virt); els_command = *elscmd; - if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { - struct lpfc_nodelist *ndlp; - - ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); - remote_ID = ndlp->nlp_DID; - if (phba->hba_state == LPFC_HBA_READY) { - continue; - } - } else { - remote_ID = cmd->un.elsreq64.remoteID; - } - list_del(&piocb->list); pring->txcmplq_cnt--; @@ -2996,9 +3330,8 @@ lpfc_els_flush_cmd(struct lpfc_hba * phba) spin_unlock_irq(phba->host->host_lock); (piocb->iocb_cmpl) (phba, piocb, piocb); spin_lock_irq(phba->host->host_lock); - } - else - list_add_tail(&piocb->list, &phba->lpfc_iocb_list); + } else + lpfc_sli_release_iocbq(phba, piocb); } list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { @@ -3011,18 +3344,6 @@ lpfc_els_flush_cmd(struct lpfc_hba * phba) elscmd = (uint32_t *) (pcmd->virt); els_command = *elscmd; - if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { - struct lpfc_nodelist *ndlp; - - ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); - remote_ID = ndlp->nlp_DID; - if (phba->hba_state == LPFC_HBA_READY) { - continue; - } - } else { - remote_ID = cmd->un.elsreq64.remoteID; - } - list_del(&piocb->list); pring->txcmplq_cnt--; @@ -3033,9 +3354,8 @@ lpfc_els_flush_cmd(struct lpfc_hba * phba) spin_unlock_irq(phba->host->host_lock); (piocb->iocb_cmpl) (phba, piocb, piocb); spin_lock_irq(phba->host->host_lock); - } - else - list_add_tail(&piocb->list, &phba->lpfc_iocb_list); + } else + lpfc_sli_release_iocbq(phba, piocb); } spin_unlock_irq(phba->host->host_lock); return; @@ -3106,10 +3426,11 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, } did = icmd->un.rcvels.remoteID; - if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) { + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did); + if (!ndlp) { /* Cannot find existing Fabric ndlp, so allocate a new one */ - if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL)) - == 0) { + ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); + if (!ndlp) { lpfc_mbuf_free(phba, mp->virt, mp->phys); kfree(mp); drop_cmd = 1; @@ -3139,7 +3460,7 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, case ELS_CMD_PLOGI: phba->fc_stat.elsRcvPLOGI++; if (phba->hba_state < LPFC_DISC_AUTH) { - rjt_err = LSEXP_NOTHING_MORE; + rjt_err = 1; break; } lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PLOGI); @@ -3154,7 +3475,7 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, case ELS_CMD_LOGO: phba->fc_stat.elsRcvLOGO++; if (phba->hba_state < LPFC_DISC_AUTH) { - rjt_err = LSEXP_NOTHING_MORE; + rjt_err = 1; break; } lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_LOGO); @@ -3162,7 +3483,7 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, case ELS_CMD_PRLO: phba->fc_stat.elsRcvPRLO++; if (phba->hba_state < LPFC_DISC_AUTH) { - rjt_err = LSEXP_NOTHING_MORE; + rjt_err = 1; break; } lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLO); @@ -3177,7 +3498,7 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, case ELS_CMD_ADISC: phba->fc_stat.elsRcvADISC++; if (phba->hba_state < LPFC_DISC_AUTH) { - rjt_err = LSEXP_NOTHING_MORE; + rjt_err = 1; break; } lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_ADISC); @@ -3185,7 +3506,7 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, case ELS_CMD_PDISC: phba->fc_stat.elsRcvPDISC++; if (phba->hba_state < LPFC_DISC_AUTH) { - rjt_err = LSEXP_NOTHING_MORE; + rjt_err = 1; break; } lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PDISC); @@ -3202,25 +3523,45 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, phba->fc_stat.elsRcvFAN++; lpfc_els_rcv_fan(phba, elsiocb, ndlp); break; - case ELS_CMD_RRQ: - phba->fc_stat.elsRcvRRQ++; - lpfc_els_rcv_rrq(phba, elsiocb, ndlp); - break; case ELS_CMD_PRLI: phba->fc_stat.elsRcvPRLI++; if (phba->hba_state < LPFC_DISC_AUTH) { - rjt_err = LSEXP_NOTHING_MORE; + rjt_err = 1; break; } lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLI); break; + case ELS_CMD_LIRR: + phba->fc_stat.elsRcvLIRR++; + lpfc_els_rcv_lirr(phba, elsiocb, ndlp); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + case ELS_CMD_RPS: + phba->fc_stat.elsRcvRPS++; + lpfc_els_rcv_rps(phba, elsiocb, ndlp); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + case ELS_CMD_RPL: + phba->fc_stat.elsRcvRPL++; + lpfc_els_rcv_rpl(phba, elsiocb, ndlp); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; case ELS_CMD_RNID: phba->fc_stat.elsRcvRNID++; lpfc_els_rcv_rnid(phba, elsiocb, ndlp); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } break; default: /* Unsupported ELS command, reject */ - rjt_err = LSEXP_NOTHING_MORE; + rjt_err = 1; /* Unknown ELS command received from NPORT */ lpfc_printf_log(phba, KERN_ERR, LOG_ELS, @@ -3236,7 +3577,7 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, if (rjt_err) { stat.un.b.lsRjtRsvd0 = 0; stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; - stat.un.b.lsRjtRsnCodeExp = rjt_err; + stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE; stat.un.b.vendorUnique = 0; lpfc_els_rsp_reject(phba, stat.un.lsRjtError, elsiocb, ndlp); } @@ -3250,8 +3591,9 @@ dropit: if (drop_cmd == 1) { lpfc_printf_log(phba, KERN_ERR, LOG_ELS, "%d:0111 Dropping received ELS cmd " - "Data: x%x x%x\n", phba->brd_no, - icmd->ulpStatus, icmd->un.ulpWord[4]); + "Data: x%x x%x x%x\n", phba->brd_no, + icmd->ulpStatus, icmd->un.ulpWord[4], + icmd->ulpTimeout); phba->fc_stat.elsRcvDrop++; } return;