aboutsummaryrefslogtreecommitdiff
path: root/hw/virtio-blk.c
diff options
context:
space:
mode:
authorMarkus Armbruster <armbru@redhat.com>2009-07-28 14:33:41 -0400
committerAnthony Liguori <aliguori@us.ibm.com>2009-07-30 09:50:37 -0500
commit213189ab65d83ecd9072f27c80a15dcb91b6bdbf (patch)
tree13a4609577ddab8d5a9aa7cae41172a77a3fae4d /hw/virtio-blk.c
parent3e28c9adf4bfe2f4792557ee45684181bd6e6d1c (diff)
Fix VM state change handlers running out of order
When a VM state change handler changes VM state, other VM state change handlers can see the state transitions out of order. bmdma_map(), scsi_disk_init() and virtio_blk_init() install VM state change handlers to restart DMA. These handlers can vm_stop() by running into a write error on a drive with werror=stop. This throws the VM state change handler callback into disarray. Here's an example case I observed: 0. The virtual IDE drive goes south. All future writes return errors. 1. Something encounters a write error, and duly stops the VM with vm_stop(). 2. vm_stop() calls vm_state_notify(0). 3. vm_state_notify() runs the callbacks in list vm_change_state_head. It contains ide_dma_restart_cb() installed by bmdma_map(). It also contains audio_vm_change_state_handler() installed by audio_init(). 4. audio_vm_change_state_handler() stops audio stuff. 5. User continues VM with monitor command "c". This runs vm_start(). 6. vm_start() calls vm_state_notify(1). 7. vm_state_notify() runs the callbacks in vm_change_state_head. 8. ide_dma_restart_cb() happens to come first. It does its work, runs into a write error, and duly stops the VM with vm_stop(). 9. vm_stop() runs vm_state_notify(0). 10. vm_state_notify() runs the callbacks in vm_change_state_head. 11. audio_vm_change_state_handler() stops audio stuff. Which isn't running. 12. vm_stop() finishes, ide_dma_restart_cb() finishes, step 7's vm_state_notify() resumes running handlers. 13. audio_vm_change_state_handler() starts audio stuff. Oopsie. Fix this by moving the actual write from each VM state change handler into a new bottom half (suggested by Gleb Natapov). Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Diffstat (limited to 'hw/virtio-blk.c')
-rw-r--r--hw/virtio-blk.c20
1 files changed, 17 insertions, 3 deletions
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index 2beff5224a..5036b5b024 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -26,6 +26,7 @@ typedef struct VirtIOBlock
VirtQueue *vq;
void *rq;
char serial_str[BLOCK_SERIAL_STRLEN + 1];
+ QEMUBH *bh;
} VirtIOBlock;
static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev)
@@ -302,13 +303,13 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
*/
}
-static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason)
+static void virtio_blk_dma_restart_bh(void *opaque)
{
VirtIOBlock *s = opaque;
VirtIOBlockReq *req = s->rq;
- if (!running)
- return;
+ qemu_bh_delete(s->bh);
+ s->bh = NULL;
s->rq = NULL;
@@ -318,6 +319,19 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason)
}
}
+static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason)
+{
+ VirtIOBlock *s = opaque;
+
+ if (!running)
+ return;
+
+ if (!s->bh) {
+ s->bh = qemu_bh_new(virtio_blk_dma_restart_bh, s);
+ qemu_bh_schedule(s->bh);
+ }
+}
+
static void virtio_blk_reset(VirtIODevice *vdev)
{
/*