md: raid1/raid10: handle allocation errors during array setup.
[safe/jmp/linux-2.6] / drivers / scsi / lpfc / lpfc_els.c
index 8c5c3ae..45337cd 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2008 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2009 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
 
+#include "lpfc_hw4.h"
 #include "lpfc_hw.h"
 #include "lpfc_sli.h"
+#include "lpfc_sli4.h"
 #include "lpfc_nl.h"
 #include "lpfc_disc.h"
 #include "lpfc_scsi.h"
@@ -144,7 +146,7 @@ lpfc_els_chk_latt(struct lpfc_vport *vport)
  *   Pointer to the newly allocated/prepared els iocb data structure
  *   NULL - when els iocb data structure allocation/preparation failed
  **/
-static struct lpfc_iocbq *
+struct lpfc_iocbq *
 lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
                   uint16_t cmdSize, uint8_t retry,
                   struct lpfc_nodelist *ndlp, uint32_t did,
@@ -166,6 +168,19 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
        if (elsiocb == NULL)
                return NULL;
 
+       /*
+        * If this command is for fabric controller and HBA running
+        * in FIP mode send FLOGI, FDISC and LOGO as FIP frames.
+        */
+       if ((did == Fabric_DID) &&
+               bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags) &&
+               ((elscmd == ELS_CMD_FLOGI) ||
+                (elscmd == ELS_CMD_FDISC) ||
+                (elscmd == ELS_CMD_LOGO)))
+               elsiocb->iocb_flag |= LPFC_FIP_ELS;
+       else
+               elsiocb->iocb_flag &= ~LPFC_FIP_ELS;
+
        icmd = &elsiocb->iocb;
 
        /* fill in BDEs for command */
@@ -220,7 +235,7 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
                icmd->un.elsreq64.myID = vport->fc_myDID;
 
                /* For ELS_REQUEST64_CR, use the VPI by default */
-               icmd->ulpContext = vport->vpi;
+               icmd->ulpContext = vport->vpi + phba->vpi_base;
                icmd->ulpCt_h = 0;
                /* The CT field must be 0=INVALID_RPI for the ECHO cmd */
                if (elscmd == ELS_CMD_ECHO)
