All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders
@ 2026-07-16 14:40 Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 01/15] qapi/misc: Fix missed query-iothreads items Zhang Chen
                   ` (14 more replies)
  0 siblings, 15 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  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 or
block exports 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 or block exports), accessible via both 
  query-iothreads (QMP) and info iothreads (HMP).

### V10 -> V9 Changelog
  - Rebase this series on the Daniel P. Berrangé's series:
    [PATCH v7 00/35] monitor: turn QMP and HMP into QOM objects
    https://lore.kernel.org/qemu-devel/20260706135824.2623960-1-berrange@redhat.com/
  - Remove the monitor type of IOThreadHolder, make the monitor as a QOM object.
  - Remove GList related code and stays within QAPI instead of mixing GList and QAPI.
  - Fix missed comments in the patch 2.
  - Rewrite parts of commit log generated by AI. 
  - Keep Stefan's reviewed-by in patch 2 and addressed Markus's comments.
  - Rewrite parts of code comments.
  - Drop original patch 6/7/8 for monitor parts.

### V9 -> V8 Changelog
  - Drop to assign unique default ID for monitor, and keep the NULL ID. 
  - Rewrite and split the monitor patch 7 and 8.
  - Add the [PATCH 17/17]tests/unit/iothread: Update the iothread_get_aio_context
    to keep the API same in the unit tests.
  - Refactorcode according comments.
  - Update the example.
  - Fix other comments issues.

### V7 -> V8 Changelog
  - Drop the assumption of the Monitor QOM, add the new type
    IO_THREAD_HOLDER_KIND_MONITOR_NAME.
  - Drop the assumption of the string '/' as QOM path.
  - Introduce new patch for Monitor:
    [PATCH 06/15] assign unique default ID to anonymous monitors
  - Fixed Monitor ID for the IOThread.
  - Refactorcode according comments.
  - Update QMP example.
  - Rebased patches on upstream code.
  - Fixed comments issues.
  - Removed redundant code.

### 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 Berrange'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 (15):
  qapi/misc: Fix missed query-iothreads items
  iothread: introduce iothread_ref/unref to track attached devices
  iothread: tracking iothread users with holder name
  iothread: introduce iothread_unsafe_get_aio_context()
  block/export: track IOThread reference in BlockExport
  monitor: Update tracking iothread users with holder
  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
  vfio-user/proxy: Update tracking iothread users with holder name
  xen-block: Update tracking iothread users with holder name
  monitor/hmp: Add holders support for hmp_info_iothreads
  iothread: simplify API by merging iothread_get_aio_context variants
  tests/unit/iothread: Update the iothread_get_aio_context

 block/export/export.c                   | 78 +++++++++++++++++---
 blockdev.c                              |  2 +-
 hw/block/dataplane/xen-block.c          | 19 ++++-
 hw/block/virtio-blk.c                   | 22 ++++--
 hw/scsi/virtio-scsi-dataplane.c         | 22 ++++--
 hw/vfio-user/proxy.c                    | 15 +++-
 hw/virtio/iothread-vq-mapping.c         | 19 +++--
 hw/virtio/virtio-balloon.c              | 22 ++++--
 include/block/export.h                  |  6 ++
 include/hw/virtio/iothread-vq-mapping.h |  6 +-
 include/system/iothread.h               | 30 +++++++-
 iothread.c                              | 96 ++++++++++++++++++++++++-
 monitor/hmp-cmds.c                      | 22 ++++++
 monitor/monitor-internal.h              |  3 +
 monitor/monitor.c                       | 22 +++++-
 monitor/qmp.c                           |  5 +-
 net/colo-compare.c                      | 29 +++++---
 qapi/misc.json                          | 71 +++++++++++++++++-
 tests/unit/iothread.c                   | 16 +++--
 tests/unit/iothread.h                   |  6 +-
 tests/unit/test-aio-multithread.c       |  5 +-
 tests/unit/test-bdrv-drain.c            | 18 +++--
 tests/unit/test-block-iothread.c        | 21 ++++--
 23 files changed, 477 insertions(+), 78 deletions(-)

-- 
2.49.0



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

* [PATCH V10 01/15] qapi/misc: Fix missed query-iothreads items
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 02/15] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  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] 18+ messages in thread

* [PATCH V10 02/15] iothread: introduce iothread_ref/unref to track attached devices
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 01/15] qapi/misc: Fix missed query-iothreads items Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 03/15] iothread: tracking iothread users with holder name Zhang Chen
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  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 IOThreadHolder to a list.
- iothread_unref(): Searches for the IOThreadHolder using a
  string comparison (strcmp), releases the associated memory
  upon a successful match.
- holders: A IOThreadHolderList storing the IOThreadHolder
  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>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 include/system/iothread.h | 11 ++++++
 iothread.c                | 70 +++++++++++++++++++++++++++++++++++++++
 qapi/misc.json            | 59 +++++++++++++++++++++++++++++++++
 3 files changed, 140 insertions(+)

diff --git a/include/system/iothread.h b/include/system/iothread.h
index a1ef7696cb..ef0b2f9648 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -38,6 +38,9 @@
 #define IOTHREAD_POLL_WEIGHT_DEFAULT 0ULL
 #endif
 
+typedef struct IOThreadHolder IOThreadHolder;
+typedef struct IOThreadHolderList IOThreadHolderList;
+
 struct IOThread {
     EventLoopBase parent_obj;
 
@@ -50,6 +53,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 export name.
+     */
+    IOThreadHolderList *holders;
 
     /* AioContext poll parameters */
     int64_t poll_max_ns;
@@ -82,4 +90,7 @@ void iothread_destroy(IOThread *iothread);
  */
 bool qemu_in_iothread(void);
 
+void iothread_ref(IOThread *iothread, const IOThreadHolder *holder);
+void iothread_unref(IOThread *iothread, const IOThreadHolder *holder);
+
 #endif /* IOTHREAD_H */
diff --git a/iothread.c b/iothread.c
index 3558535b40..38f273c0e9 100644
--- a/iothread.c
+++ b/iothread.c
@@ -21,10 +21,78 @@
 #include "system/iothread.h"
 #include "qapi/error.h"
 #include "qapi/qapi-commands-misc.h"
+#include "qapi/clone-visitor.h"
+#include "qapi/qapi-visit-misc.h"
 #include "qemu/error-report.h"
 #include "qemu/rcu.h"
 #include "qemu/main-loop.h"
 
+/*
+ * iothread_ref:
+ * @iothread: the iothread to track
+ * @holder: the IOThreadHolder object initialized by the caller
+ *
+ * Add the @holder to the iothread's tracking list.
+ */
+void iothread_ref(IOThread *iothread, const IOThreadHolder *holder)
+{
+    assert(holder);
+
+    QAPI_LIST_PREPEND(iothread->holders, QAPI_CLONE(IOThreadHolder, holder));
+}
+
+static int iothread_holder_compare(const IOThreadHolder *holder_a,
+                                   const IOThreadHolder *holder_b)
+{
+    const char *name_a, *name_b;
+
+    if (holder_a->type != holder_b->type) {
+        return holder_b->type - holder_a->type;
+    }
+
+    switch (holder_a->type) {
+    case IO_THREAD_HOLDER_KIND_QOM_OBJECT:
+        name_a = holder_a->u.qom_object.qom_path;
+        name_b = holder_b->u.qom_object.qom_path;
+        break;
+    case IO_THREAD_HOLDER_KIND_BLOCK_EXPORT:
+        name_a = holder_a->u.block_export.export_name;
+        name_b = holder_b->u.block_export.export_name;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+    return strcmp(name_a, name_b);
+}
+
+/*
+ * This function removes the @holder from the @iothread's tracking list.
+ * The @holder 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.
+ */
+void iothread_unref(IOThread *iothread, const IOThreadHolder *holder)
+{
+    IOThreadHolderList **prev = &iothread->holders;
+    IOThreadHolderList *curr;
+
+    assert(holder);
+
+    while (*prev) {
+        curr = *prev;
+        if (iothread_holder_compare(curr->value, holder) == 0) {
+            *prev = curr->next;
+            curr->next = NULL;
+            qapi_free_IOThreadHolderList(curr);
+            return;
+        }
+        prev = &curr->next;
+    }
+
+    g_assert_not_reached();
+}
+
 static void *iothread_run(void *opaque)
 {
     IOThread *iothread = opaque;
@@ -129,6 +197,7 @@ static void iothread_instance_finalize(Object *obj)
         iothread->main_loop = NULL;
     }
     qemu_sem_destroy(&iothread->init_done_sem);
+    qapi_free_IOThreadHolderList(iothread->holders);
 }
 
 static void iothread_init_gcontext(IOThread *iothread, const char *thread_name)
@@ -373,6 +442,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 = QAPI_CLONE(IOThreadHolderList, iothread->holders);
     info->poll_max_ns = iothread->poll_max_ns;
     info->poll_grow = iothread->poll_grow;
     info->poll_shrink = iothread->poll_shrink;
diff --git a/qapi/misc.json b/qapi/misc.json
index c71a5fe657..096e418b7a 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -67,6 +67,56 @@
 ##
 { 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true }
 
+
+##
+# @IOThreadHolderBlockExport:
+#
+# @export-name: Name of the block export.
+#
+# Since: 11.1
+#
+##
+{ 'struct': 'IOThreadHolderBlockExport',
+  'data': { 'export-name': 'str' } }
+
+##
+# @IOThreadHolderQomObject:
+#
+# @qom-path: Path to the object in the QOM tree.
+#
+# Since: 11.1
+#
+##
+{ 'struct': 'IOThreadHolderQomObject',
+  'data': { 'qom-path': 'str' } }
+
+##
+# @IOThreadHolderKind:
+#
+# @block-export: A block export.
+# @qom-object: A QOM Object.
+#
+# Since: 11.1
+##
+{ 'enum': 'IOThreadHolderKind',
+  'data': [ 'block-export', 'qom-object' ] }
+
+##
+# @IOThreadHolder:
+#
+# The block export or QOM object holding the I/O thread.
+#
+# @type: the kind of I/O thread holder.
+#
+# Since: 11.1
+##
+{ 'union': 'IOThreadHolder',
+  'base': { 'type': 'IOThreadHolderKind' },
+  'discriminator': 'type',
+  'data': {
+    'block-export': 'IOThreadHolderBlockExport',
+    'qom-object': 'IOThreadHolderQomObject' } }
+
 ##
 # @IOThreadInfo:
 #
@@ -76,6 +126,11 @@
 #
 # @thread-id: ID of the underlying host thread
 #
+# @holders: the QOM objects or block nodes currently
+#     associated with this iothread.  When an associated component is
+#     detached or destroyed, it is removed from this list.
+#     (Since 11.1)
+#
 # @poll-max-ns: maximum polling time in ns, 0 means polling is
 #     disabled (since 2.9)
 #
@@ -98,6 +153,7 @@
 { 'struct': 'IOThreadInfo',
   'data': {'id': 'str',
            'thread-id': 'int',
+           'holders': ['IOThreadHolder'],
            'poll-max-ns': 'int',
            'poll-grow': 'int',
            'poll-shrink': 'int',
@@ -124,6 +180,8 @@
 #              {
 #                 "id":"iothread0",
 #                 "thread-id":3134,
+#                 "holders":[{"qom-path": "/machine/peripheral/blk1/virtio-backend", "type": "qom-object"},
+#                            {"qom-path": "/machine/peripheral/blk2/virtio-backend", "type": "qom-object"}],
 #                 "poll-max-ns":32768,
 #                 "poll-grow":0,
 #                 "poll-shrink":0,
@@ -132,6 +190,7 @@
 #              {
 #                 "id":"iothread1",
 #                 "thread-id":3135,
+#                 "holders":[{"export-name": "fmt_qcow2", "type": "block-export"}],
 #                 "poll-max-ns":32768,
 #                 "poll-grow":0,
 #                 "poll-shrink":0,
-- 
2.49.0



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

* [PATCH V10 03/15] iothread: tracking iothread users with holder name
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 01/15] qapi/misc: Fix missed query-iothreads items Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 02/15] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 04/15] iothread: introduce iothread_unsafe_get_aio_context() Zhang Chen
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  Cc: Zhang Chen

Introduce iothread_get_aio_context() (In this patch named
"iothread_ref_and_get_aio_context" for build, will change the name
in the last patch) 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 using iothread_ref() and iothread_unref().

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

diff --git a/include/system/iothread.h b/include/system/iothread.h
index ef0b2f9648..86f02c5a7d 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -18,6 +18,7 @@
 #include "qemu/thread.h"
 #include "qom/object.h"
 #include "system/event-loop-base.h"
+#include "qapi/qapi-types-misc.h"
 
 #define TYPE_IOTHREAD "iothread"
 
@@ -72,7 +73,14 @@ DECLARE_INSTANCE_CHECKER(IOThread, IOTHREAD,
 
 char *iothread_get_id(IOThread *iothread);
 IOThread *iothread_by_id(const char *id);
+/*
+ * The iothread_get_aio_context() and iothread_put_aio_context() are not
+ * thread-safe and must be called under the Big QEMU Lock (BQL).
+ */
 AioContext *iothread_get_aio_context(IOThread *iothread);
+AioContext *iothread_ref_and_get_aio_context(IOThread *iothread,
+                                             const IOThreadHolder *holder);
+void iothread_put_aio_context(IOThread *iothread, const IOThreadHolder *holder);
 GMainContext *iothread_get_g_main_context(IOThread *iothread);
 
 /*
diff --git a/iothread.c b/iothread.c
index 38f273c0e9..f5d4708b25 100644
--- a/iothread.c
+++ b/iothread.c
@@ -39,6 +39,11 @@ void iothread_ref(IOThread *iothread, const IOThreadHolder *holder)
     assert(holder);
 
     QAPI_LIST_PREPEND(iothread->holders, QAPI_CLONE(IOThreadHolder, holder));
+    /*
+     * 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(const IOThreadHolder *holder_a,
@@ -85,6 +90,7 @@ void iothread_unref(IOThread *iothread, const IOThreadHolder *holder)
             *prev = curr->next;
             curr->next = NULL;
             qapi_free_IOThreadHolderList(curr);
+            object_unref(OBJECT(iothread));
             return;
         }
         prev = &curr->next;
@@ -206,7 +212,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);
@@ -428,6 +434,21 @@ AioContext *iothread_get_aio_context(IOThread *iothread)
     return iothread->ctx;
 }
 
+AioContext *iothread_ref_and_get_aio_context(IOThread *iothread,
+                                             const IOThreadHolder *holder)
+{
+    /* Add IOThreadHolder to the list */
+    iothread_ref(iothread, holder);
+
+    return iothread->ctx;
+ }
+
+void iothread_put_aio_context(IOThread *iothread, const IOThreadHolder *holder)
+{
+    /* Delete IOThreadHolder 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] 18+ messages in thread

* [PATCH V10 04/15] iothread: introduce iothread_unsafe_get_aio_context()
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (2 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 03/15] iothread: tracking iothread users with holder name Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 05/15] block/export: track IOThread reference in BlockExport Zhang Chen
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  Cc: Zhang Chen

Normally the IOThread's AioContext is fetched using
iothread_ref_and_get_aio_context(), but there are legacy callers
without a clear ref/unref lifecycle. They cannot unref (e.g. because
they do not have an IOThread pointer), so provide an unsafe way to
fetch the AioContext without holding a reference count. This unsafe
API serves legacy callers - do not use it in new code.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 blockdev.c                | 2 +-
 include/system/iothread.h | 9 +++++++++
 iothread.c                | 6 ++++++
 3 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/blockdev.c b/blockdev.c
index 6e86c6262f..baeab3a3e1 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_get_aio_context(obj);
+        new_context = iothread_unsafe_get_aio_context(obj);
     } else {
         new_context = qemu_get_aio_context();
     }
diff --git a/include/system/iothread.h b/include/system/iothread.h
index 86f02c5a7d..3cce5eeb09 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -78,6 +78,15 @@ IOThread *iothread_by_id(const char *id);
  * thread-safe and must be called under the Big QEMU Lock (BQL).
  */
 AioContext *iothread_get_aio_context(IOThread *iothread);
+/*
+ * Normally the IOThread's AioContext is fetched using
+ * iothread_ref_and_get_aio_context(), but there are legacy callers
+ * without a clear ref/unref lifecycle. They cannot unref (e.g. because
+ * they do not have an IOThread pointer), so provide an unsafe way to
+ * fetch the AioContext without holding a reference count. This unsafe
+ * API serves legacy callers - do not use it in new code.
+ */
+AioContext *iothread_unsafe_get_aio_context(IOThread *iothread);
 AioContext *iothread_ref_and_get_aio_context(IOThread *iothread,
                                              const IOThreadHolder *holder);
 void iothread_put_aio_context(IOThread *iothread, const IOThreadHolder *holder);
diff --git a/iothread.c b/iothread.c
index f5d4708b25..157bb4d302 100644
--- a/iothread.c
+++ b/iothread.c
@@ -449,6 +449,12 @@ void iothread_put_aio_context(IOThread *iothread, const IOThreadHolder *holder)
     iothread_unref(iothread, holder);
 }
 
+/* Recommend migrating to the new API with IOThreadHolder as much as possible */
+AioContext *iothread_unsafe_get_aio_context(IOThread *iothread)
+{
+    return iothread->ctx;
+}
+
 static int query_one_iothread(Object *object, void *opaque)
 {
     IOThreadInfoList ***tail = opaque;
-- 
2.49.0



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

* [PATCH V10 05/15] block/export: track IOThread reference in BlockExport
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (3 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 04/15] iothread: introduce iothread_unsafe_get_aio_context() Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 06/15] monitor: Update tracking iothread users with holder Zhang Chen
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  Cc: Zhang Chen

Add an iothreads array and an iothread_holder_name string to
BlockExport to track which block exports are using specific IOThreads.

During export creation, reference the associated IOThreads using
iothread_ref_and_get_aio_context(). Clean up and release these resources
properly in the blk_exp_add() error paths and during export deletion in
blk_exp_delete_bh(). This supports both single and multi-iothread configs.

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

diff --git a/block/export/export.c b/block/export/export.c
index b733f269f3..4dbd71de6a 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,16 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
             goto fail;
         }
 
-        new_ctx = iothread_get_aio_context(iothread);
+        holder_name = bdrv_get_node_name(bs);
+        const IOThreadHolder holder = {
+            .type = IO_THREAD_HOLDER_KIND_BLOCK_EXPORT,
+            .u.block_export.export_name = (char *)holder_name,
+        };
+
+        new_ctx = iothread_ref_and_get_aio_context(iothread, &holder);
+        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 +173,15 @@ 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);
+        const IOThreadHolder holder = {
+            .type = IO_THREAD_HOLDER_KIND_BLOCK_EXPORT,
+            .u.block_export.export_name = (char *)holder_name,
+        };
+
         for (strList *e = iothread_list; e; e = e->next) {
             IOThread *iothread = iothread_by_id(e->value);
 
@@ -172,7 +189,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);
         }
         assert(i == multithread_count);
     }
@@ -225,12 +244,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);
@@ -250,8 +272,32 @@ fail:
         blk_unref(blk);
     }
     if (exp) {
+        if (exp->iothreads) {
+            const IOThreadHolder holder = {
+                .type = IO_THREAD_HOLDER_KIND_BLOCK_EXPORT,
+                .u.block_export.export_name = exp->iothread_holder_name,
+            };
+            for (size_t j = 0; j < exp->iothread_count; j++) {
+                if (exp->iothreads[j]) {
+                    iothread_put_aio_context(exp->iothreads[j], &holder);
+                }
+            }
+            g_free(exp->iothreads);
+        }
+        g_free(exp->iothread_holder_name);
         g_free(exp->id);
         g_free(exp);
+    } else if (local_iothreads) {
+        const IOThreadHolder holder = {
+            .type = IO_THREAD_HOLDER_KIND_BLOCK_EXPORT,
+            .u.block_export.export_name = (char *)holder_name,
+        };
+
+        for (size_t j = 0; j < multithread_count; j++) {
+            if (local_iothreads[j]) {
+                iothread_put_aio_context(local_iothreads[j], &holder);
+            }
+        }
     }
     g_free(multithread_ctxs);
     return NULL;
@@ -269,10 +315,25 @@ static void blk_exp_delete_bh(void *opaque)
     BlockExport *exp = opaque;
 
     assert(exp->refcount == 0);
+
     QLIST_REMOVE(exp, next);
     exp->drv->delete(exp);
     blk_set_dev_ops(exp->blk, NULL, NULL);
     blk_unref(exp->blk);
+
+    if (exp->iothreads) {
+        const IOThreadHolder holder = {
+            .type = IO_THREAD_HOLDER_KIND_BLOCK_EXPORT,
+            .u.block_export.export_name = exp->iothread_holder_name,
+        };
+
+        for (size_t i = 0; i < exp->iothread_count; i++) {
+            iothread_put_aio_context(exp->iothreads[i], &holder);
+        }
+        g_free(exp->iothreads);
+    }
+
+    g_free(exp->iothread_holder_name);
     qapi_event_send_block_export_deleted(exp->id);
     g_free(exp->id);
     g_free(exp);
diff --git a/include/block/export.h b/include/block/export.h
index ca45da928c..8b5002ca84 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 utilized by this specific block export */
+    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] 18+ messages in thread

* [PATCH V10 06/15] monitor: Update tracking iothread users with holder
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (4 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 05/15] block/export: track IOThread reference in BlockExport Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 15:11   ` Daniel P. Berrangé
  2026-07-16 14:40 ` [PATCH V10 07/15] virtio-vq-mapping: track iothread-vq-mapping references using device path Zhang Chen
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  Cc: Zhang Chen

Based on monitor QOM path tracking iothread users with holder.
Introduce 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          | 22 ++++++++++++++++++++--
 monitor/qmp.c              |  5 +++--
 3 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
index 23829f32f9..caecceec93 100644
--- a/monitor/monitor-internal.h
+++ b/monitor/monitor-internal.h
@@ -153,6 +153,9 @@ struct Monitor {
     guint out_watch;
     int mux_out;
     int reset_seen;
+
+    /* iothread context */
+    AioContext *ctx;
 };
 
 struct MonitorHMPClass {
diff --git a/monitor/monitor.c b/monitor/monitor.c
index ed195fd97b..688bb85c81 100644
--- a/monitor/monitor.c
+++ b/monitor/monitor.c
@@ -573,7 +573,7 @@ void 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);
@@ -668,6 +668,17 @@ void monitor_cleanup(void)
         qemu_mutex_unlock(&monitor_lock);
         monitor_flush(mon);
         qemu_mutex_lock(&monitor_lock);
+
+        if (mon_iothread) {
+            g_autofree char *path = object_get_canonical_path(OBJECT(mon));
+            const IOThreadHolder io_holder = {
+                .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+                .u.qom_object.qom_path = path,
+            };
+
+            iothread_put_aio_context(mon_iothread, &io_holder);
+            mon->ctx = NULL;
+        }
         object_unparent(OBJECT(mon));
     }
     qemu_mutex_unlock(&monitor_lock);
@@ -732,7 +743,14 @@ static void monitor_complete(UserCreatable *uc, Error **errp)
             mon_iothread = iothread_create("mon_iothread", &error_abort);
         }
 
-        ctx = iothread_get_aio_context(mon_iothread);
+        g_autofree char *path = object_get_canonical_path(OBJECT(mon));
+        const IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = path,
+        };
+
+        mon->ctx = iothread_ref_and_get_aio_context(mon_iothread, &io_holder);
+        ctx = mon->ctx;
     } else {
         ctx = qemu_get_aio_context();
     }
diff --git a/monitor/qmp.c b/monitor/qmp.c
index 223e0643c2..b16bd77688 100644
--- a/monitor/qmp.c
+++ b/monitor/qmp.c
@@ -732,7 +732,8 @@ static void monitor_qmp_complete(UserCreatable *uc, Error **errp)
          * thread.  Schedule a bottom half.
          */
         mon->setup_pending = true;
-        aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
+
+        aio_bh_schedule_oneshot(MONITOR(mon)->ctx,
                                 monitor_qmp_setup_handlers_bh, mon);
         /* The bottom half will add @mon to @mon_list */
     } else {
@@ -787,7 +788,7 @@ static bool monitor_qmp_prepare_delete(UserCreatable *uc, Error **errp)
 
     /* Synchronize with in-flight iothread callbacks. */
     if (monitor_requires_iothread(mon)) {
-        aio_wait_bh_oneshot(iothread_get_aio_context(mon_iothread),
+        aio_wait_bh_oneshot(MONITOR(mon)->ctx,
                             monitor_qmp_iothread_quiesce, NULL);
     }
 
-- 
2.49.0



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

* [PATCH V10 07/15] virtio-vq-mapping: track iothread-vq-mapping references using device path
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (5 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 06/15] monitor: Update tracking iothread users with holder Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 08/15] virtio: use iothread_get/put_aio_context for thread pinning Zhang Chen
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  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         | 20 +++++++++++++++-----
 include/hw/virtio/iothread-vq-mapping.h |  6 +++++-
 4 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 6b92066aff..4b2cf3d52f 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1493,9 +1493,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;
@@ -1527,7 +1530,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..2cb48dd387 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,13 @@ 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);
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)holder,
+        };
 
-        /* Released in virtio_blk_vq_aio_context_cleanup() */
-        object_ref(OBJECT(iothread));
+        AioContext *ctx = iothread_ref_and_get_aio_context(iothread,
+                                                           &io_holder);
 
         if (node->value->vqs) {
             uint16List *vq;
@@ -120,13 +124,19 @@ 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));
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)holder,
+        };
+
+        iothread_put_aio_context(iothread, &io_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] 18+ messages in thread

* [PATCH V10 08/15] virtio: use iothread_get/put_aio_context for thread pinning
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (6 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 07/15] virtio-vq-mapping: track iothread-vq-mapping references using device path Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 09/15] net/colo: track IOThread references using path-based holder Zhang Chen
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  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           | 24 +++++++++++++-----------
 hw/scsi/virtio-scsi-dataplane.c | 23 +++++++++++++----------
 2 files changed, 26 insertions(+), 21 deletions(-)

diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 4b2cf3d52f..1865890fad 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1469,6 +1469,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,
@@ -1493,8 +1494,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,
@@ -1505,13 +1504,15 @@ 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);
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)path,
+        };
+        AioContext *ctx = iothread_ref_and_get_aio_context(conf->iothread,
+                                                           &io_holder);
         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++) {
@@ -1526,21 +1527,22 @@ 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));
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)path,
+        };
+        iothread_put_aio_context(conf->iothread, &io_holder);
     }
 
-    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..c71f33b41e 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,15 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
             return;
         }
     } else if (vs->conf.iothread) {
-        AioContext *ctx = iothread_get_aio_context(vs->conf.iothread);
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)path,
+        };
+        AioContext *ctx = iothread_ref_and_get_aio_context(vs->conf.iothread,
+                                                           &io_holder);
         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 +95,18 @@ 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));
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)path,
+        };
+        iothread_put_aio_context(vs->conf.iothread, &io_holder);
     }
 
     g_free(s->vq_aio_context);
-- 
2.49.0



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

* [PATCH V10 09/15] net/colo: track IOThread references using path-based holder
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (7 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 08/15] virtio: use iothread_get/put_aio_context for thread pinning Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 10/15] virtio-balloon: Update tracking iothread users with holder Zhang Chen
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  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 | 29 +++++++++++++++++++----------
 1 file changed, 19 insertions(+), 10 deletions(-)

diff --git a/net/colo-compare.c b/net/colo-compare.c
index 5986fb1e88..a7bbb1056d 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;
 
@@ -922,9 +923,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) +
@@ -964,8 +963,15 @@ 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));
+    IOThreadHolder io_holder = {
+        .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+        .u.qom_object.qom_path = (char *)path,
+    };
+
+    AioContext *ctx = iothread_ref_and_get_aio_context(s->iothread, &io_holder);
+
+    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,
@@ -1404,6 +1410,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) {
@@ -1430,18 +1437,20 @@ static void colo_compare_finalize(Object *obj)
     g_clear_pointer(&s->event_bh, qemu_bh_delete);
 
     if (s->iothread) {
-        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);
         }
 
         /* Release all unhandled packets after compare thread exited */
         g_queue_foreach(&s->conn_list, colo_flush_packets, s);
         AIO_WAIT_WHILE(NULL, !s->out_sendco.done);
 
-        object_unref(OBJECT(s->iothread));
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)path,
+        };
+        iothread_put_aio_context(s->iothread, &io_holder);
     }
 
     g_queue_clear(&s->conn_list);
-- 
2.49.0



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

* [PATCH V10 10/15] virtio-balloon: Update tracking iothread users with holder
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (8 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 09/15] net/colo: track IOThread references using path-based holder Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 11/15] vfio-user/proxy: Update tracking iothread users with holder name Zhang Chen
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  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 | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 4c5f486ba2..e9d023b4ec 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -892,14 +892,20 @@ 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));
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)path,
+        };
+
         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, &io_holder),
+                      virtio_ballloon_get_free_page_hints, s,
+                      &dev->mem_reentrancy_guard);
     }
 
     if (virtio_has_feature(s->host_features, VIRTIO_BALLOON_F_REPORTING)) {
@@ -919,9 +925,15 @@ 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));
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)path,
+        };
+
         qemu_bh_delete(s->free_page_bh);
-        object_unref(OBJECT(s->iothread));
         virtio_balloon_free_page_stop(s);
+        iothread_put_aio_context(s->iothread, &io_holder);
         precopy_remove_notifier(&s->free_page_hint_notify);
     }
     balloon_stats_destroy_timer(s);
-- 
2.49.0



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

* [PATCH V10 11/15] vfio-user/proxy: Update tracking iothread users with holder name
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (9 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 10/15] virtio-balloon: Update tracking iothread users with holder Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 12/15] xen-block: " Zhang Chen
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  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 | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c
index 0f7d8425d6..b4f749f245 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,11 @@ 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));
+    IOThreadHolder io_holder = {
+        .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+        .u.qom_object.qom_path = (char *)path,
+    };
 
     /* init defaults */
     proxy->max_xfer_size = VFIO_USER_DEF_MAX_XFER;
@@ -936,7 +942,8 @@ 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,
+                                                  &io_holder);
     proxy->req_bh = qemu_bh_new(vfio_user_request, proxy);
 
     QTAILQ_INIT(&proxy->outgoing);
@@ -967,6 +974,11 @@ 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));
+    IOThreadHolder io_holder = {
+        .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+        .u.qom_object.qom_path = (char *)path,
+    };
 
     qemu_mutex_lock(&proxy->lock);
 
@@ -1021,6 +1033,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, &io_holder);
+
     QLIST_REMOVE(proxy, next);
     if (QLIST_EMPTY(&vfio_user_sockets)) {
         iothread_destroy(vfio_user_iothread);
-- 
2.49.0



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

* [PATCH V10 12/15] xen-block: Update tracking iothread users with holder name
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (10 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 11/15] vfio-user/proxy: Update tracking iothread users with holder name Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 13/15] monitor/hmp: Add holders support for hmp_info_iothreads Zhang Chen
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  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 | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
index 48c2e315f3..b5bf8d359f 100644
--- a/hw/block/dataplane/xen-block.c
+++ b/hw/block/dataplane/xen-block.c
@@ -621,9 +621,15 @@ XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev,
     QLIST_INIT(&dataplane->freelist);
 
     if (iothread) {
+        g_autofree char *path = object_get_canonical_path(OBJECT(xendev));
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)path,
+        };
+
         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,
+                                                          &io_holder);
     } else {
         dataplane->ctx = qemu_get_aio_context();
     }
@@ -652,7 +658,14 @@ 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));
+        IOThreadHolder io_holder = {
+            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
+            .u.qom_object.qom_path = (char *)path,
+        };
+
+        iothread_put_aio_context(dataplane->iothread, &io_holder);
     }
 
     g_free(dataplane);
-- 
2.49.0



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

* [PATCH V10 13/15] monitor/hmp: Add holders support for hmp_info_iothreads
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (11 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 12/15] xen-block: " Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 14/15] iothread: simplify API by merging iothread_get_aio_context variants Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 15/15] tests/unit/iothread: Update the iothread_get_aio_context Zhang Chen
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  Cc: Zhang Chen

Exposed the iothread holders 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>
---
 monitor/hmp-cmds.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index e9fb8d827a..cd2d9c1d59 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -240,11 +240,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_EXPORT:
+                    monitor_printf(mon, "[block-export: %s]",
+                                   holder->u.block_export.export_name);
+                    break;
+                case IO_THREAD_HOLDER_KIND_QOM_OBJECT:
+                    monitor_printf(mon, "[qom-path: %s]",
+                                   holder->u.qom_object.qom_path);
+                    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);
-- 
2.49.0



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

* [PATCH V10 14/15] iothread: simplify API by merging iothread_get_aio_context variants
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (12 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 13/15] monitor/hmp: Add holders support for hmp_info_iothreads Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  2026-07-16 14:40 ` [PATCH V10 15/15] tests/unit/iothread: Update the iothread_get_aio_context Zhang Chen
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  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           |  5 ++---
 hw/block/dataplane/xen-block.c  |  4 ++--
 hw/block/virtio-blk.c           |  4 ++--
 hw/scsi/virtio-scsi-dataplane.c |  4 ++--
 hw/vfio-user/proxy.c            |  3 +--
 hw/virtio/iothread-vq-mapping.c |  3 +--
 hw/virtio/virtio-balloon.c      |  2 +-
 include/system/iothread.h       | 16 ++++++++--------
 iothread.c                      |  9 ++-------
 monitor/monitor.c               |  2 +-
 net/colo-compare.c              |  2 +-
 11 files changed, 23 insertions(+), 31 deletions(-)

diff --git a/block/export/export.c b/block/export/export.c
index 4dbd71de6a..32c7ef7d28 100644
--- a/block/export/export.c
+++ b/block/export/export.c
@@ -146,7 +146,7 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
             .u.block_export.export_name = (char *)holder_name,
         };
 
-        new_ctx = iothread_ref_and_get_aio_context(iothread, &holder);
+        new_ctx = iothread_get_aio_context(iothread, &holder);
         multithread_count = 1;
         local_iothreads = g_new0(IOThread *, 1);
         local_iothreads[0] = iothread;
@@ -190,8 +190,7 @@ 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);
+            multithread_ctxs[i++] = iothread_get_aio_context(iothread, &holder);
         }
         assert(i == multithread_count);
     }
diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
index b5bf8d359f..46bfd62f5a 100644
--- a/hw/block/dataplane/xen-block.c
+++ b/hw/block/dataplane/xen-block.c
@@ -628,8 +628,8 @@ XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev,
         };
 
         dataplane->iothread = iothread;
-        dataplane->ctx = iothread_ref_and_get_aio_context(dataplane->iothread,
-                                                          &io_holder);
+        dataplane->ctx = iothread_get_aio_context(dataplane->iothread,
+                                                  &io_holder);
     } else {
         dataplane->ctx = qemu_get_aio_context();
     }
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 1865890fad..2c296986ee 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -1508,8 +1508,8 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
             .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
             .u.qom_object.qom_path = (char *)path,
         };
