diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 9c6099ffc4..548a5297fa 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -187,34 +187,48 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) * scsi_handle_rw_error always manages its reference counts, independent * of the return value. */ -static bool scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed) +static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) { bool is_read = (r->req.cmd.mode == SCSI_XFER_FROM_DEV); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); - BlockErrorAction action = blk_get_error_action(s->qdev.conf.blk, - is_read, error); - SCSISense sense; + SCSISense sense = SENSE_CODE(NO_SENSE); + int error = 0; + bool req_has_sense = false; + BlockErrorAction action; + int status; + if (ret < 0) { + status = scsi_sense_from_errno(-ret, &sense); + error = -ret; + } else { + /* A passthrough command has completed with nonzero status. */ + status = ret; + if (status == CHECK_CONDITION) { + req_has_sense = true; + error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); + } else { + error = EINVAL; + } + } + + action = blk_get_error_action(s->qdev.conf.blk, is_read, error); if (action == BLOCK_ERROR_ACTION_REPORT) { if (acct_failed) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); } - if (error == 0) { + if (req_has_sense) { /* A passthrough command has run and has produced sense data; check * whether the error has to be handled by the guest or should rather * pause the host. */ - assert(r->status && *r->status); if (scsi_sense_buf_is_guest_recoverable(r->req.sense, sizeof(r->req.sense))) { /* These errors are handled by guest. */ sdc->update_sense(&r->req); - scsi_req_complete(&r->req, *r->status); + scsi_req_complete(&r->req, status); return true; } - error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); } else { - int status = scsi_sense_from_errno(error, &sense); if (status == CHECK_CONDITION) { scsi_req_build_sense(&r->req, sense); } @@ -240,8 +254,10 @@ static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed) return true; } - if (ret < 0 || (r->status && *r->status)) { - return scsi_handle_rw_error(r, -ret, acct_failed); + if (ret < 0) { + return scsi_handle_rw_error(r, ret, acct_failed); + } else if (r->status && *r->status) { + return scsi_handle_rw_error(r, *r->status, acct_failed); } return false;