@@ -386,6 +401,75 @@ fail:
 }
 
 /**
+ * lpfc_issue_reg_vfi - Register VFI for this vport's fabric login
+ * @vport: pointer to a host virtual N_Port data structure.
+ *
+ * This routine issues a REG_VFI mailbox for the vfi, vpi, fcfi triplet for
+ * the @vport. This mailbox command is necessary for FCoE only.
+ *
+ * Return code
+ *   0 - successfully issued REG_VFI for @vport
+ *   A failure code otherwise.
+ **/
+static int
+lpfc_issue_reg_vfi(struct lpfc_vport *vport)
+{
+       struct lpfc_hba  *phba = vport->phba;
+       LPFC_MBOXQ_t *mboxq;
+       struct lpfc_nodelist *ndlp;
+       struct serv_parm *sp;
+       struct lpfc_dmabuf *dmabuf;
+       int rc = 0;
+
+       sp = &phba->fc_fabparam;
+       ndlp = lpfc_findnode_did(vport, Fabric_DID);
+       if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
+               rc = -ENODEV;
+               goto fail;
+       }
+
+       dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+       if (!dmabuf) {
+               rc = -ENOMEM;
+               goto fail;
+       }
+       dmabuf->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &dmabuf->phys);
+       if (!dmabuf->virt) {
+               rc = -ENOMEM;
+               goto fail_free_dmabuf;
+       }
+       mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq) {
+               rc = -ENOMEM;
+               goto fail_free_coherent;
+       }
+       vport->port_state = LPFC_FABRIC_CFG_LINK;
+       memcpy(dmabuf->virt, &phba->fc_fabparam, sizeof(vport->fc_sparam));
+       lpfc_reg_vfi(mboxq, vport, dmabuf->phys);
+       mboxq->mbox_cmpl = lpfc_mbx_cmpl_reg_vfi;
+       mboxq->vport = vport;
+       mboxq->context1 = dmabuf;
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+       if (rc == MBX_NOT_FINISHED) {
+               rc = -ENXIO;
+               goto fail_free_mbox;
+       }
+       return 0;
+
+fail_free_mbox:
+       mempool_free(mboxq, phba->mbox_mem_pool);
+fail_free_coherent:
+       lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
+fail_free_dmabuf:
+       kfree(dmabuf);
+fail:
+       lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+       lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+               "0289 Issue Register VFI failed: Err %d\n", rc);
+       return rc;
+}
+
+/**
  * lpfc_cmpl_els_flogi_fabric - Completion function for flogi to a fabric port
  * @vport: pointer to a host virtual N_Port data structure.
  * @ndlp: pointer to a node-list data structure.
@@ -497,17 +581,24 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                }
        }
 
-       lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE);
-
-       if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED &&
-           vport->fc_flag & FC_VPORT_NEEDS_REG_VPI) {
-               lpfc_register_new_vport(phba, vport, ndlp);
-               return 0;
+       if (phba->sli_rev < LPFC_SLI_REV4) {
+               lpfc_nlp_set_state(vport, ndlp, NLP_STE_REG_LOGIN_ISSUE);
+               if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED &&
+                   vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)
+                       lpfc_register_new_vport(phba, vport, ndlp);
+               else
+                       lpfc_issue_fabric_reglogin(vport);
+       } else {
+               ndlp->nlp_type |= NLP_FABRIC;
+               lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+               if (vport->vfi_state & LPFC_VFI_REGISTERED) {
+                       lpfc_start_fdiscs(phba);
+                       lpfc_do_scr_ns_plogi(phba, vport);
+               } else
+                       lpfc_issue_reg_vfi(vport);
        }
-       lpfc_issue_fabric_reglogin(vport);
        return 0;
 }
-
 /**
  * lpfc_cmpl_els_flogi_nport - Completion function for flogi to an N_Port
  * @vport: pointer to a host virtual N_Port data structure.
@@ -815,9 +906,14 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        if (sp->cmn.fcphHigh < FC_PH3)
                sp->cmn.fcphHigh = FC_PH3;
 
-       if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
+       if  (phba->sli_rev == LPFC_SLI_REV4) {
+               elsiocb->iocb.ulpCt_h = ((SLI4_CT_FCFI >> 1) & 1);
+               elsiocb->iocb.ulpCt_l = (SLI4_CT_FCFI & 1);
+               /* FLOGI needs to be 3 for WQE FCFI */
+               /* Set the fcfi to the fcfi we registered with */
+               elsiocb->iocb.ulpContext = phba->fcf.fcfi;
+       } else if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
                sp->cmn.request_multiple_Nport = 1;
-
                /* For FLOGI, Let FLOGI rsp set the NPortID for VPI 0 */
                icmd->ulpCt_h = 1;
                icmd->ulpCt_l = 0;
@@ -930,6 +1026,8 @@ lpfc_initial_flogi(struct lpfc_vport *vport)
                if (!ndlp)
                        return 0;
                lpfc_nlp_init(vport, ndlp, Fabric_DID);
+               /* Set the node type */
+               ndlp->nlp_type |= NLP_FABRIC;
                /* Put ndlp onto node list */
                lpfc_enqueue_node(vport, ndlp);
        } else if (!NLP_CHK_NODE_ACT(ndlp)) {
@@ -1602,7 +1700,8 @@ lpfc_adisc_done(struct lpfc_vport *vport)
         * and continue discovery.
         */
        if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) &&
-           !(vport->fc_flag & FC_RSCN_MODE)) {
+           !(vport->fc_flag & FC_RSCN_MODE) &&
+           (phba->sli_rev < LPFC_SLI_REV4)) {
                lpfc_issue_reg_vpi(phba, vport);
                return;
        }
