From nobody Sat Apr 11 17:08:49 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=minio.io ARC-Seal: i=1; a=rsa-sha256; t=1775722826; cv=none; d=zohomail.com; s=zohoarc; b=fmidGWQOnpEUHlBBvT7mgYKVctDFctogEb8IhRkqtkSZO/EYTkRsUK+ydDXlen/0187gBsu68LrPp8kUwzLkHFbs1PyqXMSt8cxNOpBcxNDEzRVOG+KmZDhpegQfaePU/9609BxnFWUDa3uR4YgiZcoGqcd33SFQTO++hqCB4ZI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775722826; 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=ef/IDwXK0GaH8OkKz0gZMKyBYmbEr0v7UmTk+WfXTa0=; b=HN9xMcX+l/w/OtZduocSWHteQuoGm0jWKVUcGrb1oi/L7ITOcKX+MbkJwaZ/wmKpVQpcG7XeJTIz4aQg3z2w79ko1UKs6V/iS0ambN2qe70TNeIHI2U4DE1ATs4+bnmy1Ef3KsxbpBHGa3hpB01NjUUrSg7wXBdPwEaMLm1GfJ0= 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 (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775722826297467.37717793155673; Thu, 9 Apr 2026 01:20:26 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wAkbf-0006Lx-TB; Thu, 09 Apr 2026 04:19:19 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wAiSi-0005mo-Mh for qemu-devel@nongnu.org; Thu, 09 Apr 2026 02:01:56 -0400 Received: from mail-wr1-x42b.google.com ([2a00:1450:4864:20::42b]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wAiSg-0006ph-Vt for qemu-devel@nongnu.org; Thu, 09 Apr 2026 02:01:56 -0400 Received: by mail-wr1-x42b.google.com with SMTP id ffacd0b85a97d-43d17bb1c65so239259f8f.0 for ; Wed, 08 Apr 2026 23:01:54 -0700 (PDT) Received: from localhost (2a01cb00141148004df9ec80d3fa1cac.ipv6.abo.wanadoo.fr. [2a01:cb00:1411:4800:4df9:ec80:d3fa:1cac]) by smtp.gmail.com with UTF8SMTPSA id ffacd0b85a97d-43d1e4e5890sm62750308f8f.31.2026.04.08.23.01.52 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 08 Apr 2026 23:01:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=minio.io; s=minio; t=1775714513; x=1776319313; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ef/IDwXK0GaH8OkKz0gZMKyBYmbEr0v7UmTk+WfXTa0=; b=QKEfJjZkxx9zjFv1mwhg0rTFwC4kiecz3v7DdoR7FRlUx2bPnTLNwY9zorxfVb1prg x+5bYjrWFMBxUx4aLvy0js33P3QFfMGzodYMqGwhiMR90c5Q/xtAWxlPWHimGW3RlaZr cDadRtt8bySzPlS1MNGkS58SMVFjnziHi9TQmyxaGZB/uaZqIc6ucx2M+PN1aFbzvbLv TyVSUNmiUk5dV445yr4LUIMdy9a84EumzI1yHm4gRH56kHwfDUlY7uatl7m3qcXZ/gRv A1FnHMaZI625B7hApFywl5YWaRKLmuSM238rX25Ye70agSUaH1/4qhhp0X9JJgIG/DVx eEmw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775714513; x=1776319313; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ef/IDwXK0GaH8OkKz0gZMKyBYmbEr0v7UmTk+WfXTa0=; b=T1p/hSMxzNGHSABr7RUQorG/zrnuEXiCF6Wi/pwe9z4a0Vuqh3d5J4i0W+xNzoTQzK QUiHFmvsHi862OcVxtbfbQFK+au1TaioO879mQNvE8IXH9SIxlJg6RQKJT/AwT3gGpk1 SyifkHrs06TM+rL5SnHKq5BD0ck9B5FvZ2NwK71/oPzlLh+DExVmfArj6JSqo5bA9wJz CkoWZbIooVptzRnmwBJ+WC/VouqopDERo/iopuZ62K3d76wTokaZNu2HJ+23gkwRCC1+ WrsFhtjWNjoNDfMNCS68xYJzRVee7BACMsVUIY1g9ga1QTMI8bW6GI27EB6milTsyjr+ 0itQ== X-Gm-Message-State: AOJu0YwU5USTYY6/lRi7AYads4nm7PJAo/oV9yKs/t/07URmaHpHcqY3 Vi5GhpEhD6zl0sYfgor5+xpLSXyTJAB7dhiOjlCGdswmqWVpUhwHxfFGn438heSAvaOO7IqOLF5 DLwdWnwbeOa4lJh/WAq+6D/FztuBrzQ5yUybEByLbkts3dXpIJN+c3ZB1L9HHrjivxphF58c1lT e/HbDDdgwkqUo5QJKmZfhkJsxyVRLH0QO51NQkjkg= X-Gm-Gg: AeBDieuZvzLucpQPifSuuhRPhHGAR26GtYpc7KedrZ9AKpEjuyG4OpJK+9SNsQoAEr5 9XR54fPLRe1bohQ0FOUSesd0PfNC3TO617c6RJrcpZi1+759y5XXqy8dTiXVaF2XrNNryezbmGK /VNlbSdbBC3Ek5zQv3PMB7MnvmXrYsdsp6L/leg50qAn9zEgJIv3MweGkEU2qJqNvO/IZkkbLpV Y30i6h8Uud465+I3TLD5f1Eyb1gJYr6tzSifDN3Mp9KsRTv7/8Lieuni95qPFqMHPoLB5ZkeOfT 2JjqWjlGz0Cw6MlvD1BOCVmR/ZowbWxNuem29otHvQeacb1BhstAr3Q+2MQFnYfYDnht7vyL8sS Qk/rYQ3q2bUGbfMeeeFhSckPgpuT7aDAXncm9afZg+4g05WwHpKBD7ZXMPMJ2qhWaygx7ujXOkf SRsGwPeOzQCRXt/u0mxQ4MJJ/878M6Sq//MiJ40lca5sWWluBJd24iqQ4nd9OOHNBeeLE6a+1iS c6X4t2Pz0IOhfQ93Ye1xKudWNgKS507A0nMr1phh7mDMNNYYFhjUThdqNbi7Q== X-Received: by 2002:a5d:5847:0:b0:43c:f8b4:e58 with SMTP id ffacd0b85a97d-43d292e2a9emr36065425f8f.41.1775714513163; Wed, 08 Apr 2026 23:01:53 -0700 (PDT) From: mr-083 X-Google-Original-From: mr-083 To: qemu-devel@nongnu.org, qemu-block@nongnu.org Cc: its@irrelevant.dk, kbusch@kernel.org, stefanha@redhat.com, mr-083 Subject: [PATCH 1/2] hw/nvme: add namespace hotplug support Date: Thu, 9 Apr 2026 08:01:53 +0200 Message-ID: <20260409060155.94704-2-matthieu@min.io> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260409060155.94704-1-matthieu@min.io> References: <20260409060155.94704-1-matthieu@min.io> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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=2a00:1450:4864:20::42b; envelope-from=matthieu@minio.io; helo=mail-wr1-x42b.google.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HK_NAME_MR_MRS=0.998, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 09 Apr 2026 04:19:18 -0400 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 @minio.io) X-ZM-MESSAGEID: 1775722829487154100 Add hotplug support for nvme-ns devices on the NvmeBus. This enables namespace-level hot-swap without removing the NVMe controller, which is how physical NVMe drives behave when hot-swapped in the same PCIe slot. Mark nvme-ns devices as hotpluggable and register the NvmeBus as a hotplug handler with proper plug and unplug callbacks: - plug: attach namespace to all started controllers and send an Asynchronous Event Notification (AEN) with NS_ATTR_CHANGED so the guest kernel rescans namespaces - unplug: detach from all controllers, send AEN, remove from subsystem, then unrealize the device The plug handler skips controllers that haven't started yet (qs_created =3D=3D false) to avoid interfering with boot-time namespace attachment in nvme_start_ctrl(). Both the controller bus and subsystem bus are configured as hotplug handlers via qbus_set_bus_hotplug_handler() since nvme-ns devices may reparent to the subsystem bus during realize. Signed-off-by: Matthieu Receveur --- hw/nvme/ctrl.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ hw/nvme/ns.c | 1 + hw/nvme/subsys.c | 2 ++ 3 files changed, 88 insertions(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index be6c7028cb..5502e4ea2b 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -206,6 +206,7 @@ #include "system/hostmem.h" #include "hw/pci/msix.h" #include "hw/pci/pcie_sriov.h" +#include "hw/core/qdev.h" #include "system/spdm-socket.h" #include "migration/vmstate.h" =20 @@ -9293,6 +9294,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **= errp) } =20 qbus_init(&n->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id); + qbus_set_bus_hotplug_handler(BUS(&n->bus)); =20 if (nvme_init_subsys(n, errp)) { return; @@ -9553,10 +9555,93 @@ static const TypeInfo nvme_info =3D { }, }; =20 +static void nvme_ns_hot_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + NvmeNamespace *ns =3D NVME_NS(dev); + NvmeSubsystem *subsys =3D ns->subsys; + uint32_t nsid =3D ns->params.nsid; + int i; + + /* + * Attach to all started controllers and notify via AEN. + * Skip controllers that haven't started yet (boot-time realize) =E2= =80=94 + * nvme_start_ctrl() will attach namespaces during controller init. + */ + for (i =3D 0; i < NVME_MAX_CONTROLLERS; i++) { + NvmeCtrl *ctrl =3D nvme_subsys_ctrl(subsys, i); + if (!ctrl || !ctrl->qs_created) { + continue; + } + + if (nvme_csi_supported(ctrl, ns->csi) && !ns->params.detached) { + nvme_attach_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, ns); + + if (!test_and_set_bit(nsid, ctrl->changed_nsids)) { + nvme_enqueue_event(ctrl, NVME_AER_TYPE_NOTICE, + NVME_AER_INFO_NOTICE_NS_ATTR_CHANGED, + NVME_LOG_CHANGED_NSLIST); + } + } + } +} + +static void nvme_ns_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *d= ev, + Error **errp) +{ + NvmeNamespace *ns =3D NVME_NS(dev); + NvmeSubsystem *subsys =3D ns->subsys; + uint32_t nsid =3D ns->params.nsid; + int i; + + /* + * Detach from all controllers and notify the guest via AEN. + * Must happen before unrealize to avoid use-after-free when the + * guest sends I/O to a freed namespace. + */ + for (i =3D 0; i < NVME_MAX_CONTROLLERS; i++) { + NvmeCtrl *ctrl =3D nvme_subsys_ctrl(subsys, i); + if (!ctrl || !nvme_ns(ctrl, nsid)) { + continue; + } + + nvme_detach_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, NULL); + + if (!test_and_set_bit(nsid, ctrl->changed_nsids)) { + nvme_enqueue_event(ctrl, NVME_AER_TYPE_NOTICE, + NVME_AER_INFO_NOTICE_NS_ATTR_CHANGED, + NVME_LOG_CHANGED_NSLIST); + } + } + + /* Remove from subsystem namespace list. */ + subsys->namespaces[nsid] =3D NULL; + + /* + * Unrealize: drain I/O, flush, cleanup structures, remove from QOM. + * nvme_ns_unrealize() handles drain/shutdown/cleanup internally. + */ + qdev_unrealize(dev); +} + +static void nvme_bus_class_init(ObjectClass *klass, const void *data) +{ + HotplugHandlerClass *hc =3D HOTPLUG_HANDLER_CLASS(klass); + hc->plug =3D nvme_ns_hot_plug; + hc->unplug =3D nvme_ns_hot_unplug; +} + static const TypeInfo nvme_bus_info =3D { .name =3D TYPE_NVME_BUS, .parent =3D TYPE_BUS, .instance_size =3D sizeof(NvmeBus), + .class_init =3D nvme_bus_class_init, + .interfaces =3D (const InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, }; =20 static void nvme_register_types(void) diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index b0106eaa5c..eb628c0734 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -937,6 +937,7 @@ static void nvme_ns_class_init(ObjectClass *oc, const v= oid *data) dc->bus_type =3D TYPE_NVME_BUS; dc->realize =3D nvme_ns_realize; dc->unrealize =3D nvme_ns_unrealize; + dc->hotpluggable =3D true; device_class_set_props(dc, nvme_ns_props); dc->desc =3D "Virtual NVMe namespace"; } diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 777e1c620f..fa35055d3c 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "hw/core/qdev.h" =20 #include "nvme.h" =20 @@ -205,6 +206,7 @@ static void nvme_subsys_realize(DeviceState *dev, Error= **errp) NvmeSubsystem *subsys =3D NVME_SUBSYS(dev); =20 qbus_init(&subsys->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id); + qbus_set_bus_hotplug_handler(BUS(&subsys->bus)); =20 nvme_subsys_setup(subsys, errp); } --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 17:08:49 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=minio.io ARC-Seal: i=1; a=rsa-sha256; t=1775722856; cv=none; d=zohomail.com; s=zohoarc; b=U38wcqGF3WuvGNDWyW+qTn7Vsp4pv7StQFfhH/4545UKFbCL+Sa7NCLFATelrn7Q/7SJMqPdpYCUcZDHNtpqqc8wHVRzL3kgzk09W3kv0z2aV3UMiTTr8if6g3NAuKKEcg8TZH2ZH4qp6ZF7v596MbnSn1sXC1n0H+fwdzxmXYw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775722856; h=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=JQ+g+Ftg9KxAdBMCWpE/RWEcneIVeaJ38egOZv8j5vc=; b=UHh8Q/uegldK11PlNoSTqFNWQyaPx6SKJ7p3xcsmTntVeJ5KdfG8jFIov2Qh24awP+smtlDqHg06xouW3xB4grJcnhda7ehv5DkmOrqzAFFM7OCff8Ci+XZj//wt2V13eEAGKjBX514NyKZ1a6mci65jCX4gPCyT3Iw8ZspQEiY= 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 (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775722856813358.8001282908922; Thu, 9 Apr 2026 01:20:56 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wAkbi-0006Mj-Om; Thu, 09 Apr 2026 04:19:22 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wAiSj-0005n5-S4 for qemu-devel@nongnu.org; Thu, 09 Apr 2026 02:01:57 -0400 Received: from mail-wm1-x32c.google.com ([2a00:1450:4864:20::32c]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wAiSh-0006pp-Us for qemu-devel@nongnu.org; Thu, 09 Apr 2026 02:01:57 -0400 Received: by mail-wm1-x32c.google.com with SMTP id 5b1f17b1804b1-488971db0fdso4689325e9.0 for ; Wed, 08 Apr 2026 23:01:55 -0700 (PDT) Received: from localhost (2a01cb00141148004df9ec80d3fa1cac.ipv6.abo.wanadoo.fr. [2a01:cb00:1411:4800:4df9:ec80:d3fa:1cac]) by smtp.gmail.com with UTF8SMTPSA id 5b1f17b1804b1-488cd231a72sm47375955e9.14.2026.04.08.23.01.53 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 08 Apr 2026 23:01:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=minio.io; s=minio; t=1775714514; x=1776319314; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=JQ+g+Ftg9KxAdBMCWpE/RWEcneIVeaJ38egOZv8j5vc=; b=ZWE7DzCCuXVIqjh5sBjNMFS3gAM0vSjgc0e0VeWlpFXavBtkQfvV1N5I3YjVchNnmz UgjoF/yz0v19fpxPaoDUadRFP4UOkROF9NVGm7jPBN395rLUjjxeH+ZZE3sD5IcTuH/y 2eL2P8PiIlgGoIJbTo2UnqJZVm/JqG2FQKzUehvjAF7tB1RHJdwTXSS3FSJC0RcRS7C7 mo9gVPm0awDlKndS8uuy+fTLC0fZQyX2xwKgBJO/ijQM/dKu7RrMngy8PxCaCz9LtDkH LGYvotYux91HUU5g2meJLj0teV33dcrzehERBpi4e8wzmEoqXe4o2A/Tte4mJmYMRpfj mjWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775714514; x=1776319314; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=JQ+g+Ftg9KxAdBMCWpE/RWEcneIVeaJ38egOZv8j5vc=; b=XVZ6/cxo3WE4xse5pikzQU3JixZe2RzdR3LI5+WoK/lVYLjvRqYtpdMU9DkIRxIV8g IYl0goig8WDrn1+M0hQRaoUxl3J019xUvByhBMiNynleqo+j1nzRX57ZxP9L0RG39ZQR 4s2FKMRk5oP3jF9uG+yF6T4OmkfnIxkzADFhknr8Mt7QIbgUjbEu66W6q3Xm82El90YD 6VqA23PeZtHC+bd2XjMDzZy5HAe1+NbLBBE94IbgGYMDQqyerexO9h0ztjKJs/R4f7/3 iUSzAQQoYj43RrmOSPxe0OyFJfunCdx++lG5g19RAmXEkJJVR31nP0BJlL3LswpPvemU 1/Ew== X-Gm-Message-State: AOJu0YzrkuTY+/p7mgetIuraUThNkzi1pFr3lroFX7I8HhyhbFXMaW5F jAwdAGo2fZcZnj6fP3iW5OPmAGTS3whJBI4a8/mj4ONyBUlsyi3vkT0pg3EJYplGwHYXCHNZGUN +Av+YxJ5dmZpkqGhNAUhAMNGQujzoud1EhuELVIoi5MqnoVTbce4babVqjB+1PDxeMbUSOvsi3q FY+EuKzI8MszbS2Cri02fGwcBmPe7hJemrxlYu6Fc= X-Gm-Gg: AeBDies9KOAB2HUz4pjW/EY72hbmJ1PVZGb1BrLy0//aV4Xn1X8hJutZ1lr6gE6zZHw fPD8UXuEzygCfNAMqicKNReCEIVGC4oqTiYd3fXiyNGTed9oe5A9ty1Q/BFHQZQzNk9WA7BHzoq NvOT/lFX5PeG/Jjo68T0KVvAtCHyhyxqk2wsXydFHqCDqr1tL288Fb4MzHs2w4AsrvLDcfVj7+q bS4lHLn5uppfHhHSAbxm9otzqDPJ06ElQQDru1lFPdavhiC8kR1ucleiU5ezT5hAM6kryffpLOf XP0POJdWv37Wpg6JkPloBTjpyBd2pceVVORZsHIlallAoUtZaHj33X2LeV3PnVTimEjISDNjaFf rVNm6w7v1MHgTKFdmsJzBA6buaLpSk6fxG5b51P0JUxls0D4Blfxut437aSdMJMkI5uAndnhbSv N5t9Znpe7O/b6fc9duDVJvx9fzZyPYlNDc4EaTZNK+F865lebUJi1SlBpx6lpsvLdQN5T3WZ0Xt PF0caZpqJq0vD8B69BeR94DkKYZ8UC7faqwB9snWX+wAcWVl3NVoq57lYHeUA== X-Received: by 2002:a05:600c:a311:b0:488:9bf8:7f17 with SMTP id 5b1f17b1804b1-488cd020238mr22822145e9.14.1775714514263; Wed, 08 Apr 2026 23:01:54 -0700 (PDT) From: mr-083 X-Google-Original-From: mr-083 To: qemu-devel@nongnu.org, qemu-block@nongnu.org Cc: its@irrelevant.dk, kbusch@kernel.org, stefanha@redhat.com, mr-083 Subject: [PATCH 2/2] block/monitor: add drive_insert HMP command Date: Thu, 9 Apr 2026 08:01:54 +0200 Message-ID: <20260409060155.94704-3-matthieu@min.io> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260409060155.94704-1-matthieu@min.io> References: <20260409060155.94704-1-matthieu@min.io> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable 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=2a00:1450:4864:20::32c; envelope-from=matthieu@minio.io; helo=mail-wm1-x32c.google.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HK_NAME_MR_MRS=0.998, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 09 Apr 2026 04:19:19 -0400 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 @minio.io) X-ZM-MESSAGEID: 1775722857777154100 Content-Type: text/plain; charset="utf-8" Add a drive_insert HMP command that reconnects a host block device file to an existing guest device whose backing store was previously removed with drive_del. After drive_del, the BlockBackend remains attached to the guest device but has no BlockDriverState (shown as "[not inserted]" in info block). drive_insert opens the specified file, finds the device's BlockBackend by iterating all backends and matching the attached device ID, then calls blk_insert_bs() to reconnect the backing store. This complements drive_del for non-removable devices (such as NVMe namespaces) where blockdev-change-medium cannot be used. Combined with PCIe AER Surprise Down error injection to trigger a controller reset, this enables complete NVMe disk hot-swap simulation where the guest sees the same device names throughout. Example usage: drive_del drv0 # remove backing store drive_insert ns0 disk.qcow2 # reconnect backing pcie_aer_inject_error rp0 SDN # trigger controller reset Signed-off-by: Matthieu Receveur --- block/monitor/block-hmp-cmds.c | 59 ++++++++++++++++++++++++++++++++++ hmp-commands.hx | 18 +++++++++++ include/block/block-hmp-cmds.h | 1 + 3 files changed, 78 insertions(+) diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 1fd28d59eb..77e9662ead 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -38,7 +38,9 @@ #include "qemu/osdep.h" #include "hw/core/boards.h" #include "system/block-backend.h" +#include "system/block-backend-global-state.h" #include "system/blockdev.h" +#include "block/block-global-state.h" #include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-block-export.h" #include "qobject/qdict.h" @@ -195,6 +197,63 @@ unlock: hmp_handle_error(mon, err); } =20 +void hmp_drive_insert(Monitor *mon, const QDict *qdict) +{ + const char *id =3D qdict_get_str(qdict, "id"); + const char *filename =3D qdict_get_str(qdict, "filename"); + BlockBackend *blk =3D NULL; + BlockBackend *iter; + BlockDriverState *bs; + Error *err =3D NULL; + + GLOBAL_STATE_CODE(); + + /* + * After drive_del, the BlockBackend is removed from the monitor name + * registry but still attached to the device. Find it by iterating all + * BlockBackends and matching by the device ID shown in "info block". + */ + for (iter =3D blk_all_next(NULL); iter; iter =3D blk_all_next(iter)) { + DeviceState *dev =3D blk_get_attached_dev(iter); + if (dev && dev->id && strcmp(dev->id, id) =3D=3D 0) { + blk =3D iter; + break; + } + } + + if (!blk) { + /* Fallback: try by block backend name */ + blk =3D blk_by_name(id); + } + + if (!blk) { + error_setg(&err, "Device '%s' not found", id); + goto out; + } + + if (blk_bs(blk)) { + error_setg(&err, "Device '%s' already has a medium inserted", id); + goto out; + } + + bs =3D bdrv_open(filename, NULL, NULL, BDRV_O_RDWR, &err); + if (!bs) { + goto out; + } + + if (blk_insert_bs(blk, bs, &err) < 0) { + bdrv_unref(bs); + goto out; + } + + bdrv_unref(bs); + monitor_printf(mon, "OK\n"); + return; + +out: + hmp_handle_error(mon, err); +} + void hmp_commit(Monitor *mon, const QDict *qdict) { const char *device =3D qdict_get_str(qdict, "device"); diff --git a/hmp-commands.hx b/hmp-commands.hx index 5cc4788f12..79af8e8988 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -207,6 +207,24 @@ SRST actions (drive options rerror, werror). ERST =20 + { + .name =3D "drive_insert", + .args_type =3D "id:B,filename:F", + .params =3D "device filename", + .help =3D "insert a host block device into an empty drive", + .cmd =3D hmp_drive_insert, + }, + +SRST +``drive_insert`` *device* *filename* + Insert a host block device file into a drive that has been emptied by + ``drive_del``. This reconnects the backing store without removing the + guest device, enabling transparent disk hot-swap for non-removable devic= es + such as NVMe namespaces. Combined with PCIe AER Surprise Down error + injection (``pcie_aer_inject_error`` *device* ``SDN``), this enables + complete NVMe disk hot-swap simulation. +ERST + { .name =3D "change", .args_type =3D "device:B,force:-f,target:F,arg:s?,read-only-mode:= s?", diff --git a/include/block/block-hmp-cmds.h b/include/block/block-hmp-cmds.h index 71113cd7ef..73c9607402 100644 --- a/include/block/block-hmp-cmds.h +++ b/include/block/block-hmp-cmds.h @@ -21,6 +21,7 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict); =20 void hmp_commit(Monitor *mon, const QDict *qdict); void hmp_drive_del(Monitor *mon, const QDict *qdict); +void hmp_drive_insert(Monitor *mon, const QDict *qdict); =20 void hmp_drive_mirror(Monitor *mon, const QDict *qdict); void hmp_drive_backup(Monitor *mon, const QDict *qdict); --=20 2.50.1 (Apple Git-155) From nobody Sat Apr 11 17:08:49 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=minio.io ARC-Seal: i=1; a=rsa-sha256; t=1775833348; cv=none; d=zohomail.com; s=zohoarc; b=Vi0razkXl7SmdZr9u+Ybtz6P6nl67b+u/xl/QGF59ui9sJvwW3fsEiNLKvvzKSJTTvSFiJzNOLlU7bipcj0SoBYHRpUTGFncavZsVJTjqL/XHv6IFdTbKLyyT8x0WV1gXO28EaTNQFko+hIsP06P5MgWlnQl9XfA9ucVqK14csU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775833348; 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=ruMXow8DpCxyiShGLxBR3aEG16JIAkSYkotS72n+iGc=; b=hFAUjLA+Nh2PtfZq8sHtmJZNc7S+6jHsz0qOnvFGQLCHImbyGZCJFyNsemWjXLTsEwVzMDz1kaofws19eCSQk5k0UxkSWpTW/5jdM8vGELMsDq5MUVhYNDcLTvMt+ftGOt7a1NL9F3fcrYEKRI7+K4CqAfhCK/eBOyrlTzyLR1U= 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 (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 177583334856128.505554223418926; Fri, 10 Apr 2026 08:02:28 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wBDMs-00041K-9q; Fri, 10 Apr 2026 11:01:58 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wBCrt-0002Wi-FY for qemu-devel@nongnu.org; Fri, 10 Apr 2026 10:29:57 -0400 Received: from mail-wr1-x42c.google.com ([2a00:1450:4864:20::42c]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wBCrr-0008Gq-F2 for qemu-devel@nongnu.org; Fri, 10 Apr 2026 10:29:57 -0400 Received: by mail-wr1-x42c.google.com with SMTP id ffacd0b85a97d-43d02a71526so1301823f8f.3 for ; Fri, 10 Apr 2026 07:29:54 -0700 (PDT) Received: from localhost (2a01cb001411480071f5d8bdb591a8f9.ipv6.abo.wanadoo.fr. [2a01:cb00:1411:4800:71f5:d8bd:b591:a8f9]) by smtp.gmail.com with UTF8SMTPSA id ffacd0b85a97d-43d63de2a74sm8382197f8f.3.2026.04.10.07.29.52 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 10 Apr 2026 07:29:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=minio.io; s=minio; t=1775831393; x=1776436193; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ruMXow8DpCxyiShGLxBR3aEG16JIAkSYkotS72n+iGc=; b=M+ZuNuub6nBdX/KPhJz8dHy+xpMFZ9Iz2Iq4LjPTIUaAqdBYYEjVDf3SeqmKt+O2kk xTYabPIE2THct3wEKJ2BZZLCp6z6TaJuZHn0I5kgHinXCTdPLUir+nuxZLxzmCRGfzce 3l8LzgwKesve1lHiaBpNp9RzKMFqGgl+hPyI4k/fm9i+lzb6oxzsKXGynnAjPd/i+Sev 5fOcgJ9wVLylh7WBjtBTIiAcc3a3SMX0IVmiA0gWuWOkONunRT6piA8RcSClBY7mbv7v C6O4R3ejOr03P9fahBmy6H4vyQ4M7n+0qvc5N4PsXV9oytyoLRJ0+Q1tGzev4eJaPG13 dKSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775831393; x=1776436193; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ruMXow8DpCxyiShGLxBR3aEG16JIAkSYkotS72n+iGc=; b=iLsPJcJXeZHZz89e659pwQgJD94mzanSA0zPZF/L+Cmj+UdST9KowomHpZKNiDvUtd JDkJUeebOZqvb3nIavwx7wwQjXo2R6GEg0ZP3/ZTnDUqU+VwH2yV3bgVa93cBpuYaYyL ZezAgwGlvhZ6ISFSTUyYAqqH9UPF87SHUUX92IM2YYsrPTSn+A5X9Kqi8EjIoRJ/t8SB Ls1I+I3lLSN7u58lQgEQ+uN9h9MuE1dB4P3socyx5HNiKBdtkGdx0ESewhgI5rKE+e1B yI9naVLz9gK9A1IBaEDLWS0Wj/Fk+F1MaCO9QPWw3ApDGRFtgrThID/gGjFYFa7Ef8pL qazg== X-Gm-Message-State: AOJu0YxZH41fJnva720F1aIUkQ85g3oF65JHWsfpUTF5qZRd9F+YDYKM Q0/OSAzlW1dsRI56FHHgZkEzxIcuKFGy3kS1KR0iEluoPxT4KaM5fuPlqIhtlR/Z/8XTFAN0w7Y r2I7NJt1y39iYF/3qv5OXp3gNH3+azRZcUh2BaDx/AdURBH3dplp7jlhQULKSAiIWYh/TceFqZk qXcbig8OGbbl8PUP8e1D5FIt1t4nwMsJzndKn5hLU= X-Gm-Gg: AeBDieukLdm7wWGmth+pEu2g5rHdV6xdavUJAtwCxyYEofOAE0El5iXquQisMLPQZZr Kj5fRIwT5RkPclqwOxuM0iQqUrgBSGo009M90O+sjZisBpJflXlC1PXumWv1jcVUCcoS/iS16+x fWM8bwnrCkyOBOLCgNMtSLYiiU2QiCAc9P/Q7jeSfEqA3Wipq8eBAZPnT8XnePmQoQMn+gn+tnz LTQ3Eg+/r3wGcUWQs8D1nDtI3GsUXMABFXhHZDHHgVCKzTP1YmRnu6YC0f1f2tJLCiSwnBtqc9s wNAFcefVT4Bri8+t0zER9KHKMqgknhW2IfHytQJj5LQMXmLZYanJjYH+/oyKiP0DPsJr8GeIepE M39cpuJg1lxdOIhp4GS+XKBEL2qg6GOXCQjyRy+gw659yFA1rxTs06y35AAwFeg2u7c5B5quIPS c+F5Yk0ShdsZzfBwHAwqxM7b9a50RxnHIpJTeH6NJ4IK7oLRmbizbQpyvYguIasqgRzQ4ldMjb2 Wp+2PqB4O6S8dQUDoXipE7VEcPbFPV5EQok6wVJBW76IHM+MRP243/fSwk60MJF+DIRoeRJ X-Received: by 2002:a05:6000:2082:b0:439:c799:dbfa with SMTP id ffacd0b85a97d-43d6423cff9mr5186798f8f.9.1775831392747; Fri, 10 Apr 2026 07:29:52 -0700 (PDT) From: mr-083 X-Google-Original-From: mr-083 To: qemu-devel@nongnu.org, qemu-block@nongnu.org Cc: its@irrelevant.dk, kbusch@kernel.org, stefanha@redhat.com, mr-083 Subject: [PATCH v3] hw/nvme: add namespace hotplug support Date: Fri, 10 Apr 2026 16:30:00 +0200 Message-ID: <20260410143000.86708-1-matthieu@min.io> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260409060155.94704-1-matthieu@min.io> References: <20260409060155.94704-1-matthieu@min.io> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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=2a00:1450:4864:20::42c; envelope-from=matthieu@minio.io; helo=mail-wr1-x42c.google.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HK_NAME_MR_MRS=0.998, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Fri, 10 Apr 2026 11:01:56 -0400 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 @minio.io) X-ZM-MESSAGEID: 1775833352322158500 Add hotplug support for nvme-ns devices on the NvmeBus. This enables NVMe namespace-level hot-add and hot-remove via device_add and device_del with proper Asynchronous Event Notification (AEN), so the guest kernel can react to namespace topology changes. Mark nvme-ns devices as hotpluggable and register the NvmeBus as a hotplug handler with proper plug and unplug callbacks: - plug: attach namespace to all started controllers and send an Asynchronous Event Notification (AEN) with NS_ATTR_CHANGED so the guest kernel rescans namespaces and adds the block device - unplug: drain in-flight I/O, detach from all controllers, send AEN, then unrealize the device. The guest kernel rescans and removes the block device. The plug handler skips controllers that haven't started yet (qs_created =3D=3D false) to avoid interfering with boot-time namespace attachment in nvme_start_ctrl(). The unplug handler drains in-flight I/O via nvme_ns_drain() before detaching the namespace from controllers, so pending requests can complete normally without touching freed state. For symmetry with nvme_ns_realize() which sets subsys->namespaces[nsid], nvme_ns_unrealize() now clears that slot too =E2=80=94 making the namespace lifecycle complete. Both the controller bus and subsystem bus are configured as hotplug handlers via qbus_set_bus_hotplug_handler() since nvme-ns devices may reparent to the subsystem bus during realize. Example hot-swap sequence using the NVMe subsystem model: # Boot with: -device nvme-subsys,id=3Dsubsys0 # -device nvme,id=3Dctrl0,subsys=3Dsubsys0 # -device nvme-ns,id=3Dns0,drive=3Ddrv0,bus=3Dctrl0,nsid=3D1 device_del ns0 # guest receives AEN, removes /dev/nvme0n1 drive_del drv0 drive_add 0 file=3Ddisk.qcow2,format=3Dqcow2,id=3Ddrv0,if=3Dnone device_add nvme-ns,id=3Dns0,drive=3Ddrv0,bus=3Dctrl0,nsid=3D1 # guest receives AEN, adds /dev/nvme0n1 Tested with Linux 6.1 guest (NVMe driver processes AEN and rescans namespace list automatically). Signed-off-by: Matthieu Reviewed-by: Stefan Hajnoczi --- hw/nvme/ctrl.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ hw/nvme/ns.c | 8 +++++ hw/nvme/subsys.c | 2 ++ 3 files changed, 98 insertions(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index be6c7028cb..2024b0ff75 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -206,6 +206,7 @@ #include "system/hostmem.h" #include "hw/pci/msix.h" #include "hw/pci/pcie_sriov.h" +#include "hw/core/qdev.h" #include "system/spdm-socket.h" #include "migration/vmstate.h" =20 @@ -9293,6 +9294,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **= errp) } =20 qbus_init(&n->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id); + qbus_set_bus_hotplug_handler(BUS(&n->bus)); =20 if (nvme_init_subsys(n, errp)) { return; @@ -9553,10 +9555,96 @@ static const TypeInfo nvme_info =3D { }, }; =20 +static void nvme_ns_hot_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + NvmeNamespace *ns =3D NVME_NS(dev); + NvmeSubsystem *subsys =3D ns->subsys; + uint32_t nsid =3D ns->params.nsid; + int i; + + /* + * Attach to all started controllers and notify via AEN. + * Skip controllers that haven't started yet (boot-time realize) =E2= =80=94 + * nvme_start_ctrl() will attach namespaces during controller init. + */ + for (i =3D 0; i < NVME_MAX_CONTROLLERS; i++) { + NvmeCtrl *ctrl =3D nvme_subsys_ctrl(subsys, i); + if (!ctrl || !ctrl->qs_created) { + continue; + } + + if (nvme_csi_supported(ctrl, ns->csi) && !ns->params.detached) { + nvme_attach_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, ns); + + if (!test_and_set_bit(nsid, ctrl->changed_nsids)) { + nvme_enqueue_event(ctrl, NVME_AER_TYPE_NOTICE, + NVME_AER_INFO_NOTICE_NS_ATTR_CHANGED, + NVME_LOG_CHANGED_NSLIST); + } + } + } +} + +static void nvme_ns_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *d= ev, + Error **errp) +{ + NvmeNamespace *ns =3D NVME_NS(dev); + NvmeSubsystem *subsys =3D ns->subsys; + uint32_t nsid =3D ns->params.nsid; + int i; + + /* + * Drain in-flight I/O before tearing down the namespace. + * This must happen while the namespace is still attached to the + * controllers so any pending requests can complete normally. + */ + nvme_ns_drain(ns); + + /* + * Detach from all controllers and notify the guest via AEN. + * The guest kernel will rescan namespaces and remove the block device. + */ + for (i =3D 0; i < NVME_MAX_CONTROLLERS; i++) { + NvmeCtrl *ctrl =3D nvme_subsys_ctrl(subsys, i); + if (!ctrl || !nvme_ns(ctrl, nsid)) { + continue; + } + + nvme_detach_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, NULL); + + if (!test_and_set_bit(nsid, ctrl->changed_nsids)) { + nvme_enqueue_event(ctrl, NVME_AER_TYPE_NOTICE, + NVME_AER_INFO_NOTICE_NS_ATTR_CHANGED, + NVME_LOG_CHANGED_NSLIST); + } + } + + /* + * Unrealize: removes from subsystem (in nvme_ns_unrealize), flushes, + * cleans up structures, and removes from QOM. + */ + qdev_unrealize(dev); +} + +static void nvme_bus_class_init(ObjectClass *klass, const void *data) +{ + HotplugHandlerClass *hc =3D HOTPLUG_HANDLER_CLASS(klass); + hc->plug =3D nvme_ns_hot_plug; + hc->unplug =3D nvme_ns_hot_unplug; +} + static const TypeInfo nvme_bus_info =3D { .name =3D TYPE_NVME_BUS, .parent =3D TYPE_BUS, .instance_size =3D sizeof(NvmeBus), + .class_init =3D nvme_bus_class_init, + .interfaces =3D (const InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, }; =20 static void nvme_register_types(void) diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index b0106eaa5c..f4f755c6fc 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -719,10 +719,17 @@ void nvme_ns_cleanup(NvmeNamespace *ns) static void nvme_ns_unrealize(DeviceState *dev) { NvmeNamespace *ns =3D NVME_NS(dev); + NvmeSubsystem *subsys =3D ns->subsys; + uint32_t nsid =3D ns->params.nsid; =20 nvme_ns_drain(ns); nvme_ns_shutdown(ns); nvme_ns_cleanup(ns); + + /* Symmetric with nvme_ns_realize() which sets subsys->namespaces[nsid= ]. */ + if (subsys && nsid && subsys->namespaces[nsid] =3D=3D ns) { + subsys->namespaces[nsid] =3D NULL; + } } =20 void nvme_ns_atomic_configure_boundary(bool dn, uint16_t nabsn, @@ -937,6 +944,7 @@ static void nvme_ns_class_init(ObjectClass *oc, const v= oid *data) dc->bus_type =3D TYPE_NVME_BUS; dc->realize =3D nvme_ns_realize; dc->unrealize =3D nvme_ns_unrealize; + dc->hotpluggable =3D true; device_class_set_props(dc, nvme_ns_props); dc->desc =3D "Virtual NVMe namespace"; } diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 777e1c620f..fa35055d3c 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "hw/core/qdev.h" =20 #include "nvme.h" =20 @@ -205,6 +206,7 @@ static void nvme_subsys_realize(DeviceState *dev, Error= **errp) NvmeSubsystem *subsys =3D NVME_SUBSYS(dev); =20 qbus_init(&subsys->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id); + qbus_set_bus_hotplug_handler(BUS(&subsys->bus)); =20 nvme_subsys_setup(subsys, errp); } --=20 2.53.0 From nobody Sat Apr 11 17:08:49 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=minio.io ARC-Seal: i=1; a=rsa-sha256; t=1775781393; cv=none; d=zohomail.com; s=zohoarc; b=fQPT5kDC4sVHuEyXPL8WJMt9WeHJxsCUOgR6mFgYS44pdfhX5Y3gyYc0lqKIK7sagztPlw8JRtImSRORRojXa/WgO42C8UWBgoCDaERZnPAkAS3DBN7ICZvaxRuFiM26EZkEP56B8XoUoUikWgwJ0w9eFaxOIDYCuzeD2DdY/NM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775781393; 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=KvcC0OHWydsrQ65Aj+iaZfcTmw/zkBxH7csPVJaQ8y0=; b=AW0J+EJKWMdNuNoALpeqee5aZgAcUV5OZA5nHq1R08JRCJI7c90lDkbGGktMWZv5cvEaMFrPsCiKo8/7kxX82BWT4/R8ElwPLNeuEZTI+mG+YHDDjNC5FCMrqLOx+DWDnC/6+rr43cNYe7pvHn59SPS7HdEH1H+JL4Nq0ZW+kN8= 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 (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17757813933461002.1000989407198; Thu, 9 Apr 2026 17:36:33 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wAzqT-0005QF-Ou; Thu, 09 Apr 2026 20:35:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wAx1Y-00015p-9a for qemu-devel@nongnu.org; Thu, 09 Apr 2026 17:34:52 -0400 Received: from mail-wm1-x334.google.com ([2a00:1450:4864:20::334]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wAx1W-0000pw-Aa for qemu-devel@nongnu.org; Thu, 09 Apr 2026 17:34:51 -0400 Received: by mail-wm1-x334.google.com with SMTP id 5b1f17b1804b1-488b0e1b870so19742495e9.2 for ; Thu, 09 Apr 2026 14:34:49 -0700 (PDT) Received: from localhost (2a01cb001411480071f5d8bdb591a8f9.ipv6.abo.wanadoo.fr. [2a01:cb00:1411:4800:71f5:d8bd:b591:a8f9]) by smtp.gmail.com with UTF8SMTPSA id 5b1f17b1804b1-488d5888acbsm25725115e9.1.2026.04.09.14.34.47 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 09 Apr 2026 14:34:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=minio.io; s=minio; t=1775770489; x=1776375289; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=KvcC0OHWydsrQ65Aj+iaZfcTmw/zkBxH7csPVJaQ8y0=; b=QatAF1RC3P7XbbCf/17g5ry7yzICj0+hfLui0ZGiMp6PNDGQ7XqdVfu6Kqq0IJ40e1 sbkd0DzOa1x3GCtbvTHiJeCqClrkgyi+i03yLmrC0bugnp7tco9hsc3v9WeglvPFOzFs 2SsAfv98W9jnlD5J1vudhaipOg1/I+L+sDZ++RtbSZQcEcmU80kQ13cEaAufqAitpvbi wHSJtfkoypxT30MjdH1qgQ6SHUTr5fXNbMq0D6NmDTvJhDqabkJcUGsZYPGv+nmJ2s1M Qjme/mkxG9GaAKAOqOx2mRRp8FaTn9cka5kdqnCf2eadkE4TuGMu7b5dOHNBAqXOdQ0n jNBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775770489; x=1776375289; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=KvcC0OHWydsrQ65Aj+iaZfcTmw/zkBxH7csPVJaQ8y0=; b=R0/o0iEgou6Yk8NoV8ehGsXAr09s/i1a1uLXRG2HiSl3omiSL10mtWVhL4E1iJ2wQq NW28wkPAigjPuJHjP7tD9Qfe4aj7ONAovBq+z19TVinLkb6J4XrN8d56KDTKLdFEkYJf mwd4xmLUP0IB4LpzEBXb2JR+0h89wk4NOQNGNGNpWMAhS3rCtMDktSrioYr/psAr+Xxs 6FusbhOKbgwZgy4Kyq2YFfqI9us2LNlDEJJNHasGD5//S7MeVL/gLl5CdhkWObRthf6z 9yrgbhla6Ry+02mk+DP3FTVK0kxDvMF0ZplKkTUkzE5ocQd0xe+KJu2JE2jA/YIzhRS0 o0vA== X-Gm-Message-State: AOJu0YzvjaIL26ISBkzESiXfn0OBbVAW5Gh4cZ2ouFHsxOau9mX7qr6f tMMjPbthFFNtvI/KXJYTm6cjaWdPJYXEBtahTEjTTDMF3y6RT4Dn37BTxZbmtYYyg8xQICyF64Q wd3q+8yDX5X4j0ZIb5chLOcbgQHRBnZKYSzkD8TADN3Jlj8SQKgf/+CajNGCZ809imnmA9xdIx2 CGCfIHoNsU9FpU25ai1rWJq3yzEc8tEIp3gpLElCg= X-Gm-Gg: AeBDievjomhkWnR6IGWh1KDBsWGPQqIlkfvHtb2qJQnPihDUe+3a+ncWfsyyeORaHlN cUNXkBPw5D7PAPFVIJz9MJlTHFln8ap3ztPA7kukK6+owtfG1uRHjZzslqCYW6i79D7qWAtWSMY cqMBBK5kPumg+sktUyq8cGesZoP+wcy2zZTRyFJeyvikegQwz7wt052R6QV/quEIjJlWSgpybL3 SQafror70rDsN65eV41wkSF5nKI7mjup+Xz+ZifMCJhn3gmNO43CNdKGQsqBNOEQ3iDZYnChRo+ Ho2afiDbQU89ZNla81Jhq+/sUzz4JpUrgiWpOaH5e4jEX7J91nA/J4wIwFGMrtPsmTACc7DzXJ1 +EZXeqOIwmokznDZRxgHEE9BmLNJ4ohUaA6HovpJDIALWlJYcSnJXBHfcdiudqdII964l4L0TWT 0hSylTxfeduknQFudIb5hmEUCLfaHF8HsCaEI6g4X4Me3BoEvpdZCOkhG8puXpYsZVP1SeSn5wR ncjBnzxEmEviFLrogfsl0tDyzevelko0ITujZogCmpaj9ZxEz1W17j0VgB2aw== X-Received: by 2002:a05:600c:8b30:b0:488:b14f:b8ed with SMTP id 5b1f17b1804b1-488d6657928mr5875225e9.0.1775770488168; Thu, 09 Apr 2026 14:34:48 -0700 (PDT) From: mr-083 X-Google-Original-From: mr-083 To: qemu-devel@nongnu.org, qemu-block@nongnu.org Cc: its@irrelevant.dk, kbusch@kernel.org, stefanha@redhat.com, mr-083 Subject: [PATCH v2] hw/nvme: add namespace hotplug support Date: Thu, 9 Apr 2026 23:34:51 +0200 Message-ID: <20260409213454.5186-1-matthieu@min.io> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260409060155.94704-1-matthieu@min.io> References: <20260409060155.94704-1-matthieu@min.io> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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=2a00:1450:4864:20::334; envelope-from=matthieu@minio.io; helo=mail-wm1-x334.google.com X-Spam_score_int: -10 X-Spam_score: -1.1 X-Spam_bar: - X-Spam_report: (-1.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HK_NAME_MR_MRS=0.998, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 09 Apr 2026 20:35:35 -0400 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 @minio.io) X-ZM-MESSAGEID: 1775781397233154100 Add hotplug support for nvme-ns devices on the NvmeBus. This enables namespace-level hot-swap without removing the NVMe controller, matching the behavior of physical NVMe drives hot-swapped in the same PCIe slot. Mark nvme-ns devices as hotpluggable and register the NvmeBus as a hotplug handler with proper plug and unplug callbacks: - plug: attach namespace to all started controllers and send an Asynchronous Event Notification (AEN) with NS_ATTR_CHANGED so the guest kernel rescans namespaces and adds the block device - unplug: detach from all controllers, send AEN, remove from subsystem, then unrealize the device. The guest kernel rescans and removes the block device. The plug handler skips controllers that haven't started yet (qs_created =3D=3D false) to avoid interfering with boot-time namespace attachment in nvme_start_ctrl(). Both the controller bus and subsystem bus are configured as hotplug handlers via qbus_set_bus_hotplug_handler() since nvme-ns devices may reparent to the subsystem bus during realize. Example hot-swap sequence using the NVMe subsystem model: # Boot with: -device nvme-subsys,id=3Dsubsys0 # -device nvme,id=3Dctrl0,subsys=3Dsubsys0 # -device nvme-ns,id=3Dns0,drive=3Ddrv0,bus=3Dctrl0,nsid=3D1 device_del ns0 # guest receives AEN, removes /dev/nvme0n1 drive_del drv0 drive_add 0 file=3Ddisk.qcow2,format=3Dqcow2,id=3Ddrv0,if=3Dnone device_add nvme-ns,id=3Dns0,drive=3Ddrv0,bus=3Dctrl0,nsid=3D1 # guest receives AEN, adds /dev/nvme0n1 Tested with Linux 6.1 guest (NVMe driver processes AEN and rescans namespace list automatically). Signed-off-by: Matthieu Receveur --- hw/nvme/ctrl.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ hw/nvme/ns.c | 1 + hw/nvme/subsys.c | 2 ++ 3 files changed, 88 insertions(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index be6c7028cb..5502e4ea2b 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -206,6 +206,7 @@ #include "system/hostmem.h" #include "hw/pci/msix.h" #include "hw/pci/pcie_sriov.h" +#include "hw/core/qdev.h" #include "system/spdm-socket.h" #include "migration/vmstate.h" =20 @@ -9293,6 +9294,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **= errp) } =20 qbus_init(&n->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id); + qbus_set_bus_hotplug_handler(BUS(&n->bus)); =20 if (nvme_init_subsys(n, errp)) { return; @@ -9553,10 +9555,93 @@ static const TypeInfo nvme_info =3D { }, }; =20 +static void nvme_ns_hot_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + NvmeNamespace *ns =3D NVME_NS(dev); + NvmeSubsystem *subsys =3D ns->subsys; + uint32_t nsid =3D ns->params.nsid; + int i; + + /* + * Attach to all started controllers and notify via AEN. + * Skip controllers that haven't started yet (boot-time realize) =E2= =80=94 + * nvme_start_ctrl() will attach namespaces during controller init. + */ + for (i =3D 0; i < NVME_MAX_CONTROLLERS; i++) { + NvmeCtrl *ctrl =3D nvme_subsys_ctrl(subsys, i); + if (!ctrl || !ctrl->qs_created) { + continue; + } + + if (nvme_csi_supported(ctrl, ns->csi) && !ns->params.detached) { + nvme_attach_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, ns); + + if (!test_and_set_bit(nsid, ctrl->changed_nsids)) { + nvme_enqueue_event(ctrl, NVME_AER_TYPE_NOTICE, + NVME_AER_INFO_NOTICE_NS_ATTR_CHANGED, + NVME_LOG_CHANGED_NSLIST); + } + } + } +} + +static void nvme_ns_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *d= ev, + Error **errp) +{ + NvmeNamespace *ns =3D NVME_NS(dev); + NvmeSubsystem *subsys =3D ns->subsys; + uint32_t nsid =3D ns->params.nsid; + int i; + + /* + * Detach from all controllers and notify the guest via AEN. + * Must happen before unrealize to avoid use-after-free when the + * guest sends I/O to a freed namespace. + */ + for (i =3D 0; i < NVME_MAX_CONTROLLERS; i++) { + NvmeCtrl *ctrl =3D nvme_subsys_ctrl(subsys, i); + if (!ctrl || !nvme_ns(ctrl, nsid)) { + continue; + } + + nvme_detach_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, NULL); + + if (!test_and_set_bit(nsid, ctrl->changed_nsids)) { + nvme_enqueue_event(ctrl, NVME_AER_TYPE_NOTICE, + NVME_AER_INFO_NOTICE_NS_ATTR_CHANGED, + NVME_LOG_CHANGED_NSLIST); + } + } + + /* Remove from subsystem namespace list. */ + subsys->namespaces[nsid] =3D NULL; + + /* + * Unrealize: drain I/O, flush, cleanup structures, remove from QOM. + * nvme_ns_unrealize() handles drain/shutdown/cleanup internally. + */ + qdev_unrealize(dev); +} + +static void nvme_bus_class_init(ObjectClass *klass, const void *data) +{ + HotplugHandlerClass *hc =3D HOTPLUG_HANDLER_CLASS(klass); + hc->plug =3D nvme_ns_hot_plug; + hc->unplug =3D nvme_ns_hot_unplug; +} + static const TypeInfo nvme_bus_info =3D { .name =3D TYPE_NVME_BUS, .parent =3D TYPE_BUS, .instance_size =3D sizeof(NvmeBus), + .class_init =3D nvme_bus_class_init, + .interfaces =3D (const InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, }; =20 static void nvme_register_types(void) diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index b0106eaa5c..eb628c0734 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -937,6 +937,7 @@ static void nvme_ns_class_init(ObjectClass *oc, const v= oid *data) dc->bus_type =3D TYPE_NVME_BUS; dc->realize =3D nvme_ns_realize; dc->unrealize =3D nvme_ns_unrealize; + dc->hotpluggable =3D true; device_class_set_props(dc, nvme_ns_props); dc->desc =3D "Virtual NVMe namespace"; } diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 777e1c620f..fa35055d3c 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "hw/core/qdev.h" =20 #include "nvme.h" =20 @@ -205,6 +206,7 @@ static void nvme_subsys_realize(DeviceState *dev, Error= **errp) NvmeSubsystem *subsys =3D NVME_SUBSYS(dev); =20 qbus_init(&subsys->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev, dev->id); + qbus_set_bus_hotplug_handler(BUS(&subsys->bus)); =20 nvme_subsys_setup(subsys, errp); } --=20 2.53.0