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

### Motivation
Currently, the relationship between IOThreads and the resources using them is
opaque. From a management perspective, it is difficult to determine which 
devices or block exports are currently associated with a specific IOThread 
via QMP or HMP.

This series introduces a "holder" tracking mechanism based on QOM paths to  
significantly improve IOThread observability. In high-performance 
virtualization environments, users often pre-allocate a set of IOThreads to  
serve as a persistent thread pool. During device hotplug/unplug cycles, while 
IOThread objects remain persistent, their attachment state changes dynamically.

By tracking these "holders," administrators can use `query-iothreads` to  
monitor the real-time load distribution of the thread pool and identify which 
specific devices (e.g., virtio-blk, BlockExport) are attached to which thread.

### Technical Overview
* Atomic API Design: Merged iothread_get_aio_context and 
  iothread_ref_and_get_aio_context into a single unified function. The new 
  API enforces a 'holder' parameter, making "retrieval" and "tracking" an  
  atomic operation to prevent reference tracking leaks.
* Subsystem Integration: Fully migrated and adapted core modules including 
  Virtio (blk, vq-mapping, balloon, scsi), Block Layer (export, xen), 
  Network (COLO), VFIO, and the Monitor subsystem.
* Introspection: Expanded the IOThreadInfo QAPI structure to include a 
  'holders' array (list of QOM paths), accessible via both 
  query-iothreads (QMP) and info iothreads (HMP).



### V6 -> V7 Changelog
  - Fixed comments issues in patch 02/14 (Thanks Markus).
  - Removed redundant code.
  - Rebased patches on upstream code.
  - Fixed code conflict with latest patches.
  - Added detailed description in git log and code.
  - Added comments for patch 06/14 assuming the monitor is a QOM object,
    Depends on Daniel Berrangé's RFC patch:
    [PATCH RFC 00/17] monitor: turn QMP and HMP into QOM objects


### V5 -> V6 Changelog
* API Refactoring (Breaking Change):
  - Implement the 'union': 'IoThreadHolder' for combine the QOM objects
    and the block-node.
  - Completely merged iothread_get_aio_context variants.
  - The iothread_get_aio_context function now strictly requires a 'holder' 
    argument to ensure every context retrieval is accounted for.
* Expanded Module Coverage:
  - Added holder tracking for the monitor subsystem (tracking threads used 
    by QMP/HMP).
  - Added support for the virtio-scsi dataplane.
* Code Cleanup:
  - Removed redundant and obsolete API declarations.
  - Standardized the use of object_get_canonical_path() across all callers 
    to provide consistent and unique holder names.
* Bug Fixes:
  - Updated qapi/misc.json documentation to fix missing descriptions in
    query-iothreads items.
* Build System:
  - Improved header inclusion logic to avoid redundant declaration errors
    in unit tests.
* QAPI part:
  - Sorry to drop the "Acked-by: Markus Armbruster <armbru@redhat.com>",
    because this version have lots of changes in the:
    [PATCH V6 13/14]qapi: examine IOThread attachment status via query-iothreads



Zhang Chen (14):
  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
  block/export: track IOThread reference in BlockExport
  monitor: 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
  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
  iothread: simplify API by merging iothread_get_aio_context variants

 block/export/export.c                   |  44 +++++++--
 blockdev.c                              |   6 +-
 hw/block/dataplane/xen-block.c          |  10 ++-
 hw/block/virtio-blk.c                   |  13 ++-
 hw/scsi/virtio-scsi-dataplane.c         |  13 ++-
 hw/vfio-user/proxy.c                    |   7 +-
 hw/virtio/iothread-vq-mapping.c         |  11 ++-
 hw/virtio/virtio-balloon.c              |  14 +--
 include/block/block_int-common.h        |   5 ++
 include/block/export.h                  |   6 ++
 include/hw/virtio/iothread-vq-mapping.h |   6 +-
 include/system/iothread.h               |   9 +-
 iothread.c                              | 113 +++++++++++++++++++++++-
 monitor/hmp-cmds.c                      |  22 +++++
 monitor/monitor-internal.h              |   3 +
 monitor/monitor.c                       |  24 ++++-
 monitor/qmp.c                           |   3 +-
 net/colo-compare.c                      |  18 ++--
 qapi/misc.json                          |  71 ++++++++++++++-
 19 files changed, 342 insertions(+), 56 deletions(-)

-- 
2.49.0



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

* [PATCH V7 01/14] qapi/misc: Fix missed query-iothreads items
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 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 22b7afed9f..c71a5fe657 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -123,11 +123,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] 26+ messages in thread

* [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 01/14] qapi/misc: Fix missed query-iothreads items Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 18:14   ` Stefan Hajnoczi
  2026-05-18 11:44   ` Markus Armbruster
  2026-05-11 14:04 ` [PATCH V7 03/14] iothread: tracking iothread users with holder name Zhang Chen
                   ` (11 subsequent siblings)
  13 siblings, 2 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 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.
- 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.

A later commit will add QMP commands to let management applications
query the attachment status of IOThreads.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 include/system/iothread.h |  5 +++
 iothread.c                | 67 +++++++++++++++++++++++++++++++++++++++
 qapi/misc.json            | 48 ++++++++++++++++++++++++++++
 3 files changed, 120 insertions(+)

diff --git a/include/system/iothread.h b/include/system/iothread.h
index a1ef7696cb..2871b06edc 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -50,6 +50,11 @@ struct IOThread {
     bool stopping;              /* has iothread_stop() been called? */
     bool running;               /* should iothread_run() continue? */
     int thread_id;
+    /*
+     * The list elements are of type IoThreadHolder, which can
+     * represent either a QOM path or a block node name.
+     */
+    GList *holders;
 
     /* AioContext poll parameters */
     int64_t poll_max_ns;
diff --git a/iothread.c b/iothread.c
index 3558535b40..b805e4f97d 100644
--- a/iothread.c
+++ b/iothread.c
@@ -25,6 +25,66 @@
 #include "qemu/rcu.h"
 #include "qemu/main-loop.h"
 
+/*
+ * Add the @holder path to the iothread's tracking list.
+ * The @holder is a QOM path if it starts with '/', else a block node name.
+ */
+static void iothread_ref(IOThread *iothread, const char *holder)
+{
+    IoThreadHolder *h = g_new0(IoThreadHolder, 1);
+
+    assert(holder);
+
+    if (holder[0] == '/') {
+        h->type = IO_THREAD_HOLDER_KIND_QOM_OBJECT;
+        h->u.qom_object.data = g_strdup(holder);
+    } else {
+        h->type = IO_THREAD_HOLDER_KIND_BLOCK_NODE;
+        h->u.block_node.data = g_strdup(holder);
+    }
+
+    iothread->holders = g_list_prepend(iothread->holders, h);
+}
+
+static int iothread_holder_compare(gconstpointer a, gconstpointer b)
+{
+    const IoThreadHolder *holder_node = a;
+    const char *target_name = b;
+    const char *current_name;
+
+    if (holder_node->type == IO_THREAD_HOLDER_KIND_QOM_OBJECT) {
+        current_name = holder_node->u.qom_object.data;
+    } else if (holder_node->type == IO_THREAD_HOLDER_KIND_BLOCK_NODE) {
+        current_name = holder_node->u.block_node.data;
+    } else {
+        /*
+         * This should not happen. If it does, current_name remains
+         * NULL and g_strcmp0 will handle it safely.
+         */
+        current_name = NULL;
+    }
+
+    return g_strcmp0(current_name, target_name);
+}
+
+/*
+ * This function removes the @holder from the @iothread's tracking list.
+ * The @holder string must match the one used previously in iothread_ref().
+ * It is a programming error to call this with a @holder that is not
+ * currently associated with the @iothread.
+ */
+static void iothread_unref(IOThread *iothread, const char *holder)
+{
+    GList *link = g_list_find_custom(iothread->holders, holder,
+                                     (GCompareFunc)iothread_holder_compare);
+
+    assert(link);
+
+    IoThreadHolder *h = (IoThreadHolder *)link->data;
+    qapi_free_IoThreadHolder(h);
+    iothread->holders = g_list_delete_link(iothread->holders, link);
+}
+
 static void *iothread_run(void *opaque)
 {
     IOThread *iothread = opaque;
@@ -108,6 +168,9 @@ static void iothread_instance_finalize(Object *obj)
 
     iothread_stop(iothread);
 
+    /* We don't support finalize without holders */
+    assert(iothread->holders == NULL);
+
     /*
      * Before glib2 2.33.10, there is a glib2 bug that GSource context
      * pointer may not be cleared even if the context has already been
@@ -356,6 +419,10 @@ char *iothread_get_id(IOThread *iothread)
 
 AioContext *iothread_get_aio_context(IOThread *iothread)
 {
+    /* Remove in next patch for build */
+    iothread_ref(iothread, "tmp");
+    iothread_unref(iothread, "tmp");
+
     return iothread->ctx;
 }
 
diff --git a/qapi/misc.json b/qapi/misc.json
index c71a5fe657..5fb7dcfcad 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -67,6 +67,54 @@
 ##
 { 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true }
 
+
+##
+# @IoThreadHolderBlockNode:
+#
+# @data: Block node name.
+#
+# Since: 11.1
+#
+##
+{ 'struct': 'IoThreadHolderBlockNode',
+  'data': { 'data': 'str' } }
+
+##
+# @IoThreadHolderQomObject:
+#
+# @data: Absolute @qom-path.
+#
+# Since: 11.1
+#
+##
+{ 'struct': 'IoThreadHolderQomObject',
+  'data': { 'data': 'str' } }
+
+##
+# @IoThreadHolderKind:
+#
+# @block-node: Block node name.
+# @qom-object: Absolute @qom-path.
+#
+# Since: 11.1
+##
+{ 'enum': 'IoThreadHolderKind',
+  'data': [ 'block-node', 'qom-object' ] }
+
+##
+# @IoThreadHolder:
+#
+# @type: the kind of I/O thread holder.
+#
+# Since: 11.1
+##
+{ 'union': 'IoThreadHolder',
+  'base': { 'type': 'IoThreadHolderKind' },
+  'discriminator': 'type',
+  'data': {
+    'block-node': 'IoThreadHolderBlockNode',
+    'qom-object': 'IoThreadHolderQomObject' } }
+
 ##
 # @IOThreadInfo:
 #
-- 
2.49.0



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

* [PATCH V7 03/14] iothread: tracking iothread users with holder name
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 01/14] qapi/misc: Fix missed query-iothreads items Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 18:54   ` Stefan Hajnoczi
  2026-05-11 14:04 ` [PATCH V7 04/14] blockdev: Update " Zhang Chen
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 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                | 33 +++++++++++++++++++++++++++++----
 2 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/include/system/iothread.h b/include/system/iothread.h
index 2871b06edc..313ef61124 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -70,6 +70,9 @@ 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_ref_and_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 b805e4f97d..5d6f46d286 100644
--- a/iothread.c
+++ b/iothread.c
@@ -44,6 +44,12 @@ static void iothread_ref(IOThread *iothread, const char *holder)
     }
 
     iothread->holders = g_list_prepend(iothread->holders, h);
+
+    /*
+     * This guarantees that the IOThread and its AioContext remain alive
+     * as long as there is a holder.
+     */
+    object_ref(OBJECT(iothread));
 }
 
 static int iothread_holder_compare(gconstpointer a, gconstpointer b)
@@ -83,6 +89,8 @@ static void iothread_unref(IOThread *iothread, const char *holder)
     IoThreadHolder *h = (IoThreadHolder *)link->data;
     qapi_free_IoThreadHolder(h);
     iothread->holders = g_list_delete_link(iothread->holders, link);
+
+    object_unref(OBJECT(iothread));
 }
 
 static void *iothread_run(void *opaque)
@@ -200,7 +208,7 @@ 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));
+    source = aio_get_g_source(iothread->ctx);
     g_source_set_name(source, name);
     g_source_attach(source, iothread->worker_context);
     g_source_unref(source);
@@ -419,13 +427,30 @@ char *iothread_get_id(IOThread *iothread)
 
 AioContext *iothread_get_aio_context(IOThread *iothread)
 {
-    /* Remove in next patch for build */
-    iothread_ref(iothread, "tmp");
-    iothread_unref(iothread, "tmp");
+    return iothread->ctx;
+}
+
+AioContext *iothread_ref_and_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) {
+        /* Add holder device path to the list */
+        iothread_ref(iothread, holder);
+    }
 
     return iothread->ctx;
 }
 
