public inbox for qemu-devel@nongnu.org
 help / color / mirror / Atom feed
* [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders
@ 2026-03-05 14:24 Zhang Chen
  2026-03-05 14:24 ` [PATCH V5 01/13] qapi/misc: Fix missed query-iothreads items Zhang Chen
                   ` (12 more replies)
  0 siblings, 13 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Motivation
Currently, IOThread references are opaque. This series introduces
"holder" tracking via QOM paths to improve observability,
especially for persistent thread pools.

In hotplug scenarios, users often pre-allocate multiple
IOThreads to serve as a pool. When devices are hot-unplugged,
IOThreads are released but remain available for subsequent reuse.
This series allows users to monitor exactly which devices are
currently attached to which IOThread in the pool.

Changes
Core API: Replaced raw object_ref/unref with iothread_get/put_aio_context(..., holder).

Subsystems: Migrated Virtio (blk, vq-mapping, balloon), Block (export, xen),
            Net (COLO), and VFIO to the new API.

Observability: Added holders (a array of QOM paths) to query-iothreads
               and info iothreads.

Thanks
Chen

Zhang Chen (13):
  qapi/misc: Fix missed query-iothreads items
  iothread: introduce iothread_ref/unref to track attached devices
  iothread: tracking iothread users with holder name
  blockdev: Update tracking iothread users with holder name
  virtio-vq-mapping: track iothread-vq-mapping references using device
    path
  virtio: use iothread_get/put_aio_context for thread pinning
  net/colo: track IOThread references using path-based holder
  block/export: Update tracking iothread users with holder name
  monitor: Update tracking iothread users with holder name
  virtio-balloon: Update tracking iothread users with holder name
  vfio-user/proxy: Update tracking iothread users with holder name
  xen-block: Update tracking iothread users with holder name
  qapi: examine IOThread attachment status via query-iothreads

 block/export/export.c                   |  7 ++-
 blockdev.c                              |  9 +++-
 hw/block/dataplane/xen-block.c          | 11 ++--
 hw/block/virtio-blk.c                   | 15 +++---
 hw/scsi/virtio-scsi-dataplane.c         | 17 +++---
 hw/vfio-user/proxy.c                    |  9 +++-
 hw/virtio/iothread-vq-mapping.c         | 11 ++--
 hw/virtio/virtio-balloon.c              | 17 ++++--
 include/hw/virtio/iothread-vq-mapping.h |  6 ++-
 include/system/iothread.h               |  4 +-
 iothread.c                              | 70 ++++++++++++++++++++++++-
 monitor/hmp-cmds.c                      | 13 +++++
 monitor/monitor.c                       |  4 +-
 monitor/qmp.c                           |  3 +-
 net/colo-compare.c                      | 23 +++++---
 qapi/misc.json                          | 25 ++++++++-
 16 files changed, 198 insertions(+), 46 deletions(-)

-- 
2.49.0



^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH V5 01/13] qapi/misc: Fix missed query-iothreads items
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-05 14:24 ` [PATCH V5 02/13] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen, qemu-stable

The example is incomplete: it misses members @poll-max-ns, @poll-grow,
@poll-shrink, @aio-max-batch.  Messed up in commit 5fc00480ab1
(monitor: add poll-* properties into query-iothreads result) and
commit 1793ad0247c (iothread: add aio-max-batch parameter).

cc: qemu-stable@nongnu.org

Reviewed-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 qapi/misc.json | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/qapi/misc.json b/qapi/misc.json
index 28c641fe2f..1f5062df2a 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -117,11 +117,19 @@
 #     <- { "return": [
 #              {
 #                 "id":"iothread0",
-#                 "thread-id":3134
+#                 "thread-id":3134,
+#                 "poll-max-ns":32768,
+#                 "poll-grow":0,
+#                 "poll-shrink":0,
+#                 "aio-max-batch":0
 #              },
 #              {
 #                 "id":"iothread1",
-#                 "thread-id":3135
+#                 "thread-id":3135,
+#                 "poll-max-ns":32768,
+#                 "poll-grow":0,
+#                 "poll-shrink":0,
+#                 "aio-max-batch":0
 #              }
 #           ]
 #        }
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 02/13] iothread: introduce iothread_ref/unref to track attached devices
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
  2026-03-05 14:24 ` [PATCH V5 01/13] qapi/misc: Fix missed query-iothreads items Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-09  7:49   ` Stefan Hajnoczi
  2026-03-05 14:24 ` [PATCH V5 03/13] iothread: tracking iothread users with holder name Zhang Chen
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Currently, IOThreads do not maintain a record of which devices are
associated with them. This makes it difficult to monitor the
workload distribution of IOThreads, especially in complex
hotplug scenarios involving multiple virtio-blk or virtio-scsi devices.

This patch introduces a reference counting and tracking mechanism
within the IOThread object:

- iothread_ref(): Prepends the device's QOM path to a list.
  Note: The IOThread takes ownership of the passed 'holder' string.
- iothread_unref(): Searches for the device path using a custom
  string comparison (g_strcmp0), releases the associated memory
  upon a successful match.
- holders: A GList storing the QOM paths of attached devices
  for runtime introspection.

This infrastructure allows management tools and QMP commands to
query the attachment status of IOThreads.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 include/system/iothread.h |  1 +
 iothread.c                | 28 ++++++++++++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/include/system/iothread.h b/include/system/iothread.h
index e26d13c6c7..21a76bd70d 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -33,6 +33,7 @@ struct IOThread {
     bool stopping;              /* has iothread_stop() been called? */
     bool running;               /* should iothread_run() continue? */
     int thread_id;
+    GList *holders;             /* an array of QOM paths for attached devices */
 
     /* AioContext poll parameters */
     int64_t poll_max_ns;
diff --git a/iothread.c b/iothread.c
index caf68e0764..80a8cf4b32 100644
--- a/iothread.c
+++ b/iothread.c
@@ -36,6 +36,34 @@
 #define IOTHREAD_POLL_MAX_NS_DEFAULT 0ULL
 #endif
 
+/*
+ * Add holder device path to the list.
+ */
+static void iothread_ref(IOThread *iothread, const char *holder)
+{
+    iothread->holders = g_list_prepend(iothread->holders, g_strdup(holder));
+}
+
+/*
+ * Delete holder device path from the list.
+ */
+static void iothread_unref(IOThread *iothread, const char *holder)
+{
+    if (iothread->holders) {
+        GList *link = g_list_find_custom(iothread->holders, holder,
+                                         (GCompareFunc)g_strcmp0);
+
+        if (link) {
+            g_free(link->data);
+            iothread->holders = g_list_delete_link(iothread->holders, link);
+        } else {
+            error_report("iothread_unref can't find the holder %s", holder);
+        }
+    } else {
+        error_report("iohtread_unref iothread is not held by any devices");
+    }
+}
+
 static void *iothread_run(void *opaque)
 {
     IOThread *iothread = opaque;
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 03/13] iothread: tracking iothread users with holder name
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
  2026-03-05 14:24 ` [PATCH V5 01/13] qapi/misc: Fix missed query-iothreads items Zhang Chen
  2026-03-05 14:24 ` [PATCH V5 02/13] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-09  8:02   ` Stefan Hajnoczi
  2026-03-09  8:33   ` Stefan Hajnoczi
  2026-03-05 14:24 ` [PATCH V5 04/13] blockdev: Update " Zhang Chen
                   ` (9 subsequent siblings)
  12 siblings, 2 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Introduce iothread_get_aio_context() with a 'holder' argument and its
counterpart iothread_put_aio_context().

Previously, users of an IOThread's AioContext did not explicitly
record their identity, making it difficult to debug which devices or
subsystems were pinning an IOThread.

This patch enhances the reference counting mechanism by:
1. Automatically incrementing the object reference count when a context
   is retrieved.
2. Tracking holders by name using iothread_ref() and iothread_unref().

In iothread_instance_finalize(), we now retrieve the source name from
the GMainContext to correctly unref the initial internal holder.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 include/system/iothread.h |  3 ++-
 iothread.c                | 28 ++++++++++++++++++++++++++--
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/include/system/iothread.h b/include/system/iothread.h
index 21a76bd70d..595abeefbe 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -47,7 +47,8 @@ DECLARE_INSTANCE_CHECKER(IOThread, IOTHREAD,
 
 char *iothread_get_id(IOThread *iothread);
 IOThread *iothread_by_id(const char *id);
-AioContext *iothread_get_aio_context(IOThread *iothread);
+AioContext *iothread_get_aio_context(IOThread *iothread, const char *holder);
+void iothread_put_aio_context(IOThread *iothread, const char *holder);
 GMainContext *iothread_get_g_main_context(IOThread *iothread);
 
 /*
diff --git a/iothread.c b/iothread.c
index 80a8cf4b32..da98fbb9ad 100644
--- a/iothread.c
+++ b/iothread.c
@@ -172,7 +172,8 @@ static void iothread_init_gcontext(IOThread *iothread, const char *thread_name)
     g_autofree char *name = g_strdup_printf("%s aio-context", thread_name);
 
     iothread->worker_context = g_main_context_new();
-    source = aio_get_g_source(iothread_get_aio_context(iothread));
+    /* No need setup itself as the init holder */
+    source = aio_get_g_source(iothread_get_aio_context(iothread, NULL));
     g_source_set_name(source, name);
     g_source_attach(source, iothread->worker_context);
     g_source_unref(source);
@@ -362,11 +363,34 @@ char *iothread_get_id(IOThread *iothread)
     return g_strdup(object_get_canonical_path_component(OBJECT(iothread)));
 }
 
-AioContext *iothread_get_aio_context(IOThread *iothread)
+AioContext *iothread_get_aio_context(IOThread *iothread, const char *holder)
 {
+    /*
+     * In some cases, iothread user need the ctx to clearup other resource.
+     * When holder is empty, back to the legacy way.
+     */
+    if (holder) {
+        /*
+         * This guarantees that the IOThread and its AioContext remain alive
+         * as long as there is a holder.
+         */
+        object_ref(OBJECT(iothread));
+
+        /* Add holder device path to the list */
+        iothread_ref(iothread, holder);
+    }
+
     return iothread->ctx;
 }
 
+void iothread_put_aio_context(IOThread *iothread, const char *holder)
+{
+    object_unref(OBJECT(iothread));
+
+    /* Delete holder device path from the list */
+    iothread_unref(iothread, holder);
+}
+
 static int query_one_iothread(Object *object, void *opaque)
 {
     IOThreadInfoList ***tail = opaque;
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 04/13] blockdev: Update tracking iothread users with holder name
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (2 preceding siblings ...)
  2026-03-05 14:24 ` [PATCH V5 03/13] iothread: tracking iothread users with holder name Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-09  8:15   ` Stefan Hajnoczi
  2026-03-05 14:24 ` [PATCH V5 05/13] virtio-vq-mapping: track iothread-vq-mapping references using device path Zhang Chen
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Update the usage of "iothread_get_aio_context()".

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 blockdev.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/blockdev.c b/blockdev.c
index 6e86c6262f..01ccf64b3f 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3683,7 +3683,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
             goto out;
         }
 
-        new_context = iothread_get_aio_context(obj);
+        char *path = object_get_canonical_path(OBJECT(bs));
+        /*
+         * For this qmp case, hard to find the point put aio context ???
+         * It looks like a pairing function is needed:
+         * qmp_x_blockdev_del_iothread()
+         */
+        new_context = iothread_get_aio_context(obj, path);
+        g_free(path);
     } else {
         new_context = qemu_get_aio_context();
     }
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 05/13] virtio-vq-mapping: track iothread-vq-mapping references using device path
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (3 preceding siblings ...)
  2026-03-05 14:24 ` [PATCH V5 04/13] blockdev: Update " Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-09  8:21   ` Stefan Hajnoczi
  2026-03-05 14:24 ` [PATCH V5 06/13] virtio: use iothread_get/put_aio_context for thread pinning Zhang Chen
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Replace raw object_ref/unref calls with iothread_get/put_aio_context
in iothread-vq-mapping. This allows tracking IOThread users via
the device's canonical QOM path, improving lifecycle traceability
for virtio-blk and virtio-scsi devices.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 hw/block/virtio-blk.c                   | 10 +++++++++-
 hw/scsi/virtio-scsi-dataplane.c         | 11 +++++++++--
 hw/virtio/iothread-vq-mapping.c         | 11 +++++------
 include/hw/virtio/iothread-vq-mapping.h |  6 +++++-
 4 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index ddf0e9ee53..b6f74df68f 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1453,14 +1453,19 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
     s->vq_aio_context = g_new(AioContext *, conf->num_queues);
 
     if (conf->iothread_vq_mapping_list) {
+        char *path = object_get_canonical_path(OBJECT(vdev));
+
         if (!iothread_vq_mapping_apply(conf->iothread_vq_mapping_list,
                                        s->vq_aio_context,
                                        conf->num_queues,
+                                       path,
                                        errp)) {
+            g_free(path);
             g_free(s->vq_aio_context);
             s->vq_aio_context = NULL;
             return false;
         }
+        g_free(path);
     } else if (conf->iothread) {
         AioContext *ctx = iothread_get_aio_context(conf->iothread);
         for (unsigned i = 0; i < conf->num_queues; i++) {
@@ -1487,7 +1492,10 @@ static void virtio_blk_vq_aio_context_cleanup(VirtIOBlock *s)
     assert(!s->ioeventfd_started);
 
     if (conf->iothread_vq_mapping_list) {
-        iothread_vq_mapping_cleanup(conf->iothread_vq_mapping_list);
+        char *path = object_get_canonical_path(OBJECT(VIRTIO_DEVICE(s)));
+
+        iothread_vq_mapping_cleanup(conf->iothread_vq_mapping_list, path);
+        g_free(path);
     }
 
     if (conf->iothread) {
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index 95f13fb7c2..ed5304c6a9 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -65,13 +65,17 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
     s->vq_aio_context[1] = qemu_get_aio_context();
 
     if (vs->conf.iothread_vq_mapping_list) {
+        char *path = object_get_canonical_path(OBJECT(vdev));
+
         if (!iothread_vq_mapping_apply(vs->conf.iothread_vq_mapping_list,
                     &s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED],
-                    vs->conf.num_queues, errp)) {
+                    vs->conf.num_queues, path, errp)) {
+            g_free(path);
             g_free(s->vq_aio_context);
             s->vq_aio_context = NULL;
             return;
         }
+        g_free(path);
     } else if (vs->conf.iothread) {
         AioContext *ctx = iothread_get_aio_context(vs->conf.iothread);
         for (uint16_t i = 0; i < vs->conf.num_queues; i++) {
@@ -94,7 +98,10 @@ void virtio_scsi_dataplane_cleanup(VirtIOSCSI *s)
     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
 
     if (vs->conf.iothread_vq_mapping_list) {
-        iothread_vq_mapping_cleanup(vs->conf.iothread_vq_mapping_list);
+        char *path = object_get_canonical_path(OBJECT(VIRTIO_DEVICE(s)));
+
+        iothread_vq_mapping_cleanup(vs->conf.iothread_vq_mapping_list, path);
+        g_free(path);
     }
 
     if (vs->conf.iothread) {
diff --git a/hw/virtio/iothread-vq-mapping.c b/hw/virtio/iothread-vq-mapping.c
index 55ce62986c..7dadc43c44 100644
--- a/hw/virtio/iothread-vq-mapping.c
+++ b/hw/virtio/iothread-vq-mapping.c
@@ -77,6 +77,7 @@ bool iothread_vq_mapping_apply(
         IOThreadVirtQueueMappingList *list,
         AioContext **vq_aio_context,
         uint16_t num_queues,
+        const char *holder,
         Error **errp)
 {
     IOThreadVirtQueueMappingList *node;
@@ -93,10 +94,7 @@ bool iothread_vq_mapping_apply(
 
     for (node = list; node; node = node->next) {
         IOThread *iothread = iothread_by_id(node->value->iothread);
-        AioContext *ctx = iothread_get_aio_context(iothread);
-
-        /* Released in virtio_blk_vq_aio_context_cleanup() */
-        object_ref(OBJECT(iothread));
+        AioContext *ctx = iothread_get_aio_context(iothread, holder);
 
         if (node->value->vqs) {
             uint16List *vq;
@@ -120,13 +118,14 @@ bool iothread_vq_mapping_apply(
     return true;
 }
 
-void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list)
+void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list,
+                                 const char *holder)
 {
     IOThreadVirtQueueMappingList *node;
 
     for (node = list; node; node = node->next) {
         IOThread *iothread = iothread_by_id(node->value->iothread);
-        object_unref(OBJECT(iothread));
+        iothread_put_aio_context(iothread, holder);
     }
 }
 
diff --git a/include/hw/virtio/iothread-vq-mapping.h b/include/hw/virtio/iothread-vq-mapping.h
index 57335c3703..0d39caddf3 100644
--- a/include/hw/virtio/iothread-vq-mapping.h
+++ b/include/hw/virtio/iothread-vq-mapping.h
@@ -17,6 +17,7 @@
  * @list: The mapping of virtqueues to IOThreads.
  * @vq_aio_context: The array of AioContext pointers to fill in.
  * @num_queues: The length of @vq_aio_context.
+ * @holder: The QOM paths for attached device.
  * @errp: If an error occurs, a pointer to the area to store the error.
  *
  * Fill in the AioContext for each virtqueue in the @vq_aio_context array given
@@ -31,15 +32,18 @@ bool iothread_vq_mapping_apply(
         IOThreadVirtQueueMappingList *list,
         AioContext **vq_aio_context,
         uint16_t num_queues,
+        const char *holder,
         Error **errp);
 
 /**
  * iothread_vq_mapping_cleanup:
  * @list: The mapping of virtqueues to IOThreads.
+ * @holder: The QOM paths for attached device.
  *
  * Release IOThread object references that were acquired by
  * iothread_vq_mapping_apply().
  */
-void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list);
+void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list,
+                                 const char *holder);
 
 #endif /* HW_VIRTIO_IOTHREAD_VQ_MAPPING_H */
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 06/13] virtio: use iothread_get/put_aio_context for thread pinning
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (4 preceding siblings ...)
  2026-03-05 14:24 ` [PATCH V5 05/13] virtio-vq-mapping: track iothread-vq-mapping references using device path Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-09  8:27   ` Stefan Hajnoczi
  2026-03-05 14:24 ` [PATCH V5 07/13] net/colo: track IOThread references using path-based holder Zhang Chen
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Refactor virtio-blk and virtio-scsi to use the new iothread_get/put
APIs for AioContext management. This ensures IOThread references
are tracked via the device's canonical QOM path.

Summary of changes:
- Lift 'path' scope to cover both vq_mapping and single iothread cases.
- Replace raw object_ref/unref with iothread_get/put_aio_context.
- Ensure consistent memory cleanup of the QOM path string.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 hw/block/virtio-blk.c           | 17 ++++++-----------
 hw/scsi/virtio-scsi-dataplane.c | 18 +++++++-----------
 2 files changed, 13 insertions(+), 22 deletions(-)

diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index b6f74df68f..94a70c6212 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1429,6 +1429,7 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
     VirtIOBlkConf *conf = &s->conf;
     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+    char *path = object_get_canonical_path(OBJECT(vdev));
 
     if (conf->iothread && conf->iothread_vq_mapping_list) {
         error_setg(errp,
@@ -1453,8 +1454,6 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
     s->vq_aio_context = g_new(AioContext *, conf->num_queues);
 
     if (conf->iothread_vq_mapping_list) {
-        char *path = object_get_canonical_path(OBJECT(vdev));
-
         if (!iothread_vq_mapping_apply(conf->iothread_vq_mapping_list,
                                        s->vq_aio_context,
                                        conf->num_queues,
@@ -1465,15 +1464,11 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
             s->vq_aio_context = NULL;
             return false;
         }
-        g_free(path);
     } else if (conf->iothread) {
-        AioContext *ctx = iothread_get_aio_context(conf->iothread);
+        AioContext *ctx = iothread_get_aio_context(conf->iothread, path);
         for (unsigned i = 0; i < conf->num_queues; i++) {
             s->vq_aio_context[i] = ctx;
         }
-
-        /* Released in virtio_blk_vq_aio_context_cleanup() */
-        object_ref(OBJECT(conf->iothread));
     } else {
         AioContext *ctx = qemu_get_aio_context();
         for (unsigned i = 0; i < conf->num_queues; i++) {
@@ -1481,6 +1476,7 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
         }
     }
 
+    g_free(path);
     return true;
 }
 
@@ -1488,20 +1484,19 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
 static void virtio_blk_vq_aio_context_cleanup(VirtIOBlock *s)
 {
     VirtIOBlkConf *conf = &s->conf;
+    char *path = object_get_canonical_path(OBJECT(VIRTIO_DEVICE(s)));
 
     assert(!s->ioeventfd_started);
 
     if (conf->iothread_vq_mapping_list) {
-        char *path = object_get_canonical_path(OBJECT(VIRTIO_DEVICE(s)));
-
         iothread_vq_mapping_cleanup(conf->iothread_vq_mapping_list, path);
-        g_free(path);
     }
 
     if (conf->iothread) {
-        object_unref(OBJECT(conf->iothread));
+        iothread_put_aio_context(conf->iothread, path);
     }
 
+    g_free(path);
     g_free(s->vq_aio_context);
     s->vq_aio_context = NULL;
 }
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index ed5304c6a9..29615ec63e 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -28,6 +28,7 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
     VirtIODevice *vdev = VIRTIO_DEVICE(s);
     BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+    char *path = object_get_canonical_path(OBJECT(vdev));
 
     if (vs->conf.iothread && vs->conf.iothread_vq_mapping_list) {
         error_setg(errp,
@@ -65,8 +66,6 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
     s->vq_aio_context[1] = qemu_get_aio_context();
 
     if (vs->conf.iothread_vq_mapping_list) {
-        char *path = object_get_canonical_path(OBJECT(vdev));
-
         if (!iothread_vq_mapping_apply(vs->conf.iothread_vq_mapping_list,
                     &s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED],
                     vs->conf.num_queues, path, errp)) {
@@ -75,39 +74,36 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
             s->vq_aio_context = NULL;
             return;
         }
-        g_free(path);
     } else if (vs->conf.iothread) {
-        AioContext *ctx = iothread_get_aio_context(vs->conf.iothread);
+        AioContext *ctx = iothread_get_aio_context(vs->conf.iothread, path);
         for (uint16_t i = 0; i < vs->conf.num_queues; i++) {
             s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i] = ctx;
         }
-
-        /* Released in virtio_scsi_dataplane_cleanup() */
-        object_ref(OBJECT(vs->conf.iothread));
     } else {
         AioContext *ctx = qemu_get_aio_context();
         for (unsigned i = 0; i < vs->conf.num_queues; i++) {
             s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i] = ctx;
         }
     }
+    g_free(path);
 }
 
 /* Context: BQL held */
 void virtio_scsi_dataplane_cleanup(VirtIOSCSI *s)
 {
     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
+    char *path = object_get_canonical_path(OBJECT(VIRTIO_DEVICE(s)));
 
     if (vs->conf.iothread_vq_mapping_list) {
-        char *path = object_get_canonical_path(OBJECT(VIRTIO_DEVICE(s)));
-
         iothread_vq_mapping_cleanup(vs->conf.iothread_vq_mapping_list, path);
-        g_free(path);
+
     }
 
     if (vs->conf.iothread) {
-        object_unref(OBJECT(vs->conf.iothread));
+        iothread_put_aio_context(vs->conf.iothread, path);
     }
 
+    g_free(path);
     g_free(s->vq_aio_context);
     s->vq_aio_context = NULL;
 }
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 07/13] net/colo: track IOThread references using path-based holder
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (5 preceding siblings ...)
  2026-03-05 14:24 ` [PATCH V5 06/13] virtio: use iothread_get/put_aio_context for thread pinning Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-09  8:44   ` Stefan Hajnoczi
  2026-03-05 14:24 ` [PATCH V5 08/13] block/export: Update tracking iothread users with holder name Zhang Chen
                   ` (5 subsequent siblings)
  12 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Convert colo-compare to use the iothread_get_aio_context() and