-        AioContext *ctx = iothread_ref_and_get_aio_context(conf->iothread,
-                                                           &io_holder);
+        AioContext *ctx = iothread_get_aio_context(conf->iothread,
+                                                   &io_holder);
         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 c71f33b41e..76ab22610f 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -78,8 +78,8 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
             .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
             .u.qom_object.qom_path = (char *)path,
         };
-        AioContext *ctx = iothread_ref_and_get_aio_context(vs->conf.iothread,
-                                                           &io_holder);
+        AioContext *ctx = iothread_get_aio_context(vs->conf.iothread,
+                                                   &io_holder);
         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 b4f749f245..8ad0f4cfd3 100644
--- a/hw/vfio-user/proxy.c
+++ b/hw/vfio-user/proxy.c
@@ -942,8 +942,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,
-                                                  &io_holder);
+    proxy->ctx = iothread_get_aio_context(vfio_user_iothread, &io_holder);
     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 2cb48dd387..727358a483 100644
--- a/hw/virtio/iothread-vq-mapping.c
+++ b/hw/virtio/iothread-vq-mapping.c
@@ -99,8 +99,7 @@ bool iothread_vq_mapping_apply(
             .u.qom_object.qom_path = (char *)holder,
         };
 
-        AioContext *ctx = iothread_ref_and_get_aio_context(iothread,
-                                                           &io_holder);
+        AioContext *ctx = iothread_get_aio_context(iothread, &io_holder);
 
         if (node->value->vqs) {
             uint16List *vq;
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index e9d023b4ec..4405a87ed0 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -903,7 +903,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, &io_holder),
+                      iothread_get_aio_context(s->iothread, &io_holder),
                       virtio_ballloon_get_free_page_hints, s,
                       &dev->mem_reentrancy_guard);
     }
