OSDN Git Service

scsi: hisi_sas: Add SATA_DISK_ERR bit handling for v3 hw
authorXingui Yang <yangxingui@huawei.com>
Mon, 17 Oct 2022 09:20:30 +0000 (17:20 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 18 Oct 2022 02:37:45 +0000 (02:37 +0000)
When CQ header dw3 SATA_DISK_ERR is set it means this SATA disk is in error
state and the current IPTT is invalid. An invalid IPTT does not correspond
to any slot.

In this scenario, new I/Os that delivered to disk will be rejected by the
controller and all I/Os remaining in the disk should be aborted, which we
add here with the sas_ata_device_link_abort() call.

In hisi_sas_abort_task() we don't want to issue a soft reset as it may
cause info to be lost in the target disk for the ATA EH autopsy. In this
case, just release resources - the disk won't return other I/Os normally
after NCQ Error, so this is safe.

Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Link: https://lore.kernel.org/r/1665998435-199946-4-git-send-email-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_v3_hw.c

index 9aebf4a..6f8a52a 100644 (file)
@@ -104,6 +104,7 @@ enum {
 enum dev_status {
        HISI_SAS_DEV_INIT,
        HISI_SAS_DEV_NORMAL,
+       HISI_SAS_DEV_NCQ_ERR,
 };
 
 enum {
index 8303aa5..4c37ae9 100644 (file)
@@ -1604,13 +1604,26 @@ static int hisi_sas_abort_task(struct sas_task *task)
        } else if (task->task_proto & SAS_PROTOCOL_SATA ||
                task->task_proto & SAS_PROTOCOL_STP) {
                if (task->dev->dev_type == SAS_SATA_DEV) {
+                       struct ata_queued_cmd *qc = task->uldd_task;
+
                        rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
                        if (rc < 0) {
                                dev_err(dev, "abort task: internal abort failed\n");
                                goto out;
                        }
                        hisi_sas_dereg_device(hisi_hba, device);
-                       rc = hisi_sas_softreset_ata_disk(device);
+
+                       /*
+                        * If an ATA internal command times out in ATA EH, it
+                        * need to execute soft reset, so check the scsicmd
+                        */
+                       if ((sas_dev->dev_status == HISI_SAS_DEV_NCQ_ERR) &&
+                           qc && qc->scsicmd) {
+                               hisi_sas_do_release_task(hisi_hba, task, slot);
+                               rc = TMF_RESP_FUNC_COMPLETE;
+                       } else {
+                               rc = hisi_sas_softreset_ata_disk(device);
+                       }
                }
        } else if (slot && task->task_proto & SAS_PROTOCOL_SMP) {
                /* SMP */
@@ -1727,6 +1740,9 @@ static int hisi_sas_I_T_nexus_reset(struct domain_device *device)
        struct device *dev = hisi_hba->dev;
        int rc;
 
+       if (sas_dev->dev_status == HISI_SAS_DEV_NCQ_ERR)
+               sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
+
        rc = hisi_sas_internal_task_abort_dev(sas_dev, false);
        if (rc < 0) {
                dev_err(dev, "I_T nexus reset: internal abort (%d)\n", rc);
index d56b4bf..0ae8a60 100644 (file)
 #define CMPLT_HDR_CMPLT_MSK            (0x3 << CMPLT_HDR_CMPLT_OFF)
 #define CMPLT_HDR_ERROR_PHASE_OFF   2
 #define CMPLT_HDR_ERROR_PHASE_MSK   (0xff << CMPLT_HDR_ERROR_PHASE_OFF)
+/* bit[9:2] Error Phase */
+#define ERR_PHASE_RESPONSE_FRAME_REV_STAGE_OFF \
+                                       8
+#define ERR_PHASE_RESPONSE_FRAME_REV_STAGE_MSK \
+       (0x1 << ERR_PHASE_RESPONSE_FRAME_REV_STAGE_OFF)
 #define CMPLT_HDR_RSPNS_XFRD_OFF       10
 #define CMPLT_HDR_RSPNS_XFRD_MSK       (0x1 << CMPLT_HDR_RSPNS_XFRD_OFF)
 #define CMPLT_HDR_RSPNS_GOOD_OFF       11
 #define CMPLT_HDR_DEV_ID_OFF           16
 #define CMPLT_HDR_DEV_ID_MSK           (0xffff << CMPLT_HDR_DEV_ID_OFF)
 /* dw3 */
+#define CMPLT_HDR_SATA_DISK_ERR_OFF    16
+#define CMPLT_HDR_SATA_DISK_ERR_MSK    (0x1 << CMPLT_HDR_SATA_DISK_ERR_OFF)
 #define CMPLT_HDR_IO_IN_TARGET_OFF     17
 #define CMPLT_HDR_IO_IN_TARGET_MSK     (0x1 << CMPLT_HDR_IO_IN_TARGET_OFF)
+/* bit[23:18] ERR_FIS_ATA_STATUS */
+#define FIS_ATA_STATUS_ERR_OFF         18
+#define FIS_ATA_STATUS_ERR_MSK         (0x1 << FIS_ATA_STATUS_ERR_OFF)
+#define FIS_TYPE_SDB_OFF               31
+#define FIS_TYPE_SDB_MSK               (0x1 << FIS_TYPE_SDB_OFF)
 
 /* ITCT header */
 /* qw0 */
@@ -2148,6 +2160,18 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p)
        return IRQ_HANDLED;
 }
 
+static bool is_ncq_err_v3_hw(struct hisi_sas_complete_v3_hdr *complete_hdr)
+{
+       u32 dw0, dw3;
+
+       dw0 = le32_to_cpu(complete_hdr->dw0);
+       dw3 = le32_to_cpu(complete_hdr->dw3);
+
+       return (dw0 & ERR_PHASE_RESPONSE_FRAME_REV_STAGE_MSK) &&
+              (dw3 & FIS_TYPE_SDB_MSK) &&
+              (dw3 & FIS_ATA_STATUS_ERR_MSK);
+}
+
 static bool
 slot_err_v3_hw(struct hisi_hba *hisi_hba, struct sas_task *task,
               struct hisi_sas_slot *slot)
@@ -2381,14 +2405,34 @@ static irqreturn_t  cq_thread_v3_hw(int irq_no, void *p)
        while (rd_point != wr_point) {
                struct hisi_sas_complete_v3_hdr *complete_hdr;
                struct device *dev = hisi_hba->dev;
-               u32 dw1;
+               u32 dw0, dw1, dw3;
                int iptt;
 
                complete_hdr = &complete_queue[rd_point];
+               dw0 = le32_to_cpu(complete_hdr->dw0);
                dw1 = le32_to_cpu(complete_hdr->dw1);
+               dw3 = le32_to_cpu(complete_hdr->dw3);
 
                iptt = dw1 & CMPLT_HDR_IPTT_MSK;
-               if (likely(iptt < HISI_SAS_COMMAND_ENTRIES_V3_HW)) {
+               if (unlikely((dw0 & CMPLT_HDR_CMPLT_MSK) == 0x3) &&
+                            (dw3 & CMPLT_HDR_SATA_DISK_ERR_MSK)) {
+                       int device_id = (dw1 & CMPLT_HDR_DEV_ID_MSK) >>
+                                       CMPLT_HDR_DEV_ID_OFF;
+                       struct hisi_sas_itct *itct =
+                               &hisi_hba->itct[device_id];
+                       struct hisi_sas_device *sas_dev =
+                               &hisi_hba->devices[device_id];
+                       struct domain_device *device = sas_dev->sas_device;
+
+                       dev_err(dev, "erroneous completion disk err dev id=%d sas_addr=0x%llx CQ hdr: 0x%x 0x%x 0x%x 0x%x\n",
+                               device_id, itct->sas_addr, dw0, dw1,
+                               complete_hdr->act, dw3);
+
+                       if (is_ncq_err_v3_hw(complete_hdr))
+                               sas_dev->dev_status = HISI_SAS_DEV_NCQ_ERR;
+
+                       sas_ata_device_link_abort(device, true);
+               } else if (likely(iptt < HISI_SAS_COMMAND_ENTRIES_V3_HW)) {
                        slot = &hisi_hba->slot_info[iptt];
                        slot->cmplt_queue_slot = rd_point;
                        slot->cmplt_queue = queue;