diff options
Diffstat (limited to 'drivers/scsi/storvsc_drv.c')
-rw-r--r-- | drivers/scsi/storvsc_drv.c | 119 |
1 files changed, 56 insertions, 63 deletions
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 403753929320..328bb961c281 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1031,17 +1031,40 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb, struct storvsc_scan_work *wrk; void (*process_err_fn)(struct work_struct *work); struct hv_host_device *host_dev = shost_priv(host); - bool do_work = false; - switch (SRB_STATUS(vm_srb->srb_status)) { - case SRB_STATUS_ERROR: + /* + * In some situations, Hyper-V sets multiple bits in the + * srb_status, such as ABORTED and ERROR. So process them + * individually, with the most specific bits first. + */ + + if (vm_srb->srb_status & SRB_STATUS_INVALID_LUN) { + set_host_byte(scmnd, DID_NO_CONNECT); + process_err_fn = storvsc_remove_lun; + goto do_work; + } + + if (vm_srb->srb_status & SRB_STATUS_ABORTED) { + if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID && + /* Capacity data has changed */ + (asc == 0x2a) && (ascq == 0x9)) { + process_err_fn = storvsc_device_scan; + /* + * Retry the I/O that triggered this. + */ + set_host_byte(scmnd, DID_REQUEUE); + goto do_work; + } + } + + if (vm_srb->srb_status & SRB_STATUS_ERROR) { /* * Let upper layer deal with error when * sense message is present. */ - if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID) - break; + return; + /* * If there is an error; offline the device since all * error recovery strategies would have already been @@ -1054,37 +1077,19 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb, set_host_byte(scmnd, DID_PASSTHROUGH); break; /* - * On Some Windows hosts TEST_UNIT_READY command can return - * SRB_STATUS_ERROR, let the upper level code deal with it - * based on the sense information. + * On some Hyper-V hosts TEST_UNIT_READY command can + * return SRB_STATUS_ERROR. Let the upper level code + * deal with it based on the sense information. */ case TEST_UNIT_READY: break; default: set_host_byte(scmnd, DID_ERROR); } - break; - case SRB_STATUS_INVALID_LUN: - set_host_byte(scmnd, DID_NO_CONNECT); - do_work = true; - process_err_fn = storvsc_remove_lun; - break; - case SRB_STATUS_ABORTED: - if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID && - (asc == 0x2a) && (ascq == 0x9)) { - do_work = true; - process_err_fn = storvsc_device_scan; - /* - * Retry the I/O that triggered this. - */ - set_host_byte(scmnd, DID_REQUEUE); - } - break; } + return; - if (!do_work) - return; - +do_work: /* * We need to schedule work to process this error; schedule it. */ @@ -1112,6 +1117,7 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request, struct Scsi_Host *host; u32 payload_sz = cmd_request->payload_sz; void *payload = cmd_request->payload; + bool sense_ok; host = stor_dev->host; @@ -1121,11 +1127,10 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request, scmnd->result = vm_srb->scsi_status; if (scmnd->result) { - if (scsi_normalize_sense(scmnd->sense_buffer, - SCSI_SENSE_BUFFERSIZE, &sense_hdr) && - !(sense_hdr.sense_key == NOT_READY && - sense_hdr.asc == 0x03A) && - do_logging(STORVSC_LOGGING_ERROR)) + sense_ok = scsi_normalize_sense(scmnd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, &sense_hdr); + + if (sense_ok && do_logging(STORVSC_LOGGING_WARN)) scsi_print_sense_hdr(scmnd->device, "storvsc", &sense_hdr); } @@ -1182,53 +1187,41 @@ static void storvsc_on_io_completion(struct storvsc_device *stor_device, vstor_packet->vm_srb.srb_status = SRB_STATUS_SUCCESS; } - /* Copy over the status...etc */ stor_pkt->vm_srb.scsi_status = vstor_packet->vm_srb.scsi_status; stor_pkt->vm_srb.srb_status = vstor_packet->vm_srb.srb_status; - /* Validate sense_info_length (from Hyper-V) */ - if (vstor_packet->vm_srb.sense_info_length > sense_buffer_size) - vstor_packet->vm_srb.sense_info_length = sense_buffer_size; - - stor_pkt->vm_srb.sense_info_length = - vstor_packet->vm_srb.sense_info_length; + /* + * Copy over the sense_info_length, but limit to the known max + * size if Hyper-V returns a bad value. + */ + stor_pkt->vm_srb.sense_info_length = min_t(u8, sense_buffer_size, + vstor_packet->vm_srb.sense_info_length); if (vstor_packet->vm_srb.scsi_status != 0 || vstor_packet->vm_srb.srb_status != SRB_STATUS_SUCCESS) - storvsc_log(device, STORVSC_LOGGING_WARN, - "cmd 0x%x scsi status 0x%x srb status 0x%x\n", + storvsc_log(device, STORVSC_LOGGING_ERROR, + "tag#%d cmd 0x%x status: scsi 0x%x srb 0x%x hv 0x%x\n", + request->cmd->request->tag, stor_pkt->vm_srb.cdb[0], vstor_packet->vm_srb.scsi_status, - vstor_packet->vm_srb.srb_status); - - if ((vstor_packet->vm_srb.scsi_status & 0xFF) == 0x02) { - /* CHECK_CONDITION */ - if (vstor_packet->vm_srb.srb_status & - SRB_STATUS_AUTOSENSE_VALID) { - /* autosense data available */ - - storvsc_log(device, STORVSC_LOGGING_WARN, - "stor pkt %p autosense data valid - len %d\n", - request, vstor_packet->vm_srb.sense_info_length); - - memcpy(request->cmd->sense_buffer, - vstor_packet->vm_srb.sense_data, - vstor_packet->vm_srb.sense_info_length); + vstor_packet->vm_srb.srb_status, + vstor_packet->status); - } - } + if (vstor_packet->vm_srb.scsi_status == SAM_STAT_CHECK_CONDITION && + (vstor_packet->vm_srb.srb_status & SRB_STATUS_AUTOSENSE_VALID)) + memcpy(request->cmd->sense_buffer, + vstor_packet->vm_srb.sense_data, + stor_pkt->vm_srb.sense_info_length); stor_pkt->vm_srb.data_transfer_length = - vstor_packet->vm_srb.data_transfer_length; + vstor_packet->vm_srb.data_transfer_length; storvsc_command_completion(request, stor_device); if (atomic_dec_and_test(&stor_device->num_outstanding_req) && stor_device->drain_notify) wake_up(&stor_device->waiting_to_drain); - - } static void storvsc_on_receive(struct storvsc_device *stor_device, @@ -1717,7 +1710,7 @@ static bool storvsc_scsi_cmd_ok(struct scsi_cmnd *scmnd) * this. So, don't send it. */ case SET_WINDOW: - scmnd->result = DID_ERROR << 16; + set_host_byte(scmnd, DID_ERROR); allowed = false; break; default: |