On 2025/06/06 1:26, Alex Bennée wrote:
From: Manos Pitsidianakis <manos.pitsidiana...@linaro.org>

Change the 3 part async cleanup of a blob memory mapping to check if the
unmapping has finished already after deleting the subregion; this
condition allows us to skip suspending the command and responding to the
guest right away.

This patch has no effect because "[PATCH v3 13/20] virtio-gpu: refactor async blob unmapping" was dropped:
https://lore.kernel.org/qemu-devel/20250521164250.135776-13-alex.ben...@linaro.org/

This is because dropping the patch prevented the unmapping operation from finishing after deleting the subregion. I'll show this by showing that the patch broke the reference counting in details first, and then by showing that the correct reference counting prevents the unmapping operation from finishing.

If !vmr->finish_unmapping when virtio_gpu_virgl_unmap_resource_blob() is called, there are the following references:

- owner -> subregion
- container -> owner
- FlatView -> owner

Note that reference 2 and 3 were made by memory_region_ref(), which makes references to the owner.

For simplicity, let's assume there are only these references for the subregion.

And the following functions are called. Note that the order represents the time order and the indention represents the call stack.

The main thread:
 bql_lock()
 virtio_gpu_virgl_unmap_resource_blob(g, res, cmd_suspended)
  memory_region_del_subregion(&b->hostmem, mr)
   memory_region_transaction_commit()
    address_space_set_flatview(as)
     flatview_unref(old_view)
      call_rcu1(&old_view->rcu, flatview_destroy)
       qemu_event_set(&rcu_call_ready_event)
   memory_region_unref(mr)
    object_unref(mr->owner) (a)
  object_unparent(OBJECT(mr))
   object_property_del_child(OBJECT(&b->hostmem), OBJECT(mr))
    object_finalize_child_property(OBJECT(&b->hostmem), name, mr)
     object_unref(mr) (b)

The RCU thread:
 call_rcu_thread(NULL)
  qemu_event_wait(&rcu_call_ready_event)
  bql_lock()
  flatview_destroy(old_view)
    memory_region_unref(mr)
     object_unref(mr->owner) (c)

(c) always happens after (b) due to the BQL. The correspondence between the object_unref() call and the removed references are as follows:

(a) container -> owner
(b) owner -> subregion
(c) FlatView -> owner

With "[PATCH v3 13/20] virtio-gpu: refactor async blob unmapping", the owner is g (VirtIOGPU). So the correspondence will be interpreted as follows:

(a) container -> g
(b) g -> subregion
(c) FlatView -> g

Therefore, the subregion will be freed with (b), incorrectly ignoring (c). (b) is made during object_unparent(OBJECT(mr)), so it made the unmapping operation finish early.

Without "[PATCH v3 13/20] virtio-gpu: refactor async blob unmapping", the owner was the memory region itself. So the correspondence will be interpreted as follows:

(a) container -> subregion
(b) subregion -> subregion
(c) FlatView -> subregion

Therefore, the subregion will be freed with (c). So the unmapping operation will never finish until the BQL gets unlocked.

Regards,
Akihiko Odaki

Signed-off-by: Manos Pitsidianakis <manos.pitsidiana...@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <phi...@linaro.org>
Message-Id: <20250410122643.1747913-4-manos.pitsidiana...@linaro.org>
Cc: qemu-sta...@nongnu.org
Signed-off-by: Alex Bennée <alex.ben...@linaro.org>
Message-ID: <20250603110204.838117-11-alex.ben...@linaro.org>

diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
index 71a7500de9..b4aa8abb96 100644
--- a/hw/display/virtio-gpu-virgl.c
+++ b/hw/display/virtio-gpu-virgl.c
@@ -155,7 +155,32 @@ virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g,
       *    asynchronously by virtio_gpu_virgl_hostmem_region_free().
       * 3. Finish the unmapping with final virgl_renderer_resource_unmap().
       */
+
+    /* 1. Check if we should start unmapping now */
+    if (!vmr->finish_unmapping) {
+        /* begin async unmapping. render will be unblocked once MR is freed */
+        b->renderer_blocked++;
+
+        memory_region_set_enabled(mr, false);
+        memory_region_del_subregion(&b->hostmem, mr);
+        object_unparent(OBJECT(mr));
+        /*
+         * The unmapping might have already finished at this point if no one
+         * else held a reference to the MR; if yes, we can skip suspending the
+         * command and unmap the resource right away.
+         */
+        *cmd_suspended = !vmr->finish_unmapping;
+    }
+
+    /*
+     * 2. if virtio_gpu_virgl_hostmem_region_free hasn't been executed yet, we
+     * have marked the command to be re-processed later by setting
+     * cmd_suspended to true. The freeing callback will be called from RCU
+     * context later.
+     */
+
      if (vmr->finish_unmapping) {
+        /* 3. MemoryRegion has been freed, so finish unmapping */
          res->mr = NULL;
          g_free(vmr);
@@ -166,16 +191,6 @@ virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g,
                            __func__, strerror(-ret));
              return ret;
          }
-    } else {
-        *cmd_suspended = true;
-
-        /* render will be unblocked once MR is freed */
-        b->renderer_blocked++;
-
-        /* memory region owns self res->mr object and frees it by itself */
-        memory_region_set_enabled(mr, false);
-        memory_region_del_subregion(&b->hostmem, mr);
-        object_unparent(OBJECT(mr));
      }
return 0;


Reply via email to