Commit 213189ab65d83ecd9072f27c80a15dcb91b6bdbf
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>
Showing
3 changed files
with
54 additions
and
9 deletions
hw/ide.c
| @@ -501,6 +501,7 @@ typedef struct BMDMAState { | @@ -501,6 +501,7 @@ typedef struct BMDMAState { | ||
| 501 | QEMUIOVector qiov; | 501 | QEMUIOVector qiov; |
| 502 | int64_t sector_num; | 502 | int64_t sector_num; |
| 503 | uint32_t nsector; | 503 | uint32_t nsector; |
| 504 | + QEMUBH *bh; | ||
| 504 | } BMDMAState; | 505 | } BMDMAState; |
| 505 | 506 | ||
| 506 | typedef struct PCIIDEState { | 507 | typedef struct PCIIDEState { |
| @@ -1109,11 +1110,13 @@ static void ide_sector_write(IDEState *s) | @@ -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 | BMDMAState *bm = opaque; | 1115 | BMDMAState *bm = opaque; |
| 1115 | - if (!running) | ||
| 1116 | - return; | 1116 | + |
| 1117 | + qemu_bh_delete(bm->bh); | ||
| 1118 | + bm->bh = NULL; | ||
| 1119 | + | ||
| 1117 | if (bm->status & BM_STATUS_DMA_RETRY) { | 1120 | if (bm->status & BM_STATUS_DMA_RETRY) { |
| 1118 | bm->status &= ~BM_STATUS_DMA_RETRY; | 1121 | bm->status &= ~BM_STATUS_DMA_RETRY; |
| 1119 | ide_dma_restart(bm->ide_if); | 1122 | ide_dma_restart(bm->ide_if); |
| @@ -1123,6 +1126,19 @@ static void ide_dma_restart_cb(void *opaque, int running, int reason) | @@ -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 | static void ide_write_dma_cb(void *opaque, int ret) | 1142 | static void ide_write_dma_cb(void *opaque, int ret) |
| 1127 | { | 1143 | { |
| 1128 | BMDMAState *bm = opaque; | 1144 | BMDMAState *bm = opaque; |
hw/scsi-disk.c
| @@ -74,6 +74,7 @@ struct SCSIDeviceState | @@ -74,6 +74,7 @@ struct SCSIDeviceState | ||
| 74 | scsi_completionfn completion; | 74 | scsi_completionfn completion; |
| 75 | void *opaque; | 75 | void *opaque; |
| 76 | char drive_serial_str[21]; | 76 | char drive_serial_str[21]; |
| 77 | + QEMUBH *bh; | ||
| 77 | }; | 78 | }; |
| 78 | 79 | ||
| 79 | /* Global pool of SCSIRequest structures. */ | 80 | /* Global pool of SCSIRequest structures. */ |
| @@ -308,12 +309,13 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag) | @@ -308,12 +309,13 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag) | ||
| 308 | return 0; | 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 | SCSIDeviceState *s = opaque; | 314 | SCSIDeviceState *s = opaque; |
| 314 | SCSIRequest *r = s->requests; | 315 | SCSIRequest *r = s->requests; |
| 315 | - if (!running) | ||
| 316 | - return; | 316 | + |
| 317 | + qemu_bh_delete(s->bh); | ||
| 318 | + s->bh = NULL; | ||
| 317 | 319 | ||
| 318 | while (r) { | 320 | while (r) { |
| 319 | if (r->status & SCSI_REQ_STATUS_RETRY) { | 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,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 | /* Return a pointer to the data buffer. */ | 342 | /* Return a pointer to the data buffer. */ |
| 328 | static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) | 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,6 +26,7 @@ typedef struct VirtIOBlock | ||
| 26 | VirtQueue *vq; | 26 | VirtQueue *vq; |
| 27 | void *rq; | 27 | void *rq; |
| 28 | char serial_str[BLOCK_SERIAL_STRLEN + 1]; | 28 | char serial_str[BLOCK_SERIAL_STRLEN + 1]; |
| 29 | + QEMUBH *bh; | ||
| 29 | } VirtIOBlock; | 30 | } VirtIOBlock; |
| 30 | 31 | ||
| 31 | static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev) | 32 | static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev) |
| @@ -302,13 +303,13 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) | @@ -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 | VirtIOBlock *s = opaque; | 308 | VirtIOBlock *s = opaque; |
| 308 | VirtIOBlockReq *req = s->rq; | 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 | s->rq = NULL; | 314 | s->rq = NULL; |
| 314 | 315 | ||
| @@ -318,6 +319,19 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason) | @@ -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 | static void virtio_blk_reset(VirtIODevice *vdev) | 335 | static void virtio_blk_reset(VirtIODevice *vdev) |
| 322 | { | 336 | { |
| 323 | /* | 337 | /* |