diff --git a/include/system/iothread.h b/include/system/iothread.h
index 3cce5eeb09..3a6010b9f0 100644
--- a/include/system/iothread.h
+++ b/include/system/iothread.h
@@ -73,22 +73,22 @@ DECLARE_INSTANCE_CHECKER(IOThread, IOTHREAD,
 
 char *iothread_get_id(IOThread *iothread);
 IOThread *iothread_by_id(const char *id);
-/*
- * The iothread_get_aio_context() and iothread_put_aio_context() are not
- * thread-safe and must be called under the Big QEMU Lock (BQL).
- */
-AioContext *iothread_get_aio_context(IOThread *iothread);
+
 /*
  * Normally the IOThread's AioContext is fetched using
- * iothread_ref_and_get_aio_context(), but there are legacy callers
+ * iothread_get_aio_context(), but there are legacy callers
  * without a clear ref/unref lifecycle. They cannot unref (e.g. because
  * they do not have an IOThread pointer), so provide an unsafe way to
  * fetch the AioContext without holding a reference count. This unsafe
  * API serves legacy callers - do not use it in new code.
  */
 AioContext *iothread_unsafe_get_aio_context(IOThread *iothread);
-AioContext *iothread_ref_and_get_aio_context(IOThread *iothread,
-                                             const IOThreadHolder *holder);
+/*
+ * The iothread_get_aio_context() and iothread_put_aio_context() are not
+ * thread-safe and must be called under the Big QEMU Lock (BQL).
+ */
+AioContext *iothread_get_aio_context(IOThread *iothread,
+                                     const IOThreadHolder *holder);
 void iothread_put_aio_context(IOThread *iothread, const IOThreadHolder *holder);
 GMainContext *iothread_get_g_main_context(IOThread *iothread);
 
diff --git a/iothread.c b/iothread.c
index 157bb4d302..9d278efc45 100644
--- a/iothread.c
+++ b/iothread.c
@@ -429,13 +429,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 IOThreadHolder *holder)
+AioContext *iothread_get_aio_context(IOThread *iothread,
+                                     const IOThreadHolder *holder)
 {
     /* Add IOThreadHolder to the list */
     iothread_ref(iothread, holder);
diff --git a/monitor/monitor.c b/monitor/monitor.c
index 688bb85c81..0b75f9f69b 100644
--- a/monitor/monitor.c
+++ b/monitor/monitor.c
@@ -749,7 +749,7 @@ static void monitor_complete(UserCreatable *uc, Error **errp)
             .u.qom_object.qom_path = path,
         };
 
