[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