iothread_put_aio_context() APIs. This ensures that IOThread
references are tracked using the COLO object's canonical QOM path
as the holder ID.

Changes:
- In colo_compare_iothread(), acquire the AioContext with the object
  path and pass the context to colo_compare_timer_init() to avoid
  redundant reference counting.
- In colo_compare_finalize(), use the object path to release the
  IOThread reference.
- Properly manage the lifecycle of the canonical path string with
  g_free() to avoid memory leaks.

This refactoring improves IOThread lifecycle traceability and aligns
the code with modern QEMU iothread reference management patterns.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 net/colo-compare.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/net/colo-compare.c b/net/colo-compare.c
index c356419d6a..43addb5f00 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -924,10 +924,8 @@ void colo_notify_compares_event(void *opaque, int event, Error **errp)
     qemu_mutex_unlock(&colo_compare_mutex);
 }
 
-static void colo_compare_timer_init(CompareState *s)
+static void colo_compare_timer_init(CompareState *s, AioContext *ctx)
 {
-    AioContext *ctx = iothread_get_aio_context(s->iothread);
-
     s->packet_check_timer = aio_timer_new(ctx, QEMU_CLOCK_HOST,
                                 SCALE_MS, check_old_packet_regular,
                                 s);
@@ -968,8 +966,9 @@ static void colo_compare_handle_event(void *opaque)
 
 static void colo_compare_iothread(CompareState *s)
 {
-    AioContext *ctx = iothread_get_aio_context(s->iothread);
-    object_ref(OBJECT(s->iothread));
+    char *path = object_get_canonical_path(OBJECT(s));
+    AioContext *ctx = iothread_get_aio_context(s->iothread, path);
+
     s->worker_context = iothread_get_g_main_context(s->iothread);
 
     qemu_chr_fe_set_handlers(&s->chr_pri_in, compare_chr_can_read,
@@ -984,8 +983,10 @@ static void colo_compare_iothread(CompareState *s)
                                  s, s->worker_context, true);
     }
 
-    colo_compare_timer_init(s);
+    colo_compare_timer_init(s, ctx);
     s->event_bh = aio_bh_new(ctx, colo_compare_handle_event, s);
+
+    g_free(path);
 }
 
 static char *compare_get_pri_indev(Object *obj, Error **errp)
@@ -1408,6 +1409,7 @@ static void colo_compare_finalize(Object *obj)
 {
     CompareState *s = COLO_COMPARE(obj);
     CompareState *tmp = NULL;
+    char *path = object_get_canonical_path(OBJECT(s));
 
     qemu_mutex_lock(&colo_compare_mutex);
     QTAILQ_FOREACH(tmp, &net_compares, next) {
@@ -1434,11 +1436,17 @@ static void colo_compare_finalize(Object *obj)
 
     qemu_bh_delete(s->event_bh);
 
-    AioContext *ctx = iothread_get_aio_context(s->iothread);
+    /*
+     * Use the device's canonical path as the holder ID to track IOThread
+     * usage and ensure the AioContext remains valid during the device's
+     * lifecycle.
+     */
+    AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);
     AIO_WAIT_WHILE(ctx, !s->out_sendco.done);
     if (s->notify_dev) {
         AIO_WAIT_WHILE(ctx, !s->notify_sendco.done);
     }
+    iothread_put_aio_context(s->iothread, path);
 
     /* Release all unhandled packets after compare thead exited */
     g_queue_foreach(&s->conn_list, colo_flush_packets, s);
@@ -1456,6 +1464,7 @@ static void colo_compare_finalize(Object *obj)
 
     object_unref(OBJECT(s->iothread));
 
+    g_free(path);
     g_free(s->pri_indev);
     g_free(s->sec_indev);
     g_free(s->outdev);
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 08/13] block/export: Update tracking iothread users with holder name
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (6 preceding siblings ...)
  2026-03-05 14:24 ` [PATCH V5 07/13] net/colo: track IOThread references using path-based holder Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-09  8:52   ` Stefan Hajnoczi
  2026-03-05 14:24 ` [PATCH V5 09/13] monitor: " Zhang Chen
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

It seems we cannot find the corresponding resources
(the iothread and path) when deleting them in qmp_block_export_del.
The original method will be retained for the time being.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 block/export/export.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/block/export/export.c b/block/export/export.c
index f3bbf11070..98aca7f8c9 100644
--- a/block/export/export.c
+++ b/block/export/export.c
@@ -127,7 +127,12 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
             goto fail;
         }
 
-        new_ctx = iothread_get_aio_context(iothread);
+        /*
+         * It seems we cannot find the corresponding resources
+         * (the iothread and path) when deleting them in qmp_block_export_del.
+         * The original method will be retained for the time being.
+         */
+        new_ctx = iothread_get_aio_context(iothread, NULL);
 
         /* Ignore errors with fixed-iothread=false */
         set_context_errp = fixed_iothread ? errp : NULL;
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 09/13] monitor: Update tracking iothread users with holder name
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (7 preceding siblings ...)
  2026-03-05 14:24 ` [PATCH V5 08/13] block/export: Update tracking iothread users with holder name Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-09  8:56   ` Stefan Hajnoczi
  2026-03-05 14:24 ` [PATCH V5 10/13] virtio-balloon: " Zhang Chen
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Because of monitor create own iothread, and use the
iothread suspend/resume with bh handler and chardev handler.
The QOM lifecycle is hard to track. Update the legacy usage
of "iothread_get_aio_context()" and "iothread_put_aio_context()".

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 monitor/monitor.c | 4 ++--
 monitor/qmp.c     | 3 ++-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/monitor/monitor.c b/monitor/monitor.c
index 1273eb7260..5da3c05e8d 100644
--- a/monitor/monitor.c
+++ b/monitor/monitor.c
@@ -547,7 +547,7 @@ int monitor_suspend(Monitor *mon)
          * Kick I/O thread to make sure this takes effect.  It'll be
          * evaluated again in prepare() of the watch object.
          */
-        aio_notify(iothread_get_aio_context(mon_iothread));
+        aio_notify(iothread_get_aio_context(mon_iothread, NULL));
     }
 
     trace_monitor_suspend(mon, 1);
@@ -582,7 +582,7 @@ void monitor_resume(Monitor *mon)
         AioContext *ctx;
 
         if (mon->use_io_thread) {
-            ctx = iothread_get_aio_context(mon_iothread);
+            ctx = iothread_get_aio_context(mon_iothread, NULL);
         } else {
             ctx = qemu_get_aio_context();
         }
diff --git a/monitor/qmp.c b/monitor/qmp.c
index 687019811f..93d0328dcd 100644
--- a/monitor/qmp.c
+++ b/monitor/qmp.c
@@ -544,12 +544,13 @@ void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
          * handling between the main thread and the I/O thread.
          */
         remove_listener_fd_in_watch(chr);
+
         /*
          * We can't call qemu_chr_fe_set_handlers() directly here
          * since chardev might be running in the monitor I/O
          * thread.  Schedule a bottom half.
          */
-        aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
+        aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread, NULL),
                                 monitor_qmp_setup_handlers_bh, mon);
         /* The bottom half will add @mon to @mon_list */
     } else {
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 10/13] virtio-balloon: Update tracking iothread users with holder name
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (8 preceding siblings ...)
  2026-03-05 14:24 ` [PATCH V5 09/13] monitor: " Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-05 14:24 ` [PATCH V5 11/13] vfio-user/proxy: " Zhang Chen
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Replace raw object_ref/unref calls with iothread_get/put_aio_context.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 hw/virtio/virtio-balloon.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 38bf1e84a1..4eeae48b50 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -895,14 +895,18 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp)
     s->svq = virtio_add_queue(vdev, 128, virtio_balloon_receive_stats);
 
     if (virtio_has_feature(s->host_features, VIRTIO_BALLOON_F_FREE_PAGE_HINT)) {
+        char *path = object_get_canonical_path(OBJECT(s));
+
         s->free_page_vq = virtio_add_queue(vdev, VIRTQUEUE_MAX_SIZE,
                                            virtio_balloon_handle_free_page_vq);
         precopy_add_notifier(&s->free_page_hint_notify);
 
-        object_ref(OBJECT(s->iothread));
-        s->free_page_bh = aio_bh_new_guarded(iothread_get_aio_context(s->iothread),
-                                             virtio_ballloon_get_free_page_hints, s,
-                                             &dev->mem_reentrancy_guard);
+        s->free_page_bh = aio_bh_new_guarded(
+                              iothread_get_aio_context(s->iothread, path),
+                              virtio_ballloon_get_free_page_hints, s,
+                              &dev->mem_reentrancy_guard);
+
+        g_free(path);
     }
 
     if (virtio_has_feature(s->host_features, VIRTIO_BALLOON_F_REPORTING)) {
@@ -922,10 +926,13 @@ static void virtio_balloon_device_unrealize(DeviceState *dev)
 
     qemu_unregister_resettable(OBJECT(dev));
     if (s->free_page_bh) {
+        char *path = object_get_canonical_path(OBJECT(s));
+
         qemu_bh_delete(s->free_page_bh);
-        object_unref(OBJECT(s->iothread));
         virtio_balloon_free_page_stop(s);
+        iothread_put_aio_context(s->iothread, path);
         precopy_remove_notifier(&s->free_page_hint_notify);
+        g_free(path);
     }
     balloon_stats_destroy_timer(s);
     qemu_remove_balloon_handler(s);
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 11/13] vfio-user/proxy: Update tracking iothread users with holder name
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (9 preceding siblings ...)
  2026-03-05 14:24 ` [PATCH V5 10/13] virtio-balloon: " Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-05 14:24 ` [PATCH V5 12/13] xen-block: " Zhang Chen
  2026-03-05 14:24 ` [PATCH V5 13/13] qapi: examine IOThread attachment status via query-iothreads Zhang Chen
  12 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Add object_ref/unref calls with iothread_get/put_aio_context.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 hw/vfio-user/proxy.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c
index 314dfd23d8..9db17653d6 100644
--- a/hw/vfio-user/proxy.c
+++ b/hw/vfio-user/proxy.c
@@ -898,6 +898,7 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp)
     QIOChannelSocket *sioc;
     QIOChannel *ioc;
     char *sockname;
+    char *path;
 
     if (addr->type != SOCKET_ADDRESS_TYPE_UNIX) {
         error_setg(errp, "vfio_user_connect - bad address family");
@@ -917,6 +918,7 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp)
     proxy = g_malloc0(sizeof(VFIOUserProxy));
     proxy->sockname = g_strdup_printf("unix:%s", sockname);
     proxy->ioc = ioc;
+    path = object_get_canonical_path(OBJECT(proxy->ioc));
 
     /* init defaults */
     proxy->max_xfer_size = VFIO_USER_DEF_MAX_XFER;
@@ -936,7 +938,7 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp)
         vfio_user_iothread = iothread_create("VFIO user", errp);
     }
 
-    proxy->ctx = iothread_get_aio_context(vfio_user_iothread);
+    proxy->ctx = iothread_get_aio_context(vfio_user_iothread, path);
     proxy->req_bh = qemu_bh_new(vfio_user_request, proxy);
 
     QTAILQ_INIT(&proxy->outgoing);
@@ -944,6 +946,7 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp)
     QTAILQ_INIT(&proxy->free);
     QTAILQ_INIT(&proxy->pending);
     QLIST_INSERT_HEAD(&vfio_user_sockets, proxy, next);
+    g_free(path);
 
     return proxy;
 
@@ -967,6 +970,7 @@ void vfio_user_set_handler(VFIODevice *vbasedev,
 void vfio_user_disconnect(VFIOUserProxy *proxy)
 {
     VFIOUserMsg *r1, *r2;
+    char *path = object_get_canonical_path(OBJECT(proxy->ioc));
 
     qemu_mutex_lock(&proxy->lock);
 
@@ -1021,12 +1025,15 @@ void vfio_user_disconnect(VFIOUserProxy *proxy)
     qemu_cond_destroy(&proxy->close_cv);
     qemu_mutex_destroy(&proxy->lock);
 
+    iothread_put_aio_context(vfio_user_iothread, path);
+
     QLIST_REMOVE(proxy, next);
     if (QLIST_EMPTY(&vfio_user_sockets)) {
         iothread_destroy(vfio_user_iothread);
         vfio_user_iothread = NULL;
     }
 
+    g_free(path);
     g_free(proxy->sockname);
     g_free(proxy);
 }
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 12/13] xen-block: Update tracking iothread users with holder name
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (10 preceding siblings ...)
  2026-03-05 14:24 ` [PATCH V5 11/13] vfio-user/proxy: " Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-05 14:24 ` [PATCH V5 13/13] qapi: examine IOThread attachment status via query-iothreads Zhang Chen
  12 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Replace raw object_ref/unref calls with iothread_get/put_aio_context.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 hw/block/dataplane/xen-block.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
index 48c2e315f3..a14de14274 100644
--- a/hw/block/dataplane/xen-block.c
+++ b/hw/block/dataplane/xen-block.c
@@ -621,9 +621,11 @@ XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev,
     QLIST_INIT(&dataplane->freelist);
 
     if (iothread) {
+        char *path = object_get_canonical_path(OBJECT(xendev));
+
         dataplane->iothread = iothread;
-        object_ref(OBJECT(dataplane->iothread));
-        dataplane->ctx = iothread_get_aio_context(dataplane->iothread);
+        dataplane->ctx = iothread_get_aio_context(dataplane->iothread, path);
+        g_free(path);
     } else {
         dataplane->ctx = qemu_get_aio_context();
     }
@@ -652,7 +654,10 @@ void xen_block_dataplane_destroy(XenBlockDataPlane *dataplane)
 
     qemu_bh_delete(dataplane->bh);
     if (dataplane->iothread) {
-        object_unref(OBJECT(dataplane->iothread));
+        char *path = object_get_canonical_path(OBJECT(dataplane->xendev));
+
+        iothread_put_aio_context(dataplane->iothread, path);
+        g_free(path);
     }
 
     g_free(dataplane);
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH V5 13/13] qapi: examine IOThread attachment status via query-iothreads
  2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (11 preceding siblings ...)
  2026-03-05 14:24 ` [PATCH V5 12/13] xen-block: " Zhang Chen
@ 2026-03-05 14:24 ` Zhang Chen
  2026-03-18  6:09   ` Markus Armbruster
  12 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-05 14:24 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Extend the 'IOThreadInfo' structure to include attachment metrics.
This allows users to monitor the associated devices by identify them
by their QOM paths.

New fields added to IOThreadInfo:
- @holders: A string containing of QOM paths for the attached devices.

These fields are also exposed via the Human Monitor Interface (HMP)
command 'info iothreads' to assist with manual debugging and
performance tuning.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 iothread.c         | 14 ++++++++++++++
 monitor/hmp-cmds.c | 13 +++++++++++++
 qapi/misc.json     | 13 +++++++++++++
 3 files changed, 40 insertions(+)

diff --git a/iothread.c b/iothread.c
index da98fbb9ad..ecd09f396e 100644
--- a/iothread.c
+++ b/iothread.c
@@ -64,6 +64,19 @@ static void iothread_unref(IOThread *iothread, const char *holder)
     }
 }
 
+static strList *iothread_get_holders_list(IOThread *iothread)
+{
+    strList *head = NULL;
+    strList **tail = &head;
+    GList *l;
+
+    for (l = iothread->holders; l != NULL; l = l->next) {
+        QAPI_LIST_APPEND(tail, g_strdup(l->data));
+    }
+
+    return head;
+}
+
 static void *iothread_run(void *opaque)
 {
     IOThread *iothread = opaque;
@@ -405,6 +418,7 @@ static int query_one_iothread(Object *object, void *opaque)
     info = g_new0(IOThreadInfo, 1);
     info->id = iothread_get_id(iothread);
     info->thread_id = iothread->thread_id;
+    info->holders = iothread_get_holders_list(iothread);
     info->poll_max_ns = iothread->poll_max_ns;
     info->poll_grow = iothread->poll_grow;
     info->poll_shrink = iothread->poll_shrink;
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index bad034937a..d01ebba94d 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -203,6 +203,19 @@ void hmp_info_iothreads(Monitor *mon, const QDict *qdict)
         value = info->value;
         monitor_printf(mon, "%s:\n", value->id);
         monitor_printf(mon, "  thread_id=%" PRId64 "\n", value->thread_id);
+        monitor_printf(mon, "  holders=");
+        if (!value->holders) {
+            monitor_printf(mon, "\n");
+        } else {
+            strList *h;
+            bool first = true;
+            for (h = value->holders; h; h = h->next) {
+                monitor_printf(mon, "%s%s", first ? "" : "\n          ",
+                               h->value);
+                first = false;
+            }
+            monitor_printf(mon, "\n");
+        }
         monitor_printf(mon, "  poll-max-ns=%" PRId64 "\n", value->poll_max_ns);
         monitor_printf(mon, "  poll-grow=%" PRId64 "\n", value->poll_grow);
         monitor_printf(mon, "  poll-shrink=%" PRId64 "\n", value->poll_shrink);
diff --git a/qapi/misc.json b/qapi/misc.json
index 1f5062df2a..a381074c04 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -76,6 +76,15 @@
 #
 # @thread-id: ID of the underlying host thread
 #
+# @holders: The parameter is an array of QOM paths indicating how many
+#     active devices are currently associated with this iothread
+#     (e.g. virtio-blk).  In hotplug scenarios, users can
+#     pre-allocate multiple iothread objects to serve as a persistent
+#     thread pool.  When a device is hot-unplugged, the corresponding
+#     IOThread is released but remains available, allowing subsequent
+#     hot-plugged devices to attach to and reuse the existing thread.
+#     Returns empty if no devices are attached.  (since 11.0)
+#
 # @poll-max-ns: maximum polling time in ns, 0 means polling is
 #     disabled (since 2.9)
 #
@@ -93,6 +102,7 @@
 { 'struct': 'IOThreadInfo',
   'data': {'id': 'str',
            'thread-id': 'int',
+           'holders': ['str'],
            'poll-max-ns': 'int',
            'poll-grow': 'int',
            'poll-shrink': 'int',
@@ -118,6 +128,8 @@
 #              {
 #                 "id":"iothread0",
 #                 "thread-id":3134,
+#                 "holders":["/machine/peripheral/blk1/virtio-backend",
+#                            "/machine/peripheral/blk0/virtio-backend"],
 #                 "poll-max-ns":32768,
 #                 "poll-grow":0,
 #                 "poll-shrink":0,
@@ -126,6 +138,7 @@
 #              {
 #                 "id":"iothread1",
 #                 "thread-id":3135,
+#                 "holders":["/machine/peripheral/blk2/virtio-backend"],
 #                 "poll-max-ns":32768,
 #                 "poll-grow":0,
 #                 "poll-shrink":0,
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 02/13] iothread: introduce iothread_ref/unref to track attached devices
  2026-03-05 14:24 ` [PATCH V5 02/13] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
@ 2026-03-09  7:49   ` Stefan Hajnoczi
  2026-03-10  9:49     ` Zhang Chen
  0 siblings, 1 reply; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-09  7:49 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 3432 bytes --]

On Thu, Mar 05, 2026 at 10:24:48PM +0800, Zhang Chen wrote:
> Currently, IOThreads do not maintain a record of which devices are
> associated with them. This makes it difficult to monitor the
> workload distribution of IOThreads, especially in complex
> hotplug scenarios involving multiple virtio-blk or virtio-scsi devices.
> 
> This patch introduces a reference counting and tracking mechanism
> within the IOThread object:
> 
> - iothread_ref(): Prepends the device's QOM path to a list.
>   Note: The IOThread takes ownership of the passed 'holder' string.

This is outdated, the patch duplicates the string and the caller still
owns the holder argument:

  iothread->holders = g_list_prepend(iothread->holders, g_strdup(holder));

> - iothread_unref(): Searches for the device path using a custom
>   string comparison (g_strcmp0), releases the associated memory
>   upon a successful match.
> - holders: A GList storing the QOM paths of attached devices
>   for runtime introspection.
> 
> This infrastructure allows management tools and QMP commands to
> query the attachment status of IOThreads.
> 
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  include/system/iothread.h |  1 +
>  iothread.c                | 28 ++++++++++++++++++++++++++++
>  2 files changed, 29 insertions(+)
> 
> diff --git a/include/system/iothread.h b/include/system/iothread.h
> index e26d13c6c7..21a76bd70d 100644
> --- a/include/system/iothread.h
> +++ b/include/system/iothread.h
> @@ -33,6 +33,7 @@ struct IOThread {
>      bool stopping;              /* has iothread_stop() been called? */
>      bool running;               /* should iothread_run() continue? */
>      int thread_id;
> +    GList *holders;             /* an array of QOM paths for attached devices */
>  
>      /* AioContext poll parameters */
>      int64_t poll_max_ns;
> diff --git a/iothread.c b/iothread.c
> index caf68e0764..80a8cf4b32 100644
> --- a/iothread.c
> +++ b/iothread.c
> @@ -36,6 +36,34 @@
>  #define IOTHREAD_POLL_MAX_NS_DEFAULT 0ULL
>  #endif
>  
> +/*
> + * Add holder device path to the list.
> + */
> +static void iothread_ref(IOThread *iothread, const char *holder)
> +{
> +    iothread->holders = g_list_prepend(iothread->holders, g_strdup(holder));
> +}
> +
> +/*
> + * Delete holder device path from the list.
> + */
> +static void iothread_unref(IOThread *iothread, const char *holder)
> +{
> +    if (iothread->holders) {

g_list_find_customer(NULL, ...) returns NULL rather than crashing, so
there is no need for if (iothread->holders).

> +        GList *link = g_list_find_custom(iothread->holders, holder,
> +                                         (GCompareFunc)g_strcmp0);
> +
> +        if (link) {

assert(link) is usually used in QEMU instead. If the reference has
already been released then the program is in an invalid state and it's
not safe to continue running.

Please also add assert(iothread->holders == NULL) to
iothread_instance_finalize() so that leaked references are caught.

> +            g_free(link->data);
> +            iothread->holders = g_list_delete_link(iothread->holders, link);
> +        } else {
> +            error_report("iothread_unref can't find the holder %s", holder);
> +        }
> +    } else {
> +        error_report("iohtread_unref iothread is not held by any devices");

iohtread -> iothread

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 03/13] iothread: tracking iothread users with holder name
  2026-03-05 14:24 ` [PATCH V5 03/13] iothread: tracking iothread users with holder name Zhang Chen
@ 2026-03-09  8:02   ` Stefan Hajnoczi
  2026-03-10  9:49     ` Zhang Chen
  2026-03-09  8:33   ` Stefan Hajnoczi
  1 sibling, 1 reply; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-09  8:02 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 3875 bytes --]

On Thu, Mar 05, 2026 at 10:24:49PM +0800, Zhang Chen wrote:
> Introduce iothread_get_aio_context() with a 'holder' argument and its
> counterpart iothread_put_aio_context().
> 
> Previously, users of an IOThread's AioContext did not explicitly
> record their identity, making it difficult to debug which devices or
> subsystems were pinning an IOThread.
> 
> This patch enhances the reference counting mechanism by:
> 1. Automatically incrementing the object reference count when a context
>    is retrieved.
> 2. Tracking holders by name using iothread_ref() and iothread_unref().
> 
> In iothread_instance_finalize(), we now retrieve the source name from
> the GMainContext to correctly unref the initial internal holder.
> 
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  include/system/iothread.h |  3 ++-
>  iothread.c                | 28 ++++++++++++++++++++++++++--
>  2 files changed, 28 insertions(+), 3 deletions(-)
> 
> diff --git a/include/system/iothread.h b/include/system/iothread.h
> index 21a76bd70d..595abeefbe 100644
> --- a/include/system/iothread.h
> +++ b/include/system/iothread.h
> @@ -47,7 +47,8 @@ DECLARE_INSTANCE_CHECKER(IOThread, IOTHREAD,
>  
>  char *iothread_get_id(IOThread *iothread);
>  IOThread *iothread_by_id(const char *id);
> -AioContext *iothread_get_aio_context(IOThread *iothread);
> +AioContext *iothread_get_aio_context(IOThread *iothread, const char *holder);
> +void iothread_put_aio_context(IOThread *iothread, const char *holder);
>  GMainContext *iothread_get_g_main_context(IOThread *iothread);
>  
>  /*
> diff --git a/iothread.c b/iothread.c
> index 80a8cf4b32..da98fbb9ad 100644
> --- a/iothread.c
> +++ b/iothread.c
> @@ -172,7 +172,8 @@ static void iothread_init_gcontext(IOThread *iothread, const char *thread_name)
>      g_autofree char *name = g_strdup_printf("%s aio-context", thread_name);
>  
>      iothread->worker_context = g_main_context_new();
> -    source = aio_get_g_source(iothread_get_aio_context(iothread));
> +    /* No need setup itself as the init holder */
> +    source = aio_get_g_source(iothread_get_aio_context(iothread, NULL));

This is cleaner:

  source = aio_get_g_source(iothread->ctx);

That way iothread_get_aio_context() doesn't need a special case for
NULL.

>      g_source_set_name(source, name);
>      g_source_attach(source, iothread->worker_context);
>      g_source_unref(source);
> @@ -362,11 +363,34 @@ char *iothread_get_id(IOThread *iothread)
>      return g_strdup(object_get_canonical_path_component(OBJECT(iothread)));
>  }
>  
> -AioContext *iothread_get_aio_context(IOThread *iothread)
> +AioContext *iothread_get_aio_context(IOThread *iothread, const char *holder)
>  {
> +    /*
> +     * In some cases, iothread user need the ctx to clearup other resource.
> +     * When holder is empty, back to the legacy way.
> +     */
> +    if (holder) {
> +        /*
> +         * This guarantees that the IOThread and its AioContext remain alive
> +         * as long as there is a holder.
> +         */
> +        object_ref(OBJECT(iothread));

Can this be done in iothread_ref()/iothread_unref()? That would ensure
that callers always increment the QOM reference count regardless of
whether the iothread_ref() or iothread_get_aio_context() API is used.

> +
> +        /* Add holder device path to the list */
> +        iothread_ref(iothread, holder);
> +    }
> +
>      return iothread->ctx;
>  }
>  
> +void iothread_put_aio_context(IOThread *iothread, const char *holder)
> +{
> +    object_unref(OBJECT(iothread));
> +
> +    /* Delete holder device path from the list */
> +    iothread_unref(iothread, holder);
> +}
> +
>  static int query_one_iothread(Object *object, void *opaque)
>  {
>      IOThreadInfoList ***tail = opaque;
> -- 
> 2.49.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 04/13] blockdev: Update tracking iothread users with holder name
  2026-03-05 14:24 ` [PATCH V5 04/13] blockdev: Update " Zhang Chen
@ 2026-03-09  8:15   ` Stefan Hajnoczi
  2026-03-10 10:02     ` Zhang Chen
  0 siblings, 1 reply; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-09  8:15 UTC (permalink / raw)
  To: Zhang Chen, Kevin Wolf, armbru
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 1629 bytes --]

On Thu, Mar 05, 2026 at 10:24:50PM +0800, Zhang Chen wrote:
> Update the usage of "iothread_get_aio_context()".
> 
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  blockdev.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/blockdev.c b/blockdev.c
> index 6e86c6262f..01ccf64b3f 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -3683,7 +3683,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
>              goto out;
>          }
>  
> -        new_context = iothread_get_aio_context(obj);
> +        char *path = object_get_canonical_path(OBJECT(bs));

CCing Kevin and Markus in case they have an opinion on this.

BlockDriverState is not a QOM Object so using OBJECT(bs) is undefined
behavior and may crash.

node_name is unique across block driver graph nodes and could be used.
Unfortunately it's not connected to the QOM Object hierarchy. Maybe it's
best to build a holder name that is an invalid QOM path so there can be
no collisions between QOM paths and block driver graph nodes.

  g_autofree char *holder = g_strdup_printf("BlockDriverState %s", node_name);

(A cleaner long-term solution would be making BlockDriverStates QOM
Objects so they have a proper path.)

> +        /*
> +         * For this qmp case, hard to find the point put aio context ???
> +         * It looks like a pairing function is needed:
> +         * qmp_x_blockdev_del_iothread()
> +         */
> +        new_context = iothread_get_aio_context(obj, path);
> +        g_free(path);

Who calls iothread_put_aio_context()?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 05/13] virtio-vq-mapping: track iothread-vq-mapping references using device path
  2026-03-05 14:24 ` [PATCH V5 05/13] virtio-vq-mapping: track iothread-vq-mapping references using device path Zhang Chen
@ 2026-03-09  8:21   ` Stefan Hajnoczi
  2026-03-10 10:03     ` Zhang Chen
  0 siblings, 1 reply; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-09  8:21 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 1257 bytes --]