-        mon->ctx = iothread_ref_and_get_aio_context(mon_iothread, &io_holder);
+        mon->ctx = iothread_get_aio_context(mon_iothread, &io_holder);
         ctx = mon->ctx;
     } else {
         ctx = qemu_get_aio_context();
diff --git a/net/colo-compare.c b/net/colo-compare.c
index a7bbb1056d..6cd642d0b7 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -969,7 +969,7 @@ static void colo_compare_iothread(CompareState *s)
         .u.qom_object.qom_path = (char *)path,
     };
 
-    AioContext *ctx = iothread_ref_and_get_aio_context(s->iothread, &io_holder);
+    AioContext *ctx = iothread_get_aio_context(s->iothread, &io_holder);
 
     s->iothread_ctx = ctx;
     s->worker_context = iothread_get_g_main_context(s->iothread);
-- 
2.49.0



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

* [PATCH V10 15/15] tests/unit/iothread: Update the iothread_get_aio_context
  2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
                   ` (13 preceding siblings ...)
  2026-07-16 14:40 ` [PATCH V10 14/15] iothread: simplify API by merging iothread_get_aio_context variants Zhang Chen
@ 2026-07-16 14:40 ` Zhang Chen
  14 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 14:40 UTC (permalink / raw)
  To: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng,
	Daniel P . Berrangé
  Cc: Zhang Chen

Update the iothread_get_aio_context and the
iothread_put_aio_context for tests.

Signed-off-by: Zhang Chen <zhangckid@gmail.com>
---
 tests/unit/iothread.c             | 16 ++++++++++------
 tests/unit/iothread.h             |  6 +++++-
 tests/unit/test-aio-multithread.c |  5 ++++-
 tests/unit/test-bdrv-drain.c      | 18 +++++++++++++-----
 tests/unit/test-block-iothread.c  | 21 ++++++++++++++-------
 5 files changed, 46 insertions(+), 20 deletions(-)

diff --git a/tests/unit/iothread.c b/tests/unit/iothread.c
index a363bf8f70..6cac850035 100644
--- a/tests/unit/iothread.c
+++ b/tests/unit/iothread.c
@@ -30,12 +30,21 @@ struct IOThread {
     bool stopping;
 };
 
+AioContext *iothread_get_aio_context(IOThread *iothread, IOThreadHolder *holder)
+{
+    return iothread->ctx;
+}
+
+void iothread_put_aio_context(IOThread *iothread, IOThreadHolder *holder)
+{
+}
+
 static void iothread_init_gcontext(IOThread *iothread)
 {
     GSource *source;
 
     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_attach(source, iothread->worker_context);
     g_source_unref(source);
     iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE);
@@ -113,8 +122,3 @@ IOThread *iothread_new(void)
     qemu_mutex_unlock(&iothread->init_done_lock);
     return iothread;
 }
-
-AioContext *iothread_get_aio_context(IOThread *iothread)
-{
-    return iothread->ctx;
-}
diff --git a/tests/unit/iothread.h b/tests/unit/iothread.h
index eb4d0c77f8..42992c171d 100644
--- a/tests/unit/iothread.h
+++ b/tests/unit/iothread.h
@@ -17,9 +17,13 @@
 #include "qemu/thread.h"
 
 typedef struct IOThread IOThread;
+typedef struct IOThreadHolder IOThreadHolder;
+
+AioContext *iothread_get_aio_context(IOThread *iothread,
+                                     IOThreadHolder *holder);
+void iothread_put_aio_context(IOThread *iothread, IOThreadHolder *holder);
 
 IOThread *iothread_new(void);
 void iothread_join(IOThread *iothread);
-AioContext *iothread_get_aio_context(IOThread *iothread);
 
 #endif
diff --git a/tests/unit/test-aio-multithread.c b/tests/unit/test-aio-multithread.c
index 9179cdc6a3..92fa210ddc 100644
--- a/tests/unit/test-aio-multithread.c
+++ b/tests/unit/test-aio-multithread.c
@@ -69,7 +69,7 @@ static void create_aio_contexts(void)
 
     for (i = 0; i < NUM_CONTEXTS; i++) {
         threads[i] = iothread_new();
-        ctx[i] = iothread_get_aio_context(threads[i]);
+        ctx[i] = iothread_get_aio_context(threads[i], NULL);
     }
 
     qemu_event_init(&done_event, false);
@@ -87,6 +87,9 @@ static void join_aio_contexts(void)
     for (i = 0; i < NUM_CONTEXTS; i++) {
         aio_context_ref(ctx[i]);
     }
+    for (i = 0; i < NUM_CONTEXTS; i++) {
+        iothread_put_aio_context(threads[i], NULL);
+    }
     for (i = 0; i < NUM_CONTEXTS; i++) {
         iothread_join(threads[i]);
     }
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
index 43b0ba8648..05e52e89ac 100644
--- a/tests/unit/test-bdrv-drain.c
+++ b/tests/unit/test-bdrv-drain.c
@@ -537,8 +537,8 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread)
 
     IOThread *a = iothread_new();
     IOThread *b = iothread_new();
-    AioContext *ctx_a = iothread_get_aio_context(a);
-    AioContext *ctx_b = iothread_get_aio_context(b);
+    AioContext *ctx_a = iothread_get_aio_context(a, NULL);
+    AioContext *ctx_b = iothread_get_aio_context(b, NULL);
 
     QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0);
 
@@ -612,6 +612,8 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread)
 
     bdrv_unref(bs);
     blk_unref(blk);
+    iothread_put_aio_context(a, NULL);
+    iothread_put_aio_context(b, NULL);
 
 out:
     iothread_join(a);
@@ -762,7 +764,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type,
         AioContext *ctx;
 
         iothread = iothread_new();
-        ctx = iothread_get_aio_context(iothread);
+        ctx = iothread_get_aio_context(iothread, NULL);
         blk_set_aio_context(blk_src, ctx, &error_abort);
     }
 
@@ -892,6 +894,10 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type,
     bdrv_unref(src_overlay);
     bdrv_unref(target);
 
+    if (use_iothread) {
+        iothread_put_aio_context(iothread, NULL);
+    }
+
     if (iothread) {
         iothread_join(iothread);
     }
@@ -1398,8 +1404,8 @@ static void test_set_aio_context(void)
     BlockDriverState *bs;
     IOThread *a = iothread_new();
     IOThread *b = iothread_new();
-    AioContext *ctx_a = iothread_get_aio_context(a);
-    AioContext *ctx_b = iothread_get_aio_context(b);
+    AioContext *ctx_a = iothread_get_aio_context(a, NULL);
+    AioContext *ctx_b = iothread_get_aio_context(b, NULL);
 
     bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
                               &error_abort);
@@ -1410,6 +1416,8 @@ static void test_set_aio_context(void)
     bdrv_try_change_aio_context(bs, qemu_get_aio_context(), NULL, &error_abort);
 
     bdrv_unref(bs);
+    iothread_put_aio_context(a, NULL);
+    iothread_put_aio_context(b, NULL);
     iothread_join(a);
     iothread_join(b);
 }
diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c
index 5273ff235a..8b7ac6f980 100644
--- a/tests/unit/test-block-iothread.c
+++ b/tests/unit/test-block-iothread.c
@@ -466,7 +466,7 @@ static void test_sync_op(const void *opaque)
 {
     const SyncOpTest *t = opaque;
     IOThread *iothread = iothread_new();
-    AioContext *ctx = iothread_get_aio_context(iothread);
+    AioContext *ctx = iothread_get_aio_context(iothread, NULL);
     BlockBackend *blk;
     BlockDriverState *bs;
     BdrvChild *c;
@@ -493,6 +493,7 @@ static void test_sync_op(const void *opaque)
 
     bdrv_unref(bs);
     blk_unref(blk);
+    iothread_put_aio_context(iothread, NULL);
 }
 
 typedef struct TestBlockJob {
@@ -549,7 +550,7 @@ BlockJobDriver test_job_driver = {
 static void test_attach_blockjob(void)
 {
     IOThread *iothread = iothread_new();
-    AioContext *ctx = iothread_get_aio_context(iothread);
+    AioContext *ctx = iothread_get_aio_context(iothread, NULL);
     BlockBackend *blk;
     BlockDriverState *bs;
     TestBlockJob *tjob;
@@ -595,6 +596,7 @@ static void test_attach_blockjob(void)
 
     bdrv_unref(bs);
     blk_unref(blk);
+    iothread_put_aio_context(iothread, NULL);
 }
 
 /*
@@ -612,7 +614,7 @@ static void test_attach_blockjob(void)
 static void test_propagate_basic(void)
 {
     IOThread *iothread = iothread_new();
-    AioContext *ctx = iothread_get_aio_context(iothread);
+    AioContext *ctx = iothread_get_aio_context(iothread, NULL);
     AioContext *main_ctx;
     BlockBackend *blk;
     BlockDriverState *bs_a, *bs_b, *bs_verify;
@@ -658,6 +660,7 @@ static void test_propagate_basic(void)
     bdrv_unref(bs_b);
     bdrv_unref(bs_a);
     blk_unref(blk);
+    iothread_put_aio_context(iothread, NULL);
 }
 
 /*
@@ -676,7 +679,7 @@ static void test_propagate_basic(void)
 static void test_propagate_diamond(void)
 {
     IOThread *iothread = iothread_new();
-    AioContext *ctx = iothread_get_aio_context(iothread);
+    AioContext *ctx = iothread_get_aio_context(iothread, NULL);
     AioContext *main_ctx;
     BlockBackend *blk;
     BlockDriverState *bs_a, *bs_b, *bs_c, *bs_verify;
@@ -736,12 +739,13 @@ static void test_propagate_diamond(void)
     bdrv_unref(bs_c);
     bdrv_unref(bs_b);
     bdrv_unref(bs_a);
+    iothread_put_aio_context(iothread, NULL);
 }
 
 static void test_propagate_mirror(void)
 {
     IOThread *iothread = iothread_new();
-    AioContext *ctx = iothread_get_aio_context(iothread);
+    AioContext *ctx = iothread_get_aio_context(iothread, NULL);
     AioContext *main_ctx = qemu_get_aio_context();
     BlockDriverState *src, *target, *filter;
     BlockBackend *blk;
@@ -807,12 +811,13 @@ static void test_propagate_mirror(void)
     blk_unref(blk);
     bdrv_unref(src);
     bdrv_unref(target);
+    iothread_put_aio_context(iothread, NULL);
 }
 
 static void test_attach_second_node(void)
 {
     IOThread *iothread = iothread_new();
-    AioContext *ctx = iothread_get_aio_context(iothread);
+    AioContext *ctx = iothread_get_aio_context(iothread, NULL);
     AioContext *main_ctx = qemu_get_aio_context();
     BlockBackend *blk;
     BlockDriverState *bs, *filter;
@@ -840,12 +845,13 @@ static void test_attach_second_node(void)
     bdrv_unref(filter);
     bdrv_unref(bs);
     blk_unref(blk);
+    iothread_put_aio_context(iothread, NULL);
 }
 
 static void test_attach_preserve_blk_ctx(void)
 {
     IOThread *iothread = iothread_new();
-    AioContext *ctx = iothread_get_aio_context(iothread);
+    AioContext *ctx = iothread_get_aio_context(iothread, NULL);
     BlockBackend *blk;
     BlockDriverState *bs;
 
@@ -871,6 +877,7 @@ static void test_attach_preserve_blk_ctx(void)
     blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort);
     bdrv_unref(bs);
     blk_unref(blk);
+    iothread_put_aio_context(iothread, NULL);
 }
 
 int main(int argc, char **argv)
-- 
2.49.0



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

* Re: [PATCH V10 06/15] monitor: Update tracking iothread users with holder
  2026-07-16 14:40 ` [PATCH V10 06/15] monitor: Update tracking iothread users with holder Zhang Chen
