[SCSI] qla4xxx: added support for abort task management command
authorVikas Chaudhary <vikas.chaudhary@qlogic.com>
Wed, 28 Apr 2010 06:12:24 +0000 (11:42 +0530)
committerJames Bottomley <James.Bottomley@suse.de>
Sun, 2 May 2010 19:40:12 +0000 (15:40 -0400)
* Handles SCSI command aborts.
* Serialization srb between error handler and command
  completion path.

Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com>
Signed-off-by: Ravi Anand <ravi.anand@qlogic.com>
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
drivers/scsi/qla4xxx/ql4_def.h
drivers/scsi/qla4xxx/ql4_fw.h
drivers/scsi/qla4xxx/ql4_glbl.h
drivers/scsi/qla4xxx/ql4_isr.c
drivers/scsi/qla4xxx/ql4_mbx.c
drivers/scsi/qla4xxx/ql4_os.c

index b6f2d01524549e876a2ea099e9bfa11d43d7c71e..428802616e33744348f812cdc1f98b2785399def 100644 (file)
@@ -172,7 +172,7 @@ struct srb {
 
        struct scsi_cmnd *cmd;  /* (4) SCSI command block */
        dma_addr_t dma_handle;  /* (4) for unmap of single transfers */
-       atomic_t ref_count;     /* reference count for this srb */
+       struct kref srb_ref;    /* reference count for this srb */
        uint32_t fw_ddb_index;
        uint8_t err_id;         /* error id */
 #define SRB_ERR_PORT      1    /* Request failed because "port down" */
index dfe7b4dd3912e7d4b1a464222ac5366c17c9a621..855226e086652c54475c4fad24d0edf1a07c2eff 100644 (file)
@@ -215,6 +215,7 @@ union external_hw_config_reg {
 /*  Mailbox command definitions */
 #define MBOX_CMD_ABOUT_FW                      0x0009
 #define MBOX_CMD_PING                          0x000B
+#define MBOX_CMD_ABORT_TASK                    0x0015
 #define MBOX_CMD_LUN_RESET                     0x0016
 #define MBOX_CMD_TARGET_WARM_RESET             0x0017
 #define MBOX_CMD_GET_MANAGEMENT_DATA           0x001E
index 987658f5bc13bf73b656d67e659108a074bfe1fb..c4636f6cb3cbcb68b2ab584604df6b3c9e6cf2aa 100644 (file)
@@ -25,6 +25,7 @@ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen);
 int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha);
 int qla4xxx_relogin_device(struct scsi_qla_host * ha,
                           struct ddb_entry * ddb_entry);
+int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb);
 int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry,
                      int lun);
 int qla4xxx_reset_target(struct scsi_qla_host * ha,
@@ -65,7 +66,7 @@ void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha,
 int qla4xxx_init_rings(struct scsi_qla_host * ha);
 struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha,
                                        uint32_t index);
-void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb);
+void qla4xxx_srb_compl(struct kref *ref);
 int qla4xxx_reinitialize_ddb_list(struct scsi_qla_host * ha);
 int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
                uint32_t state, uint32_t conn_error);
index ce5838eb685e6d2265ebe44384ef8581ba3d173b..596c3031483c34f362fb979e613647711363bcf1 100644 (file)
@@ -97,7 +97,7 @@ qla4xxx_status_cont_entry(struct scsi_qla_host *ha,
 
        /* Place command on done queue. */
        if (srb->req_sense_len == 0) {
-               qla4xxx_srb_compl(ha, srb);
+               kref_put(&srb->srb_ref, qla4xxx_srb_compl);
                ha->status_srb = NULL;
        }
 }
@@ -329,7 +329,7 @@ status_entry_exit:
        /* complete the request, if not waiting for status_continuation pkt */
        srb->cc_stat = sts_entry->completionStatus;
        if (ha->status_srb == NULL)
-               qla4xxx_srb_compl(ha, srb);
+               kref_put(&srb->srb_ref, qla4xxx_srb_compl);
 }
 
 /**
@@ -393,7 +393,7 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha)
                        /* ETRY normally by sending it back with
                         * DID_BUS_BUSY */
                        srb->cmd->result = DID_BUS_BUSY << 16;
-                       qla4xxx_srb_compl(ha, srb);
+                       kref_put(&srb->srb_ref, qla4xxx_srb_compl);
                        break;
 
                case ET_CONTINUE:
index 43581ce3a1b632614ff141bbed1dbf36ae9e32d8..e1315cd8c261c8f0d9b4e562ccd6735289b93961 100644 (file)
@@ -761,6 +761,59 @@ exit_get_event_log:
                                  event_log_dma);
 }
 