On Thu, Mar 05, 2026 at 10:24:51PM +0800, Zhang Chen wrote:
> Replace raw object_ref/unref calls with iothread_get/put_aio_context
> in iothread-vq-mapping. This allows tracking IOThread users via
> the device's canonical QOM path, improving lifecycle traceability
> for virtio-blk and virtio-scsi devices.
> 
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  hw/block/virtio-blk.c                   | 10 +++++++++-
>  hw/scsi/virtio-scsi-dataplane.c         | 11 +++++++++--
>  hw/virtio/iothread-vq-mapping.c         | 11 +++++------
>  include/hw/virtio/iothread-vq-mapping.h |  6 +++++-
>  4 files changed, 28 insertions(+), 10 deletions(-)
> 
> diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
> index ddf0e9ee53..b6f74df68f 100644
> --- a/hw/block/virtio-blk.c
> +++ b/hw/block/virtio-blk.c
> @@ -1453,14 +1453,19 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
>      s->vq_aio_context = g_new(AioContext *, conf->num_queues);
>  
>      if (conf->iothread_vq_mapping_list) {
> +        char *path = object_get_canonical_path(OBJECT(vdev));

The g_free() calls can be avoided with:

  g_autofree char *path = ...;

The same applies throughout the rest of this patch series.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 06/13] virtio: use iothread_get/put_aio_context for thread pinning
  2026-03-05 14:24 ` [PATCH V5 06/13] virtio: use iothread_get/put_aio_context for thread pinning Zhang Chen
@ 2026-03-09  8:27   ` Stefan Hajnoczi
  2026-03-10 10:07     ` Zhang Chen
  0 siblings, 1 reply; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-09  8:27 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 1462 bytes --]

On Thu, Mar 05, 2026 at 10:24:52PM +0800, Zhang Chen wrote:
> Refactor virtio-blk and virtio-scsi to use the new iothread_get/put
> APIs for AioContext management. This ensures IOThread references
> are tracked via the device's canonical QOM path.
> 
> Summary of changes:
> - Lift 'path' scope to cover both vq_mapping and single iothread cases.
> - Replace raw object_ref/unref with iothread_get/put_aio_context.
> - Ensure consistent memory cleanup of the QOM path string.
> 
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  hw/block/virtio-blk.c           | 17 ++++++-----------
>  hw/scsi/virtio-scsi-dataplane.c | 18 +++++++-----------
>  2 files changed, 13 insertions(+), 22 deletions(-)
> 
> diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
> index b6f74df68f..94a70c6212 100644
> --- a/hw/block/virtio-blk.c
> +++ b/hw/block/virtio-blk.c
> @@ -1429,6 +1429,7 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
>      VirtIOBlkConf *conf = &s->conf;
>      BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
>      VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
> +    char *path = object_get_canonical_path(OBJECT(vdev));
>  
>      if (conf->iothread && conf->iothread_vq_mapping_list) {
>          error_setg(errp,

There is a memory leak when this returns false. Please use g_autofree
when declaring temporary string variables instead of calling g_free()
manually.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 03/13] iothread: tracking iothread users with holder name
  2026-03-05 14:24 ` [PATCH V5 03/13] iothread: tracking iothread users with holder name Zhang Chen
  2026-03-09  8:02   ` Stefan Hajnoczi
@ 2026-03-09  8:33   ` Stefan Hajnoczi
  2026-03-10  9:51     ` Zhang Chen
  1 sibling, 1 reply; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-09  8:33 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 2398 bytes --]

On Thu, Mar 05, 2026 at 10:24:49PM +0800, Zhang Chen wrote:
> Introduce iothread_get_aio_context() with a 'holder' argument and its
> counterpart iothread_put_aio_context().
> 
> Previously, users of an IOThread's AioContext did not explicitly
> record their identity, making it difficult to debug which devices or
> subsystems were pinning an IOThread.
> 
> This patch enhances the reference counting mechanism by:
> 1. Automatically incrementing the object reference count when a context
>    is retrieved.
> 2. Tracking holders by name using iothread_ref() and iothread_unref().
> 
> In iothread_instance_finalize(), we now retrieve the source name from
> the GMainContext to correctly unref the initial internal holder.
> 
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  include/system/iothread.h |  3 ++-
>  iothread.c                | 28 ++++++++++++++++++++++++++--
>  2 files changed, 28 insertions(+), 3 deletions(-)
> 
> diff --git a/include/system/iothread.h b/include/system/iothread.h
> index 21a76bd70d..595abeefbe 100644
> --- a/include/system/iothread.h
> +++ b/include/system/iothread.h
> @@ -47,7 +47,8 @@ DECLARE_INSTANCE_CHECKER(IOThread, IOTHREAD,
>  
>  char *iothread_get_id(IOThread *iothread);
>  IOThread *iothread_by_id(const char *id);
> -AioContext *iothread_get_aio_context(IOThread *iothread);
> +AioContext *iothread_get_aio_context(IOThread *iothread, const char *holder);

This commit breaks all iothread_get_aio_context() callers because they
are not yet converted. The build will fail here.

Every commit must build and pass the tests. This is necessary so that
git-bisect(1) works and it also makes backporting patches much easier
when there are no implicit dependencies between commits.

Please structure this patch series in a way that keeps the build and
tests passing. One way to do this is to give the new API a different
name like:

  AioContext *iothread_get_aio_context(IOThread *iothread);
+ AioContext *iothread_ref_and_get_aio_context(IOThread *iothread, const char *holder);

That name also makes clear that a reference will be taken on the
IOThread. You could then require callers to call iothread_unref()
directly instead of iothread_put_aio_context().

One of the last commits in the patch series could remove the old
iothread_get_aio_context() after everything has been converted.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 07/13] net/colo: track IOThread references using path-based holder
  2026-03-05 14:24 ` [PATCH V5 07/13] net/colo: track IOThread references using path-based holder Zhang Chen
@ 2026-03-09  8:44   ` Stefan Hajnoczi
  2026-03-10 10:15     ` Zhang Chen
  0 siblings, 1 reply; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-09  8:44 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 743 bytes --]

On Thu, Mar 05, 2026 at 10:24:53PM +0800, Zhang Chen wrote:
> @@ -1434,11 +1436,17 @@ static void colo_compare_finalize(Object *obj)
>  
>      qemu_bh_delete(s->event_bh);
>  
> -    AioContext *ctx = iothread_get_aio_context(s->iothread);
> +    /*
> +     * Use the device's canonical path as the holder ID to track IOThread
> +     * usage and ensure the AioContext remains valid during the device's
> +     * lifecycle.
> +     */
> +    AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);

Should NULL be path?

>      AIO_WAIT_WHILE(ctx, !s->out_sendco.done);
>      if (s->notify_dev) {
>          AIO_WAIT_WHILE(ctx, !s->notify_sendco.done);
>      }
> +    iothread_put_aio_context(s->iothread, path);

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 08/13] block/export: Update tracking iothread users with holder name
  2026-03-05 14:24 ` [PATCH V5 08/13] block/export: Update tracking iothread users with holder name Zhang Chen
@ 2026-03-09  8:52   ` Stefan Hajnoczi
  0 siblings, 0 replies; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-09  8:52 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 1258 bytes --]

On Thu, Mar 05, 2026 at 10:24:54PM +0800, Zhang Chen wrote:
> It seems we cannot find the corresponding resources
> (the iothread and path) when deleting them in qmp_block_export_del.
> The original method will be retained for the time being.
> 
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  block/export/export.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/block/export/export.c b/block/export/export.c
> index f3bbf11070..98aca7f8c9 100644
> --- a/block/export/export.c
> +++ b/block/export/export.c
> @@ -127,7 +127,12 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
>              goto fail;
>          }
>  
> -        new_ctx = iothread_get_aio_context(iothread);
> +        /*
> +         * It seems we cannot find the corresponding resources
> +         * (the iothread and path) when deleting them in qmp_block_export_del.
> +         * The original method will be retained for the time being.
> +         */
> +        new_ctx = iothread_get_aio_context(iothread, NULL);

This needs to be implemented properly in order to be merged.

BlockExport has an AioContext field. It's possible to modify the code to
keep an IOThread pointer too.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 09/13] monitor: Update tracking iothread users with holder name
  2026-03-05 14:24 ` [PATCH V5 09/13] monitor: " Zhang Chen
@ 2026-03-09  8:56   ` Stefan Hajnoczi
  2026-03-10 10:24     ` Zhang Chen
  0 siblings, 1 reply; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-09  8:56 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 489 bytes --]

On Thu, Mar 05, 2026 at 10:24:55PM +0800, Zhang Chen wrote:
> Because of monitor create own iothread, and use the
> iothread suspend/resume with bh handler and chardev handler.
> The QOM lifecycle is hard to track. Update the legacy usage
> of "iothread_get_aio_context()" and "iothread_put_aio_context()".

As mentioned in the prevoius patch, this feature needs to be implemented
fully in order to merged. If only half of it works, then it's not a
feature that users can rely on.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 02/13] iothread: introduce iothread_ref/unref to track attached devices
  2026-03-09  7:49   ` Stefan Hajnoczi
@ 2026-03-10  9:49     ` Zhang Chen
  0 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-10  9:49 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Mon, Mar 9, 2026 at 3:49 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Thu, Mar 05, 2026 at 10:24:48PM +0800, Zhang Chen wrote:
> > Currently, IOThreads do not maintain a record of which devices are
> > associated with them. This makes it difficult to monitor the
> > workload distribution of IOThreads, especially in complex
> > hotplug scenarios involving multiple virtio-blk or virtio-scsi devices.
> >
> > This patch introduces a reference counting and tracking mechanism
> > within the IOThread object:
> >
> > - iothread_ref(): Prepends the device's QOM path to a list.
> >   Note: The IOThread takes ownership of the passed 'holder' string.
>
> This is outdated, the patch duplicates the string and the caller still
> owns the holder argument:
>
>   iothread->holders = g_list_prepend(iothread->holders, g_strdup(holder));
>

Yes, will remove this "Note" comments.

> > - iothread_unref(): Searches for the device path using a custom
> >   string comparison (g_strcmp0), releases the associated memory
> >   upon a successful match.
> > - holders: A GList storing the QOM paths of attached devices
> >   for runtime introspection.
> >
> > This infrastructure allows management tools and QMP commands to
> > query the attachment status of IOThreads.
> >
> > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > ---
> >  include/system/iothread.h |  1 +
> >  iothread.c                | 28 ++++++++++++++++++++++++++++
> >  2 files changed, 29 insertions(+)
> >
> > diff --git a/include/system/iothread.h b/include/system/iothread.h
> > index e26d13c6c7..21a76bd70d 100644
> > --- a/include/system/iothread.h
> > +++ b/include/system/iothread.h
> > @@ -33,6 +33,7 @@ struct IOThread {
> >      bool stopping;              /* has iothread_stop() been called? */
> >      bool running;               /* should iothread_run() continue? */
> >      int thread_id;
> > +    GList *holders;             /* an array of QOM paths for attached devices */
> >
> >      /* AioContext poll parameters */
> >      int64_t poll_max_ns;
> > diff --git a/iothread.c b/iothread.c
> > index caf68e0764..80a8cf4b32 100644
> > --- a/iothread.c
> > +++ b/iothread.c
> > @@ -36,6 +36,34 @@
> >  #define IOTHREAD_POLL_MAX_NS_DEFAULT 0ULL
> >  #endif
> >
> > +/*
> > + * Add holder device path to the list.
> > + */
> > +static void iothread_ref(IOThread *iothread, const char *holder)
> > +{
> > +    iothread->holders = g_list_prepend(iothread->holders, g_strdup(holder));
> > +}
> > +
> > +/*
> > + * Delete holder device path from the list.
> > + */
> > +static void iothread_unref(IOThread *iothread, const char *holder)
> > +{
> > +    if (iothread->holders) {
>
> g_list_find_customer(NULL, ...) returns NULL rather than crashing, so
> there is no need for if (iothread->holders).

OK.

>
> > +        GList *link = g_list_find_custom(iothread->holders, holder,
> > +                                         (GCompareFunc)g_strcmp0);
> > +
> > +        if (link) {
>
> assert(link) is usually used in QEMU instead. If the reference has
> already been released then the program is in an invalid state and it's
> not safe to continue running.
>
> Please also add assert(iothread->holders == NULL) to
> iothread_instance_finalize() so that leaked references are caught.

OK. will update in next version.

>
> > +            g_free(link->data);
> > +            iothread->holders = g_list_delete_link(iothread->holders, link);
> > +        } else {
> > +            error_report("iothread_unref can't find the holder %s", holder);
> > +        }
> > +    } else {
> > +        error_report("iohtread_unref iothread is not held by any devices");
>
> iohtread -> iothread

Thanks


Chen


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 03/13] iothread: tracking iothread users with holder name
  2026-03-09  8:02   ` Stefan Hajnoczi
@ 2026-03-10  9:49     ` Zhang Chen
  0 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-10  9:49 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Mon, Mar 9, 2026 at 4:02 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Thu, Mar 05, 2026 at 10:24:49PM +0800, Zhang Chen wrote:
> > Introduce iothread_get_aio_context() with a 'holder' argument and its
> > counterpart iothread_put_aio_context().
> >
> > Previously, users of an IOThread's AioContext did not explicitly
> > record their identity, making it difficult to debug which devices or
> > subsystems were pinning an IOThread.
> >
> > This patch enhances the reference counting mechanism by:
> > 1. Automatically incrementing the object reference count when a context
> >    is retrieved.
> > 2. Tracking holders by name using iothread_ref() and iothread_unref().
> >
> > In iothread_instance_finalize(), we now retrieve the source name from
> > the GMainContext to correctly unref the initial internal holder.
> >
> > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > ---
> >  include/system/iothread.h |  3 ++-
> >  iothread.c                | 28 ++++++++++++++++++++++++++--
> >  2 files changed, 28 insertions(+), 3 deletions(-)
> >
> > diff --git a/include/system/iothread.h b/include/system/iothread.h
> > index 21a76bd70d..595abeefbe 100644
> > --- a/include/system/iothread.h
> > +++ b/include/system/iothread.h
> > @@ -47,7 +47,8 @@ DECLARE_INSTANCE_CHECKER(IOThread, IOTHREAD,
> >
> >  char *iothread_get_id(IOThread *iothread);
> >  IOThread *iothread_by_id(const char *id);
> > -AioContext *iothread_get_aio_context(IOThread *iothread);
> > +AioContext *iothread_get_aio_context(IOThread *iothread, const char *holder);
> > +void iothread_put_aio_context(IOThread *iothread, const char *holder);
> >  GMainContext *iothread_get_g_main_context(IOThread *iothread);
> >
> >  /*
> > diff --git a/iothread.c b/iothread.c
> > index 80a8cf4b32..da98fbb9ad 100644
> > --- a/iothread.c
> > +++ b/iothread.c
> > @@ -172,7 +172,8 @@ static void iothread_init_gcontext(IOThread *iothread, const char *thread_name)
> >      g_autofree char *name = g_strdup_printf("%s aio-context", thread_name);
> >
> >      iothread->worker_context = g_main_context_new();
> > -    source = aio_get_g_source(iothread_get_aio_context(iothread));
> > +    /* No need setup itself as the init holder */
> > +    source = aio_get_g_source(iothread_get_aio_context(iothread, NULL));
>
> This is cleaner:
>
>   source = aio_get_g_source(iothread->ctx);
>
> That way iothread_get_aio_context() doesn't need a special case for
> NULL.

OK for here. But according to the following patch we try to use the
NULL to handle
problems that cannot be easily solved using ref/unref or get/put.
For example the patch 7 and 9.  We can discuss the details in that mail threads.

>
> >      g_source_set_name(source, name);
> >      g_source_attach(source, iothread->worker_context);
> >      g_source_unref(source);
> > @@ -362,11 +363,34 @@ char *iothread_get_id(IOThread *iothread)
> >      return g_strdup(object_get_canonical_path_component(OBJECT(iothread)));
> >  }
> >
> > -AioContext *iothread_get_aio_context(IOThread *iothread)
> > +AioContext *iothread_get_aio_context(IOThread *iothread, const char *holder)
> >  {
> > +    /*
> > +     * In some cases, iothread user need the ctx to clearup other resource.
> > +     * When holder is empty, back to the legacy way.
> > +     */
> > +    if (holder) {
> > +        /*
> > +         * This guarantees that the IOThread and its AioContext remain alive
> > +         * as long as there is a holder.
> > +         */
> > +        object_ref(OBJECT(iothread));
>
> Can this be done in iothread_ref()/iothread_unref()? That would ensure
> that callers always increment the QOM reference count regardless of
> whether the iothread_ref() or iothread_get_aio_context() API is used.

Sure, will move it into iothread_ref()/iothread_unref().

Thanks

Chen

>
> > +
> > +        /* Add holder device path to the list */
> > +        iothread_ref(iothread, holder);
> > +    }
> > +
> >      return iothread->ctx;
> >  }
> >
> > +void iothread_put_aio_context(IOThread *iothread, const char *holder)
> > +{
> > +    object_unref(OBJECT(iothread));
> > +
> > +    /* Delete holder device path from the list */
> > +    iothread_unref(iothread, holder);
> > +}
> > +
> >  static int query_one_iothread(Object *object, void *opaque)
> >  {
> >      IOThreadInfoList ***tail = opaque;
> > --
> > 2.49.0
> >


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 03/13] iothread: tracking iothread users with holder name
  2026-03-09  8:33   ` Stefan Hajnoczi
