Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
[safe/jmp/linux-2.6] / drivers / scsi / qla4xxx / ql4_isr.c
index b47bd85..596c303 100644 (file)
 #include "ql4_inline.h"
 
 /**
- * qla2x00_process_completed_request() - Process a Fast Post response.
- * @ha: SCSI driver HA context
- * @index: SRB index
+ * 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_process_completed_request(struct scsi_qla_host *ha,
-                                             uint32_t index)
+static void qla4xxx_copy_sense(struct scsi_qla_host *ha,
+                               struct status_entry *sts_entry,
+                               struct srb *srb)
 {
-       struct srb *srb;
+       struct scsi_cmnd *cmd = srb->cmd;
+       uint16_t sense_len;
 
-       srb = qla4xxx_del_from_active_array(ha, index);
-       if (srb) {
-               /* Save ISP completion status */
-               srb->cmd->result = DID_OK << 16;
-               qla4xxx_srb_compl(ha, srb);
-       } else {
-               DEBUG2(printk("scsi%ld: Invalid ISP SCSI completion handle = "
-                             "%d\n", ha->host_no, index));
-               set_bit(DPC_RESET_HA, &ha->dpc_flags);
+       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) {
+               kref_put(&srb->srb_ref, qla4xxx_srb_compl);
+               ha->status_srb = NULL;
        }
 }
 
@@ -45,15 +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;
-
-       if (sts_entry->completionStatus == SCS_COMPLETE &&
-           sts_entry->scsiStatus == 0) {
-               qla4xxx_process_completed_request(ha,
-                                                 le32_to_cpu(sts_entry->
-                                                             handle));
-               return;
-       }
 
        srb = qla4xxx_del_from_active_array(ha, le32_to_cpu(sts_entry->handle));
        if (!srb) {
@@ -62,6 +123,9 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
                              "handle 0x%x, sp=%p. This cmd may have already "
                              "been completed.\n", ha->host_no, __func__,
                              le32_to_cpu(sts_entry->handle), srb));
+               dev_warn(&ha->pdev->dev, "%s invalid status entry:"
+                       " handle=0x%0x\n", __func__, sts_entry->handle);
+               set_bit(DPC_RESET_HA, &ha->dpc_flags);
                return;
        }
 
@@ -88,14 +152,30 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
        scsi_status = sts_entry->scsiStatus;
        switch (sts_entry->completionStatus) {
        case SCS_COMPLETE:
-               if (scsi_status == 0) {
-                       cmd->result = DID_OK << 16;
+
+               if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) {
+                       cmd->result = DID_ERROR << 16;
                        break;
                }
 
-               if (sts_entry->iscsiFlags &
-                   (ISCSI_FLAG_RESIDUAL_OVER|ISCSI_FLAG_RESIDUAL_UNDER))
+               if (sts_entry->iscsiFlags &ISCSI_FLAG_RESIDUAL_UNDER) {
                        scsi_set_resid(cmd, residual);
+                       if (!scsi_status && ((scsi_bufflen(cmd) - residual) <
+                               cmd->underflow)) {
+
+                               cmd->result = DID_ERROR << 16;
+
+                               DEBUG2(printk("scsi%ld:%d:%d:%d: %s: "
+                                       "Mid-layer Data underrun0, "
+                                       "xferlen = 0x%x, "
+                                       "residual = 0x%x\n", ha->host_no,
+                                       cmd->device->channel,
+                                       cmd->device->id,
+                                       cmd->device->lun, __func__,
+                                       scsi_bufflen(cmd), residual));
+                               break;
+                       }
+               }
 
                cmd->result = DID_OK << 16 | scsi_status;
 
@@ -103,25 +183,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
                        break;
 
                /* Copy Sense Data into sense buffer. */
-               memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
-
-               sensebytecnt = le16_to_cpu(sts_entry->senseDataByteCnt);
-               if (sensebytecnt == 0)
-                       break;
-
-               memcpy(cmd->sense_buffer, sts_entry->senseData,
-                      min(sensebytecnt,
-                          (uint16_t) sizeof(cmd->sense_buffer)));
-
-               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:
@@ -151,7 +213,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
                              ha->host_no, cmd->device->channel,
                              cmd->device->id, cmd->device->lun));
 
