On Sat, Feb 14, 2026 at 01:16:20AM +0800, Zhang Chen wrote: > Currently, IOThreads do not maintain a record of which devices are > associated with them. This makes it difficult to monitor the > workload distribution of IOThreads, especially in complex > hotplug scenarios involving multiple virtio-blk or virtio-scsi devices. > > This patch introduces a reference counting and tracking mechanism > within the IOThread object: > > - iothread_ref(): Increments the attachment counter and prepends the > device's QOM path to a list. Note: The IOThread takes ownership of > the passed 'holder' string. > - iothread_unref(): Searches for the device path using a custom > string comparison (g_strcmp0), decrements the counter, and > releases the associated memory upon a successful match. > - attached_cnt: Tracks the number of active device attachments. > - attached_dev: A GList storing the QOM paths of attached devices > for runtime introspection. > > This infrastructure allows management tools and QMP commands to > query the attachment status of IOThreads. > > Signed-off-by: Zhang Chen <[email protected]> > --- > include/system/iothread.h | 4 ++++ > iothread.c | 35 +++++++++++++++++++++++++++++++++++ > 2 files changed, 39 insertions(+) > > diff --git a/include/system/iothread.h b/include/system/iothread.h > index e26d13c6c7..4a2ffdb9bd 100644 > --- a/include/system/iothread.h > +++ b/include/system/iothread.h > @@ -33,6 +33,8 @@ struct IOThread { > bool stopping; /* has iothread_stop() been called? */ > bool running; /* should iothread_run() continue? */ > int thread_id; > + int attached_cnt;
Is this field necessary given that the user can see the list of holders?
> + GList *attached_dev;
IOThreads are general eventloop concept in QEMU and not limited to
device emulation. Non-device code may also wish to add itself (e.g.
block/export/export.c). Please call this GList *holders.
This raises questions:
- Is it mandatory for users of the IOThread's AioContext to add
themselves?
- How does it relate to object_ref()/object_unref()?
I would prefer to set things up as follows:
It is mandatory for users of the IOThread's AioContext to add
themselves. This is enforced by modifying iothread_get_aio_context() to
take a string argument with the name of the holder.
iothread_get_aio_context() is also extended to call
object_ref(OBJECT(iothread)). This guarantees that the IOThread and its
AioContext remain alive as long as there is a holder. (Some users
already do this themselves, so I think it makes sense to build this
behavior into the API so that all users get it automatically.)
A new iothread_put_aio_context() function is introduced to remove the
hold and object_unref(OBJECT(iothread)).
All existing callers of iothread_get_aio_context() need to be updated to
pass in their QOM object path as the name of the holder, remove their own
object_ref() call (if present), and call iothread_put_aio_context() at
the appropriate point.
>
> /* AioContext poll parameters */
> int64_t poll_max_ns;
> @@ -48,6 +50,8 @@ char *iothread_get_id(IOThread *iothread);
> IOThread *iothread_by_id(const char *id);
> AioContext *iothread_get_aio_context(IOThread *iothread);
> GMainContext *iothread_get_g_main_context(IOThread *iothread);
> +void iothread_ref(IOThread *iothread, const char* holder);
> +void iothread_unref(IOThread *iothread, const char* holder);
>
> /*
> * Helpers used to allocate iothreads for internal use. These
> diff --git a/iothread.c b/iothread.c
> index caf68e0764..b869637497 100644
> --- a/iothread.c
> +++ b/iothread.c
> @@ -36,6 +36,41 @@
> #define IOTHREAD_POLL_MAX_NS_DEFAULT 0ULL
> #endif
>
> +/*
> + * Increment iothread's reference count,
> + * and add holder device path to the list.
> + */
> +void iothread_ref(IOThread *iothread, const char *holder)
> +{
> + iothread->attached_cnt++;
> + iothread->attached_dev = g_list_prepend(iothread->attached_dev,
> + (gpointer)holder);
It's safer to g_strdup(holder) so the caller doesn't need to worry about
the lifetime of the argument. This is not a performance-critical code
path so the cost of the strdup is acceptable.
> +}
> +
> +/*
> + * Decrement iothread's reference count,
> + * and delete holder device path from the list.
> + */
> +void iothread_unref(IOThread *iothread, const char *holder)
> +{
> + if (iothread->attached_cnt > 0) {
> + GList *link = g_list_find_custom(iothread->attached_dev,
> + holder, (GCompareFunc)g_strcmp0);
> +
> + if (link) {
> + g_free(link->data);
iothread_ref() originally took a _const_ char * so it must not free the
pointer here. The reason why the compiler didn't complain was because
iothread_ref() cast the pointer to non-const gpointer. In any case, as
mentioned above, it's probably best to use g_strdup() and avoid placing
constraints on the lifetime of the caller's string.
> + iothread->attached_dev =
> g_list_delete_link(iothread->attached_dev,
> + link);
> + iothread->attached_cnt--;
> + } else {
> + error_report("iohtread_unref can't find device. attached_dev=%s",
"iohtread" typo here and in the line below.
> + holder);
> + }
> + } else {
> + error_report("iohtread_unref counter error. attached_dev=%s",
> holder);
> + }
> +}
> +
> static void *iothread_run(void *opaque)
> {
> IOThread *iothread = opaque;
> --
> 2.49.0
>
signature.asc
Description: PGP signature