+/**
+ * qla4xxx_abort_task - issues Abort Task
+ * @ha: Pointer to host adapter structure.
+ * @srb: Pointer to srb entry
+ *
+ * This routine performs a LUN RESET on the specified target/lun.
+ * The caller must ensure that the ddb_entry and lun_entry pointers
+ * are valid before calling this routine.
+ **/
+int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb)
+{
+       uint32_t mbox_cmd[MBOX_REG_COUNT];
+       uint32_t mbox_sts[MBOX_REG_COUNT];
+       struct scsi_cmnd *cmd = srb->cmd;
+       int status = QLA_SUCCESS;
+       unsigned long flags = 0;
+       uint32_t index;
+
+       /*
+        * Send abort task command to ISP, so that the ISP will return
+        * request with ABORT status
+        */
+       memset(&mbox_cmd, 0, sizeof(mbox_cmd));
+       memset(&mbox_sts, 0, sizeof(mbox_sts));
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       index = (unsigned long)(unsigned char *)cmd->host_scribble;
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       /* Firmware already posted completion on response queue */
+       if (index == MAX_SRBS)
+               return status;
+
+       mbox_cmd[0] = MBOX_CMD_ABORT_TASK;
+       mbox_cmd[1] = srb->fw_ddb_index;
+       mbox_cmd[2] = index;
+       /* Immediate Command Enable */
+       mbox_cmd[5] = 0x01;
+
+       qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 5, &mbox_cmd[0],
+           &mbox_sts[0]);
+       if (mbox_sts[0] != MBOX_STS_COMMAND_COMPLETE) {
+               status = QLA_ERROR;
+
+               DEBUG2(printk(KERN_WARNING "scsi%ld:%d:%d: abort task FAILED: "
+                   "mbx0=%04X, mb1=%04X, mb2=%04X, mb3=%04X, mb4=%04X\n",
+                   ha->host_no, cmd->device->id, cmd->device->lun, mbox_sts[0],
+                   mbox_sts[1], mbox_sts[2], mbox_sts[3], mbox_sts[4]));
+       }
+
+       return status;
+}
+
 /**
  * qla4xxx_reset_lun - issues LUN Reset
  * @ha: Pointer to host adapter structure.
index 2ca43f0ebcd68f9a76b222474b5384965a5004ab..38b1d38afca562c0ec7fe2d376e02ab41555dc11 100644 (file)
@@ -74,6 +74,7 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc);
  */
 static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,
                                void (*done) (struct scsi_cmnd *));
+static int qla4xxx_eh_abort(struct scsi_cmnd *cmd);
 static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd);
 static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd);
 static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd);
@@ -88,6 +89,7 @@ static struct scsi_host_template qla4xxx_driver_template = {
        .proc_name              = DRIVER_NAME,
        .queuecommand           = qla4xxx_queuecommand,
 
+       .eh_abort_handler       = qla4xxx_eh_abort,
        .eh_device_reset_handler = qla4xxx_eh_device_reset,
        .eh_target_reset_handler = qla4xxx_eh_target_reset,
        .eh_host_reset_handler  = qla4xxx_eh_host_reset,
@@ -384,7 +386,7 @@ static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha,
        if (!srb)
                return srb;
 
-       atomic_set(&srb->ref_count, 1);
+       kref_init(&srb->srb_ref);
        srb->ha = ha;
        srb->ddb = ddb_entry;
        srb->cmd = cmd;
@@ -406,9 +408,11 @@ static void qla4xxx_srb_free_dma(struct scsi_qla_host *ha, struct srb *srb)
        CMD_SP(cmd) = NULL;
 }
 
-void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb)
+void qla4xxx_srb_compl(struct kref *ref)
 {
+       struct srb *srb = container_of(ref, struct srb, srb_ref);
        struct scsi_cmnd *cmd = srb->cmd;
+       struct scsi_qla_host *ha = srb->ha;
 
        qla4xxx_srb_free_dma(ha, srb);
 
@@ -887,7 +891,7 @@ static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha)
                srb = qla4xxx_del_from_active_array(ha, i);
                if (srb != NULL) {
                        srb->cmd->result = DID_RESET << 16;
-                       qla4xxx_srb_compl(ha, srb);
+                       kref_put(&srb->srb_ref, qla4xxx_srb_compl);
                }
        }
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -1501,7 +1505,7 @@ struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t in
 
 /**
  * qla4xxx_eh_wait_on_command - waits for command to be returned by firmware
- * @ha: actual ha whose done queue will contain the comd returned by firmware.
+ * @ha: Pointer to host adapter structure.
  * @cmd: Scsi Command to wait on.
  *
  * This routine waits for the command to be returned by the Firmware
@@ -1584,6 +1588,62 @@ static int qla4xxx_eh_wait_for_commands(struct scsi_qla_host *ha,
        return status;
 }
 
+/**
+ * qla4xxx_eh_abort - callback for abort task.
+ * @cmd: Pointer to Linux's SCSI command structure
+ *
+ * This routine is called by the Linux OS to abort the specified
+ * command.
+ **/
+static int qla4xxx_eh_abort(struct scsi_cmnd *cmd)
+{
+       struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
+       unsigned int id = cmd->device->id;
+       unsigned int lun = cmd->device->lun;
+       unsigned long serial = cmd->serial_number;
+       struct srb *srb = NULL;
+       int ret = SUCCESS;
+       int wait = 0;
+
+       dev_info(&ha->pdev->dev,
+           "scsi%ld:%d:%d: Abort command issued cmd=%p, pid=%ld\n",
+           ha->host_no, id, lun, cmd, serial);
+
+       srb = (struct srb *) CMD_SP(cmd);
+
+       if (!srb)
+               return SUCCESS;
+
+       kref_get(&srb->srb_ref);
+
+       if (qla4xxx_abort_task(ha, srb) != QLA_SUCCESS) {
+               DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx failed.\n",
+                   ha->host_no, id, lun));
+               ret = FAILED;
+       } else {
+               DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx success.\n",
+                   ha->host_no, id, lun));
+               wait = 1;
+       }
+
+       kref_put(&srb->srb_ref, qla4xxx_srb_compl);
+
+       /* Wait for command to complete */
+       if (wait) {
+               if (!qla4xxx_eh_wait_on_command(ha, cmd)) {
+                       DEBUG2(printk("scsi%ld:%d:%d: Abort handler timed out\n",
+                           ha->host_no, id, lun));
+                       ret = FAILED;
+               }
+       }
+
+       dev_info(&ha->pdev->dev,
+           "scsi%ld:%d:%d: Abort command - %s\n",
+           ha->host_no, id, lun, (ret == SUCCESS) ? "succeded" : "failed");
+
+       return ret;
+}
+
 /**
  * qla4xxx_eh_device_reset - callback for target reset.
  * @cmd: Pointer to Linux's SCSI command structure