Add abilility to return media errors for SCSI block devices. Needs improvement.
Signed-off-by: Tony Asleson <tasle...@redhat.com> --- hw/scsi/scsi-disk.c | 33 +++++++++++++++++++++++++++++++++ include/scsi/utils.h | 4 ++++ scsi/utils.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 915641a0f1..a7f3274cdf 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -37,6 +37,7 @@ #include "sysemu/dma.h" #include "qemu/cutils.h" #include "trace.h" +#include "block/error_inject.h" #ifdef __linux #include <scsi/sg.h> @@ -132,6 +133,18 @@ static void scsi_check_condition(SCSIDiskReq *r, SCSISense sense) scsi_req_complete(&r->req, CHECK_CONDITION); } +/* Helper function for SCSI media error */ +static void scsi_media_error(SCSIDiskReq *r, SCSISense sense, uint32_t lba) +{ + trace_scsi_disk_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); + + r->req.sense_len = scsi_build_sense_buf_info(r->req.sense, + SCSI_SENSE_LEN, sense, lba, + 0x80, 32); + scsi_req_complete(&r->req, CHECK_CONDITION); +} + static void scsi_init_iovec(SCSIDiskReq *r, size_t size) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); @@ -2170,6 +2183,26 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) } r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); r->sector_count = len * (s->qdev.blocksize / 512); + + /* + * TODO Move this check to a more appropriate spot. Additionally we + * also need to transfer the data that was read up before encountering + * the media error. + */ + uint64_t error_sector = 0; + char device_id[32]; + sprintf(device_id, "%lu", s->qdev.wwn); + if (error_in_read(device_id, r->sector, r->sector_count, + &error_sector)) { + /* + * TODO Fix error reporting for disks > 2TiB + */ + if (error_sector > 0xFFFFFFFF) { + error_sector = 0xFFFFFFFF; + } + scsi_media_error(r, SENSE_CODE(READ_ERROR), (uint32_t)error_sector); + return 0; + } break; case WRITE_6: case WRITE_10: diff --git a/include/scsi/utils.h b/include/scsi/utils.h index fbc5588279..02ae74287f 100644 --- a/include/scsi/utils.h +++ b/include/scsi/utils.h @@ -35,6 +35,10 @@ SCSISense scsi_parse_sense_buf(const uint8_t *in_buf, int in_len); int scsi_build_sense_buf(uint8_t *buf, size_t max_size, SCSISense sense, bool fixed_sense); +int scsi_build_sense_buf_info(uint8_t *out_buf, size_t size, SCSISense sense, + uint32_t information, uint8_t sksv, + uint16_t sense_key_specific_info); + /* * Predefined sense codes */ diff --git a/scsi/utils.c b/scsi/utils.c index c50e81fdb8..9c9f1735d0 100644 --- a/scsi/utils.c +++ b/scsi/utils.c @@ -147,6 +147,37 @@ int scsi_build_sense_buf(uint8_t *out_buf, size_t size, SCSISense sense, return len; } +int scsi_build_sense_buf_info(uint8_t *out_buf, size_t size, SCSISense sense, + uint32_t information, uint8_t sksv, + uint16_t sense_key_specific_info) +{ + uint8_t buf[SCSI_SENSE_LEN] = { 0 }; + + buf[0] = 0xF0; /* Valid bit set. */ + buf[2] = sense.key; + + /* + * Set bytes 3, 4, 5, 6 value of information field + */ + *((uint32_t *)(&buf[3])) = cpu_to_be32(information); + + buf[7] = 10; + buf[12] = sense.asc; + buf[13] = sense.ascq; + + if (sksv) { + buf[15] = sksv; + /* + * Set bytes 16, 17 to Sense-key specific bytes + */ + *((uint16_t *)&buf[16]) = cpu_to_be16(sense_key_specific_info); + } + + int len = MIN(SCSI_SENSE_LEN, size); + memcpy(out_buf, buf, len); + return len; +} + int scsi_build_sense(uint8_t *buf, SCSISense sense) { return scsi_build_sense_buf(buf, SCSI_SENSE_LEN, sense, true); -- 2.21.0