@ 2026-07-16 15:11   ` Daniel P. Berrangé
  2026-07-16 16:18     ` Zhang Chen
  0 siblings, 1 reply; 18+ messages in thread
From: Daniel P. Berrangé @ 2026-07-16 15:11 UTC (permalink / raw)
  To: Zhang Chen
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng

On Thu, Jul 16, 2026 at 10:40:31PM +0800, Zhang Chen wrote:
> Based on monitor QOM path tracking iothread users with holder.
> Introduce 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          | 22 ++++++++++++++++++++--
>  monitor/qmp.c              |  5 +++--
>  3 files changed, 26 insertions(+), 4 deletions(-)
> 
> diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
> index 23829f32f9..caecceec93 100644
> --- a/monitor/monitor-internal.h
> +++ b/monitor/monitor-internal.h
> @@ -153,6 +153,9 @@ struct Monitor {
>      guint out_watch;
>      int mux_out;
>      int reset_seen;
> +
> +    /* iothread context */
> +    AioContext *ctx;
>  };
>  
>  struct MonitorHMPClass {
> diff --git a/monitor/monitor.c b/monitor/monitor.c
> index ed195fd97b..688bb85c81 100644
> --- a/monitor/monitor.c
> +++ b/monitor/monitor.c
> @@ -573,7 +573,7 @@ void 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);
> @@ -668,6 +668,17 @@ void monitor_cleanup(void)
>          qemu_mutex_unlock(&monitor_lock);
>          monitor_flush(mon);
>          qemu_mutex_lock(&monitor_lock);
> +
> +        if (mon_iothread) {
> +            g_autofree char *path = object_get_canonical_path(OBJECT(mon));
> +            const IOThreadHolder io_holder = {
> +                .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
> +                .u.qom_object.qom_path = path,
> +            };
> +
> +            iothread_put_aio_context(mon_iothread, &io_holder);
> +            mon->ctx = NULL;
> +        }
>          object_unparent(OBJECT(mon));
>      }
>      qemu_mutex_unlock(&monitor_lock);
> @@ -732,7 +743,14 @@ static void monitor_complete(UserCreatable *uc, Error **errp)
>              mon_iothread = iothread_create("mon_iothread", &error_abort);
>          }
>  
> -        ctx = iothread_get_aio_context(mon_iothread);
> +        g_autofree char *path = object_get_canonical_path(OBJECT(mon));
> +        const IOThreadHolder io_holder = {
> +            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
> +            .u.qom_object.qom_path = path,
> +        };
> +
> +        mon->ctx = iothread_ref_and_get_aio_context(mon_iothread, &io_holder);
> +        ctx = mon->ctx;
>      } else {
>          ctx = qemu_get_aio_context();
>      }

