[SCSI] aic94xx: handle REQ_TASK_ABORT
authorDarrick J. Wong <djwong@us.ibm.com>
Mon, 30 Oct 2006 23:18:56 +0000 (15:18 -0800)
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>
Wed, 15 Nov 2006 18:31:27 +0000 (12:31 -0600)
This patch straightens out the code that distinguishes the various escb
opcodes in escb_tasklet_complete so that they can be handled correctly.
It also provides all the necessary code to create a workqueue item that
tells libsas to abort a sas_task.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
drivers/scsi/aic94xx/aic94xx_scb.c

index 7ee49b5..1911c5d 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 #include <linux/pci.h>
+#include <scsi/scsi_host.h>
 
 #include "aic94xx.h"
 #include "aic94xx_reg.h"
@@ -342,6 +343,18 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
        }
 }
 
+/* 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, (void (*)(void *))sas_task_abort, task);
+       queue_work(shost->work_q, &task->abort_work);
+}
+
 static void escb_tasklet_complete(struct asd_ascb *ascb,
                                  struct done_list_struct *dl)
 {
@@ -368,6 +381,58 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
                            ascb->scb->header.opcode);
        }
 
+       /* Catch these before we mask off the sb_opcode bits */
+       switch (sb_opcode) {
+       case REQ_TASK_ABORT: {
+               struct asd_ascb *a, *b;
+               u16 tc_abort;
+
+               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]);
+
+               /* 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);
+                               break;
+                       }
+               goto out;
+       }
+       case REQ_DEVICE_RESET: {
+               struct asd_ascb *a, *b;
+               u16 conn_handle;
+
+               conn_handle = *((u16*)(&dl->status_block[1]));
+               conn_handle = le16_to_cpu(conn_handle);
+
+               ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __FUNCTION__,
+                           dl->status_block[3]);
+
+               /* Kill all pending tasks and reset the device */
+               list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) {
+                       struct sas_task *task = a->uldd_task;
+                       struct domain_device *dev = task->dev;
+                       u16 x;
+
+                       x = *((u16*)(&dev->lldd_dev));
+                       if (x == conn_handle)
+                               task_kill_later(a);
+               }
+
+               /* FIXME: Reset device port (huh?) */
+               goto out;
+       }
+       case SIGNAL_NCQ_ERROR:
+               ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __FUNCTION__);
+               goto out;
+       case CLEAR_NCQ_ERROR:
+               ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __FUNCTION__);
+               goto out;
+       }
+
        sb_opcode &= ~DL_PHY_MASK;
 
        switch (sb_opcode) {
@@ -397,22 +462,6 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
                sas_phy_disconnected(sas_phy);
                sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT);
                break;
-       case REQ_TASK_ABORT:
-               ASD_DPRINTK("%s: phy%d: REQ_TASK_ABORT\n", __FUNCTION__,
-                           phy_id);
-               break;
-       case REQ_DEVICE_RESET:
-               ASD_DPRINTK("%s: phy%d: REQ_DEVICE_RESET\n", __FUNCTION__,
-                           phy_id);
-               break;
-       case SIGNAL_NCQ_ERROR:
-               ASD_DPRINTK("%s: phy%d: SIGNAL_NCQ_ERROR\n", __FUNCTION__,
-                           phy_id);
-               break;
-       case CLEAR_NCQ_ERROR:
-               ASD_DPRINTK("%s: phy%d: CLEAR_NCQ_ERROR\n", __FUNCTION__,
-                           phy_id);
-               break;
        default:
                ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__,
                            phy_id, sb_opcode);
@@ -432,7 +481,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
 
                break;
        }
-
+out:
        asd_invalidate_edb(ascb, edb);
 }