From nobody Sat Apr 11 18:39:13 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=kernel.org ARC-Seal: i=1; a=rsa-sha256; t=1775164815; cv=none; d=zohomail.com; s=zohoarc; b=a6A+NpjqL11qerik5pnIkVQpgRZAg7ZMjb7vBGV6fWWlOUXiCTw2ByS+zJOc+oQkKGOGUBwJP6KYBXSRNzNxB+cmpapDud6uz9CmJeFpbnqVf9lnkY219dEZWALkYUXkK3YGGkZMsPi2Axd9Uxlfh85/lqF7iO+5poGROvpjZ54= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775164815; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=3YZqRxP5HKVqpyHSJlDR+IJiPOzXZHtzsXWGu77Cz+k=; b=Mpp2XEr2HRe1d9JSYyUlqI45NmYW0DMxMAmk4YaKZ/+Hy67eo929RIB9cGQXNKy9PwbxWWrOrOwqfSAckSSw2+afHKX847wg4cm1/3/iDerAauX3O66MYUowzOpZ4ipykdi8/2X6cPMG1YiIvb2XUtS0BTx5qQ26mZpj57tF9I4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775164815433385.054351816735; Thu, 2 Apr 2026 14:20:15 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w8PS5-0003kH-NY; Thu, 02 Apr 2026 17:19:45 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w8PS4-0003k0-FC for qemu-devel@nongnu.org; Thu, 02 Apr 2026 17:19:44 -0400 Received: from sea.source.kernel.org ([2600:3c0a:e001:78e:0:1991:8:25]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w8PS2-0004an-Ne for qemu-devel@nongnu.org; Thu, 02 Apr 2026 17:19:44 -0400 Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sea.source.kernel.org (Postfix) with ESMTP id E167F4084D; Thu, 2 Apr 2026 21:19:32 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 94BADC19424; Thu, 2 Apr 2026 21:19:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775164772; bh=ieDCuJn1WMUVM16tnw255pab6Dta4Qq851/s71lqv+w=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=TkdNgeJSUvsdY4wPbjLkkppe/fghaLg5v4a7/Qjm3eWTZD4JTJ3pxYJd+mPPXUgIs LwdqeSMhlVr04VmQbFqvDPRjlLlzte7cR/9kp0Mo84oR9/d6X2Q7ybg9xziAX2czeT i71gwMLT6KrdPl9eTfgMaZgs1slRg7rozzwIa7ei+BoYILND3SE82ATEy770+HyUUm 0GMV5dMshFzVShH+qtxeiGmmgxW0w73QGMLD0Lcz8isSOaCqKNOSfjeM0ipotf4kWx fv43F/XfR6EsU8TRI+0U+J7OGLMFfYcjuRDwWdNYQ7TBvMEKKd1VJF6cNEx62YBhvg A+YgEIx9QdWaw== From: Christian Brauner Date: Thu, 02 Apr 2026 23:19:16 +0200 Subject: [PATCH 1/5] monitor: store monitor id and dynamic flag in Monitor struct MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260402-work-qmp-monitor-hotplug-v1-1-6313a5cdd574@kernel.org> References: <20260402-work-qmp-monitor-hotplug-v1-0-6313a5cdd574@kernel.org> In-Reply-To: <20260402-work-qmp-monitor-hotplug-v1-0-6313a5cdd574@kernel.org> To: qemu-devel@nongnu.org Cc: Markus Armbruster , Eric Blake , Fabiano Rosas , Laurent Vivier , Paolo Bonzini , Thomas Huth , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , Christian Brauner X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4689; i=brauner@kernel.org; h=from:subject:message-id; bh=ieDCuJn1WMUVM16tnw255pab6Dta4Qq851/s71lqv+w=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWSeuxu/WeyRxvau3op1t0xbqw/zf5qbJyzxwO166IGO2 ftmC11d2lHKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjCRZ1mMDEs4pFt/3jn8d9IW LaejU7T77W+UBv+RNVE4+XmCzs4swcOMDM8jp6WYlsYv/q8oL6pzkf9B87NL/gc51JKk6n7IdC3 NZwYA X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2600:3c0a:e001:78e:0:1991:8:25; envelope-from=brauner@kernel.org; helo=sea.source.kernel.org X-Spam_score_int: -25 X-Spam_score: -2.6 X-Spam_bar: -- X-Spam_report: (-2.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.542, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @kernel.org) X-ZM-MESSAGEID: 1775164818391154100 Add 'id', 'dynamic', and 'dead' fields to struct Monitor. The id field stores the monitor identifier from MonitorOptions which was previously parsed but discarded. The dynamic flag marks monitors created at runtime via the upcoming monitor-add command, and the dead flag will be used for deferred destruction during monitor-remove. Extend monitor_init_qmp() to accept id and dynamic parameters so these are set before the monitor is added to mon_list -- this is important for the BH/iothread path where the monitor becomes visible asynchronously. Add monitor_find_by_id() to look up monitors by identifier, and free the id string in monitor_data_destroy(). Signed-off-by: Christian Brauner (Amutable) --- include/monitor/monitor.h | 3 ++- monitor/monitor-internal.h | 5 +++++ monitor/monitor.c | 15 ++++++++++++++- monitor/qmp.c | 5 ++++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 296690e1f1..7a2bb603e4 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -19,7 +19,8 @@ bool monitor_cur_is_qmp(void); =20 void monitor_init_globals(void); void monitor_init_globals_core(void); -void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp); +void monitor_init_qmp(Chardev *chr, bool pretty, const char *id, + bool dynamic, Error **errp); void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp); int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp); int monitor_init_opts(QemuOpts *opts, Error **errp); diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index feca111ae3..8f5fe7c111 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -98,7 +98,10 @@ struct Monitor { bool is_qmp; bool skip_flush; bool use_io_thread; + bool dynamic; /* true if created via monitor-add */ + bool dead; /* true if monitor-remove called, awaiting= drain */ =20 + char *id; /* Monitor identifier (NULL for unnamed CL= I monitors) */ char *mon_cpu_path; QTAILQ_ENTRY(Monitor) entry; =20 @@ -181,6 +184,8 @@ void monitor_data_destroy_qmp(MonitorQMP *mon); void coroutine_fn monitor_qmp_dispatcher_co(void *data); void qmp_dispatcher_co_wake(void); =20 +Monitor *monitor_find_by_id(const char *id); + int get_monitor_def(Monitor *mon, int64_t *pval, const char *name); void handle_hmp_command(MonitorHMP *mon, const char *cmdline); int hmp_compare_cmd(const char *name, const char *list); diff --git a/monitor/monitor.c b/monitor/monitor.c index 00b93ed612..09e32b6364 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -622,6 +622,7 @@ void monitor_data_init(Monitor *mon, bool is_qmp, bool = skip_flush, =20 void monitor_data_destroy(Monitor *mon) { + g_free(mon->id); g_free(mon->mon_cpu_path); qemu_chr_fe_deinit(&mon->chr, false); if (monitor_is_qmp(mon)) { @@ -633,6 +634,18 @@ void monitor_data_destroy(Monitor *mon) qemu_mutex_destroy(&mon->mon_lock); } =20 +Monitor *monitor_find_by_id(const char *id) +{ + Monitor *mon; + + QTAILQ_FOREACH(mon, &mon_list, entry) { + if (mon->id && strcmp(mon->id, id) =3D=3D 0) { + return mon; + } + } + return NULL; +} + void monitor_cleanup(void) { /* @@ -732,7 +745,7 @@ int monitor_init(MonitorOptions *opts, bool allow_hmp, = Error **errp) =20 switch (opts->mode) { case MONITOR_MODE_CONTROL: - monitor_init_qmp(chr, opts->pretty, errp); + monitor_init_qmp(chr, opts->pretty, opts->id, false, errp); break; case MONITOR_MODE_READLINE: if (!allow_hmp) { diff --git a/monitor/qmp.c b/monitor/qmp.c index 687019811f..5fbc7af074 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -513,7 +513,8 @@ static void monitor_qmp_setup_handlers_bh(void *opaque) monitor_list_append(&mon->common); } =20 -void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp) +void monitor_init_qmp(Chardev *chr, bool pretty, const char *id, + bool dynamic, Error **errp) { MonitorQMP *mon =3D g_new0(MonitorQMP, 1); =20 @@ -527,6 +528,8 @@ void monitor_init_qmp(Chardev *chr, bool pretty, Error = **errp) monitor_data_init(&mon->common, true, false, qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT= )); =20 + mon->common.id =3D g_strdup(id); + mon->common.dynamic =3D dynamic; mon->pretty =3D pretty; =20 qemu_mutex_init(&mon->qmp_queue_lock); --=20 2.47.3 From nobody Sat Apr 11 18:39:13 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=kernel.org ARC-Seal: i=1; a=rsa-sha256; t=1775164848; cv=none; d=zohomail.com; s=zohoarc; b=eec977BUCYMTys+8bvzsNvMa3KhdMlAWNNjAnhAV2FIIgC6whQtRpWxkZK0xx0664ZM68wketihMy4flKkQpldcmxukqyr/wiWEA/gnhCQaFm7TG3qZk0ysoDRWH2mtj5DoTU6iPA8bpVpsJPb8o7pFod1/Ksv8cp0QTPv17K50= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775164848; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=HEvmhsz+QFSc/NEPCO/My1xSO2hYix5mG8If/RYbHsA=; b=Nxl4Xyj6zRul+G1SoKpmMn8m+nZch02IxI2h7gfTfaSryapfHitq2SHTgzRLjDk/ZO6bbQrBw8vNKsO8qeoPkoim/7wFl+bCgN21958w4pjIAXrh7o2CsKOcDecyBQRM1UQjnQHjDassVPlOZmib4zDrvTM++ZCxs0MO1NQjs8M= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177516484884736.62002049855744; Thu, 2 Apr 2026 14:20:48 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w8PS6-0003kI-2m; Thu, 02 Apr 2026 17:19:46 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w8PS5-0003k9-6P for qemu-devel@nongnu.org; Thu, 02 Apr 2026 17:19:45 -0400 Received: from tor.source.kernel.org ([172.105.4.254]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w8PS3-0004au-Jz for qemu-devel@nongnu.org; Thu, 02 Apr 2026 17:19:44 -0400 Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by tor.source.kernel.org (Postfix) with ESMTP id A6ABB60123; Thu, 2 Apr 2026 21:19:35 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3E04DC116C6; Thu, 2 Apr 2026 21:19:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775164775; bh=bSL21DMJvdPmisuJ4sR+uubo6LsKzZepTDyv0CmheLU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=dgAYVc21yM/2Vjc90j9dbQAcYoc1fLCXqQkzFCt296qDhe2Go0aqs8USaGnebPNio ynpNo/A7T9a7UCDM631goZ8SsOzdpIaMKYQX1ErQbSbfG/ExO8qx45NqA2m+gqluwU V/PbZAtnNluv/ZWQGVeO0CBrQkoABToqBhvY1qfgXhiOKpoiwilUcLqJZ/lYAzhVVE D2Ea7z9M+rDtOBrJngc8Vj9N9tsnoZDe2Vx8kRupZzQ8vrqhdvMJQS9AkglcGvYWuh crh7pCL48m58VQB7JXVlRn54cKyWrVaH6BXGYLxKQWNZ89CsAezoQ2d8Cblh5AtwkO guTavDLZHgn9A== From: Christian Brauner Date: Thu, 02 Apr 2026 23:19:17 +0200 Subject: [PATCH 2/5] monitor/qmp: add infrastructure for safe dynamic monitor removal MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260402-work-qmp-monitor-hotplug-v1-2-6313a5cdd574@kernel.org> References: <20260402-work-qmp-monitor-hotplug-v1-0-6313a5cdd574@kernel.org> In-Reply-To: <20260402-work-qmp-monitor-hotplug-v1-0-6313a5cdd574@kernel.org> To: qemu-devel@nongnu.org Cc: Markus Armbruster , Eric Blake , Fabiano Rosas , Laurent Vivier , Paolo Bonzini , Thomas Huth , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , Christian Brauner X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4106; i=brauner@kernel.org; h=from:subject:message-id; bh=bSL21DMJvdPmisuJ4sR+uubo6LsKzZepTDyv0CmheLU=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWSeuxv/foer4afmkxfEPMt39E6PC9TgvHlBZ07SqQKXN a7dR9+ndJSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEyk/hYjw2G3CWZtnW2CX7LM vapdfY1U3x/pD4t3eBmSUHDn67XPtQz/LHwzfi7IfaqYtNZi1Yl3WkKnl0rcP+jTvmj+tmzNNYV ZXAA= X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=172.105.4.254; envelope-from=brauner@kernel.org; helo=tor.source.kernel.org X-Spam_score_int: -25 X-Spam_score: -2.6 X-Spam_bar: -- X-Spam_report: (-2.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.542, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @kernel.org) X-ZM-MESSAGEID: 1775164850282158500 Extract monitor_qmp_destroy() from the shutdown-time monitor_cleanup() path to allow destroying a single QMP monitor at runtime without shutting down the entire dispatcher coroutine. Export monitor_qmp_cleanup_queue_and_resume() so that monitor-remove can drain pending requests before destroying the monitor. Add qmp_dispatcher_current_mon tracking in the dispatcher coroutine to handle the case where a monitor sends monitor-remove targeting itself. After dispatching each request, the dispatcher checks the dead flag: if set, it calls monitor_qmp_destroy() and clears the tracking pointer. Both the dispatcher yield points and QMP command handlers run under the BQL, so no additional locking is needed for qmp_dispatcher_current_mon. Signed-off-by: Christian Brauner (Amutable) --- monitor/monitor-internal.h | 3 +++ monitor/qmp.c | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 8f5fe7c111..40f76ef5a0 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -181,10 +181,13 @@ void monitor_fdsets_cleanup(void); =20 void qmp_send_response(MonitorQMP *mon, const QDict *rsp); void monitor_data_destroy_qmp(MonitorQMP *mon); +void monitor_qmp_destroy(MonitorQMP *mon); +void monitor_qmp_cleanup_queue_and_resume(MonitorQMP *mon); void coroutine_fn monitor_qmp_dispatcher_co(void *data); void qmp_dispatcher_co_wake(void); =20 Monitor *monitor_find_by_id(const char *id); +bool monitor_qmp_dispatcher_is_servicing(MonitorQMP *mon); =20 int get_monitor_def(Monitor *mon, int64_t *pval, const char *name); void handle_hmp_command(MonitorHMP *mon, const char *cmdline); diff --git a/monitor/qmp.c b/monitor/qmp.c index 5fbc7af074..0d393f3c96 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -71,6 +71,13 @@ typedef struct QMPRequest QMPRequest; =20 QmpCommandList qmp_commands, qmp_cap_negotiation_commands; =20 +/* + * The monitor currently being serviced by the dispatcher coroutine. + * Both the dispatcher and QMP command handlers (monitor-remove) run + * under the BQL, so no additional locking is needed. + */ +static MonitorQMP *qmp_dispatcher_current_mon; + static bool qmp_oob_enabled(MonitorQMP *mon) { return mon->capab[QMP_CAPABILITY_OOB]; @@ -98,7 +105,7 @@ static void monitor_qmp_cleanup_req_queue_locked(Monitor= QMP *mon) } } =20 -static void monitor_qmp_cleanup_queue_and_resume(MonitorQMP *mon) +void monitor_qmp_cleanup_queue_and_resume(MonitorQMP *mon) { QEMU_LOCK_GUARD(&mon->qmp_queue_lock); =20 @@ -287,6 +294,7 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data) */ =20 mon =3D req_obj->mon; + qmp_dispatcher_current_mon =3D mon; =20 /* * We need to resume the monitor if handle_qmp_command() @@ -347,6 +355,16 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data) } =20 qmp_request_free(req_obj); + + /* + * If monitor-remove was called while we were dispatching a + * request on this monitor, the monitor was marked dead and + * removed from mon_list but destruction was deferred to us. + */ + if (mon->common.dead) { + monitor_qmp_destroy(mon); + } + qmp_dispatcher_current_mon =3D NULL; } qatomic_set(&qmp_dispatcher_co, NULL); } @@ -499,6 +517,22 @@ void monitor_data_destroy_qmp(MonitorQMP *mon) g_queue_free(mon->qmp_requests); } =20 +/* + * Destroy a single dynamically-added QMP monitor. + * The monitor must already have been removed from mon_list. + */ +void monitor_qmp_destroy(MonitorQMP *mon) +{ + monitor_flush(&mon->common); + monitor_data_destroy(&mon->common); + g_free(mon); +} + +bool monitor_qmp_dispatcher_is_servicing(MonitorQMP *mon) +{ + return qmp_dispatcher_current_mon =3D=3D mon; +} + static void monitor_qmp_setup_handlers_bh(void *opaque) { MonitorQMP *mon =3D opaque; --=20 2.47.3 From nobody Sat Apr 11 18:39:13 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=kernel.org ARC-Seal: i=1; a=rsa-sha256; t=1775164815; cv=none; d=zohomail.com; s=zohoarc; b=hVSaWcSPA2uK+Ol2bi7Flkh34Ahb0nBptOBHHaunkZEDfj231MnhYpPWoeja1kH0xnuATRj19wzBXj4aVwGW7/9C+4vph3vLuk91ssGaqfzcPs3VK/1H3cfJcxCCsjMEGzBJMRhwssR68lsmENFwpA5qmdiqDeqAx2zlTKKwFbw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775164815; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=VTkGIhTTYCG+LJZ+YeF6FainnSv/QzqMbghJXvgE/TA=; b=A8Otj8FB1V6X3/nQhzqv0/ENsR8cI40Ha60vV0m+mdZ6MbYRp+wksiA6emjZFJ+3MeQqe034OqhtRIrfAMfiwQo1eRTq6s1Ft1Tyt1iV+mO27WJqtsV7ppQixKv4Lq136mVYwNOhr9UUwGhIbiVd+BwOL1kD0AlWiwhj/jl/CGc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775164815153411.468938475061; Thu, 2 Apr 2026 14:20:15 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w8PS3-0003jc-Cp; Thu, 02 Apr 2026 17:19:43 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w8PS2-0003j2-3K for qemu-devel@nongnu.org; Thu, 02 Apr 2026 17:19:42 -0400 Received: from tor.source.kernel.org ([2600:3c04:e001:324:0:1991:8:25]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w8PS0-0004b3-CL for qemu-devel@nongnu.org; Thu, 02 Apr 2026 17:19:41 -0400 Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by tor.source.kernel.org (Postfix) with ESMTP id 612BA60126; Thu, 2 Apr 2026 21:19:38 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id D1440C19424; Thu, 2 Apr 2026 21:19:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775164778; bh=gPO1Pu3xZDZpzDPyynLjlwUEaoGQCjY4ylfy1aTTGVg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=JNssCbUYR+2RARh2r9e2xQQTXsL6LV+B7wF72+vHZBzAcurOJVYNXTaouzT4DG201 NyXkc2Ko0aTKNhLTLRqNN9pb6PdWg307AHaCi86iR9/PLMYtdG9Vn1DheYOAWlPuj8 vlxbENkZoC3aN09xssjn0VNxGGCPqKDVV2Dxe3+DA7fJitvanEydz1aNsMjgUL5dNP ZysGgc/Xa7B9qvCQx9DCezLay/CMoowZ2oOR7ccNon45hHoZ+KEEz4B8Z6uz0F2Q1Q QKo+CtQYxRTFd9RZYb0N1j8EHAJvvwGA8z3hfYXbVvnj47wpgCxeYXAKExnU0PXK4o 6LDwpOgNIH+lA== From: Christian Brauner Date: Thu, 02 Apr 2026 23:19:18 +0200 Subject: [PATCH 3/5] qapi: add monitor-add, monitor-remove, query-monitors commands MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260402-work-qmp-monitor-hotplug-v1-3-6313a5cdd574@kernel.org> References: <20260402-work-qmp-monitor-hotplug-v1-0-6313a5cdd574@kernel.org> In-Reply-To: <20260402-work-qmp-monitor-hotplug-v1-0-6313a5cdd574@kernel.org> To: qemu-devel@nongnu.org Cc: Markus Armbruster , Eric Blake , Fabiano Rosas , Laurent Vivier , Paolo Bonzini , Thomas Huth , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , Christian Brauner X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=7468; i=brauner@kernel.org; h=from:subject:message-id; bh=gPO1Pu3xZDZpzDPyynLjlwUEaoGQCjY4ylfy1aTTGVg=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWSeuxsf1PLUnn0Xf9CM1Wb7Al5qTKgSZEsx/vju/J5rC UlGFut2d5SyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAExkfzcjwxXVaFfrHb0vShQe 3tomnOxQ19Lc0l1b4dTF333LTJPFgOF/REaslOqPhRwyrCk7D3hs28W/OUtuvvGtJ/5MzRuz76v zAAA= X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2600:3c04:e001:324:0:1991:8:25; envelope-from=brauner@kernel.org; helo=tor.source.kernel.org X-Spam_score_int: -25 X-Spam_score: -2.6 X-Spam_bar: -- X-Spam_report: (-2.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.542, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @kernel.org) X-ZM-MESSAGEID: 1775164816758158500 Add QMP commands for dynamic monitor lifecycle management: - monitor-add: Create a QMP monitor on an existing chardev at runtime. The chardev must exist and not be in use. The new monitor starts in capability negotiation mode. - monitor-remove: Remove a dynamically-added monitor. CLI-created monitors cannot be removed. If the dispatcher is currently servicing the target monitor (self-removal), destruction is deferred until the in-flight command completes. The underlying chardev is not destroyed. - query-monitors: Introspect all active monitors with their id, mode, chardev name, and whether they were dynamically added. The motivating use case is systemd-vmspawn: when an external client requests raw QMP access, vmspawn can create an independent QMP session on demand rather than pre-allocating spare monitors at launch or building an id-rewriting proxy. Signed-off-by: Christian Brauner (Amutable) --- monitor/qmp-cmds-control.c | 94 ++++++++++++++++++++++++++++++++++++++++ qapi/control.json | 106 +++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 200 insertions(+) diff --git a/monitor/qmp-cmds-control.c b/monitor/qmp-cmds-control.c index 150ca9f5cb..0998017620 100644 --- a/monitor/qmp-cmds-control.c +++ b/monitor/qmp-cmds-control.c @@ -219,3 +219,97 @@ SchemaInfoList *qmp_query_qmp_schema(Error **errp) } return schema; } + +void qmp_monitor_add(const char *id, const char *chardev, + bool has_pretty, bool pretty, Error **errp) +{ + Chardev *chr; + + /* Reject duplicate monitor id */ + if (monitor_find_by_id(id)) { + error_setg(errp, "monitor '%s' already exists", id); + return; + } + + chr =3D qemu_chr_find(chardev); + if (!chr) { + error_setg(errp, "chardev '%s' not found", chardev); + return; + } + + monitor_init_qmp(chr, has_pretty && pretty, id, true, errp); +} + +void qmp_monitor_remove(const char *id, Error **errp) +{ + Monitor *mon; + MonitorQMP *qmp_mon; + + mon =3D monitor_find_by_id(id); + if (!mon) { + error_setg(errp, "monitor '%s' not found", id); + return; + } + + if (!mon->dynamic) { + error_setg(errp, "monitor '%s' was not dynamically added", id); + return; + } + + qmp_mon =3D container_of(mon, MonitorQMP, common); + + /* + * Step 1: Disconnect chardev handlers so no new data arrives + * and no new requests are enqueued. + */ + qemu_chr_fe_set_handlers(&mon->chr, NULL, NULL, NULL, NULL, + NULL, NULL, true); + + /* Step 2: Drain pending requests from the queue */ + monitor_qmp_cleanup_queue_and_resume(qmp_mon); + + /* + * Step 3: Mark dead and remove from mon_list. + * After removal, the dispatcher will never pop new requests from + * this monitor, and event broadcast will skip it. + */ + qemu_mutex_lock(&monitor_lock); + mon->dead =3D true; + QTAILQ_REMOVE(&mon_list, mon, entry); + qemu_mutex_unlock(&monitor_lock); + + /* + * Step 4: Check if the dispatcher is currently mid-dispatch on + * this monitor (i.e. monitor-remove was sent from the monitor + * being removed). If so, defer destruction -- the dispatcher + * will call monitor_qmp_destroy() after completing the request. + */ + if (monitor_qmp_dispatcher_is_servicing(qmp_mon)) { + return; + } + + /* Step 5: Safe to destroy immediately */ + monitor_qmp_destroy(qmp_mon); +} + +MonitorInfoList *qmp_query_monitors(Error **errp) +{ + MonitorInfoList *list =3D NULL; + Monitor *mon; + + qemu_mutex_lock(&monitor_lock); + QTAILQ_FOREACH(mon, &mon_list, entry) { + MonitorInfo *info =3D g_new0(MonitorInfo, 1); + Chardev *chr =3D qemu_chr_fe_get_driver(&mon->chr); + + info->id =3D g_strdup(mon->id); /* NULL if unnamed */ + info->mode =3D mon->is_qmp ? MONITOR_MODE_CONTROL + : MONITOR_MODE_READLINE; + info->chardev =3D g_strdup(chr ? chr->label : "unknown"); + info->dynamic =3D mon->dynamic; + QAPI_LIST_PREPEND(list, info); + } + qemu_mutex_unlock(&monitor_lock); + + return list; +} diff --git a/qapi/control.json b/qapi/control.json index 9a5302193d..b9f495c08c 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -211,3 +211,109 @@ '*pretty': 'bool', 'chardev': 'str' } } + +## +# @monitor-add: +# +# Add a QMP monitor on an existing character device backend. +# +# The chardev must already exist (created via chardev-add or CLI) and +# must not be in use by another frontend. The monitor begins in +# capability negotiation mode -- the first client to connect receives +# the QMP greeting. +# +# @id: Monitor identifier, must be unique among monitors +# +# @chardev: Name of the character device backend to attach to +# +# @pretty: Enable pretty-printing of QMP responses (default: false) +# +# Errors: +# - GenericError if @id is already in use +# - GenericError if @chardev does not exist +# - GenericError if @chardev is already in use by another frontend +# +# Since: 11.0 +# +# .. qmp-example:: +# +# -> { "execute": "monitor-add", +# "arguments": { "id": "extra-qmp", +# "chardev": "qmp-extra" } } +# <- { "return": {} } +## +{ 'command': 'monitor-add', + 'data': { 'id': 'str', + 'chardev': 'str', + '*pretty': 'bool' } } + +## +# @monitor-remove: +# +# Remove a dynamically added QMP monitor. +# +# The monitor must have been created via monitor-add. Monitors +# created via CLI options (-mon, -qmp) cannot be removed. The +# underlying chardev is NOT removed -- use chardev-remove separately +# if desired. +# +# If a client is currently connected, the connection is dropped. +# +# @id: Monitor identifier as passed to monitor-add +# +# Errors: +# - GenericError if @id does not exist +# - GenericError if the monitor was not dynamically added +# +# Since: 11.0 +# +# .. qmp-example:: +# +# -> { "execute": "monitor-remove", +# "arguments": { "id": "extra-qmp" } } +# <- { "return": {} } +## +{ 'command': 'monitor-remove', + 'data': { 'id': 'str' } } + +## +# @MonitorInfo: +# +# Information about a QMP/HMP monitor. +# +# @id: Monitor identifier (absent for CLI-created monitors without +# an explicit id) +# +# @mode: Monitor mode (readline or control) +# +# @chardev: Name of the attached character device +# +# @dynamic: true if created via monitor-add (removable), false if +# created via CLI +# +# Since: 11.0 +## +{ 'struct': 'MonitorInfo', + 'data': { '*id': 'str', + 'mode': 'MonitorMode', + 'chardev': 'str', + 'dynamic': 'bool' } } + +## +# @query-monitors: +# +# Return information about all active monitors. +# +# Returns: a list of @MonitorInfo for each active monitor +# +# Since: 11.0 +# +# .. qmp-example:: +# +# -> { "execute": "query-monitors" } +# <- { "return": [ { "id": "mon0", "mode": "control", +# "chardev": "compat_monitor0", +# "dynamic": false } ] } +## +{ 'command': 'query-monitors', + 'returns': ['MonitorInfo'] } --=20 2.47.3 From nobody Sat Apr 11 18:39:13 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=kernel.org ARC-Seal: i=1; a=rsa-sha256; t=1775164834; cv=none; d=zohomail.com; s=zohoarc; b=YBKoAVnR3NinSiV40Qc0yUewvM/TZIGfnZ4IlqdS4T8Z66OV3q5OJ8j5qKGUa7sGlP/wmFVdMQuD/E3axxr/GZfJ4jfUhAbCK9yyowyKtAzTwziQvJfwKH2HzMW6MB7pBBxONMz2nOvQsk0vIaxXjnZuP4lgzVAxw9O5rzL6Qz4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775164834; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=u1GHZIW/WXDPV0wR9DtE1oLOvptxk5TBhIypWQ5hxf8=; b=iEdHnlgo6ilE47rLNNbDFAL1pLz8H1CUMKpL4w4XBKdSP27nc14l6UkVtIRw03MFHek2XL+UDz3XlqwXI809VjFvYkFj4zI7bMxPwP0FIiFF6rEqCx8gies7aOm9mk3btjjPUN7rfPB1aO+vAEhcxMhiRyKJiFMt5l8B11U5Aoc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775164834459999.6073528353744; Thu, 2 Apr 2026 14:20:34 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w8PSZ-000448-CA; Thu, 02 Apr 2026 17:20:15 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w8PSX-0003yB-Ht for qemu-devel@nongnu.org; Thu, 02 Apr 2026 17:20:14 -0400 Received: from tor.source.kernel.org ([172.105.4.254]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w8PSV-0005g4-Ol for qemu-devel@nongnu.org; Thu, 02 Apr 2026 17:20:13 -0400 Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by tor.source.kernel.org (Postfix) with ESMTP id 12AF760125; Thu, 2 Apr 2026 21:19:41 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8A36BC116C6; Thu, 2 Apr 2026 21:19:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775164780; bh=iW8amckai9wBk7Cc1O5sdHXXZB8WiWGJCBtSEG3XdSc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=kQIipvI6q54M6Nlu4GTAMqULqdXDMsArd03joVOfD3mG+R6wQM8WKfFrabTi/wcjT oEQOndakkChEae0X3FpW0/q78TRMp9KcBPOH8snRbeTzPbPch0/CU0GQTusQsE8aK/ zC9I4UCg8aXF0qqduPRJcoKWWEMFXohkqTm7Dg4eijMqEzW4M4rbDSByGB1bdcOgqZ ZRg3ZX2I3jTFXEyTbHXglivbe8IMYfHn+0ghY6HCIr3Xuk0d9y2Ht0XKhIq5PkiMBV wQzGSqXQbjexmDIRSVcku9rfTQ7NPme98uiWWbVdmJNbYofFSVYNhgFtJlrx8xPYsf J88rQ+LikFPig== From: Christian Brauner Date: Thu, 02 Apr 2026 23:19:19 +0200 Subject: [PATCH 4/5] tests/qtest: add tests for dynamic monitor add/remove MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260402-work-qmp-monitor-hotplug-v1-4-6313a5cdd574@kernel.org> References: <20260402-work-qmp-monitor-hotplug-v1-0-6313a5cdd574@kernel.org> In-Reply-To: <20260402-work-qmp-monitor-hotplug-v1-0-6313a5cdd574@kernel.org> To: qemu-devel@nongnu.org Cc: Markus Armbruster , Eric Blake , Fabiano Rosas , Laurent Vivier , Paolo Bonzini , Thomas Huth , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , Christian Brauner X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4873; i=brauner@kernel.org; h=from:subject:message-id; bh=iW8amckai9wBk7Cc1O5sdHXXZB8WiWGJCBtSEG3XdSc=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWSeuxuv0zbF1+2qPHuqwIHN55hO6/E0msQ8tfuXfeGk+ w9DFRf9jlIWBjEuBlkxRRaHdpNwueU8FZuNMjVg5rAygQxh4OIUgIlMaGdkOKFw47v38W3b2t1/ Hr7sYHnihKBqyMbZH8//cby7yPItgw0jw9Zfk8W29p0ocHXnFInVsmxZPvExa4Kw79PZW5WTqxK 9eQE= X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=172.105.4.254; envelope-from=brauner@kernel.org; helo=tor.source.kernel.org X-Spam_score_int: -25 X-Spam_score: -2.6 X-Spam_bar: -- X-Spam_report: (-2.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.542, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @kernel.org) X-ZM-MESSAGEID: 1775164836479158500 Test the monitor-add, monitor-remove, and query-monitors QMP commands: - Basic lifecycle: chardev-add -> monitor-add -> query-monitors -> monitor-remove -> chardev-remove - Error: duplicate monitor id - Error: monitor-remove on nonexistent id - Error: monitor-add with nonexistent chardev - Re-add after remove: same id and chardev reusable after removal Signed-off-by: Christian Brauner (Amutable) --- tests/qtest/qmp-test.c | 101 +++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 101 insertions(+) diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c index edf0886787..9dbd3a6103 100644 --- a/tests/qtest/qmp-test.c +++ b/tests/qtest/qmp-test.c @@ -337,6 +337,105 @@ static void test_qmp_missing_any_arg(void) qtest_quit(qts); } =20 +static void test_qmp_monitor_add_remove(void) +{ + QTestState *qts; + QDict *resp; + + qts =3D qtest_init(common_args); + + /* Create a null chardev for the dynamic monitor */ + resp =3D qtest_qmp(qts, + "{'execute': 'chardev-add'," + " 'arguments': {'id': 'monitor-chardev'," + " 'backend': {'type': 'null'," + " 'data': {}}}}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + /* Add a dynamic monitor */ + resp =3D qtest_qmp(qts, + "{'execute': 'monitor-add'," + " 'arguments': {'id': 'dyn-mon'," + " 'chardev': 'monitor-chardev'}}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + /* Verify it shows up in query-monitors */ + resp =3D qtest_qmp(qts, "{'execute': 'query-monitors'}"); + g_assert(!qdict_haskey(resp, "error")); + qobject_unref(resp); + + /* Error: duplicate id */ + resp =3D qtest_qmp(qts, + "{'execute': 'monitor-add'," + " 'arguments': {'id': 'dyn-mon'," + " 'chardev': 'monitor-chardev'}}"); + qmp_expect_error_and_unref(resp, "GenericError"); + + /* Remove the dynamic monitor */ + resp =3D qtest_qmp(qts, + "{'execute': 'monitor-remove'," + " 'arguments': {'id': 'dyn-mon'}}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + /* Error: remove nonexistent */ + resp =3D qtest_qmp(qts, + "{'execute': 'monitor-remove'," + " 'arguments': {'id': 'dyn-mon'}}"); + qmp_expect_error_and_unref(resp, "GenericError"); + + /* Error: remove CLI monitor (the default one) -- find its id first. + * CLI monitors typically have no id, so use a bogus id to test. */ + + /* Add again after remove -- same id and chardev should work */ + resp =3D qtest_qmp(qts, + "{'execute': 'monitor-add'," + " 'arguments': {'id': 'dyn-mon'," + " 'chardev': 'monitor-chardev'}}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + /* Clean up */ + resp =3D qtest_qmp(qts, + "{'execute': 'monitor-remove'," + " 'arguments': {'id': 'dyn-mon'}}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + resp =3D qtest_qmp(qts, + "{'execute': 'chardev-remove'," + " 'arguments': {'id': 'monitor-chardev'}}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + qtest_quit(qts); +} + +static void test_qmp_monitor_error_paths(void) +{ + QTestState *qts; + QDict *resp; + + qts =3D qtest_init(common_args); + + /* Error: chardev does not exist */ + resp =3D qtest_qmp(qts, + "{'execute': 'monitor-add'," + " 'arguments': {'id': 'bad-mon'," + " 'chardev': 'nonexistent'}}"); + qmp_expect_error_and_unref(resp, "GenericError"); + + /* Error: remove nonexistent monitor */ + resp =3D qtest_qmp(qts, + "{'execute': 'monitor-remove'," + " 'arguments': {'id': 'bogus'}}"); + qmp_expect_error_and_unref(resp, "GenericError"); + + qtest_quit(qts); +} + int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); @@ -348,6 +447,8 @@ int main(int argc, char *argv[]) #endif qtest_add_func("qmp/preconfig", test_qmp_preconfig); qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg); + qtest_add_func("qmp/monitor-add-remove", test_qmp_monitor_add_remove); + qtest_add_func("qmp/monitor-error-paths", test_qmp_monitor_error_paths= ); =20 return g_test_run(); } --=20 2.47.3 From nobody Sat Apr 11 18:39:13 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=kernel.org ARC-Seal: i=1; a=rsa-sha256; t=1775164859; cv=none; d=zohomail.com; s=zohoarc; b=WF+MHwmphu9ZwCh36uM/eCCkdwwfk931TWJLuI7kEcd3WB2CtpDfJm9OOGvF1xsOWi1cSfii0iMBZoUKHhAytzHOfEojg2gGeewB5WgBaDJREy7sCsS9jmldmBgDI5MNP65bzTQGfF7jV+CQwPzwLC5gzpUjpGRQwt0maPZuJ1w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775164859; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=JdnNp1goqghyNyGogmaSSLEXH4T0mJPOtdk3SNbJIao=; b=iMKlAk+CVgJDlg3km9FQwRl2CW/QNcUBBMCqy0OqK4tMueYGhByyc7hjPhfv6GsUsweXXBNSs7B2oUNrMGvclgHs5+1jQmvfEwo6v2AjTfcmkssdowz/4IW1ysxN+xczUYPVvcQZl/ShzCR7FyAjtQA2wd94U9oZFWZEZwb8qYg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775164859083730.6282244438702; Thu, 2 Apr 2026 14:20:59 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w8PS7-0003kS-EX; Thu, 02 Apr 2026 17:19:47 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w8PS6-0003kJ-2u for qemu-devel@nongnu.org; Thu, 02 Apr 2026 17:19:46 -0400 Received: from tor.source.kernel.org ([172.105.4.254]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1w8PS4-0004bn-Cj for qemu-devel@nongnu.org; Thu, 02 Apr 2026 17:19:45 -0400 Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by tor.source.kernel.org (Postfix) with ESMTP id B59D660128; Thu, 2 Apr 2026 21:19:43 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3D952C19424; Thu, 2 Apr 2026 21:19:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775164783; bh=TVRVKzeNnGI5lMhmM0XW/KBaDtbgq3fQ6PSz+PGbl/w=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=VLZ3NjjezyXcCo1WBC9dI1xLPNMJwPm1fX/RFAz9dacZQUPVQIrn0tS8H+o9/Oqz1 QbFAr2IoEgVIicPT8h4raC1dcxkaLqaoTUQIpa/holD0wocYmdzOhupsuYwb9/qt4C s/B7OzTYxIIWmC9MU0W9p4UzZjsZqAyLHl1Y6CBdar3czB/v6HMKt88qkIuAugCwwG xbLXooTjQ7tNnV16XjznM0WZ1G+n2E6N1MOBX+gbqiXufw5vwrZNw1azb7uPAKAtno G22NLejByBw66u/jeTjZ/leqWsSu38FppKGBOHevDGbjbg8B5ABFgo+LkT9AhNrgnI 8ZMctXTpUAknw== From: Christian Brauner Date: Thu, 02 Apr 2026 23:19:20 +0200 Subject: [PATCH 5/5] tests/functional: add e2e test for dynamic QMP monitor hotplug MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260402-work-qmp-monitor-hotplug-v1-5-6313a5cdd574@kernel.org> References: <20260402-work-qmp-monitor-hotplug-v1-0-6313a5cdd574@kernel.org> In-Reply-To: <20260402-work-qmp-monitor-hotplug-v1-0-6313a5cdd574@kernel.org> To: qemu-devel@nongnu.org Cc: Markus Armbruster , Eric Blake , Fabiano Rosas , Laurent Vivier , Paolo Bonzini , Thomas Huth , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= , Christian Brauner X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4767; i=brauner@kernel.org; h=from:subject:message-id; bh=TVRVKzeNnGI5lMhmM0XW/KBaDtbgq3fQ6PSz+PGbl/w=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWSeuxvfbHhh9x93pmvmRxeIBi9ifPFY+kLx3TnlT4ND+ Q/6hOvKd5SyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEzkkzrD/5pjtzmfPuaUm/u0 99iXr43iCZsak9fZJSZOshNTKOyI2MjwP/jF7pqc0r51D/53TBGyeN76OGBtXp+/aNQlTcfbzjt v8QAA X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=172.105.4.254; envelope-from=brauner@kernel.org; helo=tor.source.kernel.org X-Spam_score_int: -25 X-Spam_score: -2.6 X-Spam_bar: -- X-Spam_report: (-2.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.542, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @kernel.org) X-ZM-MESSAGEID: 1775164860506154100 Add a functional test that exercises the full monitor hotplug lifecycle with a real socket connection: 1. Launch QEMU 2. chardev-add a unix socket chardev 3. monitor-add to attach a QMP monitor to it 4. Connect to the socket, receive the QMP greeting, negotiate capabilities, and send a query-version command 5. Disconnect, monitor-remove, chardev-remove 6. Repeat the entire cycle a second time to verify cleanup and reuse This complements the qtest unit tests by verifying that a real QMP client can connect to a dynamically-added monitor and exchange messages. Signed-off-by: Christian Brauner (Amutable) --- tests/functional/generic/meson.build | 1 + tests/functional/generic/test_monitor_hotplug.py | 102 +++++++++++++++++++= ++++ 2 files changed, 103 insertions(+) diff --git a/tests/functional/generic/meson.build b/tests/functional/generi= c/meson.build index 09763c5d22..c94105c62e 100644 --- a/tests/functional/generic/meson.build +++ b/tests/functional/generic/meson.build @@ -4,6 +4,7 @@ tests_generic_system =3D [ 'empty_cpu_model', 'info_usernet', 'linters', + 'monitor_hotplug', 'version', 'vnc', ] diff --git a/tests/functional/generic/test_monitor_hotplug.py b/tests/funct= ional/generic/test_monitor_hotplug.py new file mode 100644 index 0000000000..f8f24320c6 --- /dev/null +++ b/tests/functional/generic/test_monitor_hotplug.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Functional test for dynamic QMP monitor hotplug +# +# Copyright (c) 2026 Christian Brauner +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +import tempfile + +from qemu.qmp.legacy import QEMUMonitorProtocol + +from qemu_test import QemuSystemTest + + +class MonitorHotplug(QemuSystemTest): + + def setUp(self): + super().setUp() + # Use /tmp to avoid UNIX socket path length limit (108 bytes). + # The scratch_file() path is too deep for socket names. + fd, self._sock_path =3D tempfile.mkstemp( + prefix=3D'qemu-mon-', suffix=3D'.sock') + os.close(fd) + os.unlink(self._sock_path) + + def tearDown(self): + try: + os.unlink(self._sock_path) + except FileNotFoundError: + pass + super().tearDown() + + def _add_monitor(self): + """Create a chardev + monitor and return the socket path.""" + sock =3D self._sock_path + self.vm.cmd('chardev-add', id=3D'hotplug-chr', backend=3D{ + 'type': 'socket', + 'data': { + 'addr': { + 'type': 'unix', + 'data': {'path': sock} + }, + 'server': True, + 'wait': False, + } + }) + self.vm.cmd('monitor-add', id=3D'hotplug-mon', + chardev=3D'hotplug-chr') + return sock + + def _remove_monitor(self): + """Remove the monitor + chardev.""" + self.vm.cmd('monitor-remove', id=3D'hotplug-mon') + self.vm.cmd('chardev-remove', id=3D'hotplug-chr') + + def _connect_and_handshake(self, sock_path): + """ + Connect to the dynamic monitor socket, perform the QMP + greeting and capability negotiation, send a command, then + disconnect. + """ + qmp =3D QEMUMonitorProtocol(sock_path) + + # connect(negotiate=3DTrue) receives the greeting, validates it, + # and sends qmp_capabilities automatically. + greeting =3D qmp.connect(negotiate=3DTrue) + self.assertIn('QMP', greeting) + self.assertIn('version', greeting['QMP']) + self.assertIn('capabilities', greeting['QMP']) + + # Send a real command to prove the session is fully functional + resp =3D qmp.cmd_obj({'execute': 'query-version'}) + self.assertIn('return', resp) + self.assertIn('qemu', resp['return']) + + qmp.close() + + def test_hotplug_cycle(self): + """ + Hotplug a monitor, do the full QMP handshake, unplug it, + then repeat the whole cycle a second time. + """ + self.set_machine('none') + self.vm.add_args('-nodefaults') + self.vm.launch() + + # First cycle + sock =3D self._add_monitor() + self._connect_and_handshake(sock) + self._remove_monitor() + + # Second cycle -- same ids, same path, must work + sock =3D self._add_monitor() + self._connect_and_handshake(sock) + self._remove_monitor() + + +if __name__ =3D=3D '__main__': + QemuSystemTest.main() --=20 2.47.3