[PATCH v3 2/2] hw/nvme: support online resize
Alexander Mikhalitsyn <[email protected]> Mon, 3 Aug 2026 10:30:46 +0200
| Newsgroups | org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
From: Alexander Mikhalitsyn <[email protected]> Implement (BlockDevOps *)->resize_cb() for NVMe namespace to enable online resize support. We must handle cases when multiple namespaces are attached to a single controller, or namespace is shared across a few different controllers by iterating over controllers attached to a NvmeSubsystem and properly notify every controller about a size change. Signed-off-by: Alexander Mikhalitsyn <[email protected]> --- v3: - rebased v2: - check-patch fixes --- hw/nvme/ctrl.c | 9 +++++++ hw/nvme/ns.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++- hw/nvme/nvme.h | 2 ++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 710c3e0d77d..d60b3e105a1 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -7009,6 +7009,15 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) return NVME_SUCCESS; } +void nvme_ctrl_notify_ns_resize(NvmeCtrl *ctrl, NvmeNamespace *ns) +{ + if (!test_and_set_bit(ns->params.nsid, ctrl->changed_nsids)) { + nvme_enqueue_event(ctrl, NVME_AER_TYPE_NOTICE, + NVME_AER_INFO_NOTICE_NS_ATTR_CHANGED, + NVME_LOG_CHANGED_NSLIST); + } +} + typedef struct NvmeFormatAIOCB { BlockAIOCB common; BlockAIOCB *aiocb; diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 7f0f9ac7662..e2954fd91b3 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -163,6 +163,70 @@ lbaf_found: return 0; } +static void nvme_ns_resize_cb(void *opaque) +{ + NvmeNamespace *ns = opaque; + int64_t size; + int cntlid, notified_ctrls; + + size = blk_getlength(ns->blkconf.blk); + if (size < 0) { + error_report("can't get size of block device %s: %s", + blk_name(ns->blkconf.blk), strerror(-size)); + return; + } + + ns->size = size; + nvme_ns_init_format(ns); + + if (!ns->attached) { + return; + } + + /* + * Okay, the namespace is attached so we need to notify all controllers + * about size change. + * + * Let's just take ns->subsys, iterate over all controllers and find + * to which of them our namespace is attached. + */ + + assert(ns->subsys); + assert(ns->subsys->ctrls); + + notified_ctrls = 0; + for (cntlid = 0; cntlid < ARRAY_SIZE(ns->subsys->ctrls); cntlid++) { + NvmeCtrl *ctrl; + + /* notified everyone? */ + if (notified_ctrls == ns->attached) { + break; + } + + ctrl = nvme_subsys_ctrl(ns->subsys, cntlid); + if (!ctrl) { + continue; + } + + for (uint32_t nsid = 1; nsid <= NVME_MAX_NAMESPACES; nsid++) { + NvmeNamespace *ns_iter = ctrl->namespaces[nsid]; + + if (!ns_iter || ns_iter != ns) { + continue; + } + + nvme_ctrl_notify_ns_resize(ctrl, ns); + + notified_ctrls++; + break; + } + } +} + +static const BlockDevOps nvme_ns_block_ops = { + .resize_cb = nvme_ns_resize_cb, +}; + static int nvme_ns_init_blk(NvmeNamespace *ns, Error **errp) { bool read_only; @@ -172,10 +236,12 @@ static int nvme_ns_init_blk(NvmeNamespace *ns, Error **errp) } read_only = !blk_supports_write_perm(ns->blkconf.blk); - if (!blkconf_apply_backend_options(&ns->blkconf, read_only, false, errp)) { + if (!blkconf_apply_backend_options(&ns->blkconf, read_only, true, errp)) { return -1; } + blk_set_dev_ops(ns->blkconf.blk, &nvme_ns_block_ops, ns); + if (ns->blkconf.discard_granularity == -1) { ns->blkconf.discard_granularity = MAX(ns->blkconf.logical_block_size, MIN_DISCARD_GRANULARITY); diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 43e3c916f73..a4b2c01a2cc 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -764,6 +764,8 @@ void nvme_atomic_configure_max_write_size(bool dn, uint16_t awun, void nvme_ns_atomic_configure_boundary(bool dn, uint16_t nabsn, uint16_t nabspf, NvmeAtomic *atomic); +void nvme_ctrl_notify_ns_resize(NvmeCtrl *ctrl, NvmeNamespace *ns); + extern const VMStateDescription nvme_vmstate_atomic; extern const VMStateDescription nvme_vmstate_ns; -- 2.47.3