From nobody Tue Apr 7 11:18:24 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; 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=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1773411251; cv=none; d=zohomail.com; s=zohoarc; b=jbh76B0JDrfXGD/H9DYNbSESpZXyqBD673XDRXO7qPZ9XeFR6nmOMX8ChXydTxkpTEgkHVOtPPEIu+5rZpcjtZlLQ/t68zvTWwBILLw4Jy4brsl/W+1/UDh3Eu3R/OJs9NSSt9ZBAXsRIiFD7se1e/l3ferqv6LAznLa9RaKxSc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773411251; 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:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=vO92Zi9/ERTEc9krJOx/gmUGwLnlw9NS0k8O07O6ydg=; b=X2ETnFjCv7jhphiPGHnX3/D9LlIgFuSu7CbnTnK5pOjj2oKTyqFwI4TBrsaYKWn2+OPxELC8ADOCBk/y5K4EhHYyP0qE+5npB8rPJ8sdmi0B/wgZiyZDEzMv6l/1stzXGEPhR2O7okikdK4o92sbijIJ0srD4x/Ycfo8cp9RXHQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; 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=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1773411251790798.547811394698; Fri, 13 Mar 2026 07:14:11 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w13GF-0005Tr-CL; Fri, 13 Mar 2026 10:13:07 -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 1w13Fq-0005Q8-QY for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:44 -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 1w13Fl-0006M2-RI for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:42 -0400 Received: by mail-wr1-x42c.google.com with SMTP id ffacd0b85a97d-4327790c4e9so1757461f8f.2 for ; Fri, 13 Mar 2026 07:12:26 -0700 (PDT) Received: from alex-laptop.lan (p200300cf57228c00321ddb23f48fbb73.dip0.t-ipconnect.de. [2003:cf:5722:8c00:321d:db23:f48f:bb73]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe19ad9asm18580939f8f.7.2026.03.13.07.12.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Mar 2026 07:12:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mihalicyn.com; s=mihalicyn; t=1773411145; x=1774015945; 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=vO92Zi9/ERTEc9krJOx/gmUGwLnlw9NS0k8O07O6ydg=; b=j23QRBDSOP1tAuaXE0Xjbjei2KVgV08lDtZhmsLbvvThgKoVbfsujUGmnMGkuDpMsC Bqm3mHwo8gVQZJdTdaJgHaGfOcjPm0bDb6TfPJnmTrLm6wVf5QVVkjpZc8lCVl1IhgDW ar/7RkEzLjNhMQujDUAHgEoZXVFSmPlbKCTBs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773411145; x=1774015945; 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=vO92Zi9/ERTEc9krJOx/gmUGwLnlw9NS0k8O07O6ydg=; b=ezlYE1x3LZpzOwolvaDXAgqfO1GwSZDr4+YzUsDtEV0GjCnuGN7t7pltrfdiNrgFoH vRFtw8jC215r1qvusHRS1vQkpqKVveCeXbe8TkPrN1lc7PnHAV3BdgisbxxBIeazzktG k1D5ssMXB/x4Xemf8qBzdQSgin11U4YS2vqNuwMQ0iTPLZPypqHT3WdoSL63AHPQ/lNf iBrYHBdAJy1EoHpmyT0j5AJgs+DJjs9vifnd9fA09qzpuPDv8Wz4hOuxZjdMcoKulQDz UN1athn59BKGS/qbRWtamQ6J+Khqbk22As4M6GCSaPQ1+rJNv/tmrDb3gQCg1XZm0rDk ehiA== X-Gm-Message-State: AOJu0YzDik6qxyKlDLLU+sW+95wpobIcUXNbbZgCqOe0XyCi77IxAT7b bWUx/4Ico3u3JZDQ5Y9xMXRLGCpN/wwAURjkt2o15r2sAnlHCFDMKrJw35jDZUFU3ow1NcFJOLa vqYf9uuQ= X-Gm-Gg: ATEYQzwbCexE+nGZUqR16C1o6m+IVGviD3A9RAUKnMuSmDt8NXGv/S0ta2DfbOpxN03 KtQI+VnthIKvM+kwCP/puQrV5/mqikuObBYK5HiLebiCk0JnQOub4c8X3hlDGMaKS/nBRpubKvM 8uzAOGHawNN/fLse8AoAneCCDGnxWHQRf/R69jnfq+QvhjBvOe2AFPovgx7i5DABIUvi5nTeGe3 /fMNvbLRVYpZfF0+l6E+g/7ePr0RszprkGYAIzDTrtmUZJqQjcvO3/WO6F4Fgl0ArdIbRFQwZWv 0gBJwBk3UBWG6iqvGEioZpdNurqxztG0riBb7AmyVfroCJZwN18yHHsTDIQ7F5ObEtx2a8P0tH1 NUO/QBDuijV0FPS+s4SEAWEXZExRtJyznPRL2HHjDAljtjW9Up3vPuovOLQMIzABUOyKtqVlG82 yfmutt1rukWOBXAD+5WnoxqkQRtmfIKI+pjAot/aBpj2aORnOA4VNkKqQBDPilcOmbPP/ClB/2h 80iYuWM3OGyiAzHNT6ovd0= X-Received: by 2002:a05:6000:40de:b0:439:ca7b:f4b with SMTP id ffacd0b85a97d-43a04dcbebbmr6326109f8f.50.1773411145217; Fri, 13 Mar 2026 07:12:25 -0700 (PDT) To: qemu-devel@nongnu.org Cc: Peter Xu , Jesper Devantier , Kevin Wolf , Fabiano Rosas , Paolo Bonzini , Hanna Reitz , qemu-block@nongnu.org, =?UTF-8?q?St=C3=A9phane=20Graber?= , Keith Busch , Stefan Hajnoczi , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Alexander Mikhalitsyn , Fam Zheng , Klaus Jensen , Zhao Liu , Alexander Mikhalitsyn Subject: [PATCH v4 1/8] migration/vmstate: introduce vmstate_{load, save}_field helpers Date: Fri, 13 Mar 2026 15:12:14 +0100 Message-ID: <20260313141221.359503-2-alexander@mihalicyn.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260313141221.359503-1-alexander@mihalicyn.com> References: <20260313141221.359503-1-alexander@mihalicyn.com> 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::42c; envelope-from=alexander@mihalicyn.com; helo=mail-wr1-x42c.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.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, RCVD_IN_DNSWL_NONE=-0.0001, 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: , Reply-to: Alexander Mikhalitsyn From: Alexander Mikhalitsyn via qemu development Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1773411252817154100 Content-Type: text/plain; charset="utf-8" From: Alexander Mikhalitsyn Let's introduce vmstate_{load,save}_field() helpers, they will be used in next patches to support fully-dynamic arrays with NULLs. Signed-off-by: Alexander Mikhalitsyn --- include/migration/vmstate.h | 4 ++ migration/vmstate.c | 84 +++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 32 deletions(-) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 62c2abd0c49..5d7dfe70643 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -1240,8 +1240,12 @@ extern const VMStateInfo vmstate_info_qlist; .flags =3D VMS_END, \ } =20 +int vmstate_load_field(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, Error **errp); int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id, Error **errp); +int vmstate_save_field(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc, Erro= r **errp); int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc, Error **errp); int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, diff --git a/migration/vmstate.c b/migration/vmstate.c index 4d28364f7ba..8d192bcaa27 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -131,6 +131,32 @@ static void vmstate_handle_alloc(void *ptr, const VMSt= ateField *field, } } =20 +int vmstate_load_field(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, Error **errp) +{ + int ret =3D 0; + + if (field->flags & VMS_STRUCT) { + ret =3D vmstate_load_state(f, field->vmsd, pv, + field->vmsd->version_id, + errp); + } else if (field->flags & VMS_VSTRUCT) { + ret =3D vmstate_load_state(f, field->vmsd, pv, + field->struct_version_id, + errp); + } else { + ret =3D field->info->get(f, pv, size, field); + if (ret < 0) { + error_setg(errp, + "Failed to load element of type %s for %s: " + "%d", field->info->name, + field->name, ret); + } + } + + return ret; +} + int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id, Error **errp) { @@ -203,24 +229,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescr= iption *vmsd, inner_field =3D field; } =20 - if (inner_field->flags & VMS_STRUCT) { - ret =3D vmstate_load_state(f, inner_field->vmsd, curr_= elem, - inner_field->vmsd->version_id, - errp); - } else if (inner_field->flags & VMS_VSTRUCT) { - ret =3D vmstate_load_state(f, inner_field->vmsd, curr_= elem, - inner_field->struct_version_i= d, - errp); - } else { - ret =3D inner_field->info->get(f, curr_elem, size, - inner_field); - if (ret < 0) { - error_setg(errp, - "Failed to load element of type %s for = %s: " - "%d", inner_field->info->name, - inner_field->name, ret); - } - } + ret =3D vmstate_load_field(f, curr_elem, size, inner_field= , errp); =20 /* If we used a fake temp field.. free it now */ if (inner_field !=3D field) { @@ -427,6 +436,29 @@ int vmstate_save_state(QEMUFile *f, const VMStateDescr= iption *vmsd, return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_= id, errp); } =20 +int vmstate_save_field(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc, Erro= r **errp) +{ + int ret =3D 0; + + if (field->flags & VMS_STRUCT) { + ret =3D vmstate_save_state(f, field->vmsd, pv, vmdesc, errp); + } else if (field->flags & VMS_VSTRUCT) { + ret =3D vmstate_save_state_v(f, field->vmsd, pv, vmdesc, + field->struct_version_id, errp); + } else { + ret =3D field->info->put(f, pv, size, field, vmdesc); + if (ret < 0) { + error_setg(errp, + "Failed to save element of type %s for %s: " + "%d", field->info->name, + field->name, ret); + } + } + + return ret; +} + int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, JSONWriter *vmdesc, int version_id,= Error **errp) { @@ -528,19 +560,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDes= cription *vmsd, vmsd_desc_field_start(vmsd, vmdesc_loop, inner_field, i, max_elems); =20 - if (inner_field->flags & VMS_STRUCT) { - ret =3D vmstate_save_state(f, inner_field->vmsd, - curr_elem, vmdesc_loop, errp); - } else if (inner_field->flags & VMS_VSTRUCT) { - ret =3D vmstate_save_state_v(f, inner_field->vmsd, - curr_elem, vmdesc_loop, - inner_field->struct_version= _id, - errp); - } else { - ret =3D inner_field->info->put(f, curr_elem, size, - inner_field, vmdesc_loop); - } - + ret =3D vmstate_save_field(f, curr_elem, size, inner_field= , vmdesc_loop, errp); written_bytes =3D qemu_file_transferred(f) - old_offset; vmsd_desc_field_end(vmsd, vmdesc_loop, inner_field, written_bytes); @@ -551,7 +571,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDesc= ription *vmsd, } =20 if (ret) { - error_setg(errp, "Save of field %s/%s failed", + error_prepend(errp, "Save of field %s/%s failed: ", vmsd->name, field->name); if (vmsd->post_save) { vmsd->post_save(opaque); --=20 2.47.3 From nobody Tue Apr 7 11:18:24 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=mihalicyn.com ARC-Seal: i=1; a=rsa-sha256; t=1773411251; cv=none; d=zohomail.com; s=zohoarc; b=WnVgF5tQaNyZ4rfOBryxVc4KPwHOc7gcCKgbMNIRhN3CMdvMUZq8Tqz/z5xC3Bc2eKcbdQm4tUEOr9E6uKoZihrfoBkcxpJooSH1WoaPTAL5fPB10e2agZcokax9PVaVao6EMrMavVWqSIYiwtOrON0rz5Gd+yKjLvm/5myQVQ8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773411251; 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=kQ4LV65YsJl6GdSC63rqTaRoMXCCx30M/dn/ZkgCAiU=; b=TPzP3V3mDn2fCoVkTAXfcXMZM9VLTd+Dp0GHI+Q5xP6cL+i0IVRhaeV2u9RjdihdVVmnQFwE8SurnrbTtc1xegcHZy9GDy5sVeW3I+asPfnSI005yNXK0xyGBI2cfc7z+leolQyHwy0k+QbFYGOY0mfZE2eiJQPxpj6vcfpUm8E= 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 1773411251940885.6541190510629; Fri, 13 Mar 2026 07:14:11 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w13GM-0005Vv-5k; Fri, 13 Mar 2026 10:13:14 -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 1w13Fs-0005QN-Ir for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:51 -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 1w13Fl-0006MP-RD for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:44 -0400 Received: by mail-wr1-x42c.google.com with SMTP id ffacd0b85a97d-439ac15f35fso1985423f8f.0 for ; Fri, 13 Mar 2026 07:12:27 -0700 (PDT) Received: from alex-laptop.lan (p200300cf57228c00321ddb23f48fbb73.dip0.t-ipconnect.de. [2003:cf:5722:8c00:321d:db23:f48f:bb73]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe19ad9asm18580939f8f.7.2026.03.13.07.12.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Mar 2026 07:12:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mihalicyn.com; s=mihalicyn; t=1773411147; x=1774015947; 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=kQ4LV65YsJl6GdSC63rqTaRoMXCCx30M/dn/ZkgCAiU=; b=Rj9ElFrACVLec2VikyrT21BE4noCIJQuYj0sZqd7lSmQrmVGkeEP1cOT0GAyWXf8qP t/uQO2INgP3lDEDDyefxp9NOfDTpZjMAB7VssaTl1AyjET+RB/DYVnp7QtZTujXGHT0z yXPkW0olH5p/TjCsAmnS1pnW8kG67RzEpaS7c= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773411147; x=1774015947; 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=kQ4LV65YsJl6GdSC63rqTaRoMXCCx30M/dn/ZkgCAiU=; b=V833W9mfyolOYOfYwX172zAZtVYkZRvfee8aK1VDtM/314jilcwubZl0hE17sFI3M1 ihoF/+xErqcUNv3Tla19dTWjSnWNn10agbvpTImkfaXHpKw+dD9BCyvV6gaGLY2ZuNOg zTKUELQoVjf/9sCVbg5KVB9UyFVWRIwMYEKWxFnPYIwOb1z78LyPkdxVw04q6gz65G9j f2rPR+6eYjj3hBDV4J/w+gonXsGkriG0aMvWVT3MQAoZGsMADjQljpKgQfrarEYPCcMb yZG1x+Z4Eu3s6WqJoW8GvyNqGkTaIj2DDiVWWVz4mFoSQAEEUxhgLbn3Oqb6rqiVeSiz Ch6Q== X-Gm-Message-State: AOJu0Yy+2rC4gD6gYLihDtFz+/poljpDs+r8U8jewa7xtP6w6C5T7FGZ ntQ93i9igoKXM5XvjHvcCahocBnx5OQaOHdEjSrhPeSzeTNV0526j1U4XRHoOou+zC2bi0j9g7r OKgSVp/Q= X-Gm-Gg: ATEYQzxe85crOKXIqk5bbJw0ovLO5beVF8a0yCcpOx5Unvqj62YBLOVSxthhHCvD/A0 din6RFGP6blw7u1O39980kklVMjN1nJ6tuaTx6EFdRl1d4qadn75Mw/eFxFO0ZB+12JrZIVj88H LF+f17adfq/CLismPfVCIZPg8KjzwKDIEakEJD5H2XM9AfNUkeJD72uMTR5TuvFqNxxRanM2pzD /XczNMwhkY3mwHoQJWTI3u1Lg7IHVjitOLZderDEkaX8JaFLQug2YqUZ1J1TSDyPBi0TjzLJuTx vJS8i/VhCLn9Uh7I3vOyK4PfgHAwAC3150mKuGvBfJYsrBDS8+T84/yoFqzDvlyMkKyP8DTZRs6 h2TP8XOTl3L3ywqC13BmHsMUBGHNIyH8HzYLq+AftkTmi1ZgqJqUpyvOF7Sh0eTVfJCClu5OPm8 7PxY5lUQJ/puAjQLVCCz4c62VRNrSgE4VaAgH7YVqur0UrwaHUFA2FcpxjlBYW0RXJZqTIT3ast fT5XKzaLZ3voOsFUkJ1ZL0= X-Received: by 2002:a05:6000:4305:b0:439:afea:aff2 with SMTP id ffacd0b85a97d-43a04d81d37mr6812192f8f.23.1773411146429; Fri, 13 Mar 2026 07:12:26 -0700 (PDT) From: Alexander Mikhalitsyn To: qemu-devel@nongnu.org Cc: Peter Xu , Jesper Devantier , Kevin Wolf , Fabiano Rosas , Paolo Bonzini , Hanna Reitz , qemu-block@nongnu.org, =?UTF-8?q?St=C3=A9phane=20Graber?= , Keith Busch , Stefan Hajnoczi , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Alexander Mikhalitsyn , Fam Zheng , Klaus Jensen , Zhao Liu , Alexander Mikhalitsyn Subject: [PATCH v4 2/8] migration: add VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8, 32}_ALLOC Date: Fri, 13 Mar 2026 15:12:15 +0100 Message-ID: <20260313141221.359503-3-alexander@mihalicyn.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260313141221.359503-1-alexander@mihalicyn.com> References: <20260313141221.359503-1-alexander@mihalicyn.com> 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::42c; envelope-from=alexander@mihalicyn.com; helo=mail-wr1-x42c.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.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, RCVD_IN_DNSWL_NONE=-0.0001, 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 @mihalicyn.com) X-ZM-MESSAGEID: 1773411252643154100 Content-Type: text/plain; charset="utf-8" From: Alexander Mikhalitsyn Add VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT{8, 32}_ALLOC, which helps to save/restore a dynamic array of pointers to structures. Suggested-by: Peter Xu Signed-off-by: Alexander Mikhalitsyn v2: - added VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT8_ALLOC v4: - almost completely reworked, new flag VMS_ARRAY_OF_POINTER_ALLOW_NULL was introduced as suggested by Peter --- include/migration/vmstate.h | 77 ++++++++++++++++++++++++++- migration/savevm.c | 26 +++++++++ migration/vmstate-types.c | 102 ++++++++++++++++++++++++++++++++++++ migration/vmstate.c | 54 +++++++++++++++---- 4 files changed, 247 insertions(+), 12 deletions(-) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 5d7dfe70643..70588ed86d7 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -151,7 +151,19 @@ enum VMStateFlags { VMS_VSTRUCT =3D 0x8000, =20 /* Marker for end of list */ - VMS_END =3D 0x10000 + VMS_END =3D 0x10000, + + /* The field is a (fixed-size or variable-size) array of pointers + * (e.g. struct a { uint8_t **b; }) that can contain NULL values. + * This instructs vmstate engine to: + * - Dereference each array entry before using it. + * - Assume that array is initialized with NULLs on load phase + * - Automatically allocate memory for array entries (with size + * specified in (VMStateField).start) on load phase + * - Produce NULL/not-NULL markers in migration stream + * + * Note: Does not imply VMS_ARRAY_OF_POINTER; it needs to be set expli= citly. */ + VMS_ARRAY_OF_POINTER_ALLOW_NULL =3D 0x20000, }; =20 typedef enum { @@ -184,6 +196,7 @@ struct VMStateField { int version_id; int struct_version_id; bool (*field_exists)(void *opaque, int version_id); + const struct VMStateField *real_field; }; =20 struct VMStateDescription { @@ -252,8 +265,10 @@ extern const VMStateInfo vmstate_info_uint64; extern const VMStateInfo vmstate_info_fd; =20 /** Put this in the stream when migrating a null pointer.*/ -#define VMS_NULLPTR_MARKER (0x30U) /* '0' */ +#define VMS_NULLPTR_MARKER (0x30U) /* '0' */ +#define VMS_NOTNULLPTR_MARKER (0x31U) /* '1' */ extern const VMStateInfo vmstate_info_nullptr; +extern const VMStateInfo vmstate_info_maybeptr; =20 extern const VMStateInfo vmstate_info_cpudouble; =20 @@ -265,6 +280,7 @@ extern const VMStateInfo vmstate_info_bitmap; extern const VMStateInfo vmstate_info_qtailq; extern const VMStateInfo vmstate_info_gtree; extern const VMStateInfo vmstate_info_qlist; +extern const VMStateInfo vmstate_info_ptrs_array_entry; =20 #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0) /* @@ -547,6 +563,63 @@ extern const VMStateInfo vmstate_info_qlist; .offset =3D vmstate_offset_array(_s, _f, _type*, _n), \ } =20 +/* + * For migrating a dynamically allocated uint{8,32}-indexed array + * of pointers to structures (with NULL entries and with auto memory alloc= ation). + * + * _type: type of structure pointed to + * _vmsd: VMSD for structure _type (when VMS_STRUCT is set) + * _info: VMStateInfo for _type (when VMS_STRUCT is not set) + * start: size of (_type) pointed to (for auto memory allocation) + */ +#define VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT8_ALLOC(_field, _state, _f= ield_num, _version, _vmsd, _type) { \ + .name =3D (stringify(_field)), \ + .version_id =3D (_version), \ + .num_offset =3D vmstate_offset_value(_state, _field_num, uint8_t), \ + .vmsd =3D &(_vmsd), \ + .start =3D sizeof(_type), \ + .size =3D sizeof(_type *), \ + .flags =3D VMS_POINTER|VMS_VARRAY_UINT8|VMS_ARRAY_OF_POINTER| \ + VMS_ARRAY_OF_POINTER_ALLOW_NULL|VMS_STRUCT, \ + .offset =3D vmstate_offset_pointer(_state, _field, _type *), \ +} + +#define VMSTATE_VARRAY_OF_POINTER_UINT8_ALLOC(_field, _state, _field_num, = _version, _info, _type) { \ + .name =3D (stringify(_field)), \ + .version_id =3D (_version), \ + .num_offset =3D vmstate_offset_value(_state, _field_num, uint8_t), \ + .info =3D &(_info), \ + .start =3D sizeof(_type), \ + .size =3D sizeof(_type *), \ + .flags =3D VMS_POINTER|VMS_VARRAY_UINT8|VMS_ARRAY_OF_POINTER| \ + VMS_ARRAY_OF_POINTER_ALLOW_NULL, \ + .offset =3D vmstate_offset_pointer(_state, _field, _type *), \ +} + +#define VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC(_field, _state, _= field_num, _version, _vmsd, _type) { \ + .name =3D (stringify(_field)), \ + .version_id =3D (_version), \ + .num_offset =3D vmstate_offset_value(_state, _field_num, uint32_t), \ + .vmsd =3D &(_vmsd), \ + .start =3D sizeof(_type), \ + .size =3D sizeof(_type *), \ + .flags =3D VMS_POINTER|VMS_VARRAY_UINT32|VMS_ARRAY_OF_POINTER| \ + VMS_ARRAY_OF_POINTER_ALLOW_NULL|VMS_STRUCT, \ + .offset =3D vmstate_offset_pointer(_state, _field, _type *), \ +} + +#define VMSTATE_VARRAY_OF_POINTER_UINT32_ALLOC(_field, _state, _field_num,= _version, _info, _type) { \ + .name =3D (stringify(_field)), \ + .version_id =3D (_version), \ + .num_offset =3D vmstate_offset_value(_state, _field_num, uint32_t), \ + .info =3D &(_info), \ + .start =3D sizeof(_type), \ + .size =3D sizeof(_type *), \ + .flags =3D VMS_POINTER|VMS_VARRAY_UINT32|VMS_ARRAY_OF_POINTER| \ + VMS_ARRAY_OF_POINTER_ALLOW_NULL, \ + .offset =3D vmstate_offset_pointer(_state, _field, _type *), \ +} + #define VMSTATE_VARRAY_OF_POINTER_UINT32(_field, _state, _field_num, _vers= ion, _info, _type) { \ .name =3D (stringify(_field)), = \ .version_id =3D (_version), = \ diff --git a/migration/savevm.c b/migration/savevm.c index 197c89e0e65..1ce618d2e66 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -863,6 +863,32 @@ static void vmstate_check(const VMStateDescription *vm= sd) =20 if (field) { while (field->name) { + /* + * VMS_ARRAY_OF_POINTER must be used only together + * with one of VMS_(V)ARRAY* flags. + */ + assert(!(field->flags & VMS_ARRAY_OF_POINTER) || + ((field->flags & (VMS_ARRAY | VMS_VARRAY_INT32 | + VMS_VARRAY_UINT16 | VMS_VARRAY_UINT8 | VMS_VARRAY_UIN= T32)))); + + /* + * When VMS_ARRAY_OF_POINTER_ALLOW_NULL is used, we must: + * 1. have VMS_ARRAY_OF_POINTER set too; + * 2. have ->start field set and it should tell us a size + * of memory chunk we should allocate for every array membe= r. + */ + assert(!(field->flags & VMS_ARRAY_OF_POINTER_ALLOW_NULL) || + (field->flags & VMS_ARRAY_OF_POINTER)); + assert(!(field->flags & VMS_ARRAY_OF_POINTER_ALLOW_NULL) || + field->start); + + /* + * (VMStateField).real_field is only for internal purposes + * and should never be used by any user-defined VMStateField. + * Currently, it is only used by vmsd_create_fake_nullptr_fiel= d(). + */ + assert(!field->real_field); + if (field->flags & (VMS_STRUCT | VMS_VSTRUCT)) { /* Recurse to sub structures */ vmstate_check(field->vmsd); diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index 89cb2114721..8e5431057db 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -377,6 +377,108 @@ const VMStateInfo vmstate_info_nullptr =3D { .put =3D put_nullptr, }; =20 +static int get_maybeptr(QEMUFile *f, void *ppv, size_t unused_size, + const VMStateField *field) +{ + Error *local_err =3D NULL; + Error **errp =3D &local_err; + int ret =3D 0; + const VMStateField *real_field =3D field->real_field; + /* size of structure pointed to by elements of array */ + size_t size =3D real_field->start; + int marker; + + assert(size); + + if (ppv =3D=3D NULL) { + error_report("vmstate: get_maybeptr must be called with ppv !=3D N= ULL"); + return -EINVAL; + } + + /* + * We start from a clean array, all elements must be NULL, unless + * something we haven't prepared for has changed in vmstate_save_state= _v(). + * Let's check for this just in case. + */ + if (*(void **)ppv !=3D NULL) { + error_report("vmstate: get_maybeptr must be called with *ppv =3D= =3D NULL"); + return -EINVAL; + } + + marker =3D qemu_get_byte(f); + assert(marker =3D=3D VMS_NULLPTR_MARKER || marker =3D=3D VMS_NOTNULLPT= R_MARKER); + + if (marker =3D=3D VMS_NOTNULLPTR_MARKER) { + void *pv; + + /* allocate memory for structure */ + pv =3D g_malloc0(size); + + ret =3D vmstate_load_field(f, pv, size, real_field, errp); + if (ret) { + error_report_err(local_err); + g_free(pv); + return ret; + } + + *(void **)ppv =3D pv; + } + + return ret; +} + +static int put_maybeptr(QEMUFile *f, void *ppv, size_t unused_size, + const VMStateField *field, JSONWriter *vmdesc) +{ + const VMStateField *real_field =3D field->real_field; + int ret =3D 0; + Error *local_err =3D NULL; + Error **errp =3D &local_err; + /* size of structure pointed to by elements of array */ + size_t size =3D real_field->start; + void *pv; + + assert(size); + + /* + * (ppv) is an address of an i-th element of a dynamic array. + * + * (ppv) can not be NULL unless we have some regression/bug in + * vmstate_save_state_v(), because it is result of pointer arithemic l= ike: + * first_elem + size * i. + */ + if (ppv =3D=3D NULL) { + error_report("vmstate: put_maybeptr must be called with ppv !=3D N= ULL"); + return -EINVAL; + } + + /* get a pointer to a structure */ + pv =3D *(void **)ppv; + + if (pv =3D=3D NULL) { + /* write a mark telling that there was a NULL pointer */ + qemu_put_byte(f, VMS_NULLPTR_MARKER); + return 0; + } + + /* if pv is not NULL, write a marker and save field using vmstate_save= _field() */ + qemu_put_byte(f, VMS_NOTNULLPTR_MARKER); + + ret =3D vmstate_save_field(f, pv, size, real_field, vmdesc, errp); + if (ret) { + error_report_err(local_err); + return ret; + } + + return 0; +} + +const VMStateInfo vmstate_info_maybeptr =3D { + .name =3D "maybeptr", + .get =3D get_maybeptr, + .put =3D put_maybeptr, +}; + /* 64 bit unsigned int. See that the received value is the same than the o= ne in the field */ =20 diff --git a/migration/vmstate.c b/migration/vmstate.c index 8d192bcaa27..e52046f069d 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -71,10 +71,15 @@ vmsd_create_fake_nullptr_field(const VMStateField *fiel= d) /* Do not need "field_exists" check as it always exists (which is null= ) */ fake->field_exists =3D NULL; =20 - /* See vmstate_info_nullptr - use 1 byte to represent nullptr */ - fake->size =3D 1; - fake->info =3D &vmstate_info_nullptr; - fake->flags =3D VMS_SINGLE; + if (!(field->flags & VMS_ARRAY_OF_POINTER_ALLOW_NULL)) { + /* See vmstate_info_nullptr - use 1 byte to represent nullptr */ + fake->size =3D 1; + fake->info =3D &vmstate_info_nullptr; + fake->flags =3D VMS_SINGLE; + } else { + fake->real_field =3D field; + fake->info =3D &vmstate_info_maybeptr; + } =20 /* All the rest fields shouldn't matter.. */ =20 @@ -212,13 +217,28 @@ int vmstate_load_state(QEMUFile *f, const VMStateDesc= ription *vmsd, } for (i =3D 0; i < n_elems; i++) { void *curr_elem =3D first_elem + size * i; + bool need_fake_field =3D false; const VMStateField *inner_field; =20 if (field->flags & VMS_ARRAY_OF_POINTER) { - curr_elem =3D *(void **)curr_elem; + if (!(field->flags & VMS_ARRAY_OF_POINTER_ALLOW_NULL))= { + assert(curr_elem); + curr_elem =3D *(void **)curr_elem; + need_fake_field =3D !curr_elem; + } else { + /* + * We expect array of pointers to be initialized. + * We don't want to overwrite curr_elem with it's + * dereferenced value, because we may need to + * allocate memory (depending on what is in the mi= gration + * stream) and write to it later. + */ + assert(!*(void **)curr_elem); + need_fake_field =3D true; + } } =20 - if (!curr_elem && size) { + if (need_fake_field && size) { /* * If null pointer found (which should only happen in * an array of pointers), use null placeholder and do @@ -226,6 +246,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescri= ption *vmsd, */ inner_field =3D vmsd_create_fake_nullptr_field(field); } else { + assert(curr_elem || !size); inner_field =3D field; } =20 @@ -507,25 +528,38 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDe= scription *vmsd, =20 for (i =3D 0; i < n_elems; i++) { void *curr_elem =3D first_elem + size * i; + bool need_fake_field =3D false; const VMStateField *inner_field; bool is_null; int max_elems =3D n_elems - i; =20 old_offset =3D qemu_file_transferred(f); if (field->flags & VMS_ARRAY_OF_POINTER) { - assert(curr_elem); - curr_elem =3D *(void **)curr_elem; + if (!(field->flags & VMS_ARRAY_OF_POINTER_ALLOW_NULL))= { + assert(curr_elem); + curr_elem =3D *(void **)curr_elem; + need_fake_field =3D !curr_elem; + } else { + /* + * We always need a fake field to properly handle + * VMS_ARRAY_OF_POINTER_ALLOW_NULL case, because + * even if pointer is not NULL, we still want to + * write a marker in the migration stream. + */ + need_fake_field =3D true; + } } =20 - if (!curr_elem && size) { + if (need_fake_field && size) { /* * If null pointer found (which should only happen in * an array of pointers), use null placeholder and do * not follow. */ inner_field =3D vmsd_create_fake_nullptr_field(field); - is_null =3D true; + is_null =3D !curr_elem; } else { + assert(curr_elem || !size); inner_field =3D field; is_null =3D false; } --=20 2.47.3 From nobody Tue Apr 7 11:18:24 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=mihalicyn.com ARC-Seal: i=1; a=rsa-sha256; t=1773411302; cv=none; d=zohomail.com; s=zohoarc; b=fTbkrsN34sKVJcA2nNrN8e8fSr86bpKjQPm+FDgvKXhZOhO4NkedKG+YOsoVZInSIPFc+FlwnA5Ug56cBawMVQfcN8E48hPwx9LE2Q+PC71gxnqxW6kY0cY682EWvyejMzuXlpJRJZRLXHCDmzxs9pYKNdAPS7Tlk2lF21+5H0A= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773411302; 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=tVGscBXdfGh8fDEwqpswNHuWUNoSPZVqRG0/M7ov0/8=; b=ZQn59b3KXeLP/5PMxRKzPkLmSSbV241r550i6t0UvfeMFOTe0N3X3sEtzWsuhjL1LZhNaVntLpp3DHreZDVgjxumjYLaBH+o7cyhfm8cto4Lm2iBQ/GaktIVAqgIr8pi9DYJJA28Bq0M+vo8Ix6By705XM63KK/tVXYJUioOGbs= 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 1773411302435289.64839961402333; Fri, 13 Mar 2026 07:15:02 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w13GQ-0005WU-ID; Fri, 13 Mar 2026 10:13:19 -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 1w13Fw-0005Qa-Dg for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:53 -0400 Received: from mail-wm1-x331.google.com ([2a00:1450:4864:20::331]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w13Fo-0006Md-55 for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:46 -0400 Received: by mail-wm1-x331.google.com with SMTP id 5b1f17b1804b1-4852ff06541so23136695e9.2 for ; Fri, 13 Mar 2026 07:12:28 -0700 (PDT) Received: from alex-laptop.lan (p200300cf57228c00321ddb23f48fbb73.dip0.t-ipconnect.de. [2003:cf:5722:8c00:321d:db23:f48f:bb73]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe19ad9asm18580939f8f.7.2026.03.13.07.12.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Mar 2026 07:12:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mihalicyn.com; s=mihalicyn; t=1773411148; x=1774015948; 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=tVGscBXdfGh8fDEwqpswNHuWUNoSPZVqRG0/M7ov0/8=; b=JsIK2QWZhVv9SPelNnSFnBPGlLnylxiqjYNMEwemVZRQ/dq48IaP0wZFIKNQCcW6jJ EzF9WcS7S2rgUVipG6uVtsTwNtWYe8IjeyXZxoTwtA9IkgRIZcxWYtRCfgfwVGlPtMOT W0mOjmK6h4MEv+ipVcB9fqI3l2gyaUaMeBsYQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773411148; x=1774015948; 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=tVGscBXdfGh8fDEwqpswNHuWUNoSPZVqRG0/M7ov0/8=; b=ZspljbSxk+R6uvqQ5m0LY3S4F/a1LhgDVqjyDk2bbzZvDswmM0O1FSO45BuAsCRL5A KTievkByRYYnnnyjx9NYvMSSVOuze1uw5RItIHDy8qN+efyb+54YcsU7vOea0t7FDzMW 6mOh4NOphgghsQsqApKqOV3YFJLJn+NotXl9mN8IxXydzsRzu0bwCOs5TiOKB8RChj24 adbXquttmjFA2BGrsHoqIdntgDyU2RhiV3f7rkClrtfY9hO0730b+7jeEeyHKDJv3rld EDdtnzBkGbi4NKoqY0wSVFibw/O6RQhWOcsPr1OdAo7Xi2+av+/g3TIcAYZzxsKe/QsS 3Fog== X-Gm-Message-State: AOJu0YwyEEtjC59lDbt7vbXjElTwQ+2DMI2i6VCXwxubkYEpcvYbZSGc apZ4oGRV0pcfeRyGn5lTF515kVhjWep/PAFo7PG7tViCFeT5AMOCS0MUVeH6yFUVTMTyYHHCwUc 53aDd+Go= X-Gm-Gg: ATEYQzyAEVcRsFSatNg+zDbr4woGc2+mU32DwdHeZYXi+nn7h4rift3ldOBElolPxXU 8Us3HeoIWf5aG5pBlY8iVIdMHDOtOFzc02K3lC0s9VFka7sVeojsF0cbnWrysPf1CP4ZeErxth+ pkH/fOJ46ioBN2ePhNizVn9tvzkDFS0RKcX0OeOpFJwZUMaMEEuTCnSc/DZpgN8m4GMIc9Bfv85 CTAznxHWyqK4X4ctzRCqKNBnpmT23Jlar4An/mM2ymwiTURt9vbssafVV73AgZ+YwBT0KgyLLAH LTW/tof4rkIulaUzwGrNKrOu4TCNqBEi/B6wdpi/AH6BsViYmk4nePVlp3TMEBWCwhus0Q7KhKc 7fC6ZmAAib3vJxPaeDVdwMeYyspeI97oKoGGgePr5uhYF0FkZhs4aRG12MMyUPJ07Klh7GayywL tmmq63bdjshhIHWqkRlLO0yh8q6HrY3mMuCo7ecraywlgxwRPzTG3eau8IG2w33i59CqAOFZR3p 9yRjam9UCW3mwH33SLcC6k= X-Received: by 2002:a05:600c:a09:b0:483:9139:4c1d with SMTP id 5b1f17b1804b1-485566e1a4cmr60597925e9.14.1773411147609; Fri, 13 Mar 2026 07:12:27 -0700 (PDT) From: Alexander Mikhalitsyn To: qemu-devel@nongnu.org Cc: Peter Xu , Jesper Devantier , Kevin Wolf , Fabiano Rosas , Paolo Bonzini , Hanna Reitz , qemu-block@nongnu.org, =?UTF-8?q?St=C3=A9phane=20Graber?= , Keith Busch , Stefan Hajnoczi , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Alexander Mikhalitsyn , Fam Zheng , Klaus Jensen , Zhao Liu , Alexander Mikhalitsyn Subject: [PATCH v4 3/8] tests/unit/test-vmstate: add tests for VMS_ARRAY_OF_POINTER_ALLOW_NULL Date: Fri, 13 Mar 2026 15:12:16 +0100 Message-ID: <20260313141221.359503-4-alexander@mihalicyn.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260313141221.359503-1-alexander@mihalicyn.com> References: <20260313141221.359503-1-alexander@mihalicyn.com> 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::331; envelope-from=alexander@mihalicyn.com; helo=mail-wm1-x331.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 @mihalicyn.com) X-ZM-MESSAGEID: 1773411304070158500 Content-Type: text/plain; charset="utf-8" From: Alexander Mikhalitsyn Add tests for VMSTATE_VARRAY_OF_POINTER{,_TO_STRUCT}_UINT32_ALLOC. Signed-off-by: Alexander Mikhalitsyn --- tests/unit/test-vmstate.c | 157 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c index cadbab3c5e2..d10cf34fc75 100644 --- a/tests/unit/test-vmstate.c +++ b/tests/unit/test-vmstate.c @@ -702,6 +702,155 @@ static void test_arr_ptr_prim_0_load(void) } } =20 +static uint8_t wire_arr_ptr_with_nulls[] =3D { + VMS_NOTNULLPTR_MARKER, + 0x00, 0x00, 0x00, 0x00, + VMS_NULLPTR_MARKER, + VMS_NOTNULLPTR_MARKER, + 0x00, 0x00, 0x00, 0x02, + VMS_NOTNULLPTR_MARKER, + 0x00, 0x00, 0x00, 0x03, + QEMU_VM_EOF +}; + +typedef struct { + uint32_t ar_items_num; + TestStructTriv **ar; +} TestVArrayOfPtrToStuctWithNULLs; + +const VMStateDescription vmsd_arps_with_nulls =3D { + .name =3D "test/arps_with_nulls", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC( + ar, TestVArrayOfPtrToStuctWithNULLs, ar_items_num, 0, vmsd_tst= , TestStructTriv), + VMSTATE_END_OF_LIST() + } +}; + +static void test_arr_ptr_nulls_str_save(void) +{ + TestStructTriv ar[AR_SIZE] =3D { {.i =3D 0}, {.i =3D 1}, {.i =3D 2}, {= .i =3D 3} }; + TestVArrayOfPtrToStuctWithNULLs sample =3D {}; + int idx; + + sample.ar_items_num =3D AR_SIZE; + sample.ar =3D g_new0(TestStructTriv*, sample.ar_items_num); + sample.ar[0] =3D g_new0(TestStructTriv, 1); + *sample.ar[0] =3D ar[0]; + /* note, sample.ar[1] remains NULL */ + sample.ar[2] =3D g_new0(TestStructTriv, 1); + *sample.ar[2] =3D ar[2]; + sample.ar[3] =3D g_new0(TestStructTriv, 1); + *sample.ar[3] =3D ar[3]; + + save_vmstate(&vmsd_arps_with_nulls, &sample); + compare_vmstate(wire_arr_ptr_with_nulls, sizeof(wire_arr_ptr_with_null= s)); + + for (idx =3D 0; idx < AR_SIZE; ++idx) { + g_free(sample.ar[idx]); + } + g_free(sample.ar); +} + +static void test_arr_ptr_nulls_str_load(void) +{ + TestStructTriv ar_gt[AR_SIZE] =3D {{.i =3D 0}, {.i =3D 0}, {.i =3D 2},= {.i =3D 3} }; + TestVArrayOfPtrToStuctWithNULLs obj =3D {}; + int idx; + + obj.ar_items_num =3D AR_SIZE; + obj.ar =3D g_new0(TestStructTriv*, obj.ar_items_num); + + save_buffer(wire_arr_ptr_with_nulls, sizeof(wire_arr_ptr_with_nulls)); + SUCCESS(load_vmstate_one(&vmsd_arps_with_nulls, &obj, 1, + wire_arr_ptr_with_nulls, sizeof(wire_arr_ptr_wit= h_nulls))); + + for (idx =3D 0; idx < AR_SIZE; ++idx) { + if (idx =3D=3D 1) { + g_assert_cmpint((uintptr_t)(obj.ar[idx]), =3D=3D, 0); + } else { + /* compare the target array ar with the ground truth array ar_= gt */ + g_assert_cmpint(ar_gt[idx].i, =3D=3D, obj.ar[idx]->i); + } + } + + for (idx =3D 0; idx < AR_SIZE; ++idx) { + g_free(obj.ar[idx]); + } + g_free(obj.ar); +} + +typedef struct { + uint32_t ar_items_num; + int32_t **ar; +} TestVArrayOfPtrToIntWithNULLs; + +const VMStateDescription vmsd_arpp_with_nulls =3D { + .name =3D "test/arpp_with_nulls", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_VARRAY_OF_POINTER_UINT32_ALLOC( + ar, TestVArrayOfPtrToIntWithNULLs, ar_items_num, 0, vmstate_in= fo_int32, int32_t), + VMSTATE_END_OF_LIST() + } +}; + +static void test_arr_ptr_nulls_prim_save(void) +{ + int32_t ar[AR_SIZE] =3D { 0, 1, 2, 3 }; + TestVArrayOfPtrToIntWithNULLs sample =3D {}; + int idx; + + sample.ar_items_num =3D AR_SIZE; + sample.ar =3D g_new0(int32_t*, sample.ar_items_num); + sample.ar[0] =3D g_new0(int32_t, 1); + *sample.ar[0] =3D ar[0]; + /* note, sample.ar[1] remains NULL */ + sample.ar[2] =3D g_new0(int32_t, 1); + *sample.ar[2] =3D ar[2]; + sample.ar[3] =3D g_new0(int32_t, 1); + *sample.ar[3] =3D ar[3]; + + save_vmstate(&vmsd_arpp_with_nulls, &sample); + compare_vmstate(wire_arr_ptr_with_nulls, sizeof(wire_arr_ptr_with_null= s)); + + for (idx =3D 0; idx < AR_SIZE; ++idx) { + g_free(sample.ar[idx]); + } + g_free(sample.ar); +} + +static void test_arr_ptr_nulls_prim_load(void) +{ + int32_t ar_gt[AR_SIZE] =3D { 0, 0, 2, 3 }; + TestVArrayOfPtrToIntWithNULLs obj =3D {}; + int idx; + + obj.ar_items_num =3D AR_SIZE; + obj.ar =3D g_new0(int32_t*, obj.ar_items_num); + + save_buffer(wire_arr_ptr_with_nulls, sizeof(wire_arr_ptr_with_nulls)); + SUCCESS(load_vmstate_one(&vmsd_arpp_with_nulls, &obj, 1, + wire_arr_ptr_with_nulls, sizeof(wire_arr_ptr_wit= h_nulls))); + + for (idx =3D 0; idx < AR_SIZE; ++idx) { + if (idx =3D=3D 1) { + g_assert_cmpint((uintptr_t)(obj.ar[idx]), =3D=3D, 0); + } else { + /* compare the target array ar with the ground truth array ar_= gt */ + g_assert_cmpint(ar_gt[idx], =3D=3D, *obj.ar[idx]); + } + } + + for (idx =3D 0; idx < AR_SIZE; ++idx) { + g_free(obj.ar[idx]); + } + g_free(obj.ar); +} + /* test QTAILQ migration */ typedef struct TestQtailqElement TestQtailqElement; =20 @@ -1568,6 +1717,14 @@ int main(int argc, char **argv) test_arr_ptr_prim_0_save); g_test_add_func("/vmstate/array/ptr/prim/0/load", test_arr_ptr_prim_0_load); + g_test_add_func("/vmstate/array/ptr-nulls/str/save", + test_arr_ptr_nulls_str_save); + g_test_add_func("/vmstate/array/ptr-nulls/str/load", + test_arr_ptr_nulls_str_load); + g_test_add_func("/vmstate/array/ptr-nulls/prim/save", + test_arr_ptr_nulls_prim_save); + g_test_add_func("/vmstate/array/ptr-nulls/prim/load", + test_arr_ptr_nulls_prim_load); g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q); g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q); g_test_add_func("/vmstate/gtree/save/savedomain", test_gtree_save_doma= in); --=20 2.47.3 From nobody Tue Apr 7 11:18:24 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=mihalicyn.com ARC-Seal: i=1; a=rsa-sha256; t=1773411231; cv=none; d=zohomail.com; s=zohoarc; b=jLKTm+6CksBpw4RJF57Vh332o7z5pPqjeEaDLTZybGpfopCRGwqP95PH23KTCqwVLhhHlzmnA/XTK+00FBixXot4H8pyo3/oQ0jEdWpkh+LtjtCQNJ8+RLIHIwNzeRR/l85786deG9AoozxGRZn1Jb9gVKbchO6UQHOpv6zJcQU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773411231; 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=2mdV95Xn1uRZNv+wMaqt5uMFd58yUD2RuxYvZm/pTuU=; b=ZW9BuTnXtoSSIaNQYKANflSQk1B1QbljPEB191UDDCNo6rEulL9PRlIbfa2rkyZ4tiXchwrubg+1mHYKyWPfDYkdcuXjgWLCLEl+cYB2WCIXyY7YwxJW7Y7jv0VcXfPmoAppX1n6JnTh0Roo0e/NOtN2xNtWbZ2D1AdeQDva8ak= 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 1773411231765438.2792505705221; Fri, 13 Mar 2026 07:13:51 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w13GG-0005Ur-UV; Fri, 13 Mar 2026 10:13:08 -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 1w13Fq-0005Q7-Og for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:44 -0400 Received: from mail-wr1-x432.google.com ([2a00:1450:4864:20::432]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w13Fl-0006Mo-SL for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:42 -0400 Received: by mail-wr1-x432.google.com with SMTP id ffacd0b85a97d-439b611274bso1475065f8f.3 for ; Fri, 13 Mar 2026 07:12:29 -0700 (PDT) Received: from alex-laptop.lan (p200300cf57228c00321ddb23f48fbb73.dip0.t-ipconnect.de. [2003:cf:5722:8c00:321d:db23:f48f:bb73]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe19ad9asm18580939f8f.7.2026.03.13.07.12.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Mar 2026 07:12:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mihalicyn.com; s=mihalicyn; t=1773411149; x=1774015949; 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=2mdV95Xn1uRZNv+wMaqt5uMFd58yUD2RuxYvZm/pTuU=; b=XA0zGeV14fJoCUtM8l3sHhHL6+utlwRzE8JOuNzusGg8nbf7JyQBlmYEYU0t8KuqMk Xq42mcNdfnKo07jWlh+xvYvQtpUUPxFrZ/9HCt0IuJD9/66l+4O2/PqkPZgkRLtTC8pJ KquUxy7+mA306eAP6L/+DwAFw+/aYz27gy3gA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773411149; x=1774015949; 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=2mdV95Xn1uRZNv+wMaqt5uMFd58yUD2RuxYvZm/pTuU=; b=faZ81nlvLJR0rnSqr7HZ8XeVuDKcjDwmHtJ0C9aRk3m0ykhp6k5mHO0wErdpKbggzD lsPhxgx6529Hi/1AcxWsCceE/nmFMGgwFQ+eyy31VzjrlYS2/ED53twBrtEWJ5285D09 XCt1jZTGVM8RKDhJkelPEKSEvDYgarvZGDgfauuknvK8FZOx16AckdtAF26EUsq4WkUc OH7wp85+/8+eEB5DVIeS4pG9Nrco/l/HCzoNCh4lEQCftt6bDNakYBzJVnvjnDmDDZfj zjq1Rep+vFW2I9y2lu0DZtoxuSlNo+N3LqW97vEmpA+U51pwnvNembdEEefnk7mSK09x gc8Q== X-Gm-Message-State: AOJu0YwY6Fb4IYgrblgO5oqcDHM/k86TE3lQMPuq5Wujd84wJl5HXzJI VPb4ELPckTMts4UUwAdBoSgZ4GEhumuHlq6wrrkG1NG/ARvJ6ZsSG6rIM/wbRIT2y7H63e3aYXe 9KmAxRBk= X-Gm-Gg: ATEYQzyIZKZ5U2BTL72WA15L4vKdeM1Zua6taxqoO6ZB58ymytuIhDtBlEvz5oyob1T xhYN8e/y7X0eyCmVYmvEz1aCq7PIPgiiMjb9egeo1Wl29NZB/yLHSxDAgElIHIJgaKXPyYQvGAA 6PTaX3wwfmGwkF8AXAcPjDO/aBPfDo3bM1rR67UHmsHIFX+G2veqrbFYem2rVKFD46OvQMzaZrH nylJq78pV8h8iI7xItiHyY/P2+imFA24st5zKDgRYBt5Mp26RIP+6lwWiH0DcVaxdQhN24vDtOu CtBIcvyIA5PNv4ZZUDsULwgvS2MwWiI2UIL8bpJ7BIJ6GpPz8Dhx3wMpYhcmk4oIJLsvgHyf5W2 zQ9XIKiFNzuRxraDy0iYb7kGnnx2aCPPu5aQTjAo7AHwAtlOcDF9xRtiBeR9m4G9PVm6qfTAUQI ZXUrtbBh2voWZgnKxXYprflpmQr0o9JiZsD21bMqTb7JZW+KpFGOw0POLg9aDSVF90j9DDLwo1a GFFculP7WNJBaXU/c8pUIQ= X-Received: by 2002:a5d:5f46:0:b0:439:ac98:7520 with SMTP id ffacd0b85a97d-43a04dbed03mr6986089f8f.37.1773411148739; Fri, 13 Mar 2026 07:12:28 -0700 (PDT) From: Alexander Mikhalitsyn To: qemu-devel@nongnu.org Cc: Peter Xu , Jesper Devantier , Kevin Wolf , Fabiano Rosas , Paolo Bonzini , Hanna Reitz , qemu-block@nongnu.org, =?UTF-8?q?St=C3=A9phane=20Graber?= , Keith Busch , Stefan Hajnoczi , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Alexander Mikhalitsyn , Fam Zheng , Klaus Jensen , Zhao Liu , Alexander Mikhalitsyn Subject: [PATCH v4 4/8] tests/functional/migration: add VM launch/configure hooks Date: Fri, 13 Mar 2026 15:12:17 +0100 Message-ID: <20260313141221.359503-5-alexander@mihalicyn.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260313141221.359503-1-alexander@mihalicyn.com> References: <20260313141221.359503-1-alexander@mihalicyn.com> 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::432; envelope-from=alexander@mihalicyn.com; helo=mail-wr1-x432.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.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, RCVD_IN_DNSWL_NONE=-0.0001, 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 @mihalicyn.com) X-ZM-MESSAGEID: 1773411233901158500 Content-Type: text/plain; charset="utf-8" From: Alexander Mikhalitsyn Introduce configure_machine, launch_source_vm and assert_dest_vm methods to allow child classes to override some pieces of source/dest VMs creation, start and check logic. Signed-off-by: Alexander Mikhalitsyn --- tests/functional/migration.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tests/functional/migration.py b/tests/functional/migration.py index e995328e833..40e9c094c46 100644 --- a/tests/functional/migration.py +++ b/tests/functional/migration.py @@ -40,19 +40,35 @@ def assert_migration(self, src_vm, dst_vm): self.assertEqual(dst_vm.cmd('query-status')['status'], 'running') self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate= ') =20 + # Can be overridden by subclasses to configure both source/dest VMs. + def configure_machine(self, vm): + vm.add_args('-nodefaults') + + # Can be overridden by subclasses to prepare the source VM before migr= ation, + # e.g. by running some workload inside the source VM to see if it cont= inues + # to run properly after migration. + def launch_source_vm(self, vm): + vm.launch() + + # Can be overridden by subclasses to check the destination VM after mi= gration, + # e.g. by checking if the workload is still running after migration. + def assert_dest_vm(self, vm): + pass + def migrate_vms(self, dst_uri, src_uri, dst_vm, src_vm): dst_vm.qmp('migrate-incoming', uri=3Ddst_uri) src_vm.qmp('migrate', uri=3Dsrc_uri) self.assert_migration(src_vm, dst_vm) + self.assert_dest_vm(dst_vm) =20 def migrate(self, dst_uri, src_uri=3DNone): dst_vm =3D self.get_vm('-incoming', 'defer', name=3D"dst-qemu") - dst_vm.add_args('-nodefaults') + self.configure_machine(dst_vm) dst_vm.launch() =20 src_vm =3D self.get_vm(name=3D"src-qemu") - src_vm.add_args('-nodefaults') - src_vm.launch() + self.configure_machine(src_vm) + self.launch_source_vm(src_vm) =20 if src_uri is None: src_uri =3D dst_uri --=20 2.47.3 From nobody Tue Apr 7 11:18:24 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=mihalicyn.com ARC-Seal: i=1; a=rsa-sha256; t=1773411249; cv=none; d=zohomail.com; s=zohoarc; b=ldDF3HxZXv5t08F258W/ih5n26OmepCIsgazWJUz0l/a+PnVGsjDFyVjGlt63kaZf8E4rlTrFsCQni9moq492RZNLtksmmM3vAhKSl8GAqZ4ki3Yjlp90lETV2Sfh0jCjgaXoFZmOZu1YVTpeScjq/eoAhJzeMS5qTflOp7j2Xs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773411249; 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=SEO6/lrYdm4dOs+1aF7CdwIzHMn2tXQZYpY2v/1+t2k=; b=n/BKXFUTfAf2hTamqQm0keBgWjZKXp8ltjW6MDV+S8uMRWTDjR4NQ//pv4xc5UjNStXBFFeS6fzOMnZEBlvOKxjLnAHi6rJtZREpBfQvQRNjFvTqmUUKHWS4XU+XmIkwym+DluVhCDtOm++60WFoqhZuDtVnVF6duw9UBvxyL0U= 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 17734112491841002.9126863636183; Fri, 13 Mar 2026 07:14:09 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w13GA-0005TD-EF; Fri, 13 Mar 2026 10:13:03 -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 1w13Fw-0005QX-C6 for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:51 -0400 Received: from mail-wr1-x42f.google.com ([2a00:1450:4864:20::42f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w13Fl-0006Mx-Tj for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:45 -0400 Received: by mail-wr1-x42f.google.com with SMTP id ffacd0b85a97d-439bcec8613so1806668f8f.3 for ; Fri, 13 Mar 2026 07:12:31 -0700 (PDT) Received: from alex-laptop.lan (p200300cf57228c00321ddb23f48fbb73.dip0.t-ipconnect.de. [2003:cf:5722:8c00:321d:db23:f48f:bb73]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe19ad9asm18580939f8f.7.2026.03.13.07.12.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Mar 2026 07:12:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mihalicyn.com; s=mihalicyn; t=1773411150; x=1774015950; 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=SEO6/lrYdm4dOs+1aF7CdwIzHMn2tXQZYpY2v/1+t2k=; b=IyrcrCu8qOoA/cORk1FlyMFdX6dOxxhtyA3cI/iIcPxnJYKmvqLqqWuzgYLjU4n1Uc VgDEYnOSd2cviuJgO2KSXtM08P1H3xNk1ajq4iiv9FX/Bcde/tEQVeh4KbpwvpFf8RtK Sj/Y8QxYFz7ZPaWLD1V2823PrDdtVgD0cbFy4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773411150; x=1774015950; 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=SEO6/lrYdm4dOs+1aF7CdwIzHMn2tXQZYpY2v/1+t2k=; b=VlmCIh8VoQ0uwMRmTzfo37aC0QKdeAUjdo9DLZPsqvY3ntASpvmeA/bJCUrRA6h/34 b4sPOtN48oqhi2a914LWvxG1nBzFJ6cfoNHnJJ6LbwB/YSjLsz2FaqPZwu8i1jo8KO2+ u98NBlqoNK6sxsir9K/+Shx+f/VBfoy4YofKcjXIC3ZClthQe7bv4XBntfblosqL9gFO POvwq9ZIM1Ctn1vuG3eMAqSwuuMQZPKte7cHrXQCd0LiKdJiz/sOuFgLf7ozvEsdf1bK bKfMyie75Fg/RPoU79ogElJ6QUQsmYT4c4Nr/XsotcQ5+GgKbiXE8r5Ee30/LpEEisUM qoBg== X-Gm-Message-State: AOJu0YyJDQyBn0DvjRWpEcviNYykEgGIkCjmV0VluRCkXOdGZhcU0xLD bcycEmPL2l2qSrmWgLXM+XOolKZ/7AHpLTrp7fGk1GPfUgmkmT6nJk2n3LRfW9x2FUBXrE9wdx9 EHeWkUl0= X-Gm-Gg: ATEYQzxxAoXvzNCKxLUGXGN8ROUaBg8LI0LumZ+J4x5H80Xb6jwiPHwbRuZJJEyVfPs xM8SfyXuJVDQwf+Q6wmUN4zSP9YwEhhFRDtkkMyn57xHS1aAthobATSQwokoMwxOQ/AE9WPWmaZ AVAl3KeW+hYyWgMVe/iHhniS6ugQxUSpf9PAV96ePPCOLiaq8O5QJwyh+QFrAPnohU1V1qhryFd PZSxeWtwKJfByczwtco+KklQVCUAGOawNxyGQrs28SnuNzHELQxM1jbqMkG/OZfCt37K7plhCUj KAZmm2anEYl40H0wrvZCk+DQBGwRGeLO7rJJsTf+eeVtg4PhwBkoNVYTX1bpQiG9IgJnqP6NZJZ 7aMejlw7Po0uePumZNp6qEjM9EwExdGksoHJSsL5caqag7zKJuMYU1gshXiRA5Pgthc5B7U7VhB kG3d31iyyUkdyS5hBGgIbA4Z4MXWoLB6AGwBNbmCT2iLPlkBvc8ooTbip5yLgOJTCcOhgqnRpbt RGXC5IwSTs4/mfFwoIety/ZbE/jUGmQbw== X-Received: by 2002:adf:f20e:0:b0:439:fbbc:d593 with SMTP id ffacd0b85a97d-43a04db7d5amr4936542f8f.27.1773411150091; Fri, 13 Mar 2026 07:12:30 -0700 (PDT) From: Alexander Mikhalitsyn To: qemu-devel@nongnu.org Cc: Peter Xu , Jesper Devantier , Kevin Wolf , Fabiano Rosas , Paolo Bonzini , Hanna Reitz , qemu-block@nongnu.org, =?UTF-8?q?St=C3=A9phane=20Graber?= , Keith Busch , Stefan Hajnoczi , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Alexander Mikhalitsyn , Fam Zheng , Klaus Jensen , Zhao Liu , Alexander Mikhalitsyn Subject: [PATCH v4 5/8] hw/nvme: add migration blockers for non-supported cases Date: Fri, 13 Mar 2026 15:12:18 +0100 Message-ID: <20260313141221.359503-6-alexander@mihalicyn.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260313141221.359503-1-alexander@mihalicyn.com> References: <20260313141221.359503-1-alexander@mihalicyn.com> 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::42f; envelope-from=alexander@mihalicyn.com; helo=mail-wr1-x42f.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 @mihalicyn.com) X-ZM-MESSAGEID: 1773411249695158500 Content-Type: text/plain; charset="utf-8" From: Alexander Mikhalitsyn Let's block migration for cases we don't support: - SR-IOV - CMB - PMR - SPDM No functional changes here, because NVMe migration is not supported at all as of this commit. Signed-off-by: Alexander Mikhalitsyn --- hw/nvme/ctrl.c | 206 +++++++++++++++++++++++++++++++++++++++++++ hw/nvme/nvme.h | 3 + include/block/nvme.h | 12 +++ 3 files changed, 221 insertions(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index cc4593cd427..9f9c9bcbead 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -207,6 +207,7 @@ #include "hw/pci/msix.h" #include "hw/pci/pcie_sriov.h" #include "system/spdm-socket.h" +#include "migration/blocker.h" #include "migration/vmstate.h" =20 #include "nvme.h" @@ -250,6 +251,7 @@ static const bool nvme_feature_support[NVME_FID_MAX] = =3D { [NVME_COMMAND_SET_PROFILE] =3D true, [NVME_FDP_MODE] =3D true, [NVME_FDP_EVENTS] =3D true, + /* if you add something here, please update nvme_set_migration_blocker= s() */ }; =20 static const uint32_t nvme_feature_cap[NVME_FID_MAX] =3D { @@ -4601,6 +4603,7 @@ static uint16_t nvme_io_mgmt_send(NvmeCtrl *n, NvmeRe= quest *req) return 0; case NVME_IOMS_MO_RUH_UPDATE: return nvme_io_mgmt_send_ruh_update(n, req); + /* if you add something here, please update nvme_set_migration_blocker= s() */ default: return NVME_INVALID_FIELD | NVME_DNR; }; @@ -7518,6 +7521,10 @@ static uint16_t nvme_security_receive(NvmeCtrl *n, N= vmeRequest *req) =20 static uint16_t nvme_directive_send(NvmeCtrl *n, NvmeRequest *req) { + /* + * When adding a new dtype handling here, + * please also update nvme_set_migration_blockers(). + */ return NVME_INVALID_FIELD | NVME_DNR; } =20 @@ -9208,6 +9215,199 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *= pci_dev) } } =20 +#define BLOCKER_FEATURES_MAX_LEN 256 + +static inline void nvme_add_blocker_feature(char *blocker_features, const = char *feature) +{ + if (strlen(blocker_features) > 0) { + g_strlcat(blocker_features, ", ", BLOCKER_FEATURES_MAX_LEN); + } + g_strlcat(blocker_features, feature, BLOCKER_FEATURES_MAX_LEN); +} + +static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, E= rror **errp) +{ + uint64_t unsupported_cap, cap =3D ldq_le_p(&n->bar.cap); + char blocker_features[BLOCKER_FEATURES_MAX_LEN] =3D ""; + bool adm_cmd_security_checked =3D false; + bool cmd_io_mgmt_checked =3D false; + bool cmd_zone_checked =3D false; + + /* + * Idea of this function is simple, we iterate over all Command Sets a= nd + * for each supported command we provide a special handling logic to + * determine if we should block migration or not. + * + * For instance, we have NVME_ADM_CMD_NS_ATTACHMENT and it is always + * available to the guest, but if there is only 1 namespace, then it is + * safe to allow migration, but if there are more, then we need to blo= ck + * migration because we don't handle this in migration code yet. + */ + for (int opcode =3D 0; opcode < sizeof(n->cse.acs) / sizeof(n->cse.acs= [0]); opcode++) { + /* Is command supported? */ + if (!n->cse.acs[opcode]) { + continue; + } + + switch (opcode) { + case NVME_ADM_CMD_DELETE_SQ: + case NVME_ADM_CMD_CREATE_SQ: + case NVME_ADM_CMD_GET_LOG_PAGE: + case NVME_ADM_CMD_DELETE_CQ: + case NVME_ADM_CMD_CREATE_CQ: + case NVME_ADM_CMD_IDENTIFY: + case NVME_ADM_CMD_ABORT: + case NVME_ADM_CMD_SET_FEATURES: + case NVME_ADM_CMD_GET_FEATURES: + case NVME_ADM_CMD_ASYNC_EV_REQ: + case NVME_ADM_CMD_DBBUF_CONFIG: + case NVME_ADM_CMD_FORMAT_NVM: + case NVME_ADM_CMD_DIRECTIVE_SEND: + case NVME_ADM_CMD_DIRECTIVE_RECV: + break; + case NVME_ADM_CMD_NS_ATTACHMENT: + int namespaces_num =3D 0; + for (int i =3D 1; i <=3D NVME_MAX_NAMESPACES; i++) { + NvmeNamespace *ns =3D nvme_subsys_ns(n->subsys, i); + if (!ns) { + continue; + } + + namespaces_num++; + } + + if (namespaces_num > 1) { + nvme_add_blocker_feature(blocker_features, "Namespace Atta= chment"); + } + + break; + case NVME_ADM_CMD_VIRT_MNGMT: + if (n->params.sriov_max_vfs) { + nvme_add_blocker_feature(blocker_features, "SR-IOV"); + } + + break; + case NVME_ADM_CMD_SECURITY_SEND: + case NVME_ADM_CMD_SECURITY_RECV: + if (adm_cmd_security_checked) { + break; + } + + if (pci_dev->spdm_port) { + nvme_add_blocker_feature(blocker_features, "SPDM"); + } + + adm_cmd_security_checked =3D true; + + break; + default: + g_assert_not_reached(); + } + } + + for (int opcode =3D 0; opcode < sizeof(n->cse.iocs.nvm) / sizeof(n->cs= e.iocs.nvm[0]); opcode++) { + if (!n->cse.iocs.nvm[opcode]) { + continue; + } + + switch (opcode) { + case NVME_CMD_FLUSH: + case NVME_CMD_WRITE: + case NVME_CMD_READ: + case NVME_CMD_COMPARE: + case NVME_CMD_WRITE_ZEROES: + case NVME_CMD_DSM: + case NVME_CMD_VERIFY: + case NVME_CMD_COPY: + break; + case NVME_CMD_IO_MGMT_RECV: + case NVME_CMD_IO_MGMT_SEND: + if (cmd_io_mgmt_checked) { + break; + } + + /* check for NVME_IOMS_MO_RUH_UPDATE */ + if (n->subsys->params.fdp.enabled) { + nvme_add_blocker_feature(blocker_features, "FDP"); + } + + cmd_io_mgmt_checked =3D true; + + break; + default: + g_assert_not_reached(); + } + } + + for (int opcode =3D 0; opcode < sizeof(n->cse.iocs.zoned) / sizeof(n->= cse.iocs.zoned[0]); opcode++) { + /* + * If command isn't supported or we have the same command + * in n->cse.iocs.nvm, then we can skip it here. + */ + if (!n->cse.iocs.zoned[opcode] || n->cse.iocs.nvm[opcode]) { + continue; + } + + switch (opcode) { + case NVME_CMD_ZONE_APPEND: + case NVME_CMD_ZONE_MGMT_SEND: + case NVME_CMD_ZONE_MGMT_RECV: + if (cmd_zone_checked) { + break; + } + + for (int i =3D 1; i <=3D NVME_MAX_NAMESPACES; i++) { + NvmeNamespace *ns =3D nvme_subsys_ns(n->subsys, i); + if (!ns) { + continue; + } + + if (ns->params.zoned) { + nvme_add_blocker_feature(blocker_features, "Zoned Name= space"); + break; + } + } + + cmd_zone_checked =3D true; + + break; + default: + g_assert_not_reached(); + } + } + + /* + * Try our best to explicitly detect all not supported caps, + * to let users know what features cause migration to be blocked, + * but in case we miss handling here, everything else will be + * covered by unsupported_cap check. + */ + if (NVME_CAP_CMBS(cap)) { + nvme_add_blocker_feature(blocker_features, "CMB"); + cap &=3D ~((uint64_t)CAP_CMBS_MASK << CAP_CMBS_SHIFT); + } + + if (NVME_CAP_PMRS(cap)) { + nvme_add_blocker_feature(blocker_features, "PMR"); + cap &=3D ~((uint64_t)CAP_PMRS_MASK << CAP_PMRS_SHIFT); + } + + unsupported_cap =3D cap & ~NVME_MIGRATION_SUPPORTED_CAP_BITS; + if (unsupported_cap) { + nvme_add_blocker_feature(blocker_features, "unknown capability"); + } + + assert(n->migration_blocker =3D=3D NULL); + if (strlen(blocker_features) > 0) { + error_setg(&n->migration_blocker, "Migration is not supported for = %s", blocker_features); + if (migrate_add_blocker(&n->migration_blocker, errp) < 0) { + return false; + } + } + + return true; +} + static int nvme_init_subsys(NvmeCtrl *n, Error **errp) { int cntlid; @@ -9313,6 +9513,10 @@ static void nvme_realize(PCIDevice *pci_dev, Error *= *errp) =20 n->subsys->namespaces[ns->params.nsid] =3D ns; } + + if (!nvme_set_migration_blockers(n, pci_dev, errp)) { + return; + } } =20 static void nvme_exit(PCIDevice *pci_dev) @@ -9365,6 +9569,8 @@ static void nvme_exit(PCIDevice *pci_dev) } =20 memory_region_del_subregion(&n->bar0, &n->iomem); + + migrate_del_blocker(&n->migration_blocker); } =20 static const Property nvme_props[] =3D { diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index d66f7dc82d5..457b6637249 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -666,6 +666,9 @@ typedef struct NvmeCtrl { =20 /* Socket mapping to SPDM over NVMe Security In/Out commands */ int spdm_socket; + + /* Migration-related stuff */ + Error *migration_blocker; } NvmeCtrl; =20 typedef enum NvmeResetType { diff --git a/include/block/nvme.h b/include/block/nvme.h index 9d7159ed7a7..a7f586fc801 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -141,6 +141,18 @@ enum NvmeCapMask { #define NVME_CAP_SET_CMBS(cap, val) \ ((cap) |=3D (uint64_t)((val) & CAP_CMBS_MASK) << CAP_CMBS_SHIFT) =20 +#define NVME_MIGRATION_SUPPORTED_CAP_BITS ( \ + ((uint64_t)CAP_MQES_MASK << CAP_MQES_SHIFT) \ + | ((uint64_t)CAP_CQR_MASK << CAP_CQR_SHIFT) \ + | ((uint64_t)CAP_AMS_MASK << CAP_AMS_SHIFT) \ + | ((uint64_t)CAP_TO_MASK << CAP_TO_SHIFT) \ + | ((uint64_t)CAP_DSTRD_MASK << CAP_DSTRD_SHIFT) \ + | ((uint64_t)CAP_NSSRS_MASK << CAP_NSSRS_SHIFT) \ + | ((uint64_t)CAP_CSS_MASK << CAP_CSS_SHIFT) \ + | ((uint64_t)CAP_MPSMIN_MASK << CAP_MPSMIN_SHIFT) \ + | ((uint64_t)CAP_MPSMAX_MASK << CAP_MPSMAX_SHIFT) \ +) + enum NvmeCapCss { NVME_CAP_CSS_NCSS =3D 1 << 0, NVME_CAP_CSS_IOCSS =3D 1 << 6, --=20 2.47.3 From nobody Tue Apr 7 11:18:24 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=mihalicyn.com ARC-Seal: i=1; a=rsa-sha256; t=1773411213; cv=none; d=zohomail.com; s=zohoarc; b=lTgdDJNUQlmChb5iyagP9dBVI3jQhSnFeD34EOnTPQS47B1tNcOJ0SLPXfdfhn4FC5+TDvHdveBprLFzrDsKaqZZgBPNiAJN2DRSoXNJ/z1GqJN0K+JHsrPjaBUxWQC7OvXkJVTGCvY+Z4OwtyXPiI4m9m5nQXbpQ3jVTiyOCbY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773411213; 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=lv8cYOpNkSeeqxeKbJ1G68OYSr/qDeNt7sRa838SjpY=; b=Vd0Si6xYVFvkpGikbt7SWrfUnA3IXQhMmozpFUd7qe86vqmhl+c8kJqoSevm7Rzl3RlC0jdIw7+FWGCykiacpXlRACbIa8SqBHQRcJog4zQmJ44XR2A2gnpW9iRMMIivE1egsl6Nm/4tiP/qVpp7j1yaaGO47BSiTZLLE6ngfUM= 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 177341121319756.19883285630408; Fri, 13 Mar 2026 07:13:33 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w13G9-0005T9-No; Fri, 13 Mar 2026 10:13:02 -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 1w13Fs-0005QL-HX for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:51 -0400 Received: from mail-wr1-x434.google.com ([2a00:1450:4864:20::434]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w13Fl-0006O0-U3 for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:43 -0400 Received: by mail-wr1-x434.google.com with SMTP id ffacd0b85a97d-439c6fc2910so1812363f8f.0 for ; Fri, 13 Mar 2026 07:12:32 -0700 (PDT) Received: from alex-laptop.lan (p200300cf57228c00321ddb23f48fbb73.dip0.t-ipconnect.de. [2003:cf:5722:8c00:321d:db23:f48f:bb73]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe19ad9asm18580939f8f.7.2026.03.13.07.12.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Mar 2026 07:12:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mihalicyn.com; s=mihalicyn; t=1773411151; x=1774015951; 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=lv8cYOpNkSeeqxeKbJ1G68OYSr/qDeNt7sRa838SjpY=; b=JPatPrlfjY+suhBbavLzhbRPW/TPwuNQHsWDLOoZobfCrdbE28cmB+bGm4m/sPnSad geK9gjgcSHFRq0/zVHr+97hTvOQGYmGkQfc3f//07jSCRssT9tYSSTkdZ1a1WNw9icbD co043sFi6lrDMuHySWuvwuQVzoO5AyoNZKSFg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773411151; x=1774015951; 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=lv8cYOpNkSeeqxeKbJ1G68OYSr/qDeNt7sRa838SjpY=; b=hd08JXzkc2OLRmLOfIrxv2INBEiyUzJrX3GhQydJfvxamxH8XX1ozyacrs4onYyoAB 2nVKsbgZpzjyNUXkHet3/WlK7dtSf4w35+tgRKuo5GDFyas/F8SLT5fYVENJjAWppXcU kKy8U93HNfSCf/dHd3CIedrhhv++AqHO2e0d9uMD3tq3vTr8une/lJdnR02Oyruu5Ahs Y8FBql/6zGsbQOzrn8k3xd1tO5DZ9B9ZxEUdzCl2fnLzKqyjNEjmOW7+Fyzvi3nsEBOi xf1FJgMkdV0M6E2rKALAHwJaRKYBs48auP8DbjwsHvbIQro+rN1qzIgGVn63v7ADnSDJ XkWg== X-Gm-Message-State: AOJu0YyQpObfYj0BoZiAM4F3FWCHwNTz0jMMg8u6vLFVwzq2Ek6kAiAZ Ja6d6XxPwqsZobhqGAqHMz7UmwskAvS2P6YhzeBUtgB5BtaNxhqcdn8EqMLPA+z7NJ4Q+ElrtMO CAxMyaeY= X-Gm-Gg: ATEYQzysBX+SymqOyPU6GMYxgft9clv5TvQt8ATNXEWmgFyTW+9fUI0wcOms3VE0ddF 2paBbU4lNEuPe2G009YNTVO79MxqDRWrTYS+zKn7mriHj/H1579J9t8k8Kej3MUCjHF9ylxhs0r RMalYGbt8pZ/owsw3t9R8QfjLBPHjvyctrFUyDEwbaAPnguMPL21n+xuIju11JbR6YWu5dqBd9T MZ2kyi0Bvhr3TMftePxHap2WeREvlQAklHAV8n6kQiBw1wD+lBCv3QemSWXC+pZwg0AbYkObkDS HJUK2Lfhuoc8v3NLnMsMMJApEvXS/Fh/oc4oorTwI03UIY+WU5RZH5yeW609AyWBVs1fpnOLlTE MdxCV5HdCeVG6elgTVZlfRgI2wiTMv3WQrfZBTYXhWmH/5RiqT4KWWhBfxmHdypH2Vue1VSOYY4 fSHVxcZo0bWsbstWFjpU3meOlpZ2B+QuaKTd8RigypcKI28RahWHvqI0j9pM80JkDWj1fIBzNWb R4ts9WwPuSoIA2UXdWW/JU= X-Received: by 2002:a5d:5e01:0:b0:439:fcf4:32c6 with SMTP id ffacd0b85a97d-43a04dbec41mr6655959f8f.51.1773411151187; Fri, 13 Mar 2026 07:12:31 -0700 (PDT) From: Alexander Mikhalitsyn To: qemu-devel@nongnu.org Cc: Peter Xu , Jesper Devantier , Kevin Wolf , Fabiano Rosas , Paolo Bonzini , Hanna Reitz , qemu-block@nongnu.org, =?UTF-8?q?St=C3=A9phane=20Graber?= , Keith Busch , Stefan Hajnoczi , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Alexander Mikhalitsyn , Fam Zheng , Klaus Jensen , Zhao Liu , Alexander Mikhalitsyn Subject: [PATCH v4 6/8] hw/nvme: split nvme_init_sq/nvme_init_cq into helpers Date: Fri, 13 Mar 2026 15:12:19 +0100 Message-ID: <20260313141221.359503-7-alexander@mihalicyn.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260313141221.359503-1-alexander@mihalicyn.com> References: <20260313141221.359503-1-alexander@mihalicyn.com> 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::434; envelope-from=alexander@mihalicyn.com; helo=mail-wr1-x434.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.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, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable 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 @mihalicyn.com) X-ZM-MESSAGEID: 1773411215431158500 Content-Type: text/plain; charset="utf-8" From: Alexander Mikhalitsyn Signed-off-by: Alexander Mikhalitsyn --- hw/nvme/ctrl.c | 57 +++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 9f9c9bcbead..68ce9802505 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -4854,18 +4854,14 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeReques= t *req) return NVME_SUCCESS; } =20 -static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr, - uint16_t sqid, uint16_t cqid, uint16_t size) +static void __nvme_init_sq(NvmeSQueue *sq) { + NvmeCtrl *n =3D sq->ctrl; + uint16_t sqid =3D sq->sqid; + uint16_t cqid =3D sq->cqid; int i; NvmeCQueue *cq; =20 - sq->ctrl =3D n; - sq->dma_addr =3D dma_addr; - sq->sqid =3D sqid; - sq->size =3D size; - sq->cqid =3D cqid; - sq->head =3D sq->tail =3D 0; sq->io_req =3D g_new0(NvmeRequest, sq->size); =20 QTAILQ_INIT(&sq->req_list); @@ -4895,6 +4891,18 @@ static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n= , uint64_t dma_addr, n->sq[sqid] =3D sq; } =20 +static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr, + uint16_t sqid, uint16_t cqid, uint16_t size) +{ + sq->ctrl =3D n; + sq->dma_addr =3D dma_addr; + sq->sqid =3D sqid; + sq->size =3D size; + sq->cqid =3D cqid; + sq->head =3D sq->tail =3D 0; + __nvme_init_sq(sq); +} + static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeRequest *req) { NvmeSQueue *sq; @@ -5555,24 +5563,16 @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeReques= t *req) return NVME_SUCCESS; } =20 -static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr, - uint16_t cqid, uint16_t vector, uint16_t size, - uint16_t irq_enabled) +static void __nvme_init_cq(NvmeCQueue *cq) { + NvmeCtrl *n =3D cq->ctrl; PCIDevice *pci =3D PCI_DEVICE(n); + uint16_t cqid =3D cq->cqid; =20 - if (msix_enabled(pci) && irq_enabled) { - msix_vector_use(pci, vector); + if (msix_enabled(pci) && cq->irq_enabled) { + msix_vector_use(pci, cq->vector); } =20 - cq->ctrl =3D n; - cq->cqid =3D cqid; - cq->size =3D size; - cq->dma_addr =3D dma_addr; - cq->phase =3D 1; - cq->irq_enabled =3D irq_enabled; - cq->vector =3D vector; - cq->head =3D cq->tail =3D 0; QTAILQ_INIT(&cq->req_list); QTAILQ_INIT(&cq->sq_list); if (n->dbbuf_enabled) { @@ -5590,6 +5590,21 @@ static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n= , uint64_t dma_addr, &DEVICE(cq->ctrl)->mem_reentrancy_guard); } =20 +static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr, + uint16_t cqid, uint16_t vector, uint16_t size, + uint16_t irq_enabled) +{ + cq->ctrl =3D n; + cq->cqid =3D cqid; + cq->size =3D size; + cq->dma_addr =3D dma_addr; + cq->phase =3D 1; + cq->irq_enabled =3D irq_enabled; + cq->vector =3D vector; + cq->head =3D cq->tail =3D 0; + __nvme_init_cq(cq); +} + static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRequest *req) { NvmeCQueue *cq; --=20 2.47.3 From nobody Tue Apr 7 11:18:24 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=mihalicyn.com ARC-Seal: i=1; a=rsa-sha256; t=1773411280; cv=none; d=zohomail.com; s=zohoarc; b=S5Uz2Vq0++09/64nria66QVuW2gpbh/O/WwSJikopp93wDEMQcy7cc5yYRRwppx1ZvKVlrnkEjh7QZsyiDE/7ZN/iJ0uZSk/raxkJ8yIDRMZAa6PUzdl+7g3TYGHICTh0APqsZGxb6BzbLfMBsphajrxpNExBhUTuEj/H4tmfeQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773411280; 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=5oDn1DyyxJp+kkjF+9H5BBpPByqnknuxzyFDSX/Voqs=; b=jLdYk+2WTJhClR+rAzinfCsbIQLKWGrmeUxcEjnSQoKgOoVc5QLWVdkxqD0hJOo3OWsUWZI0YrxXbyyyWFzzT3idypy0kMQao4gvkql1bNprJXqdda3t/No4OUeru70GgQRmDbRyqyYTGzOvyijvS4aYOxw1y1KLlYsgZLIukp0= 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 1773411280385689.2929536862309; Fri, 13 Mar 2026 07:14:40 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w13G8-0005T3-3g; Fri, 13 Mar 2026 10:13:00 -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 1w13Fq-0005Q6-Nh for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:44 -0400 Received: from mail-wm1-x329.google.com ([2a00:1450:4864:20::329]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w13Fk-0006OU-Up for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:41 -0400 Received: by mail-wm1-x329.google.com with SMTP id 5b1f17b1804b1-4852a9c6309so18233725e9.0 for ; Fri, 13 Mar 2026 07:12:34 -0700 (PDT) Received: from alex-laptop.lan (p200300cf57228c00321ddb23f48fbb73.dip0.t-ipconnect.de. [2003:cf:5722:8c00:321d:db23:f48f:bb73]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe19ad9asm18580939f8f.7.2026.03.13.07.12.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Mar 2026 07:12:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mihalicyn.com; s=mihalicyn; t=1773411153; x=1774015953; 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=5oDn1DyyxJp+kkjF+9H5BBpPByqnknuxzyFDSX/Voqs=; b=aZLFSxxjDCYAkxuuwuYrqtFwDbFKbtWJXLnzBz8FbdPhGJGArfJtju3oR6Bj4UKjwk L5UXo+xyHPdc13JTwCHwIeXqYd87Pd6I/hqtqlNyQoKPukxy/l/qVqIcuFfmyM0VjprI vQVSfjc2Rdsr6zyXUTSkF+5zgeaE8r6vPj7EA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773411153; x=1774015953; 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=5oDn1DyyxJp+kkjF+9H5BBpPByqnknuxzyFDSX/Voqs=; b=qELdHg8dgsvEcBgC0SKqfVJdK2sZLL0j8+hB/hLDuFm5Guy3W1zkcfB3CHPMjyD3EY sWgQwW3lCjEGXbi5ZpguvOt1fFuc9t6dr5QNS2SCmhPDnoM5R89Ug9Y74inwuj30Ie6e 6SgTDDex3ENy187oNoekKWjgPp7Qi0KeR9e+sWboxU69kpoFrHjLF4QIRHgkTc0XCvSI +jVcb7enm6KpsSrXSqB72+OFu0NtlvZUk5s8Yo0zsIH2mwas77XkZhiSjHepUERFHyOn r04Xc7KA6eH1IlN6dM8NPfRYBJKPGVIHWghFjV/8XsySdfpktQ0ZJUGdGq9lvdFKRbTY 4rxg== X-Gm-Message-State: AOJu0YydmOktsx2xFq7d51E/yMXW5s8OI7Qv5YC5tapFhsh5dj+BRdM5 AEQV41M8qc+ZxezfFumqT2fmzylSDU+vxHxE986zvqG68YFpYWpSZWlgNrHOpq3c5gW4xzILZF1 Hsr4J7OE= X-Gm-Gg: ATEYQzxkd0pEQxriqVZEJuUoNgEQDYUphTBcaCGEpg3hR0wjM/xOQUuh8fhF5R98ctq Owr3cqVJeOINgHs2+z4XTfVQAVjizIa46zAU3SXAhhpu2fueM1UD/TdWbT89w/yjTG7gfgzC+za iVmTjMmgCIysFuBGCcwwKhuWrlXCimMxNU+ZI8EAF5BOBJ2N3tEq6sDnZdXd1LMGu4UWBOmAwKf DcVWUzBFYX2INngpq0VqzJdIEMop9CesMJp1fFltgKwLv9iqgL+VlPX/WaOHnO2TB2rKBnywQNF coz0cJahktMdT7+SVawUFdYFS0rKyu9vl9Nyzi8FVLu1TuHMcrQ+T/63Zj+1RgkuVNSdzzEvt5d tAj2NqFaMmz+jU/IGYsEYTe80iTei6dZ3tdwQTkk8vkVoFCBR986o5PJJIq/MRTD4II5+vo+bqz cjDziRO0Is1a0T5s7lD41gZVbzMKr0HFxtFakh9E3ySUwlzV4U5gE1LJUeqA64x3jTOUIhpm9Ir hQgMmD0knQ9KFuy9ZQJahsStZy/nKz2Eg== X-Received: by 2002:a05:600d:4448:20b0:485:3ae8:2236 with SMTP id 5b1f17b1804b1-4855671140emr41665445e9.32.1773411152489; Fri, 13 Mar 2026 07:12:32 -0700 (PDT) From: Alexander Mikhalitsyn To: qemu-devel@nongnu.org Cc: Peter Xu , Jesper Devantier , Kevin Wolf , Fabiano Rosas , Paolo Bonzini , Hanna Reitz , qemu-block@nongnu.org, =?UTF-8?q?St=C3=A9phane=20Graber?= , Keith Busch , Stefan Hajnoczi , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Alexander Mikhalitsyn , Fam Zheng , Klaus Jensen , Zhao Liu , Alexander Mikhalitsyn Subject: [PATCH v4 7/8] hw/nvme: add basic live migration support Date: Fri, 13 Mar 2026 15:12:20 +0100 Message-ID: <20260313141221.359503-8-alexander@mihalicyn.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260313141221.359503-1-alexander@mihalicyn.com> References: <20260313141221.359503-1-alexander@mihalicyn.com> 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::329; envelope-from=alexander@mihalicyn.com; helo=mail-wm1-x329.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.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, RCVD_IN_DNSWL_NONE=-0.0001, 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 @mihalicyn.com) X-ZM-MESSAGEID: 1773411301290158500 Content-Type: text/plain; charset="utf-8" From: Alexander Mikhalitsyn It has some limitations: - only one NVMe namespace is supported - SMART counters are not preserved - CMB is not supported - PMR is not supported - SPDM is not supported - SR-IOV is not supported Signed-off-by: Alexander Mikhalitsyn v2: - AERs are now fully supported --- hw/nvme/ctrl.c | 611 ++++++++++++++++++++++++++++++++++++++++++- hw/nvme/ns.c | 160 +++++++++++ hw/nvme/nvme.h | 5 + hw/nvme/trace-events | 11 + 4 files changed, 778 insertions(+), 9 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 68ce9802505..e3030bfadcb 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -208,6 +208,7 @@ #include "hw/pci/pcie_sriov.h" #include "system/spdm-socket.h" #include "migration/blocker.h" +#include "migration/qemu-file-types.h" #include "migration/vmstate.h" =20 #include "nvme.h" @@ -4903,6 +4904,25 @@ static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n= , uint64_t dma_addr, __nvme_init_sq(sq); } =20 +static void nvme_restore_sq(NvmeSQueue *sq_from) +{ + NvmeCtrl *n =3D sq_from->ctrl; + NvmeSQueue *sq =3D sq_from; + + if (sq_from->sqid =3D=3D 0) { + sq =3D &n->admin_sq; + sq->ctrl =3D n; + sq->dma_addr =3D sq_from->dma_addr; + sq->sqid =3D sq_from->sqid; + sq->size =3D sq_from->size; + sq->cqid =3D sq_from->cqid; + sq->head =3D sq_from->head; + sq->tail =3D sq_from->tail; + } + + __nvme_init_sq(sq); +} + static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeRequest *req) { NvmeSQueue *sq; @@ -5605,6 +5625,27 @@ static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n= , uint64_t dma_addr, __nvme_init_cq(cq); } =20 +static void nvme_restore_cq(NvmeCQueue *cq_from) +{ + NvmeCtrl *n =3D cq_from->ctrl; + NvmeCQueue *cq =3D cq_from; + + if (cq_from->cqid =3D=3D 0) { + cq =3D &n->admin_cq; + cq->ctrl =3D n; + cq->cqid =3D cq_from->cqid; + cq->size =3D cq_from->size; + cq->dma_addr =3D cq_from->dma_addr; + cq->phase =3D cq_from->phase; + cq->irq_enabled =3D cq_from->irq_enabled; + cq->vector =3D cq_from->vector; + cq->head =3D cq_from->head; + cq->tail =3D cq_from->tail; + } + + __nvme_init_cq(cq); +} + static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRequest *req) { NvmeCQueue *cq; @@ -7293,7 +7334,7 @@ static uint16_t nvme_dbbuf_config(NvmeCtrl *n, const = NvmeRequest *req) n->dbbuf_eis =3D eis_addr; n->dbbuf_enabled =3D true; =20 - for (i =3D 0; i < n->params.max_ioqpairs + 1; i++) { + for (i =3D 0; i < n->num_queues; i++) { NvmeSQueue *sq =3D n->sq[i]; NvmeCQueue *cq =3D n->cq[i]; =20 @@ -7737,7 +7778,7 @@ static int nvme_atomic_write_check(NvmeCtrl *n, NvmeC= md *cmd, /* * Walk the queues to see if there are any atomic conflicts. */ - for (i =3D 1; i < n->params.max_ioqpairs + 1; i++) { + for (i =3D 1; i < n->num_queues; i++) { NvmeSQueue *sq; NvmeRequest *req; NvmeRwCmd *req_rw; @@ -7807,6 +7848,10 @@ static void nvme_process_sq(void *opaque) NvmeCmd cmd; NvmeRequest *req; =20 + if (qatomic_read(&n->stop_processing_sq)) { + return; + } + if (n->dbbuf_enabled) { nvme_update_sq_tail(sq); } @@ -7815,6 +7860,10 @@ static void nvme_process_sq(void *opaque) NvmeAtomic *atomic; bool cmd_is_atomic; =20 + if (qatomic_read(&n->stop_processing_sq)) { + return; + } + addr =3D sq->dma_addr + (sq->head << NVME_SQES); if (nvme_addr_read(n, addr, (void *)&cmd, sizeof(cmd))) { trace_pci_nvme_err_addr_read(addr); @@ -7923,12 +7972,12 @@ static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetT= ype rst) nvme_ns_drain(ns); } =20 - for (i =3D 0; i < n->params.max_ioqpairs + 1; i++) { + for (i =3D 0; i < n->num_queues; i++) { if (n->sq[i] !=3D NULL) { nvme_free_sq(n->sq[i], n); } } - for (i =3D 0; i < n->params.max_ioqpairs + 1; i++) { + for (i =3D 0; i < n->num_queues; i++) { if (n->cq[i] !=3D NULL) { nvme_free_cq(n->cq[i], n); } @@ -8598,6 +8647,8 @@ static bool nvme_check_params(NvmeCtrl *n, Error **er= rp) params->max_ioqpairs =3D params->num_queues - 1; } =20 + n->num_queues =3D params->max_ioqpairs + 1; + if (n->namespace.blkconf.blk && n->subsys) { error_setg(errp, "subsystem support is unavailable with legacy " "namespace ('drive' property)"); @@ -8752,8 +8803,8 @@ static void nvme_init_state(NvmeCtrl *n) n->conf_msix_qsize =3D n->params.msix_qsize; } =20 - n->sq =3D g_new0(NvmeSQueue *, n->params.max_ioqpairs + 1); - n->cq =3D g_new0(NvmeCQueue *, n->params.max_ioqpairs + 1); + n->sq =3D g_new0(NvmeSQueue *, n->num_queues); + n->cq =3D g_new0(NvmeCQueue *, n->num_queues); n->temperature =3D NVME_TEMPERATURE; n->features.temp_thresh_hi =3D NVME_TEMPERATURE_WARNING; n->starttime_ms =3D qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); @@ -8988,7 +9039,7 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci= _dev, Error **errp) } =20 if (n->params.msix_exclusive_bar && !pci_is_vf(pci_dev)) { - bar_size =3D nvme_mbar_size(n->params.max_ioqpairs + 1, 0, NULL, N= ULL); + bar_size =3D nvme_mbar_size(n->num_queues, 0, NULL, NULL); memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nv= me", bar_size); pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | @@ -9000,7 +9051,7 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci= _dev, Error **errp) /* add one to max_ioqpairs to account for the admin queue pair */ if (!pci_is_vf(pci_dev)) { nr_vectors =3D n->params.msix_qsize; - bar_size =3D nvme_mbar_size(n->params.max_ioqpairs + 1, + bar_size =3D nvme_mbar_size(n->num_queues, nr_vectors, &msix_table_offset, &msix_pba_offset); } else { @@ -9723,9 +9774,551 @@ static uint32_t nvme_pci_read_config(PCIDevice *dev= , uint32_t address, int len) return pci_default_read_config(dev, address, len); } =20 +static bool nvme_ctrl_pre_save(void *opaque, Error **errp) +{ + NvmeCtrl *n =3D opaque; + int i; + + trace_pci_nvme_pre_save_enter(n); + + /* ask SQ processing code not to take new requests */ + qatomic_set(&n->stop_processing_sq, true); + + /* prevent new in-flight IO from appearing */ + for (i =3D 0; i < n->num_queues; i++) { + NvmeSQueue *sq =3D n->sq[i]; + + if (!sq) + continue; + + qemu_bh_cancel(sq->bh); + } + + /* drain all IO */ + for (i =3D 1; i <=3D NVME_MAX_NAMESPACES; i++) { + NvmeNamespace *ns; + + ns =3D nvme_ns(n, i); + if (!ns) { + continue; + } + + trace_pci_nvme_pre_save_ns_drain(n, i); + nvme_ns_drain(ns); + } + + /* + * Now, we should take care of AERs. + * + * 1. Save all queued events (n->aer_queue). + * This is done automatically, see nvme_vmstate VMStateDescription. + * Here we only need to print them for debugging purpose. + * 2. Go over outstanding AER requests (n->aer_reqs) and check they are + * all have expected opcode (NVME_ADM_CMD_ASYNC_EV_REQ) and other f= ields. + * + * We must be really careful here, because in case of further QEMU NVM= e changes, + * we may break migration without noticing it, or worse, introduce sil= ent + * data corruption during migration. + */ + if (n->aer_queued) { + NvmeAsyncEvent *event; + + QTAILQ_FOREACH(event, &n->aer_queue, entry) { + trace_pci_nvme_pre_save_aer(event->result.event_type, event->r= esult.event_info, + event->result.log_page); + } + } + + for (i =3D 0; i < n->outstanding_aers; i++) { + NvmeRequest *re =3D n->aer_reqs[i]; + + /* + * Can't use assert() here, because we don't want + * to just crash QEMU when user requests a migration. + */ + if (!(re->cmd.opcode =3D=3D NVME_ADM_CMD_ASYNC_EV_REQ)) { + error_setg(errp, "re->cmd.opcode (%u) !=3D NVME_ADM_CMD_ASYNC_= EV_REQ", re->cmd.opcode); + goto err; + } + + if (!(re->ns =3D=3D NULL)) { + error_setg(errp, "re->ns !=3D NULL"); + goto err; + } + + if (!(re->sq =3D=3D &n->admin_sq)) { + error_setg(errp, "re->sq !=3D &n->admin_sq"); + goto err; + } + + if (!(re->aiocb =3D=3D NULL)) { + error_setg(errp, "re->aiocb !=3D NULL"); + goto err; + } + + if (!(re->opaque =3D=3D NULL)) { + error_setg(errp, "re->opaque !=3D NULL"); + goto err; + } + + if (!(re->atomic_write =3D=3D false)) { + error_setg(errp, "re->atomic_write !=3D false"); + goto err; + } + + if (re->sg.flags & NVME_SG_ALLOC) { + error_setg(errp, "unexpected NVME_SG_ALLOC flag in re->sg.flag= s"); + goto err; + } + } + + /* wait when all in-flight IO requests (except NVME_ADM_CMD_ASYNC_EV_R= EQ) are processed */ + for (i =3D 0; i < n->num_queues; i++) { + NvmeRequest *req; + NvmeSQueue *sq =3D n->sq[i]; + + if (!sq) + continue; + + trace_pci_nvme_pre_save_sq_out_req_drain_wait(n, i, sq->head, sq->= tail, sq->size); + +wait_out_reqs: + QTAILQ_FOREACH(req, &sq->out_req_list, entry) { + if (req->cmd.opcode !=3D NVME_ADM_CMD_ASYNC_EV_REQ) { + cpu_relax(); + goto wait_out_reqs; + } + } + + trace_pci_nvme_pre_save_sq_out_req_drain_wait_end(n, i, sq->head, = sq->tail); + } + + /* wait when all IO requests completions are written to guest memory */ + for (i =3D 0; i < n->num_queues; i++) { + NvmeCQueue *cq =3D n->cq[i]; + + if (!cq) + continue; + + trace_pci_nvme_pre_save_cq_req_drain_wait(n, i, cq->head, cq->tail= , cq->size); + + while (!QTAILQ_EMPTY(&cq->req_list)) { + /* + * nvme_post_cqes() can't do its job of cleaning cq->req_list + * when CQ is full, it means that we need to save what we have= in + * cq->req_list and restore it back on VM resume. + * + * Good thing is that this can only happen when guest hasn't + * processed CQ for a long time and at the same time, many SQEs + * are in flight. + * + * For now, let's just block migration in this rare case. + */ + if (nvme_cq_full(cq)) { + error_setg(errp, "no free space in CQ (not supported)"); + goto err; + } + + cpu_relax(); + } + + trace_pci_nvme_pre_save_cq_req_drain_wait_end(n, i, cq->head, cq->= tail); + } + + for (uint32_t nsid =3D 0; nsid <=3D NVME_MAX_NAMESPACES; nsid++) { + NvmeNamespace *ns =3D n->namespaces[nsid]; + + if (!ns) + continue; + + if (ns !=3D &n->namespace) { + error_setg(errp, "only one NVMe namespace is supported for mig= ration"); + goto err; + } + } + + return true; + +err: + /* restore sq processing back to normal */ + qatomic_set(&n->stop_processing_sq, false); + return false; +} + +static bool nvme_ctrl_post_load(void *opaque, int version_id, Error **errp) +{ + NvmeCtrl *n =3D opaque; + int i; + + trace_pci_nvme_post_load_enter(n); + + /* restore CQs first */ + for (i =3D 0; i < n->num_queues; i++) { + NvmeCQueue *cq =3D n->cq[i]; + + if (!cq) + continue; + + cq->ctrl =3D n; + nvme_restore_cq(cq); + trace_pci_nvme_post_load_restore_cq(n, i, cq->head, cq->tail, cq->= size); + + if (i =3D=3D 0) { + /* + * Admin CQ lives in n->admin_cq, we don't need + * memory allocated for it in get_ptrs_array_entry() anymore. + * + * nvme_restore_cq() also takes care of: + * n->cq[0] =3D &n->admin_cq; + * so n->cq[0] remains valid. + */ + g_free(cq); + } + } + + for (i =3D 0; i < n->num_queues; i++) { + NvmeSQueue *sq =3D n->sq[i]; + + if (!sq) + continue; + + sq->ctrl =3D n; + nvme_restore_sq(sq); + trace_pci_nvme_post_load_restore_sq(n, i, sq->head, sq->tail, sq->= size); + + if (i =3D=3D 0) { + /* same as for CQ */ + g_free(sq); + } + } + + if (n->aer_queued) { + NvmeAsyncEvent *event; + + QTAILQ_FOREACH(event, &n->aer_queue, entry) { + trace_pci_nvme_post_load_aer(event->result.event_type, event->= result.event_info, + event->result.log_page); + } + } + + for (i =3D 0; i < n->outstanding_aers; i++) { + NvmeSQueue *sq =3D &n->admin_sq; + NvmeRequest *req_from =3D n->aer_reqs[i]; + NvmeRequest *req; + + /* + * We use nvme_vmstate VMStateDescription to save/restore + * NvmeRequest structures, but tricky thing here is that + * memory for each n->aer_reqs[i] will be allocated separately + * during restore. It doesn't work for us. We need to take + * an existing NvmeRequest structure from SQ's req_list + * and fill it with data from the newly allocated one (req_from). + * Then, we can safely release allocated memory for it. + */ + + /* take an NvmeRequest struct from SQ */ + req =3D QTAILQ_FIRST(&sq->req_list); + QTAILQ_REMOVE(&sq->req_list, req, entry); + QTAILQ_INSERT_TAIL(&sq->out_req_list, req, entry); + nvme_req_clear(req); + + /* copy data from the source NvmeRequest */ + req->status =3D req_from->status; + memcpy(&req->cqe, &req_from->cqe, sizeof(NvmeCqe)); + memcpy(&req->cmd, &req_from->cmd, sizeof(NvmeCmd)); + + n->aer_reqs[i] =3D req; + g_free(req_from); + } + + /* + * We need to attach namespaces (currently, only one namespace is + * supported for migration). + * This logic comes from nvme_start_ctrl(). + */ + for (i =3D 1; i <=3D NVME_MAX_NAMESPACES; i++) { + NvmeNamespace *ns =3D nvme_subsys_ns(n->subsys, i); + + if (!ns || (!ns->params.shared && ns->ctrl !=3D n)) { + continue; + } + + if (nvme_csi_supported(n, ns->csi) && !ns->params.detached) { + if (!ns->attached || ns->params.shared) { + nvme_attach_ns(n, ns); + } + } + } + + /* schedule SQ processing */ + for (i =3D 0; i < n->num_queues; i++) { + NvmeSQueue *sq =3D n->sq[i]; + + if (!sq) + continue; + + qemu_bh_schedule(sq->bh); + } + + /* + * We ensured in pre_save() that cq->req_list was empty, + * so we don't need to schedule BH for CQ processing. + */ + + return true; +} + +static const VMStateDescription nvme_vmstate_bar =3D { + .name =3D "nvme-bar", + .minimum_version_id =3D 1, + .version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT64(cap, NvmeBar), + VMSTATE_UINT32(vs, NvmeBar), + VMSTATE_UINT32(intms, NvmeBar), + VMSTATE_UINT32(intmc, NvmeBar), + VMSTATE_UINT32(cc, NvmeBar), + VMSTATE_UINT8_ARRAY(rsvd24, NvmeBar, 4), + VMSTATE_UINT32(csts, NvmeBar), + VMSTATE_UINT32(nssr, NvmeBar), + VMSTATE_UINT32(aqa, NvmeBar), + VMSTATE_UINT64(asq, NvmeBar), + VMSTATE_UINT64(acq, NvmeBar), + VMSTATE_UINT32(cmbloc, NvmeBar), + VMSTATE_UINT32(cmbsz, NvmeBar), + VMSTATE_UINT32(bpinfo, NvmeBar), + VMSTATE_UINT32(bprsel, NvmeBar), + VMSTATE_UINT64(bpmbl, NvmeBar), + VMSTATE_UINT64(cmbmsc, NvmeBar), + VMSTATE_UINT32(cmbsts, NvmeBar), + VMSTATE_UINT8_ARRAY(rsvd92, NvmeBar, 3492), + VMSTATE_UINT32(pmrcap, NvmeBar), + VMSTATE_UINT32(pmrctl, NvmeBar), + VMSTATE_UINT32(pmrsts, NvmeBar), + VMSTATE_UINT32(pmrebs, NvmeBar), + VMSTATE_UINT32(pmrswtp, NvmeBar), + VMSTATE_UINT32(pmrmscl, NvmeBar), + VMSTATE_UINT32(pmrmscu, NvmeBar), + VMSTATE_UINT8_ARRAY(css, NvmeBar, 484), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription nvme_vmstate_cqueue =3D { + .name =3D "nvme-cq", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT8(phase, NvmeCQueue), + VMSTATE_UINT16(cqid, NvmeCQueue), + VMSTATE_UINT16(irq_enabled, NvmeCQueue), + VMSTATE_UINT32(head, NvmeCQueue), + VMSTATE_UINT32(tail, NvmeCQueue), + VMSTATE_UINT32(vector, NvmeCQueue), + VMSTATE_UINT32(size, NvmeCQueue), + VMSTATE_UINT64(dma_addr, NvmeCQueue), + /* db_addr, ei_addr, etc will be recalculated */ + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_squeue =3D { + .name =3D "nvme-sq", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT16(sqid, NvmeSQueue), + VMSTATE_UINT16(cqid, NvmeSQueue), + VMSTATE_UINT32(head, NvmeSQueue), + VMSTATE_UINT32(tail, NvmeSQueue), + VMSTATE_UINT32(size, NvmeSQueue), + VMSTATE_UINT64(dma_addr, NvmeSQueue), + /* db_addr, ei_addr, etc will be recalculated */ + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_async_event_result =3D { + .name =3D "nvme-async-event-result", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT8(event_type, NvmeAerResult), + VMSTATE_UINT8(event_info, NvmeAerResult), + VMSTATE_UINT8(log_page, NvmeAerResult), + VMSTATE_UINT8(resv, NvmeAerResult), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_async_event =3D { + .name =3D "nvme-async-event", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_STRUCT(result, NvmeAsyncEvent, 0, nvme_vmstate_async_event= _result, NvmeAerResult), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_cqe =3D { + .name =3D "nvme-cqe", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT32(result, NvmeCqe), + VMSTATE_UINT32(dw1, NvmeCqe), + VMSTATE_UINT16(sq_head, NvmeCqe), + VMSTATE_UINT16(sq_id, NvmeCqe), + VMSTATE_UINT16(cid, NvmeCqe), + VMSTATE_UINT16(status, NvmeCqe), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_cmd_dptr_sgl =3D { + .name =3D "nvme-request-cmd-dptr-sgl", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT64(addr, NvmeSglDescriptor), + VMSTATE_UINT32(len, NvmeSglDescriptor), + VMSTATE_UINT8_ARRAY(rsvd, NvmeSglDescriptor, 3), + VMSTATE_UINT8(type, NvmeSglDescriptor), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_cmd_dptr =3D { + .name =3D "nvme-request-cmd-dptr", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT64(prp1, NvmeCmdDptr), + VMSTATE_UINT64(prp2, NvmeCmdDptr), + VMSTATE_STRUCT(sgl, NvmeCmdDptr, 0, nvme_vmstate_cmd_dptr_sgl, Nvm= eSglDescriptor), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_cmd =3D { + .name =3D "nvme-request-cmd", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT8(opcode, NvmeCmd), + VMSTATE_UINT8(flags, NvmeCmd), + VMSTATE_UINT16(cid, NvmeCmd), + VMSTATE_UINT32(nsid, NvmeCmd), + VMSTATE_UINT64(res1, NvmeCmd), + VMSTATE_UINT64(mptr, NvmeCmd), + VMSTATE_STRUCT(dptr, NvmeCmd, 0, nvme_vmstate_cmd_dptr, NvmeCmdDpt= r), + VMSTATE_UINT32(cdw10, NvmeCmd), + VMSTATE_UINT32(cdw11, NvmeCmd), + VMSTATE_UINT32(cdw12, NvmeCmd), + VMSTATE_UINT32(cdw13, NvmeCmd), + VMSTATE_UINT32(cdw14, NvmeCmd), + VMSTATE_UINT32(cdw15, NvmeCmd), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_request =3D { + .name =3D "nvme-request", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT16(status, NvmeRequest), + VMSTATE_STRUCT(cqe, NvmeRequest, 0, nvme_vmstate_cqe, NvmeCqe), + VMSTATE_STRUCT(cmd, NvmeRequest, 0, nvme_vmstate_cmd, NvmeCmd), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_hbs =3D { + .name =3D "nvme-hbs", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT8(acre, NvmeHostBehaviorSupport), + VMSTATE_UINT8(etdas, NvmeHostBehaviorSupport), + VMSTATE_UINT8(lbafee, NvmeHostBehaviorSupport), + VMSTATE_UINT8(rsvd3, NvmeHostBehaviorSupport), + VMSTATE_UINT16(cdfe, NvmeHostBehaviorSupport), + VMSTATE_UINT8_ARRAY(rsvd6, NvmeHostBehaviorSupport, 506), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription nvme_vmstate_atomic =3D { + .name =3D "nvme-atomic", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT32(atomic_max_write_size, NvmeAtomic), + VMSTATE_UINT64(atomic_boundary, NvmeAtomic), + VMSTATE_UINT64(atomic_nabo, NvmeAtomic), + VMSTATE_BOOL(atomic_writes, NvmeAtomic), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription nvme_vmstate =3D { .name =3D "nvme", - .unmigratable =3D 1, + .minimum_version_id =3D 1, + .version_id =3D 1, + .pre_save_errp =3D nvme_ctrl_pre_save, + .post_load_errp =3D nvme_ctrl_post_load, + .fields =3D (const VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, NvmeCtrl), + VMSTATE_MSIX(parent_obj, NvmeCtrl), + VMSTATE_STRUCT(bar, NvmeCtrl, 0, nvme_vmstate_bar, NvmeBar), + + VMSTATE_BOOL(qs_created, NvmeCtrl), + VMSTATE_UINT32(page_size, NvmeCtrl), + VMSTATE_UINT16(page_bits, NvmeCtrl), + VMSTATE_UINT16(max_prp_ents, NvmeCtrl), + VMSTATE_UINT32(max_q_ents, NvmeCtrl), + VMSTATE_UINT8(outstanding_aers, NvmeCtrl), + VMSTATE_UINT32(irq_status, NvmeCtrl), + VMSTATE_INT32(cq_pending, NvmeCtrl), + + VMSTATE_UINT64(host_timestamp, NvmeCtrl), + VMSTATE_UINT64(timestamp_set_qemu_clock_ms, NvmeCtrl), + VMSTATE_UINT64(starttime_ms, NvmeCtrl), + VMSTATE_UINT16(temperature, NvmeCtrl), + VMSTATE_UINT8(smart_critical_warning, NvmeCtrl), + + VMSTATE_UINT32(conf_msix_qsize, NvmeCtrl), + VMSTATE_UINT32(conf_ioqpairs, NvmeCtrl), + VMSTATE_UINT64(dbbuf_dbs, NvmeCtrl), + VMSTATE_UINT64(dbbuf_eis, NvmeCtrl), + VMSTATE_BOOL(dbbuf_enabled, NvmeCtrl), + + VMSTATE_UINT8(aer_mask, NvmeCtrl), + VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT8_ALLOC( + aer_reqs, NvmeCtrl, outstanding_aers, 0, nvme_vmstate_request,= NvmeRequest), + VMSTATE_QTAILQ_V(aer_queue, NvmeCtrl, 1, nvme_vmstate_async_event, + NvmeAsyncEvent, entry), + VMSTATE_INT32(aer_queued, NvmeCtrl), + + VMSTATE_STRUCT(namespace, NvmeCtrl, 0, nvme_vmstate_ns, NvmeNamesp= ace), + + VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC( + sq, NvmeCtrl, num_queues, 0, nvme_vmstate_squeue, NvmeSQueue), + VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC( + cq, NvmeCtrl, num_queues, 0, nvme_vmstate_cqueue, NvmeCQueue), + + VMSTATE_UINT16(features.temp_thresh_hi, NvmeCtrl), + VMSTATE_UINT16(features.temp_thresh_low, NvmeCtrl), + VMSTATE_UINT32(features.async_config, NvmeCtrl), + VMSTATE_STRUCT(features.hbs, NvmeCtrl, 0, nvme_vmstate_hbs, NvmeHo= stBehaviorSupport), + + VMSTATE_UINT32(dn, NvmeCtrl), + VMSTATE_STRUCT(atomic, NvmeCtrl, 0, nvme_vmstate_atomic, NvmeAtomi= c), + + VMSTATE_END_OF_LIST() + }, }; =20 static void nvme_class_init(ObjectClass *oc, const void *data) diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 38f86a17268..dd374677078 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -20,6 +20,7 @@ #include "qemu/bitops.h" #include "system/system.h" #include "system/block-backend.h" +#include "migration/vmstate.h" =20 #include "nvme.h" #include "trace.h" @@ -886,6 +887,164 @@ static void nvme_ns_realize(DeviceState *dev, Error *= *errp) } } =20 +static const VMStateDescription nvme_vmstate_lbaf =3D { + .name =3D "nvme_lbaf", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT16(ms, NvmeLBAF), + VMSTATE_UINT8(ds, NvmeLBAF), + VMSTATE_UINT8(rp, NvmeLBAF), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_id_ns =3D { + .name =3D "nvme_id_ns", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT64(nsze, NvmeIdNs), + VMSTATE_UINT64(ncap, NvmeIdNs), + VMSTATE_UINT64(nuse, NvmeIdNs), + VMSTATE_UINT8(nsfeat, NvmeIdNs), + VMSTATE_UINT8(nlbaf, NvmeIdNs), + VMSTATE_UINT8(flbas, NvmeIdNs), + VMSTATE_UINT8(mc, NvmeIdNs), + VMSTATE_UINT8(dpc, NvmeIdNs), + VMSTATE_UINT8(dps, NvmeIdNs), + VMSTATE_UINT8(nmic, NvmeIdNs), + VMSTATE_UINT8(rescap, NvmeIdNs), + VMSTATE_UINT8(fpi, NvmeIdNs), + VMSTATE_UINT8(dlfeat, NvmeIdNs), + VMSTATE_UINT16(nawun, NvmeIdNs), + VMSTATE_UINT16(nawupf, NvmeIdNs), + VMSTATE_UINT16(nacwu, NvmeIdNs), + VMSTATE_UINT16(nabsn, NvmeIdNs), + VMSTATE_UINT16(nabo, NvmeIdNs), + VMSTATE_UINT16(nabspf, NvmeIdNs), + VMSTATE_UINT16(noiob, NvmeIdNs), + VMSTATE_UINT8_ARRAY(nvmcap, NvmeIdNs, 16), + VMSTATE_UINT16(npwg, NvmeIdNs), + VMSTATE_UINT16(npwa, NvmeIdNs), + VMSTATE_UINT16(npdg, NvmeIdNs), + VMSTATE_UINT16(npda, NvmeIdNs), + VMSTATE_UINT16(nows, NvmeIdNs), + VMSTATE_UINT16(mssrl, NvmeIdNs), + VMSTATE_UINT32(mcl, NvmeIdNs), + VMSTATE_UINT8(msrc, NvmeIdNs), + VMSTATE_UINT8_ARRAY(rsvd81, NvmeIdNs, 18), + VMSTATE_UINT8(nsattr, NvmeIdNs), + VMSTATE_UINT16(nvmsetid, NvmeIdNs), + VMSTATE_UINT16(endgid, NvmeIdNs), + VMSTATE_UINT8_ARRAY(nguid, NvmeIdNs, 16), + VMSTATE_UINT64(eui64, NvmeIdNs), + VMSTATE_STRUCT_ARRAY(lbaf, NvmeIdNs, NVME_MAX_NLBAF, 1, + nvme_vmstate_lbaf, NvmeLBAF), + VMSTATE_UINT8_ARRAY(vs, NvmeIdNs, 3712), + + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_id_ns_nvm =3D { + .name =3D "nvme_id_ns_nvm", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT64(lbstm, NvmeIdNsNvm), + VMSTATE_UINT8(pic, NvmeIdNsNvm), + VMSTATE_UINT8_ARRAY(rsvd9, NvmeIdNsNvm, 3), + VMSTATE_UINT32_ARRAY(elbaf, NvmeIdNsNvm, NVME_MAX_NLBAF), + VMSTATE_UINT32(npdgl, NvmeIdNsNvm), + VMSTATE_UINT32(nprg, NvmeIdNsNvm), + VMSTATE_UINT32(npra, NvmeIdNsNvm), + VMSTATE_UINT32(nors, NvmeIdNsNvm), + VMSTATE_UINT32(npdal, NvmeIdNsNvm), + VMSTATE_UINT8_ARRAY(rsvd288, NvmeIdNsNvm, 3808), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription nvme_vmstate_id_ns_ind =3D { + .name =3D "nvme_id_ns_ind", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT8(nsfeat, NvmeIdNsInd), + VMSTATE_UINT8(nmic, NvmeIdNsInd), + VMSTATE_UINT8(rescap, NvmeIdNsInd), + VMSTATE_UINT8(fpi, NvmeIdNsInd), + VMSTATE_UINT32(anagrpid, NvmeIdNsInd), + VMSTATE_UINT8(nsattr, NvmeIdNsInd), + VMSTATE_UINT8(rsvd9, NvmeIdNsInd), + VMSTATE_UINT16(nvmsetid, NvmeIdNsInd), + VMSTATE_UINT16(endgrpid, NvmeIdNsInd), + VMSTATE_UINT8(nstat, NvmeIdNsInd), + VMSTATE_UINT8_ARRAY(rsvd15, NvmeIdNsInd, 4081), + VMSTATE_END_OF_LIST() + } +}; + +typedef struct TmpNvmeNamespace { + NvmeNamespace *parent; + bool enable_write_cache; +} TmpNvmeNamespace; + +static bool nvme_ns_tmp_pre_save(void *opaque, Error **errp) +{ + struct TmpNvmeNamespace *tns =3D opaque; + + tns->enable_write_cache =3D blk_enable_write_cache(tns->parent->blkcon= f.blk); + + return true; +} + +static bool nvme_ns_tmp_post_load(void *opaque, int version_id, Error **er= rp) +{ + struct TmpNvmeNamespace *tns =3D opaque; + + blk_set_enable_write_cache(tns->parent->blkconf.blk, tns->enable_write= _cache); + + return true; +} + +static const VMStateDescription nvme_vmstate_ns_tmp =3D { + .name =3D "nvme_ns_tmp", + .pre_save_errp =3D nvme_ns_tmp_pre_save, + .post_load_errp =3D nvme_ns_tmp_post_load, + .fields =3D (const VMStateField[]) { + VMSTATE_BOOL(enable_write_cache, TmpNvmeNamespace), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription nvme_vmstate_ns =3D { + .name =3D "nvme_ns", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_WITH_TMP(NvmeNamespace, TmpNvmeNamespace, nvme_vmstate_ns_= tmp), + + VMSTATE_STRUCT(id_ns, NvmeNamespace, 0, nvme_vmstate_id_ns, NvmeId= Ns), + VMSTATE_STRUCT(id_ns_nvm, NvmeNamespace, 0, nvme_vmstate_id_ns_nvm= , NvmeIdNsNvm), + VMSTATE_STRUCT(id_ns_ind, NvmeNamespace, 0, nvme_vmstate_id_ns_ind= , NvmeIdNsInd), + VMSTATE_STRUCT(lbaf, NvmeNamespace, 0, nvme_vmstate_lbaf, NvmeLBAF= ), + VMSTATE_UINT32(nlbaf, NvmeNamespace), + VMSTATE_UINT8(csi, NvmeNamespace), + VMSTATE_UINT16(status, NvmeNamespace), + VMSTATE_UINT8(pif, NvmeNamespace), + + VMSTATE_UINT16(zns.zrwas, NvmeNamespace), + VMSTATE_UINT16(zns.zrwafg, NvmeNamespace), + VMSTATE_UINT32(zns.numzrwa, NvmeNamespace), + + VMSTATE_UINT32(features.err_rec, NvmeNamespace), + VMSTATE_STRUCT(atomic, NvmeNamespace, 0, nvme_vmstate_atomic, Nvme= Atomic), + VMSTATE_END_OF_LIST() + } +}; + static const Property nvme_ns_props[] =3D { DEFINE_BLOCK_PROPERTIES(NvmeNamespace, blkconf), DEFINE_PROP_BOOL("detached", NvmeNamespace, params.detached, false), @@ -937,6 +1096,7 @@ static void nvme_ns_class_init(ObjectClass *oc, const = void *data) dc->bus_type =3D TYPE_NVME_BUS; dc->realize =3D nvme_ns_realize; dc->unrealize =3D nvme_ns_unrealize; + dc->vmsd =3D &nvme_vmstate_ns; device_class_set_props(dc, nvme_ns_props); dc->desc =3D "Virtual NVMe namespace"; } diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 457b6637249..03d6aecb7d7 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -638,6 +638,7 @@ typedef struct NvmeCtrl { =20 NvmeNamespace namespace; NvmeNamespace *namespaces[NVME_MAX_NAMESPACES + 1]; + uint32_t num_queues; NvmeSQueue **sq; NvmeCQueue **cq; NvmeSQueue admin_sq; @@ -669,6 +670,7 @@ typedef struct NvmeCtrl { =20 /* Migration-related stuff */ Error *migration_blocker; + bool stop_processing_sq; } NvmeCtrl; =20 typedef enum NvmeResetType { @@ -749,4 +751,7 @@ void nvme_atomic_configure_max_write_size(bool dn, uint= 16_t awun, void nvme_ns_atomic_configure_boundary(bool dn, uint16_t nabsn, uint16_t nabspf, NvmeAtomic *atomic= ); =20 +extern const VMStateDescription nvme_vmstate_atomic; +extern const VMStateDescription nvme_vmstate_ns; + #endif /* HW_NVME_NVME_H */ diff --git a/hw/nvme/trace-events b/hw/nvme/trace-events index 6be0bfa1c1f..8e5544e0008 100644 --- a/hw/nvme/trace-events +++ b/hw/nvme/trace-events @@ -7,6 +7,17 @@ pci_nvme_dbbuf_config(uint64_t dbs_addr, uint64_t eis_addr= ) "dbs_addr=3D0x%"PRIx64 pci_nvme_map_addr(uint64_t addr, uint64_t len) "addr 0x%"PRIx64" len %"PRI= u64"" pci_nvme_map_addr_cmb(uint64_t addr, uint64_t len) "addr 0x%"PRIx64" len %= "PRIu64"" pci_nvme_map_prp(uint64_t trans_len, uint32_t len, uint64_t prp1, uint64_t= prp2, int num_prps) "trans_len %"PRIu64" len %"PRIu32" prp1 0x%"PRIx64" pr= p2 0x%"PRIx64" num_prps %d" +pci_nvme_pre_save_enter(void *n) "n=3D%p" +pci_nvme_pre_save_ns_drain(void *n, int i) "n=3D%p i=3D%d" +pci_nvme_pre_save_sq_out_req_drain_wait(void *n, int i, uint32_t head, uin= t32_t tail, uint32_t size) "n=3D%p i=3D%d head=3D0x%"PRIx32" tail=3D0x%"PRI= x32" size=3D0x%"PRIx32"" +pci_nvme_pre_save_sq_out_req_drain_wait_end(void *n, int i, uint32_t head,= uint32_t tail) "n=3D%p i=3D%d head=3D0x%"PRIx32" tail=3D0x%"PRIx32"" +pci_nvme_pre_save_cq_req_drain_wait(void *n, int i, uint32_t head, uint32_= t tail, uint32_t size) "n=3D%p i=3D%d head=3D0x%"PRIx32" tail=3D0x%"PRIx32"= size=3D0x%"PRIx32"" +pci_nvme_pre_save_cq_req_drain_wait_end(void *n, int i, uint32_t head, uin= t32_t tail) "n=3D%p i=3D%d head=3D0x%"PRIx32" tail=3D0x%"PRIx32"" +pci_nvme_pre_save_aer(uint8_t typ, uint8_t info, uint8_t log_page) "type 0= x%"PRIx8" info 0x%"PRIx8" lid 0x%"PRIx8"" +pci_nvme_post_load_enter(void *n) "n=3D%p" +pci_nvme_post_load_restore_cq(void *n, int i, uint32_t head, uint32_t tail= , uint32_t size) "n=3D%p i=3D%d head=3D0x%"PRIx32" tail=3D0x%"PRIx32" size= =3D0x%"PRIx32"" +pci_nvme_post_load_restore_sq(void *n, int i, uint32_t head, uint32_t tail= , uint32_t size) "n=3D%p i=3D%d head=3D0x%"PRIx32" tail=3D0x%"PRIx32" size= =3D0x%"PRIx32"" +pci_nvme_post_load_aer(uint8_t typ, uint8_t info, uint8_t log_page) "type = 0x%"PRIx8" info 0x%"PRIx8" lid 0x%"PRIx8"" pci_nvme_map_sgl(uint8_t typ, uint64_t len) "type 0x%"PRIx8" len %"PRIu64"" pci_nvme_io_cmd(uint16_t cid, uint32_t nsid, uint16_t sqid, uint8_t opcode= , const char *opname) "cid %"PRIu16" nsid 0x%"PRIx32" sqid %"PRIu16" opc 0x= %"PRIx8" opname '%s'" pci_nvme_admin_cmd(uint16_t cid, uint16_t sqid, uint8_t opcode, const char= *opname) "cid %"PRIu16" sqid %"PRIu16" opc 0x%"PRIx8" opname '%s'" --=20 2.47.3 From nobody Tue Apr 7 11:18:24 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=mihalicyn.com ARC-Seal: i=1; a=rsa-sha256; t=1773411232; cv=none; d=zohomail.com; s=zohoarc; b=fH8MXxTno7SrOZyxi8ZDgt3yXoBBDfavixdywV9AQeksFQa0pAXyAYUGLzliSzynLktlSKw7RcaM+ONjbaZjWcyYwAabvZ4liMgKQcVGMqstw/HwDDKklnGbwOYQsHfsvIxG6JFUG6Ld4Dw7sdvRoe09fcWzcKsBK+0oIoVaAwc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773411232; 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=sBpcKOoEk+DQ0qOwh5vSLzEuS5T0qQxiJFTEUttDnck=; b=RBmy3+uPV9BmeVb/QO0C+xNBYKmBQ42mcNaBvzY1fFkY1QcVTstbDC5sLSy8jR7Dw6LditWgZ8XP1jSl399qZvqZPjaxC7a5TKPwdsRLt9gJz9kly5B2a5PydDUwWzVI1pEwCC0ixfDjr4Blg5ovrrfY6OvmAA5PgIFbx9QcBDs= 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 1773411232420151.38938056362315; Fri, 13 Mar 2026 07:13:52 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1w13GM-0005WM-RH; Fri, 13 Mar 2026 10:13:16 -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 1w13Fs-0005QJ-H7 for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:51 -0400 Received: from mail-wr1-x42f.google.com ([2a00:1450:4864:20::42f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1w13Fl-0006Ox-RB for qemu-devel@nongnu.org; Fri, 13 Mar 2026 10:12:44 -0400 Received: by mail-wr1-x42f.google.com with SMTP id ffacd0b85a97d-439ce3605ecso1759366f8f.0 for ; Fri, 13 Mar 2026 07:12:37 -0700 (PDT) Received: from alex-laptop.lan (p200300cf57228c00321ddb23f48fbb73.dip0.t-ipconnect.de. [2003:cf:5722:8c00:321d:db23:f48f:bb73]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe19ad9asm18580939f8f.7.2026.03.13.07.12.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 13 Mar 2026 07:12:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mihalicyn.com; s=mihalicyn; t=1773411156; x=1774015956; 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=sBpcKOoEk+DQ0qOwh5vSLzEuS5T0qQxiJFTEUttDnck=; b=N4wiHlzvFI4x7gBhsUgkMqiXESy1kAKjuIzzPrdOkopcIBhZKJgCXqth/YW000di0H iIlVkCVm3DvPckFt0GV8sfLCGsfwa9sv0F/3toofJ0t5oMOmxFw5yo7kIQxbUqPjJLES +/R7dwLv8ffs/Thq1uY4FYsnmjWdAOXyDK8zc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773411156; x=1774015956; 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=sBpcKOoEk+DQ0qOwh5vSLzEuS5T0qQxiJFTEUttDnck=; b=YXYEWwsCDFotAbEa8bKUoKJlsleY74Dx6n6I4hxXLgvX3W19i8ttq5vCO1eEfvpDV0 83BZGH1ghbYqdHgn4/4bmIpwhePJ+sKhwHQF+XN+PgonUFATApVyOq27Z9w4OmQnvC/j aK2JElfveiKsoxkWjRz1fgOwwR+ZKECJ319CrzgA1zCLP6PhuIl9cUmsAMj4bdPqLX6y gEUlokXw5yHUiWh0vnrjp0sKUMQhAFIAYeepBPo+ATCeZqtSB9QfSGGpHTdd65mPwWkJ xeb0dg3/ICqP3xUx6r+Mr0eBTh0h7Z4RrzqTLF0LbjNQu32lsKpfQG5JP6+LxNUcW+c2 sM7Q== X-Gm-Message-State: AOJu0YxDyu4dKfQNL1CKqFObgvgIgg7mH2AzK5dg4xH/MDhRAmHE5kgB tkyLfoUIrmkv42KuRNk05j2tiS8KsJ15mcddcHLZrbJ3WAydIMTIB7MSLWXafr4XAHyPAlBSZF+ /UaefaJA= X-Gm-Gg: ATEYQzxSnycpAgRY8Zy5FQFAhGQ7kpVycIPIH7JC6lTYw6pjAYJ1AakMFzFEwbyv7N7 Bs8KfXF3VTeAblkCjE8c2tfygm+3Sb9ENmjENjdk2NE3pdQbMN502P7wldP+7sk9Jbn6TPp4RZm lsMRStNc9sIFykMvH2Aec/Z5MDxvgKvO60u7F/wSUurrWx4w6DNHOOFoi1PTLDVx5MrpOpGABCq 1NSmsEDypK8EagXW48CiUh7zG5+hZUfBPyG+2KnWggc4tJRc4lzwAMTT4i//vLIaaqAjt+/mT4V zJ1Sj3tsCbpjJB8PpbaVzub6cenu4CPvF5RmtvX44eNQMPYgHzEb8wF5QrpP+YFvjoLIJUkO1H4 B7IXBsfF0gVrWxvBVcCTpFz2cJSbTVitBbDPVYHJQgzOM+P1KvWAM39qh7wRHc2RvXlf1mg0Jzr XHbhzl5pj11JfFbfYWg2PnfaUyHaLhV7yooILZsyv2rDikQHoa1aTgfKl5ZUAv4lLKCoJZiXlQ/ 552dAiNLShmFi6PoJLn4V++QHFyZ0AFbg== X-Received: by 2002:a05:6000:2c07:b0:439:d923:c20a with SMTP id ffacd0b85a97d-43a04dbd550mr6351064f8f.49.1773411156203; Fri, 13 Mar 2026 07:12:36 -0700 (PDT) From: Alexander Mikhalitsyn To: qemu-devel@nongnu.org Cc: Peter Xu , Jesper Devantier , Kevin Wolf , Fabiano Rosas , Paolo Bonzini , Hanna Reitz , qemu-block@nongnu.org, =?UTF-8?q?St=C3=A9phane=20Graber?= , Keith Busch , Stefan Hajnoczi , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Alexander Mikhalitsyn , Fam Zheng , Klaus Jensen , Zhao Liu , Alexander Mikhalitsyn Subject: [PATCH v4 8/8] tests/functional/x86_64: add migration test for NVMe device Date: Fri, 13 Mar 2026 15:12:21 +0100 Message-ID: <20260313141221.359503-9-alexander@mihalicyn.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260313141221.359503-1-alexander@mihalicyn.com> References: <20260313141221.359503-1-alexander@mihalicyn.com> 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::42f; envelope-from=alexander@mihalicyn.com; helo=mail-wr1-x42f.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.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, RCVD_IN_DNSWL_NONE=-0.0001, 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 @mihalicyn.com) X-ZM-MESSAGEID: 1773411233986158500 Content-Type: text/plain; charset="utf-8" From: Alexander Mikhalitsyn Introduce a very simple test to ensure that NVMe device migration works fine. Test plan is simple: 1. prepare VM with NVMe device 2. run workload that produces relatively heavy IO on the device 3. migrate VM 4. ensure that workload is alive and finishes without errors Test can be run as simple as: $ meson test 'func-x86_64-nvme_migration' --setup thorough -C build In the future we can extend this approach, and introduce some fio-based tests. And probably, it makes sense to make this test to apply not only to NVMe device, but also virtio-{blk,scsi}, ide, sata and other migratable devices. Signed-off-by: Alexander Mikhalitsyn --- tests/functional/x86_64/meson.build | 1 + .../functional/x86_64/test_nvme_migration.py | 159 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100755 tests/functional/x86_64/test_nvme_migration.py diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/= meson.build index 1ed10ad6c29..fd77f19d726 100644 --- a/tests/functional/x86_64/meson.build +++ b/tests/functional/x86_64/meson.build @@ -37,6 +37,7 @@ tests_x86_64_system_thorough =3D [ 'linux_initrd', 'multiprocess', 'netdev_ethtool', + 'nvme_migration', 'replay', 'reverse_debug', 'tuxrun', diff --git a/tests/functional/x86_64/test_nvme_migration.py b/tests/functio= nal/x86_64/test_nvme_migration.py new file mode 100755 index 00000000000..3788a8e3473 --- /dev/null +++ b/tests/functional/x86_64/test_nvme_migration.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# x86_64 NVMe migration test + +from migration import MigrationTest +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command, exec_command_and_wait_for_pattern + + +class X8664NVMeMigrationTest(MigrationTest): + ASSET_KERNEL =3D Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/rele= ases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD =3D Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/rele= ases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + ASSET_DISKIMAGE =3D Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/rele= ases' + '/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'), + 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0') + + DEFAULT_KERNEL_PARAMS =3D ('root=3D/dev/nvme0n1p1 console=3DttyS0 net.= ifnames=3D0 ' + 'rd.rescue quiet') + + def wait_for_console_pattern(self, success_message, vm): + wait_for_console_pattern( + self, + success_message, + failure_message=3D"Kernel panic - not syncing", + vm=3Dvm, + ) + + def exec_command_and_check(self, command, vm): + prompt =3D '# ' + exec_command_and_wait_for_pattern(self, + f"{command} && echo OK || echo FAI= L", + 'FAIL', vm=3Dvm) + # Note, that commands we send to the console are echo-ed back, so = if we have a word "FAIL" + # in the command itself, we should expect to see it once. + wait_for_console_pattern(self, 'OK', failure_message=3D"FAIL", vm= =3Dvm) + self.wait_for_console_pattern(prompt, vm) + + def configure_machine(self, vm): + kernel_path =3D self.ASSET_KERNEL.fetch() + initrd_path =3D self.ASSET_INITRD.fetch() + diskimage_path =3D self.ASSET_DISKIMAGE.fetch() + + vm.set_console() + vm.add_args("-cpu", "max") + vm.add_args("-m", "2G") + vm.add_args("-accel", "kvm") + + vm.add_args('-drive', + f'file=3D{diskimage_path},if=3Dnone,id=3Ddrv0,sna= pshot=3Don') + vm.add_args('-device', 'nvme,bus=3Dpcie.0,' + + 'drive=3Ddrv0,id=3Dnvme-disk0,serial=3Dnvmemigrat= etest,bootindex=3D1') + + vm.add_args( + "-kernel", + kernel_path, + "-initrd", + initrd_path, + "-append", + self.DEFAULT_KERNEL_PARAMS + ) + + def launch_source_vm(self, vm): + vm.launch() + + self.wait_for_console_pattern('Entering emergency mode.', vm) + prompt =3D '# ' + self.wait_for_console_pattern(prompt, vm) + + # Synchronize on NVMe driver creating the root device + exec_command_and_wait_for_pattern(self, + "while ! (dmesg -c | grep nvme0n1:) ; do sleep 1 ;= done", + "nvme0n1", vm=3Dvm) + self.wait_for_console_pattern(prompt, vm) + + # prepare system + exec_command_and_wait_for_pattern(self, 'mount /dev/nvme0n1p1 /sys= root', + prompt, vm=3Dvm) + exec_command_and_wait_for_pattern(self, 'chroot /sysroot', + prompt, vm=3Dvm) + exec_command_and_wait_for_pattern(self, 'mount -t proc proc /proc', + prompt, vm=3Dvm) + exec_command_and_wait_for_pattern(self, 'mount -t sysfs sysfs /sys= ', + prompt, vm=3Dvm) + + # Run workload before migration to check if it continues to run pr= operly after migration + # + # Workload is simple: it continuously calculates checksums of all = files in /usr/bin + # to generate some I/O load on the NVMe disk and at the same time = it drops caches to + # make sure that we have some read I/O on the disk as well. + # If there are any issues with the migration of the NVMe device, w= e should see errors + # in dmesg and consequently in the workload log. + exec_command_and_wait_for_pattern(self, + "(while [ ! -f /tmp/test_nvme_migr= ation_workload.stop ]; do \ + rm -f /tmp/test_nvme_migration= _workload.iteration_finished; \ + echo 3 > /proc/sys/vm/drop_cac= hes; \ + find /usr/bin -type f -exec ck= sum {} \\;; \ + touch /tmp/test_nvme_migration= _workload.iteration_finished; \ + done) > /dev/null 2> /tmp/test_nvm= e_migration_workload.errors &", + prompt, vm=3Dvm) + exec_command_and_wait_for_pattern(self, 'echo $! > /tmp/test_nvme_= migration_workload.pid', + prompt, vm=3Dvm) + + # check if process is alive and running + self.exec_command_and_check("kill -0 $(cat /tmp/test_nvme_migratio= n_workload.pid)", vm) + + def assert_dest_vm(self, vm): + prompt =3D '# ' + + # check if process is alive and running after migration, if not - = fail the test + self.exec_command_and_check("kill -0 $(cat /tmp/test_nvme_migratio= n_workload.pid)", vm) + + # signal workload to stop + exec_command_and_wait_for_pattern(self, 'touch /tmp/test_nvme_migr= ation_workload.stop', + prompt, vm=3Dvm) + + # wait workload to finish, because we want to examine log to see i= f there are any errors + exec_command_and_wait_for_pattern(self, + "while [ ! -f /tmp/test_nvme_migra= tion_workload.iteration_finished ]; do sleep 1; done;", + prompt, vm=3Dvm) + + exec_command_and_wait_for_pattern(self, 'cat /tmp/test_nvme_migrat= ion_workload.errors', + prompt, vm=3Dvm) + + # fail the test if non-empty + self.exec_command_and_check("[ ! -s /tmp/test_nvme_migration_workl= oad.errors ]", vm) + + def test_migration_with_tcp_localhost(self): + self.set_machine('q35') + self.require_accelerator("kvm") + + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('q35') + self.require_accelerator("kvm") + + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('q35') + self.require_accelerator("kvm") + + self.migration_with_exec() + + +if __name__ =3D=3D '__main__': + MigrationTest.main() --=20 2.47.3