+void iothread_put_aio_context(IOThread *iothread, const char *holder)
+{
+    /* 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] 26+ messages in thread

* [PATCH V7 04/14] blockdev: Update tracking iothread users with holder name
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (2 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 03/14] iothread: tracking iothread users with holder name Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 19:05   ` Stefan Hajnoczi
  2026-05-11 14:04 ` [PATCH V7 05/14] block/export: track IOThread reference in BlockExport Zhang Chen
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Currently, x-blockdev-set-iothread changes the AioContext but does not
track the IOThread object itself within the block layer. This makes it
difficult to manage IOThread reference counting (put/get) during
dynamic context switching.

Introduce an 'iothread' field to BlockDriverState to store the current
assigned IOThread. Update qmp_x_blockdev_set_iothread to perform
proper reference counting using iothread_ref/put when moving nodes
between an IOThread and the main loop.

This ensures 'info iothreads' (holders) accurately reflects the block
device attachment state after dynamic migrations.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 blockdev.c                       | 6 +++++-
 include/block/block_int-common.h | 5 +++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/blockdev.c b/blockdev.c
index 6e86c6262f..6e20579187 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3683,8 +3683,12 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
             goto out;
         }
 
-        new_context = iothread_get_aio_context(obj);
+        new_context = iothread_ref_and_get_aio_context(obj, node_name);
+        bs->iothread = obj;
     } else {
+        if (bs->iothread) {
+            iothread_put_aio_context(bs->iothread, node_name);
+        }
         new_context = qemu_get_aio_context();
     }
 
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
index 147c08155f..6edaf53377 100644
--- a/include/block/block_int-common.h
+++ b/include/block/block_int-common.h
@@ -1101,6 +1101,8 @@ typedef struct BdrvBlockStatusCache {
     int64_t data_end;
 } BdrvBlockStatusCache;
 
+typedef struct IOThread IOThread;
+
 struct BlockDriverState {
     /*
      * Protected by big QEMU lock or read-only after opening.  No special
@@ -1289,6 +1291,9 @@ struct BlockDriverState {
 
     /* array of write pointers' location of each zone in the zoned device. */
     BlockZoneWps *wps;
+
+    /* Track the iothread for detach aio context*/
+    IOThread *iothread;
 };
 
 struct BlockBackendRootState {
-- 
2.49.0



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

* [PATCH V7 05/14] block/export: track IOThread reference in BlockExport
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (3 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 04/14] blockdev: Update " Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 06/14] monitor: Update tracking iothread users with holder name Zhang Chen
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Users currently lack visibility into which block exports
are utilizing specific IOThreads. This patch integrates IOThread
referencing into the BlockExport lifecycle.

- Add iothreads array and holder_name to BlockExport struct.
- Use iothread_ref_and_get_aio_context during export creation.
- Implement proper cleanup in blk_exp_add fail path and blk_exp_delete_bh.
- Support both single and multi-iothread export configurations.

This ensures IOThread 'holders' status correctly reflects active block
exports for better debugging and resource tracking.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 block/export/export.c  | 44 +++++++++++++++++++++++++++++++++---------
 include/block/export.h |  6 ++++++
 2 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/block/export/export.c b/block/export/export.c
index b733f269f3..636633c324 100644
--- a/block/export/export.c
+++ b/block/export/export.c
@@ -15,7 +15,6 @@
 
 #include "block/block.h"
 #include "system/block-backend.h"
-#include "system/iothread.h"
 #include "block/export.h"
 #include "block/fuse.h"
 #include "block/nbd.h"
@@ -85,6 +84,8 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
     AioContext *ctx;
     AioContext **multithread_ctxs = NULL;
     size_t multithread_count = 0;
+    g_autofree IOThread **local_iothreads = NULL;
+    const char *holder_name = NULL;
     uint64_t perm;
     int ret;
 
@@ -139,7 +140,11 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
             goto fail;
         }
 
-        new_ctx = iothread_get_aio_context(iothread);
+        holder_name = bdrv_get_node_name(bs);
+        new_ctx = iothread_ref_and_get_aio_context(iothread, holder_name);
+        multithread_count = 1;
+        local_iothreads = g_new0(IOThread *, 1);
+        local_iothreads[0] = iothread;
 
         /* Ignore errors with fixed-iothread=false */
         set_context_errp = fixed_iothread ? errp : NULL;
@@ -163,8 +168,10 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
             return NULL;
         }
 
+        local_iothreads = g_new0(IOThread *, multithread_count);
         multithread_ctxs = g_new(AioContext *, multithread_count);
         i = 0;
+        holder_name = bdrv_get_node_name(bs);
         for (strList *e = iothread_list; e; e = e->next) {
             IOThread *iothread = iothread_by_id(e->value);
 
@@ -172,7 +179,9 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
                 error_setg(errp, "iothread \"%s\" not found", e->value);
                 goto fail;
             }
-            multithread_ctxs[i++] = iothread_get_aio_context(iothread);
+            local_iothreads[i] = iothread;
+            multithread_ctxs[i++] = iothread_ref_and_get_aio_context(iothread,
+                                                                  holder_name);
         }
         assert(i == multithread_count);
     }
@@ -225,12 +234,15 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
     assert(drv->instance_size >= sizeof(BlockExport));
     exp = g_malloc0(drv->instance_size);
     *exp = (BlockExport) {
-        .drv        = drv,
-        .refcount   = 1,
-        .user_owned = true,
-        .id         = g_strdup(export->id),
-        .ctx        = ctx,
-        .blk        = blk,
+        .drv                  = drv,
+        .refcount             = 1,
+        .user_owned           = true,
+        .id                   = g_strdup(export->id),
+        .ctx                  = ctx,
+        .blk                  = blk,
+        .iothreads            = g_steal_pointer(&local_iothreads),
+        .iothread_count       = multithread_count,
+        .iothread_holder_name = g_strdup(holder_name),
     };
 
     ret = drv->create(exp, export, multithread_ctxs, multithread_count, errp);
@@ -253,6 +265,13 @@ fail:
         g_free(exp->id);
         g_free(exp);
     }
+    if (local_iothreads) {
+        for (size_t j = 0; j < multithread_count; j++) {
+            if (local_iothreads[j]) {
+                iothread_put_aio_context(local_iothreads[j], holder_name);
+            }
+        }
+    }
     g_free(multithread_ctxs);
     return NULL;
 }
@@ -269,6 +288,13 @@ static void blk_exp_delete_bh(void *opaque)
     BlockExport *exp = opaque;
 
     assert(exp->refcount == 0);
+    if (exp->iothreads) {
+        for (size_t i = 0; i < exp->iothread_count; i++) {
+            iothread_put_aio_context(exp->iothreads[i],
+                                     exp->iothread_holder_name);
+        }
+        g_free(exp->iothreads);
+    }
     QLIST_REMOVE(exp, next);
     exp->drv->delete(exp);
     blk_set_dev_ops(exp->blk, NULL, NULL);
diff --git a/include/block/export.h b/include/block/export.h
index ca45da928c..2bb98aae31 100644
--- a/include/block/export.h
+++ b/include/block/export.h
@@ -16,6 +16,7 @@
 
 #include "qapi/qapi-types-block-export.h"
 #include "qemu/queue.h"
+#include "system/iothread.h"
 
 typedef struct BlockExport BlockExport;
 
@@ -89,6 +90,11 @@ struct BlockExport {
 
     /* List entry for block_exports */
     QLIST_ENTRY(BlockExport) next;
+
+    /* The iothreads list for block_exports */
+    IOThread **iothreads;
+    size_t iothread_count;
+    char *iothread_holder_name;
 };
 
 BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp);
-- 
2.49.0



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

* [PATCH V7 06/14] monitor: Update tracking iothread users with holder name
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (4 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 05/14] block/export: track IOThread reference in BlockExport Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-18 11:57   ` Markus Armbruster
  2026-05-11 14:04 ` [PATCH V7 07/14] virtio-vq-mapping: track iothread-vq-mapping references using device path Zhang Chen
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Since the Monitor struct is not a QOM Object, we cannot use
object_get_canonical_path(). Instead, this patch uses the monitor's
name (or a default type-based name) as the holder identifier like QOM
Object.

Because Daniel Berrangé's "[PATCH RFC 00/17] monitor: turn QMP and HMP into
QOM objects" under review, when that series gets merged, the kind monitor
can go away. Current patch assume the monitor already been a QOM Object
in IOthread. Will keep looking at Daniel Berrangé's patch about monitor
QOM in case any future changes needed here.

Cache the AioContext in the Monitor struct to avoid repeated calls to
iothread_get_aio_context() and ensure symmetrical ref/unref during
monitor lifecycle.

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

diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
index a5c4aba306..3e2c141c27 100644
--- a/monitor/monitor-internal.h
+++ b/monitor/monitor-internal.h
@@ -125,6 +125,9 @@ struct Monitor {
     guint out_watch;
     int mux_out;
     int reset_seen;
+
+    /* iothread context */
+    AioContext *ctx;
 };
 
 struct MonitorHMP {
diff --git a/monitor/monitor.c b/monitor/monitor.c
index 00b93ed612..b6efe776d6 100644
--- a/monitor/monitor.c
+++ b/monitor/monitor.c
@@ -529,7 +529,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(mon->ctx);
     }
 
     trace_monitor_suspend(mon, 1);
@@ -564,7 +564,7 @@ void monitor_resume(Monitor *mon)
         AioContext *ctx;
 
         if (mon->use_io_thread) {
-            ctx = iothread_get_aio_context(mon_iothread);
+            ctx = mon->ctx;
         } else {
             ctx = qemu_get_aio_context();
         }
@@ -612,6 +612,18 @@ void monitor_data_init(Monitor *mon, bool is_qmp, bool skip_flush,
 {
     if (use_io_thread && !mon_iothread) {
         monitor_iothread_init();
+        /*
+         * Because of current Monitor is not a QOM Object,
+         * so using OBJECT(mon) is undefined behavior and may crash.
+         * Try using a hard-coded future implementation of the qom path instead.
+         * (Like the name of the "mon_iothread").
+         * long-term solution would be making Monitor QOM, after that change
+         * here to:
+         * g_autofree path = object_get_canonical_path(OBJECT(mon));
+         */
+        g_autofree char *path = g_strdup(is_qmp ? "/monitor/qmp_mon0" :
+                                                  "/monitor/hmp_mon0");
+        mon->ctx = iothread_ref_and_get_aio_context(mon_iothread, path);
     }
     qemu_mutex_init(&mon->mon_lock);
     mon->is_qmp = is_qmp;
@@ -631,6 +643,14 @@ void monitor_data_destroy(Monitor *mon)
     }
     g_string_free(mon->outbuf, true);
     qemu_mutex_destroy(&mon->mon_lock);
+
+    if (mon->ctx && mon_iothread) {
+        g_autofree char *path = g_strdup(monitor_is_qmp(mon) ?
+                                        "/monitor/qmp_mon0" :
+                                        "/monitor/hmp_mon0");
+        iothread_put_aio_context(mon_iothread, path);
+        mon->ctx = NULL;
+    }
 }
 
 void monitor_cleanup(void)
diff --git a/monitor/qmp.c b/monitor/qmp.c
index 687019811f..8e4a775fac 100644
--- a/monitor/qmp.c
+++ b/monitor/qmp.c
@@ -549,7 +549,8 @@ void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
          * 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),
+        Monitor *mon_p = &mon->common;
+        aio_bh_schedule_oneshot(mon_p->ctx,
                                 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] 26+ messages in thread

* [PATCH V7 07/14] virtio-vq-mapping: track iothread-vq-mapping references using device path
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (5 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 06/14] monitor: Update tracking iothread users with holder name Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 08/14] virtio: use iothread_get/put_aio_context for thread pinning Zhang Chen
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 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                   |  8 +++++++-
 hw/scsi/virtio-scsi-dataplane.c         |  9 +++++++--
 hw/virtio/iothread-vq-mapping.c         | 11 +++++------
 include/hw/virtio/iothread-vq-mapping.h |  6 +++++-
 4 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 9cb9f1fb2b..30e9fcf870 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1487,9 +1487,12 @@ 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) {
+        g_autofree 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(s->vq_aio_context);
             s->vq_aio_context = NULL;
@@ -1521,7 +1524,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);
+        g_autofree char *path = object_get_canonical_path(
+                                OBJECT(VIRTIO_DEVICE(s)));
+
+        iothread_vq_mapping_cleanup(conf->iothread_vq_mapping_list, path);
     }
 
     if (conf->iothread) {
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index 95f13fb7c2..26ecefd547 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -65,9 +65,11 @@ 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) {
+        g_autofree 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(s->vq_aio_context);
             s->vq_aio_context = NULL;
             return;
@@ -94,7 +96,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);
+        g_autofree char *path = object_get_canonical_path(
+                                OBJECT(VIRTIO_DEVICE(s)));
+
+        iothread_vq_mapping_cleanup(vs->conf.iothread_vq_mapping_list, path);
     }
 
     if (vs->conf.iothread) {
diff --git a/hw/virtio/iothread-vq-mapping.c b/hw/virtio/iothread-vq-mapping.c
index 55ce62986c..c993281d7f 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_ref_and_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] 26+ messages in thread