-               cmd->result = DID_BUS_BUSY << 16;
+               cmd->result = DID_TRANSPORT_DISRUPTED << 16;
 
                /*
                 * Mark device missing so that we won't continue to send
@@ -164,31 +226,18 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
 
        case SCS_DATA_UNDERRUN:
        case SCS_DATA_OVERRUN:
-               if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) {
-                       DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun, "
-                                     "residual = 0x%x\n", ha->host_no,
+               if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) ||
+                    (sts_entry->completionStatus == SCS_DATA_OVERRUN)) {
+                       DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun\n",
+                                     ha->host_no,
                                      cmd->device->channel, cmd->device->id,
-                                     cmd->device->lun, __func__, residual));
+                                     cmd->device->lun, __func__));
 
                        cmd->result = DID_ERROR << 16;
                        break;
                }
 
-               if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_UNDER) == 0) {
-                       /*
-                        * Firmware detected a SCSI transport underrun
-                        * condition
-                        */
-                       scsi_set_resid(cmd, residual);
-                       DEBUG2(printk("scsi%ld:%d:%d:%d: %s: UNDERRUN status "
-                                     "detected, xferlen = 0x%x, residual = "
-                                     "0x%x\n",
-                                     ha->host_no, cmd->device->channel,
-                                     cmd->device->id,
-                                     cmd->device->lun, __func__,
-                                     scsi_bufflen(cmd),
-                                     residual));
-               }
+               scsi_set_resid(cmd, residual);
 
                /*
                 * If there is scsi_status, it takes precedense over
@@ -201,25 +250,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
                                break;
 
                        /* Copy Sense Data into sense buffer. */
-                       memset(cmd->sense_buffer, 0,
-                              sizeof(cmd->sense_buffer));
-
-                       sensebytecnt =
-                               le16_to_cpu(sts_entry->senseDataByteCnt);
-                       if (sensebytecnt == 0)
-                               break;
-
-                       memcpy(cmd->sense_buffer, sts_entry->senseData,
-                              min(sensebytecnt,
-                                  (uint16_t) sizeof(cmd->sense_buffer)));
-
-                       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
@@ -245,13 +276,13 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
                                 * will return DID_ERROR.
                                 */
                                DEBUG2(printk("scsi%ld:%d:%d:%d: %s: "
-                                             "Mid-layer Data underrun, "
-                                             "xferlen = 0x%x, "
-                                             "residual = 0x%x\n", ha->host_no,
-                                             cmd->device->channel,
-                                             cmd->device->id,
-                                             cmd->device->lun, __func__,
-                                             scsi_bufflen(cmd), residual));
+                                       "Mid-layer Data underrun1, "
+                                       "xferlen = 0x%x, "
+                                       "residual = 0x%x\n", ha->host_no,
+                                       cmd->device->channel,
+                                       cmd->device->id,
+                                       cmd->device->lun, __func__,
+                                       scsi_bufflen(cmd), residual));
 
                                cmd->result = DID_ERROR << 16;
                        } else {
@@ -270,7 +301,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
                if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE)
                        qla4xxx_mark_device_missing(ha, ddb_entry);
 
-               cmd->result = DID_BUS_BUSY << 16;
+               cmd->result = DID_TRANSPORT_DISRUPTED << 16;
                break;
 
        case SCS_QUEUE_FULL:
@@ -295,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)
+               kref_put(&srb->srb_ref, qla4xxx_srb_compl);
 }
 
 /**
@@ -332,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;
 
@@ -343,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:
@@ -365,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:
@@ -470,15 +498,22 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
                        break;
 
                case MBOX_ASTS_LINK_UP:
-                       DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK UP\n",
-                                     ha->host_no, mbox_status));
                        set_bit(AF_LINK_UP, &ha->flags);
+                       if (test_bit(AF_INIT_DONE, &ha->flags))
+                               set_bit(DPC_LINK_CHANGED, &ha->dpc_flags);
+
+                       DEBUG2(printk(KERN_INFO "scsi%ld: AEN %04x Adapter"
+                                       " LINK UP\n", ha->host_no,
+                                       mbox_status));
                        break;
 
                case MBOX_ASTS_LINK_DOWN:
-                       DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK DOWN\n",
-                                     ha->host_no, mbox_status));
                        clear_bit(AF_LINK_UP, &ha->flags);
+                       set_bit(DPC_LINK_CHANGED, &ha->dpc_flags);
+
+                       DEBUG2(printk(KERN_INFO "scsi%ld: AEN %04x Adapter"
+                                       " LINK DOWN\n", ha->host_no,
+                                       mbox_status));
                        break;
 
                case MBOX_ASTS_HEARTBEAT:
@@ -803,7 +838,7 @@ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen)
                                qla4xxx_reinitialize_ddb_list(ha);
                        } else if (mbox_sts[1] == 1) {  /* Specific device. */
                                qla4xxx_process_ddb_changed(ha, mbox_sts[2],
-                                                           mbox_sts[3]);
+                                               mbox_sts[3], mbox_sts[4]);
                        }
                        break;
                }