If the scsi controller uses scatter-gather lists, do not copy them to an intermediate buffer. Instead, use them as is via the dma_blk_ioctl() function.
In order to make this feature possible, the following changes have been made to the code: * All I/O functions have been branched into two types of functions: "sg_*" and "buf_*", which are used for sg and non-sg operations respectively. The shared code between them is likewise moved in "common_*" functions. * The scsi_req_data() function is predictably used only for non-sg operations. * The `sg_io_hdr' struct for scatter-gather operations does not include the `SG_FLAG_DIRECT_IO' flag, since it does not work with iovecs, as explained here [1]. [1]: http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/x192.html Signed-off-by: Alex Pyrgiotis <apyr...@arrikto.com> Signed-off-by: Dimitris Aragiorgis <dim...@arrikto.com> diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 6704861..5f3c401 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -16,6 +16,7 @@ #include "hw/scsi/scsi.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" +#include "sysemu/dma.h" #ifdef __linux__ @@ -98,12 +99,57 @@ static void scsi_buf_init_io_header(SCSIGenericReq *r, int direction) r->io_header.flags |= SG_FLAG_DIRECT_IO; } -/* Return a pointer to the data buffer. */ +/* + * Create an io_header for sg* operations. + * + * Note: The rest of the fields are filled when scsi_sg_handle_iov() is called. + */ +static void scsi_sg_init_io_header(SCSIGenericReq *r, int direction) +{ + _init_io_header(r, direction); +} + +/* + * Handle a QEMUIOVector, as created by the dma_blk_ioctl() function. + * + * As mentioned here [1], in order to use an iovec for the ioctl operation, the + * iovec_count field must be filled with the number of vectors, the dxferp + * field must point to the iovecs and the dxfer_len field must state the size + * of the request, which should amount to the sum of iov_lens. + * + * [1] http://sg.danny.cz/sg/p/sg_v3_ho.html#id2495162 + */ +static void scsi_sg_handle_iov(void *opaque, QEMUIOVector *iov) +{ + SCSIGenericReq *r = (SCSIGenericReq *)opaque; + + r->io_header.iovec_count = iov->niov; + r->io_header.dxferp = iov->iov; + r->io_header.dxfer_len = iov->size; +} + +/* + * Return a pointer to the request's data buffer. + * + * If the SCSI controller does not provide a scatter-gather list, then the data + * buffer of the request is stored in the `buf' field of SCSIGenericReq. + * + * Else, the data buffer is mapped to a list of iovecs and this function is + * never called externally. If called internally, it will be solely for the + * manipulation of the first 9 bytes of data of a SCSI response. In this case, + * we can safely return a pointer to the data of the first iovec. + */ static uint8_t *scsi_get_buf(SCSIRequest *req) { + struct iovec *iov; SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - return r->buf; + if (req->sg) { + iov = (struct iovec *)r->io_header.dxferp; + return iov->iov_base; + } else { + return r->buf; + } } static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req) @@ -265,6 +311,18 @@ static int scsi_common_read_complete(SCSIGenericReq *r, int ret) return len; } +static void scsi_sg_read_complete(void *opaque, int ret) +{ + int len; + SCSIGenericReq *r = (SCSIGenericReq *)opaque; + + len = scsi_common_read_complete(r, ret); + if (len > 0) { + r->req.resid = r->io_header.resid; + scsi_command_complete_noio(r, 0); + } +} + static void scsi_buf_read_complete(void *opaque, int ret) { int len; @@ -279,6 +337,39 @@ static void scsi_buf_read_complete(void *opaque, int ret) } /* + * Execute the request using the provided scatter-gather list. + * + * This function does the following: + * + * a. Initialize the io header for the ioctl request. + * b. Derive the DMA direction from the provided request direction. + * c. Perform the ioctl request using dma_blk_ioctl() from dma-helpers. Also, + * register a callback for the handling of the resulting iovector from the + * internal mapping of the scatter-gather list. + */ +static void scsi_sg_do_request(SCSIGenericReq *r, int direction, + BlockCompletionFunc *complete) +{ + SCSIDevice *s = r->req.dev; + DMADirection dir; + + scsi_sg_init_io_header(r, direction); + + if (direction == SG_DXFER_TO_DEV) { + dir = DMA_DIRECTION_TO_DEVICE; + } else if (direction == SG_DXFER_FROM_DEV) { + dir = DMA_DIRECTION_FROM_DEVICE; + } + + r->req.aiocb = dma_blk_ioctl(s->conf.blk, SG_IO, &r->io_header, r->req.sg, + dir, scsi_sg_handle_iov, complete, r); + + if (!r->req.aiocb) { + scsi_command_complete_noio(r, -EIO); + } +} + +/* * Execute the request using an intermediate buffer. * * This function does the following: @@ -301,7 +392,18 @@ static void scsi_buf_do_request(SCSIGenericReq *r, int direction, } } -/* Read more data from scsi device into buffer. */ +static void scsi_sg_read_data(SCSIRequest *req) +{ + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + + DPRINTF("scsi_sg_read_data 0x%x\n", req->tag); + + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); + + scsi_sg_do_request(r, SG_DXFER_FROM_DEV, scsi_sg_read_complete); +} + static void scsi_buf_read_data(SCSIRequest *req) { SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); @@ -322,7 +424,11 @@ static void scsi_buf_read_data(SCSIRequest *req) static void scsi_common_read_data(SCSIRequest *req) { - scsi_buf_read_data(req); + if (req->sg) { + scsi_sg_read_data(req); + } else { + scsi_buf_read_data(req); + } } /* Intercept the write response in order to snoop or alter it */ @@ -360,6 +466,11 @@ static void scsi_common_write_complete(void *opaque, int ret) scsi_command_complete_noio(r, ret); } +static void scsi_sg_write_complete(void *opaque, int ret) +{ + scsi_common_write_complete(opaque, ret); +} + static void scsi_buf_write_complete(void *opaque, int ret) { scsi_common_write_complete(opaque, ret); @@ -367,6 +478,20 @@ static void scsi_buf_write_complete(void *opaque, int ret) /* Write data to a scsi device. Returns nonzero on failure. The transfer may complete asynchronously. */ +static void scsi_sg_write_data(SCSIRequest *req) +{ + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + + DPRINTF("scsi_sg_write_data 0x%x\n", req->tag); + + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); + + scsi_sg_do_request(r, SG_DXFER_TO_DEV, scsi_sg_write_complete); +} + +/* Write data to a scsi device. Returns nonzero on failure. + The transfer may complete asynchronously. */ static void scsi_buf_write_data(SCSIRequest *req) { SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); @@ -374,8 +499,8 @@ static void scsi_buf_write_data(SCSIRequest *req) DPRINTF("scsi_buf_write_data 0x%x\n", req->tag); /* - * Before performing the write request, we need to transfer the data to - * our intermediate buffer. + * If the controller does not provide a scatter-gather list, we need to + * sync the controller's data with the request's intermediate buffer. */ if (!r->synced) { r->synced = 1; @@ -391,7 +516,11 @@ static void scsi_buf_write_data(SCSIRequest *req) static void scsi_common_write_data(SCSIRequest *req) { - scsi_buf_write_data(req); + if (req->sg) { + scsi_sg_write_data(req); + } else { + scsi_buf_write_data(req); + } } /* Execute a scsi command. Returns the length of the data expected by the @@ -413,6 +542,9 @@ static int32_t scsi_common_send_command(SCSIRequest *req, uint8_t *cmd) } #endif + /* + * SCSI commands that have nothing to transfer can be executed immediately. + */ if (r->req.cmd.xfer == 0) { g_free(r->buf); r->buflen = 0; @@ -423,13 +555,16 @@ static int32_t scsi_common_send_command(SCSIRequest *req, uint8_t *cmd) return 0; } - if (r->buflen != r->req.cmd.xfer) { - g_free(r->buf); - r->buf = g_malloc(r->req.cmd.xfer); - r->buflen = r->req.cmd.xfer; + r->buflen = r->req.cmd.xfer; + + /* + * If the controller does not support scatter-gather lists, allocate an + * intermediate buffer that can hold the request data. + */ + if (!req->sg) { + r->buf = g_malloc0(r->buflen); } - memset(r->buf, 0, r->buflen); if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { return -r->req.cmd.xfer; } else { -- 2.6.2