* [PATCH V7 08/14] virtio: use iothread_get/put_aio_context for thread pinning
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (6 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 07/14] virtio-vq-mapping: track iothread-vq-mapping references using device path Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 09/14] net/colo: track IOThread references using path-based holder Zhang Chen
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 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           | 16 +++++-----------
 hw/scsi/virtio-scsi-dataplane.c | 15 +++++----------
 2 files changed, 10 insertions(+), 21 deletions(-)

diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 30e9fcf870..e9f1dd5f8d 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1463,6 +1463,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);
+    g_autofree char *path = object_get_canonical_path(OBJECT(vdev));
 
     if (conf->iothread && conf->iothread_vq_mapping_list) {
         error_setg(errp,
@@ -1487,8 +1488,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) {
-        g_autofree 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,
@@ -1499,13 +1498,11 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
             return false;
         }
     } else if (conf->iothread) {
-        AioContext *ctx = iothread_get_aio_context(conf->iothread);
+        AioContext *ctx = iothread_ref_and_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++) {
@@ -1520,21 +1517,18 @@ 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;
+    g_autofree char *path = object_get_canonical_path(OBJECT(VIRTIO_DEVICE(s)));
 
     assert(!s->ioeventfd_started);
 
     if (conf->iothread_vq_mapping_list) {
-        g_autofree char *path = object_get_canonical_path(
-                                OBJECT(VIRTIO_DEVICE(s)));
-
         iothread_vq_mapping_cleanup(conf->iothread_vq_mapping_list, path);
     }
 
     if (conf->iothread) {
-        object_unref(OBJECT(conf->iothread));
+        iothread_put_aio_context(conf->iothread, 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 26ecefd547..cc318162f5 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);
+    g_autofree 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) {
-        g_autofree 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,13 +74,11 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
             return;
         }
     } else if (vs->conf.iothread) {
-        AioContext *ctx = iothread_get_aio_context(vs->conf.iothread);
+        AioContext *ctx = iothread_ref_and_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++) {
@@ -94,16 +91,14 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
 void virtio_scsi_dataplane_cleanup(VirtIOSCSI *s)
 {
     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
+    g_autofree char *path = object_get_canonical_path(OBJECT(VIRTIO_DEVICE(s)));
 
     if (vs->conf.iothread_vq_mapping_list) {
-        g_autofree char *path = object_get_canonical_path(
-                                OBJECT(VIRTIO_DEVICE(s)));
-
         iothread_vq_mapping_cleanup(vs->conf.iothread_vq_mapping_list, path);
     }
 
     if (vs->conf.iothread) {
-        object_unref(OBJECT(vs->conf.iothread));
+        iothread_put_aio_context(vs->conf.iothread, path);
     }
 
     g_free(s->vq_aio_context);
-- 
2.49.0



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

* [PATCH V7 09/14] net/colo: track IOThread references using path-based holder
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (7 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 08/14] virtio: use iothread_get/put_aio_context for thread pinning Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 10/14] virtio-balloon: Update tracking iothread users with holder name Zhang Chen
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 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_ref_and_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.

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 | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/net/colo-compare.c b/net/colo-compare.c
index abc1326b70..8c83da95e8 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -130,6 +130,7 @@ struct CompareState {
     GHashTable *connection_track_table;
 
     IOThread *iothread;
+    AioContext *iothread_ctx;
     GMainContext *worker_context;
     QEMUTimer *packet_check_timer;
 
@@ -926,9 +927,7 @@ void colo_notify_compares_event(void *opaque, int event, Error **errp)
 
 static void colo_compare_timer_init(CompareState *s)
 {
-    AioContext *ctx = iothread_get_aio_context(s->iothread);
-
-    s->packet_check_timer = aio_timer_new(ctx, QEMU_CLOCK_HOST,
+    s->packet_check_timer = aio_timer_new(s->iothread_ctx, QEMU_CLOCK_HOST,
                                 SCALE_MS, check_old_packet_regular,
                                 s);
     timer_mod(s->packet_check_timer, qemu_clock_get_ms(QEMU_CLOCK_HOST) +
@@ -968,8 +967,10 @@ 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));
+    g_autofree char *path = object_get_canonical_path(OBJECT(s));
+    AioContext *ctx = iothread_ref_and_get_aio_context(s->iothread, path);
+
+    s->iothread_ctx = ctx;
     s->worker_context = iothread_get_g_main_context(s->iothread);
 
     qemu_chr_fe_set_handlers(&s->chr_pri_in, compare_chr_can_read,
@@ -1408,6 +1409,7 @@ static void colo_compare_finalize(Object *obj)
 {
     CompareState *s = COLO_COMPARE(obj);
     CompareState *tmp;
+    g_autofree char *path = object_get_canonical_path(OBJECT(s));
 
     qemu_mutex_lock(&colo_compare_mutex);
     QTAILQ_FOREACH(tmp, &net_compares, next) {
@@ -1434,11 +1436,11 @@ static void colo_compare_finalize(Object *obj)
 
     qemu_bh_delete(s->event_bh);
 
-    AioContext *ctx = iothread_get_aio_context(s->iothread);
-    AIO_WAIT_WHILE(ctx, !s->out_sendco.done);
+    AIO_WAIT_WHILE(s->iothread_ctx, !s->out_sendco.done);
     if (s->notify_dev) {
-        AIO_WAIT_WHILE(ctx, !s->notify_sendco.done);
+        AIO_WAIT_WHILE(s->iothread_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);
-- 
2.49.0



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

* [PATCH V7 10/14] virtio-balloon: Update tracking iothread users with holder name
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (8 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 09/14] net/colo: track IOThread references using path-based holder Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 11/14] vfio-user/proxy: " Zhang Chen
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 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 | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 4c5f486ba2..8a4494905a 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -892,14 +892,16 @@ 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)) {
+        g_autofree 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_ref_and_get_aio_context(s->iothread, path),
+                            virtio_ballloon_get_free_page_hints, s,
+                            &dev->mem_reentrancy_guard);
     }
 
     if (virtio_has_feature(s->host_features, VIRTIO_BALLOON_F_REPORTING)) {
@@ -919,9 +921,11 @@ static void virtio_balloon_device_unrealize(DeviceState *dev)
 
     qemu_unregister_resettable(OBJECT(dev));
     if (s->free_page_bh) {
+        g_autofree 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);
     }
     balloon_stats_destroy_timer(s);
-- 
2.49.0



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

* [PATCH V7 11/14] vfio-user/proxy: Update tracking iothread users with holder name
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (9 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 10/14] virtio-balloon: Update tracking iothread users with holder name Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 12/14] xen-block: " Zhang Chen
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 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 | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c
index 314dfd23d8..eebe202c4a 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;
+    g_autofree 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_ref_and_get_aio_context(vfio_user_iothread, path);
     proxy->req_bh = qemu_bh_new(vfio_user_request, proxy);
 
     QTAILQ_INIT(&proxy->outgoing);
@@ -967,6 +969,7 @@ void vfio_user_set_handler(VFIODevice *vbasedev,
 void vfio_user_disconnect(VFIOUserProxy *proxy)
 {
     VFIOUserMsg *r1, *r2;
+    g_autofree char *path = object_get_canonical_path(OBJECT(proxy->ioc));
 
     qemu_mutex_lock(&proxy->lock);
 
@@ -1021,6 +1024,8 @@ 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);
-- 
2.49.0



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

* [PATCH V7 12/14] xen-block: Update tracking iothread users with holder name
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (10 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 11/14] vfio-user/proxy: " Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 13/14] qapi: examine IOThread attachment status via query-iothreads Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 14/14] iothread: simplify API by merging iothread_get_aio_context variants Zhang Chen
  13 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 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..552bd8b039 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) {
+        g_autofree 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_ref_and_get_aio_context(dataplane->iothread,
+                                                          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));
+        g_autofree char *path = object_get_canonical_path(
+                                        OBJECT(dataplane->xendev));
+
+        iothread_put_aio_context(dataplane->iothread, path);
     }
 
     g_free(dataplane);
-- 
2.49.0



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

* [PATCH V7 13/14] qapi: examine IOThread attachment status via query-iothreads
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (11 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 12/14] xen-block: " Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  2026-05-11 14:04 ` [PATCH V7 14/14] iothread: simplify API by merging iothread_get_aio_context variants Zhang Chen
  13 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 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: IoThreadHolder (QOM path or block node name) 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. (Since 11.1)

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         | 22 ++++++++++++++++++++++
 monitor/hmp-cmds.c | 22 ++++++++++++++++++++++
 qapi/misc.json     | 11 +++++++++++
 3 files changed, 55 insertions(+)

diff --git a/iothread.c b/iothread.c
index 5d6f46d286..a78744284b 100644
--- a/iothread.c
+++ b/iothread.c
@@ -24,6 +24,8 @@
 #include "qemu/error-report.h"
 #include "qemu/rcu.h"
 #include "qemu/main-loop.h"
+#include "qapi/clone-visitor.h"
+#include "qapi/qapi-visit-misc.h"
 
 /*
  * Add the @holder path to the iothread's tracking list.
@@ -93,6 +95,25 @@ static void iothread_unref(IOThread *iothread, const char *holder)
     object_unref(OBJECT(iothread));
 }
 
+static IoThreadHolderList *iothread_get_holders_list(IOThread *iothread)
+{
+    IoThreadHolderList *head = NULL;
+    IoThreadHolderList **prev = &head;
+    GList *l;
+
+    for (l = iothread->holders; l; l = l->next) {
+        IoThreadHolder *src = l->data;
+        IoThreadHolderList *entry = g_new0(IoThreadHolderList, 1);
+
+        entry->value = QAPI_CLONE(IoThreadHolder, src);
+
+        *prev = entry;
+        prev = &entry->next;
+    }
+
+    return head;
+}
+
 static void *iothread_run(void *opaque)
 {
     IOThread *iothread = opaque;
@@ -465,6 +486,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 db92d6cb86..be799766d6 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -237,11 +237,33 @@ void hmp_info_iothreads(Monitor *mon, const QDict *qdict)
     IOThreadInfoList *info_list = qmp_query_iothreads(NULL);
     IOThreadInfoList *info;
     IOThreadInfo *value;
+    IoThreadHolderList *h;
 
     for (info = info_list; info; info = info->next) {
         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) {
+            for (h = value->holders; h; h = h->next) {
+                IoThreadHolder *holder = h->value;
+
+                switch (holder->type) {
+                case IO_THREAD_HOLDER_KIND_BLOCK_NODE:
+                    monitor_printf(mon, "[block-node: %s]",
+                                   holder->u.block_node.data);
+                    break;
+                case IO_THREAD_HOLDER_KIND_QOM_OBJECT:
+                    monitor_printf(mon, "[qom: %s]",
+                                   holder->u.qom_object.data);
+                    break;
+                default:
+                    monitor_printf(mon, "[unknown]");
+                    break;
+                }
+            }
+            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 5fb7dcfcad..39ec19362c 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -124,6 +124,13 @@
 #
 # @thread-id: ID of the underlying host thread
 #
+# @holders: IoThreadHolder (QOM path or block node name) 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.  (Since 11.1)
+#
 # @poll-max-ns: maximum polling time in ns, 0 means polling is
 #     disabled (since 2.9)
 #
@@ -146,6 +153,7 @@
 { 'struct': 'IOThreadInfo',
   'data': {'id': 'str',
            'thread-id': 'int',
+           'holders': ['IoThreadHolder'],
            'poll-max-ns': 'int',
            'poll-grow': 'int',
            'poll-shrink': 'int',
@@ -172,6 +180,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,
@@ -180,6 +190,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] 26+ messages in thread

* [PATCH V7 14/14] iothread: simplify API by merging iothread_get_aio_context variants
  2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (12 preceding siblings ...)
  2026-05-11 14:04 ` [PATCH V7 13/14] qapi: examine IOThread attachment status via query-iothreads Zhang Chen
@ 2026-05-11 14:04 ` Zhang Chen
  13 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 14:04 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi
  Cc: Zhang Chen

Simplify the interface by merging iothread_ref_and_get_aio_context()
into iothread_get_aio_context(). The updated function now requires a
'holder' parameter, ensuring that every retrieval of an AioContext for
long-term use is automatically registered in the IOThread's holder list.

Update all callers across block, virtio, scsi, net, and monitor
subsystems to match the new signature. This cleanup reduces code
redundancy and improves the reliability of IOThread introspection.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 block/export/export.c           | 6 +++---
 blockdev.c                      | 2 +-
 hw/block/dataplane/xen-block.c  | 3 +--
 hw/block/virtio-blk.c           | 3 +--
 hw/scsi/virtio-scsi-dataplane.c | 3 +--
 hw/vfio-user/proxy.c            | 2 +-
 hw/virtio/iothread-vq-mapping.c | 2 +-
 hw/virtio/virtio-balloon.c      | 2 +-
 include/system/iothread.h       | 5 ++---
 iothread.c                      | 9 ++-------
 monitor/monitor.c               | 2 +-
 net/colo-compare.c              | 2 +-
 12 files changed, 16 insertions(+), 25 deletions(-)

diff --git a/block/export/export.c b/block/export/export.c
index 636633c324..37bcb4f4d6 100644
--- a/block/export/export.c
+++ b/block/export/export.c
@@ -141,7 +141,7 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
         }
 
         holder_name = bdrv_get_node_name(bs);
-        new_ctx = iothread_ref_and_get_aio_context(iothread, holder_name);
+        new_ctx = iothread_get_aio_context(iothread, holder_name);
         multithread_count = 1;
         local_iothreads = g_new0(IOThread *, 1);
         local_iothreads[0] = iothread;
@@ -180,8 +180,8 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
                 goto fail;
             }
             local_iothreads[i] = iothread;
-            multithread_ctxs[i++] = iothread_ref_and_get_aio_context(iothread,
-                                                                  holder_name);
+            multithread_ctxs[i++] = iothread_get_aio_context(iothread,
+                                                             holder_name);
         }
         assert(i == multithread_count);
     }
diff --git a/blockdev.c b/blockdev.c
index 6e20579187..7eb206a02a 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3683,7 +3683,7 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
             goto out;
         }
 
-        new_context = iothread_ref_and_get_aio_context(obj, node_name);
+        new_context = iothread_get_aio_context(obj, node_name);
         bs->iothread = obj;
     } else {
         if (bs->iothread) {
diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
index 552bd8b039..f5984c86e9 100644
--- a/hw/block/dataplane/xen-block.c
+++ b/hw/block/dataplane/xen-block.c
@@ -624,8 +624,7 @@ XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev,
         g_autofree char *path = object_get_canonical_path(OBJECT(xendev));
 
         dataplane->iothread = iothread;
-        dataplane->ctx = iothread_ref_and_get_aio_context(dataplane->iothread,
-                                                          path);
+        dataplane->ctx = iothread_get_aio_context(dataplane->iothread, path);
     } else {
         dataplane->ctx = qemu_get_aio_context();
     }
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index e9f1dd5f8d..eaefd7d245 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1498,8 +1498,7 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
             return false;
         }
     } else if (conf->iothread) {
-        AioContext *ctx = iothread_ref_and_get_aio_context(conf->iothread,
-                                                           path);
+        AioContext *ctx = iothread_get_aio_context(conf->iothread, path);
         for (unsigned i = 0; i < conf->num_queues; i++) {
             s->vq_aio_context[i] = ctx;
         }
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index cc318162f5..7b37e3d223 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -74,8 +74,7 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
             return;
         }
     } else if (vs->conf.iothread) {
-        AioContext *ctx = iothread_ref_and_get_aio_context(vs->conf.iothread,
-                                                           path);
+        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;
         }
diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c
index eebe202c4a..9a9059290f 100644
--- a/hw/vfio-user/proxy.c
+++ b/hw/vfio-user/proxy.c
@@ -938,7 +938,7 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp)
         vfio_user_iothread = iothread_create("VFIO user", errp);
     }
 
-    proxy->ctx = iothread_ref_and_get_aio_context(vfio_user_iothread, path);
+    proxy->ctx = iothread_get_aio_context(vfio_user_iothread, path);
     proxy->req_bh = qemu_bh_new(vfio_user_request, proxy);
 
     QTAILQ_INIT(&proxy->outgoing);
diff --git a/hw/virtio/iothread-vq-mapping.c b/hw/virtio/iothread-vq-mapping.c
index c993281d7f..7dadc43c44 100644
--- a/hw/virtio/iothread-vq-mapping.c
+++ b/hw/virtio/iothread-vq-mapping.c
@@ -94,7 +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_ref_and_get_aio_context(iothread, holder);
+        AioContext *ctx = iothread_get_aio_context(iothread, holder);
 
         if (node->value->vqs) {
             uint16List *vq;
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 8a4494905a..bd47ba8256 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -899,7 +899,7 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp)
         precopy_add_notifier(&s->free_page_hint_notify);
 
         s->free_page_bh = aio_bh_new_guarded(
-                            iothread_ref_and_get_aio_context(s->iothread, path),
+                            iothread_get_aio_context(s->iothread, path),
                             virtio_ballloon_get_free_page_hints, s,
                             &dev->mem_reentrancy_guard);
     }
diff --git a/include/system/iothread.h b/include/system/iothread.h
index 313ef61124..cdeab63f0a 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -69,9 +69,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_ref_and_get_aio_context(IOThread *iothread,
-                                             const char *holder);
+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 a78744284b..d73de821c2 100644
--- a/iothread.c
+++ b/iothread.c
@@ -446,13 +446,8 @@ char *iothread_get_id(IOThread *iothread)
     return g_strdup(object_get_canonical_path_component(OBJECT(iothread)));
 }
 
-AioContext *iothread_get_aio_context(IOThread *iothread)
-{
-    return iothread->ctx;
-}
-
-AioContext *iothread_ref_and_get_aio_context(IOThread *iothread,
-                                             const char *holder)
+AioContext *iothread_get_aio_context(IOThread *iothread,
+                                     const char *holder)
 {
     /*
      * In some cases, iothread user need the ctx to clearup other resource.
diff --git a/monitor/monitor.c b/monitor/monitor.c
index b6efe776d6..0f9642f11a 100644
--- a/monitor/monitor.c
+++ b/monitor/monitor.c
@@ -623,7 +623,7 @@ void monitor_data_init(Monitor *mon, bool is_qmp, bool skip_flush,
          */
         g_autofree char *path = g_strdup(is_qmp ? "/monitor/qmp_mon0" :
                                                   "/monitor/hmp_mon0");
-        mon->ctx = iothread_ref_and_get_aio_context(mon_iothread, path);
+        mon->ctx = iothread_get_aio_context(mon_iothread, path);
     }
     qemu_mutex_init(&mon->mon_lock);
     mon->is_qmp = is_qmp;