Even though QMP only takes the first branch, I'd be more
comfortable if we set mon->ctx in both branches of this
condition, so we can always assume that mon->ctx is
non-NULL.

> diff --git a/monitor/qmp.c b/monitor/qmp.c
> index 223e0643c2..b16bd77688 100644
> --- a/monitor/qmp.c
> +++ b/monitor/qmp.c
> @@ -732,7 +732,8 @@ static void monitor_qmp_complete(UserCreatable *uc, Error **errp)
>           * thread.  Schedule a bottom half.
>           */
>          mon->setup_pending = true;
> -        aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
> +
> +        aio_bh_schedule_oneshot(MONITOR(mon)->ctx,
>                                  monitor_qmp_setup_handlers_bh, mon);
>          /* The bottom half will add @mon to @mon_list */
>      } else {
> @@ -787,7 +788,7 @@ static bool monitor_qmp_prepare_delete(UserCreatable *uc, Error **errp)
>  
>      /* Synchronize with in-flight iothread callbacks. */
>      if (monitor_requires_iothread(mon)) {
> -        aio_wait_bh_oneshot(iothread_get_aio_context(mon_iothread),
> +        aio_wait_bh_oneshot(MONITOR(mon)->ctx,
>                              monitor_qmp_iothread_quiesce, NULL);
>      }

...this unconditionally accesses mon->ctx, and so will surely hit
a NULL

>  
> -- 
> 2.49.0
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



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

* Re: [PATCH V10 06/15] monitor: Update tracking iothread users with holder
  2026-07-16 15:11   ` Daniel P. Berrangé
@ 2026-07-16 16:18     ` Zhang Chen
  0 siblings, 0 replies; 18+ messages in thread
From: Zhang Chen @ 2026-07-16 16:18 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: qemu-devel, Dr . David Alan Gilbert, Eric Blake,
	Markus Armbruster, Michael S . Tsirkin, Stefan Hajnoczi,
	Paolo Bonzini, Kevin Wolf, Jason Wang, Fam Zheng

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

On Thu, Jul 16, 2026, 11:11 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:

> On Thu, Jul 16, 2026 at 10:40:31PM +0800, Zhang Chen wrote:
> > Based on monitor QOM path tracking iothread users with holder.
> > Introduce 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          | 22 ++++++++++++++++++++--
> >  monitor/qmp.c              |  5 +++--
> >  3 files changed, 26 insertions(+), 4 deletions(-)
> >
> > diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
> > index 23829f32f9..caecceec93 100644
> > --- a/monitor/monitor-internal.h
> > +++ b/monitor/monitor-internal.h
> > @@ -153,6 +153,9 @@ struct Monitor {
> >      guint out_watch;
> >      int mux_out;
> >      int reset_seen;
> > +
> > +    /* iothread context */
> > +    AioContext *ctx;
> >  };
> >
> >  struct MonitorHMPClass {
> > diff --git a/monitor/monitor.c b/monitor/monitor.c
> > index ed195fd97b..688bb85c81 100644
> > --- a/monitor/monitor.c
> > +++ b/monitor/monitor.c
> > @@ -573,7 +573,7 @@ void 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);
> > @@ -668,6 +668,17 @@ void monitor_cleanup(void)
> >          qemu_mutex_unlock(&monitor_lock);
> >          monitor_flush(mon);
> >          qemu_mutex_lock(&monitor_lock);
> > +
> > +        if (mon_iothread) {
> > +            g_autofree char *path =
> object_get_canonical_path(OBJECT(mon));
> > +            const IOThreadHolder io_holder = {
> > +                .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
> > +                .u.qom_object.qom_path = path,
> > +            };
> > +
> > +            iothread_put_aio_context(mon_iothread, &io_holder);
> > +            mon->ctx = NULL;
> > +        }
> >          object_unparent(OBJECT(mon));
> >      }
> >      qemu_mutex_unlock(&monitor_lock);
> > @@ -732,7 +743,14 @@ static void monitor_complete(UserCreatable *uc,
> Error **errp)
> >              mon_iothread = iothread_create("mon_iothread",
> &error_abort);
> >          }
> >
> > -        ctx = iothread_get_aio_context(mon_iothread);
> > +        g_autofree char *path = object_get_canonical_path(OBJECT(mon));
> > +        const IOThreadHolder io_holder = {
> > +            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
> > +            .u.qom_object.qom_path = path,
> > +        };
> > +
> > +        mon->ctx = iothread_ref_and_get_aio_context(mon_iothread,
> &io_holder);
> > +        ctx = mon->ctx;
> >      } else {
> >          ctx = qemu_get_aio_context();
> >      }
>
> Even though QMP only takes the first branch, I'd be more
> comfortable if we set mon->ctx in both branches of this
> condition, so we can always assume that mon->ctx is
> non-NULL.
>

