On Mon, Aug 24, 2026 at 3:24 PM Markus Armbruster <[email protected]> wrote:
>
> Zhang Chen <[email protected]> writes:
>
> > On Thu, Aug 20, 2026 at 4:17 PM Markus Armbruster <[email protected]> wrote:
> >>
> >> Zhang Chen <[email protected]> writes:
> >>
> >> > Display holder information in info iothreads to aid manual debugging.
> >> > Omit the holders line when an IOThread has no holders.
> >> >
> >> > Signed-off-by: Zhang Chen <[email protected]>
> >> > ---
> >> >  monitor/hmp-cmds.c | 21 +++++++++++++++++++++
> >> >  1 file changed, 21 insertions(+)
> >> >
> >> > diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
> >> > index e9fb8d827a..41a813e79f 100644
> >> > --- a/monitor/hmp-cmds.c
> >> > +++ b/monitor/hmp-cmds.c
> >> > @@ -240,11 +240,32 @@ 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=");
> >> > +        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_id);
> >> > +                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;
> >>
> >> This is unreachable.  Please use
> >>
> >>                default:
> >>                    g_assert_not_reached();
> >>
> >> to make it obvious.
> >>
> >> > +            }
> >> > +        }
> >> > +        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);
> >>
> >> Quick test:
> >>
> >>     $ qemu-system-x86_64 -S -nodefaults -display none -chardev 
> >> socket,id=chr-qmp,path=test-qmp,server=on,wait=off -object 
> >> monitor-qmp,id=qmp,chardev=chr-qmp -monitor stdio
> >>     QEMU 11.1.50 monitor - type 'help' for more information
> >>     (qemu) info iothreads
> >>     (qemu) q
> >>
> >> Hmm, no output?  The QMP monitor should be holding the monitor I/O
> >> thread.  To confirm that, I made monitor_complete() print things (patch
> >> appended), and got
> >>
> >>     ### monitor qmp holding iothread mon_iothread
> >>     ### monitor compat_monitor0 without iothread
> >>
> >> What's going on here?
> >>
> >
> > This is expected with the existing query-iothreads behavior.
> > mon_iothread is created under the internal object root, while
> > qmp_query_iothreads() only walks /objects, so internal IOThreads are
> > intentionally not exposed through QMP/HMP.
>
> TIL about the internal object root.  It's the root of a *second* QOM
> composition tree.  I have been operating under the assumnption there is
> just one.
>
> Your series adds holder tracking for any I/O thread, whether it's in the
> externally visible QOM composition tree (the one returned by
> object_get_root()) or the internal one (the one returned by
> object_get_internal_root()).  Makes sense, because at the place where we
> track we don't want to know / make assumptions about which tree it is.
>
> query-iothreads shows only the I/O threads in the externally visible QOM
> composition tree.  Makes sense.
>
> Fortunately, you were able to eliminate the special case for monitors.
> If it was still there, the query-iothreads code to show it would be
> dead.
>
> Back to what got us there.  My attempt to test query-iothreads showing
> holders failed, because monitors are not usable for this test.  Please
> show me how to test this.  Ideally, a simple command line and a few
> simple monitor commands ending with a query-iothreads that shows
> holders.
>

Thanks. Here is a reproducer that also exercises holder removal and
registration by another device type:

$ qemu-system-x86_64 \
    -S -nodefaults -display none \
    -object iothread,id=iothread0 \
    -blockdev driver=null-co,node-name=null0 \
    -qmp stdio

First, hotplug a virtio-scsi device and query the IOThread:
{"execute":"qmp_capabilities"}
{"execute":"query-iothreads"}

{"return": [{"poll-shrink": 2, "thread-id": 4002255, "poll-weight": 3,
"aio-max-batch": 0, "poll-grow": 2, "holders": [], "poll-max-ns":
32768, "id": "iothread0"}]}

{"execute":"device_add","arguments":{"driver":"virtio-scsi-pci","id":"scsi0","iothread":"iothread0"}}
{"execute":"query-iothreads"}

{"return": [{"poll-shrink": 2, "thread-id": 4002255, "poll-weight": 3,
"aio-max-batch": 0, "poll-grow": 2, "holders": [{"qom-path":
"/machine/peripheral/scsi0/virtio-backend", "type": "qom-object"}],
"poll-max-ns": 32768, "id": "iothread0"}]}

 The result contains:
  > "holders": [

Remove the device:

{"execute":"device_del","arguments":{"id":"scsi0"}}
{"return": {}}

{"execute":"query-iothreads"}
{"return": [{"poll-shrink": 2, "thread-id": 4002255, "poll-weight": 3,
"aio-max-batch": 0, "poll-grow": 2, "holders": [], "poll-max-ns":
32768, "id": "iothread0"}]}

And use the HMP is the same logic, just need change the "-qmp" to "-monitor":
$ qemu-system-x86_64 \
    -S -nodefaults -display none \
    -object iothread,id=iothread0 \
    -blockdev driver=null-co,node-name=null0 \
    -monitor stdio

(qemu) info iothreads
iothread0:
  thread_id=4005900
  holders=
  poll-max-ns=32768
  poll-grow=2
  poll-shrink=2
  poll-weight=3
  aio-max-batch=0

.......


Thanks
Chen

> > I prefer to preserve that behavior in this series, since exposing internal
> > IOThreads would require additional API decisions, including handling ID
> > collisions with user-created IOThreads.
>
> Okay!
>
> > I’ll also change the unreachable default case to
> > g_assert_not_reached().
> >
> > Thanks
> > Chen
>
> [...]
>

Reply via email to