diff options
-rw-r--r-- | drivers/scsi/hpsa.c | 145 | ||||
-rw-r--r-- | drivers/scsi/hpsa_cmd.h | 9 |
2 files changed, 119 insertions, 35 deletions
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index d5a5a6413b6d..0c4751c0d144 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -43,6 +43,7 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> +#include <scsi/scsi_eh.h> #include <linux/cciss_ioctl.h> #include <linux/string.h> #include <linux/bitmap.h> @@ -268,16 +269,49 @@ static inline struct ctlr_info *shost_to_hba(struct Scsi_Host *sh) return (struct ctlr_info *) *priv; } +/* extract sense key, asc, and ascq from sense data. -1 means invalid. */ +static void decode_sense_data(const u8 *sense_data, int sense_data_len, + u8 *sense_key, u8 *asc, u8 *ascq) +{ + struct scsi_sense_hdr sshdr; + bool rc; + + *sense_key = -1; + *asc = -1; + *ascq = -1; + + if (sense_data_len < 1) + return; + + rc = scsi_normalize_sense(sense_data, sense_data_len, &sshdr); + if (rc) { + *sense_key = sshdr.sense_key; + *asc = sshdr.asc; + *ascq = sshdr.ascq; + } +} + static int check_for_unit_attention(struct ctlr_info *h, struct CommandList *c) { - if (c->err_info->SenseInfo[2] != UNIT_ATTENTION) + u8 sense_key, asc, ascq; + int sense_len; + + if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo)) + sense_len = sizeof(c->err_info->SenseInfo); + else + sense_len = c->err_info->SenseLen; + + decode_sense_data(c->err_info->SenseInfo, sense_len, + &sense_key, &asc, &ascq); + if (sense_key != UNIT_ATTENTION || asc == -1) return 0; - switch (c->err_info->SenseInfo[12]) { + switch (asc) { case STATE_CHANGED: - dev_warn(&h->pdev->dev, HPSA "%d: a state change " - "detected, command retried\n", h->ctlr); + dev_warn(&h->pdev->dev, + HPSA "%d: a state change detected, command retried\n", + h->ctlr); break; case LUN_FAILED: dev_warn(&h->pdev->dev, @@ -1855,6 +1889,34 @@ retry_cmd: queue_work_on(raw_smp_processor_id(), h->resubmit_wq, &c->work); } +/* Returns 0 on success, < 0 otherwise. */ +static int hpsa_evaluate_tmf_status(struct ctlr_info *h, + struct CommandList *cp) +{ + u8 tmf_status = cp->err_info->ScsiStatus; + + switch (tmf_status) { + case CISS_TMF_COMPLETE: + /* + * CISS_TMF_COMPLETE never happens, instead, + * ei->CommandStatus == 0 for this case. + */ + case CISS_TMF_SUCCESS: + return 0; + case CISS_TMF_INVALID_FRAME: + case CISS_TMF_NOT_SUPPORTED: + case CISS_TMF_FAILED: + case CISS_TMF_WRONG_LUN: + case CISS_TMF_OVERLAPPED_TAG: + break; + default: + dev_warn(&h->pdev->dev, "Unknown TMF status: 0x%02x\n", + tmf_status); + break; + } + return -tmf_status; +} + static void complete_scsi_command(struct CommandList *cp) { struct scsi_cmnd *cmd; @@ -1862,9 +1924,9 @@ static void complete_scsi_command(struct CommandList *cp) struct ErrorInfo *ei; struct hpsa_scsi_dev_t *dev; - unsigned char sense_key; - unsigned char asc; /* additional sense code */ - unsigned char ascq; /* additional sense code qualifier */ + u8 sense_key; + u8 asc; /* additional sense code */ + u8 ascq; /* additional sense code qualifier */ unsigned long sense_data_size; ei = cp->err_info; @@ -1899,8 +1961,6 @@ static void complete_scsi_command(struct CommandList *cp) if (cp->cmd_type == CMD_IOACCEL2) return process_ioaccel2_completion(h, cp, cmd, dev); - cmd->result |= ei->ScsiStatus; - scsi_set_resid(cmd, ei->ResidualCnt); if (ei->CommandStatus == 0) { if (cp->cmd_type == CMD_IOACCEL1) @@ -1910,16 +1970,6 @@ static void complete_scsi_command(struct CommandList *cp) return; } - /* copy the sense data */ - if (SCSI_SENSE_BUFFERSIZE < sizeof(ei->SenseInfo)) - sense_data_size = SCSI_SENSE_BUFFERSIZE; - else - sense_data_size = sizeof(ei->SenseInfo); - if (ei->SenseLen < sense_data_size) - sense_data_size = ei->SenseLen; - - memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size); - /* For I/O accelerator commands, copy over some fields to the normal * CISS header used below for error handling. */ @@ -1951,14 +2001,18 @@ static void complete_scsi_command(struct CommandList *cp) switch (ei->CommandStatus) { case CMD_TARGET_STATUS: - if (ei->ScsiStatus) { - /* Get sense key */ - sense_key = 0xf & ei->SenseInfo[2]; - /* Get additional sense code */ - asc = ei->SenseInfo[12]; - /* Get addition sense code qualifier */ - ascq = ei->SenseInfo[13]; - } + cmd->result |= ei->ScsiStatus; + /* copy the sense data */ + if (SCSI_SENSE_BUFFERSIZE < sizeof(ei->SenseInfo)) + sense_data_size = SCSI_SENSE_BUFFERSIZE; + else + sense_data_size = sizeof(ei->SenseInfo); + if (ei->SenseLen < sense_data_size) + sense_data_size = ei->SenseLen; + memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size); + if (ei->ScsiStatus) + decode_sense_data(ei->SenseInfo, sense_data_size, + &sense_key, &asc, &ascq); if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION) { if (sense_key == ABORTED_COMMAND) { cmd->result |= DID_SOFT_ERROR << 16; @@ -2053,6 +2107,10 @@ static void complete_scsi_command(struct CommandList *cp) cmd->result = DID_ERROR << 16; dev_warn(&h->pdev->dev, "Command unabortable\n"); break; + case CMD_TMF_STATUS: + if (hpsa_evaluate_tmf_status(h, cp)) /* TMF failed? */ + cmd->result = DID_ERROR << 16; + break; case CMD_IOACCEL_DISABLED: /* This only handles the direct pass-through case since RAID * offload is handled above. Just attempt a retry. @@ -2203,16 +2261,23 @@ static void hpsa_scsi_interpret_error(struct ctlr_info *h, { const struct ErrorInfo *ei = cp->err_info; struct device *d = &cp->h->pdev->dev; - const u8 *sd = ei->SenseInfo; + u8 sense_key, asc, ascq; + int sense_len; switch (ei->CommandStatus) { case CMD_TARGET_STATUS: + if (ei->SenseLen > sizeof(ei->SenseInfo)) + sense_len = sizeof(ei->SenseInfo); + else + sense_len = ei->SenseLen; + decode_sense_data(ei->SenseInfo, sense_len, + &sense_key, &asc, &ascq); hpsa_print_cmd(h, "SCSI status", cp); if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION) - dev_warn(d, "SCSI Status = 02, Sense key = %02x, ASC = %02x, ASCQ = %02x\n", - sd[2] & 0x0f, sd[12], sd[13]); + dev_warn(d, "SCSI Status = 02, Sense key = 0x%02x, ASC = 0x%02x, ASCQ = 0x%02x\n", + sense_key, asc, ascq); else - dev_warn(d, "SCSI Status = %02x\n", ei->ScsiStatus); + dev_warn(d, "SCSI Status = 0x%02x\n", ei->ScsiStatus); if (ei->ScsiStatus == 0) dev_warn(d, "SCSI status is abnormally zero. " "(probably indicates selection timeout " @@ -2757,7 +2822,9 @@ static int hpsa_volume_offline(struct ctlr_info *h, unsigned char scsi3addr[]) { struct CommandList *c; - unsigned char *sense, sense_key, asc, ascq; + unsigned char *sense; + u8 sense_key, asc, ascq; + int sense_len; int rc, ldstat = 0; u16 cmd_status; u8 scsi_status; @@ -2775,9 +2842,11 @@ static int hpsa_volume_offline(struct ctlr_info *h, return 0; } sense = c->err_info->SenseInfo; - sense_key = sense[2]; - asc = sense[12]; - ascq = sense[13]; + if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo)) + sense_len = sizeof(c->err_info->SenseInfo); + else + sense_len = c->err_info->SenseLen; + decode_sense_data(sense, sense_len, &sense_key, &asc, &ascq); cmd_status = c->err_info->CommandStatus; scsi_status = c->err_info->ScsiStatus; cmd_free(h, c); @@ -2853,6 +2922,9 @@ static int hpsa_device_supports_aborts(struct ctlr_info *h, case CMD_ABORT_FAILED: rc = 1; break; + case CMD_TMF_STATUS: + rc = hpsa_evaluate_tmf_status(h, c); + break; default: rc = 0; break; @@ -4650,6 +4722,9 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr, switch (ei->CommandStatus) { case CMD_SUCCESS: break; + case CMD_TMF_STATUS: + rc = hpsa_evaluate_tmf_status(h, c); + break; case CMD_UNABORTABLE: /* Very common, don't make noise. */ rc = -1; break; diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h index f52c847db89f..f6ca5fa44730 100644 --- a/drivers/scsi/hpsa_cmd.h +++ b/drivers/scsi/hpsa_cmd.h @@ -42,6 +42,7 @@ #define CMD_UNSOLICITED_ABORT 0x000A #define CMD_TIMEOUT 0x000B #define CMD_UNABORTABLE 0x000C +#define CMD_TMF_STATUS 0x000D #define CMD_IOACCEL_DISABLED 0x000E #define CMD_CTLR_LOCKUP 0xffff /* Note: CMD_CTLR_LOCKUP is not a value defined by the CISS spec @@ -49,6 +50,14 @@ * with when a controller lockup has been detected by the driver */ +/* TMF function status values */ +#define CISS_TMF_COMPLETE 0x00 +#define CISS_TMF_INVALID_FRAME 0x02 +#define CISS_TMF_NOT_SUPPORTED 0x04 +#define CISS_TMF_FAILED 0x05 +#define CISS_TMF_SUCCESS 0x08 +#define CISS_TMF_WRONG_LUN 0x09 +#define CISS_TMF_OVERLAPPED_TAG 0x0a /* Unit Attentions ASC's as defined for the MSA2012sa */ #define POWER_OR_RESET 0x29 |