@ 2026-03-10  9:51     ` Zhang Chen
  0 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-10  9:51 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Mon, Mar 9, 2026 at 4:33 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Thu, Mar 05, 2026 at 10:24:49PM +0800, Zhang Chen wrote:
> > Introduce iothread_get_aio_context() with a 'holder' argument and its
> > counterpart iothread_put_aio_context().
> >
> > Previously, users of an IOThread's AioContext did not explicitly
> > record their identity, making it difficult to debug which devices or
> > subsystems were pinning an IOThread.
> >
> > This patch enhances the reference counting mechanism by:
> > 1. Automatically incrementing the object reference count when a context
> >    is retrieved.
> > 2. Tracking holders by name using iothread_ref() and iothread_unref().
> >
> > In iothread_instance_finalize(), we now retrieve the source name from
> > the GMainContext to correctly unref the initial internal holder.
> >
> > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > ---
> >  include/system/iothread.h |  3 ++-
> >  iothread.c                | 28 ++++++++++++++++++++++++++--
> >  2 files changed, 28 insertions(+), 3 deletions(-)
> >
> > diff --git a/include/system/iothread.h b/include/system/iothread.h
> > index 21a76bd70d..595abeefbe 100644
> > --- a/include/system/iothread.h
> > +++ b/include/system/iothread.h
> > @@ -47,7 +47,8 @@ DECLARE_INSTANCE_CHECKER(IOThread, IOTHREAD,
> >
> >  char *iothread_get_id(IOThread *iothread);
> >  IOThread *iothread_by_id(const char *id);
> > -AioContext *iothread_get_aio_context(IOThread *iothread);
> > +AioContext *iothread_get_aio_context(IOThread *iothread, const char *holder);
>
> This commit breaks all iothread_get_aio_context() callers because they
> are not yet converted. The build will fail here.
>
> Every commit must build and pass the tests. This is necessary so that
> git-bisect(1) works and it also makes backporting patches much easier
> when there are no implicit dependencies between commits.
>
> Please structure this patch series in a way that keeps the build and
> tests passing. One way to do this is to give the new API a different
> name like:
>
>   AioContext *iothread_get_aio_context(IOThread *iothread);
> + AioContext *iothread_ref_and_get_aio_context(IOThread *iothread, const char *holder);
>
> That name also makes clear that a reference will be taken on the
> IOThread. You could then require callers to call iothread_unref()
> directly instead of iothread_put_aio_context().
>
> One of the last commits in the patch series could remove the old
> iothread_get_aio_context() after everything has been converted.

Make sense, thanks for detailed instructions. I even considered combining
the later parts into a single patch to solve this problem, but that would be
too difficult for the individual modules to review.

Thanks

Chen


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 04/13] blockdev: Update tracking iothread users with holder name
  2026-03-09  8:15   ` Stefan Hajnoczi
@ 2026-03-10 10:02     ` Zhang Chen
  2026-03-12  5:24       ` Stefan Hajnoczi
  0 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-10 10:02 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Kevin Wolf, armbru, qemu-devel, Dr . David Alan Gilbert,
	Eric Blake, Michael S . Tsirkin

On Mon, Mar 9, 2026 at 4:15 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Thu, Mar 05, 2026 at 10:24:50PM +0800, Zhang Chen wrote:
> > Update the usage of "iothread_get_aio_context()".
> >
> > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > ---
> >  blockdev.c | 9 ++++++++-
> >  1 file changed, 8 insertions(+), 1 deletion(-)
> >
> > diff --git a/blockdev.c b/blockdev.c
> > index 6e86c6262f..01ccf64b3f 100644
> > --- a/blockdev.c
> > +++ b/blockdev.c
> > @@ -3683,7 +3683,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
> >              goto out;
> >          }
> >
> > -        new_context = iothread_get_aio_context(obj);
> > +        char *path = object_get_canonical_path(OBJECT(bs));
>
> CCing Kevin and Markus in case they have an opinion on this.
>
> BlockDriverState is not a QOM Object so using OBJECT(bs) is undefined
> behavior and may crash.
>
> node_name is unique across block driver graph nodes and could be used.
> Unfortunately it's not connected to the QOM Object hierarchy. Maybe it's
> best to build a holder name that is an invalid QOM path so there can be
> no collisions between QOM paths and block driver graph nodes.
>
>   g_autofree char *holder = g_strdup_printf("BlockDriverState %s", node_name);
>
> (A cleaner long-term solution would be making BlockDriverStates QOM
> Objects so they have a proper path.)

If no other comments, it's OK for me. This issue like I mentioned in
patch 7 and 9.

>
> > +        /*
> > +         * For this qmp case, hard to find the point put aio context ???
> > +         * It looks like a pairing function is needed:
> > +         * qmp_x_blockdev_del_iothread()
> > +         */
> > +        new_context = iothread_get_aio_context(obj, path);
> > +        g_free(path);
>
> Who calls iothread_put_aio_context()?

As the comments I wrote:
> > +        /*
> > +         * For this qmp case, hard to find the point put aio context ???
> > +         * It looks like a pairing function is needed:
> > +         * qmp_x_blockdev_del_iothread()
> > +         */

Please Kevin and Markus help to see if we need to add new function
'qmp_x_blockdev_del_iothread()'?
The iothread_put_aio_context() will be called in it.

Thanks
Chen


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 05/13] virtio-vq-mapping: track iothread-vq-mapping references using device path
  2026-03-09  8:21   ` Stefan Hajnoczi
@ 2026-03-10 10:03     ` Zhang Chen
  0 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-10 10:03 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Mon, Mar 9, 2026 at 4:21 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Thu, Mar 05, 2026 at 10:24:51PM +0800, Zhang Chen wrote:
> > Replace raw object_ref/unref calls with iothread_get/put_aio_context
> > in iothread-vq-mapping. This allows tracking IOThread users via
> > the device's canonical QOM path, improving lifecycle traceability
> > for virtio-blk and virtio-scsi devices.
> >
> > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > ---
> >  hw/block/virtio-blk.c                   | 10 +++++++++-
> >  hw/scsi/virtio-scsi-dataplane.c         | 11 +++++++++--
> >  hw/virtio/iothread-vq-mapping.c         | 11 +++++------
> >  include/hw/virtio/iothread-vq-mapping.h |  6 +++++-
> >  4 files changed, 28 insertions(+), 10 deletions(-)
> >
> > diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
> > index ddf0e9ee53..b6f74df68f 100644
> > --- a/hw/block/virtio-blk.c
> > +++ b/hw/block/virtio-blk.c
> > @@ -1453,14 +1453,19 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
> >      s->vq_aio_context = g_new(AioContext *, conf->num_queues);
> >
> >      if (conf->iothread_vq_mapping_list) {
> > +        char *path = object_get_canonical_path(OBJECT(vdev));
>
> The g_free() calls can be avoided with:
>
>   g_autofree char *path = ...;
>
> The same applies throughout the rest of this patch series.

Make sense~ I will fix it in next version.

Thanks
Chen


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 06/13] virtio: use iothread_get/put_aio_context for thread pinning
  2026-03-09  8:27   ` Stefan Hajnoczi
@ 2026-03-10 10:07     ` Zhang Chen
  0 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-10 10:07 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Mon, Mar 9, 2026 at 4:27 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Thu, Mar 05, 2026 at 10:24:52PM +0800, Zhang Chen wrote:
> > Refactor virtio-blk and virtio-scsi to use the new iothread_get/put
> > APIs for AioContext management. This ensures IOThread references
> > are tracked via the device's canonical QOM path.
> >
> > Summary of changes:
> > - Lift 'path' scope to cover both vq_mapping and single iothread cases.
> > - Replace raw object_ref/unref with iothread_get/put_aio_context.
> > - Ensure consistent memory cleanup of the QOM path string.
> >
> > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > ---
> >  hw/block/virtio-blk.c           | 17 ++++++-----------
> >  hw/scsi/virtio-scsi-dataplane.c | 18 +++++++-----------
> >  2 files changed, 13 insertions(+), 22 deletions(-)
> >
> > diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
> > index b6f74df68f..94a70c6212 100644
> > --- a/hw/block/virtio-blk.c
> > +++ b/hw/block/virtio-blk.c
> > @@ -1429,6 +1429,7 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
> >      VirtIOBlkConf *conf = &s->conf;
> >      BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
> >      VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
> > +    char *path = object_get_canonical_path(OBJECT(vdev));
> >
> >      if (conf->iothread && conf->iothread_vq_mapping_list) {
> >          error_setg(errp,
>
> There is a memory leak when this returns false. Please use g_autofree
> when declaring temporary string variables instead of calling g_free()
> manually.

Sure.

Thanks
Chen


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 07/13] net/colo: track IOThread references using path-based holder
  2026-03-09  8:44   ` Stefan Hajnoczi
@ 2026-03-10 10:15     ` Zhang Chen
  2026-03-12  5:36       ` Stefan Hajnoczi
  0 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-10 10:15 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Mon, Mar 9, 2026 at 4:44 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Thu, Mar 05, 2026 at 10:24:53PM +0800, Zhang Chen wrote:
> > @@ -1434,11 +1436,17 @@ static void colo_compare_finalize(Object *obj)
> >
> >      qemu_bh_delete(s->event_bh);
> >
> > -    AioContext *ctx = iothread_get_aio_context(s->iothread);
> > +    /*
> > +     * Use the device's canonical path as the holder ID to track IOThread
> > +     * usage and ensure the AioContext remains valid during the device's
> > +     * lifecycle.
> > +     */
> > +    AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);
>
> Should NULL be path?
>

Yes, here code in the finalize function need to waiting for the
coroutine to complete processing
Or do you have any other suggestion?

Thanks
Chen

> >      AIO_WAIT_WHILE(ctx, !s->out_sendco.done);
> >      if (s->notify_dev) {
> >          AIO_WAIT_WHILE(ctx, !s->notify_sendco.done);
> >      }
> > +    iothread_put_aio_context(s->iothread, path);


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 09/13] monitor: Update tracking iothread users with holder name
  2026-03-09  8:56   ` Stefan Hajnoczi
@ 2026-03-10 10:24     ` Zhang Chen
  0 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-10 10:24 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Mon, Mar 9, 2026 at 4:57 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Thu, Mar 05, 2026 at 10:24:55PM +0800, Zhang Chen wrote:
> > Because of monitor create own iothread, and use the
> > iothread suspend/resume with bh handler and chardev handler.
> > The QOM lifecycle is hard to track. Update the legacy usage
> > of "iothread_get_aio_context()" and "iothread_put_aio_context()".
>
> As mentioned in the prevoius patch, this feature needs to be implemented
> fully in order to merged. If only half of it works, then it's not a
> feature that users can rely on.

Sure, I will try to implement it as the original iothread get/put design.

Hi Markus and Dave,  do you have any suggestion for how to track the
monitor iothread QOM lifecycle?
Need to do iothread_get/put each time when monitor_suspend/resume ?

Thanks
Chen

>
> Stefan


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 04/13] blockdev: Update tracking iothread users with holder name
  2026-03-10 10:02     ` Zhang Chen
@ 2026-03-12  5:24       ` Stefan Hajnoczi
  2026-03-12  7:05         ` Zhang Chen
  2026-03-12  9:16         ` Markus Armbruster
  0 siblings, 2 replies; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-12  5:24 UTC (permalink / raw)
  To: Zhang Chen
  Cc: Kevin Wolf, armbru, qemu-devel, Dr . David Alan Gilbert,
	Eric Blake, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 2090 bytes --]

On Tue, Mar 10, 2026 at 06:02:54PM +0800, Zhang Chen wrote:
> On Mon, Mar 9, 2026 at 4:15 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> >
> > On Thu, Mar 05, 2026 at 10:24:50PM +0800, Zhang Chen wrote:
> > > Update the usage of "iothread_get_aio_context()".
> > >
> > > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > > ---
> > >  blockdev.c | 9 ++++++++-
> > >  1 file changed, 8 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/blockdev.c b/blockdev.c
> > > index 6e86c6262f..01ccf64b3f 100644
> > > --- a/blockdev.c
> > > +++ b/blockdev.c
> > > @@ -3683,7 +3683,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
> > >              goto out;
> > >          }
> > >
> > > -        new_context = iothread_get_aio_context(obj);
> > > +        char *path = object_get_canonical_path(OBJECT(bs));
> >
> > CCing Kevin and Markus in case they have an opinion on this.
> >
> > BlockDriverState is not a QOM Object so using OBJECT(bs) is undefined
> > behavior and may crash.
> >
> > node_name is unique across block driver graph nodes and could be used.
> > Unfortunately it's not connected to the QOM Object hierarchy. Maybe it's
> > best to build a holder name that is an invalid QOM path so there can be
> > no collisions between QOM paths and block driver graph nodes.
> >
> >   g_autofree char *holder = g_strdup_printf("BlockDriverState %s", node_name);
> >
> > (A cleaner long-term solution would be making BlockDriverStates QOM
> > Objects so they have a proper path.)
> 
> If no other comments, it's OK for me. This issue like I mentioned in
> patch 7 and 9.

A thought about the QAPI interface:

QAPI expresses as much information in the schema as possible, so I think
the right approach would be a {'union': 'IOThreadHolder',
'discriminator': 'type', ...} that supports at least "qom" and
"block-node". That way there are proper types to encode QOM Object paths
vs block node-names. Let's avoid having a single string value that takes
on different meaning depending on the type of holder.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 07/13] net/colo: track IOThread references using path-based holder
  2026-03-10 10:15     ` Zhang Chen
@ 2026-03-12  5:36       ` Stefan Hajnoczi
  2026-03-12  6:31         ` Zhang Chen
  0 siblings, 1 reply; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-12  5:36 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 1221 bytes --]

On Tue, Mar 10, 2026 at 06:15:16PM +0800, Zhang Chen wrote:
> On Mon, Mar 9, 2026 at 4:44 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> >
> > On Thu, Mar 05, 2026 at 10:24:53PM +0800, Zhang Chen wrote:
> > > @@ -1434,11 +1436,17 @@ static void colo_compare_finalize(Object *obj)
> > >
> > >      qemu_bh_delete(s->event_bh);
> > >
> > > -    AioContext *ctx = iothread_get_aio_context(s->iothread);
> > > +    /*
> > > +     * Use the device's canonical path as the holder ID to track IOThread
> > > +     * usage and ensure the AioContext remains valid during the device's
> > > +     * lifecycle.
> > > +     */
> > > +    AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);
> >
> > Should NULL be path?
> >
> 
> Yes, here code in the finalize function need to waiting for the
> coroutine to complete processing
> Or do you have any other suggestion?

I'm not sure I understand. My question was about:

  AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);
                                                          ^^^^

