When we still have an AIOCB registered for DMA operations, we try to settle the respective operation by draining the BlockBackend associated with the IDE device.
However, this assumes that every DMA operation is associated with some I/O operation on the BlockBackend, and so settling the latter will settle the former. That is not the case; for example, the guest is free to issue a zero-length TRIM operation that will not result in any I/O operation forwarded to the BlockBackend. In such a case, blk_drain() will be a no-op if no other operations are in flight. It is clear that if blk_drain() is a no-op, the value of s->bus->dma->aiocb will not change between checking it in the `if` condition and asserting that it is NULL after blk_drain(). To settle the DMA operation, we will thus need to explicitly invoke aio_poll() ourselves, which will run any outstanding BHs (like ide_trim_bh_cb()), until s->bus->dma->aiocb is NULL. To stop this from being an infinite loop, assert that we made progress with every aio_poll() call (i.e., invoked some BH). Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2029980 Signed-off-by: Hanna Reitz <hre...@redhat.com> Signed-off-by: Lukas Straub <lukasstra...@web.de> --- hw/ide/core.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index d172e70f1e..a5fd89ebdd 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -736,7 +736,17 @@ void ide_cancel_dma_sync(IDEState *s) if (s->bus->dma->aiocb) { trace_ide_cancel_dma_sync_remaining(); blk_drain(s->blk); - assert(s->bus->dma->aiocb == NULL); + + /* + * Wait for potentially still-scheduled BHs, like ide_trim_bh_cb() + * (blk_drain() will only poll if there are in-flight requests on the + * BlockBackend, which there may not necessarily be, e.g. when the + * guest has issued a zero-length TRIM request) + */ + while (s->bus->dma->aiocb) { + bool progress = aio_poll(qemu_get_aio_context(), true); + assert(progress); + } } } -- 2.39.2
pgpimE5CmA6Oq.pgp
Description: OpenPGP digital signature