[PATCH 2/2] virtio-blk: add SG_IO passthru support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add support for SG_IO passthru (packet commands) to the virtio-blk
backend.  Conceptually based on an older patch from Hannes Reinecke
but largely rewritten to match the code structure and layering in
virtio-blk aswell as doing asynchronous I/O.


Signed-off-by: Christoph Hellwig <hch@xxxxxx>

Index: qemu/hw/virtio-blk.h
===================================================================
--- qemu.orig/hw/virtio-blk.h	2009-04-28 11:42:14.059074434 +0200
+++ qemu/hw/virtio-blk.h	2009-04-28 11:44:24.930074531 +0200
@@ -28,6 +28,9 @@
 #define VIRTIO_BLK_F_SIZE_MAX   1       /* Indicates maximum segment size */
 #define VIRTIO_BLK_F_SEG_MAX    2       /* Indicates maximum # of segments */
 #define VIRTIO_BLK_F_GEOMETRY   4       /* Indicates support of legacy geometry */
+#define VIRTIO_BLK_F_RO         5       /* Disk is read-only */
+#define VIRTIO_BLK_F_BLK_SIZE   6       /* Block size of disk is available*/
+#define VIRTIO_BLK_F_SCSI       7       /* Supports scsi command passthru */
 
 struct virtio_blk_config
 {
@@ -70,6 +73,15 @@ struct virtio_blk_inhdr
     unsigned char status;
 };
 
+/* SCSI pass-through header */
+struct virtio_scsi_inhdr
+{
+    uint32_t errors;
+    uint32_t data_len;
+    uint32_t sense_len;
+    uint32_t residual;
+};
+
 void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs);
 
 #endif
Index: qemu/hw/virtio-blk.c
===================================================================
--- qemu.orig/hw/virtio-blk.c	2009-04-28 11:42:14.066074487 +0200
+++ qemu/hw/virtio-blk.c	2009-04-28 11:52:45.836079580 +0200
@@ -15,6 +15,9 @@
 #include <sysemu.h>
 #include "virtio-blk.h"
 #include "block_int.h"
+#ifdef __linux__
+# include <scsi/sg.h>
+#endif
 
 typedef struct VirtIOBlock
 {
@@ -35,6 +38,8 @@ typedef struct VirtIOBlockReq
     VirtQueueElement elem;
     struct virtio_blk_inhdr *in;
     struct virtio_blk_outhdr *out;
+    struct virtio_scsi_inhdr *scsi;
+    struct sg_io_hdr scsi_hdr;
     QEMUIOVector qiov;
     struct VirtIOBlockReq *next;
 } VirtIOBlockReq;
@@ -103,6 +108,108 @@ static VirtIOBlockReq *virtio_blk_get_re
     return req;
 }
 
+#ifdef __linux__
+static void virtio_blk_scsi_complete(void *opaque, int ret)
+{
+    VirtIOBlockReq *req = opaque;
+    int status;
+
+    if (ret) {
+        status = VIRTIO_BLK_S_UNSUPP;
+        req->scsi_hdr.status = -ret;
+        req->scsi_hdr.resid = req->scsi_hdr.dxfer_len;
+    } else if (req->scsi_hdr.status) {
+        status = VIRTIO_BLK_S_IOERR;
+    } else {
+        status = VIRTIO_BLK_S_OK;
+    }
+
+    req->scsi->errors = req->scsi_hdr.status;
+    req->scsi->residual = req->scsi_hdr.resid;
+    req->scsi->sense_len = req->scsi_hdr.sb_len_wr;
+    req->scsi->data_len = req->scsi_hdr.dxfer_len;
+
+    virtio_blk_req_complete(req, status);
+}
+
+static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
+{
+    int i;
+
+    /*
+     * We require at least one output segment each for the virtio_blk_outhdr
+     * and the SCSI command block.
+     *
+     * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr
+     * and the sense buffer pointer in the input segments.
+     */
+    if (req->elem.out_num < 2 || req->elem.in_num < 3) {
+        virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
+        return;
+    }
+
+    /*
+     * No support for bidirection commands yet.
+     */
+    if (req->elem.out_num > 2 && req->elem.in_num > 3) {
+        virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
+        return;
+    }
+
+    /*
+     * The scsi inhdr is placed in the second-to-last input segment, just
+     * before the regular inhdr.
+     */
+    req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base;
+
+    memset(&req->scsi_hdr, 0, sizeof(struct sg_io_hdr));
+    req->scsi_hdr.interface_id = 'S';
+    req->scsi_hdr.cmd_len = req->elem.out_sg[1].iov_len;
+    req->scsi_hdr.cmdp = req->elem.out_sg[1].iov_base;
+    req->scsi_hdr.dxfer_len = 0;
+
+    if (req->elem.out_num > 2) {
+        /*
+         * If there are more than the minimally required 2 output segments
+         * there is write payload starting from the third iovec.
+         */
+        req->scsi_hdr.dxfer_direction = SG_DXFER_TO_DEV;
+        req->scsi_hdr.iovec_count = req->elem.out_num - 2;
+
+        for (i = 0; i < req->scsi_hdr.iovec_count; i++)
+            req->scsi_hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len;
+        req->scsi_hdr.dxferp = req->elem.out_sg + 2;
+    } else if (req->elem.in_num > 3) {
+        /*
+         * If we have more than 3 input segments the guest wants to actually
+         * read data.
+         */
+        req->scsi_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+        req->scsi_hdr.iovec_count = req->elem.in_num - 3;
+
+        for (i = 0; i < req->scsi_hdr.iovec_count; i++)
+            req->scsi_hdr.dxfer_len += req->elem.in_sg[i].iov_len;
+        req->scsi_hdr.dxferp = req->elem.in_sg;
+    } else {
+        /*
+         * Some SCSI commands don't actually transfer any data.
+         */
+        req->scsi_hdr.dxfer_direction = SG_DXFER_NONE;
+    }
+
+    req->scsi_hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base;
+    req->scsi_hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len;
+
+    bdrv_aio_ioctl(req->dev->bs, SG_IO, &req->scsi_hdr,
+                   virtio_blk_scsi_complete, req);
+}
+#else
+static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
+{
+    virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
+}
+#endif /* __linux__ */
+
 static void virtio_blk_handle_write(VirtIOBlockReq *req)
 {
     bdrv_aio_writev(req->dev->bs, req->out->sector, &req->qiov,
@@ -136,12 +243,7 @@ static void virtio_blk_handle_output(Vir
         req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
 
         if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
-            unsigned int len = sizeof(*req->in);
-
-            req->in->status = VIRTIO_BLK_S_UNSUPP;
-            virtqueue_push(vq, &req->elem, len);
-            virtio_notify(vdev, vq);
-            qemu_free(req);
+            virtio_blk_handle_scsi(req);
         } else if (req->out->type & VIRTIO_BLK_T_OUT) {
             qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
                                      req->elem.out_num - 1);
@@ -203,7 +305,15 @@ static void virtio_blk_update_config(Vir
 
 static uint32_t virtio_blk_get_features(VirtIODevice *vdev)
 {
-    return (1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_GEOMETRY);
+    uint32_t features = 0;
+
+    features |= (1 << VIRTIO_BLK_F_SEG_MAX);
+    features |= (1 << VIRTIO_BLK_F_GEOMETRY);
+#ifdef __linux__
+    features |= (1 << VIRTIO_BLK_F_SCSI);
+#endif
+
+    return features;
 }
 
 static void virtio_blk_save(QEMUFile *f, void *opaque)
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux