replay: add BH oneshot event for block layer
Replay is capable of recording normal BH events, but sometimes there are single use callbacks scheduled with aio_bh_schedule_oneshot function. This patch enables recording and replaying such callbacks. Block layer uses these events for calling the completion function. Replaying these calls makes the execution deterministic. Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> Acked-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
ae25dccb7d
commit
e4ec5ad464
@ -18,6 +18,8 @@
|
|||||||
#include "hw/qdev-core.h"
|
#include "hw/qdev-core.h"
|
||||||
#include "sysemu/blockdev.h"
|
#include "sysemu/blockdev.h"
|
||||||
#include "sysemu/runstate.h"
|
#include "sysemu/runstate.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qapi-events-block.h"
|
#include "qapi/qapi-events-block.h"
|
||||||
#include "qemu/id.h"
|
#include "qemu/id.h"
|
||||||
@ -1306,7 +1308,8 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk,
|
|||||||
acb->blk = blk;
|
acb->blk = blk;
|
||||||
acb->ret = ret;
|
acb->ret = ret;
|
||||||
|
|
||||||
aio_bh_schedule_oneshot(blk_get_aio_context(blk), error_callback_bh, acb);
|
replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
|
||||||
|
error_callback_bh, acb);
|
||||||
return &acb->common;
|
return &acb->common;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1362,7 +1365,7 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes,
|
|||||||
|
|
||||||
acb->has_returned = true;
|
acb->has_returned = true;
|
||||||
if (acb->rwco.ret != NOT_DONE) {
|
if (acb->rwco.ret != NOT_DONE) {
|
||||||
aio_bh_schedule_oneshot(blk_get_aio_context(blk),
|
replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
|
||||||
blk_aio_complete_bh, acb);
|
blk_aio_complete_bh, acb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +369,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
|||||||
if (bs) {
|
if (bs) {
|
||||||
bdrv_inc_in_flight(bs);
|
bdrv_inc_in_flight(bs);
|
||||||
}
|
}
|
||||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
|
replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs),
|
||||||
bdrv_co_drain_bh_cb, &data);
|
bdrv_co_drain_bh_cb, &data);
|
||||||
|
|
||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/uuid.h"
|
#include "qemu/uuid.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qapi-commands-misc.h"
|
#include "qapi/qapi-commands-misc.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
@ -280,7 +281,7 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (iTask->co) {
|
if (iTask->co) {
|
||||||
aio_bh_schedule_oneshot(iTask->iscsilun->aio_context,
|
replay_bh_schedule_oneshot_event(iTask->iscsilun->aio_context,
|
||||||
iscsi_co_generic_bh_cb, iTask);
|
iscsi_co_generic_bh_cb, iTask);
|
||||||
} else {
|
} else {
|
||||||
iTask->complete = 1;
|
iTask->complete = 1;
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/uri.h"
|
#include "qemu/uri.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
#include "qapi/qapi-visit-block-core.h"
|
#include "qapi/qapi-visit-block-core.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
@ -257,7 +259,7 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
|
|||||||
if (task->ret < 0) {
|
if (task->ret < 0) {
|
||||||
error_report("NFS Error: %s", nfs_get_error(nfs));
|
error_report("NFS Error: %s", nfs_get_error(nfs));
|
||||||
}
|
}
|
||||||
aio_bh_schedule_oneshot(task->client->aio_context,
|
replay_bh_schedule_oneshot_event(task->client->aio_context,
|
||||||
nfs_co_generic_bh_cb, task);
|
nfs_co_generic_bh_cb, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
|
|
||||||
#define NULL_OPT_LATENCY "latency-ns"
|
#define NULL_OPT_LATENCY "latency-ns"
|
||||||
#define NULL_OPT_ZEROES "read-zeroes"
|
#define NULL_OPT_ZEROES "read-zeroes"
|
||||||
@ -179,7 +180,8 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
|
|||||||
timer_mod_ns(&acb->timer,
|
timer_mod_ns(&acb->timer,
|
||||||
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns);
|
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns);
|
||||||
} else {
|
} else {
|
||||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), null_bh_cb, acb);
|
replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs),
|
||||||
|
null_bh_cb, acb);
|
||||||
}
|
}
|
||||||
return &acb->common;
|
return &acb->common;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/vfio-helpers.h"
|
#include "qemu/vfio-helpers.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#include "block/nvme.h"
|
#include "block/nvme.h"
|
||||||
@ -351,7 +352,8 @@ static bool nvme_process_completion(BDRVNVMeState *s, NVMeQueuePair *q)
|
|||||||
smp_mb_release();
|
smp_mb_release();
|
||||||
*q->cq.doorbell = cpu_to_le32(q->cq.head);
|
*q->cq.doorbell = cpu_to_le32(q->cq.head);
|
||||||
if (!qemu_co_queue_empty(&q->free_req_queue)) {
|
if (!qemu_co_queue_empty(&q->free_req_queue)) {
|
||||||
aio_bh_schedule_oneshot(s->aio_context, nvme_free_req_queue_cb, q);
|
replay_bh_schedule_oneshot_event(s->aio_context,
|
||||||
|
nvme_free_req_queue_cb, q);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
q->busy = false;
|
q->busy = false;
|
||||||
@ -935,7 +937,7 @@ static void nvme_rw_cb(void *opaque, int ret)
|
|||||||
/* The rw coroutine hasn't yielded, don't try to enter. */
|
/* The rw coroutine hasn't yielded, don't try to enter. */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
aio_bh_schedule_oneshot(data->ctx, nvme_rw_cb_bh, data);
|
replay_bh_schedule_oneshot_event(data->ctx, nvme_rw_cb_bh, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn int nvme_co_prw_aligned(BlockDriverState *bs,
|
static coroutine_fn int nvme_co_prw_aligned(BlockDriverState *bs,
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "block/qdict.h"
|
#include "block/qdict.h"
|
||||||
#include "crypto/secret.h"
|
#include "crypto/secret.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qjson.h"
|
#include "qapi/qmp/qjson.h"
|
||||||
@ -884,7 +885,7 @@ static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb)
|
|||||||
rcb->ret = rbd_aio_get_return_value(c);
|
rcb->ret = rbd_aio_get_return_value(c);
|
||||||
rbd_aio_release(c);
|
rbd_aio_release(c);
|
||||||
|
|
||||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
|
replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs),
|
||||||
rbd_finish_bh, rcb);
|
rbd_finish_bh, rcb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/uuid.h"
|
#include "qemu/uuid.h"
|
||||||
#include "crypto/tlscredsx509.h"
|
#include "crypto/tlscredsx509.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
|
|
||||||
#define VXHS_OPT_FILENAME "filename"
|
#define VXHS_OPT_FILENAME "filename"
|
||||||
#define VXHS_OPT_VDISK_ID "vdisk-id"
|
#define VXHS_OPT_VDISK_ID "vdisk-id"
|
||||||
@ -105,7 +106,7 @@ static void vxhs_iio_callback(void *ctx, uint32_t opcode, uint32_t error)
|
|||||||
trace_vxhs_iio_callback(error);
|
trace_vxhs_iio_callback(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
|
replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs),
|
||||||
vxhs_complete_aio_bh, acb);
|
vxhs_complete_aio_bh, acb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "qapi/qapi-types-misc.h"
|
#include "qapi/qapi-types-misc.h"
|
||||||
#include "qapi/qapi-types-run-state.h"
|
#include "qapi/qapi-types-run-state.h"
|
||||||
#include "qapi/qapi-types-ui.h"
|
#include "qapi/qapi-types-ui.h"
|
||||||
|
#include "block/aio.h"
|
||||||
|
|
||||||
/* replay clock kinds */
|
/* replay clock kinds */
|
||||||
enum ReplayClockKind {
|
enum ReplayClockKind {
|
||||||
@ -140,6 +141,9 @@ void replay_enable_events(void);
|
|||||||
bool replay_events_enabled(void);
|
bool replay_events_enabled(void);
|
||||||
/*! Adds bottom half event to the queue */
|
/*! Adds bottom half event to the queue */
|
||||||
void replay_bh_schedule_event(QEMUBH *bh);
|
void replay_bh_schedule_event(QEMUBH *bh);
|
||||||
|
/* Adds oneshot bottom half event to the queue */
|
||||||
|
void replay_bh_schedule_oneshot_event(AioContext *ctx,
|
||||||
|
QEMUBHFunc *cb, void *opaque);
|
||||||
/*! Adds input event to the queue */
|
/*! Adds input event to the queue */
|
||||||
void replay_input_event(QemuConsole *src, InputEvent *evt);
|
void replay_input_event(QemuConsole *src, InputEvent *evt);
|
||||||
/*! Adds input sync event to the queue */
|
/*! Adds input sync event to the queue */
|
||||||
|
@ -36,6 +36,9 @@ static void replay_run_event(Event *event)
|
|||||||
case REPLAY_ASYNC_EVENT_BH:
|
case REPLAY_ASYNC_EVENT_BH:
|
||||||
aio_bh_call(event->opaque);
|
aio_bh_call(event->opaque);
|
||||||
break;
|
break;
|
||||||
|
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
|
||||||
|
((QEMUBHFunc *)event->opaque)(event->opaque2);
|
||||||
|
break;
|
||||||
case REPLAY_ASYNC_EVENT_INPUT:
|
case REPLAY_ASYNC_EVENT_INPUT:
|
||||||
qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque);
|
qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque);
|
||||||
qapi_free_InputEvent((InputEvent *)event->opaque);
|
qapi_free_InputEvent((InputEvent *)event->opaque);
|
||||||
@ -131,6 +134,17 @@ void replay_bh_schedule_event(QEMUBH *bh)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void replay_bh_schedule_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void replay_add_input_event(struct InputEvent *event)
|
void replay_add_input_event(struct InputEvent *event)
|
||||||
{
|
{
|
||||||
replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0);
|
replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0);
|
||||||
@ -161,6 +175,7 @@ static void replay_save_event(Event *event, int checkpoint)
|
|||||||
/* save event-specific data */
|
/* save event-specific data */
|
||||||
switch (event->event_kind) {
|
switch (event->event_kind) {
|
||||||
case REPLAY_ASYNC_EVENT_BH:
|
case REPLAY_ASYNC_EVENT_BH:
|
||||||
|
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
|
||||||
replay_put_qword(event->id);
|
replay_put_qword(event->id);
|
||||||
break;
|
break;
|
||||||
case REPLAY_ASYNC_EVENT_INPUT:
|
case REPLAY_ASYNC_EVENT_INPUT:
|
||||||
@ -216,6 +231,7 @@ static Event *replay_read_event(int checkpoint)
|
|||||||
/* Events that has not to be in the queue */
|
/* Events that has not to be in the queue */
|
||||||
switch (replay_state.read_event_kind) {
|
switch (replay_state.read_event_kind) {
|
||||||
case REPLAY_ASYNC_EVENT_BH:
|
case REPLAY_ASYNC_EVENT_BH:
|
||||||
|
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
|
||||||
if (replay_state.read_event_id == -1) {
|
if (replay_state.read_event_id == -1) {
|
||||||
replay_state.read_event_id = replay_get_qword();
|
replay_state.read_event_id = replay_get_qword();
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ enum ReplayEvents {
|
|||||||
|
|
||||||
enum ReplayAsyncEventKind {
|
enum ReplayAsyncEventKind {
|
||||||
REPLAY_ASYNC_EVENT_BH,
|
REPLAY_ASYNC_EVENT_BH,
|
||||||
|
REPLAY_ASYNC_EVENT_BH_ONESHOT,
|
||||||
REPLAY_ASYNC_EVENT_INPUT,
|
REPLAY_ASYNC_EVENT_INPUT,
|
||||||
REPLAY_ASYNC_EVENT_INPUT_SYNC,
|
REPLAY_ASYNC_EVENT_INPUT_SYNC,
|
||||||
REPLAY_ASYNC_EVENT_CHAR_READ,
|
REPLAY_ASYNC_EVENT_CHAR_READ,
|
||||||
|
@ -20,6 +20,7 @@ stub-obj-y += monitor.o
|
|||||||
stub-obj-y += notify-event.o
|
stub-obj-y += notify-event.o
|
||||||
stub-obj-y += qtest.o
|
stub-obj-y += qtest.o
|
||||||
stub-obj-y += replay.o
|
stub-obj-y += replay.o
|
||||||
|
stub-obj-y += replay-user.o
|
||||||
stub-obj-y += runstate-check.o
|
stub-obj-y += runstate-check.o
|
||||||
stub-obj-y += set-fd-handler.o
|
stub-obj-y += set-fd-handler.o
|
||||||
stub-obj-y += sysbus.o
|
stub-obj-y += sysbus.o
|
||||||
|
9
stubs/replay-user.c
Normal file
9
stubs/replay-user.c
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
|
||||||
|
void replay_bh_schedule_oneshot_event(AioContext *ctx,
|
||||||
|
QEMUBHFunc *cb, void *opaque)
|
||||||
|
{
|
||||||
|
aio_bh_schedule_oneshot(ctx, cb, opaque);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user