diff --git a/net/colo-compare.c b/net/colo-compare.c
index 8c83da95e8..57d81f8bb0 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -968,7 +968,7 @@ static void colo_compare_handle_event(void *opaque)
 static void colo_compare_iothread(CompareState *s)
 {
     g_autofree char *path = object_get_canonical_path(OBJECT(s));
-    AioContext *ctx = iothread_ref_and_get_aio_context(s->iothread, path);
+    AioContext *ctx = iothread_get_aio_context(s->iothread, path);
 
     s->iothread_ctx = ctx;
     s->worker_context = iothread_get_g_main_context(s->iothread);
-- 
2.49.0



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

* Re: [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices
  2026-05-11 14:04 ` [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
@ 2026-05-11 18:14   ` Stefan Hajnoczi
  2026-05-11 19:15     ` Zhang Chen
  2026-05-18 11:58     ` Markus Armbruster
  2026-05-18 11:44   ` Markus Armbruster
  1 sibling, 2 replies; 26+ messages in thread
From: Stefan Hajnoczi @ 2026-05-11 18:14 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: 6984 bytes --]

On Mon, May 11, 2026 at 10:04:04PM +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.
> - 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.
> 
> A later commit will add QMP commands to let management applications
> query the attachment status of IOThreads.
> 
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  include/system/iothread.h |  5 +++
>  iothread.c                | 67 +++++++++++++++++++++++++++++++++++++++
>  qapi/misc.json            | 48 ++++++++++++++++++++++++++++
>  3 files changed, 120 insertions(+)
> 
> diff --git a/include/system/iothread.h b/include/system/iothread.h
> index a1ef7696cb..2871b06edc 100644
> --- a/include/system/iothread.h
> +++ b/include/system/iothread.h
> @@ -50,6 +50,11 @@ struct IOThread {
>      bool stopping;              /* has iothread_stop() been called? */
>      bool running;               /* should iothread_run() continue? */
>      int thread_id;
> +    /*
> +     * The list elements are of type IoThreadHolder, which can
> +     * represent either a QOM path or a block node name.
> +     */
> +    GList *holders;
>  
>      /* AioContext poll parameters */
>      int64_t poll_max_ns;
> diff --git a/iothread.c b/iothread.c
> index 3558535b40..b805e4f97d 100644
> --- a/iothread.c
> +++ b/iothread.c
> @@ -25,6 +25,66 @@
>  #include "qemu/rcu.h"
>  #include "qemu/main-loop.h"
>  
> +/*
> + * Add the @holder path to the iothread's tracking list.
> + * The @holder is a QOM path if it starts with '/', else a block node name.
> + */

It would be cleaner to use IoThreadHolder in the iothread_ref() and
iothread_unref() APIs instead of a string:

  static void iothread_ref(IOThread *iothread, const IoThreadHolder *holder);
  static void iothread_unref(IOThread *iothread, const IoThreadHolder *holder);

That way the caller already uses IO_THREAD_HOLDER_KIND_QOM_OBJECT or
IO_THREAD_HOLDER_KIND_BLOCK_NODE to clearly distinguish QOM paths from
block node names. No string parsing is necessary.

> +static void iothread_ref(IOThread *iothread, const char *holder)
> +{
> +    IoThreadHolder *h = g_new0(IoThreadHolder, 1);

IoThreadHolder vs IOThread. Name it IOThreadHolder for consistency?

> +
> +    assert(holder);
> +
> +    if (holder[0] == '/') {
> +        h->type = IO_THREAD_HOLDER_KIND_QOM_OBJECT;
> +        h->u.qom_object.data = g_strdup(holder);
> +    } else {
> +        h->type = IO_THREAD_HOLDER_KIND_BLOCK_NODE;
> +        h->u.block_node.data = g_strdup(holder);
> +    }
> +
> +    iothread->holders = g_list_prepend(iothread->holders, h);
> +}
> +
> +static int iothread_holder_compare(gconstpointer a, gconstpointer b)
> +{
> +    const IoThreadHolder *holder_node = a;
> +    const char *target_name = b;
> +    const char *current_name;
> +
> +    if (holder_node->type == IO_THREAD_HOLDER_KIND_QOM_OBJECT) {
> +        current_name = holder_node->u.qom_object.data;
> +    } else if (holder_node->type == IO_THREAD_HOLDER_KIND_BLOCK_NODE) {
> +        current_name = holder_node->u.block_node.data;
> +    } else {
> +        /*
> +         * This should not happen. If it does, current_name remains
> +         * NULL and g_strcmp0 will handle it safely.
> +         */
> +        current_name = NULL;
> +    }
> +
> +    return g_strcmp0(current_name, target_name);
> +}
> +
> +/*
> + * This function removes the @holder from the @iothread's tracking list.
> + * The @holder string must match the one used previously in iothread_ref().
> + * It is a programming error to call this with a @holder that is not
> + * currently associated with the @iothread.
> + */
> +static void iothread_unref(IOThread *iothread, const char *holder)
> +{
> +    GList *link = g_list_find_custom(iothread->holders, holder,
> +                                     (GCompareFunc)iothread_holder_compare);
> +
> +    assert(link);
> +
> +    IoThreadHolder *h = (IoThreadHolder *)link->data;
> +    qapi_free_IoThreadHolder(h);
> +    iothread->holders = g_list_delete_link(iothread->holders, link);
> +}
> +
>  static void *iothread_run(void *opaque)
>  {
>      IOThread *iothread = opaque;
> @@ -108,6 +168,9 @@ static void iothread_instance_finalize(Object *obj)
>  
>      iothread_stop(iothread);
>  
> +    /* We don't support finalize without holders */

The double-negative is confusing and the logic seems incorrect: we don't
support finalize while there are holders. A clearer comment would be:

  /* All holders must have called iothread_unref() */

> +    assert(iothread->holders == NULL);
> +
>      /*
>       * Before glib2 2.33.10, there is a glib2 bug that GSource context
>       * pointer may not be cleared even if the context has already been
> @@ -356,6 +419,10 @@ char *iothread_get_id(IOThread *iothread)
>  
>  AioContext *iothread_get_aio_context(IOThread *iothread)
>  {
> +    /* Remove in next patch for build */
> +    iothread_ref(iothread, "tmp");
> +    iothread_unref(iothread, "tmp");
> +
>      return iothread->ctx;
>  }
>  
> diff --git a/qapi/misc.json b/qapi/misc.json
> index c71a5fe657..5fb7dcfcad 100644
> --- a/qapi/misc.json
> +++ b/qapi/misc.json
> @@ -67,6 +67,54 @@
>  ##
>  { 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true }
>  
> +
> +##
> +# @IoThreadHolderBlockNode:
> +#
> +# @data: Block node name.
> +#
> +# Since: 11.1
> +#
> +##
> +{ 'struct': 'IoThreadHolderBlockNode',
> +  'data': { 'data': 'str' } }
> +
> +##
> +# @IoThreadHolderQomObject:
> +#
> +# @data: Absolute @qom-path.
> +#
> +# Since: 11.1
> +#
> +##
> +{ 'struct': 'IoThreadHolderQomObject',
> +  'data': { 'data': 'str' } }
> +
> +##
> +# @IoThreadHolderKind:
> +#
> +# @block-node: Block node name.
> +# @qom-object: Absolute @qom-path.
> +#
> +# Since: 11.1
> +##
> +{ 'enum': 'IoThreadHolderKind',
> +  'data': [ 'block-node', 'qom-object' ] }
> +
> +##
> +# @IoThreadHolder:
> +#
> +# @type: the kind of I/O thread holder.
> +#
> +# Since: 11.1
> +##
> +{ 'union': 'IoThreadHolder',
> +  'base': { 'type': 'IoThreadHolderKind' },
> +  'discriminator': 'type',
> +  'data': {
> +    'block-node': 'IoThreadHolderBlockNode',
> +    'qom-object': 'IoThreadHolderQomObject' } }
> +
>  ##
>  # @IOThreadInfo:
>  #
> -- 
> 2.49.0
> 

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

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

* Re: [PATCH V7 03/14] iothread: tracking iothread users with holder name
  2026-05-11 14:04 ` [PATCH V7 03/14] iothread: tracking iothread users with holder name Zhang Chen
@ 2026-05-11 18:54   ` Stefan Hajnoczi
  2026-05-11 19:06     ` Zhang Chen
  0 siblings, 1 reply; 26+ messages in thread
From: Stefan Hajnoczi @ 2026-05-11 18:54 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: 4629 bytes --]

On Mon, May 11, 2026 at 10:04:05PM +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                | 33 +++++++++++++++++++++++++++++----
>  2 files changed, 32 insertions(+), 4 deletions(-)
> 
> diff --git a/include/system/iothread.h b/include/system/iothread.h
> index 2871b06edc..313ef61124 100644
> --- a/include/system/iothread.h
> +++ b/include/system/iothread.h
> @@ -70,6 +70,9 @@ 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_ref_and_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 b805e4f97d..5d6f46d286 100644
> --- a/iothread.c
> +++ b/iothread.c
> @@ -44,6 +44,12 @@ static void iothread_ref(IOThread *iothread, const char *holder)
>      }
>  
>      iothread->holders = g_list_prepend(iothread->holders, h);
> +
> +    /*
> +     * This guarantees that the IOThread and its AioContext remain alive
> +     * as long as there is a holder.
> +     */
> +    object_ref(OBJECT(iothread));
>  }
>  
>  static int iothread_holder_compare(gconstpointer a, gconstpointer b)
> @@ -83,6 +89,8 @@ static void iothread_unref(IOThread *iothread, const char *holder)
>      IoThreadHolder *h = (IoThreadHolder *)link->data;
>      qapi_free_IoThreadHolder(h);
>      iothread->holders = g_list_delete_link(iothread->holders, link);
> +
> +    object_unref(OBJECT(iothread));
>  }
>  
>  static void *iothread_run(void *opaque)
> @@ -200,7 +208,7 @@ 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));
> +    source = aio_get_g_source(iothread->ctx);
>      g_source_set_name(source, name);
>      g_source_attach(source, iothread->worker_context);
>      g_source_unref(source);
> @@ -419,13 +427,30 @@ char *iothread_get_id(IOThread *iothread)
>  
>  AioContext *iothread_get_aio_context(IOThread *iothread)
>  {
> -    /* Remove in next patch for build */
> -    iothread_ref(iothread, "tmp");
> -    iothread_unref(iothread, "tmp");
> +    return iothread->ctx;
> +}
> +
> +AioContext *iothread_ref_and_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.