NULL is passed as the argument but this function has a 'path' local
variable. The path variable should be passed instead of NULL.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 07/13] net/colo: track IOThread references using path-based holder
  2026-03-12  5:36       ` Stefan Hajnoczi
@ 2026-03-12  6:31         ` Zhang Chen
  2026-03-12  7:36           ` Stefan Hajnoczi
  0 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-12  6:31 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Thu, Mar 12, 2026 at 1:36 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Tue, Mar 10, 2026 at 06:15:16PM +0800, Zhang Chen wrote:
> > On Mon, Mar 9, 2026 at 4:44 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> > >
> > > On Thu, Mar 05, 2026 at 10:24:53PM +0800, Zhang Chen wrote:
> > > > @@ -1434,11 +1436,17 @@ static void colo_compare_finalize(Object *obj)
> > > >
> > > >      qemu_bh_delete(s->event_bh);
> > > >
> > > > -    AioContext *ctx = iothread_get_aio_context(s->iothread);
> > > > +    /*
> > > > +     * Use the device's canonical path as the holder ID to track IOThread
> > > > +     * usage and ensure the AioContext remains valid during the device's
> > > > +     * lifecycle.
> > > > +     */
> > > > +    AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);
> > >
> > > Should NULL be path?
> > >
> >
> > Yes, here code in the finalize function need to waiting for the
> > coroutine to complete processing
> > Or do you have any other suggestion?
>
> I'm not sure I understand. My question was about:
>
>   AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);
>                                                           ^^^^
>
> NULL is passed as the argument but this function has a 'path' local
> variable. The path variable should be passed instead of NULL.

Let me explain in detail.
colo-compare have two real holders for the iothread,
1. colo_compare_timer_init.
2. worker_context for chardev handlers.

I try to make them use one pair of iothread_get/put.

The real iothread_get with "path" is here:
 static void colo_compare_iothread(CompareState *s)
 {
-    AioContext *ctx = iothread_get_aio_context(s->iothread);
-    object_ref(OBJECT(s->iothread));
+    char *path = object_get_canonical_path(OBJECT(s));
+    AioContext *ctx = iothread_get_aio_context(s->iothread, path);
+

The real pairing iothread_put with the "path" is here:
     AIO_WAIT_WHILE(ctx, !s->out_sendco.done);
     if (s->notify_dev) {
         AIO_WAIT_WHILE(ctx, !s->notify_sendco.done);
     }
+    iothread_put_aio_context(s->iothread, path);

     /* Release all unhandled packets after compare thead exited */
     g_queue_foreach(&s->conn_list, colo_flush_packets, s);

Thanks
Chen



>
> Stefan


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 04/13] blockdev: Update tracking iothread users with holder name
  2026-03-12  5:24       ` Stefan Hajnoczi
@ 2026-03-12  7:05         ` Zhang Chen
  2026-03-12  7:44           ` Stefan Hajnoczi
  2026-03-12  9:16         ` Markus Armbruster
  1 sibling, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-12  7:05 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Kevin Wolf, armbru, qemu-devel, Dr . David Alan Gilbert,
	Eric Blake, Michael S . Tsirkin

On Thu, Mar 12, 2026 at 1:24 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Tue, Mar 10, 2026 at 06:02:54PM +0800, Zhang Chen wrote:
> > On Mon, Mar 9, 2026 at 4:15 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> > >
> > > On Thu, Mar 05, 2026 at 10:24:50PM +0800, Zhang Chen wrote:
> > > > Update the usage of "iothread_get_aio_context()".
> > > >
> > > > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > > > ---
> > > >  blockdev.c | 9 ++++++++-
> > > >  1 file changed, 8 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/blockdev.c b/blockdev.c
> > > > index 6e86c6262f..01ccf64b3f 100644
> > > > --- a/blockdev.c
> > > > +++ b/blockdev.c
> > > > @@ -3683,7 +3683,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
> > > >              goto out;
> > > >          }
> > > >
> > > > -        new_context = iothread_get_aio_context(obj);
> > > > +        char *path = object_get_canonical_path(OBJECT(bs));
> > >
> > > CCing Kevin and Markus in case they have an opinion on this.
> > >
> > > BlockDriverState is not a QOM Object so using OBJECT(bs) is undefined
> > > behavior and may crash.
> > >
> > > node_name is unique across block driver graph nodes and could be used.
> > > Unfortunately it's not connected to the QOM Object hierarchy. Maybe it's
> > > best to build a holder name that is an invalid QOM path so there can be
> > > no collisions between QOM paths and block driver graph nodes.
> > >
> > >   g_autofree char *holder = g_strdup_printf("BlockDriverState %s", node_name);
> > >
> > > (A cleaner long-term solution would be making BlockDriverStates QOM
> > > Objects so they have a proper path.)
> >
> > If no other comments, it's OK for me. This issue like I mentioned in
> > patch 7 and 9.
>
> A thought about the QAPI interface:
>
> QAPI expresses as much information in the schema as possible, so I think
> the right approach would be a {'union': 'IOThreadHolder',
> 'discriminator': 'type', ...} that supports at least "qom" and
> "block-node". That way there are proper types to encode QOM Object paths
> vs block node-names. Let's avoid having a single string value that takes
> on different meaning depending on the type of holder.

In the iothread_get side:
The same issue in the patch 8 (the node-name too), the point is
qmp_xxxx_function input is a string.
It's hard to track the QOM Object paths and the iothread. I will try
to fill the gap.

In the iothread_put side:
* It looks like a pairing function is needed:
+         * qmp_x_blockdev_del_iothread()


Patch 8 comments:
+        /*
+         * It seems we cannot find the corresponding resources
+         * (the iothread and path) when deleting them in qmp_block_export_del.
+         * The original method will be retained for the time being.
+         */

# @BlockExportOptions:
#
# Describes a block export, i.e. how single node should be exported on
# an external interface.
#
# @type: Block export type
#
# @id: A unique identifier for the block export (across all export
#     types)
#
# @node-name: The node name of the block node to be exported
#     (since: 5.2)

Thanks
Chen


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 07/13] net/colo: track IOThread references using path-based holder
  2026-03-12  6:31         ` Zhang Chen
@ 2026-03-12  7:36           ` Stefan Hajnoczi
  2026-03-12  8:45             ` Zhang Chen
  0 siblings, 1 reply; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-12  7:36 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 1922 bytes --]

On Thu, Mar 12, 2026 at 02:31:32PM +0800, Zhang Chen wrote:
> On Thu, Mar 12, 2026 at 1:36 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> >
> > On Tue, Mar 10, 2026 at 06:15:16PM +0800, Zhang Chen wrote:
> > > On Mon, Mar 9, 2026 at 4:44 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> > > >
> > > > On Thu, Mar 05, 2026 at 10:24:53PM +0800, Zhang Chen wrote:
> > > > > @@ -1434,11 +1436,17 @@ static void colo_compare_finalize(Object *obj)
> > > > >
> > > > >      qemu_bh_delete(s->event_bh);
> > > > >
> > > > > -    AioContext *ctx = iothread_get_aio_context(s->iothread);
> > > > > +    /*
> > > > > +     * Use the device's canonical path as the holder ID to track IOThread
> > > > > +     * usage and ensure the AioContext remains valid during the device's
> > > > > +     * lifecycle.
> > > > > +     */
> > > > > +    AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);
> > > >
> > > > Should NULL be path?
> > > >
> > >
> > > Yes, here code in the finalize function need to waiting for the
> > > coroutine to complete processing
> > > Or do you have any other suggestion?
> >
> > I'm not sure I understand. My question was about:
> >
> >   AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);
> >                                                           ^^^^
> >
> > NULL is passed as the argument but this function has a 'path' local
> > variable. The path variable should be passed instead of NULL.
> 
> Let me explain in detail.
> colo-compare have two real holders for the iothread,
> 1. colo_compare_timer_init.
> 2. worker_context for chardev handlers.
> 
> I try to make them use one pair of iothread_get/put.

I suggest storing the AioContext pointer in a new CompareState field in
colo_compare_iothread() and and then using it in colo_compare_finalize()
instead of calling iothread_get_aio_context(s->iothread, NULL).

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 04/13] blockdev: Update tracking iothread users with holder name
  2026-03-12  7:05         ` Zhang Chen
@ 2026-03-12  7:44           ` Stefan Hajnoczi
  0 siblings, 0 replies; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-12  7:44 UTC (permalink / raw)
  To: Zhang Chen
  Cc: Kevin Wolf, armbru, qemu-devel, Dr . David Alan Gilbert,
	Eric Blake, Michael S . Tsirkin

[-- Attachment #1: Type: text/plain, Size: 2966 bytes --]

On Thu, Mar 12, 2026 at 03:05:55PM +0800, Zhang Chen wrote:
> On Thu, Mar 12, 2026 at 1:24 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> >
> > On Tue, Mar 10, 2026 at 06:02:54PM +0800, Zhang Chen wrote:
> > > On Mon, Mar 9, 2026 at 4:15 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> > > >
> > > > On Thu, Mar 05, 2026 at 10:24:50PM +0800, Zhang Chen wrote:
> > > > > Update the usage of "iothread_get_aio_context()".
> > > > >
> > > > > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > > > > ---
> > > > >  blockdev.c | 9 ++++++++-
> > > > >  1 file changed, 8 insertions(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/blockdev.c b/blockdev.c
> > > > > index 6e86c6262f..01ccf64b3f 100644
> > > > > --- a/blockdev.c
> > > > > +++ b/blockdev.c
> > > > > @@ -3683,7 +3683,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
> > > > >              goto out;
> > > > >          }
> > > > >
> > > > > -        new_context = iothread_get_aio_context(obj);
> > > > > +        char *path = object_get_canonical_path(OBJECT(bs));
> > > >
> > > > CCing Kevin and Markus in case they have an opinion on this.
> > > >
> > > > BlockDriverState is not a QOM Object so using OBJECT(bs) is undefined
> > > > behavior and may crash.
> > > >
> > > > node_name is unique across block driver graph nodes and could be used.
> > > > Unfortunately it's not connected to the QOM Object hierarchy. Maybe it's
> > > > best to build a holder name that is an invalid QOM path so there can be
> > > > no collisions between QOM paths and block driver graph nodes.
> > > >
> > > >   g_autofree char *holder = g_strdup_printf("BlockDriverState %s", node_name);
> > > >
> > > > (A cleaner long-term solution would be making BlockDriverStates QOM
> > > > Objects so they have a proper path.)
> > >
> > > If no other comments, it's OK for me. This issue like I mentioned in
> > > patch 7 and 9.
> >
> > A thought about the QAPI interface:
> >
> > QAPI expresses as much information in the schema as possible, so I think
> > the right approach would be a {'union': 'IOThreadHolder',
> > 'discriminator': 'type', ...} that supports at least "qom" and
> > "block-node". That way there are proper types to encode QOM Object paths
> > vs block node-names. Let's avoid having a single string value that takes
> > on different meaning depending on the type of holder.
> 
> In the iothread_get side:
> The same issue in the patch 8 (the node-name too), the point is
> qmp_xxxx_function input is a string.
> It's hard to track the QOM Object paths and the iothread. I will try
> to fill the gap.

I'm not sure if we are talking about the same thing, but when the QAPI
schema is modified to use an enum it would also be necessary to modify
the C API:

iothread_get_aio_context() and iothread_put_aio_context() would take a
struct IOThreadHolder instead of a const char *holder argument.

Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 07/13] net/colo: track IOThread references using path-based holder
  2026-03-12  7:36           ` Stefan Hajnoczi
@ 2026-03-12  8:45             ` Zhang Chen
  0 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-12  8:45 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Thu, Mar 12, 2026 at 3:36 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Thu, Mar 12, 2026 at 02:31:32PM +0800, Zhang Chen wrote:
> > On Thu, Mar 12, 2026 at 1:36 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> > >
> > > On Tue, Mar 10, 2026 at 06:15:16PM +0800, Zhang Chen wrote:
> > > > On Mon, Mar 9, 2026 at 4:44 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> > > > >
> > > > > On Thu, Mar 05, 2026 at 10:24:53PM +0800, Zhang Chen wrote:
> > > > > > @@ -1434,11 +1436,17 @@ static void colo_compare_finalize(Object *obj)
> > > > > >
> > > > > >      qemu_bh_delete(s->event_bh);
> > > > > >
> > > > > > -    AioContext *ctx = iothread_get_aio_context(s->iothread);
> > > > > > +    /*
> > > > > > +     * Use the device's canonical path as the holder ID to track IOThread
> > > > > > +     * usage and ensure the AioContext remains valid during the device's
> > > > > > +     * lifecycle.
> > > > > > +     */
> > > > > > +    AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);
> > > > >
> > > > > Should NULL be path?
> > > > >
> > > >
> > > > Yes, here code in the finalize function need to waiting for the
> > > > coroutine to complete processing
> > > > Or do you have any other suggestion?
> > >
> > > I'm not sure I understand. My question was about:
> > >
> > >   AioContext *ctx = iothread_get_aio_context(s->iothread, NULL);
> > >                                                           ^^^^
> > >
> > > NULL is passed as the argument but this function has a 'path' local
> > > variable. The path variable should be passed instead of NULL.
> >
> > Let me explain in detail.
> > colo-compare have two real holders for the iothread,
> > 1. colo_compare_timer_init.
> > 2. worker_context for chardev handlers.
> >
> > I try to make them use one pair of iothread_get/put.
>
> I suggest storing the AioContext pointer in a new CompareState field in
> colo_compare_iothread() and and then using it in colo_compare_finalize()
> instead of calling iothread_get_aio_context(s->iothread, NULL).

OK, it works and fewer misunderstandings.

Thanks
Chen

>
> Stefan


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 04/13] blockdev: Update tracking iothread users with holder name
  2026-03-12  5:24       ` Stefan Hajnoczi
  2026-03-12  7:05         ` Zhang Chen
@ 2026-03-12  9:16         ` Markus Armbruster
  2026-03-17 13:25           ` Zhang Chen
  1 sibling, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2026-03-12  9:16 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Zhang Chen, Kevin Wolf, qemu-devel, Dr . David Alan Gilbert,
	Eric Blake, Michael S . Tsirkin

Context...  we're talking about this command:

    ##
    # @x-blockdev-set-iothread:
    #
    # Move @node and its children into the @iothread.  If @iothread is
    # null then move @node and its children into the main loop.
    #
    # The node must not be attached to a BlockBackend.
    #
    # @node-name: the name of the block driver node
    #
    # @iothread: the name of the IOThread object or null for the main loop
    #
    # @force: true if the node and its children should be moved when a
    #     BlockBackend is already attached
    #
    # Features:
    #
    # @unstable: This command is experimental and intended for test cases
    #     that need control over IOThreads only.
    #
    # Since: 2.12
    #
    # .. qmp-example::
    #    :title: Move a node into an IOThread
    #
    #     -> { "execute": "x-blockdev-set-iothread",
    #          "arguments": { "node-name": "disk1",
    #                         "iothread": "iothread0" } }
    #     <- { "return": {} }
    #
    # .. qmp-example::
    #    :title: Move a node into the main loop
    #
    #     -> { "execute": "x-blockdev-set-iothread",
    #          "arguments": { "node-name": "disk1",
    #                         "iothread": null } }
    #     <- { "return": {} }
    ##
    { 'command': 'x-blockdev-set-iothread',
      'data' : { 'node-name': 'str',
                 'iothread': 'StrOrNull',
                 '*force': 'bool' },
      'features': [ 'unstable' ],
      'allow-preconfig': true }


Stefan Hajnoczi <stefanha@redhat.com> writes:

> On Tue, Mar 10, 2026 at 06:02:54PM +0800, Zhang Chen wrote:
>> On Mon, Mar 9, 2026 at 4:15 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>> >
>> > On Thu, Mar 05, 2026 at 10:24:50PM +0800, Zhang Chen wrote:
>> > > Update the usage of "iothread_get_aio_context()".
>> > >
>> > > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
>> > > ---
>> > >  blockdev.c | 9 ++++++++-
>> > >  1 file changed, 8 insertions(+), 1 deletion(-)
>> > >
>> > > diff --git a/blockdev.c b/blockdev.c
>> > > index 6e86c6262f..01ccf64b3f 100644
>> > > --- a/blockdev.c
>> > > +++ b/blockdev.c
>> > > @@ -3683,7 +3683,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
>> > >              goto out;
>> > >          }
>> > >
>> > > -        new_context = iothread_get_aio_context(obj);
>> > > +        char *path = object_get_canonical_path(OBJECT(bs));
>> >
>> > CCing Kevin and Markus in case they have an opinion on this.
>> >
>> > BlockDriverState is not a QOM Object so using OBJECT(bs) is undefined
>> > behavior and may crash.

Yes.

>> > node_name is unique across block driver graph nodes and could be used.
>> > Unfortunately it's not connected to the QOM Object hierarchy.

Correct.

>> >                                                               Maybe it's
>> > best to build a holder name that is an invalid QOM path so there can be
>> > no collisions between QOM paths and block driver graph nodes.

I guess you're talking about the values that go into IOThreadInfo member
holders.  From PATCH 13:

    # @holders: The parameter is an array of QOM paths indicating how many
    #     active devices are currently associated with this iothread
    #     (e.g. virtio-blk).  In hotplug scenarios, users can
    #     pre-allocate multiple iothread objects to serve as a persistent
    #     thread pool.  When a device is hot-unplugged, the corresponding
    #     IOThread is released but remains available, allowing subsequent
    #     hot-plugged devices to attach to and reuse the existing thread.
    #     Returns empty if no devices are attached.  (since 11.0)
    #

I further guess you need it to refer to both QOM objects and block
nodes, and you worry about ambiguity.

Ambiguity indeed exists: a block node name can be a valid QOM path.

