Scatter/gather functionality uses the newly added DMA helpers. The device can choose between doing DMA itself, or calling scsi_req_data as usual, which will use the newly added DMA helpers to map the destination area(s) piecewise and copy to/from them.
Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- hw/esp.c | 2 +- hw/lsi53c895a.c | 2 +- hw/scsi-bus.c | 34 +++++++++++++++++++++++++++++++--- hw/scsi.h | 4 +++- hw/spapr_vscsi.c | 2 +- hw/usb-msd.c | 2 +- 6 files changed, 38 insertions(+), 8 deletions(-) diff --git a/hw/esp.c b/hw/esp.c index 5d29071..2d098ea 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 = scsi_req_new(s->current_dev, 0, lun, buf, NULL); - datalen = scsi_req_enqueue(s->current_req); + datalen = scsi_req_enqueue(s->current_req, NULL); s->ti_size = datalen; if (datalen != 0) { s->rregs[ESP_RSTAT] = STAT_TC; diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index a0c2419..ee7f67c 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -785,7 +785,7 @@ static void lsi_do_command(LSIState *s) s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun, buf, s->current); - n = scsi_req_enqueue(s->current->req); + n = scsi_req_enqueue(s->current->req, NULL); if (n) { if (n > 0) { lsi_set_phase(s, PHASE_DI); diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index b49d02d..5876f78 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -5,6 +5,7 @@ #include "qdev.h" #include "blockdev.h" #include "trace.h" +#include "dma.h" static char *scsibus_get_fw_dev_path(DeviceState *dev); static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); @@ -521,7 +522,7 @@ void scsi_req_build_sense(SCSIRequest *req, SCSISense sense) req->sense_len = 18; } -int32_t scsi_req_enqueue(SCSIRequest *req) +int32_t scsi_req_enqueue(SCSIRequest *req, QEMUSGList *sg) { int32_t rc; @@ -529,6 +530,7 @@ int32_t scsi_req_enqueue(SCSIRequest *req) scsi_req_ref(req); req->enqueued = true; QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); + req->sg = sg; scsi_req_ref(req); rc = req->ops->send_command(req, req->cmd.buf); @@ -1039,15 +1041,41 @@ void scsi_req_continue(SCSIRequest *req) } } +static void scsi_dma_cb(void *opaque, int ret) +{ + SCSIRequest *req = opaque; + assert(ret == 0); + assert(req->dma_started); + req->resid = qemu_sglist_get_resid(req->sg); + scsi_req_continue(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 scsi_req_continue will restart I/O. */ void scsi_req_data(SCSIRequest *req, int len) { + uint8_t *buf; trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); assert(req->cmd.mode != SCSI_XFER_NONE); - req->resid -= len; - req->bus->ops->transfer_data(req, len); + if (!req->sg) { + req->resid -= len; + req->bus->ops->transfer_data(req, len); + return; + } + + /* If the device calls scsi_req_data and the HBA specified a + * scatter/gather list, the transfer has to happen in a single + * step. */ + assert(!req->dma_started); + req->dma_started = true; + + buf = scsi_req_get_buf(req); + if (req->cmd.mode == SCSI_XFER_FROM_DEV) { + req->aiocb = dma_buf_read(buf, len, req->sg, scsi_dma_cb, req); + } else { + req->aiocb = dma_buf_write(buf, len, req->sg, scsi_dma_cb, req); + } } void scsi_req_print(SCSIRequest *req) diff --git a/hw/scsi.h b/hw/scsi.h index 76d4df2..febb6fd 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -50,6 +50,8 @@ struct SCSIRequest { size_t resid; SCSICommand cmd; BlockDriverAIOCB *aiocb; + QEMUSGList *sg; + bool dma_started; uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; bool enqueued; @@ -174,7 +176,7 @@ SCSIRequest *scsi_req_alloc(SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag, uint32_t lun, void *hba_private); SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, uint8_t *buf, void *hba_private); -int32_t scsi_req_enqueue(SCSIRequest *req); +int32_t scsi_req_enqueue(SCSIRequest *req, QEMUSGList *qsg); 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 128b5a6..3a6d35f 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -601,7 +601,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) req->lun = lun; req->sreq = scsi_req_new(sdev, req->qtag, lun, srp->cmd.cdb, req); - n = scsi_req_enqueue(req->sreq); + n = scsi_req_enqueue(req->sreq, NULL); 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 8cddf80..167da8a 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -381,7 +381,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) s->residue = 0; s->scsi_len = 0; s->req = scsi_req_new(s->scsi_dev, s->tag, 0, cbw.cmd, NULL); - scsi_req_enqueue(s->req); + scsi_req_enqueue(s->req, NULL); /* ??? Should check that USB and SCSI data transfer directions match. */ if (s->mode != USB_MSDM_CSW && s->residue == 0) { -- 1.7.6