From 361dcc790db8c87b2e46ab610739191ced894c44 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Wed, 15 Oct 2014 15:15:25 +0200 Subject: [PATCH] virtio-scsi: dataplane: fail setup gracefully The dataplane code is currently doing a hard exit on various setup failures. In practice, this may mean that a guest suddenly dies after a dataplane device failed to come up (e.g., when a file descriptor limit is hit for the nth device). Let's just try to unwind the setup instead and return. Signed-off-by: Cornelia Huck Signed-off-by: Paolo Bonzini --- hw/scsi/virtio-scsi-dataplane.c | 79 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 999c783fa6..243a4765e9 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -53,7 +53,7 @@ static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, if (rc != 0) { fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", rc); - exit(1); + return NULL; } r->host_notifier = *virtio_queue_get_host_notifier(vq); r->guest_notifier = *virtio_queue_get_guest_notifier(vq); @@ -63,9 +63,15 @@ static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, if (!vring_setup(&r->vring, VIRTIO_DEVICE(s), n)) { fprintf(stderr, "virtio-scsi: VRing setup failed\n"); - exit(1); + goto fail_vring; } return r; + +fail_vring: + aio_set_event_notifier(s->ctx, &r->host_notifier, NULL); + k->set_host_notifier(qbus->parent, n, false); + g_slice_free(VirtIOSCSIVring, r); + return NULL; } VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s, @@ -141,6 +147,46 @@ static void virtio_scsi_iothread_handle_cmd(EventNotifier *notifier) } } +/* assumes s->ctx held */ +static void virtio_scsi_clear_aio(VirtIOSCSI *s) +{ + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + int i; + + if (s->ctrl_vring) { + aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL); + } + if (s->event_vring) { + aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL); + } + if (s->cmd_vrings) { + for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { + aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL); + } + } +} + +static void virtio_scsi_vring_teardown(VirtIOSCSI *s) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(s); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + int i; + + if (s->ctrl_vring) { + vring_teardown(&s->ctrl_vring->vring, vdev, 0); + } + if (s->event_vring) { + vring_teardown(&s->event_vring->vring, vdev, 1); + } + if (s->cmd_vrings) { + for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { + vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i); + } + free(s->cmd_vrings); + s->cmd_vrings = NULL; + } +} + /* Context: QEMU global mutex held */ void virtio_scsi_dataplane_start(VirtIOSCSI *s) { @@ -165,27 +211,47 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s) if (rc != 0) { fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " "ensure -enable-kvm is set\n", rc); - exit(1); + goto fail_guest_notifiers; } aio_context_acquire(s->ctx); s->ctrl_vring = virtio_scsi_vring_init(s, vs->ctrl_vq, virtio_scsi_iothread_handle_ctrl, 0); + if (!s->ctrl_vring) { + goto fail_vrings; + } s->event_vring = virtio_scsi_vring_init(s, vs->event_vq, virtio_scsi_iothread_handle_event, 1); + if (!s->event_vring) { + goto fail_vrings; + } 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); + if (!s->cmd_vrings[i]) { + goto fail_vrings; + } } aio_context_release(s->ctx); s->dataplane_starting = false; s->dataplane_started = true; + +fail_vrings: + virtio_scsi_clear_aio(s); + aio_context_release(s->ctx); + virtio_scsi_vring_teardown(s); + for (i = 0; i < vs->conf.num_queues + 2; i++) { + k->set_host_notifier(qbus->parent, i, false); + } + k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); +fail_guest_notifiers: + s->dataplane_starting = false; } /* Context: QEMU global mutex held */ @@ -193,7 +259,6 @@ 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; @@ -220,11 +285,7 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s) /* 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); - } + virtio_scsi_vring_teardown(s); for (i = 0; i < vs->conf.num_queues + 2; i++) { k->set_host_notifier(qbus->parent, i, false); -- 2.11.4.GIT