]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/scsi/pm8001/pm8001_hwi.c
[SCSI] pm8001: deficient responses to IO_XFER_ERROR_BREAK and IO_XFER_OPEN_RETRY_TIMEOUT
[mirror_ubuntu-bionic-kernel.git] / drivers / scsi / pm8001 / pm8001_hwi.c
index f3c44b96c1c9daaec57a97e1e6c159a962325aa4..3920b49f4f57dc9db909f358f66513e2c47d0ac8 100644 (file)
@@ -622,7 +622,8 @@ static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
        update_inbnd_queue_table(pm8001_ha, 0);
        update_outbnd_queue_table(pm8001_ha, 0);
        mpi_set_phys_g3_with_ssc(pm8001_ha, 0);
-       mpi_set_open_retry_interval_reg(pm8001_ha, 7);
+       /* 7->130ms, 34->500ms, 119->1.5s */
+       mpi_set_open_retry_interval_reg(pm8001_ha, 119);
        /* notify firmware update finished and check initialization status */
        if (0 == mpi_init_check(pm8001_ha)) {
                PM8001_INIT_DBG(pm8001_ha,
@@ -1421,24 +1422,191 @@ static void pm8001_work_fn(struct work_struct *work)
        struct pm8001_device *pm8001_dev;
        struct domain_device *dev;
 
+       /*
+        * So far, all users of this stash an associated structure here.
+        * If we get here, and this pointer is null, then the action
+        * was cancelled. This nullification happens when the device
+        * goes away.
+        */
+       pm8001_dev = pw->data; /* Most stash device structure */
+       if ((pm8001_dev == NULL)
+        || ((pw->handler != IO_XFER_ERROR_BREAK)
+         && (pm8001_dev->dev_type == NO_DEVICE))) {
+               kfree(pw);
+               return;
+       }
+
        switch (pw->handler) {
+       case IO_XFER_ERROR_BREAK:
+       {       /* This one stashes the sas_task instead */
+               struct sas_task *t = (struct sas_task *)pm8001_dev;
+               u32 tag;
+               struct pm8001_ccb_info *ccb;
+               struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
+               unsigned long flags, flags1;
+               struct task_status_struct *ts;
+               int i;
+
+               if (pm8001_query_task(t) == TMF_RESP_FUNC_SUCC)
+                       break; /* Task still on lu */
+               spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+               spin_lock_irqsave(&t->task_state_lock, flags1);
+               if (unlikely((t->task_state_flags & SAS_TASK_STATE_DONE))) {
+                       spin_unlock_irqrestore(&t->task_state_lock, flags1);
+                       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+                       break; /* Task got completed by another */
+               }
+               spin_unlock_irqrestore(&t->task_state_lock, flags1);
+
+               /* Search for a possible ccb that matches the task */
+               for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
+                       ccb = &pm8001_ha->ccb_info[i];
+                       tag = ccb->ccb_tag;
+                       if ((tag != 0xFFFFFFFF) && (ccb->task == t))
+                               break;
+               }
+               if (!ccb) {
+                       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+                       break; /* Task got freed by another */
+               }
+               ts = &t->task_status;
+               ts->resp = SAS_TASK_COMPLETE;
+               /* Force the midlayer to retry */
+               ts->stat = SAS_QUEUE_FULL;
+               pm8001_dev = ccb->device;
+               if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               spin_lock_irqsave(&t->task_state_lock, flags1);
+               t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+               t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+               t->task_state_flags |= SAS_TASK_STATE_DONE;
+               if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+                       spin_unlock_irqrestore(&t->task_state_lock, flags1);
+                       PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p"
+                               " done with event 0x%x resp 0x%x stat 0x%x but"
+                               " aborted by upper layer!\n",
+                               t, pw->handler, ts->resp, ts->stat));
+                       pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+                       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+               } else {
+                       spin_unlock_irqrestore(&t->task_state_lock, flags1);
+                       pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+                       mb();/* in order to force CPU ordering */
+                       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+                       t->task_done(t);
+               }
+       }       break;
+       case IO_XFER_OPEN_RETRY_TIMEOUT:
+       {       /* This one stashes the sas_task instead */
+               struct sas_task *t = (struct sas_task *)pm8001_dev;
+               u32 tag;
+               struct pm8001_ccb_info *ccb;
+               struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
+               unsigned long flags, flags1;
+               int i, ret = 0;
+
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+
+               ret = pm8001_query_task(t);
+
+               PM8001_IO_DBG(pm8001_ha,
+                       switch (ret) {
+                       case TMF_RESP_FUNC_SUCC:
+                               pm8001_printk("...Task on lu\n");
+                               break;
+
+                       case TMF_RESP_FUNC_COMPLETE:
+                               pm8001_printk("...Task NOT on lu\n");
+                               break;
+
+                       default:
+                               pm8001_printk("...query task failed!!!\n");
+                               break;
+                       });
+
+               spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+               spin_lock_irqsave(&t->task_state_lock, flags1);
+
+               if (unlikely((t->task_state_flags & SAS_TASK_STATE_DONE))) {
+                       spin_unlock_irqrestore(&t->task_state_lock, flags1);
+                       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+                       if (ret == TMF_RESP_FUNC_SUCC) /* task on lu */
+                               (void)pm8001_abort_task(t);
+                       break; /* Task got completed by another */
+               }
+
+               spin_unlock_irqrestore(&t->task_state_lock, flags1);
+
+               /* Search for a possible ccb that matches the task */
+               for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
+                       ccb = &pm8001_ha->ccb_info[i];
+                       tag = ccb->ccb_tag;
+                       if ((tag != 0xFFFFFFFF) && (ccb->task == t))
+                               break;
+               }
+               if (!ccb) {
+                       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+                       if (ret == TMF_RESP_FUNC_SUCC) /* task on lu */
+                               (void)pm8001_abort_task(t);
+                       break; /* Task got freed by another */
+               }
+
+               pm8001_dev = ccb->device;
+               dev = pm8001_dev->sas_device;
+
+               switch (ret) {
+               case TMF_RESP_FUNC_SUCC: /* task on lu */
+                       ccb->open_retry = 1; /* Snub completion */
+                       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+                       ret = pm8001_abort_task(t);
+                       ccb->open_retry = 0;
+                       switch (ret) {
+                       case TMF_RESP_FUNC_SUCC:
+                       case TMF_RESP_FUNC_COMPLETE:
+                               break;
+                       default: /* device misbehavior */
+                               ret = TMF_RESP_FUNC_FAILED;
+                               PM8001_IO_DBG(pm8001_ha,
+                                       pm8001_printk("...Reset phy\n"));
+                               pm8001_I_T_nexus_reset(dev);
+                               break;
+                       }
+                       break;
+
+               case TMF_RESP_FUNC_COMPLETE: /* task not on lu */
+                       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+                       /* Do we need to abort the task locally? */
+                       break;
+
+               default: /* device misbehavior */
+                       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+                       ret = TMF_RESP_FUNC_FAILED;
+                       PM8001_IO_DBG(pm8001_ha,
+                               pm8001_printk("...Reset phy\n"));
+                       pm8001_I_T_nexus_reset(dev);
+               }
+
+               if (ret == TMF_RESP_FUNC_FAILED)
+                       t = NULL;
+               pm8001_open_reject_retry(pm8001_ha, t, pm8001_dev);
+               PM8001_IO_DBG(pm8001_ha, pm8001_printk("...Complete\n"));
+       }       break;
        case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
