/** * qla2x00_status_cont_entry() - Process a Status Continuations entry. * @ha: SCSI driver HA context * @pkt: Entry pointer * * Extended sense data. */ static void qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt) { uint8_t sense_sz = 0; srb_t *sp = ha->status_srb; struct scsi_cmnd *cp; if (sp != NULL && sp->request_sense_length != 0) { cp = sp->cmd; if (cp == NULL) { DEBUG2(printk("%s(): Cmd already returned back to OS " "sp=%p.\n", __func__, sp)); qla_printk(KERN_INFO, ha, "cmd is NULL: already returned to OS (sp=%p)\n", sp); ha->status_srb = NULL; return; } if (sp->request_sense_length > sizeof(pkt->data)) { sense_sz = sizeof(pkt->data); } else { sense_sz = sp->request_sense_length; } /* Move sense data. */ if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) host_to_fcp_swap(pkt->data, sizeof(pkt->data)); memcpy(sp->request_sense_ptr, pkt->data, sense_sz); DEBUG5(qla2x00_dump_buffer(sp->request_sense_ptr, sense_sz)); sp->request_sense_ptr += sense_sz; sp->request_sense_length -= sense_sz; /* Place command on done queue. */ if (sp->request_sense_length == 0) { ha->status_srb = NULL; qla2x00_sp_compl(ha, sp); } } }
/** * qla2x00_status_entry() - Process a Status IOCB entry. * @ha: SCSI driver HA context * @pkt: Entry pointer */ static void qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) { srb_t *sp; fc_port_t *fcport; struct scsi_cmnd *cp; sts_entry_t *sts; struct sts_entry_24xx *sts24; uint16_t comp_status; uint16_t scsi_status; uint8_t lscsi_status; int32_t resid; uint32_t sense_len, rsp_info_len, resid_len, fw_resid_len; uint8_t *rsp_info, *sense_data; sts = (sts_entry_t *) pkt; sts24 = (struct sts_entry_24xx *) pkt; if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { comp_status = le16_to_cpu(sts24->comp_status); scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK; } else { comp_status = le16_to_cpu(sts->comp_status); scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK; } /* Fast path completion. */ if (comp_status == CS_COMPLETE && scsi_status == 0) { qla2x00_process_completed_request(ha, sts->handle); return; } /* Validate handle. */ if (sts->handle < MAX_OUTSTANDING_COMMANDS) { sp = ha->outstanding_cmds[sts->handle]; ha->outstanding_cmds[sts->handle] = NULL; } else sp = NULL; if (sp == NULL) { DEBUG2(printk("scsi(%ld): Status Entry invalid handle.\n", ha->host_no)); qla_printk(KERN_WARNING, ha, "Status Entry invalid handle.\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); qla2xxx_wake_dpc(ha); return; } cp = sp->cmd; if (cp == NULL) { DEBUG2(printk("scsi(%ld): Command already returned back to OS " "pkt->handle=%d sp=%p.\n", ha->host_no, sts->handle, sp)); qla_printk(KERN_WARNING, ha, "Command is NULL: already returned to OS (sp=%p)\n", sp); return; } lscsi_status = scsi_status & STATUS_MASK; CMD_ENTRY_STATUS(cp) = sts->entry_status; CMD_COMPL_STATUS(cp) = comp_status; CMD_SCSI_STATUS(cp) = scsi_status; fcport = sp->fcport; sense_len = rsp_info_len = resid_len = fw_resid_len = 0; if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { sense_len = le32_to_cpu(sts24->sense_len); rsp_info_len = le32_to_cpu(sts24->rsp_data_len); resid_len = le32_to_cpu(sts24->rsp_residual_count); fw_resid_len = le32_to_cpu(sts24->residual_len); rsp_info = sts24->data; sense_data = sts24->data; host_to_fcp_swap(sts24->data, sizeof(sts24->data)); } else { sense_len = le16_to_cpu(sts->req_sense_length); rsp_info_len = le16_to_cpu(sts->rsp_info_len); resid_len = le32_to_cpu(sts->residual_length); rsp_info = sts->rsp_info; sense_data = sts->req_sense_data; } /* Check for any FCP transport errors. */ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) { /* Sense data lies beyond any FCP RESPONSE data. */ if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) sense_data += rsp_info_len; if (rsp_info_len > 3 && rsp_info[3]) { DEBUG2(printk("scsi(%ld:%d:%d:%d) FCP I/O protocol " "failure (%x/%02x%02x%02x%02x%02x%02x%02x%02x)..." "retrying command\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, rsp_info_len, rsp_info[0], rsp_info[1], rsp_info[2], rsp_info[3], rsp_info[4], rsp_info[5], rsp_info[6], rsp_info[7])); cp->result = DID_BUS_BUSY << 16; qla2x00_sp_compl(ha, sp); return; } } /* * Based on Host and scsi status generate status code for Linux */ switch (comp_status) { case CS_COMPLETE: if (scsi_status == 0) { cp->result = DID_OK << 16; break; } if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) { resid = resid_len; cp->resid = resid; CMD_RESID_LEN(cp) = resid; if (!lscsi_status && ((unsigned)(cp->request_bufflen - resid) < cp->underflow)) { qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d:%d): Mid-layer underflow " "detected (%x of %x bytes)...returning " "error status.\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, resid, cp->request_bufflen); cp->result = DID_ERROR << 16; break; } } cp->result = DID_OK << 16 | lscsi_status; if (lscsi_status != SS_CHECK_CONDITION) break; /* Copy Sense Data into sense buffer. */ memset(cp->sense_buffer, 0, sizeof(cp->sense_buffer)); if (!(scsi_status & SS_SENSE_LEN_VALID)) break; if (sense_len >= sizeof(cp->sense_buffer)) sense_len = sizeof(cp->sense_buffer); CMD_ACTUAL_SNSLEN(cp) = sense_len; sp->request_sense_length = sense_len; sp->request_sense_ptr = cp->sense_buffer; if (sp->request_sense_length > 32) sense_len = 32; memcpy(cp->sense_buffer, sense_data, sense_len); sp->request_sense_ptr += sense_len; sp->request_sense_length -= sense_len; if (sp->request_sense_length != 0) ha->status_srb = sp; DEBUG5(printk("%s(): Check condition Sense data, " "scsi(%ld:%d:%d:%d) cmd=%p pid=%ld\n", __func__, ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, cp, cp->serial_number)); if (sense_len) DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, CMD_ACTUAL_SNSLEN(cp))); break; case CS_DATA_UNDERRUN: resid = resid_len; /* Use F/W calculated residual length. */ if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) resid = fw_resid_len; if (scsi_status & SS_RESIDUAL_UNDER) { cp->resid = resid; CMD_RESID_LEN(cp) = resid; } else { DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d) UNDERRUN status detected " "0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x " "os_underflow=0x%x\n", ha->host_no, cp->device->id, cp->device->lun, comp_status, scsi_status, resid_len, resid, cp->cmnd[0], cp->underflow)); } /* * Check to see if SCSI Status is non zero. If so report SCSI * Status. */ if (lscsi_status != 0) { cp->result = DID_OK << 16 | lscsi_status; if (lscsi_status != SS_CHECK_CONDITION) break; /* Copy Sense Data into sense buffer */ memset(cp->sense_buffer, 0, sizeof(cp->sense_buffer)); if (!(scsi_status & SS_SENSE_LEN_VALID)) break; if (sense_len >= sizeof(cp->sense_buffer)) sense_len = sizeof(cp->sense_buffer); CMD_ACTUAL_SNSLEN(cp) = sense_len; sp->request_sense_length = sense_len; sp->request_sense_ptr = cp->sense_buffer; if (sp->request_sense_length > 32) sense_len = 32; memcpy(cp->sense_buffer, sense_data, sense_len); sp->request_sense_ptr += sense_len; sp->request_sense_length -= sense_len; if (sp->request_sense_length != 0) ha->status_srb = sp; DEBUG5(printk("%s(): Check condition Sense data, " "scsi(%ld:%d:%d:%d) cmd=%p pid=%ld\n", __func__, ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, cp, cp->serial_number)); if (sense_len) DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, CMD_ACTUAL_SNSLEN(cp))); } else { /* * If RISC reports underrun and target does not report * it then we must have a lost frame, so tell upper * layer to retry it by reporting a bus busy. */ if (!(scsi_status & SS_RESIDUAL_UNDER)) { DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped " "frame(s) detected (%x of %x bytes)..." "retrying command.\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, resid, cp->request_bufflen)); cp->result = DID_BUS_BUSY << 16; break; } /* Handle mid-layer underflow */ if ((unsigned)(cp->request_bufflen - resid) < cp->underflow) { qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d:%d): Mid-layer underflow " "detected (%x of %x bytes)...returning " "error status.\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, resid, cp->request_bufflen); cp->result = DID_ERROR << 16; break; } /* Everybody online, looking good... */ cp->result = DID_OK << 16; } break; case CS_DATA_OVERRUN: DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d): OVERRUN status detected 0x%x-0x%x\n", ha->host_no, cp->device->id, cp->device->lun, comp_status, scsi_status)); DEBUG2(printk(KERN_INFO "CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3], cp->cmnd[4], cp->cmnd[5])); DEBUG2(printk(KERN_INFO "PID=0x%lx req=0x%x xtra=0x%x -- returning DID_ERROR " "status!\n", cp->serial_number, cp->request_bufflen, resid_len)); cp->result = DID_ERROR << 16; break; case CS_PORT_LOGGED_OUT: case CS_PORT_CONFIG_CHG: case CS_PORT_BUSY: case CS_INCOMPLETE: case CS_PORT_UNAVAILABLE: /* * If the port is in Target Down state, return all IOs for this * Target with DID_NO_CONNECT ELSE Queue the IOs in the * retry_queue. */ DEBUG2(printk("scsi(%ld:%d:%d): status_entry: Port Down " "pid=%ld, compl status=0x%x, port state=0x%x\n", ha->host_no, cp->device->id, cp->device->lun, cp->serial_number, comp_status, atomic_read(&fcport->state))); cp->result = DID_BUS_BUSY << 16; if (atomic_read(&fcport->state) == FCS_ONLINE) { qla2x00_mark_device_lost(ha, fcport, 1, 1); } break; case CS_RESET: DEBUG2(printk(KERN_INFO "scsi(%ld): RESET status detected 0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); cp->result = DID_RESET << 16; break; case CS_ABORTED: /* * hv2.19.12 - DID_ABORT does not retry the request if we * aborted this request then abort otherwise it must be a * reset. */ DEBUG2(printk(KERN_INFO "scsi(%ld): ABORT status detected 0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); cp->result = DID_RESET << 16; break; case CS_TIMEOUT: cp->result = DID_BUS_BUSY << 16; if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d:%d): TIMEOUT status detected " "0x%x-0x%x\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, comp_status, scsi_status)); break; } DEBUG2(printk(KERN_INFO "scsi(%ld:%d:%d:%d): TIMEOUT status detected 0x%x-0x%x " "sflags=%x.\n", ha->host_no, cp->device->channel, cp->device->id, cp->device->lun, comp_status, scsi_status, le16_to_cpu(sts->status_flags))); /* Check to see if logout occurred. */ if ((le16_to_cpu(sts->status_flags) & SF_LOGOUT_SENT)) qla2x00_mark_device_lost(ha, fcport, 1, 1); break; case CS_QUEUE_FULL: DEBUG2(printk(KERN_INFO "scsi(%ld): QUEUE FULL status detected 0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); /* SCSI Mid-Layer handles device queue full */ cp->result = DID_OK << 16 | lscsi_status; break; default: DEBUG3(printk("scsi(%ld): Error detected (unknown status) " "0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status)); qla_printk(KERN_INFO, ha, "Unknown status detected 0x%x-0x%x.\n", comp_status, scsi_status); cp->result = DID_ERROR << 16; break; } /* Place command on done queue. */ if (ha->status_srb == NULL) qla2x00_sp_compl(ha, sp); }
/** * qla24xx_start_scsi() - Send a SCSI command to the ISP * @sp: command to send to the ISP * * Returns non-zero if a failure occured, else zero. */ int qla24xx_start_scsi(srb_t *sp) { int ret; unsigned long flags; scsi_qla_host_t *ha; struct scsi_cmnd *cmd; uint32_t *clr_ptr; uint32_t index; uint32_t handle; struct cmd_type_7 *cmd_pkt; struct scatterlist *sg; uint16_t cnt; uint16_t req_cnt; uint16_t tot_dsds; struct device_reg_24xx __iomem *reg; /* Setup device pointers. */ ret = 0; ha = sp->ha; reg = &ha->iobase->isp24; cmd = sp->cmd; /* So we know we haven't pci_map'ed anything yet */ tot_dsds = 0; /* Send marker if required */ if (ha->marker_needed != 0) { if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) { return QLA_FUNCTION_FAILED; } ha->marker_needed = 0; } /* Acquire ring specific lock */ spin_lock_irqsave(&ha->hardware_lock, flags); /* Check for room in outstanding command list. */ handle = ha->current_outstanding_cmd; for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { handle++; if (handle == MAX_OUTSTANDING_COMMANDS) handle = 1; if (ha->outstanding_cmds[handle] == 0) break; } if (index == MAX_OUTSTANDING_COMMANDS) goto queuing_error; /* Map the sg table so we have an accurate count of sg entries needed */ if (cmd->use_sg) { sg = (struct scatterlist *) cmd->request_buffer; tot_dsds = pci_map_sg(ha->pdev, sg, cmd->use_sg, cmd->sc_data_direction); if (tot_dsds == 0) goto queuing_error; } else if (cmd->request_bufflen) { dma_addr_t req_dma; req_dma = pci_map_single(ha->pdev, cmd->request_buffer, cmd->request_bufflen, cmd->sc_data_direction); if (dma_mapping_error(req_dma)) goto queuing_error; sp->dma_handle = req_dma; tot_dsds = 1; } req_cnt = qla24xx_calc_iocbs(tot_dsds); if (ha->req_q_cnt < (req_cnt + 2)) { cnt = (uint16_t)RD_REG_DWORD_RELAXED(®->req_q_out); if (ha->req_ring_index < cnt) ha->req_q_cnt = cnt - ha->req_ring_index; else ha->req_q_cnt = ha->request_q_length - (ha->req_ring_index - cnt); } if (ha->req_q_cnt < (req_cnt + 2)) goto queuing_error; /* Build command packet. */ ha->current_outstanding_cmd = handle; ha->outstanding_cmds[handle] = sp; sp->ha = ha; sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle; ha->req_q_cnt -= req_cnt; cmd_pkt = (struct cmd_type_7 *)ha->request_ring_ptr; cmd_pkt->handle = handle; /* Zero out remaining portion of packet. */ /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ clr_ptr = (uint32_t *)cmd_pkt + 2; memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); /* Set NPORT-ID and LUN number*/ cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun); host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); /* Load SCSI command packet. */ memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb)); cmd_pkt->byte_count = cpu_to_le32((uint32_t)cmd->request_bufflen); /* Build IOCB segments */ qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds); /* Set total data segment count. */ cmd_pkt->entry_count = (uint8_t)req_cnt; wmb(); /* Adjust ring index. */ ha->req_ring_index++; if (ha->req_ring_index == ha->request_q_length) { ha->req_ring_index = 0; ha->request_ring_ptr = ha->request_ring; } else ha->request_ring_ptr++; sp->flags |= SRB_DMA_VALID; sp->state = SRB_ACTIVE_STATE; /* Set chip new ring index. */ WRT_REG_DWORD(®->req_q_in, ha->req_ring_index); RD_REG_DWORD_RELAXED(®->req_q_in); /* PCI Posting. */ /* Manage unprocessed RIO/ZIO commands in response queue. */ if (ha->flags.process_response_queue && ha->response_ring_ptr->signature != RESPONSE_PROCESSED) qla24xx_process_response_queue(ha); spin_unlock_irqrestore(&ha->hardware_lock, flags); return QLA_SUCCESS; queuing_error: if (cmd->use_sg && tot_dsds) { sg = (struct scatterlist *) cmd->request_buffer; pci_unmap_sg(ha->pdev, sg, cmd->use_sg, cmd->sc_data_direction); } else if (tot_dsds) { pci_unmap_single(ha->pdev, sp->dma_handle, cmd->request_bufflen, cmd->sc_data_direction); } spin_unlock_irqrestore(&ha->hardware_lock, flags); return QLA_FUNCTION_FAILED; }