|
@@ -48,6 +48,7 @@
|
|
|
#include "lpfc_version.h"
|
|
|
#include "lpfc_compat.h"
|
|
|
#include "lpfc_debugfs.h"
|
|
|
+#include "lpfc_bsg.h"
|
|
|
|
|
|
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
|
|
|
/*
|
|
@@ -135,7 +136,11 @@ lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size)
|
|
|
int i, index, len, enable;
|
|
|
uint32_t ms;
|
|
|
struct lpfc_debugfs_trc *dtp;
|
|
|
- char buffer[LPFC_DEBUG_TRC_ENTRY_SIZE];
|
|
|
+ char *buffer;
|
|
|
+
|
|
|
+ buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL);
|
|
|
+ if (!buffer)
|
|
|
+ return 0;
|
|
|
|
|
|
enable = lpfc_debugfs_enable;
|
|
|
lpfc_debugfs_enable = 0;
|
|
@@ -167,6 +172,8 @@ lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size)
|
|
|
}
|
|
|
|
|
|
lpfc_debugfs_enable = enable;
|
|
|
+ kfree(buffer);
|
|
|
+
|
|
|
return len;
|
|
|
}
|
|
|
|
|
@@ -195,8 +202,11 @@ lpfc_debugfs_slow_ring_trc_data(struct lpfc_hba *phba, char *buf, int size)
|
|
|
int i, index, len, enable;
|
|
|
uint32_t ms;
|
|
|
struct lpfc_debugfs_trc *dtp;
|
|
|
- char buffer[LPFC_DEBUG_TRC_ENTRY_SIZE];
|
|
|
+ char *buffer;
|
|
|
|
|
|
+ buffer = kmalloc(LPFC_DEBUG_TRC_ENTRY_SIZE, GFP_KERNEL);
|
|
|
+ if (!buffer)
|
|
|
+ return 0;
|
|
|
|
|
|
enable = lpfc_debugfs_enable;
|
|
|
lpfc_debugfs_enable = 0;
|
|
@@ -228,6 +238,8 @@ lpfc_debugfs_slow_ring_trc_data(struct lpfc_hba *phba, char *buf, int size)
|
|
|
}
|
|
|
|
|
|
lpfc_debugfs_enable = enable;
|
|
|
+ kfree(buffer);
|
|
|
+
|
|
|
return len;
|
|
|
}
|
|
|
|
|
@@ -378,7 +390,11 @@ lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size)
|
|
|
int len = 0;
|
|
|
int i, off;
|
|
|
uint32_t *ptr;
|
|
|
- char buffer[1024];
|
|
|
+ char *buffer;
|
|
|
+
|
|
|
+ buffer = kmalloc(1024, GFP_KERNEL);
|
|
|
+ if (!buffer)
|
|
|
+ return 0;
|
|
|
|
|
|
off = 0;
|
|
|
spin_lock_irq(&phba->hbalock);
|
|
@@ -407,6 +423,8 @@ lpfc_debugfs_dumpHBASlim_data(struct lpfc_hba *phba, char *buf, int size)
|
|
|
}
|
|
|
|
|
|
spin_unlock_irq(&phba->hbalock);
|
|
|
+ kfree(buffer);
|
|
|
+
|
|
|
return len;
|
|
|
}
|
|
|
|
|
@@ -1327,8 +1345,8 @@ lpfc_idiag_pcicfg_read(struct file *file, char __user *buf, size_t nbytes,
|
|
|
return 0;
|
|
|
|
|
|
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_RD) {
|
|
|
- where = idiag.cmd.data[0];
|
|
|
- count = idiag.cmd.data[1];
|
|
|
+ where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
|
|
|
+ count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
|
|
|
} else
|
|
|
return 0;
|
|
|
|
|
@@ -1373,6 +1391,11 @@ pcicfg_browse:
|
|
|
len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
|
|
|
"%08x ", u32val);
|
|
|
offset += sizeof(uint32_t);
|
|
|
+ if (offset >= LPFC_PCI_CFG_SIZE) {
|
|
|
+ len += snprintf(pbuffer+len,
|
|
|
+ LPFC_PCI_CFG_SIZE-len, "\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
index -= sizeof(uint32_t);
|
|
|
if (!index)
|
|
|
len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
|
|
@@ -1385,8 +1408,11 @@ pcicfg_browse:
|
|
|
}
|
|
|
|
|
|
/* Set up the offset for next portion of pci cfg read */
|
|
|
- idiag.offset.last_rd += LPFC_PCI_CFG_RD_SIZE;
|
|
|
- if (idiag.offset.last_rd >= LPFC_PCI_CFG_SIZE)
|
|
|
+ if (index == 0) {
|
|
|
+ idiag.offset.last_rd += LPFC_PCI_CFG_RD_SIZE;
|
|
|
+ if (idiag.offset.last_rd >= LPFC_PCI_CFG_SIZE)
|
|
|
+ idiag.offset.last_rd = 0;
|
|
|
+ } else
|
|
|
idiag.offset.last_rd = 0;
|
|
|
|
|
|
return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
|
|
@@ -1439,8 +1465,8 @@ lpfc_idiag_pcicfg_write(struct file *file, const char __user *buf,
|
|
|
if (rc != LPFC_PCI_CFG_RD_CMD_ARG)
|
|
|
goto error_out;
|
|
|
/* Read command from PCI config space, set up command fields */
|
|
|
- where = idiag.cmd.data[0];
|
|
|
- count = idiag.cmd.data[1];
|
|
|
+ where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
|
|
|
+ count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
|
|
|
if (count == LPFC_PCI_CFG_BROWSE) {
|
|
|
if (where % sizeof(uint32_t))
|
|
|
goto error_out;
|
|
@@ -1475,9 +1501,9 @@ lpfc_idiag_pcicfg_write(struct file *file, const char __user *buf,
|
|
|
if (rc != LPFC_PCI_CFG_WR_CMD_ARG)
|
|
|
goto error_out;
|
|
|
/* Write command to PCI config space, read-modify-write */
|
|
|
- where = idiag.cmd.data[0];
|
|
|
- count = idiag.cmd.data[1];
|
|
|
- value = idiag.cmd.data[2];
|
|
|
+ where = idiag.cmd.data[IDIAG_PCICFG_WHERE_INDX];
|
|
|
+ count = idiag.cmd.data[IDIAG_PCICFG_COUNT_INDX];
|
|
|
+ value = idiag.cmd.data[IDIAG_PCICFG_VALUE_INDX];
|
|
|
/* Sanity checks */
|
|
|
if ((count != sizeof(uint8_t)) &&
|
|
|
(count != sizeof(uint16_t)) &&
|
|
@@ -1569,6 +1595,292 @@ error_out:
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * lpfc_idiag_baracc_read - idiag debugfs pci bar access read
|
|
|
+ * @file: The file pointer to read from.
|
|
|
+ * @buf: The buffer to copy the data to.
|
|
|
+ * @nbytes: The number of bytes to read.
|
|
|
+ * @ppos: The position in the file to start reading from.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine reads data from the @phba pci bar memory mapped space
|
|
|
+ * according to the idiag command, and copies to user @buf.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * This function returns the amount of data that was read (this could be less
|
|
|
+ * than @nbytes if the end of the file was reached) or a negative error value.
|
|
|
+ **/
|
|
|
+static ssize_t
|
|
|
+lpfc_idiag_baracc_read(struct file *file, char __user *buf, size_t nbytes,
|
|
|
+ loff_t *ppos)
|
|
|
+{
|
|
|
+ struct lpfc_debug *debug = file->private_data;
|
|
|
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
|
|
|
+ int offset_label, offset, offset_run, len = 0, index;
|
|
|
+ int bar_num, acc_range, bar_size;
|
|
|
+ char *pbuffer;
|
|
|
+ void __iomem *mem_mapped_bar;
|
|
|
+ uint32_t if_type;
|
|
|
+ struct pci_dev *pdev;
|
|
|
+ uint32_t u32val;
|
|
|
+
|
|
|
+ pdev = phba->pcidev;
|
|
|
+ if (!pdev)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* This is a user read operation */
|
|
|
+ debug->op = LPFC_IDIAG_OP_RD;
|
|
|
+
|
|
|
+ if (!debug->buffer)
|
|
|
+ debug->buffer = kmalloc(LPFC_PCI_BAR_RD_BUF_SIZE, GFP_KERNEL);
|
|
|
+ if (!debug->buffer)
|
|
|
+ return 0;
|
|
|
+ pbuffer = debug->buffer;
|
|
|
+
|
|
|
+ if (*ppos)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_RD) {
|
|
|
+ bar_num = idiag.cmd.data[IDIAG_BARACC_BAR_NUM_INDX];
|
|
|
+ offset = idiag.cmd.data[IDIAG_BARACC_OFF_SET_INDX];
|
|
|
+ acc_range = idiag.cmd.data[IDIAG_BARACC_ACC_MOD_INDX];
|
|
|
+ bar_size = idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX];
|
|
|
+ } else
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (acc_range == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
|
|
|
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
|
|
|
+ if (bar_num == IDIAG_BARACC_BAR_0)
|
|
|
+ mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
|
|
|
+ else if (bar_num == IDIAG_BARACC_BAR_1)
|
|
|
+ mem_mapped_bar = phba->sli4_hba.ctrl_regs_memmap_p;
|
|
|
+ else if (bar_num == IDIAG_BARACC_BAR_2)
|
|
|
+ mem_mapped_bar = phba->sli4_hba.drbl_regs_memmap_p;
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
+ } else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
|
|
|
+ if (bar_num == IDIAG_BARACC_BAR_0)
|
|
|
+ mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
+ } else
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Read single PCI bar space register */
|
|
|
+ if (acc_range == SINGLE_WORD) {
|
|
|
+ offset_run = offset;
|
|
|
+ u32val = readl(mem_mapped_bar + offset_run);
|
|
|
+ len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
|
|
|
+ "%05x: %08x\n", offset_run, u32val);
|
|
|
+ } else
|
|
|
+ goto baracc_browse;
|
|
|
+
|
|
|
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
|
|
|
+
|
|
|
+baracc_browse:
|
|
|
+
|
|
|
+ /* Browse all PCI bar space registers */
|
|
|
+ offset_label = idiag.offset.last_rd;
|
|
|
+ offset_run = offset_label;
|
|
|
+
|
|
|
+ /* Read PCI bar memory mapped space */
|
|
|
+ len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
|
|
|
+ "%05x: ", offset_label);
|
|
|
+ index = LPFC_PCI_BAR_RD_SIZE;
|
|
|
+ while (index > 0) {
|
|
|
+ u32val = readl(mem_mapped_bar + offset_run);
|
|
|
+ len += snprintf(pbuffer+len, LPFC_PCI_BAR_RD_BUF_SIZE-len,
|
|
|
+ "%08x ", u32val);
|
|
|
+ offset_run += sizeof(uint32_t);
|
|
|
+ if (acc_range == LPFC_PCI_BAR_BROWSE) {
|
|
|
+ if (offset_run >= bar_size) {
|
|
|
+ len += snprintf(pbuffer+len,
|
|
|
+ LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (offset_run >= offset +
|
|
|
+ (acc_range * sizeof(uint32_t))) {
|
|
|
+ len += snprintf(pbuffer+len,
|
|
|
+ LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ index -= sizeof(uint32_t);
|
|
|
+ if (!index)
|
|
|
+ len += snprintf(pbuffer+len,
|
|
|
+ LPFC_PCI_BAR_RD_BUF_SIZE-len, "\n");
|
|
|
+ else if (!(index % (8 * sizeof(uint32_t)))) {
|
|
|
+ offset_label += (8 * sizeof(uint32_t));
|
|
|
+ len += snprintf(pbuffer+len,
|
|
|
+ LPFC_PCI_BAR_RD_BUF_SIZE-len,
|
|
|
+ "\n%05x: ", offset_label);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Set up the offset for next portion of pci bar read */
|
|
|
+ if (index == 0) {
|
|
|
+ idiag.offset.last_rd += LPFC_PCI_BAR_RD_SIZE;
|
|
|
+ if (acc_range == LPFC_PCI_BAR_BROWSE) {
|
|
|
+ if (idiag.offset.last_rd >= bar_size)
|
|
|
+ idiag.offset.last_rd = 0;
|
|
|
+ } else {
|
|
|
+ if (offset_run >= offset +
|
|
|
+ (acc_range * sizeof(uint32_t)))
|
|
|
+ idiag.offset.last_rd = offset;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (acc_range == LPFC_PCI_BAR_BROWSE)
|
|
|
+ idiag.offset.last_rd = 0;
|
|
|
+ else
|
|
|
+ idiag.offset.last_rd = offset;
|
|
|
+ }
|
|
|
+
|
|
|
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_idiag_baracc_write - Syntax check and set up idiag bar access commands
|
|
|
+ * @file: The file pointer to read from.
|
|
|
+ * @buf: The buffer to copy the user data from.
|
|
|
+ * @nbytes: The number of bytes to get.
|
|
|
+ * @ppos: The position in the file to start reading from.
|
|
|
+ *
|
|
|
+ * This routine get the debugfs idiag command struct from user space and
|
|
|
+ * then perform the syntax check for PCI bar memory mapped space read or
|
|
|
+ * write command accordingly. In the case of PCI bar memory mapped space
|
|
|
+ * read command, it sets up the command in the idiag command struct for
|
|
|
+ * the debugfs read operation. In the case of PCI bar memorpy mapped space
|
|
|
+ * write operation, it executes the write operation into the PCI bar memory
|
|
|
+ * mapped space accordingly.
|
|
|
+ *
|
|
|
+ * It returns the @nbytges passing in from debugfs user space when successful.
|
|
|
+ * In case of error conditions, it returns proper error code back to the user
|
|
|
+ * space.
|
|
|
+ */
|
|
|
+static ssize_t
|
|
|
+lpfc_idiag_baracc_write(struct file *file, const char __user *buf,
|
|
|
+ size_t nbytes, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct lpfc_debug *debug = file->private_data;
|
|
|
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
|
|
|
+ uint32_t bar_num, bar_size, offset, value, acc_range;
|
|
|
+ struct pci_dev *pdev;
|
|
|
+ void __iomem *mem_mapped_bar;
|
|
|
+ uint32_t if_type;
|
|
|
+ uint32_t u32val;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ pdev = phba->pcidev;
|
|
|
+ if (!pdev)
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ /* This is a user write operation */
|
|
|
+ debug->op = LPFC_IDIAG_OP_WR;
|
|
|
+
|
|
|
+ rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
|
|
|
+ bar_num = idiag.cmd.data[IDIAG_BARACC_BAR_NUM_INDX];
|
|
|
+
|
|
|
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
|
|
|
+ if ((bar_num != IDIAG_BARACC_BAR_0) &&
|
|
|
+ (bar_num != IDIAG_BARACC_BAR_1) &&
|
|
|
+ (bar_num != IDIAG_BARACC_BAR_2))
|
|
|
+ goto error_out;
|
|
|
+ } else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
|
|
|
+ if (bar_num != IDIAG_BARACC_BAR_0)
|
|
|
+ goto error_out;
|
|
|
+ } else
|
|
|
+ goto error_out;
|
|
|
+
|
|
|
+ if (if_type == LPFC_SLI_INTF_IF_TYPE_0) {
|
|
|
+ if (bar_num == IDIAG_BARACC_BAR_0) {
|
|
|
+ idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
|
|
|
+ LPFC_PCI_IF0_BAR0_SIZE;
|
|
|
+ mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
|
|
|
+ } else if (bar_num == IDIAG_BARACC_BAR_1) {
|
|
|
+ idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
|
|
|
+ LPFC_PCI_IF0_BAR1_SIZE;
|
|
|
+ mem_mapped_bar = phba->sli4_hba.ctrl_regs_memmap_p;
|
|
|
+ } else if (bar_num == IDIAG_BARACC_BAR_2) {
|
|
|
+ idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
|
|
|
+ LPFC_PCI_IF0_BAR2_SIZE;
|
|
|
+ mem_mapped_bar = phba->sli4_hba.drbl_regs_memmap_p;
|
|
|
+ } else
|
|
|
+ goto error_out;
|
|
|
+ } else if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
|
|
|
+ if (bar_num == IDIAG_BARACC_BAR_0) {
|
|
|
+ idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX] =
|
|
|
+ LPFC_PCI_IF2_BAR0_SIZE;
|
|
|
+ mem_mapped_bar = phba->sli4_hba.conf_regs_memmap_p;
|
|
|
+ } else
|
|
|
+ goto error_out;
|
|
|
+ } else
|
|
|
+ goto error_out;
|
|
|
+
|
|
|
+ offset = idiag.cmd.data[IDIAG_BARACC_OFF_SET_INDX];
|
|
|
+ if (offset % sizeof(uint32_t))
|
|
|
+ goto error_out;
|
|
|
+
|
|
|
+ bar_size = idiag.cmd.data[IDIAG_BARACC_BAR_SZE_INDX];
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_RD) {
|
|
|
+ /* Sanity check on PCI config read command line arguments */
|
|
|
+ if (rc != LPFC_PCI_BAR_RD_CMD_ARG)
|
|
|
+ goto error_out;
|
|
|
+ acc_range = idiag.cmd.data[IDIAG_BARACC_ACC_MOD_INDX];
|
|
|
+ if (acc_range == LPFC_PCI_BAR_BROWSE) {
|
|
|
+ if (offset > bar_size - sizeof(uint32_t))
|
|
|
+ goto error_out;
|
|
|
+ /* Starting offset to browse */
|
|
|
+ idiag.offset.last_rd = offset;
|
|
|
+ } else if (acc_range > SINGLE_WORD) {
|
|
|
+ if (offset + acc_range * sizeof(uint32_t) > bar_size)
|
|
|
+ goto error_out;
|
|
|
+ /* Starting offset to browse */
|
|
|
+ idiag.offset.last_rd = offset;
|
|
|
+ } else if (acc_range != SINGLE_WORD)
|
|
|
+ goto error_out;
|
|
|
+ } else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_WR ||
|
|
|
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_ST ||
|
|
|
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_CL) {
|
|
|
+ /* Sanity check on PCI bar write command line arguments */
|
|
|
+ if (rc != LPFC_PCI_BAR_WR_CMD_ARG)
|
|
|
+ goto error_out;
|
|
|
+ /* Write command to PCI bar space, read-modify-write */
|
|
|
+ acc_range = SINGLE_WORD;
|
|
|
+ value = idiag.cmd.data[IDIAG_BARACC_REG_VAL_INDX];
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_WR) {
|
|
|
+ writel(value, mem_mapped_bar + offset);
|
|
|
+ readl(mem_mapped_bar + offset);
|
|
|
+ }
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_ST) {
|
|
|
+ u32val = readl(mem_mapped_bar + offset);
|
|
|
+ u32val |= value;
|
|
|
+ writel(u32val, mem_mapped_bar + offset);
|
|
|
+ readl(mem_mapped_bar + offset);
|
|
|
+ }
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_BARACC_CL) {
|
|
|
+ u32val = readl(mem_mapped_bar + offset);
|
|
|
+ u32val &= ~value;
|
|
|
+ writel(u32val, mem_mapped_bar + offset);
|
|
|
+ readl(mem_mapped_bar + offset);
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ /* All other opecodes are illegal for now */
|
|
|
+ goto error_out;
|
|
|
+
|
|
|
+ return nbytes;
|
|
|
+error_out:
|
|
|
+ memset(&idiag, 0, sizeof(idiag));
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* lpfc_idiag_queinfo_read - idiag debugfs read queue information
|
|
|
* @file: The file pointer to read from.
|
|
@@ -1871,8 +2183,8 @@ lpfc_idiag_queacc_read(struct file *file, char __user *buf, size_t nbytes,
|
|
|
return 0;
|
|
|
|
|
|
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_RD) {
|
|
|
- index = idiag.cmd.data[2];
|
|
|
- count = idiag.cmd.data[3];
|
|
|
+ index = idiag.cmd.data[IDIAG_QUEACC_INDEX_INDX];
|
|
|
+ count = idiag.cmd.data[IDIAG_QUEACC_COUNT_INDX];
|
|
|
pque = (struct lpfc_queue *)idiag.ptr_private;
|
|
|
} else
|
|
|
return 0;
|
|
@@ -1944,12 +2256,12 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
|
|
|
return rc;
|
|
|
|
|
|
/* Get and sanity check on command feilds */
|
|
|
- quetp = idiag.cmd.data[0];
|
|
|
- queid = idiag.cmd.data[1];
|
|
|
- index = idiag.cmd.data[2];
|
|
|
- count = idiag.cmd.data[3];
|
|
|
- offset = idiag.cmd.data[4];
|
|
|
- value = idiag.cmd.data[5];
|
|
|
+ quetp = idiag.cmd.data[IDIAG_QUEACC_QUETP_INDX];
|
|
|
+ queid = idiag.cmd.data[IDIAG_QUEACC_QUEID_INDX];
|
|
|
+ index = idiag.cmd.data[IDIAG_QUEACC_INDEX_INDX];
|
|
|
+ count = idiag.cmd.data[IDIAG_QUEACC_COUNT_INDX];
|
|
|
+ offset = idiag.cmd.data[IDIAG_QUEACC_OFFST_INDX];
|
|
|
+ value = idiag.cmd.data[IDIAG_QUEACC_VALUE_INDX];
|
|
|
|
|
|
/* Sanity check on command line arguments */
|
|
|
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR ||
|
|
@@ -2218,7 +2530,7 @@ lpfc_idiag_drbacc_read(struct file *file, char __user *buf, size_t nbytes,
|
|
|
return 0;
|
|
|
|
|
|
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_RD)
|
|
|
- drb_reg_id = idiag.cmd.data[0];
|
|
|
+ drb_reg_id = idiag.cmd.data[IDIAG_DRBACC_REGID_INDX];
|
|
|
else
|
|
|
return 0;
|
|
|
|
|
@@ -2257,7 +2569,7 @@ lpfc_idiag_drbacc_write(struct file *file, const char __user *buf,
|
|
|
{
|
|
|
struct lpfc_debug *debug = file->private_data;
|
|
|
struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
|
|
|
- uint32_t drb_reg_id, value, reg_val;
|
|
|
+ uint32_t drb_reg_id, value, reg_val = 0;
|
|
|
void __iomem *drb_reg;
|
|
|
int rc;
|
|
|
|
|
@@ -2269,8 +2581,8 @@ lpfc_idiag_drbacc_write(struct file *file, const char __user *buf,
|
|
|
return rc;
|
|
|
|
|
|
/* Sanity check on command line arguments */
|
|
|
- drb_reg_id = idiag.cmd.data[0];
|
|
|
- value = idiag.cmd.data[1];
|
|
|
+ drb_reg_id = idiag.cmd.data[IDIAG_DRBACC_REGID_INDX];
|
|
|
+ value = idiag.cmd.data[IDIAG_DRBACC_VALUE_INDX];
|
|
|
|
|
|
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_WR ||
|
|
|
idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST ||
|
|
@@ -2330,63 +2642,736 @@ error_out:
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
-#undef lpfc_debugfs_op_disc_trc
|
|
|
-static const struct file_operations lpfc_debugfs_op_disc_trc = {
|
|
|
- .owner = THIS_MODULE,
|
|
|
- .open = lpfc_debugfs_disc_trc_open,
|
|
|
- .llseek = lpfc_debugfs_lseek,
|
|
|
- .read = lpfc_debugfs_read,
|
|
|
- .release = lpfc_debugfs_release,
|
|
|
-};
|
|
|
+/**
|
|
|
+ * lpfc_idiag_ctlacc_read_reg - idiag debugfs read a control registers
|
|
|
+ * @phba: The pointer to hba structure.
|
|
|
+ * @pbuffer: The pointer to the buffer to copy the data to.
|
|
|
+ * @len: The lenght of bytes to copied.
|
|
|
+ * @drbregid: The id to doorbell registers.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine reads a control register and copies its content to the
|
|
|
+ * user buffer pointed to by @pbuffer.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * This function returns the amount of data that was copied into @pbuffer.
|
|
|
+ **/
|
|
|
+static int
|
|
|
+lpfc_idiag_ctlacc_read_reg(struct lpfc_hba *phba, char *pbuffer,
|
|
|
+ int len, uint32_t ctlregid)
|
|
|
+{
|
|
|
|
|
|
-#undef lpfc_debugfs_op_nodelist
|
|
|
-static const struct file_operations lpfc_debugfs_op_nodelist = {
|
|
|
- .owner = THIS_MODULE,
|
|
|
- .open = lpfc_debugfs_nodelist_open,
|
|
|
- .llseek = lpfc_debugfs_lseek,
|
|
|
- .read = lpfc_debugfs_read,
|
|
|
- .release = lpfc_debugfs_release,
|
|
|
-};
|
|
|
+ if (!pbuffer)
|
|
|
+ return 0;
|
|
|
|
|
|
-#undef lpfc_debugfs_op_hbqinfo
|
|
|
-static const struct file_operations lpfc_debugfs_op_hbqinfo = {
|
|
|
- .owner = THIS_MODULE,
|
|
|
- .open = lpfc_debugfs_hbqinfo_open,
|
|
|
- .llseek = lpfc_debugfs_lseek,
|
|
|
- .read = lpfc_debugfs_read,
|
|
|
- .release = lpfc_debugfs_release,
|
|
|
-};
|
|
|
+ switch (ctlregid) {
|
|
|
+ case LPFC_CTL_PORT_SEM:
|
|
|
+ len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
|
|
|
+ "Port SemReg: 0x%08x\n",
|
|
|
+ readl(phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PORT_SEM_OFFSET));
|
|
|
+ break;
|
|
|
+ case LPFC_CTL_PORT_STA:
|
|
|
+ len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
|
|
|
+ "Port StaReg: 0x%08x\n",
|
|
|
+ readl(phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PORT_STA_OFFSET));
|
|
|
+ break;
|
|
|
+ case LPFC_CTL_PORT_CTL:
|
|
|
+ len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
|
|
|
+ "Port CtlReg: 0x%08x\n",
|
|
|
+ readl(phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PORT_CTL_OFFSET));
|
|
|
+ break;
|
|
|
+ case LPFC_CTL_PORT_ER1:
|
|
|
+ len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
|
|
|
+ "Port Er1Reg: 0x%08x\n",
|
|
|
+ readl(phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PORT_ER1_OFFSET));
|
|
|
+ break;
|
|
|
+ case LPFC_CTL_PORT_ER2:
|
|
|
+ len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
|
|
|
+ "Port Er2Reg: 0x%08x\n",
|
|
|
+ readl(phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PORT_ER2_OFFSET));
|
|
|
+ break;
|
|
|
+ case LPFC_CTL_PDEV_CTL:
|
|
|
+ len += snprintf(pbuffer+len, LPFC_CTL_ACC_BUF_SIZE-len,
|
|
|
+ "PDev CtlReg: 0x%08x\n",
|
|
|
+ readl(phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PDEV_CTL_OFFSET));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return len;
|
|
|
+}
|
|
|
|
|
|
-#undef lpfc_debugfs_op_dumpHBASlim
|
|
|
-static const struct file_operations lpfc_debugfs_op_dumpHBASlim = {
|
|
|
- .owner = THIS_MODULE,
|
|
|
- .open = lpfc_debugfs_dumpHBASlim_open,
|
|
|
- .llseek = lpfc_debugfs_lseek,
|
|
|
- .read = lpfc_debugfs_read,
|
|
|
- .release = lpfc_debugfs_release,
|
|
|
-};
|
|
|
+/**
|
|
|
+ * lpfc_idiag_ctlacc_read - idiag debugfs read port and device control register
|
|
|
+ * @file: The file pointer to read from.
|
|
|
+ * @buf: The buffer to copy the data to.
|
|
|
+ * @nbytes: The number of bytes to read.
|
|
|
+ * @ppos: The position in the file to start reading from.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine reads data from the @phba port and device registers according
|
|
|
+ * to the idiag command, and copies to user @buf.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * This function returns the amount of data that was read (this could be less
|
|
|
+ * than @nbytes if the end of the file was reached) or a negative error value.
|
|
|
+ **/
|
|
|
+static ssize_t
|
|
|
+lpfc_idiag_ctlacc_read(struct file *file, char __user *buf, size_t nbytes,
|
|
|
+ loff_t *ppos)
|
|
|
+{
|
|
|
+ struct lpfc_debug *debug = file->private_data;
|
|
|
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
|
|
|
+ uint32_t ctl_reg_id, i;
|
|
|
+ char *pbuffer;
|
|
|
+ int len = 0;
|
|
|
|
|
|
-#undef lpfc_debugfs_op_dumpHostSlim
|
|
|
-static const struct file_operations lpfc_debugfs_op_dumpHostSlim = {
|
|
|
- .owner = THIS_MODULE,
|
|
|
- .open = lpfc_debugfs_dumpHostSlim_open,
|
|
|
- .llseek = lpfc_debugfs_lseek,
|
|
|
- .read = lpfc_debugfs_read,
|
|
|
- .release = lpfc_debugfs_release,
|
|
|
-};
|
|
|
+ /* This is a user read operation */
|
|
|
+ debug->op = LPFC_IDIAG_OP_RD;
|
|
|
|
|
|
-#undef lpfc_debugfs_op_dumpData
|
|
|
-static const struct file_operations lpfc_debugfs_op_dumpData = {
|
|
|
- .owner = THIS_MODULE,
|
|
|
- .open = lpfc_debugfs_dumpData_open,
|
|
|
- .llseek = lpfc_debugfs_lseek,
|
|
|
- .read = lpfc_debugfs_read,
|
|
|
- .write = lpfc_debugfs_dumpDataDif_write,
|
|
|
- .release = lpfc_debugfs_dumpDataDif_release,
|
|
|
-};
|
|
|
+ if (!debug->buffer)
|
|
|
+ debug->buffer = kmalloc(LPFC_CTL_ACC_BUF_SIZE, GFP_KERNEL);
|
|
|
+ if (!debug->buffer)
|
|
|
+ return 0;
|
|
|
+ pbuffer = debug->buffer;
|
|
|
|
|
|
-#undef lpfc_debugfs_op_dumpDif
|
|
|
-static const struct file_operations lpfc_debugfs_op_dumpDif = {
|
|
|
+ if (*ppos)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_RD)
|
|
|
+ ctl_reg_id = idiag.cmd.data[IDIAG_CTLACC_REGID_INDX];
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (ctl_reg_id == LPFC_CTL_ACC_ALL)
|
|
|
+ for (i = 1; i <= LPFC_CTL_MAX; i++)
|
|
|
+ len = lpfc_idiag_ctlacc_read_reg(phba,
|
|
|
+ pbuffer, len, i);
|
|
|
+ else
|
|
|
+ len = lpfc_idiag_ctlacc_read_reg(phba,
|
|
|
+ pbuffer, len, ctl_reg_id);
|
|
|
+
|
|
|
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_idiag_ctlacc_write - Syntax check and set up idiag ctlacc commands
|
|
|
+ * @file: The file pointer to read from.
|
|
|
+ * @buf: The buffer to copy the user data from.
|
|
|
+ * @nbytes: The number of bytes to get.
|
|
|
+ * @ppos: The position in the file to start reading from.
|
|
|
+ *
|
|
|
+ * This routine get the debugfs idiag command struct from user space and then
|
|
|
+ * perform the syntax check for port and device control register read (dump)
|
|
|
+ * or write (set) command accordingly.
|
|
|
+ *
|
|
|
+ * It returns the @nbytges passing in from debugfs user space when successful.
|
|
|
+ * In case of error conditions, it returns proper error code back to the user
|
|
|
+ * space.
|
|
|
+ **/
|
|
|
+static ssize_t
|
|
|
+lpfc_idiag_ctlacc_write(struct file *file, const char __user *buf,
|
|
|
+ size_t nbytes, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct lpfc_debug *debug = file->private_data;
|
|
|
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
|
|
|
+ uint32_t ctl_reg_id, value, reg_val = 0;
|
|
|
+ void __iomem *ctl_reg;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ /* This is a user write operation */
|
|
|
+ debug->op = LPFC_IDIAG_OP_WR;
|
|
|
+
|
|
|
+ rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ /* Sanity check on command line arguments */
|
|
|
+ ctl_reg_id = idiag.cmd.data[IDIAG_CTLACC_REGID_INDX];
|
|
|
+ value = idiag.cmd.data[IDIAG_CTLACC_VALUE_INDX];
|
|
|
+
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR ||
|
|
|
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST ||
|
|
|
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
|
|
|
+ if (rc != LPFC_CTL_ACC_WR_CMD_ARG)
|
|
|
+ goto error_out;
|
|
|
+ if (ctl_reg_id > LPFC_CTL_MAX)
|
|
|
+ goto error_out;
|
|
|
+ } else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_RD) {
|
|
|
+ if (rc != LPFC_CTL_ACC_RD_CMD_ARG)
|
|
|
+ goto error_out;
|
|
|
+ if ((ctl_reg_id > LPFC_CTL_MAX) &&
|
|
|
+ (ctl_reg_id != LPFC_CTL_ACC_ALL))
|
|
|
+ goto error_out;
|
|
|
+ } else
|
|
|
+ goto error_out;
|
|
|
+
|
|
|
+ /* Perform the write access operation */
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR ||
|
|
|
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST ||
|
|
|
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
|
|
|
+ switch (ctl_reg_id) {
|
|
|
+ case LPFC_CTL_PORT_SEM:
|
|
|
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PORT_SEM_OFFSET;
|
|
|
+ break;
|
|
|
+ case LPFC_CTL_PORT_STA:
|
|
|
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PORT_STA_OFFSET;
|
|
|
+ break;
|
|
|
+ case LPFC_CTL_PORT_CTL:
|
|
|
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PORT_CTL_OFFSET;
|
|
|
+ break;
|
|
|
+ case LPFC_CTL_PORT_ER1:
|
|
|
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PORT_ER1_OFFSET;
|
|
|
+ break;
|
|
|
+ case LPFC_CTL_PORT_ER2:
|
|
|
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PORT_ER2_OFFSET;
|
|
|
+ break;
|
|
|
+ case LPFC_CTL_PDEV_CTL:
|
|
|
+ ctl_reg = phba->sli4_hba.conf_regs_memmap_p +
|
|
|
+ LPFC_CTL_PDEV_CTL_OFFSET;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ goto error_out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_WR)
|
|
|
+ reg_val = value;
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_ST) {
|
|
|
+ reg_val = readl(ctl_reg);
|
|
|
+ reg_val |= value;
|
|
|
+ }
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_CTLACC_CL) {
|
|
|
+ reg_val = readl(ctl_reg);
|
|
|
+ reg_val &= ~value;
|
|
|
+ }
|
|
|
+ writel(reg_val, ctl_reg);
|
|
|
+ readl(ctl_reg); /* flush */
|
|
|
+ }
|
|
|
+ return nbytes;
|
|
|
+
|
|
|
+error_out:
|
|
|
+ /* Clean out command structure on command error out */
|
|
|
+ memset(&idiag, 0, sizeof(idiag));
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_idiag_mbxacc_get_setup - idiag debugfs get mailbox access setup
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @pbuffer: Pointer to data buffer.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine gets the driver mailbox access debugfs setup information.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * This function returns the amount of data that was read (this could be less
|
|
|
+ * than @nbytes if the end of the file was reached) or a negative error value.
|
|
|
+ **/
|
|
|
+static int
|
|
|
+lpfc_idiag_mbxacc_get_setup(struct lpfc_hba *phba, char *pbuffer)
|
|
|
+{
|
|
|
+ uint32_t mbx_dump_map, mbx_dump_cnt, mbx_word_cnt, mbx_mbox_cmd;
|
|
|
+ int len = 0;
|
|
|
+
|
|
|
+ mbx_mbox_cmd = idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
|
|
|
+ mbx_dump_map = idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
|
|
|
+ mbx_dump_cnt = idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
|
|
|
+ mbx_word_cnt = idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
|
|
|
+ "mbx_dump_map: 0x%08x\n", mbx_dump_map);
|
|
|
+ len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
|
|
|
+ "mbx_dump_cnt: %04d\n", mbx_dump_cnt);
|
|
|
+ len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
|
|
|
+ "mbx_word_cnt: %04d\n", mbx_word_cnt);
|
|
|
+ len += snprintf(pbuffer+len, LPFC_MBX_ACC_BUF_SIZE-len,
|
|
|
+ "mbx_mbox_cmd: 0x%02x\n", mbx_mbox_cmd);
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_idiag_mbxacc_read - idiag debugfs read on mailbox access
|
|
|
+ * @file: The file pointer to read from.
|
|
|
+ * @buf: The buffer to copy the data to.
|
|
|
+ * @nbytes: The number of bytes to read.
|
|
|
+ * @ppos: The position in the file to start reading from.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine reads data from the @phba driver mailbox access debugfs setup
|
|
|
+ * information.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * This function returns the amount of data that was read (this could be less
|
|
|
+ * than @nbytes if the end of the file was reached) or a negative error value.
|
|
|
+ **/
|
|
|
+static ssize_t
|
|
|
+lpfc_idiag_mbxacc_read(struct file *file, char __user *buf, size_t nbytes,
|
|
|
+ loff_t *ppos)
|
|
|
+{
|
|
|
+ struct lpfc_debug *debug = file->private_data;
|
|
|
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
|
|
|
+ char *pbuffer;
|
|
|
+ int len = 0;
|
|
|
+
|
|
|
+ /* This is a user read operation */
|
|
|
+ debug->op = LPFC_IDIAG_OP_RD;
|
|
|
+
|
|
|
+ if (!debug->buffer)
|
|
|
+ debug->buffer = kmalloc(LPFC_MBX_ACC_BUF_SIZE, GFP_KERNEL);
|
|
|
+ if (!debug->buffer)
|
|
|
+ return 0;
|
|
|
+ pbuffer = debug->buffer;
|
|
|
+
|
|
|
+ if (*ppos)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if ((idiag.cmd.opcode != LPFC_IDIAG_CMD_MBXACC_DP) &&
|
|
|
+ (idiag.cmd.opcode != LPFC_IDIAG_BSG_MBXACC_DP))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ len = lpfc_idiag_mbxacc_get_setup(phba, pbuffer);
|
|
|
+
|
|
|
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_idiag_mbxacc_write - Syntax check and set up idiag mbxacc commands
|
|
|
+ * @file: The file pointer to read from.
|
|
|
+ * @buf: The buffer to copy the user data from.
|
|
|
+ * @nbytes: The number of bytes to get.
|
|
|
+ * @ppos: The position in the file to start reading from.
|
|
|
+ *
|
|
|
+ * This routine get the debugfs idiag command struct from user space and then
|
|
|
+ * perform the syntax check for driver mailbox command (dump) and sets up the
|
|
|
+ * necessary states in the idiag command struct accordingly.
|
|
|
+ *
|
|
|
+ * It returns the @nbytges passing in from debugfs user space when successful.
|
|
|
+ * In case of error conditions, it returns proper error code back to the user
|
|
|
+ * space.
|
|
|
+ **/
|
|
|
+static ssize_t
|
|
|
+lpfc_idiag_mbxacc_write(struct file *file, const char __user *buf,
|
|
|
+ size_t nbytes, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct lpfc_debug *debug = file->private_data;
|
|
|
+ uint32_t mbx_dump_map, mbx_dump_cnt, mbx_word_cnt, mbx_mbox_cmd;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ /* This is a user write operation */
|
|
|
+ debug->op = LPFC_IDIAG_OP_WR;
|
|
|
+
|
|
|
+ rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ /* Sanity check on command line arguments */
|
|
|
+ mbx_mbox_cmd = idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
|
|
|
+ mbx_dump_map = idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
|
|
|
+ mbx_dump_cnt = idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
|
|
|
+ mbx_word_cnt = idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
|
|
|
+
|
|
|
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_MBXACC_DP) {
|
|
|
+ if (!(mbx_dump_map & LPFC_MBX_DMP_MBX_ALL))
|
|
|
+ goto error_out;
|
|
|
+ if ((mbx_dump_map & ~LPFC_MBX_DMP_MBX_ALL) &&
|
|
|
+ (mbx_dump_map != LPFC_MBX_DMP_ALL))
|
|
|
+ goto error_out;
|
|
|
+ if (mbx_word_cnt > sizeof(MAILBOX_t))
|
|
|
+ goto error_out;
|
|
|
+ } else if (idiag.cmd.opcode == LPFC_IDIAG_BSG_MBXACC_DP) {
|
|
|
+ if (!(mbx_dump_map & LPFC_BSG_DMP_MBX_ALL))
|
|
|
+ goto error_out;
|
|
|
+ if ((mbx_dump_map & ~LPFC_BSG_DMP_MBX_ALL) &&
|
|
|
+ (mbx_dump_map != LPFC_MBX_DMP_ALL))
|
|
|
+ goto error_out;
|
|
|
+ if (mbx_word_cnt > (BSG_MBOX_SIZE)/4)
|
|
|
+ goto error_out;
|
|
|
+ if (mbx_mbox_cmd != 0x9b)
|
|
|
+ goto error_out;
|
|
|
+ } else
|
|
|
+ goto error_out;
|
|
|
+
|
|
|
+ if (mbx_word_cnt == 0)
|
|
|
+ goto error_out;
|
|
|
+ if (rc != LPFC_MBX_DMP_ARG)
|
|
|
+ goto error_out;
|
|
|
+ if (mbx_mbox_cmd & ~0xff)
|
|
|
+ goto error_out;
|
|
|
+
|
|
|
+ /* condition for stop mailbox dump */
|
|
|
+ if (mbx_dump_cnt == 0)
|
|
|
+ goto reset_out;
|
|
|
+
|
|
|
+ return nbytes;
|
|
|
+
|
|
|
+reset_out:
|
|
|
+ /* Clean out command structure on command error out */
|
|
|
+ memset(&idiag, 0, sizeof(idiag));
|
|
|
+ return nbytes;
|
|
|
+
|
|
|
+error_out:
|
|
|
+ /* Clean out command structure on command error out */
|
|
|
+ memset(&idiag, 0, sizeof(idiag));
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_idiag_extacc_avail_get - get the available extents information
|
|
|
+ * @phba: pointer to lpfc hba data structure.
|
|
|
+ * @pbuffer: pointer to internal buffer.
|
|
|
+ * @len: length into the internal buffer data has been copied.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine is to get the available extent information.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * overall lenth of the data read into the internal buffer.
|
|
|
+ **/
|
|
|
+static int
|
|
|
+lpfc_idiag_extacc_avail_get(struct lpfc_hba *phba, char *pbuffer, int len)
|
|
|
+{
|
|
|
+ uint16_t ext_cnt, ext_size;
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\nAvailable Extents Information:\n");
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tPort Available VPI extents: ");
|
|
|
+ lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_VPI,
|
|
|
+ &ext_cnt, &ext_size);
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "Count %3d, Size %3d\n", ext_cnt, ext_size);
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tPort Available VFI extents: ");
|
|
|
+ lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_VFI,
|
|
|
+ &ext_cnt, &ext_size);
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "Count %3d, Size %3d\n", ext_cnt, ext_size);
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tPort Available RPI extents: ");
|
|
|
+ lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_RPI,
|
|
|
+ &ext_cnt, &ext_size);
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "Count %3d, Size %3d\n", ext_cnt, ext_size);
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tPort Available XRI extents: ");
|
|
|
+ lpfc_sli4_get_avail_extnt_rsrc(phba, LPFC_RSC_TYPE_FCOE_XRI,
|
|
|
+ &ext_cnt, &ext_size);
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "Count %3d, Size %3d\n", ext_cnt, ext_size);
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_idiag_extacc_alloc_get - get the allocated extents information
|
|
|
+ * @phba: pointer to lpfc hba data structure.
|
|
|
+ * @pbuffer: pointer to internal buffer.
|
|
|
+ * @len: length into the internal buffer data has been copied.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine is to get the allocated extent information.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * overall lenth of the data read into the internal buffer.
|
|
|
+ **/
|
|
|
+static int
|
|
|
+lpfc_idiag_extacc_alloc_get(struct lpfc_hba *phba, char *pbuffer, int len)
|
|
|
+{
|
|
|
+ uint16_t ext_cnt, ext_size;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\nAllocated Extents Information:\n");
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tHost Allocated VPI extents: ");
|
|
|
+ rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_VPI,
|
|
|
+ &ext_cnt, &ext_size);
|
|
|
+ if (!rc)
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "Port %d Extent %3d, Size %3d\n",
|
|
|
+ phba->brd_no, ext_cnt, ext_size);
|
|
|
+ else
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "N/A\n");
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tHost Allocated VFI extents: ");
|
|
|
+ rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_VFI,
|
|
|
+ &ext_cnt, &ext_size);
|
|
|
+ if (!rc)
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "Port %d Extent %3d, Size %3d\n",
|
|
|
+ phba->brd_no, ext_cnt, ext_size);
|
|
|
+ else
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "N/A\n");
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tHost Allocated RPI extents: ");
|
|
|
+ rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_RPI,
|
|
|
+ &ext_cnt, &ext_size);
|
|
|
+ if (!rc)
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "Port %d Extent %3d, Size %3d\n",
|
|
|
+ phba->brd_no, ext_cnt, ext_size);
|
|
|
+ else
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "N/A\n");
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tHost Allocated XRI extents: ");
|
|
|
+ rc = lpfc_sli4_get_allocated_extnts(phba, LPFC_RSC_TYPE_FCOE_XRI,
|
|
|
+ &ext_cnt, &ext_size);
|
|
|
+ if (!rc)
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "Port %d Extent %3d, Size %3d\n",
|
|
|
+ phba->brd_no, ext_cnt, ext_size);
|
|
|
+ else
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "N/A\n");
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_idiag_extacc_drivr_get - get driver extent information
|
|
|
+ * @phba: pointer to lpfc hba data structure.
|
|
|
+ * @pbuffer: pointer to internal buffer.
|
|
|
+ * @len: length into the internal buffer data has been copied.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine is to get the driver extent information.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * overall lenth of the data read into the internal buffer.
|
|
|
+ **/
|
|
|
+static int
|
|
|
+lpfc_idiag_extacc_drivr_get(struct lpfc_hba *phba, char *pbuffer, int len)
|
|
|
+{
|
|
|
+ struct lpfc_rsrc_blks *rsrc_blks;
|
|
|
+ int index;
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\nDriver Extents Information:\n");
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tVPI extents:\n");
|
|
|
+ index = 0;
|
|
|
+ list_for_each_entry(rsrc_blks, &phba->lpfc_vpi_blk_list, list) {
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\t\tBlock %3d: Start %4d, Count %4d\n",
|
|
|
+ index, rsrc_blks->rsrc_start,
|
|
|
+ rsrc_blks->rsrc_size);
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tVFI extents:\n");
|
|
|
+ index = 0;
|
|
|
+ list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_vfi_blk_list,
|
|
|
+ list) {
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\t\tBlock %3d: Start %4d, Count %4d\n",
|
|
|
+ index, rsrc_blks->rsrc_start,
|
|
|
+ rsrc_blks->rsrc_size);
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tRPI extents:\n");
|
|
|
+ index = 0;
|
|
|
+ list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_rpi_blk_list,
|
|
|
+ list) {
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\t\tBlock %3d: Start %4d, Count %4d\n",
|
|
|
+ index, rsrc_blks->rsrc_start,
|
|
|
+ rsrc_blks->rsrc_size);
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\tXRI extents:\n");
|
|
|
+ index = 0;
|
|
|
+ list_for_each_entry(rsrc_blks, &phba->sli4_hba.lpfc_xri_blk_list,
|
|
|
+ list) {
|
|
|
+ len += snprintf(pbuffer+len, LPFC_EXT_ACC_BUF_SIZE-len,
|
|
|
+ "\t\tBlock %3d: Start %4d, Count %4d\n",
|
|
|
+ index, rsrc_blks->rsrc_start,
|
|
|
+ rsrc_blks->rsrc_size);
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_idiag_extacc_write - Syntax check and set up idiag extacc commands
|
|
|
+ * @file: The file pointer to read from.
|
|
|
+ * @buf: The buffer to copy the user data from.
|
|
|
+ * @nbytes: The number of bytes to get.
|
|
|
+ * @ppos: The position in the file to start reading from.
|
|
|
+ *
|
|
|
+ * This routine get the debugfs idiag command struct from user space and then
|
|
|
+ * perform the syntax check for extent information access commands and sets
|
|
|
+ * up the necessary states in the idiag command struct accordingly.
|
|
|
+ *
|
|
|
+ * It returns the @nbytges passing in from debugfs user space when successful.
|
|
|
+ * In case of error conditions, it returns proper error code back to the user
|
|
|
+ * space.
|
|
|
+ **/
|
|
|
+static ssize_t
|
|
|
+lpfc_idiag_extacc_write(struct file *file, const char __user *buf,
|
|
|
+ size_t nbytes, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct lpfc_debug *debug = file->private_data;
|
|
|
+ uint32_t ext_map;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ /* This is a user write operation */
|
|
|
+ debug->op = LPFC_IDIAG_OP_WR;
|
|
|
+
|
|
|
+ rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ ext_map = idiag.cmd.data[IDIAG_EXTACC_EXMAP_INDX];
|
|
|
+
|
|
|
+ if (idiag.cmd.opcode != LPFC_IDIAG_CMD_EXTACC_RD)
|
|
|
+ goto error_out;
|
|
|
+ if (rc != LPFC_EXT_ACC_CMD_ARG)
|
|
|
+ goto error_out;
|
|
|
+ if (!(ext_map & LPFC_EXT_ACC_ALL))
|
|
|
+ goto error_out;
|
|
|
+
|
|
|
+ return nbytes;
|
|
|
+error_out:
|
|
|
+ /* Clean out command structure on command error out */
|
|
|
+ memset(&idiag, 0, sizeof(idiag));
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_idiag_extacc_read - idiag debugfs read access to extent information
|
|
|
+ * @file: The file pointer to read from.
|
|
|
+ * @buf: The buffer to copy the data to.
|
|
|
+ * @nbytes: The number of bytes to read.
|
|
|
+ * @ppos: The position in the file to start reading from.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine reads data from the proper extent information according to
|
|
|
+ * the idiag command, and copies to user @buf.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * This function returns the amount of data that was read (this could be less
|
|
|
+ * than @nbytes if the end of the file was reached) or a negative error value.
|
|
|
+ **/
|
|
|
+static ssize_t
|
|
|
+lpfc_idiag_extacc_read(struct file *file, char __user *buf, size_t nbytes,
|
|
|
+ loff_t *ppos)
|
|
|
+{
|
|
|
+ struct lpfc_debug *debug = file->private_data;
|
|
|
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
|
|
|
+ char *pbuffer;
|
|
|
+ uint32_t ext_map;
|
|
|
+ int len = 0;
|
|
|
+
|
|
|
+ /* This is a user read operation */
|
|
|
+ debug->op = LPFC_IDIAG_OP_RD;
|
|
|
+
|
|
|
+ if (!debug->buffer)
|
|
|
+ debug->buffer = kmalloc(LPFC_EXT_ACC_BUF_SIZE, GFP_KERNEL);
|
|
|
+ if (!debug->buffer)
|
|
|
+ return 0;
|
|
|
+ pbuffer = debug->buffer;
|
|
|
+ if (*ppos)
|
|
|
+ return 0;
|
|
|
+ if (idiag.cmd.opcode != LPFC_IDIAG_CMD_EXTACC_RD)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ext_map = idiag.cmd.data[IDIAG_EXTACC_EXMAP_INDX];
|
|
|
+ if (ext_map & LPFC_EXT_ACC_AVAIL)
|
|
|
+ len = lpfc_idiag_extacc_avail_get(phba, pbuffer, len);
|
|
|
+ if (ext_map & LPFC_EXT_ACC_ALLOC)
|
|
|
+ len = lpfc_idiag_extacc_alloc_get(phba, pbuffer, len);
|
|
|
+ if (ext_map & LPFC_EXT_ACC_DRIVR)
|
|
|
+ len = lpfc_idiag_extacc_drivr_get(phba, pbuffer, len);
|
|
|
+
|
|
|
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
|
|
|
+}
|
|
|
+
|
|
|
+#undef lpfc_debugfs_op_disc_trc
|
|
|
+static const struct file_operations lpfc_debugfs_op_disc_trc = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = lpfc_debugfs_disc_trc_open,
|
|
|
+ .llseek = lpfc_debugfs_lseek,
|
|
|
+ .read = lpfc_debugfs_read,
|
|
|
+ .release = lpfc_debugfs_release,
|
|
|
+};
|
|
|
+
|
|
|
+#undef lpfc_debugfs_op_nodelist
|
|
|
+static const struct file_operations lpfc_debugfs_op_nodelist = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = lpfc_debugfs_nodelist_open,
|
|
|
+ .llseek = lpfc_debugfs_lseek,
|
|
|
+ .read = lpfc_debugfs_read,
|
|
|
+ .release = lpfc_debugfs_release,
|
|
|
+};
|
|
|
+
|
|
|
+#undef lpfc_debugfs_op_hbqinfo
|
|
|
+static const struct file_operations lpfc_debugfs_op_hbqinfo = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = lpfc_debugfs_hbqinfo_open,
|
|
|
+ .llseek = lpfc_debugfs_lseek,
|
|
|
+ .read = lpfc_debugfs_read,
|
|
|
+ .release = lpfc_debugfs_release,
|
|
|
+};
|
|
|
+
|
|
|
+#undef lpfc_debugfs_op_dumpHBASlim
|
|
|
+static const struct file_operations lpfc_debugfs_op_dumpHBASlim = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = lpfc_debugfs_dumpHBASlim_open,
|
|
|
+ .llseek = lpfc_debugfs_lseek,
|
|
|
+ .read = lpfc_debugfs_read,
|
|
|
+ .release = lpfc_debugfs_release,
|
|
|
+};
|
|
|
+
|
|
|
+#undef lpfc_debugfs_op_dumpHostSlim
|
|
|
+static const struct file_operations lpfc_debugfs_op_dumpHostSlim = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = lpfc_debugfs_dumpHostSlim_open,
|
|
|
+ .llseek = lpfc_debugfs_lseek,
|
|
|
+ .read = lpfc_debugfs_read,
|
|
|
+ .release = lpfc_debugfs_release,
|
|
|
+};
|
|
|
+
|
|
|
+#undef lpfc_debugfs_op_dumpData
|
|
|
+static const struct file_operations lpfc_debugfs_op_dumpData = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = lpfc_debugfs_dumpData_open,
|
|
|
+ .llseek = lpfc_debugfs_lseek,
|
|
|
+ .read = lpfc_debugfs_read,
|
|
|
+ .write = lpfc_debugfs_dumpDataDif_write,
|
|
|
+ .release = lpfc_debugfs_dumpDataDif_release,
|
|
|
+};
|
|
|
+
|
|
|
+#undef lpfc_debugfs_op_dumpDif
|
|
|
+static const struct file_operations lpfc_debugfs_op_dumpDif = {
|
|
|
.owner = THIS_MODULE,
|
|
|
.open = lpfc_debugfs_dumpDif_open,
|
|
|
.llseek = lpfc_debugfs_lseek,
|
|
@@ -2420,6 +3405,16 @@ static const struct file_operations lpfc_idiag_op_pciCfg = {
|
|
|
.release = lpfc_idiag_cmd_release,
|
|
|
};
|
|
|
|
|
|
+#undef lpfc_idiag_op_barAcc
|
|
|
+static const struct file_operations lpfc_idiag_op_barAcc = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = lpfc_idiag_open,
|
|
|
+ .llseek = lpfc_debugfs_lseek,
|
|
|
+ .read = lpfc_idiag_baracc_read,
|
|
|
+ .write = lpfc_idiag_baracc_write,
|
|
|
+ .release = lpfc_idiag_cmd_release,
|
|
|
+};
|
|
|
+
|
|
|
#undef lpfc_idiag_op_queInfo
|
|
|
static const struct file_operations lpfc_idiag_op_queInfo = {
|
|
|
.owner = THIS_MODULE,
|
|
@@ -2428,7 +3423,7 @@ static const struct file_operations lpfc_idiag_op_queInfo = {
|
|
|
.release = lpfc_idiag_release,
|
|
|
};
|
|
|
|
|
|
-#undef lpfc_idiag_op_queacc
|
|
|
+#undef lpfc_idiag_op_queAcc
|
|
|
static const struct file_operations lpfc_idiag_op_queAcc = {
|
|
|
.owner = THIS_MODULE,
|
|
|
.open = lpfc_idiag_open,
|
|
@@ -2438,7 +3433,7 @@ static const struct file_operations lpfc_idiag_op_queAcc = {
|
|
|
.release = lpfc_idiag_cmd_release,
|
|
|
};
|
|
|
|
|
|
-#undef lpfc_idiag_op_drbacc
|
|
|
+#undef lpfc_idiag_op_drbAcc
|
|
|
static const struct file_operations lpfc_idiag_op_drbAcc = {
|
|
|
.owner = THIS_MODULE,
|
|
|
.open = lpfc_idiag_open,
|
|
@@ -2448,8 +3443,234 @@ static const struct file_operations lpfc_idiag_op_drbAcc = {
|
|
|
.release = lpfc_idiag_cmd_release,
|
|
|
};
|
|
|
|
|
|
+#undef lpfc_idiag_op_ctlAcc
|
|
|
+static const struct file_operations lpfc_idiag_op_ctlAcc = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = lpfc_idiag_open,
|
|
|
+ .llseek = lpfc_debugfs_lseek,
|
|
|
+ .read = lpfc_idiag_ctlacc_read,
|
|
|
+ .write = lpfc_idiag_ctlacc_write,
|
|
|
+ .release = lpfc_idiag_cmd_release,
|
|
|
+};
|
|
|
+
|
|
|
+#undef lpfc_idiag_op_mbxAcc
|
|
|
+static const struct file_operations lpfc_idiag_op_mbxAcc = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = lpfc_idiag_open,
|
|
|
+ .llseek = lpfc_debugfs_lseek,
|
|
|
+ .read = lpfc_idiag_mbxacc_read,
|
|
|
+ .write = lpfc_idiag_mbxacc_write,
|
|
|
+ .release = lpfc_idiag_cmd_release,
|
|
|
+};
|
|
|
+
|
|
|
+#undef lpfc_idiag_op_extAcc
|
|
|
+static const struct file_operations lpfc_idiag_op_extAcc = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = lpfc_idiag_open,
|
|
|
+ .llseek = lpfc_debugfs_lseek,
|
|
|
+ .read = lpfc_idiag_extacc_read,
|
|
|
+ .write = lpfc_idiag_extacc_write,
|
|
|
+ .release = lpfc_idiag_cmd_release,
|
|
|
+};
|
|
|
+
|
|
|
#endif
|
|
|
|
|
|
+/* lpfc_idiag_mbxacc_dump_bsg_mbox - idiag debugfs dump bsg mailbox command
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @dmabuf: Pointer to a DMA buffer descriptor.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine dump a bsg pass-through non-embedded mailbox command with
|
|
|
+ * external buffer.
|
|
|
+ **/
|
|
|
+void
|
|
|
+lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp,
|
|
|
+ enum mbox_type mbox_tp, enum dma_type dma_tp,
|
|
|
+ enum sta_type sta_tp,
|
|
|
+ struct lpfc_dmabuf *dmabuf, uint32_t ext_buf)
|
|
|
+{
|
|
|
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
|
|
|
+ uint32_t *mbx_mbox_cmd, *mbx_dump_map, *mbx_dump_cnt, *mbx_word_cnt;
|
|
|
+ char line_buf[LPFC_MBX_ACC_LBUF_SZ];
|
|
|
+ int len = 0;
|
|
|
+ uint32_t do_dump = 0;
|
|
|
+ uint32_t *pword;
|
|
|
+ uint32_t i;
|
|
|
+
|
|
|
+ if (idiag.cmd.opcode != LPFC_IDIAG_BSG_MBXACC_DP)
|
|
|
+ return;
|
|
|
+
|
|
|
+ mbx_mbox_cmd = &idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
|
|
|
+ mbx_dump_map = &idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
|
|
|
+ mbx_dump_cnt = &idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
|
|
|
+ mbx_word_cnt = &idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
|
|
|
+
|
|
|
+ if (!(*mbx_dump_map & LPFC_MBX_DMP_ALL) ||
|
|
|
+ (*mbx_dump_cnt == 0) ||
|
|
|
+ (*mbx_word_cnt == 0))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (*mbx_mbox_cmd != 0x9B)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if ((mbox_tp == mbox_rd) && (dma_tp == dma_mbox)) {
|
|
|
+ if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_MBX) {
|
|
|
+ do_dump |= LPFC_BSG_DMP_MBX_RD_MBX;
|
|
|
+ printk(KERN_ERR "\nRead mbox command (x%x), "
|
|
|
+ "nemb:0x%x, extbuf_cnt:%d:\n",
|
|
|
+ sta_tp, nemb_tp, ext_buf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((mbox_tp == mbox_rd) && (dma_tp == dma_ebuf)) {
|
|
|
+ if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_BUF) {
|
|
|
+ do_dump |= LPFC_BSG_DMP_MBX_RD_BUF;
|
|
|
+ printk(KERN_ERR "\nRead mbox buffer (x%x), "
|
|
|
+ "nemb:0x%x, extbuf_seq:%d:\n",
|
|
|
+ sta_tp, nemb_tp, ext_buf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((mbox_tp == mbox_wr) && (dma_tp == dma_mbox)) {
|
|
|
+ if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_MBX) {
|
|
|
+ do_dump |= LPFC_BSG_DMP_MBX_WR_MBX;
|
|
|
+ printk(KERN_ERR "\nWrite mbox command (x%x), "
|
|
|
+ "nemb:0x%x, extbuf_cnt:%d:\n",
|
|
|
+ sta_tp, nemb_tp, ext_buf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((mbox_tp == mbox_wr) && (dma_tp == dma_ebuf)) {
|
|
|
+ if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_BUF) {
|
|
|
+ do_dump |= LPFC_BSG_DMP_MBX_WR_BUF;
|
|
|
+ printk(KERN_ERR "\nWrite mbox buffer (x%x), "
|
|
|
+ "nemb:0x%x, extbuf_seq:%d:\n",
|
|
|
+ sta_tp, nemb_tp, ext_buf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* dump buffer content */
|
|
|
+ if (do_dump) {
|
|
|
+ pword = (uint32_t *)dmabuf->virt;
|
|
|
+ for (i = 0; i < *mbx_word_cnt; i++) {
|
|
|
+ if (!(i % 8)) {
|
|
|
+ if (i != 0)
|
|
|
+ printk(KERN_ERR "%s\n", line_buf);
|
|
|
+ len = 0;
|
|
|
+ len += snprintf(line_buf+len,
|
|
|
+ LPFC_MBX_ACC_LBUF_SZ-len,
|
|
|
+ "%03d: ", i);
|
|
|
+ }
|
|
|
+ len += snprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len,
|
|
|
+ "%08x ", (uint32_t)*pword);
|
|
|
+ pword++;
|
|
|
+ }
|
|
|
+ if ((i - 1) % 8)
|
|
|
+ printk(KERN_ERR "%s\n", line_buf);
|
|
|
+ (*mbx_dump_cnt)--;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Clean out command structure on reaching dump count */
|
|
|
+ if (*mbx_dump_cnt == 0)
|
|
|
+ memset(&idiag, 0, sizeof(idiag));
|
|
|
+ return;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+/* lpfc_idiag_mbxacc_dump_issue_mbox - idiag debugfs dump issue mailbox command
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @dmabuf: Pointer to a DMA buffer descriptor.
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * This routine dump a pass-through non-embedded mailbox command from issue
|
|
|
+ * mailbox command.
|
|
|
+ **/
|
|
|
+void
|
|
|
+lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *phba, MAILBOX_t *pmbox)
|
|
|
+{
|
|
|
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
|
|
|
+ uint32_t *mbx_dump_map, *mbx_dump_cnt, *mbx_word_cnt, *mbx_mbox_cmd;
|
|
|
+ char line_buf[LPFC_MBX_ACC_LBUF_SZ];
|
|
|
+ int len = 0;
|
|
|
+ uint32_t *pword;
|
|
|
+ uint8_t *pbyte;
|
|
|
+ uint32_t i, j;
|
|
|
+
|
|
|
+ if (idiag.cmd.opcode != LPFC_IDIAG_CMD_MBXACC_DP)
|
|
|
+ return;
|
|
|
+
|
|
|
+ mbx_mbox_cmd = &idiag.cmd.data[IDIAG_MBXACC_MBCMD_INDX];
|
|
|
+ mbx_dump_map = &idiag.cmd.data[IDIAG_MBXACC_DPMAP_INDX];
|
|
|
+ mbx_dump_cnt = &idiag.cmd.data[IDIAG_MBXACC_DPCNT_INDX];
|
|
|
+ mbx_word_cnt = &idiag.cmd.data[IDIAG_MBXACC_WDCNT_INDX];
|
|
|
+
|
|
|
+ if (!(*mbx_dump_map & LPFC_MBX_DMP_MBX_ALL) ||
|
|
|
+ (*mbx_dump_cnt == 0) ||
|
|
|
+ (*mbx_word_cnt == 0))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if ((*mbx_mbox_cmd != LPFC_MBX_ALL_CMD) &&
|
|
|
+ (*mbx_mbox_cmd != pmbox->mbxCommand))
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* dump buffer content */
|
|
|
+ if (*mbx_dump_map & LPFC_MBX_DMP_MBX_WORD) {
|
|
|
+ printk(KERN_ERR "Mailbox command:0x%x dump by word:\n",
|
|
|
+ pmbox->mbxCommand);
|
|
|
+ pword = (uint32_t *)pmbox;
|
|
|
+ for (i = 0; i < *mbx_word_cnt; i++) {
|
|
|
+ if (!(i % 8)) {
|
|
|
+ if (i != 0)
|
|
|
+ printk(KERN_ERR "%s\n", line_buf);
|
|
|
+ len = 0;
|
|
|
+ memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ);
|
|
|
+ len += snprintf(line_buf+len,
|
|
|
+ LPFC_MBX_ACC_LBUF_SZ-len,
|
|
|
+ "%03d: ", i);
|
|
|
+ }
|
|
|
+ len += snprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len,
|
|
|
+ "%08x ",
|
|
|
+ ((uint32_t)*pword) & 0xffffffff);
|
|
|
+ pword++;
|
|
|
+ }
|
|
|
+ if ((i - 1) % 8)
|
|
|
+ printk(KERN_ERR "%s\n", line_buf);
|
|
|
+ printk(KERN_ERR "\n");
|
|
|
+ }
|
|
|
+ if (*mbx_dump_map & LPFC_MBX_DMP_MBX_BYTE) {
|
|
|
+ printk(KERN_ERR "Mailbox command:0x%x dump by byte:\n",
|
|
|
+ pmbox->mbxCommand);
|
|
|
+ pbyte = (uint8_t *)pmbox;
|
|
|
+ for (i = 0; i < *mbx_word_cnt; i++) {
|
|
|
+ if (!(i % 8)) {
|
|
|
+ if (i != 0)
|
|
|
+ printk(KERN_ERR "%s\n", line_buf);
|
|
|
+ len = 0;
|
|
|
+ memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ);
|
|
|
+ len += snprintf(line_buf+len,
|
|
|
+ LPFC_MBX_ACC_LBUF_SZ-len,
|
|
|
+ "%03d: ", i);
|
|
|
+ }
|
|
|
+ for (j = 0; j < 4; j++) {
|
|
|
+ len += snprintf(line_buf+len,
|
|
|
+ LPFC_MBX_ACC_LBUF_SZ-len,
|
|
|
+ "%02x",
|
|
|
+ ((uint8_t)*pbyte) & 0xff);
|
|
|
+ pbyte++;
|
|
|
+ }
|
|
|
+ len += snprintf(line_buf+len,
|
|
|
+ LPFC_MBX_ACC_LBUF_SZ-len, " ");
|
|
|
+ }
|
|
|
+ if ((i - 1) % 8)
|
|
|
+ printk(KERN_ERR "%s\n", line_buf);
|
|
|
+ printk(KERN_ERR "\n");
|
|
|
+ }
|
|
|
+ (*mbx_dump_cnt)--;
|
|
|
+
|
|
|
+ /* Clean out command structure on reaching dump count */
|
|
|
+ if (*mbx_dump_cnt == 0)
|
|
|
+ memset(&idiag, 0, sizeof(idiag));
|
|
|
+ return;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* lpfc_debugfs_initialize - Initialize debugfs for a vport
|
|
|
* @vport: The vport pointer to initialize.
|
|
@@ -2673,7 +3894,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
|
|
|
vport, &lpfc_debugfs_op_nodelist);
|
|
|
if (!vport->debug_nodelist) {
|
|
|
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
|
|
|
- "0409 Can't create debugfs nodelist\n");
|
|
|
+ "2985 Can't create debugfs nodelist\n");
|
|
|
goto debug_failed;
|
|
|
}
|
|
|
|
|
@@ -2710,6 +3931,20 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
|
|
|
idiag.offset.last_rd = 0;
|
|
|
}
|
|
|
|
|
|
+ /* iDiag PCI BAR access */
|
|
|
+ snprintf(name, sizeof(name), "barAcc");
|
|
|
+ if (!phba->idiag_bar_acc) {
|
|
|
+ phba->idiag_bar_acc =
|
|
|
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
|
|
|
+ phba->idiag_root, phba, &lpfc_idiag_op_barAcc);
|
|
|
+ if (!phba->idiag_bar_acc) {
|
|
|
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
|
|
|
+ "3056 Can't create idiag debugfs\n");
|
|
|
+ goto debug_failed;
|
|
|
+ }
|
|
|
+ idiag.offset.last_rd = 0;
|
|
|
+ }
|
|
|
+
|
|
|
/* iDiag get PCI function queue information */
|
|
|
snprintf(name, sizeof(name), "queInfo");
|
|
|
if (!phba->idiag_que_info) {
|
|
@@ -2749,6 +3984,50 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* iDiag access PCI function control registers */
|
|
|
+ snprintf(name, sizeof(name), "ctlAcc");
|
|
|
+ if (!phba->idiag_ctl_acc) {
|
|
|
+ phba->idiag_ctl_acc =
|
|
|
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
|
|
|
+ phba->idiag_root, phba, &lpfc_idiag_op_ctlAcc);
|
|
|
+ if (!phba->idiag_ctl_acc) {
|
|
|
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
|
|
|
+ "2981 Can't create idiag debugfs\n");
|
|
|
+ goto debug_failed;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* iDiag access mbox commands */
|
|
|
+ snprintf(name, sizeof(name), "mbxAcc");
|
|
|
+ if (!phba->idiag_mbx_acc) {
|
|
|
+ phba->idiag_mbx_acc =
|
|
|
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
|
|
|
+ phba->idiag_root, phba, &lpfc_idiag_op_mbxAcc);
|
|
|
+ if (!phba->idiag_mbx_acc) {
|
|
|
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
|
|
|
+ "2980 Can't create idiag debugfs\n");
|
|
|
+ goto debug_failed;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* iDiag extents access commands */
|
|
|
+ if (phba->sli4_hba.extents_in_use) {
|
|
|
+ snprintf(name, sizeof(name), "extAcc");
|
|
|
+ if (!phba->idiag_ext_acc) {
|
|
|
+ phba->idiag_ext_acc =
|
|
|
+ debugfs_create_file(name,
|
|
|
+ S_IFREG|S_IRUGO|S_IWUSR,
|
|
|
+ phba->idiag_root, phba,
|
|
|
+ &lpfc_idiag_op_extAcc);
|
|
|
+ if (!phba->idiag_ext_acc) {
|
|
|
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
|
|
|
+ "2986 Cant create "
|
|
|
+ "idiag debugfs\n");
|
|
|
+ goto debug_failed;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
debug_failed:
|
|
|
return;
|
|
|
#endif
|
|
@@ -2783,7 +4062,6 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
|
|
|
debugfs_remove(vport->debug_nodelist); /* nodelist */
|
|
|
vport->debug_nodelist = NULL;
|
|
|
}
|
|
|
-
|
|
|
if (vport->vport_debugfs_root) {
|
|
|
debugfs_remove(vport->vport_debugfs_root); /* vportX */
|
|
|
vport->vport_debugfs_root = NULL;
|
|
@@ -2827,6 +4105,21 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
|
|
|
* iDiag release
|
|
|
*/
|
|
|
if (phba->sli_rev == LPFC_SLI_REV4) {
|
|
|
+ if (phba->idiag_ext_acc) {
|
|
|
+ /* iDiag extAcc */
|
|
|
+ debugfs_remove(phba->idiag_ext_acc);
|
|
|
+ phba->idiag_ext_acc = NULL;
|
|
|
+ }
|
|
|
+ if (phba->idiag_mbx_acc) {
|
|
|
+ /* iDiag mbxAcc */
|
|
|
+ debugfs_remove(phba->idiag_mbx_acc);
|
|
|
+ phba->idiag_mbx_acc = NULL;
|
|
|
+ }
|
|
|
+ if (phba->idiag_ctl_acc) {
|
|
|
+ /* iDiag ctlAcc */
|
|
|
+ debugfs_remove(phba->idiag_ctl_acc);
|
|
|
+ phba->idiag_ctl_acc = NULL;
|
|
|
+ }
|
|
|
if (phba->idiag_drb_acc) {
|
|
|
/* iDiag drbAcc */
|
|
|
debugfs_remove(phba->idiag_drb_acc);
|
|
@@ -2842,6 +4135,11 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
|
|
|
debugfs_remove(phba->idiag_que_info);
|
|
|
phba->idiag_que_info = NULL;
|
|
|
}
|
|
|
+ if (phba->idiag_bar_acc) {
|
|
|
+ /* iDiag barAcc */
|
|
|
+ debugfs_remove(phba->idiag_bar_acc);
|
|
|
+ phba->idiag_bar_acc = NULL;
|
|
|
+ }
|
|
|
if (phba->idiag_pci_cfg) {
|
|
|
/* iDiag pciCfg */
|
|
|
debugfs_remove(phba->idiag_pci_cfg);
|