From 357a44f0604c961f667fd72d665cd9a17c0d7018 Mon Sep 17 00:00:00 2001 From: Justin M. Forbes Date: Feb 07 2012 22:15:59 +0000 Subject: Add support for virtio-scsi --- 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 - 2:1.0-4 +- Add support for virtio-scsi + * Tue Jan 24 2012 Justin M. Forbes - 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 +@@ -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 ++ * Paolo Bonzini ++ * ++ * 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 ++#include ++ ++#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 ++ * ++ * 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