-               pm8001_dev = pw->data;
                dev = pm8001_dev->sas_device;
                pm8001_I_T_nexus_reset(dev);
                break;
        case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
-               pm8001_dev = pw->data;
                dev = pm8001_dev->sas_device;
                pm8001_I_T_nexus_reset(dev);
                break;
        case IO_DS_IN_ERROR:
-               pm8001_dev = pw->data;
                dev = pm8001_dev->sas_device;
                pm8001_I_T_nexus_reset(dev);
                break;
        case IO_DS_NON_OPERATIONAL:
-               pm8001_dev = pw->data;
                dev = pm8001_dev->sas_device;
                pm8001_I_T_nexus_reset(dev);
                break;
@@ -1493,6 +1661,11 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
        status = le32_to_cpu(psspPayload->status);
        tag = le32_to_cpu(psspPayload->tag);
        ccb = &pm8001_ha->ccb_info[tag];
+       if ((status == IO_ABORTED) && ccb->open_retry) {
+               /* Being completed by another */
+               ccb->open_retry = 0;
+               return;
+       }
        pm8001_dev = ccb->device;
        param = le32_to_cpu(psspPayload->param);
 
@@ -1548,6 +1721,8 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
                        pm8001_printk("IO_XFER_ERROR_BREAK\n"));
                ts->resp = SAS_TASK_COMPLETE;
                ts->stat = SAS_OPEN_REJECT;
+               /* Force the midlayer to retry */
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
                break;
        case IO_XFER_ERROR_PHY_NOT_READY:
                PM8001_IO_DBG(pm8001_ha,
@@ -1752,9 +1927,8 @@ static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
        case IO_XFER_ERROR_BREAK:
                PM8001_IO_DBG(pm8001_ha,
                        pm8001_printk("IO_XFER_ERROR_BREAK\n"));
-               ts->resp = SAS_TASK_COMPLETE;
-               ts->stat = SAS_INTERRUPTED;
-               break;
+               pm8001_handle_event(pm8001_ha, t, IO_XFER_ERROR_BREAK);
+               return;
        case IO_XFER_ERROR_PHY_NOT_READY:
                PM8001_IO_DBG(pm8001_ha,
                        pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
@@ -1833,10 +2007,8 @@ static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
        case IO_XFER_OPEN_RETRY_TIMEOUT:
                PM8001_IO_DBG(pm8001_ha,
                        pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
-               ts->resp = SAS_TASK_COMPLETE;
-               ts->stat = SAS_OPEN_REJECT;
-               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
-               break;
+               pm8001_handle_event(pm8001_ha, t, IO_XFER_OPEN_RETRY_TIMEOUT);
+               return;
        case IO_XFER_ERROR_UNEXPECTED_PHASE:
                PM8001_IO_DBG(pm8001_ha,
                        pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));