Let's avoid referring to the legacy way. Developers working on the code
after this series has been merged may not know what the legacy way was. 

If an API to fetch iothread->ctx without taking a reference is needed,
then it should probably be a separate API.
iothread_ref_and_get_aio_context() calls should have a matching
iothread_put_aio_context() call. That's not the case when
iothread_ref_and_get_aio_context(iothread, NULL) is allowed.

> +     */
> +    if (holder) {
> +        /* Add holder device path to the list */
> +        iothread_ref(iothread, holder);
> +    }
>  
>      return iothread->ctx;
>  }
>  
> +void iothread_put_aio_context(IOThread *iothread, const char *holder)
> +{
> +    /* 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] 26+ messages in thread

* Re: [PATCH V7 04/14] blockdev: Update tracking iothread users with holder name
  2026-05-11 14:04 ` [PATCH V7 04/14] blockdev: Update " Zhang Chen
@ 2026-05-11 19:05   ` Stefan Hajnoczi
  2026-05-11 19:25     ` Zhang Chen
  0 siblings, 1 reply; 26+ messages in thread
From: Stefan Hajnoczi @ 2026-05-11 19:05 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: 3393 bytes --]

On Mon, May 11, 2026 at 10:04:06PM +0800, Zhang Chen wrote:
> Currently, x-blockdev-set-iothread changes the AioContext but does not
> track the IOThread object itself within the block layer. This makes it
> difficult to manage IOThread reference counting (put/get) during
> dynamic context switching.
> 
> Introduce an 'iothread' field to BlockDriverState to store the current
> assigned IOThread. Update qmp_x_blockdev_set_iothread to perform
> proper reference counting using iothread_ref/put when moving nodes
> between an IOThread and the main loop.
> 
> This ensures 'info iothreads' (holders) accurately reflects the block
> device attachment state after dynamic migrations.
> 
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  blockdev.c                       | 6 +++++-
>  include/block/block_int-common.h | 5 +++++
>  2 files changed, 10 insertions(+), 1 deletion(-)
> 
> diff --git a/blockdev.c b/blockdev.c
> index 6e86c6262f..6e20579187 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -3683,8 +3683,12 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
>              goto out;
>          }
>  
> -        new_context = iothread_get_aio_context(obj);
> +        new_context = iothread_ref_and_get_aio_context(obj, node_name);
> +        bs->iothread = obj;
>      } else {
> +        if (bs->iothread) {
> +            iothread_put_aio_context(bs->iothread, node_name);
> +        }
>          new_context = qemu_get_aio_context();
>      }

This command is the wrong place to associate an IOThread with a
BlockDriverState:
1. Do we need to call iothread_put_aio_context() on the current IOThread
   when x-blockdev-set-iothread is called with a new IOThread?
2. Is the AioContext leaked when x-blockdev-set-iothread iothread=null
   is not called before the BlockDriverState is freed?

Every BlockDriverState would need to get/put the IOThread AioContext in
bdrv_change_aio_context() or related functions.

However, the QEMU block layer has been moving away from having a
per-BlockDriverState AioContext. It is possible to use a
BlockDriverState from any AioContext (including multiple AioContexts at
the same time). I'm in favor of not associating BlockDriverStates with
IOThreads and instead relying on their device owners (e.g. emulated
storage controllers) to be the IOThread holders. That means you could
use iothread->ctx directly or create an
iothread_unsafe_get_aio_context() API to document that this is a
low-level unsafe way of getting the AioContext.

>  
> diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
> index 147c08155f..6edaf53377 100644
> --- a/include/block/block_int-common.h
> +++ b/include/block/block_int-common.h
> @@ -1101,6 +1101,8 @@ typedef struct BdrvBlockStatusCache {
>      int64_t data_end;
>  } BdrvBlockStatusCache;
>  
> +typedef struct IOThread IOThread;
> +
>  struct BlockDriverState {
>      /*
>       * Protected by big QEMU lock or read-only after opening.  No special
> @@ -1289,6 +1291,9 @@ struct BlockDriverState {
>  
>      /* array of write pointers' location of each zone in the zoned device. */
>      BlockZoneWps *wps;
> +
> +    /* Track the iothread for detach aio context*/
> +    IOThread *iothread;
>  };
>  
>  struct BlockBackendRootState {
> -- 
> 2.49.0
> 

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

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

* Re: [PATCH V7 03/14] iothread: tracking iothread users with holder name
  2026-05-11 18:54   ` Stefan Hajnoczi
@ 2026-05-11 19:06     ` Zhang Chen
  0 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 19:06 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Tue, May 12, 2026 at 2:54 AM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Mon, May 11, 2026 at 10:04:05PM +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                | 33 +++++++++++++++++++++++++++++----
> >  2 files changed, 32 insertions(+), 4 deletions(-)
> >
> > diff --git a/include/system/iothread.h b/include/system/iothread.h
> > index 2871b06edc..313ef61124 100644
> > --- a/include/system/iothread.h
> > +++ b/include/system/iothread.h
> > @@ -70,6 +70,9 @@ 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_ref_and_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 b805e4f97d..5d6f46d286 100644
> > --- a/iothread.c
> > +++ b/iothread.c
> > @@ -44,6 +44,12 @@ static void iothread_ref(IOThread *iothread, const char *holder)
> >      }
> >
> >      iothread->holders = g_list_prepend(iothread->holders, h);
> > +
> > +    /*
> > +     * This guarantees that the IOThread and its AioContext remain alive
> > +     * as long as there is a holder.
> > +     */
> > +    object_ref(OBJECT(iothread));
> >  }
> >
> >  static int iothread_holder_compare(gconstpointer a, gconstpointer b)
> > @@ -83,6 +89,8 @@ static void iothread_unref(IOThread *iothread, const char *holder)
> >      IoThreadHolder *h = (IoThreadHolder *)link->data;
> >      qapi_free_IoThreadHolder(h);
> >      iothread->holders = g_list_delete_link(iothread->holders, link);
> > +
> > +    object_unref(OBJECT(iothread));
> >  }
> >
> >  static void *iothread_run(void *opaque)
> > @@ -200,7 +208,7 @@ 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));
> > +    source = aio_get_g_source(iothread->ctx);
> >      g_source_set_name(source, name);
> >      g_source_attach(source, iothread->worker_context);
> >      g_source_unref(source);
> > @@ -419,13 +427,30 @@ char *iothread_get_id(IOThread *iothread)
> >
> >  AioContext *iothread_get_aio_context(IOThread *iothread)
> >  {
> > -    /* Remove in next patch for build */
> > -    iothread_ref(iothread, "tmp");
> > -    iothread_unref(iothread, "tmp");
> > +    return iothread->ctx;
> > +}
> > +
> > +AioContext *iothread_ref_and_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.
>
> Let's avoid referring to the legacy way. Developers working on the code
> after this series has been merged may not know what the legacy way was.
>
> If an API to fetch iothread->ctx without taking a reference is needed,
> then it should probably be a separate API.
> iothread_ref_and_get_aio_context() calls should have a matching
> iothread_put_aio_context() call. That's not the case when
> iothread_ref_and_get_aio_context(iothread, NULL) is allowed.

Sorry, my comments are out of date, for the V7 patch, the legacy
way has been completely abandoned(you can double check it in the patch 14/14).
I will remove this comments in the next version.

Thanks
Chen

