Code using old bh APIs must be updated to account for whether the bh is related to the target machine model or host QEMU operation, in order for record/replay to work properly.
Add some assertions in the old APIs to catch unconverted code when record/replay is active. This caught the IDE bug when running replay_linux.py avocado test with the x86-64 q35 non-virtio machine. Signed-off-by: Nicholas Piggin <npig...@gmail.com> --- include/block/aio.h | 2 +- replay/replay-events.c | 20 ++++++++------------ util/async.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/include/block/aio.h b/include/block/aio.h index 26859bd0b93..991aaae707d 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -309,7 +309,7 @@ void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, */ #define aio_bh_schedule_oneshot(ctx, cb, opaque) \ aio_bh_schedule_oneshot_full((ctx), (cb), (opaque), (stringify(cb)), \ - QEMU_CLOCK_REALTIME) + QEMU_CLOCK_MAX) /** * aio_bh_new_full: Allocate a new bottom half structure. diff --git a/replay/replay-events.c b/replay/replay-events.c index 6a7c27cac1e..0b3dbfd46b9 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -123,23 +123,19 @@ void replay_add_event(ReplayAsyncEventKind event_kind, void replay_bh_schedule_event(QEMUBH *bh) { - if (events_enabled) { - uint64_t id = replay_get_current_icount(); - replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id); - } else { - qemu_bh_schedule(bh); - } + uint64_t id; + g_assert(events_enabled); + id = replay_get_current_icount(); + replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id); } void replay_bh_oneshot_event(AioContext *ctx, QEMUBHFunc *cb, void *opaque) { - if (events_enabled) { - uint64_t id = replay_get_current_icount(); - replay_add_event(REPLAY_ASYNC_EVENT_BH_ONESHOT, cb, opaque, id); - } else { - aio_bh_schedule_oneshot(ctx, cb, opaque); - } + uint64_t id; + g_assert(events_enabled); + id = replay_get_current_icount(); + replay_add_event(REPLAY_ASYNC_EVENT_BH_ONESHOT, cb, opaque, id); } void replay_add_input_event(struct InputEvent *event) diff --git a/util/async.c b/util/async.c index 5d2c76dec08..72a9eccffbe 100644 --- a/util/async.c +++ b/util/async.c @@ -58,6 +58,9 @@ enum { /* Schedule periodically when the event loop is idle */ BH_IDLE = (1 << 4), + + /* BH being handled by replay machinery */ + BH_REPLAY = (1 << 4), }; struct QEMUBH { @@ -145,6 +148,17 @@ void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, const char *name, QEMUClockType clock_type) { + if (clock_type == QEMU_CLOCK_MAX) { + /* + * aio_bh_schedule_oneshot() uses QEMU_CLOCK_MAX to say it does not + * know about clock context to use. It will not work in record/replay. + * Callers should be converted to aio_bh_schedule_oneshot_event() + * then this can be removed when the old API goes away. + */ + g_assert(replay_mode == REPLAY_MODE_NONE); + clock_type = QEMU_CLOCK_REALTIME; + } + switch (clock_type) { case QEMU_CLOCK_VIRTUAL: case QEMU_CLOCK_VIRTUAL_RT: @@ -178,6 +192,12 @@ void aio_bh_call(QEMUBH *bh) { bool last_engaged_in_io = false; + if (bh->flags & BH_REPLAY) { + g_assert(!(bh->flags & BH_SCHEDULED)); + g_assert(!(bh->flags & BH_DELETED)); + g_assert(!(bh->flags & BH_PENDING)); + bh->flags &= ~BH_REPLAY; + } /* Make a copy of the guard-pointer as cb may free the bh */ MemReentrancyGuard *reentrancy_guard = bh->reentrancy_guard; if (reentrancy_guard) { @@ -252,6 +272,7 @@ void qemu_bh_schedule_event(QEMUBH *bh, QEMUClockType clock_type) case QEMU_CLOCK_VIRTUAL_RT: if (replay_mode != REPLAY_MODE_NONE) { /* Record/replay must intercept bh events */ + qatomic_fetch_or(&bh->flags, BH_REPLAY); replay_bh_schedule_event(bh); break; } @@ -268,11 +289,15 @@ void qemu_bh_schedule_event_noreplay(QEMUBH *bh) void qemu_bh_schedule_idle(QEMUBH *bh) { + /* No mechanism for scheduling idle replay-scheduled bh at the moment */ + g_assert(replay_mode == REPLAY_MODE_NONE); aio_bh_enqueue(bh, BH_SCHEDULED | BH_IDLE); } void qemu_bh_schedule(QEMUBH *bh) { + /* Callers should be converted to use qemu_bh_schedule_event */ + g_assert(replay_mode == REPLAY_MODE_NONE); aio_bh_enqueue(bh, BH_SCHEDULED); } @@ -280,6 +305,8 @@ void qemu_bh_schedule(QEMUBH *bh) */ void qemu_bh_cancel(QEMUBH *bh) { + /* No mechanism for canceling replay-scheduled bh at the moment */ + g_assert(!(bh->flags & BH_REPLAY)); qatomic_and(&bh->flags, ~BH_SCHEDULED); } @@ -288,6 +315,8 @@ void qemu_bh_cancel(QEMUBH *bh) */ void qemu_bh_delete(QEMUBH *bh) { + /* No mechanism for deleting replay-scheduled bh at the moment */ + g_assert(!(bh->flags & BH_REPLAY)); aio_bh_enqueue(bh, BH_DELETED); } -- 2.45.2