Commit 213189ab65d83ecd9072f27c80a15dcb91b6bdbf

Authored by Markus Armbruster
Committed by Anthony Liguori
1 parent 3e28c9ad

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>
hw/ide.c
... ... @@ -501,6 +501,7 @@ typedef struct BMDMAState {
501 501 QEMUIOVector qiov;
502 502 int64_t sector_num;
503 503 uint32_t nsector;
  504 + QEMUBH *bh;
504 505 } BMDMAState;
505 506  
506 507 typedef struct PCIIDEState {
... ... @@ -1109,11 +1110,13 @@ static void ide_sector_write(IDEState *s)
1109 1110 }
1110 1111 }
1111 1112  
1112   -static void ide_dma_restart_cb(void *opaque, int running, int reason)
  1113 +static void ide_dma_restart_bh(void *opaque)
1113 1114 {
1114 1115 BMDMAState *bm = opaque;
1115   - if (!running)
1116   - return;
  1116 +
  1117 + qemu_bh_delete(bm->bh);
  1118 + bm->bh = NULL;
  1119 +
1117 1120 if (bm->status & BM_STATUS_DMA_RETRY) {
1118 1121 bm->status &= ~BM_STATUS_DMA_RETRY;
1119 1122 ide_dma_restart(bm->ide_if);
... ... @@ -1123,6 +1126,19 @@ static void ide_dma_restart_cb(void *opaque, int running, int reason)
1123 1126 }
1124 1127 }
1125 1128  
  1129 +static void ide_dma_restart_cb(void *opaque, int running, int reason)
  1130 +{
  1131 + BMDMAState *bm = opaque;
  1132 +
  1133 + if (!running)
  1134 + return;
  1135 +
  1136 + if (!bm->bh) {
  1137 + bm->bh = qemu_bh_new(ide_dma_restart_bh, bm);
  1138 + qemu_bh_schedule(bm->bh);
  1139 + }
  1140 +}
  1141 +
1126 1142 static void ide_write_dma_cb(void *opaque, int ret)
1127 1143 {
1128 1144 BMDMAState *bm = opaque;
... ...
hw/scsi-disk.c
... ... @@ -74,6 +74,7 @@ struct SCSIDeviceState
74 74 scsi_completionfn completion;
75 75 void *opaque;
76 76 char drive_serial_str[21];
  77 + QEMUBH *bh;
77 78 };
78 79  
79 80 /* Global pool of SCSIRequest structures. */
... ... @@ -308,12 +309,13 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag)
308 309 return 0;
309 310 }
310 311  
311   -static void scsi_dma_restart_cb(void *opaque, int running, int reason)
  312 +static void scsi_dma_restart_bh(void *opaque)
312 313 {
313 314 SCSIDeviceState *s = opaque;
314 315 SCSIRequest *r = s->requests;
315   - if (!running)
316   - return;
  316 +
  317 + qemu_bh_delete(s->bh);
  318 + s->bh = NULL;
317 319  
318 320 while (r) {
319 321 if (r->status & SCSI_REQ_STATUS_RETRY) {
... ... @@ -324,6 +326,19 @@ static void scsi_dma_restart_cb(void *opaque, int running, int reason)
324 326 }
325 327 }
326 328  
  329 +static void scsi_dma_restart_cb(void *opaque, int running, int reason)
  330 +{
  331 + SCSIDeviceState *s = opaque;
  332 +
  333 + if (!running)
  334 + return;
  335 +
  336 + if (!s->bh) {
  337 + s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
  338 + qemu_bh_schedule(s->bh);
  339 + }
  340 +}
  341 +
327 342 /* Return a pointer to the data buffer. */
328 343 static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
329 344 {
... ...
hw/virtio-blk.c
... ... @@ -26,6 +26,7 @@ typedef struct VirtIOBlock
26 26 VirtQueue *vq;
27 27 void *rq;
28 28 char serial_str[BLOCK_SERIAL_STRLEN + 1];
  29 + QEMUBH *bh;
29 30 } VirtIOBlock;
30 31  
31 32 static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev)
... ... @@ -302,13 +303,13 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
302 303 */
303 304 }
304 305  
305   -static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason)
  306 +static void virtio_blk_dma_restart_bh(void *opaque)
306 307 {
307 308 VirtIOBlock *s = opaque;
308 309 VirtIOBlockReq *req = s->rq;
309 310  
310   - if (!running)
311   - return;
  311 + qemu_bh_delete(s->bh);
  312 + s->bh = NULL;
312 313  
313 314 s->rq = NULL;
314 315  
... ... @@ -318,6 +319,19 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason)
318 319 }
319 320 }
320 321  
  322 +static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason)
  323 +{
  324 + VirtIOBlock *s = opaque;
  325 +
  326 + if (!running)
  327 + return;
  328 +
  329 + if (!s->bh) {
  330 + s->bh = qemu_bh_new(virtio_blk_dma_restart_bh, s);
  331 + qemu_bh_schedule(s->bh);
  332 + }
  333 +}
  334 +
321 335 static void virtio_blk_reset(VirtIODevice *vdev)
322 336 {
323 337 /*
... ...