>
> > +     */
> > +    if (holder) {
> > +        /* Add holder device path to the list */
> > +        iothread_ref(iothread, holder);
> > +    }
> >
> >      return iothread->ctx;
> >  }
> >
> > +void iothread_put_aio_context(IOThread *iothread, const char *holder)
> > +{
> > +    /* 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] 26+ messages in thread

* Re: [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices
  2026-05-11 18:14   ` Stefan Hajnoczi
@ 2026-05-11 19:15     ` Zhang Chen
  2026-05-18 11:58     ` Markus Armbruster
  1 sibling, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 19:15 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Tue, May 12, 2026 at 2:14 AM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Mon, May 11, 2026 at 10:04:04PM +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.
> > - 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.
> >
> > A later commit will add QMP commands to let management applications
> > query the attachment status of IOThreads.
> >
> > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > ---
> >  include/system/iothread.h |  5 +++
> >  iothread.c                | 67 +++++++++++++++++++++++++++++++++++++++
> >  qapi/misc.json            | 48 ++++++++++++++++++++++++++++
> >  3 files changed, 120 insertions(+)
> >
> > diff --git a/include/system/iothread.h b/include/system/iothread.h
> > index a1ef7696cb..2871b06edc 100644
> > --- a/include/system/iothread.h
> > +++ b/include/system/iothread.h
> > @@ -50,6 +50,11 @@ struct IOThread {
> >      bool stopping;              /* has iothread_stop() been called? */
> >      bool running;               /* should iothread_run() continue? */
> >      int thread_id;
> > +    /*
> > +     * The list elements are of type IoThreadHolder, which can
> > +     * represent either a QOM path or a block node name.
> > +     */
> > +    GList *holders;
> >
> >      /* AioContext poll parameters */
> >      int64_t poll_max_ns;
> > diff --git a/iothread.c b/iothread.c
> > index 3558535b40..b805e4f97d 100644
> > --- a/iothread.c
> > +++ b/iothread.c
> > @@ -25,6 +25,66 @@
> >  #include "qemu/rcu.h"
> >  #include "qemu/main-loop.h"
> >
> > +/*
> > + * Add the @holder path to the iothread's tracking list.
> > + * The @holder is a QOM path if it starts with '/', else a block node name.
> > + */
>
> It would be cleaner to use IoThreadHolder in the iothread_ref() and
> iothread_unref() APIs instead of a string:
>
>   static void iothread_ref(IOThread *iothread, const IoThreadHolder *holder);
>   static void iothread_unref(IOThread *iothread, const IoThreadHolder *holder);
>
> That way the caller already uses IO_THREAD_HOLDER_KIND_QOM_OBJECT or
> IO_THREAD_HOLDER_KIND_BLOCK_NODE to clearly distinguish QOM paths from
> block node names. No string parsing is necessary.
>

Good idea.
Will optimize it in the next version.

> > +static void iothread_ref(IOThread *iothread, const char *holder)
> > +{
> > +    IoThreadHolder *h = g_new0(IoThreadHolder, 1);
>
> IoThreadHolder vs IOThread. Name it IOThreadHolder for consistency?

OK, will change the "IoThreadHolder" to "IOThreadHolder".

>
> > +
> > +    assert(holder);
> > +
> > +    if (holder[0] == '/') {
> > +        h->type = IO_THREAD_HOLDER_KIND_QOM_OBJECT;
> > +        h->u.qom_object.data = g_strdup(holder);
> > +    } else {
> > +        h->type = IO_THREAD_HOLDER_KIND_BLOCK_NODE;
> > +        h->u.block_node.data = g_strdup(holder);
> > +    }
> > +
> > +    iothread->holders = g_list_prepend(iothread->holders, h);
> > +}
> > +
> > +static int iothread_holder_compare(gconstpointer a, gconstpointer b)
> > +{
> > +    const IoThreadHolder *holder_node = a;
> > +    const char *target_name = b;
> > +    const char *current_name;
> > +
> > +    if (holder_node->type == IO_THREAD_HOLDER_KIND_QOM_OBJECT) {
> > +        current_name = holder_node->u.qom_object.data;
> > +    } else if (holder_node->type == IO_THREAD_HOLDER_KIND_BLOCK_NODE) {
> > +        current_name = holder_node->u.block_node.data;
> > +    } else {
> > +        /*
> > +         * This should not happen. If it does, current_name remains
> > +         * NULL and g_strcmp0 will handle it safely.
> > +         */
> > +        current_name = NULL;
> > +    }
> > +
> > +    return g_strcmp0(current_name, target_name);
> > +}
> > +
> > +/*
> > + * This function removes the @holder from the @iothread's tracking list.
> > + * The @holder string must match the one used previously in iothread_ref().
> > + * It is a programming error to call this with a @holder that is not
> > + * currently associated with the @iothread.
> > + */
> > +static void iothread_unref(IOThread *iothread, const char *holder)
> > +{
> > +    GList *link = g_list_find_custom(iothread->holders, holder,
> > +                                     (GCompareFunc)iothread_holder_compare);
> > +
> > +    assert(link);
> > +
> > +    IoThreadHolder *h = (IoThreadHolder *)link->data;
> > +    qapi_free_IoThreadHolder(h);
> > +    iothread->holders = g_list_delete_link(iothread->holders, link);
> > +}
> > +
> >  static void *iothread_run(void *opaque)
> >  {
> >      IOThread *iothread = opaque;
> > @@ -108,6 +168,9 @@ static void iothread_instance_finalize(Object *obj)
> >
> >      iothread_stop(iothread);
> >
> > +    /* We don't support finalize without holders */
>
> The double-negative is confusing and the logic seems incorrect: we don't
> support finalize while there are holders. A clearer comment would be:
>
>   /* All holders must have called iothread_unref() */
>

Sure.

Thanks
Chen

> > +    assert(iothread->holders == NULL);
> > +
> >      /*
> >       * Before glib2 2.33.10, there is a glib2 bug that GSource context
> >       * pointer may not be cleared even if the context has already been
> > @@ -356,6 +419,10 @@ char *iothread_get_id(IOThread *iothread)
> >
> >  AioContext *iothread_get_aio_context(IOThread *iothread)
> >  {
> > +    /* Remove in next patch for build */
> > +    iothread_ref(iothread, "tmp");
> > +    iothread_unref(iothread, "tmp");
> > +
> >      return iothread->ctx;
> >  }
> >
> > diff --git a/qapi/misc.json b/qapi/misc.json
> > index c71a5fe657..5fb7dcfcad 100644
> > --- a/qapi/misc.json
> > +++ b/qapi/misc.json
> > @@ -67,6 +67,54 @@
> >  ##
> >  { 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true }
> >
> > +
> > +##
> > +# @IoThreadHolderBlockNode:
> > +#
> > +# @data: Block node name.
> > +#
> > +# Since: 11.1
> > +#
> > +##
> > +{ 'struct': 'IoThreadHolderBlockNode',
> > +  'data': { 'data': 'str' } }
> > +
> > +##
> > +# @IoThreadHolderQomObject:
> > +#
> > +# @data: Absolute @qom-path.
> > +#
> > +# Since: 11.1
> > +#
> > +##
> > +{ 'struct': 'IoThreadHolderQomObject',
> > +  'data': { 'data': 'str' } }
> > +
> > +##
> > +# @IoThreadHolderKind:
> > +#
> > +# @block-node: Block node name.
> > +# @qom-object: Absolute @qom-path.
> > +#
> > +# Since: 11.1
> > +##
> > +{ 'enum': 'IoThreadHolderKind',
> > +  'data': [ 'block-node', 'qom-object' ] }
> > +
> > +##
> > +# @IoThreadHolder:
> > +#
> > +# @type: the kind of I/O thread holder.
> > +#
> > +# Since: 11.1
> > +##
> > +{ 'union': 'IoThreadHolder',
> > +  'base': { 'type': 'IoThreadHolderKind' },
> > +  'discriminator': 'type',
> > +  'data': {
> > +    'block-node': 'IoThreadHolderBlockNode',
> > +    'qom-object': 'IoThreadHolderQomObject' } }
> > +
> >  ##
> >  # @IOThreadInfo:
> >  #
> > --
> > 2.49.0
> >


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

* Re: [PATCH V7 04/14] blockdev: Update tracking iothread users with holder name
  2026-05-11 19:05   ` Stefan Hajnoczi
@ 2026-05-11 19:25     ` Zhang Chen
  0 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-11 19:25 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin

On Tue, May 12, 2026 at 3:05 AM Stefan Hajnoczi <stefanha@redhat.com> wrote:
>
> On Mon, May 11, 2026 at 10:04:06PM +0800, Zhang Chen wrote:
> > Currently, x-blockdev-set-iothread changes the AioContext but does not
> > track the IOThread object itself within the block layer. This makes it
> > difficult to manage IOThread reference counting (put/get) during
> > dynamic context switching.
> >
> > Introduce an 'iothread' field to BlockDriverState to store the current
> > assigned IOThread. Update qmp_x_blockdev_set_iothread to perform
> > proper reference counting using iothread_ref/put when moving nodes
> > between an IOThread and the main loop.
> >
> > This ensures 'info iothreads' (holders) accurately reflects the block
> > device attachment state after dynamic migrations.
> >
> > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > ---
> >  blockdev.c                       | 6 +++++-
> >  include/block/block_int-common.h | 5 +++++
> >  2 files changed, 10 insertions(+), 1 deletion(-)
> >
> > diff --git a/blockdev.c b/blockdev.c
> > index 6e86c6262f..6e20579187 100644
> > --- a/blockdev.c
> > +++ b/blockdev.c
> > @@ -3683,8 +3683,12 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
> >              goto out;
> >          }
> >
> > -        new_context = iothread_get_aio_context(obj);
> > +        new_context = iothread_ref_and_get_aio_context(obj, node_name);
> > +        bs->iothread = obj;
> >      } else {
> > +        if (bs->iothread) {
> > +            iothread_put_aio_context(bs->iothread, node_name);
> > +        }
> >          new_context = qemu_get_aio_context();
> >      }
>
> This command is the wrong place to associate an IOThread with a
> BlockDriverState:
> 1. Do we need to call iothread_put_aio_context() on the current IOThread
>    when x-blockdev-set-iothread is called with a new IOThread?
> 2. Is the AioContext leaked when x-blockdev-set-iothread iothread=null
>    is not called before the BlockDriverState is freed?
>
> Every BlockDriverState would need to get/put the IOThread AioContext in
> bdrv_change_aio_context() or related functions.
>
> However, the QEMU block layer has been moving away from having a
> per-BlockDriverState AioContext. It is possible to use a
> BlockDriverState from any AioContext (including multiple AioContexts at
> the same time). I'm in favor of not associating BlockDriverStates with
> IOThreads and instead relying on their device owners (e.g. emulated
> storage controllers) to be the IOThread holders. That means you could
> use iothread->ctx directly or create an
> iothread_unsafe_get_aio_context() API to document that this is a
> low-level unsafe way of getting the AioContext.

It's fine for me, I was troubled for a long time by the specific
implementation of this problem.

Thanks
Chen

