diff options
author | James Smart <james.smart@emulex.com> | 2011-07-23 02:37:42 +0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2011-07-27 15:14:00 +0400 |
commit | b76f2dc91c0fff7a66616affdc039dc2e4b7ff98 (patch) | |
tree | c918b99e5e368f32118044a3ecbb4e793443028c /drivers/scsi/lpfc | |
parent | 0a96e9754d6c4a2a31e50ee6c6e36ec13f80bc25 (diff) | |
download | linux-b76f2dc91c0fff7a66616affdc039dc2e4b7ff98.tar.xz |
[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 <alex.iannicelli@emulex.com>
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/lpfc')
-rw-r--r-- | drivers/scsi/lpfc/lpfc.h | 4 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_attr.c | 9 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_bsg.c | 37 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_crtn.h | 8 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_debugfs.c | 1354 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_debugfs.h | 125 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 1 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.c | 162 |
8 files changed, 1664 insertions, 36 deletions
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)) && @@ -1570,6 +1596,292 @@ error_out: } /** + * 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. * @buf: The buffer to copy the data to. @@ -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,6 +2642,679 @@ error_out: return -EINVAL; } +/** + * 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) +{ + + if (!pbuffer) + return 0; + + 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; +} + +/** + * 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; + + /* This is a user read operation */ + debug->op = LPFC_IDIAG_OP_RD; + + if (!debug->buffer) + debug->buffer = kmalloc(LPFC_CTL_ACC_BUF_SIZE, GFP_KERNEL); + if (!debug->buffer) + return 0; + pbuffer = debug->buffer; + + 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, @@ -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; @@ -5508,6 +5513,154 @@ lpfc_sli4_dealloc_resource_identifiers(struct lpfc_hba *phba) } /** + * 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, |