Good catch,I will send a RESEND version for v10 as a quick fix.

Thanks
Chen


> > diff --git a/monitor/qmp.c b/monitor/qmp.c
> > index 223e0643c2..b16bd77688 100644
> > --- a/monitor/qmp.c
> > +++ b/monitor/qmp.c
> > @@ -732,7 +732,8 @@ static void monitor_qmp_complete(UserCreatable *uc,
> Error **errp)
> >           * thread.  Schedule a bottom half.
> >           */
> >          mon->setup_pending = true;
> > -        aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
> > +
> > +        aio_bh_schedule_oneshot(MONITOR(mon)->ctx,
> >                                  monitor_qmp_setup_handlers_bh, mon);
> >          /* The bottom half will add @mon to @mon_list */
> >      } else {
> > @@ -787,7 +788,7 @@ static bool monitor_qmp_prepare_delete(UserCreatable
> *uc, Error **errp)
> >
> >      /* Synchronize with in-flight iothread callbacks. */
> >      if (monitor_requires_iothread(mon)) {
> > -        aio_wait_bh_oneshot(iothread_get_aio_context(mon_iothread),
> > +        aio_wait_bh_oneshot(MONITOR(mon)->ctx,
> >                              monitor_qmp_iothread_quiesce, NULL);
> >      }
>
> ...this unconditionally accesses mon->ctx, and so will surely hit
> a NULL
>
> >
> > --
> > 2.49.0
> >
>
> With regards,
> Daniel
> --
> |: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
> |: https://libvirt.org          ~~          https://entangle-photo.org :|
> |: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|
>
>

[-- Attachment #2: Type: text/html, Size: 6918 bytes --]

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

end of thread, other threads:[~2026-07-16 16:19 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-16 14:40 [PATCH V10 00/15] iothread: Support tracking and querying IOThread holders Zhang Chen
2026-07-16 14:40 ` [PATCH V10 01/15] qapi/misc: Fix missed query-iothreads items Zhang Chen
2026-07-16 14:40 ` [PATCH V10 02/15] iothread: introduce iothread_ref/unref to track attached devices Zhang Chen
2026-07-16 14:40 ` [PATCH V10 03/15] iothread: tracking iothread users with holder name Zhang Chen
2026-07-16 14:40 ` [PATCH V10 04/15] iothread: introduce iothread_unsafe_get_aio_context() Zhang Chen
2026-07-16 14:40 ` [PATCH V10 05/15] block/export: track IOThread reference in BlockExport Zhang Chen
2026-07-16 14:40 ` [PATCH V10 06/15] monitor: Update tracking iothread users with holder Zhang Chen
2026-07-16 15:11   ` Daniel P. Berrangé
2026-07-16 16:18     ` Zhang Chen
2026-07-16 14:40 ` [PATCH V10 07/15] virtio-vq-mapping: track iothread-vq-mapping references using device path Zhang Chen
2026-07-16 14:40 ` [PATCH V10 08/15] virtio: use iothread_get/put_aio_context for thread pinning Zhang Chen
2026-07-16 14:40 ` [PATCH V10 09/15] net/colo: track IOThread references using path-based holder Zhang Chen
2026-07-16 14:40 ` [PATCH V10 10/15] virtio-balloon: Update tracking iothread users with holder Zhang Chen
2026-07-16 14:40 ` [PATCH V10 11/15] vfio-user/proxy: Update tracking iothread users with holder name Zhang Chen
2026-07-16 14:40 ` [PATCH V10 12/15] xen-block: " Zhang Chen
2026-07-16 14:40 ` [PATCH V10 13/15] monitor/hmp: Add holders support for hmp_info_iothreads Zhang Chen
2026-07-16 14:40 ` [PATCH V10 14/15] iothread: simplify API by merging iothread_get_aio_context variants Zhang Chen
2026-07-16 14:40 ` [PATCH V10 15/15] tests/unit/iothread: Update the iothread_get_aio_context 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.