>
> >
> > diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
> > index 147c08155f..6edaf53377 100644
> > --- a/include/block/block_int-common.h
> > +++ b/include/block/block_int-common.h
> > @@ -1101,6 +1101,8 @@ typedef struct BdrvBlockStatusCache {
> >      int64_t data_end;
> >  } BdrvBlockStatusCache;
> >
> > +typedef struct IOThread IOThread;
> > +
> >  struct BlockDriverState {
> >      /*
> >       * Protected by big QEMU lock or read-only after opening.  No special
> > @@ -1289,6 +1291,9 @@ struct BlockDriverState {
> >
> >      /* array of write pointers' location of each zone in the zoned device. */
> >      BlockZoneWps *wps;
> > +
> > +    /* Track the iothread for detach aio context*/
> > +    IOThread *iothread;
> >  };
> >
> >  struct BlockBackendRootState {
> > --
> > 2.49.0
> >


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

* Re: [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices
  2026-05-11 14:04 ` [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
  2026-05-11 18:14   ` Stefan Hajnoczi
@ 2026-05-18 11:44   ` Markus Armbruster
  1 sibling, 0 replies; 26+ messages in thread
From: Markus Armbruster @ 2026-05-18 11:44 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi

Zhang Chen <zhangckid@gmail.com> writes:

> 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.
> - 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.
>
> A later commit will add QMP commands to let management applications
> query the attachment status of IOThreads.
>
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  include/system/iothread.h |  5 +++
>  iothread.c                | 67 +++++++++++++++++++++++++++++++++++++++
>  qapi/misc.json            | 48 ++++++++++++++++++++++++++++
>  3 files changed, 120 insertions(+)
>
> diff --git a/include/system/iothread.h b/include/system/iothread.h
> index a1ef7696cb..2871b06edc 100644
> --- a/include/system/iothread.h
> +++ b/include/system/iothread.h
> @@ -50,6 +50,11 @@ struct IOThread {
>      bool stopping;              /* has iothread_stop() been called? */
>      bool running;               /* should iothread_run() continue? */
>      int thread_id;
> +    /*
> +     * The list elements are of type IoThreadHolder, which can
> +     * represent either a QOM path or a block node name.
> +     */
> +    GList *holders;
>  
>      /* AioContext poll parameters */
>      int64_t poll_max_ns;
> diff --git a/iothread.c b/iothread.c
> index 3558535b40..b805e4f97d 100644
> --- a/iothread.c
> +++ b/iothread.c
> @@ -25,6 +25,66 @@
>  #include "qemu/rcu.h"
>  #include "qemu/main-loop.h"
>  
> +/*
> + * Add the @holder path to the iothread's tracking list.
> + * The @holder is a QOM path if it starts with '/', else a block node name.
> + */

This interface overloads two string keys: QOM paths and block node
names.  For that to work, a key value must be unambiguously either QOM
path or block node name.  To get that, you restrict QOM path to
absolute.  Not exactly pretty, but it works.

What if you ever need to add another kind of holder?

Any new kind of holder would need a string key that cannot start with
'/', and cannot clash with block node names.  You'd either have to redo
this interface, or outlaw the clashes.

PATCH 06 in fact adds a new kind of holder: monitors.  To keep this
interface working, it uses made-up QOM paths as keys.  This is cheating.

Daniel Berrangé posted a series that turns monitors into QOM objects.
If it gets merged, your series can be fixed not to cheat.

More in review of PATCH 06.

> +static void iothread_ref(IOThread *iothread, const char *holder)
> +{
> +    IoThreadHolder *h = g_new0(IoThreadHolder, 1);
> +
> +    assert(holder);
> +
> +    if (holder[0] == '/') {
> +        h->type = IO_THREAD_HOLDER_KIND_QOM_OBJECT;
> +        h->u.qom_object.data = g_strdup(holder);
> +    } else {
> +        h->type = IO_THREAD_HOLDER_KIND_BLOCK_NODE;
> +        h->u.block_node.data = g_strdup(holder);
> +    }
> +
> +    iothread->holders = g_list_prepend(iothread->holders, h);
> +}
> +
> +static int iothread_holder_compare(gconstpointer a, gconstpointer b)
> +{
> +    const IoThreadHolder *holder_node = a;
> +    const char *target_name = b;
> +    const char *current_name;
> +
> +    if (holder_node->type == IO_THREAD_HOLDER_KIND_QOM_OBJECT) {
> +        current_name = holder_node->u.qom_object.data;
> +    } else if (holder_node->type == IO_THREAD_HOLDER_KIND_BLOCK_NODE) {
> +        current_name = holder_node->u.block_node.data;
> +    } else {
> +        /*
> +         * This should not happen. If it does, current_name remains
> +         * NULL and g_strcmp0 will handle it safely.
> +         */
> +        current_name = NULL;
> +    }
> +
> +    return g_strcmp0(current_name, target_name);
> +}
> +
> +/*
> + * This function removes the @holder from the @iothread's tracking list.
> + * The @holder string must match the one used previously in iothread_ref).
> + * It is a programming error to call this with a @holder that is not
> + * currently associated with the @iothread.
> + */
> +static void iothread_unref(IOThread *iothread, const char *holder)
> +{
> +    GList *link = g_list_find_custom(iothread->holders, holder,
> +                                     (GCompareFunc)iothread_holder_compare);
> +
> +    assert(link);
> +
> +    IoThreadHolder *h = (IoThreadHolder *)link->data;
> +    qapi_free_IoThreadHolder(h);
> +    iothread->holders = g_list_delete_link(iothread->holders, link);
> +}
> +
>  static void *iothread_run(void *opaque)
>  {
>      IOThread *iothread = opaque;
> @@ -108,6 +168,9 @@ static void iothread_instance_finalize(Object *obj)
>  
>      iothread_stop(iothread);
>  
> +    /* We don't support finalize without holders */
> +    assert(iothread->holders == NULL);

The comment seems to say "no holders is not supposed to happen".

The assertion says "holders is not supposed to happen".

Which one is wrong?

> +
>      /*
>       * Before glib2 2.33.10, there is a glib2 bug that GSource context
>       * pointer may not be cleared even if the context has already been
> @@ -356,6 +419,10 @@ char *iothread_get_id(IOThread *iothread)
>  
>  AioContext *iothread_get_aio_context(IOThread *iothread)
>  {
> +    /* Remove in next patch for build */
> +    iothread_ref(iothread, "tmp");
> +    iothread_unref(iothread, "tmp");
> +
>      return iothread->ctx;
>  }
>  
> diff --git a/qapi/misc.json b/qapi/misc.json
> index c71a5fe657..5fb7dcfcad 100644
> --- a/qapi/misc.json
> +++ b/qapi/misc.json
> @@ -67,6 +67,54 @@
>  ##
>  { 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true }
>  
> +
> +##
> +# @IoThreadHolderBlockNode:
> +#
> +# @data: Block node name.

Could we name this @node-name?

> +#
> +# Since: 11.1
> +#
> +##
> +{ 'struct': 'IoThreadHolderBlockNode',
> +  'data': { 'data': 'str' } }
> +
> +##
> +# @IoThreadHolderQomObject:
> +#
> +# @data: Absolute @qom-path.

Could we name this @qom-path?

> +#
> +# Since: 11.1
> +#
> +##
> +{ 'struct': 'IoThreadHolderQomObject',
> +  'data': { 'data': 'str' } }
> +
> +##
> +# @IoThreadHolderKind:
> +#
> +# @block-node: Block node name.
> +# @qom-object: Absolute @qom-path.

Let's use

   # @block-node: A block node
   #
   # @qom-object: A QOM Object

> +#
> +# Since: 11.1
> +##
> +{ 'enum': 'IoThreadHolderKind',
> +  'data': [ 'block-node', 'qom-object' ] }
> +
> +##
> +# @IoThreadHolder:
> +#
> +# @type: the kind of I/O thread holder.
> +#
> +# Since: 11.1
> +##
> +{ 'union': 'IoThreadHolder',
> +  'base': { 'type': 'IoThreadHolderKind' },
> +  'discriminator': 'type',
> +  'data': {
> +    'block-node': 'IoThreadHolderBlockNode',
> +    'qom-object': 'IoThreadHolderQomObject' } }
> +
>  ##
>  # @IOThreadInfo:
>  #



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

* Re: [PATCH V7 06/14] monitor: Update tracking iothread users with holder name
  2026-05-11 14:04 ` [PATCH V7 06/14] monitor: Update tracking iothread users with holder name Zhang Chen
@ 2026-05-18 11:57   ` Markus Armbruster
  2026-05-18 13:47     ` Zhang Chen
  0 siblings, 1 reply; 26+ messages in thread
From: Markus Armbruster @ 2026-05-18 11:57 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:

> Since the Monitor struct is not a QOM Object, we cannot use
> object_get_canonical_path(). Instead, this patch uses the monitor's
> name (or a default type-based name) as the holder identifier like QOM
> Object.
>
> Because Daniel Berrangé's "[PATCH RFC 00/17] monitor: turn QMP and HMP into
> QOM objects" under review, when that series gets merged, the kind monitor
> can go away. Current patch assume the monitor already been a QOM Object
> in IOthread. Will keep looking at Daniel Berrangé's patch about monitor
> QOM in case any future changes needed here.

Feels like this patch should not be merged as is.

> Cache the AioContext in the Monitor struct to avoid repeated calls to
> iothread_get_aio_context() and ensure symmetrical ref/unref during
> monitor lifecycle.
>
> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> ---
>  monitor/monitor-internal.h |  3 +++
>  monitor/monitor.c          | 24 ++++++++++++++++++++++--
>  monitor/qmp.c              |  3 ++-
>  3 files changed, 27 insertions(+), 3 deletions(-)
>
> diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
> index a5c4aba306..3e2c141c27 100644
> --- a/monitor/monitor-internal.h
> +++ b/monitor/monitor-internal.h
> @@ -125,6 +125,9 @@ struct Monitor {
>      guint out_watch;
>      int mux_out;
>      int reset_seen;
> +
> +    /* iothread context */
> +    AioContext *ctx;
>  };
>  
>  struct MonitorHMP {
> diff --git a/monitor/monitor.c b/monitor/monitor.c
> index 00b93ed612..b6efe776d6 100644
> --- a/monitor/monitor.c
> +++ b/monitor/monitor.c
> @@ -529,7 +529,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(mon->ctx);
>      }
>  
>      trace_monitor_suspend(mon, 1);
> @@ -564,7 +564,7 @@ void monitor_resume(Monitor *mon)
>          AioContext *ctx;
>  
>          if (mon->use_io_thread) {
> -            ctx = iothread_get_aio_context(mon_iothread);
> +            ctx = mon->ctx;
>          } else {
>              ctx = qemu_get_aio_context();
>          }
> @@ -612,6 +612,18 @@ void monitor_data_init(Monitor *mon, bool is_qmp, bool skip_flush,
>  {
>      if (use_io_thread && !mon_iothread) {
>          monitor_iothread_init();
> +        /*
> +         * Because of current Monitor is not a QOM Object,
> +         * so using OBJECT(mon) is undefined behavior and may crash.
> +         * Try using a hard-coded future implementation of the qom path instead.
> +         * (Like the name of the "mon_iothread").
> +         * long-term solution would be making Monitor QOM, after that change
> +         * here to:
> +         * g_autofree path = object_get_canonical_path(OBJECT(mon));
> +         */
> +        g_autofree char *path = g_strdup(is_qmp ? "/monitor/qmp_mon0" :
> +                                                  "/monitor/hmp_mon0");

This is wrong.

First, inventing QOM paths is cheating.  Fine for RFC patches, but not
for merging.

Second, we can have multiple QMP and multiple HMP monitors.  For
instance

    $ qemu-system-x86_64 -S -display none -monitor stdio -chardev "socket,id=chr-mon1,path=hmp-sock,server=on,wait=off" -mon "id=mon1,chardev=chr-mon1,mode=readline"

runs QEMU with an HMP monitor on stdio, and another one on Unix domain
socket hmp-sock.

> +        mon->ctx = iothread_ref_and_get_aio_context(mon_iothread, path);
>      }
>      qemu_mutex_init(&mon->mon_lock);
>      mon->is_qmp = is_qmp;
> @@ -631,6 +643,14 @@ void monitor_data_destroy(Monitor *mon)
>      }
>      g_string_free(mon->outbuf, true);
>      qemu_mutex_destroy(&mon->mon_lock);
> +
> +    if (mon->ctx && mon_iothread) {
> +        g_autofree char *path = g_strdup(monitor_is_qmp(mon) ?
> +                                        "/monitor/qmp_mon0" :
> +                                        "/monitor/hmp_mon0");
> +        iothread_put_aio_context(mon_iothread, path);
> +        mon->ctx = NULL;
> +    }
>  }
>  
>  void monitor_cleanup(void)
> diff --git a/monitor/qmp.c b/monitor/qmp.c
> index 687019811f..8e4a775fac 100644
> --- a/monitor/qmp.c
> +++ b/monitor/qmp.c
> @@ -549,7 +549,8 @@ void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
>           * 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),
> +        Monitor *mon_p = &mon->common;
> +        aio_bh_schedule_oneshot(mon_p->ctx,
>                                  monitor_qmp_setup_handlers_bh, mon);
>          /* The bottom half will add @mon to @mon_list */
>      } else {



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

* Re: [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices
  2026-05-11 18:14   ` Stefan Hajnoczi
  2026-05-11 19:15     ` Zhang Chen
@ 2026-05-18 11:58     ` Markus Armbruster
  2026-05-18 13:28       ` Zhang Chen
  1 sibling, 1 reply; 26+ messages in thread
From: Markus Armbruster @ 2026-05-18 11:58 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Zhang Chen, qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Michael S . Tsirkin

Stefan Hajnoczi <stefanha@redhat.com> writes:

> On Mon, May 11, 2026 at 10:04:04PM +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.
>> - 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.
>> 
>> A later commit will add QMP commands to let management applications
>> query the attachment status of IOThreads.
>> 
>> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
>> ---
>>  include/system/iothread.h |  5 +++
>>  iothread.c                | 67 +++++++++++++++++++++++++++++++++++++++
>>  qapi/misc.json            | 48 ++++++++++++++++++++++++++++
>>  3 files changed, 120 insertions(+)
>> 
>> diff --git a/include/system/iothread.h b/include/system/iothread.h
>> index a1ef7696cb..2871b06edc 100644
>> --- a/include/system/iothread.h
>> +++ b/include/system/iothread.h
>> @@ -50,6 +50,11 @@ struct IOThread {
>>      bool stopping;              /* has iothread_stop() been called? */
>>      bool running;               /* should iothread_run() continue? */
>>      int thread_id;
>> +    /*
>> +     * The list elements are of type IoThreadHolder, which can
>> +     * represent either a QOM path or a block node name.
>> +     */
>> +    GList *holders;
>>  
>>      /* AioContext poll parameters */
>>      int64_t poll_max_ns;
>> diff --git a/iothread.c b/iothread.c
>> index 3558535b40..b805e4f97d 100644
>> --- a/iothread.c
>> +++ b/iothread.c
>> @@ -25,6 +25,66 @@
>>  #include "qemu/rcu.h"
>>  #include "qemu/main-loop.h"
>>  
>> +/*
>> + * Add the @holder path to the iothread's tracking list.
>> + * The @holder is a QOM path if it starts with '/', else a block node name.
>> + */
>
> It would be cleaner to use IoThreadHolder in the iothread_ref() and
> iothread_unref() APIs instead of a string:
>
>   static void iothread_ref(IOThread *iothread, const IoThreadHolder *holder);
>   static void iothread_unref(IOThread *iothread, const IoThreadHolder *holder);
>
> That way the caller already uses IO_THREAD_HOLDER_KIND_QOM_OBJECT or
> IO_THREAD_HOLDER_KIND_BLOCK_NODE to clearly distinguish QOM paths from
> block node names. No string parsing is necessary.

Exactly.

[...]



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

* Re: [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices
  2026-05-18 11:58     ` Markus Armbruster
@ 2026-05-18 13:28       ` Zhang Chen
  0 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-18 13:28 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Stefan Hajnoczi, qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Michael S . Tsirkin

On Mon, May 18, 2026 at 7:58 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Stefan Hajnoczi <stefanha@redhat.com> writes:
>
> > On Mon, May 11, 2026 at 10:04:04PM +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.
> >> - 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.
> >>
> >> A later commit will add QMP commands to let management applications
> >> query the attachment status of IOThreads.
> >>
> >> Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> >> ---
> >>  include/system/iothread.h |  5 +++
> >>  iothread.c                | 67 +++++++++++++++++++++++++++++++++++++++
> >>  qapi/misc.json            | 48 ++++++++++++++++++++++++++++
> >>  3 files changed, 120 insertions(+)
> >>
> >> diff --git a/include/system/iothread.h b/include/system/iothread.h
> >> index a1ef7696cb..2871b06edc 100644
> >> --- a/include/system/iothread.h
> >> +++ b/include/system/iothread.h
> >> @@ -50,6 +50,11 @@ struct IOThread {
> >>      bool stopping;              /* has iothread_stop() been called? */
> >>      bool running;               /* should iothread_run() continue? */
> >>      int thread_id;
> >> +    /*
> >> +     * The list elements are of type IoThreadHolder, which can
> >> +     * represent either a QOM path or a block node name.
> >> +     */
> >> +    GList *holders;
> >>
> >>      /* AioContext poll parameters */
> >>      int64_t poll_max_ns;
> >> diff --git a/iothread.c b/iothread.c
> >> index 3558535b40..b805e4f97d 100644
> >> --- a/iothread.c
> >> +++ b/iothread.c
> >> @@ -25,6 +25,66 @@
> >>  #include "qemu/rcu.h"
> >>  #include "qemu/main-loop.h"
> >>
> >> +/*
> >> + * Add the @holder path to the iothread's tracking list.
> >> + * The @holder is a QOM path if it starts with '/', else a block node name.
> >> + */
> >
> > It would be cleaner to use IoThreadHolder in the iothread_ref() and
> > iothread_unref() APIs instead of a string:
> >
> >   static void iothread_ref(IOThread *iothread, const IoThreadHolder *holder);
> >   static void iothread_unref(IOThread *iothread, const IoThreadHolder *holder);
> >
> > That way the caller already uses IO_THREAD_HOLDER_KIND_QOM_OBJECT or
> > IO_THREAD_HOLDER_KIND_BLOCK_NODE to clearly distinguish QOM paths from
> > block node names. No string parsing is necessary.
>
> Exactly.
>

Please double-check the details:

We need every caller init its own IOThreadHolder then pass to the
AioContext *iothread_ref_and_get_aio_context(IOThread *iothread,
const IoThreadHolder *holder)

Like this:
IOThreadHolder holder = {
             .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
             .u.qom_object.data = (char *)virtio_blk_path,
};

new_context = iothread_ref_and_get_aio_context(iothread, &holder);


Thanks
Chen


> [...]
>


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

* Re: [PATCH V7 06/14] monitor: Update tracking iothread users with holder name
  2026-05-18 11:57   ` Markus Armbruster
@ 2026-05-18 13:47     ` Zhang Chen
  0 siblings, 0 replies; 26+ messages in thread
From: Zhang Chen @ 2026-05-18 13:47 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Michael S . Tsirkin, Stefan Hajnoczi

On Mon, May 18, 2026 at 7:57 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Zhang Chen <zhangckid@gmail.com> writes:
>
> > Since the Monitor struct is not a QOM Object, we cannot use
> > object_get_canonical_path(). Instead, this patch uses the monitor's
> > name (or a default type-based name) as the holder identifier like QOM
> > Object.
> >
> > Because Daniel Berrangé's "[PATCH RFC 00/17] monitor: turn QMP and HMP into
> > QOM objects" under review, when that series gets merged, the kind monitor
> > can go away. Current patch assume the monitor already been a QOM Object
> > in IOthread. Will keep looking at Daniel Berrangé's patch about monitor
> > QOM in case any future changes needed here.
>
> Feels like this patch should not be merged as is.
>
> > Cache the AioContext in the Monitor struct to avoid repeated calls to
> > iothread_get_aio_context() and ensure symmetrical ref/unref during
> > monitor lifecycle.
> >
> > Signed-off-by: Zhang Chen <zhangckid@gmail.com>
> > ---
> >  monitor/monitor-internal.h |  3 +++
> >  monitor/monitor.c          | 24 ++++++++++++++++++++++--
> >  monitor/qmp.c              |  3 ++-
> >  3 files changed, 27 insertions(+), 3 deletions(-)
> >
> > diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
> > index a5c4aba306..3e2c141c27 100644
> > --- a/monitor/monitor-internal.h
> > +++ b/monitor/monitor-internal.h
> > @@ -125,6 +125,9 @@ struct Monitor {
> >      guint out_watch;
> >      int mux_out;
> >      int reset_seen;
> > +
> > +    /* iothread context */
> > +    AioContext *ctx;
> >  };
> >
> >  struct MonitorHMP {
> > diff --git a/monitor/monitor.c b/monitor/monitor.c
> > index 00b93ed612..b6efe776d6 100644
> > --- a/monitor/monitor.c
> > +++ b/monitor/monitor.c
> > @@ -529,7 +529,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(mon->ctx);
> >      }
> >
> >      trace_monitor_suspend(mon, 1);
> > @@ -564,7 +564,7 @@ void monitor_resume(Monitor *mon)
> >          AioContext *ctx;
> >
> >          if (mon->use_io_thread) {
> > -            ctx = iothread_get_aio_context(mon_iothread);
> > +            ctx = mon->ctx;
> >          } else {
> >              ctx = qemu_get_aio_context();
> >          }
> > @@ -612,6 +612,18 @@ void monitor_data_init(Monitor *mon, bool is_qmp, bool skip_flush,
> >  {
> >      if (use_io_thread && !mon_iothread) {
> >          monitor_iothread_init();
> > +        /*
> > +         * Because of current Monitor is not a QOM Object,
> > +         * so using OBJECT(mon) is undefined behavior and may crash.
> > +         * Try using a hard-coded future implementation of the qom path instead.
> > +         * (Like the name of the "mon_iothread").
> > +         * long-term solution would be making Monitor QOM, after that change
> > +         * here to:
> > +         * g_autofree path = object_get_canonical_path(OBJECT(mon));
> > +         */
> > +        g_autofree char *path = g_strdup(is_qmp ? "/monitor/qmp_mon0" :
> > +                                                  "/monitor/hmp_mon0");
>
> This is wrong.
>
> First, inventing QOM paths is cheating.  Fine for RFC patches, but not
> for merging.
>
> Second, we can have multiple QMP and multiple HMP monitors.  For
> instance
>
>     $ qemu-system-x86_64 -S -display none -monitor stdio -chardev "socket,id=chr-mon1,path=hmp-sock,server=on,wait=off" -mon "id=mon1,chardev=chr-mon1,mode=readline"
>
> runs QEMU with an HMP monitor on stdio, and another one on Unix domain
> socket hmp-sock.

Yes, you are right.
And here looks has same issue for multi iothreads:
static void monitor_iothread_init(void)
{
    mon_iothread = iothread_create("mon_iothread", &error_abort);
}

If yes, I will fix it by the way in next version.

Back to this patch, the "struct Monitor" doesn't seem to be a unique
field like id or qom path.
We need to add this field or any comments here?

Then, as discussed in patch 02/14, I will add a new type for the monitor:
"IO_THREAD_HOLDER_KIND_MONITOR"

Thanks
Chen

>
> > +        mon->ctx = iothread_ref_and_get_aio_context(mon_iothread, path);
> >      }
> >      qemu_mutex_init(&mon->mon_lock);
> >      mon->is_qmp = is_qmp;
> > @@ -631,6 +643,14 @@ void monitor_data_destroy(Monitor *mon)
> >      }
> >      g_string_free(mon->outbuf, true);
> >      qemu_mutex_destroy(&mon->mon_lock);
> > +
> > +    if (mon->ctx && mon_iothread) {
> > +        g_autofree char *path = g_strdup(monitor_is_qmp(mon) ?
> > +                                        "/monitor/qmp_mon0" :
> > +                                        "/monitor/hmp_mon0");
> > +        iothread_put_aio_context(mon_iothread, path);
> > +        mon->ctx = NULL;
> > +    }
> >  }
> >
> >  void monitor_cleanup(void)
> > diff --git a/monitor/qmp.c b/monitor/qmp.c
> > index 687019811f..8e4a775fac 100644
> > --- a/monitor/qmp.c
> > +++ b/monitor/qmp.c
> > @@ -549,7 +549,8 @@ void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp)
> >           * 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),
> > +        Monitor *mon_p = &mon->common;
> > +        aio_bh_schedule_oneshot(mon_p->ctx,
> >                                  monitor_qmp_setup_handlers_bh, mon);
> >          /* The bottom half will add @mon to @mon_list */
> >      } else {
>


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

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

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-11 14:04 [PATCH V7 00/14] iothread: Support tracking and querying IOThread holders Zhang Chen
2026-05-11 14:04 ` [PATCH V7 01/14] qapi/misc: Fix missed query-iothreads items Zhang Chen
2026-05-11 14:04 ` [PATCH V7 02/14] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
2026-05-11 18:14   ` Stefan Hajnoczi
2026-05-11 19:15     ` Zhang Chen
2026-05-18 11:58     ` Markus Armbruster
2026-05-18 13:28       ` Zhang Chen
2026-05-18 11:44   ` Markus Armbruster
2026-05-11 14:04 ` [PATCH V7 03/14] iothread: tracking iothread users with holder name Zhang Chen
2026-05-11 18:54   ` Stefan Hajnoczi
2026-05-11 19:06     ` Zhang Chen
2026-05-11 14:04 ` [PATCH V7 04/14] blockdev: Update " Zhang Chen
2026-05-11 19:05   ` Stefan Hajnoczi
2026-05-11 19:25     ` Zhang Chen
2026-05-11 14:04 ` [PATCH V7 05/14] block/export: track IOThread reference in BlockExport Zhang Chen
2026-05-11 14:04 ` [PATCH V7 06/14] monitor: Update tracking iothread users with holder name Zhang Chen
2026-05-18 11:57   ` Markus Armbruster
2026-05-18 13:47     ` Zhang Chen
2026-05-11 14:04 ` [PATCH V7 07/14] virtio-vq-mapping: track iothread-vq-mapping references using device path Zhang Chen
2026-05-11 14:04 ` [PATCH V7 08/14] virtio: use iothread_get/put_aio_context for thread pinning Zhang Chen
2026-05-11 14:04 ` [PATCH V7 09/14] net/colo: track IOThread references using path-based holder Zhang Chen
2026-05-11 14:04 ` [PATCH V7 10/14] virtio-balloon: Update tracking iothread users with holder name Zhang Chen
2026-05-11 14:04 ` [PATCH V7 11/14] vfio-user/proxy: " Zhang Chen
2026-05-11 14:04 ` [PATCH V7 12/14] xen-block: " Zhang Chen
2026-05-11 14:04 ` [PATCH V7 13/14] qapi: examine IOThread attachment status via query-iothreads Zhang Chen
2026-05-11 14:04 ` [PATCH V7 14/14] iothread: simplify API by merging iothread_get_aio_context variants Zhang Chen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.