Actual fix for live migration with iothread

This commit is contained in:
Vitaliy Filippov
2026-06-23 00:07:34 +03:00
parent 278852b4d5
commit 09607ddcbe
+25
View File
@@ -880,6 +880,25 @@ static int vitastor_refresh_limits(BlockDriverState *bs)
// return 0; // return 0;
//} //}
// Move the running coroutine to the BlockDriverState's home AioContext.
//
// The block-coroutine-wrapper generator sets poll_state.ctx to
// qemu_get_current_aio_context() in the sync wrappers (bdrv_flush(),
// bdrv_pread() etc.). When bdrv_flush_all() runs under BQL from outside the
// bs's iothread (e.g. on the migration thread inside do_vm_stop()), that is
// the main AioContext, not the iothread that actually owns the bs. The
// coroutine then runs on the wrong context while completions are delivered on
// the iothread, and racing aio_co_schedule() vs. qemu_aio_coroutine_enter()
// on the same coroutine triggers "Co-routine was already scheduled in
// aio_co_schedule" and aborts the process (observed during live migration).
//
// aio_co_reschedule_self() is a no-op when we are already on the target ctx.
#if QEMU_VERSION_MAJOR > 5 || QEMU_VERSION_MAJOR == 5 && QEMU_VERSION_MINOR >= 2
#define vitastor_co_pin_to_bs_ctx(bs) aio_co_reschedule_self(bdrv_get_aio_context(bs))
#else
#define vitastor_co_pin_to_bs_ctx(bs) ((void)0)
#endif
static void vitastor_co_init_task(BlockDriverState *bs, VitastorRPC *task) static void vitastor_co_init_task(BlockDriverState *bs, VitastorRPC *task)
{ {
*task = (VitastorRPC) { *task = (VitastorRPC) {
@@ -938,6 +957,7 @@ static int coroutine_fn vitastor_co_preadv(BlockDriverState *bs,
{ {
VitastorClient *client = bs->opaque; VitastorClient *client = bs->opaque;
VitastorRPC task; VitastorRPC task;
vitastor_co_pin_to_bs_ctx(bs);
vitastor_co_init_task(bs, &task); vitastor_co_init_task(bs, &task);
task.iov = iov; task.iov = iov;
@@ -966,6 +986,7 @@ static int coroutine_fn vitastor_co_pwritev(BlockDriverState *bs,
{ {
VitastorClient *client = bs->opaque; VitastorClient *client = bs->opaque;
VitastorRPC task; VitastorRPC task;
vitastor_co_pin_to_bs_ctx(bs);
vitastor_co_init_task(bs, &task); vitastor_co_init_task(bs, &task);
task.iov = iov; task.iov = iov;
@@ -1039,6 +1060,7 @@ static int coroutine_fn vitastor_co_block_status(BlockDriverState *bs,
#endif #endif
VitastorRPC task; VitastorRPC task;
VitastorClient *client = bs->opaque; VitastorClient *client = bs->opaque;
vitastor_co_pin_to_bs_ctx(bs);
uint64_t inode = client->watch ? vitastor_c_inode_get_num(client->watch) : client->inode; uint64_t inode = client->watch ? vitastor_c_inode_get_num(client->watch) : client->inode;
uint8_t bit = 0; uint8_t bit = 0;
if (client->last_bitmap && client->last_bitmap_inode == inode && if (client->last_bitmap && client->last_bitmap_inode == inode &&
@@ -1151,6 +1173,7 @@ static int coroutine_fn vitastor_co_flush(BlockDriverState *bs)
{ {
VitastorClient *client = bs->opaque; VitastorClient *client = bs->opaque;
VitastorRPC task; VitastorRPC task;
vitastor_co_pin_to_bs_ctx(bs);
vitastor_co_init_task(bs, &task); vitastor_co_init_task(bs, &task);
qemu_mutex_lock(&client->mutex); qemu_mutex_lock(&client->mutex);
@@ -1236,6 +1259,8 @@ static BlockDriver bdrv_vitastor = {
#endif #endif
.bdrv_close = vitastor_close, .bdrv_close = vitastor_close,
// Re-register fd handlers when the bs is moved to a different AioContext
// (live migration, drain, iothread reassignment).
.bdrv_detach_aio_context = vitastor_detach_aio_context, .bdrv_detach_aio_context = vitastor_detach_aio_context,
.bdrv_attach_aio_context = vitastor_attach_aio_context, .bdrv_attach_aio_context = vitastor_attach_aio_context,