summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/lpfc
diff options
context:
space:
mode:
authorJames Smart <james.smart@emulex.com>2011-07-22 18:37:42 -0400
committerJames Bottomley <JBottomley@Parallels.com>2011-07-27 15:14:00 +0400
commitb76f2dc91c0fff7a66616affdc039dc2e4b7ff98 (patch)
treec918b99e5e368f32118044a3ecbb4e793443028c /drivers/scsi/lpfc
parent0a96e9754d6c4a2a31e50ee6c6e36ec13f80bc25 (diff)
[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.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c9
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c37
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h8
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c1354
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.h125
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c1
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c162
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.