/** * 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.
**/ staticvoid 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) {
DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d:%llu: %s:" " sense len 0\n", ha->host_no,
cmd->device->channel, cmd->device->id,
cmd->device->lun, __func__));
ha->status_srb = NULL; 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);
srb = qla4xxx_del_from_active_array(ha, le32_to_cpu(sts_entry->handle)); if (!srb) {
ql4_printk(KERN_WARNING, ha, "%s invalid status entry: " "handle=0x%0x, srb=%p\n", __func__,
sts_entry->handle, srb); if (is_qla80XX(ha))
set_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags); else
set_bit(DPC_RESET_HA, &ha->dpc_flags); return;
}
cmd = srb->cmd; if (cmd == NULL) {
DEBUG2(printk("scsi%ld: %s: Command already returned back to " "OS pkt->handle=%d srb=%p srb->state:%d\n",
ha->host_no, __func__, sts_entry->handle,
srb, srb->state));
ql4_printk(KERN_WARNING, ha, "Command is NULL:" " already returned to OS (srb=%p)\n", srb); return;
}
if (scsi_status != SAM_STAT_CHECK_CONDITION) break;
/* Copy Sense Data into sense buffer. */
qla4xxx_copy_sense(ha, sts_entry, srb); break;
case SCS_INCOMPLETE: /* Always set the status to DID_ERROR, since
* all conditions result in that status anyway */
cmd->result = DID_ERROR << 16; break;
case SCS_TIMEOUT:
DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%llu: Timeout\n",
ha->host_no, cmd->device->channel,
cmd->device->id, cmd->device->lun));
cmd->result = DID_TRANSPORT_DISRUPTED << 16;
/* * Mark device missing so that we won't continue to send * I/O to this device. We should get a ddb state change * AEN soon.
*/ if (iscsi_is_session_online(ddb_entry->sess))
qla4xxx_mark_device_missing(ddb_entry->sess); break;
case SCS_DATA_UNDERRUN: case SCS_DATA_OVERRUN: if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) ||
(sts_entry->completionStatus == SCS_DATA_OVERRUN)) {
DEBUG2(printk("scsi%ld:%d:%d:%llu: %s: ""Data overrun\n",
ha->host_no,
cmd->device->channel, cmd->device->id,
cmd->device->lun, __func__));
cmd->result = DID_ERROR << 16; break;
}
scsi_set_resid(cmd, residual);
if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_UNDER) {
/* Both the firmware and target reported UNDERRUN: * * MID-LAYER UNDERFLOW case: * Some kernels do not properly detect midlayer * underflow, so we manually check it and return * ERROR if the minimum required data was not * received. * * ALL OTHER cases: * Fall thru to check scsi_status
*/ if (!scsi_status && (scsi_bufflen(cmd) - residual) <
cmd->underflow) {
DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d:%llu: %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));
/* * The firmware reports UNDERRUN, but the target does * not report it: * * scsi_status | host_byte device_byte * | (19:16) (7:0) * ============= | ========= =========== * TASK_SET_FULL | DID_OK scsi_status * BUSY | DID_OK scsi_status * ALL OTHERS | DID_ERROR scsi_status * * Note: If scsi_status is task set full or busy, * then this else if would fall thru to check the * scsi_status and return DID_OK.
*/
DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d:%llu: %s: Dropped frame(s) detected (0x%x of 0x%x bytes).\n",
ha->host_no,
cmd->device->channel,
cmd->device->id,
cmd->device->lun, __func__,
residual,
scsi_bufflen(cmd)));
check_scsi_status: if (scsi_status == SAM_STAT_CHECK_CONDITION)
qla4xxx_copy_sense(ha, sts_entry, srb);
break;
case SCS_DEVICE_LOGGED_OUT: case SCS_DEVICE_UNAVAILABLE:
DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%llu: SCS_DEVICE " "state: 0x%x\n", ha->host_no,
cmd->device->channel, cmd->device->id,
cmd->device->lun, sts_entry->completionStatus)); /* * Mark device missing so that we won't continue to * send I/O to this device. We should get a ddb * state change AEN soon.
*/ if (iscsi_is_session_online(ddb_entry->sess))
qla4xxx_mark_device_missing(ddb_entry->sess);
/* complete the request, if not waiting for status_continuation pkt */
srb->cc_stat = sts_entry->completionStatus; if (ha->status_srb == NULL)
kref_put(&srb->srb_ref, qla4xxx_srb_compl);
}
/** * qla4xxx_process_response_queue - process response queue completions * @ha: Pointer to host adapter structure. * * This routine process response queue completions in interrupt context. * Hardware_lock locked upon entry
**/ void qla4xxx_process_response_queue(struct scsi_qla_host *ha)
{ struct srb *srb = NULL; struct status_entry *sts_entry;
/* Process all responses from response queue */ while ((ha->response_ptr->signature != RESPONSE_PROCESSED)) {
sts_entry = (struct status_entry *) ha->response_ptr;
/* Advance pointers for next entry */ if (ha->response_out == (RESPONSE_QUEUE_DEPTH - 1)) {
ha->response_out = 0;
ha->response_ptr = ha->response_ring;
} else {
ha->response_out++;
ha->response_ptr++;
}
/* process entry */ switch (sts_entry->hdr.entryType) { case ET_STATUS: /* Common status */
qla4xxx_status_entry(ha, sts_entry); break;
case ET_PASSTHRU_STATUS: if (sts_entry->hdr.systemDefined == SD_ISCSI_PDU)
qla4xxx_passthru_status_entry(ha,
(struct passthru_status *)sts_entry); else
ql4_printk(KERN_ERR, ha, "%s: Invalid status received\n",
__func__);
break;
case ET_STATUS_CONTINUATION:
qla4xxx_status_cont_entry(ha,
(struct status_cont_entry *) sts_entry); break;
case ET_COMMAND: /* ISP device queue is full. Command not * accepted by ISP. Queue command for
* later */
/* ETRY normally by sending it back with
* DID_BUS_BUSY */
srb->cmd->result = DID_BUS_BUSY << 16;
kref_put(&srb->srb_ref, qla4xxx_srb_compl); break;
case ET_CONTINUE: /* Just throw away the continuation entries */
DEBUG2(printk("scsi%ld: %s: Continuation entry - " "ignoring\n", ha->host_no, __func__)); break;
case ET_MBOX_STATUS:
DEBUG2(ql4_printk(KERN_INFO, ha, "%s: mbox status IOCB\n", __func__));
qla4xxx_mbox_status_entry(ha,
(struct mbox_status_iocb *)sts_entry); break;
if (test_bit(AF_MBOX_COMMAND, &ha->flags)) { /* * Copy all mailbox registers to a temporary * location and set mailbox command done flag
*/ for (i = 0; i < ha->mbox_status_count; i++)
ha->mbox_status[i] = readl(&mailbox_out[i]);
set_bit(AF_MBOX_COMMAND_DONE, &ha->flags);
if (test_bit(AF_MBOX_COMMAND_NOPOLL, &ha->flags))
complete(&ha->mbx_intr_comp);
}
} elseif (mbox_status >> 12 == MBOX_ASYNC_EVENT_STATUS) { for (i = 0; i < MBOX_AEN_REG_COUNT; i++)
mbox_sts[i] = readl(&mailbox_out[i]);
/* Immediately process the AENs that don't require much work.
* Only queue the database_changed AENs */ if (ha->aen_log.count < MAX_AEN_ENTRIES) { for (i = 0; i < MBOX_AEN_REG_COUNT; i++)
ha->aen_log.entry[ha->aen_log.count].mbox_sts[i] =
mbox_sts[i];
ha->aen_log.count++;
} switch (mbox_status) { case MBOX_ASTS_SYSTEM_ERROR: /* Log Mailbox registers */
ql4_printk(KERN_INFO, ha, "%s: System Err\n", __func__);
qla4xxx_dump_registers(ha);
case MBOX_ASTS_REQUEST_TRANSFER_ERROR: case MBOX_ASTS_RESPONSE_TRANSFER_ERROR: case MBOX_ASTS_NVRAM_INVALID: case MBOX_ASTS_IP_ADDRESS_CHANGED: case MBOX_ASTS_DHCP_LEASE_EXPIRED:
DEBUG2(printk("scsi%ld: AEN %04x, ERROR Status, " "Reset HA\n", ha->host_no, mbox_status)); if (is_qla80XX(ha))
set_bit(DPC_RESET_HA_FW_CONTEXT,
&ha->dpc_flags); else
set_bit(DPC_RESET_HA, &ha->dpc_flags); break;
case MBOX_ASTS_LINK_UP:
set_bit(AF_LINK_UP, &ha->flags); if (test_bit(AF_INIT_DONE, &ha->flags))
set_bit(DPC_LINK_CHANGED, &ha->dpc_flags);
ql4_printk(KERN_INFO, ha, "%s: LINK UP\n", __func__);
qla4xxx_post_aen_work(ha, ISCSI_EVENT_LINKUP, sizeof(mbox_sts),
(uint8_t *) mbox_sts);
if ((is_qla8032(ha) || is_qla8042(ha)) &&
ha->notify_link_up_comp)
complete(&ha->link_up_comp);
break;
case MBOX_ASTS_LINK_DOWN:
clear_bit(AF_LINK_UP, &ha->flags); if (test_bit(AF_INIT_DONE, &ha->flags)) {
set_bit(DPC_LINK_CHANGED, &ha->dpc_flags);
qla4xxx_wake_dpc(ha);
}
ql4_printk(KERN_INFO, ha, "%s: LINK DOWN\n", __func__);
qla4xxx_post_aen_work(ha, ISCSI_EVENT_LINKDOWN, sizeof(mbox_sts),
(uint8_t *) mbox_sts); break;
case MBOX_ASTS_HEARTBEAT:
ha->seconds_since_last_heartbeat = 0; break;
case MBOX_ASTS_PROTOCOL_STATISTIC_ALARM: case MBOX_ASTS_SCSI_COMMAND_PDU_REJECTED: /* Target * mode
* only */ case MBOX_ASTS_UNSOLICITED_PDU_RECEIVED: /* Connection mode */ case MBOX_ASTS_IPSEC_SYSTEM_FATAL_ERROR: case MBOX_ASTS_SUBNET_STATE_CHANGE: case MBOX_ASTS_DUPLICATE_IP: /* No action */
DEBUG2(printk("scsi%ld: AEN %04x\n", ha->host_no,
mbox_status)); break;
qla4xxx_update_ipaddr_state(ha, mbox_sts[5],
mbox_sts[3]); /* mbox_sts[2] = Old ACB state
* mbox_sts[3] = new ACB state */ if ((mbox_sts[3] == IP_ADDRSTATE_PREFERRED) &&
((mbox_sts[2] == IP_ADDRSTATE_TENTATIVE) ||
(mbox_sts[2] == IP_ADDRSTATE_ACQUIRING))) {
set_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags);
} elseif ((mbox_sts[3] == IP_ADDRSTATE_ACQUIRING) &&
(mbox_sts[2] == IP_ADDRSTATE_PREFERRED)) { if (is_qla80XX(ha))
set_bit(DPC_RESET_HA_FW_CONTEXT,
&ha->dpc_flags); else
set_bit(DPC_RESET_HA, &ha->dpc_flags);
} elseif (mbox_sts[3] == IP_ADDRSTATE_DISABLING) {
ql4_printk(KERN_INFO, ha, "scsi%ld: %s: ACB in disabling state\n",
ha->host_no, __func__);
} elseif (mbox_sts[3] == IP_ADDRSTATE_UNCONFIGURED) {
complete(&ha->disable_acb_comp);
ql4_printk(KERN_INFO, ha, "scsi%ld: %s: ACB state unconfigured\n",
ha->host_no, __func__);
} break;
case MBOX_ASTS_IPV6_LINK_MTU_CHANGE: case MBOX_ASTS_IPV6_AUTO_PREFIX_IGNORED: case MBOX_ASTS_IPV6_ND_LOCAL_PREFIX_IGNORED: /* No action */
DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld: AEN %04x\n",
ha->host_no, mbox_status)); break;
void qla4_83xx_interrupt_service_routine(struct scsi_qla_host *ha,
uint32_t intr_status)
{ /* Process mailbox/asynch event interrupt.*/ if (intr_status) {
qla4xxx_isr_decode_mailbox(ha,
readl(&ha->qla4_83xx_reg->mailbox_out[0])); /* clear the interrupt */
writel(0, &ha->qla4_83xx_reg->risc_intr);
} else {
qla4xxx_process_response_queue(ha);
}
/* clear the interrupt */
writel(0, &ha->qla4_83xx_reg->mb_int_mask);
}
/** * qla4_82xx_interrupt_service_routine - isr * @ha: pointer to host adapter structure. * @intr_status: Local interrupt status/type. * * This is the main interrupt service routine. * hardware_lock locked upon entry. runs in interrupt context.
**/ void qla4_82xx_interrupt_service_routine(struct scsi_qla_host *ha,
uint32_t intr_status)
{ /* Process response queue interrupt. */ if ((intr_status & HSRX_RISC_IOCB_INT) &&
test_bit(AF_INIT_DONE, &ha->flags))
qla4xxx_process_response_queue(ha);
/* Process mailbox/asynch event interrupt.*/ if (intr_status & HSRX_RISC_MB_INT)
qla4xxx_isr_decode_mailbox(ha,
readl(&ha->qla4_82xx_reg->mailbox_out[0]));
/* clear the interrupt */
writel(0, &ha->qla4_82xx_reg->host_int);
readl(&ha->qla4_82xx_reg->host_int);
}
/** * qla4xxx_interrupt_service_routine - isr * @ha: pointer to host adapter structure. * @intr_status: Local interrupt status/type. * * This is the main interrupt service routine. * hardware_lock locked upon entry. runs in interrupt context.
**/ void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha,
uint32_t intr_status)
{ /* Process response queue interrupt. */ if (intr_status & CSR_SCSI_COMPLETION_INTR)
qla4xxx_process_response_queue(ha);
/* Process mailbox/asynch event interrupt.*/ if (intr_status & CSR_SCSI_PROCESSOR_INTR) {
qla4xxx_isr_decode_mailbox(ha,
readl(&ha->reg->mailbox[0]));
ha = (struct scsi_qla_host *) dev_id; if (!ha) {
DEBUG2(printk(KERN_INFO "qla4xxx: Interrupt with NULL host ptr\n")); return IRQ_NONE;
}
spin_lock_irqsave(&ha->hardware_lock, flags);
ha->isr_count++; /* * Repeatedly service interrupts up to a maximum of * MAX_REQS_SERVICED_PER_INTR
*/ while (1) { /* * Read interrupt status
*/ if (ha->isp_ops->rd_shdw_rsp_q_in(ha) !=
ha->response_out)
intr_status = CSR_SCSI_COMPLETION_INTR; else
intr_status = readl(&ha->reg->ctrl_status);
if ((intr_status &
(CSR_SCSI_RESET_INTR|CSR_FATAL_ERROR|INTR_PENDING)) == 0) { if (reqs_count == 0)
ha->spurious_int_count++; break;
}
/* Issue Soft Reset to clear this error condition. * This will prevent the RISC from repeatedly * interrupting the driver; thus, allowing the DPC to * get scheduled to continue error recovery. * NOTE: Disabling RISC interrupts does not work in * this case, as CSR_FATAL_ERROR overrides
* CSR_SCSI_INTR_ENABLE */ if ((readl(&ha->reg->ctrl_status) &
CSR_SCSI_RESET_INTR) == 0) {
writel(set_rmask(CSR_SOFT_RESET),
&ha->reg->ctrl_status);
readl(&ha->reg->ctrl_status);
}
if (unlikely(pci_channel_offline(ha->pdev))) return IRQ_HANDLED;
ha->isr_count++;
status = qla4_82xx_rd_32(ha, ISR_INT_VECTOR); if (!(status & ha->nx_legacy_intr.int_vec_bit)) return IRQ_NONE;
status = qla4_82xx_rd_32(ha, ISR_INT_STATE_REG); if (!ISR_IS_LEGACY_INTR_TRIGGERED(status)) {
DEBUG7(ql4_printk(KERN_INFO, ha, "%s legacy Int not triggered\n", __func__)); return IRQ_NONE;
}
/* clear the interrupt */
qla4_82xx_wr_32(ha, ha->nx_legacy_intr.tgt_status_reg, 0xffffffff);
/* read twice to ensure write is flushed */
qla4_82xx_rd_32(ha, ISR_INT_VECTOR);
qla4_82xx_rd_32(ha, ISR_INT_VECTOR);
spin_lock_irqsave(&ha->hardware_lock, flags); while (1) { if (!(readl(&ha->qla4_82xx_reg->host_int) &
ISRX_82XX_RISC_INT)) {
qla4_82xx_spurious_interrupt(ha, reqs_count); break;
}
intr_status = readl(&ha->qla4_82xx_reg->host_status); if ((intr_status &
(HSRX_RISC_MB_INT | HSRX_RISC_IOCB_INT)) == 0) {
qla4_82xx_spurious_interrupt(ha, reqs_count); break;
}
/* Legacy interrupt is valid if bit31 of leg_int_ptr is set */ if (!(leg_int_ptr & LEG_INT_PTR_B31)) {
DEBUG7(ql4_printk(KERN_ERR, ha, "%s: Legacy Interrupt Bit 31 not set, spurious interrupt!\n",
__func__)); return IRQ_NONE;
}
/* Validate the PCIE function ID set in leg_int_ptr bits [19..16] */ if ((leg_int_ptr & PF_BITS_MASK) != ha->pf_bit) {
DEBUG7(ql4_printk(KERN_ERR, ha, "%s: Incorrect function ID 0x%x in legacy interrupt register, ha->pf_bit = 0x%x\n",
__func__, (leg_int_ptr & PF_BITS_MASK),
ha->pf_bit)); return IRQ_NONE;
}
/* To de-assert legacy interrupt, write 0 to Legacy Interrupt Trigger * Control register and poll till Legacy Interrupt Pointer register * bit30 is 0.
*/
writel(0, &ha->qla4_83xx_reg->leg_int_trig); do {
leg_int_ptr = readl(&ha->qla4_83xx_reg->leg_int_ptr); if ((leg_int_ptr & PF_BITS_MASK) != ha->pf_bit) break;
} while (leg_int_ptr & LEG_INT_PTR_B30);
spin_lock_irqsave(&ha->hardware_lock, flags); if (is_qla8032(ha) || is_qla8042(ha)) {
ival = readl(&ha->qla4_83xx_reg->iocb_int_mask); if (ival == 0) {
ql4_printk(KERN_INFO, ha, "%s: It is a spurious iocb interrupt!\n",
__func__); goto exit_msix_rsp_q;
}
qla4xxx_process_response_queue(ha);
writel(0, &ha->qla4_83xx_reg->iocb_int_mask);
} else {
intr_status = readl(&ha->qla4_82xx_reg->host_status); if (intr_status & HSRX_RISC_IOCB_INT) {
qla4xxx_process_response_queue(ha);
writel(0, &ha->qla4_82xx_reg->host_int);
} else {
ql4_printk(KERN_INFO, ha, "%s: spurious iocb interrupt...\n",
__func__); goto exit_msix_rsp_q;
}
}
ha->isr_count++;
exit_msix_rsp_q:
spin_unlock_irqrestore(&ha->hardware_lock, flags); return IRQ_HANDLED;
}
/** * qla4xxx_process_aen - processes AENs generated by firmware * @ha: pointer to host adapter structure. * @process_aen: type of AENs to process * * Processes specific types of Asynchronous Events generated by firmware. * The type of AENs to process is specified by process_aen and can be * PROCESS_ALL_AENS 0 * FLUSH_DDB_CHANGED_AENS 1 * RELOGIN_DDB_CHANGED_AENS 2
**/ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen)
{
uint32_t mbox_sts[MBOX_AEN_REG_COUNT]; struct aen *aen; int i; unsignedlong flags;
spin_lock_irqsave(&ha->hardware_lock, flags); while (ha->aen_out != ha->aen_in) {
aen = &ha->aen_q[ha->aen_out]; /* copy aen information to local structure */ for (i = 0; i < MBOX_AEN_REG_COUNT; i++)
mbox_sts[i] = aen->mbox_sts[i];
ha->aen_q_count++;
ha->aen_out++;
if (ha->aen_out == MAX_AEN_ENTRIES)
ha->aen_out = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.