@@ -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 {