From 40f16dd1279e7f26357b3c4b3838a89ffc6153da Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 3 May 2011 14:15:59 +0200 Subject: scsi-generic: Remove bogus double complete scsi-generic scsi_read_complete() should not -both- call the client complete callback with SCSI_REASON_DATA -and- call scsi_command_complete(). The former will cause the client to queue a new read or write request, while the later will free the request data structure, thus causing the new read or write request to use a freed/stale structure when it completes. This patch fixes the bug, fixing a crash with scsi-generic & RHEL5.5 installer. Cc: Benjamin Herrenschmidt Cc: David Gibson Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/scsi-generic.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 9be1cca4c3..102f1dae09 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -172,9 +172,11 @@ static void scsi_read_complete(void * opaque, int ret) DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); r->len = -1; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); - if (len == 0) + if (len == 0) { scsi_command_complete(r, 0); + } else { + r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); + } } /* Read more data from scsi device into buffer. */ -- cgit v1.2.3-55-g7522 From ab9adc88c80186cfef29bda076363e20aa675241 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Apr 2011 14:59:13 +0200 Subject: scsi: introduce scsi_req_data This abstracts calling the command_complete callback, reducing churn in the following patches. Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/scsi-bus.c | 11 +++++++++++ hw/scsi-disk.c | 8 ++++---- hw/scsi-generic.c | 6 +++--- hw/scsi.h | 1 + trace-events | 1 + 5 files changed, 20 insertions(+), 7 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 0fd85fc56d..191cbabc44 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -495,6 +495,17 @@ static const char *scsi_command_name(uint8_t cmd) return names[cmd]; } +/* Called by the devices when data is ready for the HBA. The HBA should + start a DMA operation to read or fill the device's data buffer. + Once it completes, calling one of req->dev->info->read_data or + req->dev->info->write_data (depending on the direction of the + transfer) will restart I/O. */ +void scsi_req_data(SCSIRequest *req, int len) +{ + trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); + req->bus->complete(req->bus, SCSI_REASON_DATA, req->tag, len); +} + void scsi_req_print(SCSIRequest *req) { FILE *fp = stderr; diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 397b9d6b39..741cf396bf 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -170,7 +170,7 @@ static void scsi_read_complete(void * opaque, int ret) n = r->iov.iov_len / 512; r->sector += n; r->sector_count -= n; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); + scsi_req_data(&r->req, r->iov.iov_len); } @@ -182,7 +182,7 @@ static void scsi_read_request(SCSIDiskReq *r) if (r->sector_count == (uint32_t)-1) { DPRINTF("Read buf_len=%zd\n", r->iov.iov_len); r->sector_count = 0; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); + scsi_req_data(&r->req, r->iov.iov_len); return; } DPRINTF("Read sector_count=%d\n", r->sector_count); @@ -245,7 +245,7 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) vm_stop(VMSTOP_DISKFULL); } else { if (type == SCSI_REQ_STATUS_RETRY_READ) { - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0); + scsi_req_data(&r->req, 0); } scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); @@ -281,7 +281,7 @@ static void scsi_write_complete(void * opaque, int ret) } r->iov.iov_len = len; DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, len); - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); + scsi_req_data(&r->req, len); } } diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 102f1dae09..e4f1f3079e 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -175,7 +175,7 @@ static void scsi_read_complete(void * opaque, int ret) if (len == 0) { scsi_command_complete(r, 0); } else { - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); + scsi_req_data(&r->req, len); } } @@ -212,7 +212,7 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) DPRINTF("Sense: %d %d %d %d %d %d %d %d\n", r->buf[0], r->buf[1], r->buf[2], r->buf[3], r->buf[4], r->buf[5], r->buf[6], r->buf[7]); - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, s->senselen); + scsi_req_data(&r->req, s->senselen); return; } @@ -263,7 +263,7 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag) if (r->len == 0) { r->len = r->buflen; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->len); + scsi_req_data(&r->req, r->len); return 0; } diff --git a/hw/scsi.h b/hw/scsi.h index d3b5d56cd6..7c09f32d4d 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -105,6 +105,7 @@ void scsi_req_free(SCSIRequest *req); int scsi_req_parse(SCSIRequest *req, uint8_t *buf); void scsi_req_print(SCSIRequest *req); +void scsi_req_data(SCSIRequest *req, int len); void scsi_req_complete(SCSIRequest *req); #endif diff --git a/trace-events b/trace-events index b11b71d205..0340eb2d77 100644 --- a/trace-events +++ b/trace-events @@ -207,6 +207,7 @@ disable usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature # hw/scsi-bus.c disable scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d" +disable scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d" disable scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d" disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer, uint64_t lba) "target %d lun %d tag %d command %d dir %d length %d lba %"PRIu64"" disable scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d" -- cgit v1.2.3-55-g7522 From cfdc1bb06ee4cd3a7e4aa0ebf14b00c0ce3a5e94 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Apr 2011 17:11:14 +0200 Subject: scsi: introduce SCSIBusOps There are more operations than a SCSI bus can handle, besides completing commands. One example, which this series will introduce, is cleaning up after a request is cancelled. More long term, a "SCSI bus" can represent the LUNs attached to a target; in this case, while all commands will ultimately reach a logical unit, it is the target who is in charge of answering REPORT LUNs. Signed-off-by: Paolo Bonzini Cc: Christoph Hellwig --- hw/esp.c | 6 +++++- hw/lsi53c895a.c | 6 +++++- hw/scsi-bus.c | 12 ++++++------ hw/scsi-generic.c | 2 +- hw/scsi.h | 13 +++++++------ hw/spapr_vscsi.c | 6 +++++- hw/usb-msd.c | 6 +++++- 7 files changed, 34 insertions(+), 17 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/esp.c b/hw/esp.c index fa9d2a2706..ae18401a25 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -714,6 +714,10 @@ void esp_init(target_phys_addr_t espaddr, int it_shift, *dma_enable = qdev_get_gpio_in(dev, 1); } +static const struct SCSIBusOps esp_scsi_ops = { + .complete = esp_command_complete +}; + static int esp_init1(SysBusDevice *dev) { ESPState *s = FROM_SYSBUS(ESPState, dev); @@ -728,7 +732,7 @@ static int esp_init1(SysBusDevice *dev) qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2); - scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, esp_command_complete); + scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, &esp_scsi_ops); return scsi_bus_legacy_handle_cmdline(&s->bus); } diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 2ce38a97ae..704e8ad2d4 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -2205,6 +2205,10 @@ static int lsi_scsi_uninit(PCIDevice *d) return 0; } +static const struct SCSIBusOps lsi_scsi_ops = { + .complete = lsi_command_complete +}; + static int lsi_scsi_init(PCIDevice *dev) { LSIState *s = DO_UPCAST(LSIState, dev, dev); @@ -2241,7 +2245,7 @@ static int lsi_scsi_init(PCIDevice *dev) PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_ram_mapfunc); QTAILQ_INIT(&s->queue); - scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, lsi_command_complete); + scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, &lsi_scsi_ops); if (!dev->qdev.hotplugged) { return scsi_bus_legacy_handle_cmdline(&s->bus); } diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 191cbabc44..1850a87da0 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -21,13 +21,13 @@ static int next_scsi_bus; /* Create a scsi bus, and attach devices to it. */ void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, - scsi_completionfn complete) + const SCSIBusOps *ops) { qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL); bus->busnr = next_scsi_bus++; bus->tcq = tcq; bus->ndev = ndev; - bus->complete = complete; + bus->ops = ops; bus->qbus.allow_hotplug = 1; } @@ -503,7 +503,7 @@ static const char *scsi_command_name(uint8_t cmd) void scsi_req_data(SCSIRequest *req, int len) { trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); - req->bus->complete(req->bus, SCSI_REASON_DATA, req->tag, len); + req->bus->ops->complete(req->bus, SCSI_REASON_DATA, req->tag, len); } void scsi_req_print(SCSIRequest *req) @@ -538,9 +538,9 @@ void scsi_req_complete(SCSIRequest *req) { assert(req->status != -1); scsi_req_dequeue(req); - req->bus->complete(req->bus, SCSI_REASON_DONE, - req->tag, - req->status); + req->bus->ops->complete(req->bus, SCSI_REASON_DONE, + req->tag, + req->status); } static char *scsibus_get_fw_dev_path(DeviceState *dev) diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index e4f1f3079e..f09458bec1 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -335,7 +335,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, s->senselen = 7; s->driver_status = SG_ERR_DRIVER_SENSE; bus = scsi_bus_from_device(d); - bus->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION); + bus->ops->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION); return 0; } diff --git a/hw/scsi.h b/hw/scsi.h index 7c09f32d4d..d4ecc9bec4 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -16,10 +16,9 @@ enum scsi_reason { }; typedef struct SCSIBus SCSIBus; +typedef struct SCSIBusOps SCSIBusOps; typedef struct SCSIDevice SCSIDevice; typedef struct SCSIDeviceInfo SCSIDeviceInfo; -typedef void (*scsi_completionfn)(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg); enum SCSIXferMode { SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */ @@ -74,20 +73,22 @@ struct SCSIDeviceInfo { uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag); }; -typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv, - int unit); +struct SCSIBusOps { + void (*complete)(SCSIBus *bus, int reason, uint32_t tag, uint32_t arg); +}; + struct SCSIBus { BusState qbus; int busnr; int tcq, ndev; - scsi_completionfn complete; + const SCSIBusOps *ops; SCSIDevice *devs[MAX_SCSI_DEVS]; }; void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, - scsi_completionfn complete); + const SCSIBusOps *ops); void scsi_qdev_register(SCSIDeviceInfo *info); static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 992833450c..c183008e42 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -907,6 +907,10 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data) return 0; } +static const struct SCSIBusOps vscsi_scsi_ops = { + .complete = vscsi_command_complete +}; + static int spapr_vscsi_init(VIOsPAPRDevice *dev) { VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev); @@ -923,7 +927,7 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev) dev->crq.SendFunc = vscsi_do_crq; scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT, - vscsi_command_complete); + &vscsi_scsi_ops); if (!dev->qdev.hotplugged) { scsi_bus_legacy_handle_cmdline(&s->bus); } diff --git a/hw/usb-msd.c b/hw/usb-msd.c index bd1c3a415f..c0a381abb3 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -487,6 +487,10 @@ static void usb_msd_password_cb(void *opaque, int err) qdev_unplug(&s->dev.qdev); } +static const struct SCSIBusOps usb_msd_scsi_ops = { + .complete = usb_msd_command_complete +}; + static int usb_msd_initfn(USBDevice *dev) { MSDState *s = DO_UPCAST(MSDState, dev, dev); @@ -516,7 +520,7 @@ static int usb_msd_initfn(USBDevice *dev) } usb_desc_init(dev); - scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete); + scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, &usb_msd_scsi_ops); s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable); if (!s->scsi_dev) { return -1; -- cgit v1.2.3-55-g7522 From d33e0ce213cec82a059f5e37667231200eb77325 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 25 May 2011 16:53:46 +0200 Subject: scsi-generic: do not use a stale aiocb If a request is canceled after it has been completed, scsi_cancel_io would pass a stale aiocb to bdrv_aio_cancel. Avoid this. Signed-off-by: Paolo Bonzini Cc: Christoph Hellwig --- hw/scsi-generic.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index f09458bec1..bd099835f4 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -91,6 +91,7 @@ static void scsi_command_complete(void *opaque, int ret) SCSIGenericReq *r = (SCSIGenericReq *)opaque; SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); + r->req.aiocb = NULL; s->driver_status = r->io_header.driver_status; if (s->driver_status & SG_ERR_DRIVER_SENSE) s->senselen = r->io_header.sb_len_wr; @@ -163,6 +164,7 @@ static void scsi_read_complete(void * opaque, int ret) SCSIGenericReq *r = (SCSIGenericReq *)opaque; int len; + r->req.aiocb = NULL; if (ret) { DPRINTF("IO error ret %d\n", ret); scsi_command_complete(r, ret); @@ -229,6 +231,7 @@ static void scsi_write_complete(void * opaque, int ret) SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); DPRINTF("scsi_write_complete() ret = %d\n", ret); + r->req.aiocb = NULL; if (ret) { DPRINTF("IO error\n"); scsi_command_complete(r, ret); -- cgit v1.2.3-55-g7522 From ad2d30f79d3b0812f02c741be2189796b788d6d7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Apr 2011 16:01:56 +0200 Subject: scsi: reference-count requests With the next patch, a device may hold SCSIRequest for an indefinite time. Split a rather big patch, and protect against access errors, by reference counting them. There is some ugliness in scsi_send_command implementation due to the need to unref the request when it fails. This will go away with the next patches, which move the unref'ing to the devices. Signed-off-by: Paolo Bonzini Cc: Christoph Hellwig --- hw/scsi-bus.c | 29 ++++++++++++++++++++++------- hw/scsi-disk.c | 23 +++++++++++++++-------- hw/scsi-generic.c | 24 ++++++++++++++++-------- hw/scsi.h | 5 +++++ 4 files changed, 58 insertions(+), 23 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 1850a87da0..e7fd903621 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -136,6 +136,8 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l SCSIRequest *req; req = qemu_mallocz(size); + /* Two references: one is passed back to the HBA, one is in d->requests. */ + req->refcount = 2; req->bus = scsi_bus_from_device(d); req->dev = d; req->tag = tag; @@ -159,21 +161,16 @@ SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag) return NULL; } -static void scsi_req_dequeue(SCSIRequest *req) +void scsi_req_dequeue(SCSIRequest *req) { trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); if (req->enqueued) { QTAILQ_REMOVE(&req->dev->requests, req, next); req->enqueued = false; + scsi_req_unref(req); } } -void scsi_req_free(SCSIRequest *req) -{ - scsi_req_dequeue(req); - qemu_free(req); -} - static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) { switch (cmd[0] >> 5) { @@ -495,6 +492,22 @@ static const char *scsi_command_name(uint8_t cmd) return names[cmd]; } +SCSIRequest *scsi_req_ref(SCSIRequest *req) +{ + req->refcount++; + return req; +} + +void scsi_req_unref(SCSIRequest *req) +{ + if (--req->refcount == 0) { + if (req->dev->info->free_req) { + req->dev->info->free_req(req); + } + qemu_free(req); + } +} + /* Called by the devices when data is ready for the HBA. The HBA should start a DMA operation to read or fill the device's data buffer. Once it completes, calling one of req->dev->info->read_data or @@ -537,10 +550,12 @@ void scsi_req_print(SCSIRequest *req) void scsi_req_complete(SCSIRequest *req) { assert(req->status != -1); + scsi_req_ref(req); scsi_req_dequeue(req); req->bus->ops->complete(req->bus, SCSI_REASON_DONE, req->tag, req->status); + scsi_req_unref(req); } static char *scsibus_get_fw_dev_path(DeviceState *dev) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 741cf396bf..87d7b9305d 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -98,10 +98,11 @@ static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag, return r; } -static void scsi_remove_request(SCSIDiskReq *r) +static void scsi_free_request(SCSIRequest *req) { + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); + qemu_vfree(r->iov.iov_base); - scsi_req_free(&r->req); } static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag) @@ -134,7 +135,6 @@ static void scsi_command_complete(SCSIDiskReq *r, int status, int sense) r->req.tag, status, sense); scsi_req_set_status(r, status, sense); scsi_req_complete(&r->req); - scsi_remove_request(r); } /* Cancel a pending data transfer. */ @@ -148,7 +148,7 @@ static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) if (r->req.aiocb) bdrv_aio_cancel(r->req.aiocb); r->req.aiocb = NULL; - scsi_remove_request(r); + scsi_req_dequeue(&r->req); } } @@ -1033,7 +1033,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, uint8_t *buf, int lun) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - uint32_t len; + int32_t len; int is_write; uint8_t command; uint8_t *outbuf; @@ -1095,6 +1095,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, case REZERO_UNIT: rc = scsi_disk_emulate_command(r, outbuf); if (rc < 0) { + scsi_req_unref(&r->req); return 0; } @@ -1181,9 +1182,11 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); fail: scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); + scsi_req_unref(&r->req); return 0; illegal_lba: scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); + scsi_req_unref(&r->req); return 0; } if (r->sector_count == 0 && r->iov.iov_len == 0) { @@ -1191,12 +1194,13 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, } len = r->sector_count * 512 + r->iov.iov_len; if (is_write) { - return -len; + len = -len; } else { if (!r->sector_count) r->sector_count = -1; - return len; } + scsi_req_unref(&r->req); + return len; } static void scsi_disk_purge_requests(SCSIDiskState *s) @@ -1208,7 +1212,7 @@ static void scsi_disk_purge_requests(SCSIDiskState *s) if (r->req.aiocb) { bdrv_aio_cancel(r->req.aiocb); } - scsi_remove_request(r); + scsi_req_dequeue(&r->req); } } @@ -1321,6 +1325,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .qdev.reset = scsi_disk_reset, .init = scsi_hd_initfn, .destroy = scsi_destroy, + .free_req = scsi_free_request, .send_command = scsi_send_command, .read_data = scsi_read_data, .write_data = scsi_write_data, @@ -1339,6 +1344,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .qdev.reset = scsi_disk_reset, .init = scsi_cd_initfn, .destroy = scsi_destroy, + .free_req = scsi_free_request, .send_command = scsi_send_command, .read_data = scsi_read_data, .write_data = scsi_write_data, @@ -1356,6 +1362,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .qdev.reset = scsi_disk_reset, .init = scsi_disk_initfn, .destroy = scsi_destroy, + .free_req = scsi_free_request, .send_command = scsi_send_command, .read_data = scsi_read_data, .write_data = scsi_write_data, diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index bd099835f4..06e9dfea8b 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -74,10 +74,11 @@ static SCSIGenericReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lu return DO_UPCAST(SCSIGenericReq, req, req); } -static void scsi_remove_request(SCSIGenericReq *r) +static void scsi_free_request(SCSIRequest *req) { + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + qemu_free(r->buf); - scsi_req_free(&r->req); } static SCSIGenericReq *scsi_find_request(SCSIGenericState *s, uint32_t tag) @@ -113,7 +114,6 @@ static void scsi_command_complete(void *opaque, int ret) r, r->req.tag, r->req.status); scsi_req_complete(&r->req); - scsi_remove_request(r); } /* Cancel a pending data transfer. */ @@ -128,7 +128,7 @@ static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) if (r->req.aiocb) bdrv_aio_cancel(r->req.aiocb); r->req.aiocb = NULL; - scsi_remove_request(r); + scsi_req_dequeue(&r->req); } } @@ -323,6 +323,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, SCSIGenericReq *r; SCSIBus *bus; int ret; + int32_t len; if (cmd[0] != REQUEST_SENSE && (lun != s->lun || (cmd[1] >> 5) != s->lun)) { @@ -351,7 +352,8 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, if (-1 == scsi_req_parse(&r->req, cmd)) { BADF("Unsupported command length, command %x\n", cmd[0]); - scsi_remove_request(r); + scsi_req_dequeue(&r->req); + scsi_req_unref(&r->req); return 0; } scsi_req_fixup(&r->req); @@ -377,8 +379,10 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete); if (ret == -1) { scsi_command_complete(r, -EINVAL); + scsi_req_unref(&r->req); return 0; } + scsi_req_unref(&r->req); return 0; } @@ -393,10 +397,13 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, r->len = r->req.cmd.xfer; if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { r->len = 0; - return -r->req.cmd.xfer; + len = -r->req.cmd.xfer; + } else { + len = r->req.cmd.xfer; } - return r->req.cmd.xfer; + scsi_req_unref(&r->req); + return len; } static int get_blocksize(BlockDriverState *bdrv) @@ -469,7 +476,7 @@ static void scsi_generic_purge_requests(SCSIGenericState *s) if (r->req.aiocb) { bdrv_aio_cancel(r->req.aiocb); } - scsi_remove_request(r); + scsi_req_dequeue(&r->req); } } @@ -561,6 +568,7 @@ static SCSIDeviceInfo scsi_generic_info = { .qdev.reset = scsi_generic_reset, .init = scsi_generic_initfn, .destroy = scsi_destroy, + .free_req = scsi_free_request, .send_command = scsi_send_command, .read_data = scsi_read_data, .write_data = scsi_write_data, diff --git a/hw/scsi.h b/hw/scsi.h index d4ecc9bec4..a1d0e7451f 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -29,6 +29,7 @@ enum SCSIXferMode { typedef struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; + uint32_t refcount; uint32_t tag; uint32_t lun; uint32_t status; @@ -65,6 +66,7 @@ struct SCSIDeviceInfo { DeviceInfo qdev; scsi_qdev_initfn init; void (*destroy)(SCSIDevice *s); + void (*free_req)(SCSIRequest *req); int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun); void (*read_data)(SCSIDevice *s, uint32_t tag); @@ -103,6 +105,9 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus); SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag); void scsi_req_free(SCSIRequest *req); +void scsi_req_dequeue(SCSIRequest *req); +SCSIRequest *scsi_req_ref(SCSIRequest *req); +void scsi_req_unref(SCSIRequest *req); int scsi_req_parse(SCSIRequest *req, uint8_t *buf); void scsi_req_print(SCSIRequest *req); -- cgit v1.2.3-55-g7522 From 5c6c0e513600ba57c3e73b7151d3c0664438f7b5 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 18 Apr 2011 12:35:39 +0200 Subject: scsi: Use 'SCSIRequest' directly Currently the SCSIRequest structure is abstracted away and cannot accessed directly from the driver. This requires the handler to do a lookup on an abstract 'tag' which identifies the SCSIRequest structure. With this patch the SCSIRequest structure is exposed to the driver. This allows use to use it directly as an argument to the SCSIDeviceInfo callback functions and remove the lookup. A new callback function 'alloc_req' is introduced matching 'free req'; unref'ing to free up resources after use is moved into the scsi_command_complete callbacks. This temporarily introduces a leak of requests that are cancelled, when they are removed from the queue and not from the driver. This is fixed later by introducing scsi_req_cancel. That patch in turn depends on this one, because the argument to scsi_req_cancel is a SCSIRequest. Signed-off-by: Hannes Reinecke Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/esp.c | 27 ++++++++----- hw/lsi53c895a.c | 56 +++++++++++++++----------- hw/scsi-bus.c | 25 ++++-------- hw/scsi-disk.c | 118 +++++++++++++++--------------------------------------- hw/scsi-generic.c | 107 +++++++++++++++---------------------------------- hw/scsi.h | 21 +++++----- hw/spapr_vscsi.c | 44 +++++++++++--------- hw/usb-msd.c | 27 +++++++------ 8 files changed, 173 insertions(+), 252 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/esp.c b/hw/esp.c index ae18401a25..57061ca8c4 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -65,6 +65,7 @@ struct ESPState { uint32_t dma; SCSIBus bus; SCSIDevice *current_dev; + SCSIRequest *current_req; uint8_t cmdbuf[TI_BUFSZ]; uint32_t cmdlen; uint32_t do_cmd; @@ -209,7 +210,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) if (s->current_dev) { /* Started a new command before the old one finished. Cancel it. */ - s->current_dev->info->cancel_io(s->current_dev, 0); + s->current_dev->info->cancel_io(s->current_req); s->async_len = 0; } @@ -232,7 +233,8 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) DPRINTF("do_busid_cmd: busid 0x%x\n", busid); lun = busid & 7; - datalen = s->current_dev->info->send_command(s->current_dev, 0, buf, lun); + s->current_req = s->current_dev->info->alloc_req(s->current_dev, 0, lun); + datalen = s->current_dev->info->send_command(s->current_req, buf); s->ti_size = datalen; if (datalen != 0) { s->rregs[ESP_RSTAT] = STAT_TC; @@ -240,10 +242,10 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) s->dma_counter = 0; if (datalen > 0) { s->rregs[ESP_RSTAT] |= STAT_DI; - s->current_dev->info->read_data(s->current_dev, 0); + s->current_dev->info->read_data(s->current_req); } else { s->rregs[ESP_RSTAT] |= STAT_DO; - s->current_dev->info->write_data(s->current_dev, 0); + s->current_dev->info->write_data(s->current_req); } } s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; @@ -372,9 +374,9 @@ static void esp_do_dma(ESPState *s) if (s->async_len == 0) { if (to_device) { // ti_size is negative - s->current_dev->info->write_data(s->current_dev, 0); + s->current_dev->info->write_data(s->current_req); } else { - s->current_dev->info->read_data(s->current_dev, 0); + s->current_dev->info->read_data(s->current_req); /* If there is still data to be read from the device then complete the DMA operation immediately. Otherwise defer until the scsi layer has completed. */ @@ -388,10 +390,9 @@ static void esp_do_dma(ESPState *s) } } -static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) +static void esp_command_complete(SCSIRequest *req, int reason, uint32_t arg) { - ESPState *s = DO_UPCAST(ESPState, busdev.qdev, bus->qbus.parent); + ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); if (reason == SCSI_REASON_DONE) { DPRINTF("SCSI Command complete\n"); @@ -405,11 +406,15 @@ static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag, s->sense = arg; s->rregs[ESP_RSTAT] = STAT_ST; esp_dma_done(s); - s->current_dev = NULL; + if (s->current_req) { + scsi_req_unref(s->current_req); + s->current_req = NULL; + s->current_dev = NULL; + } } else { DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size); s->async_len = arg; - s->async_buf = s->current_dev->info->get_buf(s->current_dev, 0); + s->async_buf = s->current_dev->info->get_buf(req); if (s->dma_left) { esp_do_dma(s); } else if (s->dma_counter != 0 && s->ti_size <= 0) { diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 1ebcde74ef..56234f836c 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -174,6 +174,7 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0) #define LSI_TAG_VALID (1 << 16) typedef struct lsi_request { + SCSIRequest *req; uint32_t tag; uint32_t dma_len; uint8_t *dma_buf; @@ -567,11 +568,9 @@ static void lsi_do_dma(LSIState *s, int out) s->csbc += count; s->dnad += count; s->dbc -= count; - - if (s->current->dma_buf == NULL) { - s->current->dma_buf = dev->info->get_buf(dev, s->current->tag); + if (s->current->dma_buf == NULL) { + s->current->dma_buf = dev->info->get_buf(s->current->req); } - /* ??? Set SFBR to first data byte. */ if (out) { cpu_physical_memory_read(addr, s->current->dma_buf, count); @@ -583,10 +582,10 @@ static void lsi_do_dma(LSIState *s, int out) s->current->dma_buf = NULL; if (out) { /* Write the data. */ - dev->info->write_data(dev, s->current->tag); + dev->info->write_data(s->current->req); } else { /* Request any remaining data. */ - dev->info->read_data(dev, s->current->tag); + dev->info->read_data(s->current->req); } } else { s->current->dma_buf += count; @@ -698,12 +697,10 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg) return 1; } } - -/* Callback to indicate that the SCSI layer has completed a transfer. */ -static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) + /* Callback to indicate that the SCSI layer has completed a transfer. */ +static void lsi_command_complete(SCSIRequest *req, int reason, uint32_t arg) { - LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent); + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); int out; out = (s->sstat1 & PHASE_MASK) == PHASE_DO; @@ -718,21 +715,24 @@ static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, lsi_set_phase(s, PHASE_ST); } - qemu_free(s->current); - s->current = NULL; - + if (s->current && req == s->current->req) { + scsi_req_unref(s->current->req); + qemu_free(s->current); + s->current = NULL; + } lsi_resume_script(s); return; } - if (s->waiting == 1 || !s->current || tag != s->current->tag || + if (s->waiting == 1 || !s->current || req->tag != s->current->tag || (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { - if (lsi_queue_tag(s, tag, arg)) + if (lsi_queue_tag(s, req->tag, arg)) { return; + } } /* host adapter (re)connected */ - DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg); + DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, arg); s->current->dma_len = arg; s->command_complete = 1; if (!s->waiting) @@ -768,14 +768,16 @@ static void lsi_do_command(LSIState *s) assert(s->current == NULL); s->current = qemu_mallocz(sizeof(lsi_request)); s->current->tag = s->select_tag; + s->current->req = dev->info->alloc_req(dev, s->current->tag, + s->current_lun); - n = dev->info->send_command(dev, s->current->tag, buf, s->current_lun); + n = dev->info->send_command(s->current->req, buf); if (n > 0) { lsi_set_phase(s, PHASE_DI); - dev->info->read_data(dev, s->current->tag); + dev->info->read_data(s->current->req); } else if (n < 0) { lsi_set_phase(s, PHASE_DO); - dev->info->write_data(dev, s->current->tag); + dev->info->write_data(s->current->req); } if (!s->command_complete) { @@ -868,13 +870,15 @@ static void lsi_do_msgout(LSIState *s) int len; uint32_t current_tag; SCSIDevice *current_dev; - lsi_request *p, *p_next; + lsi_request *current_req, *p, *p_next; int id; if (s->current) { current_tag = s->current->tag; + current_req = s->current; } else { current_tag = s->select_tag; + current_req = lsi_find_by_tag(s, current_tag); } id = (current_tag >> 8) & 0xf; current_dev = s->bus.devs[id]; @@ -926,7 +930,9 @@ static void lsi_do_msgout(LSIState *s) case 0x0d: /* The ABORT TAG message clears the current I/O process only. */ DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag); - current_dev->info->cancel_io(current_dev, current_tag); + if (current_req) { + current_dev->info->cancel_io(current_req->req); + } lsi_disconnect(s); break; case 0x06: @@ -949,7 +955,9 @@ static void lsi_do_msgout(LSIState *s) } /* clear the current I/O process */ - current_dev->info->cancel_io(current_dev, current_tag); + if (s->current) { + current_dev->info->cancel_io(s->current->req); + } /* As the current implemented devices scsi_disk and scsi_generic only support one LUN, we don't need to keep track of LUNs. @@ -961,7 +969,7 @@ static void lsi_do_msgout(LSIState *s) id = current_tag & 0x0000ff00; QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) { if ((p->tag & 0x0000ff00) == id) { - current_dev->info->cancel_io(current_dev, p->tag); + current_dev->info->cancel_io(p->req); QTAILQ_REMOVE(&s->queue, p, next); } } diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index e7fd903621..c7748d0ead 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -136,29 +136,22 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l SCSIRequest *req; req = qemu_mallocz(size); - /* Two references: one is passed back to the HBA, one is in d->requests. */ - req->refcount = 2; + req->refcount = 1; req->bus = scsi_bus_from_device(d); req->dev = d; req->tag = tag; req->lun = lun; req->status = -1; - req->enqueued = true; trace_scsi_req_alloc(req->dev->id, req->lun, req->tag); - QTAILQ_INSERT_TAIL(&d->requests, req, next); return req; } -SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag) +void scsi_req_enqueue(SCSIRequest *req) { - SCSIRequest *req; - - QTAILQ_FOREACH(req, &d->requests, next) { - if (req->tag == tag) { - return req; - } - } - return NULL; + assert(!req->enqueued); + scsi_req_ref(req); + req->enqueued = true; + QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); } void scsi_req_dequeue(SCSIRequest *req) @@ -516,7 +509,7 @@ void scsi_req_unref(SCSIRequest *req) void scsi_req_data(SCSIRequest *req, int len) { trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); - req->bus->ops->complete(req->bus, SCSI_REASON_DATA, req->tag, len); + req->bus->ops->complete(req, SCSI_REASON_DATA, len); } void scsi_req_print(SCSIRequest *req) @@ -552,9 +545,7 @@ void scsi_req_complete(SCSIRequest *req) assert(req->status != -1); scsi_req_ref(req); scsi_req_dequeue(req); - req->bus->ops->complete(req->bus, SCSI_REASON_DONE, - req->tag, - req->status); + req->bus->ops->complete(req, SCSI_REASON_DONE, req->status); scsi_req_unref(req); } diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 87d7b9305d..f7c09c9b79 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -86,16 +86,17 @@ struct SCSIDiskState static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf); -static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag, +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); SCSIRequest *req; SCSIDiskReq *r; req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun); r = DO_UPCAST(SCSIDiskReq, req, req); r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE); - return r; + return req; } static void scsi_free_request(SCSIRequest *req) @@ -105,11 +106,6 @@ static void scsi_free_request(SCSIRequest *req) qemu_vfree(r->iov.iov_base); } -static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag) -{ - return DO_UPCAST(SCSIDiskReq, req, scsi_req_find(&s->qdev, tag)); -} - static void scsi_disk_clear_sense(SCSIDiskState *s) { memset(&s->sense, 0, sizeof(s->sense)); @@ -138,18 +134,16 @@ static void scsi_command_complete(SCSIDiskReq *r, int status, int sense) } /* Cancel a pending data transfer. */ -static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) +static void scsi_cancel_io(SCSIRequest *req) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - DPRINTF("Cancel tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (r) { - if (r->req.aiocb) - bdrv_aio_cancel(r->req.aiocb); - r->req.aiocb = NULL; - scsi_req_dequeue(&r->req); + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); + + DPRINTF("Cancel tag=0x%x\n", req->tag); + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); } + r->req.aiocb = NULL; + scsi_req_dequeue(&r->req); } static void scsi_read_complete(void * opaque, int ret) @@ -174,8 +168,10 @@ static void scsi_read_complete(void * opaque, int ret) } -static void scsi_read_request(SCSIDiskReq *r) +/* Read more data from scsi device into buffer. */ +static void scsi_read_data(SCSIRequest *req) { + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; @@ -207,23 +203,6 @@ static void scsi_read_request(SCSIDiskReq *r) } } -/* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIDevice *d, uint32_t tag) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad read tag 0x%x\n", tag); - /* ??? This is the wrong error. */ - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - return; - } - - scsi_read_request(r); -} - static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) { int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); @@ -285,8 +264,9 @@ static void scsi_write_complete(void * opaque, int ret) } } -static void scsi_write_request(SCSIDiskReq *r) +static int scsi_write_data(SCSIRequest *req) { + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; @@ -305,24 +285,6 @@ static void scsi_write_request(SCSIDiskReq *r) /* Invoke completion routine to fetch data from host. */ scsi_write_complete(r, 0); } -} - -/* Write data to a scsi device. Returns nonzero on failure. - The transfer may complete asynchronously. */ -static int scsi_write_data(SCSIDevice *d, uint32_t tag) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - - DPRINTF("Write data tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad write tag 0x%x\n", tag); - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - return 1; - } - - scsi_write_request(r); return 0; } @@ -347,10 +309,10 @@ static void scsi_dma_restart_bh(void *opaque) switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) { case SCSI_REQ_STATUS_RETRY_READ: - scsi_read_request(r); + scsi_read_data(&r->req); break; case SCSI_REQ_STATUS_RETRY_WRITE: - scsi_write_request(r); + scsi_write_data(&r->req); break; case SCSI_REQ_STATUS_RETRY_FLUSH: ret = scsi_disk_emulate_command(r, r->iov.iov_base); @@ -376,16 +338,10 @@ static void scsi_dma_restart_cb(void *opaque, int running, int reason) } /* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) +static uint8_t *scsi_get_buf(SCSIRequest *req) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad buffer tag 0x%x\n", tag); - return NULL; - } return (uint8_t *)r->iov.iov_base; } @@ -1029,26 +985,18 @@ illegal_request: (eg. disk reads), negative for transfers to the device (eg. disk writes), and zero if the command does not transfer any data. */ -static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, - uint8_t *buf, int lun) +static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); int32_t len; int is_write; uint8_t command; uint8_t *outbuf; - SCSIDiskReq *r; int rc; + scsi_req_enqueue(req); command = buf[0]; - r = scsi_find_request(s, tag); - if (r) { - BADF("Tag 0x%x already in use\n", tag); - scsi_cancel_io(d, tag); - } - /* ??? Tags are not unique for different luns. We only implement a - single lun, so this should not matter. */ - r = scsi_new_request(s, tag, lun); outbuf = (uint8_t *)r->iov.iov_base; is_write = 0; DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); @@ -1067,9 +1015,9 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, } #endif - if (lun || buf[1] >> 5) { + if (req->lun || buf[1] >> 5) { /* Only LUN 0 supported. */ - DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5); + DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : buf[1] >> 5); if (command != REQUEST_SENSE && command != INQUIRY) goto fail; } @@ -1095,7 +1043,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, case REZERO_UNIT: rc = scsi_disk_emulate_command(r, outbuf); if (rc < 0) { - scsi_req_unref(&r->req); return 0; } @@ -1105,7 +1052,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, case READ_10: case READ_12: case READ_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); if (r->req.cmd.lba > s->max_lba) goto illegal_lba; @@ -1119,7 +1066,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, case WRITE_VERIFY: case WRITE_VERIFY_12: case WRITE_VERIFY_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("Write %s(sector %" PRId64 ", count %d)\n", (command & 0xe) == 0xe ? "And Verify " : "", r->req.cmd.lba, len); @@ -1154,7 +1101,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, } break; case WRITE_SAME_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); @@ -1182,11 +1129,9 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); fail: scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); - scsi_req_unref(&r->req); return 0; illegal_lba: scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - scsi_req_unref(&r->req); return 0; } if (r->sector_count == 0 && r->iov.iov_len == 0) { @@ -1199,7 +1144,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, if (!r->sector_count) r->sector_count = -1; } - scsi_req_unref(&r->req); return len; } @@ -1213,6 +1157,7 @@ static void scsi_disk_purge_requests(SCSIDiskState *s) bdrv_aio_cancel(r->req.aiocb); } scsi_req_dequeue(&r->req); + scsi_req_unref(&r->req); } } @@ -1325,6 +1270,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .qdev.reset = scsi_disk_reset, .init = scsi_hd_initfn, .destroy = scsi_destroy, + .alloc_req = scsi_new_request, .free_req = scsi_free_request, .send_command = scsi_send_command, .read_data = scsi_read_data, @@ -1344,6 +1290,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .qdev.reset = scsi_disk_reset, .init = scsi_cd_initfn, .destroy = scsi_destroy, + .alloc_req = scsi_new_request, .free_req = scsi_free_request, .send_command = scsi_send_command, .read_data = scsi_read_data, @@ -1362,6 +1309,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .qdev.reset = scsi_disk_reset, .init = scsi_disk_initfn, .destroy = scsi_destroy, + .alloc_req = scsi_new_request, .free_req = scsi_free_request, .send_command = scsi_send_command, .read_data = scsi_read_data, diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 06e9dfea8b..3740432d9e 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -66,12 +66,12 @@ struct SCSIGenericState uint8_t senselen; }; -static SCSIGenericReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) { SCSIRequest *req; req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun); - return DO_UPCAST(SCSIGenericReq, req, req); + return req; } static void scsi_free_request(SCSIRequest *req) @@ -81,11 +81,6 @@ static void scsi_free_request(SCSIRequest *req) qemu_free(r->buf); } -static SCSIGenericReq *scsi_find_request(SCSIGenericState *s, uint32_t tag) -{ - return DO_UPCAST(SCSIGenericReq, req, scsi_req_find(&s->qdev, tag)); -} - /* Helper function for command completion. */ static void scsi_command_complete(void *opaque, int ret) { @@ -117,19 +112,16 @@ static void scsi_command_complete(void *opaque, int ret) } /* Cancel a pending data transfer. */ -static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) +static void scsi_cancel_io(SCSIRequest *req) { - DPRINTF("scsi_cancel_io 0x%x\n", tag); - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; - DPRINTF("Cancel tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (r) { - if (r->req.aiocb) - bdrv_aio_cancel(r->req.aiocb); - r->req.aiocb = NULL; - scsi_req_dequeue(&r->req); + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + + DPRINTF("Cancel tag=0x%x\n", req->tag); + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); } + r->req.aiocb = NULL; + scsi_req_dequeue(&r->req); } static int execute_command(BlockDriverState *bdrv, @@ -182,21 +174,13 @@ static void scsi_read_complete(void * opaque, int ret) } /* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIDevice *d, uint32_t tag) +static void scsi_read_data(SCSIRequest *req) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); int ret; - DPRINTF("scsi_read_data 0x%x\n", tag); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad read tag 0x%x\n", tag); - /* ??? This is the wrong error. */ - scsi_command_complete(r, -EINVAL); - return; - } - + DPRINTF("scsi_read_data 0x%x\n", req->tag); if (r->len == -1) { scsi_command_complete(r, 0); return; @@ -249,21 +233,13 @@ static void scsi_write_complete(void * opaque, int ret) /* Write data to a scsi device. Returns nonzero on failure. The transfer may complete asynchronously. */ -static int scsi_write_data(SCSIDevice *d, uint32_t tag) +static int scsi_write_data(SCSIRequest *req) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); int ret; - DPRINTF("scsi_write_data 0x%x\n", tag); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad write tag 0x%x\n", tag); - /* ??? This is the wrong error. */ - scsi_command_complete(r, -EINVAL); - return 0; - } - + DPRINTF("scsi_write_data 0x%x\n", req->tag); if (r->len == 0) { r->len = r->buflen; scsi_req_data(&r->req, r->len); @@ -280,15 +256,10 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag) } /* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) +static uint8_t *scsi_get_buf(SCSIRequest *req) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad buffer tag 0x%x\n", tag); - return NULL; - } + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + return r->buf; } @@ -316,18 +287,17 @@ static void scsi_req_fixup(SCSIRequest *req) (eg. disk reads), negative for transfers to the device (eg. disk writes), and zero if the command does not transfer any data. */ -static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, - uint8_t *cmd, int lun) +static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); SCSIBus *bus; int ret; - int32_t len; + scsi_req_enqueue(req); if (cmd[0] != REQUEST_SENSE && - (lun != s->lun || (cmd[1] >> 5) != s->lun)) { - DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5); + (req->lun != s->lun || (cmd[1] >> 5) != s->lun)) { + DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5); s->sensebuf[0] = 0x70; s->sensebuf[1] = 0x00; @@ -338,18 +308,11 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, s->sensebuf[6] = 0x00; s->senselen = 7; s->driver_status = SG_ERR_DRIVER_SENSE; - bus = scsi_bus_from_device(d); - bus->ops->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION); + bus = scsi_bus_from_device(&s->qdev); + bus->ops->complete(req, SCSI_REASON_DONE, CHECK_CONDITION); return 0; } - r = scsi_find_request(s, tag); - if (r) { - BADF("Tag 0x%x already in use %p\n", tag, r); - scsi_cancel_io(d, tag); - } - r = scsi_new_request(d, tag, lun); - if (-1 == scsi_req_parse(&r->req, cmd)) { BADF("Unsupported command length, command %x\n", cmd[0]); scsi_req_dequeue(&r->req); @@ -379,10 +342,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete); if (ret == -1) { scsi_command_complete(r, -EINVAL); - scsi_req_unref(&r->req); - return 0; } - scsi_req_unref(&r->req); return 0; } @@ -397,13 +357,10 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, r->len = r->req.cmd.xfer; if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { r->len = 0; - len = -r->req.cmd.xfer; + return -r->req.cmd.xfer; } else { - len = r->req.cmd.xfer; + return r->req.cmd.xfer; } - - scsi_req_unref(&r->req); - return len; } static int get_blocksize(BlockDriverState *bdrv) @@ -477,6 +434,7 @@ static void scsi_generic_purge_requests(SCSIGenericState *s) bdrv_aio_cancel(r->req.aiocb); } scsi_req_dequeue(&r->req); + scsi_req_unref(&r->req); } } @@ -568,6 +526,7 @@ static SCSIDeviceInfo scsi_generic_info = { .qdev.reset = scsi_generic_reset, .init = scsi_generic_initfn, .destroy = scsi_destroy, + .alloc_req = scsi_new_request, .free_req = scsi_free_request, .send_command = scsi_send_command, .read_data = scsi_read_data, diff --git a/hw/scsi.h b/hw/scsi.h index a1d0e7451f..19bd1ae774 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -19,6 +19,7 @@ typedef struct SCSIBus SCSIBus; typedef struct SCSIBusOps SCSIBusOps; typedef struct SCSIDevice SCSIDevice; typedef struct SCSIDeviceInfo SCSIDeviceInfo; +typedef struct SCSIRequest SCSIRequest; enum SCSIXferMode { SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */ @@ -26,7 +27,7 @@ enum SCSIXferMode { SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */ }; -typedef struct SCSIRequest { +struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; uint32_t refcount; @@ -43,7 +44,7 @@ typedef struct SCSIRequest { BlockDriverAIOCB *aiocb; bool enqueued; QTAILQ_ENTRY(SCSIRequest) next; -} SCSIRequest; +}; struct SCSIDevice { @@ -66,17 +67,17 @@ struct SCSIDeviceInfo { DeviceInfo qdev; scsi_qdev_initfn init; void (*destroy)(SCSIDevice *s); + SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun); void (*free_req)(SCSIRequest *req); - int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf, - int lun); - void (*read_data)(SCSIDevice *s, uint32_t tag); - int (*write_data)(SCSIDevice *s, uint32_t tag); - void (*cancel_io)(SCSIDevice *s, uint32_t tag); - uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag); + int32_t (*send_command)(SCSIRequest *req, uint8_t *buf); + void (*read_data)(SCSIRequest *req); + int (*write_data)(SCSIRequest *req); + void (*cancel_io)(SCSIRequest *req); + uint8_t *(*get_buf)(SCSIRequest *req); }; struct SCSIBusOps { - void (*complete)(SCSIBus *bus, int reason, uint32_t tag, uint32_t arg); + void (*complete)(SCSIRequest *req, int reason, uint32_t arg); }; struct SCSIBus { @@ -103,7 +104,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, int scsi_bus_legacy_handle_cmdline(SCSIBus *bus); SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); -SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag); +void scsi_req_enqueue(SCSIRequest *req); void scsi_req_free(SCSIRequest *req); void scsi_req_dequeue(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index c183008e42..36dd7445af 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -75,6 +75,7 @@ typedef struct vscsi_req { /* SCSI request tracking */ SCSIDevice *sdev; + SCSIRequest *sreq; uint32_t qtag; /* qemu tag != srp tag */ int lun; int active; @@ -123,11 +124,16 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s) static void vscsi_put_req(VSCSIState *s, vscsi_req *req) { + if (req->sreq != NULL) { + scsi_req_unref(req->sreq); + } + req->sreq = NULL; req->active = 0; } -static vscsi_req *vscsi_find_req(VSCSIState *s, uint32_t tag) +static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req) { + uint32_t tag = req->tag; if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) { return NULL; } @@ -453,11 +459,11 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) cdb[4] = 96; cdb[5] = 0; req->sensing = 1; - n = sdev->info->send_command(sdev, req->qtag, cdb, req->lun); + n = sdev->info->send_command(req->sreq, cdb); dprintf("VSCSI: Queued request sense tag 0x%x\n", req->qtag); if (n < 0) { fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n"); - sdev->info->cancel_io(sdev, req->qtag); + sdev->info->cancel_io(req->sreq); vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); vscsi_put_req(s, req); @@ -465,24 +471,23 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) } else if (n == 0) { return; } - sdev->info->read_data(sdev, req->qtag); + sdev->info->read_data(req->sreq); } /* Callback to indicate that the SCSI layer has completed a transfer. */ -static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) +static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg) { - VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, bus->qbus.parent); - vscsi_req *req = vscsi_find_req(s, tag); + VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); + vscsi_req *req = vscsi_find_req(s, sreq); SCSIDevice *sdev; uint8_t *buf; int32_t res_in = 0, res_out = 0; int len, rc = 0; dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x arg=0x%x, req=%p\n", - reason, tag, arg, req); + reason, sreq->tag, arg, req); if (req == NULL) { - fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", tag); + fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag); return; } sdev = req->sdev; @@ -493,7 +498,7 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); vscsi_put_req(s, req); } else { - uint8_t *buf = sdev->info->get_buf(sdev, tag); + uint8_t *buf = sdev->info->get_buf(sreq); len = MIN(arg, SCSI_SENSE_BUF_SIZE); dprintf("VSCSI: Sense data, %d bytes:\n", len); @@ -505,7 +510,7 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, buf[12], buf[13], buf[14], buf[15]); memcpy(req->sense, buf, len); req->senselen = len; - sdev->info->read_data(sdev, req->qtag); + sdev->info->read_data(sreq); } return; } @@ -537,12 +542,12 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, * to write for writes (ie, how much is to be DMA'd) */ if (arg) { - buf = sdev->info->get_buf(sdev, tag); + buf = sdev->info->get_buf(sreq); rc = vscsi_srp_transfer_data(s, req, req->writing, buf, arg); } if (rc < 0) { fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc); - sdev->info->cancel_io(sdev, req->qtag); + sdev->info->cancel_io(sreq); vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); vscsi_put_req(s, req); @@ -552,9 +557,9 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, /* Start next chunk */ req->data_len -= rc; if (req->writing) { - sdev->info->write_data(sdev, req->qtag); + sdev->info->write_data(sreq); } else { - sdev->info->read_data(sdev, req->qtag); + sdev->info->read_data(sreq); } } @@ -644,7 +649,8 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) req->sdev = sdev; req->lun = lun; - n = sdev->info->send_command(sdev, req->qtag, srp->cmd.cdb, lun); + req->sreq = sdev->info->alloc_req(sdev, req->qtag, lun); + n = sdev->info->send_command(req->sreq, srp->cmd.cdb); dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n", req->qtag, srp->cmd.cdb[0], id, lun, n); @@ -662,10 +668,10 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) /* Get transfer direction and initiate transfer */ if (n > 0) { req->data_len = n; - sdev->info->read_data(sdev, req->qtag); + sdev->info->read_data(req->sreq); } else if (n < 0) { req->data_len = -n; - sdev->info->write_data(sdev, req->qtag); + sdev->info->write_data(req->sreq); } /* Don't touch req here, it may have been recycled already */ diff --git a/hw/usb-msd.c b/hw/usb-msd.c index c0a381abb3..8e6d48bf25 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -48,6 +48,7 @@ typedef struct { uint32_t data_len; uint32_t residue; uint32_t tag; + SCSIRequest *req; SCSIBus bus; BlockConf conf; SCSIDevice *scsi_dev; @@ -190,9 +191,9 @@ static void usb_msd_copy_data(MSDState *s) s->data_len -= len; if (s->scsi_len == 0 || s->data_len == 0) { if (s->mode == USB_MSDM_DATAIN) { - s->scsi_dev->info->read_data(s->scsi_dev, s->tag); + s->scsi_dev->info->read_data(s->req); } else if (s->mode == USB_MSDM_DATAOUT) { - s->scsi_dev->info->write_data(s->scsi_dev, s->tag); + s->scsi_dev->info->write_data(s->req); } } } @@ -211,14 +212,13 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p) memcpy(p->data, &csw, len); } -static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) +static void usb_msd_command_complete(SCSIRequest *req, int reason, uint32_t arg) { - MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent); + MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; - if (tag != s->tag) { - fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag); + if (req->tag != s->tag) { + fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag); } if (reason == SCSI_REASON_DONE) { DPRINTF("Command complete %d\n", arg); @@ -245,10 +245,12 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag, } else if (s->data_len == 0) { s->mode = USB_MSDM_CSW; } + scsi_req_unref(req); + s->req = NULL; return; } s->scsi_len = arg; - s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag); + s->scsi_buf = s->scsi_dev->info->get_buf(req); if (p) { usb_msd_copy_data(s); if (s->usb_len == 0) { @@ -316,7 +318,7 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value, static void usb_msd_cancel_io(USBPacket *p, void *opaque) { MSDState *s = opaque; - s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag); + s->scsi_dev->info->cancel_io(s->req); s->packet = NULL; s->scsi_len = 0; } @@ -365,14 +367,15 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->residue = 0; s->scsi_len = 0; - s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0); + s->req = s->scsi_dev->info->alloc_req(s->scsi_dev, s->tag, 0); + s->scsi_dev->info->send_command(s->req, cbw.cmd); /* ??? Should check that USB and SCSI data transfer directions match. */ if (s->residue == 0) { if (s->mode == USB_MSDM_DATAIN) { - s->scsi_dev->info->read_data(s->scsi_dev, s->tag); + s->scsi_dev->info->read_data(s->req); } else if (s->mode == USB_MSDM_DATAOUT) { - s->scsi_dev->info->write_data(s->scsi_dev, s->tag); + s->scsi_dev->info->write_data(s->req); } } ret = len; -- cgit v1.2.3-55-g7522 From c557e889156c5f5da23b4b047aea804aefce4982 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Apr 2011 18:47:12 +0200 Subject: scsi: commonize purging requests The code for canceling requests upon reset is already the same. Clean it up and move it to scsi-bus.c. Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/scsi-bus.c | 12 ++++++++++++ hw/scsi-disk.c | 18 ++---------------- hw/scsi-generic.c | 18 ++---------------- hw/scsi.h | 1 + 4 files changed, 17 insertions(+), 32 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index c7748d0ead..c1e94fac9e 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -549,6 +549,18 @@ void scsi_req_complete(SCSIRequest *req) scsi_req_unref(req); } +void scsi_device_purge_requests(SCSIDevice *sdev) +{ + SCSIRequest *req; + + while (!QTAILQ_EMPTY(&sdev->requests)) { + req = QTAILQ_FIRST(&sdev->requests); + sdev->info->cancel_io(req); + scsi_req_dequeue(req); + scsi_req_unref(req); + } +} + static char *scsibus_get_fw_dev_path(DeviceState *dev) { SCSIDevice *d = (SCSIDevice*)dev; diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index f7c09c9b79..38fbb05ab8 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -1147,26 +1147,12 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) return len; } -static void scsi_disk_purge_requests(SCSIDiskState *s) -{ - SCSIDiskReq *r; - - while (!QTAILQ_EMPTY(&s->qdev.requests)) { - r = DO_UPCAST(SCSIDiskReq, req, QTAILQ_FIRST(&s->qdev.requests)); - if (r->req.aiocb) { - bdrv_aio_cancel(r->req.aiocb); - } - scsi_req_dequeue(&r->req); - scsi_req_unref(&r->req); - } -} - static void scsi_disk_reset(DeviceState *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev); uint64_t nb_sectors; - scsi_disk_purge_requests(s); + scsi_device_purge_requests(&s->qdev); bdrv_get_geometry(s->bs, &nb_sectors); nb_sectors /= s->cluster_size; @@ -1180,7 +1166,7 @@ static void scsi_destroy(SCSIDevice *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - scsi_disk_purge_requests(s); + scsi_device_purge_requests(&s->qdev); blockdev_mark_auto_del(s->qdev.conf.bs); } diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 3740432d9e..72c4cc702d 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -424,32 +424,18 @@ static int get_stream_blocksize(BlockDriverState *bdrv) return (buf[9] << 16) | (buf[10] << 8) | buf[11]; } -static void scsi_generic_purge_requests(SCSIGenericState *s) -{ - SCSIGenericReq *r; - - while (!QTAILQ_EMPTY(&s->qdev.requests)) { - r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests)); - if (r->req.aiocb) { - bdrv_aio_cancel(r->req.aiocb); - } - scsi_req_dequeue(&r->req); - scsi_req_unref(&r->req); - } -} - static void scsi_generic_reset(DeviceState *dev) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev); - scsi_generic_purge_requests(s); + scsi_device_purge_requests(&s->qdev); } static void scsi_destroy(SCSIDevice *d) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - scsi_generic_purge_requests(s); + scsi_device_purge_requests(&s->qdev); blockdev_mark_auto_del(s->qdev.conf.bs); } diff --git a/hw/scsi.h b/hw/scsi.h index 19bd1ae774..f1d8888aca 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -114,5 +114,6 @@ int scsi_req_parse(SCSIRequest *req, uint8_t *buf); void scsi_req_print(SCSIRequest *req); void scsi_req_data(SCSIRequest *req, int len); void scsi_req_complete(SCSIRequest *req); +void scsi_device_purge_requests(SCSIDevice *sdev); #endif -- cgit v1.2.3-55-g7522 From 94d3f98a3f3caddd7875f9a11776daeb84962a7b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Apr 2011 22:53:08 +0200 Subject: scsi: introduce scsi_req_cancel This is for when the request must be dropped in the void, but still memory should be freed. To this end, the devices register a second callback in SCSIBusOps. Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/esp.c | 16 ++++++++++++++-- hw/lsi53c895a.c | 30 +++++++++++++++++++++++++----- hw/scsi-bus.c | 17 ++++++++++++++--- hw/scsi-disk.c | 1 - hw/scsi-generic.c | 1 - hw/scsi.h | 2 ++ hw/spapr_vscsi.c | 11 ++++++++++- hw/usb-msd.c | 19 +++++++++++++++---- 8 files changed, 80 insertions(+), 17 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/esp.c b/hw/esp.c index 57061ca8c4..f2677dc88e 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -188,6 +188,17 @@ static void esp_dma_enable(void *opaque, int irq, int level) } } +static void esp_request_cancelled(SCSIRequest *req) +{ + ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); + + if (req == s->current_req) { + scsi_req_unref(s->current_req); + s->current_req = NULL; + s->current_dev = NULL; + } +} + static uint32_t get_cmd(ESPState *s, uint8_t *buf) { uint32_t dmalen; @@ -210,7 +221,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) if (s->current_dev) { /* Started a new command before the old one finished. Cancel it. */ - s->current_dev->info->cancel_io(s->current_req); + scsi_req_cancel(s->current_req); s->async_len = 0; } @@ -720,7 +731,8 @@ void esp_init(target_phys_addr_t espaddr, int it_shift, } static const struct SCSIBusOps esp_scsi_ops = { - .complete = esp_command_complete + .complete = esp_command_complete, + .cancel = esp_request_cancelled }; static int esp_init1(SysBusDevice *dev) diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 56234f836c..bca889a880 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -664,6 +664,26 @@ static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag) return NULL; } +static void lsi_request_cancelled(SCSIRequest *req) +{ + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); + lsi_request *p; + + if (s->current && req == s->current->req) { + scsi_req_unref(req); + qemu_free(s->current); + s->current = NULL; + return; + } + + p = lsi_find_by_tag(s, req->tag); + if (p) { + QTAILQ_REMOVE(&s->queue, p, next); + scsi_req_unref(req); + qemu_free(p); + } +} + /* Record that data is available for a queued command. Returns zero if the device was reselected, nonzero if the IO is deferred. */ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg) @@ -931,7 +951,7 @@ static void lsi_do_msgout(LSIState *s) /* The ABORT TAG message clears the current I/O process only. */ DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag); if (current_req) { - current_dev->info->cancel_io(current_req->req); + scsi_req_cancel(current_req->req); } lsi_disconnect(s); break; @@ -956,7 +976,7 @@ static void lsi_do_msgout(LSIState *s) /* clear the current I/O process */ if (s->current) { - current_dev->info->cancel_io(s->current->req); + scsi_req_cancel(s->current->req); } /* As the current implemented devices scsi_disk and scsi_generic @@ -969,8 +989,7 @@ static void lsi_do_msgout(LSIState *s) id = current_tag & 0x0000ff00; QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) { if ((p->tag & 0x0000ff00) == id) { - current_dev->info->cancel_io(p->req); - QTAILQ_REMOVE(&s->queue, p, next); + scsi_req_cancel(p->req); } } @@ -2227,7 +2246,8 @@ static int lsi_scsi_uninit(PCIDevice *d) } static const struct SCSIBusOps lsi_scsi_ops = { - .complete = lsi_command_complete + .complete = lsi_command_complete, + .cancel = lsi_request_cancelled }; static int lsi_scsi_init(PCIDevice *dev) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index d6a055fa54..fd1d60fdbb 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -549,6 +549,19 @@ void scsi_req_complete(SCSIRequest *req) scsi_req_unref(req); } +void scsi_req_cancel(SCSIRequest *req) +{ + if (req->dev && req->dev->info->cancel_io) { + req->dev->info->cancel_io(req); + } + scsi_req_ref(req); + scsi_req_dequeue(req); + if (req->bus->ops->cancel) { + req->bus->ops->cancel(req); + } + scsi_req_unref(req); +} + void scsi_req_abort(SCSIRequest *req, int status) { req->status = status; @@ -564,9 +577,7 @@ void scsi_device_purge_requests(SCSIDevice *sdev) while (!QTAILQ_EMPTY(&sdev->requests)) { req = QTAILQ_FIRST(&sdev->requests); - sdev->info->cancel_io(req); - scsi_req_dequeue(req); - scsi_req_unref(req); + scsi_req_cancel(req); } } diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 38fbb05ab8..08633db169 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -143,7 +143,6 @@ static void scsi_cancel_io(SCSIRequest *req) bdrv_aio_cancel(r->req.aiocb); } r->req.aiocb = NULL; - scsi_req_dequeue(&r->req); } static void scsi_read_complete(void * opaque, int ret) diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 72c4cc702d..c008e9c59f 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -121,7 +121,6 @@ static void scsi_cancel_io(SCSIRequest *req) bdrv_aio_cancel(r->req.aiocb); } r->req.aiocb = NULL; - scsi_req_dequeue(&r->req); } static int execute_command(BlockDriverState *bdrv, diff --git a/hw/scsi.h b/hw/scsi.h index 970e8129c9..e2dc7cb5e1 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -78,6 +78,7 @@ struct SCSIDeviceInfo { struct SCSIBusOps { void (*complete)(SCSIRequest *req, int reason, uint32_t arg); + void (*cancel)(SCSIRequest *req); }; struct SCSIBus { @@ -115,6 +116,7 @@ void scsi_req_print(SCSIRequest *req); void scsi_req_data(SCSIRequest *req, int len); void scsi_req_complete(SCSIRequest *req); void scsi_req_abort(SCSIRequest *req, int status); +void scsi_req_cancel(SCSIRequest *req); void scsi_device_purge_requests(SCSIDevice *sdev); #endif diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 24cebd1843..5aaf95b974 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -559,6 +559,14 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg) } } +static void vscsi_request_cancelled(SCSIRequest *sreq) +{ + VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); + vscsi_req *req = vscsi_find_req(s, sreq); + + vscsi_put_req(s, req); +} + static void vscsi_process_login(VSCSIState *s, vscsi_req *req) { union viosrp_iu *iu = &req->iu; @@ -910,7 +918,8 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data) } static const struct SCSIBusOps vscsi_scsi_ops = { - .complete = vscsi_command_complete + .complete = vscsi_command_complete, + .cancel = vscsi_request_cancelled }; static int spapr_vscsi_init(VIOsPAPRDevice *dev) diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 8e6d48bf25..ce926828ec 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -264,6 +264,18 @@ static void usb_msd_command_complete(SCSIRequest *req, int reason, uint32_t arg) } } +static void usb_msd_request_cancelled(SCSIRequest *req) +{ + MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); + + if (req == s->req) { + scsi_req_unref(s->req); + s->req = NULL; + s->packet = NULL; + s->scsi_len = 0; + } +} + static void usb_msd_handle_reset(USBDevice *dev) { MSDState *s = (MSDState *)dev; @@ -318,9 +330,7 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value, static void usb_msd_cancel_io(USBPacket *p, void *opaque) { MSDState *s = opaque; - s->scsi_dev->info->cancel_io(s->req); - s->packet = NULL; - s->scsi_len = 0; + scsi_req_cancel(s->req); } static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) @@ -491,7 +501,8 @@ static void usb_msd_password_cb(void *opaque, int err) } static const struct SCSIBusOps usb_msd_scsi_ops = { - .complete = usb_msd_command_complete + .complete = usb_msd_command_complete, + .cancel = usb_msd_request_cancelled }; static int usb_msd_initfn(USBDevice *dev) -- cgit v1.2.3-55-g7522 From 2b8b3bb9dd1c67d8296b7610dca909539e25a196 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Apr 2011 17:06:17 +0200 Subject: scsi: use scsi_req_complete Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/scsi-generic.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index c008e9c59f..229d24ff1f 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -290,7 +290,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - SCSIBus *bus; int ret; scsi_req_enqueue(req); @@ -307,8 +306,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) s->sensebuf[6] = 0x00; s->senselen = 7; s->driver_status = SG_ERR_DRIVER_SENSE; - bus = scsi_bus_from_device(&s->qdev); - bus->ops->complete(req, SCSI_REASON_DONE, CHECK_CONDITION); + r->req.status = CHECK_CONDITION; + scsi_req_complete(&r->req); return 0; } -- cgit v1.2.3-55-g7522 From a1f0cce2ac0243572ff72aa561da67fe3766a395 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 18 Apr 2011 12:53:14 +0200 Subject: scsi: Update sense code handling The SCSI spec has a quite detailed list of sense codes available. It even mandates the use of specific ones for some failure cases. The current implementation just has one type of generic error which is actually a violation of the spec in certain cases. This patch introduces various predefined sense codes to have the sense code reporting more in line with the spec. On top of Hannes's patch I fixed the reply to REQUEST SENSE commands with DESC=0 and a small (<18) length. Signed-off-by: Hannes Reinecke Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/scsi-bus.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- hw/scsi-disk.c | 82 ++++++++++++++++++++++++------------------------- hw/scsi-generic.c | 63 ++++++++++++++++++++++++-------------- hw/scsi.h | 39 +++++++++++++++++++++++- 4 files changed, 208 insertions(+), 67 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index fd1d60fdbb..d322f3a326 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -154,7 +154,7 @@ void scsi_req_enqueue(SCSIRequest *req) QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); } -void scsi_req_dequeue(SCSIRequest *req) +static void scsi_req_dequeue(SCSIRequest *req) { trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); if (req->enqueued) { @@ -391,6 +391,95 @@ int scsi_req_parse(SCSIRequest *req, uint8_t *buf) return 0; } +/* + * Predefined sense codes + */ + +/* No sense data available */ +const struct SCSISense sense_code_NO_SENSE = { + .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00 +}; + +/* LUN not ready, Manual intervention required */ +const struct SCSISense sense_code_LUN_NOT_READY = { + .key = NOT_READY, .asc = 0x04, .ascq = 0x03 +}; + +/* LUN not ready, Medium not present */ +const struct SCSISense sense_code_NO_MEDIUM = { + .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 +}; + +/* Hardware error, internal target failure */ +const struct SCSISense sense_code_TARGET_FAILURE = { + .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00 +}; + +/* Illegal request, invalid command operation code */ +const struct SCSISense sense_code_INVALID_OPCODE = { + .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00 +}; + +/* Illegal request, LBA out of range */ +const struct SCSISense sense_code_LBA_OUT_OF_RANGE = { + .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00 +}; + +/* Illegal request, Invalid field in CDB */ +const struct SCSISense sense_code_INVALID_FIELD = { + .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00 +}; + +/* Illegal request, LUN not supported */ +const struct SCSISense sense_code_LUN_NOT_SUPPORTED = { + .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00 +}; + +/* Command aborted, I/O process terminated */ +const struct SCSISense sense_code_IO_ERROR = { + .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 +}; + +/* Command aborted, I_T Nexus loss occurred */ +const struct SCSISense sense_code_I_T_NEXUS_LOSS = { + .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07 +}; + +/* Command aborted, Logical Unit failure */ +const struct SCSISense sense_code_LUN_FAILURE = { + .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 +}; + +/* + * scsi_build_sense + * + * Build a sense buffer + */ +int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed) +{ + if (!fixed && len < 8) { + return 0; + } + + memset(buf, 0, len); + if (fixed) { + /* Return fixed format sense buffer */ + buf[0] = 0xf0; + buf[2] = sense.key; + buf[7] = 7; + buf[12] = sense.asc; + buf[13] = sense.ascq; + return MIN(len, 18); + } else { + /* Return descriptor format sense buffer */ + buf[0] = 0x72; + buf[1] = sense.key; + buf[2] = sense.asc; + buf[3] = sense.ascq; + return 8; + } +} + static const char *scsi_command_name(uint8_t cmd) { static const char *names[] = { diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 08633db169..63aa8f1138 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -49,10 +49,6 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) typedef struct SCSIDiskState SCSIDiskState; -typedef struct SCSISense { - uint8_t key; -} SCSISense; - typedef struct SCSIDiskReq { SCSIRequest req; /* ??? We should probably keep track of whether the data transfer is @@ -111,24 +107,19 @@ static void scsi_disk_clear_sense(SCSIDiskState *s) memset(&s->sense, 0, sizeof(s->sense)); } -static void scsi_disk_set_sense(SCSIDiskState *s, uint8_t key) -{ - s->sense.key = key; -} - -static void scsi_req_set_status(SCSIDiskReq *r, int status, int sense_code) +static void scsi_req_set_status(SCSIDiskReq *r, int status, SCSISense sense) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); r->req.status = status; - scsi_disk_set_sense(s, sense_code); + s->sense = sense; } /* Helper function for command completion. */ -static void scsi_command_complete(SCSIDiskReq *r, int status, int sense) +static void scsi_command_complete(SCSIDiskReq *r, int status, SCSISense sense) { - DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", - r->req.tag, status, sense); + DPRINTF("Command complete tag=0x%x status=%d sense=%d/%d/%d\n", + r->req.tag, status, sense.key, sense.asc, sense.ascq); scsi_req_set_status(r, status, sense); scsi_req_complete(&r->req); } @@ -182,7 +173,7 @@ static void scsi_read_data(SCSIRequest *req) } DPRINTF("Read sector_count=%d\n", r->sector_count); if (r->sector_count == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); return; } @@ -225,8 +216,13 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) if (type == SCSI_REQ_STATUS_RETRY_READ) { scsi_req_data(&r->req, 0); } - scsi_command_complete(r, CHECK_CONDITION, - HARDWARE_ERROR); + if (error == ENOMEM) { + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(TARGET_FAILURE)); + } else { + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(IO_ERROR)); + } bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); } @@ -251,7 +247,7 @@ static void scsi_write_complete(void * opaque, int ret) r->sector += n; r->sector_count -= n; if (r->sector_count == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } else { len = r->sector_count * 512; if (len > SCSI_DMA_BUF_SIZE) { @@ -278,7 +274,7 @@ static int scsi_write_data(SCSIRequest *req) r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n, scsi_write_complete, r); if (r->req.aiocb == NULL) { - scsi_write_complete(r, -EIO); + scsi_write_complete(r, -ENOMEM); } } else { /* Invoke completion routine to fetch data from host. */ @@ -316,7 +312,7 @@ static void scsi_dma_restart_bh(void *opaque) case SCSI_REQ_STATUS_RETRY_FLUSH: ret = scsi_disk_emulate_command(r, r->iov.iov_base); if (ret == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } } } @@ -815,19 +811,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) case REQUEST_SENSE: if (req->cmd.xfer < 4) goto illegal_request; - memset(outbuf, 0, 4); - buflen = 4; - if (s->sense.key == NOT_READY && req->cmd.xfer >= 18) { - memset(outbuf, 0, 18); - buflen = 18; - outbuf[7] = 10; - /* asc 0x3a, ascq 0: Medium not present */ - outbuf[12] = 0x3a; - outbuf[13] = 0; - } - outbuf[0] = 0xf0; - outbuf[1] = 0; - outbuf[2] = s->sense.key; + buflen = scsi_build_sense(s->sense, outbuf, req->cmd.xfer, + req->cmd.xfer > 13); scsi_disk_clear_sense(s); break; case INQUIRY: @@ -965,17 +950,22 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) } break; default: - goto illegal_request; + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return -1; } - scsi_req_set_status(r, GOOD, NO_SENSE); + scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE)); return buflen; not_ready: - scsi_command_complete(r, CHECK_CONDITION, NOT_READY); + if (!bdrv_is_inserted(s->bs)) { + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(NO_MEDIUM)); + } else { + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_READY)); + } return -1; illegal_request: - scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD)); return -1; } @@ -1002,7 +992,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) if (scsi_req_parse(&r->req, buf) != 0) { BADF("Unsupported command length, command %x\n", command); - goto fail; + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return 0; } #ifdef DEBUG_SCSI { @@ -1017,8 +1008,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) if (req->lun || buf[1] >> 5) { /* Only LUN 0 supported. */ DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : buf[1] >> 5); - if (command != REQUEST_SENSE && command != INQUIRY) - goto fail; + if (command != REQUEST_SENSE && command != INQUIRY) { + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(LUN_NOT_SUPPORTED)); + return 0; + } } switch (command) { case TEST_UNIT_READY: @@ -1126,15 +1120,17 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) break; default: DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return 0; fail: - scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD)); return 0; illegal_lba: - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LBA_OUT_OF_RANGE)); return 0; } if (r->sector_count == 0 && r->iov.iov_len == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } len = r->sector_count * 512 + r->iov.iov_len; if (is_write) { diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 229d24ff1f..64cbe8b8c0 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -66,6 +66,19 @@ struct SCSIGenericState uint8_t senselen; }; +static void scsi_set_sense(SCSIGenericState *s, SCSISense sense) +{ + s->senselen = scsi_build_sense(sense, s->sensebuf, SCSI_SENSE_BUF_SIZE, 0); + s->driver_status = SG_ERR_DRIVER_SENSE; +} + +static void scsi_clear_sense(SCSIGenericState *s) +{ + memset(s->sensebuf, 0, SCSI_SENSE_BUF_SIZE); + s->senselen = 0; + s->driver_status = 0; +} + static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) { SCSIRequest *req; @@ -92,9 +105,22 @@ static void scsi_command_complete(void *opaque, int ret) if (s->driver_status & SG_ERR_DRIVER_SENSE) s->senselen = r->io_header.sb_len_wr; - if (ret != 0) - r->req.status = BUSY; - else { + if (ret != 0) { + switch (ret) { + case -EINVAL: + r->req.status = CHECK_CONDITION; + scsi_set_sense(s, SENSE_CODE(INVALID_FIELD)); + break; + case -ENOMEM: + r->req.status = CHECK_CONDITION; + scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE)); + break; + default: + r->req.status = CHECK_CONDITION; + scsi_set_sense(s, SENSE_CODE(IO_ERROR)); + break; + } + } else { if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { r->req.status = BUSY; BADF("Driver Timeout\n"); @@ -144,7 +170,7 @@ static int execute_command(BlockDriverState *bdrv, r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r); if (r->req.aiocb == NULL) { BADF("execute_command: read failed !\n"); - return -1; + return -ENOMEM; } return 0; @@ -198,12 +224,14 @@ static void scsi_read_data(SCSIRequest *req) r->buf[0], r->buf[1], r->buf[2], r->buf[3], r->buf[4], r->buf[5], r->buf[6], r->buf[7]); scsi_req_data(&r->req, s->senselen); + /* Clear sensebuf after REQUEST_SENSE */ + scsi_clear_sense(s); return; } ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete); - if (ret == -1) { - scsi_command_complete(r, -EINVAL); + if (ret < 0) { + scsi_command_complete(r, ret); return; } } @@ -246,8 +274,8 @@ static int scsi_write_data(SCSIRequest *req) } ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete); - if (ret == -1) { - scsi_command_complete(r, -EINVAL); + if (ret < 0) { + scsi_command_complete(r, ret); return 1; } @@ -296,16 +324,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) if (cmd[0] != REQUEST_SENSE && (req->lun != s->lun || (cmd[1] >> 5) != s->lun)) { DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5); - - s->sensebuf[0] = 0x70; - s->sensebuf[1] = 0x00; - s->sensebuf[2] = ILLEGAL_REQUEST; - s->sensebuf[3] = 0x00; - s->sensebuf[4] = 0x00; - s->sensebuf[5] = 0x00; - s->sensebuf[6] = 0x00; - s->senselen = 7; - s->driver_status = SG_ERR_DRIVER_SENSE; + scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED)); r->req.status = CHECK_CONDITION; scsi_req_complete(&r->req); return 0; @@ -313,8 +332,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) if (-1 == scsi_req_parse(&r->req, cmd)) { BADF("Unsupported command length, command %x\n", cmd[0]); - scsi_req_dequeue(&r->req); - scsi_req_unref(&r->req); + scsi_command_complete(r, -EINVAL); return 0; } scsi_req_fixup(&r->req); @@ -338,8 +356,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) r->buflen = 0; r->buf = NULL; ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete); - if (ret == -1) { - scsi_command_complete(r, -EINVAL); + if (ret < 0) { + scsi_command_complete(r, ret); + return 0; } return 0; } diff --git a/hw/scsi.h b/hw/scsi.h index e2dc7cb5e1..7a7c9efb57 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -27,6 +27,12 @@ enum SCSIXferMode { SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */ }; +typedef struct SCSISense { + uint8_t key; + uint8_t asc; + uint8_t ascq; +} SCSISense; + struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; @@ -104,10 +110,41 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, int unit, bool removable); int scsi_bus_legacy_handle_cmdline(SCSIBus *bus); +/* + * Predefined sense codes + */ + +/* No sense data available */ +extern const struct SCSISense sense_code_NO_SENSE; +/* LUN not ready, Manual intervention required */ +extern const struct SCSISense sense_code_LUN_NOT_READY; +/* LUN not ready, Medium not present */ +extern const struct SCSISense sense_code_NO_MEDIUM; +/* Hardware error, internal target failure */ +extern const struct SCSISense sense_code_TARGET_FAILURE; +/* Illegal request, invalid command operation code */ +extern const struct SCSISense sense_code_INVALID_OPCODE; +/* Illegal request, LBA out of range */ +extern const struct SCSISense sense_code_LBA_OUT_OF_RANGE; +/* Illegal request, Invalid field in CDB */ +extern const struct SCSISense sense_code_INVALID_FIELD; +/* Illegal request, LUN not supported */ +extern const struct SCSISense sense_code_LUN_NOT_SUPPORTED; +/* Command aborted, I/O process terminated */ +extern const struct SCSISense sense_code_IO_ERROR; +/* Command aborted, I_T Nexus loss occurred */ +extern const struct SCSISense sense_code_I_T_NEXUS_LOSS; +/* Command aborted, Logical Unit failure */ +extern const struct SCSISense sense_code_LUN_FAILURE; + +#define SENSE_CODE(x) sense_code_ ## x + +int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed); +int scsi_sense_valid(SCSISense sense); + SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); void scsi_req_enqueue(SCSIRequest *req); void scsi_req_free(SCSIRequest *req); -void scsi_req_dequeue(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); void scsi_req_unref(SCSIRequest *req); -- cgit v1.2.3-55-g7522 From fc4f0754c775d4b5e0fb90e503f7e505f62fb8ed Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Apr 2011 19:07:23 +0200 Subject: scsi: do not call send_command directly Move the common part of scsi-disk.c and scsi-generic.c to the SCSI layer. Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/esp.c | 2 +- hw/lsi53c895a.c | 2 +- hw/scsi-bus.c | 9 ++++++++- hw/scsi-disk.c | 1 - hw/scsi-generic.c | 1 - hw/scsi.h | 2 +- hw/spapr_vscsi.c | 4 ++-- hw/usb-msd.c | 2 +- 8 files changed, 14 insertions(+), 9 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/esp.c b/hw/esp.c index f2677dc88e..238422a81d 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -245,7 +245,7 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) DPRINTF("do_busid_cmd: busid 0x%x\n", busid); lun = busid & 7; s->current_req = s->current_dev->info->alloc_req(s->current_dev, 0, lun); - datalen = s->current_dev->info->send_command(s->current_req, buf); + datalen = scsi_req_enqueue(s->current_req, buf); s->ti_size = datalen; if (datalen != 0) { s->rregs[ESP_RSTAT] = STAT_TC; diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index bca889a880..185622dc94 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -791,7 +791,7 @@ static void lsi_do_command(LSIState *s) s->current->req = dev->info->alloc_req(dev, s->current->tag, s->current_lun); - n = dev->info->send_command(s->current->req, buf); + n = scsi_req_enqueue(s->current->req, buf); if (n > 0) { lsi_set_phase(s, PHASE_DI); dev->info->read_data(s->current->req); diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index d322f3a326..2e6e7c89b9 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -146,12 +146,19 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l return req; } -void scsi_req_enqueue(SCSIRequest *req) +int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf) { + int32_t rc; + assert(!req->enqueued); scsi_req_ref(req); req->enqueued = true; QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); + + scsi_req_ref(req); + rc = req->dev->info->send_command(req, buf); + scsi_req_unref(req); + return rc; } static void scsi_req_dequeue(SCSIRequest *req) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 63aa8f1138..adee8feb91 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -984,7 +984,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) uint8_t *outbuf; int rc; - scsi_req_enqueue(req); command = buf[0]; outbuf = (uint8_t *)r->iov.iov_base; is_write = 0; diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 64cbe8b8c0..90f2a4af1b 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -320,7 +320,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); int ret; - scsi_req_enqueue(req); if (cmd[0] != REQUEST_SENSE && (req->lun != s->lun || (cmd[1] >> 5) != s->lun)) { DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5); diff --git a/hw/scsi.h b/hw/scsi.h index 7a7c9efb57..839bc0b3dd 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -143,7 +143,7 @@ int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed); int scsi_sense_valid(SCSISense sense); SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); -void scsi_req_enqueue(SCSIRequest *req); +int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf); void scsi_req_free(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); void scsi_req_unref(SCSIRequest *req); diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 5aaf95b974..54fd4e891f 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -459,7 +459,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) cdb[4] = 96; cdb[5] = 0; req->sensing = 1; - n = sdev->info->send_command(req->sreq, cdb); + n = scsi_req_enqueue(req->sreq, cdb); dprintf("VSCSI: Queued request sense tag 0x%x\n", req->qtag); if (n < 0) { fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n"); @@ -654,7 +654,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) req->sdev = sdev; req->lun = lun; req->sreq = sdev->info->alloc_req(sdev, req->qtag, lun); - n = sdev->info->send_command(req->sreq, srp->cmd.cdb); + n = scsi_req_enqueue(req->sreq, srp->cmd.cdb); dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n", req->qtag, srp->cmd.cdb[0], id, lun, n); diff --git a/hw/usb-msd.c b/hw/usb-msd.c index ce926828ec..ccfae61247 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -378,7 +378,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) s->residue = 0; s->scsi_len = 0; s->req = s->scsi_dev->info->alloc_req(s->scsi_dev, s->tag, 0); - s->scsi_dev->info->send_command(s->req, cbw.cmd); + scsi_req_enqueue(s->req, cbw.cmd); /* ??? Should check that USB and SCSI data transfer directions match. */ if (s->residue == 0) { -- cgit v1.2.3-55-g7522 From 74382217ca8f25a530c9f63e6b523e6259d7719a Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 18 Apr 2011 13:36:02 +0200 Subject: scsi: Implement 'get_sense' callback The get_sense callback copies existing sense information into the provided buffer. This is required if sense information should be transferred together with the command response. Signed-off-by: Hannes Reinecke Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/scsi-bus.c | 9 +++++++++ hw/scsi-disk.c | 11 +++++++++++ hw/scsi-generic.c | 18 ++++++++++++++++++ hw/scsi.h | 2 ++ hw/spapr_vscsi.c | 10 +++++++++- 5 files changed, 49 insertions(+), 1 deletion(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 686d59d818..ae16a2d6f7 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -156,6 +156,15 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req) return req->dev->info->get_buf(req); } +int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) +{ + if (req->dev->info->get_sense) { + return req->dev->info->get_sense(req, buf, len); + } else { + return 0; + } +} + int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf) { int32_t rc; diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index adee8feb91..9567c7c0c8 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -340,6 +340,14 @@ static uint8_t *scsi_get_buf(SCSIRequest *req) return (uint8_t *)r->iov.iov_base; } +/* Copy sense information into the provided buffer */ +static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + + return scsi_build_sense(s->sense, outbuf, len, len > 14); +} + static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); @@ -1257,6 +1265,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), @@ -1277,6 +1286,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_END_OF_LIST(), @@ -1296,6 +1306,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 90f2a4af1b..fc015e0755 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -79,6 +79,23 @@ static void scsi_clear_sense(SCSIGenericState *s) s->driver_status = 0; } +static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) +{ + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); + int size = SCSI_SENSE_BUF_SIZE; + + if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) { + size = scsi_build_sense(SENSE_CODE(NO_SENSE), s->sensebuf, + SCSI_SENSE_BUF_SIZE, 0); + } + if (size > len) { + size = len; + } + memcpy(outbuf, s->sensebuf, size); + + return size; +} + static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) { SCSIRequest *req; @@ -535,6 +552,7 @@ static SCSIDeviceInfo scsi_generic_info = { .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, .qdev.props = (Property[]) { DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/scsi.h b/hw/scsi.h index edf68283c1..5730faa553 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -80,6 +80,7 @@ struct SCSIDeviceInfo { int (*write_data)(SCSIRequest *req); void (*cancel_io)(SCSIRequest *req); uint8_t *(*get_buf)(SCSIRequest *req); + int (*get_sense)(SCSIRequest *req, uint8_t *buf, int len); }; struct SCSIBusOps { @@ -155,6 +156,7 @@ void scsi_req_continue(SCSIRequest *req); void scsi_req_data(SCSIRequest *req, int len); void scsi_req_complete(SCSIRequest *req); uint8_t *scsi_req_get_buf(SCSIRequest *req); +int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len); void scsi_req_abort(SCSIRequest *req, int status); void scsi_req_cancel(SCSIRequest *req); void scsi_device_purge_requests(SCSIDevice *sdev); diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 762a22ec4a..bae670a377 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -450,6 +450,15 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) uint8_t *cdb = req->iu.srp.cmd.cdb; int n; + n = scsi_req_get_sense(req->sreq, req->sense, sizeof(req->sense)); + if (n) { + req->senselen = n; + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + vscsi_put_req(s, req); + return; + } + + dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n"); cdb[0] = 3; cdb[1] = 0; cdb[2] = 0; @@ -522,7 +531,6 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg) } vscsi_send_rsp(s, req, 0, res_in, res_out); } else if (arg == CHECK_CONDITION) { - dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n"); vscsi_send_request_sense(s, req); return; } else { -- cgit v1.2.3-55-g7522 From 42741212ebe703a5b9273475e7c65829b8fa2e51 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 22 Apr 2011 09:39:16 +0200 Subject: scsi: make write_data return void The return value is unused anyway. Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/scsi-disk.c | 6 ++---- hw/scsi-generic.c | 7 ++----- hw/scsi.h | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index f3eba52850..e0c384f148 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -269,7 +269,7 @@ static void scsi_write_complete(void * opaque, int ret) } } -static int scsi_write_data(SCSIRequest *req) +static void scsi_write_data(SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); @@ -281,7 +281,7 @@ static int scsi_write_data(SCSIRequest *req) if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { DPRINTF("Data transfer direction invalid\n"); scsi_write_complete(r, -EINVAL); - return 0; + return; } n = r->iov.iov_len / 512; @@ -296,8 +296,6 @@ static int scsi_write_data(SCSIRequest *req) /* Invoke completion routine to fetch data from host. */ scsi_write_complete(r, 0); } - - return 0; } static void scsi_dma_restart_bh(void *opaque) diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index fc015e0755..579bab9e7c 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -277,7 +277,7 @@ static void scsi_write_complete(void * opaque, int ret) /* Write data to a scsi device. Returns nonzero on failure. The transfer may complete asynchronously. */ -static int scsi_write_data(SCSIRequest *req) +static void scsi_write_data(SCSIRequest *req) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); @@ -287,16 +287,13 @@ static int scsi_write_data(SCSIRequest *req) if (r->len == 0) { r->len = r->buflen; scsi_req_data(&r->req, r->len); - return 0; + return; } ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete); if (ret < 0) { scsi_command_complete(r, ret); - return 1; } - - return 0; } /* Return a pointer to the data buffer. */ diff --git a/hw/scsi.h b/hw/scsi.h index 5730faa553..b56338d72a 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -77,7 +77,7 @@ struct SCSIDeviceInfo { void (*free_req)(SCSIRequest *req); int32_t (*send_command)(SCSIRequest *req, uint8_t *buf); void (*read_data)(SCSIRequest *req); - int (*write_data)(SCSIRequest *req); + void (*write_data)(SCSIRequest *req); void (*cancel_io)(SCSIRequest *req); uint8_t *(*get_buf)(SCSIRequest *req); int (*get_sense)(SCSIRequest *req, uint8_t *buf, int len); -- cgit v1.2.3-55-g7522 From 2e7cc4d604206ce15d298686c0ffd77dcedc33a2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 18 Apr 2011 14:29:16 +0200 Subject: scsi-generic: Handle queue full The sg driver currently has a hardcoded limit of commands it can handle simultaneously. When this limit is reached the driver will return -EDOM. So we need to capture this to enable proper return values here. Signed-off-by: Hannes Reinecke Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/scsi-generic.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 579bab9e7c..7670606419 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -124,6 +124,9 @@ static void scsi_command_complete(void *opaque, int ret) if (ret != 0) { switch (ret) { + case -EDOM: + r->req.status = TASK_SET_FULL; + break; case -EINVAL: r->req.status = CHECK_CONDITION; scsi_set_sense(s, SENSE_CODE(INVALID_FIELD)); -- cgit v1.2.3-55-g7522 From 1455084ea2c48abf23c4e4e15e378ee43457f381 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 May 2011 16:47:28 +0200 Subject: scsi: ignore LUN field in the CDB The LUN field in the CDB is a historical relic. Ignore it as reserved, which is what modern SCSI specifications actually say. Signed-off-by: Paolo Bonzini Reviewed-by: Christoph Hellwig --- hw/scsi-disk.c | 6 +++--- hw/scsi-generic.c | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'hw/scsi-generic.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index e0c384f148..a8c7372d3e 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -518,7 +518,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) memset(outbuf, 0, buflen); - if (req->lun || req->cmd.buf[1] >> 5) { + if (req->lun) { outbuf[0] = 0x7f; /* LUN not supported */ return buflen; } @@ -1024,9 +1024,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) } #endif - if (req->lun || buf[1] >> 5) { + if (req->lun) { /* Only LUN 0 supported. */ - DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : buf[1] >> 5); + DPRINTF("Unimplemented LUN %d\n", req->lun); if (command != REQUEST_SENSE && command != INQUIRY) { scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_SUPPORTED)); diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 7670606419..8e59c7ee89 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -337,9 +337,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); int ret; - if (cmd[0] != REQUEST_SENSE && - (req->lun != s->lun || (cmd[1] >> 5) != s->lun)) { - DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5); + if (cmd[0] != REQUEST_SENSE && req->lun != s->lun) { + DPRINTF("Unimplemented LUN %d\n", req->lun); scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED)); r->req.status = CHECK_CONDITION; scsi_req_complete(&r->req); -- cgit v1.2.3-55-g7522