We could restrict QOM paths to absolute paths.  These start with '/'.
If I remember correctly, node names cannot contain '/'.

Note that canonical paths (returned object_get_canonical_path()) are
absolute.

We ran into a similar design issue in review of Vladimir's "[PATCH v10
4/8] qapi: add blockdev-replace command" not too long ago:

    Subject: Re: [PATCH v10 4/8] qapi: add blockdev-replace command
    Date: Wed, 04 Feb 2026 13:26:35 +0100
    Message-ID: <87wm0sy9s4.fsf@pond.sub.org>

There, the new command needs to refer to QOM object, block node, or
block export.

>> >   g_autofree char *holder = g_strdup_printf("BlockDriverState %s", node_name);
>> >
>> > (A cleaner long-term solution would be making BlockDriverStates QOM
>> > Objects so they have a proper path.)

Yes, but that's a beefy project, isn't it?

>> If no other comments, it's OK for me. This issue like I mentioned in
>> patch 7 and 9.
>
> A thought about the QAPI interface:
>
> QAPI expresses as much information in the schema as possible, so I think
> the right approach would be a {'union': 'IOThreadHolder',
> 'discriminator': 'type', ...} that supports at least "qom" and
> "block-node". That way there are proper types to encode QOM Object paths
> vs block node-names. Let's avoid having a single string value that takes
> on different meaning depending on the type of holder.

This shifts the complexity from semantics to syntax.

Semantics: the member can have multiple meanings, and you have to
examine its value to decide which one applies.  The member's
documentation should specify how to decide.  Say something like "if the
value starts with '/', it's an absolute QOM path, else it's a block node
name".

Syntax: meaning is syntactically obvious.  For instance, union of QOM
path and block node name.

Complex semantics tend to require more complex documentation.

Which choice is better depends on the specific case.  I generally lean
towards syntax.



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 04/13] blockdev: Update tracking iothread users with holder name
  2026-03-12  9:16         ` Markus Armbruster
@ 2026-03-17 13:25           ` Zhang Chen
  2026-03-18  6:19             ` Markus Armbruster
  0 siblings, 1 reply; 44+ messages in thread
From: Zhang Chen @ 2026-03-17 13:25 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Stefan Hajnoczi, Kevin Wolf, qemu-devel, Dr . David Alan Gilbert,
	Eric Blake, Michael S . Tsirkin

On Thu, Mar 12, 2026 at 5:16 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Context...  we're talking about this command:
>
>     ##
>     # @x-blockdev-set-iothread:
>     #
>     # Move @node and its children into the @iothread.  If @iothread is
>     # null then move @node and its children into the main loop.
>     #
>     # The node must not be attached to a BlockBackend.
>     #
>     # @node-name: the name of the block driver node
>     #
>     # @iothread: the name of the IOThread object or null for the main loop
>     #
>     # @force: true if the node and its children should be moved when a
>     #     BlockBackend is already attached
>     #
>     # Features:
>     #
>     # @unstable: This command is experimental and intended for test cases
>     #     that need control over IOThreads only.
>     #
>     # Since: 2.12
>     #
>     # .. qmp-example::
>     #    :title: Move a node into an IOThread
>     #
>     #     -> { "execute": "x-blockdev-set-iothread",
>     #          "arguments": { "node-name": "disk1",
>     #                         "iothread": "iothread0" } }
>     #     <- { "return": {} }
>     #
>     # .. qmp-example::
>     #    :title: Move a node into the main loop
>     #
>     #     -> { "execute": "x-blockdev-set-iothread",
>     #          "arguments": { "node-name": "disk1",
>     #                         "iothread": null } }
>     #     <- { "return": {} }
>     ##
>     { 'command': 'x-blockdev-set-iothread',
>       'data' : { 'node-name': 'str',
>                  'iothread': 'StrOrNull',
>                  '*force': 'bool' },
>       'features': [ 'unstable' ],
>       'allow-preconfig': true }
>
>
> Stefan Hajnoczi <stefanha@redhat.com> writes:
>
> > On Tue, Mar 10, 2026 at 06:02:54PM +0800, Zhang Chen wrote:
> >> On Mon, Mar 9, 2026 at 4:15 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> >> >
> >> > On Thu, Mar 05, 2026 at 10:24:50PM +0800, Zhang Chen wrote:
> >> > > Update the usage of "iothread_get_aio_context()".
> >> > >
> >> > > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> >> > > ---
> >> > >  blockdev.c | 9 ++++++++-
> >> > >  1 file changed, 8 insertions(+), 1 deletion(-)
> >> > >
> >> > > diff --git a/blockdev.c b/blockdev.c
> >> > > index 6e86c6262f..01ccf64b3f 100644
> >> > > --- a/blockdev.c
> >> > > +++ b/blockdev.c
> >> > > @@ -3683,7 +3683,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
> >> > >              goto out;
> >> > >          }
> >> > >
> >> > > -        new_context = iothread_get_aio_context(obj);
> >> > > +        char *path = object_get_canonical_path(OBJECT(bs));
> >> >
> >> > CCing Kevin and Markus in case they have an opinion on this.
> >> >
> >> > BlockDriverState is not a QOM Object so using OBJECT(bs) is undefined
> >> > behavior and may crash.
>
> Yes.
>
> >> > node_name is unique across block driver graph nodes and could be used.
> >> > Unfortunately it's not connected to the QOM Object hierarchy.
>
> Correct.
>
> >> >                                                               Maybe it's
> >> > best to build a holder name that is an invalid QOM path so there can be
> >> > no collisions between QOM paths and block driver graph nodes.
>
> I guess you're talking about the values that go into IOThreadInfo member
> holders.  From PATCH 13:
>
>     # @holders: The parameter is an array of QOM paths indicating how many
>     #     active devices are currently associated with this iothread
>     #     (e.g. virtio-blk).  In hotplug scenarios, users can
>     #     pre-allocate multiple iothread objects to serve as a persistent
>     #     thread pool.  When a device is hot-unplugged, the corresponding
>     #     IOThread is released but remains available, allowing subsequent
>     #     hot-plugged devices to attach to and reuse the existing thread.
>     #     Returns empty if no devices are attached.  (since 11.0)
>     #
>
> I further guess you need it to refer to both QOM objects and block
> nodes, and you worry about ambiguity.
>
> Ambiguity indeed exists: a block node name can be a valid QOM path.
>
> We could restrict QOM paths to absolute paths.  These start with '/'.
> If I remember correctly, node names cannot contain '/'.
>
> Note that canonical paths (returned object_get_canonical_path()) are
> absolute.
>
> We ran into a similar design issue in review of Vladimir's "[PATCH v10
> 4/8] qapi: add blockdev-replace command" not too long ago:
>
>     Subject: Re: [PATCH v10 4/8] qapi: add blockdev-replace command
>     Date: Wed, 04 Feb 2026 13:26:35 +0100
>     Message-ID: <87wm0sy9s4.fsf@pond.sub.org>
>
> There, the new command needs to refer to QOM object, block node, or
> block export.
>
> >> >   g_autofree char *holder = g_strdup_printf("BlockDriverState %s", node_name);
> >> >
> >> > (A cleaner long-term solution would be making BlockDriverStates QOM
> >> > Objects so they have a proper path.)
>
> Yes, but that's a beefy project, isn't it?
>
> >> If no other comments, it's OK for me. This issue like I mentioned in
> >> patch 7 and 9.
> >
> > A thought about the QAPI interface:
> >
> > QAPI expresses as much information in the schema as possible, so I think
> > the right approach would be a {'union': 'IOThreadHolder',
> > 'discriminator': 'type', ...} that supports at least "qom" and
> > "block-node". That way there are proper types to encode QOM Object paths
> > vs block node-names. Let's avoid having a single string value that takes
> > on different meaning depending on the type of holder.
>
> This shifts the complexity from semantics to syntax.
>
> Semantics: the member can have multiple meanings, and you have to
> examine its value to decide which one applies.  The member's
> documentation should specify how to decide.  Say something like "if the
> value starts with '/', it's an absolute QOM path, else it's a block node
> name".
>
> Syntax: meaning is syntactically obvious.  For instance, union of QOM
> path and block node name.
>
> Complex semantics tend to require more complex documentation.
>
> Which choice is better depends on the specific case.  I generally lean
> towards syntax.
>

I agree Markus's suggestion.
Compare with standard QOM path:
/machine/peripheral/blk0/virtio-backend

I will try to implement block nodes path like this:
/machine/blockdriverstate/node-name

And Markus, could you please take a look at patch 13?
I will prepare the V6 after your comments.

Thanks
Chen


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 13/13] qapi: examine IOThread attachment status via query-iothreads
  2026-03-05 14:24 ` [PATCH V5 13/13] qapi: examine IOThread attachment status via query-iothreads Zhang Chen
@ 2026-03-18  6:09   ` Markus Armbruster
  2026-03-18 13:25     ` Zhang Chen
  0 siblings, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2026-03-18  6:09 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Michael S . Tsirkin, Stefan Hajnoczi

Zhang Chen <zhangckid@gmail.com> writes:

> Extend the 'IOThreadInfo' structure to include attachment metrics.
> This allows users to monitor the associated devices by identify them
> by their QOM paths.
>
> New fields added to IOThreadInfo:
> - @holders: A string containing of QOM paths for the attached devices.
>
> These fields are also exposed via the Human Monitor Interface (HMP)
> command 'info iothreads' to assist with manual debugging and
> performance tuning.
>
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>

[...]

> diff --git a/qapi/misc.json b/qapi/misc.json
> index 1f5062df2a..a381074c04 100644
> --- a/qapi/misc.json
> +++ b/qapi/misc.json
> @@ -76,6 +76,15 @@
>  #
>  # @thread-id: ID of the underlying host thread
>  #
> +# @holders: The parameter is an array of QOM paths indicating how many
> +#     active devices are currently associated with this iothread
> +#     (e.g. virtio-blk).  In hotplug scenarios, users can
> +#     pre-allocate multiple iothread objects to serve as a persistent
> +#     thread pool.  When a device is hot-unplugged, the corresponding
> +#     IOThread is released but remains available, allowing subsequent
> +#     hot-plugged devices to attach to and reuse the existing thread.
> +#     Returns empty if no devices are attached.  (since 11.0)

Let me try to polish this a bit:

   # @holders: QOM paths of the devices currently associated with this
   #     iothread.  Users can pre-allocate multiple iothread objects to
   #     serve as a persistent thread pool.  When a device is
   #     hot-unplugged, it is detached from its iothread, but the
   #     iothread remains available, allowing future hot-plugged devices
   #     to attach to it.

