From 3b7cd9fd8fc2d34da6a42f49421e3549918adf58 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 16 Nov 2016 17:31:14 +0100 Subject: qed: Use bottom half to resume waiting requests The qed driver serialises allocating write requests. When the active allocation is finished, the AIO callback is called, but after this, the next allocating request is immediately processed instead of leaving the coroutine. Resuming another allocation request in the same request coroutine means that the request now runs in the wrong coroutine. The following is one of the possible effects of this: The completed request will generally reenter its request coroutine in a bottom half, expecting that it completes the request in bdrv_driver_pwritev(). However, if the second request actually yielded before leaving the coroutine, the reused request coroutine is in an entirely different place and is reentered prematurely. Not a good idea. Let's make sure that we exit the coroutine after completing the first request by resuming the next allocating request only with a bottom half. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Stefan Hajnoczi --- block/qed.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'block/qed.c') diff --git a/block/qed.c b/block/qed.c index 8d899fd479..a837a28655 100644 --- a/block/qed.c +++ b/block/qed.c @@ -967,6 +967,11 @@ static void qed_aio_complete_bh(void *opaque) qed_release(s); } +static void qed_resume_alloc_bh(void *opaque) +{ + qed_aio_start_io(opaque); +} + static void qed_aio_complete(QEDAIOCB *acb, int ret) { BDRVQEDState *s = acb_to_s(acb); @@ -995,10 +1000,12 @@ static void qed_aio_complete(QEDAIOCB *acb, int ret) * requests multiple times but rather finish one at a time completely. */ if (acb == QSIMPLEQ_FIRST(&s->allocating_write_reqs)) { + QEDAIOCB *next_acb; QSIMPLEQ_REMOVE_HEAD(&s->allocating_write_reqs, next); - acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); - if (acb) { - qed_aio_start_io(acb); + next_acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); + if (next_acb) { + aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs), + qed_resume_alloc_bh, next_acb); } else if (s->header.features & QED_F_NEED_CHECK) { qed_start_need_check_timer(s); } -- cgit v1.2.1