On Thu, Feb 19, 2026 at 4:53 AM Stefan Hajnoczi <[email protected]> wrote:
>
> 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?

Will remove it in next version.

>
> > +    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.

OK, will change the name next version.


>
> 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.

Make sense, I will implement it in next version.

>
> >
> >      /* 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.

OK.

>
> > +}
> > +
> > +/*
> > + * 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.

OK, will use the g_strdup() in ref side next version.

>
> > +            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.

Good catch.

Thanks
Chen

>
> > +                        holder);
> > +        }
> > +    } else {
> > +        error_report("iohtread_unref counter error. attached_dev=%s", 
> > holder);
> > +    }
> > +}
> > +
> >  static void *iothread_run(void *opaque)
> >  {
> >      IOThread *iothread = opaque;
> > --
> > 2.49.0
> >

Reply via email to