thebeanogamer / rpms / qemu-kvm

Forked from rpms/qemu-kvm 5 months ago
Clone
ed5979
From b886411a682b56bfe674f0a35d40c67c8e9dc87a Mon Sep 17 00:00:00 2001
ed5979
From: Stefan Hajnoczi <stefanha@redhat.com>
ed5979
Date: Tue, 21 Feb 2023 16:22:17 -0500
ed5979
Subject: [PATCH 02/12] dma-helpers: prevent dma_blk_cb() vs dma_aio_cancel()
ed5979
 race
ed5979
ed5979
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
ed5979
RH-MergeRequest: 155: virtio-scsi: reset SCSI devices from main loop thread
ed5979
RH-Bugzilla: 2155748
ed5979
RH-Acked-by: Eric Blake <eblake@redhat.com>
ed5979
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
ed5979
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
ed5979
RH-Commit: [2/3] eeeea43c25d8f4fa84591b05547fb77e4058abff (stefanha/centos-stream-qemu-kvm)
ed5979
ed5979
dma_blk_cb() only takes the AioContext lock around ->io_func(). That
ed5979
means the rest of dma_blk_cb() is not protected. In particular, the
ed5979
DMAAIOCB field accesses happen outside the lock.
ed5979
ed5979
There is a race when the main loop thread holds the AioContext lock and
ed5979
invokes scsi_device_purge_requests() -> bdrv_aio_cancel() ->
ed5979
dma_aio_cancel() while an IOThread executes dma_blk_cb(). The dbs->acb
ed5979
field determines how cancellation proceeds. If dma_aio_cancel() sees
ed5979
dbs->acb == NULL while dma_blk_cb() is still running, the request can be
ed5979
completed twice (-ECANCELED and the actual return value).
ed5979
ed5979
The following assertion can occur with virtio-scsi when an IOThread is
ed5979
used:
ed5979
ed5979
  ../hw/scsi/scsi-disk.c:368: scsi_dma_complete: Assertion `r->req.aiocb != NULL' failed.
ed5979
ed5979
Fix the race by holding the AioContext across dma_blk_cb(). Now
ed5979
dma_aio_cancel() under the AioContext lock will not see
ed5979
inconsistent/intermediate states.
ed5979
ed5979
Cc: Paolo Bonzini <pbonzini@redhat.com>
ed5979
Reviewed-by: Eric Blake <eblake@redhat.com>
ed5979
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
ed5979
Message-Id: <20230221212218.1378734-3-stefanha@redhat.com>
ed5979
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
ed5979
(cherry picked from commit abfcd2760b3e70727bbc0792221b8b98a733dc32)
ed5979
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
ed5979
---
ed5979
 hw/scsi/scsi-disk.c   |  4 +---
ed5979
 softmmu/dma-helpers.c | 12 +++++++-----
ed5979
 2 files changed, 8 insertions(+), 8 deletions(-)
ed5979
ed5979
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
ed5979
index 5327f93f4c..b12d8b0816 100644
ed5979
--- a/hw/scsi/scsi-disk.c
ed5979
+++ b/hw/scsi/scsi-disk.c
ed5979
@@ -354,13 +354,12 @@ done:
ed5979
     scsi_req_unref(&r->req);
ed5979
 }
ed5979
 
ed5979
+/* Called with AioContext lock held */
ed5979
 static void scsi_dma_complete(void *opaque, int ret)
ed5979
 {
ed5979
     SCSIDiskReq *r = (SCSIDiskReq *)opaque;
ed5979
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
ed5979
 
ed5979
-    aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
ed5979
-
ed5979
     assert(r->req.aiocb != NULL);
ed5979
     r->req.aiocb = NULL;
ed5979
 
ed5979
@@ -370,7 +369,6 @@ static void scsi_dma_complete(void *opaque, int ret)
ed5979
         block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
ed5979
     }
ed5979
     scsi_dma_complete_noio(r, ret);
ed5979
-    aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
ed5979
 }
ed5979
 
ed5979
 static void scsi_read_complete_noio(SCSIDiskReq *r, int ret)
ed5979
diff --git a/softmmu/dma-helpers.c b/softmmu/dma-helpers.c
ed5979
index 7820fec54c..2463964805 100644
ed5979
--- a/softmmu/dma-helpers.c
ed5979
+++ b/softmmu/dma-helpers.c
ed5979
@@ -113,17 +113,19 @@ static void dma_complete(DMAAIOCB *dbs, int ret)
ed5979
 static void dma_blk_cb(void *opaque, int ret)
ed5979
 {
ed5979
     DMAAIOCB *dbs = (DMAAIOCB *)opaque;
ed5979
+    AioContext *ctx = dbs->ctx;
ed5979
     dma_addr_t cur_addr, cur_len;
ed5979
     void *mem;
ed5979
 
ed5979
     trace_dma_blk_cb(dbs, ret);
ed5979
 
ed5979
+    aio_context_acquire(ctx);
ed5979
     dbs->acb = NULL;
ed5979
     dbs->offset += dbs->iov.size;
ed5979
 
ed5979
     if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) {
ed5979
         dma_complete(dbs, ret);
ed5979
-        return;
ed5979
+        goto out;
ed5979
     }
ed5979
     dma_blk_unmap(dbs);
ed5979
 
ed5979
@@ -164,9 +166,9 @@ static void dma_blk_cb(void *opaque, int ret)
ed5979
 
ed5979
     if (dbs->iov.size == 0) {
ed5979
         trace_dma_map_wait(dbs);
ed5979
-        dbs->bh = aio_bh_new(dbs->ctx, reschedule_dma, dbs);
ed5979
+        dbs->bh = aio_bh_new(ctx, reschedule_dma, dbs);
ed5979
         cpu_register_map_client(dbs->bh);
ed5979
-        return;
ed5979
+        goto out;
ed5979
     }
ed5979
 
ed5979
     if (!QEMU_IS_ALIGNED(dbs->iov.size, dbs->align)) {
ed5979
@@ -174,11 +176,11 @@ static void dma_blk_cb(void *opaque, int ret)
ed5979
                                 QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align));
ed5979
     }
ed5979
 
ed5979
-    aio_context_acquire(dbs->ctx);
ed5979
     dbs->acb = dbs->io_func(dbs->offset, &dbs->iov,
ed5979
                             dma_blk_cb, dbs, dbs->io_func_opaque);
ed5979
-    aio_context_release(dbs->ctx);
ed5979
     assert(dbs->acb);
ed5979
+out:
ed5979
+    aio_context_release(ctx);
ed5979
 }
ed5979
 
ed5979
 static void dma_aio_cancel(BlockAIOCB *acb)
ed5979
-- 
ed5979
2.39.1
ed5979