[qemu: 1/2] Add support for virtio-scsi

Justin M. Forbes jforbes at fedoraproject.org
Tue Feb 7 22:18:47 UTC 2012


commit 357a44f0604c961f667fd72d665cd9a17c0d7018
Author: Justin M. Forbes <jforbes at redhat.com>
Date:   Tue Feb 7 16:15:59 2012 -0600

    Add support for virtio-scsi

 qemu.spec                      |    7 +-
 qemu_virtio-scsi_support.patch | 1652 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1658 insertions(+), 1 deletions(-)
---
diff --git a/qemu.spec b/qemu.spec
index 62e8d03..e53df40 100644
--- a/qemu.spec
+++ b/qemu.spec
@@ -1,7 +1,7 @@
 Summary: QEMU is a FAST! processor emulator
 Name: qemu
 Version: 1.0
-Release: 3%{?dist}
+Release: 4%{?dist}
 # Epoch because we pushed a qemu-1.0 package
 Epoch: 2
 License: GPLv2+ and LGPLv2+ and BSD
@@ -92,6 +92,7 @@ Patch201: Fix_save-restore_of_in-kernel_i8259.patch
 
 # Feature patches, should be in 1.1 before release
 Patch301: enable_architectural_PMU_cpuid_leaf.patch
+Patch302: qemu_virtio-scsi_support.patch
 
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 BuildRequires: SDL-devel zlib-devel which texi2html gnutls-devel cyrus-sasl-devel
@@ -361,6 +362,7 @@ such as kvm_stat.
 %patch201 -p1
 
 %patch301 -p1
+%patch302 -p1
 
 %build
 # By default we build everything, but allow x86 to build a minimal version
@@ -740,6 +742,9 @@ fi
 %{_mandir}/man1/qemu-img.1*
 
 %changelog
+* Tue Feb 07 2012 Justin M. Forbes <jforbes at redhat.com> - 2:1.0-4
+- Add support for virtio-scsi
+
 * Tue Jan 24 2012 Justin M. Forbes <jforbes at redhat.com> - 2:1.0-3
 - Add support for vPMU
 - e1000: bounds packet size against buffer size CVE-2012-0029
