On Tue, Sep 15, 2026 at 6:49 PM Markus Armbruster <[email protected]> wrote:
>
> Zhang Chen <[email protected]> writes:
>
> > Monitors that require an IOThread share the internal mon_iothread.
> > Account for each monitor explicitly in IOThread holder tracking, using
> > the monitor's canonical QOM path to distinguish holders.
> >
> > Acquire the shared monitor IOThread AioContext once and store the
> > IOThread and canonical QOM path so the holder can be released after the
> > monitor has been removed from the QOM tree. Use the stored IOThread for
> > per-monitor checks and accesses after initialization, and reuse the
> > stored context for later operations.
> >
> > Release the holder from monitor_finalize(), after resources associated
> > with the AioContext have been destroyed. This pairs the reference taken
> > in complete() with instance finalization and keeps the IOThread alive
> > throughout monitor teardown.
> >
> > The internal mon_iothread remains hidden from query-iothreads, as before.
> > Management applications therefore cannot use it to determine which
> > monitors are using the IOThread. Nevertheless, track the monitor
> > holders so that they follow the same explicit holder and
> > IOThread reference lifecycle as other AioContext users.
> >
> > Signed-off-by: Zhang Chen <[email protected]>
> > ---
> >  monitor/monitor-internal.h |  7 +++++--
> >  monitor/monitor.c          | 36 ++++++++++++++++++++++++++----------
> >  monitor/qmp.c              | 14 +++++++-------
> >  3 files changed, 38 insertions(+), 19 deletions(-)
> >
> > diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
> > index 822a66d06d..020afdaebf 100644
> > --- a/monitor/monitor-internal.h
> > +++ b/monitor/monitor-internal.h
> > @@ -77,6 +77,11 @@ struct Monitor {
> >      GString *outbuf;
> >      guint out_watch;
> >      int mux_out;
> > +
> > +    /* iothread context and holder identity */
> > +    IOThread *iothread;
> > +    char *iothread_qom_path;
> > +    AioContext *ctx;
> >  };
> >
> >  struct MonitorQMPClass {
> > @@ -109,14 +114,12 @@ struct MonitorQMP {
> >  };
> >
> >  typedef QTAILQ_HEAD(MonitorList, Monitor) MonitorList;
> > -extern IOThread *mon_iothread;
> >  extern Coroutine *qmp_dispatcher_co;
> >  extern bool qmp_dispatcher_co_shutdown;
> >  extern QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
> >  extern QemuMutex monitor_lock;
> >  extern MonitorList mon_list;
> >
> > -bool monitor_requires_iothread(const Monitor *mon);
> >  int monitor_can_read(void *opaque);
> >  void monitor_cancel_out_watch(Monitor *mon);
> >  void monitor_list_append(Monitor *mon);
> > diff --git a/monitor/monitor.c b/monitor/monitor.c
> > index 2654b81890..a80d8dbf45 100644
> > --- a/monitor/monitor.c
> > +++ b/monitor/monitor.c
> > @@ -55,7 +55,7 @@ typedef struct {
> >  } MonitorQAPIEventConf;
> >
> >  /* Shared monitor I/O thread */
> > -IOThread *mon_iothread;
> > +static IOThread *mon_iothread;
> >
> >  /* Coroutine to dispatch the requests received from I/O thread */
> >  Coroutine *qmp_dispatcher_co;
> > @@ -93,6 +93,16 @@ static void monitor_finalize(Object *obj)
> >      qemu_chr_fe_deinit(&mon->chr, false);
> >      g_string_free(mon->outbuf, true);
> >      qemu_mutex_destroy(&mon->mon_lock);
> > +
> > +    if (mon->iothread) {
> > +        const IOThreadHolder io_holder = {
> > +            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
> > +            .u.qom_object.qom_path = mon->iothread_qom_path,
> > +        };
> > +
> > +        iothread_unref_and_put_aio_context(mon->iothread, &io_holder);
> > +    }
> > +    g_free(mon->iothread_qom_path);
> >  }
> >
> >  static char *monitor_get_chardev_id(Object *obj, Error **errp)
> > @@ -164,7 +174,7 @@ Monitor *monitor_set_cur(Coroutine *co, Monitor *mon)
> >      return old_monitor;
> >  }
> >
> > -bool monitor_requires_iothread(const Monitor *mon)
> > +static bool monitor_requires_iothread(const Monitor *mon)
> >  {
> >      MonitorClass *cls = MONITOR_GET_CLASS(mon);
> >      return cls->requires_iothread && cls->requires_iothread(mon);
> > @@ -188,8 +198,8 @@ void monitor_cancel_out_watch(Monitor *mon)
> >          GMainContext *ctx = NULL;
> >          GSource *src;
> >
> > -        if (monitor_requires_iothread(mon)) {
> > -            ctx = iothread_get_g_main_context(mon_iothread);
> > +        if (mon->iothread) {
> > +            ctx = iothread_get_g_main_context(mon->iothread);
> >          }
> >          src = g_main_context_find_source_by_id(ctx, mon->out_watch);
> >          if (!src && ctx) {
> > @@ -516,12 +526,12 @@ void monitor_suspend(Monitor *mon)
> >  {
> >      qatomic_inc(&mon->suspend_cnt);
> >
> > -    if (monitor_requires_iothread(mon)) {
> > +    if (mon->iothread) {
> >          /*
> >           * 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);
> > @@ -661,7 +671,6 @@ char *monitor_compat_id(void)
> >  static void monitor_complete(UserCreatable *uc, Error **errp)
> >  {
> >      Monitor *mon = MONITOR(uc);
> > -    AioContext *ctx;
> >
> >      if (mon->chardev_id) {
> >          Chardev *chr = qemu_chr_find(mon->chardev_id);
> > @@ -680,11 +689,18 @@ static void monitor_complete(UserCreatable *uc, Error 
> > **errp)
> >              mon_iothread = iothread_create("mon_iothread", &error_abort);
> >          }
> >
> > -        ctx = iothread_get_aio_context(mon_iothread);
> > +        mon->iothread = mon_iothread;
> > +        mon->iothread_qom_path = object_get_canonical_path(OBJECT(mon));
>
> Hmm, this is the monitor's QOM path, not the I/O threads.  Let's rename
> Monitor member iothread_qom_path to monitor_qom_path or just qom_path.

OK.

>
> > +        const IOThreadHolder io_holder = {
> > +            .type = IO_THREAD_HOLDER_KIND_QOM_OBJECT,
> > +            .u.qom_object.qom_path = mon->iothread_qom_path,
> > +        };
> > +
> > +        mon->ctx = iothread_ref_and_get_aio_context(mon->iothread, 
> > &io_holder);
> >      } else {
> > -        ctx = qemu_get_aio_context();
> > +        mon->ctx = qemu_get_aio_context();
> >      }
> > -    mon->accept_input_bh = aio_bh_new(ctx, monitor_accept_input, mon);
> > +    mon->accept_input_bh = aio_bh_new(mon->ctx, monitor_accept_input, mon);
> >  }
> >
> >  int monitor_new(MonitorOptions *opts, bool allow_hmp, Error **errp)
> > diff --git a/monitor/qmp.c b/monitor/qmp.c
> > index aec0315775..e235302fb3 100644
> > --- a/monitor/qmp.c
> > +++ b/monitor/qmp.c
> > @@ -187,7 +187,7 @@ static void monitor_qmp_caps_reset(MonitorQMP *mon)
> >      memset(mon->capab_offered, 0, sizeof(mon->capab_offered));
> >      memset(mon->capab, 0, sizeof(mon->capab));
> >      mon->capab_offered[QMP_CAPABILITY_OOB] =
> > -        monitor_requires_iothread(MONITOR(mon));
> > +        MONITOR(mon)->iothread != NULL;
> >  }
> >
> >  static void qmp_request_free(QMPRequest *req)
> > @@ -678,8 +678,8 @@ static void monitor_qmp_setup_handlers_bh(void *opaque)
> >      MonitorQMP *mon = opaque;
> >      GMainContext *context;
> >
> > -    assert(monitor_requires_iothread(MONITOR(mon)));
> > -    context = iothread_get_g_main_context(mon_iothread);
> > +    assert(MONITOR(mon)->iothread);
> > +    context = iothread_get_g_main_context(MONITOR(mon)->iothread);
> >      assert(context);
> >      qemu_chr_fe_set_handlers(&mon->parent_obj.chr, monitor_can_read,
> >                               monitor_qmp_read, monitor_qmp_event,
> > @@ -717,7 +717,7 @@ static void monitor_qmp_complete(UserCreatable *uc, 
> > Error **errp)
> >
> >      qemu_chr_fe_set_echo(&mon->parent_obj.chr, true);
> >
> > -    if (monitor_requires_iothread(MONITOR(mon))) {
> > +    if (MONITOR(mon)->iothread) {
> >          /*
> >           * Make sure the old iowatch is gone.  It's possible when
> >           * e.g. the chardev is in client mode, with wait=on.
> > @@ -734,7 +734,7 @@ 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 {
> > @@ -788,8 +788,8 @@ 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),
> > +    if (mon->iothread) {
> > +        aio_wait_bh_oneshot(mon->ctx,
> >                              monitor_qmp_iothread_quiesce, NULL);
> >      }
>
> With the rename
> Reviewed-by: Markus Armbruster <[email protected]>

Thanks
Chen

>

Reply via email to