This patch introduces bottom half event for replay queue. It saves the events into the queue and process them at the checkpoints and instructions execution.
Signed-off-by: Pavel Dovgalyuk <pavel.dovga...@ispras.ru> --- async.c | 46 ++++++++++++++++++++++++++++++++++++++++------ dma-helpers.c | 4 +++- hw/ide/ahci.c | 4 +++- hw/ide/core.c | 4 +++- hw/timer/arm_timer.c | 2 +- hw/usb/hcd-uhci.c | 2 +- include/block/aio.h | 18 ++++++++++++++++++ include/qemu/main-loop.h | 1 + main-loop.c | 5 +++++ replay/replay-events.c | 16 ++++++++++++++++ replay/replay-internal.h | 3 ++- replay/replay.h | 2 ++ stubs/replay.c | 4 ++++ 13 files changed, 99 insertions(+), 12 deletions(-) diff --git a/async.c b/async.c index 6e1b282..97111c0 100644 --- a/async.c +++ b/async.c @@ -27,6 +27,7 @@ #include "block/thread-pool.h" #include "qemu/main-loop.h" #include "qemu/atomic.h" +#include "replay/replay.h" /***********************************************************/ /* bottom halves (can be seen as timers which expire ASAP) */ @@ -39,24 +40,53 @@ struct QEMUBH { bool scheduled; bool idle; bool deleted; + bool replay; + uint64_t id; }; QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque) { - QEMUBH *bh; + QEMUBH *bh, **last; bh = g_malloc0(sizeof(QEMUBH)); bh->ctx = ctx; bh->cb = cb; bh->opaque = opaque; qemu_mutex_lock(&ctx->bh_lock); - bh->next = ctx->first_bh; - /* Make sure that the members are ready before putting bh into list */ - smp_wmb(); - ctx->first_bh = bh; + if (replay_mode != REPLAY_MODE_NONE) { + /* Slower way, but this is a queue and not a stack. + Replay will process the BH in the same order they + came into the queue. */ + last = &ctx->first_bh; + while (*last) { + last = &(*last)->next; + } + smp_wmb(); + *last = bh; + } else { + bh->next = ctx->first_bh; + /* Make sure that the members are ready before putting bh into list */ + smp_wmb(); + ctx->first_bh = bh; + } qemu_mutex_unlock(&ctx->bh_lock); return bh; } +QEMUBH *aio_bh_new_replay(AioContext *ctx, QEMUBHFunc *cb, void *opaque, + uint64_t id) +{ + QEMUBH *bh = aio_bh_new(ctx, cb, opaque); + bh->replay = true; + bh->id = id; + return bh; +} + +void aio_bh_call(void *opaque) +{ + QEMUBH *bh = (QEMUBH *)opaque; + bh->cb(bh->opaque); +} + /* Multiple occurrences of aio_bh_poll cannot be called concurrently */ int aio_bh_poll(AioContext *ctx) { @@ -79,7 +109,11 @@ int aio_bh_poll(AioContext *ctx) if (!bh->idle) ret = 1; bh->idle = 0; - bh->cb(bh->opaque); + if (!bh->replay) { + aio_bh_call(bh); + } else { + replay_add_bh_event(bh, bh->id); + } } } diff --git a/dma-helpers.c b/dma-helpers.c index 6918572..357d7e9 100644 --- a/dma-helpers.c +++ b/dma-helpers.c @@ -13,6 +13,7 @@ #include "qemu/range.h" #include "qemu/thread.h" #include "qemu/main-loop.h" +#include "replay/replay.h" /* #define DEBUG_IOMMU */ @@ -96,7 +97,8 @@ static void continue_after_map_failure(void *opaque) { DMAAIOCB *dbs = (DMAAIOCB *)opaque; - dbs->bh = qemu_bh_new(reschedule_dma, dbs); + dbs->bh = qemu_bh_new_replay(reschedule_dma, dbs, + replay_get_current_step()); qemu_bh_schedule(dbs->bh); } diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 70958e3..fbefc52 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -33,6 +33,7 @@ #include "internal.h" #include <hw/ide/pci.h> #include <hw/ide/ahci.h> +#include "replay/replay.h" /* #define DEBUG_AHCI */ @@ -1192,7 +1193,8 @@ static void ahci_cmd_done(IDEDMA *dma) if (!ad->check_bh) { /* maybe we still have something to process, check later */ - ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); + ad->check_bh = qemu_bh_new_replay(ahci_check_cmd_bh, ad, + replay_get_current_step()); qemu_bh_schedule(ad->check_bh); } } diff --git a/hw/ide/core.c b/hw/ide/core.c index 44e3d50..b622aae 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -32,6 +32,7 @@ #include "sysemu/dma.h" #include "hw/block/block.h" #include "sysemu/block-backend.h" +#include "replay/replay.h" #include <hw/ide/internal.h> @@ -448,7 +449,8 @@ BlockAIOCB *ide_issue_trim(BlockBackend *blk, iocb = blk_aio_get(&trim_aiocb_info, blk, cb, opaque); iocb->blk = blk; - iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb); + iocb->bh = qemu_bh_new_replay(ide_trim_bh_cb, iocb, + replay_get_current_step()); iocb->ret = 0; iocb->qiov = qiov; iocb->i = -1; diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index 1452910..97784a0 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -168,7 +168,7 @@ static arm_timer_state *arm_timer_init(uint32_t freq) s->freq = freq; s->control = TIMER_CTRL_IE; - bh = qemu_bh_new(arm_timer_tick, s); + bh = qemu_bh_new_replay(arm_timer_tick, s, 0); s->timer = ptimer_init(bh); vmstate_register(NULL, -1, &vmstate_arm_timer, s); return s; diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 5b88f30..dc17e5f 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1224,7 +1224,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev) USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); } } - s->bh = qemu_bh_new(uhci_bh, s); + s->bh = qemu_bh_new_replay(uhci_bh, s, 0); s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; QTAILQ_INIT(&s->queues); diff --git a/include/block/aio.h b/include/block/aio.h index 6bf0e04..5a77431 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -35,6 +35,8 @@ struct BlockAIOCB { const AIOCBInfo *aiocb_info; BlockDriverState *bs; BlockCompletionFunc *cb; + bool replay; + uint64_t replay_step; void *opaque; int refcnt; }; @@ -144,6 +146,17 @@ void aio_context_release(AioContext *ctx); QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque); /** + * aio_bh_new_replay: Allocate a new bottom half structure for replay. + * + * This function calls aio_bh_new function and also fills replay parameters + * of the BH structure. BH created with this function in record/replay mode + * are executed through the replay queue only at checkpoints and instructions + * executions. + */ +QEMUBH *aio_bh_new_replay(AioContext *ctx, QEMUBHFunc *cb, void *opaque, + uint64_t id); + +/** * aio_notify: Force processing of pending events. * * Similar to signaling a condition variable, aio_notify forces @@ -159,6 +172,11 @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque); void aio_notify(AioContext *ctx); /** + * aio_bh_call: Executes callback function of the specified BH. + */ +void aio_bh_call(void *opaque); + +/** * aio_bh_poll: Poll bottom halves for an AioContext. * * These are internal functions used by the QEMU main loop. diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 62c68c0..f5a98fe 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -306,6 +306,7 @@ void qemu_iohandler_fill(GArray *pollfds); void qemu_iohandler_poll(GArray *pollfds, int rc); QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque); +QEMUBH *qemu_bh_new_replay(QEMUBHFunc *cb, void *opaque, uint64_t id); void qemu_bh_schedule_idle(QEMUBH *bh); #endif diff --git a/main-loop.c b/main-loop.c index 981bcb5..342aeef 100644 --- a/main-loop.c +++ b/main-loop.c @@ -508,3 +508,8 @@ QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) { return aio_bh_new(qemu_aio_context, cb, opaque); } + +QEMUBH *qemu_bh_new_replay(QEMUBHFunc *cb, void *opaque, uint64_t id) +{ + return aio_bh_new_replay(qemu_aio_context, cb, opaque, id); +} diff --git a/replay/replay-events.c b/replay/replay-events.c index f3c9b16..1aee0a4 100755 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -36,6 +36,9 @@ static bool replay_events_enabled = false; static void replay_run_event(Event *event) { switch (event->event_kind) { + case REPLAY_ASYNC_EVENT_BH: + aio_bh_call(event->opaque); + break; default: fprintf(stderr, "Replay: invalid async event ID (%d) in the queue\n", event->event_kind); @@ -119,6 +122,11 @@ void replay_add_event(int event_kind, void *opaque) replay_add_event_internal(event_kind, opaque, NULL, 0); } +void replay_add_bh_event(void *bh, uint64_t id) +{ + replay_add_event_internal(REPLAY_ASYNC_EVENT_BH, bh, NULL, id); +} + void replay_save_events(int opt) { qemu_mutex_lock(&lock); @@ -132,6 +140,9 @@ void replay_save_events(int opt) /* save event-specific data */ switch (event->event_kind) { + case REPLAY_ASYNC_EVENT_BH: + replay_put_qword(event->id); + break; } } @@ -158,6 +169,11 @@ void replay_read_events(int opt) } /* Execute some events without searching them in the queue */ switch (read_event_kind) { + case REPLAY_ASYNC_EVENT_BH: + if (read_id == -1) { + read_id = replay_get_qword(); + } + break; default: fprintf(stderr, "Unknown ID %d of replay event\n", read_event_kind); exit(1); diff --git a/replay/replay-internal.h b/replay/replay-internal.h index e73fa7a..ec36f44 100755 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -37,7 +37,8 @@ /* Asynchronous events IDs */ -#define REPLAY_ASYNC_COUNT 0 +#define REPLAY_ASYNC_EVENT_BH 0 +#define REPLAY_ASYNC_COUNT 1 typedef struct ReplayState { /*! Cached clock values. */ diff --git a/replay/replay.h b/replay/replay.h index e76c541..ad8bdd3 100755 --- a/replay/replay.h +++ b/replay/replay.h @@ -91,5 +91,7 @@ int replay_checkpoint(unsigned int checkpoint); /*! Disables storing events in the queue */ void replay_disable_events(void); +/*! Adds BH event to the queue */ +void replay_add_bh_event(void *bh, uint64_t id); #endif diff --git a/stubs/replay.c b/stubs/replay.c index ab9ede9..6d6c2eb 100755 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -26,3 +26,7 @@ int runstate_is_running(void) { return 0; } + +void replay_add_bh_event(void *bh, uint64_t id) +{ +}