Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / scsi / lpfc / lpfc_els.c
index c62d567cc8457b6ba880247f081f3efcb61ec4c2..8e28edf9801eb85ee7fb7111f435fb4770542824 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-2009 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2011 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -484,6 +484,59 @@ fail:
        return rc;
 }
 
+/**
+ * lpfc_check_clean_addr_bit - Check whether assigned FCID is clean.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @sp: pointer to service parameter data structure.
+ *
+ * This routine is called from FLOGI/FDISC completion handler functions.
+ * lpfc_check_clean_addr_bit return 1 when FCID/Fabric portname/ Fabric
+ * node nodename is changed in the completion service parameter else return
+ * 0. This function also set flag in the vport data structure to delay
+ * NP_Port discovery after the FLOGI/FDISC completion if Clean address bit
+ * in FLOGI/FDISC response is cleared and FCID/Fabric portname/ Fabric
+ * node nodename is changed in the completion service parameter.
+ *
+ * Return code
+ *   0 - FCID and Fabric Nodename and Fabric portname is not changed.
+ *   1 - FCID or Fabric Nodename or Fabric portname is changed.
+ *
+ **/
+static uint8_t
+lpfc_check_clean_addr_bit(struct lpfc_vport *vport,
+               struct serv_parm *sp)
+{
+       uint8_t fabric_param_changed = 0;
+       struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+
+       if ((vport->fc_prevDID != vport->fc_myDID) ||
+               memcmp(&vport->fabric_portname, &sp->portName,
+                       sizeof(struct lpfc_name)) ||
+               memcmp(&vport->fabric_nodename, &sp->nodeName,
+                       sizeof(struct lpfc_name)))
+               fabric_param_changed = 1;
+
+       /*
+        * Word 1 Bit 31 in common service parameter is overloaded.
+        * Word 1 Bit 31 in FLOGI request is multiple NPort request
+        * Word 1 Bit 31 in FLOGI response is clean address bit
+        *
+        * If fabric parameter is changed and clean address bit is
+        * cleared delay nport discovery if
+        * - vport->fc_prevDID != 0 (not initial discovery) OR
+        * - lpfc_delay_discovery module parameter is set.
+        */
+       if (fabric_param_changed && !sp->cmn.clean_address_bit &&
+           (vport->fc_prevDID || lpfc_delay_discovery)) {
+               spin_lock_irq(shost->host_lock);
+               vport->fc_flag |= FC_DISC_DELAYED;
+               spin_unlock_irq(shost->host_lock);
+       }
+
+       return fabric_param_changed;
+}
+
+
 /**
  * lpfc_cmpl_els_flogi_fabric - Completion function for flogi to a fabric port
  * @vport: pointer to a host virtual N_Port data structure.
@@ -512,6 +565,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        struct lpfc_hba  *phba = vport->phba;
        struct lpfc_nodelist *np;
        struct lpfc_nodelist *next_np;
+       uint8_t fabric_param_changed;
 
        spin_lock_irq(shost->host_lock);
        vport->fc_flag |= FC_FABRIC;
@@ -544,6 +598,12 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                ndlp->nlp_class_sup |= FC_COS_CLASS4;
        ndlp->nlp_maxframe = ((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) |
                                sp->cmn.bbRcvSizeLsb;
+
+       fabric_param_changed = lpfc_check_clean_addr_bit(vport, sp);
+       memcpy(&vport->fabric_portname, &sp->portName,
+                       sizeof(struct lpfc_name));
+       memcpy(&vport->fabric_nodename, &sp->nodeName,
+                       sizeof(struct lpfc_name));
        memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));
 
        if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
@@ -565,7 +625,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                }
        }
 
-       if ((vport->fc_prevDID != vport->fc_myDID) &&
+       if (fabric_param_changed &&
                !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {
 
                /* If our NportID changed, we need to ensure all
@@ -2203,6 +2263,7 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
        struct Scsi_Host  *shost = lpfc_shost_from_vport(vport);
        IOCB_t *irsp;
        struct lpfc_sli *psli;
+       struct lpfcMboxq *mbox;
 
        psli = &phba->sli;
        /* we pass cmdiocb to state machine which needs rspiocb as well */
@@ -2260,6 +2321,21 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                                        NLP_EVT_CMPL_LOGO);
 out:
        lpfc_els_free_iocb(phba, cmdiocb);
