From: Hannes Reinecke <h...@suse.de> 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 <h...@suse.de> Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> Cc: Christoph Hellwig <h...@lst.de> --- hw/esp.c | 29 ++++++++----- hw/lsi53c895a.c | 56 +++++++++++++++----------- hw/scsi-bus.c | 24 ++++------- hw/scsi-disk.c | 116 ++++++++++++++-------------------------------------- hw/scsi-generic.c | 107 +++++++++++++++---------------------------------- hw/scsi.h | 21 +++++----- hw/spapr_vscsi.c | 44 +++++++++++--------- hw/usb-msd.c | 27 +++++++----- 8 files changed, 172 insertions(+), 252 deletions(-) diff --git a/hw/esp.c b/hw/esp.c index d8bba7a..096f4dc 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; } @@ -230,9 +231,10 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) int32_t datalen; int lun; - DPRINTF("do_busid_cmd: busid 0x%x\n", 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 3b67155..b9febae 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 (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 c0bc275..a175590 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -136,28 +136,22 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l SCSIRequest *req; req = qemu_mallocz(size); - 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) @@ -514,7 +508,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) @@ -550,9 +544,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 ba7ffa1..7446115 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -83,16 +83,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) @@ -103,11 +104,6 @@ static void scsi_free_request(SCSIRequest *req) scsi_req_free(&r->req); } -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)); @@ -136,18 +132,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) @@ -172,8 +166,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; @@ -205,23 +201,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); @@ -283,8 +262,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; @@ -303,24 +283,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; } @@ -345,10 +307,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); @@ -374,16 +336,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; } @@ -1027,26 +983,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]); @@ -1065,9 +1013,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; } @@ -1093,7 +1041,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; } @@ -1103,7 +1050,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; @@ -1117,7 +1064,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); @@ -1152,7 +1099,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); @@ -1180,11 +1127,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) { @@ -1197,7 +1142,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; } @@ -1211,6 +1155,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); } } @@ -1293,6 +1238,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 5739067..e3e4187 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) @@ -82,11 +82,6 @@ static void scsi_free_request(SCSIRequest *req) scsi_req_free(&r->req); } -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, @@ -181,21 +173,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; @@ -247,21 +231,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); @@ -278,15 +254,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; } @@ -314,18 +285,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; @@ -336,18 +306,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); @@ -377,10 +340,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; } @@ -395,13 +355,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) @@ -475,6 +432,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); } } @@ -566,6 +524,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 ecf1aeb..e709989 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 9f5c154..cf2ed73 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 7a07a12..38f97d0 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; -- 1.7.4.4