Merge branch 'topic/oss' into for-linus
[linux-2.6-block.git] / drivers / scsi / qla4xxx / ql4_isr.c
index 799120fcb9be20a43901482781afd7dfbbd29884..8025ee16588e5a9f7104cf82fc80d4531fe01b70 100644 (file)
 #include "ql4_dbg.h"
 #include "ql4_inline.h"
 
+/**
+ * qla4xxx_copy_sense - copy sense data        into cmd sense buffer
+ * @ha: Pointer to host adapter structure.
+ * @sts_entry: Pointer to status entry structure.
+ * @srb: Pointer to srb structure.
+ **/
+static void qla4xxx_copy_sense(struct scsi_qla_host *ha,
+                               struct status_entry *sts_entry,
+                               struct srb *srb)
+{
+       struct scsi_cmnd *cmd = srb->cmd;
+       uint16_t sense_len;
+
+       memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+       sense_len = le16_to_cpu(sts_entry->senseDataByteCnt);
+       if (sense_len == 0)
+               return;
+
+       /* Save total available sense length,
+        * not to exceed cmd's sense buffer size */
+       sense_len = min_t(uint16_t, sense_len, SCSI_SENSE_BUFFERSIZE);
+       srb->req_sense_ptr = cmd->sense_buffer;
+       srb->req_sense_len = sense_len;
+
+       /* Copy sense from sts_entry pkt */
+       sense_len = min_t(uint16_t, sense_len, IOCB_MAX_SENSEDATA_LEN);
+       memcpy(cmd->sense_buffer, sts_entry->senseData, sense_len);
+
+       DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: %s: sense key = %x, "
+               "ASL= %02x, ASC/ASCQ = %02x/%02x\n", ha->host_no,
+               cmd->device->channel, cmd->device->id,
+               cmd->device->lun, __func__,
+               sts_entry->senseData[2] & 0x0f,
+               sts_entry->senseData[7],
+               sts_entry->senseData[12],
+               sts_entry->senseData[13]));
+
+       DEBUG5(qla4xxx_dump_buffer(cmd->sense_buffer, sense_len));
+       srb->flags |= SRB_GOT_SENSE;
+
+       /* Update srb, in case a sts_cont pkt follows */
+       srb->req_sense_ptr += sense_len;
+       srb->req_sense_len -= sense_len;
+       if (srb->req_sense_len != 0)
+               ha->status_srb = srb;
+       else
+               ha->status_srb = NULL;
+}
+
+/**
+ * qla4xxx_status_cont_entry - Process a Status Continuations entry.
+ * @ha: SCSI driver HA context
+ * @sts_cont: Entry pointer
+ *
+ * Extended sense data.
+ */
+static void
+qla4xxx_status_cont_entry(struct scsi_qla_host *ha,
+                         struct status_cont_entry *sts_cont)
+{
+       struct srb *srb = ha->status_srb;
+       struct scsi_cmnd *cmd;
+       uint8_t sense_len;
+
+       if (srb == NULL)
+               return;
+
+       cmd = srb->cmd;
+       if (cmd == NULL) {
+               DEBUG2(printk(KERN_INFO "scsi%ld: %s: Cmd already returned "
+                       "back to OS srb=%p srb->state:%d\n", ha->host_no,
+                       __func__, srb, srb->state));
+               ha->status_srb = NULL;
+               return;
+       }
+
+       /* Copy sense data. */
+       sense_len = min_t(uint16_t, srb->req_sense_len,
+                         IOCB_MAX_EXT_SENSEDATA_LEN);
+       memcpy(srb->req_sense_ptr, sts_cont->ext_sense_data, sense_len);
+       DEBUG5(qla4xxx_dump_buffer(srb->req_sense_ptr, sense_len));
+
+       srb->req_sense_ptr += sense_len;
+       srb->req_sense_len -= sense_len;
+
+       /* Place command on done queue. */
+       if (srb->req_sense_len == 0) {
+               qla4xxx_srb_compl(ha, srb);
+               ha->status_srb = NULL;
+       }
+}
+
 /**
  * qla4xxx_status_entry - processes status IOCBs
  * @ha: Pointer to host adapter structure.
@@ -23,7 +115,6 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
        struct srb *srb;
        struct ddb_entry *ddb_entry;
        uint32_t residual;
-       uint16_t sensebytecnt;
 
        srb = qla4xxx_del_from_active_array(ha, le32_to_cpu(sts_entry->handle));
        if (!srb) {
@@ -92,24 +183,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
                        break;
 
                /* Copy Sense Data into sense buffer. */
-               memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
-
-               sensebytecnt = le16_to_cpu(sts_entry->senseDataByteCnt);
-               if (sensebytecnt == 0)
-                       break;
-
-               memcpy(cmd->sense_buffer, sts_entry->senseData,
-                      min_t(uint16_t, sensebytecnt, SCSI_SENSE_BUFFERSIZE));
-
-               DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, "
-                             "ASC/ASCQ = %02x/%02x\n", ha->host_no,
-                             cmd->device->channel, cmd->device->id,
-                             cmd->device->lun, __func__,
-                             sts_entry->senseData[2] & 0x0f,
-                             sts_entry->senseData[12],
-                             sts_entry->senseData[13]));
-
-               srb->flags |= SRB_GOT_SENSE;
+               qla4xxx_copy_sense(ha, sts_entry, srb);
                break;
 
        case SCS_INCOMPLETE:
@@ -176,23 +250,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
                                break;
 
                        /* Copy Sense Data into sense buffer. */
-                       memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
-
-                       sensebytecnt =
-                               le16_to_cpu(sts_entry->senseDataByteCnt);
-                       if (sensebytecnt == 0)
-                               break;
-
-                       memcpy(cmd->sense_buffer, sts_entry->senseData,
-                              min_t(uint16_t, sensebytecnt, SCSI_SENSE_BUFFERSIZE));
-
-                       DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, "
-                                     "ASC/ASCQ = %02x/%02x\n", ha->host_no,
-                                     cmd->device->channel, cmd->device->id,
-                                     cmd->device->lun, __func__,
-                                     sts_entry->senseData[2] & 0x0f,
-                                     sts_entry->senseData[12],
-                                     sts_entry->senseData[13]));
+                       qla4xxx_copy_sense(ha, sts_entry, srb);
                } else {
                        /*
                         * If RISC reports underrun and target does not
@@ -268,9 +326,10 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
 
 status_entry_exit:
 
-       /* complete the request */
+       /* complete the request, if not waiting for status_continuation pkt */
        srb->cc_stat = sts_entry->completionStatus;
-       qla4xxx_srb_compl(ha, srb);
+       if (ha->status_srb == NULL)
+               qla4xxx_srb_compl(ha, srb);
 }
 
 /**
@@ -305,10 +364,7 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha)
                /* process entry */
                switch (sts_entry->hdr.entryType) {
                case ET_STATUS:
-                       /*
-                        * Common status - Single completion posted in single
-                        * IOSB.
-                        */
+                       /* Common status */
                        qla4xxx_status_entry(ha, sts_entry);
                        break;
 
@@ -316,9 +372,8 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha)
                        break;
 
                case ET_STATUS_CONTINUATION:
-                       /* Just throw away the status continuation entries */
-                       DEBUG2(printk("scsi%ld: %s: Status Continuation entry "
-                                     "- ignoring\n", ha->host_no, __func__));
+                       qla4xxx_status_cont_entry(ha,
+                               (struct status_cont_entry *) sts_entry);
                        break;
 
                case ET_COMMAND: