File 0165-virtio-scsi-translate-SG_IO-host-st.patch of Package qemu-testsuite
From: Hannes Reinecke <hare@suse.de>
Date: Tue, 10 Nov 2020 10:41:55 +0100
Subject: virtio-scsi: translate SG_IO host status
References: bsc#1178049
when running with an SG_IO backend we might be getting a SCSI host
status back, which should be translated into a virtio scsi status
to avoid having a silent data corruption if the status isn't
translated properly.
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Bruce Rogers <brogers@suse.com>
---
hw/scsi/scsi-generic.c | 8 +++++---
hw/scsi/trace-events | 2 +-
hw/scsi/virtio-scsi.c | 43 +++++++++++++++++++++++++++++++++++++++---
include/scsi/utils.h | 12 +++++++++---
scsi/qemu-pr-helper.c | 6 +++---
scsi/utils.c | 23 +++++++++-------------
6 files changed, 67 insertions(+), 27 deletions(-)
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index d84f3d3e75a9b29b9cbe8c8cc021..4c2867abaf22f08d1ed5f2daced6 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -69,7 +69,7 @@ static void scsi_free_request(SCSIRequest *req)
/* Helper function for command completion. */
static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
{
- int status;
+ uint32_t status;
SCSISense sense;
assert(r->req.aiocb == NULL);
@@ -79,7 +79,7 @@ static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
goto done;
}
status = sg_io_sense_from_errno(-ret, &r->io_header, &sense);
- if (status == CHECK_CONDITION) {
+ if ((status & 0xff) == CHECK_CONDITION) {
if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
r->req.sense_len = r->io_header.sb_len_wr;
} else {
@@ -87,7 +87,8 @@ static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
}
}
- trace_scsi_generic_command_complete_noio(r, r->req.tag, status);
+ trace_scsi_generic_command_complete_noio(r, r->req.tag, status & 0xff,
+ (status >> 8) & 0xff);
scsi_req_complete(&r->req, status);
done:
@@ -232,6 +233,7 @@ static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s)
* the hardware in scsi_command_complete_noio. Clean
* up the io_header to avoid reporting it.
*/
+ r->io_header.host_status = 0;
r->io_header.driver_status = 0;
r->io_header.status = 0;
diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events
index 5b957abad543982a5fef2994bb55..0e177db8070721921c5698e173e7 100644
--- a/hw/scsi/trace-events
+++ b/hw/scsi/trace-events
@@ -329,7 +329,7 @@ scsi_disk_new_request(uint32_t lun, uint32_t tag, const char *line) "Command: lu
scsi_disk_aio_sgio_command(uint32_t tag, uint8_t cmd, uint64_t lba, int len, uint32_t timeout) "disk aio sgio: tag=0x%x cmd 0x%x (sector %" PRId64 ", count %d) timeout %u"
# hw/scsi/scsi-generic.c
-scsi_generic_command_complete_noio(void *req, uint32_t tag, int statuc) "Command complete %p tag=0x%x status=%d"
+scsi_generic_command_complete_noio(void *req, uint32_t tag, uint8_t status, uint8_t host_status) "Command complete %p tag=0x%x status=0x%x host_status=0x%x"
scsi_generic_read_complete(uint32_t tag, int len) "Data ready tag=0x%x len=%d"
scsi_generic_read_data(uint32_t tag, uint32_t timeout) "scsi_read_data tag=0x%x timeout %u"
scsi_generic_write_complete(int ret) "scsi_write_complete() ret = %d"
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index d5f024994cd0607051ad2806c574..ec8412d94dc6996bf8c46283c3f7 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -486,9 +486,46 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
return;
}
- req->resp.cmd.response = VIRTIO_SCSI_S_OK;
- req->resp.cmd.status = status;
- if (req->resp.cmd.status == GOOD) {
+ switch ((status >> 8) & 0xff) {
+ case SG_ERR_DID_OK:
+ req->resp.cmd.response = VIRTIO_SCSI_S_OK;
+ break;
+ case SG_ERR_DID_ERROR:
+ req->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN;
+ break;
+ case SG_ERR_DID_NO_CONNECT:
+ req->resp.cmd.response = VIRTIO_SCSI_S_INCORRECT_LUN;
+ break;
+ case SG_ERR_DID_ABORT:
+ case SG_ERR_DID_TIME_OUT:
+ req->resp.cmd.response = VIRTIO_SCSI_S_ABORTED;
+ break;
+ case SG_ERR_DID_BAD_TARGET:
+ req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET;
+ break;
+ case SG_ERR_DID_RESET:
+ req->resp.cmd.response = VIRTIO_SCSI_S_RESET;
+ break;
+ case SG_ERR_DID_BUS_BUSY:
+ req->resp.cmd.response = VIRTIO_SCSI_S_BUSY;
+ break;
+ case SG_ERR_DID_TRANSPORT_DISRUPTED:
+ req->resp.cmd.response = VIRTIO_SCSI_S_TRANSPORT_FAILURE;
+ break;
+ case SG_ERR_DID_TARGET_FAILURE:
+ req->resp.cmd.response = VIRTIO_SCSI_S_TARGET_FAILURE;
+ break;
+ case SG_ERR_DID_NEXUS_FAILURE:
+ req->resp.cmd.response = VIRTIO_SCSI_S_NEXUS_FAILURE;
+ break;
+ default:
+ req->resp.cmd.response = VIRTIO_SCSI_S_FAILURE;
+ break;
+ }
+
+ req->resp.cmd.status = (status & 0xff);
+ if (req->resp.cmd.status == GOOD &&
+ req->resp.cmd.response == VIRTIO_SCSI_S_OK) {
req->resp.cmd.resid = virtio_tswap32(vdev, resid);
} else {
req->resp.cmd.resid = 0;
diff --git a/include/scsi/utils.h b/include/scsi/utils.h
index 0a98661eab6d9e3dd42fe1729c6e..9d3b034bc1d466794afacaf4e884 100644
--- a/include/scsi/utils.h
+++ b/include/scsi/utils.h
@@ -126,11 +126,17 @@ int scsi_cdb_length(uint8_t *buf);
#define SG_ERR_DID_NO_CONNECT 0x01
#define SG_ERR_DID_BUS_BUSY 0x02
#define SG_ERR_DID_TIME_OUT 0x03
-
+#define SG_ERR_DID_BAD_TARGET 0x04
+#define SG_ERR_DID_ABORT 0x05
+#define SG_ERR_DID_ERROR 0x07
+#define SG_ERR_DID_RESET 0x08
+#define SG_ERR_DID_TRANSPORT_DISRUPTED 0x0e
+#define SG_ERR_DID_TARGET_FAILURE 0x10
+#define SG_ERR_DID_NEXUS_FAILURE 0x11
#define SG_ERR_DRIVER_SENSE 0x08
-int sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr,
- SCSISense *sense);
+uint32_t sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr,
+ SCSISense *sense);
#endif
#endif
diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c
index e7af637232e19bc0464cbbcd7694..83c25639a1cef0629b444e4d88ba 100644
--- a/scsi/qemu-pr-helper.c
+++ b/scsi/qemu-pr-helper.c
@@ -133,7 +133,7 @@ static int do_sgio_worker(void *opaque)
PRHelperSGIOData *data = opaque;
struct sg_io_hdr io_hdr;
int ret;
- int status;
+ uint32_t status;
SCSISense sense_code;
memset(data->sense, 0, PR_HELPER_SENSE_SIZE);
@@ -150,13 +150,13 @@ static int do_sgio_worker(void *opaque)
ret = ioctl(data->fd, SG_IO, &io_hdr);
status = sg_io_sense_from_errno(ret < 0 ? errno : 0, &io_hdr,
&sense_code);
- if (status == GOOD) {
+ if ((status & 0xff) == GOOD) {
data->sz -= io_hdr.resid;
} else {
data->sz = 0;
}
- if (status == CHECK_CONDITION &&
+ if ((status & 0xff) == CHECK_CONDITION &&
!(io_hdr.driver_status & SG_ERR_DRIVER_SENSE)) {
scsi_build_sense(data->sense, sense_code);
}
diff --git a/scsi/utils.c b/scsi/utils.c
index c50e81fdb87f535e6f49dd31699e..c09f4aff21e34860c1b41612cd0d 100644
--- a/scsi/utils.c
+++ b/scsi/utils.c
@@ -565,7 +565,7 @@ const char *scsi_command_name(uint8_t cmd)
}
#ifdef CONFIG_LINUX
-int sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr,
+uint32_t sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr,
SCSISense *sense)
{
if (errno_value != 0) {
@@ -580,21 +580,16 @@ int sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr,
return CHECK_CONDITION;
}
} else {
- if (io_hdr->host_status == SG_ERR_DID_NO_CONNECT ||
- io_hdr->host_status == SG_ERR_DID_BUS_BUSY ||
- io_hdr->host_status == SG_ERR_DID_TIME_OUT ||
- (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT)) {
- return BUSY;
- } else if (io_hdr->host_status) {
- *sense = SENSE_CODE(I_T_NEXUS_LOSS);
- return CHECK_CONDITION;
- } else if (io_hdr->status) {
- return io_hdr->status;
+ uint32_t status = GOOD;
+
+ if (io_hdr->status) {
+ status = io_hdr->status;
} else if (io_hdr->driver_status & SG_ERR_DRIVER_SENSE) {
- return CHECK_CONDITION;
- } else {
- return GOOD;
+ status = CHECK_CONDITION;
}
+ if (io_hdr->host_status)
+ status |= (io_hdr->host_status << 8);
+ return status;
}
}
#endif