aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/iscsi.c19
-rw-r--r--hw/s390x/s390-virtio-bus.c38
-rw-r--r--hw/s390x/virtio-ccw.c42
-rw-r--r--hw/scsi/Makefile.objs2
-rw-r--r--hw/scsi/scsi-bus.c51
-rw-r--r--hw/scsi/scsi-disk.c59
-rw-r--r--hw/scsi/scsi-generic.c37
-rw-r--r--hw/scsi/spapr_vscsi.c13
-rw-r--r--hw/scsi/vhost-scsi.c5
-rw-r--r--hw/scsi/virtio-scsi-dataplane.c229
-rw-r--r--hw/scsi/virtio-scsi.c364
-rw-r--r--hw/virtio/virtio-pci.c48
-rw-r--r--hw/virtio/virtio.c11
-rw-r--r--include/hw/scsi/scsi.h27
-rw-r--r--include/hw/virtio/virtio-scsi.h88
-rw-r--r--include/hw/virtio/virtio.h3
-rw-r--r--include/qemu/bitmap.h13
17 files changed, 755 insertions, 294 deletions
diff --git a/block/iscsi.c b/block/iscsi.c
index 5c72ffeb31..3a01de0edb 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -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;
diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c
index 6b6fb61c47..f451ca1ed3 100644
--- a/hw/s390x/s390-virtio-bus.c
+++ b/hw/s390x/s390-virtio-bus.c
@@ -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(),
};
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 33a1d863b1..e7d3ea178a 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -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(),
diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs
index 121ddc5cf4..40c79d34c9 100644
--- a/hw/scsi/Makefile.objs
+++ b/hw/scsi/Makefile.objs
@@ -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
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 954c6072bf..0f3e0395f5 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -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,40 +1716,56 @@ 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);
}
-void scsi_req_cancel(SCSIRequest *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 (!req->enqueued) {
+ 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->ops->cancel_io) {
- req->ops->cancel_io(req);
- }
- if (req->bus->info->cancel) {
- req->bus->info->cancel(req);
+ if (req->aiocb) {
+ bdrv_aio_cancel_async(req->aiocb);
}
- scsi_req_unref(req);
}
-void scsi_req_abort(SCSIRequest *req, int status)
+void scsi_req_cancel(SCSIRequest *req)
{
+ trace_scsi_req_cancel(req->dev->id, req->lun, req->tag);
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);
+ if (req->aiocb) {
+ bdrv_aio_cancel(req->aiocb);
}
- scsi_req_complete(req, status);
- scsi_req_unref(req);
}
static int scsi_ua_precedence(SCSISense sense)
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 9645d0194a..7a7938a5bf 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -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,
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 20587b41c1..01bca084e6 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -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,
diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index 048cfc7b05..20b20f0bae 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -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);
}
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
index 7146e0ec49..308b393f96 100644
--- a/hw/scsi/vhost-scsi.c
+++ b/hw/scsi/vhost-scsi.c
@@ -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);
}
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
new file mode 100644
index 0000000000..b778e051f8
--- /dev/null
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -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;
+}
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 86aba8851d..203e62449a 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -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, &notifier->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, &notifier->notifier);
}
}
}
+ if (--req->remaining > 0) {
+ ret = -EINPROGRESS;
+ }
break;
case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
@@ -323,50 +354,66 @@ 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;
}
-static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
- VirtIOSCSI *s = (VirtIOSCSI *)vdev;
- VirtIOSCSIReq *req;
+ VirtIODevice *vdev = (VirtIODevice *)s;
+ int type;
+ int r = 0;
- 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();
+ return;
+ }
- if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0,
- &type, sizeof(type)) < sizeof(type)) {
+ 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();
- continue;
+ } else {
+ r = virtio_scsi_do_tmf(s, req);
}
- 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;
- }
+ } 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)
+{
+ 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))) {
+ 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;
- }
-
- 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;
+ if (virtio_scsi_handle_cmd_req_prepare(s, req)) {
+ QTAILQ_INSERT_TAIL(&reqs, req, next);
}
+ }
- 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,
};
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index a827cd41bf..390f8244f3 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -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,
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 5c981801f3..2c236bf271 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -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)
{
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index 2e3a8f987d..b61bedb28c 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -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);
diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h
index 188a2d9144..d6e5e7935c 100644
--- a/include/hw/virtio/virtio-scsi.h
+++ b/include/hw/virtio/virtio-scsi.h
@@ -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 */
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index a60104ca24..0726d76f49 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.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);
diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h
index 1babd5d812..edf4f17d9c 100644
--- a/include/qemu/bitmap.h
+++ b/include/qemu/bitmap.h
@@ -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)