mirror of
https://github.com/xemu-project/xemu.git
synced 2025-01-24 21:05:01 +00:00
This update brings dataplane to virtio-scsi (NOT
yet 100% thread-safe, though, which makes it really, really experimental. It also brings asynchronous cancellation to the SCSI subsystem and implements it in virtio-scsi. This is a pretty important feature. Almost all the work here was done by Fam Zheng. I also included the virtio refcount fixes from Gonglei, because they had a small conflict with virtio-scsi dataplane. This pull request is using the new subkey 4E6B09D7. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJUKpR2AAoJEBRUblpOawnXNLAH/RBeF66ZqWc29dl78JKEbv0+ C5pL61GhlI5vFIjSbPU3/iaZQifw3E4NLvX3SCN5ImsLzBw4r3qerapP2Ut96K/j 5CYdWTF1oqE32oCefvlWhJulHmE1vxGN53BvOz3HHxoehdF1/tJ0wUoZyfztGTOF tiW85VMewi6CKm47/ns5tSNfGMVzWHqnUg67z/mwN6ZmPFU1dXBlgmiIv8Znahrn B1AOAeMjWaKvOS+tiYNVG6k0GENWGoiypxiTR3ZXLQKxOYdkh/X0ARULqLMonASX YsT772nzO9KZDIsdLj9QZZmM7vxs7UhW0MgQlvcSWP9vfZa5SeuRSgoXorPDj3Q= =54T3 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging This update brings dataplane to virtio-scsi (NOT yet 100% thread-safe, though, which makes it really, really experimental. It also brings asynchronous cancellation to the SCSI subsystem and implements it in virtio-scsi. This is a pretty important feature. Almost all the work here was done by Fam Zheng. I also included the virtio refcount fixes from Gonglei, because they had a small conflict with virtio-scsi dataplane. This pull request is using the new subkey 4E6B09D7. # gpg: Signature made Tue 30 Sep 2014 12:31:02 BST using RSA key ID 4E6B09D7 # gpg: Good signature from "Paolo Bonzini <pbonzini@redhat.com>" # gpg: aka "Paolo Bonzini <bonzini@gnu.org>" * remotes/bonzini/tags/for-upstream: (39 commits) block/iscsi: handle failure on malloc of the allocationmap util: introduce bitmap_try_new virtio-scsi: Handle TMF request cancellation asynchronously scsi: Introduce scsi_req_cancel_async scsi: Introduce scsi_req_cancel_complete scsi: Drop SCSIReqOps.cancel_io scsi: Unify request unref in scsi_req_cancel scsi-generic: Handle canceled request in scsi_command_complete scsi: Drop scsi_req_abort virtio-scsi: Process ".iothread" property virtio-scsi: Call bdrv_io_plug/bdrv_io_unplug in cmd request handling virtio-scsi: Batched prepare for cmd reqs virtio-scsi: Two stages processing of cmd request virtio-scsi: Add migration state notifier for dataplane code virtio-scsi: Hook up with dataplane virtio-scsi-dataplane: Code to run virtio-scsi on iothread virtio-scsi: Add VirtIOSCSIVring in VirtIOSCSIReq virtio-scsi: Add 'iothread' property to virtio-scsi virtio: add a wrapper for virtio-backend initialization virtio-9p: fix virtio-9p child refcount in transports ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1831e15060
@ -316,6 +316,13 @@ static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned long *iscsi_allocationmap_init(IscsiLun *iscsilun)
|
||||
{
|
||||
return bitmap_try_new(DIV_ROUND_UP(sector_lun2qemu(iscsilun->num_blocks,
|
||||
iscsilun),
|
||||
iscsilun->cluster_sectors));
|
||||
}
|
||||
|
||||
static void iscsi_allocationmap_set(IscsiLun *iscsilun, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
{
|
||||
@ -1402,9 +1409,10 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran *
|
||||
iscsilun->block_size) >> BDRV_SECTOR_BITS;
|
||||
if (iscsilun->lbprz && !(bs->open_flags & BDRV_O_NOCACHE)) {
|
||||
iscsilun->allocationmap =
|
||||
bitmap_new(DIV_ROUND_UP(bs->total_sectors,
|
||||
iscsilun->cluster_sectors));
|
||||
iscsilun->allocationmap = iscsi_allocationmap_init(iscsilun);
|
||||
if (iscsilun->allocationmap == NULL) {
|
||||
ret = -ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1497,10 +1505,7 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
|
||||
|
||||
if (iscsilun->allocationmap != NULL) {
|
||||
g_free(iscsilun->allocationmap);
|
||||
iscsilun->allocationmap =
|
||||
bitmap_new(DIV_ROUND_UP(sector_lun2qemu(iscsilun->num_blocks,
|
||||
iscsilun),
|
||||
iscsilun->cluster_sectors));
|
||||
iscsilun->allocationmap = iscsi_allocationmap_init(iscsilun);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -159,8 +159,9 @@ static int s390_virtio_net_init(VirtIOS390Device *s390_dev)
|
||||
static void s390_virtio_net_instance_init(Object *obj)
|
||||
{
|
||||
VirtIONetS390 *dev = VIRTIO_NET_S390(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_NET);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_NET);
|
||||
}
|
||||
|
||||
static int s390_virtio_blk_init(VirtIOS390Device *s390_dev)
|
||||
@ -177,10 +178,9 @@ static int s390_virtio_blk_init(VirtIOS390Device *s390_dev)
|
||||
static void s390_virtio_blk_instance_init(Object *obj)
|
||||
{
|
||||
VirtIOBlkS390 *dev = VIRTIO_BLK_S390(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
object_unref(OBJECT(&dev->vdev));
|
||||
qdev_alias_all_properties(DEVICE(&dev->vdev), obj);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_BLK);
|
||||
object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread",
|
||||
&error_abort);
|
||||
}
|
||||
@ -222,8 +222,9 @@ static int s390_virtio_serial_init(VirtIOS390Device *s390_dev)
|
||||
static void s390_virtio_serial_instance_init(Object *obj)
|
||||
{
|
||||
VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SERIAL);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_SERIAL);
|
||||
}
|
||||
|
||||
static int s390_virtio_scsi_init(VirtIOS390Device *s390_dev)
|
||||
@ -254,8 +255,9 @@ static int s390_virtio_scsi_init(VirtIOS390Device *s390_dev)
|
||||
static void s390_virtio_scsi_instance_init(Object *obj)
|
||||
{
|
||||
VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SCSI);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_SCSI);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
@ -275,8 +277,9 @@ static int s390_vhost_scsi_init(VirtIOS390Device *s390_dev)
|
||||
static void s390_vhost_scsi_instance_init(Object *obj)
|
||||
{
|
||||
VHostSCSIS390 *dev = VHOST_SCSI_S390(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VHOST_SCSI);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VHOST_SCSI);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -301,8 +304,9 @@ static int s390_virtio_rng_init(VirtIOS390Device *s390_dev)
|
||||
static void s390_virtio_rng_instance_init(Object *obj)
|
||||
{
|
||||
VirtIORNGS390 *dev = VIRTIO_RNG_S390(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_RNG);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_RNG);
|
||||
object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
|
||||
(Object **)&dev->vdev.conf.rng,
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
@ -493,10 +497,8 @@ static unsigned virtio_s390_get_features(DeviceState *d)
|
||||
/**************** S390 Virtio Bus Device Descriptions *******************/
|
||||
|
||||
static Property s390_virtio_net_properties[] = {
|
||||
DEFINE_NIC_PROPERTIES(VirtIONetS390, vdev.nic_conf),
|
||||
DEFINE_VIRTIO_COMMON_FEATURES(VirtIOS390Device, host_features),
|
||||
DEFINE_VIRTIO_NET_FEATURES(VirtIOS390Device, host_features),
|
||||
DEFINE_VIRTIO_NET_PROPERTIES(VirtIONetS390, vdev.net_conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -533,7 +535,6 @@ static const TypeInfo s390_virtio_blk = {
|
||||
};
|
||||
|
||||
static Property s390_virtio_serial_properties[] = {
|
||||
DEFINE_VIRTIO_SERIAL_PROPERTIES(VirtIOSerialS390, vdev.serial),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -556,7 +557,6 @@ static const TypeInfo s390_virtio_serial = {
|
||||
|
||||
static Property s390_virtio_rng_properties[] = {
|
||||
DEFINE_VIRTIO_COMMON_FEATURES(VirtIOS390Device, host_features),
|
||||
DEFINE_VIRTIO_RNG_PROPERTIES(VirtIORNGS390, vdev.conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -614,7 +614,6 @@ static const TypeInfo virtio_s390_device_info = {
|
||||
};
|
||||
|
||||
static Property s390_virtio_scsi_properties[] = {
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSIS390, vdev.parent_obj.conf),
|
||||
DEFINE_VIRTIO_COMMON_FEATURES(VirtIOS390Device, host_features),
|
||||
DEFINE_VIRTIO_SCSI_FEATURES(VirtIOS390Device, host_features),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
@ -640,7 +639,6 @@ static const TypeInfo s390_virtio_scsi = {
|
||||
#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(),
|
||||
};
|
||||
|
||||
|
@ -792,8 +792,9 @@ static int virtio_ccw_net_init(VirtioCcwDevice *ccw_dev)
|
||||
static void virtio_ccw_net_instance_init(Object *obj)
|
||||
{
|
||||
VirtIONetCcw *dev = VIRTIO_NET_CCW(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_NET);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_NET);
|
||||
}
|
||||
|
||||
static int virtio_ccw_blk_init(VirtioCcwDevice *ccw_dev)
|
||||
@ -811,10 +812,9 @@ static int virtio_ccw_blk_init(VirtioCcwDevice *ccw_dev)
|
||||
static void virtio_ccw_blk_instance_init(Object *obj)
|
||||
{
|
||||
VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
object_unref(OBJECT(&dev->vdev));
|
||||
qdev_alias_all_properties(DEVICE(&dev->vdev), obj);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_BLK);
|
||||
object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread",
|
||||
&error_abort);
|
||||
}
|
||||
@ -848,8 +848,9 @@ static int virtio_ccw_serial_init(VirtioCcwDevice *ccw_dev)
|
||||
static void virtio_ccw_serial_instance_init(Object *obj)
|
||||
{
|
||||
VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SERIAL);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_SERIAL);
|
||||
}
|
||||
|
||||
static int virtio_ccw_balloon_init(VirtioCcwDevice *ccw_dev)
|
||||
@ -896,7 +897,7 @@ static void virtio_ccw_balloon_instance_init(Object *obj)
|
||||
VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BALLOON);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
object_unref(OBJECT(&dev->vdev));
|
||||
object_property_add(obj, "guest-stats", "guest statistics",
|
||||
balloon_ccw_stats_get_all, NULL, NULL, dev, NULL);
|
||||
|
||||
@ -934,8 +935,11 @@ static int virtio_ccw_scsi_init(VirtioCcwDevice *ccw_dev)
|
||||
static void virtio_ccw_scsi_instance_init(Object *obj)
|
||||
{
|
||||
VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SCSI);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_SCSI);
|
||||
object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread",
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VHOST_SCSI
|
||||
@ -955,8 +959,9 @@ static int vhost_ccw_scsi_init(VirtioCcwDevice *ccw_dev)
|
||||
static void vhost_ccw_scsi_instance_init(Object *obj)
|
||||
{
|
||||
VHostSCSICcw *dev = VHOST_SCSI_CCW(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VHOST_SCSI);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VHOST_SCSI);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1374,8 +1379,6 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
|
||||
static Property virtio_ccw_net_properties[] = {
|
||||
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
|
||||
DEFINE_VIRTIO_NET_FEATURES(VirtioCcwDevice, host_features[0]),
|
||||
DEFINE_VIRTIO_NET_PROPERTIES(VirtIONetCcw, vdev.net_conf),
|
||||
DEFINE_NIC_PROPERTIES(VirtIONetCcw, vdev.nic_conf),
|
||||
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
|
||||
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
@ -1428,7 +1431,6 @@ static const TypeInfo virtio_ccw_blk = {
|
||||
|
||||
static Property virtio_ccw_serial_properties[] = {
|
||||
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
|
||||
DEFINE_VIRTIO_SERIAL_PROPERTIES(VirtioSerialCcw, vdev.serial),
|
||||
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
|
||||
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
@ -1481,7 +1483,6 @@ 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.parent_obj.conf),
|
||||
DEFINE_VIRTIO_SCSI_FEATURES(VirtioCcwDevice, host_features[0]),
|
||||
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
|
||||
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
|
||||
@ -1510,7 +1511,6 @@ static const TypeInfo virtio_ccw_scsi = {
|
||||
#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_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -1537,8 +1537,9 @@ static const TypeInfo vhost_ccw_scsi = {
|
||||
static void virtio_ccw_rng_instance_init(Object *obj)
|
||||
{
|
||||
VirtIORNGCcw *dev = VIRTIO_RNG_CCW(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_RNG);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_RNG);
|
||||
object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
|
||||
(Object **)&dev->vdev.conf.rng,
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
@ -1547,7 +1548,6 @@ static void virtio_ccw_rng_instance_init(Object *obj)
|
||||
|
||||
static Property virtio_ccw_rng_properties[] = {
|
||||
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
|
||||
DEFINE_VIRTIO_RNG_PROPERTIES(VirtIORNGCcw, vdev.conf),
|
||||
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
|
||||
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
|
@ -8,6 +8,6 @@ common-obj-$(CONFIG_ESP_PCI) += esp-pci.o
|
||||
obj-$(CONFIG_PSERIES) += spapr_vscsi.o
|
||||
|
||||
ifeq ($(CONFIG_VIRTIO),y)
|
||||
obj-y += virtio-scsi.o
|
||||
obj-y += virtio-scsi.o virtio-scsi-dataplane.o
|
||||
obj-$(CONFIG_VHOST_SCSI) += vhost-scsi.o
|
||||
endif
|
||||
|
@ -551,8 +551,11 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
|
||||
SCSIRequest *req;
|
||||
SCSIBus *bus = scsi_bus_from_device(d);
|
||||
BusState *qbus = BUS(bus);
|
||||
const int memset_off = offsetof(SCSIRequest, sense)
|
||||
+ sizeof(req->sense);
|
||||
|
||||
req = g_malloc0(reqops->size);
|
||||
req = g_slice_alloc(reqops->size);
|
||||
memset((uint8_t *)req + memset_off, 0, reqops->size - memset_off);
|
||||
req->refcount = 1;
|
||||
req->bus = bus;
|
||||
req->dev = d;
|
||||
@ -560,10 +563,10 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
|
||||
req->lun = lun;
|
||||
req->hba_private = hba_private;
|
||||
req->status = -1;
|
||||
req->sense_len = 0;
|
||||
req->ops = reqops;
|
||||
object_ref(OBJECT(d));
|
||||
object_ref(OBJECT(qbus->parent));
|
||||
notifier_list_init(&req->cancel_notifiers);
|
||||
trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
|
||||
return req;
|
||||
}
|
||||
@ -1603,7 +1606,7 @@ void scsi_req_unref(SCSIRequest *req)
|
||||
}
|
||||
object_unref(OBJECT(req->dev));
|
||||
object_unref(OBJECT(qbus->parent));
|
||||
g_free(req);
|
||||
g_slice_free1(req->ops->size, req);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1713,9 +1716,44 @@ void scsi_req_complete(SCSIRequest *req, int status)
|
||||
scsi_req_ref(req);
|
||||
scsi_req_dequeue(req);
|
||||
req->bus->info->complete(req, req->status, req->resid);
|
||||
|
||||
/* Cancelled requests might end up being completed instead of cancelled */
|
||||
notifier_list_notify(&req->cancel_notifiers, req);
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
/* Called by the devices when the request is canceled. */
|
||||
void scsi_req_cancel_complete(SCSIRequest *req)
|
||||
{
|
||||
assert(req->io_canceled);
|
||||
if (req->bus->info->cancel) {
|
||||
req->bus->info->cancel(req);
|
||||
}
|
||||
notifier_list_notify(&req->cancel_notifiers, req);
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
/* Cancel @req asynchronously. @notifier is added to @req's cancellation
|
||||
* notifier list, the bus will be notified the requests cancellation is
|
||||
* completed.
|
||||
* */
|
||||
void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier)
|
||||
{
|
||||
trace_scsi_req_cancel(req->dev->id, req->lun, req->tag);
|
||||
if (notifier) {
|
||||
notifier_list_add(&req->cancel_notifiers, notifier);
|
||||
}
|
||||
if (req->io_canceled) {
|
||||
return;
|
||||
}
|
||||
scsi_req_ref(req);
|
||||
scsi_req_dequeue(req);
|
||||
req->io_canceled = true;
|
||||
if (req->aiocb) {
|
||||
bdrv_aio_cancel_async(req->aiocb);
|
||||
}
|
||||
}
|
||||
|
||||
void scsi_req_cancel(SCSIRequest *req)
|
||||
{
|
||||
trace_scsi_req_cancel(req->dev->id, req->lun, req->tag);
|
||||
@ -1725,28 +1763,9 @@ void scsi_req_cancel(SCSIRequest *req)
|
||||
scsi_req_ref(req);
|
||||
scsi_req_dequeue(req);
|
||||
req->io_canceled = true;
|
||||
if (req->ops->cancel_io) {
|
||||
req->ops->cancel_io(req);
|
||||
if (req->aiocb) {
|
||||
bdrv_aio_cancel(req->aiocb);
|
||||
}
|
||||
if (req->bus->info->cancel) {
|
||||
req->bus->info->cancel(req);
|
||||
}
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
void scsi_req_abort(SCSIRequest *req, int status)
|
||||
{
|
||||
if (!req->enqueued) {
|
||||
return;
|
||||
}
|
||||
scsi_req_ref(req);
|
||||
scsi_req_dequeue(req);
|
||||
req->io_canceled = true;
|
||||
if (req->ops->cancel_io) {
|
||||
req->ops->cancel_io(req);
|
||||
}
|
||||
scsi_req_complete(req, status);
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
static int scsi_ua_precedence(SCSISense sense)
|
||||
|
@ -105,23 +105,6 @@ static void scsi_check_condition(SCSIDiskReq *r, SCSISense sense)
|
||||
scsi_req_complete(&r->req, CHECK_CONDITION);
|
||||
}
|
||||
|
||||
/* Cancel a pending data transfer. */
|
||||
static void scsi_cancel_io(SCSIRequest *req)
|
||||
{
|
||||
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||
|
||||
DPRINTF("Cancel tag=0x%x\n", req->tag);
|
||||
if (r->req.aiocb) {
|
||||
bdrv_aio_cancel(r->req.aiocb);
|
||||
|
||||
/* This reference was left in by scsi_*_data. We take ownership of
|
||||
* it the moment scsi_req_cancel is called, independent of whether
|
||||
* bdrv_aio_cancel completes the request or not. */
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
r->req.aiocb = NULL;
|
||||
}
|
||||
|
||||
static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
@ -185,6 +168,7 @@ static void scsi_aio_complete(void *opaque, int ret)
|
||||
r->req.aiocb = NULL;
|
||||
block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
|
||||
if (r->req.io_canceled) {
|
||||
scsi_req_cancel_complete(&r->req);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -197,9 +181,7 @@ static void scsi_aio_complete(void *opaque, int ret)
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
|
||||
static bool scsi_is_cmd_fua(SCSICommand *cmd)
|
||||
@ -233,6 +215,7 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
|
||||
if (r->req.io_canceled) {
|
||||
scsi_req_cancel_complete(&r->req);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -246,9 +229,7 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
|
||||
static void scsi_dma_complete_noio(void *opaque, int ret)
|
||||
@ -261,6 +242,7 @@ static void scsi_dma_complete_noio(void *opaque, int ret)
|
||||
block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
|
||||
}
|
||||
if (r->req.io_canceled) {
|
||||
scsi_req_cancel_complete(&r->req);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -280,9 +262,7 @@ static void scsi_dma_complete_noio(void *opaque, int ret)
|
||||
}
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
|
||||
static void scsi_dma_complete(void *opaque, int ret)
|
||||
@ -303,6 +283,7 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
r->req.aiocb = NULL;
|
||||
block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
|
||||
if (r->req.io_canceled) {
|
||||
scsi_req_cancel_complete(&r->req);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -320,9 +301,7 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
scsi_req_data(&r->req, r->qiov.size);
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
|
||||
/* Actually issue a read to the block device. */
|
||||
@ -337,6 +316,7 @@ static void scsi_do_read(void *opaque, int ret)
|
||||
block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
|
||||
}
|
||||
if (r->req.io_canceled) {
|
||||
scsi_req_cancel_complete(&r->req);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -363,9 +343,7 @@ static void scsi_do_read(void *opaque, int ret)
|
||||
}
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
|
||||
/* Read more data from scsi device into buffer. */
|
||||
@ -459,6 +437,7 @@ static void scsi_write_complete(void * opaque, int ret)
|
||||
block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
|
||||
}
|
||||
if (r->req.io_canceled) {
|
||||
scsi_req_cancel_complete(&r->req);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -481,9 +460,7 @@ static void scsi_write_complete(void * opaque, int ret)
|
||||
}
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
|
||||
static void scsi_write_data(SCSIRequest *req)
|
||||
@ -1553,6 +1530,7 @@ static void scsi_unmap_complete(void *opaque, int ret)
|
||||
|
||||
r->req.aiocb = NULL;
|
||||
if (r->req.io_canceled) {
|
||||
scsi_req_cancel_complete(&r->req);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -1582,9 +1560,7 @@ static void scsi_unmap_complete(void *opaque, int ret)
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
scsi_req_unref(&r->req);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
@ -1654,6 +1630,7 @@ static void scsi_write_same_complete(void *opaque, int ret)
|
||||
r->req.aiocb = NULL;
|
||||
block_acct_done(bdrv_get_stats(s->qdev.conf.bs), &r->acct);
|
||||
if (r->req.io_canceled) {
|
||||
scsi_req_cancel_complete(&r->req);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -1678,9 +1655,7 @@ static void scsi_write_same_complete(void *opaque, int ret)
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
|
||||
done:
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
scsi_req_unref(&r->req);
|
||||
qemu_vfree(data->iov.iov_base);
|
||||
g_free(data);
|
||||
}
|
||||
@ -2346,7 +2321,6 @@ static const SCSIReqOps scsi_disk_emulate_reqops = {
|
||||
.send_command = scsi_disk_emulate_command,
|
||||
.read_data = scsi_disk_emulate_read_data,
|
||||
.write_data = scsi_disk_emulate_write_data,
|
||||
.cancel_io = scsi_cancel_io,
|
||||
.get_buf = scsi_get_buf,
|
||||
};
|
||||
|
||||
@ -2356,7 +2330,6 @@ static const SCSIReqOps scsi_disk_dma_reqops = {
|
||||
.send_command = scsi_disk_dma_command,
|
||||
.read_data = scsi_read_data,
|
||||
.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,
|
||||
|
@ -93,6 +93,10 @@ static void scsi_command_complete(void *opaque, int ret)
|
||||
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
|
||||
|
||||
r->req.aiocb = NULL;
|
||||
if (r->req.io_canceled) {
|
||||
scsi_req_cancel_complete(&r->req);
|
||||
goto done;
|
||||
}
|
||||
if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
|
||||
r->req.sense_len = r->io_header.sb_len_wr;
|
||||
}
|
||||
@ -133,26 +137,8 @@ static void scsi_command_complete(void *opaque, int ret)
|
||||
r, r->req.tag, status);
|
||||
|
||||
scsi_req_complete(&r->req, status);
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cancel a pending data transfer. */
|
||||
static void scsi_cancel_io(SCSIRequest *req)
|
||||
{
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
|
||||
DPRINTF("Cancel tag=0x%x\n", req->tag);
|
||||
if (r->req.aiocb) {
|
||||
bdrv_aio_cancel(r->req.aiocb);
|
||||
|
||||
/* This reference was left in by scsi_*_data. We take ownership of
|
||||
* it independent of whether bdrv_aio_cancel completes the request
|
||||
* or not. */
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
r->req.aiocb = NULL;
|
||||
done:
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
|
||||
static int execute_command(BlockDriverState *bdrv,
|
||||
@ -186,8 +172,7 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
int len;
|
||||
|
||||
r->req.aiocb = NULL;
|
||||
if (ret) {
|
||||
DPRINTF("IO error ret %d\n", ret);
|
||||
if (ret || r->req.io_canceled) {
|
||||
scsi_command_complete(r, ret);
|
||||
return;
|
||||
}
|
||||
@ -211,9 +196,7 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
bdrv_set_guest_block_size(s->conf.bs, s->blocksize);
|
||||
|
||||
scsi_req_data(&r->req, len);
|
||||
if (!r->req.io_canceled) {
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
scsi_req_unref(&r->req);
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,8 +229,7 @@ static void scsi_write_complete(void * opaque, int ret)
|
||||
|
||||
DPRINTF("scsi_write_complete() ret = %d\n", ret);
|
||||
r->req.aiocb = NULL;
|
||||
if (ret) {
|
||||
DPRINTF("IO error\n");
|
||||
if (ret || r->req.io_canceled) {
|
||||
scsi_command_complete(r, ret);
|
||||
return;
|
||||
}
|
||||
@ -465,7 +447,6 @@ const SCSIReqOps scsi_generic_req_ops = {
|
||||
.send_command = scsi_send_command,
|
||||
.read_data = scsi_read_data,
|
||||
.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,
|
||||
|
@ -77,8 +77,9 @@ typedef struct vscsi_req {
|
||||
SCSIRequest *sreq;
|
||||
uint32_t qtag; /* qemu tag != srp tag */
|
||||
bool active;
|
||||
uint32_t data_len;
|
||||
bool writing;
|
||||
bool dma_error;
|
||||
uint32_t data_len;
|
||||
uint32_t senselen;
|
||||
uint8_t sense[SCSI_SENSE_BUF_SIZE];
|
||||
|
||||
@ -536,8 +537,8 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
|
||||
}
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc);
|
||||
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
|
||||
scsi_req_abort(req->sreq, CHECK_CONDITION);
|
||||
req->dma_error = true;
|
||||
scsi_req_cancel(req->sreq);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -591,6 +592,12 @@ static void vscsi_request_cancelled(SCSIRequest *sreq)
|
||||
{
|
||||
vscsi_req *req = sreq->hba_private;
|
||||
|
||||
if (req->dma_error) {
|
||||
VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(sreq->bus->qbus.parent);
|
||||
|
||||
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
|
||||
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
|
||||
}
|
||||
vscsi_put_req(req);
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "hw/virtio/vhost.h"
|
||||
#include "hw/virtio/virtio-scsi.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
|
||||
/* Features supported by host kernel. */
|
||||
static const int kernel_feature_bits[] = {
|
||||
@ -163,8 +164,8 @@ static void vhost_scsi_set_config(VirtIODevice *vdev,
|
||||
VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config;
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||
|
||||
if ((uint32_t) ldl_p(&scsiconf->sense_size) != vs->sense_size ||
|
||||
(uint32_t) ldl_p(&scsiconf->cdb_size) != vs->cdb_size) {
|
||||
if ((uint32_t) virtio_ldl_p(vdev, &scsiconf->sense_size) != vs->sense_size ||
|
||||
(uint32_t) virtio_ldl_p(vdev, &scsiconf->cdb_size) != vs->cdb_size) {
|
||||
error_report("vhost-scsi does not support changing the sense data and CDB sizes");
|
||||
exit(1);
|
||||
}
|
||||
|
229
hw/scsi/virtio-scsi-dataplane.c
Normal file
229
hw/scsi/virtio-scsi-dataplane.c
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Virtio SCSI dataplane
|
||||
*
|
||||
* Copyright Red Hat, Inc. 2014
|
||||
*
|
||||
* Authors:
|
||||
* Fam Zheng <famz@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/virtio/virtio-scsi.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include <hw/scsi/scsi.h>
|
||||
#include <block/scsi.h>
|
||||
#include <hw/virtio/virtio-bus.h>
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
#include "stdio.h"
|
||||
|
||||
/* Context: QEMU global mutex held */
|
||||
void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread)
|
||||
{
|
||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
|
||||
assert(!s->ctx);
|
||||
s->ctx = iothread_get_aio_context(vs->conf.iothread);
|
||||
|
||||
/* Don't try if transport does not support notifiers. */
|
||||
if (!k->set_guest_notifiers || !k->set_host_notifier) {
|
||||
fprintf(stderr, "virtio-scsi: Failed to set iothread "
|
||||
"(transport does not support notifiers)");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s,
|
||||
VirtQueue *vq,
|
||||
EventNotifierHandler *handler,
|
||||
int n)
|
||||
{
|
||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||
VirtIOSCSIVring *r = g_slice_new(VirtIOSCSIVring);
|
||||
|
||||
/* Set up virtqueue notify */
|
||||
if (k->set_host_notifier(qbus->parent, n, true) != 0) {
|
||||
fprintf(stderr, "virtio-scsi: Failed to set host notifier\n");
|
||||
exit(1);
|
||||
}
|
||||
r->host_notifier = *virtio_queue_get_host_notifier(vq);
|
||||
r->guest_notifier = *virtio_queue_get_guest_notifier(vq);
|
||||
aio_set_event_notifier(s->ctx, &r->host_notifier, handler);
|
||||
|
||||
r->parent = s;
|
||||
|
||||
if (!vring_setup(&r->vring, VIRTIO_DEVICE(s), n)) {
|
||||
fprintf(stderr, "virtio-scsi: VRing setup failed\n");
|
||||
exit(1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s,
|
||||
VirtIOSCSIVring *vring)
|
||||
{
|
||||
VirtIOSCSIReq *req = virtio_scsi_init_req(s, NULL);
|
||||
int r;
|
||||
|
||||
req->vring = vring;
|
||||
r = vring_pop((VirtIODevice *)s, &vring->vring, &req->elem);
|
||||
if (r < 0) {
|
||||
virtio_scsi_free_req(req);
|
||||
req = NULL;
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req)
|
||||
{
|
||||
vring_push(&req->vring->vring, &req->elem,
|
||||
req->qsgl.size + req->resp_iov.size);
|
||||
event_notifier_set(&req->vring->guest_notifier);
|
||||
}
|
||||
|
||||
static void virtio_scsi_iothread_handle_ctrl(EventNotifier *notifier)
|
||||
{
|
||||
VirtIOSCSIVring *vring = container_of(notifier,
|
||||
VirtIOSCSIVring, host_notifier);
|
||||
VirtIOSCSI *s = VIRTIO_SCSI(vring->parent);
|
||||
VirtIOSCSIReq *req;
|
||||
|
||||
event_notifier_test_and_clear(notifier);
|
||||
while ((req = virtio_scsi_pop_req_vring(s, vring))) {
|
||||
virtio_scsi_handle_ctrl_req(s, req);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_scsi_iothread_handle_event(EventNotifier *notifier)
|
||||
{
|
||||
VirtIOSCSIVring *vring = container_of(notifier,
|
||||
VirtIOSCSIVring, host_notifier);
|
||||
VirtIOSCSI *s = vring->parent;
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||
|
||||
event_notifier_test_and_clear(notifier);
|
||||
|
||||
if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->events_dropped) {
|
||||
virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_scsi_iothread_handle_cmd(EventNotifier *notifier)
|
||||
{
|
||||
VirtIOSCSIVring *vring = container_of(notifier,
|
||||
VirtIOSCSIVring, host_notifier);
|
||||
VirtIOSCSI *s = (VirtIOSCSI *)vring->parent;
|
||||
VirtIOSCSIReq *req, *next;
|
||||
QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
|
||||
|
||||
event_notifier_test_and_clear(notifier);
|
||||
while ((req = virtio_scsi_pop_req_vring(s, vring))) {
|
||||
if (virtio_scsi_handle_cmd_req_prepare(s, req)) {
|
||||
QTAILQ_INSERT_TAIL(&reqs, req, next);
|
||||
}
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH_SAFE(req, &reqs, next, next) {
|
||||
virtio_scsi_handle_cmd_req_submit(s, req);
|
||||
}
|
||||
}
|
||||
|
||||
/* Context: QEMU global mutex held */
|
||||
void virtio_scsi_dataplane_start(VirtIOSCSI *s)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
|
||||
if (s->dataplane_started ||
|
||||
s->dataplane_starting ||
|
||||
s->ctx != iothread_get_aio_context(vs->conf.iothread)) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->dataplane_starting = true;
|
||||
|
||||
/* Set up guest notifier (irq) */
|
||||
rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "virtio-scsi: Failed to set guest notifiers, "
|
||||
"ensure -enable-kvm is set\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
aio_context_acquire(s->ctx);
|
||||
s->ctrl_vring = virtio_scsi_vring_init(s, vs->ctrl_vq,
|
||||
virtio_scsi_iothread_handle_ctrl,
|
||||
0);
|
||||
s->event_vring = virtio_scsi_vring_init(s, vs->event_vq,
|
||||
virtio_scsi_iothread_handle_event,
|
||||
1);
|
||||
s->cmd_vrings = g_malloc0(sizeof(VirtIOSCSIVring) * vs->conf.num_queues);
|
||||
for (i = 0; i < vs->conf.num_queues; i++) {
|
||||
s->cmd_vrings[i] =
|
||||
virtio_scsi_vring_init(s, vs->cmd_vqs[i],
|
||||
virtio_scsi_iothread_handle_cmd,
|
||||
i + 2);
|
||||
}
|
||||
|
||||
aio_context_release(s->ctx);
|
||||
s->dataplane_starting = false;
|
||||
s->dataplane_started = true;
|
||||
}
|
||||
|
||||
/* Context: QEMU global mutex held */
|
||||
void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
|
||||
{
|
||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
int i;
|
||||
|
||||
if (!s->dataplane_started || s->dataplane_stopping) {
|
||||
return;
|
||||
}
|
||||
s->dataplane_stopping = true;
|
||||
assert(s->ctx == iothread_get_aio_context(vs->conf.iothread));
|
||||
|
||||
aio_context_acquire(s->ctx);
|
||||
|
||||
aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL);
|
||||
aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL);
|
||||
for (i = 0; i < vs->conf.num_queues; i++) {
|
||||
aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL);
|
||||
}
|
||||
|
||||
bdrv_drain_all(); /* ensure there are no in-flight requests */
|
||||
|
||||
aio_context_release(s->ctx);
|
||||
|
||||
/* Sync vring state back to virtqueue so that non-dataplane request
|
||||
* processing can continue when we disable the host notifier below.
|
||||
*/
|
||||
vring_teardown(&s->ctrl_vring->vring, vdev, 0);
|
||||
vring_teardown(&s->event_vring->vring, vdev, 1);
|
||||
for (i = 0; i < vs->conf.num_queues; i++) {
|
||||
vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i);
|
||||
}
|
||||
|
||||
for (i = 0; i < vs->conf.num_queues + 2; i++) {
|
||||
k->set_host_notifier(qbus->parent, i, false);
|
||||
}
|
||||
|
||||
/* Clean up guest notifier (irq) */
|
||||
k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
|
||||
s->dataplane_stopping = false;
|
||||
s->dataplane_started = false;
|
||||
}
|
@ -20,34 +20,7 @@
|
||||
#include <block/scsi.h>
|
||||
#include <hw/virtio/virtio-bus.h>
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
|
||||
typedef struct VirtIOSCSIReq {
|
||||
VirtIOSCSI *dev;
|
||||
VirtQueue *vq;
|
||||
VirtQueueElement elem;
|
||||
QEMUSGList qsgl;
|
||||
SCSIRequest *sreq;
|
||||
size_t resp_size;
|
||||
enum SCSIXferMode mode;
|
||||
QEMUIOVector resp_iov;
|
||||
union {
|
||||
VirtIOSCSICmdResp cmd;
|
||||
VirtIOSCSICtrlTMFResp tmf;
|
||||
VirtIOSCSICtrlANResp an;
|
||||
VirtIOSCSIEvent event;
|
||||
} resp;
|
||||
union {
|
||||
struct {
|
||||
VirtIOSCSICmdReq cmd;
|
||||
uint8_t cdb[];
|
||||
} QEMU_PACKED;
|
||||
VirtIOSCSICtrlTMFReq tmf;
|
||||
VirtIOSCSICtrlANReq an;
|
||||
} req;
|
||||
} VirtIOSCSIReq;
|
||||
|
||||
QEMU_BUILD_BUG_ON(offsetof(VirtIOSCSIReq, req.cdb) !=
|
||||
offsetof(VirtIOSCSIReq, req.cmd) + sizeof(VirtIOSCSICmdReq));
|
||||
#include "migration/migration.h"
|
||||
|
||||
static inline int virtio_scsi_get_lun(uint8_t *lun)
|
||||
{
|
||||
@ -65,26 +38,29 @@ static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun)
|
||||
return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
|
||||
}
|
||||
|
||||
static VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq)
|
||||
VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq)
|
||||
{
|
||||
VirtIOSCSIReq *req;
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
|
||||
req = g_malloc0(sizeof(*req) + vs->cdb_size);
|
||||
VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s;
|
||||
const size_t zero_skip = offsetof(VirtIOSCSIReq, elem)
|
||||
+ sizeof(VirtQueueElement);
|
||||
|
||||
req = g_slice_alloc(sizeof(*req) + vs->cdb_size);
|
||||
req->vq = vq;
|
||||
req->dev = s;
|
||||
req->sreq = NULL;
|
||||
qemu_sglist_init(&req->qsgl, DEVICE(s), 8, &address_space_memory);
|
||||
qemu_iovec_init(&req->resp_iov, 1);
|
||||
memset((uint8_t *)req + zero_skip, 0, sizeof(*req) - zero_skip);
|
||||
return req;
|
||||
}
|
||||
|
||||
static void virtio_scsi_free_req(VirtIOSCSIReq *req)
|
||||
void virtio_scsi_free_req(VirtIOSCSIReq *req)
|
||||
{
|
||||
VirtIOSCSICommon *vs = (VirtIOSCSICommon *)req->dev;
|
||||
|
||||
qemu_iovec_destroy(&req->resp_iov);
|
||||
qemu_sglist_destroy(&req->qsgl);
|
||||
g_free(req);
|
||||
g_slice_free1(sizeof(*req) + vs->cdb_size, req);
|
||||
}
|
||||
|
||||
static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
|
||||
@ -94,13 +70,19 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||
|
||||
qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size);
|
||||
virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size);
|
||||
if (req->vring) {
|
||||
assert(req->vq == NULL);
|
||||
virtio_scsi_vring_push_notify(req);
|
||||
} else {
|
||||
virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size);
|
||||
virtio_notify(vdev, vq);
|
||||
}
|
||||
|
||||
if (req->sreq) {
|
||||
req->sreq->hba_private = NULL;
|
||||
scsi_req_unref(req->sreq);
|
||||
}
|
||||
virtio_scsi_free_req(req);
|
||||
virtio_notify(vdev, vq);
|
||||
}
|
||||
|
||||
static void virtio_scsi_bad_req(void)
|
||||
@ -226,13 +208,39 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
|
||||
return req;
|
||||
}
|
||||
|
||||
static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||
typedef struct {
|
||||
Notifier notifier;
|
||||
VirtIOSCSIReq *tmf_req;
|
||||
} VirtIOSCSICancelNotifier;
|
||||
|
||||
static void virtio_scsi_cancel_notify(Notifier *notifier, void *data)
|
||||
{
|
||||
VirtIOSCSICancelNotifier *n = container_of(notifier,
|
||||
VirtIOSCSICancelNotifier,
|
||||
notifier);
|
||||
|
||||
if (--n->tmf_req->remaining == 0) {
|
||||
virtio_scsi_complete_req(n->tmf_req);
|
||||
}
|
||||
g_slice_free(VirtIOSCSICancelNotifier, n);
|
||||
}
|
||||
|
||||
/* Return 0 if the request is ready to be completed and return to guest;
|
||||
* -EINPROGRESS if the request is submitted and will be completed later, in the
|
||||
* case of async cancellation. */
|
||||
static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||
{
|
||||
SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf.lun);
|
||||
SCSIRequest *r, *next;
|
||||
BusChild *kid;
|
||||
int target;
|
||||
int ret = 0;
|
||||
|
||||
if (s->dataplane_started && bdrv_get_aio_context(d->conf.bs) != s->ctx) {
|
||||
aio_context_acquire(s->ctx);
|
||||
bdrv_set_aio_context(d->conf.bs, s->ctx);
|
||||
aio_context_release(s->ctx);
|
||||
}
|
||||
/* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */
|
||||
req->resp.tmf.response = VIRTIO_SCSI_S_OK;
|
||||
|
||||
@ -264,7 +272,14 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||
*/
|
||||
req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
|
||||
} else {
|
||||
scsi_req_cancel(r);
|
||||
VirtIOSCSICancelNotifier *notifier;
|
||||
|
||||
req->remaining = 1;
|
||||
notifier = g_slice_new(VirtIOSCSICancelNotifier);
|
||||
notifier->tmf_req = req;
|
||||
notifier->notifier.notify = virtio_scsi_cancel_notify;
|
||||
scsi_req_cancel_async(r, ¬ifier->notifier);
|
||||
ret = -EINPROGRESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -290,6 +305,13 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||
if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) {
|
||||
goto incorrect_lun;
|
||||
}
|
||||
|
||||
/* Add 1 to "remaining" until virtio_scsi_do_tmf returns.
|
||||
* This way, if the bus starts calling back to the notifiers
|
||||
* even before we finish the loop, virtio_scsi_cancel_notify
|
||||
* will not complete the TMF too early.
|
||||
*/
|
||||
req->remaining = 1;
|
||||
QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
|
||||
if (r->hba_private) {
|
||||
if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) {
|
||||
@ -299,10 +321,19 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||
req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
|
||||
break;
|
||||
} else {
|
||||
scsi_req_cancel(r);
|
||||
VirtIOSCSICancelNotifier *notifier;
|
||||
|
||||
req->remaining++;
|
||||
notifier = g_slice_new(VirtIOSCSICancelNotifier);
|
||||
notifier->notifier.notify = virtio_scsi_cancel_notify;
|
||||
notifier->tmf_req = req;
|
||||
scsi_req_cancel_async(r, ¬ifier->notifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (--req->remaining > 0) {
|
||||
ret = -EINPROGRESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
|
||||
@ -323,14 +354,53 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
return ret;
|
||||
|
||||
incorrect_lun:
|
||||
req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN;
|
||||
return;
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||
{
|
||||
VirtIODevice *vdev = (VirtIODevice *)s;
|
||||
int type;
|
||||
int r = 0;
|
||||
|
||||
if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0,
|
||||
&type, sizeof(type)) < sizeof(type)) {
|
||||
virtio_scsi_bad_req();
|
||||
return;
|
||||
}
|
||||
|
||||
virtio_tswap32s(vdev, &req->req.tmf.type);
|
||||
if (req->req.tmf.type == VIRTIO_SCSI_T_TMF) {
|
||||
if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq),
|
||||
sizeof(VirtIOSCSICtrlTMFResp)) < 0) {
|
||||
virtio_scsi_bad_req();
|
||||
} else {
|
||||
r = 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 (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlANReq),
|
||||
sizeof(VirtIOSCSICtrlANResp)) < 0) {
|
||||
virtio_scsi_bad_req();
|
||||
} else {
|
||||
req->resp.an.event_actual = 0;
|
||||
req->resp.an.response = VIRTIO_SCSI_S_OK;
|
||||
}
|
||||
}
|
||||
if (r == 0) {
|
||||
virtio_scsi_complete_req(req);
|
||||
} else {
|
||||
assert(r == -EINPROGRESS);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
|
||||
@ -338,35 +408,12 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
|
||||
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
|
||||
VirtIOSCSIReq *req;
|
||||
|
||||
if (s->ctx && !s->dataplane_disabled) {
|
||||
virtio_scsi_dataplane_start(s);
|
||||
return;
|
||||
}
|
||||
while ((req = virtio_scsi_pop_req(s, vq))) {
|
||||
int type;
|
||||
|
||||
if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0,
|
||||
&type, sizeof(type)) < sizeof(type)) {
|
||||
virtio_scsi_bad_req();
|
||||
continue;
|
||||
}
|
||||
|
||||
virtio_tswap32s(vdev, &req->req.tmf.type);
|
||||
if (req->req.tmf.type == VIRTIO_SCSI_T_TMF) {
|
||||
if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq),
|
||||
sizeof(VirtIOSCSICtrlTMFResp)) < 0) {
|
||||
virtio_scsi_bad_req();
|
||||
} else {
|
||||
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 (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlANReq),
|
||||
sizeof(VirtIOSCSICtrlANResp)) < 0) {
|
||||
virtio_scsi_bad_req();
|
||||
} else {
|
||||
req->resp.an.event_actual = 0;
|
||||
req->resp.an.response = VIRTIO_SCSI_S_OK;
|
||||
}
|
||||
}
|
||||
virtio_scsi_complete_req(req);
|
||||
virtio_scsi_handle_ctrl_req(s, req);
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,13 +467,7 @@ static int virtio_scsi_parse_cdb(SCSIDevice *dev, SCSICommand *cmd,
|
||||
* host device passthrough.
|
||||
*/
|
||||
cmd->xfer = req->qsgl.size;
|
||||
if (cmd->xfer == 0) {
|
||||
cmd->mode = SCSI_XFER_NONE;
|
||||
} else if (iov_size(req->elem.in_sg, req->elem.in_num) > req->resp_size) {
|
||||
cmd->mode = SCSI_XFER_FROM_DEV;
|
||||
} else {
|
||||
cmd->mode = SCSI_XFER_TO_DEV;
|
||||
}
|
||||
cmd->mode = req->mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -458,52 +499,78 @@ static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req)
|
||||
virtio_scsi_complete_cmd_req(req);
|
||||
}
|
||||
|
||||
bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||
{
|
||||
VirtIOSCSICommon *vs = &s->parent_obj;
|
||||
SCSIDevice *d;
|
||||
int rc;
|
||||
|
||||
rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
|
||||
sizeof(VirtIOSCSICmdResp) + vs->sense_size);
|
||||
if (rc < 0) {
|
||||
if (rc == -ENOTSUP) {
|
||||
virtio_scsi_fail_cmd_req(req);
|
||||
} else {
|
||||
virtio_scsi_bad_req();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
d = virtio_scsi_device_find(s, req->req.cmd.lun);
|
||||
if (!d) {
|
||||
req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET;
|
||||
virtio_scsi_complete_cmd_req(req);
|
||||
return false;
|
||||
}
|
||||
if (s->dataplane_started && bdrv_get_aio_context(d->conf.bs) != s->ctx) {
|
||||
aio_context_acquire(s->ctx);
|
||||
bdrv_set_aio_context(d->conf.bs, s->ctx);
|
||||
aio_context_release(s->ctx);
|
||||
}
|
||||
req->sreq = scsi_req_new(d, req->req.cmd.tag,
|
||||
virtio_scsi_get_lun(req->req.cmd.lun),
|
||||
req->req.cdb, req);
|
||||
|
||||
if (req->sreq->cmd.mode != SCSI_XFER_NONE
|
||||
&& (req->sreq->cmd.mode != req->mode ||
|
||||
req->sreq->cmd.xfer > req->qsgl.size)) {
|
||||
req->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN;
|
||||
virtio_scsi_complete_cmd_req(req);
|
||||
return false;
|
||||
}
|
||||
scsi_req_ref(req->sreq);
|
||||
bdrv_io_plug(d->conf.bs);
|
||||
return true;
|
||||
}
|
||||
|
||||
void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||
{
|
||||
if (scsi_req_enqueue(req->sreq)) {
|
||||
scsi_req_continue(req->sreq);
|
||||
}
|
||||
bdrv_io_unplug(req->sreq->dev->conf.bs);
|
||||
scsi_req_unref(req->sreq);
|
||||
}
|
||||
|
||||
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;
|
||||
VirtIOSCSIReq *req, *next;
|
||||
QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
|
||||
|
||||
if (s->ctx && !s->dataplane_disabled) {
|
||||
virtio_scsi_dataplane_start(s);
|
||||
return;
|
||||
}
|
||||
while ((req = virtio_scsi_pop_req(s, vq))) {
|
||||
SCSIDevice *d;
|
||||
int rc;
|
||||
|
||||
rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
|
||||
sizeof(VirtIOSCSICmdResp) + vs->sense_size);
|
||||
if (rc < 0) {
|
||||
if (rc == -ENOTSUP) {
|
||||
virtio_scsi_fail_cmd_req(req);
|
||||
} else {
|
||||
virtio_scsi_bad_req();
|
||||
}
|
||||
continue;
|
||||
if (virtio_scsi_handle_cmd_req_prepare(s, req)) {
|
||||
QTAILQ_INSERT_TAIL(&reqs, req, next);
|
||||
}
|
||||
}
|
||||
|
||||
d = virtio_scsi_device_find(s, req->req.cmd.lun);
|
||||
if (!d) {
|
||||
req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET;
|
||||
virtio_scsi_complete_cmd_req(req);
|
||||
continue;
|
||||
}
|
||||
req->sreq = scsi_req_new(d, req->req.cmd.tag,
|
||||
virtio_scsi_get_lun(req->req.cmd.lun),
|
||||
req->req.cdb, req);
|
||||
|
||||
if (req->sreq->cmd.mode != SCSI_XFER_NONE
|
||||
&& (req->sreq->cmd.mode != req->mode ||
|
||||
req->sreq->cmd.xfer > req->qsgl.size)) {
|
||||
req->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN;
|
||||
virtio_scsi_complete_cmd_req(req);
|
||||
continue;
|
||||
}
|
||||
|
||||
n = scsi_req_enqueue(req->sreq);
|
||||
if (n) {
|
||||
scsi_req_continue(req->sreq);
|
||||
}
|
||||
QTAILQ_FOREACH_SAFE(req, &reqs, next, next) {
|
||||
virtio_scsi_handle_cmd_req_submit(s, req);
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,6 +619,9 @@ static void virtio_scsi_reset(VirtIODevice *vdev)
|
||||
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||
|
||||
if (s->ctx) {
|
||||
virtio_scsi_dataplane_stop(s);
|
||||
}
|
||||
s->resetting++;
|
||||
qbus_reset_all(&s->bus.qbus);
|
||||
s->resetting--;
|
||||
@ -582,8 +652,8 @@ static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||
uint32_t event, uint32_t reason)
|
||||
void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||
uint32_t event, uint32_t reason)
|
||||
{
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
VirtIOSCSIReq *req;
|
||||
@ -594,10 +664,19 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||
return;
|
||||
}
|
||||
|
||||
req = virtio_scsi_pop_req(s, vs->event_vq);
|
||||
if (s->dataplane_started) {
|
||||
assert(s->ctx);
|
||||
aio_context_acquire(s->ctx);
|
||||
}
|
||||
|
||||
if (s->dataplane_started) {
|
||||
req = virtio_scsi_pop_req_vring(s, s->event_vring);
|
||||
} else {
|
||||
req = virtio_scsi_pop_req(s, vs->event_vq);
|
||||
}
|
||||
if (!req) {
|
||||
s->events_dropped = true;
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (s->events_dropped) {
|
||||
@ -626,12 +705,20 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||
evt->lun[3] = dev->lun & 0xFF;
|
||||
}
|
||||
virtio_scsi_complete_req(req);
|
||||
out:
|
||||
if (s->dataplane_started) {
|
||||
aio_context_release(s->ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
|
||||
|
||||
if (s->ctx && !s->dataplane_disabled) {
|
||||
virtio_scsi_dataplane_start(s);
|
||||
return;
|
||||
}
|
||||
if (s->events_dropped) {
|
||||
virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
|
||||
}
|
||||
@ -717,6 +804,35 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp,
|
||||
s->cmd_vqs[i] = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE,
|
||||
cmd);
|
||||
}
|
||||
|
||||
if (s->conf.iothread) {
|
||||
virtio_scsi_set_iothread(VIRTIO_SCSI(s), s->conf.iothread);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable dataplane thread during live migration since it does not
|
||||
* update the dirty memory bitmap yet.
|
||||
*/
|
||||
static void virtio_scsi_migration_state_changed(Notifier *notifier, void *data)
|
||||
{
|
||||
VirtIOSCSI *s = container_of(notifier, VirtIOSCSI,
|
||||
migration_state_notifier);
|
||||
MigrationState *mig = data;
|
||||
|
||||
if (migration_in_setup(mig)) {
|
||||
if (!s->dataplane_started) {
|
||||
return;
|
||||
}
|
||||
virtio_scsi_dataplane_stop(s);
|
||||
s->dataplane_disabled = true;
|
||||
} else if (migration_has_finished(mig) ||
|
||||
migration_has_failed(mig)) {
|
||||
if (s->dataplane_started) {
|
||||
return;
|
||||
}
|
||||
bdrv_drain_all(); /* complete in-flight non-dataplane requests */
|
||||
s->dataplane_disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_scsi_device_realize(DeviceState *dev, Error **errp)
|
||||
@ -747,6 +863,18 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1,
|
||||
virtio_scsi_save, virtio_scsi_load, s);
|
||||
s->migration_state_notifier.notify = virtio_scsi_migration_state_changed;
|
||||
add_migration_state_change_notifier(&s->migration_state_notifier);
|
||||
}
|
||||
|
||||
static void virtio_scsi_instance_init(Object *obj)
|
||||
{
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(obj);
|
||||
|
||||
object_property_add_link(obj, "iothread", TYPE_IOTHREAD,
|
||||
(Object **)&vs->conf.iothread,
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
|
||||
}
|
||||
|
||||
void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp)
|
||||
@ -763,6 +891,7 @@ static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp)
|
||||
VirtIOSCSI *s = VIRTIO_SCSI(dev);
|
||||
|
||||
unregister_savevm(dev, "virtio-scsi", s);
|
||||
remove_migration_state_change_notifier(&s->migration_state_notifier);
|
||||
|
||||
virtio_scsi_common_unrealize(dev, errp);
|
||||
}
|
||||
@ -807,6 +936,7 @@ static const TypeInfo virtio_scsi_info = {
|
||||
.name = TYPE_VIRTIO_SCSI,
|
||||
.parent = TYPE_VIRTIO_SCSI_COMMON,
|
||||
.instance_size = sizeof(VirtIOSCSI),
|
||||
.instance_init = virtio_scsi_instance_init,
|
||||
.class_init = virtio_scsi_class_init,
|
||||
};
|
||||
|
||||
|
@ -924,7 +924,6 @@ static Property virtio_9p_pci_properties[] = {
|
||||
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
|
||||
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
|
||||
DEFINE_VIRTIO_9P_PROPERTIES(V9fsPCIState, vdev.fsconf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -946,8 +945,9 @@ static void virtio_9p_pci_class_init(ObjectClass *klass, void *data)
|
||||
static void virtio_9p_pci_instance_init(Object *obj)
|
||||
{
|
||||
V9fsPCIState *dev = VIRTIO_9P_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_9P);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_9P);
|
||||
}
|
||||
|
||||
static const TypeInfo virtio_9p_pci_info = {
|
||||
@ -1110,10 +1110,9 @@ static void virtio_blk_pci_class_init(ObjectClass *klass, void *data)
|
||||
static void virtio_blk_pci_instance_init(Object *obj)
|
||||
{
|
||||
VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
object_unref(OBJECT(&dev->vdev));
|
||||
qdev_alias_all_properties(DEVICE(&dev->vdev), obj);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_BLK);
|
||||
object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread",
|
||||
&error_abort);
|
||||
}
|
||||
@ -1134,7 +1133,6 @@ 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.parent_obj.conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -1184,8 +1182,11 @@ static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data)
|
||||
static void virtio_scsi_pci_instance_init(Object *obj)
|
||||
{
|
||||
VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SCSI);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_SCSI);
|
||||
object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread",
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static const TypeInfo virtio_scsi_pci_info = {
|
||||
@ -1202,7 +1203,6 @@ static const TypeInfo virtio_scsi_pci_info = {
|
||||
static Property vhost_scsi_pci_properties[] = {
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
|
||||
DEV_NVECTORS_UNSPECIFIED),
|
||||
DEFINE_VHOST_SCSI_PROPERTIES(VHostSCSIPCI, vdev.parent_obj.conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -1240,8 +1240,9 @@ static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data)
|
||||
static void vhost_scsi_pci_instance_init(Object *obj)
|
||||
{
|
||||
VHostSCSIPCI *dev = VHOST_SCSI_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VHOST_SCSI);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VHOST_SCSI);
|
||||
}
|
||||
|
||||
static const TypeInfo vhost_scsi_pci_info = {
|
||||
@ -1322,7 +1323,7 @@ static void virtio_balloon_pci_instance_init(Object *obj)
|
||||
VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BALLOON);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
object_unref(OBJECT(&dev->vdev));
|
||||
object_property_add(obj, "guest-stats", "guest statistics",
|
||||
balloon_pci_stats_get_all, NULL, NULL, dev,
|
||||
NULL);
|
||||
@ -1384,7 +1385,6 @@ static Property virtio_serial_pci_properties[] = {
|
||||
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
|
||||
DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
|
||||
DEFINE_VIRTIO_SERIAL_PROPERTIES(VirtIOSerialPCI, vdev.serial),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -1405,8 +1405,9 @@ static void virtio_serial_pci_class_init(ObjectClass *klass, void *data)
|
||||
static void virtio_serial_pci_instance_init(Object *obj)
|
||||
{
|
||||
VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SERIAL);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_SERIAL);
|
||||
}
|
||||
|
||||
static const TypeInfo virtio_serial_pci_info = {
|
||||
@ -1424,8 +1425,6 @@ static Property virtio_net_properties[] = {
|
||||
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false),
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
|
||||
DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features),
|
||||
DEFINE_NIC_PROPERTIES(VirtIONetPCI, vdev.nic_conf),
|
||||
DEFINE_VIRTIO_NET_PROPERTIES(VirtIONetPCI, vdev.net_conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -1464,8 +1463,9 @@ static void virtio_net_pci_class_init(ObjectClass *klass, void *data)
|
||||
static void virtio_net_pci_instance_init(Object *obj)
|
||||
{
|
||||
VirtIONetPCI *dev = VIRTIO_NET_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_NET);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_NET);
|
||||
}
|
||||
|
||||
static const TypeInfo virtio_net_pci_info = {
|
||||
@ -1479,7 +1479,6 @@ static const TypeInfo virtio_net_pci_info = {
|
||||
/* virtio-rng-pci */
|
||||
|
||||
static Property virtio_rng_pci_properties[] = {
|
||||
DEFINE_VIRTIO_RNG_PROPERTIES(VirtIORngPCI, vdev.conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -1519,8 +1518,9 @@ static void virtio_rng_pci_class_init(ObjectClass *klass, void *data)
|
||||
static void virtio_rng_initfn(Object *obj)
|
||||
{
|
||||
VirtIORngPCI *dev = VIRTIO_RNG_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_RNG);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VIRTIO_RNG);
|
||||
object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
|
||||
(Object **)&dev->vdev.conf.rng,
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
|
@ -1123,6 +1123,17 @@ static void virtio_vmstate_change(void *opaque, int running, RunState state)
|
||||
}
|
||||
}
|
||||
|
||||
void virtio_instance_init_common(Object *proxy_obj, void *data,
|
||||
size_t vdev_size, const char *vdev_name)
|
||||
{
|
||||
DeviceState *vdev = data;
|
||||
|
||||
object_initialize(vdev, vdev_size, vdev_name);
|
||||
object_property_add_child(proxy_obj, "virtio-backend", OBJECT(vdev), NULL);
|
||||
object_unref(OBJECT(vdev));
|
||||
qdev_alias_all_properties(vdev, proxy_obj);
|
||||
}
|
||||
|
||||
void virtio_init(VirtIODevice *vdev, const char *name,
|
||||
uint16_t device_id, size_t config_size)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "block/block.h"
|
||||
#include "hw/block/block.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/notify.h"
|
||||
|
||||
#define MAX_SCSI_DEVS 255
|
||||
|
||||
@ -50,17 +51,25 @@ struct SCSIRequest {
|
||||
uint32_t tag;
|
||||
uint32_t lun;
|
||||
uint32_t status;
|
||||
void *hba_private;
|
||||
size_t resid;
|
||||
SCSICommand cmd;
|
||||
NotifierList cancel_notifiers;
|
||||
|
||||
/* Note:
|
||||
* - fields before sense are initialized by scsi_req_alloc;
|
||||
* - sense[] is uninitialized;
|
||||
* - fields after sense are memset to 0 by scsi_req_alloc.
|
||||
* */
|
||||
|
||||
uint8_t sense[SCSI_SENSE_BUF_SIZE];
|
||||
uint32_t sense_len;
|
||||
bool enqueued;
|
||||
bool io_canceled;
|
||||
bool retry;
|
||||
bool dma_started;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
QEMUSGList *sg;
|
||||
bool dma_started;
|
||||
uint8_t sense[SCSI_SENSE_BUF_SIZE];
|
||||
uint32_t sense_len;
|
||||
bool enqueued;
|
||||
bool io_canceled;
|
||||
bool retry;
|
||||
void *hba_private;
|
||||
QTAILQ_ENTRY(SCSIRequest) next;
|
||||
};
|
||||
|
||||
@ -123,7 +132,6 @@ struct SCSIReqOps {
|
||||
int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
|
||||
void (*read_data)(SCSIRequest *req);
|
||||
void (*write_data)(SCSIRequest *req);
|
||||
void (*cancel_io)(SCSIRequest *req);
|
||||
uint8_t *(*get_buf)(SCSIRequest *req);
|
||||
|
||||
void (*save_request)(QEMUFile *f, SCSIRequest *req);
|
||||
@ -258,8 +266,9 @@ void scsi_req_data(SCSIRequest *req, int len);
|
||||
void scsi_req_complete(SCSIRequest *req, int status);
|
||||
uint8_t *scsi_req_get_buf(SCSIRequest *req);
|
||||
int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len);
|
||||
void scsi_req_abort(SCSIRequest *req, int status);
|
||||
void scsi_req_cancel_complete(SCSIRequest *req);
|
||||
void scsi_req_cancel(SCSIRequest *req);
|
||||
void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier);
|
||||
void scsi_req_retry(SCSIRequest *req);
|
||||
void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense);
|
||||
void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense);
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/scsi/scsi.h"
|
||||
#include "sysemu/iothread.h"
|
||||
#include "hw/virtio/dataplane/vring.h"
|
||||
|
||||
#define TYPE_VIRTIO_SCSI_COMMON "virtio-scsi-common"
|
||||
#define VIRTIO_SCSI_COMMON(obj) \
|
||||
@ -151,8 +153,18 @@ struct VirtIOSCSIConf {
|
||||
uint32_t cmd_per_lun;
|
||||
char *vhostfd;
|
||||
char *wwpn;
|
||||
IOThread *iothread;
|
||||
};
|
||||
|
||||
struct VirtIOSCSI;
|
||||
|
||||
typedef struct {
|
||||
struct VirtIOSCSI *parent;
|
||||
Vring vring;
|
||||
EventNotifier host_notifier;
|
||||
EventNotifier guest_notifier;
|
||||
} VirtIOSCSIVring;
|
||||
|
||||
typedef struct VirtIOSCSICommon {
|
||||
VirtIODevice parent_obj;
|
||||
VirtIOSCSIConf conf;
|
||||
@ -164,14 +176,74 @@ typedef struct VirtIOSCSICommon {
|
||||
VirtQueue **cmd_vqs;
|
||||
} VirtIOSCSICommon;
|
||||
|
||||
typedef struct {
|
||||
typedef struct VirtIOSCSI {
|
||||
VirtIOSCSICommon parent_obj;
|
||||
|
||||
SCSIBus bus;
|
||||
int resetting;
|
||||
bool events_dropped;
|
||||
|
||||
/* Fields for dataplane below */
|
||||
AioContext *ctx; /* one iothread per virtio-scsi-pci for now */
|
||||
|
||||
/* Vring is used instead of vq in dataplane code, because of the underlying
|
||||
* memory layer thread safety */
|
||||
VirtIOSCSIVring *ctrl_vring;
|
||||
VirtIOSCSIVring *event_vring;
|
||||
VirtIOSCSIVring **cmd_vrings;
|
||||
bool dataplane_started;
|
||||
bool dataplane_starting;
|
||||
bool dataplane_stopping;
|
||||
bool dataplane_disabled;
|
||||
Notifier migration_state_notifier;
|
||||
} VirtIOSCSI;
|
||||
|
||||
typedef struct VirtIOSCSIReq {
|
||||
VirtIOSCSI *dev;
|
||||
VirtQueue *vq;
|
||||
QEMUSGList qsgl;
|
||||
QEMUIOVector resp_iov;
|
||||
|
||||
/* Note:
|
||||
* - fields before elem are initialized by virtio_scsi_init_req;
|
||||
* - elem is uninitialized at the time of allocation.
|
||||
* - fields after elem are zeroed by virtio_scsi_init_req.
|
||||
* */
|
||||
|
||||
VirtQueueElement elem;
|
||||
/* Set by dataplane code. */
|
||||
VirtIOSCSIVring *vring;
|
||||
|
||||
union {
|
||||
/* Used for two-stage request submission */
|
||||
QTAILQ_ENTRY(VirtIOSCSIReq) next;
|
||||
|
||||
/* Used for cancellation of request during TMFs */
|
||||
int remaining;
|
||||
};
|
||||
|
||||
SCSIRequest *sreq;
|
||||
size_t resp_size;
|
||||
enum SCSIXferMode mode;
|
||||
union {
|
||||
VirtIOSCSICmdResp cmd;
|
||||
VirtIOSCSICtrlTMFResp tmf;
|
||||
VirtIOSCSICtrlANResp an;
|
||||
VirtIOSCSIEvent event;
|
||||
} resp;
|
||||
union {
|
||||
struct {
|
||||
VirtIOSCSICmdReq cmd;
|
||||
uint8_t cdb[];
|
||||
} QEMU_PACKED;
|
||||
VirtIOSCSICtrlTMFReq tmf;
|
||||
VirtIOSCSICtrlANReq an;
|
||||
} req;
|
||||
} VirtIOSCSIReq;
|
||||
|
||||
QEMU_BUILD_BUG_ON(offsetof(VirtIOSCSIReq, req.cdb) !=
|
||||
offsetof(VirtIOSCSIReq, req.cmd) + sizeof(VirtIOSCSICmdReq));
|
||||
|
||||
#define DEFINE_VIRTIO_SCSI_PROPERTIES(_state, _conf_field) \
|
||||
DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \
|
||||
DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF),\
|
||||
@ -192,5 +264,19 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp,
|
||||
HandleOutput cmd);
|
||||
|
||||
void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp);
|
||||
void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req);
|
||||
bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req);
|
||||
void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req);
|
||||
VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq);
|
||||
void virtio_scsi_free_req(VirtIOSCSIReq *req);
|
||||
void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||
uint32_t event, uint32_t reason);
|
||||
|
||||
void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread);
|
||||
void virtio_scsi_dataplane_start(VirtIOSCSI *s);
|
||||
void virtio_scsi_dataplane_stop(VirtIOSCSI *s);
|
||||
void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req);
|
||||
VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s,
|
||||
VirtIOSCSIVring *vring);
|
||||
|
||||
#endif /* _QEMU_VIRTIO_SCSI_H */
|
||||
|
@ -161,6 +161,9 @@ typedef struct VirtioDeviceClass {
|
||||
int (*load)(VirtIODevice *vdev, QEMUFile *f, int version_id);
|
||||
} VirtioDeviceClass;
|
||||
|
||||
void virtio_instance_init_common(Object *proxy_obj, void *data,
|
||||
size_t vdev_size, const char *vdev_name);
|
||||
|
||||
void virtio_init(VirtIODevice *vdev, const char *name,
|
||||
uint16_t device_id, size_t config_size);
|
||||
void virtio_cleanup(VirtIODevice *vdev);
|
||||
|
@ -88,10 +88,19 @@ int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
|
||||
int slow_bitmap_intersects(const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, long bits);
|
||||
|
||||
static inline unsigned long *bitmap_new(long nbits)
|
||||
static inline unsigned long *bitmap_try_new(long nbits)
|
||||
{
|
||||
long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
|
||||
return g_malloc0(len);
|
||||
return g_try_malloc0(len);
|
||||
}
|
||||
|
||||
static inline unsigned long *bitmap_new(long nbits)
|
||||
{
|
||||
unsigned long *ptr = bitmap_try_new(nbits);
|
||||
if (ptr == NULL) {
|
||||
abort();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline void bitmap_zero(unsigned long *dst, long nbits)
|
||||
|
Loading…
x
Reference in New Issue
Block a user