From 9738c657208800298a7d68272b861fb2dc49fee1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 6 Mar 2021 11:24:12 +0100 Subject: scsi-generic: do not snoop the output of failed commands If a READ CAPACITY command would fail, for example s->qdev.blocksize would be set to zero and cause a division by zero on the next use. Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-generic.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'hw/scsi/scsi-generic.c') diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index cf7e11cf44..f9fd2ccfdd 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -288,7 +288,10 @@ static void scsi_read_complete(void * opaque, int ret) } } - if (len == 0) { + if (r->io_header.host_status != SCSI_HOST_OK || + (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT) || + r->io_header.status != GOOD || + len == 0) { scsi_command_complete_noio(r, 0); goto done; } -- cgit 1.4.1 From a108557bbff8a3f44233982f015f996426411be8 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 16 Nov 2020 19:40:40 +0100 Subject: scsi: inline sg_io_sense_from_errno() into the callers. Currently sg_io_sense_from_errno() converts the two input parameters 'errno' and 'io_hdr' into sense code and SCSI status. Having split the function off into scsi_sense_from_errno() and scsi_sense_from_host_status(), both of which are available generically, we now inline the logic in the callers so that scsi-disk and scsi-generic will be able to pass host_status to the HBA. Signed-off-by: Hannes Reinecke Message-Id: <20201116184041.60465-7-hare@suse.de> [Put together from "scsi-disk: Add sg_io callback to evaluate status" and what remains of "scsi: split sg_io_sense_from_errno() in two functions", with many other fixes. - Paolo] Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- hw/scsi/scsi-generic.c | 22 ++++++++++++++++------ include/scsi/utils.h | 3 --- scsi/qemu-pr-helper.c | 24 +++++++++++++++++------- scsi/utils.c | 23 ----------------------- 5 files changed, 75 insertions(+), 44 deletions(-) (limited to 'hw/scsi/scsi-generic.c') diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index a5a58d7db3..ceaf78b423 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -77,7 +77,6 @@ typedef struct SCSIDiskReq { struct iovec iov; QEMUIOVector qiov; BlockAcctCookie acct; - unsigned char *status; } SCSIDiskReq; #define SCSI_DISK_F_REMOVABLE 0 @@ -261,8 +260,6 @@ static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool 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; @@ -2697,8 +2694,47 @@ typedef struct SCSIBlockReq { /* CDB passed to SG_IO. */ uint8_t cdb[16]; + BlockCompletionFunc *cb; + void *cb_opaque; } SCSIBlockReq; +static void scsi_block_sgio_complete(void *opaque, int ret) +{ + SCSIBlockReq *req = (SCSIBlockReq *)opaque; + SCSIDiskReq *r = &req->req; + SCSIDevice *s = r->req.dev; + sg_io_hdr_t *io_hdr = &req->io_header; + SCSISense sense; + + if (ret == 0) { + if (io_hdr->host_status != SCSI_HOST_OK) { + ret = scsi_sense_from_host_status(io_hdr->host_status, &sense); + if (ret == CHECK_CONDITION) { + scsi_req_build_sense(&r->req, sense); + } + } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { + ret = BUSY; + } else { + ret = io_hdr->status; + } + + if (ret > 0) { + aio_context_acquire(blk_get_aio_context(s->conf.blk)); + if (scsi_handle_rw_error(r, ret, true)) { + aio_context_release(blk_get_aio_context(s->conf.blk)); + scsi_req_unref(&r->req); + return; + } + aio_context_release(blk_get_aio_context(s->conf.blk)); + + /* Ignore error. */ + ret = 0; + } + } + + req->cb(req->cb_opaque, ret); +} + static BlockAIOCB *scsi_block_do_sgio(SCSIBlockReq *req, int64_t offset, QEMUIOVector *iov, int direction, @@ -2777,9 +2813,11 @@ static BlockAIOCB *scsi_block_do_sgio(SCSIBlockReq *req, io_header->timeout = s->qdev.io_timeout * 1000; io_header->usr_ptr = r; io_header->flags |= SG_FLAG_DIRECT_IO; + req->cb = cb; + req->cb_opaque = opaque; 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); + aiocb = blk_aio_ioctl(s->qdev.conf.blk, SG_IO, io_header, scsi_block_sgio_complete, req); assert(aiocb != NULL); return aiocb; } @@ -2893,7 +2931,6 @@ static int32_t scsi_block_dma_command(SCSIRequest *req, uint8_t *buf) return 0; } - r->req.status = &r->io_header.status; return scsi_disk_dma_command(req, buf); } diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index f9fd2ccfdd..02b87819e5 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -75,6 +75,7 @@ static void scsi_command_complete_noio(SCSIGenericReq *r, int ret) { int status; SCSISense sense; + sg_io_hdr_t *io_hdr = &r->io_header; assert(r->req.aiocb == NULL); @@ -82,15 +83,24 @@ static void scsi_command_complete_noio(SCSIGenericReq *r, int ret) scsi_req_cancel_complete(&r->req); goto done; } - status = sg_io_sense_from_errno(-ret, &r->io_header, &sense); - if (status == CHECK_CONDITION) { - if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { - r->req.sense_len = r->io_header.sb_len_wr; - } else { + if (ret < 0) { + status = scsi_sense_from_errno(-ret, &sense); + if (status == CHECK_CONDITION) { + scsi_req_build_sense(&r->req, sense); + } + } else if (io_hdr->host_status != SCSI_HOST_OK) { + status = scsi_sense_from_host_status(io_hdr->host_status, &sense); + if (status == CHECK_CONDITION) { scsi_req_build_sense(&r->req, sense); } + } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { + status = BUSY; + } else { + status = io_hdr->status; + if (io_hdr->driver_status & SG_ERR_DRIVER_SENSE) { + r->req.sense_len = io_hdr->sb_len_wr; + } } - trace_scsi_generic_command_complete_noio(r, r->req.tag, status); scsi_req_complete(&r->req, status); diff --git a/include/scsi/utils.h b/include/scsi/utils.h index 9080d65e27..d5c8efa16e 100644 --- a/include/scsi/utils.h +++ b/include/scsi/utils.h @@ -139,9 +139,6 @@ int scsi_cdb_length(uint8_t *buf); #ifdef CONFIG_LINUX #define SG_ERR_DRIVER_TIMEOUT 0x06 #define SG_ERR_DRIVER_SENSE 0x08 - -int sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr, - SCSISense *sense); #endif int scsi_sense_from_errno(int errno_value, SCSISense *sense); diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index 2733d92f2d..7b9389b47b 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -149,19 +149,29 @@ static int do_sgio_worker(void *opaque) io_hdr.dxferp = (char *)data->buf; io_hdr.dxfer_len = data->sz; ret = ioctl(data->fd, SG_IO, &io_hdr); - status = sg_io_sense_from_errno(ret < 0 ? errno : 0, &io_hdr, - &sense_code); + + if (ret < 0) { + status = scsi_sense_from_errno(errno, &sense_code); + if (status == CHECK_CONDITION) { + scsi_build_sense(data->sense, sense_code); + } + } else if (io_hdr.host_status != SCSI_HOST_OK) { + status = scsi_sense_from_host_status(io_hdr.host_status, &sense_code); + if (status == CHECK_CONDITION) { + scsi_build_sense(data->sense, sense_code); + } + } else if (io_hdr.driver_status & SG_ERR_DRIVER_TIMEOUT) { + status = BUSY; + } else { + status = io_hdr.status; + } + if (status == GOOD) { data->sz -= io_hdr.resid; } else { data->sz = 0; } - if (status == CHECK_CONDITION && - !(io_hdr.driver_status & SG_ERR_DRIVER_SENSE)) { - scsi_build_sense(data->sense, sense_code); - } - return status; } diff --git a/scsi/utils.c b/scsi/utils.c index 28eb32746e..873e05aeaf 100644 --- a/scsi/utils.c +++ b/scsi/utils.c @@ -658,26 +658,3 @@ int scsi_sense_from_host_status(uint8_t host_status, } return GOOD; } - -#ifdef CONFIG_LINUX -int sg_io_sense_from_errno(int errno_value, struct sg_io_hdr *io_hdr, - SCSISense *sense) -{ - if (errno_value != 0) { - return scsi_sense_from_errno(errno_value, sense); - } else { - int status = scsi_sense_from_host_status(io_hdr->host_status, sense); - if (status) { - return status; - } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { - return BUSY; - } else if (io_hdr->status) { - return io_hdr->status; - } else if (io_hdr->driver_status & SG_ERR_DRIVER_SENSE) { - return CHECK_CONDITION; - } else { - return GOOD; - } - } -} -#endif -- cgit 1.4.1 From f3126d65b393c015e8f87763fdccee99bb1119af Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 24 Feb 2021 19:14:50 +0100 Subject: scsi: move host_status handling into SCSI drivers Some SCSI drivers like virtio have an internal mapping for the host_status. This patch moves the host_status translation into the SCSI drivers to allow those drivers to set up the correct values. Signed-off-by: Hannes Reinecke . [Added default handling to avoid touching all drivers. - Paolo] Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-bus.c | 33 +++++++++++++++++++++++++++++++-- hw/scsi/scsi-disk.c | 12 ++++++------ hw/scsi/scsi-generic.c | 6 ++---- hw/scsi/virtio-scsi.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ hw/scsi/vmw_pvscsi.c | 39 +++++++++++++++++++++++++++++++++++++++ include/hw/scsi/scsi.h | 5 ++++- 6 files changed, 128 insertions(+), 13 deletions(-) (limited to 'hw/scsi/scsi-generic.c') diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index dc4141ec8d..2d674f94d7 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -692,6 +692,7 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, req->lun = lun; req->hba_private = hba_private; req->status = -1; + req->host_status = -1; req->ops = reqops; object_ref(OBJECT(d)); object_ref(OBJECT(qbus->parent)); @@ -1455,10 +1456,38 @@ void scsi_req_print(SCSIRequest *req) } } +void scsi_req_complete_failed(SCSIRequest *req, int host_status) +{ + SCSISense sense; + int status; + + assert(req->status == -1 && req->host_status == -1); + assert(req->ops != &reqops_unit_attention); + + if (!req->bus->info->fail) { + status = scsi_sense_from_host_status(req->host_status, &sense); + if (status == CHECK_CONDITION) { + scsi_req_build_sense(req, sense); + } + scsi_req_complete(req, status); + return; + } + + req->host_status = host_status; + scsi_req_ref(req); + scsi_req_dequeue(req); + req->bus->info->fail(req); + + /* Cancelled requests might end up being completed instead of cancelled */ + notifier_list_notify(&req->cancel_notifiers, req); + scsi_req_unref(req); +} + void scsi_req_complete(SCSIRequest *req, int status) { - assert(req->status == -1); + assert(req->status == -1 && req->host_status == -1); req->status = status; + req->host_status = SCSI_HOST_OK; assert(req->sense_len <= sizeof(req->sense)); if (status == GOOD) { @@ -1646,7 +1675,7 @@ static int put_scsi_requests(QEMUFile *f, void *pv, size_t size, QTAILQ_FOREACH(req, &s->requests, next) { assert(!req->io_canceled); - assert(req->status == -1); + assert(req->status == -1 && req->host_status == -1); assert(req->enqueued); qemu_put_sbyte(f, req->retry ? 1 : 2); diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index ceaf78b423..bd7103cd0e 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2704,15 +2704,15 @@ static void scsi_block_sgio_complete(void *opaque, int ret) SCSIDiskReq *r = &req->req; SCSIDevice *s = r->req.dev; sg_io_hdr_t *io_hdr = &req->io_header; - SCSISense sense; if (ret == 0) { if (io_hdr->host_status != SCSI_HOST_OK) { - ret = scsi_sense_from_host_status(io_hdr->host_status, &sense); - if (ret == CHECK_CONDITION) { - scsi_req_build_sense(&r->req, sense); - } - } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { + scsi_req_complete_failed(&r->req, io_hdr->host_status); + scsi_req_unref(&r->req); + return; + } + + if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { ret = BUSY; } else { ret = io_hdr->status; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 02b87819e5..98c30c5d5c 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -89,10 +89,8 @@ static void scsi_command_complete_noio(SCSIGenericReq *r, int ret) scsi_req_build_sense(&r->req, sense); } } else if (io_hdr->host_status != SCSI_HOST_OK) { - status = scsi_sense_from_host_status(io_hdr->host_status, &sense); - if (status == CHECK_CONDITION) { - scsi_req_build_sense(&r->req, sense); - } + scsi_req_complete_failed(&r->req, io_hdr->host_status); + goto done; } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { status = BUSY; } else { diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 358c0e70b0..6d80730287 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -500,6 +500,51 @@ static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) virtio_scsi_complete_req(req); } +static void virtio_scsi_command_failed(SCSIRequest *r) +{ + VirtIOSCSIReq *req = r->hba_private; + + if (r->io_canceled) { + return; + } + + req->resp.cmd.status = GOOD; + switch (r->host_status) { + case SCSI_HOST_NO_LUN: + req->resp.cmd.response = VIRTIO_SCSI_S_INCORRECT_LUN; + break; + case SCSI_HOST_BUSY: + req->resp.cmd.response = VIRTIO_SCSI_S_BUSY; + break; + case SCSI_HOST_TIME_OUT: + case SCSI_HOST_ABORTED: + req->resp.cmd.response = VIRTIO_SCSI_S_ABORTED; + break; + case SCSI_HOST_BAD_RESPONSE: + req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET; + break; + case SCSI_HOST_RESET: + req->resp.cmd.response = VIRTIO_SCSI_S_RESET; + break; + case SCSI_HOST_TRANSPORT_DISRUPTED: + req->resp.cmd.response = VIRTIO_SCSI_S_TRANSPORT_FAILURE; + break; + case SCSI_HOST_TARGET_FAILURE: + req->resp.cmd.response = VIRTIO_SCSI_S_TARGET_FAILURE; + break; + case SCSI_HOST_RESERVATION_ERROR: + req->resp.cmd.response = VIRTIO_SCSI_S_NEXUS_FAILURE; + break; + case SCSI_HOST_ALLOCATION_FAILURE: + case SCSI_HOST_MEDIUM_ERROR: + case SCSI_HOST_ERROR: + default: + req->resp.cmd.response = VIRTIO_SCSI_S_FAILURE; + break; + } + virtio_scsi_complete_cmd_req(req); +} + static void virtio_scsi_command_complete(SCSIRequest *r, size_t resid) { VirtIOSCSIReq *req = r->hba_private; @@ -908,6 +953,7 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = { .max_lun = VIRTIO_SCSI_MAX_LUN, .complete = virtio_scsi_command_complete, + .fail = virtio_scsi_command_failed, .cancel = virtio_scsi_request_cancelled, .change = virtio_scsi_change, .parse_cdb = virtio_scsi_parse_cdb, diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 0da378ed50..1f30cb020a 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -510,6 +510,44 @@ pvscsi_write_sense(PVSCSIRequest *r, uint8_t *sense, int len) cpu_physical_memory_write(r->req.senseAddr, sense, r->cmp.senseLen); } +static void +pvscsi_command_failed(SCSIRequest *req) +{ + PVSCSIRequest *pvscsi_req = req->hba_private; + PVSCSIState *s; + + if (!pvscsi_req) { + trace_pvscsi_command_complete_not_found(req->tag); + return; + } + s = pvscsi_req->dev; + + switch (req->host_status) { + case SCSI_HOST_NO_LUN: + pvscsi_req->cmp.hostStatus = BTSTAT_LUNMISMATCH; + break; + case SCSI_HOST_BUSY: + pvscsi_req->cmp.hostStatus = BTSTAT_ABORTQUEUE; + break; + case SCSI_HOST_TIME_OUT: + case SCSI_HOST_ABORTED: + pvscsi_req->cmp.hostStatus = BTSTAT_SENTRST; + break; + case SCSI_HOST_BAD_RESPONSE: + pvscsi_req->cmp.hostStatus = BTSTAT_SELTIMEO; + break; + case SCSI_HOST_RESET: + pvscsi_req->cmp.hostStatus = BTSTAT_BUSRESET; + break; + default: + pvscsi_req->cmp.hostStatus = BTSTAT_HASOFTWARE; + break; + } + pvscsi_req->cmp.scsiStatus = GOOD; + qemu_sglist_destroy(&pvscsi_req->sgl); + pvscsi_complete_request(s, pvscsi_req); +} + static void pvscsi_command_complete(SCSIRequest *req, size_t resid) { @@ -1103,6 +1141,7 @@ static const struct SCSIBusInfo pvscsi_scsi_info = { .get_sg_list = pvscsi_get_sg_list, .complete = pvscsi_command_complete, .cancel = pvscsi_request_cancelled, + .fail = pvscsi_command_failed, }; static void diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 5d992e6e1d..0b726bc78c 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -27,7 +27,8 @@ struct SCSIRequest { uint32_t refcount; uint32_t tag; uint32_t lun; - uint32_t status; + int16_t status; + int16_t host_status; void *hba_private; size_t resid; SCSICommand cmd; @@ -123,6 +124,7 @@ struct SCSIBusInfo { int (*parse_cdb)(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, void *hba_private); void (*transfer_data)(SCSIRequest *req, uint32_t arg); + void (*fail)(SCSIRequest *req); void (*complete)(SCSIRequest *req, size_t resid); void (*cancel)(SCSIRequest *req); void (*change)(SCSIBus *bus, SCSIDevice *dev, SCSISense sense); @@ -177,6 +179,7 @@ void scsi_req_print(SCSIRequest *req); void scsi_req_continue(SCSIRequest *req); void scsi_req_data(SCSIRequest *req, int len); void scsi_req_complete(SCSIRequest *req, int status); +void scsi_req_complete_failed(SCSIRequest *req, int host_status); uint8_t *scsi_req_get_buf(SCSIRequest *req); int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len); void scsi_req_cancel_complete(SCSIRequest *req); -- cgit 1.4.1