From 88a2cfbb8bf3802ca5a90c7d1567a1e542e6ef0c Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 22 Jul 2011 18:36:33 -0400 Subject: [SCSI] lpfc 8.3.25: Miscellaneous Bug fixes and code cleanup Miscellaneous Bug fixes and code cleanup - Fix 16G link speed reporting by adding check for 16G check. - Change the check and enforcement of MAILBOX_EXT_SIZE (2048B) to the check and enforcement of BSG_MBOX_SIZE - sizeof(MAILBOX_t) (3840B). - Instead of waiting for a fixed amount of time after performing firmware reset, the driver shall wait for the Lancer SLIPORT_STATUS register for the readiness of the firmware for bring up. - Add logging to indicate when dynamic parameters are changed. - Add revision and date to the firmware image format. - Use revision instead of rev_name to check firmware image version. - Update temporary offset after memcopy is complete for firmware update. - Consolidated the use of the macros to get rid of duplicated register offset definitions. - Removed the unused second parameter in routine lpfc_bsg_diag_mode_enter() - Enable debugfs when debugfs is enabled. - Update function comments for lpfc_sli4_alloc_xri and lpfc_sli4_init_rpi_hdrs. Signed-off-by: Alex Iannicelli Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_sli.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/scsi/lpfc/lpfc_sli.c') diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 98999bbd8cbf..6740c52e99c4 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -12345,19 +12345,18 @@ lpfc_sli4_post_sgl(struct lpfc_hba *phba, } /** - * lpfc_sli4_init_rpi_hdrs - Post the rpi header memory region to the port + * lpfc_sli4_alloc_xri - Get an available rpi in the device's range * @phba: pointer to lpfc hba data structure. * * This routine is invoked to post rpi header templates to the - * port for those SLI4 ports that do not support extents. This routine - * posts a PAGE_SIZE memory region to the port to hold up to - * PAGE_SIZE modulo 64 rpi context headers. This is an initialization routine - * and should be called only when interrupts are disabled. + * HBA consistent with the SLI-4 interface spec. This routine + * posts a SLI4_PAGE_SIZE memory region to the port to hold up to + * SLI4_PAGE_SIZE modulo 64 rpi context headers. * - * Return codes - * 0 - successful - * -ERROR - otherwise. - */ + * Returns + * A nonzero rpi defined as rpi_base <= rpi < max_rpi if successful + * LPFC_RPI_ALLOC_ERROR if no rpis are available. + **/ uint16_t lpfc_sli4_alloc_xri(struct lpfc_hba *phba) { -- cgit v1.2.3 From 7851fe2c7f294d0beccf4c3d6af52e8247b89f00 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 22 Jul 2011 18:36:52 -0400 Subject: [SCSI] lpfc 8.3.25: Adapter Interface fixes and changes Adapter Interface fixes and changes - Modify the macro field from lpfc_init_vpi_vpi to lpfc_init_vfi_vpi - Add the new CQE_CODE_RECEIVE_V1 CQE Code, add code in the driver to handle the new Code the same as the CQE_CODE_RECEIVE code except that there are two new checks for this code that will cause the driver to use the new V1 macros for rq_id and fcf_id. - Fix a bug in lpfc_prep_seq() where the size out of the first CQE was ONLY being used, even though multiple dmabufs make up the sequence, each have their own CQE with potentially different sizes. - Fix bug in lpfc_bsg_ct_unsol_event() where the ulpContext and ulpWord[3] fields of the XMIT_SEQUENCE64_CX IOCB were being calculated incorrectly. - Do physical to logical translation before indexing into the active XRI array. - Populate physical vpi in the iocb data structure. - Put the current accumulated total in each IOCB in the chain as we are walking thru then. The last IOCB in the chain should have the total length of the sequence. Signed-off-by: Alex Iannicelli Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 3 +- drivers/scsi/lpfc/lpfc_bsg.c | 15 ++++---- drivers/scsi/lpfc/lpfc_crtn.h | 1 + drivers/scsi/lpfc/lpfc_els.c | 70 +++++++++++++++++++++++----------- drivers/scsi/lpfc/lpfc_hw.h | 7 +++- drivers/scsi/lpfc/lpfc_hw4.h | 9 ++++- drivers/scsi/lpfc/lpfc_mbox.c | 2 +- drivers/scsi/lpfc/lpfc_sli.c | 88 ++++++++++++++++++++++++++++--------------- 8 files changed, 133 insertions(+), 62 deletions(-) (limited to 'drivers/scsi/lpfc/lpfc_sli.c') diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 42b583b26139..0441361c79c8 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -470,9 +470,10 @@ enum intr_type_t { struct unsol_rcv_ct_ctx { uint32_t ctxt_id; uint32_t SID; - uint32_t oxid; uint32_t flags; #define UNSOL_VALID 0x00000001 + uint16_t oxid; + uint16_t rxid; }; #define LPFC_USER_LINK_SPEED_AUTO 0 /* auto select (default)*/ diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 8675aa20642a..f46378fb802c 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -960,8 +960,10 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, evt_dat->immed_dat].oxid, phba->ct_ctx[ evt_dat->immed_dat].SID); + phba->ct_ctx[evt_dat->immed_dat].rxid = + piocbq->iocb.ulpContext; phba->ct_ctx[evt_dat->immed_dat].oxid = - piocbq->iocb.ulpContext; + piocbq->iocb.unsli3.rcvsli3.ox_id; phba->ct_ctx[evt_dat->immed_dat].SID = piocbq->iocb.un.rcvels.remoteID; phba->ct_ctx[evt_dat->immed_dat].flags = UNSOL_VALID; @@ -1312,7 +1314,8 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, rc = IOCB_ERROR; goto issue_ct_rsp_exit; } - icmd->ulpContext = phba->ct_ctx[tag].oxid; + icmd->ulpContext = phba->ct_ctx[tag].rxid; + icmd->unsli3.rcvsli3.ox_id = phba->ct_ctx[tag].oxid; ndlp = lpfc_findnode_did(phba->pport, phba->ct_ctx[tag].SID); if (!ndlp) { lpfc_printf_log(phba, KERN_WARNING, LOG_ELS, @@ -1337,9 +1340,7 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, goto issue_ct_rsp_exit; } - icmd->un.ulpWord[3] = ndlp->nlp_rpi; - if (phba->sli_rev == LPFC_SLI_REV4) - icmd->ulpContext = + icmd->un.ulpWord[3] = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]; /* The exchange is done, mark the entry as invalid */ @@ -1351,8 +1352,8 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, /* Xmit CT response on exchange */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, - "2722 Xmit CT response on exchange x%x Data: x%x x%x\n", - icmd->ulpContext, icmd->ulpIoTag, phba->link_state); + "2722 Xmit CT response on exchange x%x Data: x%x x%x x%x\n", + icmd->ulpContext, icmd->ulpIoTag, tag, phba->link_state); ctiocb->iocb_cmpl = NULL; ctiocb->iocb_flag |= LPFC_IO_LIBDFC; diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index fc20c247f36b..a4f00cc72f41 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -432,6 +432,7 @@ void lpfc_handle_rrq_active(struct lpfc_hba *); int lpfc_send_rrq(struct lpfc_hba *, struct lpfc_node_rrq *); int lpfc_set_rrq_active(struct lpfc_hba *, struct lpfc_nodelist *, uint16_t, uint16_t, uint16_t); +uint16_t lpfc_sli4_xri_inrange(struct lpfc_hba *, uint16_t); void lpfc_cleanup_wt_rrqs(struct lpfc_hba *); void lpfc_cleanup_vports_rrqs(struct lpfc_vport *, struct lpfc_nodelist *); struct lpfc_node_rrq *lpfc_get_active_rrq(struct lpfc_vport *, uint16_t, diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 32a084534f3e..d3a712a27e31 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -3656,7 +3656,8 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, } icmd = &elsiocb->iocb; - icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */ + icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id; pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); *((uint32_t *) (pcmd)) = ELS_CMD_ACC; pcmd += sizeof(uint32_t); @@ -3673,7 +3674,8 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, return 1; icmd = &elsiocb->iocb; - icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */ + icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id; pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); if (mbox) @@ -3695,7 +3697,8 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, return 1; icmd = &elsiocb->iocb; - icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */ + icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id; pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); memcpy(pcmd, ((struct lpfc_dmabuf *) oldiocb->context2)->virt, @@ -3781,7 +3784,8 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError, icmd = &elsiocb->iocb; oldcmd = &oldiocb->iocb; - icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */ + icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); *((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT; @@ -3853,7 +3857,8 @@ lpfc_els_rsp_adisc_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, icmd = &elsiocb->iocb; oldcmd = &oldiocb->iocb; - icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */ + icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id; /* Xmit ADISC ACC response tag */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, @@ -3931,7 +3936,9 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, icmd = &elsiocb->iocb; oldcmd = &oldiocb->iocb; - icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */ + icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id; + /* Xmit PRLI ACC response tag */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0131 Xmit PRLI ACC response tag x%x xri x%x, " @@ -4035,7 +4042,9 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format, icmd = &elsiocb->iocb; oldcmd = &oldiocb->iocb; - icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */ + icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id; + /* Xmit RNID ACC response tag */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0132 Xmit RNID ACC response tag x%x xri x%x\n", @@ -4163,7 +4172,9 @@ lpfc_els_rsp_echo_acc(struct lpfc_vport *vport, uint8_t *data, if (!elsiocb) return 1; - elsiocb->iocb.ulpContext = oldiocb->iocb.ulpContext; /* Xri */ + elsiocb->iocb.ulpContext = oldiocb->iocb.ulpContext; /* Xri / rx_id */ + elsiocb->iocb.unsli3.rcvsli3.ox_id = oldiocb->iocb.unsli3.rcvsli3.ox_id; + /* Xmit ECHO ACC response tag */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "2876 Xmit ECHO ACC response tag x%x xri x%x\n", @@ -5054,13 +5065,15 @@ lpfc_els_rsp_rls_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) uint8_t *pcmd; struct lpfc_iocbq *elsiocb; struct lpfc_nodelist *ndlp; - uint16_t xri; + uint16_t oxid; + uint16_t rxid; uint32_t cmdsize; mb = &pmb->u.mb; ndlp = (struct lpfc_nodelist *) pmb->context2; - xri = (uint16_t) ((unsigned long)(pmb->context1)); + rxid = (uint16_t) ((unsigned long)(pmb->context1) & 0xffff); + oxid = (uint16_t) (((unsigned long)(pmb->context1) >> 16) & 0xffff); pmb->context1 = NULL; pmb->context2 = NULL; @@ -5082,7 +5095,8 @@ lpfc_els_rsp_rls_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) return; icmd = &elsiocb->iocb; - icmd->ulpContext = xri; + icmd->ulpContext = rxid; + icmd->unsli3.rcvsli3.ox_id = oxid; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); *((uint32_t *) (pcmd)) = ELS_CMD_ACC; @@ -5137,13 +5151,16 @@ lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) uint8_t *pcmd; struct lpfc_iocbq *elsiocb; struct lpfc_nodelist *ndlp; - uint16_t xri, status; + uint16_t status; + uint16_t oxid; + uint16_t rxid; uint32_t cmdsize; mb = &pmb->u.mb; ndlp = (struct lpfc_nodelist *) pmb->context2; - xri = (uint16_t) ((unsigned long)(pmb->context1)); + rxid = (uint16_t) ((unsigned long)(pmb->context1) & 0xffff); + oxid = (uint16_t) (((unsigned long)(pmb->context1) >> 16) & 0xffff); pmb->context1 = NULL; pmb->context2 = NULL; @@ -5165,7 +5182,8 @@ lpfc_els_rsp_rps_acc(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) return; icmd = &elsiocb->iocb; - icmd->ulpContext = xri; + icmd->ulpContext = rxid; + icmd->unsli3.rcvsli3.ox_id = oxid; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); *((uint32_t *) (pcmd)) = ELS_CMD_ACC; @@ -5238,8 +5256,9 @@ lpfc_els_rcv_rls(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC); if (mbox) { lpfc_read_lnk_stat(phba, mbox); - mbox->context1 = - (void *)((unsigned long) cmdiocb->iocb.ulpContext); + mbox->context1 = (void *)((unsigned long) + ((cmdiocb->iocb.unsli3.rcvsli3.ox_id << 16) | + cmdiocb->iocb.ulpContext)); /* rx_id */ mbox->context2 = lpfc_nlp_get(ndlp); mbox->vport = vport; mbox->mbox_cmpl = lpfc_els_rsp_rls_acc; @@ -5314,7 +5333,8 @@ lpfc_els_rcv_rtv(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, pcmd += sizeof(uint32_t); /* Skip past command */ /* use the command's xri in the response */ - elsiocb->iocb.ulpContext = cmdiocb->iocb.ulpContext; + elsiocb->iocb.ulpContext = cmdiocb->iocb.ulpContext; /* Xri / rx_id */ + elsiocb->iocb.unsli3.rcvsli3.ox_id = cmdiocb->iocb.unsli3.rcvsli3.ox_id; rtv_rsp = (struct RTV_RSP *)pcmd; @@ -5399,8 +5419,9 @@ lpfc_els_rcv_rps(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC); if (mbox) { lpfc_read_lnk_stat(phba, mbox); - mbox->context1 = - (void *)((unsigned long) cmdiocb->iocb.ulpContext); + mbox->context1 = (void *)((unsigned long) + ((cmdiocb->iocb.unsli3.rcvsli3.ox_id << 16) | + cmdiocb->iocb.ulpContext)); /* rx_id */ mbox->context2 = lpfc_nlp_get(ndlp); mbox->vport = vport; mbox->mbox_cmpl = lpfc_els_rsp_rps_acc; @@ -5554,7 +5575,8 @@ lpfc_els_rsp_rpl_acc(struct lpfc_vport *vport, uint16_t cmdsize, icmd = &elsiocb->iocb; oldcmd = &oldiocb->iocb; - icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + icmd->ulpContext = oldcmd->ulpContext; /* Xri / rx_id */ + icmd->unsli3.rcvsli3.ox_id = oldcmd->unsli3.rcvsli3.ox_id; pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); *((uint32_t *) (pcmd)) = ELS_CMD_ACC; @@ -7787,6 +7809,7 @@ lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba, { uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri); uint16_t rxid = bf_get(lpfc_wcqe_xa_remote_xid, axri); + uint16_t lxri = 0; struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL; unsigned long iflag = 0; @@ -7815,7 +7838,12 @@ lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba, } } spin_unlock(&phba->sli4_hba.abts_sgl_list_lock); - sglq_entry = __lpfc_get_active_sglq(phba, xri); + lxri = lpfc_sli4_xri_inrange(phba, xri); + if (lxri == NO_XRI) { + spin_unlock_irqrestore(&phba->hbalock, iflag); + return; + } + sglq_entry = __lpfc_get_active_sglq(phba, lxri); if (!sglq_entry || (sglq_entry->sli4_xritag != xri)) { spin_unlock_irqrestore(&phba->hbalock, iflag); return; diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index ab4c4d651d0c..046edc4ab35f 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -3470,11 +3470,16 @@ typedef struct { or CMD_IOCB_RCV_SEQ64_CX (0xB5) */ struct rcv_sli3 { - uint32_t word8Rsvd; #ifdef __BIG_ENDIAN_BITFIELD + uint16_t ox_id; + uint16_t seq_cnt; + uint16_t vpi; uint16_t word9Rsvd; #else /* __LITTLE_ENDIAN */ + uint16_t seq_cnt; + uint16_t ox_id; + uint16_t word9Rsvd; uint16_t vpi; #endif diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index cc6f7c34ea2a..7f8003b5181e 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -330,6 +330,7 @@ struct lpfc_cqe { #define CQE_CODE_RELEASE_WQE 0x2 #define CQE_CODE_RECEIVE 0x4 #define CQE_CODE_XRI_ABORTED 0x5 +#define CQE_CODE_RECEIVE_V1 0x9 /* completion queue entry for wqe completions */ struct lpfc_wcqe_complete { @@ -433,7 +434,10 @@ struct lpfc_rcqe { #define FC_STATUS_RQ_BUF_LEN_EXCEEDED 0x11 /* payload truncated */ #define FC_STATUS_INSUFF_BUF_NEED_BUF 0x12 /* Insufficient buffers */ #define FC_STATUS_INSUFF_BUF_FRM_DISC 0x13 /* Frame Discard */ - uint32_t reserved1; + uint32_t word1; +#define lpfc_rcqe_fcf_id_v1_SHIFT 0 +#define lpfc_rcqe_fcf_id_v1_MASK 0x0000003F +#define lpfc_rcqe_fcf_id_v1_WORD word1 uint32_t word2; #define lpfc_rcqe_length_SHIFT 16 #define lpfc_rcqe_length_MASK 0x0000FFFF @@ -444,6 +448,9 @@ struct lpfc_rcqe { #define lpfc_rcqe_fcf_id_SHIFT 0 #define lpfc_rcqe_fcf_id_MASK 0x0000003F #define lpfc_rcqe_fcf_id_WORD word2 +#define lpfc_rcqe_rq_id_v1_SHIFT 0 +#define lpfc_rcqe_rq_id_v1_MASK 0x0000FFFF +#define lpfc_rcqe_rq_id_v1_WORD word2 uint32_t word3; #define lpfc_rcqe_valid_SHIFT lpfc_cqe_valid_SHIFT #define lpfc_rcqe_valid_MASK lpfc_cqe_valid_MASK diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index 556767028353..83450cc5c4d3 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -2031,7 +2031,7 @@ lpfc_init_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport) bf_set(lpfc_init_vfi_vp, init_vfi, 1); bf_set(lpfc_init_vfi_vfi, init_vfi, vport->phba->sli4_hba.vfi_ids[vport->vfi]); - bf_set(lpfc_init_vpi_vpi, init_vfi, + bf_set(lpfc_init_vfi_vpi, init_vfi, vport->phba->vpi_ids[vport->vpi]); bf_set(lpfc_init_vfi_fcfi, init_vfi, vport->phba->fcf.fcfi); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 6740c52e99c4..59d81624a855 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -560,7 +560,7 @@ __lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, rrq = mempool_alloc(phba->rrq_pool, GFP_KERNEL); if (rrq) { rrq->send_rrq = send_rrq; - rrq->xritag = phba->sli4_hba.xri_ids[xritag]; + rrq->xritag = xritag; rrq->rrq_stop_time = jiffies + HZ * (phba->fc_ratov + 1); rrq->ndlp = ndlp; rrq->nlp_DID = ndlp->nlp_DID; @@ -2452,7 +2452,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, /* search continue save q for same XRI */ list_for_each_entry(iocbq, &pring->iocb_continue_saveq, clist) { - if (iocbq->iocb.ulpContext == saveq->iocb.ulpContext) { + if (iocbq->iocb.unsli3.rcvsli3.ox_id == + saveq->iocb.unsli3.rcvsli3.ox_id) { list_add_tail(&saveq->list, &iocbq->list); found = 1; break; @@ -3355,6 +3356,7 @@ lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba, irspiocbq); break; case CQE_CODE_RECEIVE: + case CQE_CODE_RECEIVE_V1: dmabuf = container_of(cq_event, struct hbq_dmabuf, cq_event); lpfc_sli4_handle_received_buffer(phba, dmabuf); @@ -7318,12 +7320,12 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_qosd, &wqe->els_req.wqe_com, 1); bf_set(wqe_lenloc, &wqe->els_req.wqe_com, LPFC_WQE_LENLOC_NONE); bf_set(wqe_ebde_cnt, &wqe->els_req.wqe_com, 0); - break; + break; case CMD_XMIT_SEQUENCE64_CX: bf_set(wqe_ctxt_tag, &wqe->xmit_sequence.wqe_com, iocbq->iocb.un.ulpWord[3]); bf_set(wqe_rcvoxid, &wqe->xmit_sequence.wqe_com, - iocbq->iocb.ulpContext); + iocbq->iocb.unsli3.rcvsli3.ox_id); /* The entire sequence is transmitted for this IOCB */ xmit_len = total_len; cmnd = CMD_XMIT_SEQUENCE64_CR; @@ -7341,7 +7343,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_ebde_cnt, &wqe->xmit_sequence.wqe_com, 0); wqe->xmit_sequence.xmit_len = xmit_len; command_type = OTHER_COMMAND; - break; + break; case CMD_XMIT_BCAST64_CN: /* word3 iocb=iotag32 wqe=seq_payload_len */ wqe->xmit_bcast64.seq_payload_len = xmit_len; @@ -7355,7 +7357,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_lenloc, &wqe->xmit_bcast64.wqe_com, LPFC_WQE_LENLOC_WORD3); bf_set(wqe_ebde_cnt, &wqe->xmit_bcast64.wqe_com, 0); - break; + break; case CMD_FCP_IWRITE64_CR: command_type = FCP_COMMAND_DATA_OUT; /* word3 iocb=iotag wqe=payload_offset_len */ @@ -7375,7 +7377,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, LPFC_WQE_LENLOC_WORD4); bf_set(wqe_ebde_cnt, &wqe->fcp_iwrite.wqe_com, 0); bf_set(wqe_pu, &wqe->fcp_iwrite.wqe_com, iocbq->iocb.ulpPU); - break; + break; case CMD_FCP_IREAD64_CR: /* word3 iocb=iotag wqe=payload_offset_len */ /* Add the FCP_CMD and FCP_RSP sizes to get the offset */ @@ -7394,7 +7396,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, LPFC_WQE_LENLOC_WORD4); bf_set(wqe_ebde_cnt, &wqe->fcp_iread.wqe_com, 0); bf_set(wqe_pu, &wqe->fcp_iread.wqe_com, iocbq->iocb.ulpPU); - break; + break; case CMD_FCP_ICMND64_CR: /* word3 iocb=IO_TAG wqe=reserved */ wqe->fcp_icmd.rsrvd3 = 0; @@ -7407,7 +7409,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_lenloc, &wqe->fcp_icmd.wqe_com, LPFC_WQE_LENLOC_NONE); bf_set(wqe_ebde_cnt, &wqe->fcp_icmd.wqe_com, 0); - break; + break; case CMD_GEN_REQUEST64_CR: /* For this command calculate the xmit length of the * request bde. @@ -7442,7 +7444,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_lenloc, &wqe->gen_req.wqe_com, LPFC_WQE_LENLOC_NONE); bf_set(wqe_ebde_cnt, &wqe->gen_req.wqe_com, 0); command_type = OTHER_COMMAND; - break; + break; case CMD_XMIT_ELS_RSP64_CX: ndlp = (struct lpfc_nodelist *)iocbq->context1; /* words0-2 BDE memcpy */ @@ -7457,7 +7459,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, ((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l)); bf_set(wqe_pu, &wqe->xmit_els_rsp.wqe_com, iocbq->iocb.ulpPU); bf_set(wqe_rcvoxid, &wqe->xmit_els_rsp.wqe_com, - iocbq->iocb.ulpContext); + iocbq->iocb.unsli3.rcvsli3.ox_id); if (!iocbq->iocb.ulpCt_h && iocbq->iocb.ulpCt_l) bf_set(wqe_ctxt_tag, &wqe->xmit_els_rsp.wqe_com, phba->vpi_ids[iocbq->vport->vpi]); @@ -7470,7 +7472,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_rsp_temp_rpi, &wqe->xmit_els_rsp, phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]); command_type = OTHER_COMMAND; - break; + break; case CMD_CLOSE_XRI_CN: case CMD_ABORT_XRI_CN: case CMD_ABORT_XRI_CX: @@ -7509,7 +7511,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, cmnd = CMD_ABORT_XRI_CX; command_type = OTHER_COMMAND; xritag = 0; - break; + break; case CMD_XMIT_BLS_RSP64_CX: /* As BLS ABTS RSP WQE is very different from other WQEs, * we re-construct this WQE here based on information in @@ -7553,7 +7555,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_get(lpfc_rsn_code, &iocbq->iocb.un.bls_rsp)); } - break; + break; case CMD_XRI_ABORTED_CX: case CMD_CREATE_XRI_CR: /* Do we expect to use this? */ case CMD_IOCB_FCP_IBIDIR64_CR: /* bidirectional xfer */ @@ -7565,7 +7567,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, "2014 Invalid command 0x%x\n", iocbq->iocb.ulpCommand); return IOCB_ERROR; - break; + break; } bf_set(wqe_xri_tag, &wqe->generic.wqe_com, xritag); @@ -10481,10 +10483,14 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_rcqe *rcqe) struct lpfc_queue *hrq = phba->sli4_hba.hdr_rq; struct lpfc_queue *drq = phba->sli4_hba.dat_rq; struct hbq_dmabuf *dma_buf; - uint32_t status; + uint32_t status, rq_id; unsigned long iflags; - if (bf_get(lpfc_rcqe_rq_id, rcqe) != hrq->queue_id) + if (bf_get(lpfc_cqe_code, rcqe) == CQE_CODE_RECEIVE_V1) + rq_id = bf_get(lpfc_rcqe_rq_id_v1, rcqe); + else + rq_id = bf_get(lpfc_rcqe_rq_id, rcqe); + if (rq_id != hrq->queue_id) goto out; status = bf_get(lpfc_rcqe_status, rcqe); @@ -10563,6 +10569,7 @@ lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq, (struct sli4_wcqe_xri_aborted *)&cqevt); break; case CQE_CODE_RECEIVE: + case CQE_CODE_RECEIVE_V1: /* Process the RQ event */ phba->last_completion_time = jiffies; workposted = lpfc_sli4_sp_handle_rcqe(phba, @@ -13405,7 +13412,7 @@ lpfc_sli4_seq_abort_rsp_cmpl(struct lpfc_hba *phba, * This function validates the xri maps to the known range of XRIs allocated an * used by the driver. **/ -static uint16_t +uint16_t lpfc_sli4_xri_inrange(struct lpfc_hba *phba, uint16_t xri) { @@ -13642,10 +13649,12 @@ lpfc_seq_complete(struct hbq_dmabuf *dmabuf) static struct lpfc_iocbq * lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) { + struct hbq_dmabuf *hbq_buf; struct lpfc_dmabuf *d_buf, *n_buf; struct lpfc_iocbq *first_iocbq, *iocbq; struct fc_frame_header *fc_hdr; uint32_t sid; + uint32_t len, tot_len; struct ulp_bde64 *pbde; fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt; @@ -13654,6 +13663,7 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) lpfc_update_rcv_time_stamp(vport); /* get the Remote Port's SID */ sid = sli4_sid_from_fc_hdr(fc_hdr); + tot_len = 0; /* Get an iocbq struct to fill in. */ first_iocbq = lpfc_sli_get_iocbq(vport->phba); if (first_iocbq) { @@ -13661,9 +13671,12 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) first_iocbq->iocb.unsli3.rcvsli3.acc_len = 0; first_iocbq->iocb.ulpStatus = IOSTAT_SUCCESS; first_iocbq->iocb.ulpCommand = CMD_IOCB_RCV_SEQ64_CX; - first_iocbq->iocb.ulpContext = be16_to_cpu(fc_hdr->fh_ox_id); - /* iocbq is prepped for internal consumption. Logical vpi. */ - first_iocbq->iocb.unsli3.rcvsli3.vpi = vport->vpi; + first_iocbq->iocb.ulpContext = NO_XRI; + first_iocbq->iocb.unsli3.rcvsli3.ox_id = + be16_to_cpu(fc_hdr->fh_ox_id); + /* iocbq is prepped for internal consumption. Physical vpi. */ + first_iocbq->iocb.unsli3.rcvsli3.vpi = + vport->phba->vpi_ids[vport->vpi]; /* put the first buffer into the first IOCBq */ first_iocbq->context2 = &seq_dmabuf->dbuf; first_iocbq->context3 = NULL; @@ -13671,9 +13684,9 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) first_iocbq->iocb.un.cont64[0].tus.f.bdeSize = LPFC_DATA_BUF_SIZE; first_iocbq->iocb.un.rcvels.remoteID = sid; - first_iocbq->iocb.unsli3.rcvsli3.acc_len += - bf_get(lpfc_rcqe_length, + tot_len = bf_get(lpfc_rcqe_length, &seq_dmabuf->cq_event.cqe.rcqe_cmpl); + first_iocbq->iocb.unsli3.rcvsli3.acc_len = tot_len; } iocbq = first_iocbq; /* @@ -13691,9 +13704,13 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) pbde = (struct ulp_bde64 *) &iocbq->iocb.unsli3.sli3Words[4]; pbde->tus.f.bdeSize = LPFC_DATA_BUF_SIZE; - first_iocbq->iocb.unsli3.rcvsli3.acc_len += - bf_get(lpfc_rcqe_length, - &seq_dmabuf->cq_event.cqe.rcqe_cmpl); + + /* We need to get the size out of the right CQE */ + hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf); + len = bf_get(lpfc_rcqe_length, + &hbq_buf->cq_event.cqe.rcqe_cmpl); + iocbq->iocb.unsli3.rcvsli3.acc_len += len; + tot_len += len; } else { iocbq = lpfc_sli_get_iocbq(vport->phba); if (!iocbq) { @@ -13711,9 +13728,14 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) iocbq->iocb.ulpBdeCount = 1; iocbq->iocb.un.cont64[0].tus.f.bdeSize = LPFC_DATA_BUF_SIZE; - first_iocbq->iocb.unsli3.rcvsli3.acc_len += - bf_get(lpfc_rcqe_length, - &seq_dmabuf->cq_event.cqe.rcqe_cmpl); + + /* We need to get the size out of the right CQE */ + hbq_buf = container_of(d_buf, struct hbq_dmabuf, dbuf); + len = bf_get(lpfc_rcqe_length, + &hbq_buf->cq_event.cqe.rcqe_cmpl); + tot_len += len; + iocbq->iocb.unsli3.rcvsli3.acc_len = tot_len; + iocbq->iocb.un.rcvels.remoteID = sid; list_add_tail(&iocbq->list, &first_iocbq->list); } @@ -13786,7 +13808,13 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba, lpfc_in_buf_free(phba, &dmabuf->dbuf); return; } - fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->cq_event.cqe.rcqe_cmpl); + if ((bf_get(lpfc_cqe_code, + &dmabuf->cq_event.cqe.rcqe_cmpl) == CQE_CODE_RECEIVE_V1)) + fcfi = bf_get(lpfc_rcqe_fcf_id_v1, + &dmabuf->cq_event.cqe.rcqe_cmpl); + else + fcfi = bf_get(lpfc_rcqe_fcf_id, + &dmabuf->cq_event.cqe.rcqe_cmpl); vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi); if (!vport || !(vport->vpi_state & LPFC_VPI_REGISTERED)) { /* throw out the frame */ -- cgit v1.2.3 From 0a96e9754d6c4a2a31e50ee6c6e36ec13f80bc25 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 22 Jul 2011 18:37:28 -0400 Subject: [SCSI] lpfc 8.3.25: PCI and SR-IOV Fixes PCI and SR-IOV Fixes - Call pci_save_state after the pci_restore_state completes. - After calling pci_enable_pcie_error_reporting() and checking the return value for logging messages from rc, reset rc to 0 to it will not later be interpreted for error. - Read PCI config space SR-IOV capability to get the number of VFs supported. - Check for the PF's supported number of VFs before invoking PCI enable sriov API call and log error message that user requested number of VFs is beyond the PF capability if such request is passed in. - Added check for Physical function with Virtual Functions attached. If so, first disable all the VFs before proceeding to device reset. Signed-off-by: Alex Iannicelli Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_attr.c | 76 ++----------------------------------------- drivers/scsi/lpfc/lpfc_crtn.h | 1 + drivers/scsi/lpfc/lpfc_init.c | 44 +++++++++++++++++++++++++ drivers/scsi/lpfc/lpfc_sli.c | 1 + 4 files changed, 49 insertions(+), 73 deletions(-) (limited to 'drivers/scsi/lpfc/lpfc_sli.c') diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index bffd86f204dd..80ca11c5158c 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1466,80 +1466,10 @@ lpfc_sriov_hw_max_virtfn_show(struct device *dev, struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; - struct pci_dev *pdev = phba->pcidev; - union lpfc_sli4_cfg_shdr *shdr; - uint32_t shdr_status, shdr_add_status; - LPFC_MBOXQ_t *mboxq; - struct lpfc_mbx_get_prof_cfg *get_prof_cfg; - struct lpfc_rsrc_desc_pcie *desc; - uint32_t max_nr_virtfn; - uint32_t desc_count; - int length, rc, i; - - if ((phba->sli_rev < LPFC_SLI_REV4) || - (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != - LPFC_SLI_INTF_IF_TYPE_2)) - return -EPERM; - - if (!pdev->is_physfn) - return snprintf(buf, PAGE_SIZE, "%d\n", 0); - - mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); - if (!mboxq) - return -ENOMEM; - - /* get the maximum number of virtfn support by physfn */ - length = (sizeof(struct lpfc_mbx_get_prof_cfg) - - sizeof(struct lpfc_sli4_cfg_mhdr)); - lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON, - LPFC_MBOX_OPCODE_GET_PROFILE_CONFIG, - length, LPFC_SLI4_MBX_EMBED); - shdr = (union lpfc_sli4_cfg_shdr *) - &mboxq->u.mqe.un.sli4_config.header.cfg_shdr; - bf_set(lpfc_mbox_hdr_pf_num, &shdr->request, - phba->sli4_hba.iov.pf_number + 1); - - get_prof_cfg = &mboxq->u.mqe.un.get_prof_cfg; - bf_set(lpfc_mbx_get_prof_cfg_prof_tp, &get_prof_cfg->u.request, - LPFC_CFG_TYPE_CURRENT_ACTIVE); - - rc = lpfc_sli_issue_mbox_wait(phba, mboxq, - lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG)); - - if (rc != MBX_TIMEOUT) { - /* check return status */ - shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); - shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, - &shdr->response); - if (shdr_status || shdr_add_status || rc) - goto error_out; - - } else - goto error_out; - - desc_count = get_prof_cfg->u.response.prof_cfg.rsrc_desc_count; - - for (i = 0; i < LPFC_RSRC_DESC_MAX_NUM; i++) { - desc = (struct lpfc_rsrc_desc_pcie *) - &get_prof_cfg->u.response.prof_cfg.desc[i]; - if (LPFC_RSRC_DESC_TYPE_PCIE == - bf_get(lpfc_rsrc_desc_pcie_type, desc)) { - max_nr_virtfn = bf_get(lpfc_rsrc_desc_pcie_nr_virtfn, - desc); - break; - } - } - - if (i < LPFC_RSRC_DESC_MAX_NUM) { - if (rc != MBX_TIMEOUT) - mempool_free(mboxq, phba->mbox_mem_pool); - return snprintf(buf, PAGE_SIZE, "%d\n", max_nr_virtfn); - } + uint16_t max_nr_virtfn; -error_out: - if (rc != MBX_TIMEOUT) - mempool_free(mboxq, phba->mbox_mem_pool); - return -EIO; + max_nr_virtfn = lpfc_sli_sriov_nr_virtfn_get(phba); + return snprintf(buf, PAGE_SIZE, "%d\n", max_nr_virtfn); } /** diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index a4f00cc72f41..1e41af8dea74 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -440,3 +440,4 @@ struct lpfc_node_rrq *lpfc_get_active_rrq(struct lpfc_vport *, uint16_t, int lpfc_wr_object(struct lpfc_hba *, struct list_head *, uint32_t, uint32_t *); /* functions to support SR-IOV */ int lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *, int); +uint16_t lpfc_sli_sriov_nr_virtfn_get(struct lpfc_hba *); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 9d3e8affccb8..027b797c9916 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -4036,6 +4036,34 @@ lpfc_reset_hba(struct lpfc_hba *phba) lpfc_unblock_mgmt_io(phba); } +/** + * lpfc_sli_sriov_nr_virtfn_get - Get the number of sr-iov virtual functions + * @phba: pointer to lpfc hba data structure. + * + * This function enables the PCI SR-IOV virtual functions to a physical + * function. It invokes the PCI SR-IOV api with the @nr_vfn provided to + * enable the number of virtual functions to the physical function. As + * not all devices support SR-IOV, the return code from the pci_enable_sriov() + * API call does not considered as an error condition for most of the device. + **/ +uint16_t +lpfc_sli_sriov_nr_virtfn_get(struct lpfc_hba *phba) +{ + struct pci_dev *pdev = phba->pcidev; + uint16_t nr_virtfn; + int pos; + + if (!pdev->is_physfn) + return 0; + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); + if (pos == 0) + return 0; + + pci_read_config_word(pdev, pos + PCI_SRIOV_TOTAL_VF, &nr_virtfn); + return nr_virtfn; +} + /** * lpfc_sli_probe_sriov_nr_virtfn - Enable a number of sr-iov virtual functions * @phba: pointer to lpfc hba data structure. @@ -4051,8 +4079,17 @@ int lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *phba, int nr_vfn) { struct pci_dev *pdev = phba->pcidev; + uint16_t max_nr_vfn; int rc; + max_nr_vfn = lpfc_sli_sriov_nr_virtfn_get(phba); + if (nr_vfn > max_nr_vfn) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3057 Requested vfs (%d) greater than " + "supported vfs (%d)", nr_vfn, max_nr_vfn); + return -EINVAL; + } + rc = pci_enable_sriov(pdev, nr_vfn); if (rc) { lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, @@ -9487,6 +9524,13 @@ lpfc_io_slot_reset_s4(struct pci_dev *pdev) } pci_restore_state(pdev); + + /* + * As the new kernel behavior of pci_restore_state() API call clears + * device saved_state flag, need to save the restored state again. + */ + pci_save_state(pdev); + if (pdev->is_busmaster) pci_set_master(pdev); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 59d81624a855..5b28ea1d72c5 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -5839,6 +5839,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) "Advanced Error Reporting (AER)\n"); phba->cfg_aer_support = 0; } + rc = 0; } if (!(phba->hba_flag & HBA_FCOE_MODE)) { -- cgit v1.2.3 From b76f2dc91c0fff7a66616affdc039dc2e4b7ff98 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 22 Jul 2011 18:37:42 -0400 Subject: [SCSI] lpfc 8.3.25: Enhancements to Debug infrastructure Enhancements to Debug infrastructure - debugfs additions for new hardware. - Correct stack overflow in lpfc_debugfs_dumpHBASlim_data() - Correct warning on uninitialized reg_val in lpfc_idiag_drbacc_write() - Separated the iDiag command for capturing mailbox commands for generic issue mailbox command entry point and for BSG multi-buffer handling. - Added capturing dumping capabiliy of mailbox command and external buffer for the completion of the mailbox command so that the outcome can be examined. - Changed all the iDiag command structure data array indexing introduced so far with properly defined macros. - Added SLI4 device PCI BAR memory mapped register read/browse, write-by- value, set-bit, and clear-bit methods for both interface type 0 and interface type 2. - Corrected warnings on mbxstatus being uninitialized in error paths in lpfc_bsg.c Signed-off-by: Alex Iannicelli Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 4 + drivers/scsi/lpfc/lpfc_attr.c | 9 + drivers/scsi/lpfc/lpfc_bsg.c | 37 +- drivers/scsi/lpfc/lpfc_crtn.h | 8 + drivers/scsi/lpfc/lpfc_debugfs.c | 1456 +++++++++++++++++++++++++++++++++++--- drivers/scsi/lpfc/lpfc_debugfs.h | 125 +++- drivers/scsi/lpfc/lpfc_init.c | 1 - drivers/scsi/lpfc/lpfc_sli.c | 162 ++++- 8 files changed, 1715 insertions(+), 87 deletions(-) (limited to 'drivers/scsi/lpfc/lpfc_sli.c') diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 0441361c79c8..b932067bf2d8 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -851,9 +851,13 @@ struct lpfc_hba { /* iDiag debugfs sub-directory */ struct dentry *idiag_root; struct dentry *idiag_pci_cfg; + struct dentry *idiag_bar_acc; struct dentry *idiag_que_info; struct dentry *idiag_que_acc; struct dentry *idiag_drb_acc; + struct dentry *idiag_ctl_acc; + struct dentry *idiag_mbx_acc; + struct dentry *idiag_ext_acc; #endif /* Used for deferred freeing of ELS data buffers */ diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 80ca11c5158c..fa5bc146d67b 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -810,6 +810,7 @@ static ssize_t lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode) { struct completion online_compl; + struct pci_dev *pdev = phba->pcidev; uint32_t reg_val; int status = 0; int rc; @@ -822,6 +823,14 @@ lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode) LPFC_SLI_INTF_IF_TYPE_2)) return -EPERM; + if (!pdev->is_physfn) + return -EPERM; + + /* Disable SR-IOV virtual functions if enabled */ + if (phba->cfg_sriov_nr_virtfn) { + pci_disable_sriov(pdev); + phba->cfg_sriov_nr_virtfn = 0; + } status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); if (status != 0) diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index f46378fb802c..6760c69f5253 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -42,6 +42,7 @@ #include "lpfc.h" #include "lpfc_logmsg.h" #include "lpfc_crtn.h" +#include "lpfc_debugfs.h" #include "lpfc_vport.h" #include "lpfc_version.h" @@ -1566,7 +1567,7 @@ lpfc_sli3_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job) uint32_t link_flags; uint32_t timeout; LPFC_MBOXQ_t *pmboxq; - int mbxstatus; + int mbxstatus = MBX_SUCCESS; int i = 0; int rc = 0; @@ -1740,7 +1741,7 @@ lpfc_sli4_bsg_diag_loopback_mode(struct lpfc_hba *phba, struct fc_bsg_job *job) uint32_t link_flags, timeout, req_len, alloc_len; struct lpfc_mbx_set_link_diag_loopback *link_diag_loopback; LPFC_MBOXQ_t *pmboxq = NULL; - int mbxstatus, i, rc = 0; + int mbxstatus = MBX_SUCCESS, i, rc = 0; /* no data to return just the return code */ job->reply->reply_payload_rcv_len = 0; @@ -3177,6 +3178,11 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) "(x%x/x%x) complete bsg job done, bsize:%d\n", phba->mbox_ext_buf_ctx.nembType, phba->mbox_ext_buf_ctx.mboxType, size); + lpfc_idiag_mbxacc_dump_bsg_mbox(phba, + phba->mbox_ext_buf_ctx.nembType, + phba->mbox_ext_buf_ctx.mboxType, + dma_ebuf, sta_pos_addr, + phba->mbox_ext_buf_ctx.mbx_dmabuf, 0); } else spin_unlock_irqrestore(&phba->ct_ev_lock, flags); @@ -3429,6 +3435,10 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job, "ext_buf_cnt:%d\n", ext_buf_cnt); } + /* before dma descriptor setup */ + lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_rd, dma_mbox, + sta_pre_addr, dmabuf, ext_buf_cnt); + /* reject non-embedded mailbox command with none external buffer */ if (ext_buf_cnt == 0) { rc = -EPERM; @@ -3476,6 +3486,10 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job, } } + /* after dma descriptor setup */ + lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_rd, dma_mbox, + sta_pos_addr, dmabuf, ext_buf_cnt); + /* construct base driver mbox command */ pmb = &pmboxq->u.mb; pmbx = (uint8_t *)dmabuf->virt; @@ -3590,12 +3604,20 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct fc_bsg_job *job, "ext_buf_cnt:%d\n", ext_buf_cnt); } + /* before dma buffer descriptor setup */ + lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_wr, dma_mbox, + sta_pre_addr, dmabuf, ext_buf_cnt); + if (ext_buf_cnt == 0) return -EPERM; /* for the first external buffer */ lpfc_bsg_sli_cfg_dma_desc_setup(phba, nemb_tp, 0, dmabuf, dmabuf); + /* after dma descriptor setup */ + lpfc_idiag_mbxacc_dump_bsg_mbox(phba, nemb_tp, mbox_wr, dma_mbox, + sta_pos_addr, dmabuf, ext_buf_cnt); + /* log for looking forward */ for (i = 1; i < ext_buf_cnt; i++) { if (nemb_tp == nemb_mse) @@ -3844,6 +3866,12 @@ lpfc_bsg_read_ebuf_get(struct lpfc_hba *phba, struct fc_bsg_job *job) dmabuf = list_first_entry(&phba->mbox_ext_buf_ctx.ext_dmabuf_list, struct lpfc_dmabuf, list); list_del_init(&dmabuf->list); + + /* after dma buffer descriptor setup */ + lpfc_idiag_mbxacc_dump_bsg_mbox(phba, phba->mbox_ext_buf_ctx.nembType, + mbox_rd, dma_ebuf, sta_pos_addr, + dmabuf, index); + pbuf = (uint8_t *)dmabuf->virt; job->reply->reply_payload_rcv_len = sg_copy_from_buffer(job->reply_payload.sg_list, @@ -3926,6 +3954,11 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job, dmabuf); list_add_tail(&dmabuf->list, &phba->mbox_ext_buf_ctx.ext_dmabuf_list); + /* after write dma buffer */ + lpfc_idiag_mbxacc_dump_bsg_mbox(phba, phba->mbox_ext_buf_ctx.nembType, + mbox_wr, dma_ebuf, sta_pos_addr, + dmabuf, index); + if (phba->mbox_ext_buf_ctx.seqNum == phba->mbox_ext_buf_ctx.numBuf) { lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "2968 SLI_CONFIG ext-buffer wr all %d " diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 1e41af8dea74..1f3cad0b1ce8 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -371,6 +371,10 @@ extern struct lpfc_hbq_init *lpfc_hbq_defs[]; /* SLI4 if_type 2 externs. */ int lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *); int lpfc_sli4_dealloc_resource_identifiers(struct lpfc_hba *); +int lpfc_sli4_get_allocated_extnts(struct lpfc_hba *, uint16_t, + uint16_t *, uint16_t *); +int lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *, uint16_t, + uint16_t *, uint16_t *); /* externs BlockGuard */ extern char *_dump_buf_data; @@ -437,6 +441,10 @@ void lpfc_cleanup_wt_rrqs(struct lpfc_hba *); void lpfc_cleanup_vports_rrqs(struct lpfc_vport *, struct lpfc_nodelist *); struct lpfc_node_rrq *lpfc_get_active_rrq(struct lpfc_vport *, uint16_t, uint32_t); +void lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *, enum nemb_type, + enum mbox_type, enum dma_type, enum sta_type, + struct lpfc_dmabuf *, uint32_t); +void lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *, MAILBOX_t *); int lpfc_wr_object(struct lpfc_hba *, struct list_head *, uint32_t, uint32_t *); /* functions to support SR-IOV */ int lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *, int); diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 30b25c5fdd7e..a0424dd90e40 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -48,6 +48,7 @@ #include "lpfc_version.h" #include "lpfc_compat.h" #include "lpfc_debugfs.h" +#include "lpfc_bsg.h" #ifdef CONFIG_SCSI_LPFC_DEBUG_FS /* @@ -135,7 +136,11 @@ lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size) int i, index, len, enable; uint32_t ms; struct lpfc_debugfs_trc *dtp; - char buffer[LPFC_DEBUG_TRC_ENTRY_SIZE]; + char *buffer; + + buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL); + if (!buffer) + return 0; enable = lpfc_debugfs_enable; lpfc_debugfs_enable = 0; @@ -167,6 +172,8 @@ lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size) } lpfc_debugfs_enable = enable; + kfree(buffer); + return len; } @@ -195,8 +202,11 @@ lpfc_debugfs_slow_ring_trc_data(struct lpfc_hba *phba, char *buf, int size) int i, index, len, enable; uint32_t ms; struct lpfc_debugfs_trc *dtp; - char buffer[LPFC_DEBUG_TRC_ENTRY_SIZE]; + char *buffer; + buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL); + if (!buffer) + return 0; enable = lpfc_debugfs_enable; lpfc_debugfs_enable = 0; @@ -228,6 +238,8 @@ lpfc_debugfs_slow_ring_trc_data(struct lpfc_hba *phba, char *buf, int size) } lpfc_debugfs_enable = enable; + kfree(buffer); + return len; } @@ -378,7 +390,11 @@ lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size) int len = 0; int i, off; uint32_t *ptr; - char buffer[1024]; + char *buffer; + + buffer = kmalloc(1024, GFP_KERNEL); + if (!buffer) + return 0; off = 0; spin_lock_irq(&phba->hbalock); @@ -407,6 +423,8 @@ lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size) } spin_unlock_irq(&phba->hbalock); + kfree(buffer); + return len; } @@ -1327,8 +1345,8 @@ lpfc_idiag_pcicfg_read(struct file *file, char __user *buf, size_t nbytes, return 0; if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_RD) { - where = idiag.cmd.data[0]; - count = idiag.cmd.data[1]; + where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX]; + count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX]; } else return 0; @@ -1373,6 +1391,11 @@ pcicfg_browse: len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len, "%08x ", u32val); offset += sizeof(uint32_t); + if (offset >= LPFC_PCI_CFG_SIZE) { + len += snprintf(pbuffer+len, + LPFC_PCI_CFG_SIZE-len, "\n"); + break; + } index -= sizeof(uint32_t); if (!index) len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len, @@ -1385,8 +1408,11 @@ pcicfg_browse: } /* Set up the offset for next portion of pci cfg read */ - idiag.offset.last_rd += LPFC_PCI_CFG_RD_SIZE; - if (idiag.offset.last_rd >= LPFC_PCI_CFG_SIZE) + if (index == 0) { + idiag.offset.last_rd += LPFC_PCI_CFG_RD_SIZE; + if (idiag.offset.last_rd >= LPFC_PCI_CFG_SIZE) + idiag.offset.last_rd = 0; + } else idiag.offset.last_rd = 0; return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len); @@ -1439,8 +1465,8 @@ lpfc_idiag_pcicfg_write(struct file *file, const char __user *buf, if (rc != LPFC_PCI_CFG_RD_CMD_ARG) goto error_out; /* Read command from PCI config space, set up command fields */ - where = idiag.cmd.data[0]; - count = idiag.cmd.data[1]; + where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX]; + count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX]; if (count == LPFC_PCI_CFG_BROWSE) { if (where % sizeof(uint32_t)) goto error_out; @@ -1475,9 +1501,9 @@ lpfc_idiag_pcicfg_write(struct file *file, const char __user *buf, if (rc != LPFC_PCI_CFG_WR_CMD_ARG) goto error_out; /* Write command to PCI config space, read-modify-write */ - where = idiag.cmd.data[0]; - count = idiag.cmd.data[1]; - value = idiag.cmd.data[2]; + where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX]; + count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX]; + value = idiag.cmd.data[IDIAG_PCICFG_VALUE_INDX]; /* Sanity checks */ if ((count != sizeof(uint8_t)) && (count != sizeof(uint16_t)) && @@ -1569,6 +1595,292 @@ error_out: return -EINVAL; } +/** + * lpfc_idiag_baracc_read - idiag debugfs pci bar access read + * @file: The file pointer to read from. + * @buf: The buffer to copy the data to. + * @nbytes: The number of bytes to read. + * @ppos: The position in the file to start reading from. + * + * Description: + * This routine reads data from the @phba pci bar memory mapped space + * according to the idiag command, and copies to user @buf. + * + * Returns: + * This function returns the amount of data that was read (this could be less + * than @nbytes if the end of the file was reached) or a negative error value. + **/ +static ssize_t +lpfc_idiag_baracc_read(struct file *file, char __user *buf, size_t nbytes, + loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; + int offset_label, offset, offset_run, len = 0, index; + int bar_num, acc_range, bar_size; + char *pbuffer; + void __iomem *mem_mapped_bar; + uint32_t if_type; + struct pci_dev *pdev; + uint32_t u32val; + + pdev = phba->pcidev; + if (!pdev) + return 0; + + /* This is a user read operation */ + debug->op = LPFC_IDIAG_OP_RD; + + if (!debug->buffer) + debug->buffer = kmalloc(LPFC_PCI_BAR_RD_BUF_SIZE, GFP_KERNEL); + if (!debug->buffer) + return 0; + pbuffer = debug->buffer; + + if (*ppos) + return 0; + + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_RD) { + bar_num = idiag.cmd.data[IDIAG_BARACC_BAR_NUM_INDX]; + offset = idiag.cmd.data[IDIAG_BARACC_OFF_SET_INDX]; + acc_range = idiag.cmd.data[IDIAG_BARACC_ACC_MOD_INDX]; + bar_size = idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX]; + } else + return 0; + + if (acc_range == 0) + return 0; + + if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf); + if (if_type == LPFC_SLI_INTF_IF_TYPE_0) { + if (bar_num == IDIAG_BARACC_BAR_0) + mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p; + else if (bar_num == IDIAG_BARACC_BAR_1) + mem_mapped_bar = phba->sli4_hba.ctrl_regs_memmap_p; + else if (bar_num == IDIAG_BARACC_BAR_2) + mem_mapped_bar = phba->sli4_hba.drbl_regs_memmap_p; + else + return 0; + } else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) { + if (bar_num == IDIAG_BARACC_BAR_0) + mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p; + else + return 0; + } else + return 0; + + /* Read single PCI bar space register */ + if (acc_range == SINGLE_WORD) { + offset_run = offset; + u32val = readl(mem_mapped_bar + offset_run); + len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len, + "%05x: %08x\n", offset_run, u32val); + } else + goto baracc_browse; + + return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len); + +baracc_browse: + + /* Browse all PCI bar space registers */ + offset_label = idiag.offset.last_rd; + offset_run = offset_label; + + /* Read PCI bar memory mapped space */ + len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len, + "%05x: ", offset_label); + index = LPFC_PCI_BAR_RD_SIZE; + while (index > 0) { + u32val = readl(mem_mapped_bar + offset_run); + len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len, + "%08x ", u32val); + offset_run += sizeof(uint32_t); + if (acc_range == LPFC_PCI_BAR_BROWSE) { + if (offset_run >= bar_size) { + len += snprintf(pbuffer+len, + LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n"); + break; + } + } else { + if (offset_run >= offset + + (acc_range * sizeof(uint32_t))) { + len += snprintf(pbuffer+len, + LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n"); + break; + } + } + index -= sizeof(uint32_t); + if (!index) + len += snprintf(pbuffer+len, + LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n"); + else if (!(index % (8 * sizeof(uint32_t)))) { + offset_label += (8 * sizeof(uint32_t)); + len += snprintf(pbuffer+len, + LPFC_PCI_BAR_RD_BUF_SIZE-len, + "\n%05x: ", offset_label); + } + } + + /* Set up the offset for next portion of pci bar read */ + if (index == 0) { + idiag.offset.last_rd += LPFC_PCI_BAR_RD_SIZE; + if (acc_range == LPFC_PCI_BAR_BROWSE) { + if (idiag.offset.last_rd >= bar_size) + idiag.offset.last_rd = 0; + } else { + if (offset_run >= offset + + (acc_range * sizeof(uint32_t))) + idiag.offset.last_rd = offset; + } + } else { + if (acc_range == LPFC_PCI_BAR_BROWSE) + idiag.offset.last_rd = 0; + else + idiag.offset.last_rd = offset; + } + + return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len); +} + +/** + * lpfc_idiag_baracc_write - Syntax check and set up idiag bar access commands + * @file: The file pointer to read from. + * @buf: The buffer to copy the user data from. + * @nbytes: The number of bytes to get. + * @ppos: The position in the file to start reading from. + * + * This routine get the debugfs idiag command struct from user space and + * then perform the syntax check for PCI bar memory mapped space read or + * write command accordingly. In the case of PCI bar memory mapped space + * read command, it sets up the command in the idiag command struct for + * the debugfs read operation. In the case of PCI bar memorpy mapped space + * write operation, it executes the write operation into the PCI bar memory + * mapped space accordingly. + * + * It returns the @nbytges passing in from debugfs user space when successful. + * In case of error conditions, it returns proper error code back to the user + * space. + */ +static ssize_t +lpfc_idiag_baracc_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; + uint32_t bar_num, bar_size, offset, value, acc_range; + struct pci_dev *pdev; + void __iomem *mem_mapped_bar; + uint32_t if_type; + uint32_t u32val; + int rc; + + pdev = phba->pcidev; + if (!pdev) + return -EFAULT; + + /* This is a user write operation */ + debug->op = LPFC_IDIAG_OP_WR; + + rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd); + if (rc < 0) + return rc; + + if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf); + bar_num = idiag.cmd.data[IDIAG_BARACC_BAR_NUM_INDX]; + + if (if_type == LPFC_SLI_INTF_IF_TYPE_0) { + if ((bar_num != IDIAG_BARACC_BAR_0) && + (bar_num != IDIAG_BARACC_BAR_1) && + (bar_num != IDIAG_BARACC_BAR_2)) + goto error_out; + } else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) { + if (bar_num != IDIAG_BARACC_BAR_0) + goto error_out; + } else + goto error_out; + + if (if_type == LPFC_SLI_INTF_IF_TYPE_0) { + if (bar_num == IDIAG_BARACC_BAR_0) { + idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] = + LPFC_PCI_IF0_BAR0_SIZE; + mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p; + } else if (bar_num == IDIAG_BARACC_BAR_1) { + idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] = + LPFC_PCI_IF0_BAR1_SIZE; + mem_mapped_bar = phba->sli4_hba.ctrl_regs_memmap_p; + } else if (bar_num == IDIAG_BARACC_BAR_2) { + idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] = + LPFC_PCI_IF0_BAR2_SIZE; + mem_mapped_bar = phba->sli4_hba.drbl_regs_memmap_p; + } else + goto error_out; + } else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) { + if (bar_num == IDIAG_BARACC_BAR_0) { + idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] = + LPFC_PCI_IF2_BAR0_SIZE; + mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p; + } else + goto error_out; + } else + goto error_out; + + offset = idiag.cmd.data[IDIAG_BARACC_OFF_SET_INDX]; + if (offset % sizeof(uint32_t)) + goto error_out; + + bar_size = idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX]; + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_RD) { + /* Sanity check on PCI config read command line arguments */ + if (rc != LPFC_PCI_BAR_RD_CMD_ARG) + goto error_out; + acc_range = idiag.cmd.data[IDIAG_BARACC_ACC_MOD_INDX]; + if (acc_range == LPFC_PCI_BAR_BROWSE) { + if (offset > bar_size - sizeof(uint32_t)) + goto error_out; + /* Starting offset to browse */ + idiag.offset.last_rd = offset; + } else if (acc_range > SINGLE_WORD) { + if (offset + acc_range * sizeof(uint32_t) > bar_size) + goto error_out; + /* Starting offset to browse */ + idiag.offset.last_rd = offset; + } else if (acc_range != SINGLE_WORD) + goto error_out; + } else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_WR || + idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_ST || + idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_CL) { + /* Sanity check on PCI bar write command line arguments */ + if (rc != LPFC_PCI_BAR_WR_CMD_ARG) + goto error_out; + /* Write command to PCI bar space, read-modify-write */ + acc_range = SINGLE_WORD; + value = idiag.cmd.data[IDIAG_BARACC_REG_VAL_INDX]; + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_WR) { + writel(value, mem_mapped_bar + offset); + readl(mem_mapped_bar + offset); + } + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_ST) { + u32val = readl(mem_mapped_bar + offset); + u32val |= value; + writel(u32val, mem_mapped_bar + offset); + readl(mem_mapped_bar + offset); + } + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_CL) { + u32val = readl(mem_mapped_bar + offset); + u32val &= ~value; + writel(u32val, mem_mapped_bar + offset); + readl(mem_mapped_bar + offset); + } + } else + /* All other opecodes are illegal for now */ + goto error_out; + + return nbytes; +error_out: + memset(&idiag, 0, sizeof(idiag)); + return -EINVAL; +} + /** * lpfc_idiag_queinfo_read - idiag debugfs read queue information * @file: The file pointer to read from. @@ -1871,8 +2183,8 @@ lpfc_idiag_queacc_read(struct file *file, char __user *buf, size_t nbytes, return 0; if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_RD) { - index = idiag.cmd.data[2]; - count = idiag.cmd.data[3]; + index = idiag.cmd.data[IDIAG_QUEACC_INDEX_INDX]; + count = idiag.cmd.data[IDIAG_QUEACC_COUNT_INDX]; pque = (struct lpfc_queue *)idiag.ptr_private; } else return 0; @@ -1944,12 +2256,12 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, return rc; /* Get and sanity check on command feilds */ - quetp = idiag.cmd.data[0]; - queid = idiag.cmd.data[1]; - index = idiag.cmd.data[2]; - count = idiag.cmd.data[3]; - offset = idiag.cmd.data[4]; - value = idiag.cmd.data[5]; + quetp = idiag.cmd.data[IDIAG_QUEACC_QUETP_INDX]; + queid = idiag.cmd.data[IDIAG_QUEACC_QUEID_INDX]; + index = idiag.cmd.data[IDIAG_QUEACC_INDEX_INDX]; + count = idiag.cmd.data[IDIAG_QUEACC_COUNT_INDX]; + offset = idiag.cmd.data[IDIAG_QUEACC_OFFST_INDX]; + value = idiag.cmd.data[IDIAG_QUEACC_VALUE_INDX]; /* Sanity check on command line arguments */ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR || @@ -2218,7 +2530,7 @@ lpfc_idiag_drbacc_read(struct file *file, char __user *buf, size_t nbytes, return 0; if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_RD) - drb_reg_id = idiag.cmd.data[0]; + drb_reg_id = idiag.cmd.data[IDIAG_DRBACC_REGID_INDX]; else return 0; @@ -2257,7 +2569,7 @@ lpfc_idiag_drbacc_write(struct file *file, const char __user *buf, { struct lpfc_debug *debug = file->private_data; struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; - uint32_t drb_reg_id, value, reg_val; + uint32_t drb_reg_id, value, reg_val = 0; void __iomem *drb_reg; int rc; @@ -2269,8 +2581,8 @@ lpfc_idiag_drbacc_write(struct file *file, const char __user *buf, return rc; /* Sanity check on command line arguments */ - drb_reg_id = idiag.cmd.data[0]; - value = idiag.cmd.data[1]; + drb_reg_id = idiag.cmd.data[IDIAG_DRBACC_REGID_INDX]; + value = idiag.cmd.data[IDIAG_DRBACC_VALUE_INDX]; if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_WR || idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST || @@ -2330,63 +2642,736 @@ error_out: return -EINVAL; } -#undef lpfc_debugfs_op_disc_trc -static const struct file_operations lpfc_debugfs_op_disc_trc = { - .owner = THIS_MODULE, - .open = lpfc_debugfs_disc_trc_open, - .llseek = lpfc_debugfs_lseek, - .read = lpfc_debugfs_read, - .release = lpfc_debugfs_release, -}; +/** + * lpfc_idiag_ctlacc_read_reg - idiag debugfs read a control registers + * @phba: The pointer to hba structure. + * @pbuffer: The pointer to the buffer to copy the data to. + * @len: The lenght of bytes to copied. + * @drbregid: The id to doorbell registers. + * + * Description: + * This routine reads a control register and copies its content to the + * user buffer pointed to by @pbuffer. + * + * Returns: + * This function returns the amount of data that was copied into @pbuffer. + **/ +static int +lpfc_idiag_ctlacc_read_reg(struct lpfc_hba *phba, char *pbuffer, + int len, uint32_t ctlregid) +{ -#undef lpfc_debugfs_op_nodelist -static const struct file_operations lpfc_debugfs_op_nodelist = { - .owner = THIS_MODULE, - .open = lpfc_debugfs_nodelist_open, - .llseek = lpfc_debugfs_lseek, - .read = lpfc_debugfs_read, - .release = lpfc_debugfs_release, -}; + if (!pbuffer) + return 0; -#undef lpfc_debugfs_op_hbqinfo -static const struct file_operations lpfc_debugfs_op_hbqinfo = { - .owner = THIS_MODULE, - .open = lpfc_debugfs_hbqinfo_open, - .llseek = lpfc_debugfs_lseek, - .read = lpfc_debugfs_read, - .release = lpfc_debugfs_release, -}; + switch (ctlregid) { + case LPFC_CTL_PORT_SEM: + len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len, + "Port SemReg: 0x%08x\n", + readl(phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PORT_SEM_OFFSET)); + break; + case LPFC_CTL_PORT_STA: + len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len, + "Port StaReg: 0x%08x\n", + readl(phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PORT_STA_OFFSET)); + break; + case LPFC_CTL_PORT_CTL: + len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len, + "Port CtlReg: 0x%08x\n", + readl(phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PORT_CTL_OFFSET)); + break; + case LPFC_CTL_PORT_ER1: + len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len, + "Port Er1Reg: 0x%08x\n", + readl(phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PORT_ER1_OFFSET)); + break; + case LPFC_CTL_PORT_ER2: + len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len, + "Port Er2Reg: 0x%08x\n", + readl(phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PORT_ER2_OFFSET)); + break; + case LPFC_CTL_PDEV_CTL: + len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len, + "PDev CtlReg: 0x%08x\n", + readl(phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PDEV_CTL_OFFSET)); + break; + default: + break; + } + return len; +} -#undef lpfc_debugfs_op_dumpHBASlim -static const struct file_operations lpfc_debugfs_op_dumpHBASlim = { - .owner = THIS_MODULE, - .open = lpfc_debugfs_dumpHBASlim_open, - .llseek = lpfc_debugfs_lseek, - .read = lpfc_debugfs_read, - .release = lpfc_debugfs_release, -}; +/** + * lpfc_idiag_ctlacc_read - idiag debugfs read port and device control register + * @file: The file pointer to read from. + * @buf: The buffer to copy the data to. + * @nbytes: The number of bytes to read. + * @ppos: The position in the file to start reading from. + * + * Description: + * This routine reads data from the @phba port and device registers according + * to the idiag command, and copies to user @buf. + * + * Returns: + * This function returns the amount of data that was read (this could be less + * than @nbytes if the end of the file was reached) or a negative error value. + **/ +static ssize_t +lpfc_idiag_ctlacc_read(struct file *file, char __user *buf, size_t nbytes, + loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; + uint32_t ctl_reg_id, i; + char *pbuffer; + int len = 0; -#undef lpfc_debugfs_op_dumpHostSlim -static const struct file_operations lpfc_debugfs_op_dumpHostSlim = { - .owner = THIS_MODULE, - .open = lpfc_debugfs_dumpHostSlim_open, - .llseek = lpfc_debugfs_lseek, - .read = lpfc_debugfs_read, - .release = lpfc_debugfs_release, -}; + /* This is a user read operation */ + debug->op = LPFC_IDIAG_OP_RD; -#undef lpfc_debugfs_op_dumpData -static const struct file_operations lpfc_debugfs_op_dumpData = { - .owner = THIS_MODULE, - .open = lpfc_debugfs_dumpData_open, - .llseek = lpfc_debugfs_lseek, - .read = lpfc_debugfs_read, - .write = lpfc_debugfs_dumpDataDif_write, - .release = lpfc_debugfs_dumpDataDif_release, -}; + if (!debug->buffer) + debug->buffer = kmalloc(LPFC_CTL_ACC_BUF_SIZE, GFP_KERNEL); + if (!debug->buffer) + return 0; + pbuffer = debug->buffer; -#undef lpfc_debugfs_op_dumpDif -static const struct file_operations lpfc_debugfs_op_dumpDif = { + if (*ppos) + return 0; + + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_RD) + ctl_reg_id = idiag.cmd.data[IDIAG_CTLACC_REGID_INDX]; + else + return 0; + + if (ctl_reg_id == LPFC_CTL_ACC_ALL) + for (i = 1; i <= LPFC_CTL_MAX; i++) + len = lpfc_idiag_ctlacc_read_reg(phba, + pbuffer, len, i); + else + len = lpfc_idiag_ctlacc_read_reg(phba, + pbuffer, len, ctl_reg_id); + + return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len); +} + +/** + * lpfc_idiag_ctlacc_write - Syntax check and set up idiag ctlacc commands + * @file: The file pointer to read from. + * @buf: The buffer to copy the user data from. + * @nbytes: The number of bytes to get. + * @ppos: The position in the file to start reading from. + * + * This routine get the debugfs idiag command struct from user space and then + * perform the syntax check for port and device control register read (dump) + * or write (set) command accordingly. + * + * It returns the @nbytges passing in from debugfs user space when successful. + * In case of error conditions, it returns proper error code back to the user + * space. + **/ +static ssize_t +lpfc_idiag_ctlacc_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; + uint32_t ctl_reg_id, value, reg_val = 0; + void __iomem *ctl_reg; + int rc; + + /* This is a user write operation */ + debug->op = LPFC_IDIAG_OP_WR; + + rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd); + if (rc < 0) + return rc; + + /* Sanity check on command line arguments */ + ctl_reg_id = idiag.cmd.data[IDIAG_CTLACC_REGID_INDX]; + value = idiag.cmd.data[IDIAG_CTLACC_VALUE_INDX]; + + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR || + idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST || + idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) { + if (rc != LPFC_CTL_ACC_WR_CMD_ARG) + goto error_out; + if (ctl_reg_id > LPFC_CTL_MAX) + goto error_out; + } else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_RD) { + if (rc != LPFC_CTL_ACC_RD_CMD_ARG) + goto error_out; + if ((ctl_reg_id > LPFC_CTL_MAX) && + (ctl_reg_id != LPFC_CTL_ACC_ALL)) + goto error_out; + } else + goto error_out; + + /* Perform the write access operation */ + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR || + idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST || + idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) { + switch (ctl_reg_id) { + case LPFC_CTL_PORT_SEM: + ctl_reg = phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PORT_SEM_OFFSET; + break; + case LPFC_CTL_PORT_STA: + ctl_reg = phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PORT_STA_OFFSET; + break; + case LPFC_CTL_PORT_CTL: + ctl_reg = phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PORT_CTL_OFFSET; + break; + case LPFC_CTL_PORT_ER1: + ctl_reg = phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PORT_ER1_OFFSET; + break; + case LPFC_CTL_PORT_ER2: + ctl_reg = phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PORT_ER2_OFFSET; + break; + case LPFC_CTL_PDEV_CTL: + ctl_reg = phba->sli4_hba.conf_regs_memmap_p + + LPFC_CTL_PDEV_CTL_OFFSET; + break; + default: + goto error_out; + } + + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR) + reg_val = value; + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST) { + reg_val = readl(ctl_reg); + reg_val |= value; + } + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) { + reg_val = readl(ctl_reg); + reg_val &= ~value; + } + writel(reg_val, ctl_reg); + readl(ctl_reg); /* flush */ + } + return nbytes; + +error_out: + /* Clean out command structure on command error out */ + memset(&idiag, 0, sizeof(idiag)); + return -EINVAL; +} + +/** + * lpfc_idiag_mbxacc_get_setup - idiag debugfs get mailbox access setup + * @phba: Pointer to HBA context object. + * @pbuffer: Pointer to data buffer. + * + * Description: + * This routine gets the driver mailbox access debugfs setup information. + * + * Returns: + * This function returns the amount of data that was read (this could be less + * than @nbytes if the end of the file was reached) or a negative error value. + **/ +static int +lpfc_idiag_mbxacc_get_setup(struct lpfc_hba *phba, char *pbuffer) +{ + uint32_t mbx_dump_map, mbx_dump_cnt, mbx_word_cnt, mbx_mbox_cmd; + int len = 0; + + mbx_mbox_cmd = idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX]; + mbx_dump_map = idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX]; + mbx_dump_cnt = idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX]; + mbx_word_cnt = idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX]; + + len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len, + "mbx_dump_map: 0x%08x\n", mbx_dump_map); + len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len, + "mbx_dump_cnt: %04d\n", mbx_dump_cnt); + len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len, + "mbx_word_cnt: %04d\n", mbx_word_cnt); + len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len, + "mbx_mbox_cmd: 0x%02x\n", mbx_mbox_cmd); + + return len; +} + +/** + * lpfc_idiag_mbxacc_read - idiag debugfs read on mailbox access + * @file: The file pointer to read from. + * @buf: The buffer to copy the data to. + * @nbytes: The number of bytes to read. + * @ppos: The position in the file to start reading from. + * + * Description: + * This routine reads data from the @phba driver mailbox access debugfs setup + * information. + * + * Returns: + * This function returns the amount of data that was read (this could be less + * than @nbytes if the end of the file was reached) or a negative error value. + **/ +static ssize_t +lpfc_idiag_mbxacc_read(struct file *file, char __user *buf, size_t nbytes, + loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; + char *pbuffer; + int len = 0; + + /* This is a user read operation */ + debug->op = LPFC_IDIAG_OP_RD; + + if (!debug->buffer) + debug->buffer = kmalloc(LPFC_MBX_ACC_BUF_SIZE, GFP_KERNEL); + if (!debug->buffer) + return 0; + pbuffer = debug->buffer; + + if (*ppos) + return 0; + + if ((idiag.cmd.opcode != LPFC_IDIAG_CMD_MBXACC_DP) && + (idiag.cmd.opcode != LPFC_IDIAG_BSG_MBXACC_DP)) + return 0; + + len = lpfc_idiag_mbxacc_get_setup(phba, pbuffer); + + return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len); +} + +/** + * lpfc_idiag_mbxacc_write - Syntax check and set up idiag mbxacc commands + * @file: The file pointer to read from. + * @buf: The buffer to copy the user data from. + * @nbytes: The number of bytes to get. + * @ppos: The position in the file to start reading from. + * + * This routine get the debugfs idiag command struct from user space and then + * perform the syntax check for driver mailbox command (dump) and sets up the + * necessary states in the idiag command struct accordingly. + * + * It returns the @nbytges passing in from debugfs user space when successful. + * In case of error conditions, it returns proper error code back to the user + * space. + **/ +static ssize_t +lpfc_idiag_mbxacc_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + uint32_t mbx_dump_map, mbx_dump_cnt, mbx_word_cnt, mbx_mbox_cmd; + int rc; + + /* This is a user write operation */ + debug->op = LPFC_IDIAG_OP_WR; + + rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd); + if (rc < 0) + return rc; + + /* Sanity check on command line arguments */ + mbx_mbox_cmd = idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX]; + mbx_dump_map = idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX]; + mbx_dump_cnt = idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX]; + mbx_word_cnt = idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX]; + + if (idiag.cmd.opcode == LPFC_IDIAG_CMD_MBXACC_DP) { + if (!(mbx_dump_map & LPFC_MBX_DMP_MBX_ALL)) + goto error_out; + if ((mbx_dump_map & ~LPFC_MBX_DMP_MBX_ALL) && + (mbx_dump_map != LPFC_MBX_DMP_ALL)) + goto error_out; + if (mbx_word_cnt > sizeof(MAILBOX_t)) + goto error_out; + } else if (idiag.cmd.opcode == LPFC_IDIAG_BSG_MBXACC_DP) { + if (!(mbx_dump_map & LPFC_BSG_DMP_MBX_ALL)) + goto error_out; + if ((mbx_dump_map & ~LPFC_BSG_DMP_MBX_ALL) && + (mbx_dump_map != LPFC_MBX_DMP_ALL)) + goto error_out; + if (mbx_word_cnt > (BSG_MBOX_SIZE)/4) + goto error_out; + if (mbx_mbox_cmd != 0x9b) + goto error_out; + } else + goto error_out; + + if (mbx_word_cnt == 0) + goto error_out; + if (rc != LPFC_MBX_DMP_ARG) + goto error_out; + if (mbx_mbox_cmd & ~0xff) + goto error_out; + + /* condition for stop mailbox dump */ + if (mbx_dump_cnt == 0) + goto reset_out; + + return nbytes; + +reset_out: + /* Clean out command structure on command error out */ + memset(&idiag, 0, sizeof(idiag)); + return nbytes; + +error_out: + /* Clean out command structure on command error out */ + memset(&idiag, 0, sizeof(idiag)); + return -EINVAL; +} + +/** + * lpfc_idiag_extacc_avail_get - get the available extents information + * @phba: pointer to lpfc hba data structure. + * @pbuffer: pointer to internal buffer. + * @len: length into the internal buffer data has been copied. + * + * Description: + * This routine is to get the available extent information. + * + * Returns: + * overall lenth of the data read into the internal buffer. + **/ +static int +lpfc_idiag_extacc_avail_get(struct lpfc_hba *phba, char *pbuffer, int len) +{ + uint16_t ext_cnt, ext_size; + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\nAvailable Extents Information:\n"); + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tPort Available VPI extents: "); + lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_VPI, + &ext_cnt, &ext_size); + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "Count %3d, Size %3d\n", ext_cnt, ext_size); + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tPort Available VFI extents: "); + lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_VFI, + &ext_cnt, &ext_size); + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "Count %3d, Size %3d\n", ext_cnt, ext_size); + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tPort Available RPI extents: "); + lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_RPI, + &ext_cnt, &ext_size); + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "Count %3d, Size %3d\n", ext_cnt, ext_size); + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tPort Available XRI extents: "); + lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_XRI, + &ext_cnt, &ext_size); + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "Count %3d, Size %3d\n", ext_cnt, ext_size); + + return len; +} + +/** + * lpfc_idiag_extacc_alloc_get - get the allocated extents information + * @phba: pointer to lpfc hba data structure. + * @pbuffer: pointer to internal buffer. + * @len: length into the internal buffer data has been copied. + * + * Description: + * This routine is to get the allocated extent information. + * + * Returns: + * overall lenth of the data read into the internal buffer. + **/ +static int +lpfc_idiag_extacc_alloc_get(struct lpfc_hba *phba, char *pbuffer, int len) +{ + uint16_t ext_cnt, ext_size; + int rc; + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\nAllocated Extents Information:\n"); + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tHost Allocated VPI extents: "); + rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_VPI, + &ext_cnt, &ext_size); + if (!rc) + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "Port %d Extent %3d, Size %3d\n", + phba->brd_no, ext_cnt, ext_size); + else + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "N/A\n"); + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tHost Allocated VFI extents: "); + rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_VFI, + &ext_cnt, &ext_size); + if (!rc) + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "Port %d Extent %3d, Size %3d\n", + phba->brd_no, ext_cnt, ext_size); + else + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "N/A\n"); + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tHost Allocated RPI extents: "); + rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_RPI, + &ext_cnt, &ext_size); + if (!rc) + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "Port %d Extent %3d, Size %3d\n", + phba->brd_no, ext_cnt, ext_size); + else + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "N/A\n"); + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tHost Allocated XRI extents: "); + rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_XRI, + &ext_cnt, &ext_size); + if (!rc) + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "Port %d Extent %3d, Size %3d\n", + phba->brd_no, ext_cnt, ext_size); + else + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "N/A\n"); + + return len; +} + +/** + * lpfc_idiag_extacc_drivr_get - get driver extent information + * @phba: pointer to lpfc hba data structure. + * @pbuffer: pointer to internal buffer. + * @len: length into the internal buffer data has been copied. + * + * Description: + * This routine is to get the driver extent information. + * + * Returns: + * overall lenth of the data read into the internal buffer. + **/ +static int +lpfc_idiag_extacc_drivr_get(struct lpfc_hba *phba, char *pbuffer, int len) +{ + struct lpfc_rsrc_blks *rsrc_blks; + int index; + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\nDriver Extents Information:\n"); + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tVPI extents:\n"); + index = 0; + list_for_each_entry(rsrc_blks, &phba->lpfc_vpi_blk_list, list) { + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\t\tBlock %3d: Start %4d, Count %4d\n", + index, rsrc_blks->rsrc_start, + rsrc_blks->rsrc_size); + index++; + } + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tVFI extents:\n"); + index = 0; + list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_vfi_blk_list, + list) { + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\t\tBlock %3d: Start %4d, Count %4d\n", + index, rsrc_blks->rsrc_start, + rsrc_blks->rsrc_size); + index++; + } + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tRPI extents:\n"); + index = 0; + list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_rpi_blk_list, + list) { + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\t\tBlock %3d: Start %4d, Count %4d\n", + index, rsrc_blks->rsrc_start, + rsrc_blks->rsrc_size); + index++; + } + + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\tXRI extents:\n"); + index = 0; + list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_xri_blk_list, + list) { + len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len, + "\t\tBlock %3d: Start %4d, Count %4d\n", + index, rsrc_blks->rsrc_start, + rsrc_blks->rsrc_size); + index++; + } + + return len; +} + +/** + * lpfc_idiag_extacc_write - Syntax check and set up idiag extacc commands + * @file: The file pointer to read from. + * @buf: The buffer to copy the user data from. + * @nbytes: The number of bytes to get. + * @ppos: The position in the file to start reading from. + * + * This routine get the debugfs idiag command struct from user space and then + * perform the syntax check for extent information access commands and sets + * up the necessary states in the idiag command struct accordingly. + * + * It returns the @nbytges passing in from debugfs user space when successful. + * In case of error conditions, it returns proper error code back to the user + * space. + **/ +static ssize_t +lpfc_idiag_extacc_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + uint32_t ext_map; + int rc; + + /* This is a user write operation */ + debug->op = LPFC_IDIAG_OP_WR; + + rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd); + if (rc < 0) + return rc; + + ext_map = idiag.cmd.data[IDIAG_EXTACC_EXMAP_INDX]; + + if (idiag.cmd.opcode != LPFC_IDIAG_CMD_EXTACC_RD) + goto error_out; + if (rc != LPFC_EXT_ACC_CMD_ARG) + goto error_out; + if (!(ext_map & LPFC_EXT_ACC_ALL)) + goto error_out; + + return nbytes; +error_out: + /* Clean out command structure on command error out */ + memset(&idiag, 0, sizeof(idiag)); + return -EINVAL; +} + +/** + * lpfc_idiag_extacc_read - idiag debugfs read access to extent information + * @file: The file pointer to read from. + * @buf: The buffer to copy the data to. + * @nbytes: The number of bytes to read. + * @ppos: The position in the file to start reading from. + * + * Description: + * This routine reads data from the proper extent information according to + * the idiag command, and copies to user @buf. + * + * Returns: + * This function returns the amount of data that was read (this could be less + * than @nbytes if the end of the file was reached) or a negative error value. + **/ +static ssize_t +lpfc_idiag_extacc_read(struct file *file, char __user *buf, size_t nbytes, + loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; + char *pbuffer; + uint32_t ext_map; + int len = 0; + + /* This is a user read operation */ + debug->op = LPFC_IDIAG_OP_RD; + + if (!debug->buffer) + debug->buffer = kmalloc(LPFC_EXT_ACC_BUF_SIZE, GFP_KERNEL); + if (!debug->buffer) + return 0; + pbuffer = debug->buffer; + if (*ppos) + return 0; + if (idiag.cmd.opcode != LPFC_IDIAG_CMD_EXTACC_RD) + return 0; + + ext_map = idiag.cmd.data[IDIAG_EXTACC_EXMAP_INDX]; + if (ext_map & LPFC_EXT_ACC_AVAIL) + len = lpfc_idiag_extacc_avail_get(phba, pbuffer, len); + if (ext_map & LPFC_EXT_ACC_ALLOC) + len = lpfc_idiag_extacc_alloc_get(phba, pbuffer, len); + if (ext_map & LPFC_EXT_ACC_DRIVR) + len = lpfc_idiag_extacc_drivr_get(phba, pbuffer, len); + + return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len); +} + +#undef lpfc_debugfs_op_disc_trc +static const struct file_operations lpfc_debugfs_op_disc_trc = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_disc_trc_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .release = lpfc_debugfs_release, +}; + +#undef lpfc_debugfs_op_nodelist +static const struct file_operations lpfc_debugfs_op_nodelist = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_nodelist_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .release = lpfc_debugfs_release, +}; + +#undef lpfc_debugfs_op_hbqinfo +static const struct file_operations lpfc_debugfs_op_hbqinfo = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_hbqinfo_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .release = lpfc_debugfs_release, +}; + +#undef lpfc_debugfs_op_dumpHBASlim +static const struct file_operations lpfc_debugfs_op_dumpHBASlim = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_dumpHBASlim_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .release = lpfc_debugfs_release, +}; + +#undef lpfc_debugfs_op_dumpHostSlim +static const struct file_operations lpfc_debugfs_op_dumpHostSlim = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_dumpHostSlim_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .release = lpfc_debugfs_release, +}; + +#undef lpfc_debugfs_op_dumpData +static const struct file_operations lpfc_debugfs_op_dumpData = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_dumpData_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .write = lpfc_debugfs_dumpDataDif_write, + .release = lpfc_debugfs_dumpDataDif_release, +}; + +#undef lpfc_debugfs_op_dumpDif +static const struct file_operations lpfc_debugfs_op_dumpDif = { .owner = THIS_MODULE, .open = lpfc_debugfs_dumpDif_open, .llseek = lpfc_debugfs_lseek, @@ -2420,6 +3405,16 @@ static const struct file_operations lpfc_idiag_op_pciCfg = { .release = lpfc_idiag_cmd_release, }; +#undef lpfc_idiag_op_barAcc +static const struct file_operations lpfc_idiag_op_barAcc = { + .owner = THIS_MODULE, + .open = lpfc_idiag_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_idiag_baracc_read, + .write = lpfc_idiag_baracc_write, + .release = lpfc_idiag_cmd_release, +}; + #undef lpfc_idiag_op_queInfo static const struct file_operations lpfc_idiag_op_queInfo = { .owner = THIS_MODULE, @@ -2428,7 +3423,7 @@ static const struct file_operations lpfc_idiag_op_queInfo = { .release = lpfc_idiag_release, }; -#undef lpfc_idiag_op_queacc +#undef lpfc_idiag_op_queAcc static const struct file_operations lpfc_idiag_op_queAcc = { .owner = THIS_MODULE, .open = lpfc_idiag_open, @@ -2438,7 +3433,7 @@ static const struct file_operations lpfc_idiag_op_queAcc = { .release = lpfc_idiag_cmd_release, }; -#undef lpfc_idiag_op_drbacc +#undef lpfc_idiag_op_drbAcc static const struct file_operations lpfc_idiag_op_drbAcc = { .owner = THIS_MODULE, .open = lpfc_idiag_open, @@ -2448,8 +3443,234 @@ static const struct file_operations lpfc_idiag_op_drbAcc = { .release = lpfc_idiag_cmd_release, }; +#undef lpfc_idiag_op_ctlAcc +static const struct file_operations lpfc_idiag_op_ctlAcc = { + .owner = THIS_MODULE, + .open = lpfc_idiag_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_idiag_ctlacc_read, + .write = lpfc_idiag_ctlacc_write, + .release = lpfc_idiag_cmd_release, +}; + +#undef lpfc_idiag_op_mbxAcc +static const struct file_operations lpfc_idiag_op_mbxAcc = { + .owner = THIS_MODULE, + .open = lpfc_idiag_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_idiag_mbxacc_read, + .write = lpfc_idiag_mbxacc_write, + .release = lpfc_idiag_cmd_release, +}; + +#undef lpfc_idiag_op_extAcc +static const struct file_operations lpfc_idiag_op_extAcc = { + .owner = THIS_MODULE, + .open = lpfc_idiag_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_idiag_extacc_read, + .write = lpfc_idiag_extacc_write, + .release = lpfc_idiag_cmd_release, +}; + #endif +/* lpfc_idiag_mbxacc_dump_bsg_mbox - idiag debugfs dump bsg mailbox command + * @phba: Pointer to HBA context object. + * @dmabuf: Pointer to a DMA buffer descriptor. + * + * Description: + * This routine dump a bsg pass-through non-embedded mailbox command with + * external buffer. + **/ +void +lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp, + enum mbox_type mbox_tp, enum dma_type dma_tp, + enum sta_type sta_tp, + struct lpfc_dmabuf *dmabuf, uint32_t ext_buf) +{ +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + uint32_t *mbx_mbox_cmd, *mbx_dump_map, *mbx_dump_cnt, *mbx_word_cnt; + char line_buf[LPFC_MBX_ACC_LBUF_SZ]; + int len = 0; + uint32_t do_dump = 0; + uint32_t *pword; + uint32_t i; + + if (idiag.cmd.opcode != LPFC_IDIAG_BSG_MBXACC_DP) + return; + + mbx_mbox_cmd = &idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX]; + mbx_dump_map = &idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX]; + mbx_dump_cnt = &idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX]; + mbx_word_cnt = &idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX]; + + if (!(*mbx_dump_map & LPFC_MBX_DMP_ALL) || + (*mbx_dump_cnt == 0) || + (*mbx_word_cnt == 0)) + return; + + if (*mbx_mbox_cmd != 0x9B) + return; + + if ((mbox_tp == mbox_rd) && (dma_tp == dma_mbox)) { + if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_MBX) { + do_dump |= LPFC_BSG_DMP_MBX_RD_MBX; + printk(KERN_ERR "\nRead mbox command (x%x), " + "nemb:0x%x, extbuf_cnt:%d:\n", + sta_tp, nemb_tp, ext_buf); + } + } + if ((mbox_tp == mbox_rd) && (dma_tp == dma_ebuf)) { + if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_BUF) { + do_dump |= LPFC_BSG_DMP_MBX_RD_BUF; + printk(KERN_ERR "\nRead mbox buffer (x%x), " + "nemb:0x%x, extbuf_seq:%d:\n", + sta_tp, nemb_tp, ext_buf); + } + } + if ((mbox_tp == mbox_wr) && (dma_tp == dma_mbox)) { + if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_MBX) { + do_dump |= LPFC_BSG_DMP_MBX_WR_MBX; + printk(KERN_ERR "\nWrite mbox command (x%x), " + "nemb:0x%x, extbuf_cnt:%d:\n", + sta_tp, nemb_tp, ext_buf); + } + } + if ((mbox_tp == mbox_wr) && (dma_tp == dma_ebuf)) { + if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_BUF) { + do_dump |= LPFC_BSG_DMP_MBX_WR_BUF; + printk(KERN_ERR "\nWrite mbox buffer (x%x), " + "nemb:0x%x, extbuf_seq:%d:\n", + sta_tp, nemb_tp, ext_buf); + } + } + + /* dump buffer content */ + if (do_dump) { + pword = (uint32_t *)dmabuf->virt; + for (i = 0; i < *mbx_word_cnt; i++) { + if (!(i % 8)) { + if (i != 0) + printk(KERN_ERR "%s\n", line_buf); + len = 0; + len += snprintf(line_buf+len, + LPFC_MBX_ACC_LBUF_SZ-len, + "%03d: ", i); + } + len += snprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len, + "%08x ", (uint32_t)*pword); + pword++; + } + if ((i - 1) % 8) + printk(KERN_ERR "%s\n", line_buf); + (*mbx_dump_cnt)--; + } + + /* Clean out command structure on reaching dump count */ + if (*mbx_dump_cnt == 0) + memset(&idiag, 0, sizeof(idiag)); + return; +#endif +} + +/* lpfc_idiag_mbxacc_dump_issue_mbox - idiag debugfs dump issue mailbox command + * @phba: Pointer to HBA context object. + * @dmabuf: Pointer to a DMA buffer descriptor. + * + * Description: + * This routine dump a pass-through non-embedded mailbox command from issue + * mailbox command. + **/ +void +lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *phba, MAILBOX_t *pmbox) +{ +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + uint32_t *mbx_dump_map, *mbx_dump_cnt, *mbx_word_cnt, *mbx_mbox_cmd; + char line_buf[LPFC_MBX_ACC_LBUF_SZ]; + int len = 0; + uint32_t *pword; + uint8_t *pbyte; + uint32_t i, j; + + if (idiag.cmd.opcode != LPFC_IDIAG_CMD_MBXACC_DP) + return; + + mbx_mbox_cmd = &idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX]; + mbx_dump_map = &idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX]; + mbx_dump_cnt = &idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX]; + mbx_word_cnt = &idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX]; + + if (!(*mbx_dump_map & LPFC_MBX_DMP_MBX_ALL) || + (*mbx_dump_cnt == 0) || + (*mbx_word_cnt == 0)) + return; + + if ((*mbx_mbox_cmd != LPFC_MBX_ALL_CMD) && + (*mbx_mbox_cmd != pmbox->mbxCommand)) + return; + + /* dump buffer content */ + if (*mbx_dump_map & LPFC_MBX_DMP_MBX_WORD) { + printk(KERN_ERR "Mailbox command:0x%x dump by word:\n", + pmbox->mbxCommand); + pword = (uint32_t *)pmbox; + for (i = 0; i < *mbx_word_cnt; i++) { + if (!(i % 8)) { + if (i != 0) + printk(KERN_ERR "%s\n", line_buf); + len = 0; + memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ); + len += snprintf(line_buf+len, + LPFC_MBX_ACC_LBUF_SZ-len, + "%03d: ", i); + } + len += snprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len, + "%08x ", + ((uint32_t)*pword) & 0xffffffff); + pword++; + } + if ((i - 1) % 8) + printk(KERN_ERR "%s\n", line_buf); + printk(KERN_ERR "\n"); + } + if (*mbx_dump_map & LPFC_MBX_DMP_MBX_BYTE) { + printk(KERN_ERR "Mailbox command:0x%x dump by byte:\n", + pmbox->mbxCommand); + pbyte = (uint8_t *)pmbox; + for (i = 0; i < *mbx_word_cnt; i++) { + if (!(i % 8)) { + if (i != 0) + printk(KERN_ERR "%s\n", line_buf); + len = 0; + memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ); + len += snprintf(line_buf+len, + LPFC_MBX_ACC_LBUF_SZ-len, + "%03d: ", i); + } + for (j = 0; j < 4; j++) { + len += snprintf(line_buf+len, + LPFC_MBX_ACC_LBUF_SZ-len, + "%02x", + ((uint8_t)*pbyte) & 0xff); + pbyte++; + } + len += snprintf(line_buf+len, + LPFC_MBX_ACC_LBUF_SZ-len, " "); + } + if ((i - 1) % 8) + printk(KERN_ERR "%s\n", line_buf); + printk(KERN_ERR "\n"); + } + (*mbx_dump_cnt)--; + + /* Clean out command structure on reaching dump count */ + if (*mbx_dump_cnt == 0) + memset(&idiag, 0, sizeof(idiag)); + return; +#endif +} + /** * lpfc_debugfs_initialize - Initialize debugfs for a vport * @vport: The vport pointer to initialize. @@ -2673,7 +3894,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) vport, &lpfc_debugfs_op_nodelist); if (!vport->debug_nodelist) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, - "0409 Can't create debugfs nodelist\n"); + "2985 Can't create debugfs nodelist\n"); goto debug_failed; } @@ -2710,6 +3931,20 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) idiag.offset.last_rd = 0; } + /* iDiag PCI BAR access */ + snprintf(name, sizeof(name), "barAcc"); + if (!phba->idiag_bar_acc) { + phba->idiag_bar_acc = + debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + phba->idiag_root, phba, &lpfc_idiag_op_barAcc); + if (!phba->idiag_bar_acc) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "3056 Can't create idiag debugfs\n"); + goto debug_failed; + } + idiag.offset.last_rd = 0; + } + /* iDiag get PCI function queue information */ snprintf(name, sizeof(name), "queInfo"); if (!phba->idiag_que_info) { @@ -2749,6 +3984,50 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) } } + /* iDiag access PCI function control registers */ + snprintf(name, sizeof(name), "ctlAcc"); + if (!phba->idiag_ctl_acc) { + phba->idiag_ctl_acc = + debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + phba->idiag_root, phba, &lpfc_idiag_op_ctlAcc); + if (!phba->idiag_ctl_acc) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "2981 Can't create idiag debugfs\n"); + goto debug_failed; + } + } + + /* iDiag access mbox commands */ + snprintf(name, sizeof(name), "mbxAcc"); + if (!phba->idiag_mbx_acc) { + phba->idiag_mbx_acc = + debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + phba->idiag_root, phba, &lpfc_idiag_op_mbxAcc); + if (!phba->idiag_mbx_acc) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "2980 Can't create idiag debugfs\n"); + goto debug_failed; + } + } + + /* iDiag extents access commands */ + if (phba->sli4_hba.extents_in_use) { + snprintf(name, sizeof(name), "extAcc"); + if (!phba->idiag_ext_acc) { + phba->idiag_ext_acc = + debugfs_create_file(name, + S_IFREG|S_IRUGO|S_IWUSR, + phba->idiag_root, phba, + &lpfc_idiag_op_extAcc); + if (!phba->idiag_ext_acc) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "2986 Cant create " + "idiag debugfs\n"); + goto debug_failed; + } + } + } + debug_failed: return; #endif @@ -2783,7 +4062,6 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport) debugfs_remove(vport->debug_nodelist); /* nodelist */ vport->debug_nodelist = NULL; } - if (vport->vport_debugfs_root) { debugfs_remove(vport->vport_debugfs_root); /* vportX */ vport->vport_debugfs_root = NULL; @@ -2827,6 +4105,21 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport) * iDiag release */ if (phba->sli_rev == LPFC_SLI_REV4) { + if (phba->idiag_ext_acc) { + /* iDiag extAcc */ + debugfs_remove(phba->idiag_ext_acc); + phba->idiag_ext_acc = NULL; + } + if (phba->idiag_mbx_acc) { + /* iDiag mbxAcc */ + debugfs_remove(phba->idiag_mbx_acc); + phba->idiag_mbx_acc = NULL; + } + if (phba->idiag_ctl_acc) { + /* iDiag ctlAcc */ + debugfs_remove(phba->idiag_ctl_acc); + phba->idiag_ctl_acc = NULL; + } if (phba->idiag_drb_acc) { /* iDiag drbAcc */ debugfs_remove(phba->idiag_drb_acc); @@ -2842,6 +4135,11 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport) debugfs_remove(phba->idiag_que_info); phba->idiag_que_info = NULL; } + if (phba->idiag_bar_acc) { + /* iDiag barAcc */ + debugfs_remove(phba->idiag_bar_acc); + phba->idiag_bar_acc = NULL; + } if (phba->idiag_pci_cfg) { /* iDiag pciCfg */ debugfs_remove(phba->idiag_pci_cfg); diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h index 6525a5e62d27..f83bd944edd8 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.h +++ b/drivers/scsi/lpfc/lpfc_debugfs.h @@ -39,14 +39,51 @@ /* hbqinfo output buffer size */ #define LPFC_HBQINFO_SIZE 8192 +/* + * For SLI4 iDiag debugfs diagnostics tool + */ + /* pciConf */ #define LPFC_PCI_CFG_BROWSE 0xffff #define LPFC_PCI_CFG_RD_CMD_ARG 2 #define LPFC_PCI_CFG_WR_CMD_ARG 3 #define LPFC_PCI_CFG_SIZE 4096 -#define LPFC_PCI_CFG_RD_BUF_SIZE (LPFC_PCI_CFG_SIZE/2) #define LPFC_PCI_CFG_RD_SIZE (LPFC_PCI_CFG_SIZE/4) +#define IDIAG_PCICFG_WHERE_INDX 0 +#define IDIAG_PCICFG_COUNT_INDX 1 +#define IDIAG_PCICFG_VALUE_INDX 2 + +/* barAcc */ +#define LPFC_PCI_BAR_BROWSE 0xffff +#define LPFC_PCI_BAR_RD_CMD_ARG 3 +#define LPFC_PCI_BAR_WR_CMD_ARG 3 + +#define LPFC_PCI_IF0_BAR0_SIZE (1024 * 16) +#define LPFC_PCI_IF0_BAR1_SIZE (1024 * 128) +#define LPFC_PCI_IF0_BAR2_SIZE (1024 * 128) +#define LPFC_PCI_IF2_BAR0_SIZE (1024 * 32) + +#define LPFC_PCI_BAR_RD_BUF_SIZE 4096 +#define LPFC_PCI_BAR_RD_SIZE (LPFC_PCI_BAR_RD_BUF_SIZE/4) + +#define LPFC_PCI_IF0_BAR0_RD_SIZE (LPFC_PCI_IF0_BAR0_SIZE/4) +#define LPFC_PCI_IF0_BAR1_RD_SIZE (LPFC_PCI_IF0_BAR1_SIZE/4) +#define LPFC_PCI_IF0_BAR2_RD_SIZE (LPFC_PCI_IF0_BAR2_SIZE/4) +#define LPFC_PCI_IF2_BAR0_RD_SIZE (LPFC_PCI_IF2_BAR0_SIZE/4) + +#define IDIAG_BARACC_BAR_NUM_INDX 0 +#define IDIAG_BARACC_OFF_SET_INDX 1 +#define IDIAG_BARACC_ACC_MOD_INDX 2 +#define IDIAG_BARACC_REG_VAL_INDX 2 +#define IDIAG_BARACC_BAR_SZE_INDX 3 + +#define IDIAG_BARACC_BAR_0 0 +#define IDIAG_BARACC_BAR_1 1 +#define IDIAG_BARACC_BAR_2 2 + +#define SINGLE_WORD 1 + /* queue info */ #define LPFC_QUE_INFO_GET_BUF_SIZE 4096 @@ -63,7 +100,14 @@ #define LPFC_IDIAG_WQ 4 #define LPFC_IDIAG_RQ 5 -/* doorbell acc */ +#define IDIAG_QUEACC_QUETP_INDX 0 +#define IDIAG_QUEACC_QUEID_INDX 1 +#define IDIAG_QUEACC_INDEX_INDX 2 +#define IDIAG_QUEACC_COUNT_INDX 3 +#define IDIAG_QUEACC_OFFST_INDX 4 +#define IDIAG_QUEACC_VALUE_INDX 5 + +/* doorbell register acc */ #define LPFC_DRB_ACC_ALL 0xffff #define LPFC_DRB_ACC_RD_CMD_ARG 1 #define LPFC_DRB_ACC_WR_CMD_ARG 2 @@ -76,6 +120,67 @@ #define LPFC_DRB_MAX 4 +#define IDIAG_DRBACC_REGID_INDX 0 +#define IDIAG_DRBACC_VALUE_INDX 1 + +/* control register acc */ +#define LPFC_CTL_ACC_ALL 0xffff +#define LPFC_CTL_ACC_RD_CMD_ARG 1 +#define LPFC_CTL_ACC_WR_CMD_ARG 2 +#define LPFC_CTL_ACC_BUF_SIZE 256 + +#define LPFC_CTL_PORT_SEM 1 +#define LPFC_CTL_PORT_STA 2 +#define LPFC_CTL_PORT_CTL 3 +#define LPFC_CTL_PORT_ER1 4 +#define LPFC_CTL_PORT_ER2 5 +#define LPFC_CTL_PDEV_CTL 6 + +#define LPFC_CTL_MAX 6 + +#define IDIAG_CTLACC_REGID_INDX 0 +#define IDIAG_CTLACC_VALUE_INDX 1 + +/* mailbox access */ +#define LPFC_MBX_DMP_ARG 4 + +#define LPFC_MBX_ACC_BUF_SIZE 512 +#define LPFC_MBX_ACC_LBUF_SZ 128 + +#define LPFC_MBX_DMP_MBX_WORD 0x00000001 +#define LPFC_MBX_DMP_MBX_BYTE 0x00000002 +#define LPFC_MBX_DMP_MBX_ALL (LPFC_MBX_DMP_MBX_WORD | LPFC_MBX_DMP_MBX_BYTE) + +#define LPFC_BSG_DMP_MBX_RD_MBX 0x00000001 +#define LPFC_BSG_DMP_MBX_RD_BUF 0x00000002 +#define LPFC_BSG_DMP_MBX_WR_MBX 0x00000004 +#define LPFC_BSG_DMP_MBX_WR_BUF 0x00000008 +#define LPFC_BSG_DMP_MBX_ALL (LPFC_BSG_DMP_MBX_RD_MBX | \ + LPFC_BSG_DMP_MBX_RD_BUF | \ + LPFC_BSG_DMP_MBX_WR_MBX | \ + LPFC_BSG_DMP_MBX_WR_BUF) + +#define LPFC_MBX_DMP_ALL 0xffff +#define LPFC_MBX_ALL_CMD 0xff + +#define IDIAG_MBXACC_MBCMD_INDX 0 +#define IDIAG_MBXACC_DPMAP_INDX 1 +#define IDIAG_MBXACC_DPCNT_INDX 2 +#define IDIAG_MBXACC_WDCNT_INDX 3 + +/* extents access */ +#define LPFC_EXT_ACC_CMD_ARG 1 +#define LPFC_EXT_ACC_BUF_SIZE 4096 + +#define LPFC_EXT_ACC_AVAIL 0x1 +#define LPFC_EXT_ACC_ALLOC 0x2 +#define LPFC_EXT_ACC_DRIVR 0x4 +#define LPFC_EXT_ACC_ALL (LPFC_EXT_ACC_DRIVR | \ + LPFC_EXT_ACC_AVAIL | \ + LPFC_EXT_ACC_ALLOC) + +#define IDIAG_EXTACC_EXMAP_INDX 0 + #define SIZE_U8 sizeof(uint8_t) #define SIZE_U16 sizeof(uint16_t) #define SIZE_U32 sizeof(uint32_t) @@ -110,6 +215,11 @@ struct lpfc_idiag_cmd { #define LPFC_IDIAG_CMD_PCICFG_ST 0x00000003 #define LPFC_IDIAG_CMD_PCICFG_CL 0x00000004 +#define LPFC_IDIAG_CMD_BARACC_RD 0x00000008 +#define LPFC_IDIAG_CMD_BARACC_WR 0x00000009 +#define LPFC_IDIAG_CMD_BARACC_ST 0x0000000a +#define LPFC_IDIAG_CMD_BARACC_CL 0x0000000b + #define LPFC_IDIAG_CMD_QUEACC_RD 0x00000011 #define LPFC_IDIAG_CMD_QUEACC_WR 0x00000012 #define LPFC_IDIAG_CMD_QUEACC_ST 0x00000013 @@ -119,6 +229,17 @@ struct lpfc_idiag_cmd { #define LPFC_IDIAG_CMD_DRBACC_WR 0x00000022 #define LPFC_IDIAG_CMD_DRBACC_ST 0x00000023 #define LPFC_IDIAG_CMD_DRBACC_CL 0x00000024 + +#define LPFC_IDIAG_CMD_CTLACC_RD 0x00000031 +#define LPFC_IDIAG_CMD_CTLACC_WR 0x00000032 +#define LPFC_IDIAG_CMD_CTLACC_ST 0x00000033 +#define LPFC_IDIAG_CMD_CTLACC_CL 0x00000034 + +#define LPFC_IDIAG_CMD_MBXACC_DP 0x00000041 +#define LPFC_IDIAG_BSG_MBXACC_DP 0x00000042 + +#define LPFC_IDIAG_CMD_EXTACC_RD 0x00000051 + uint32_t data[LPFC_IDIAG_CMD_DATA_SIZE]; }; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 027b797c9916..4c58402fa698 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -9153,7 +9153,6 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) /* Check if there are static vports to be created. */ lpfc_create_static_vport(phba); - return 0; out_disable_intr: diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 5b28ea1d72c5..247c24f438f5 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -4714,10 +4714,15 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) * lpfc_sli4_get_avail_extnt_rsrc - Get available resource extent count. * @phba: Pointer to HBA context object. * @type: The resource extent type. + * @extnt_count: buffer to hold port available extent count. + * @extnt_size: buffer to hold element count per extent. * - * This function allocates all SLI4 resource identifiers. + * This function calls the port and retrievs the number of available + * extents and their size for a particular extent type. + * + * Returns: 0 if successful. Nonzero otherwise. **/ -static int +int lpfc_sli4_get_avail_extnt_rsrc(struct lpfc_hba *phba, uint16_t type, uint16_t *extnt_count, uint16_t *extnt_size) { @@ -4894,7 +4899,7 @@ lpfc_sli4_cfg_post_extnts(struct lpfc_hba *phba, uint16_t *extnt_cnt, req_len, *emb); if (alloc_len < req_len) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "9000 Allocated DMA memory size (x%x) is " + "2982 Allocated DMA memory size (x%x) is " "less than the requested DMA memory " "size (x%x)\n", alloc_len, req_len); return -ENOMEM; @@ -5507,6 +5512,154 @@ lpfc_sli4_dealloc_resource_identifiers(struct lpfc_hba *phba) return 0; } +/** + * lpfc_sli4_get_allocated_extnts - Get the port's allocated extents. + * @phba: Pointer to HBA context object. + * @type: The resource extent type. + * @extnt_count: buffer to hold port extent count response + * @extnt_size: buffer to hold port extent size response. + * + * This function calls the port to read the host allocated extents + * for a particular type. + **/ +int +lpfc_sli4_get_allocated_extnts(struct lpfc_hba *phba, uint16_t type, + uint16_t *extnt_cnt, uint16_t *extnt_size) +{ + bool emb; + int rc = 0; + uint16_t curr_blks = 0; + uint32_t req_len, emb_len; + uint32_t alloc_len, mbox_tmo; + struct list_head *blk_list_head; + struct lpfc_rsrc_blks *rsrc_blk; + LPFC_MBOXQ_t *mbox; + void *virtaddr = NULL; + struct lpfc_mbx_nembed_rsrc_extent *n_rsrc; + struct lpfc_mbx_alloc_rsrc_extents *rsrc_ext; + union lpfc_sli4_cfg_shdr *shdr; + + switch (type) { + case LPFC_RSC_TYPE_FCOE_VPI: + blk_list_head = &phba->lpfc_vpi_blk_list; + break; + case LPFC_RSC_TYPE_FCOE_XRI: + blk_list_head = &phba->sli4_hba.lpfc_xri_blk_list; + break; + case LPFC_RSC_TYPE_FCOE_VFI: + blk_list_head = &phba->sli4_hba.lpfc_vfi_blk_list; + break; + case LPFC_RSC_TYPE_FCOE_RPI: + blk_list_head = &phba->sli4_hba.lpfc_rpi_blk_list; + break; + default: + return -EIO; + } + + /* Count the number of extents currently allocatd for this type. */ + list_for_each_entry(rsrc_blk, blk_list_head, list) { + if (curr_blks == 0) { + /* + * The GET_ALLOCATED mailbox does not return the size, + * just the count. The size should be just the size + * stored in the current allocated block and all sizes + * for an extent type are the same so set the return + * value now. + */ + *extnt_size = rsrc_blk->rsrc_size; + } + curr_blks++; + } + + /* Calculate the total requested length of the dma memory. */ + req_len = curr_blks * sizeof(uint16_t); + + /* + * Calculate the size of an embedded mailbox. The uint32_t + * accounts for extents-specific word. + */ + emb_len = sizeof(MAILBOX_t) - sizeof(struct mbox_header) - + sizeof(uint32_t); + + /* + * Presume the allocation and response will fit into an embedded + * mailbox. If not true, reconfigure to a non-embedded mailbox. + */ + emb = LPFC_SLI4_MBX_EMBED; + req_len = emb_len; + if (req_len > emb_len) { + req_len = curr_blks * sizeof(uint16_t) + + sizeof(union lpfc_sli4_cfg_shdr) + + sizeof(uint32_t); + emb = LPFC_SLI4_MBX_NEMBED; + } + + mbox = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return -ENOMEM; + memset(mbox, 0, sizeof(LPFC_MBOXQ_t)); + + alloc_len = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON, + LPFC_MBOX_OPCODE_GET_ALLOC_RSRC_EXTENT, + req_len, emb); + if (alloc_len < req_len) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2983 Allocated DMA memory size (x%x) is " + "less than the requested DMA memory " + "size (x%x)\n", alloc_len, req_len); + rc = -ENOMEM; + goto err_exit; + } + rc = lpfc_sli4_mbox_rsrc_extent(phba, mbox, curr_blks, type, emb); + if (unlikely(rc)) { + rc = -EIO; + goto err_exit; + } + + if (!phba->sli4_hba.intr_enable) + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + else { + mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG); + rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); + } + + if (unlikely(rc)) { + rc = -EIO; + goto err_exit; + } + + /* + * Figure out where the response is located. Then get local pointers + * to the response data. The port does not guarantee to respond to + * all extents counts request so update the local variable with the + * allocated count from the port. + */ + if (emb == LPFC_SLI4_MBX_EMBED) { + rsrc_ext = &mbox->u.mqe.un.alloc_rsrc_extents; + shdr = &rsrc_ext->header.cfg_shdr; + *extnt_cnt = bf_get(lpfc_mbx_rsrc_cnt, &rsrc_ext->u.rsp); + } else { + virtaddr = mbox->sge_array->addr[0]; + n_rsrc = (struct lpfc_mbx_nembed_rsrc_extent *) virtaddr; + shdr = &n_rsrc->cfg_shdr; + *extnt_cnt = bf_get(lpfc_mbx_rsrc_cnt, n_rsrc); + } + + if (bf_get(lpfc_mbox_hdr_status, &shdr->response)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT, + "2984 Failed to read allocated resources " + "for type %d - Status 0x%x Add'l Status 0x%x.\n", + type, + bf_get(lpfc_mbox_hdr_status, &shdr->response), + bf_get(lpfc_mbox_hdr_add_status, &shdr->response)); + rc = -EIO; + goto err_exit; + } + err_exit: + lpfc_sli4_mbox_cmd_free(phba, mbox); + return rc; +} + /** * lpfc_sli4_hba_setup - SLI4 device intialization PCI function * @phba: Pointer to HBA context object. @@ -6637,6 +6790,9 @@ lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq, unsigned long iflags; int rc; + /* dump from issue mailbox command if setup */ + lpfc_idiag_mbxacc_dump_issue_mbox(phba, &mboxq->u.mb); + rc = lpfc_mbox_dev_check(phba); if (unlikely(rc)) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, -- cgit v1.2.3 From 7d791df730844932feeac3bbf3ac08b510682174 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 22 Jul 2011 18:37:52 -0400 Subject: [SCSI] lpfc 8.3.25: Add FCF priority failover functionality This patch implements a new FCF failover policy for the lpfc driver. It allows the driver to choose which FCF to failover to based on the FCF priority. This patch also introduces a new sysfs parameter (fcf_failover_policy) to allow the user to choose which FCF failover policy to use. Signed-off-by: Alex Iannicelli Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 3 + drivers/scsi/lpfc/lpfc_attr.c | 5 + drivers/scsi/lpfc/lpfc_crtn.h | 2 + drivers/scsi/lpfc/lpfc_els.c | 2 + drivers/scsi/lpfc/lpfc_hbadisc.c | 220 +++++++++++++++++++++++++++++++++++++-- drivers/scsi/lpfc/lpfc_init.c | 7 +- drivers/scsi/lpfc/lpfc_sli.c | 131 ++++++++++++++++++++++- drivers/scsi/lpfc/lpfc_sli4.h | 27 ++++- 8 files changed, 377 insertions(+), 20 deletions(-) (limited to 'drivers/scsi/lpfc/lpfc_sli.c') diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index b932067bf2d8..c088a36d1f33 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -680,6 +680,9 @@ struct lpfc_hba { uint32_t cfg_enable_rrq; uint32_t cfg_topology; uint32_t cfg_link_speed; +#define LPFC_FCF_FOV 1 /* Fast fcf failover */ +#define LPFC_FCF_PRIORITY 2 /* Priority fcf failover */ + uint32_t cfg_fcf_failover_policy; uint32_t cfg_cr_delay; uint32_t cfg_cr_count; uint32_t cfg_multi_ring_support; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index fa5bc146d67b..2542f1f8bf86 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -2193,6 +2193,9 @@ lpfc_param_show(enable_npiv); lpfc_param_init(enable_npiv, 1, 0, 1); static DEVICE_ATTR(lpfc_enable_npiv, S_IRUGO, lpfc_enable_npiv_show, NULL); +LPFC_ATTR_R(fcf_failover_policy, 1, 1, 2, + "FCF Fast failover=1 Priority failover=2"); + int lpfc_enable_rrq; module_param(lpfc_enable_rrq, int, S_IRUGO); MODULE_PARM_DESC(lpfc_enable_rrq, "Enable RRQ functionality"); @@ -3775,6 +3778,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_fdmi_on, &dev_attr_lpfc_max_luns, &dev_attr_lpfc_enable_npiv, + &dev_attr_lpfc_fcf_failover_policy, &dev_attr_lpfc_enable_rrq, &dev_attr_nport_evt_cnt, &dev_attr_board_mode, @@ -4995,6 +4999,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_link_speed_init(phba, lpfc_link_speed); lpfc_poll_tmo_init(phba, lpfc_poll_tmo); lpfc_enable_npiv_init(phba, lpfc_enable_npiv); + lpfc_fcf_failover_policy_init(phba, lpfc_fcf_failover_policy); lpfc_enable_rrq_init(phba, lpfc_enable_rrq); lpfc_use_msi_init(phba, lpfc_use_msi); lpfc_fcp_imax_init(phba, lpfc_fcp_imax); diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 1f3cad0b1ce8..a6db6aef1331 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -235,9 +235,11 @@ int lpfc_sli4_redisc_fcf_table(struct lpfc_hba *); void lpfc_fcf_redisc_wait_start_timer(struct lpfc_hba *); void lpfc_sli4_fcf_dead_failthrough(struct lpfc_hba *); uint16_t lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *); +void lpfc_sli4_set_fcf_flogi_fail(struct lpfc_hba *, uint16_t); int lpfc_sli4_fcf_rr_index_set(struct lpfc_hba *, uint16_t); void lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *, uint16_t); int lpfc_sli4_fcf_rr_next_proc(struct lpfc_vport *, uint16_t); +void lpfc_sli4_clear_fcf_rr_bmask(struct lpfc_hba *); int lpfc_mem_alloc(struct lpfc_hba *, int align); void lpfc_mem_free(struct lpfc_hba *); diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 1725b81770e9..023da0e00d38 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -874,6 +874,8 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, phba->fcf.current_rec.fcf_indx, irsp->ulpStatus, irsp->un.ulpWord[4], irsp->ulpTimeout); + lpfc_sli4_set_fcf_flogi_fail(phba, + phba->fcf.current_rec.fcf_indx); fcf_index = lpfc_sli4_fcf_rr_next_index_get(phba); rc = lpfc_sli4_fcf_rr_next_proc(vport, fcf_index); if (rc) diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index bef17e3e419a..0b47adf9fee8 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1109,6 +1109,28 @@ out: return; } +/** + * lpfc_sli4_clear_fcf_rr_bmask + * @phba pointer to the struct lpfc_hba for this port. + * This fucnction resets the round robin bit mask and clears the + * fcf priority list. The list deletions are done while holding the + * hbalock. The ON_LIST flag and the FLOGI_FAILED flags are cleared + * from the lpfc_fcf_pri record. + **/ +void +lpfc_sli4_clear_fcf_rr_bmask(struct lpfc_hba *phba) +{ + struct lpfc_fcf_pri *fcf_pri; + struct lpfc_fcf_pri *next_fcf_pri; + memset(phba->fcf.fcf_rr_bmask, 0, sizeof(*phba->fcf.fcf_rr_bmask)); + spin_lock_irq(&phba->hbalock); + list_for_each_entry_safe(fcf_pri, next_fcf_pri, + &phba->fcf.fcf_pri_list, list) { + list_del_init(&fcf_pri->list); + fcf_pri->fcf_rec.flag = 0; + } + spin_unlock_irq(&phba->hbalock); +} static void lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) { @@ -1130,7 +1152,8 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) spin_unlock_irq(&phba->hbalock); /* If there is a pending FCoE event, restart FCF table scan. */ - if (lpfc_check_pending_fcoe_event(phba, LPFC_UNREG_FCF)) + if ((!(phba->hba_flag & FCF_RR_INPROG)) && + lpfc_check_pending_fcoe_event(phba, LPFC_UNREG_FCF)) goto fail_out; /* Mark successful completion of FCF table scan */ @@ -1249,6 +1272,30 @@ lpfc_vlan_id_match(uint16_t curr_vlan_id, uint16_t new_vlan_id) return (curr_vlan_id == new_vlan_id); } +/** + * lpfc_update_fcf_record - Update driver fcf record + * __lpfc_update_fcf_record_pri - update the lpfc_fcf_pri record. + * @phba: pointer to lpfc hba data structure. + * @fcf_index: Index for the lpfc_fcf_record. + * @new_fcf_record: pointer to hba fcf record. + * + * This routine updates the driver FCF priority record from the new HBA FCF + * record. This routine is called with the host lock held. + **/ +static void +__lpfc_update_fcf_record_pri(struct lpfc_hba *phba, uint16_t fcf_index, + struct fcf_record *new_fcf_record + ) +{ + struct lpfc_fcf_pri *fcf_pri; + + fcf_pri = &phba->fcf.fcf_pri[fcf_index]; + fcf_pri->fcf_rec.fcf_index = fcf_index; + /* FCF record priority */ + fcf_pri->fcf_rec.priority = new_fcf_record->fip_priority; + +} + /** * lpfc_copy_fcf_record - Copy fcf information to lpfc_hba. * @fcf: pointer to driver fcf record. @@ -1332,6 +1379,9 @@ __lpfc_update_fcf_record(struct lpfc_hba *phba, struct lpfc_fcf_rec *fcf_rec, fcf_rec->addr_mode = addr_mode; fcf_rec->vlan_id = vlan_id; fcf_rec->flag |= (flag | RECORD_VALID); + __lpfc_update_fcf_record_pri(phba, + bf_get(lpfc_fcf_record_fcf_index, new_fcf_record), + new_fcf_record); } /** @@ -1834,6 +1884,8 @@ lpfc_sli4_fcf_record_match(struct lpfc_hba *phba, return false; if (!lpfc_fab_name_match(fcf_rec->fabric_name, new_fcf_record)) return false; + if (fcf_rec->priority != new_fcf_record->fip_priority) + return false; return true; } @@ -1896,6 +1948,152 @@ stop_flogi_current_fcf: return 1; } +/** + * lpfc_sli4_fcf_pri_list_del + * @phba: pointer to lpfc hba data structure. + * @fcf_index the index of the fcf record to delete + * This routine checks the on list flag of the fcf_index to be deleted. + * If it is one the list then it is removed from the list, and the flag + * is cleared. This routine grab the hbalock before removing the fcf + * record from the list. + **/ +static void lpfc_sli4_fcf_pri_list_del(struct lpfc_hba *phba, + uint16_t fcf_index) +{ + struct lpfc_fcf_pri *new_fcf_pri; + + new_fcf_pri = &phba->fcf.fcf_pri[fcf_index]; + lpfc_printf_log(phba, KERN_INFO, LOG_FIP, + "3058 deleting idx x%x pri x%x flg x%x\n", + fcf_index, new_fcf_pri->fcf_rec.priority, + new_fcf_pri->fcf_rec.flag); + spin_lock_irq(&phba->hbalock); + if (new_fcf_pri->fcf_rec.flag & LPFC_FCF_ON_PRI_LIST) { + if (phba->fcf.current_rec.priority == + new_fcf_pri->fcf_rec.priority) + phba->fcf.eligible_fcf_cnt--; + list_del_init(&new_fcf_pri->list); + new_fcf_pri->fcf_rec.flag &= ~LPFC_FCF_ON_PRI_LIST; + } + spin_unlock_irq(&phba->hbalock); +} + +/** + * lpfc_sli4_set_fcf_flogi_fail + * @phba: pointer to lpfc hba data structure. + * @fcf_index the index of the fcf record to update + * This routine acquires the hbalock and then set the LPFC_FCF_FLOGI_FAILED + * flag so the the round robin slection for the particular priority level + * will try a different fcf record that does not have this bit set. + * If the fcf record is re-read for any reason this flag is cleared brfore + * adding it to the priority list. + **/ +void +lpfc_sli4_set_fcf_flogi_fail(struct lpfc_hba *phba, uint16_t fcf_index) +{ + struct lpfc_fcf_pri *new_fcf_pri; + new_fcf_pri = &phba->fcf.fcf_pri[fcf_index]; + spin_lock_irq(&phba->hbalock); + new_fcf_pri->fcf_rec.flag |= LPFC_FCF_FLOGI_FAILED; + spin_unlock_irq(&phba->hbalock); +} + +/** + * lpfc_sli4_fcf_pri_list_add + * @phba: pointer to lpfc hba data structure. + * @fcf_index the index of the fcf record to add + * This routine checks the priority of the fcf_index to be added. + * If it is a lower priority than the current head of the fcf_pri list + * then it is added to the list in the right order. + * If it is the same priority as the current head of the list then it + * is added to the head of the list and its bit in the rr_bmask is set. + * If the fcf_index to be added is of a higher priority than the current + * head of the list then the rr_bmask is cleared, its bit is set in the + * rr_bmask and it is added to the head of the list. + * returns: + * 0=success 1=failure + **/ +int lpfc_sli4_fcf_pri_list_add(struct lpfc_hba *phba, uint16_t fcf_index, + struct fcf_record *new_fcf_record) +{ + uint16_t current_fcf_pri; + uint16_t last_index; + struct lpfc_fcf_pri *fcf_pri; + struct lpfc_fcf_pri *next_fcf_pri; + struct lpfc_fcf_pri *new_fcf_pri; + int ret; + + new_fcf_pri = &phba->fcf.fcf_pri[fcf_index]; + lpfc_printf_log(phba, KERN_INFO, LOG_FIP, + "3059 adding idx x%x pri x%x flg x%x\n", + fcf_index, new_fcf_record->fip_priority, + new_fcf_pri->fcf_rec.flag); + spin_lock_irq(&phba->hbalock); + if (new_fcf_pri->fcf_rec.flag & LPFC_FCF_ON_PRI_LIST) + list_del_init(&new_fcf_pri->list); + new_fcf_pri->fcf_rec.fcf_index = fcf_index; + new_fcf_pri->fcf_rec.priority = new_fcf_record->fip_priority; + if (list_empty(&phba->fcf.fcf_pri_list)) { + list_add(&new_fcf_pri->list, &phba->fcf.fcf_pri_list); + ret = lpfc_sli4_fcf_rr_index_set(phba, + new_fcf_pri->fcf_rec.fcf_index); + goto out; + } + + last_index = find_first_bit(phba->fcf.fcf_rr_bmask, + LPFC_SLI4_FCF_TBL_INDX_MAX); + if (last_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) { + ret = 0; /* Empty rr list */ + goto out; + } + current_fcf_pri = phba->fcf.fcf_pri[last_index].fcf_rec.priority; + if (new_fcf_pri->fcf_rec.priority <= current_fcf_pri) { + list_add(&new_fcf_pri->list, &phba->fcf.fcf_pri_list); + if (new_fcf_pri->fcf_rec.priority < current_fcf_pri) { + memset(phba->fcf.fcf_rr_bmask, 0, + sizeof(*phba->fcf.fcf_rr_bmask)); + /* fcfs_at_this_priority_level = 1; */ + phba->fcf.eligible_fcf_cnt = 1; + } else + /* fcfs_at_this_priority_level++; */ + phba->fcf.eligible_fcf_cnt++; + ret = lpfc_sli4_fcf_rr_index_set(phba, + new_fcf_pri->fcf_rec.fcf_index); + goto out; + } + + list_for_each_entry_safe(fcf_pri, next_fcf_pri, + &phba->fcf.fcf_pri_list, list) { + if (new_fcf_pri->fcf_rec.priority <= + fcf_pri->fcf_rec.priority) { + if (fcf_pri->list.prev == &phba->fcf.fcf_pri_list) + list_add(&new_fcf_pri->list, + &phba->fcf.fcf_pri_list); + else + list_add(&new_fcf_pri->list, + &((struct lpfc_fcf_pri *) + fcf_pri->list.prev)->list); + ret = 0; + goto out; + } else if (fcf_pri->list.next == &phba->fcf.fcf_pri_list + || new_fcf_pri->fcf_rec.priority < + next_fcf_pri->fcf_rec.priority) { + list_add(&new_fcf_pri->list, &fcf_pri->list); + ret = 0; + goto out; + } + if (new_fcf_pri->fcf_rec.priority > fcf_pri->fcf_rec.priority) + continue; + + } + ret = 1; +out: + /* we use = instead of |= to clear the FLOGI_FAILED flag. */ + new_fcf_pri->fcf_rec.flag = LPFC_FCF_ON_PRI_LIST; + spin_unlock_irq(&phba->hbalock); + return ret; +} + /** * lpfc_mbx_cmpl_fcf_scan_read_fcf_rec - fcf scan read_fcf mbox cmpl handler. * @phba: pointer to lpfc hba data structure. @@ -1958,6 +2156,9 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) * record for roundrobin FCF failover. */ if (!rc) { + lpfc_sli4_fcf_pri_list_del(phba, + bf_get(lpfc_fcf_record_fcf_index, + new_fcf_record)); lpfc_printf_log(phba, KERN_WARNING, LOG_FIP, "2781 FCF (x%x) failed connection " "list check: (x%x/x%x)\n", @@ -2005,7 +2206,8 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) goto read_next_fcf; } else { fcf_index = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record); - rc = lpfc_sli4_fcf_rr_index_set(phba, fcf_index); + rc = lpfc_sli4_fcf_pri_list_add(phba, fcf_index, + new_fcf_record); if (rc) goto read_next_fcf; } @@ -2018,7 +2220,8 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) */ spin_lock_irq(&phba->hbalock); if (phba->fcf.fcf_flag & FCF_IN_USE) { - if (lpfc_sli4_fcf_record_match(phba, &phba->fcf.current_rec, + if (phba->cfg_fcf_failover_policy == LPFC_FCF_FOV && + lpfc_sli4_fcf_record_match(phba, &phba->fcf.current_rec, new_fcf_record, vlan_id)) { if (bf_get(lpfc_fcf_record_fcf_index, new_fcf_record) == phba->fcf.current_rec.fcf_indx) { @@ -2232,7 +2435,8 @@ read_next_fcf: (phba->fcf.fcf_flag & FCF_REDISC_PEND)) return; - if (phba->fcf.fcf_flag & FCF_IN_USE) { + if (phba->cfg_fcf_failover_policy == LPFC_FCF_FOV && + phba->fcf.fcf_flag & FCF_IN_USE) { /* * In case the current in-use FCF record no * longer existed during FCF discovery that @@ -2423,7 +2627,8 @@ lpfc_mbx_cmpl_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) /* Update the eligible FCF record index bmask */ fcf_index = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record); - rc = lpfc_sli4_fcf_rr_index_set(phba, fcf_index); + + rc = lpfc_sli4_fcf_pri_list_add(phba, fcf_index, new_fcf_record); out: lpfc_sli4_mbox_cmd_free(phba, mboxq); @@ -2893,8 +3098,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la) goto out; } /* Reset FCF roundrobin bmask for new discovery */ - memset(phba->fcf.fcf_rr_bmask, 0, - sizeof(*phba->fcf.fcf_rr_bmask)); + lpfc_sli4_clear_fcf_rr_bmask(phba); } return; @@ -5592,7 +5796,7 @@ lpfc_unregister_fcf_rescan(struct lpfc_hba *phba) spin_unlock_irq(&phba->hbalock); /* Reset FCF roundrobin bmask for new discovery */ - memset(phba->fcf.fcf_rr_bmask, 0, sizeof(*phba->fcf.fcf_rr_bmask)); + lpfc_sli4_clear_fcf_rr_bmask(phba); rc = lpfc_sli4_fcf_scan_read_fcf_rec(phba, LPFC_FCOE_FCF_GET_FIRST); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 4c58402fa698..a3c820083c36 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -3634,8 +3634,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, lpfc_sli4_fcf_dead_failthrough(phba); } else { /* Reset FCF roundrobin bmask for new discovery */ - memset(phba->fcf.fcf_rr_bmask, 0, - sizeof(*phba->fcf.fcf_rr_bmask)); + lpfc_sli4_clear_fcf_rr_bmask(phba); /* * Handling fast FCF failover to a DEAD FCF event is * considered equalivant to receiving CVL to all vports. @@ -3721,8 +3720,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, * Reset FCF roundrobin bmask for new * discovery. */ - memset(phba->fcf.fcf_rr_bmask, 0, - sizeof(*phba->fcf.fcf_rr_bmask)); + lpfc_sli4_clear_fcf_rr_bmask(phba); } break; default: @@ -9046,6 +9044,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) } INIT_LIST_HEAD(&phba->active_rrq_list); + INIT_LIST_HEAD(&phba->fcf.fcf_pri_list); /* Set up common device driver resources */ error = lpfc_setup_driver_resource_phase2(phba); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 247c24f438f5..8b799f047a99 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -14634,6 +14634,92 @@ fail_fcf_read: return error; } +/** + * lpfc_check_next_fcf_pri + * phba pointer to the lpfc_hba struct for this port. + * This routine is called from the lpfc_sli4_fcf_rr_next_index_get + * routine when the rr_bmask is empty. The FCF indecies are put into the + * rr_bmask based on their priority level. Starting from the highest priority + * to the lowest. The most likely FCF candidate will be in the highest + * priority group. When this routine is called it searches the fcf_pri list for + * next lowest priority group and repopulates the rr_bmask with only those + * fcf_indexes. + * returns: + * 1=success 0=failure + **/ +int +lpfc_check_next_fcf_pri_level(struct lpfc_hba *phba) +{ + uint16_t next_fcf_pri; + uint16_t last_index; + struct lpfc_fcf_pri *fcf_pri; + int rc; + int ret = 0; + + last_index = find_first_bit(phba->fcf.fcf_rr_bmask, + LPFC_SLI4_FCF_TBL_INDX_MAX); + lpfc_printf_log(phba, KERN_INFO, LOG_FIP, + "3060 Last IDX %d\n", last_index); + if (list_empty(&phba->fcf.fcf_pri_list)) { + lpfc_printf_log(phba, KERN_ERR, LOG_FIP, + "3061 Last IDX %d\n", last_index); + return 0; /* Empty rr list */ + } + next_fcf_pri = 0; + /* + * Clear the rr_bmask and set all of the bits that are at this + * priority. + */ + memset(phba->fcf.fcf_rr_bmask, 0, + sizeof(*phba->fcf.fcf_rr_bmask)); + spin_lock_irq(&phba->hbalock); + list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) { + if (fcf_pri->fcf_rec.flag & LPFC_FCF_FLOGI_FAILED) + continue; + /* + * the 1st priority that has not FLOGI failed + * will be the highest. + */ + if (!next_fcf_pri) + next_fcf_pri = fcf_pri->fcf_rec.priority; + spin_unlock_irq(&phba->hbalock); + if (fcf_pri->fcf_rec.priority == next_fcf_pri) { + rc = lpfc_sli4_fcf_rr_index_set(phba, + fcf_pri->fcf_rec.fcf_index); + if (rc) + return 0; + } + spin_lock_irq(&phba->hbalock); + } + /* + * if next_fcf_pri was not set above and the list is not empty then + * we have failed flogis on all of them. So reset flogi failed + * and start at the begining. + */ + if (!next_fcf_pri && !list_empty(&phba->fcf.fcf_pri_list)) { + list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) { + fcf_pri->fcf_rec.flag &= ~LPFC_FCF_FLOGI_FAILED; + /* + * the 1st priority that has not FLOGI failed + * will be the highest. + */ + if (!next_fcf_pri) + next_fcf_pri = fcf_pri->fcf_rec.priority; + spin_unlock_irq(&phba->hbalock); + if (fcf_pri->fcf_rec.priority == next_fcf_pri) { + rc = lpfc_sli4_fcf_rr_index_set(phba, + fcf_pri->fcf_rec.fcf_index); + if (rc) + return 0; + } + spin_lock_irq(&phba->hbalock); + } + } else + ret = 1; + spin_unlock_irq(&phba->hbalock); + + return ret; +} /** * lpfc_sli4_fcf_rr_next_index_get - Get next eligible fcf record index * @phba: pointer to lpfc hba data structure. @@ -14650,6 +14736,7 @@ lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *phba) uint16_t next_fcf_index; /* Search start from next bit of currently registered FCF index */ +next_priority: next_fcf_index = (phba->fcf.current_rec.fcf_indx + 1) % LPFC_SLI4_FCF_TBL_INDX_MAX; next_fcf_index = find_next_bit(phba->fcf.fcf_rr_bmask, @@ -14657,17 +14744,46 @@ lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *phba) next_fcf_index); /* Wrap around condition on phba->fcf.fcf_rr_bmask */ - if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) + if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) { + /* + * If we have wrapped then we need to clear the bits that + * have been tested so that we can detect when we should + * change the priority level. + */ next_fcf_index = find_next_bit(phba->fcf.fcf_rr_bmask, LPFC_SLI4_FCF_TBL_INDX_MAX, 0); + } + /* Check roundrobin failover list empty condition */ - if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) { + if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX || + next_fcf_index == phba->fcf.current_rec.fcf_indx) { + /* + * If next fcf index is not found check if there are lower + * Priority level fcf's in the fcf_priority list. + * Set up the rr_bmask with all of the avaiable fcf bits + * at that level and continue the selection process. + */ + if (lpfc_check_next_fcf_pri_level(phba)) + goto next_priority; lpfc_printf_log(phba, KERN_WARNING, LOG_FIP, "2844 No roundrobin failover FCF available\n"); - return LPFC_FCOE_FCF_NEXT_NONE; + if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) + return LPFC_FCOE_FCF_NEXT_NONE; + else { + lpfc_printf_log(phba, KERN_WARNING, LOG_FIP, + "3063 Only FCF available idx %d, flag %x\n", + next_fcf_index, + phba->fcf.fcf_pri[next_fcf_index].fcf_rec.flag); + return next_fcf_index; + } } + if (next_fcf_index < LPFC_SLI4_FCF_TBL_INDX_MAX && + phba->fcf.fcf_pri[next_fcf_index].fcf_rec.flag & + LPFC_FCF_FLOGI_FAILED) + goto next_priority; + lpfc_printf_log(phba, KERN_INFO, LOG_FIP, "2845 Get next roundrobin failover FCF (x%x)\n", next_fcf_index); @@ -14719,6 +14835,7 @@ lpfc_sli4_fcf_rr_index_set(struct lpfc_hba *phba, uint16_t fcf_index) void lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *phba, uint16_t fcf_index) { + struct lpfc_fcf_pri *fcf_pri; if (fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) { lpfc_printf_log(phba, KERN_ERR, LOG_FIP, "2762 FCF (x%x) reached driver's book " @@ -14727,6 +14844,14 @@ lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *phba, uint16_t fcf_index) return; } /* Clear the eligible FCF record index bmask */ + spin_lock_irq(&phba->hbalock); + list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) { + if (fcf_pri->fcf_rec.fcf_index == fcf_index) { + list_del_init(&fcf_pri->list); + break; + } + } + spin_unlock_irq(&phba->hbalock); clear_bit(fcf_index, phba->fcf.fcf_rr_bmask); lpfc_printf_log(phba, KERN_INFO, LOG_FIP, diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 88387c1c2dcc..19bb87ae8597 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -159,6 +159,25 @@ struct lpfc_fcf_rec { #define RECORD_VALID 0x02 }; +struct lpfc_fcf_pri_rec { + uint16_t fcf_index; +#define LPFC_FCF_ON_PRI_LIST 0x0001 +#define LPFC_FCF_FLOGI_FAILED 0x0002 + uint16_t flag; + uint32_t priority; +}; + +struct lpfc_fcf_pri { + struct list_head list; + struct lpfc_fcf_pri_rec fcf_rec; +}; + +/* + * Maximum FCF table index, it is for driver internal book keeping, it + * just needs to be no less than the supported HBA's FCF table size. + */ +#define LPFC_SLI4_FCF_TBL_INDX_MAX 32 + struct lpfc_fcf { uint16_t fcfi; uint32_t fcf_flag; @@ -178,15 +197,13 @@ struct lpfc_fcf { uint32_t eligible_fcf_cnt; struct lpfc_fcf_rec current_rec; struct lpfc_fcf_rec failover_rec; + struct list_head fcf_pri_list; + struct lpfc_fcf_pri fcf_pri[LPFC_SLI4_FCF_TBL_INDX_MAX]; + uint32_t current_fcf_scan_pri; struct timer_list redisc_wait; unsigned long *fcf_rr_bmask; /* Eligible FCF indexes for RR failover */ }; -/* - * Maximum FCF table index, it is for driver internal book keeping, it - * just needs to be no less than the supported HBA's FCF table size. - */ -#define LPFC_SLI4_FCF_TBL_INDX_MAX 32 #define LPFC_REGION23_SIGNATURE "RG23" #define LPFC_REGION23_VERSION 1 -- cgit v1.2.3