diff mbox

[9/9] scsi-generic: Allow full scatter-gather support

Message ID 1450284917-10508-10-git-send-email-apyrgio@arrikto.com
State New
Headers show

Commit Message

Alex Pyrgiotis Dec. 16, 2015, 4:55 p.m. UTC
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 <apyrgio@arrikto.com>
Signed-off-by: Dimitris Aragiorgis <dimara@arrikto.com>
diff mbox

Patch

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 {