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);
*/
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;
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) {
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:
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)) {
sizeof(VirtIOSCSICtrlTMFResp)) < 0) {
virtio_scsi_bad_req();
} else {
- virtio_scsi_do_tmf(s, req);
+ r = virtio_scsi_do_tmf(s, req);
}
} else if (req->req.tmf.type == VIRTIO_SCSI_T_AN_QUERY ||
req->resp.an.response = VIRTIO_SCSI_S_OK;
}
}
- virtio_scsi_complete_req(req);
+ if (r == 0) {
+ virtio_scsi_complete_req(req);
+ } else {
+ assert(r == -EINPROGRESS);
+ }
}
static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
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);
+ SCSIRequest *sreq = req->sreq;
+ if (scsi_req_enqueue(sreq)) {
+ scsi_req_continue(sreq);
}
+ bdrv_io_unplug(sreq->dev->conf.bs);
+ scsi_req_unref(sreq);
}
static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
{
/* use non-QOM casts in the data path */
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
- VirtIOSCSIReq *req;
+ VirtIOSCSIReq *req, *next;
+ QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
if (s->ctx && !s->dataplane_disabled) {
virtio_scsi_dataplane_start(s);
}
while ((req = virtio_scsi_pop_req(s, vq))) {
if (virtio_scsi_handle_cmd_req_prepare(s, req)) {
- virtio_scsi_handle_cmd_req_submit(s, req);
+ QTAILQ_INSERT_TAIL(&reqs, req, next);
}
}
+
+ QTAILQ_FOREACH_SAFE(req, &reqs, next, next) {
+ virtio_scsi_handle_cmd_req_submit(s, req);
+ }
}
static void virtio_scsi_get_config(VirtIODevice *vdev,
}
}
-static void virtio_scsi_hotplug(SCSIBus *bus, SCSIDevice *dev)
+static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
{
- VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
+ VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev);
if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) {
- virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET,
+ virtio_scsi_push_event(VIRTIO_SCSI(hotplug_dev), SCSI_DEVICE(dev),
+ VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_RESCAN);
}
}
-static void virtio_scsi_hot_unplug(SCSIBus *bus, SCSIDevice *dev)
+static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
{
- VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
+ VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev);
if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) {
- virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET,
+ virtio_scsi_push_event(VIRTIO_SCSI(hotplug_dev), SCSI_DEVICE(dev),
+ VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_REMOVED);
}
+ qdev_simple_device_unplug_cb(hotplug_dev, dev, errp);
}
static struct SCSIBusInfo virtio_scsi_scsi_info = {
.complete = virtio_scsi_command_complete,
.cancel = virtio_scsi_request_cancelled,
.change = virtio_scsi_change,
- .hotplug = virtio_scsi_hotplug,
- .hot_unplug = virtio_scsi_hot_unplug,
.parse_cdb = virtio_scsi_parse_cdb,
.get_sg_list = virtio_scsi_get_sg_list,
.save_request = virtio_scsi_save_request,
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
scsi_bus_new(&s->bus, sizeof(s->bus), dev,
&virtio_scsi_scsi_info, vdev->bus_name);
+ /* override default SCSI bus hotplug-handler, with virtio-scsi's one */
+ qbus_set_hotplug_handler(BUS(&s->bus), dev, &error_abort);
if (!dev->hotplugged) {
scsi_bus_legacy_handle_cmdline(&s->bus, &err);
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
dc->props = virtio_scsi_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
vdc->set_config = virtio_scsi_set_config;
vdc->get_features = virtio_scsi_get_features;
vdc->reset = virtio_scsi_reset;
+ hc->plug = virtio_scsi_hotplug;
+ hc->unplug = virtio_scsi_hotunplug;
}
static const TypeInfo virtio_scsi_common_info = {
.instance_size = sizeof(VirtIOSCSI),
.instance_init = virtio_scsi_instance_init,
.class_init = virtio_scsi_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
static void virtio_register_types(void)