> +#
>  # @poll-max-ns: maximum polling time in ns, 0 means polling is
>  #     disabled (since 2.9)
>  #
> @@ -93,6 +102,7 @@
>  { 'struct': 'IOThreadInfo',
>    'data': {'id': 'str',
>             'thread-id': 'int',
> +           'holders': ['str'],
>             'poll-max-ns': 'int',
>             'poll-grow': 'int',
>             'poll-shrink': 'int',
> @@ -118,6 +128,8 @@
>  #              {
>  #                 "id":"iothread0",
>  #                 "thread-id":3134,
> +#                 "holders":["/machine/peripheral/blk1/virtio-backend",
> +#                            "/machine/peripheral/blk0/virtio-backend"],
>  #                 "poll-max-ns":32768,
>  #                 "poll-grow":0,
>  #                 "poll-shrink":0,
> @@ -126,6 +138,7 @@
>  #              {
>  #                 "id":"iothread1",
>  #                 "thread-id":3135,
> +#                 "holders":["/machine/peripheral/blk2/virtio-backend"],
>  #                 "poll-max-ns":32768,
>  #                 "poll-grow":0,
>  #                 "poll-shrink":0,

With a bit of doc polish:
Acked-by: Markus Armbruster <armbru@redhat.com>



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 04/13] blockdev: Update tracking iothread users with holder name
  2026-03-17 13:25           ` Zhang Chen
@ 2026-03-18  6:19             ` Markus Armbruster
  2026-03-18  9:13               ` Stefan Hajnoczi
  0 siblings, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2026-03-18  6:19 UTC (permalink / raw)
  To: Zhang Chen
  Cc: Stefan Hajnoczi, Kevin Wolf, qemu-devel, Dr . David Alan Gilbert,
	Eric Blake, Michael S . Tsirkin, Hanna Czenczek

Zhang Chen <zhangckid@gmail.com> writes:

> On Thu, Mar 12, 2026 at 5:16 PM Markus Armbruster <armbru@redhat.com> wrote:
>>
>> Context...  we're talking about this command:
>>
>>     ##
>>     # @x-blockdev-set-iothread:
>>     #
>>     # Move @node and its children into the @iothread.  If @iothread is
>>     # null then move @node and its children into the main loop.
>>     #
>>     # The node must not be attached to a BlockBackend.
>>     #
>>     # @node-name: the name of the block driver node
>>     #
>>     # @iothread: the name of the IOThread object or null for the main loop
>>     #
>>     # @force: true if the node and its children should be moved when a
>>     #     BlockBackend is already attached
>>     #
>>     # Features:
>>     #
>>     # @unstable: This command is experimental and intended for test cases
>>     #     that need control over IOThreads only.
>>     #
>>     # Since: 2.12
>>     #
>>     # .. qmp-example::
>>     #    :title: Move a node into an IOThread
>>     #
>>     #     -> { "execute": "x-blockdev-set-iothread",
>>     #          "arguments": { "node-name": "disk1",
>>     #                         "iothread": "iothread0" } }
>>     #     <- { "return": {} }
>>     #
>>     # .. qmp-example::
>>     #    :title: Move a node into the main loop
>>     #
>>     #     -> { "execute": "x-blockdev-set-iothread",
>>     #          "arguments": { "node-name": "disk1",
>>     #                         "iothread": null } }
>>     #     <- { "return": {} }
>>     ##
>>     { 'command': 'x-blockdev-set-iothread',
>>       'data' : { 'node-name': 'str',
>>                  'iothread': 'StrOrNull',
>>                  '*force': 'bool' },
>>       'features': [ 'unstable' ],
>>       'allow-preconfig': true }
>>
>>
>> Stefan Hajnoczi <stefanha@redhat.com> writes:
>>
>> > On Tue, Mar 10, 2026 at 06:02:54PM +0800, Zhang Chen wrote:
>> >> On Mon, Mar 9, 2026 at 4:15 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>> >> >
>> >> > On Thu, Mar 05, 2026 at 10:24:50PM +0800, Zhang Chen wrote:
>> >> > > Update the usage of "iothread_get_aio_context()".
>> >> > >
>> >> > > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
>> >> > > ---
>> >> > >  blockdev.c | 9 ++++++++-
>> >> > >  1 file changed, 8 insertions(+), 1 deletion(-)
>> >> > >
>> >> > > diff --git a/blockdev.c b/blockdev.c
>> >> > > index 6e86c6262f..01ccf64b3f 100644
>> >> > > --- a/blockdev.c
>> >> > > +++ b/blockdev.c
>> >> > > @@ -3683,7 +3683,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
>> >> > >              goto out;
>> >> > >          }
>> >> > >
>> >> > > -        new_context = iothread_get_aio_context(obj);
>> >> > > +        char *path = object_get_canonical_path(OBJECT(bs));
>> >> >
>> >> > CCing Kevin and Markus in case they have an opinion on this.
>> >> >
>> >> > BlockDriverState is not a QOM Object so using OBJECT(bs) is undefined
>> >> > behavior and may crash.
>>
>> Yes.
>>
>> >> > node_name is unique across block driver graph nodes and could be used.
>> >> > Unfortunately it's not connected to the QOM Object hierarchy.
>>
>> Correct.
>>
>> >> >                                                               Maybe it's
>> >> > best to build a holder name that is an invalid QOM path so there can be
>> >> > no collisions between QOM paths and block driver graph nodes.
>>
>> I guess you're talking about the values that go into IOThreadInfo member
>> holders.  From PATCH 13:
>>
>>     # @holders: The parameter is an array of QOM paths indicating how many
>>     #     active devices are currently associated with this iothread
>>     #     (e.g. virtio-blk).  In hotplug scenarios, users can
>>     #     pre-allocate multiple iothread objects to serve as a persistent
>>     #     thread pool.  When a device is hot-unplugged, the corresponding
>>     #     IOThread is released but remains available, allowing subsequent
>>     #     hot-plugged devices to attach to and reuse the existing thread.
>>     #     Returns empty if no devices are attached.  (since 11.0)
>>     #
>>
>> I further guess you need it to refer to both QOM objects and block
>> nodes, and you worry about ambiguity.
>>
>> Ambiguity indeed exists: a block node name can be a valid QOM path.
>>
>> We could restrict QOM paths to absolute paths.  These start with '/'.
>> If I remember correctly, node names cannot contain '/'.
>>
>> Note that canonical paths (returned object_get_canonical_path()) are
>> absolute.
>>
>> We ran into a similar design issue in review of Vladimir's "[PATCH v10
>> 4/8] qapi: add blockdev-replace command" not too long ago:
>>
>>     Subject: Re: [PATCH v10 4/8] qapi: add blockdev-replace command
>>     Date: Wed, 04 Feb 2026 13:26:35 +0100
>>     Message-ID: <87wm0sy9s4.fsf@pond.sub.org>
>>
>> There, the new command needs to refer to QOM object, block node, or
>> block export.
>>
>> >> >   g_autofree char *holder = g_strdup_printf("BlockDriverState %s", node_name);
>> >> >
>> >> > (A cleaner long-term solution would be making BlockDriverStates QOM
>> >> > Objects so they have a proper path.)
>>
>> Yes, but that's a beefy project, isn't it?
>>
>> >> If no other comments, it's OK for me. This issue like I mentioned in
>> >> patch 7 and 9.
>> >
>> > A thought about the QAPI interface:
>> >
>> > QAPI expresses as much information in the schema as possible, so I think
>> > the right approach would be a {'union': 'IOThreadHolder',
>> > 'discriminator': 'type', ...} that supports at least "qom" and
>> > "block-node". That way there are proper types to encode QOM Object paths
>> > vs block node-names. Let's avoid having a single string value that takes
>> > on different meaning depending on the type of holder.
>>
>> This shifts the complexity from semantics to syntax.
>>
>> Semantics: the member can have multiple meanings, and you have to
>> examine its value to decide which one applies.  The member's
>> documentation should specify how to decide.  Say something like "if the
>> value starts with '/', it's an absolute QOM path, else it's a block node
>> name".
>>
>> Syntax: meaning is syntactically obvious.  For instance, union of QOM
>> path and block node name.
>>
>> Complex semantics tend to require more complex documentation.
>>
>> Which choice is better depends on the specific case.  I generally lean
>> towards syntax.
>>
>
> I agree Markus's suggestion.
> Compare with standard QOM path:
> /machine/peripheral/blk0/virtio-backend
>
> I will try to implement block nodes path like this:
> /machine/blockdriverstate/node-name

I gather you'd like to try creating QOM objects for block nodes.

First, this should not go into /machine.  We already have /chardevs and
/audiodevs, which suggests something like /blockdevs or /block-nodes.

Second, QOMifying block nodes feels ambitious to me.  Please discuss it
with block maintainers Kevin and Hanna.

> And Markus, could you please take a look at patch 13?
> I will prepare the V6 after your comments.

Done.  It may need an update for changes made here, though.



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 04/13] blockdev: Update tracking iothread users with holder name
  2026-03-18  6:19             ` Markus Armbruster
@ 2026-03-18  9:13               ` Stefan Hajnoczi
  0 siblings, 0 replies; 44+ messages in thread
From: Stefan Hajnoczi @ 2026-03-18  9:13 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Zhang Chen, Stefan Hajnoczi, Kevin Wolf, qemu-devel,
	Dr . David Alan Gilbert, Eric Blake, Michael S . Tsirkin,
	Hanna Czenczek

On Wed, Mar 18, 2026 at 2:20 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Zhang Chen <zhangckid@gmail.com> writes:
>
> > On Thu, Mar 12, 2026 at 5:16 PM Markus Armbruster <armbru@redhat.com> wrote:
> >>
> >> Context...  we're talking about this command:
> >>
> >>     ##
> >>     # @x-blockdev-set-iothread:
> >>     #
> >>     # Move @node and its children into the @iothread.  If @iothread is
> >>     # null then move @node and its children into the main loop.
> >>     #
> >>     # The node must not be attached to a BlockBackend.
> >>     #
> >>     # @node-name: the name of the block driver node
> >>     #
> >>     # @iothread: the name of the IOThread object or null for the main loop
> >>     #
> >>     # @force: true if the node and its children should be moved when a
> >>     #     BlockBackend is already attached
> >>     #
> >>     # Features:
> >>     #
> >>     # @unstable: This command is experimental and intended for test cases
> >>     #     that need control over IOThreads only.
> >>     #
> >>     # Since: 2.12
> >>     #
> >>     # .. qmp-example::
> >>     #    :title: Move a node into an IOThread
> >>     #
> >>     #     -> { "execute": "x-blockdev-set-iothread",
> >>     #          "arguments": { "node-name": "disk1",
> >>     #                         "iothread": "iothread0" } }
> >>     #     <- { "return": {} }
> >>     #
> >>     # .. qmp-example::
> >>     #    :title: Move a node into the main loop
> >>     #
> >>     #     -> { "execute": "x-blockdev-set-iothread",
> >>     #          "arguments": { "node-name": "disk1",
> >>     #                         "iothread": null } }
> >>     #     <- { "return": {} }
> >>     ##
> >>     { 'command': 'x-blockdev-set-iothread',
> >>       'data' : { 'node-name': 'str',
> >>                  'iothread': 'StrOrNull',
> >>                  '*force': 'bool' },
> >>       'features': [ 'unstable' ],
> >>       'allow-preconfig': true }
> >>
> >>
> >> Stefan Hajnoczi <stefanha@redhat.com> writes:
> >>
> >> > On Tue, Mar 10, 2026 at 06:02:54PM +0800, Zhang Chen wrote:
> >> >> On Mon, Mar 9, 2026 at 4:15 PM Stefan Hajnoczi <stefanha@redhat.com> wrote:
> >> >> >
> >> >> > On Thu, Mar 05, 2026 at 10:24:50PM +0800, Zhang Chen wrote:
> >> >> > > Update the usage of "iothread_get_aio_context()".
> >> >> > >
> >> >> > > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> >> >> > > ---
> >> >> > >  blockdev.c | 9 ++++++++-
> >> >> > >  1 file changed, 8 insertions(+), 1 deletion(-)
> >> >> > >
> >> >> > > diff --git a/blockdev.c b/blockdev.c
> >> >> > > index 6e86c6262f..01ccf64b3f 100644
> >> >> > > --- a/blockdev.c
> >> >> > > +++ b/blockdev.c
> >> >> > > @@ -3683,7 +3683,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
> >> >> > >              goto out;
> >> >> > >          }
> >> >> > >
> >> >> > > -        new_context = iothread_get_aio_context(obj);
> >> >> > > +        char *path = object_get_canonical_path(OBJECT(bs));
> >> >> >
> >> >> > CCing Kevin and Markus in case they have an opinion on this.
> >> >> >
> >> >> > BlockDriverState is not a QOM Object so using OBJECT(bs) is undefined
> >> >> > behavior and may crash.
> >>
> >> Yes.
> >>
> >> >> > node_name is unique across block driver graph nodes and could be used.
> >> >> > Unfortunately it's not connected to the QOM Object hierarchy.
> >>
> >> Correct.
> >>
> >> >> >                                                               Maybe it's
> >> >> > best to build a holder name that is an invalid QOM path so there can be
> >> >> > no collisions between QOM paths and block driver graph nodes.
> >>
> >> I guess you're talking about the values that go into IOThreadInfo member
> >> holders.  From PATCH 13:
> >>
> >>     # @holders: The parameter is an array of QOM paths indicating how many
> >>     #     active devices are currently associated with this iothread
> >>     #     (e.g. virtio-blk).  In hotplug scenarios, users can
> >>     #     pre-allocate multiple iothread objects to serve as a persistent
> >>     #     thread pool.  When a device is hot-unplugged, the corresponding
> >>     #     IOThread is released but remains available, allowing subsequent
> >>     #     hot-plugged devices to attach to and reuse the existing thread.
> >>     #     Returns empty if no devices are attached.  (since 11.0)
> >>     #
> >>
> >> I further guess you need it to refer to both QOM objects and block
> >> nodes, and you worry about ambiguity.
> >>
> >> Ambiguity indeed exists: a block node name can be a valid QOM path.
> >>
> >> We could restrict QOM paths to absolute paths.  These start with '/'.
> >> If I remember correctly, node names cannot contain '/'.
> >>
> >> Note that canonical paths (returned object_get_canonical_path()) are
> >> absolute.
> >>
> >> We ran into a similar design issue in review of Vladimir's "[PATCH v10
> >> 4/8] qapi: add blockdev-replace command" not too long ago:
> >>
> >>     Subject: Re: [PATCH v10 4/8] qapi: add blockdev-replace command
> >>     Date: Wed, 04 Feb 2026 13:26:35 +0100
> >>     Message-ID: <87wm0sy9s4.fsf@pond.sub.org>
> >>
> >> There, the new command needs to refer to QOM object, block node, or
> >> block export.
> >>
> >> >> >   g_autofree char *holder = g_strdup_printf("BlockDriverState %s", node_name);
> >> >> >
> >> >> > (A cleaner long-term solution would be making BlockDriverStates QOM
> >> >> > Objects so they have a proper path.)
> >>
> >> Yes, but that's a beefy project, isn't it?
> >>
> >> >> If no other comments, it's OK for me. This issue like I mentioned in
> >> >> patch 7 and 9.
> >> >
> >> > A thought about the QAPI interface:
> >> >
> >> > QAPI expresses as much information in the schema as possible, so I think
> >> > the right approach would be a {'union': 'IOThreadHolder',
> >> > 'discriminator': 'type', ...} that supports at least "qom" and
> >> > "block-node". That way there are proper types to encode QOM Object paths
> >> > vs block node-names. Let's avoid having a single string value that takes
> >> > on different meaning depending on the type of holder.
> >>
> >> This shifts the complexity from semantics to syntax.
> >>
> >> Semantics: the member can have multiple meanings, and you have to
> >> examine its value to decide which one applies.  The member's
> >> documentation should specify how to decide.  Say something like "if the
> >> value starts with '/', it's an absolute QOM path, else it's a block node
> >> name".
> >>
> >> Syntax: meaning is syntactically obvious.  For instance, union of QOM
> >> path and block node name.
> >>
> >> Complex semantics tend to require more complex documentation.
> >>
> >> Which choice is better depends on the specific case.  I generally lean
> >> towards syntax.
> >>
> >
> > I agree Markus's suggestion.
> > Compare with standard QOM path:
> > /machine/peripheral/blk0/virtio-backend
> >
> > I will try to implement block nodes path like this:
> > /machine/blockdriverstate/node-name
>
> I gather you'd like to try creating QOM objects for block nodes.
>
> First, this should not go into /machine.  We already have /chardevs and
> /audiodevs, which suggests something like /blockdevs or /block-nodes.

It may be a lot of work. An alternative I suggested was a holder enum
instead of a string so that QOM paths and block node names can be
separated with no chance of collisions. That approach is more
straightforward to implement and the main drawback I see is that the
enum would become superfluous if block nodes become QOM objects in the
future. That's not terrible.

Stefan


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH V5 13/13] qapi: examine IOThread attachment status via query-iothreads
  2026-03-18  6:09   ` Markus Armbruster
@ 2026-03-18 13:25     ` Zhang Chen
  0 siblings, 0 replies; 44+ messages in thread
From: Zhang Chen @ 2026-03-18 13:25 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Michael S . Tsirkin, Stefan Hajnoczi

On Wed, Mar 18, 2026 at 2:09 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Zhang Chen <zhangckid@gmail.com> writes:
>
> > Extend the 'IOThreadInfo' structure to include attachment metrics.
> > This allows users to monitor the associated devices by identify them
> > by their QOM paths.
> >
> > New fields added to IOThreadInfo:
> > - @holders: A string containing of QOM paths for the attached devices.
> >
> > These fields are also exposed via the Human Monitor Interface (HMP)
> > command 'info iothreads' to assist with manual debugging and
> > performance tuning.
> >
> > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
>
> [...]
>
> > diff --git a/qapi/misc.json b/qapi/misc.json
> > index 1f5062df2a..a381074c04 100644
> > --- a/qapi/misc.json
> > +++ b/qapi/misc.json
> > @@ -76,6 +76,15 @@
> >  #
> >  # @thread-id: ID of the underlying host thread
> >  #
> > +# @holders: The parameter is an array of QOM paths indicating how many
> > +#     active devices are currently associated with this iothread
> > +#     (e.g. virtio-blk).  In hotplug scenarios, users can
> > +#     pre-allocate multiple iothread objects to serve as a persistent
> > +#     thread pool.  When a device is hot-unplugged, the corresponding
> > +#     IOThread is released but remains available, allowing subsequent
> > +#     hot-plugged devices to attach to and reuse the existing thread.
> > +#     Returns empty if no devices are attached.  (since 11.0)
>
> Let me try to polish this a bit:
>
>    # @holders: QOM paths of the devices currently associated with this
>    #     iothread.  Users can pre-allocate multiple iothread objects to
>    #     serve as a persistent thread pool.  When a device is
>    #     hot-unplugged, it is detached from its iothread, but the
>    #     iothread remains available, allowing future hot-plugged devices
>    #     to attach to it.
>
> > +#
> >  # @poll-max-ns: maximum polling time in ns, 0 means polling is
> >  #     disabled (since 2.9)
> >  #
> > @@ -93,6 +102,7 @@
> >  { 'struct': 'IOThreadInfo',
> >    'data': {'id': 'str',
> >             'thread-id': 'int',
> > +           'holders': ['str'],
> >             'poll-max-ns': 'int',
> >             'poll-grow': 'int',
> >             'poll-shrink': 'int',
> > @@ -118,6 +128,8 @@
> >  #              {
> >  #                 "id":"iothread0",
> >  #                 "thread-id":3134,
> > +#                 "holders":["/machine/peripheral/blk1/virtio-backend",
> > +#                            "/machine/peripheral/blk0/virtio-backend"],
> >  #                 "poll-max-ns":32768,
> >  #                 "poll-grow":0,
> >  #                 "poll-shrink":0,
> > @@ -126,6 +138,7 @@
> >  #              {
> >  #                 "id":"iothread1",
> >  #                 "thread-id":3135,
> > +#                 "holders":["/machine/peripheral/blk2/virtio-backend"],
> >  #                 "poll-max-ns":32768,
> >  #                 "poll-grow":0,
> >  #                 "poll-shrink":0,
>
> With a bit of doc polish:
> Acked-by: Markus Armbruster <armbru@redhat.com>

Thank you for your suggestion, I will update it next version.

Thanks
Chen

>


^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2026-03-18 13:29 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-05 14:24 [PATCH V5 00/13] iothread: Support tracking and querying IOThread holders Zhang Chen
2026-03-05 14:24 ` [PATCH V5 01/13] qapi/misc: Fix missed query-iothreads items Zhang Chen
2026-03-05 14:24 ` [PATCH V5 02/13] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
2026-03-09  7:49   ` Stefan Hajnoczi
2026-03-10  9:49     ` Zhang Chen
2026-03-05 14:24 ` [PATCH V5 03/13] iothread: tracking iothread users with holder name Zhang Chen
2026-03-09  8:02   ` Stefan Hajnoczi
2026-03-10  9:49     ` Zhang Chen
2026-03-09  8:33   ` Stefan Hajnoczi
2026-03-10  9:51     ` Zhang Chen
2026-03-05 14:24 ` [PATCH V5 04/13] blockdev: Update " Zhang Chen
2026-03-09  8:15   ` Stefan Hajnoczi
2026-03-10 10:02     ` Zhang Chen
2026-03-12  5:24       ` Stefan Hajnoczi
2026-03-12  7:05         ` Zhang Chen
2026-03-12  7:44           ` Stefan Hajnoczi
2026-03-12  9:16         ` Markus Armbruster
2026-03-17 13:25           ` Zhang Chen
2026-03-18  6:19             ` Markus Armbruster
2026-03-18  9:13               ` Stefan Hajnoczi
2026-03-05 14:24 ` [PATCH V5 05/13] virtio-vq-mapping: track iothread-vq-mapping references using device path Zhang Chen
2026-03-09  8:21   ` Stefan Hajnoczi
2026-03-10 10:03     ` Zhang Chen
2026-03-05 14:24 ` [PATCH V5 06/13] virtio: use iothread_get/put_aio_context for thread pinning Zhang Chen
2026-03-09  8:27   ` Stefan Hajnoczi
2026-03-10 10:07     ` Zhang Chen
2026-03-05 14:24 ` [PATCH V5 07/13] net/colo: track IOThread references using path-based holder Zhang Chen
2026-03-09  8:44   ` Stefan Hajnoczi
2026-03-10 10:15     ` Zhang Chen
2026-03-12  5:36       ` Stefan Hajnoczi
2026-03-12  6:31         ` Zhang Chen
2026-03-12  7:36           ` Stefan Hajnoczi
2026-03-12  8:45             ` Zhang Chen
2026-03-05 14:24 ` [PATCH V5 08/13] block/export: Update tracking iothread users with holder name Zhang Chen
2026-03-09  8:52   ` Stefan Hajnoczi
2026-03-05 14:24 ` [PATCH V5 09/13] monitor: " Zhang Chen
2026-03-09  8:56   ` Stefan Hajnoczi
2026-03-10 10:24     ` Zhang Chen
2026-03-05 14:24 ` [PATCH V5 10/13] virtio-balloon: " Zhang Chen
2026-03-05 14:24 ` [PATCH V5 11/13] vfio-user/proxy: " Zhang Chen
2026-03-05 14:24 ` [PATCH V5 12/13] xen-block: " Zhang Chen
2026-03-05 14:24 ` [PATCH V5 13/13] qapi: examine IOThread attachment status via query-iothreads Zhang Chen
2026-03-18  6:09   ` Markus Armbruster
2026-03-18 13:25     ` Zhang Chen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox