scsi: hisi_sas: Take debugfs snapshot for all regs
authorLuo Jiaxing <luojiaxing@huawei.com>
Wed, 19 Dec 2018 15:56:41 +0000 (23:56 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 9 Jan 2019 02:58:37 +0000 (21:58 -0500)
This patch takes snapshot for global regs, port regs, CQ, DQ, IOST, ITCT.

Add code for snapshot trig and generate dump directory.

Signed-off-by: Luo Jiaxing <luojiaxing@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/hisi_sas/hisi_sas.h
drivers/scsi/hisi_sas/hisi_sas_main.c
drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
drivers/scsi/hisi_sas/hisi_sas_v3_hw.c

index eca7e474ea018bb39d2e35768f10844b6fbc5a40..3b5fd6d638f0117fd8bdeca224b01e553a5b17c6 100644 (file)
@@ -223,6 +223,12 @@ struct hisi_sas_slot {
 
 struct hisi_sas_debugfs_reg {
        int count;
+       int base_off;
+       union {
+               u32 (*read_global_reg)(struct hisi_hba *hisi_hba, u32 off);
+               u32 (*read_port_reg)(struct hisi_hba *hisi_hba, int port,
+                                    u32 off);
+       };
 };
 
 struct hisi_sas_hw {
@@ -264,8 +270,10 @@ struct hisi_sas_hw {
        u32 (*get_phys_state)(struct hisi_hba *hisi_hba);
        int (*write_gpio)(struct hisi_hba *hisi_hba, u8 reg_type,
                                u8 reg_index, u8 reg_count, u8 *write_data);
-       void (*wait_cmds_complete_timeout)(struct hisi_hba *hisi_hba,
-                                          int delay_ms, int timeout_ms);
+       int (*wait_cmds_complete_timeout)(struct hisi_hba *hisi_hba,
+                                         int delay_ms, int timeout_ms);
+       void (*snapshot_prepare)(struct hisi_hba *hisi_hba);
+       void (*snapshot_restore)(struct hisi_hba *hisi_hba);
        int max_command_entries;
        int complete_hdr_size;
        struct scsi_host_template *sht;
@@ -337,6 +345,7 @@ struct hisi_hba {
        const struct hisi_sas_hw *hw;   /* Low level hw interface */
        unsigned long sata_dev_bitmap[BITS_TO_LONGS(HISI_SAS_MAX_DEVICES)];
        struct work_struct rst_work;
+       struct work_struct debugfs_work;
        u32 phy_state;
        u32 intr_coal_ticks;    /* Time of interrupt coalesce in us */
        u32 intr_coal_count;    /* Interrupt count to coalesce */
@@ -350,6 +359,7 @@ struct hisi_hba {
        struct hisi_sas_itct *debugfs_itct;
 
        struct dentry *debugfs_dir;
+       struct dentry *debugfs_dump_dentry;
 };
 
 /* Generic HW DMA host memory structures */
@@ -517,4 +527,5 @@ extern void hisi_sas_controller_reset_prepare(struct hisi_hba *hisi_hba);
 extern void hisi_sas_controller_reset_done(struct hisi_hba *hisi_hba);
 extern void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba);
 extern void hisi_sas_debugfs_exit(struct hisi_hba *hisi_hba);
+extern void hisi_sas_debugfs_work_handler(struct work_struct *work);
 #endif
index 0b0dbaabcce8c7a01859736a2f6da599158c6898..742f71a8412a6c025033901fec317f332800aa74 100644 (file)
@@ -1429,6 +1429,10 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
        struct Scsi_Host *shost = hisi_hba->shost;
        int rc;
 
+       if (hisi_sas_debugfs_enable && hisi_hba->debugfs_itct &&
+           !hisi_hba->debugfs_dump_dentry)
+               queue_work(hisi_hba->wq, &hisi_hba->debugfs_work);
+
        if (!hisi_hba->hw->soft_reset)
                return -1;
 
@@ -1923,6 +1927,7 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
                                slot->task = NULL;
                        }
                        dev_err(dev, "internal task abort: timeout and not done.\n");
+
                        res = -EIO;
                        goto exit;
                } else
@@ -2459,6 +2464,126 @@ EXPORT_SYMBOL_GPL(hisi_sas_probe);
 
 struct dentry *hisi_sas_debugfs_dir;
 
+static void hisi_sas_debugfs_snapshot_cq_reg(struct hisi_hba *hisi_hba)
+{
+       int queue_entry_size = hisi_hba->hw->complete_hdr_size;
+       int i;
+
+       for (i = 0; i < hisi_hba->queue_count; i++)
+               memcpy(hisi_hba->debugfs_complete_hdr[i],
+                      hisi_hba->complete_hdr[i],
+                      HISI_SAS_QUEUE_SLOTS * queue_entry_size);
+}
+
+static void hisi_sas_debugfs_snapshot_dq_reg(struct hisi_hba *hisi_hba)
+{
+       int queue_entry_size = hisi_hba->hw->complete_hdr_size;
+       int i;
+
+       for (i = 0; i < hisi_hba->queue_count; i++)
+               memcpy(hisi_hba->debugfs_cmd_hdr[i],
+                      hisi_hba->cmd_hdr[i],
+                      HISI_SAS_QUEUE_SLOTS * queue_entry_size);
+}
+
+static void hisi_sas_debugfs_snapshot_port_reg(struct hisi_hba *hisi_hba)
+{
+       const struct hisi_sas_debugfs_reg *port =
+               hisi_hba->hw->debugfs_reg_port;
+       int i, phy_cnt;
+       u32 offset;
+       u32 *databuf;
+
+       for (phy_cnt = 0; phy_cnt < hisi_hba->n_phy; phy_cnt++) {
+               databuf = (u32 *)hisi_hba->debugfs_port_reg[phy_cnt];
+               for (i = 0; i < port->count; i++, databuf++) {
+                       offset = port->base_off + 4 * i;
+                       *databuf = port->read_port_reg(hisi_hba, phy_cnt,
+                                                      offset);
+               }
+       }
+}
+
+static void hisi_sas_debugfs_snapshot_global_reg(struct hisi_hba *hisi_hba)
+{
+       u32 *databuf = (u32 *)hisi_hba->debugfs_global_reg;
+       const struct hisi_sas_debugfs_reg *global =
+               hisi_hba->hw->debugfs_reg_global;
+       int i;
+
+       for (i = 0; i < global->count; i++, databuf++)
+               *databuf = global->read_global_reg(hisi_hba, 4 * i);
+}
+
+static void hisi_sas_debugfs_snapshot_itct_reg(struct hisi_hba *hisi_hba)
+{
+       void *databuf = hisi_hba->debugfs_itct;
+       struct hisi_sas_itct *itct;
+       int i;
+
+       itct = hisi_hba->itct;
+
+       for (i = 0; i < HISI_SAS_MAX_ITCT_ENTRIES; i++, itct++) {
+               memcpy(databuf, itct, sizeof(struct hisi_sas_itct));
+               databuf += sizeof(struct hisi_sas_itct);
+       }
+}
+
+static void hisi_sas_debugfs_snapshot_iost_reg(struct hisi_hba *hisi_hba)
+{
+       int max_command_entries = hisi_hba->hw->max_command_entries;
+       void *databuf = hisi_hba->debugfs_iost;
+       struct hisi_sas_iost *iost;
+       int i;
+
+       iost = hisi_hba->iost;
+
+       for (i = 0; i < max_command_entries; i++, iost++) {
+               memcpy(databuf, iost, sizeof(struct hisi_sas_iost));
+               databuf += sizeof(struct hisi_sas_iost);
+       }
+}
+
+static void hisi_sas_debugfs_create_files(struct hisi_hba *hisi_hba)
+{
+       struct dentry *dump_dentry;
+
+       /* Create dump dir inside device dir */
+       dump_dentry = debugfs_create_dir("dump", hisi_hba->debugfs_dir);
+       if (!dump_dentry)
+               goto fail;
+
+       hisi_hba->debugfs_dump_dentry = dump_dentry;
+       return;
+fail:
+       debugfs_remove_recursive(hisi_hba->debugfs_dir);
+}
+
+static void hisi_sas_debugfs_snapshot_regs(struct hisi_hba *hisi_hba)
+{
+       hisi_hba->hw->snapshot_prepare(hisi_hba);
+
+       hisi_sas_debugfs_snapshot_global_reg(hisi_hba);
+       hisi_sas_debugfs_snapshot_port_reg(hisi_hba);
+       hisi_sas_debugfs_snapshot_cq_reg(hisi_hba);
+       hisi_sas_debugfs_snapshot_dq_reg(hisi_hba);
+       hisi_sas_debugfs_snapshot_itct_reg(hisi_hba);
+       hisi_sas_debugfs_snapshot_iost_reg(hisi_hba);
+
+       hisi_sas_debugfs_create_files(hisi_hba);
+
+       hisi_hba->hw->snapshot_restore(hisi_hba);
+}
+
+void hisi_sas_debugfs_work_handler(struct work_struct *work)
+{
+       struct hisi_hba *hisi_hba =
+               container_of(work, struct hisi_hba, debugfs_work);
+
+       hisi_sas_debugfs_snapshot_regs(hisi_hba);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_debugfs_work_handler);
+
 void hisi_sas_debugfs_init(struct hisi_hba *hisi_hba)
 {
        int max_command_entries = hisi_hba->hw->max_command_entries;
index c8ebff3ba559ddbaf51793d9d7698b3dffc48a60..5c4b8dff4c10d9fd7b7b8068ad667f0c4eb4770f 100644 (file)
@@ -3542,8 +3542,8 @@ static int write_gpio_v2_hw(struct hisi_hba *hisi_hba, u8 reg_type,
        return 0;
 }
 
-static void wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba,
-                                            int delay_ms, int timeout_ms)
+static int wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba,
+                                           int delay_ms, int timeout_ms)
 {
        struct device *dev = hisi_hba->dev;
        int entries, entries_old = 0, time;
@@ -3557,7 +3557,12 @@ static void wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba,
                msleep(delay_ms);
        }
 
+       if (time >= timeout_ms)
+               return -ETIMEDOUT;
+
        dev_dbg(dev, "wait commands complete %dms\n", time);
+
+       return 0;
 }
 
 static struct device_attribute *host_attrs_v2_hw[] = {
index c1b47022d6fd5b8f83233fba38adc144da38dc6c..5727c265abc763399873824810266972513c12e0 100644 (file)
@@ -2201,8 +2201,8 @@ static int write_gpio_v3_hw(struct hisi_hba *hisi_hba, u8 reg_type,
        return 0;
 }
 
-static void wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba,
-                                            int delay_ms, int timeout_ms)
+static int wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba,
+                                           int delay_ms, int timeout_ms)
 {
        struct device *dev = hisi_hba->dev;
        int entries, entries_old = 0, time;
@@ -2216,7 +2216,12 @@ static void wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba,
                msleep(delay_ms);
        }
 
+       if (time >= timeout_ms)
+               return -ETIMEDOUT;
+
        dev_dbg(dev, "wait commands complete %dms\n", time);
+
+       return 0;
 }
 
 static ssize_t intr_conv_v3_hw_show(struct device *dev,
@@ -2333,11 +2338,36 @@ static struct device_attribute *host_attrs_v3_hw[] = {
 };
 
 static const struct hisi_sas_debugfs_reg debugfs_port_reg = {
+       .base_off = PORT_BASE,
+       .read_port_reg = hisi_sas_phy_read32,
 };
 
 static const struct hisi_sas_debugfs_reg debugfs_global_reg = {
+       .read_global_reg = hisi_sas_read32,
 };
 
+static void debugfs_snapshot_prepare_v3_hw(struct hisi_hba *hisi_hba)
+{
+       struct device *dev = hisi_hba->dev;
+
+       set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+
+       hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0);
+
+       if (wait_cmds_complete_timeout_v3_hw(hisi_hba, 100, 5000) == -ETIMEDOUT)
+               dev_dbg(dev, "Wait commands complete timeout!\n");
+
+       hisi_sas_kill_tasklets(hisi_hba);
+}
+
+static void debugfs_snapshot_restore_v3_hw(struct hisi_hba *hisi_hba)
+{
+       hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE,
+                        (u32)((1ULL << hisi_hba->queue_count) - 1));
+
+       clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+}
+
 static struct scsi_host_template sht_v3_hw = {
        .name                   = DRV_NAME,
        .module                 = THIS_MODULE,
@@ -2388,6 +2418,8 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = {
        .wait_cmds_complete_timeout = wait_cmds_complete_timeout_v3_hw,
        .debugfs_reg_global = &debugfs_global_reg,
        .debugfs_reg_port = &debugfs_port_reg,
+       .snapshot_prepare = debugfs_snapshot_prepare_v3_hw,
+       .snapshot_restore = debugfs_snapshot_restore_v3_hw,
 };
 
 static struct Scsi_Host *
@@ -2405,6 +2437,7 @@ hisi_sas_shost_alloc_pci(struct pci_dev *pdev)
        hisi_hba = shost_priv(shost);
 
        INIT_WORK(&hisi_hba->rst_work, hisi_sas_rst_work_handler);
+       INIT_WORK(&hisi_hba->debugfs_work, hisi_sas_debugfs_work_handler);
        hisi_hba->hw = &hisi_sas_v3_hw;
        hisi_hba->pci_dev = pdev;
        hisi_hba->dev = dev;