scsi: lpfc: Add MDS Diagnostic support.
authorJames Smart <jsmart2021@gmail.com>
Mon, 15 May 2017 22:20:51 +0000 (15:20 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 17 May 2017 01:24:47 +0000 (21:24 -0400)
Added code to support Cisco MDS loopback diagnostic. The diagnostics run
various loopbacks including one which loops-back frame through the
driver.

Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: James Smart <james.smart@broadcom.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_els.c
drivers/scsi/lpfc/lpfc_hbadisc.c
drivers/scsi/lpfc/lpfc_hw4.h
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_sli.c

index c47bde6205c9bc5db0e52dd2b4bcce0a1a481cf9..f2c0ba6ced78bad65694cde021611d9e0e9f9f25 100644 (file)
@@ -675,6 +675,8 @@ struct lpfc_hba {
                                        /* INIT_LINK mailbox command */
 #define LS_NPIV_FAB_SUPPORTED 0x2      /* Fabric supports NPIV */
 #define LS_IGNORE_ERATT       0x4      /* intr handler should ignore ERATT */
+#define LS_MDS_LINK_DOWN      0x8      /* MDS Diagnostics Link Down */
+#define LS_MDS_LOOPBACK      0x16      /* MDS Diagnostics Link Up (Loopback) */
 
        uint32_t hba_flag;      /* hba generic flags */
 #define HBA_ERATT_HANDLED      0x1 /* This flag is set when eratt handled */
index 3085895464d916026af067c6a7d9745d3a1bc62b..1d36f82fa369ce4fff0b3483474477a993f93b59 100644 (file)
@@ -1047,6 +1047,13 @@ stop_rr_fcf_flogi:
                                 irsp->ulpStatus, irsp->un.ulpWord[4],
                                 irsp->ulpTimeout);
 
+
+               /* If this is not a loop open failure, bail out */
+               if (!(irsp->ulpStatus == IOSTAT_LOCAL_REJECT &&
+                     ((irsp->un.ulpWord[4] & IOERR_PARAM_MASK) ==
+                                       IOERR_LOOP_OPEN_FAILURE)))
+                       goto flogifail;
+
                /* FLOGI failed, so there is no fabric */
                spin_lock_irq(shost->host_lock);
                vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
index dcc9b38587782a68003bf7562e2f0f8bcf6b296a..3ffcd9215ca892eb7ef3e5972df50a427ef17369 100644 (file)
@@ -701,7 +701,8 @@ lpfc_work_done(struct lpfc_hba *phba)
                        /* Set the lpfc data pending flag */
                        set_bit(LPFC_DATA_READY, &phba->data_flags);
                } else {
-                       if (phba->link_state >= LPFC_LINK_UP) {
+                       if (phba->link_state >= LPFC_LINK_UP ||
+                           phba->link_flag & LS_MDS_LOOPBACK) {
                                pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
                                lpfc_sli_handle_slow_ring_event(phba, pring,
                                                                (status &
index df97c6b7433bd661a5a97031f736dc28e4fc0587..e0a5fce416aeea7604ab9a46464d8514e82fb0cb 100644 (file)
@@ -4421,6 +4421,19 @@ struct fcp_treceive64_wqe {
 };
 #define TXRDY_PAYLOAD_LEN      12
 
+#define CMD_SEND_FRAME 0xE1
+
+struct send_frame_wqe {
+       struct ulp_bde64 bde;          /* words 0-2 */
+       uint32_t frame_len;            /* word 3 */
+       uint32_t fc_hdr_wd0;           /* word 4 */
+       uint32_t fc_hdr_wd1;           /* word 5 */
+       struct wqe_common wqe_com;     /* words 6-11 */
+       uint32_t fc_hdr_wd2;           /* word 12 */
+       uint32_t fc_hdr_wd3;           /* word 13 */
+       uint32_t fc_hdr_wd4;           /* word 14 */
+       uint32_t fc_hdr_wd5;           /* word 15 */
+};
 
 union lpfc_wqe {
        uint32_t words[16];
@@ -4439,7 +4452,7 @@ union lpfc_wqe {
        struct fcp_trsp64_wqe fcp_trsp;
        struct fcp_tsend64_wqe fcp_tsend;
        struct fcp_treceive64_wqe fcp_treceive;
-
+       struct send_frame_wqe send_frame;
 };
 
 union lpfc_wqe128 {
index 9f6c7e71814b065b9edc2052d55f7cc2f8089c26..9add9473cae52a1f2bf5d1b78a8854a57c9f6192 100644 (file)
@@ -4540,6 +4540,19 @@ lpfc_sli4_async_fc_evt(struct lpfc_hba *phba, struct lpfc_acqe_fc_la *acqe_fc)
        pmb->vport = phba->pport;
 
        if (phba->sli4_hba.link_state.status != LPFC_FC_LA_TYPE_LINK_UP) {
+               phba->link_flag &= ~(LS_MDS_LINK_DOWN | LS_MDS_LOOPBACK);
+
+               switch (phba->sli4_hba.link_state.status) {
+               case LPFC_FC_LA_TYPE_MDS_LINK_DOWN:
+                       phba->link_flag |= LS_MDS_LINK_DOWN;
+                       break;
+               case LPFC_FC_LA_TYPE_MDS_LOOPBACK:
+                       phba->link_flag |= LS_MDS_LOOPBACK;
+                       break;
+               default:
+                       break;
+               }
+
                /* Parse and translate status field */
                mb = &pmb->u.mb;
                mb->mbxStatus = lpfc_sli4_parse_latt_fault(phba,
index 903c06ff828a37b88bbb43bf3c8b964981cab2e4..d6b184839bc2ff951233ee8fbcf477d6b133206f 100644 (file)
@@ -74,6 +74,8 @@ static struct lpfc_iocbq *lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *,
                                                         struct lpfc_iocbq *);
 static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *,
                                      struct hbq_dmabuf *);
+static void lpfc_sli4_handle_mds_loopback(struct lpfc_vport *vport,
+                                         struct hbq_dmabuf *dmabuf);
 static int lpfc_sli4_fp_handle_cqe(struct lpfc_hba *, struct lpfc_queue *,
                                    struct lpfc_cqe *);
 static int lpfc_sli4_post_sgl_list(struct lpfc_hba *, struct list_head *,
@@ -5907,7 +5909,7 @@ lpfc_set_features(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox,
                bf_set(lpfc_mbx_set_feature_mds,
                       &mbox->u.mqe.un.set_feature, 1);
                bf_set(lpfc_mbx_set_feature_mds_deep_loopbk,
-                      &mbox->u.mqe.un.set_feature, 0);
+                      &mbox->u.mqe.un.set_feature, 1);
                mbox->u.mqe.un.set_feature.feature = LPFC_SET_MDS_DIAGS;
                mbox->u.mqe.un.set_feature.param_len = 8;
                break;
@@ -8688,8 +8690,11 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                memset(wqe, 0, sizeof(union lpfc_wqe128));
        /* Some of the fields are in the right position already */
        memcpy(wqe, &iocbq->iocb, sizeof(union lpfc_wqe));
-       wqe->generic.wqe_com.word7 = 0; /* The ct field has moved so reset */
-       wqe->generic.wqe_com.word10 = 0;
+       if (iocbq->iocb.ulpCommand != CMD_SEND_FRAME) {
+               /* The ct field has moved so reset */
+               wqe->generic.wqe_com.word7 = 0;
+               wqe->generic.wqe_com.word10 = 0;
+       }
 
        abort_tag = (uint32_t) iocbq->iotag;
        xritag = iocbq->sli4_xritag;
@@ -9183,6 +9188,10 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                }
 
                break;
+       case CMD_SEND_FRAME:
+               bf_set(wqe_xri_tag, &wqe->generic.wqe_com, xritag);
+               bf_set(wqe_reqtag, &wqe->generic.wqe_com, iocbq->iotag);
+               return 0;
        case CMD_XRI_ABORTED_CX:
        case CMD_CREATE_XRI_CR: /* Do we expect to use this? */
        case CMD_IOCB_FCP_IBIDIR64_CR: /* bidirectional xfer */
@@ -16137,6 +16146,8 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
        struct fc_vft_header *fc_vft_hdr;
        uint32_t *header = (uint32_t *) fc_hdr;
 
+#define FC_RCTL_MDS_DIAGS      0xF4
+
        switch (fc_hdr->fh_r_ctl) {
        case FC_RCTL_DD_UNCAT:          /* uncategorized information */
        case FC_RCTL_DD_SOL_DATA:       /* solicited data */
@@ -16164,6 +16175,7 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
        case FC_RCTL_F_BSY:     /* fabric busy to data frame */
        case FC_RCTL_F_BSYL:    /* fabric busy to link control frame */
        case FC_RCTL_LCR:       /* link credit reset */
+       case FC_RCTL_MDS_DIAGS: /* MDS Diagnostics */
        case FC_RCTL_END:       /* end */
                break;
        case FC_RCTL_VFTH:      /* Virtual Fabric tagging Header */
@@ -16173,12 +16185,16 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
        default:
                goto drop;
        }
+
+#define FC_TYPE_VENDOR_UNIQUE  0xFF
+
        switch (fc_hdr->fh_type) {
        case FC_TYPE_BLS:
        case FC_TYPE_ELS:
        case FC_TYPE_FCP:
        case FC_TYPE_CT:
        case FC_TYPE_NVME:
+       case FC_TYPE_VENDOR_UNIQUE:
                break;
        case FC_TYPE_IP:
        case FC_TYPE_ILS:
@@ -16189,12 +16205,14 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr)
        lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
                        "2538 Received frame rctl:%s (x%x), type:%s (x%x), "
                        "frame Data:%08x %08x %08x %08x %08x %08x %08x\n",
+                       (fc_hdr->fh_r_ctl == FC_RCTL_MDS_DIAGS) ? "MDS Diags" :
                        lpfc_rctl_names[fc_hdr->fh_r_ctl], fc_hdr->fh_r_ctl,
-                       lpfc_type_names[fc_hdr->fh_type], fc_hdr->fh_type,
-                       be32_to_cpu(header[0]), be32_to_cpu(header[1]),
-                       be32_to_cpu(header[2]), be32_to_cpu(header[3]),
-                       be32_to_cpu(header[4]), be32_to_cpu(header[5]),
-                       be32_to_cpu(header[6]));
+                       (fc_hdr->fh_type == FC_TYPE_VENDOR_UNIQUE) ?
+                       "Vendor Unique" : lpfc_type_names[fc_hdr->fh_type],
+                       fc_hdr->fh_type, be32_to_cpu(header[0]),
+                       be32_to_cpu(header[1]), be32_to_cpu(header[2]),
+                       be32_to_cpu(header[3]), be32_to_cpu(header[4]),
+                       be32_to_cpu(header[5]), be32_to_cpu(header[6]));
        return 0;
 drop:
        lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
@@ -17000,6 +17018,96 @@ lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport,
        lpfc_sli_release_iocbq(phba, iocbq);
 }
 
+static void
+lpfc_sli4_mds_loopback_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+                           struct lpfc_iocbq *rspiocb)
+{
+       struct lpfc_dmabuf *pcmd = cmdiocb->context2;
+
+       if (pcmd && pcmd->virt)
+               pci_pool_free(phba->lpfc_drb_pool, pcmd->virt, pcmd->phys);
+       kfree(pcmd);
+       lpfc_sli_release_iocbq(phba, cmdiocb);
+}
+
+static void
+lpfc_sli4_handle_mds_loopback(struct lpfc_vport *vport,
+                             struct hbq_dmabuf *dmabuf)
+{
+       struct fc_frame_header *fc_hdr;
+       struct lpfc_hba *phba = vport->phba;
+       struct lpfc_iocbq *iocbq = NULL;
+       union  lpfc_wqe *wqe;
+       struct lpfc_dmabuf *pcmd = NULL;
+       uint32_t frame_len;
+       int rc;
+
+       fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+       frame_len = bf_get(lpfc_rcqe_length, &dmabuf->cq_event.cqe.rcqe_cmpl);
+
+       /* Send the received frame back */
+       iocbq = lpfc_sli_get_iocbq(phba);
+       if (!iocbq)
+               goto exit;
+
+       /* Allocate buffer for command payload */
+       pcmd = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+       if (pcmd)
+               pcmd->virt = pci_pool_alloc(phba->lpfc_drb_pool, GFP_KERNEL,
+                                           &pcmd->phys);
+       if (!pcmd || !pcmd->virt)
+               goto exit;
+
+       INIT_LIST_HEAD(&pcmd->list);
+
+       /* copyin the payload */
+       memcpy(pcmd->virt, dmabuf->dbuf.virt, frame_len);
+
+       /* fill in BDE's for command */
+       iocbq->iocb.un.xseq64.bdl.addrHigh = putPaddrHigh(pcmd->phys);
+       iocbq->iocb.un.xseq64.bdl.addrLow = putPaddrLow(pcmd->phys);
+       iocbq->iocb.un.xseq64.bdl.bdeFlags = BUFF_TYPE_BDE_64;
+       iocbq->iocb.un.xseq64.bdl.bdeSize = frame_len;
+
+       iocbq->context2 = pcmd;
+       iocbq->vport = vport;
+       iocbq->iocb_flag &= ~LPFC_FIP_ELS_ID_MASK;
+       iocbq->iocb_flag |= LPFC_USE_FCPWQIDX;
+
+       /*
+        * Setup rest of the iocb as though it were a WQE
+        * Build the SEND_FRAME WQE
+        */
+       wqe = (union lpfc_wqe *)&iocbq->iocb;
+
+       wqe->send_frame.frame_len = frame_len;
+       wqe->send_frame.fc_hdr_wd0 = be32_to_cpu(*((uint32_t *)fc_hdr));
+       wqe->send_frame.fc_hdr_wd1 = be32_to_cpu(*((uint32_t *)fc_hdr + 1));
+       wqe->send_frame.fc_hdr_wd2 = be32_to_cpu(*((uint32_t *)fc_hdr + 2));
+       wqe->send_frame.fc_hdr_wd3 = be32_to_cpu(*((uint32_t *)fc_hdr + 3));
+       wqe->send_frame.fc_hdr_wd4 = be32_to_cpu(*((uint32_t *)fc_hdr + 4));
+       wqe->send_frame.fc_hdr_wd5 = be32_to_cpu(*((uint32_t *)fc_hdr + 5));
+
+       iocbq->iocb.ulpCommand = CMD_SEND_FRAME;
+       iocbq->iocb.ulpLe = 1;
+       iocbq->iocb_cmpl = lpfc_sli4_mds_loopback_cmpl;
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, iocbq, 0);
+       if (rc == IOCB_ERROR)
+               goto exit;
+
+       lpfc_in_buf_free(phba, &dmabuf->dbuf);
+       return;
+
+exit:
+       lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+                       "2023 Unable to process MDS loopback frame\n");
+       if (pcmd && pcmd->virt)
+               pci_pool_free(phba->lpfc_drb_pool, pcmd->virt, pcmd->phys);
+       kfree(pcmd);
+       lpfc_sli_release_iocbq(phba, iocbq);
+       lpfc_in_buf_free(phba, &dmabuf->dbuf);
+}
+
 /**
  * lpfc_sli4_handle_received_buffer - Handle received buffers from firmware
  * @phba: Pointer to HBA context object.
@@ -17038,6 +17146,13 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba,
                fcfi = bf_get(lpfc_rcqe_fcf_id,
                              &dmabuf->cq_event.cqe.rcqe_cmpl);
 
+       if (fc_hdr->fh_r_ctl == 0xF4 && fc_hdr->fh_type == 0xFF) {
+               vport = phba->pport;
+               /* Handle MDS Loopback frames */
+               lpfc_sli4_handle_mds_loopback(vport, dmabuf);
+               return;
+       }
+
        /* d_id this frame is directed to */
        did = sli4_did_from_fc_hdr(fc_hdr);