File virtio-scsi-translate-SG_IO-host-status.patch of Package qemu.18959
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 32875bedaedf25e7b0cea8363887..b3ea492beedc2a075157957e0595 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -72,7 +72,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);
@@ -82,7 +82,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 {
@@ -90,7 +90,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:
@@ -235,6 +236,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 13babd26dff43d5052886cf955a5..bce865c2222b0ece52d16ab1d90a 100644
--- a/hw/scsi/trace-events
+++ b/hw/scsi/trace-events
@@ -330,7 +330,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"
# 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 de25a1c21d84f38eca9aaf1114d4..3155658db33f95a572a4c7ff495e 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -496,9 +496,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 fbc5588279939d70a5e31627bd2a..92f6c47944cfd1fb6284b4e2b210 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 38c273de19573ad8421da6439153..3c474bdd5688fe9d6e2b64e53637 100644
--- a/scsi/qemu-pr-helper.c
+++ b/scsi/qemu-pr-helper.c
@@ -134,7 +134,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);
@@ -151,13 +151,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