|
@@ -413,40 +413,6 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* hard reset a phy later */
|
|
|
-static void do_phy_reset_later(struct work_struct *work)
|
|
|
-{
|
|
|
- struct sas_phy *sas_phy =
|
|
|
- container_of(work, struct sas_phy, reset_work);
|
|
|
- int error;
|
|
|
-
|
|
|
- ASD_DPRINTK("%s: About to hard reset phy %d\n", __FUNCTION__,
|
|
|
- sas_phy->identify.phy_identifier);
|
|
|
- /* Reset device port */
|
|
|
- error = sas_phy_reset(sas_phy, 1);
|
|
|
- if (error)
|
|
|
- ASD_DPRINTK("%s: Hard reset of phy %d failed (%d).\n",
|
|
|
- __FUNCTION__, sas_phy->identify.phy_identifier, error);
|
|
|
-}
|
|
|
-
|
|
|
-static void phy_reset_later(struct sas_phy *sas_phy, struct Scsi_Host *shost)
|
|
|
-{
|
|
|
- INIT_WORK(&sas_phy->reset_work, do_phy_reset_later);
|
|
|
- queue_work(shost->work_q, &sas_phy->reset_work);
|
|
|
-}
|
|
|
-
|
|
|
-/* start up the ABORT TASK tmf... */
|
|
|
-static void task_kill_later(struct asd_ascb *ascb)
|
|
|
-{
|
|
|
- struct asd_ha_struct *asd_ha = ascb->ha;
|
|
|
- struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
|
|
|
- struct Scsi_Host *shost = sas_ha->core.shost;
|
|
|
- struct sas_task *task = ascb->uldd_task;
|
|
|
-
|
|
|
- INIT_WORK(&task->abort_work, sas_task_abort);
|
|
|
- queue_work(shost->work_q, &task->abort_work);
|
|
|
-}
|
|
|
-
|
|
|
static void escb_tasklet_complete(struct asd_ascb *ascb,
|
|
|
struct done_list_struct *dl)
|
|
|
{
|
|
@@ -479,26 +445,55 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
|
|
|
case REQ_TASK_ABORT: {
|
|
|
struct asd_ascb *a, *b;
|
|
|
u16 tc_abort;
|
|
|
+ struct domain_device *failed_dev = NULL;
|
|
|
+
|
|
|
+ ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n",
|
|
|
+ __FUNCTION__, dl->status_block[3]);
|
|
|
|
|
|
+ /*
|
|
|
+ * Find the task that caused the abort and abort it first.
|
|
|
+ * The sequencer won't put anything on the done list until
|
|
|
+ * that happens.
|
|
|
+ */
|
|
|
tc_abort = *((u16*)(&dl->status_block[1]));
|
|
|
tc_abort = le16_to_cpu(tc_abort);
|
|
|
|
|
|
- ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n",
|
|
|
- __FUNCTION__, dl->status_block[3]);
|
|
|
+ list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) {
|
|
|
+ struct sas_task *task = ascb->uldd_task;
|
|
|
|
|
|
- /* Find the pending task and abort it. */
|
|
|
- list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list)
|
|
|
- if (a->tc_index == tc_abort) {
|
|
|
- task_kill_later(a);
|
|
|
+ if (task && a->tc_index == tc_abort) {
|
|
|
+ failed_dev = task->dev;
|
|
|
+ sas_task_abort(task);
|
|
|
break;
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!failed_dev) {
|
|
|
+ ASD_DPRINTK("%s: Can't find task (tc=%d) to abort!\n",
|
|
|
+ __FUNCTION__, tc_abort);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Now abort everything else for that device (hba?) so
|
|
|
+ * that the EH will wake up and do something.
|
|
|
+ */
|
|
|
+ list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) {
|
|
|
+ struct sas_task *task = ascb->uldd_task;
|
|
|
+
|
|
|
+ if (task &&
|
|
|
+ task->dev == failed_dev &&
|
|
|
+ a->tc_index != tc_abort)
|
|
|
+ sas_task_abort(task);
|
|
|
+ }
|
|
|
+
|
|
|
goto out;
|
|
|
}
|
|
|
case REQ_DEVICE_RESET: {
|
|
|
- struct Scsi_Host *shost = sas_ha->core.shost;
|
|
|
- struct sas_phy *dev_phy;
|
|
|
struct asd_ascb *a;
|
|
|
u16 conn_handle;
|
|
|
+ unsigned long flags;
|
|
|
+ struct sas_task *last_dev_task = NULL;
|
|
|
|
|
|
conn_handle = *((u16*)(&dl->status_block[1]));
|
|
|
conn_handle = le16_to_cpu(conn_handle);
|
|
@@ -506,32 +501,47 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
|
|
|
ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __FUNCTION__,
|
|
|
dl->status_block[3]);
|
|
|
|
|
|
- /* Kill all pending tasks and reset the device */
|
|
|
- dev_phy = NULL;
|
|
|
+ /* Find the last pending task for the device... */
|
|
|
list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
|
|
|
- struct sas_task *task;
|
|
|
- struct domain_device *dev;
|
|
|
u16 x;
|
|
|
+ struct domain_device *dev;
|
|
|
+ struct sas_task *task = a->uldd_task;
|
|
|
|
|
|
- task = a->uldd_task;
|
|
|
if (!task)
|
|
|
continue;
|
|
|
dev = task->dev;
|
|
|
|
|
|
x = (unsigned long)dev->lldd_dev;
|
|
|
- if (x == conn_handle) {
|
|
|
- dev_phy = dev->port->phy;
|
|
|
- task_kill_later(a);
|
|
|
- }
|
|
|
+ if (x == conn_handle)
|
|
|
+ last_dev_task = task;
|
|
|
}
|
|
|
|
|
|
- /* Reset device port */
|
|
|
- if (!dev_phy) {
|
|
|
- ASD_DPRINTK("%s: No pending commands; can't reset.\n",
|
|
|
- __FUNCTION__);
|
|
|
+ if (!last_dev_task) {
|
|
|
+ ASD_DPRINTK("%s: Device reset for idle device %d?\n",
|
|
|
+ __FUNCTION__, conn_handle);
|
|
|
goto out;
|
|
|
}
|
|
|
- phy_reset_later(dev_phy, shost);
|
|
|
+
|
|
|
+ /* ...and set the reset flag */
|
|
|
+ spin_lock_irqsave(&last_dev_task->task_state_lock, flags);
|
|
|
+ last_dev_task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
|
|
|
+ spin_unlock_irqrestore(&last_dev_task->task_state_lock, flags);
|
|
|
+
|
|
|
+ /* Kill all pending tasks for the device */
|
|
|
+ list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
|
|
|
+ u16 x;
|
|
|
+ struct domain_device *dev;
|
|
|
+ struct sas_task *task = a->uldd_task;
|
|
|
+
|
|
|
+ if (!task)
|
|
|
+ continue;
|
|
|
+ dev = task->dev;
|
|
|
+
|
|
|
+ x = (unsigned long)dev->lldd_dev;
|
|
|
+ if (x == conn_handle)
|
|
|
+ sas_task_abort(task);
|
|
|
+ }
|
|
|
+
|
|
|
goto out;
|
|
|
}
|
|
|
case SIGNAL_NCQ_ERROR:
|