@@ -2935,6 +3034,14 @@ lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
        struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
 
+       /*
+        * This routine is used to register and unregister in previous SLI
+        * modes.
+        */
+       if ((pmb->u.mb.mbxCommand == MBX_UNREG_LOGIN) &&
+           (phba->sli_rev == LPFC_SLI_REV4))
+               lpfc_sli4_free_rpi(phba, pmb->u.mb.un.varUnregLogin.rpi);
+
        pmb->context1 = NULL;
        lpfc_mbuf_free(phba, mp->virt, mp->phys);
        kfree(mp);
@@ -3814,7 +3921,9 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did)
                        payload_len -= sizeof(uint32_t);
                        switch (rscn_did.un.b.resv & RSCN_ADDRESS_FORMAT_MASK) {
                        case RSCN_ADDRESS_FORMAT_PORT:
-                               if (ns_did.un.word == rscn_did.un.word)
+                               if ((ns_did.un.b.domain == rscn_did.un.b.domain)
+                                   && (ns_did.un.b.area == rscn_did.un.b.area)
+                                   && (ns_did.un.b.id == rscn_did.un.b.id))
                                        goto return_did_out;
                                break;
                        case RSCN_ADDRESS_FORMAT_AREA:
@@ -4275,7 +4384,7 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
                        lpfc_init_link(phba, mbox,
                                       phba->cfg_topology,
                                       phba->cfg_link_speed);
-                       mbox->mb.un.varInitLnk.lipsr_AL_PA = 0;
+                       mbox->u.mb.un.varInitLnk.lipsr_AL_PA = 0;
                        mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
                        mbox->vport = vport;
                        rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
@@ -4424,7 +4533,7 @@ lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        uint16_t xri, status;
        uint32_t cmdsize;
 
-       mb = &pmb->mb;
+       mb = &pmb->u.mb;
 
        ndlp = (struct lpfc_nodelist *) pmb->context2;
        xri = (uint16_t) ((unsigned long)(pmb->context1));
@@ -4855,7 +4964,10 @@ lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
                } else {
                        /* FAN verified - skip FLOGI */
                        vport->fc_myDID = vport->fc_prevDID;
-                       lpfc_issue_fabric_reglogin(vport);
+                       if (phba->sli_rev < LPFC_SLI_REV4)
+                               lpfc_issue_fabric_reglogin(vport);
+                       else
+                               lpfc_issue_reg_vfi(vport);
                }
        }
        return 0;
@@ -5538,11 +5650,10 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
 
 dropit:
        if (vport && !(vport->load_flag & FC_UNLOADING))
-               lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
-                       "(%d):0111 Dropping received ELS cmd "
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+                       "0111 Dropping received ELS cmd "
                        "Data: x%x x%x x%x\n",
-                       vport->vpi, icmd->ulpStatus,
-                       icmd->un.ulpWord[4], icmd->ulpTimeout);
+                       icmd->ulpStatus, icmd->un.ulpWord[4], icmd->ulpTimeout);
        phba->fc_stat.elsRcvDrop++;
 }
 
@@ -5618,10 +5729,9 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
             icmd->ulpCommand == CMD_IOCB_RCV_SEQ64_CX)) {
                if (icmd->unsli3.rcvsli3.vpi == 0xffff)
                        vport = phba->pport;
-               else {
-                       uint16_t vpi = icmd->unsli3.rcvsli3.vpi;
-                       vport = lpfc_find_vport_by_vpid(phba, vpi);
-               }
+               else
+                       vport = lpfc_find_vport_by_vpid(phba,
+                               icmd->unsli3.rcvsli3.vpi - phba->vpi_base);
        }
        /* If there are no BDEs associated
         * with this IOCB, there is nothing to do.
@@ -5753,7 +5863,7 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        struct lpfc_vport *vport = pmb->vport;
        struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
        struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *) pmb->context2;
-       MAILBOX_t *mb = &pmb->mb;
+       MAILBOX_t *mb = &pmb->u.mb;
 
        spin_lock_irq(shost->host_lock);
        vport->fc_flag &= ~FC_VPORT_NEEDS_REG_VPI;
@@ -5790,7 +5900,10 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 
        } else {
                if (vport == phba->pport)
-                       lpfc_issue_fabric_reglogin(vport);
+                       if (phba->sli_rev < LPFC_SLI_REV4)
+                               lpfc_issue_fabric_reglogin(vport);
+                       else
+                               lpfc_issue_reg_vfi(vport);
                else
                        lpfc_do_scr_ns_plogi(phba, vport);
        }
@@ -5822,7 +5935,7 @@ lpfc_register_new_vport(struct lpfc_hba *phba, struct lpfc_vport *vport,
 
        mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
        if (mbox) {
-               lpfc_reg_vpi(phba, vport->vpi, vport->fc_myDID, mbox);
+               lpfc_reg_vpi(vport, mbox);
                mbox->vport = vport;
                mbox->context2 = lpfc_nlp_get(ndlp);
                mbox->mbox_cmpl = lpfc_cmpl_reg_new_vport;
@@ -6008,9 +6121,17 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        icmd->un.elsreq64.myID = 0;
        icmd->un.elsreq64.fl = 1;
 
-       /* For FDISC, Let FDISC rsp set the NPortID for this VPI */
-       icmd->ulpCt_h = 1;
-       icmd->ulpCt_l = 0;
+       if  (phba->sli_rev == LPFC_SLI_REV4) {
+               /* FDISC needs to be 1 for WQE VPI */
+               elsiocb->iocb.ulpCt_h = (SLI4_CT_VPI >> 1) & 1;
+               elsiocb->iocb.ulpCt_l = SLI4_CT_VPI & 1 ;
+               /* Set the ulpContext to the vpi */
+               elsiocb->iocb.ulpContext = vport->vpi + phba->vpi_base;
+       } else {
+               /* For FDISC, Let FDISC rsp set the NPortID for this VPI */
+               icmd->ulpCt_h = 1;
+               icmd->ulpCt_l = 0;
+       }
 
        pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
        *((uint32_t *) (pcmd)) = ELS_CMD_FDISC;
@@ -6494,3 +6615,38 @@ void lpfc_fabric_abort_hba(struct lpfc_hba *phba)
        lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
                              IOERR_SLI_ABORTED);
 }
+
+/**
+ * lpfc_sli4_els_xri_aborted - Slow-path process of els xri abort
+ * @phba: pointer to lpfc hba data structure.
+ * @axri: pointer to the els xri abort wcqe structure.
+ *
+ * This routine is invoked by the worker thread to process a SLI4 slow-path
+ * ELS aborted xri.
+ **/
+void
+lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba,
+                         struct sli4_wcqe_xri_aborted *axri)
+{
+       uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri);
+       struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
+       unsigned long iflag = 0;
+
+       spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock, iflag);
+       list_for_each_entry_safe(sglq_entry, sglq_next,
+                       &phba->sli4_hba.lpfc_abts_els_sgl_list, list) {
+               if (sglq_entry->sli4_xritag == xri) {
+                       list_del(&sglq_entry->list);
+                       spin_unlock_irqrestore(
+                                       &phba->sli4_hba.abts_sgl_list_lock,
+                                        iflag);
+                       spin_lock_irqsave(&phba->hbalock, iflag);
+
+                       list_add_tail(&sglq_entry->list,
+                               &phba->sli4_hba.lpfc_sgl_list);
+                       spin_unlock_irqrestore(&phba->hbalock, iflag);
+                       return;
+               }
+       }
+       spin_unlock_irqrestore(&phba->sli4_hba.abts_sgl_list_lock, iflag);
+}