+       /* If we are in pt2pt mode, we could rcv new S_ID on PLOGI */
+       if ((vport->fc_flag & FC_PT2PT) &&
+               !(vport->fc_flag & FC_PT2PT_PLOGI)) {
+               phba->pport->fc_myDID = 0;
+               mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+               if (mbox) {
+                       lpfc_config_link(phba, mbox);
+                       mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+                       mbox->vport = vport;
+                       if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) ==
+                               MBX_NOT_FINISHED) {
+                               mempool_free(mbox, phba->mbox_mem_pool);
+                       }
+               }
+       }
        return;
 }
 
@@ -2745,7 +2821,8 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
                }
                break;
        case ELS_CMD_FDISC:
-               lpfc_issue_els_fdisc(vport, ndlp, retry);
+               if (!(vport->fc_flag & FC_VPORT_NEEDS_INIT_VPI))
+                       lpfc_issue_els_fdisc(vport, ndlp, retry);
                break;
        }
        return;
@@ -2815,9 +2892,17 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 
        switch (irsp->ulpStatus) {
        case IOSTAT_FCP_RSP_ERROR:
+               break;
        case IOSTAT_REMOTE_STOP:
+               if (phba->sli_rev == LPFC_SLI_REV4) {
+                       /* This IO was aborted by the target, we don't
+                        * know the rxid and because we did not send the
+                        * ABTS we cannot generate and RRQ.
+                        */
+                       lpfc_set_rrq_active(phba, ndlp,
+                                        cmdiocb->sli4_xritag, 0, 0);
+               }
                break;
-
        case IOSTAT_LOCAL_REJECT:
                switch ((irsp->un.ulpWord[4] & 0xff)) {
                case IOERR_LOOP_OPEN_FAILURE:
@@ -4013,28 +4098,34 @@ lpfc_els_clear_rrq(struct lpfc_vport *vport,
        uint8_t *pcmd;
        struct RRQ *rrq;
        uint16_t rxid;
+       uint16_t xri;
        struct lpfc_node_rrq *prrq;
 
 
        pcmd = (uint8_t *) (((struct lpfc_dmabuf *) iocb->context2)->virt);
        pcmd += sizeof(uint32_t);
        rrq = (struct RRQ *)pcmd;
-       rxid = bf_get(rrq_oxid, rrq);
+       rrq->rrq_exchg = be32_to_cpu(rrq->rrq_exchg);
+       rxid = be16_to_cpu(bf_get(rrq_rxid, rrq));
 
        lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
                        "2883 Clear RRQ for SID:x%x OXID:x%x RXID:x%x"
                        " x%x x%x\n",
-                       bf_get(rrq_did, rrq),
-                       bf_get(rrq_oxid, rrq),
+                       be32_to_cpu(bf_get(rrq_did, rrq)),
+                       be16_to_cpu(bf_get(rrq_oxid, rrq)),
                        rxid,
                        iocb->iotag, iocb->iocb.ulpContext);
 
        lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
                "Clear RRQ:  did:x%x flg:x%x exchg:x%.08x",
                ndlp->nlp_DID, ndlp->nlp_flag, rrq->rrq_exchg);
-       prrq = lpfc_get_active_rrq(vport, rxid, ndlp->nlp_DID);
+       if (vport->fc_myDID == be32_to_cpu(bf_get(rrq_did, rrq)))
+               xri = be16_to_cpu(bf_get(rrq_oxid, rrq));
+       else
+               xri = rxid;
+       prrq = lpfc_get_active_rrq(vport, xri, ndlp->nlp_DID);
        if (prrq)
-               lpfc_clr_rrq_active(phba, rxid, prrq);
+               lpfc_clr_rrq_active(phba, xri, prrq);
        return;
 }
 
@@ -6166,6 +6257,11 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
        if (vport->load_flag & FC_UNLOADING)
                goto dropit;
 
+       /* If NPort discovery is delayed drop incoming ELS */
+       if ((vport->fc_flag & FC_DISC_DELAYED) &&
+                       (cmd != ELS_CMD_PLOGI))
+               goto dropit;
+
        ndlp = lpfc_findnode_did(vport, did);
        if (!ndlp) {
                /* Cannot find existing Fabric ndlp, so allocate a new one */
@@ -6218,6 +6314,12 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp);
 
                lpfc_send_els_event(vport, ndlp, payload);
