diff options
Diffstat (limited to 'hw/scsi/scsi-disk.c')
-rw-r--r-- | hw/scsi/scsi-disk.c | 168 |
1 files changed, 83 insertions, 85 deletions
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index a2716b2..18ab777 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -111,8 +111,6 @@ struct SCSIDiskState { uint16_t rotation_rate; }; -static bool scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed); - static void scsi_free_request(SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); @@ -182,6 +180,89 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) qemu_iovec_init_external(&r->qiov, &r->iov, 1); } +/* + * scsi_handle_rw_error has two return values. False means that the error + * must be ignored, true means that the error has been processed and the + * caller should not do anything else for this request. Note that + * 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) +{ + 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); + + if (action == BLOCK_ERROR_ACTION_REPORT) { + if (acct_failed) { + block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); + } + switch (error) { + case 0: + /* 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); + return true; + } + error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); + break; +#ifdef CONFIG_LINUX + /* These errno mapping are specific to Linux. For more information: + * - scsi_decide_disposition in drivers/scsi/scsi_error.c + * - scsi_result_to_blk_status in drivers/scsi/scsi_lib.c + * - blk_errors[] in block/blk-core.c + */ + case EBADE: + /* DID_NEXUS_FAILURE -> BLK_STS_NEXUS. */ + scsi_req_complete(&r->req, RESERVATION_CONFLICT); + break; + case ENODATA: + /* DID_MEDIUM_ERROR -> BLK_STS_MEDIUM. */ + scsi_check_condition(r, SENSE_CODE(READ_ERROR)); + break; + case EREMOTEIO: + /* DID_TARGET_FAILURE -> BLK_STS_TARGET. */ + scsi_req_complete(&r->req, HARDWARE_ERROR); + break; +#endif + case ENOMEDIUM: + scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); + break; + case ENOMEM: + scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE)); + break; + case EINVAL: + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); + break; + case ENOSPC: + scsi_check_condition(r, SENSE_CODE(SPACE_ALLOC_FAILED)); + break; + default: + scsi_check_condition(r, SENSE_CODE(IO_ERROR)); + break; + } + } + + blk_error_action(s->qdev.conf.blk, action, is_read, error); + if (action == BLOCK_ERROR_ACTION_IGNORE) { + scsi_req_complete(&r->req, 0); + return true; + } + + if (action == BLOCK_ERROR_ACTION_STOP) { + scsi_req_retry(&r->req); + } + return true; +} + static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed) { if (r->req.io_canceled) { @@ -428,89 +509,6 @@ static void scsi_read_data(SCSIRequest *req) } } -/* - * scsi_handle_rw_error has two return values. False means that the error - * must be ignored, true means that the error has been processed and the - * caller should not do anything else for this request. Note that - * 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) -{ - 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); - - if (action == BLOCK_ERROR_ACTION_REPORT) { - if (acct_failed) { - block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } - switch (error) { - case 0: - /* 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); - return true; - } - error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); - break; -#ifdef CONFIG_LINUX - /* These errno mapping are specific to Linux. For more information: - * - scsi_decide_disposition in drivers/scsi/scsi_error.c - * - scsi_result_to_blk_status in drivers/scsi/scsi_lib.c - * - blk_errors[] in block/blk-core.c - */ - case EBADE: - /* DID_NEXUS_FAILURE -> BLK_STS_NEXUS. */ - scsi_req_complete(&r->req, RESERVATION_CONFLICT); - break; - case ENODATA: - /* DID_MEDIUM_ERROR -> BLK_STS_MEDIUM. */ - scsi_check_condition(r, SENSE_CODE(READ_ERROR)); - break; - case EREMOTEIO: - /* DID_TARGET_FAILURE -> BLK_STS_TARGET. */ - scsi_req_complete(&r->req, HARDWARE_ERROR); - break; -#endif - case ENOMEDIUM: - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - break; - case ENOMEM: - scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE)); - break; - case EINVAL: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - break; - case ENOSPC: - scsi_check_condition(r, SENSE_CODE(SPACE_ALLOC_FAILED)); - break; - default: - scsi_check_condition(r, SENSE_CODE(IO_ERROR)); - break; - } - } - - blk_error_action(s->qdev.conf.blk, action, is_read, error); - if (action == BLOCK_ERROR_ACTION_IGNORE) { - scsi_req_complete(&r->req, 0); - return true; - } - - if (action == BLOCK_ERROR_ACTION_STOP) { - scsi_req_retry(&r->req); - } - return true; -} - static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) { uint32_t n; |