diff --git a/qemu_virtio-scsi_support.patch b/qemu_virtio-scsi_support.patch
new file mode 100644
index 0000000..59f6876
--- /dev/null
+++ b/qemu_virtio-scsi_support.patch
@@ -0,0 +1,1652 @@
+diff -ruNp qemu-kvm-1.0/default-configs/pci.mak qemu-kvm-1.0.virtio-scsi/default-configs/pci.mak
+--- qemu-kvm-1.0/default-configs/pci.mak	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/default-configs/pci.mak	2012-02-07 14:44:53.424905251 -0600
+@@ -1,5 +1,6 @@
+ CONFIG_PCI=y
+ CONFIG_VIRTIO_PCI=y
++CONFIG_VIRTIO_SCSI=y
+ CONFIG_VIRTIO=y
+ CONFIG_USB_UHCI=y
+ CONFIG_USB_OHCI=y
+diff -ruNp qemu-kvm-1.0/default-configs/s390x-softmmu.mak qemu-kvm-1.0.virtio-scsi/default-configs/s390x-softmmu.mak
+--- qemu-kvm-1.0/default-configs/s390x-softmmu.mak	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/default-configs/s390x-softmmu.mak	2012-02-07 14:44:53.424905251 -0600
+@@ -1 +1,2 @@
+ CONFIG_VIRTIO=y
++CONFIG_VIRTIO_SCSI=y
+diff -ruNp qemu-kvm-1.0/dma.h qemu-kvm-1.0.virtio-scsi/dma.h
+--- qemu-kvm-1.0/dma.h	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/dma.h	2012-02-07 14:44:53.425905267 -0600
+@@ -17,6 +17,13 @@
+ 
+ typedef struct ScatterGatherEntry ScatterGatherEntry;
+ 
++struct QEMUSGList {
++    ScatterGatherEntry *sg;
++    int nsg;
++    int nalloc;
++    size_t size;
++};
++
+ #if defined(TARGET_PHYS_ADDR_BITS)
+ typedef target_phys_addr_t dma_addr_t;
+ 
+@@ -32,13 +39,6 @@ struct ScatterGatherEntry {
+     dma_addr_t len;
+ };
+ 
+-struct QEMUSGList {
+-    ScatterGatherEntry *sg;
+-    int nsg;
+-    int nalloc;
+-    dma_addr_t size;
+-};
+-
+ void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint);
+ void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len);
+ void qemu_sglist_destroy(QEMUSGList *qsg);
+@@ -58,4 +58,10 @@ BlockDriverAIOCB *dma_bdrv_read(BlockDri
+ BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs,
+                                  QEMUSGList *sg, uint64_t sector,
+                                  BlockDriverCompletionFunc *cb, void *opaque);
++uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg);
++uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg);
++
++void dma_acct_start(BlockDriverState *bs, BlockAcctCookie *cookie,
++                    QEMUSGList *sg, enum BlockAcctType type);
++
+ #endif
+diff -ruNp qemu-kvm-1.0/dma-helpers.c qemu-kvm-1.0.virtio-scsi/dma-helpers.c
+--- qemu-kvm-1.0/dma-helpers.c	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/dma-helpers.c	2012-02-07 14:44:53.424905251 -0600
+@@ -196,3 +196,39 @@ BlockDriverAIOCB *dma_bdrv_write(BlockDr
+ {
+     return dma_bdrv_io(bs, sg, sector, bdrv_aio_writev, cb, opaque, true);
+ }
++
++
++static uint64_t dma_buf_rw(uint8_t *ptr, int32_t len, QEMUSGList *sg, bool to_dev)
++{
++    uint64_t resid;
++    int sg_cur_index;
++
++    resid = sg->size;
++    sg_cur_index = 0;
++    len = MIN(len, resid);
++    while (len > 0) {
++        ScatterGatherEntry entry = sg->sg[sg_cur_index++];
++        cpu_physical_memory_rw(entry.base, ptr, MIN(len, entry.len), !to_dev);
++        ptr += entry.len;
++        len -= entry.len;
++        resid -= entry.len;
++    }
++
++    return resid;
++}
++
++uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg)
++{
++    return dma_buf_rw(ptr, len, sg, 0);
++}
++
++uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg)
++{
++    return dma_buf_rw(ptr, len, sg, 1);
++}
++
++void dma_acct_start(BlockDriverState *bs, BlockAcctCookie *cookie,
++                    QEMUSGList *sg, enum BlockAcctType type)
++{
++    bdrv_acct_start(bs, cookie, sg->size, type);
++}
+diff -ruNp qemu-kvm-1.0/hw/esp.c qemu-kvm-1.0.virtio-scsi/hw/esp.c
+--- qemu-kvm-1.0/hw/esp.c	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/esp.c	2012-02-07 14:44:53.425905267 -0600
+@@ -389,7 +389,8 @@ static void esp_do_dma(ESPState *s)
+     esp_dma_done(s);
+ }
+ 
+-static void esp_command_complete(SCSIRequest *req, uint32_t status)
++static void esp_command_complete(SCSIRequest *req, uint32_t status,
++                                 int32_t resid)
+ {
+     ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
+ 
+diff -ruNp qemu-kvm-1.0/hw/ide/ahci.c qemu-kvm-1.0.virtio-scsi/hw/ide/ahci.c
+--- qemu-kvm-1.0/hw/ide/ahci.c	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/ide/ahci.c	2012-02-07 14:44:53.426905283 -0600
+@@ -425,55 +425,6 @@ static void ahci_reg_init(AHCIState *s)
+     }
+ }
+ 
+-static uint32_t read_from_sglist(uint8_t *buffer, uint32_t len,
+-                                 QEMUSGList *sglist)
+-{
+-    uint32_t i = 0;
+-    uint32_t total = 0, once;
+-    ScatterGatherEntry *cur_prd;
+-    uint32_t sgcount;
+-
+-    cur_prd = sglist->sg;
+-    sgcount = sglist->nsg;
+-    for (i = 0; len && sgcount; i++) {
+-        once = MIN(cur_prd->len, len);
+-        cpu_physical_memory_read(cur_prd->base, buffer, once);
+-        cur_prd++;
+-        sgcount--;
+-        len -= once;
+-        buffer += once;
+-        total += once;
+-    }
+-
+-    return total;
+-}
+-
+-static uint32_t write_to_sglist(uint8_t *buffer, uint32_t len,
+-                                QEMUSGList *sglist)
+-{
+-    uint32_t i = 0;
+-    uint32_t total = 0, once;
+-    ScatterGatherEntry *cur_prd;
+-    uint32_t sgcount;
+-
+-    DPRINTF(-1, "total: 0x%x bytes\n", len);
+-
+-    cur_prd = sglist->sg;
+-    sgcount = sglist->nsg;
+-    for (i = 0; len && sgcount; i++) {
+-        once = MIN(cur_prd->len, len);
+-        DPRINTF(-1, "write 0x%x bytes to 0x%lx\n", once, (long)cur_prd->base);
+-        cpu_physical_memory_write(cur_prd->base, buffer, once);
+-        cur_prd++;
+-        sgcount--;
+-        len -= once;
+-        buffer += once;
+-        total += once;
+-    }
+-
+-    return total;
+-}
+-
+ static void check_cmd(AHCIState *s, int port)
+ {
+     AHCIPortRegs *pr = &s->dev[port].port_regs;
+@@ -794,9 +745,8 @@ static void process_ncq_command(AHCIStat
+             DPRINTF(port, "tag %d aio read %"PRId64"\n",
+                     ncq_tfs->tag, ncq_tfs->lba);
+ 
+-            bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
+-                            (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE,
+-                            BDRV_ACCT_READ);
++            dma_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
++                           &ncq_tfs->sglist, BDRV_ACCT_READ);
+             ncq_tfs->aiocb = dma_bdrv_read(ncq_tfs->drive->port.ifs[0].bs,
+                                            &ncq_tfs->sglist, ncq_tfs->lba,
+                                            ncq_cb, ncq_tfs);
+@@ -808,9 +758,8 @@ static void process_ncq_command(AHCIStat
+             DPRINTF(port, "tag %d aio write %"PRId64"\n",
+                     ncq_tfs->tag, ncq_tfs->lba);
+ 
+-            bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
+-                            (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE,
+-                            BDRV_ACCT_WRITE);
++            dma_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
++                           &ncq_tfs->sglist, BDRV_ACCT_WRITE);
+             ncq_tfs->aiocb = dma_bdrv_write(ncq_tfs->drive->port.ifs[0].bs,
+                                             &ncq_tfs->sglist, ncq_tfs->lba,
+                                             ncq_cb, ncq_tfs);
+@@ -1015,12 +964,12 @@ static int ahci_start_transfer(IDEDMA *d
+             is_write ? "writ" : "read", size, is_atapi ? "atapi" : "ata",
+             has_sglist ? "" : "o");
+ 
+-    if (is_write && has_sglist && (s->data_ptr < s->data_end)) {
+-        read_from_sglist(s->data_ptr, size, &s->sg);
+-    }
+-
+-    if (!is_write && has_sglist && (s->data_ptr < s->data_end)) {
+-        write_to_sglist(s->data_ptr, size, &s->sg);
++    if (has_sglist && size) {
++        if (is_write) {
++            dma_buf_write(s->data_ptr, size, &s->sg);
++        } else {
++            dma_buf_read(s->data_ptr, size, &s->sg);
++        }
+     }
+ 
+     /* update number of transferred bytes */
+@@ -1059,14 +1008,9 @@ static int ahci_dma_prepare_buf(IDEDMA *
+ {
+     AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
+     IDEState *s = &ad->port.ifs[0];
+-    int i;
+ 
+     ahci_populate_sglist(ad, &s->sg);
+-
+-    s->io_buffer_size = 0;
+-    for (i = 0; i < s->sg.nsg; i++) {
+-        s->io_buffer_size += s->sg.sg[i].len;
+-    }
++    s->io_buffer_size = s->sg.size;
+ 
+     DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size);
+     return s->io_buffer_size != 0;
+@@ -1084,9 +1028,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma,
+     }
+ 
+     if (is_write) {
+-        write_to_sglist(p, l, &s->sg);
++        dma_buf_read(p, l, &s->sg);
+     } else {
+-        read_from_sglist(p, l, &s->sg);
++        dma_buf_write(p, l, &s->sg);
+     }
+ 
+     /* update number of transferred bytes */
+diff -ruNp qemu-kvm-1.0/hw/lsi53c895a.c qemu-kvm-1.0.virtio-scsi/hw/lsi53c895a.c
+--- qemu-kvm-1.0/hw/lsi53c895a.c	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/lsi53c895a.c	2012-02-07 14:44:53.427905299 -0600
+@@ -699,7 +699,7 @@ static int lsi_queue_req(LSIState *s, SC
+ }
+ 
+  /* Callback to indicate that the SCSI layer has completed a command.  */
+-static void lsi_command_complete(SCSIRequest *req, uint32_t status)
++static void lsi_command_complete(SCSIRequest *req, uint32_t status, int32_t resid)
+ {
+     LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
+     int out;
+diff -ruNp qemu-kvm-1.0/hw/pci.h qemu-kvm-1.0.virtio-scsi/hw/pci.h
+--- qemu-kvm-1.0/hw/pci.h	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/pci.h	2012-02-07 14:44:53.427905299 -0600
+@@ -76,6 +76,7 @@
+ #define PCI_DEVICE_ID_VIRTIO_BLOCK       0x1001
+ #define PCI_DEVICE_ID_VIRTIO_BALLOON     0x1002
+ #define PCI_DEVICE_ID_VIRTIO_CONSOLE     0x1003
++#define PCI_DEVICE_ID_VIRTIO_SCSI        0x1004
+ 
+ #define FMT_PCIBUS                      PRIx64
+ 
+diff -ruNp qemu-kvm-1.0/hw/s390-virtio-bus.c qemu-kvm-1.0.virtio-scsi/hw/s390-virtio-bus.c
+--- qemu-kvm-1.0/hw/s390-virtio-bus.c	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/s390-virtio-bus.c	2012-02-07 14:44:53.428905315 -0600
+@@ -158,6 +158,18 @@ static int s390_virtio_serial_init(VirtI
+     return r;
+ }
+ 
++static int s390_virtio_scsi_init(VirtIOS390Device *dev)
++{
++    VirtIODevice *vdev;
++
++    vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi);
++    if (!vdev) {
++        return -1;
++    }
++
++    return s390_virtio_device_init(dev, vdev);
++}
++
+ static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq)
+ {
+     ram_addr_t token_off;
+@@ -370,6 +382,17 @@ static VirtIOS390DeviceInfo s390_virtio_
+     },
+ };
+ 
++static VirtIOS390DeviceInfo s390_virtio_scsi = {
++    .init = s390_virtio_scsi_init,
++    .qdev.name = "virtio-scsi-s390",
++    .qdev.alias = "virtio-scsi",
++    .qdev.size = sizeof(VirtIOS390Device),
++    .qdev.props = (Property[]) {
++        DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOS390Device, host_features, scsi),
++        DEFINE_PROP_END_OF_LIST(),
++    },
++};
++
+ static int s390_virtio_busdev_init(DeviceState *dev, DeviceInfo *info)
+ {
+     VirtIOS390DeviceInfo *_info = (VirtIOS390DeviceInfo *)info;
+@@ -392,6 +415,7 @@ static void s390_virtio_register(void)
+     s390_virtio_bus_register_withprop(&s390_virtio_serial);
+     s390_virtio_bus_register_withprop(&s390_virtio_blk);
+     s390_virtio_bus_register_withprop(&s390_virtio_net);
++    s390_virtio_bus_register_withprop(&s390_virtio_scsi);
+ }
+ device_init(s390_virtio_register);
+ 
+diff -ruNp qemu-kvm-1.0/hw/s390-virtio-bus.h qemu-kvm-1.0.virtio-scsi/hw/s390-virtio-bus.h
+--- qemu-kvm-1.0/hw/s390-virtio-bus.h	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/s390-virtio-bus.h	2012-02-07 14:44:53.428905315 -0600
+@@ -19,6 +19,7 @@
+ 
+ #include "virtio-net.h"
+ #include "virtio-serial.h"
++#include "virtio-scsi.h"
+ 
+ #define VIRTIO_DEV_OFFS_TYPE		0	/* 8 bits */
+ #define VIRTIO_DEV_OFFS_NUM_VQ		1	/* 8 bits */
+@@ -47,6 +48,7 @@ typedef struct VirtIOS390Device {
+     uint32_t host_features;
+     virtio_serial_conf serial;
+     virtio_net_conf net;
++    VirtIOSCSIConf scsi;
+ } VirtIOS390Device;
+ 
+ typedef struct VirtIOS390Bus {
+diff -ruNp qemu-kvm-1.0/hw/scsi-bus.c qemu-kvm-1.0.virtio-scsi/hw/scsi-bus.c
+--- qemu-kvm-1.0/hw/scsi-bus.c	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/scsi-bus.c	2012-02-07 14:44:53.428905315 -0600
+@@ -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);
+@@ -50,6 +51,7 @@ static void scsi_dma_restart_bh(void *op
+                 scsi_req_continue(req);
+                 break;
+             case SCSI_XFER_NONE:
++                assert(!req->sg);
+                 scsi_req_dequeue(req);
+                 scsi_req_enqueue(req);
+                 break;
+@@ -512,6 +514,8 @@ SCSIRequest *scsi_req_new(SCSIDevice *d,
+     }
+ 
+     req->cmd = cmd;
++    req->resid = req->cmd.xfer;
++
+     switch (buf[0]) {
+     case INQUIRY:
+         trace_scsi_inquiry(d->id, lun, tag, cmd.buf[1], cmd.buf[2]);
+@@ -624,15 +628,25 @@ void scsi_req_build_sense(SCSIRequest *r
+     req->sense_len = 18;
+ }
+ 
+-int32_t scsi_req_enqueue(SCSIRequest *req)
++static void scsi_req_enqueue_internal(SCSIRequest *req)
+ {
+-    int32_t rc;
+-
+     assert(!req->enqueued);
+     scsi_req_ref(req);
++    if (req->bus->info->get_sg_list) {
++        req->sg = req->bus->info->get_sg_list(req);
++    } else {
++        req->sg = NULL;
++    }
+     req->enqueued = true;
+     QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
++}
++
++int32_t scsi_req_enqueue(SCSIRequest *req)
++{
++    int32_t rc;
+ 
++    assert (!req->retry);
++    scsi_req_enqueue_internal(req);
+     scsi_req_ref(req);
+     rc = req->ops->send_command(req, req->cmd.buf);
+     scsi_req_unref(req);
+@@ -1254,12 +1268,32 @@ void scsi_req_continue(SCSIRequest *req)
+    Once it completes, calling scsi_req_continue will restart I/O.  */
+ void scsi_req_data(SCSIRequest *req, int len)
+ {
++    uint8_t *buf;
+     if (req->io_canceled) {
+         trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len);
+-    } else {
+-        trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
++        return;
++    }
++    trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
++    assert(req->cmd.mode != SCSI_XFER_NONE);
++    if (!req->sg) {
++        req->resid -= len;
+         req->bus->info->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->resid = dma_buf_read(buf, len, req->sg);
++    } else {
++        req->resid = dma_buf_write(buf, len, req->sg);
+     }
++    scsi_req_continue(req);
+ }
+ 
+ void scsi_req_print(SCSIRequest *req)
+@@ -1318,7 +1352,7 @@ void scsi_req_complete(SCSIRequest *req,
+ 
+     scsi_req_ref(req);
+     scsi_req_dequeue(req);
+-    req->bus->info->complete(req, req->status);
++    req->bus->info->complete(req, req->status, req->resid);
+     scsi_req_unref(req);
+ }
+ 
+@@ -1393,3 +1427,100 @@ SCSIDevice *scsi_device_find(SCSIBus *bu
+     }
+     return target_dev;
+ }
++
++
++/* SCSI request list.  For simplicity, pv points to the whole device */
++
++static void put_scsi_requests(QEMUFile *f, void *pv, size_t size)
++{
++    SCSIDevice *s = pv;
++    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
++    SCSIRequest *req;
++
++    QTAILQ_FOREACH(req, &s->requests, next) {
++        assert(!req->io_canceled);
++        assert(req->status == -1);
++        assert(req->retry);
++        assert(req->enqueued);
++
++        qemu_put_sbyte(f, 1);
++        qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf));
++        qemu_put_be32s(f, &req->tag);
++        qemu_put_be32s(f, &req->lun);
++        if (bus->info->save_request) {
++            bus->info->save_request(f, req);
++        }
++        if (req->ops->save_request) {
++            req->ops->save_request(f, req);
++        }
++    }
++    qemu_put_sbyte(f, 0);
++}
++
++static int get_scsi_requests(QEMUFile *f, void *pv, size_t size)
++{
++    SCSIDevice *s = pv;
++    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
++
++    while (qemu_get_sbyte(f)) {
++        uint8_t buf[SCSI_CMD_BUF_SIZE];
++        uint32_t tag;
++        uint32_t lun;
++        SCSIRequest *req;
++
++        qemu_get_buffer(f, buf, sizeof(buf));
++        qemu_get_be32s(f, &tag);
++        qemu_get_be32s(f, &lun);
++        req = scsi_req_new(s, tag, lun, buf, NULL);
++        if (bus->info->load_request) {
++            req->hba_private = bus->info->load_request(f, req);
++        }
++        if (req->ops->load_request) {
++            req->ops->load_request(f, req);
++        }
++
++        /* Just restart it later.  */
++        req->retry = true;
++        scsi_req_enqueue_internal(req);
++
++        /* At this point, the request will be kept alive by the reference
++         * added by scsi_req_enqueue_internal, so we can release our reference.
++         * The HBA of course will add its own reference in the load_request
++         * callback if it needs to hold on the SCSIRequest.
++         */
++        scsi_req_unref(req);
++    }
++
++    return 0;
++}
++
++const VMStateInfo vmstate_info_scsi_requests = {
++    .name = "scsi-requests",
++    .get  = get_scsi_requests,
++    .put  = put_scsi_requests,
++};
++
++const VMStateDescription vmstate_scsi_device = {
++    .name = "SCSIDevice",
++    .version_id = 1,
++    .minimum_version_id = 1,
++    .minimum_version_id_old = 1,
++    .fields = (VMStateField[]) {
++        VMSTATE_UINT8(unit_attention.key, SCSIDevice),
++        VMSTATE_UINT8(unit_attention.asc, SCSIDevice),
++        VMSTATE_UINT8(unit_attention.ascq, SCSIDevice),
++        VMSTATE_BOOL(sense_is_ua, SCSIDevice),
++        VMSTATE_UINT8_ARRAY(sense, SCSIDevice, SCSI_SENSE_BUF_SIZE),
++        VMSTATE_UINT32(sense_len, SCSIDevice),
++        {
++            .name         = "requests",
++            .version_id   = 0,
++            .field_exists = NULL,
++            .size         = 0,   /* ouch */
++            .info         = &vmstate_info_scsi_requests,
++            .flags        = VMS_SINGLE,
++            .offset       = 0,
++        },
++        VMSTATE_END_OF_LIST()
++    }
++};
+diff -ruNp qemu-kvm-1.0/hw/scsi-disk.c qemu-kvm-1.0.virtio-scsi/hw/scsi-disk.c
+--- qemu-kvm-1.0/hw/scsi-disk.c	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/scsi-disk.c	2012-02-07 14:44:53.429905331 -0600
+@@ -38,6 +38,7 @@ do { fprintf(stderr, "scsi-disk: " fmt ,
+ #include "sysemu.h"
+ #include "blockdev.h"
+ #include "block_int.h"
++#include "dma.h"
+ 
+ #ifdef __linux
+ #include <scsi/sg.h>
+@@ -110,12 +111,12 @@ static void scsi_cancel_io(SCSIRequest *
+     r->req.aiocb = NULL;
+ }
+ 
+-static uint32_t scsi_init_iovec(SCSIDiskReq *r)
++static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size)
+ {
+     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ 
+     if (!r->iov.iov_base) {
+-        r->buflen = SCSI_DMA_BUF_SIZE;
++        r->buflen = size;
+         r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
+     }
+     r->iov.iov_len = MIN(r->sector_count * 512, r->buflen);
+@@ -123,6 +124,56 @@ static uint32_t scsi_init_iovec(SCSIDisk
+     return r->qiov.size / 512;
+ }
+ 
++static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req)
++{
++    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
++
++    qemu_put_be64s(f, &r->sector);
++    qemu_put_be32s(f, &r->sector_count);
++    qemu_put_be32s(f, &r->buflen);
++    if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
++        qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
++    }
++}
++
++static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
++{
++    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
++
++    qemu_get_be64s(f, &r->sector);
++    qemu_get_be32s(f, &r->sector_count);
++    qemu_get_be32s(f, &r->buflen);
++    if (r->buflen) {
++        scsi_init_iovec(r, r->buflen);
++        if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
++            qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
++        }
++    }
++
++    qemu_iovec_init_external(&r->qiov, &r->iov, 1);
++}
++
++static void scsi_dma_complete(void * opaque, int ret)
++{
++    SCSIDiskReq *r = (SCSIDiskReq *)opaque;
++    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
++
++    bdrv_acct_done(s->qdev.conf.bs, &r->acct);
++
++    if (ret) {
++        if (scsi_handle_rw_error(r, -ret)) {
++            goto done;
++        }
++    }
++
++    r->sector += r->sector_count;
++    r->sector_count = 0;
++    scsi_req_complete(&r->req, GOOD);
++
++done:
++    scsi_req_unref(&r->req);
++}
++
+ static void scsi_read_complete(void * opaque, int ret)
+ {
+     SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+@@ -213,10 +264,17 @@ static void scsi_read_data(SCSIRequest *
+         return;
+     }
+ 
+-    n = scsi_init_iovec(r);
+-    bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
+-    r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
+-                              scsi_read_complete, r);
++    if (r->req.sg) {
++        dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
++	r->req.resid -= r->req.sg->size;
++        r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
++                                     scsi_dma_complete, r);
++    } else {
++        n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
++        bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
++        r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
++                                      scsi_read_complete, r);
++    }
+     if (r->req.aiocb == NULL) {
+         scsi_read_complete(r, -EIO);
+     }
+@@ -290,7 +348,7 @@ static void scsi_write_complete(void * o
+     if (r->sector_count == 0) {
+         scsi_req_complete(&r->req, GOOD);
+     } else {
+-        scsi_init_iovec(r);
++        scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
+         DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size);
+         scsi_req_data(&r->req, r->qiov.size);
+     }
+@@ -318,21 +376,29 @@ static void scsi_write_data(SCSIRequest
+         return;
+     }
+ 
+-    n = r->qiov.size / 512;
+-    if (n) {
+-        if (s->tray_open) {
+-            scsi_write_complete(r, -ENOMEDIUM);
+-            return;
+-        }
++    if (!r->req.sg && !r->qiov.size) {
++        /* Called for the first time.  Ask the driver to send us more data.  */
++        scsi_write_complete(r, 0);
++        return;
++    }
++    if (s->tray_open) {
++        scsi_write_complete(r, -ENOMEDIUM);
++        return;
++    }
++
++    if (r->req.sg) {
++        dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE);
++	r->req.resid -= r->req.sg->size;
++        r->req.aiocb = dma_bdrv_write(s->qdev.conf.bs, r->req.sg, r->sector,
++                                      scsi_dma_complete, r);
++    } else {
++        n = r->qiov.size / 512;
+         bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE);
+         r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n,
+                                        scsi_write_complete, r);
+-        if (r->req.aiocb == NULL) {
+-            scsi_write_complete(r, -ENOMEM);
+-        }
+-    } else {
+-        /* Called for the first time.  Ask the driver to send us more data.  */
+-        scsi_write_complete(r, 0);
++    }
++    if (r->req.aiocb == NULL) {
++        scsi_write_complete(r, -ENOMEM);
+     }
+ }
+ 
+@@ -1601,6 +1667,8 @@ static const SCSIReqOps scsi_disk_reqops
+     .write_data   = scsi_write_data,
+     .cancel_io    = scsi_cancel_io,
+     .get_buf      = scsi_get_buf,
++    .load_request = scsi_disk_load_request,
++    .save_request = scsi_disk_save_request,
+ };
+ 
+ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
+@@ -1729,6 +1797,22 @@ static SCSIRequest *scsi_block_new_reque
+     DEFINE_PROP_STRING("ver",  SCSIDiskState, version),         \
+     DEFINE_PROP_STRING("serial",  SCSIDiskState, serial)
+ 
++static const VMStateDescription vmstate_scsi_disk_state = {
++    .name = "scsi-disk",
++    .version_id = 1,
++    .minimum_version_id = 1,
++    .minimum_version_id_old = 1,
++    .fields = (VMStateField[]) {
++        VMSTATE_SCSI_DEVICE(qdev, SCSIDiskState),
++        VMSTATE_BOOL(media_changed, SCSIDiskState),
++        VMSTATE_BOOL(media_event, SCSIDiskState),
++        VMSTATE_BOOL(eject_request, SCSIDiskState),
++        VMSTATE_BOOL(tray_open, SCSIDiskState),
++        VMSTATE_BOOL(tray_locked, SCSIDiskState),
++        VMSTATE_END_OF_LIST()
++    }
++};
++
+ static SCSIDeviceInfo scsi_disk_info[] = {
+     {
+         .qdev.name    = "scsi-hd",
+@@ -1736,6 +1820,7 @@ static SCSIDeviceInfo scsi_disk_info[] =
+         .qdev.desc    = "virtual SCSI disk",
+         .qdev.size    = sizeof(SCSIDiskState),
+         .qdev.reset   = scsi_disk_reset,
++        .qdev.vmsd    = &vmstate_scsi_disk_state,
+         .init         = scsi_hd_initfn,
+         .destroy      = scsi_destroy,
+         .alloc_req    = scsi_new_request,
+@@ -1751,6 +1836,7 @@ static SCSIDeviceInfo scsi_disk_info[] =
+         .qdev.desc    = "virtual SCSI CD-ROM",
+         .qdev.size    = sizeof(SCSIDiskState),
+         .qdev.reset   = scsi_disk_reset,
++        .qdev.vmsd    = &vmstate_scsi_disk_state,
+         .init         = scsi_cd_initfn,
+         .destroy      = scsi_destroy,
+         .alloc_req    = scsi_new_request,
+@@ -1766,6 +1852,7 @@ static SCSIDeviceInfo scsi_disk_info[] =
+         .qdev.desc    = "SCSI block device passthrough",
+         .qdev.size    = sizeof(SCSIDiskState),
+         .qdev.reset   = scsi_disk_reset,
++        .qdev.vmsd    = &vmstate_scsi_disk_state,
+         .init         = scsi_block_initfn,
+         .destroy      = scsi_destroy,
+         .alloc_req    = scsi_block_new_request,
+@@ -1780,6 +1867,7 @@ static SCSIDeviceInfo scsi_disk_info[] =
+         .qdev.desc    = "virtual SCSI disk or CD-ROM (legacy)",
+         .qdev.size    = sizeof(SCSIDiskState),
+         .qdev.reset   = scsi_disk_reset,
++        .qdev.vmsd    = &vmstate_scsi_disk_state,
+         .init         = scsi_disk_initfn,
+         .destroy      = scsi_destroy,
+         .alloc_req    = scsi_new_request,
+diff -ruNp qemu-kvm-1.0/hw/scsi-generic.c qemu-kvm-1.0.virtio-scsi/hw/scsi-generic.c
+--- qemu-kvm-1.0/hw/scsi-generic.c	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/scsi-generic.c	2012-02-07 14:44:53.430905347 -0600
+@@ -59,6 +59,28 @@ typedef struct SCSIGenericReq {
+     sg_io_hdr_t io_header;
+ } SCSIGenericReq;
+ 
++static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
++{
++    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
++
++    qemu_put_sbe32s(f, &r->buflen);
++    if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
++        assert(!r->req.sg);
++        qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
++    }
++}
++
++static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
++{
++    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
++
++    qemu_get_sbe32s(f, &r->buflen);
++    if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
++        assert(!r->req.sg);
++        qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
++    }
++}
++
+ static void scsi_free_request(SCSIRequest *req)
+ {
+     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+@@ -450,6 +472,8 @@ const SCSIReqOps scsi_generic_req_ops =
+     .write_data   = scsi_write_data,
+     .cancel_io    = scsi_cancel_io,
+     .get_buf      = scsi_get_buf,
++    .load_request = scsi_generic_load_request,
++    .save_request = scsi_generic_save_request,
+ };
+ 
+ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
+@@ -467,6 +491,7 @@ static SCSIDeviceInfo scsi_generic_info
+     .qdev.desc    = "pass through generic scsi device (/dev/sg*)",
+     .qdev.size    = sizeof(SCSIDevice),
+     .qdev.reset   = scsi_generic_reset,
++    .qdev.vmsd    = &vmstate_scsi_device,
+     .init         = scsi_generic_initfn,
+     .destroy      = scsi_destroy,
+     .alloc_req    = scsi_new_request,
+diff -ruNp qemu-kvm-1.0/hw/scsi.h qemu-kvm-1.0.virtio-scsi/hw/scsi.h
+--- qemu-kvm-1.0/hw/scsi.h	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/scsi.h	2012-02-07 14:44:53.430905347 -0600
+@@ -47,8 +47,11 @@ struct SCSIRequest {
+     uint32_t          tag;
+     uint32_t          lun;
+     uint32_t          status;
++    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;
+@@ -78,6 +81,16 @@ struct SCSIDevice
+     uint64_t max_lba;
+ };
+ 
++extern const VMStateDescription vmstate_scsi_device;
++
++#define VMSTATE_SCSI_DEVICE(_field, _state) {                        \
++    .name       = (stringify(_field)),                               \
++    .size       = sizeof(SCSIDevice),                                \
++    .vmsd       = &vmstate_scsi_device,                              \
++    .flags      = VMS_STRUCT,                                        \
++    .offset     = vmstate_offset_value(_state, _field, SCSIDevice),  \
++}
++
+ /* cdrom.c */
+ int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
+ int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
+@@ -91,6 +104,9 @@ struct SCSIReqOps {
+     void (*write_data)(SCSIRequest *req);
+     void (*cancel_io)(SCSIRequest *req);
+     uint8_t *(*get_buf)(SCSIRequest *req);
++
++    void (*save_request)(QEMUFile *f, SCSIRequest *req);
++    void (*load_request)(QEMUFile *f, SCSIRequest *req);
+ };
+ 
+ typedef int (*scsi_qdev_initfn)(SCSIDevice *dev);
+@@ -107,8 +123,12 @@ struct SCSIBusInfo {
+     int tcq;
+     int max_channel, max_target, max_lun;
+     void (*transfer_data)(SCSIRequest *req, uint32_t arg);
+-    void (*complete)(SCSIRequest *req, uint32_t arg);
++    void (*complete)(SCSIRequest *req, uint32_t arg, int32_t len);
+     void (*cancel)(SCSIRequest *req);
++    QEMUSGList *(*get_sg_list)(SCSIRequest *req);
++
++    void (*save_request)(QEMUFile *f, SCSIRequest *req);
++    void *(*load_request)(QEMUFile *f, SCSIRequest *req);
+ };
+ 
+ struct SCSIBus {
+diff -ruNp qemu-kvm-1.0/hw/spapr_vscsi.c qemu-kvm-1.0.virtio-scsi/hw/spapr_vscsi.c
+--- qemu-kvm-1.0/hw/spapr_vscsi.c	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/spapr_vscsi.c	2012-02-07 14:44:53.430905347 -0600
+@@ -494,7 +494,7 @@ static void vscsi_transfer_data(SCSIRequ
+ }
+ 
+ /* Callback to indicate that the SCSI layer has completed a transfer.  */
+-static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
++static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status, int32_t resid)
+ {
+     VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
+     vscsi_req *req = sreq->hba_private;
+diff -ruNp qemu-kvm-1.0/hw/usb-msd.c qemu-kvm-1.0.virtio-scsi/hw/usb-msd.c
+--- qemu-kvm-1.0/hw/usb-msd.c	2012-02-07 14:44:04.881123501 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/usb-msd.c	2012-02-07 14:44:53.431905363 -0600
+@@ -223,7 +223,7 @@ static void usb_msd_transfer_data(SCSIRe
+     }
+ }
+ 
+-static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
++static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, int32_t resid)
+ {
+     MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
+     USBPacket *p = s->packet;
+diff -ruNp qemu-kvm-1.0/hw/virtio.h qemu-kvm-1.0.virtio-scsi/hw/virtio.h
+--- qemu-kvm-1.0/hw/virtio.h	2011-12-04 04:38:06.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/virtio.h	2012-02-07 14:44:53.433905395 -0600
+@@ -199,6 +199,8 @@ VirtIODevice *virtio_net_init(DeviceStat
+ typedef struct virtio_serial_conf virtio_serial_conf;
+ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial);
+ VirtIODevice *virtio_balloon_init(DeviceState *dev);
++typedef struct VirtIOSCSIConf VirtIOSCSIConf;
++VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf);
+ #ifdef CONFIG_LINUX
+ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
+ #endif
+@@ -208,6 +210,7 @@ void virtio_net_exit(VirtIODevice *vdev)
+ void virtio_blk_exit(VirtIODevice *vdev);
+ void virtio_serial_exit(VirtIODevice *vdev);
+ void virtio_balloon_exit(VirtIODevice *vdev);
++void virtio_scsi_exit(VirtIODevice *vdev);
+ 
+ #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
+ 	DEFINE_PROP_BIT("indirect_desc", _state, _field, \
+diff -ruNp qemu-kvm-1.0/hw/virtio-pci.c qemu-kvm-1.0.virtio-scsi/hw/virtio-pci.c
+--- qemu-kvm-1.0/hw/virtio-pci.c	2012-02-07 14:44:04.850123002 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/virtio-pci.c	2012-02-07 14:44:53.432905379 -0600
+@@ -19,6 +19,7 @@
+ #include "virtio-blk.h"
+ #include "virtio-net.h"
+ #include "virtio-serial.h"
++#include "virtio-scsi.h"
+ #include "pci.h"
+ #include "qemu-error.h"
+ #include "msix.h"
+@@ -855,6 +856,32 @@ static int virtio_balloon_exit_pci(PCIDe
+     return virtio_exit_pci(pci_dev);
+ }
+ 
++static int virtio_scsi_init_pci(PCIDevice *pci_dev)
++{
++    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
++    VirtIODevice *vdev;
++
++    vdev = virtio_scsi_init(&pci_dev->qdev, &proxy->scsi);
++    if (!vdev) {
++        return -EINVAL;
++    }
++
++    vdev->nvectors = proxy->nvectors;
++    virtio_init_pci(proxy, vdev);
++
++    /* make the actual value visible */
++    proxy->nvectors = vdev->nvectors;
++    return 0;
++}
++
++static int virtio_scsi_exit_pci(PCIDevice *pci_dev)
++{
++    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
++
++    virtio_scsi_exit(proxy->vdev);
++    return virtio_exit_pci(pci_dev);
++}
++
+ static PCIDeviceInfo virtio_info[] = {
+     {
+         .qdev.name = "virtio-blk-pci",
+@@ -940,6 +967,21 @@ static PCIDeviceInfo virtio_info[] = {
+         },
+         .qdev.reset = virtio_pci_reset,
+     },{
++        .qdev.name = "virtio-scsi-pci",
++        .qdev.alias = "virtio-scsi",
++        .qdev.size = sizeof(VirtIOPCIProxy),
++        .init      = virtio_scsi_init_pci,
++        .exit      = virtio_scsi_exit_pci,
++        .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET,
++        .device_id = PCI_DEVICE_ID_VIRTIO_SCSI,
++        .class_id  = PCI_CLASS_STORAGE_SCSI,
++        .revision  = 0x00,
++        .qdev.props = (Property[]) {
++            DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
++            DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOPCIProxy, host_features, scsi),
++            DEFINE_PROP_END_OF_LIST(),
++        },
++    }, {
+         /* end of list */
+     }
+ };
+diff -ruNp qemu-kvm-1.0/hw/virtio-pci.h qemu-kvm-1.0.virtio-scsi/hw/virtio-pci.h
+--- qemu-kvm-1.0/hw/virtio-pci.h	2012-02-07 14:44:04.850123002 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/virtio-pci.h	2012-02-07 14:44:53.432905379 -0600
+@@ -17,6 +17,7 @@
+ 
+ #include "virtio-net.h"
+ #include "virtio-serial.h"
++#include "virtio-scsi.h"
+ 
+ /* Performance improves when virtqueue kick processing is decoupled from the
+  * vcpu thread using ioeventfd for some devices. */
+@@ -40,6 +41,7 @@ typedef struct {
+ #endif
+     virtio_serial_conf serial;
+     virtio_net_conf net;
++    VirtIOSCSIConf scsi;
+     bool ioeventfd_disabled;
+     bool ioeventfd_started;
+ } VirtIOPCIProxy;
+diff -ruNp qemu-kvm-1.0/hw/virtio-scsi.c qemu-kvm-1.0.virtio-scsi/hw/virtio-scsi.c
+--- qemu-kvm-1.0/hw/virtio-scsi.c	1969-12-31 18:00:00.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/virtio-scsi.c	2012-02-07 14:44:53.432905379 -0600
+@@ -0,0 +1,607 @@
++/*
++ * Virtio SCSI HBA
++ *
++ * Copyright IBM, Corp. 2010
++ * Copyright Red Hat, Inc. 2011
++ *
++ * Authors:
++ *   Stefan Hajnoczi    <stefanha at linux.vnet.ibm.com>
++ *   Paolo Bonzini      <pbonzini at redhat.com>
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2 or later.
++ * See the COPYING file in the top-level directory.
++ *
++ */
++
++#include "virtio-scsi.h"
++#include <hw/scsi.h>
++#include <hw/scsi-defs.h>
++
++#define VIRTIO_SCSI_VQ_SIZE     128
++#define VIRTIO_SCSI_CDB_SIZE    32
++#define VIRTIO_SCSI_SENSE_SIZE  96
++#define VIRTIO_SCSI_MAX_CHANNEL	0
++#define VIRTIO_SCSI_MAX_TARGET  255
++#define VIRTIO_SCSI_MAX_LUN     16383
++
++/* Response codes */
++#define VIRTIO_SCSI_S_OK                       0
++#define VIRTIO_SCSI_S_OVERRUN                  1
++#define VIRTIO_SCSI_S_ABORTED                  2
++#define VIRTIO_SCSI_S_BAD_TARGET               3
++#define VIRTIO_SCSI_S_RESET                    4
++#define VIRTIO_SCSI_S_BUSY                     5
++#define VIRTIO_SCSI_S_TRANSPORT_FAILURE        6
++#define VIRTIO_SCSI_S_TARGET_FAILURE           7
++#define VIRTIO_SCSI_S_NEXUS_FAILURE            8
++#define VIRTIO_SCSI_S_FAILURE                  9
++#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED       10
++#define VIRTIO_SCSI_S_FUNCTION_REJECTED        11
++#define VIRTIO_SCSI_S_INCORRECT_LUN            12
++
++/* Controlq type codes.  */
++#define VIRTIO_SCSI_T_TMF                      0
++#define VIRTIO_SCSI_T_AN_QUERY                 1
++#define VIRTIO_SCSI_T_AN_SUBSCRIBE             2
++
++/* Valid TMF subtypes.  */
++#define VIRTIO_SCSI_T_TMF_ABORT_TASK           0
++#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET       1
++#define VIRTIO_SCSI_T_TMF_CLEAR_ACA            2
++#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET       3
++#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET      4
++#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET   5
++#define VIRTIO_SCSI_T_TMF_QUERY_TASK           6
++#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET       7
++
++/* Events.  */
++#define VIRTIO_SCSI_T_EVENTS_MISSED            0x80000000
++#define VIRTIO_SCSI_T_NO_EVENT                 0
++#define VIRTIO_SCSI_T_TRANSPORT_RESET          1
++#define VIRTIO_SCSI_T_ASYNC_NOTIFY             2
++
++/* SCSI command request, followed by data-out */
++typedef struct {
++    uint8_t lun[8];              /* Logical Unit Number */
++    uint64_t tag;                /* Command identifier */
++    uint8_t task_attr;           /* Task attribute */
++    uint8_t prio;
++    uint8_t crn;
++    uint8_t cdb[];
++} QEMU_PACKED VirtIOSCSICmdReq;
++
++/* Response, followed by sense data and data-in */
++typedef struct {
++    uint32_t sense_len;          /* Sense data length */
++    uint32_t resid;              /* Residual bytes in data buffer */
++    uint16_t status_qualifier;   /* Status qualifier */
++    uint8_t status;              /* Command completion status */
++    uint8_t response;            /* Response values */
++    uint8_t sense[];
++} QEMU_PACKED VirtIOSCSICmdResp;
++
++/* Task Management Request */
++typedef struct {
++    uint32_t type;
++    uint32_t subtype;
++    uint8_t lun[8];
++    uint64_t tag;
++} QEMU_PACKED VirtIOSCSICtrlTMFReq;
++
++typedef struct {
++    uint8_t response;
++} QEMU_PACKED VirtIOSCSICtrlTMFResp;
++
++/* Asynchronous notification query/subscription */
++typedef struct {
++    uint32_t type;
++    uint8_t lun[8];
++    uint32_t event_requested;
++} QEMU_PACKED VirtIOSCSICtrlANReq;
++
++typedef struct {
++    uint32_t event_actual;
++    uint8_t response;
++} QEMU_PACKED VirtIOSCSICtrlANResp;
++
++typedef struct {
++    uint32_t event;
++    uint8_t lun[8];
++    uint32_t reason;
++} QEMU_PACKED VirtIOSCSIEvent;
++
++typedef struct {
++    uint32_t num_queues;
++    uint32_t seg_max;
++    uint32_t max_sectors;
++    uint32_t cmd_per_lun;
++    uint32_t event_info_size;
++    uint32_t sense_size;
++    uint32_t cdb_size;
++    uint16_t max_channel;
++    uint16_t max_target;
++    uint32_t max_lun;
++} QEMU_PACKED VirtIOSCSIConfig;
++
++typedef struct {
++    VirtIODevice vdev;
++    DeviceState *qdev;
++    VirtIOSCSIConf *conf;
++
++    SCSIBus bus;
++    VirtQueue *ctrl_vq;
++    VirtQueue *event_vq;
++    VirtQueue *cmd_vq;
++    uint32_t sense_size;
++    uint32_t cdb_size;
++    bool resetting;
++} VirtIOSCSI;
++
++typedef struct VirtIOSCSIReq {
++    VirtIOSCSI *dev;
++    VirtQueue *vq;
++    VirtQueueElement elem;
++    QEMUSGList qsgl;
++    SCSIRequest *sreq;
++    union {
++        char                  *buf;
++        VirtIOSCSICmdReq      *cmd;
++        VirtIOSCSICtrlTMFReq  *tmf;
++        VirtIOSCSICtrlANReq   *an;
++    } req;
++    union {
++        char                  *buf;
++        VirtIOSCSICmdResp     *cmd;
++        VirtIOSCSICtrlTMFResp *tmf;
++        VirtIOSCSICtrlANResp  *an;
++        VirtIOSCSIEvent       *event;
++    } resp;
++} VirtIOSCSIReq;
++
++static inline int virtio_scsi_get_lun(uint8_t *lun)
++{
++    return ((lun[2] << 8) | lun[3]) & 0x3FFF;
++}
++
++static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun)
++{
++    if (lun[0] != 1) {
++        return NULL;
++    }
++    if (lun[2] != 0 && !(lun[2] >= 0x40 && lun[2] < 0x80)) {
++        return NULL;
++    }
++    return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
++}
++
++static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
++{
++    VirtIOSCSI *s = req->dev;
++    VirtQueue *vq = req->vq;
++    virtqueue_push(vq, &req->elem, req->qsgl.size + req->elem.in_sg[0].iov_len);
++    qemu_sglist_destroy(&req->qsgl);
++    if (req->sreq) {
++        req->sreq->hba_private = NULL;
++        scsi_req_unref(req->sreq);
++    }
++    g_free(req);
++    virtio_notify(&s->vdev, vq);
++}
++
++static void virtio_scsi_bad_req(void)
++{
++    error_report("wrong size for virtio-scsi headers");
++    exit(1);
++}
++
++static void qemu_sgl_init_external(QEMUSGList *qsgl, struct iovec *sg,
++                                   target_phys_addr_t *addr, int num)
++{
++    memset(qsgl, 0, sizeof(*qsgl));
++    while (num--) {
++        qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len);
++    }
++}
++
++static void virtio_scsi_parse_req(VirtIOSCSI *s, VirtQueue *vq,
++                                  VirtIOSCSIReq *req)
++{
++    assert(req->elem.out_num && req->elem.in_num);
++    req->vq = vq;
++    req->dev = s;
++    req->sreq = NULL;
++    req->req.buf = req->elem.out_sg[0].iov_base;
++    req->resp.buf = req->elem.in_sg[0].iov_base;
++
++    if (req->elem.out_num > 1) {
++        qemu_sgl_init_external(&req->qsgl, &req->elem.out_sg[1],
++                               &req->elem.out_addr[1],
++                               req->elem.out_num - 1);
++    } else {
++        qemu_sgl_init_external(&req->qsgl, &req->elem.in_sg[1],
++                               &req->elem.in_addr[1],
++                               req->elem.in_num - 1);
++    }
++}
++
++static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
++{
++    VirtIOSCSIReq *req;
++    req = g_malloc(sizeof(*req));
++    if (!virtqueue_pop(vq, &req->elem)) {
++        g_free(req);
++        return NULL;
++    }
++
++    virtio_scsi_parse_req(s, vq, req);
++    return req;
++}
++
++static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq)
++{
++    VirtIOSCSIReq *req = sreq->hba_private;
++
++    qemu_put_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
++}
++
++static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
++{
++    SCSIBus *bus = sreq->bus;
++    VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
++    VirtIOSCSIReq *req;
++
++    req = g_malloc(sizeof(*req));
++    qemu_get_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
++    virtio_scsi_parse_req(s, s->cmd_vq, req);
++
++    scsi_req_ref(sreq);
++    req->sreq = sreq;
++    if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
++        int req_mode =
++            (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV);
++
++	assert (req->sreq->cmd.mode == req_mode);
++    }
++    return req;
++}
++
++static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
++{
++    SCSIDevice *d = virtio_scsi_device_find(s, req->req.cmd->lun);
++    SCSIRequest *r, *next;
++    DeviceState *qdev;
++    int target;
++
++    switch (req->req.tmf->subtype) {
++    case VIRTIO_SCSI_T_TMF_ABORT_TASK:
++    case VIRTIO_SCSI_T_TMF_QUERY_TASK:
++        d = virtio_scsi_device_find(s, req->req.cmd->lun);
++        if (!d) {
++            goto fail;
++        }
++        if (d->lun != virtio_scsi_get_lun(req->req.cmd->lun)) {
++            req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN;
++            break;
++        }
++        QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
++            if (r->tag == req->req.cmd->tag) {
++                break;
++            }
++        }
++        if (r && r->hba_private) {
++            if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_ABORT_TASK) {
++                scsi_req_cancel(r);
++            }
++            req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
++        } else {
++            req->resp.tmf->response = VIRTIO_SCSI_S_OK;
++        }
++        break;
++
++    case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET:
++        d = virtio_scsi_device_find(s, req->req.cmd->lun);
++        if (!d) {
++            goto fail;
++        }
++        if (d->lun == virtio_scsi_get_lun(req->req.cmd->lun)) {
++            s->resetting++;
++            qdev_reset_all(&d->qdev);
++            s->resetting--;
++        }
++        break;
++
++    case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET:
++    case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET:
++    case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET:
++        d = virtio_scsi_device_find(s, req->req.cmd->lun);
++        if (!d) {
++            goto fail;
++        }
++        if (d->lun != virtio_scsi_get_lun(req->req.cmd->lun)) {
++            req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN;
++            break;
++        }
++        req->resp.tmf->response = VIRTIO_SCSI_S_OK;
++        QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
++            if (r->hba_private) {
++                if (req->req.tmf->subtype != VIRTIO_SCSI_T_TMF_QUERY_TASK) {
++                    scsi_req_cancel(r);
++                }
++                req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
++            }
++        }
++        break;
++
++    case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
++        target = req->req.cmd->lun[1];
++        s->resetting++;
++        QTAILQ_FOREACH(qdev, &s->bus.qbus.children, sibling) {
++             d = DO_UPCAST(SCSIDevice, qdev, qdev);
++             if (d->channel == 0 && d->id == target) {
++                qdev_reset_all(&d->qdev);
++             }
++        }
++        s->resetting--;
++        break;
++
++    case VIRTIO_SCSI_T_TMF_CLEAR_ACA:
++    default:
++        req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_REJECTED;
++        break;
++    }
++
++    return;
++
++fail:
++    req->resp.tmf->response = VIRTIO_SCSI_S_BAD_TARGET;
++}
++
++static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
++{
++    VirtIOSCSI *s = (VirtIOSCSI *)vdev;
++    VirtIOSCSIReq *req;
++
++    while ((req = virtio_scsi_pop_req(s, vq))) {
++        int out_size, in_size;
++        if (req->elem.out_num < 1 || req->elem.in_num < 1) {
++            virtio_scsi_bad_req();
++            continue;
++        }
++
++        out_size = req->elem.out_sg[0].iov_len;
++        in_size = req->elem.in_sg[0].iov_len;
++        if (req->req.tmf->type == VIRTIO_SCSI_T_TMF) {
++            if (out_size < sizeof(VirtIOSCSICtrlTMFReq) ||
++                in_size < sizeof(VirtIOSCSICtrlTMFResp)) {
++                virtio_scsi_bad_req();
++            }
++            virtio_scsi_do_tmf(s, req);
++
++        } else if (req->req.tmf->type == VIRTIO_SCSI_T_AN_QUERY ||
++                   req->req.tmf->type == VIRTIO_SCSI_T_AN_SUBSCRIBE) {
++            if (out_size < sizeof(VirtIOSCSICtrlANReq) ||
++                in_size < sizeof(VirtIOSCSICtrlANResp)) {
++                virtio_scsi_bad_req();
++            }
++            req->resp.an->event_actual = 0;
++            req->resp.an->response = VIRTIO_SCSI_S_OK;
++        }
++        virtio_scsi_complete_req(req);
++    }
++}
++
++static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
++                                         int32_t resid)
++{
++    VirtIOSCSIReq *req = r->hba_private;
++
++    req->resp.cmd->response = VIRTIO_SCSI_S_OK;
++    req->resp.cmd->status = status;
++    if (req->resp.cmd->status == GOOD) {
++        req->resp.cmd->resid = resid;
++    } else {
++       req->resp.cmd->resid = 0;
++       scsi_req_get_sense(r, req->resp.cmd->sense, VIRTIO_SCSI_SENSE_SIZE);
++    }
++    virtio_scsi_complete_req(req);
++}
++
++static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r)
++{
++    VirtIOSCSIReq *req = r->hba_private;
++
++    return &req->qsgl;
++}
++
++static void virtio_scsi_request_cancelled(SCSIRequest *r)
++{
++    VirtIOSCSIReq *req = r->hba_private;
++
++    if (!req) {
++        return;
++    }
++    if (req->dev->resetting) {
++        req->resp.cmd->response = VIRTIO_SCSI_S_RESET;
++    } else {
++        req->resp.cmd->response = VIRTIO_SCSI_S_ABORTED;
++    }
++    virtio_scsi_complete_req(req);
++}
++
++static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req)
++{
++    req->resp.cmd->response = VIRTIO_SCSI_S_FAILURE;
++    virtio_scsi_complete_req(req);
++}
++
++static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
++{
++    VirtIOSCSI *s = (VirtIOSCSI *)vdev;
++    VirtIOSCSIReq *req;
++    int n;
++
++    while ((req = virtio_scsi_pop_req(s, vq))) {
++        SCSIDevice *d;
++        int out_size, in_size;
++        if (req->elem.out_num < 1 || req->elem.in_num < 1) {
++            virtio_scsi_bad_req();
++        }
++
++        out_size = req->elem.out_sg[0].iov_len;
++        in_size = req->elem.in_sg[0].iov_len;
++        if (out_size < sizeof(VirtIOSCSICmdReq) + s->cdb_size ||
++            in_size < sizeof(VirtIOSCSICmdResp) + s->sense_size) {
++            virtio_scsi_bad_req();
++        }
++
++        if (req->elem.out_num > 1 && req->elem.in_num > 1) {
++            virtio_scsi_fail_cmd_req(req);
++            continue;
++        }
++
++        d = virtio_scsi_device_find(s, req->req.cmd->lun);
++        if (!d) {
++            req->resp.cmd->response = VIRTIO_SCSI_S_BAD_TARGET;
++            virtio_scsi_complete_req(req);
++            continue;
++        }
++        req->sreq = scsi_req_new(d, req->req.cmd->tag,
++                                 virtio_scsi_get_lun(req->req.cmd->lun),
++                                 req->req.cmd->cdb, req);
++
++        if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
++            int req_mode =
++                (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV);
++
++            if (req->sreq->cmd.mode != req_mode ||
++                req->sreq->cmd.xfer > req->qsgl.size) {
++                req->resp.cmd->response = VIRTIO_SCSI_S_OVERRUN;
++                virtio_scsi_complete_req(req);
++                continue;
++            }
++        }
++
++        n = scsi_req_enqueue(req->sreq);
++        if (n) {
++            scsi_req_continue(req->sreq);
++        }
++    }
++}
++
++static void virtio_scsi_get_config(VirtIODevice *vdev,
++                                   uint8_t *config)
++{
++    VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config;
++    VirtIOSCSI *s = (VirtIOSCSI *)vdev;
++
++    stl_raw(&scsiconf->num_queues, s->conf->num_queues);
++    stl_raw(&scsiconf->seg_max, 128 - 2);
++    stl_raw(&scsiconf->max_sectors, s->conf->max_sectors);
++    stl_raw(&scsiconf->cmd_per_lun, s->conf->cmd_per_lun);
++    stl_raw(&scsiconf->event_info_size, sizeof(VirtIOSCSIEvent));
++    stl_raw(&scsiconf->sense_size, s->sense_size);
++    stl_raw(&scsiconf->cdb_size, s->cdb_size);
++    stl_raw(&scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL);
++    stl_raw(&scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET);
++    stl_raw(&scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN);
++}
++
++static void virtio_scsi_set_config(VirtIODevice *vdev,
++                                   const uint8_t *config)
++{
++    VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config;
++    VirtIOSCSI *s = (VirtIOSCSI *)vdev;
++
++    if ((uint32_t) ldl_raw(&scsiconf->sense_size) >= 65536 ||
++        (uint32_t) ldl_raw(&scsiconf->cdb_size) >= 256) {
++        error_report("bad data written to virtio-scsi configuration space");
++        exit(1);
++    }
++
++    s->sense_size = ldl_raw(&scsiconf->sense_size);
++    s->cdb_size = ldl_raw(&scsiconf->cdb_size);
++}
++
++static uint32_t virtio_scsi_get_features(VirtIODevice *vdev,
++                                         uint32_t requested_features)
++{
++    return requested_features;
++}
++
++static void virtio_scsi_reset(VirtIODevice *vdev)
++{
++    VirtIOSCSI *s = (VirtIOSCSI *)vdev;
++
++    s->sense_size = VIRTIO_SCSI_SENSE_SIZE;
++    s->cdb_size = VIRTIO_SCSI_CDB_SIZE;
++}
++
++/* The device does not have anything to save beyond the virtio data.
++ * Request data is saved with callbacks from SCSI devices.
++ */
++static void virtio_scsi_save(QEMUFile *f, void *opaque)
++{
++    VirtIOSCSI *s = opaque;
++    virtio_save(&s->vdev, f);
++}
++
++static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id)
++{
++    VirtIOSCSI *s = opaque;
++    virtio_load(&s->vdev, f);
++    return 0;
++}
++
++static struct SCSIBusInfo virtio_scsi_scsi_info = {
++    .tcq = true,
++    .max_channel = VIRTIO_SCSI_MAX_CHANNEL,
++    .max_target = VIRTIO_SCSI_MAX_TARGET,
++    .max_lun = VIRTIO_SCSI_MAX_LUN,
++
++    .complete = virtio_scsi_command_complete,
++    .cancel = virtio_scsi_request_cancelled,
++    .get_sg_list = virtio_scsi_get_sg_list,
++    .save_request = virtio_scsi_save_request,
++    .load_request = virtio_scsi_load_request,
++};
++
++VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf)
++{
++    VirtIOSCSI *s;
++    static int virtio_scsi_id;
++
++    s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI,
++                                         sizeof(VirtIOSCSIConfig),
++                                         sizeof(VirtIOSCSI));
++
++    s->qdev = dev;
++    s->conf = proxyconf;
++
++    /* TODO set up vdev function pointers */
++    s->vdev.get_config = virtio_scsi_get_config;
++    s->vdev.set_config = virtio_scsi_set_config;
++    s->vdev.get_features = virtio_scsi_get_features;
++    s->vdev.reset = virtio_scsi_reset;
++
++    s->ctrl_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
++                                   virtio_scsi_handle_ctrl);
++    s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
++                                   NULL);
++    s->cmd_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
++                                   virtio_scsi_handle_cmd);
++
++    scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info);
++    if (!dev->hotplugged) {
++        scsi_bus_legacy_handle_cmdline(&s->bus);
++    }
++
++    register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1,
++                    virtio_scsi_save, virtio_scsi_load, s);
++
++    return &s->vdev;
++}
++
++void virtio_scsi_exit(VirtIODevice *vdev)
++{
++    virtio_cleanup(vdev);
++}
+diff -ruNp qemu-kvm-1.0/hw/virtio-scsi.h qemu-kvm-1.0.virtio-scsi/hw/virtio-scsi.h
+--- qemu-kvm-1.0/hw/virtio-scsi.h	1969-12-31 18:00:00.000000000 -0600
++++ qemu-kvm-1.0.virtio-scsi/hw/virtio-scsi.h	2012-02-07 14:44:53.432905379 -0600
+@@ -0,0 +1,36 @@
++/*
++ * Virtio SCSI HBA
++ *
++ * Copyright IBM, Corp. 2010
++ *
++ * Authors:
++ *  Stefan Hajnoczi    <stefanha at linux.vnet.ibm.com>
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2.  See
++ * the COPYING file in the top-level directory.
++ *
++ */
++
++#ifndef _QEMU_VIRTIO_SCSI_H
++#define _QEMU_VIRTIO_SCSI_H
++
++#include "virtio.h"
++#include "net.h"
++#include "pci.h"
++
++/* The ID for virtio_scsi */
++#define VIRTIO_ID_SCSI  8
++
++struct VirtIOSCSIConf {
++    uint32_t num_queues;
++    uint32_t max_sectors;
++    uint32_t cmd_per_lun;
++};
++
++#define DEFINE_VIRTIO_SCSI_PROPERTIES(_state, _features_field, _conf_field) \
++    DEFINE_VIRTIO_COMMON_FEATURES(_state, _features_field), \
++    DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \
++    DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF), \
++    DEFINE_PROP_UINT32("cmd_per_lun", _state, _conf_field.cmd_per_lun, 128)
++
++#endif /* _QEMU_VIRTIO_SCSI_H */
+diff -ruNp qemu-kvm-1.0/Makefile.target qemu-kvm-1.0.virtio-scsi/Makefile.target
+--- qemu-kvm-1.0/Makefile.target	2012-02-07 14:44:04.965124855 -0600
++++ qemu-kvm-1.0.virtio-scsi/Makefile.target	2012-02-07 14:44:53.126900450 -0600
+@@ -205,6 +205,7 @@ obj-y = arch_init.o cpus.o monitor.o mac
+ obj-$(CONFIG_NO_PCI) += pci-stub.o
+ obj-$(CONFIG_PCI) += pci.o
+ obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
++obj-$(CONFIG_VIRTIO_SCSI) += virtio-scsi.o
+ obj-y += vhost_net.o
+ obj-$(CONFIG_VHOST_NET) += vhost.o
+ obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o


More information about the scm-commits mailing list