mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 19:49:43 +00:00
Merge remote-tracking branch 'bonzini/scsi-next' into staging
# By Paolo Bonzini (5) and others # Via Paolo Bonzini * bonzini/scsi-next: vhost-scsi-s390: new device supporting the tcm_vhost Linux kernel module vhost-scsi-ccw: new device supporting the tcm_vhost Linux kernel module vhost-scsi-pci: new device supporting the tcm_vhost Linux kernel module vhost-scsi: new device supporting the tcm_vhost Linux kernel module virtio: simplify Makefile conditionals virtio-scsi: create VirtIOSCSICommon vhost: Add vhost_commit callback for SeaBIOS ROM region re-mapping scsi: VMWare PVSCSI paravirtual device implementation scsi: avoid assertion failure on VERIFY command Message-id: 1366381460-6041-1-git-send-email-pbonzini@redhat.com Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
commit
6165daa4c8
10
configure
vendored
10
configure
vendored
@ -179,6 +179,7 @@ libattr=""
|
||||
xfs=""
|
||||
|
||||
vhost_net="no"
|
||||
vhost_scsi="no"
|
||||
kvm="no"
|
||||
gprof="no"
|
||||
debug_tcg="no"
|
||||
@ -543,6 +544,7 @@ Haiku)
|
||||
usb="linux"
|
||||
kvm="yes"
|
||||
vhost_net="yes"
|
||||
vhost_scsi="yes"
|
||||
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
|
||||
audio_possible_drivers="$audio_possible_drivers fmod"
|
||||
fi
|
||||
@ -870,6 +872,10 @@ for opt do
|
||||
;;
|
||||
--enable-vhost-net) vhost_net="yes"
|
||||
;;
|
||||
--disable-vhost-scsi) vhost_scsi="no"
|
||||
;;
|
||||
--enable-vhost-scsi) vhost_scsi="yes"
|
||||
;;
|
||||
--disable-glx) glx="no"
|
||||
;;
|
||||
--enable-glx) glx="yes"
|
||||
@ -3553,6 +3559,7 @@ echo "sigev_thread_id $sigev_thread_id"
|
||||
echo "uuid support $uuid"
|
||||
echo "libcap-ng support $cap_ng"
|
||||
echo "vhost-net support $vhost_net"
|
||||
echo "vhost-scsi support $vhost_scsi"
|
||||
echo "Trace backend $trace_backend"
|
||||
echo "Trace output file $trace_file-<pid>"
|
||||
echo "spice support $spice ($spice_protocol_version/$spice_server_version)"
|
||||
@ -3836,6 +3843,9 @@ fi
|
||||
if test "$virtfs" = "yes" ; then
|
||||
echo "CONFIG_VIRTFS=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vhost_scsi" = "yes" ; then
|
||||
echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$blobs" = "yes" ; then
|
||||
echo "INSTALL_BLOBS=yes" >> $config_host_mak
|
||||
fi
|
||||
|
@ -10,6 +10,7 @@ CONFIG_EEPRO100_PCI=y
|
||||
CONFIG_PCNET_PCI=y
|
||||
CONFIG_PCNET_COMMON=y
|
||||
CONFIG_LSI_SCSI_PCI=y
|
||||
CONFIG_VMW_PVSCSI_SCSI_PCI=y
|
||||
CONFIG_MEGASAS_SCSI_PCI=y
|
||||
CONFIG_RTL8139_PCI=y
|
||||
CONFIG_E1000_PCI=y
|
||||
|
92
docs/specs/vmw_pvscsi-spec.txt
Normal file
92
docs/specs/vmw_pvscsi-spec.txt
Normal file
@ -0,0 +1,92 @@
|
||||
General Description
|
||||
===================
|
||||
|
||||
This document describes VMWare PVSCSI device interface specification.
|
||||
Created by Dmitry Fleytman (dmitry@daynix.com), Daynix Computing LTD.
|
||||
Based on source code of PVSCSI Linux driver from kernel 3.0.4
|
||||
|
||||
PVSCSI Device Interface Overview
|
||||
================================
|
||||
|
||||
The interface is based on memory area shared between hypervisor and VM.
|
||||
Memory area is obtained by driver as device IO memory resource of
|
||||
PVSCSI_MEM_SPACE_SIZE length.
|
||||
The shared memory consists of registers area and rings area.
|
||||
The registers area is used to raise hypervisor interrupts and issue device
|
||||
commands. The rings area is used to transfer data descriptors and SCSI
|
||||
commands from VM to hypervisor and to transfer messages produced by
|
||||
hypervisor to VM. Data itself is transferred via virtual scatter-gather DMA.
|
||||
|
||||
PVSCSI Device Registers
|
||||
=======================
|
||||
|
||||
The length of the registers area is 1 page (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES).
|
||||
The structure of the registers area is described by the PVSCSIRegOffset enum.
|
||||
There are registers to issue device command (with optional short data),
|
||||
issue device interrupt, control interrupts masking.
|
||||
|
||||
PVSCSI Device Rings
|
||||
===================
|
||||
|
||||
There are three rings in shared memory:
|
||||
|
||||
1. Request ring (struct PVSCSIRingReqDesc *req_ring)
|
||||
- ring for OS to device requests
|
||||
2. Completion ring (struct PVSCSIRingCmpDesc *cmp_ring)
|
||||
- ring for device request completions
|
||||
3. Message ring (struct PVSCSIRingMsgDesc *msg_ring)
|
||||
- ring for messages from device.
|
||||
This ring is optional and the guest might not configure it.
|
||||
There is a control area (struct PVSCSIRingsState *rings_state) used to control
|
||||
rings operation.
|
||||
|
||||
PVSCSI Device to Host Interrupts
|
||||
================================
|
||||
There are following interrupt types supported by PVSCSI device:
|
||||
1. Completion interrupts (completion ring notifications):
|
||||
PVSCSI_INTR_CMPL_0
|
||||
PVSCSI_INTR_CMPL_1
|
||||
2. Message interrupts (message ring notifications):
|
||||
PVSCSI_INTR_MSG_0
|
||||
PVSCSI_INTR_MSG_1
|
||||
|
||||
Interrupts are controlled via PVSCSI_REG_OFFSET_INTR_MASK register
|
||||
Bit set means interrupt enabled, bit cleared - disabled
|
||||
|
||||
Interrupt modes supported are legacy, MSI and MSI-X
|
||||
In case of legacy interrupts, register PVSCSI_REG_OFFSET_INTR_STATUS
|
||||
is used to check which interrupt has arrived. Interrupts are
|
||||
acknowledged when the corresponding bit is written to the interrupt
|
||||
status register.
|
||||
|
||||
PVSCSI Device Operation Sequences
|
||||
=================================
|
||||
|
||||
1. Startup sequence:
|
||||
a. Issue PVSCSI_CMD_ADAPTER_RESET command;
|
||||
aa. Windows driver reads interrupt status register here;
|
||||
b. Issue PVSCSI_CMD_SETUP_MSG_RING command with no additional data,
|
||||
check status and disable device messages if error returned;
|
||||
(Omitted if device messages disabled by driver configuration)
|
||||
c. Issue PVSCSI_CMD_SETUP_RINGS command, provide rings configuration
|
||||
as struct PVSCSICmdDescSetupRings;
|
||||
d. Issue PVSCSI_CMD_SETUP_MSG_RING command again, provide
|
||||
rings configuration as struct PVSCSICmdDescSetupMsgRing;
|
||||
e. Unmask completion and message (if device messages enabled) interrupts.
|
||||
|
||||
2. Shutdown sequences
|
||||
a. Mask interrupts;
|
||||
b. Flush request ring using PVSCSI_REG_OFFSET_KICK_NON_RW_IO;
|
||||
c. Issue PVSCSI_CMD_ADAPTER_RESET command.
|
||||
|
||||
3. Send request
|
||||
a. Fill next free request ring descriptor;
|
||||
b. Issue PVSCSI_REG_OFFSET_KICK_RW_IO for R/W operations;
|
||||
or PVSCSI_REG_OFFSET_KICK_NON_RW_IO for other operations.
|
||||
|
||||
4. Abort command
|
||||
a. Issue PVSCSI_CMD_ABORT_CMD command;
|
||||
|
||||
5. Request completion processing
|
||||
a. Upon completion interrupt arrival process completion
|
||||
and message (if enabled) rings.
|
@ -24,7 +24,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += ssi/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += timer/
|
||||
devices-dirs-$(CONFIG_TPM) += tpm/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += usb/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += virtio/
|
||||
devices-dirs-$(CONFIG_VIRTIO) += virtio/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += watchdog/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += xen/
|
||||
devices-dirs-y += core/
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "hw/virtio/virtio-rng.h"
|
||||
#include "hw/virtio/virtio-serial.h"
|
||||
#include "hw/virtio/virtio-net.h"
|
||||
#include "hw/virtio/vhost-scsi.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/kvm.h"
|
||||
|
||||
@ -239,6 +240,28 @@ static void s390_virtio_scsi_instance_init(Object *obj)
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
static int s390_vhost_scsi_init(VirtIOS390Device *s390_dev)
|
||||
{
|
||||
VHostSCSIS390 *dev = VHOST_SCSI_S390(s390_dev);
|
||||
DeviceState *vdev = DEVICE(&dev->vdev);
|
||||
|
||||
qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
|
||||
if (qdev_init(vdev) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev));
|
||||
}
|
||||
|
||||
static void s390_vhost_scsi_instance_init(Object *obj)
|
||||
{
|
||||
VHostSCSIS390 *dev = VHOST_SCSI_S390(obj);
|
||||
object_initialize(OBJECT(&dev->vdev), TYPE_VHOST_SCSI);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int s390_virtio_rng_init(VirtIOS390Device *dev)
|
||||
{
|
||||
VirtIODevice *vdev;
|
||||
@ -560,7 +583,7 @@ static const TypeInfo virtio_s390_device_info = {
|
||||
};
|
||||
|
||||
static Property s390_virtio_scsi_properties[] = {
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSIS390, vdev.conf),
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSIS390, vdev.parent_obj.conf),
|
||||
DEFINE_VIRTIO_SCSI_FEATURES(VirtIOS390Device, host_features),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
@ -582,6 +605,31 @@ static const TypeInfo s390_virtio_scsi = {
|
||||
.class_init = s390_virtio_scsi_class_init,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
static Property s390_vhost_scsi_properties[] = {
|
||||
DEFINE_VIRTIO_COMMON_FEATURES(VirtIOS390Device, host_features),
|
||||
DEFINE_VHOST_SCSI_PROPERTIES(VHostSCSIS390, vdev.parent_obj.conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void s390_vhost_scsi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = s390_vhost_scsi_init;
|
||||
dc->props = s390_vhost_scsi_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo s390_vhost_scsi = {
|
||||
.name = TYPE_VHOST_SCSI_S390,
|
||||
.parent = TYPE_VIRTIO_S390_DEVICE,
|
||||
.instance_size = sizeof(VHostSCSIS390),
|
||||
.instance_init = s390_vhost_scsi_instance_init,
|
||||
.class_init = s390_vhost_scsi_class_init,
|
||||
};
|
||||
#endif
|
||||
|
||||
/***************** S390 Virtio Bus Bridge Device *******************/
|
||||
/* Only required to have the virtio bus as child in the system bus */
|
||||
|
||||
@ -643,6 +691,7 @@ static void s390_virtio_register_types(void)
|
||||
type_register_static(&s390_virtio_blk);
|
||||
type_register_static(&s390_virtio_net);
|
||||
type_register_static(&s390_virtio_scsi);
|
||||
type_register_static(&s390_vhost_scsi);
|
||||
type_register_static(&s390_virtio_rng);
|
||||
type_register_static(&s390_virtio_bridge_info);
|
||||
}
|
||||
|
@ -25,6 +25,9 @@
|
||||
#include "hw/virtio/virtio-serial.h"
|
||||
#include "hw/virtio/virtio-scsi.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
#include "hw/virtio/vhost-scsi.h"
|
||||
#endif
|
||||
|
||||
#define VIRTIO_DEV_OFFS_TYPE 0 /* 8 bits */
|
||||
#define VIRTIO_DEV_OFFS_NUM_VQ 1 /* 8 bits */
|
||||
@ -160,4 +163,17 @@ typedef struct VirtIONetS390 {
|
||||
VirtIONet vdev;
|
||||
} VirtIONetS390;
|
||||
|
||||
/* vhost-scsi-s390 */
|
||||
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
#define TYPE_VHOST_SCSI_S390 "vhost-scsi-s390"
|
||||
#define VHOST_SCSI_S390(obj) \
|
||||
OBJECT_CHECK(VHostSCSIS390, (obj), TYPE_VHOST_SCSI_S390)
|
||||
|
||||
typedef struct VHostSCSIS390 {
|
||||
VirtIOS390Device parent_obj;
|
||||
VHostSCSI vdev;
|
||||
} VHostSCSIS390;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -687,6 +687,28 @@ static void virtio_ccw_scsi_instance_init(Object *obj)
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
static int vhost_ccw_scsi_init(VirtioCcwDevice *ccw_dev)
|
||||
{
|
||||
VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev);
|
||||
DeviceState *vdev = DEVICE(&dev->vdev);
|
||||
|
||||
qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
|
||||
if (qdev_init(vdev) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev));
|
||||
}
|
||||
|
||||
static void vhost_ccw_scsi_instance_init(Object *obj)
|
||||
{
|
||||
VHostSCSICcw *dev = VHOST_SCSI_CCW(obj);
|
||||
object_initialize(OBJECT(&dev->vdev), TYPE_VHOST_SCSI);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int virtio_ccw_rng_init(VirtioCcwDevice *dev)
|
||||
{
|
||||
VirtIODevice *vdev;
|
||||
@ -873,7 +895,7 @@ static const TypeInfo virtio_ccw_balloon = {
|
||||
|
||||
static Property virtio_ccw_scsi_properties[] = {
|
||||
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSICcw, vdev.conf),
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSICcw, vdev.parent_obj.conf),
|
||||
DEFINE_VIRTIO_SCSI_FEATURES(VirtioCcwDevice, host_features[0]),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
@ -897,6 +919,34 @@ static const TypeInfo virtio_ccw_scsi = {
|
||||
.class_init = virtio_ccw_scsi_class_init,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
static Property vhost_ccw_scsi_properties[] = {
|
||||
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
|
||||
DEFINE_VHOST_SCSI_PROPERTIES(VirtIOSCSICcw, vdev.parent_obj.conf),
|
||||
DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = vhost_ccw_scsi_init;
|
||||
k->exit = virtio_ccw_exit;
|
||||
dc->reset = virtio_ccw_reset;
|
||||
dc->props = vhost_ccw_scsi_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo vhost_ccw_scsi = {
|
||||
.name = TYPE_VHOST_SCSI_CCW,
|
||||
.parent = TYPE_VIRTIO_CCW_DEVICE,
|
||||
.instance_size = sizeof(VirtIOSCSICcw),
|
||||
.instance_init = vhost_ccw_scsi_instance_init,
|
||||
.class_init = vhost_ccw_scsi_class_init,
|
||||
};
|
||||
#endif
|
||||
|
||||
static void virtio_ccw_rng_initfn(Object *obj)
|
||||
{
|
||||
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(obj);
|
||||
@ -1054,6 +1104,7 @@ static void virtio_ccw_register(void)
|
||||
type_register_static(&virtio_ccw_net);
|
||||
type_register_static(&virtio_ccw_balloon);
|
||||
type_register_static(&virtio_ccw_scsi);
|
||||
type_register_static(&vhost_ccw_scsi);
|
||||
type_register_static(&virtio_ccw_rng);
|
||||
type_register_static(&virtual_css_bridge_info);
|
||||
}
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include <hw/virtio/virtio-net.h>
|
||||
#include <hw/virtio/virtio-serial.h>
|
||||
#include <hw/virtio/virtio-scsi.h>
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
#include <hw/virtio/vhost-scsi.h>
|
||||
#endif
|
||||
#include <hw/virtio/virtio-balloon.h>
|
||||
#include <hw/virtio/virtio-rng.h>
|
||||
#include <hw/virtio/virtio-bus.h>
|
||||
@ -101,6 +104,17 @@ typedef struct VirtIOSCSICcw {
|
||||
VirtIOSCSI vdev;
|
||||
} VirtIOSCSICcw;
|
||||
|
||||
/* vhost-scsi-ccw */
|
||||
|
||||
#define TYPE_VHOST_SCSI_CCW "vhost-scsi-ccw"
|
||||
#define VHOST_SCSI_CCW(obj) \
|
||||
OBJECT_CHECK(VHostSCSICcw, (obj), TYPE_VHOST_SCSI_CCW)
|
||||
|
||||
typedef struct VHostSCSICcw {
|
||||
VirtioCcwDevice parent_obj;
|
||||
VHostSCSI vdev;
|
||||
} VHostSCSICcw;
|
||||
|
||||
/* virtio-blk-ccw */
|
||||
|
||||
#define TYPE_VIRTIO_BLK_CCW "virtio-blk-ccw"
|
||||
|
@ -2,7 +2,12 @@ common-obj-y += scsi-disk.o
|
||||
common-obj-y += scsi-generic.o scsi-bus.o
|
||||
common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
|
||||
common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o
|
||||
common-obj-$(CONFIG_VMW_PVSCSI_SCSI_PCI) += vmw_pvscsi.o
|
||||
common-obj-$(CONFIG_ESP) += esp.o
|
||||
common-obj-$(CONFIG_ESP_PCI) += esp-pci.o
|
||||
obj-$(CONFIG_PSERIES) += spapr_vscsi.o
|
||||
obj-$(CONFIG_VIRTIO) += virtio-scsi.o
|
||||
|
||||
ifeq ($(CONFIG_VIRTIO),y)
|
||||
obj-y += virtio-scsi.o
|
||||
obj-$(CONFIG_VHOST_SCSI) += vhost-scsi.o
|
||||
endif
|
||||
|
@ -244,14 +244,15 @@ done:
|
||||
}
|
||||
}
|
||||
|
||||
static void scsi_dma_complete(void *opaque, int ret)
|
||||
static void scsi_dma_complete_noio(void *opaque, int ret)
|
||||
{
|
||||
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
|
||||
assert(r->req.aiocb != NULL);
|
||||
r->req.aiocb = NULL;
|
||||
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
|
||||
if (r->req.aiocb != NULL) {
|
||||
r->req.aiocb = NULL;
|
||||
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
|
||||
}
|
||||
if (r->req.io_canceled) {
|
||||
goto done;
|
||||
}
|
||||
@ -277,6 +278,14 @@ done:
|
||||
}
|
||||
}
|
||||
|
||||
static void scsi_dma_complete(void *opaque, int ret)
|
||||
{
|
||||
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
|
||||
|
||||
assert(r->req.aiocb != NULL);
|
||||
scsi_dma_complete_noio(opaque, ret);
|
||||
}
|
||||
|
||||
static void scsi_read_complete(void * opaque, int ret)
|
||||
{
|
||||
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
|
||||
@ -496,7 +505,7 @@ static void scsi_write_data(SCSIRequest *req)
|
||||
if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 ||
|
||||
r->req.cmd.buf[0] == VERIFY_16) {
|
||||
if (r->req.sg) {
|
||||
scsi_dma_complete(r, 0);
|
||||
scsi_dma_complete_noio(r, 0);
|
||||
} else {
|
||||
scsi_write_complete(r, 0);
|
||||
}
|
||||
|
288
hw/scsi/vhost-scsi.c
Normal file
288
hw/scsi/vhost-scsi.c
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
* vhost_scsi host device
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||||
*
|
||||
* Changes for QEMU mainline + tcm_vhost kernel upstream:
|
||||
* Nicholas Bellinger <nab@risingtidesystems.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include "config.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "migration/migration.h"
|
||||
#include "hw/virtio/vhost-scsi.h"
|
||||
#include "hw/virtio/vhost.h"
|
||||
#include "hw/virtio/virtio-scsi.h"
|
||||
|
||||
static int vhost_scsi_set_endpoint(VHostSCSI *s)
|
||||
{
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
struct vhost_scsi_target backend;
|
||||
int ret;
|
||||
|
||||
memset(&backend, 0, sizeof(backend));
|
||||
pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
|
||||
ret = ioctl(s->dev.control, VHOST_SCSI_SET_ENDPOINT, &backend);
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vhost_scsi_clear_endpoint(VHostSCSI *s)
|
||||
{
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
struct vhost_scsi_target backend;
|
||||
|
||||
memset(&backend, 0, sizeof(backend));
|
||||
pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
|
||||
ioctl(s->dev.control, VHOST_SCSI_CLEAR_ENDPOINT, &backend);
|
||||
}
|
||||
|
||||
static int vhost_scsi_start(VHostSCSI *s)
|
||||
{
|
||||
int ret, abi_version, i;
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||
|
||||
if (!vdev->binding->set_guest_notifiers) {
|
||||
error_report("binding does not support guest notifiers");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
ret = ioctl(s->dev.control, VHOST_SCSI_GET_ABI_VERSION, &abi_version);
|
||||
if (ret < 0) {
|
||||
return -errno;
|
||||
}
|
||||
if (abi_version > VHOST_SCSI_ABI_VERSION) {
|
||||
error_report("vhost-scsi: The running tcm_vhost kernel abi_version:"
|
||||
" %d is greater than vhost_scsi userspace supports: %d, please"
|
||||
" upgrade your version of QEMU\n", abi_version,
|
||||
VHOST_SCSI_ABI_VERSION);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
ret = vhost_dev_enable_notifiers(&s->dev, vdev);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
s->dev.acked_features = vdev->guest_features;
|
||||
ret = vhost_dev_start(&s->dev, vdev);
|
||||
if (ret < 0) {
|
||||
error_report("Error start vhost dev");
|
||||
goto err_notifiers;
|
||||
}
|
||||
|
||||
ret = vhost_scsi_set_endpoint(s);
|
||||
if (ret < 0) {
|
||||
error_report("Error set vhost-scsi endpoint");
|
||||
goto err_vhost_stop;
|
||||
}
|
||||
|
||||
ret = vdev->binding->set_guest_notifiers(vdev->binding_opaque, s->dev.nvqs, true);
|
||||
if (ret < 0) {
|
||||
error_report("Error binding guest notifier");
|
||||
goto err_endpoint;
|
||||
}
|
||||
|
||||
/* guest_notifier_mask/pending not used yet, so just unmask
|
||||
* everything here. virtio-pci will do the right thing by
|
||||
* enabling/disabling irqfd.
|
||||
*/
|
||||
for (i = 0; i < s->dev.nvqs; i++) {
|
||||
vhost_virtqueue_mask(&s->dev, vdev, i, false);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_endpoint:
|
||||
vhost_scsi_clear_endpoint(s);
|
||||
err_vhost_stop:
|
||||
vhost_dev_stop(&s->dev, vdev);
|
||||
err_notifiers:
|
||||
vhost_dev_disable_notifiers(&s->dev, vdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vhost_scsi_stop(VHostSCSI *s)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||
int ret = 0;
|
||||
|
||||
if (!vdev->binding->set_guest_notifiers) {
|
||||
ret = vdev->binding->set_guest_notifiers(vdev->binding_opaque,
|
||||
s->dev.nvqs, false);
|
||||
if (ret < 0) {
|
||||
error_report("vhost guest notifier cleanup failed: %d\n", ret);
|
||||
}
|
||||
}
|
||||
assert(ret >= 0);
|
||||
|
||||
vhost_scsi_clear_endpoint(s);
|
||||
vhost_dev_stop(&s->dev, vdev);
|
||||
vhost_dev_disable_notifiers(&s->dev, vdev);
|
||||
}
|
||||
|
||||
static uint32_t vhost_scsi_get_features(VirtIODevice *vdev,
|
||||
uint32_t features)
|
||||
{
|
||||
VHostSCSI *s = VHOST_SCSI(vdev);
|
||||
|
||||
/* Clear features not supported by host kernel. */
|
||||
if (!(s->dev.features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY))) {
|
||||
features &= ~(1 << VIRTIO_F_NOTIFY_ON_EMPTY);
|
||||
}
|
||||
if (!(s->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) {
|
||||
features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);
|
||||
}
|
||||
if (!(s->dev.features & (1 << VIRTIO_RING_F_EVENT_IDX))) {
|
||||
features &= ~(1 << VIRTIO_RING_F_EVENT_IDX);
|
||||
}
|
||||
if (!(s->dev.features & (1 << VIRTIO_SCSI_F_HOTPLUG))) {
|
||||
features &= ~(1 << VIRTIO_SCSI_F_HOTPLUG);
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
static void vhost_scsi_set_config(VirtIODevice *vdev,
|
||||
const uint8_t *config)
|
||||
{
|
||||
VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config;
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||
|
||||
if ((uint32_t) ldl_raw(&scsiconf->sense_size) != vs->sense_size ||
|
||||
(uint32_t) ldl_raw(&scsiconf->cdb_size) != vs->cdb_size) {
|
||||
error_report("vhost-scsi does not support changing the sense data and CDB sizes");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val)
|
||||
{
|
||||
VHostSCSI *s = (VHostSCSI *)vdev;
|
||||
bool start = (val & VIRTIO_CONFIG_S_DRIVER_OK);
|
||||
|
||||
if (s->dev.started == start) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (start) {
|
||||
int ret;
|
||||
|
||||
ret = vhost_scsi_start(s);
|
||||
if (ret < 0) {
|
||||
error_report("virtio-scsi: unable to start vhost: %s\n",
|
||||
strerror(-ret));
|
||||
|
||||
/* There is no userspace virtio-scsi fallback so exit */
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
vhost_scsi_stop(s);
|
||||
}
|
||||
}
|
||||
|
||||
static int vhost_scsi_init(VirtIODevice *vdev)
|
||||
{
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||
VHostSCSI *s = VHOST_SCSI(vdev);
|
||||
int vhostfd = -1;
|
||||
int ret;
|
||||
|
||||
if (!vs->conf.wwpn) {
|
||||
error_report("vhost-scsi: missing wwpn\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (vs->conf.vhostfd) {
|
||||
vhostfd = monitor_handle_fd_param(cur_mon, vs->conf.vhostfd);
|
||||
if (vhostfd == -1) {
|
||||
error_report("vhost-scsi: unable to parse vhostfd\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = virtio_scsi_common_init(vs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
vdev->get_features = vhost_scsi_get_features;
|
||||
vdev->set_config = vhost_scsi_set_config;
|
||||
vdev->set_status = vhost_scsi_set_status;
|
||||
|
||||
s->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues;
|
||||
s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs);
|
||||
s->dev.vq_index = 0;
|
||||
|
||||
ret = vhost_dev_init(&s->dev, vhostfd, "/dev/vhost-scsi", true);
|
||||
if (ret < 0) {
|
||||
error_report("vhost-scsi: vhost initialization failed: %s\n",
|
||||
strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
s->dev.backend_features = 0;
|
||||
|
||||
error_setg(&s->migration_blocker,
|
||||
"vhost-scsi does not support migration");
|
||||
migrate_add_blocker(s->migration_blocker);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_scsi_exit(DeviceState *qdev)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
|
||||
VHostSCSI *s = VHOST_SCSI(qdev);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(qdev);
|
||||
|
||||
migrate_del_blocker(s->migration_blocker);
|
||||
error_free(s->migration_blocker);
|
||||
|
||||
/* This will stop vhost backend. */
|
||||
vhost_scsi_set_status(vdev, 0);
|
||||
|
||||
g_free(s->dev.vqs);
|
||||
return virtio_scsi_common_exit(vs);
|
||||
}
|
||||
|
||||
static Property vhost_scsi_properties[] = {
|
||||
DEFINE_VHOST_SCSI_PROPERTIES(VHostSCSI, parent_obj.conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void vhost_scsi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||
dc->exit = vhost_scsi_exit;
|
||||
dc->props = vhost_scsi_properties;
|
||||
vdc->init = vhost_scsi_init;
|
||||
vdc->get_features = vhost_scsi_get_features;
|
||||
vdc->set_config = vhost_scsi_set_config;
|
||||
vdc->set_status = vhost_scsi_set_status;
|
||||
}
|
||||
|
||||
static const TypeInfo vhost_scsi_info = {
|
||||
.name = TYPE_VHOST_SCSI,
|
||||
.parent = TYPE_VIRTIO_SCSI_COMMON,
|
||||
.instance_size = sizeof(VHostSCSI),
|
||||
.class_init = vhost_scsi_class_init,
|
||||
};
|
||||
|
||||
static void virtio_register_types(void)
|
||||
{
|
||||
type_register_static(&vhost_scsi_info);
|
||||
}
|
||||
|
||||
type_init(virtio_register_types)
|
@ -19,118 +19,6 @@
|
||||
#include <block/scsi.h>
|
||||
#include <hw/virtio/virtio-bus.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
|
||||
#define VIRTIO_SCSI_T_PARAM_CHANGE 3
|
||||
|
||||
/* Reasons for transport reset event */
|
||||
#define VIRTIO_SCSI_EVT_RESET_HARD 0
|
||||
#define VIRTIO_SCSI_EVT_RESET_RESCAN 1
|
||||
#define VIRTIO_SCSI_EVT_RESET_REMOVED 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 VirtIOSCSIReq {
|
||||
VirtIOSCSI *dev;
|
||||
VirtQueue *vq;
|
||||
@ -237,9 +125,10 @@ static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
|
||||
static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq)
|
||||
{
|
||||
VirtIOSCSIReq *req = sreq->hba_private;
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(req->dev);
|
||||
uint32_t n = virtio_queue_get_id(req->vq) - 2;
|
||||
|
||||
assert(n < req->dev->conf.num_queues);
|
||||
assert(n < vs->conf.num_queues);
|
||||
qemu_put_be32s(f, &n);
|
||||
qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
|
||||
}
|
||||
@ -248,14 +137,15 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
|
||||
{
|
||||
SCSIBus *bus = sreq->bus;
|
||||
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
VirtIOSCSIReq *req;
|
||||
uint32_t n;
|
||||
|
||||
req = g_malloc(sizeof(*req));
|
||||
qemu_get_be32s(f, &n);
|
||||
assert(n < s->conf.num_queues);
|
||||
assert(n < vs->conf.num_queues);
|
||||
qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
|
||||
virtio_scsi_parse_req(s, s->cmd_vqs[n], req);
|
||||
virtio_scsi_parse_req(s, vs->cmd_vqs[n], req);
|
||||
|
||||
scsi_req_ref(sreq);
|
||||
req->sreq = sreq;
|
||||
@ -457,7 +347,10 @@ static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req)
|
||||
|
||||
static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
/* use non-QOM casts in the data path */
|
||||
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
|
||||
VirtIOSCSICommon *vs = &s->parent_obj;
|
||||
|
||||
VirtIOSCSIReq *req;
|
||||
int n;
|
||||
|
||||
@ -470,8 +363,8 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
|
||||
|
||||
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) {
|
||||
if (out_size < sizeof(VirtIOSCSICmdReq) + vs->cdb_size ||
|
||||
in_size < sizeof(VirtIOSCSICmdResp) + vs->sense_size) {
|
||||
virtio_scsi_bad_req();
|
||||
}
|
||||
|
||||
@ -513,7 +406,7 @@ static void virtio_scsi_get_config(VirtIODevice *vdev,
|
||||
uint8_t *config)
|
||||
{
|
||||
VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config;
|
||||
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
|
||||
VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(vdev);
|
||||
|
||||
stl_raw(&scsiconf->num_queues, s->conf.num_queues);
|
||||
stl_raw(&scsiconf->seg_max, 128 - 2);
|
||||
@ -531,7 +424,7 @@ static void virtio_scsi_set_config(VirtIODevice *vdev,
|
||||
const uint8_t *config)
|
||||
{
|
||||
VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config;
|
||||
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||
|
||||
if ((uint32_t) ldl_raw(&scsiconf->sense_size) >= 65536 ||
|
||||
(uint32_t) ldl_raw(&scsiconf->cdb_size) >= 256) {
|
||||
@ -539,8 +432,8 @@ static void virtio_scsi_set_config(VirtIODevice *vdev,
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s->sense_size = ldl_raw(&scsiconf->sense_size);
|
||||
s->cdb_size = ldl_raw(&scsiconf->cdb_size);
|
||||
vs->sense_size = ldl_raw(&scsiconf->sense_size);
|
||||
vs->cdb_size = ldl_raw(&scsiconf->cdb_size);
|
||||
}
|
||||
|
||||
static uint32_t virtio_scsi_get_features(VirtIODevice *vdev,
|
||||
@ -551,14 +444,15 @@ static uint32_t virtio_scsi_get_features(VirtIODevice *vdev,
|
||||
|
||||
static void virtio_scsi_reset(VirtIODevice *vdev)
|
||||
{
|
||||
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
|
||||
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||
|
||||
s->resetting++;
|
||||
qbus_reset_all(&s->bus.qbus);
|
||||
s->resetting--;
|
||||
|
||||
s->sense_size = VIRTIO_SCSI_SENSE_SIZE;
|
||||
s->cdb_size = VIRTIO_SCSI_CDB_SIZE;
|
||||
vs->sense_size = VIRTIO_SCSI_SENSE_SIZE;
|
||||
vs->cdb_size = VIRTIO_SCSI_CDB_SIZE;
|
||||
s->events_dropped = false;
|
||||
}
|
||||
|
||||
@ -586,7 +480,8 @@ static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id)
|
||||
static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||
uint32_t event, uint32_t reason)
|
||||
{
|
||||
VirtIOSCSIReq *req = virtio_scsi_pop_req(s, s->event_vq);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
VirtIOSCSIReq *req = virtio_scsi_pop_req(s, vs->event_vq);
|
||||
VirtIOSCSIEvent *evt;
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||
int in_size;
|
||||
@ -692,23 +587,19 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = {
|
||||
.load_request = virtio_scsi_load_request,
|
||||
};
|
||||
|
||||
static int virtio_scsi_device_init(VirtIODevice *vdev)
|
||||
int virtio_scsi_common_init(VirtIOSCSICommon *s)
|
||||
{
|
||||
DeviceState *qdev = DEVICE(vdev);
|
||||
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
|
||||
static int virtio_scsi_id;
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||
int i;
|
||||
|
||||
virtio_init(VIRTIO_DEVICE(s), "virtio-scsi", VIRTIO_ID_SCSI,
|
||||
sizeof(VirtIOSCSIConfig));
|
||||
|
||||
s->cmd_vqs = g_malloc0(s->conf.num_queues * sizeof(VirtQueue *));
|
||||
s->sense_size = VIRTIO_SCSI_SENSE_SIZE;
|
||||
s->cdb_size = VIRTIO_SCSI_CDB_SIZE;
|
||||
|
||||
/* TODO set up vdev function pointers */
|
||||
vdev->get_config = virtio_scsi_get_config;
|
||||
vdev->set_config = virtio_scsi_set_config;
|
||||
vdev->get_features = virtio_scsi_get_features;
|
||||
vdev->reset = virtio_scsi_reset;
|
||||
|
||||
s->ctrl_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE,
|
||||
virtio_scsi_handle_ctrl);
|
||||
@ -719,6 +610,26 @@ static int virtio_scsi_device_init(VirtIODevice *vdev)
|
||||
virtio_scsi_handle_cmd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_scsi_device_init(VirtIODevice *vdev)
|
||||
{
|
||||
DeviceState *qdev = DEVICE(vdev);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
|
||||
static int virtio_scsi_id;
|
||||
int ret;
|
||||
|
||||
ret = virtio_scsi_common_init(vs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
vdev->get_features = virtio_scsi_get_features;
|
||||
vdev->set_config = virtio_scsi_set_config;
|
||||
vdev->reset = virtio_scsi_reset;
|
||||
|
||||
scsi_bus_new(&s->bus, qdev, &virtio_scsi_scsi_info);
|
||||
if (!qdev->hotplugged) {
|
||||
scsi_bus_legacy_handle_cmdline(&s->bus);
|
||||
@ -730,22 +641,36 @@ static int virtio_scsi_device_init(VirtIODevice *vdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_scsi_device_exit(DeviceState *qdev)
|
||||
int virtio_scsi_common_exit(VirtIOSCSICommon *vs)
|
||||
{
|
||||
VirtIOSCSI *s = VIRTIO_SCSI(qdev);
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(vs);
|
||||
|
||||
unregister_savevm(qdev, "virtio-scsi", s);
|
||||
g_free(s->cmd_vqs);
|
||||
g_free(vs->cmd_vqs);
|
||||
virtio_common_cleanup(vdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_scsi_device_exit(DeviceState *qdev)
|
||||
{
|
||||
VirtIOSCSI *s = VIRTIO_SCSI(qdev);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(qdev);
|
||||
|
||||
unregister_savevm(qdev, "virtio-scsi", s);
|
||||
return virtio_scsi_common_exit(vs);
|
||||
}
|
||||
|
||||
static Property virtio_scsi_properties[] = {
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSI, conf),
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSI, parent_obj.conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void virtio_scsi_common_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||
|
||||
vdc->get_config = virtio_scsi_get_config;
|
||||
}
|
||||
|
||||
static void virtio_scsi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
@ -753,21 +678,28 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data)
|
||||
dc->exit = virtio_scsi_device_exit;
|
||||
dc->props = virtio_scsi_properties;
|
||||
vdc->init = virtio_scsi_device_init;
|
||||
vdc->get_config = virtio_scsi_get_config;
|
||||
vdc->set_config = virtio_scsi_set_config;
|
||||
vdc->get_features = virtio_scsi_get_features;
|
||||
vdc->reset = virtio_scsi_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo virtio_scsi_common_info = {
|
||||
.name = TYPE_VIRTIO_SCSI_COMMON,
|
||||
.parent = TYPE_VIRTIO_DEVICE,
|
||||
.instance_size = sizeof(VirtIOSCSICommon),
|
||||
.class_init = virtio_scsi_common_class_init,
|
||||
};
|
||||
|
||||
static const TypeInfo virtio_scsi_info = {
|
||||
.name = TYPE_VIRTIO_SCSI,
|
||||
.parent = TYPE_VIRTIO_DEVICE,
|
||||
.parent = TYPE_VIRTIO_SCSI_COMMON,
|
||||
.instance_size = sizeof(VirtIOSCSI),
|
||||
.class_init = virtio_scsi_class_init,
|
||||
};
|
||||
|
||||
static void virtio_register_types(void)
|
||||
{
|
||||
type_register_static(&virtio_scsi_common_info);
|
||||
type_register_static(&virtio_scsi_info);
|
||||
}
|
||||
|
||||
|
1216
hw/scsi/vmw_pvscsi.c
Normal file
1216
hw/scsi/vmw_pvscsi.c
Normal file
File diff suppressed because it is too large
Load Diff
434
hw/scsi/vmw_pvscsi.h
Normal file
434
hw/scsi/vmw_pvscsi.h
Normal file
@ -0,0 +1,434 @@
|
||||
/*
|
||||
* VMware PVSCSI header file
|
||||
*
|
||||
* Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; version 2 of the License and no later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
|
||||
* NON INFRINGEMENT. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Maintained by: Arvind Kumar <arvindkumar@vmware.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef VMW_PVSCSI_H
|
||||
#define VMW_PVSCSI_H
|
||||
|
||||
#define VMW_PAGE_SIZE (4096)
|
||||
#define VMW_PAGE_SHIFT (12)
|
||||
|
||||
#define MASK(n) ((1 << (n)) - 1) /* make an n-bit mask */
|
||||
|
||||
/*
|
||||
* host adapter status/error codes
|
||||
*/
|
||||
enum HostBusAdapterStatus {
|
||||
BTSTAT_SUCCESS = 0x00, /* CCB complete normally with no errors */
|
||||
BTSTAT_LINKED_COMMAND_COMPLETED = 0x0a,
|
||||
BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b,
|
||||
BTSTAT_DATA_UNDERRUN = 0x0c,
|
||||
BTSTAT_SELTIMEO = 0x11, /* SCSI selection timeout */
|
||||
BTSTAT_DATARUN = 0x12, /* data overrun/underrun */
|
||||
BTSTAT_BUSFREE = 0x13, /* unexpected bus free */
|
||||
BTSTAT_INVPHASE = 0x14, /* invalid bus phase or sequence */
|
||||
/* requested by target */
|
||||
BTSTAT_LUNMISMATCH = 0x17, /* linked CCB has different LUN */
|
||||
/* from first CCB */
|
||||
BTSTAT_SENSFAILED = 0x1b, /* auto request sense failed */
|
||||
BTSTAT_TAGREJECT = 0x1c, /* SCSI II tagged queueing message */
|
||||
/* rejected by target */
|
||||
BTSTAT_BADMSG = 0x1d, /* unsupported message received by */
|
||||
/* the host adapter */
|
||||
BTSTAT_HAHARDWARE = 0x20, /* host adapter hardware failed */
|
||||
BTSTAT_NORESPONSE = 0x21, /* target did not respond to SCSI ATN, */
|
||||
/* sent a SCSI RST */
|
||||
BTSTAT_SENTRST = 0x22, /* host adapter asserted a SCSI RST */
|
||||
BTSTAT_RECVRST = 0x23, /* other SCSI devices asserted a SCSI RST */
|
||||
BTSTAT_DISCONNECT = 0x24, /* target device reconnected improperly */
|
||||
/* (w/o tag) */
|
||||
BTSTAT_BUSRESET = 0x25, /* host adapter issued BUS device reset */
|
||||
BTSTAT_ABORTQUEUE = 0x26, /* abort queue generated */
|
||||
BTSTAT_HASOFTWARE = 0x27, /* host adapter software error */
|
||||
BTSTAT_HATIMEOUT = 0x30, /* host adapter hardware timeout error */
|
||||
BTSTAT_SCSIPARITY = 0x34, /* SCSI parity error detected */
|
||||
};
|
||||
|
||||
/*
|
||||
* Register offsets.
|
||||
*
|
||||
* These registers are accessible both via i/o space and mm i/o.
|
||||
*/
|
||||
|
||||
enum PVSCSIRegOffset {
|
||||
PVSCSI_REG_OFFSET_COMMAND = 0x0,
|
||||
PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4,
|
||||
PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8,
|
||||
PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100,
|
||||
PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104,
|
||||
PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108,
|
||||
PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c,
|
||||
PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c,
|
||||
PVSCSI_REG_OFFSET_INTR_MASK = 0x2010,
|
||||
PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014,
|
||||
PVSCSI_REG_OFFSET_DEBUG = 0x3018,
|
||||
PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018,
|
||||
};
|
||||
|
||||
/*
|
||||
* Virtual h/w commands.
|
||||
*/
|
||||
|
||||
enum PVSCSICommands {
|
||||
PVSCSI_CMD_FIRST = 0, /* has to be first */
|
||||
|
||||
PVSCSI_CMD_ADAPTER_RESET = 1,
|
||||
PVSCSI_CMD_ISSUE_SCSI = 2,
|
||||
PVSCSI_CMD_SETUP_RINGS = 3,
|
||||
PVSCSI_CMD_RESET_BUS = 4,
|
||||
PVSCSI_CMD_RESET_DEVICE = 5,
|
||||
PVSCSI_CMD_ABORT_CMD = 6,
|
||||
PVSCSI_CMD_CONFIG = 7,
|
||||
PVSCSI_CMD_SETUP_MSG_RING = 8,
|
||||
PVSCSI_CMD_DEVICE_UNPLUG = 9,
|
||||
|
||||
PVSCSI_CMD_LAST = 10 /* has to be last */
|
||||
};
|
||||
|
||||
#define PVSCSI_COMMAND_PROCESSING_SUCCEEDED (0)
|
||||
#define PVSCSI_COMMAND_PROCESSING_FAILED (-1)
|
||||
#define PVSCSI_COMMAND_NOT_ENOUGH_DATA (-2)
|
||||
|
||||
/*
|
||||
* Command descriptor for PVSCSI_CMD_RESET_DEVICE --
|
||||
*/
|
||||
|
||||
struct PVSCSICmdDescResetDevice {
|
||||
uint32_t target;
|
||||
uint8_t lun[8];
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct PVSCSICmdDescResetDevice PVSCSICmdDescResetDevice;
|
||||
|
||||
/*
|
||||
* Command descriptor for PVSCSI_CMD_ABORT_CMD --
|
||||
*
|
||||
* - currently does not support specifying the LUN.
|
||||
* - pad should be 0.
|
||||
*/
|
||||
|
||||
struct PVSCSICmdDescAbortCmd {
|
||||
uint64_t context;
|
||||
uint32_t target;
|
||||
uint32_t pad;
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct PVSCSICmdDescAbortCmd PVSCSICmdDescAbortCmd;
|
||||
|
||||
/*
|
||||
* Command descriptor for PVSCSI_CMD_SETUP_RINGS --
|
||||
*
|
||||
* Notes:
|
||||
* - reqRingNumPages and cmpRingNumPages need to be power of two.
|
||||
* - reqRingNumPages and cmpRingNumPages need to be different from 0,
|
||||
* - reqRingNumPages and cmpRingNumPages need to be inferior to
|
||||
* PVSCSI_SETUP_RINGS_MAX_NUM_PAGES.
|
||||
*/
|
||||
|
||||
#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32
|
||||
struct PVSCSICmdDescSetupRings {
|
||||
uint32_t reqRingNumPages;
|
||||
uint32_t cmpRingNumPages;
|
||||
uint64_t ringsStatePPN;
|
||||
uint64_t reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
|
||||
uint64_t cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct PVSCSICmdDescSetupRings PVSCSICmdDescSetupRings;
|
||||
|
||||
/*
|
||||
* Command descriptor for PVSCSI_CMD_SETUP_MSG_RING --
|
||||
*
|
||||
* Notes:
|
||||
* - this command was not supported in the initial revision of the h/w
|
||||
* interface. Before using it, you need to check that it is supported by
|
||||
* writing PVSCSI_CMD_SETUP_MSG_RING to the 'command' register, then
|
||||
* immediately after read the 'command status' register:
|
||||
* * a value of -1 means that the cmd is NOT supported,
|
||||
* * a value != -1 means that the cmd IS supported.
|
||||
* If it's supported the 'command status' register should return:
|
||||
* sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(uint32_t).
|
||||
* - this command should be issued _after_ the usual SETUP_RINGS so that the
|
||||
* RingsState page is already setup. If not, the command is a nop.
|
||||
* - numPages needs to be a power of two,
|
||||
* - numPages needs to be different from 0,
|
||||
* - pad should be zero.
|
||||
*/
|
||||
|
||||
#define PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES 16
|
||||
|
||||
struct PVSCSICmdDescSetupMsgRing {
|
||||
uint32_t numPages;
|
||||
uint32_t pad;
|
||||
uint64_t ringPPNs[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES];
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct PVSCSICmdDescSetupMsgRing PVSCSICmdDescSetupMsgRing;
|
||||
|
||||
enum PVSCSIMsgType {
|
||||
PVSCSI_MSG_DEV_ADDED = 0,
|
||||
PVSCSI_MSG_DEV_REMOVED = 1,
|
||||
PVSCSI_MSG_LAST = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* Msg descriptor.
|
||||
*
|
||||
* sizeof(struct PVSCSIRingMsgDesc) == 128.
|
||||
*
|
||||
* - type is of type enum PVSCSIMsgType.
|
||||
* - the content of args depend on the type of event being delivered.
|
||||
*/
|
||||
|
||||
struct PVSCSIRingMsgDesc {
|
||||
uint32_t type;
|
||||
uint32_t args[31];
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct PVSCSIRingMsgDesc PVSCSIRingMsgDesc;
|
||||
|
||||
struct PVSCSIMsgDescDevStatusChanged {
|
||||
uint32_t type; /* PVSCSI_MSG_DEV _ADDED / _REMOVED */
|
||||
uint32_t bus;
|
||||
uint32_t target;
|
||||
uint8_t lun[8];
|
||||
uint32_t pad[27];
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct PVSCSIMsgDescDevStatusChanged PVSCSIMsgDescDevStatusChanged;
|
||||
|
||||
/*
|
||||
* Rings state.
|
||||
*
|
||||
* - the fields:
|
||||
* . msgProdIdx,
|
||||
* . msgConsIdx,
|
||||
* . msgNumEntriesLog2,
|
||||
* .. are only used once the SETUP_MSG_RING cmd has been issued.
|
||||
* - 'pad' helps to ensure that the msg related fields are on their own
|
||||
* cache-line.
|
||||
*/
|
||||
|
||||
struct PVSCSIRingsState {
|
||||
uint32_t reqProdIdx;
|
||||
uint32_t reqConsIdx;
|
||||
uint32_t reqNumEntriesLog2;
|
||||
|
||||
uint32_t cmpProdIdx;
|
||||
uint32_t cmpConsIdx;
|
||||
uint32_t cmpNumEntriesLog2;
|
||||
|
||||
uint8_t pad[104];
|
||||
|
||||
uint32_t msgProdIdx;
|
||||
uint32_t msgConsIdx;
|
||||
uint32_t msgNumEntriesLog2;
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct PVSCSIRingsState PVSCSIRingsState;
|
||||
|
||||
/*
|
||||
* Request descriptor.
|
||||
*
|
||||
* sizeof(RingReqDesc) = 128
|
||||
*
|
||||
* - context: is a unique identifier of a command. It could normally be any
|
||||
* 64bit value, however we currently store it in the serialNumber variable
|
||||
* of struct SCSI_Command, so we have the following restrictions due to the
|
||||
* way this field is handled in the vmkernel storage stack:
|
||||
* * this value can't be 0,
|
||||
* * the upper 32bit need to be 0 since serialNumber is as a uint32_t.
|
||||
* Currently tracked as PR 292060.
|
||||
* - dataLen: contains the total number of bytes that need to be transferred.
|
||||
* - dataAddr:
|
||||
* * if PVSCSI_FLAG_CMD_WITH_SG_LIST is set: dataAddr is the PA of the first
|
||||
* s/g table segment, each s/g segment is entirely contained on a single
|
||||
* page of physical memory,
|
||||
* * if PVSCSI_FLAG_CMD_WITH_SG_LIST is NOT set, then dataAddr is the PA of
|
||||
* the buffer used for the DMA transfer,
|
||||
* - flags:
|
||||
* * PVSCSI_FLAG_CMD_WITH_SG_LIST: see dataAddr above,
|
||||
* * PVSCSI_FLAG_CMD_DIR_NONE: no DMA involved,
|
||||
* * PVSCSI_FLAG_CMD_DIR_TOHOST: transfer from device to main memory,
|
||||
* * PVSCSI_FLAG_CMD_DIR_TODEVICE: transfer from main memory to device,
|
||||
* * PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB: reserved to handle CDBs larger than
|
||||
* 16bytes. To be specified.
|
||||
* - vcpuHint: vcpuId of the processor that will be most likely waiting for the
|
||||
* completion of the i/o. For guest OSes that use lowest priority message
|
||||
* delivery mode (such as windows), we use this "hint" to deliver the
|
||||
* completion action to the proper vcpu. For now, we can use the vcpuId of
|
||||
* the processor that initiated the i/o as a likely candidate for the vcpu
|
||||
* that will be waiting for the completion..
|
||||
* - bus should be 0: we currently only support bus 0 for now.
|
||||
* - unused should be zero'd.
|
||||
*/
|
||||
|
||||
#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0)
|
||||
#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1)
|
||||
#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2)
|
||||
#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3)
|
||||
#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4)
|
||||
|
||||
#define PVSCSI_KNOWN_FLAGS \
|
||||
(PVSCSI_FLAG_CMD_WITH_SG_LIST | \
|
||||
PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB | \
|
||||
PVSCSI_FLAG_CMD_DIR_NONE | \
|
||||
PVSCSI_FLAG_CMD_DIR_TOHOST | \
|
||||
PVSCSI_FLAG_CMD_DIR_TODEVICE)
|
||||
|
||||
struct PVSCSIRingReqDesc {
|
||||
uint64_t context;
|
||||
uint64_t dataAddr;
|
||||
uint64_t dataLen;
|
||||
uint64_t senseAddr;
|
||||
uint32_t senseLen;
|
||||
uint32_t flags;
|
||||
uint8_t cdb[16];
|
||||
uint8_t cdbLen;
|
||||
uint8_t lun[8];
|
||||
uint8_t tag;
|
||||
uint8_t bus;
|
||||
uint8_t target;
|
||||
uint8_t vcpuHint;
|
||||
uint8_t unused[59];
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct PVSCSIRingReqDesc PVSCSIRingReqDesc;
|
||||
|
||||
/*
|
||||
* Scatter-gather list management.
|
||||
*
|
||||
* As described above, when PVSCSI_FLAG_CMD_WITH_SG_LIST is set in the
|
||||
* RingReqDesc.flags, then RingReqDesc.dataAddr is the PA of the first s/g
|
||||
* table segment.
|
||||
*
|
||||
* - each segment of the s/g table contain a succession of struct
|
||||
* PVSCSISGElement.
|
||||
* - each segment is entirely contained on a single physical page of memory.
|
||||
* - a "chain" s/g element has the flag PVSCSI_SGE_FLAG_CHAIN_ELEMENT set in
|
||||
* PVSCSISGElement.flags and in this case:
|
||||
* * addr is the PA of the next s/g segment,
|
||||
* * length is undefined, assumed to be 0.
|
||||
*/
|
||||
|
||||
struct PVSCSISGElement {
|
||||
uint64_t addr;
|
||||
uint32_t length;
|
||||
uint32_t flags;
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct PVSCSISGElement PVSCSISGElement;
|
||||
|
||||
/*
|
||||
* Completion descriptor.
|
||||
*
|
||||
* sizeof(RingCmpDesc) = 32
|
||||
*
|
||||
* - context: identifier of the command. The same thing that was specified
|
||||
* under "context" as part of struct RingReqDesc at initiation time,
|
||||
* - dataLen: number of bytes transferred for the actual i/o operation,
|
||||
* - senseLen: number of bytes written into the sense buffer,
|
||||
* - hostStatus: adapter status,
|
||||
* - scsiStatus: device status,
|
||||
* - pad should be zero.
|
||||
*/
|
||||
|
||||
struct PVSCSIRingCmpDesc {
|
||||
uint64_t context;
|
||||
uint64_t dataLen;
|
||||
uint32_t senseLen;
|
||||
uint16_t hostStatus;
|
||||
uint16_t scsiStatus;
|
||||
uint32_t pad[2];
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct PVSCSIRingCmpDesc PVSCSIRingCmpDesc;
|
||||
|
||||
/*
|
||||
* Interrupt status / IRQ bits.
|
||||
*/
|
||||
|
||||
#define PVSCSI_INTR_CMPL_0 (1 << 0)
|
||||
#define PVSCSI_INTR_CMPL_1 (1 << 1)
|
||||
#define PVSCSI_INTR_CMPL_MASK MASK(2)
|
||||
|
||||
#define PVSCSI_INTR_MSG_0 (1 << 2)
|
||||
#define PVSCSI_INTR_MSG_1 (1 << 3)
|
||||
#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2)
|
||||
|
||||
#define PVSCSI_INTR_ALL_SUPPORTED MASK(4)
|
||||
|
||||
/*
|
||||
* Number of MSI-X vectors supported.
|
||||
*/
|
||||
#define PVSCSI_MAX_INTRS 24
|
||||
|
||||
/*
|
||||
* Enumeration of supported MSI-X vectors
|
||||
*/
|
||||
#define PVSCSI_VECTOR_COMPLETION 0
|
||||
|
||||
/*
|
||||
* Misc constants for the rings.
|
||||
*/
|
||||
|
||||
#define PVSCSI_MAX_NUM_PAGES_REQ_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
|
||||
#define PVSCSI_MAX_NUM_PAGES_CMP_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
|
||||
#define PVSCSI_MAX_NUM_PAGES_MSG_RING PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES
|
||||
|
||||
#define PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE \
|
||||
(VMW_PAGE_SIZE / sizeof(struct PVSCSIRingReqDesc))
|
||||
|
||||
#define PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE \
|
||||
(VMW_PAGE_SIZE / sizeof(PVSCSIRingCmpDesc))
|
||||
|
||||
#define PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE \
|
||||
(VMW_PAGE_SIZE / sizeof(PVSCSIRingMsgDesc))
|
||||
|
||||
#define PVSCSI_MAX_REQ_QUEUE_DEPTH \
|
||||
(PVSCSI_MAX_NUM_PAGES_REQ_RING * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE)
|
||||
|
||||
#define PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES 1
|
||||
#define PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES 1
|
||||
#define PVSCSI_MEM_SPACE_MISC_NUM_PAGES 2
|
||||
#define PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES 2
|
||||
#define PVSCSI_MEM_SPACE_MSIX_NUM_PAGES 2
|
||||
|
||||
enum PVSCSIMemSpace {
|
||||
PVSCSI_MEM_SPACE_COMMAND_PAGE = 0,
|
||||
PVSCSI_MEM_SPACE_INTR_STATUS_PAGE = 1,
|
||||
PVSCSI_MEM_SPACE_MISC_PAGE = 2,
|
||||
PVSCSI_MEM_SPACE_KICK_IO_PAGE = 4,
|
||||
PVSCSI_MEM_SPACE_MSIX_TABLE_PAGE = 6,
|
||||
PVSCSI_MEM_SPACE_MSIX_PBA_PAGE = 7,
|
||||
};
|
||||
|
||||
#define PVSCSI_MEM_SPACE_NUM_PAGES \
|
||||
(PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES + \
|
||||
PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES + \
|
||||
PVSCSI_MEM_SPACE_MISC_NUM_PAGES + \
|
||||
PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES + \
|
||||
PVSCSI_MEM_SPACE_MSIX_NUM_PAGES)
|
||||
|
||||
#define PVSCSI_MEM_SPACE_SIZE (PVSCSI_MEM_SPACE_NUM_PAGES * VMW_PAGE_SIZE)
|
||||
|
||||
#endif /* VMW_PVSCSI_H */
|
@ -1,7 +1,7 @@
|
||||
common-obj-$(CONFIG_VIRTIO) += virtio-rng.o
|
||||
common-obj-y += virtio-rng.o
|
||||
common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
||||
common-obj-$(CONFIG_VIRTIO) += virtio-bus.o
|
||||
common-obj-y += virtio-bus.o
|
||||
common-obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += dataplane/
|
||||
|
||||
obj-$(CONFIG_VIRTIO) += virtio.o virtio-balloon.o
|
||||
obj-$(CONFIG_VHOST_NET) += vhost.o
|
||||
obj-y += virtio.o virtio-balloon.o
|
||||
obj-$(CONFIG_LINUX) += vhost.o
|
||||
|
@ -382,8 +382,6 @@ static void vhost_set_memory(MemoryListener *listener,
|
||||
bool log_dirty = memory_region_is_logging(section->mr);
|
||||
int s = offsetof(struct vhost_memory, regions) +
|
||||
(dev->mem->nregions + 1) * sizeof dev->mem->regions[0];
|
||||
uint64_t log_size;
|
||||
int r;
|
||||
void *ram;
|
||||
|
||||
dev->mem = g_realloc(dev->mem, s);
|
||||
@ -416,12 +414,47 @@ static void vhost_set_memory(MemoryListener *listener,
|
||||
/* Remove old mapping for this memory, if any. */
|
||||
vhost_dev_unassign_memory(dev, start_addr, size);
|
||||
}
|
||||
dev->mem_changed_start_addr = MIN(dev->mem_changed_start_addr, start_addr);
|
||||
dev->mem_changed_end_addr = MAX(dev->mem_changed_end_addr, start_addr + size - 1);
|
||||
dev->memory_changed = true;
|
||||
}
|
||||
|
||||
static bool vhost_section(MemoryRegionSection *section)
|
||||
{
|
||||
return memory_region_is_ram(section->mr);
|
||||
}
|
||||
|
||||
static void vhost_begin(MemoryListener *listener)
|
||||
{
|
||||
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
|
||||
memory_listener);
|
||||
dev->mem_changed_end_addr = 0;
|
||||
dev->mem_changed_start_addr = -1;
|
||||
}
|
||||
|
||||
static void vhost_commit(MemoryListener *listener)
|
||||
{
|
||||
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
|
||||
memory_listener);
|
||||
hwaddr start_addr = 0;
|
||||
ram_addr_t size = 0;
|
||||
uint64_t log_size;
|
||||
int r;
|
||||
|
||||
if (!dev->memory_changed) {
|
||||
return;
|
||||
}
|
||||
if (!dev->started) {
|
||||
return;
|
||||
}
|
||||
if (dev->mem_changed_start_addr > dev->mem_changed_end_addr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->started) {
|
||||
start_addr = dev->mem_changed_start_addr;
|
||||
size = dev->mem_changed_end_addr - dev->mem_changed_start_addr + 1;
|
||||
|
||||
r = vhost_verify_ring_mappings(dev, start_addr, size);
|
||||
assert(r >= 0);
|
||||
}
|
||||
@ -429,6 +462,7 @@ static void vhost_set_memory(MemoryListener *listener,
|
||||
if (!dev->log_enabled) {
|
||||
r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
|
||||
assert(r >= 0);
|
||||
dev->memory_changed = false;
|
||||
return;
|
||||
}
|
||||
log_size = vhost_get_log_size(dev);
|
||||
@ -445,19 +479,7 @@ static void vhost_set_memory(MemoryListener *listener,
|
||||
if (dev->log_size > log_size + VHOST_LOG_BUFFER) {
|
||||
vhost_dev_log_resize(dev, log_size);
|
||||
}
|
||||
}
|
||||
|
||||
static bool vhost_section(MemoryRegionSection *section)
|
||||
{
|
||||
return memory_region_is_ram(section->mr);
|
||||
}
|
||||
|
||||
static void vhost_begin(MemoryListener *listener)
|
||||
{
|
||||
}
|
||||
|
||||
static void vhost_commit(MemoryListener *listener)
|
||||
{
|
||||
dev->memory_changed = false;
|
||||
}
|
||||
|
||||
static void vhost_region_add(MemoryListener *listener,
|
||||
@ -842,6 +864,7 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, const char *devpath,
|
||||
hdev->log_size = 0;
|
||||
hdev->log_enabled = false;
|
||||
hdev->started = false;
|
||||
hdev->memory_changed = false;
|
||||
memory_listener_register(&hdev->memory_listener, &address_space_memory);
|
||||
hdev->force = force;
|
||||
return 0;
|
||||
|
@ -1236,7 +1236,7 @@ static Property virtio_scsi_pci_properties[] = {
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
|
||||
DEV_NVECTORS_UNSPECIFIED),
|
||||
DEFINE_VIRTIO_SCSI_FEATURES(VirtIOPCIProxy, host_features),
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSIPCI, vdev.conf),
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSIPCI, vdev.parent_obj.conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -1244,9 +1244,10 @@ static int virtio_scsi_pci_init_pci(VirtIOPCIProxy *vpci_dev)
|
||||
{
|
||||
VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(vpci_dev);
|
||||
DeviceState *vdev = DEVICE(&dev->vdev);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||
|
||||
if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
|
||||
vpci_dev->nvectors = dev->vdev.conf.num_queues + 3;
|
||||
vpci_dev->nvectors = vs->conf.num_queues + 3;
|
||||
}
|
||||
|
||||
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
|
||||
@ -1284,6 +1285,63 @@ static const TypeInfo virtio_scsi_pci_info = {
|
||||
.class_init = virtio_scsi_pci_class_init,
|
||||
};
|
||||
|
||||
/* vhost-scsi-pci */
|
||||
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
static Property vhost_scsi_pci_properties[] = {
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
|
||||
DEV_NVECTORS_UNSPECIFIED),
|
||||
DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
|
||||
DEFINE_VHOST_SCSI_PROPERTIES(VHostSCSIPCI, vdev.parent_obj.conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static int vhost_scsi_pci_init_pci(VirtIOPCIProxy *vpci_dev)
|
||||
{
|
||||
VHostSCSIPCI *dev = VHOST_SCSI_PCI(vpci_dev);
|
||||
DeviceState *vdev = DEVICE(&dev->vdev);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||
|
||||
if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
|
||||
vpci_dev->nvectors = vs->conf.num_queues + 3;
|
||||
}
|
||||
|
||||
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
|
||||
if (qdev_init(vdev) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
|
||||
PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
|
||||
k->init = vhost_scsi_pci_init_pci;
|
||||
dc->props = vhost_scsi_pci_properties;
|
||||
pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
|
||||
pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
|
||||
pcidev_k->revision = 0x00;
|
||||
pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
|
||||
}
|
||||
|
||||
static void vhost_scsi_pci_instance_init(Object *obj)
|
||||
{
|
||||
VHostSCSIPCI *dev = VHOST_SCSI_PCI(obj);
|
||||
object_initialize(OBJECT(&dev->vdev), TYPE_VHOST_SCSI);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
}
|
||||
|
||||
static const TypeInfo vhost_scsi_pci_info = {
|
||||
.name = TYPE_VHOST_SCSI_PCI,
|
||||
.parent = TYPE_VIRTIO_PCI,
|
||||
.instance_size = sizeof(VHostSCSIPCI),
|
||||
.instance_init = vhost_scsi_pci_instance_init,
|
||||
.class_init = vhost_scsi_pci_class_init,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* virtio-balloon-pci */
|
||||
|
||||
static void balloon_pci_stats_get_all(Object *obj, struct Visitor *v,
|
||||
@ -1540,6 +1598,9 @@ static void virtio_pci_register_types(void)
|
||||
type_register_static(&virtio_balloon_pci_info);
|
||||
type_register_static(&virtio_serial_pci_info);
|
||||
type_register_static(&virtio_net_pci_info);
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
type_register_static(&vhost_scsi_pci_info);
|
||||
#endif
|
||||
}
|
||||
|
||||
type_init(virtio_pci_register_types)
|
||||
|
@ -24,6 +24,9 @@
|
||||
#include "hw/virtio/virtio-balloon.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
#include "hw/virtio/virtio-9p.h"
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
#include "hw/virtio/vhost-scsi.h"
|
||||
#endif
|
||||
|
||||
typedef struct VirtIOPCIProxy VirtIOPCIProxy;
|
||||
typedef struct VirtIOBlkPCI VirtIOBlkPCI;
|
||||
@ -31,6 +34,7 @@ typedef struct VirtIOSCSIPCI VirtIOSCSIPCI;
|
||||
typedef struct VirtIOBalloonPCI VirtIOBalloonPCI;
|
||||
typedef struct VirtIOSerialPCI VirtIOSerialPCI;
|
||||
typedef struct VirtIONetPCI VirtIONetPCI;
|
||||
typedef struct VHostSCSIPCI VHostSCSIPCI;
|
||||
|
||||
/* virtio-pci-bus */
|
||||
|
||||
@ -104,6 +108,20 @@ struct VirtIOSCSIPCI {
|
||||
VirtIOSCSI vdev;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
/*
|
||||
* vhost-scsi-pci: This extends VirtioPCIProxy.
|
||||
*/
|
||||
#define TYPE_VHOST_SCSI_PCI "vhost-scsi-pci"
|
||||
#define VHOST_SCSI_PCI(obj) \
|
||||
OBJECT_CHECK(VHostSCSIPCI, (obj), TYPE_VHOST_SCSI_PCI)
|
||||
|
||||
struct VHostSCSIPCI {
|
||||
VirtIOPCIProxy parent_obj;
|
||||
VHostSCSI vdev;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* virtio-blk-pci: This extends VirtioPCIProxy.
|
||||
*/
|
||||
|
@ -59,6 +59,7 @@
|
||||
#define PCI_DEVICE_ID_VMWARE_SVGA 0x0710
|
||||
#define PCI_DEVICE_ID_VMWARE_NET 0x0720
|
||||
#define PCI_DEVICE_ID_VMWARE_SCSI 0x0730
|
||||
#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0
|
||||
#define PCI_DEVICE_ID_VMWARE_IDE 0x1729
|
||||
#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07B0
|
||||
|
||||
|
73
include/hw/virtio/vhost-scsi.h
Normal file
73
include/hw/virtio/vhost-scsi.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* vhost_scsi host device
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef VHOST_SCSI_H
|
||||
#define VHOST_SCSI_H
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "hw/qdev.h"
|
||||
#include "hw/virtio/virtio-scsi.h"
|
||||
#include "hw/virtio/vhost.h"
|
||||
|
||||
/*
|
||||
* Used by QEMU userspace to ensure a consistent vhost-scsi ABI.
|
||||
*
|
||||
* ABI Rev 0: July 2012 version starting point for v3.6-rc merge candidate +
|
||||
* RFC-v2 vhost-scsi userspace. Add GET_ABI_VERSION ioctl usage
|
||||
* ABI Rev 1: January 2013. Ignore vhost_tpgt filed in struct vhost_scsi_target.
|
||||
* All the targets under vhost_wwpn can be seen and used by guest.
|
||||
*/
|
||||
|
||||
#define VHOST_SCSI_ABI_VERSION 1
|
||||
|
||||
/* TODO #include <linux/vhost.h> properly */
|
||||
/* For VHOST_SCSI_SET_ENDPOINT/VHOST_SCSI_CLEAR_ENDPOINT ioctl */
|
||||
struct vhost_scsi_target {
|
||||
int abi_version;
|
||||
char vhost_wwpn[224];
|
||||
unsigned short vhost_tpgt;
|
||||
unsigned short reserved;
|
||||
};
|
||||
|
||||
enum vhost_scsi_vq_list {
|
||||
VHOST_SCSI_VQ_CONTROL = 0,
|
||||
VHOST_SCSI_VQ_EVENT = 1,
|
||||
VHOST_SCSI_VQ_NUM_FIXED = 2,
|
||||
};
|
||||
|
||||
#define VHOST_VIRTIO 0xAF
|
||||
#define VHOST_SCSI_SET_ENDPOINT _IOW(VHOST_VIRTIO, 0x40, struct vhost_scsi_target)
|
||||
#define VHOST_SCSI_CLEAR_ENDPOINT _IOW(VHOST_VIRTIO, 0x41, struct vhost_scsi_target)
|
||||
#define VHOST_SCSI_GET_ABI_VERSION _IOW(VHOST_VIRTIO, 0x42, int)
|
||||
|
||||
#define TYPE_VHOST_SCSI "vhost-scsi"
|
||||
#define VHOST_SCSI(obj) \
|
||||
OBJECT_CHECK(VHostSCSI, (obj), TYPE_VHOST_SCSI)
|
||||
|
||||
typedef struct VHostSCSI {
|
||||
VirtIOSCSICommon parent_obj;
|
||||
|
||||
Error *migration_blocker;
|
||||
|
||||
struct vhost_dev dev;
|
||||
} VHostSCSI;
|
||||
|
||||
#define DEFINE_VHOST_SCSI_PROPERTIES(_state, _conf_field) \
|
||||
DEFINE_PROP_STRING("vhostfd", _state, _conf_field.vhostfd), \
|
||||
DEFINE_PROP_STRING("wwpn", _state, _conf_field.wwpn), \
|
||||
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
|
@ -45,6 +45,9 @@ struct vhost_dev {
|
||||
vhost_log_chunk_t *log;
|
||||
unsigned long long log_size;
|
||||
bool force;
|
||||
bool memory_changed;
|
||||
hwaddr mem_changed_start_addr;
|
||||
hwaddr mem_changed_end_addr;
|
||||
};
|
||||
|
||||
int vhost_dev_init(struct vhost_dev *hdev, int devfd, const char *devpath,
|
||||
|
@ -18,6 +18,10 @@
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/scsi/scsi.h"
|
||||
|
||||
#define TYPE_VIRTIO_SCSI_COMMON "virtio-scsi-common"
|
||||
#define VIRTIO_SCSI_COMMON(obj) \
|
||||
OBJECT_CHECK(VirtIOSCSICommon, (obj), TYPE_VIRTIO_SCSI_COMMON)
|
||||
|
||||
#define TYPE_VIRTIO_SCSI "virtio-scsi-device"
|
||||
#define VIRTIO_SCSI(obj) \
|
||||
OBJECT_CHECK(VirtIOSCSI, (obj), TYPE_VIRTIO_SCSI)
|
||||
@ -31,24 +35,143 @@
|
||||
#define VIRTIO_SCSI_F_HOTPLUG 1
|
||||
#define VIRTIO_SCSI_F_CHANGE 2
|
||||
|
||||
#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
|
||||
#define VIRTIO_SCSI_T_PARAM_CHANGE 3
|
||||
|
||||
/* Reasons for transport reset event */
|
||||
#define VIRTIO_SCSI_EVT_RESET_HARD 0
|
||||
#define VIRTIO_SCSI_EVT_RESET_RESCAN 1
|
||||
#define VIRTIO_SCSI_EVT_RESET_REMOVED 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;
|
||||
|
||||
struct VirtIOSCSIConf {
|
||||
uint32_t num_queues;
|
||||
uint32_t max_sectors;
|
||||
uint32_t cmd_per_lun;
|
||||
char *vhostfd;
|
||||
char *wwpn;
|
||||
};
|
||||
|
||||
typedef struct VirtIOSCSI {
|
||||
typedef struct VirtIOSCSICommon {
|
||||
VirtIODevice parent_obj;
|
||||
VirtIOSCSIConf conf;
|
||||
|
||||
SCSIBus bus;
|
||||
uint32_t sense_size;
|
||||
uint32_t cdb_size;
|
||||
int resetting;
|
||||
bool events_dropped;
|
||||
VirtQueue *ctrl_vq;
|
||||
VirtQueue *event_vq;
|
||||
VirtQueue **cmd_vqs;
|
||||
} VirtIOSCSICommon;
|
||||
|
||||
typedef struct {
|
||||
VirtIOSCSICommon parent_obj;
|
||||
|
||||
SCSIBus bus;
|
||||
int resetting;
|
||||
bool events_dropped;
|
||||
} VirtIOSCSI;
|
||||
|
||||
#define DEFINE_VIRTIO_SCSI_PROPERTIES(_state, _conf_field) \
|
||||
@ -63,4 +186,8 @@ typedef struct VirtIOSCSI {
|
||||
DEFINE_PROP_BIT("param_change", _state, _feature_field, \
|
||||
VIRTIO_SCSI_F_CHANGE, true)
|
||||
|
||||
int virtio_scsi_common_init(VirtIOSCSICommon *vs);
|
||||
int virtio_scsi_common_exit(VirtIOSCSICommon *vs);
|
||||
|
||||
|
||||
#endif /* _QEMU_VIRTIO_SCSI_H */
|
||||
|
@ -67,6 +67,10 @@ typedef signed int int_fast16_t;
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef ROUND_UP
|
||||
#define ROUND_UP(n,d) (((n) + (d) - 1) & -(d))
|
||||
#endif
|
||||
|
||||
#ifndef DIV_ROUND_UP
|
||||
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
|
||||
#endif
|
||||
|
35
trace-events
35
trace-events
@ -766,6 +766,41 @@ pc87312_info_ide(uint32_t base) "base 0x%x"
|
||||
pc87312_info_parallel(uint32_t base, uint32_t irq) "base 0x%x, irq %u"
|
||||
pc87312_info_serial(int n, uint32_t base, uint32_t irq) "id=%d, base 0x%x, irq %u"
|
||||
|
||||
# hw/scsi/vmw_pvscsi.c
|
||||
pvscsi_ring_init_data(uint32_t txr_len_log2, uint32_t rxr_len_log2) "TX/RX rings logarithms set to %d/%d"
|
||||
pvscsi_ring_init_msg(uint32_t len_log2) "MSG ring logarithm set to %d"
|
||||
pvscsi_ring_flush_cmp(uint64_t filled_cmp_ptr) "new production counter of completion ring is 0x%"PRIx64""
|
||||
pvscsi_ring_flush_msg(uint64_t filled_cmp_ptr) "new production counter of message ring is 0x%"PRIx64""
|
||||
pvscsi_update_irq_level(bool raise, uint64_t mask, uint64_t status) "interrupt level set to %d (MASK: 0x%"PRIx64", STATUS: 0x%"PRIx64")"
|
||||
pvscsi_update_irq_msi(void) "sending MSI notification"
|
||||
pvscsi_cmp_ring_put(unsigned long addr) "got completion descriptor 0x%lx"
|
||||
pvscsi_msg_ring_put(unsigned long addr) "got message descriptor 0x%lx"
|
||||
pvscsi_complete_request(uint64_t context, uint64_t len, uint8_t sense_key) "completion: ctx: 0x%"PRIx64", len: 0x%"PRIx64", sense key: %u"
|
||||
pvscsi_get_sg_list(int nsg, size_t size) "get SG list: depth: %u, size: %lu"
|
||||
pvscsi_get_next_sg_elem(uint32_t flags) "unknown flags in SG element (val: 0x%x)"
|
||||
pvscsi_command_complete_not_found(uint32_t tag) "can't find request for tag 0x%x"
|
||||
pvscsi_command_complete_data_run(void) "not all data required for command transferred"
|
||||
pvscsi_command_complete_sense_len(int len) "sense information length is %d bytes"
|
||||
pvscsi_convert_sglist(uint64_t context, unsigned long addr, uint32_t resid) "element: ctx: 0x%"PRIx64" addr: 0x%lx, len: %ul"
|
||||
pvscsi_process_req_descr(uint8_t cmd, uint64_t ctx) "SCSI cmd 0x%x, ctx: 0x%"PRIx64""
|
||||
pvscsi_process_req_descr_unknown_device(void) "command directed to unknown device rejected"
|
||||
pvscsi_process_req_descr_invalid_dir(void) "command with invalid transfer direction rejected"
|
||||
pvscsi_process_io(unsigned long addr) "got descriptor 0x%lx"
|
||||
pvscsi_on_cmd_noimpl(const char* cmd) "unimplemented command %s ignored"
|
||||
pvscsi_on_cmd_reset_dev(uint32_t tgt, int lun, void* dev) "PVSCSI_CMD_RESET_DEVICE[target %u lun %d (dev 0x%p)]"
|
||||
pvscsi_on_cmd_arrived(const char* cmd) "command %s arrived"
|
||||
pvscsi_on_cmd_abort(uint64_t ctx, uint32_t tgt) "command PVSCSI_CMD_ABORT_CMD for ctx 0x%"PRIx64", target %u"
|
||||
pvscsi_on_cmd_unknown(uint64_t cmd_id) "unknown command %"PRIx64""
|
||||
pvscsi_on_cmd_unknown_data(uint32_t data) "data for unknown command 0x:%x"
|
||||
pvscsi_io_write(const char* cmd, uint64_t val) "%s write: %"PRIx64""
|
||||
pvscsi_io_write_unknown(unsigned long addr, unsigned sz, uint64_t val) "unknown write address: 0x%lx size: %u bytes value: 0x%"PRIx64""
|
||||
pvscsi_io_read(const char* cmd, uint64_t status) "%s read: 0x%"PRIx64""
|
||||
pvscsi_io_read_unknown(unsigned long addr, unsigned sz) "unknown read address: 0x%lx size: %u bytes"
|
||||
pvscsi_init_msi_fail(int res) "failed to initialize MSI, error %d"
|
||||
pvscsi_state(const char* state) "starting %s ..."
|
||||
pvscsi_tx_rings_ppn(const char* label, uint64_t ppn) "%s page: %"PRIx64""
|
||||
pvscsi_tx_rings_num_pages(const char* label, uint32_t num) "Number of %s pages: %u"
|
||||
|
||||
# xen-all.c
|
||||
xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %#lx, size %#lx"
|
||||
xen_client_set_memory(uint64_t start_addr, unsigned long size, bool log_dirty) "%#"PRIx64" size %#lx, log_dirty %i"
|
||||
|
Loading…
Reference in New Issue
Block a user