+
+               /* If Nport discovery is delayed, reject PLOGIs */
+               if (vport->fc_flag & FC_DISC_DELAYED) {
+                       rjt_err = LSRJT_UNABLE_TPC;
+                       break;
+               }
                if (vport->port_state < LPFC_DISC_AUTH) {
                        if (!(phba->pport->fc_flag & FC_PT2PT) ||
                                (phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
@@ -6596,6 +6698,21 @@ void
 lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
 {
        struct lpfc_nodelist *ndlp, *ndlp_fdmi;
+       struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+
+       /*
+        * If lpfc_delay_discovery parameter is set and the clean address
+        * bit is cleared and fc fabric parameters chenged, delay FC NPort
+        * discovery.
+        */
+       spin_lock_irq(shost->host_lock);
+       if (vport->fc_flag & FC_DISC_DELAYED) {
+               spin_unlock_irq(shost->host_lock);
+               mod_timer(&vport->delayed_disc_tmo,
+                       jiffies + HZ * phba->fc_ratov);
+               return;
+       }
+       spin_unlock_irq(shost->host_lock);
 
        ndlp = lpfc_findnode_did(vport, NameServer_DID);
        if (!ndlp) {
@@ -6938,6 +7055,9 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
        struct lpfc_nodelist *next_np;
        IOCB_t *irsp = &rspiocb->iocb;
        struct lpfc_iocbq *piocb;
+       struct lpfc_dmabuf *pcmd = cmdiocb->context2, *prsp;
+       struct serv_parm *sp;
+       uint8_t fabric_param_changed;
 
        lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
                         "0123 FDISC completes. x%x/x%x prevDID: x%x\n",
@@ -6981,7 +7101,14 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 
        vport->fc_myDID = irsp->un.ulpWord[4] & Mask_DID;
        lpfc_vport_set_state(vport, FC_VPORT_ACTIVE);
-       if ((vport->fc_prevDID != vport->fc_myDID) &&
+       prsp = list_get_first(&pcmd->list, struct lpfc_dmabuf, list);
+       sp = prsp->virt + sizeof(uint32_t);
+       fabric_param_changed = lpfc_check_clean_addr_bit(vport, sp);
+       memcpy(&vport->fabric_portname, &sp->portName,
+               sizeof(struct lpfc_name));
+       memcpy(&vport->fabric_nodename, &sp->nodeName,
+               sizeof(struct lpfc_name));
+       if (fabric_param_changed &&
                !(vport->fc_flag & FC_VPORT_NEEDS_REG_VPI)) {
                /* If our NportID changed, we need to ensure all
                 * remaining NPORTs get unreg_login'ed so we can
@@ -7581,6 +7708,32 @@ void lpfc_fabric_abort_hba(struct lpfc_hba *phba)
                              IOERR_SLI_ABORTED);
 }
 
+/**
+ * lpfc_sli4_vport_delete_els_xri_aborted -Remove all ndlp references for vport
+ * @vport: pointer to lpfc vport data structure.
+ *
+ * This routine is invoked by the vport cleanup for deletions and the cleanup
+ * for an ndlp on removal.
+ **/
+void
+lpfc_sli4_vport_delete_els_xri_aborted(struct lpfc_vport *vport)
+{
+       struct lpfc_hba *phba = vport->phba;
+       struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL;
+       unsigned long iflag = 0;
+
+       spin_lock_irqsave(&phba->hbalock, iflag);
+       spin_lock(&phba->sli4_hba.abts_sgl_list_lock);
+       list_for_each_entry_safe(sglq_entry, sglq_next,
+                       &phba->sli4_hba.lpfc_abts_els_sgl_list, list) {
+               if (sglq_entry->ndlp && sglq_entry->ndlp->vport == vport)
+                       sglq_entry->ndlp = NULL;
+       }
+       spin_unlock(&phba->sli4_hba.abts_sgl_list_lock);
+       spin_unlock_irqrestore(&phba->hbalock, iflag);
+       return;
+}
+
 /**
  * lpfc_sli4_els_xri_aborted - Slow-path process of els xri abort
  * @phba: pointer to lpfc hba data structure.