aboutsummaryrefslogtreecommitdiff
path: root/hw/scsi/scsi-disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/scsi/scsi-disk.c')
-rw-r--r--hw/scsi/scsi-disk.c172
1 files changed, 82 insertions, 90 deletions
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index ed52fcd..a5a58d7 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,78 @@ 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 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));
+ 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;
+ }
+ }
+
+ /*
+ * Check whether the error has to be handled by the guest or should
+ * rather follow the rerror=/werror= settings. Guest-handled errors
+ * are usually retried immediately, so do not post them to QMP and
+ * do not account them as failed I/O.
+ */
+ if (req_has_sense &&
+ scsi_sense_buf_is_guest_recoverable(r->req.sense, sizeof(r->req.sense))) {
+ action = BLOCK_ERROR_ACTION_REPORT;
+ acct_failed = false;
+ } else {
+ action = blk_get_error_action(s->qdev.conf.blk, is_read, error);
+ blk_error_action(s->qdev.conf.blk, action, is_read, error);
+ }
+
+ switch (action) {
+ case BLOCK_ERROR_ACTION_REPORT:
+ if (acct_failed) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ if (req_has_sense) {
+ sdc->update_sense(&r->req);
+ } else if (status == CHECK_CONDITION) {
+ scsi_req_build_sense(&r->req, sense);
+ }
+ scsi_req_complete(&r->req, status);
+ return true;
+
+ case BLOCK_ERROR_ACTION_IGNORE:
+ return false;
+
+ case BLOCK_ERROR_ACTION_STOP:
+ scsi_req_retry(&r->req);
+ return true;
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed)
{
if (r->req.io_canceled) {
@@ -189,8 +259,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;
@@ -428,89 +500,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;
@@ -2624,7 +2613,7 @@ static int get_device_type(SCSIDiskState *s)
cmd[4] = sizeof(buf);
ret = scsi_SG_IO_FROM_DEV(s->qdev.conf.blk, cmd, sizeof(cmd),
- buf, sizeof(buf));
+ buf, sizeof(buf), s->qdev.io_timeout);
if (ret < 0) {
return -1;
}
@@ -2785,10 +2774,11 @@ static BlockAIOCB *scsi_block_do_sgio(SCSIBlockReq *req,
/* The rest is as in scsi-generic.c. */
io_header->mx_sb_len = sizeof(r->req.sense);
io_header->sbp = r->req.sense;
- io_header->timeout = UINT_MAX;
+ io_header->timeout = s->qdev.io_timeout * 1000;
io_header->usr_ptr = r;
io_header->flags |= SG_FLAG_DIRECT_IO;
-
+ trace_scsi_disk_aio_sgio_command(r->req.tag, req->cdb[0], lba,
+ nb_logical_blocks, io_header->timeout);
aiocb = blk_aio_ioctl(s->qdev.conf.blk, SG_IO, io_header, cb, opaque);
assert(aiocb != NULL);
return aiocb;
@@ -3103,6 +3093,8 @@ static Property scsi_block_properties[] = {
DEFAULT_MAX_IO_SIZE),
DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version,
-1),
+ DEFINE_PROP_UINT32("io_timeout", SCSIDiskState, qdev.io_timeout,
+ DEFAULT_IO_TIMEOUT),
DEFINE_PROP_END_OF_LIST(),
};