From ce5450392fa3ab54f0a84aa3b7589f8d6f2a58af Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 29 Feb 2008 18:25:20 -0600 Subject: [SCSI] qla4xxx: Add target reset functionality This patch adds target reset functionalty. Signed-off-by: Mike Christie Acked-by: David Somayajulu Signed-off-by: James Bottomley --- drivers/scsi/qla4xxx/ql4_fw.h | 1 + drivers/scsi/qla4xxx/ql4_glbl.h | 2 + drivers/scsi/qla4xxx/ql4_mbx.c | 39 ++++++++++++++++++++ drivers/scsi/qla4xxx/ql4_os.c | 82 +++++++++++++++++++++++++++++++---------- 4 files changed, 105 insertions(+), 19 deletions(-) (limited to 'drivers/scsi/qla4xxx') diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index fe415ec85655..ed8ee66a7da5 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -216,6 +216,7 @@ union external_hw_config_reg { #define MBOX_CMD_ABOUT_FW 0x0009 #define MBOX_CMD_PING 0x000B #define MBOX_CMD_LUN_RESET 0x0016 +#define MBOX_CMD_TARGET_WARM_RESET 0x0017 #define MBOX_CMD_GET_MANAGEMENT_DATA 0x001E #define MBOX_CMD_GET_FW_STATUS 0x001F #define MBOX_CMD_SET_ISNS_SERVICE 0x0021 diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index a3608e028bf6..b403a17106c5 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -27,6 +27,8 @@ int qla4xxx_relogin_device(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry); 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, + struct ddb_entry * ddb_entry); int qla4xxx_get_flash(struct scsi_qla_host * ha, dma_addr_t dma_addr, uint32_t offset, uint32_t len); int qla4xxx_get_firmware_status(struct scsi_qla_host * ha); diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index 35cd73c72a68..c577d79bd7e8 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -713,6 +713,45 @@ int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry, return status; } +/** + * qla4xxx_reset_target - issues target Reset + * @ha: Pointer to host adapter structure. + * @db_entry: Pointer to device database entry + * @un_entry: Pointer to lun entry structure + * + * This routine performs a TARGET RESET on the specified target. + * The caller must ensure that the ddb_entry pointers + * are valid before calling this routine. + **/ +int qla4xxx_reset_target(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + int status = QLA_SUCCESS; + + DEBUG2(printk("scsi%ld:%d: target reset issued\n", ha->host_no, + ddb_entry->os_target_id)); + + /* + * Send target reset command to ISP, so that the ISP will return all + * outstanding requests with RESET status + */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + + mbox_cmd[0] = MBOX_CMD_TARGET_WARM_RESET; + mbox_cmd[1] = ddb_entry->fw_ddb_index; + mbox_cmd[5] = 0x01; /* Immediate Command Enable */ + + qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], + &mbox_sts[0]); + if (mbox_sts[0] != MBOX_STS_COMMAND_COMPLETE && + mbox_sts[0] != MBOX_STS_COMMAND_ERROR) + status = QLA_ERROR; + + return status; +} int qla4xxx_get_flash(struct scsi_qla_host * ha, dma_addr_t dma_addr, uint32_t offset, uint32_t len) diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 8b92f348f02c..31e605caf0f1 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -71,6 +71,7 @@ static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session); static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *)); 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); static int qla4xxx_slave_alloc(struct scsi_device *device); static int qla4xxx_slave_configure(struct scsi_device *device); @@ -84,6 +85,7 @@ static struct scsi_host_template qla4xxx_driver_template = { .queuecommand = qla4xxx_queuecommand, .eh_device_reset_handler = qla4xxx_eh_device_reset, + .eh_target_reset_handler = qla4xxx_eh_target_reset, .eh_host_reset_handler = qla4xxx_eh_host_reset, .slave_configure = qla4xxx_slave_configure, @@ -1482,7 +1484,7 @@ static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha) } /** - * qla4xxx_eh_wait_for_active_target_commands - wait for active cmds to finish. + * qla4xxx_eh_wait_for_commands - wait for active cmds to finish. * @ha: pointer to to HBA * @t: target id * @l: lun id @@ -1490,20 +1492,22 @@ static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha) * This function waits for all outstanding commands to a lun to complete. It * returns 0 if all pending commands are returned and 1 otherwise. **/ -static int qla4xxx_eh_wait_for_active_target_commands(struct scsi_qla_host *ha, - int t, int l) +static int qla4xxx_eh_wait_for_commands(struct scsi_qla_host *ha, + struct scsi_target *stgt, + struct scsi_device *sdev) { int cnt; int status = 0; struct scsi_cmnd *cmd; /* - * Waiting for all commands for the designated target in the active - * array + * Waiting for all commands for the designated target or dev + * in the active array */ for (cnt = 0; cnt < ha->host->can_queue; cnt++) { cmd = scsi_host_find_tag(ha->host, cnt); - if (cmd && cmd->device->id == t && cmd->device->lun == l) { + if (cmd && stgt == scsi_target(cmd->device) && + (!sdev || sdev == cmd->device)) { if (!qla4xxx_eh_wait_on_command(ha, cmd)) { status++; break; @@ -1551,19 +1555,12 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) /* Send marker. */ ha->marker_needed = 1; - /* - * If we are coming down the EH path, wait for all commands to complete - * for the device. - */ - if (cmd->device->host->shost_state == SHOST_RECOVERY) { - if (qla4xxx_eh_wait_for_active_target_commands(ha, - cmd->device->id, - cmd->device->lun)){ - dev_info(&ha->pdev->dev, - "DEVICE RESET FAILED - waiting for " - "commands.\n"); - goto eh_dev_reset_done; - } + if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device), + cmd->device)) { + dev_info(&ha->pdev->dev, + "DEVICE RESET FAILED - waiting for " + "commands.\n"); + goto eh_dev_reset_done; } dev_info(&ha->pdev->dev, @@ -1578,6 +1575,53 @@ eh_dev_reset_done: return ret; } +/** + * qla4xxx_eh_target_reset - callback for target reset. + * @cmd: Pointer to Linux's SCSI command structure + * + * This routine is called by the Linux OS to reset the target. + **/ +static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd) +{ + struct scsi_qla_host *ha = to_qla_host(cmd->device->host); + struct ddb_entry *ddb_entry = cmd->device->hostdata; + int stat; + + if (!ddb_entry) + return FAILED; + + starget_printk(KERN_INFO, scsi_target(cmd->device), + "WARM TARGET RESET ISSUED.\n"); + + DEBUG2(printk(KERN_INFO + "scsi%ld: TARGET_DEVICE_RESET cmd=%p jiffies = 0x%lx, " + "to=%x,dpc_flags=%lx, status=%x allowed=%d\n", + ha->host_no, cmd, jiffies, cmd->timeout_per_command / HZ, + ha->dpc_flags, cmd->result, cmd->allowed)); + + stat = qla4xxx_reset_target(ha, ddb_entry); + if (stat != QLA_SUCCESS) { + starget_printk(KERN_INFO, scsi_target(cmd->device), + "WARM TARGET RESET FAILED.\n"); + return FAILED; + } + + /* Send marker. */ + ha->marker_needed = 1; + + if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device), + NULL)) { + starget_printk(KERN_INFO, scsi_target(cmd->device), + "WARM TARGET DEVICE RESET FAILED - " + "waiting for commands.\n"); + return FAILED; + } + + starget_printk(KERN_INFO, scsi_target(cmd->device), + "WARM TARGET RESET SUCCEEDED.\n"); + return SUCCESS; +} + /** * qla4xxx_eh_host_reset - kernel callback * @cmd: Pointer to Linux's SCSI command structure -- cgit v1.2.3 From 9d56291366cd6ab156be722e42cf487bef20f5fd Mon Sep 17 00:00:00 2001 From: David C Somayajulu Date: Wed, 19 Mar 2008 11:23:03 -0700 Subject: [SCSI] qla4xxx: fix scsi command completion, lun reset and target reset code This patch contains the following: 1. when hba completion status is good, check for iscsi transport errors (underflow/overflow) prior to checking the scsi status 2. New firmware requires that one marker iocb be issued for each task management command. The patch issues marker iocb immediately following a LUN or Target reset. Signed-off-by: David C Somayajulu Signed-off-by: James Bottomley --- drivers/scsi/qla4xxx/ql4_fw.h | 3 ++- drivers/scsi/qla4xxx/ql4_glbl.h | 2 ++ drivers/scsi/qla4xxx/ql4_iocb.c | 14 +++----------- drivers/scsi/qla4xxx/ql4_isr.c | 40 +++++----------------------------------- drivers/scsi/qla4xxx/ql4_os.c | 20 ++++++++++++++------ 5 files changed, 26 insertions(+), 53 deletions(-) (limited to 'drivers/scsi/qla4xxx') diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index ed8ee66a7da5..1b667a70cffa 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -678,7 +678,8 @@ struct qla4_marker_entry { uint32_t system_defined; /* 04-07 */ uint16_t target; /* 08-09 */ uint16_t modifier; /* 0A-0B */ -#define MM_LUN_RESET 0 +#define MM_LUN_RESET 0 +#define MM_TGT_WARM_RESET 1 uint16_t flags; /* 0C-0D */ uint16_t reserved1; /* 0E-0F */ diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index b403a17106c5..96ebfb021f6c 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -70,6 +70,8 @@ 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); void qla4xxx_dump_buffer(void *b, uint32_t size); +int qla4xxx_send_marker_iocb(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry, int lun, uint16_t mrkr_mod); extern int ql4xextended_error_logging; extern int ql4xdiscoverywait; diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c index e4461b5d767a..912a67494adf 100644 --- a/drivers/scsi/qla4xxx/ql4_iocb.c +++ b/drivers/scsi/qla4xxx/ql4_iocb.c @@ -66,8 +66,8 @@ static int qla4xxx_get_req_pkt(struct scsi_qla_host *ha, * * This routine issues a marker IOCB. **/ -static int qla4xxx_send_marker_iocb(struct scsi_qla_host *ha, - struct ddb_entry *ddb_entry, int lun) +int qla4xxx_send_marker_iocb(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry, int lun, uint16_t mrkr_mod) { struct qla4_marker_entry *marker_entry; unsigned long flags = 0; @@ -87,7 +87,7 @@ static int qla4xxx_send_marker_iocb(struct scsi_qla_host *ha, marker_entry->hdr.entryType = ET_MARKER; marker_entry->hdr.entryCount = 1; marker_entry->target = cpu_to_le16(ddb_entry->fw_ddb_index); - marker_entry->modifier = cpu_to_le16(MM_LUN_RESET); + marker_entry->modifier = cpu_to_le16(mrkr_mod); int_to_scsilun(lun, &marker_entry->lun); wmb(); @@ -210,14 +210,6 @@ int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb) /* Get real lun and adapter */ ddb_entry = srb->ddb; - /* Send marker(s) if needed. */ - if (ha->marker_needed == 1) { - if (qla4xxx_send_marker_iocb(ha, ddb_entry, - cmd->device->lun) != QLA_SUCCESS) - return QLA_ERROR; - - ha->marker_needed = 0; - } tot_dsds = 0; /* Acquire hardware specific lock */ diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index fc84db4069f4..a91a57c57bff 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -10,28 +10,6 @@ #include "ql4_dbg.h" #include "ql4_inline.h" -/** - * qla2x00_process_completed_request() - Process a Fast Post response. - * @ha: SCSI driver HA context - * @index: SRB index - **/ -static void qla4xxx_process_completed_request(struct scsi_qla_host *ha, - uint32_t index) -{ - struct srb *srb; - - 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); - } -} - /** * qla4xxx_status_entry - processes status IOCBs * @ha: Pointer to host adapter structure. @@ -47,14 +25,6 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, 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) { /* FIXMEdg: Don't we need to reset ISP in this case??? */ @@ -62,6 +32,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,10 +61,6 @@ 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; - break; - } if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) { cmd->result = DID_ERROR << 16; @@ -100,7 +69,8 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, if (sts_entry->iscsiFlags &ISCSI_FLAG_RESIDUAL_UNDER) { scsi_set_resid(cmd, residual); - if ((scsi_bufflen(cmd) - residual) < cmd->underflow) { + if (!scsi_status && ((scsi_bufflen(cmd) - residual) < + cmd->underflow)) { cmd->result = DID_ERROR << 16; diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 31e605caf0f1..0c786944d2c2 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -1552,9 +1552,6 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) goto eh_dev_reset_done; } - /* Send marker. */ - ha->marker_needed = 1; - if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device), cmd->device)) { dev_info(&ha->pdev->dev, @@ -1563,6 +1560,11 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) goto eh_dev_reset_done; } + /* Send marker. */ + if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun, + MM_LUN_RESET) != QLA_SUCCESS) + goto eh_dev_reset_done; + dev_info(&ha->pdev->dev, "scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n", ha->host_no, cmd->device->channel, cmd->device->id, @@ -1606,9 +1608,6 @@ static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd) return FAILED; } - /* Send marker. */ - ha->marker_needed = 1; - if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device), NULL)) { starget_printk(KERN_INFO, scsi_target(cmd->device), @@ -1617,6 +1616,15 @@ static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd) return FAILED; } + /* Send marker. */ + if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun, + MM_TGT_WARM_RESET) != QLA_SUCCESS) { + starget_printk(KERN_INFO, scsi_target(cmd->device), + "WARM TARGET DEVICE RESET FAILED - " + "marker iocb failed.\n"); + return FAILED; + } + starget_printk(KERN_INFO, scsi_target(cmd->device), "WARM TARGET RESET SUCCEEDED.\n"); return SUCCESS; -- cgit v1.2.3