qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects
@ 2021-09-27  5:17 Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 01/16] hw/nvme: reattach subsystem namespaces on hotplug Klaus Jensen
                   ` (15 more replies)
  0 siblings, 16 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

v2
  * Use 'zns' instead of 'zoned' for various zns based function name
    prefixes (Keith)

  * Fix the intialization order and get rid of the machine done
    notifier (Kevin). This requires removing the 'attached-ctrls'
    parameter and instead controller just attaching to all namespaces in
    the subsystem. The example below in the cover letter has been
    updated to reflect this.

    Kevin just posted a series that would allow an 'attached-ns'
    list-style parameter on the device, so this functionality may be
    added in the future, but let's see if there is actually any request
    for this.

  * Added patch from Hannes to attach namespaces on hotplug (this is
    also out in non-RFC, but this depends on it, so including it here).

  * Added preliminary documentation on the new experimental setup.

Hi,

This is an attempt at adressing a bunch of issues that have presented
themselves since we added subsystem support. It's been brewing for a
while now.

Fundamentally, I've come to the conclusion that modeling namespaces and
subsystems as "devices" is wrong. They should have been user-creatable
objects. We've run into multiple issues with wrt. hotplugging due to how
namespaces hook up to the controller with a bus. The bus-based design
made a lot of sense when we didn't have subsystem support and it follows
the design of hw/scsi. But, the problem here is that the bus-based
design dictates a one parent relationship, and with shared namespaces,
that is just not true. If the namespaces are considered to have a single
parent, that parent is the subsystem, not any specific controller.

This series adds a set of experimental user-creatable objects:

  -object x-nvme-subsystem
  -object x-nvme-ns-nvm
  -object x-nvme-ns-zoned

It also adds a new controller device (-device x-nvme-ctrl) that supports
these new objects (and gets rid of a bunch of deprecated and confusing
parameters). Invoking with an nvme controller now becomes along these
lines:

  -object x-nvme-subsystem,id=nvme-subsys-0,subnqn=foo
  -drive  id=blk-nvm-1,file=nvm-1.img,if=none
  -object x-nvme-ns-nvm,id=nvme-ns-nvm-1,blockdev=blk-nvm-1,subsys=nvme-subsys-0
  -device x-nvme-ctrl,id=nvme-ctrl-0,serial=foo,subsys=nvme-subsys-0

This new approach has a bunch of benefits (other than just
fixing the hotplugging issues properly) - we also get support for some
nice introspection through some new dynamic properties.

  (qemu) qom-get /objects/nvme-subsys-0 controllers
  [
      "/machine/peripheral/nvme-ctrl-1",
      "/machine/peripheral/nvme-ctrl-0"
  ]

  (qemu) qom-get /objects/nvme-subsys-0 namespaces
  [
      "/objects/nvme-ns-nvm-1",
      "/objects/nvme-ns-zns-1"
  ]

  (qemu) qom-list /objects/nvme-ns-zns-1
  type (string)
  subsys (link<x-nvme-subsystem>)
  nsid (string)
  uuid (string)
  eui64 (string)
  blockdev (string)
  pi-first (bool)
  pi-type (NvmeProtInfoType)
  extended-lba (bool)
  metadata-size (uint16)
  lba-size (size)
  zone-descriptor-extension-size (size)
  zone-cross-read (bool)
  zone-max-open (uint32)
  zone-capacity (size)
  zone-size (size)
  zone-max-active (uint32)

  (qemu) qom-get /objects/nvme-ns-zns-1 pi-type
  "none"

  (qemu) qom-get /objects/nvme-ns-zns-1 eui64
  "52:54:00:17:67:a0:40:15"

  (qemu) qom-get /objects/nvme-ns-zns-1 zone-capacity
  12582912

The first patches in this series reorganize a bunch of structs to make
it easier to separate them in later patches. Then, it proceeds to hoist
the device states into separate structures such that we can reuse the
core logic in both the new objects and the existing devices. Thus, full
backwards compatibility is kept and the existing device all work as the
do prior to this series being applied. I have chosen to separate the nvm
and zoned namespace types in to individual objects. The core namespace
functionality is contained in an abstract (non user-creatable) x-nvme-ns
object and the x-nvme-ns-nvm object extends this and serves at the
parent of the x-nvme-ns-zoned object itself.

There are definitely an alternative to this approach - one that I've
previously discussed with Hannes (and other QEMU devs, thanks!), and
that would be to add the subsystem as a system bus device.

Cheers, Klaus

Hannes Reinecke (1):
  hw/nvme: reattach subsystem namespaces on hotplug

Klaus Jensen (15):
  hw/nvme: change nvme-ns 'shared' default
  hw/nvme: move dif/pi prototypes into dif.h
  hw/nvme: move zns helpers and types into zns.h
  hw/nvme: move zoned namespace members to separate struct
  hw/nvme: move nvm namespace members to separate struct
  hw/nvme: move BlockBackend to NvmeNamespaceNvm
  hw/nvme: hoist qdev state from namespace
  hw/nvme: hoist qdev state from subsystem
  hw/nvme: hoist qdev state from controller
  hw/nvme: add experimental object x-nvme-subsystem
  nvme: add structured type for nguid
  hw/nvme: add experimental abstract object x-nvme-ns
  hw/nvme: add experimental objects x-nvme-ns-{nvm,zoned}
  hw/nvme: add experimental device x-nvme-ctrl
  docs: add documentation for experimental nvme emulation

 docs/system/device-emulation.rst          |    1 +
 docs/system/devices/nvme-experimental.rst |  107 ++
 docs/system/devices/nvme.rst              |   24 +-
 hw/core/machine.c                         |    4 +-
 hw/nvme/ctrl.c                            | 1157 ++++++++++++---------
 hw/nvme/dif.c                             |  120 ++-
 hw/nvme/dif.h                             |   55 +
 hw/nvme/meson.build                       |    2 +-
 hw/nvme/ns-nvm.c                          |  354 +++++++
 hw/nvme/ns-zoned.c                        |  443 ++++++++
 hw/nvme/ns.c                              |  752 ++++++-------
 hw/nvme/nvm.h                             |   65 ++
 hw/nvme/nvme.h                            |  324 ++----
 hw/nvme/subsys.c                          |  240 ++++-
 hw/nvme/zns.h                             |  147 +++
 include/block/nvme.h                      |   11 +-
 qapi/qom.json                             |   82 ++
 softmmu/vl.c                              |    8 +
 18 files changed, 2711 insertions(+), 1185 deletions(-)
 create mode 100644 docs/system/devices/nvme-experimental.rst
 create mode 100644 hw/nvme/dif.h
 create mode 100644 hw/nvme/ns-nvm.c
 create mode 100644 hw/nvme/ns-zoned.c
 create mode 100644 hw/nvme/nvm.h
 create mode 100644 hw/nvme/zns.h

-- 
2.33.0



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 01/16] hw/nvme: reattach subsystem namespaces on hotplug
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 02/16] hw/nvme: change nvme-ns 'shared' default Klaus Jensen
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Hannes Reinecke <hare@suse.de>

With commit 5ffbaeed16 ("hw/nvme: fix controller hot unplugging")
namespaces get moved from the controller to the subsystem if one
is specified.
That keeps the namespaces alive after a controller hot-unplug, but
after a controller hotplug we have to reconnect the namespaces
from the subsystem to the controller.

Fixes: 5ffbaeed16 ("hw/nvme: fix controller hot unplugging")
Cc: Klaus Jensen <k.jensen@samsung.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
[k.jensen: only attach to shared and non-detached namespaces]
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/subsys.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c
index 93c35950d69d..6b2e4c975f5b 100644
--- a/hw/nvme/subsys.c
+++ b/hw/nvme/subsys.c
@@ -14,7 +14,7 @@
 int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp)
 {
     NvmeSubsystem *subsys = n->subsys;
-    int cntlid;
+    int cntlid, nsid;
 
     for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) {
         if (!subsys->ctrls[cntlid]) {
@@ -29,12 +29,20 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp)
 
     subsys->ctrls[cntlid] = n;
 
+    for (nsid = 1; nsid < ARRAY_SIZE(subsys->namespaces); nsid++) {
+        NvmeNamespace *ns = subsys->namespaces[nsid];
+        if (ns && ns->params.shared && !ns->params.detached) {
+            nvme_attach_ns(n, ns);
+        }
+    }
+
     return cntlid;
 }
 
 void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n)
 {
     subsys->ctrls[n->cntlid] = NULL;
+    n->cntlid = -1;
 }
 
 static void nvme_subsys_setup(NvmeSubsystem *subsys)
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 02/16] hw/nvme: change nvme-ns 'shared' default
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 01/16] hw/nvme: reattach subsystem namespaces on hotplug Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 03/16] hw/nvme: move dif/pi prototypes into dif.h Klaus Jensen
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Change namespaces to be shared namespaces by default (parameter
shared=on). Keep shared=off for older machine types.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 docs/system/devices/nvme.rst | 24 ++++++++++++++----------
 hw/core/machine.c            |  4 +++-
 hw/nvme/ns.c                 |  8 +-------
 3 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/docs/system/devices/nvme.rst b/docs/system/devices/nvme.rst
index bff72d1c24d0..a1c0db01f6d5 100644
--- a/docs/system/devices/nvme.rst
+++ b/docs/system/devices/nvme.rst
@@ -110,28 +110,32 @@ multipath I/O.
 This will create an NVM subsystem with two controllers. Having controllers
 linked to an ``nvme-subsys`` device allows additional ``nvme-ns`` parameters:
 
-``shared`` (default: ``off``)
+``shared`` (default: ``on`` since 6.2)
   Specifies that the namespace will be attached to all controllers in the
-  subsystem. If set to ``off`` (the default), the namespace will remain a
-  private namespace and may only be attached to a single controller at a time.
+  subsystem. If set to ``off``, the namespace will remain a private namespace
+  and may only be attached to a single controller at a time. Shared namespaces
+  are always automatically attached to all controllers (also when controllers
+  are hotplugged).
 
 ``detached`` (default: ``off``)
   If set to ``on``, the namespace will be be available in the subsystem, but
-  not attached to any controllers initially.
+  not attached to any controllers initially. A shared namespace with this set
+  to ``on`` will never be automatically attached to controllers.
 
 Thus, adding
 
 .. code-block:: console
 
    -drive file=nvm-1.img,if=none,id=nvm-1
-   -device nvme-ns,drive=nvm-1,nsid=1,shared=on
+   -device nvme-ns,drive=nvm-1,nsid=1
    -drive file=nvm-2.img,if=none,id=nvm-2
-   -device nvme-ns,drive=nvm-2,nsid=3,detached=on
+   -device nvme-ns,drive=nvm-2,nsid=3,shared=off,detached=on
 
-will cause NSID 1 will be a shared namespace (due to ``shared=on``) that is
-initially attached to both controllers. NSID 3 will be a private namespace
-(i.e. only attachable to a single controller at a time) and will not be
-attached to any controller initially (due to ``detached=on``).
+will cause NSID 1 will be a shared namespace that is initially attached to both
+controllers. NSID 3 will be a private namespace due to ``shared=off`` and only
+attachable to a single controller at a time. Additionally it will not be
+attached to any controller initially (due to ``detached=on``) or to hotplugged
+controllers.
 
 Optional Features
 =================
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 067f42b528fd..5e2fa3e392b9 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -37,7 +37,9 @@
 #include "hw/virtio/virtio.h"
 #include "hw/virtio/virtio-pci.h"
 
-GlobalProperty hw_compat_6_1[] = {};
+GlobalProperty hw_compat_6_1[] = {
+    { "nvme-ns", "shared", "off" },
+};
 const size_t hw_compat_6_1_len = G_N_ELEMENTS(hw_compat_6_1);
 
 GlobalProperty hw_compat_6_0[] = {
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index b7cf1494e75b..8b5f98c76180 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -465,12 +465,6 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
                        "linked to an nvme-subsys device");
             return;
         }
-
-        if (ns->params.shared) {
-            error_setg(errp, "shared requires that the nvme device is "
-                       "linked to an nvme-subsys device");
-            return;
-        }
     } else {
         /*
          * If this namespace belongs to a subsystem (through a link on the
@@ -532,7 +526,7 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
 static Property nvme_ns_props[] = {
     DEFINE_BLOCK_PROPERTIES(NvmeNamespace, blkconf),
     DEFINE_PROP_BOOL("detached", NvmeNamespace, params.detached, false),
-    DEFINE_PROP_BOOL("shared", NvmeNamespace, params.shared, false),
+    DEFINE_PROP_BOOL("shared", NvmeNamespace, params.shared, true),
     DEFINE_PROP_UINT32("nsid", NvmeNamespace, params.nsid, 0),
     DEFINE_PROP_UUID("uuid", NvmeNamespace, params.uuid),
     DEFINE_PROP_UINT64("eui64", NvmeNamespace, params.eui64, 0),
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 03/16] hw/nvme: move dif/pi prototypes into dif.h
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 01/16] hw/nvme: reattach subsystem namespaces on hotplug Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 02/16] hw/nvme: change nvme-ns 'shared' default Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 04/16] hw/nvme: move zns helpers and types into zns.h Klaus Jensen
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c |  1 +
 hw/nvme/dif.c  |  1 +
 hw/nvme/dif.h  | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/nvme/nvme.h | 50 -----------------------------------------------
 4 files changed, 55 insertions(+), 50 deletions(-)
 create mode 100644 hw/nvme/dif.h

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 2f247a9275ca..b148e1dbb148 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -163,6 +163,7 @@
 #include "migration/vmstate.h"
 
 #include "nvme.h"
+#include "dif.h"
 #include "trace.h"
 
 #define NVME_MAX_IOQPAIRS 0xffff
diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c
index 5dbd18b2a4a5..cd0cea2b5ebd 100644
--- a/hw/nvme/dif.c
+++ b/hw/nvme/dif.c
@@ -13,6 +13,7 @@
 #include "sysemu/block-backend.h"
 
 #include "nvme.h"
+#include "dif.h"
 #include "trace.h"
 
 uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
diff --git a/hw/nvme/dif.h b/hw/nvme/dif.h
new file mode 100644
index 000000000000..e36fea30e71e
--- /dev/null
+++ b/hw/nvme/dif.h
@@ -0,0 +1,53 @@
+#ifndef HW_NVME_DIF_H
+#define HW_NVME_DIF_H
+
+/* from Linux kernel (crypto/crct10dif_common.c) */
+static const uint16_t t10_dif_crc_table[256] = {
+    0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B,
+    0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6,
+    0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6,
+    0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B,
+    0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1,
+    0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C,
+    0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C,
+    0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781,
+    0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8,
+    0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255,
+    0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925,
+    0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698,
+    0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472,
+    0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF,
+    0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF,
+    0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02,
+    0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA,
+    0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067,
+    0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17,
+    0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA,
+    0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640,
+    0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD,
+    0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D,
+    0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30,
+    0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759,
+    0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4,
+    0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394,
+    0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29,
+    0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3,
+    0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E,
+    0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E,
+    0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
+};
+
+uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
+                           uint32_t reftag);
+uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
+                               uint64_t slba);
+void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
+                                 uint8_t *mbuf, size_t mlen, uint16_t apptag,
+                                 uint32_t *reftag);
+uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
+                        uint8_t *mbuf, size_t mlen, uint8_t prinfo,
+                        uint64_t slba, uint16_t apptag,
+                        uint16_t appmask, uint32_t *reftag);
+uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req);
+
+#endif /* HW_NVME_DIF_H */
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 83ffabade4cf..45bf96d65321 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -503,54 +503,4 @@ void nvme_rw_complete_cb(void *opaque, int ret);
 uint16_t nvme_map_dptr(NvmeCtrl *n, NvmeSg *sg, size_t len,
                        NvmeCmd *cmd);
 
-/* from Linux kernel (crypto/crct10dif_common.c) */
-static const uint16_t t10_dif_crc_table[256] = {
-    0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B,
-    0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6,
-    0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6,
-    0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B,
-    0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1,
-    0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C,
-    0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C,
-    0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781,
-    0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8,
-    0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255,
-    0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925,
-    0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698,
-    0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472,
-    0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF,
-    0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF,
-    0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02,
-    0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA,
-    0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067,
-    0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17,
-    0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA,
-    0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640,
-    0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD,
-    0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D,
-    0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30,
-    0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759,
-    0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4,
-    0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394,
-    0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29,
-    0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3,
-    0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E,
-    0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E,
-    0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
-};
-
-uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
-                           uint32_t reftag);
-uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
-                               uint64_t slba);
-void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
-                                 uint8_t *mbuf, size_t mlen, uint16_t apptag,
-                                 uint32_t *reftag);
-uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
-                        uint8_t *mbuf, size_t mlen, uint8_t prinfo,
-                        uint64_t slba, uint16_t apptag,
-                        uint16_t appmask, uint32_t *reftag);
-uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req);
-
-
 #endif /* HW_NVME_INTERNAL_H */
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 04/16] hw/nvme: move zns helpers and types into zns.h
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (2 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 03/16] hw/nvme: move dif/pi prototypes into dif.h Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 05/16] hw/nvme: move zoned namespace members to separate struct Klaus Jensen
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Move ZNS related helpers and types into zns.h. Use a common prefix
(nvme_zoned or nvme_ns_zoned) for zns related functions.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c | 110 +++++++++++++++++++++----------------------------
 hw/nvme/ns.c   |  47 ++++++++++-----------
 hw/nvme/nvme.h |  72 --------------------------------
 hw/nvme/zns.h  |  97 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 169 insertions(+), 157 deletions(-)
 create mode 100644 hw/nvme/zns.h

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index b148e1dbb148..85937f57686c 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -164,6 +164,8 @@
 
 #include "nvme.h"
 #include "dif.h"
+#include "zns.h"
+
 #include "trace.h"
 
 #define NVME_MAX_IOQPAIRS 0xffff
@@ -258,11 +260,11 @@ static uint16_t nvme_sqid(NvmeRequest *req)
     return le16_to_cpu(req->sq->sqid);
 }
 
-static void nvme_assign_zone_state(NvmeNamespace *ns, NvmeZone *zone,
-                                   NvmeZoneState state)
+static void nvme_zns_assign_state(NvmeNamespace *ns, NvmeZone *zone,
+                                  NvmeZoneState state)
 {
     if (QTAILQ_IN_USE(zone, entry)) {
-        switch (nvme_get_zone_state(zone)) {
+        switch (nvme_zns_zs(zone)) {
         case NVME_ZONE_STATE_EXPLICITLY_OPEN:
             QTAILQ_REMOVE(&ns->exp_open_zones, zone, entry);
             break;
@@ -279,7 +281,7 @@ static void nvme_assign_zone_state(NvmeNamespace *ns, NvmeZone *zone,
         }
     }
 
-    nvme_set_zone_state(zone, state);
+    nvme_zns_set_zs(zone, state);
 
     switch (state) {
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
@@ -304,7 +306,8 @@ static void nvme_assign_zone_state(NvmeNamespace *ns, NvmeZone *zone,
  * Check if we can open a zone without exceeding open/active limits.
  * AOR stands for "Active and Open Resources" (see TP 4053 section 2.5).
  */
-static int nvme_aor_check(NvmeNamespace *ns, uint32_t act, uint32_t opn)
+static int nvme_zns_aor_check(NvmeNamespace *ns, uint32_t act,
+                                   uint32_t opn)
 {
     if (ns->params.max_active_zones != 0 &&
         ns->nr_active_zones + act > ns->params.max_active_zones) {
@@ -1552,28 +1555,11 @@ static void nvme_aio_err(NvmeRequest *req, int ret)
     req->status = status;
 }
 
-static inline uint32_t nvme_zone_idx(NvmeNamespace *ns, uint64_t slba)
-{
-    return ns->zone_size_log2 > 0 ? slba >> ns->zone_size_log2 :
-                                    slba / ns->zone_size;
-}
-
-static inline NvmeZone *nvme_get_zone_by_slba(NvmeNamespace *ns, uint64_t slba)
-{
-    uint32_t zone_idx = nvme_zone_idx(ns, slba);
-
-    if (zone_idx >= ns->num_zones) {
-        return NULL;
-    }
-
-    return &ns->zone_array[zone_idx];
-}
-
 static uint16_t nvme_check_zone_state_for_write(NvmeZone *zone)
 {
     uint64_t zslba = zone->d.zslba;
 
-    switch (nvme_get_zone_state(zone)) {
+    switch (nvme_zns_zs(zone)) {
     case NVME_ZONE_STATE_EMPTY:
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
@@ -1598,7 +1584,7 @@ static uint16_t nvme_check_zone_state_for_write(NvmeZone *zone)
 static uint16_t nvme_check_zone_write(NvmeNamespace *ns, NvmeZone *zone,
                                       uint64_t slba, uint32_t nlb)
 {
-    uint64_t zcap = nvme_zone_wr_boundary(zone);
+    uint64_t zcap = nvme_zns_write_boundary(zone);
     uint16_t status;
 
     status = nvme_check_zone_state_for_write(zone);
@@ -1621,7 +1607,7 @@ static uint16_t nvme_check_zone_write(NvmeNamespace *ns, NvmeZone *zone,
 
 static uint16_t nvme_check_zone_state_for_read(NvmeZone *zone)
 {
-    switch (nvme_get_zone_state(zone)) {
+    switch (nvme_zns_zs(zone)) {
     case NVME_ZONE_STATE_EMPTY:
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
@@ -1646,10 +1632,10 @@ static uint16_t nvme_check_zone_read(NvmeNamespace *ns, uint64_t slba,
     uint64_t bndry, end;
     uint16_t status;
 
-    zone = nvme_get_zone_by_slba(ns, slba);
+    zone = nvme_zns_get_by_slba(ns, slba);
     assert(zone);
 
-    bndry = nvme_zone_rd_boundary(ns, zone);
+    bndry = nvme_zns_read_boundary(ns, zone);
     end = slba + nlb;
 
     status = nvme_check_zone_state_for_read(zone);
@@ -1669,7 +1655,7 @@ static uint16_t nvme_check_zone_read(NvmeNamespace *ns, uint64_t slba,
                 if (status) {
                     break;
                 }
-            } while (end > nvme_zone_rd_boundary(ns, zone));
+            } while (end > nvme_zns_read_boundary(ns, zone));
         }
     }
 
@@ -1678,19 +1664,19 @@ static uint16_t nvme_check_zone_read(NvmeNamespace *ns, uint64_t slba,
 
 static uint16_t nvme_zrm_finish(NvmeNamespace *ns, NvmeZone *zone)
 {
-    switch (nvme_get_zone_state(zone)) {
+    switch (nvme_zns_zs(zone)) {
     case NVME_ZONE_STATE_FULL:
         return NVME_SUCCESS;
 
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
-        nvme_aor_dec_open(ns);
+        nvme_zns_aor_dec_open(ns);
         /* fallthrough */
     case NVME_ZONE_STATE_CLOSED:
-        nvme_aor_dec_active(ns);
+        nvme_zns_aor_dec_active(ns);
         /* fallthrough */
     case NVME_ZONE_STATE_EMPTY:
-        nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_FULL);
+        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_FULL);
         return NVME_SUCCESS;
 
     default:
@@ -1700,11 +1686,11 @@ static uint16_t nvme_zrm_finish(NvmeNamespace *ns, NvmeZone *zone)
 
 static uint16_t nvme_zrm_close(NvmeNamespace *ns, NvmeZone *zone)
 {
-    switch (nvme_get_zone_state(zone)) {
+    switch (nvme_zns_zs(zone)) {
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
-        nvme_aor_dec_open(ns);
-        nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_CLOSED);
+        nvme_zns_aor_dec_open(ns);
+        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_CLOSED);
         /* fall through */
     case NVME_ZONE_STATE_CLOSED:
         return NVME_SUCCESS;
@@ -1716,18 +1702,18 @@ static uint16_t nvme_zrm_close(NvmeNamespace *ns, NvmeZone *zone)
 
 static uint16_t nvme_zrm_reset(NvmeNamespace *ns, NvmeZone *zone)
 {
-    switch (nvme_get_zone_state(zone)) {
+    switch (nvme_zns_zs(zone)) {
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
-        nvme_aor_dec_open(ns);
+        nvme_zns_aor_dec_open(ns);
         /* fallthrough */
     case NVME_ZONE_STATE_CLOSED:
-        nvme_aor_dec_active(ns);
+        nvme_zns_aor_dec_active(ns);
         /* fallthrough */
     case NVME_ZONE_STATE_FULL:
         zone->w_ptr = zone->d.zslba;
         zone->d.wp = zone->w_ptr;
-        nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_EMPTY);
+        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_EMPTY);
         /* fallthrough */
     case NVME_ZONE_STATE_EMPTY:
         return NVME_SUCCESS;
@@ -1764,7 +1750,7 @@ static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespace *ns,
     int act = 0;
     uint16_t status;
 
-    switch (nvme_get_zone_state(zone)) {
+    switch (nvme_zns_zs(zone)) {
     case NVME_ZONE_STATE_EMPTY:
         act = 1;
 
@@ -1774,19 +1760,19 @@ static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespace *ns,
         if (n->params.auto_transition_zones) {
             nvme_zrm_auto_transition_zone(ns);
         }
-        status = nvme_aor_check(ns, act, 1);
+        status = nvme_zns_aor_check(ns, act, 1);
         if (status) {
             return status;
         }
 
         if (act) {
-            nvme_aor_inc_active(ns);
+            nvme_zns_aor_inc_active(ns);
         }
 
-        nvme_aor_inc_open(ns);
+        nvme_zns_aor_inc_open(ns);
 
         if (flags & NVME_ZRM_AUTO) {
-            nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_IMPLICITLY_OPEN);
+            nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_IMPLICITLY_OPEN);
             return NVME_SUCCESS;
         }
 
@@ -1797,7 +1783,7 @@ static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespace *ns,
             return NVME_SUCCESS;
         }
 
-        nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_EXPLICITLY_OPEN);
+        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_EXPLICITLY_OPEN);
 
         /* fallthrough */
 
@@ -1826,7 +1812,7 @@ static void nvme_advance_zone_wp(NvmeNamespace *ns, NvmeZone *zone,
 {
     zone->d.wp += nlb;
 
-    if (zone->d.wp == nvme_zone_wr_boundary(zone)) {
+    if (zone->d.wp == nvme_zns_write_boundary(zone)) {
         nvme_zrm_finish(ns, zone);
     }
 }
@@ -1840,7 +1826,7 @@ static void nvme_finalize_zoned_write(NvmeNamespace *ns, NvmeRequest *req)
 
     slba = le64_to_cpu(rw->slba);
     nlb = le16_to_cpu(rw->nlb) + 1;
-    zone = nvme_get_zone_by_slba(ns, slba);
+    zone = nvme_zns_get_by_slba(ns, slba);
     assert(zone);
 
     nvme_advance_zone_wp(ns, zone, nlb);
@@ -2821,7 +2807,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
     iocb->slba = le64_to_cpu(copy->sdlba);
 
     if (ns->params.zoned) {
-        iocb->zone = nvme_get_zone_by_slba(ns, iocb->slba);
+        iocb->zone = nvme_zns_get_by_slba(ns, iocb->slba);
         if (!iocb->zone) {
             status = NVME_LBA_RANGE | NVME_DNR;
             goto invalid;
@@ -3176,7 +3162,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
     }
 
     if (ns->params.zoned) {
-        zone = nvme_get_zone_by_slba(ns, slba);
+        zone = nvme_zns_get_by_slba(ns, slba);
         assert(zone);
 
         if (append) {
@@ -3297,7 +3283,7 @@ static uint16_t nvme_get_mgmt_zone_slba_idx(NvmeNamespace *ns, NvmeCmd *c,
         return NVME_LBA_RANGE | NVME_DNR;
     }
 
-    *zone_idx = nvme_zone_idx(ns, *slba);
+    *zone_idx = nvme_zns_zidx(ns, *slba);
     assert(*zone_idx < ns->num_zones);
 
     return NVME_SUCCESS;
@@ -3337,7 +3323,7 @@ static uint16_t nvme_offline_zone(NvmeNamespace *ns, NvmeZone *zone,
 {
     switch (state) {
     case NVME_ZONE_STATE_READ_ONLY:
-        nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_OFFLINE);
+        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_OFFLINE);
         /* fall through */
     case NVME_ZONE_STATE_OFFLINE:
         return NVME_SUCCESS;
@@ -3349,16 +3335,16 @@ static uint16_t nvme_offline_zone(NvmeNamespace *ns, NvmeZone *zone,
 static uint16_t nvme_set_zd_ext(NvmeNamespace *ns, NvmeZone *zone)
 {
     uint16_t status;
-    uint8_t state = nvme_get_zone_state(zone);
+    uint8_t state = nvme_zns_zs(zone);
 
     if (state == NVME_ZONE_STATE_EMPTY) {
-        status = nvme_aor_check(ns, 1, 0);
+        status = nvme_zns_aor_check(ns, 1, 0);
         if (status) {
             return status;
         }
-        nvme_aor_inc_active(ns);
+        nvme_zns_aor_inc_active(ns);
         zone->d.za |= NVME_ZA_ZD_EXT_VALID;
-        nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_CLOSED);
+        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_CLOSED);
         return NVME_SUCCESS;
     }
 
@@ -3370,7 +3356,7 @@ static uint16_t nvme_bulk_proc_zone(NvmeNamespace *ns, NvmeZone *zone,
                                     op_handler_t op_hndlr, NvmeRequest *req)
 {
     uint16_t status = NVME_SUCCESS;
-    NvmeZoneState zs = nvme_get_zone_state(zone);
+    NvmeZoneState zs = nvme_zns_zs(zone);
     bool proc_zone;
 
     switch (zs) {
@@ -3407,7 +3393,7 @@ static uint16_t nvme_do_zone_op(NvmeNamespace *ns, NvmeZone *zone,
     int i;
 
     if (!proc_mask) {
-        status = op_hndlr(ns, zone, nvme_get_zone_state(zone), req);
+        status = op_hndlr(ns, zone, nvme_zns_zs(zone), req);
     } else {
         if (proc_mask & NVME_PROC_CLOSED_ZONES) {
             QTAILQ_FOREACH_SAFE(zone, &ns->closed_zones, entry, next) {
@@ -3555,7 +3541,7 @@ static void nvme_zone_reset_cb(void *opaque, int ret)
     while (iocb->idx < ns->num_zones) {
         NvmeZone *zone = &ns->zone_array[iocb->idx++];
 
-        switch (nvme_get_zone_state(zone)) {
+        switch (nvme_zns_zs(zone)) {
         case NVME_ZONE_STATE_EMPTY:
             if (!iocb->all) {
                 goto done;
@@ -3682,7 +3668,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
         if (all || !ns->params.zd_extension_size) {
             return NVME_INVALID_FIELD | NVME_DNR;
         }
-        zd_ext = nvme_get_zd_extension(ns, zone_idx);
+        zd_ext = nvme_zns_zde(ns, zone_idx);
         status = nvme_h2c(n, zd_ext, ns->params.zd_extension_size, req);
         if (status) {
             trace_pci_nvme_err_zd_extension_map_error(zone_idx);
@@ -3714,7 +3700,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
 
 static bool nvme_zone_matches_filter(uint32_t zafs, NvmeZone *zl)
 {
-    NvmeZoneState zs = nvme_get_zone_state(zl);
+    NvmeZoneState zs = nvme_zns_zs(zl);
 
     switch (zafs) {
     case NVME_ZONE_REPORT_ALL:
@@ -3820,7 +3806,7 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req)
             z->zslba = cpu_to_le64(zone->d.zslba);
             z->za = zone->d.za;
 
-            if (nvme_wp_is_valid(zone)) {
+            if (nvme_zns_wp_valid(zone)) {
                 z->wp = cpu_to_le64(zone->d.wp);
             } else {
                 z->wp = cpu_to_le64(~0ULL);
@@ -3828,7 +3814,7 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req)
 
             if (zra == NVME_ZONE_REPORT_EXTENDED) {
                 if (zone->d.za & NVME_ZA_ZD_EXT_VALID) {
-                    memcpy(buf_p, nvme_get_zd_extension(ns, zone_idx),
+                    memcpy(buf_p, nvme_zns_zde(ns, zone_idx),
                            ns->params.zd_extension_size);
                 }
                 buf_p += ns->params.zd_extension_size;
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index 8b5f98c76180..471d1ddc016a 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -20,10 +20,11 @@
 #include "sysemu/block-backend.h"
 
 #include "nvme.h"
+#include "zns.h"
+
 #include "trace.h"
 
 #define MIN_DISCARD_GRANULARITY (4 * KiB)
-#define NVME_DEFAULT_ZONE_SIZE   (128 * MiB)
 
 void nvme_ns_init_format(NvmeNamespace *ns)
 {
@@ -164,7 +165,7 @@ static int nvme_ns_init_blk(NvmeNamespace *ns, Error **errp)
     return 0;
 }
 
-static int nvme_ns_zoned_check_calc_geometry(NvmeNamespace *ns, Error **errp)
+static int nvme_zns_check_calc_geometry(NvmeNamespace *ns, Error **errp)
 {
     uint64_t zone_size, zone_cap;
 
@@ -214,7 +215,7 @@ static int nvme_ns_zoned_check_calc_geometry(NvmeNamespace *ns, Error **errp)
     return 0;
 }
 
-static void nvme_ns_zoned_init_state(NvmeNamespace *ns)
+static void nvme_zns_init_state(NvmeNamespace *ns)
 {
     uint64_t start = 0, zone_size = ns->zone_size;
     uint64_t capacity = ns->num_zones * zone_size;
@@ -238,7 +239,7 @@ static void nvme_ns_zoned_init_state(NvmeNamespace *ns)
             zone_size = capacity - start;
         }
         zone->d.zt = NVME_ZONE_TYPE_SEQ_WRITE;
-        nvme_set_zone_state(zone, NVME_ZONE_STATE_EMPTY);
+        nvme_zns_set_zs(zone, NVME_ZONE_STATE_EMPTY);
         zone->d.za = 0;
         zone->d.zcap = ns->zone_capacity;
         zone->d.zslba = start;
@@ -253,12 +254,12 @@ static void nvme_ns_zoned_init_state(NvmeNamespace *ns)
     }
 }
 
-static void nvme_ns_init_zoned(NvmeNamespace *ns)
+static void nvme_zns_init(NvmeNamespace *ns)
 {
     NvmeIdNsZoned *id_ns_z;
     int i;
 
-    nvme_ns_zoned_init_state(ns);
+    nvme_zns_init_state(ns);
 
     id_ns_z = g_malloc0(sizeof(NvmeIdNsZoned));
 
@@ -298,49 +299,49 @@ static void nvme_ns_init_zoned(NvmeNamespace *ns)
     ns->id_ns_zoned = id_ns_z;
 }
 
-static void nvme_clear_zone(NvmeNamespace *ns, NvmeZone *zone)
+static void nvme_zns_clear_zone(NvmeNamespace *ns, NvmeZone *zone)
 {
     uint8_t state;
 
     zone->w_ptr = zone->d.wp;
-    state = nvme_get_zone_state(zone);
+    state = nvme_zns_zs(zone);
     if (zone->d.wp != zone->d.zslba ||
         (zone->d.za & NVME_ZA_ZD_EXT_VALID)) {
         if (state != NVME_ZONE_STATE_CLOSED) {
             trace_pci_nvme_clear_ns_close(state, zone->d.zslba);
-            nvme_set_zone_state(zone, NVME_ZONE_STATE_CLOSED);
+            nvme_zns_set_zs(zone, NVME_ZONE_STATE_CLOSED);
         }
-        nvme_aor_inc_active(ns);
+        nvme_zns_aor_inc_active(ns);
         QTAILQ_INSERT_HEAD(&ns->closed_zones, zone, entry);
     } else {
         trace_pci_nvme_clear_ns_reset(state, zone->d.zslba);
-        nvme_set_zone_state(zone, NVME_ZONE_STATE_EMPTY);
+        nvme_zns_set_zs(zone, NVME_ZONE_STATE_EMPTY);
     }
 }
 
 /*
  * Close all the zones that are currently open.
  */
-static void nvme_zoned_ns_shutdown(NvmeNamespace *ns)
+static void nvme_zns_shutdown(NvmeNamespace *ns)
 {
     NvmeZone *zone, *next;
 
     QTAILQ_FOREACH_SAFE(zone, &ns->closed_zones, entry, next) {
         QTAILQ_REMOVE(&ns->closed_zones, zone, entry);
-        nvme_aor_dec_active(ns);
-        nvme_clear_zone(ns, zone);
+        nvme_zns_aor_dec_active(ns);
+        nvme_zns_clear_zone(ns, zone);
     }
     QTAILQ_FOREACH_SAFE(zone, &ns->imp_open_zones, entry, next) {
         QTAILQ_REMOVE(&ns->imp_open_zones, zone, entry);
-        nvme_aor_dec_open(ns);
-        nvme_aor_dec_active(ns);
-        nvme_clear_zone(ns, zone);
+        nvme_zns_aor_dec_open(ns);
+        nvme_zns_aor_dec_active(ns);
+        nvme_zns_clear_zone(ns, zone);
     }
     QTAILQ_FOREACH_SAFE(zone, &ns->exp_open_zones, entry, next) {
         QTAILQ_REMOVE(&ns->exp_open_zones, zone, entry);
-        nvme_aor_dec_open(ns);
-        nvme_aor_dec_active(ns);
-        nvme_clear_zone(ns, zone);
+        nvme_zns_aor_dec_open(ns);
+        nvme_zns_aor_dec_active(ns);
+        nvme_zns_clear_zone(ns, zone);
     }
 
     assert(ns->nr_open_zones == 0);
@@ -410,10 +411,10 @@ int nvme_ns_setup(NvmeNamespace *ns, Error **errp)
         return -1;
     }
     if (ns->params.zoned) {
-        if (nvme_ns_zoned_check_calc_geometry(ns, errp) != 0) {
+        if (nvme_zns_check_calc_geometry(ns, errp) != 0) {
             return -1;
         }
-        nvme_ns_init_zoned(ns);
+        nvme_zns_init(ns);
     }
 
     return 0;
@@ -428,7 +429,7 @@ void nvme_ns_shutdown(NvmeNamespace *ns)
 {
     blk_flush(ns->blkconf.blk);
     if (ns->params.zoned) {
-        nvme_zoned_ns_shutdown(ns);
+        nvme_zns_shutdown(ns);
     }
 }
 
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 45bf96d65321..99d8b9066cc9 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -182,78 +182,6 @@ static inline bool nvme_ns_ext(NvmeNamespace *ns)
     return !!NVME_ID_NS_FLBAS_EXTENDED(ns->id_ns.flbas);
 }
 
-static inline NvmeZoneState nvme_get_zone_state(NvmeZone *zone)
-{
-    return zone->d.zs >> 4;
-}
-
-static inline void nvme_set_zone_state(NvmeZone *zone, NvmeZoneState state)
-{
-    zone->d.zs = state << 4;
-}
-
-static inline uint64_t nvme_zone_rd_boundary(NvmeNamespace *ns, NvmeZone *zone)
-{
-    return zone->d.zslba + ns->zone_size;
-}
-
-static inline uint64_t nvme_zone_wr_boundary(NvmeZone *zone)
-{
-    return zone->d.zslba + zone->d.zcap;
-}
-
-static inline bool nvme_wp_is_valid(NvmeZone *zone)
-{
-    uint8_t st = nvme_get_zone_state(zone);
-
-    return st != NVME_ZONE_STATE_FULL &&
-           st != NVME_ZONE_STATE_READ_ONLY &&
-           st != NVME_ZONE_STATE_OFFLINE;
-}
-
-static inline uint8_t *nvme_get_zd_extension(NvmeNamespace *ns,
-                                             uint32_t zone_idx)
-{
-    return &ns->zd_extensions[zone_idx * ns->params.zd_extension_size];
-}
-
-static inline void nvme_aor_inc_open(NvmeNamespace *ns)
-{
-    assert(ns->nr_open_zones >= 0);
-    if (ns->params.max_open_zones) {
-        ns->nr_open_zones++;
-        assert(ns->nr_open_zones <= ns->params.max_open_zones);
-    }
-}
-
-static inline void nvme_aor_dec_open(NvmeNamespace *ns)
-{
-    if (ns->params.max_open_zones) {
-        assert(ns->nr_open_zones > 0);
-        ns->nr_open_zones--;
-    }
-    assert(ns->nr_open_zones >= 0);
-}
-
-static inline void nvme_aor_inc_active(NvmeNamespace *ns)
-{
-    assert(ns->nr_active_zones >= 0);
-    if (ns->params.max_active_zones) {
-        ns->nr_active_zones++;
-        assert(ns->nr_active_zones <= ns->params.max_active_zones);
-    }
-}
-
-static inline void nvme_aor_dec_active(NvmeNamespace *ns)
-{
-    if (ns->params.max_active_zones) {
-        assert(ns->nr_active_zones > 0);
-        ns->nr_active_zones--;
-        assert(ns->nr_active_zones >= ns->nr_open_zones);
-    }
-    assert(ns->nr_active_zones >= 0);
-}
-
 void nvme_ns_init_format(NvmeNamespace *ns);
 int nvme_ns_setup(NvmeNamespace *ns, Error **errp);
 void nvme_ns_drain(NvmeNamespace *ns);
diff --git a/hw/nvme/zns.h b/hw/nvme/zns.h
new file mode 100644
index 000000000000..609db6eda7e5
--- /dev/null
+++ b/hw/nvme/zns.h
@@ -0,0 +1,97 @@
+#ifndef HW_NVME_ZONED_H
+#define HW_NVME_ZONED_H
+
+#include "qemu/units.h"
+
+#include "nvme.h"
+
+#define NVME_DEFAULT_ZONE_SIZE   (128 * MiB)
+
+static inline NvmeZoneState nvme_zns_zs(NvmeZone *zone)
+{
+    return zone->d.zs >> 4;
+}
+
+static inline void nvme_zns_set_zs(NvmeZone *zone, NvmeZoneState state)
+{
+    zone->d.zs = state << 4;
+}
+
+static inline uint64_t nvme_zns_read_boundary(NvmeNamespace *ns,
+                                              NvmeZone *zone)
+{
+    return zone->d.zslba + ns->zone_size;
+}
+
+static inline uint64_t nvme_zns_write_boundary(NvmeZone *zone)
+{
+    return zone->d.zslba + zone->d.zcap;
+}
+
+static inline bool nvme_zns_wp_valid(NvmeZone *zone)
+{
+    uint8_t st = nvme_zns_zs(zone);
+
+    return st != NVME_ZONE_STATE_FULL &&
+           st != NVME_ZONE_STATE_READ_ONLY &&
+           st != NVME_ZONE_STATE_OFFLINE;
+}
+
+static inline uint32_t nvme_zns_zidx(NvmeNamespace *ns, uint64_t slba)
+{
+    return ns->zone_size_log2 > 0 ? slba >> ns->zone_size_log2 :
+                                    slba / ns->zone_size;
+}
+
+static inline NvmeZone *nvme_zns_get_by_slba(NvmeNamespace *ns, uint64_t slba)
+{
+    uint32_t zone_idx = nvme_zns_zidx(ns, slba);
+
+    assert(zone_idx < ns->num_zones);
+    return &ns->zone_array[zone_idx];
+}
+
+static inline uint8_t *nvme_zns_zde(NvmeNamespace *ns, uint32_t zone_idx)
+{
+    return &ns->zd_extensions[zone_idx * ns->params.zd_extension_size];
+}
+
+static inline void nvme_zns_aor_inc_open(NvmeNamespace *ns)
+{
+    assert(ns->nr_open_zones >= 0);
+    if (ns->params.max_open_zones) {
+        ns->nr_open_zones++;
+        assert(ns->nr_open_zones <= ns->params.max_open_zones);
+    }
+}
+
+static inline void nvme_zns_aor_dec_open(NvmeNamespace *ns)
+{
+    if (ns->params.max_open_zones) {
+        assert(ns->nr_open_zones > 0);
+        ns->nr_open_zones--;
+    }
+    assert(ns->nr_open_zones >= 0);
+}
+
+static inline void nvme_zns_aor_inc_active(NvmeNamespace *ns)
+{
+    assert(ns->nr_active_zones >= 0);
+    if (ns->params.max_active_zones) {
+        ns->nr_active_zones++;
+        assert(ns->nr_active_zones <= ns->params.max_active_zones);
+    }
+}
+
+static inline void nvme_zns_aor_dec_active(NvmeNamespace *ns)
+{
+    if (ns->params.max_active_zones) {
+        assert(ns->nr_active_zones > 0);
+        ns->nr_active_zones--;
+        assert(ns->nr_active_zones >= ns->nr_open_zones);
+    }
+    assert(ns->nr_active_zones >= 0);
+}
+
+
+#endif /* HW_NVME_ZONED_H */
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 05/16] hw/nvme: move zoned namespace members to separate struct
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (3 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 04/16] hw/nvme: move zns helpers and types into zns.h Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 06/16] hw/nvme: move nvm " Klaus Jensen
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

In preparation for nvm and zoned namespace separation, move zoned
related members from NvmeNamespace into NvmeNamespaceZoned.

There are no functional changes here, basically just a
s/NvmeNamespace/NvmeNamespaceZoned and s/ns/zoned where applicable.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c       | 302 ++++++++++++++++++++++---------------------
 hw/nvme/ns.c         | 142 +++++++++++---------
 hw/nvme/nvme.h       |  66 +++++++---
 hw/nvme/zns.h        |  80 ++++++------
 include/block/nvme.h |   4 +
 5 files changed, 327 insertions(+), 267 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 85937f57686c..e357329d85b8 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -260,41 +260,41 @@ static uint16_t nvme_sqid(NvmeRequest *req)
     return le16_to_cpu(req->sq->sqid);
 }
 
-static void nvme_zns_assign_state(NvmeNamespace *ns, NvmeZone *zone,
+static void nvme_zns_assign_state(NvmeNamespaceZoned *zoned, NvmeZone *zone,
                                   NvmeZoneState state)
 {
     if (QTAILQ_IN_USE(zone, entry)) {
-        switch (nvme_zns_zs(zone)) {
+        switch (nvme_zns_state(zone)) {
         case NVME_ZONE_STATE_EXPLICITLY_OPEN:
-            QTAILQ_REMOVE(&ns->exp_open_zones, zone, entry);
+            QTAILQ_REMOVE(&zoned->exp_open_zones, zone, entry);
             break;
         case NVME_ZONE_STATE_IMPLICITLY_OPEN:
-            QTAILQ_REMOVE(&ns->imp_open_zones, zone, entry);
+            QTAILQ_REMOVE(&zoned->imp_open_zones, zone, entry);
             break;
         case NVME_ZONE_STATE_CLOSED:
-            QTAILQ_REMOVE(&ns->closed_zones, zone, entry);
+            QTAILQ_REMOVE(&zoned->closed_zones, zone, entry);
             break;
         case NVME_ZONE_STATE_FULL:
-            QTAILQ_REMOVE(&ns->full_zones, zone, entry);
+            QTAILQ_REMOVE(&zoned->full_zones, zone, entry);
         default:
             ;
         }
     }
 
-    nvme_zns_set_zs(zone, state);
+    nvme_zns_set_state(zone, state);
 
     switch (state) {
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
-        QTAILQ_INSERT_TAIL(&ns->exp_open_zones, zone, entry);
+        QTAILQ_INSERT_TAIL(&zoned->exp_open_zones, zone, entry);
         break;
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
-        QTAILQ_INSERT_TAIL(&ns->imp_open_zones, zone, entry);
+        QTAILQ_INSERT_TAIL(&zoned->imp_open_zones, zone, entry);
         break;
     case NVME_ZONE_STATE_CLOSED:
-        QTAILQ_INSERT_TAIL(&ns->closed_zones, zone, entry);
+        QTAILQ_INSERT_TAIL(&zoned->closed_zones, zone, entry);
         break;
     case NVME_ZONE_STATE_FULL:
-        QTAILQ_INSERT_TAIL(&ns->full_zones, zone, entry);
+        QTAILQ_INSERT_TAIL(&zoned->full_zones, zone, entry);
     case NVME_ZONE_STATE_READ_ONLY:
         break;
     default:
@@ -306,17 +306,17 @@ static void nvme_zns_assign_state(NvmeNamespace *ns, NvmeZone *zone,
  * Check if we can open a zone without exceeding open/active limits.
  * AOR stands for "Active and Open Resources" (see TP 4053 section 2.5).
  */
-static int nvme_zns_aor_check(NvmeNamespace *ns, uint32_t act,
-                                   uint32_t opn)
+static int nvme_zns_aor_check(NvmeNamespaceZoned *zoned, uint32_t act,
+                              uint32_t opn)
 {
-    if (ns->params.max_active_zones != 0 &&
-        ns->nr_active_zones + act > ns->params.max_active_zones) {
-        trace_pci_nvme_err_insuff_active_res(ns->params.max_active_zones);
+    if (zoned->max_active_zones != 0 &&
+        zoned->nr_active_zones + act > zoned->max_active_zones) {
+        trace_pci_nvme_err_insuff_active_res(zoned->max_active_zones);
         return NVME_ZONE_TOO_MANY_ACTIVE | NVME_DNR;
     }
-    if (ns->params.max_open_zones != 0 &&
-        ns->nr_open_zones + opn > ns->params.max_open_zones) {
-        trace_pci_nvme_err_insuff_open_res(ns->params.max_open_zones);
+    if (zoned->max_open_zones != 0 &&
+        zoned->nr_open_zones + opn > zoned->max_open_zones) {
+        trace_pci_nvme_err_insuff_open_res(zoned->max_open_zones);
         return NVME_ZONE_TOO_MANY_OPEN | NVME_DNR;
     }
 
@@ -1559,7 +1559,7 @@ static uint16_t nvme_check_zone_state_for_write(NvmeZone *zone)
 {
     uint64_t zslba = zone->d.zslba;
 
-    switch (nvme_zns_zs(zone)) {
+    switch (nvme_zns_state(zone)) {
     case NVME_ZONE_STATE_EMPTY:
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
@@ -1581,8 +1581,8 @@ static uint16_t nvme_check_zone_state_for_write(NvmeZone *zone)
     return NVME_INTERNAL_DEV_ERROR;
 }
 
-static uint16_t nvme_check_zone_write(NvmeNamespace *ns, NvmeZone *zone,
-                                      uint64_t slba, uint32_t nlb)
+static uint16_t nvme_check_zone_write(NvmeZone *zone, uint64_t slba,
+                                      uint32_t nlb)
 {
     uint64_t zcap = nvme_zns_write_boundary(zone);
     uint16_t status;
@@ -1607,7 +1607,7 @@ static uint16_t nvme_check_zone_write(NvmeNamespace *ns, NvmeZone *zone,
 
 static uint16_t nvme_check_zone_state_for_read(NvmeZone *zone)
 {
-    switch (nvme_zns_zs(zone)) {
+    switch (nvme_zns_state(zone)) {
     case NVME_ZONE_STATE_EMPTY:
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
@@ -1625,24 +1625,24 @@ static uint16_t nvme_check_zone_state_for_read(NvmeZone *zone)
     return NVME_INTERNAL_DEV_ERROR;
 }
 
-static uint16_t nvme_check_zone_read(NvmeNamespace *ns, uint64_t slba,
+static uint16_t nvme_check_zone_read(NvmeNamespaceZoned *zoned, uint64_t slba,
                                      uint32_t nlb)
 {
     NvmeZone *zone;
     uint64_t bndry, end;
     uint16_t status;
 
-    zone = nvme_zns_get_by_slba(ns, slba);
+    zone = nvme_zns_get_by_slba(zoned, slba);
     assert(zone);
 
-    bndry = nvme_zns_read_boundary(ns, zone);
+    bndry = nvme_zns_read_boundary(zoned, zone);
     end = slba + nlb;
 
     status = nvme_check_zone_state_for_read(zone);
     if (status) {
         ;
     } else if (unlikely(end > bndry)) {
-        if (!ns->params.cross_zone_read) {
+        if (!(zoned->flags & NVME_NS_ZONED_CROSS_READ)) {
             status = NVME_ZONE_BOUNDARY_ERROR;
         } else {
             /*
@@ -1655,28 +1655,28 @@ static uint16_t nvme_check_zone_read(NvmeNamespace *ns, uint64_t slba,
                 if (status) {
                     break;
                 }
-            } while (end > nvme_zns_read_boundary(ns, zone));
+            } while (end > nvme_zns_read_boundary(zoned, zone));
         }
     }
 
     return status;
 }
 
-static uint16_t nvme_zrm_finish(NvmeNamespace *ns, NvmeZone *zone)
+static uint16_t nvme_zrm_finish(NvmeNamespaceZoned *zoned, NvmeZone *zone)
 {
-    switch (nvme_zns_zs(zone)) {
+    switch (nvme_zns_state(zone)) {
     case NVME_ZONE_STATE_FULL:
         return NVME_SUCCESS;
 
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
-        nvme_zns_aor_dec_open(ns);
+        nvme_zns_aor_dec_open(zoned);
         /* fallthrough */
     case NVME_ZONE_STATE_CLOSED:
-        nvme_zns_aor_dec_active(ns);
+        nvme_zns_aor_dec_active(zoned);
         /* fallthrough */
     case NVME_ZONE_STATE_EMPTY:
-        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_FULL);
+        nvme_zns_assign_state(zoned, zone, NVME_ZONE_STATE_FULL);
         return NVME_SUCCESS;
 
     default:
@@ -1684,13 +1684,13 @@ static uint16_t nvme_zrm_finish(NvmeNamespace *ns, NvmeZone *zone)
     }
 }
 
-static uint16_t nvme_zrm_close(NvmeNamespace *ns, NvmeZone *zone)
+static uint16_t nvme_zrm_close(NvmeNamespaceZoned *zoned, NvmeZone *zone)
 {
-    switch (nvme_zns_zs(zone)) {
+    switch (nvme_zns_state(zone)) {
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
-        nvme_zns_aor_dec_open(ns);
-        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_CLOSED);
+        nvme_zns_aor_dec_open(zoned);
+        nvme_zns_assign_state(zoned, zone, NVME_ZONE_STATE_CLOSED);
         /* fall through */
     case NVME_ZONE_STATE_CLOSED:
         return NVME_SUCCESS;
@@ -1700,20 +1700,20 @@ static uint16_t nvme_zrm_close(NvmeNamespace *ns, NvmeZone *zone)
     }
 }
 
-static uint16_t nvme_zrm_reset(NvmeNamespace *ns, NvmeZone *zone)
+static uint16_t nvme_zrm_reset(NvmeNamespaceZoned *zoned, NvmeZone *zone)
 {
-    switch (nvme_zns_zs(zone)) {
+    switch (nvme_zns_state(zone)) {
     case NVME_ZONE_STATE_EXPLICITLY_OPEN:
     case NVME_ZONE_STATE_IMPLICITLY_OPEN:
-        nvme_zns_aor_dec_open(ns);
+        nvme_zns_aor_dec_open(zoned);
         /* fallthrough */
     case NVME_ZONE_STATE_CLOSED:
-        nvme_zns_aor_dec_active(ns);
+        nvme_zns_aor_dec_active(zoned);
         /* fallthrough */
     case NVME_ZONE_STATE_FULL:
         zone->w_ptr = zone->d.zslba;
         zone->d.wp = zone->w_ptr;
-        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_EMPTY);
+        nvme_zns_assign_state(zoned, zone, NVME_ZONE_STATE_EMPTY);
         /* fallthrough */
     case NVME_ZONE_STATE_EMPTY:
         return NVME_SUCCESS;
@@ -1723,19 +1723,19 @@ static uint16_t nvme_zrm_reset(NvmeNamespace *ns, NvmeZone *zone)
     }
 }
 
-static void nvme_zrm_auto_transition_zone(NvmeNamespace *ns)
+static void nvme_zrm_auto_transition_zone(NvmeNamespaceZoned *zoned)
 {
     NvmeZone *zone;
 
-    if (ns->params.max_open_zones &&
-        ns->nr_open_zones == ns->params.max_open_zones) {
-        zone = QTAILQ_FIRST(&ns->imp_open_zones);
+    if (zoned->max_open_zones &&
+        zoned->nr_open_zones == zoned->max_open_zones) {
+        zone = QTAILQ_FIRST(&zoned->imp_open_zones);
         if (zone) {
             /*
              * Automatically close this implicitly open zone.
              */
-            QTAILQ_REMOVE(&ns->imp_open_zones, zone, entry);
-            nvme_zrm_close(ns, zone);
+            QTAILQ_REMOVE(&zoned->imp_open_zones, zone, entry);
+            nvme_zrm_close(zoned, zone);
         }
     }
 }
@@ -1744,13 +1744,13 @@ enum {
     NVME_ZRM_AUTO = 1 << 0,
 };
 
-static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespace *ns,
+static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespaceZoned *zoned,
                                     NvmeZone *zone, int flags)
 {
     int act = 0;
     uint16_t status;
 
-    switch (nvme_zns_zs(zone)) {
+    switch (nvme_zns_state(zone)) {
     case NVME_ZONE_STATE_EMPTY:
         act = 1;
 
@@ -1758,21 +1758,21 @@ static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespace *ns,
 
     case NVME_ZONE_STATE_CLOSED:
         if (n->params.auto_transition_zones) {
-            nvme_zrm_auto_transition_zone(ns);
+            nvme_zrm_auto_transition_zone(zoned);
         }
-        status = nvme_zns_aor_check(ns, act, 1);
+        status = nvme_zns_aor_check(zoned, act, 1);
         if (status) {
             return status;
         }
 
         if (act) {
-            nvme_zns_aor_inc_active(ns);
+            nvme_zns_aor_inc_active(zoned);
         }
 
-        nvme_zns_aor_inc_open(ns);
+        nvme_zns_aor_inc_open(zoned);
 
         if (flags & NVME_ZRM_AUTO) {
-            nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_IMPLICITLY_OPEN);
+            nvme_zns_assign_state(zoned, zone, NVME_ZONE_STATE_IMPLICITLY_OPEN);
             return NVME_SUCCESS;
         }
 
@@ -1783,7 +1783,7 @@ static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespace *ns,
             return NVME_SUCCESS;
         }
 
-        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_EXPLICITLY_OPEN);
+        nvme_zns_assign_state(zoned, zone, NVME_ZONE_STATE_EXPLICITLY_OPEN);
 
         /* fallthrough */
 
@@ -1795,29 +1795,30 @@ static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespace *ns,
     }
 }
 
-static inline uint16_t nvme_zrm_auto(NvmeCtrl *n, NvmeNamespace *ns,
+static inline uint16_t nvme_zrm_auto(NvmeCtrl *n, NvmeNamespaceZoned *zoned,
                                      NvmeZone *zone)
 {
-    return nvme_zrm_open_flags(n, ns, zone, NVME_ZRM_AUTO);
+    return nvme_zrm_open_flags(n, zoned, zone, NVME_ZRM_AUTO);
 }
 
-static inline uint16_t nvme_zrm_open(NvmeCtrl *n, NvmeNamespace *ns,
+static inline uint16_t nvme_zrm_open(NvmeCtrl *n, NvmeNamespaceZoned *zoned,
                                      NvmeZone *zone)
 {
-    return nvme_zrm_open_flags(n, ns, zone, 0);
+    return nvme_zrm_open_flags(n, zoned, zone, 0);
 }
 
-static void nvme_advance_zone_wp(NvmeNamespace *ns, NvmeZone *zone,
+static void nvme_advance_zone_wp(NvmeNamespaceZoned *zoned, NvmeZone *zone,
                                  uint32_t nlb)
 {
     zone->d.wp += nlb;
 
     if (zone->d.wp == nvme_zns_write_boundary(zone)) {
-        nvme_zrm_finish(ns, zone);
+        nvme_zrm_finish(zoned, zone);
     }
 }
 
-static void nvme_finalize_zoned_write(NvmeNamespace *ns, NvmeRequest *req)
+static void nvme_finalize_zoned_write(NvmeNamespaceZoned *zoned,
+                                      NvmeRequest *req)
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeZone *zone;
@@ -1826,10 +1827,10 @@ static void nvme_finalize_zoned_write(NvmeNamespace *ns, NvmeRequest *req)
 
     slba = le64_to_cpu(rw->slba);
     nlb = le16_to_cpu(rw->nlb) + 1;
-    zone = nvme_zns_get_by_slba(ns, slba);
+    zone = nvme_zns_get_by_slba(zoned, slba);
     assert(zone);
 
-    nvme_advance_zone_wp(ns, zone, nlb);
+    nvme_advance_zone_wp(zoned, zone, nlb);
 }
 
 static inline bool nvme_is_write(NvmeRequest *req)
@@ -1876,8 +1877,8 @@ void nvme_rw_complete_cb(void *opaque, int ret)
         block_acct_done(stats, acct);
     }
 
-    if (ns->params.zoned && nvme_is_write(req)) {
-        nvme_finalize_zoned_write(ns, req);
+    if (nvme_ns_zoned(ns) && nvme_is_write(req)) {
+        nvme_finalize_zoned_write(NVME_NAMESPACE_ZONED(ns), req);
     }
 
     nvme_enqueue_req_completion(nvme_cq(req), req);
@@ -2503,8 +2504,8 @@ static void nvme_copy_out_completed_cb(void *opaque, int ret)
         goto out;
     }
 
-    if (ns->params.zoned) {
-        nvme_advance_zone_wp(ns, iocb->zone, nlb);
+    if (nvme_ns_zoned(ns)) {
+        nvme_advance_zone_wp(NVME_NAMESPACE_ZONED(ns), iocb->zone, nlb);
     }
 
     iocb->idx++;
@@ -2623,8 +2624,8 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
         goto invalid;
     }
 
-    if (ns->params.zoned) {
-        status = nvme_check_zone_write(ns, iocb->zone, iocb->slba, nlb);
+    if (nvme_ns_zoned(ns)) {
+        status = nvme_check_zone_write(iocb->zone, iocb->slba, nlb);
         if (status) {
             goto invalid;
         }
@@ -2737,8 +2738,8 @@ static void nvme_copy_cb(void *opaque, int ret)
         }
     }
 
-    if (ns->params.zoned) {
-        status = nvme_check_zone_read(ns, slba, nlb);
+    if (nvme_ns_zoned(ns)) {
+        status = nvme_check_zone_read(NVME_NAMESPACE_ZONED(ns), slba, nlb);
         if (status) {
             goto invalid;
         }
@@ -2806,14 +2807,16 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
 
     iocb->slba = le64_to_cpu(copy->sdlba);
 
-    if (ns->params.zoned) {
-        iocb->zone = nvme_zns_get_by_slba(ns, iocb->slba);
+    if (nvme_ns_zoned(ns)) {
+        NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
+
+        iocb->zone = nvme_zns_get_by_slba(zoned, iocb->slba);
         if (!iocb->zone) {
             status = NVME_LBA_RANGE | NVME_DNR;
             goto invalid;
         }
 
-        status = nvme_zrm_auto(n, ns, iocb->zone);
+        status = nvme_zrm_auto(n, zoned, iocb->zone);
         if (status) {
             goto invalid;
         }
@@ -3081,8 +3084,8 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req)
         goto invalid;
     }
 
-    if (ns->params.zoned) {
-        status = nvme_check_zone_read(ns, slba, nlb);
+    if (nvme_ns_zoned(ns)) {
+        status = nvme_check_zone_read(NVME_NAMESPACE_ZONED(ns), slba, nlb);
         if (status) {
             trace_pci_nvme_err_zone_read_not_ok(slba, nlb, status);
             goto invalid;
@@ -3161,8 +3164,10 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
         goto invalid;
     }
 
-    if (ns->params.zoned) {
-        zone = nvme_zns_get_by_slba(ns, slba);
+    if (nvme_ns_zoned(ns)) {
+        NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
+
+        zone = nvme_zns_get_by_slba(zoned, slba);
         assert(zone);
 
         if (append) {
@@ -3209,12 +3214,12 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
             }
         }
 
-        status = nvme_check_zone_write(ns, zone, slba, nlb);
+        status = nvme_check_zone_write(zone, slba, nlb);
         if (status) {
             goto invalid;
         }
 
-        status = nvme_zrm_auto(n, ns, zone);
+        status = nvme_zrm_auto(n, zoned, zone);
         if (status) {
             goto invalid;
         }
@@ -3268,14 +3273,18 @@ static inline uint16_t nvme_zone_append(NvmeCtrl *n, NvmeRequest *req)
 static uint16_t nvme_get_mgmt_zone_slba_idx(NvmeNamespace *ns, NvmeCmd *c,
                                             uint64_t *slba, uint32_t *zone_idx)
 {
+    NvmeNamespaceZoned *zoned;
+
     uint32_t dw10 = le32_to_cpu(c->cdw10);
     uint32_t dw11 = le32_to_cpu(c->cdw11);
 
-    if (!ns->params.zoned) {
+    if (!nvme_ns_zoned(ns)) {
         trace_pci_nvme_err_invalid_opc(c->opcode);
         return NVME_INVALID_OPCODE | NVME_DNR;
     }
 
+    zoned = NVME_NAMESPACE_ZONED(ns);
+
     *slba = ((uint64_t)dw11) << 32 | dw10;
     if (unlikely(*slba >= ns->id_ns.nsze)) {
         trace_pci_nvme_err_invalid_lba_range(*slba, 0, ns->id_ns.nsze);
@@ -3283,14 +3292,14 @@ static uint16_t nvme_get_mgmt_zone_slba_idx(NvmeNamespace *ns, NvmeCmd *c,
         return NVME_LBA_RANGE | NVME_DNR;
     }
 
-    *zone_idx = nvme_zns_zidx(ns, *slba);
-    assert(*zone_idx < ns->num_zones);
+    *zone_idx = nvme_zns_zidx(zoned, *slba);
+    assert(*zone_idx < zoned->num_zones);
 
     return NVME_SUCCESS;
 }
 
-typedef uint16_t (*op_handler_t)(NvmeNamespace *, NvmeZone *, NvmeZoneState,
-                                 NvmeRequest *);
+typedef uint16_t (*op_handler_t)(NvmeNamespaceZoned *, NvmeZone *,
+                                 NvmeZoneState, NvmeRequest *);
 
 enum NvmeZoneProcessingMask {
     NVME_PROC_CURRENT_ZONE    = 0,
@@ -3300,30 +3309,30 @@ enum NvmeZoneProcessingMask {
     NVME_PROC_FULL_ZONES      = 1 << 3,
 };
 
-static uint16_t nvme_open_zone(NvmeNamespace *ns, NvmeZone *zone,
+static uint16_t nvme_open_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone,
                                NvmeZoneState state, NvmeRequest *req)
 {
-    return nvme_zrm_open(nvme_ctrl(req), ns, zone);
+    return nvme_zrm_open(nvme_ctrl(req), zoned, zone);
 }
 
-static uint16_t nvme_close_zone(NvmeNamespace *ns, NvmeZone *zone,
+static uint16_t nvme_close_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone,
                                 NvmeZoneState state, NvmeRequest *req)
 {
-    return nvme_zrm_close(ns, zone);
+    return nvme_zrm_close(zoned, zone);
 }
 
-static uint16_t nvme_finish_zone(NvmeNamespace *ns, NvmeZone *zone,
+static uint16_t nvme_finish_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone,
                                  NvmeZoneState state, NvmeRequest *req)
 {
-    return nvme_zrm_finish(ns, zone);
+    return nvme_zrm_finish(zoned, zone);
 }
 
-static uint16_t nvme_offline_zone(NvmeNamespace *ns, NvmeZone *zone,
+static uint16_t nvme_offline_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone,
                                   NvmeZoneState state, NvmeRequest *req)
 {
     switch (state) {
     case NVME_ZONE_STATE_READ_ONLY:
-        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_OFFLINE);
+        nvme_zns_assign_state(zoned, zone, NVME_ZONE_STATE_OFFLINE);
         /* fall through */
     case NVME_ZONE_STATE_OFFLINE:
         return NVME_SUCCESS;
@@ -3332,31 +3341,31 @@ static uint16_t nvme_offline_zone(NvmeNamespace *ns, NvmeZone *zone,
     }
 }
 
-static uint16_t nvme_set_zd_ext(NvmeNamespace *ns, NvmeZone *zone)
+static uint16_t nvme_set_zd_ext(NvmeNamespaceZoned *zoned, NvmeZone *zone)
 {
     uint16_t status;
-    uint8_t state = nvme_zns_zs(zone);
+    uint8_t state = nvme_zns_state(zone);
 
     if (state == NVME_ZONE_STATE_EMPTY) {
-        status = nvme_zns_aor_check(ns, 1, 0);
+        status = nvme_zns_aor_check(zoned, 1, 0);
         if (status) {
             return status;
         }
-        nvme_zns_aor_inc_active(ns);
+        nvme_zns_aor_inc_active(zoned);
         zone->d.za |= NVME_ZA_ZD_EXT_VALID;
-        nvme_zns_assign_state(ns, zone, NVME_ZONE_STATE_CLOSED);
+        nvme_zns_assign_state(zoned, zone, NVME_ZONE_STATE_CLOSED);
         return NVME_SUCCESS;
     }
 
     return NVME_ZONE_INVAL_TRANSITION;
 }
 
-static uint16_t nvme_bulk_proc_zone(NvmeNamespace *ns, NvmeZone *zone,
+static uint16_t nvme_bulk_proc_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone,
                                     enum NvmeZoneProcessingMask proc_mask,
                                     op_handler_t op_hndlr, NvmeRequest *req)
 {
     uint16_t status = NVME_SUCCESS;
-    NvmeZoneState zs = nvme_zns_zs(zone);
+    NvmeZoneState zs = nvme_zns_state(zone);
     bool proc_zone;
 
     switch (zs) {
@@ -3378,13 +3387,13 @@ static uint16_t nvme_bulk_proc_zone(NvmeNamespace *ns, NvmeZone *zone,
     }
 
     if (proc_zone) {
-        status = op_hndlr(ns, zone, zs, req);
+        status = op_hndlr(zoned, zone, zs, req);
     }
 
     return status;
 }
 
-static uint16_t nvme_do_zone_op(NvmeNamespace *ns, NvmeZone *zone,
+static uint16_t nvme_do_zone_op(NvmeNamespaceZoned *zoned, NvmeZone *zone,
                                 enum NvmeZoneProcessingMask proc_mask,
                                 op_handler_t op_hndlr, NvmeRequest *req)
 {
@@ -3393,11 +3402,11 @@ static uint16_t nvme_do_zone_op(NvmeNamespace *ns, NvmeZone *zone,
     int i;
 
     if (!proc_mask) {
-        status = op_hndlr(ns, zone, nvme_zns_zs(zone), req);
+        status = op_hndlr(zoned, zone, nvme_zns_state(zone), req);
     } else {
         if (proc_mask & NVME_PROC_CLOSED_ZONES) {
-            QTAILQ_FOREACH_SAFE(zone, &ns->closed_zones, entry, next) {
-                status = nvme_bulk_proc_zone(ns, zone, proc_mask, op_hndlr,
+            QTAILQ_FOREACH_SAFE(zone, &zoned->closed_zones, entry, next) {
+                status = nvme_bulk_proc_zone(zoned, zone, proc_mask, op_hndlr,
                                              req);
                 if (status && status != NVME_NO_COMPLETE) {
                     goto out;
@@ -3405,16 +3414,16 @@ static uint16_t nvme_do_zone_op(NvmeNamespace *ns, NvmeZone *zone,
             }
         }
         if (proc_mask & NVME_PROC_OPENED_ZONES) {
-            QTAILQ_FOREACH_SAFE(zone, &ns->imp_open_zones, entry, next) {
-                status = nvme_bulk_proc_zone(ns, zone, proc_mask, op_hndlr,
+            QTAILQ_FOREACH_SAFE(zone, &zoned->imp_open_zones, entry, next) {
+                status = nvme_bulk_proc_zone(zoned, zone, proc_mask, op_hndlr,
                                              req);
                 if (status && status != NVME_NO_COMPLETE) {
                     goto out;
                 }
             }
 
-            QTAILQ_FOREACH_SAFE(zone, &ns->exp_open_zones, entry, next) {
-                status = nvme_bulk_proc_zone(ns, zone, proc_mask, op_hndlr,
+            QTAILQ_FOREACH_SAFE(zone, &zoned->exp_open_zones, entry, next) {
+                status = nvme_bulk_proc_zone(zoned, zone, proc_mask, op_hndlr,
                                              req);
                 if (status && status != NVME_NO_COMPLETE) {
                     goto out;
@@ -3422,8 +3431,8 @@ static uint16_t nvme_do_zone_op(NvmeNamespace *ns, NvmeZone *zone,
             }
         }
         if (proc_mask & NVME_PROC_FULL_ZONES) {
-            QTAILQ_FOREACH_SAFE(zone, &ns->full_zones, entry, next) {
-                status = nvme_bulk_proc_zone(ns, zone, proc_mask, op_hndlr,
+            QTAILQ_FOREACH_SAFE(zone, &zoned->full_zones, entry, next) {
+                status = nvme_bulk_proc_zone(zoned, zone, proc_mask, op_hndlr,
                                              req);
                 if (status && status != NVME_NO_COMPLETE) {
                     goto out;
@@ -3432,8 +3441,8 @@ static uint16_t nvme_do_zone_op(NvmeNamespace *ns, NvmeZone *zone,
         }
 
         if (proc_mask & NVME_PROC_READ_ONLY_ZONES) {
-            for (i = 0; i < ns->num_zones; i++, zone++) {
-                status = nvme_bulk_proc_zone(ns, zone, proc_mask, op_hndlr,
+            for (i = 0; i < zoned->num_zones; i++, zone++) {
+                status = nvme_bulk_proc_zone(zoned, zone, proc_mask, op_hndlr,
                                              req);
                 if (status && status != NVME_NO_COMPLETE) {
                     goto out;
@@ -3464,7 +3473,7 @@ static void nvme_zone_reset_cancel(BlockAIOCB *aiocb)
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
 
-    iocb->idx = ns->num_zones;
+    iocb->idx = NVME_NAMESPACE_ZONED(ns)->num_zones;
 
     iocb->ret = -ECANCELED;
 
@@ -3511,7 +3520,7 @@ static void nvme_zone_reset_epilogue_cb(void *opaque, int ret)
     }
 
     moff = nvme_moff(ns, iocb->zone->d.zslba);
-    count = nvme_m2b(ns, ns->zone_size);
+    count = nvme_m2b(ns, NVME_NAMESPACE_ZONED(ns)->zone_size);
 
     iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk, moff, count,
                                         BDRV_REQ_MAY_UNMAP,
@@ -3524,6 +3533,7 @@ static void nvme_zone_reset_cb(void *opaque, int ret)
     NvmeZoneResetAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
 
     if (ret < 0) {
         iocb->ret = ret;
@@ -3531,17 +3541,17 @@ static void nvme_zone_reset_cb(void *opaque, int ret)
     }
 
     if (iocb->zone) {
-        nvme_zrm_reset(ns, iocb->zone);
+        nvme_zrm_reset(zoned, iocb->zone);
 
         if (!iocb->all) {
             goto done;
         }
     }
 
-    while (iocb->idx < ns->num_zones) {
-        NvmeZone *zone = &ns->zone_array[iocb->idx++];
+    while (iocb->idx < zoned->num_zones) {
+        NvmeZone *zone = &zoned->zone_array[iocb->idx++];
 
-        switch (nvme_zns_zs(zone)) {
+        switch (nvme_zns_state(zone)) {
         case NVME_ZONE_STATE_EMPTY:
             if (!iocb->all) {
                 goto done;
@@ -3564,7 +3574,7 @@ static void nvme_zone_reset_cb(void *opaque, int ret)
 
         iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk,
                                             nvme_l2b(ns, zone->d.zslba),
-                                            nvme_l2b(ns, ns->zone_size),
+                                            nvme_l2b(ns, zoned->zone_size),
                                             BDRV_REQ_MAY_UNMAP,
                                             nvme_zone_reset_epilogue_cb,
                                             iocb);
@@ -3582,6 +3592,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
 {
     NvmeCmd *cmd = (NvmeCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
     NvmeZone *zone;
     NvmeZoneResetAIOCB *iocb;
     uint8_t *zd_ext;
@@ -3605,7 +3616,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
         }
     }
 
-    zone = &ns->zone_array[zone_idx];
+    zone = &zoned->zone_array[zone_idx];
     if (slba != zone->d.zslba) {
         trace_pci_nvme_err_unaligned_zone_cmd(action, slba, zone->d.zslba);
         return NVME_INVALID_FIELD | NVME_DNR;
@@ -3618,7 +3629,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
             proc_mask = NVME_PROC_CLOSED_ZONES;
         }
         trace_pci_nvme_open_zone(slba, zone_idx, all);
-        status = nvme_do_zone_op(ns, zone, proc_mask, nvme_open_zone, req);
+        status = nvme_do_zone_op(zoned, zone, proc_mask, nvme_open_zone, req);
         break;
 
     case NVME_ZONE_ACTION_CLOSE:
@@ -3626,7 +3637,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
             proc_mask = NVME_PROC_OPENED_ZONES;
         }
         trace_pci_nvme_close_zone(slba, zone_idx, all);
-        status = nvme_do_zone_op(ns, zone, proc_mask, nvme_close_zone, req);
+        status = nvme_do_zone_op(zoned, zone, proc_mask, nvme_close_zone, req);
         break;
 
     case NVME_ZONE_ACTION_FINISH:
@@ -3634,7 +3645,8 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
             proc_mask = NVME_PROC_OPENED_ZONES | NVME_PROC_CLOSED_ZONES;
         }
         trace_pci_nvme_finish_zone(slba, zone_idx, all);
-        status = nvme_do_zone_op(ns, zone, proc_mask, nvme_finish_zone, req);
+        status = nvme_do_zone_op(zoned, zone, proc_mask, nvme_finish_zone,
+                                 req);
         break;
 
     case NVME_ZONE_ACTION_RESET:
@@ -3660,22 +3672,23 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
             proc_mask = NVME_PROC_READ_ONLY_ZONES;
         }
         trace_pci_nvme_offline_zone(slba, zone_idx, all);
-        status = nvme_do_zone_op(ns, zone, proc_mask, nvme_offline_zone, req);
+        status = nvme_do_zone_op(zoned, zone, proc_mask, nvme_offline_zone,
+                                 req);
         break;
 
     case NVME_ZONE_ACTION_SET_ZD_EXT:
         trace_pci_nvme_set_descriptor_extension(slba, zone_idx);
-        if (all || !ns->params.zd_extension_size) {
+        if (all || !zoned->zd_extension_size) {
             return NVME_INVALID_FIELD | NVME_DNR;
         }
-        zd_ext = nvme_zns_zde(ns, zone_idx);
-        status = nvme_h2c(n, zd_ext, ns->params.zd_extension_size, req);
+        zd_ext = nvme_zns_zde(zoned, zone_idx);
+        status = nvme_h2c(n, zd_ext, zoned->zd_extension_size, req);
         if (status) {
             trace_pci_nvme_err_zd_extension_map_error(zone_idx);
             return status;
         }
 
-        status = nvme_set_zd_ext(ns, zone);
+        status = nvme_set_zd_ext(zoned, zone);
         if (status == NVME_SUCCESS) {
             trace_pci_nvme_zd_extension_set(zone_idx);
             return status;
@@ -3700,7 +3713,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
 
 static bool nvme_zone_matches_filter(uint32_t zafs, NvmeZone *zl)
 {
-    NvmeZoneState zs = nvme_zns_zs(zl);
+    NvmeZoneState zs = nvme_zns_state(zl);
 
     switch (zafs) {
     case NVME_ZONE_REPORT_ALL:
@@ -3728,6 +3741,7 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req)
 {
     NvmeCmd *cmd = (NvmeCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
     /* cdw12 is zero-based number of dwords to return. Convert to bytes */
     uint32_t data_size = (le32_to_cpu(cmd->cdw12) + 1) << 2;
     uint32_t dw13 = le32_to_cpu(cmd->cdw13);
@@ -3753,7 +3767,7 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req)
     if (zra != NVME_ZONE_REPORT && zra != NVME_ZONE_REPORT_EXTENDED) {
         return NVME_INVALID_FIELD | NVME_DNR;
     }
-    if (zra == NVME_ZONE_REPORT_EXTENDED && !ns->params.zd_extension_size) {
+    if (zra == NVME_ZONE_REPORT_EXTENDED && !zoned->zd_extension_size) {
         return NVME_INVALID_FIELD | NVME_DNR;
     }
 
@@ -3775,14 +3789,14 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req)
 
     zone_entry_sz = sizeof(NvmeZoneDescr);
     if (zra == NVME_ZONE_REPORT_EXTENDED) {
-        zone_entry_sz += ns->params.zd_extension_size;
+        zone_entry_sz += zoned->zd_extension_size;
     }
 
     max_zones = (data_size - sizeof(NvmeZoneReportHeader)) / zone_entry_sz;
     buf = g_malloc0(data_size);
 
-    zone = &ns->zone_array[zone_idx];
-    for (i = zone_idx; i < ns->num_zones; i++) {
+    zone = &zoned->zone_array[zone_idx];
+    for (i = zone_idx; i < zoned->num_zones; i++) {
         if (partial && nr_zones >= max_zones) {
             break;
         }
@@ -3794,8 +3808,8 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req)
     header->nr_zones = cpu_to_le64(nr_zones);
 
     buf_p = buf + sizeof(NvmeZoneReportHeader);
-    for (; zone_idx < ns->num_zones && max_zones > 0; zone_idx++) {
-        zone = &ns->zone_array[zone_idx];
+    for (; zone_idx < zoned->num_zones && max_zones > 0; zone_idx++) {
+        zone = &zoned->zone_array[zone_idx];
         if (nvme_zone_matches_filter(zrasf, zone)) {
             z = (NvmeZoneDescr *)buf_p;
             buf_p += sizeof(NvmeZoneDescr);
@@ -3814,10 +3828,10 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req)
 
             if (zra == NVME_ZONE_REPORT_EXTENDED) {
                 if (zone->d.za & NVME_ZA_ZD_EXT_VALID) {
-                    memcpy(buf_p, nvme_zns_zde(ns, zone_idx),
-                           ns->params.zd_extension_size);
+                    memcpy(buf_p, nvme_zns_zde(zoned, zone_idx),
+                           zoned->zd_extension_size);
                 }
-                buf_p += ns->params.zd_extension_size;
+                buf_p += zoned->zd_extension_size;
             }
 
             max_zones--;
@@ -4542,8 +4556,8 @@ static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req,
     if (c->csi == NVME_CSI_NVM) {
         return nvme_rpt_empty_id_struct(n, req);
     } else if (c->csi == NVME_CSI_ZONED && ns->csi == NVME_CSI_ZONED) {
-        return nvme_c2h(n, (uint8_t *)ns->id_ns_zoned, sizeof(NvmeIdNsZoned),
-                        req);
+        return nvme_c2h(n, (uint8_t *)&NVME_NAMESPACE_ZONED(ns)->id_ns,
+                        sizeof(NvmeIdNsZoned), req);
     }
 
     return NVME_INVALID_FIELD | NVME_DNR;
@@ -5343,7 +5357,7 @@ done:
 
 static uint16_t nvme_format_check(NvmeNamespace *ns, uint8_t lbaf, uint8_t pi)
 {
-    if (ns->params.zoned) {
+    if (nvme_ns_zoned(ns)) {
         return NVME_INVALID_FORMAT | NVME_DNR;
     }
 
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index 471d1ddc016a..183483969088 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -167,6 +167,8 @@ static int nvme_ns_init_blk(NvmeNamespace *ns, Error **errp)
 
 static int nvme_zns_check_calc_geometry(NvmeNamespace *ns, Error **errp)
 {
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
+
     uint64_t zone_size, zone_cap;
 
     /* Make sure that the values of ZNS properties are sane */
@@ -200,12 +202,12 @@ static int nvme_zns_check_calc_geometry(NvmeNamespace *ns, Error **errp)
      * Save the main zone geometry values to avoid
      * calculating them later again.
      */
-    ns->zone_size = zone_size / ns->lbasz;
-    ns->zone_capacity = zone_cap / ns->lbasz;
-    ns->num_zones = le64_to_cpu(ns->id_ns.nsze) / ns->zone_size;
+    zoned->zone_size = zone_size / ns->lbasz;
+    zoned->zone_capacity = zone_cap / ns->lbasz;
+    zoned->num_zones = le64_to_cpu(ns->id_ns.nsze) / zoned->zone_size;
 
     /* Do a few more sanity checks of ZNS properties */
-    if (!ns->num_zones) {
+    if (!zoned->num_zones) {
         error_setg(errp,
                    "insufficient drive capacity, must be at least the size "
                    "of one zone (%"PRIu64"B)", zone_size);
@@ -215,68 +217,70 @@ static int nvme_zns_check_calc_geometry(NvmeNamespace *ns, Error **errp)
     return 0;
 }
 
-static void nvme_zns_init_state(NvmeNamespace *ns)
+static void nvme_zns_init_state(NvmeNamespaceZoned *zoned)
 {
-    uint64_t start = 0, zone_size = ns->zone_size;
-    uint64_t capacity = ns->num_zones * zone_size;
+    uint64_t start = 0, zone_size = zoned->zone_size;
+    uint64_t capacity = zoned->num_zones * zone_size;
     NvmeZone *zone;
     int i;
 
-    ns->zone_array = g_new0(NvmeZone, ns->num_zones);
-    if (ns->params.zd_extension_size) {
-        ns->zd_extensions = g_malloc0(ns->params.zd_extension_size *
-                                      ns->num_zones);
+    zoned->zone_array = g_new0(NvmeZone, zoned->num_zones);
+    if (zoned->zd_extension_size) {
+        zoned->zd_extensions = g_malloc0(zoned->zd_extension_size *
+                                         zoned->num_zones);
     }
 
-    QTAILQ_INIT(&ns->exp_open_zones);
-    QTAILQ_INIT(&ns->imp_open_zones);
-    QTAILQ_INIT(&ns->closed_zones);
-    QTAILQ_INIT(&ns->full_zones);
+    QTAILQ_INIT(&zoned->exp_open_zones);
+    QTAILQ_INIT(&zoned->imp_open_zones);
+    QTAILQ_INIT(&zoned->closed_zones);
+    QTAILQ_INIT(&zoned->full_zones);
 
-    zone = ns->zone_array;
-    for (i = 0; i < ns->num_zones; i++, zone++) {
+    zone = zoned->zone_array;
+    for (i = 0; i < zoned->num_zones; i++, zone++) {
         if (start + zone_size > capacity) {
             zone_size = capacity - start;
         }
         zone->d.zt = NVME_ZONE_TYPE_SEQ_WRITE;
-        nvme_zns_set_zs(zone, NVME_ZONE_STATE_EMPTY);
+        nvme_zns_set_state(zone, NVME_ZONE_STATE_EMPTY);
         zone->d.za = 0;
-        zone->d.zcap = ns->zone_capacity;
+        zone->d.zcap = zoned->zone_capacity;
         zone->d.zslba = start;
         zone->d.wp = start;
         zone->w_ptr = start;
         start += zone_size;
     }
 
-    ns->zone_size_log2 = 0;
-    if (is_power_of_2(ns->zone_size)) {
-        ns->zone_size_log2 = 63 - clz64(ns->zone_size);
+    zoned->zone_size_log2 = 0;
+    if (is_power_of_2(zoned->zone_size)) {
+        zoned->zone_size_log2 = 63 - clz64(zoned->zone_size);
     }
 }
 
 static void nvme_zns_init(NvmeNamespace *ns)
 {
-    NvmeIdNsZoned *id_ns_z;
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
+    NvmeIdNsZoned *id_ns_z = &zoned->id_ns;
     int i;
 
-    nvme_zns_init_state(ns);
-
-    id_ns_z = g_malloc0(sizeof(NvmeIdNsZoned));
+    nvme_zns_init_state(zoned);
 
     /* MAR/MOR are zeroes-based, FFFFFFFFFh means no limit */
-    id_ns_z->mar = cpu_to_le32(ns->params.max_active_zones - 1);
-    id_ns_z->mor = cpu_to_le32(ns->params.max_open_zones - 1);
+    id_ns_z->mar = cpu_to_le32(zoned->max_active_zones - 1);
+    id_ns_z->mor = cpu_to_le32(zoned->max_open_zones - 1);
     id_ns_z->zoc = 0;
-    id_ns_z->ozcs = ns->params.cross_zone_read ? 0x01 : 0x00;
+
+    if (zoned->flags & NVME_NS_ZONED_CROSS_READ) {
+        id_ns_z->ozcs |= NVME_ID_NS_ZONED_OZCS_CROSS_READ;
+    }
 
     for (i = 0; i <= ns->id_ns.nlbaf; i++) {
-        id_ns_z->lbafe[i].zsze = cpu_to_le64(ns->zone_size);
+        id_ns_z->lbafe[i].zsze = cpu_to_le64(zoned->zone_size);
         id_ns_z->lbafe[i].zdes =
-            ns->params.zd_extension_size >> 6; /* Units of 64B */
+            zoned->zd_extension_size >> 6; /* Units of 64B */
     }
 
     ns->csi = NVME_CSI_ZONED;
-    ns->id_ns.nsze = cpu_to_le64(ns->num_zones * ns->zone_size);
+    ns->id_ns.nsze = cpu_to_le64(zoned->num_zones * zoned->zone_size);
     ns->id_ns.ncap = ns->id_ns.nsze;
     ns->id_ns.nuse = ns->id_ns.ncap;
 
@@ -287,64 +291,62 @@ static void nvme_zns_init(NvmeNamespace *ns)
      * we can only support DULBE if the zone size is a multiple of the
      * calculated NPDG.
      */
-    if (ns->zone_size % (ns->id_ns.npdg + 1)) {
+    if (zoned->zone_size % (ns->id_ns.npdg + 1)) {
         warn_report("the zone size (%"PRIu64" blocks) is not a multiple of "
                     "the calculated deallocation granularity (%d blocks); "
                     "DULBE support disabled",
-                    ns->zone_size, ns->id_ns.npdg + 1);
+                    zoned->zone_size, ns->id_ns.npdg + 1);
 
         ns->id_ns.nsfeat &= ~0x4;
     }
-
-    ns->id_ns_zoned = id_ns_z;
 }
 
-static void nvme_zns_clear_zone(NvmeNamespace *ns, NvmeZone *zone)
+static void nvme_zns_clear_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone)
 {
     uint8_t state;
 
     zone->w_ptr = zone->d.wp;
-    state = nvme_zns_zs(zone);
+    state = nvme_zns_state(zone);
     if (zone->d.wp != zone->d.zslba ||
         (zone->d.za & NVME_ZA_ZD_EXT_VALID)) {
         if (state != NVME_ZONE_STATE_CLOSED) {
             trace_pci_nvme_clear_ns_close(state, zone->d.zslba);
-            nvme_zns_set_zs(zone, NVME_ZONE_STATE_CLOSED);
+            nvme_zns_set_state(zone, NVME_ZONE_STATE_CLOSED);
         }
-        nvme_zns_aor_inc_active(ns);
-        QTAILQ_INSERT_HEAD(&ns->closed_zones, zone, entry);
+        nvme_zns_aor_inc_active(zoned);
+        QTAILQ_INSERT_HEAD(&zoned->closed_zones, zone, entry);
     } else {
         trace_pci_nvme_clear_ns_reset(state, zone->d.zslba);
-        nvme_zns_set_zs(zone, NVME_ZONE_STATE_EMPTY);
+        nvme_zns_set_state(zone, NVME_ZONE_STATE_EMPTY);
     }
 }
 
 /*
  * Close all the zones that are currently open.
  */
-static void nvme_zns_shutdown(NvmeNamespace *ns)
+static void nvme_zns_shutdown(NvmeNamespaceZoned *zoned)
 {
     NvmeZone *zone, *next;
 
-    QTAILQ_FOREACH_SAFE(zone, &ns->closed_zones, entry, next) {
-        QTAILQ_REMOVE(&ns->closed_zones, zone, entry);
-        nvme_zns_aor_dec_active(ns);
-        nvme_zns_clear_zone(ns, zone);
+    QTAILQ_FOREACH_SAFE(zone, &zoned->closed_zones, entry, next) {
+        QTAILQ_REMOVE(&zoned->closed_zones, zone, entry);
+        nvme_zns_aor_dec_active(zoned);
+        nvme_zns_clear_zone(zoned, zone);
     }
-    QTAILQ_FOREACH_SAFE(zone, &ns->imp_open_zones, entry, next) {
-        QTAILQ_REMOVE(&ns->imp_open_zones, zone, entry);
-        nvme_zns_aor_dec_open(ns);
-        nvme_zns_aor_dec_active(ns);
-        nvme_zns_clear_zone(ns, zone);
+    QTAILQ_FOREACH_SAFE(zone, &zoned->imp_open_zones, entry, next) {
+        QTAILQ_REMOVE(&zoned->imp_open_zones, zone, entry);
+        nvme_zns_aor_dec_open(zoned);
+        nvme_zns_aor_dec_active(zoned);
+        nvme_zns_clear_zone(zoned, zone);
     }
-    QTAILQ_FOREACH_SAFE(zone, &ns->exp_open_zones, entry, next) {
-        QTAILQ_REMOVE(&ns->exp_open_zones, zone, entry);
-        nvme_zns_aor_dec_open(ns);
-        nvme_zns_aor_dec_active(ns);
-        nvme_zns_clear_zone(ns, zone);
+    QTAILQ_FOREACH_SAFE(zone, &zoned->exp_open_zones, entry, next) {
+        QTAILQ_REMOVE(&zoned->exp_open_zones, zone, entry);
+        nvme_zns_aor_dec_open(zoned);
+        nvme_zns_aor_dec_active(zoned);
+        nvme_zns_clear_zone(zoned, zone);
     }
 
-    assert(ns->nr_open_zones == 0);
+    assert(zoned->nr_open_zones == 0);
 }
 
 static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp)
@@ -411,9 +413,20 @@ int nvme_ns_setup(NvmeNamespace *ns, Error **errp)
         return -1;
     }
     if (ns->params.zoned) {
+        NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
+
         if (nvme_zns_check_calc_geometry(ns, errp) != 0) {
             return -1;
         }
+
+        /* copy device parameters */
+        zoned->zd_extension_size = ns->params.zd_extension_size;
+        zoned->max_open_zones = ns->params.max_open_zones;
+        zoned->max_active_zones = ns->params.max_open_zones;
+        if (ns->params.cross_zone_read) {
+            zoned->flags |= NVME_NS_ZONED_CROSS_READ;
+        }
+
         nvme_zns_init(ns);
     }
 
@@ -428,17 +441,18 @@ void nvme_ns_drain(NvmeNamespace *ns)
 void nvme_ns_shutdown(NvmeNamespace *ns)
 {
     blk_flush(ns->blkconf.blk);
-    if (ns->params.zoned) {
-        nvme_zns_shutdown(ns);
+    if (nvme_ns_zoned(ns)) {
+        nvme_zns_shutdown(NVME_NAMESPACE_ZONED(ns));
     }
 }
 
 void nvme_ns_cleanup(NvmeNamespace *ns)
 {
-    if (ns->params.zoned) {
-        g_free(ns->id_ns_zoned);
-        g_free(ns->zone_array);
-        g_free(ns->zd_extensions);
+    if (nvme_ns_zoned(ns)) {
+        NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
+
+        g_free(zoned->zone_array);
+        g_free(zoned->zd_extensions);
     }
 }
 
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 99d8b9066cc9..9cfb172101a9 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -84,12 +84,6 @@ static inline NvmeNamespace *nvme_subsys_ns(NvmeSubsystem *subsys,
 #define NVME_NS(obj) \
     OBJECT_CHECK(NvmeNamespace, (obj), TYPE_NVME_NS)
 
-typedef struct NvmeZone {
-    NvmeZoneDescr   d;
-    uint64_t        w_ptr;
-    QTAILQ_ENTRY(NvmeZone) entry;
-} NvmeZone;
-
 typedef struct NvmeNamespaceParams {
     bool     detached;
     bool     shared;
@@ -116,6 +110,43 @@ typedef struct NvmeNamespaceParams {
     uint32_t zd_extension_size;
 } NvmeNamespaceParams;
 
+typedef struct NvmeZone {
+    NvmeZoneDescr   d;
+    uint64_t        w_ptr;
+    QTAILQ_ENTRY(NvmeZone) entry;
+} NvmeZone;
+
+enum {
+    NVME_NS_ZONED_CROSS_READ = 1 << 0,
+};
+
+typedef struct NvmeNamespaceZoned {
+    NvmeIdNsZoned id_ns;
+
+    uint32_t num_zones;
+    NvmeZone *zone_array;
+
+    uint64_t zone_size;
+    uint32_t zone_size_log2;
+
+    uint64_t zone_capacity;
+
+    uint32_t zd_extension_size;
+    uint8_t  *zd_extensions;
+
+    uint32_t max_open_zones;
+    int32_t  nr_open_zones;
+    uint32_t max_active_zones;
+    int32_t  nr_active_zones;
+
+    unsigned long flags;
+
+    QTAILQ_HEAD(, NvmeZone) exp_open_zones;
+    QTAILQ_HEAD(, NvmeZone) imp_open_zones;
+    QTAILQ_HEAD(, NvmeZone) closed_zones;
+    QTAILQ_HEAD(, NvmeZone) full_zones;
+} NvmeNamespaceZoned;
+
 typedef struct NvmeNamespace {
     DeviceState  parent_obj;
     BlockConf    blkconf;
@@ -132,27 +163,17 @@ typedef struct NvmeNamespace {
 
     QTAILQ_ENTRY(NvmeNamespace) entry;
 
-    NvmeIdNsZoned   *id_ns_zoned;
-    NvmeZone        *zone_array;
-    QTAILQ_HEAD(, NvmeZone) exp_open_zones;
-    QTAILQ_HEAD(, NvmeZone) imp_open_zones;
-    QTAILQ_HEAD(, NvmeZone) closed_zones;
-    QTAILQ_HEAD(, NvmeZone) full_zones;
-    uint32_t        num_zones;
-    uint64_t        zone_size;
-    uint64_t        zone_capacity;
-    uint32_t        zone_size_log2;
-    uint8_t         *zd_extensions;
-    int32_t         nr_open_zones;
-    int32_t         nr_active_zones;
-
     NvmeNamespaceParams params;
 
     struct {
         uint32_t err_rec;
     } features;
+
+    NvmeNamespaceZoned zoned;
 } NvmeNamespace;
 
+#define NVME_NAMESPACE_ZONED(ns) (&(ns)->zoned)
+
 static inline uint32_t nvme_nsid(NvmeNamespace *ns)
 {
     if (ns) {
@@ -188,6 +209,11 @@ void nvme_ns_drain(NvmeNamespace *ns);
 void nvme_ns_shutdown(NvmeNamespace *ns);
 void nvme_ns_cleanup(NvmeNamespace *ns);
 
+static inline bool nvme_ns_zoned(NvmeNamespace *ns)
+{
+    return ns->csi == NVME_CSI_ZONED;
+}
+
 typedef struct NvmeAsyncEvent {
     QTAILQ_ENTRY(NvmeAsyncEvent) entry;
     NvmeAerResult result;
diff --git a/hw/nvme/zns.h b/hw/nvme/zns.h
index 609db6eda7e5..967adc62d730 100644
--- a/hw/nvme/zns.h
+++ b/hw/nvme/zns.h
@@ -1,5 +1,5 @@
-#ifndef HW_NVME_ZONED_H
-#define HW_NVME_ZONED_H
+#ifndef HW_NVME_ZNS_H
+#define HW_NVME_ZNS_H
 
 #include "qemu/units.h"
 
@@ -7,20 +7,20 @@
 
 #define NVME_DEFAULT_ZONE_SIZE   (128 * MiB)
 
-static inline NvmeZoneState nvme_zns_zs(NvmeZone *zone)
+static inline NvmeZoneState nvme_zns_state(NvmeZone *zone)
 {
     return zone->d.zs >> 4;
 }
 
-static inline void nvme_zns_set_zs(NvmeZone *zone, NvmeZoneState state)
+static inline void nvme_zns_set_state(NvmeZone *zone, NvmeZoneState state)
 {
     zone->d.zs = state << 4;
 }
 
-static inline uint64_t nvme_zns_read_boundary(NvmeNamespace *ns,
+static inline uint64_t nvme_zns_read_boundary(NvmeNamespaceZoned *zoned,
                                               NvmeZone *zone)
 {
-    return zone->d.zslba + ns->zone_size;
+    return zone->d.zslba + zoned->zone_size;
 }
 
 static inline uint64_t nvme_zns_write_boundary(NvmeZone *zone)
@@ -30,68 +30,70 @@ static inline uint64_t nvme_zns_write_boundary(NvmeZone *zone)
 
 static inline bool nvme_zns_wp_valid(NvmeZone *zone)
 {
-    uint8_t st = nvme_zns_zs(zone);
+    uint8_t st = nvme_zns_state(zone);
 
     return st != NVME_ZONE_STATE_FULL &&
            st != NVME_ZONE_STATE_READ_ONLY &&
            st != NVME_ZONE_STATE_OFFLINE;
 }
 
-static inline uint32_t nvme_zns_zidx(NvmeNamespace *ns, uint64_t slba)
+static inline uint32_t nvme_zns_zidx(NvmeNamespaceZoned *zoned,
+                                     uint64_t slba)
 {
-    return ns->zone_size_log2 > 0 ? slba >> ns->zone_size_log2 :
-                                    slba / ns->zone_size;
+    return zoned->zone_size_log2 > 0 ?
+        slba >> zoned->zone_size_log2 : slba / zoned->zone_size;
 }
 
-static inline NvmeZone *nvme_zns_get_by_slba(NvmeNamespace *ns, uint64_t slba)
+static inline NvmeZone *nvme_zns_get_by_slba(NvmeNamespaceZoned *zoned,
+                                             uint64_t slba)
 {
-    uint32_t zone_idx = nvme_zns_zidx(ns, slba);
+    uint32_t zone_idx = nvme_zns_zidx(zoned, slba);
 
-    assert(zone_idx < ns->num_zones);
-    return &ns->zone_array[zone_idx];
+    assert(zone_idx < zoned->num_zones);
+    return &zoned->zone_array[zone_idx];
 }
 
-static inline uint8_t *nvme_zns_zde(NvmeNamespace *ns, uint32_t zone_idx)
+static inline uint8_t *nvme_zns_zde(NvmeNamespaceZoned *zoned,
+                                    uint32_t zone_idx)
 {
-    return &ns->zd_extensions[zone_idx * ns->params.zd_extension_size];
+    return &zoned->zd_extensions[zone_idx * zoned->zd_extension_size];
 }
 
-static inline void nvme_zns_aor_inc_open(NvmeNamespace *ns)
+static inline void nvme_zns_aor_inc_open(NvmeNamespaceZoned *zoned)
 {
-    assert(ns->nr_open_zones >= 0);
-    if (ns->params.max_open_zones) {
-        ns->nr_open_zones++;
-        assert(ns->nr_open_zones <= ns->params.max_open_zones);
+    assert(zoned->nr_open_zones >= 0);
+    if (zoned->max_open_zones) {
+        zoned->nr_open_zones++;
+        assert(zoned->nr_open_zones <= zoned->max_open_zones);
     }
 }
 
-static inline void nvme_zns_aor_dec_open(NvmeNamespace *ns)
+static inline void nvme_zns_aor_dec_open(NvmeNamespaceZoned *zoned)
 {
-    if (ns->params.max_open_zones) {
-        assert(ns->nr_open_zones > 0);
-        ns->nr_open_zones--;
+    if (zoned->max_open_zones) {
+        assert(zoned->nr_open_zones > 0);
+        zoned->nr_open_zones--;
     }
-    assert(ns->nr_open_zones >= 0);
+    assert(zoned->nr_open_zones >= 0);
 }
 
-static inline void nvme_zns_aor_inc_active(NvmeNamespace *ns)
+static inline void nvme_zns_aor_inc_active(NvmeNamespaceZoned *zoned)
 {
-    assert(ns->nr_active_zones >= 0);
-    if (ns->params.max_active_zones) {
-        ns->nr_active_zones++;
-        assert(ns->nr_active_zones <= ns->params.max_active_zones);
+    assert(zoned->nr_active_zones >= 0);
+    if (zoned->max_active_zones) {
+        zoned->nr_active_zones++;
+        assert(zoned->nr_active_zones <= zoned->max_active_zones);
     }
 }
 
-static inline void nvme_zns_aor_dec_active(NvmeNamespace *ns)
+static inline void nvme_zns_aor_dec_active(NvmeNamespaceZoned *zoned)
 {
-    if (ns->params.max_active_zones) {
-        assert(ns->nr_active_zones > 0);
-        ns->nr_active_zones--;
-        assert(ns->nr_active_zones >= ns->nr_open_zones);
+    if (zoned->max_active_zones) {
+        assert(zoned->nr_active_zones > 0);
+        zoned->nr_active_zones--;
+        assert(zoned->nr_active_zones >= zoned->nr_open_zones);
     }
-    assert(ns->nr_active_zones >= 0);
+    assert(zoned->nr_active_zones >= 0);
 }
 
-
-#endif /* HW_NVME_ZONED_H */
+#endif /* HW_NVME_ZNS_H */
diff --git a/include/block/nvme.h b/include/block/nvme.h
index e3bd47bf76ab..2bcabe561589 100644
--- a/include/block/nvme.h
+++ b/include/block/nvme.h
@@ -1338,6 +1338,10 @@ enum NvmeCsi {
 
 #define NVME_SET_CSI(vec, csi) (vec |= (uint8_t)(1 << (csi)))
 
+enum NvmeIdNsZonedOzcs {
+    NVME_ID_NS_ZONED_OZCS_CROSS_READ = 1 << 0,
+};
+
 typedef struct QEMU_PACKED NvmeIdNsZoned {
     uint16_t    zoc;
     uint16_t    ozcs;
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 06/16] hw/nvme: move nvm namespace members to separate struct
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (4 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 05/16] hw/nvme: move zoned namespace members to separate struct Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 07/16] hw/nvme: move BlockBackend to NvmeNamespaceNvm Klaus Jensen
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c | 282 +++++++++++++++++++++++++++----------------------
 hw/nvme/dif.c  | 101 +++++++++---------
 hw/nvme/dif.h  |  12 +--
 hw/nvme/ns.c   |  72 +++++++------
 hw/nvme/nvme.h |  45 +++++---
 5 files changed, 290 insertions(+), 222 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index e357329d85b8..026dfaa71bda 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -528,11 +528,11 @@ static inline void nvme_sg_unmap(NvmeSg *sg)
  * holds both data and metadata. This function splits the data and metadata
  * into two separate QSG/IOVs.
  */
-static void nvme_sg_split(NvmeSg *sg, NvmeNamespace *ns, NvmeSg *data,
+static void nvme_sg_split(NvmeSg *sg, NvmeNamespaceNvm *nvm, NvmeSg *data,
                           NvmeSg *mdata)
 {
     NvmeSg *dst = data;
-    uint32_t trans_len, count = ns->lbasz;
+    uint32_t trans_len, count = nvm->lbasz;
     uint64_t offset = 0;
     bool dma = sg->flags & NVME_SG_DMA;
     size_t sge_len;
@@ -564,7 +564,7 @@ static void nvme_sg_split(NvmeSg *sg, NvmeNamespace *ns, NvmeSg *data,
 
         if (count == 0) {
             dst = (dst == data) ? mdata : data;
-            count = (dst == data) ? ns->lbasz : ns->lbaf.ms;
+            count = (dst == data) ? nvm->lbasz : nvm->lbaf.ms;
         }
 
         if (sge_len == offset) {
@@ -1029,17 +1029,17 @@ static uint16_t nvme_map_mptr(NvmeCtrl *n, NvmeSg *sg, size_t len,
 
 static uint16_t nvme_map_data(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req)
 {
-    NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(req->ns);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
-    bool pi = !!NVME_ID_NS_DPS_TYPE(ns->id_ns.dps);
+    bool pi = !!NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps);
     bool pract = !!(le16_to_cpu(rw->control) & NVME_RW_PRINFO_PRACT);
-    size_t len = nvme_l2b(ns, nlb);
+    size_t len = nvme_l2b(nvm, nlb);
     uint16_t status;
 
-    if (nvme_ns_ext(ns) && !(pi && pract && ns->lbaf.ms == 8)) {
+    if (nvme_ns_ext(nvm) && !(pi && pract && nvm->lbaf.ms == 8)) {
         NvmeSg sg;
 
-        len += nvme_m2b(ns, nlb);
+        len += nvme_m2b(nvm, nlb);
 
         status = nvme_map_dptr(n, &sg, len, &req->cmd);
         if (status) {
@@ -1047,7 +1047,7 @@ static uint16_t nvme_map_data(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req)
         }
 
         nvme_sg_init(n, &req->sg, sg.flags & NVME_SG_DMA);
-        nvme_sg_split(&sg, ns, &req->sg, NULL);
+        nvme_sg_split(&sg, nvm, &req->sg, NULL);
         nvme_sg_unmap(&sg);
 
         return NVME_SUCCESS;
@@ -1058,14 +1058,14 @@ static uint16_t nvme_map_data(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req)
 
 static uint16_t nvme_map_mdata(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req)
 {
-    NvmeNamespace *ns = req->ns;
-    size_t len = nvme_m2b(ns, nlb);
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(req->ns);
+    size_t len = nvme_m2b(nvm, nlb);
     uint16_t status;
 
-    if (nvme_ns_ext(ns)) {
+    if (nvme_ns_ext(nvm)) {
         NvmeSg sg;
 
-        len += nvme_l2b(ns, nlb);
+        len += nvme_l2b(nvm, nlb);
 
         status = nvme_map_dptr(n, &sg, len, &req->cmd);
         if (status) {
@@ -1073,7 +1073,7 @@ static uint16_t nvme_map_mdata(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req)
         }
 
         nvme_sg_init(n, &req->sg, sg.flags & NVME_SG_DMA);
-        nvme_sg_split(&sg, ns, NULL, &req->sg);
+        nvme_sg_split(&sg, nvm, NULL, &req->sg);
         nvme_sg_unmap(&sg);
 
         return NVME_SUCCESS;
@@ -1209,14 +1209,14 @@ static inline uint16_t nvme_h2c(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
 uint16_t nvme_bounce_data(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
                           NvmeTxDirection dir, NvmeRequest *req)
 {
-    NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(req->ns);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
-    bool pi = !!NVME_ID_NS_DPS_TYPE(ns->id_ns.dps);
+    bool pi = !!NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps);
     bool pract = !!(le16_to_cpu(rw->control) & NVME_RW_PRINFO_PRACT);
 
-    if (nvme_ns_ext(ns) && !(pi && pract && ns->lbaf.ms == 8)) {
-        return nvme_tx_interleaved(n, &req->sg, ptr, len, ns->lbasz,
-                                   ns->lbaf.ms, 0, dir);
+    if (nvme_ns_ext(nvm) && !(pi && pract && nvm->lbaf.ms == 8)) {
+        return nvme_tx_interleaved(n, &req->sg, ptr, len, nvm->lbasz,
+                                   nvm->lbaf.ms, 0, dir);
     }
 
     return nvme_tx(n, &req->sg, ptr, len, dir);
@@ -1225,12 +1225,12 @@ uint16_t nvme_bounce_data(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
 uint16_t nvme_bounce_mdata(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
                            NvmeTxDirection dir, NvmeRequest *req)
 {
-    NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(req->ns);
     uint16_t status;
 
-    if (nvme_ns_ext(ns)) {
-        return nvme_tx_interleaved(n, &req->sg, ptr, len, ns->lbaf.ms,
-                                   ns->lbasz, ns->lbasz, dir);
+    if (nvme_ns_ext(nvm)) {
+        return nvme_tx_interleaved(n, &req->sg, ptr, len, nvm->lbaf.ms,
+                                   nvm->lbasz, nvm->lbasz, dir);
     }
 
     nvme_sg_unmap(&req->sg);
@@ -1448,10 +1448,10 @@ static inline uint16_t nvme_check_mdts(NvmeCtrl *n, size_t len)
     return NVME_SUCCESS;
 }
 
-static inline uint16_t nvme_check_bounds(NvmeNamespace *ns, uint64_t slba,
+static inline uint16_t nvme_check_bounds(NvmeNamespaceNvm *nvm, uint64_t slba,
                                          uint32_t nlb)
 {
-    uint64_t nsze = le64_to_cpu(ns->id_ns.nsze);
+    uint64_t nsze = le64_to_cpu(nvm->id_ns.nsze);
 
     if (unlikely(UINT64_MAX - slba < nlb || slba + nlb > nsze)) {
         trace_pci_nvme_err_invalid_lba_range(slba, nlb, nsze);
@@ -1464,10 +1464,11 @@ static inline uint16_t nvme_check_bounds(NvmeNamespace *ns, uint64_t slba,
 static int nvme_block_status_all(NvmeNamespace *ns, uint64_t slba,
                                  uint32_t nlb, int flags)
 {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     BlockDriverState *bs = blk_bs(ns->blkconf.blk);
 
-    int64_t pnum = 0, bytes = nvme_l2b(ns, nlb);
-    int64_t offset = nvme_l2b(ns, slba);
+    int64_t pnum = 0, bytes = nvme_l2b(nvm, nlb);
+    int64_t offset = nvme_l2b(nvm, slba);
     int ret;
 
     /*
@@ -1888,6 +1889,7 @@ static void nvme_rw_cb(void *opaque, int ret)
 {
     NvmeRequest *req = opaque;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
 
     BlockBackend *blk = ns->blkconf.blk;
 
@@ -1897,14 +1899,14 @@ static void nvme_rw_cb(void *opaque, int ret)
         goto out;
     }
 
-    if (ns->lbaf.ms) {
+    if (nvm->lbaf.ms) {
         NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
         uint64_t slba = le64_to_cpu(rw->slba);
         uint32_t nlb = (uint32_t)le16_to_cpu(rw->nlb) + 1;
-        uint64_t offset = nvme_moff(ns, slba);
+        uint64_t offset = nvme_moff(nvm, slba);
 
         if (req->cmd.opcode == NVME_CMD_WRITE_ZEROES) {
-            size_t mlen = nvme_m2b(ns, nlb);
+            size_t mlen = nvme_m2b(nvm, nlb);
 
             req->aiocb = blk_aio_pwrite_zeroes(blk, offset, mlen,
                                                BDRV_REQ_MAY_UNMAP,
@@ -1912,7 +1914,7 @@ static void nvme_rw_cb(void *opaque, int ret)
             return;
         }
 
-        if (nvme_ns_ext(ns) || req->cmd.mptr) {
+        if (nvme_ns_ext(nvm) || req->cmd.mptr) {
             uint16_t status;
 
             nvme_sg_unmap(&req->sg);
@@ -1939,6 +1941,7 @@ static void nvme_verify_cb(void *opaque, int ret)
     NvmeBounceContext *ctx = opaque;
     NvmeRequest *req = ctx->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     BlockBackend *blk = ns->blkconf.blk;
     BlockAcctCookie *acct = &req->acct;
     BlockAcctStats *stats = blk_get_stats(blk);
@@ -1960,7 +1963,7 @@ static void nvme_verify_cb(void *opaque, int ret)
 
     block_acct_done(stats, acct);
 
-    if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+    if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
         status = nvme_dif_mangle_mdata(ns, ctx->mdata.bounce,
                                        ctx->mdata.iov.size, slba);
         if (status) {
@@ -1968,7 +1971,7 @@ static void nvme_verify_cb(void *opaque, int ret)
             goto out;
         }
 
-        req->status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
+        req->status = nvme_dif_check(nvm, ctx->data.bounce, ctx->data.iov.size,
                                      ctx->mdata.bounce, ctx->mdata.iov.size,
                                      prinfo, slba, apptag, appmask, &reftag);
     }
@@ -1991,11 +1994,12 @@ static void nvme_verify_mdata_in_cb(void *opaque, int ret)
     NvmeBounceContext *ctx = opaque;
     NvmeRequest *req = ctx->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     uint64_t slba = le64_to_cpu(rw->slba);
     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
-    size_t mlen = nvme_m2b(ns, nlb);
-    uint64_t offset = nvme_moff(ns, slba);
+    size_t mlen = nvme_m2b(nvm, nlb);
+    uint64_t offset = nvme_moff(nvm, slba);
     BlockBackend *blk = ns->blkconf.blk;
 
     trace_pci_nvme_verify_mdata_in_cb(nvme_cid(req), blk_name(blk));
@@ -2033,6 +2037,7 @@ static void nvme_compare_mdata_cb(void *opaque, int ret)
 {
     NvmeRequest *req = opaque;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeCtrl *n = nvme_ctrl(req);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
@@ -2063,14 +2068,14 @@ static void nvme_compare_mdata_cb(void *opaque, int ret)
         goto out;
     }
 
-    if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+    if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
         uint64_t slba = le64_to_cpu(rw->slba);
         uint8_t *bufp;
         uint8_t *mbufp = ctx->mdata.bounce;
         uint8_t *end = mbufp + ctx->mdata.iov.size;
         int16_t pil = 0;
 
-        status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
+        status = nvme_dif_check(nvm, ctx->data.bounce, ctx->data.iov.size,
                                 ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
                                 slba, apptag, appmask, &reftag);
         if (status) {
@@ -2082,12 +2087,12 @@ static void nvme_compare_mdata_cb(void *opaque, int ret)
          * When formatted with protection information, do not compare the DIF
          * tuple.
          */
-        if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
-            pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
+        if (!(nvm->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
+            pil = nvm->lbaf.ms - sizeof(NvmeDifTuple);
         }
 
-        for (bufp = buf; mbufp < end; bufp += ns->lbaf.ms, mbufp += ns->lbaf.ms) {
-            if (memcmp(bufp + pil, mbufp + pil, ns->lbaf.ms - pil)) {
+        for (bufp = buf; mbufp < end; bufp += nvm->lbaf.ms, mbufp += nvm->lbaf.ms) {
+            if (memcmp(bufp + pil, mbufp + pil, nvm->lbaf.ms - pil)) {
                 req->status = NVME_CMP_FAILURE;
                 goto out;
             }
@@ -2120,6 +2125,7 @@ static void nvme_compare_data_cb(void *opaque, int ret)
     NvmeRequest *req = opaque;
     NvmeCtrl *n = nvme_ctrl(req);
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     BlockBackend *blk = ns->blkconf.blk;
     BlockAcctCookie *acct = &req->acct;
     BlockAcctStats *stats = blk_get_stats(blk);
@@ -2150,12 +2156,12 @@ static void nvme_compare_data_cb(void *opaque, int ret)
         goto out;
     }
 
-    if (ns->lbaf.ms) {
+    if (nvm->lbaf.ms) {
         NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
         uint64_t slba = le64_to_cpu(rw->slba);
         uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
-        size_t mlen = nvme_m2b(ns, nlb);
-        uint64_t offset = nvme_moff(ns, slba);
+        size_t mlen = nvme_m2b(nvm, nlb);
+        uint64_t offset = nvme_moff(nvm, slba);
 
         ctx->mdata.bounce = g_malloc(mlen);
 
@@ -2232,6 +2238,7 @@ static void nvme_dsm_md_cb(void *opaque, int ret)
     NvmeDSMAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeDsmRange *range;
     uint64_t slba;
     uint32_t nlb;
@@ -2241,7 +2248,7 @@ static void nvme_dsm_md_cb(void *opaque, int ret)
         goto done;
     }
 
-    if (!ns->lbaf.ms) {
+    if (!nvm->lbaf.ms) {
         nvme_dsm_cb(iocb, 0);
         return;
     }
@@ -2265,8 +2272,8 @@ static void nvme_dsm_md_cb(void *opaque, int ret)
         nvme_dsm_cb(iocb, 0);
     }
 
-    iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk, nvme_moff(ns, slba),
-                                        nvme_m2b(ns, nlb), BDRV_REQ_MAY_UNMAP,
+    iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk, nvme_moff(nvm, slba),
+                                        nvme_m2b(nvm, nlb), BDRV_REQ_MAY_UNMAP,
                                         nvme_dsm_cb, iocb);
     return;
 
@@ -2281,6 +2288,7 @@ static void nvme_dsm_cb(void *opaque, int ret)
     NvmeRequest *req = iocb->req;
     NvmeCtrl *n = nvme_ctrl(req);
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeDsmRange *range;
     uint64_t slba;
     uint32_t nlb;
@@ -2306,14 +2314,14 @@ next:
         goto next;
     }
 
-    if (nvme_check_bounds(ns, slba, nlb)) {
+    if (nvme_check_bounds(nvm, slba, nlb)) {
         trace_pci_nvme_err_invalid_lba_range(slba, nlb,
-                                             ns->id_ns.nsze);
+                                             nvm->id_ns.nsze);
         goto next;
     }
 
-    iocb->aiocb = blk_aio_pdiscard(ns->blkconf.blk, nvme_l2b(ns, slba),
-                                   nvme_l2b(ns, nlb),
+    iocb->aiocb = blk_aio_pdiscard(ns->blkconf.blk, nvme_l2b(nvm, slba),
+                                   nvme_l2b(nvm, nlb),
                                    nvme_dsm_md_cb, iocb);
     return;
 
@@ -2362,11 +2370,12 @@ static uint16_t nvme_verify(NvmeCtrl *n, NvmeRequest *req)
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     BlockBackend *blk = ns->blkconf.blk;
     uint64_t slba = le64_to_cpu(rw->slba);
     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
-    size_t len = nvme_l2b(ns, nlb);
-    int64_t offset = nvme_l2b(ns, slba);
+    size_t len = nvme_l2b(nvm, nlb);
+    int64_t offset = nvme_l2b(nvm, slba);
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
     uint32_t reftag = le32_to_cpu(rw->reftag);
     NvmeBounceContext *ctx = NULL;
@@ -2374,8 +2383,8 @@ static uint16_t nvme_verify(NvmeCtrl *n, NvmeRequest *req)
 
     trace_pci_nvme_verify(nvme_cid(req), nvme_nsid(ns), slba, nlb);
 
-    if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
-        status = nvme_check_prinfo(ns, prinfo, slba, reftag);
+    if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
+        status = nvme_check_prinfo(nvm, prinfo, slba, reftag);
         if (status) {
             return status;
         }
@@ -2389,7 +2398,7 @@ static uint16_t nvme_verify(NvmeCtrl *n, NvmeRequest *req)
         return NVME_INVALID_FIELD | NVME_DNR;
     }
 
-    status = nvme_check_bounds(ns, slba, nlb);
+    status = nvme_check_bounds(nvm, slba, nlb);
     if (status) {
         return status;
     }
@@ -2519,6 +2528,7 @@ static void nvme_copy_out_cb(void *opaque, int ret)
     NvmeCopyAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeCopySourceRange *range;
     uint32_t nlb;
     size_t mlen;
@@ -2531,7 +2541,7 @@ static void nvme_copy_out_cb(void *opaque, int ret)
         goto out;
     }
 
-    if (!ns->lbaf.ms) {
+    if (!nvm->lbaf.ms) {
         nvme_copy_out_completed_cb(iocb, 0);
         return;
     }
@@ -2539,13 +2549,13 @@ static void nvme_copy_out_cb(void *opaque, int ret)
     range = &iocb->ranges[iocb->idx];
     nlb = le32_to_cpu(range->nlb) + 1;
 
-    mlen = nvme_m2b(ns, nlb);
-    mbounce = iocb->bounce + nvme_l2b(ns, nlb);
+    mlen = nvme_m2b(nvm, nlb);
+    mbounce = iocb->bounce + nvme_l2b(nvm, nlb);
 
     qemu_iovec_reset(&iocb->iov);
     qemu_iovec_add(&iocb->iov, mbounce, mlen);
 
-    iocb->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_moff(ns, iocb->slba),
+    iocb->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_moff(nvm, iocb->slba),
                                   &iocb->iov, 0, nvme_copy_out_completed_cb,
                                   iocb);
 
@@ -2560,6 +2570,7 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
     NvmeCopyAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeCopySourceRange *range;
     uint32_t nlb;
     size_t len;
@@ -2574,11 +2585,11 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
 
     range = &iocb->ranges[iocb->idx];
     nlb = le32_to_cpu(range->nlb) + 1;
-    len = nvme_l2b(ns, nlb);
+    len = nvme_l2b(nvm, nlb);
 
     trace_pci_nvme_copy_out(iocb->slba, nlb);
 
-    if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+    if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
         NvmeCopyCmd *copy = (NvmeCopyCmd *)&req->cmd;
 
         uint16_t prinfor = ((copy->control[0] >> 4) & 0xf);
@@ -2589,10 +2600,10 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
         uint32_t reftag = le32_to_cpu(range->reftag);
 
         uint64_t slba = le64_to_cpu(range->slba);
-        size_t mlen = nvme_m2b(ns, nlb);
-        uint8_t *mbounce = iocb->bounce + nvme_l2b(ns, nlb);
+        size_t mlen = nvme_m2b(nvm, nlb);
+        uint8_t *mbounce = iocb->bounce + nvme_l2b(nvm, nlb);
 
-        status = nvme_dif_check(ns, iocb->bounce, len, mbounce, mlen, prinfor,
+        status = nvme_dif_check(nvm, iocb->bounce, len, mbounce, mlen, prinfor,
                                 slba, apptag, appmask, &reftag);
         if (status) {
             goto invalid;
@@ -2602,15 +2613,15 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
         appmask = le16_to_cpu(copy->appmask);
 
         if (prinfow & NVME_PRINFO_PRACT) {
-            status = nvme_check_prinfo(ns, prinfow, iocb->slba, iocb->reftag);
+            status = nvme_check_prinfo(nvm, prinfow, iocb->slba, iocb->reftag);
             if (status) {
                 goto invalid;
             }
 
-            nvme_dif_pract_generate_dif(ns, iocb->bounce, len, mbounce, mlen,
+            nvme_dif_pract_generate_dif(nvm, iocb->bounce, len, mbounce, mlen,
                                         apptag, &iocb->reftag);
         } else {
-            status = nvme_dif_check(ns, iocb->bounce, len, mbounce, mlen,
+            status = nvme_dif_check(nvm, iocb->bounce, len, mbounce, mlen,
                                     prinfow, iocb->slba, apptag, appmask,
                                     &iocb->reftag);
             if (status) {
@@ -2619,7 +2630,7 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
         }
     }
 
-    status = nvme_check_bounds(ns, iocb->slba, nlb);
+    status = nvme_check_bounds(nvm, iocb->slba, nlb);
     if (status) {
         goto invalid;
     }
@@ -2636,7 +2647,7 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
     qemu_iovec_reset(&iocb->iov);
     qemu_iovec_add(&iocb->iov, iocb->bounce, len);
 
-    iocb->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_l2b(ns, iocb->slba),
+    iocb->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_l2b(nvm, iocb->slba),
                                   &iocb->iov, 0, nvme_copy_out_cb, iocb);
 
     return;
@@ -2659,6 +2670,7 @@ static void nvme_copy_in_cb(void *opaque, int ret)
     NvmeCopyAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeCopySourceRange *range;
     uint64_t slba;
     uint32_t nlb;
@@ -2670,7 +2682,7 @@ static void nvme_copy_in_cb(void *opaque, int ret)
         goto out;
     }
 
-    if (!ns->lbaf.ms) {
+    if (!nvm->lbaf.ms) {
         nvme_copy_in_completed_cb(iocb, 0);
         return;
     }
@@ -2680,10 +2692,10 @@ static void nvme_copy_in_cb(void *opaque, int ret)
     nlb = le32_to_cpu(range->nlb) + 1;
 
     qemu_iovec_reset(&iocb->iov);
-    qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(ns, nlb),
-                   nvme_m2b(ns, nlb));
+    qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(nvm, nlb),
+                   nvme_m2b(nvm, nlb));
 
-    iocb->aiocb = blk_aio_preadv(ns->blkconf.blk, nvme_moff(ns, slba),
+    iocb->aiocb = blk_aio_preadv(ns->blkconf.blk, nvme_moff(nvm, slba),
                                  &iocb->iov, 0, nvme_copy_in_completed_cb,
                                  iocb);
     return;
@@ -2697,6 +2709,7 @@ static void nvme_copy_cb(void *opaque, int ret)
     NvmeCopyAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeCopySourceRange *range;
     uint64_t slba;
     uint32_t nlb;
@@ -2717,16 +2730,16 @@ static void nvme_copy_cb(void *opaque, int ret)
     range = &iocb->ranges[iocb->idx];
     slba = le64_to_cpu(range->slba);
     nlb = le32_to_cpu(range->nlb) + 1;
-    len = nvme_l2b(ns, nlb);
+    len = nvme_l2b(nvm, nlb);
 
     trace_pci_nvme_copy_source_range(slba, nlb);
 
-    if (nlb > le16_to_cpu(ns->id_ns.mssrl)) {
+    if (nlb > le16_to_cpu(nvm->id_ns.mssrl)) {
         status = NVME_CMD_SIZE_LIMIT | NVME_DNR;
         goto invalid;
     }
 
-    status = nvme_check_bounds(ns, slba, nlb);
+    status = nvme_check_bounds(nvm, slba, nlb);
     if (status) {
         goto invalid;
     }
@@ -2748,7 +2761,7 @@ static void nvme_copy_cb(void *opaque, int ret)
     qemu_iovec_reset(&iocb->iov);
     qemu_iovec_add(&iocb->iov, iocb->bounce, len);
 
-    iocb->aiocb = blk_aio_preadv(ns->blkconf.blk, nvme_l2b(ns, slba),
+    iocb->aiocb = blk_aio_preadv(ns->blkconf.blk, nvme_l2b(nvm, slba),
                                  &iocb->iov, 0, nvme_copy_in_cb, iocb);
     return;
 
@@ -2765,6 +2778,7 @@ done:
 static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
 {
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeCopyCmd *copy = (NvmeCopyCmd *)&req->cmd;
     NvmeCopyAIOCB *iocb = blk_aio_get(&nvme_copy_aiocb_info, ns->blkconf.blk,
                                       nvme_misc_cb, req);
@@ -2780,7 +2794,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
     iocb->ranges = NULL;
     iocb->zone = NULL;
 
-    if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) &&
+    if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps) &&
         ((prinfor & NVME_PRINFO_PRACT) != (prinfow & NVME_PRINFO_PRACT))) {
         status = NVME_INVALID_FIELD | NVME_DNR;
         goto invalid;
@@ -2792,7 +2806,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
         goto invalid;
     }
 
-    if (nr > ns->id_ns.msrc + 1) {
+    if (nr > nvm->id_ns.msrc + 1) {
         status = NVME_CMD_SIZE_LIMIT | NVME_DNR;
         goto invalid;
     }
@@ -2828,8 +2842,8 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
     iocb->nr = nr;
     iocb->idx = 0;
     iocb->reftag = le32_to_cpu(copy->reftag);
-    iocb->bounce = g_malloc_n(le16_to_cpu(ns->id_ns.mssrl),
-                              ns->lbasz + ns->lbaf.ms);
+    iocb->bounce = g_malloc_n(le16_to_cpu(nvm->id_ns.mssrl),
+                              nvm->lbasz + nvm->lbaf.ms);
 
     qemu_iovec_init(&iocb->iov, 1);
 
@@ -2853,24 +2867,25 @@ static uint16_t nvme_compare(NvmeCtrl *n, NvmeRequest *req)
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     BlockBackend *blk = ns->blkconf.blk;
     uint64_t slba = le64_to_cpu(rw->slba);
     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
-    size_t data_len = nvme_l2b(ns, nlb);
+    size_t data_len = nvme_l2b(nvm, nlb);
     size_t len = data_len;
-    int64_t offset = nvme_l2b(ns, slba);
+    int64_t offset = nvme_l2b(nvm, slba);
     struct nvme_compare_ctx *ctx = NULL;
     uint16_t status;
 
     trace_pci_nvme_compare(nvme_cid(req), nvme_nsid(ns), slba, nlb);
 
-    if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) && (prinfo & NVME_PRINFO_PRACT)) {
+    if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps) && (prinfo & NVME_PRINFO_PRACT)) {
         return NVME_INVALID_PROT_INFO | NVME_DNR;
     }
 
-    if (nvme_ns_ext(ns)) {
-        len += nvme_m2b(ns, nlb);
+    if (nvme_ns_ext(nvm)) {
+        len += nvme_m2b(nvm, nlb);
     }
 
     status = nvme_check_mdts(n, len);
@@ -2878,7 +2893,7 @@ static uint16_t nvme_compare(NvmeCtrl *n, NvmeRequest *req)
         return status;
     }
 
-    status = nvme_check_bounds(ns, slba, nlb);
+    status = nvme_check_bounds(nvm, slba, nlb);
     if (status) {
         return status;
     }
@@ -3051,22 +3066,23 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req)
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     uint64_t slba = le64_to_cpu(rw->slba);
     uint32_t nlb = (uint32_t)le16_to_cpu(rw->nlb) + 1;
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
-    uint64_t data_size = nvme_l2b(ns, nlb);
+    uint64_t data_size = nvme_l2b(nvm, nlb);
     uint64_t mapped_size = data_size;
     uint64_t data_offset;
     BlockBackend *blk = ns->blkconf.blk;
     uint16_t status;
 
-    if (nvme_ns_ext(ns)) {
-        mapped_size += nvme_m2b(ns, nlb);
+    if (nvme_ns_ext(nvm)) {
+        mapped_size += nvme_m2b(nvm, nlb);
 
-        if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+        if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
             bool pract = prinfo & NVME_PRINFO_PRACT;
 
-            if (pract && ns->lbaf.ms == 8) {
+            if (pract && nvm->lbaf.ms == 8) {
                 mapped_size = data_size;
             }
         }
@@ -3079,7 +3095,7 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req)
         goto invalid;
     }
 
-    status = nvme_check_bounds(ns, slba, nlb);
+    status = nvme_check_bounds(nvm, slba, nlb);
     if (status) {
         goto invalid;
     }
@@ -3099,7 +3115,7 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req)
         }
     }
 
-    if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+    if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
         return nvme_dif_rw(n, req);
     }
 
@@ -3108,7 +3124,7 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req)
         goto invalid;
     }
 
-    data_offset = nvme_l2b(ns, slba);
+    data_offset = nvme_l2b(nvm, slba);
 
     block_acct_start(blk_get_stats(blk), &req->acct, data_size,
                      BLOCK_ACCT_READ);
@@ -3125,11 +3141,12 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     uint64_t slba = le64_to_cpu(rw->slba);
     uint32_t nlb = (uint32_t)le16_to_cpu(rw->nlb) + 1;
     uint16_t ctrl = le16_to_cpu(rw->control);
     uint8_t prinfo = NVME_RW_PRINFO(ctrl);
-    uint64_t data_size = nvme_l2b(ns, nlb);
+    uint64_t data_size = nvme_l2b(nvm, nlb);
     uint64_t mapped_size = data_size;
     uint64_t data_offset;
     NvmeZone *zone;
@@ -3137,14 +3154,14 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
     BlockBackend *blk = ns->blkconf.blk;
     uint16_t status;
 
-    if (nvme_ns_ext(ns)) {
-        mapped_size += nvme_m2b(ns, nlb);
+    if (nvme_ns_ext(nvm)) {
+        mapped_size += nvme_m2b(nvm, nlb);
 
-        if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+        if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
             bool pract = prinfo & NVME_PRINFO_PRACT;
 
-            if (pract && ns->lbaf.ms == 8) {
-                mapped_size -= nvme_m2b(ns, nlb);
+            if (pract && nvm->lbaf.ms == 8) {
+                mapped_size -= nvme_m2b(nvm, nlb);
             }
         }
     }
@@ -3159,7 +3176,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
         }
     }
 
-    status = nvme_check_bounds(ns, slba, nlb);
+    status = nvme_check_bounds(nvm, slba, nlb);
     if (status) {
         goto invalid;
     }
@@ -3189,7 +3206,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
             rw->slba = cpu_to_le64(slba);
             res->slba = cpu_to_le64(slba);
 
-            switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+            switch (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
             case NVME_ID_NS_DPS_TYPE_1:
                 if (!piremap) {
                     return NVME_INVALID_PROT_INFO | NVME_DNR;
@@ -3227,9 +3244,9 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
         zone->w_ptr += nlb;
     }
 
-    data_offset = nvme_l2b(ns, slba);
+    data_offset = nvme_l2b(nvm, slba);
 
-    if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+    if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
         return nvme_dif_rw(n, req);
     }
 
@@ -3273,6 +3290,7 @@ static inline uint16_t nvme_zone_append(NvmeCtrl *n, NvmeRequest *req)
 static uint16_t nvme_get_mgmt_zone_slba_idx(NvmeNamespace *ns, NvmeCmd *c,
                                             uint64_t *slba, uint32_t *zone_idx)
 {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeNamespaceZoned *zoned;
 
     uint32_t dw10 = le32_to_cpu(c->cdw10);
@@ -3286,8 +3304,8 @@ static uint16_t nvme_get_mgmt_zone_slba_idx(NvmeNamespace *ns, NvmeCmd *c,
     zoned = NVME_NAMESPACE_ZONED(ns);
 
     *slba = ((uint64_t)dw11) << 32 | dw10;
-    if (unlikely(*slba >= ns->id_ns.nsze)) {
-        trace_pci_nvme_err_invalid_lba_range(*slba, 0, ns->id_ns.nsze);
+    if (unlikely(*slba >= nvm->id_ns.nsze)) {
+        trace_pci_nvme_err_invalid_lba_range(*slba, 0, nvm->id_ns.nsze);
         *slba = 0;
         return NVME_LBA_RANGE | NVME_DNR;
     }
@@ -3506,6 +3524,8 @@ static void nvme_zone_reset_epilogue_cb(void *opaque, int ret)
     NvmeZoneResetAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
     int64_t moff;
     int count;
 
@@ -3514,13 +3534,13 @@ static void nvme_zone_reset_epilogue_cb(void *opaque, int ret)
         return;
     }
 
-    if (!ns->lbaf.ms) {
+    if (!nvm->lbaf.ms) {
         nvme_zone_reset_cb(iocb, 0);
         return;
     }
 
-    moff = nvme_moff(ns, iocb->zone->d.zslba);
-    count = nvme_m2b(ns, NVME_NAMESPACE_ZONED(ns)->zone_size);
+    moff = nvme_moff(nvm, iocb->zone->d.zslba);
+    count = nvme_m2b(nvm, zoned->zone_size);
 
     iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk, moff, count,
                                         BDRV_REQ_MAY_UNMAP,
@@ -3533,6 +3553,7 @@ static void nvme_zone_reset_cb(void *opaque, int ret)
     NvmeZoneResetAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
 
     if (ret < 0) {
@@ -3573,8 +3594,8 @@ static void nvme_zone_reset_cb(void *opaque, int ret)
         trace_pci_nvme_zns_zone_reset(zone->d.zslba);
 
         iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk,
-                                            nvme_l2b(ns, zone->d.zslba),
-                                            nvme_l2b(ns, zoned->zone_size),
+                                            nvme_l2b(nvm, zone->d.zslba),
+                                            nvme_l2b(nvm, zoned->zone_size),
                                             BDRV_REQ_MAY_UNMAP,
                                             nvme_zone_reset_epilogue_cb,
                                             iocb);
@@ -4475,7 +4496,8 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeRequest *req, bool active)
     }
 
     if (active || ns->csi == NVME_CSI_NVM) {
-        return nvme_c2h(n, (uint8_t *)&ns->id_ns, sizeof(NvmeIdNs), req);
+        NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
+        return nvme_c2h(n, (uint8_t *)&nvm->id_ns, sizeof(NvmeIdNs), req);
     }
 
     return NVME_INVALID_CMD_SET | NVME_DNR;
@@ -4994,6 +5016,7 @@ static uint16_t nvme_set_feature_timestamp(NvmeCtrl *n, NvmeRequest *req)
 static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
 {
     NvmeNamespace *ns = NULL;
+    NvmeNamespaceNvm *nvm;
 
     NvmeCmd *cmd = &req->cmd;
     uint32_t dw10 = le32_to_cpu(cmd->cdw10);
@@ -5068,7 +5091,9 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
                     continue;
                 }
 
-                if (NVME_ID_NS_NSFEAT_DULBE(ns->id_ns.nsfeat)) {
+                nvm = NVME_NAMESPACE_NVM(ns);
+
+                if (NVME_ID_NS_NSFEAT_DULBE(nvm->id_ns.nsfeat)) {
                     ns->features.err_rec = dw11;
                 }
             }
@@ -5077,7 +5102,8 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
         }
 
         assert(ns);
-        if (NVME_ID_NS_NSFEAT_DULBE(ns->id_ns.nsfeat))  {
+        nvm = NVME_NAMESPACE_NVM(ns);
+        if (NVME_ID_NS_NSFEAT_DULBE(nvm->id_ns.nsfeat))  {
             ns->features.err_rec = dw11;
         }
         break;
@@ -5159,12 +5185,15 @@ static void nvme_update_dmrsl(NvmeCtrl *n)
 
     for (nsid = 1; nsid <= NVME_MAX_NAMESPACES; nsid++) {
         NvmeNamespace *ns = nvme_ns(n, nsid);
+        NvmeNamespaceNvm *nvm;
         if (!ns) {
             continue;
         }
 
+        nvm = NVME_NAMESPACE_NVM(ns);
+
         n->dmrsl = MIN_NON_ZERO(n->dmrsl,
-                                BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1));
+                                BDRV_REQUEST_MAX_BYTES / nvme_l2b(nvm, 1));
     }
 }
 
@@ -5306,6 +5335,7 @@ static const AIOCBInfo nvme_format_aiocb_info = {
 
 static void nvme_format_set(NvmeNamespace *ns, NvmeCmd *cmd)
 {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     uint32_t dw10 = le32_to_cpu(cmd->cdw10);
     uint8_t lbaf = dw10 & 0xf;
     uint8_t pi = (dw10 >> 5) & 0x7;
@@ -5314,8 +5344,8 @@ static void nvme_format_set(NvmeNamespace *ns, NvmeCmd *cmd)
 
     trace_pci_nvme_format_set(ns->params.nsid, lbaf, mset, pi, pil);
 
-    ns->id_ns.dps = (pil << 3) | pi;
-    ns->id_ns.flbas = lbaf | (mset << 4);
+    nvm->id_ns.dps = (pil << 3) | pi;
+    nvm->id_ns.flbas = lbaf | (mset << 4);
 
     nvme_ns_init_format(ns);
 }
@@ -5325,6 +5355,7 @@ static void nvme_format_ns_cb(void *opaque, int ret)
     NvmeFormatAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = iocb->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     int bytes;
 
     if (ret < 0) {
@@ -5334,8 +5365,8 @@ static void nvme_format_ns_cb(void *opaque, int ret)
 
     assert(ns);
 
-    if (iocb->offset < ns->size) {
-        bytes = MIN(BDRV_REQUEST_MAX_BYTES, ns->size - iocb->offset);
+    if (iocb->offset < nvm->size) {
+        bytes = MIN(BDRV_REQUEST_MAX_BYTES, nvm->size - iocb->offset);
 
         iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk, iocb->offset,
                                             bytes, BDRV_REQ_MAY_UNMAP,
@@ -5357,15 +5388,17 @@ done:
 
 static uint16_t nvme_format_check(NvmeNamespace *ns, uint8_t lbaf, uint8_t pi)
 {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
+
     if (nvme_ns_zoned(ns)) {
         return NVME_INVALID_FORMAT | NVME_DNR;
     }
 
-    if (lbaf > ns->id_ns.nlbaf) {
+    if (lbaf > nvm->id_ns.nlbaf) {
         return NVME_INVALID_FORMAT | NVME_DNR;
     }
 
-    if (pi && (ns->id_ns.lbaf[lbaf].ms < sizeof(NvmeDifTuple))) {
+    if (pi && (nvm->id_ns.lbaf[lbaf].ms < sizeof(NvmeDifTuple))) {
         return NVME_INVALID_FORMAT | NVME_DNR;
     }
 
@@ -6518,6 +6551,7 @@ static int nvme_init_subsys(NvmeCtrl *n, Error **errp)
 
 void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns)
 {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     uint32_t nsid = ns->params.nsid;
     assert(nsid && nsid <= NVME_MAX_NAMESPACES);
 
@@ -6525,7 +6559,7 @@ void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns)
     ns->attached++;
 
     n->dmrsl = MIN_NON_ZERO(n->dmrsl,
-                            BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1));
+                            BDRV_REQUEST_MAX_BYTES / nvme_l2b(nvm, 1));
 }
 
 static void nvme_realize(PCIDevice *pci_dev, Error **errp)
diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c
index cd0cea2b5ebd..26c7412eb523 100644
--- a/hw/nvme/dif.c
+++ b/hw/nvme/dif.c
@@ -16,10 +16,10 @@
 #include "dif.h"
 #include "trace.h"
 
-uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
-                           uint32_t reftag)
+uint16_t nvme_check_prinfo(NvmeNamespaceNvm *nvm, uint8_t prinfo,
+                           uint64_t slba, uint32_t reftag)
 {
-    if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_1) &&
+    if ((NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps) == NVME_ID_NS_DPS_TYPE_1) &&
         (prinfo & NVME_PRINFO_PRCHK_REF) && (slba & 0xffffffff) != reftag) {
         return NVME_INVALID_PROT_INFO | NVME_DNR;
     }
@@ -40,23 +40,23 @@ static uint16_t crc_t10dif(uint16_t crc, const unsigned char *buffer,
     return crc;
 }
 
-void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
-                                 uint8_t *mbuf, size_t mlen, uint16_t apptag,
-                                 uint32_t *reftag)
+void nvme_dif_pract_generate_dif(NvmeNamespaceNvm *nvm, uint8_t *buf,
+                                 size_t len, uint8_t *mbuf, size_t mlen,
+                                 uint16_t apptag, uint32_t *reftag)
 {
     uint8_t *end = buf + len;
     int16_t pil = 0;
 
-    if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
-        pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
+    if (!(nvm->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
+        pil = nvm->lbaf.ms - sizeof(NvmeDifTuple);
     }
 
-    trace_pci_nvme_dif_pract_generate_dif(len, ns->lbasz, ns->lbasz + pil,
+    trace_pci_nvme_dif_pract_generate_dif(len, nvm->lbasz, nvm->lbasz + pil,
                                           apptag, *reftag);
 
-    for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
+    for (; buf < end; buf += nvm->lbasz, mbuf += nvm->lbaf.ms) {
         NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
-        uint16_t crc = crc_t10dif(0x0, buf, ns->lbasz);
+        uint16_t crc = crc_t10dif(0x0, buf, nvm->lbasz);
 
         if (pil) {
             crc = crc_t10dif(crc, mbuf, pil);
@@ -66,18 +66,18 @@ void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
         dif->apptag = cpu_to_be16(apptag);
         dif->reftag = cpu_to_be32(*reftag);
 
-        if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
+        if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
             (*reftag)++;
         }
     }
 }
 
-static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif,
+static uint16_t nvme_dif_prchk(NvmeNamespaceNvm *nvm, NvmeDifTuple *dif,
                                uint8_t *buf, uint8_t *mbuf, size_t pil,
                                uint8_t prinfo, uint16_t apptag,
                                uint16_t appmask, uint32_t reftag)
 {
-    switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+    switch (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
     case NVME_ID_NS_DPS_TYPE_3:
         if (be32_to_cpu(dif->reftag) != 0xffffffff) {
             break;
@@ -97,7 +97,7 @@ static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif,
     }
 
     if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
-        uint16_t crc = crc_t10dif(0x0, buf, ns->lbasz);
+        uint16_t crc = crc_t10dif(0x0, buf, nvm->lbasz);
 
         if (pil) {
             crc = crc_t10dif(crc, mbuf, pil);
@@ -130,7 +130,7 @@ static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif,
     return NVME_SUCCESS;
 }
 
-uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
+uint16_t nvme_dif_check(NvmeNamespaceNvm *nvm, uint8_t *buf, size_t len,
                         uint8_t *mbuf, size_t mlen, uint8_t prinfo,
                         uint64_t slba, uint16_t apptag,
                         uint16_t appmask, uint32_t *reftag)
@@ -139,27 +139,27 @@ uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
     int16_t pil = 0;
     uint16_t status;
 
-    status = nvme_check_prinfo(ns, prinfo, slba, *reftag);
+    status = nvme_check_prinfo(nvm, prinfo, slba, *reftag);
     if (status) {
         return status;
     }
 
-    if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
-        pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
+    if (!(nvm->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
+        pil = nvm->lbaf.ms - sizeof(NvmeDifTuple);
     }
 
-    trace_pci_nvme_dif_check(prinfo, ns->lbasz + pil);
+    trace_pci_nvme_dif_check(prinfo, nvm->lbasz + pil);
 
-    for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
+    for (; buf < end; buf += nvm->lbasz, mbuf += nvm->lbaf.ms) {
         NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
 
-        status = nvme_dif_prchk(ns, dif, buf, mbuf, pil, prinfo, apptag,
+        status = nvme_dif_prchk(nvm, dif, buf, mbuf, pil, prinfo, apptag,
                                 appmask, *reftag);
         if (status) {
             return status;
         }
 
-        if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
+        if (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
             (*reftag)++;
         }
     }
@@ -170,21 +170,22 @@ uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
 uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
                                uint64_t slba)
 {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     BlockBackend *blk = ns->blkconf.blk;
     BlockDriverState *bs = blk_bs(blk);
 
-    int64_t moffset = 0, offset = nvme_l2b(ns, slba);
+    int64_t moffset = 0, offset = nvme_l2b(nvm, slba);
     uint8_t *mbufp, *end;
     bool zeroed;
     int16_t pil = 0;
-    int64_t bytes = (mlen / ns->lbaf.ms) << ns->lbaf.ds;
+    int64_t bytes = (mlen / nvm->lbaf.ms) << nvm->lbaf.ds;
     int64_t pnum = 0;
 
     Error *err = NULL;
 
 
-    if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
-        pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
+    if (!(nvm->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
+        pil = nvm->lbaf.ms - sizeof(NvmeDifTuple);
     }
 
     do {
@@ -206,15 +207,15 @@ uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
 
         if (zeroed) {
             mbufp = mbuf + moffset;
-            mlen = (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
+            mlen = (pnum >> nvm->lbaf.ds) * nvm->lbaf.ms;
             end = mbufp + mlen;
 
-            for (; mbufp < end; mbufp += ns->lbaf.ms) {
+            for (; mbufp < end; mbufp += nvm->lbaf.ms) {
                 memset(mbufp + pil, 0xff, sizeof(NvmeDifTuple));
             }
         }
 
-        moffset += (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
+        moffset += (pnum >> nvm->lbaf.ds) * nvm->lbaf.ms;
         offset += pnum;
     } while (pnum != bytes);
 
@@ -246,6 +247,7 @@ static void nvme_dif_rw_check_cb(void *opaque, int ret)
     NvmeBounceContext *ctx = opaque;
     NvmeRequest *req = ctx->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeCtrl *n = nvme_ctrl(req);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     uint64_t slba = le64_to_cpu(rw->slba);
@@ -269,7 +271,7 @@ static void nvme_dif_rw_check_cb(void *opaque, int ret)
         goto out;
     }
 
-    status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
+    status = nvme_dif_check(nvm, ctx->data.bounce, ctx->data.iov.size,
                             ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
                             slba, apptag, appmask, &reftag);
     if (status) {
@@ -284,7 +286,7 @@ static void nvme_dif_rw_check_cb(void *opaque, int ret)
         goto out;
     }
 
-    if (prinfo & NVME_PRINFO_PRACT && ns->lbaf.ms == 8) {
+    if (prinfo & NVME_PRINFO_PRACT && nvm->lbaf.ms == 8) {
         goto out;
     }
 
@@ -303,11 +305,12 @@ static void nvme_dif_rw_mdata_in_cb(void *opaque, int ret)
     NvmeBounceContext *ctx = opaque;
     NvmeRequest *req = ctx->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     uint64_t slba = le64_to_cpu(rw->slba);
     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
-    size_t mlen = nvme_m2b(ns, nlb);
-    uint64_t offset = nvme_moff(ns, slba);
+    size_t mlen = nvme_m2b(nvm, nlb);
+    uint64_t offset = nvme_moff(nvm, slba);
     BlockBackend *blk = ns->blkconf.blk;
 
     trace_pci_nvme_dif_rw_mdata_in_cb(nvme_cid(req), blk_name(blk));
@@ -334,9 +337,10 @@ static void nvme_dif_rw_mdata_out_cb(void *opaque, int ret)
     NvmeBounceContext *ctx = opaque;
     NvmeRequest *req = ctx->req;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     uint64_t slba = le64_to_cpu(rw->slba);
-    uint64_t offset = nvme_moff(ns, slba);
+    uint64_t offset = nvme_moff(nvm, slba);
     BlockBackend *blk = ns->blkconf.blk;
 
     trace_pci_nvme_dif_rw_mdata_out_cb(nvme_cid(req), blk_name(blk));
@@ -357,14 +361,15 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     BlockBackend *blk = ns->blkconf.blk;
     bool wrz = rw->opcode == NVME_CMD_WRITE_ZEROES;
     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
     uint64_t slba = le64_to_cpu(rw->slba);
-    size_t len = nvme_l2b(ns, nlb);
-    size_t mlen = nvme_m2b(ns, nlb);
+    size_t len = nvme_l2b(nvm, nlb);
+    size_t mlen = nvme_m2b(nvm, nlb);
     size_t mapped_len = len;
-    int64_t offset = nvme_l2b(ns, slba);
+    int64_t offset = nvme_l2b(nvm, slba);
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
     uint16_t apptag = le16_to_cpu(rw->apptag);
     uint16_t appmask = le16_to_cpu(rw->appmask);
@@ -388,9 +393,9 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
 
         if (pract) {
             uint8_t *mbuf, *end;
-            int16_t pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
+            int16_t pil = nvm->lbaf.ms - sizeof(NvmeDifTuple);
 
-            status = nvme_check_prinfo(ns, prinfo, slba, reftag);
+            status = nvme_check_prinfo(nvm, prinfo, slba, reftag);
             if (status) {
                 goto err;
             }
@@ -405,17 +410,17 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
             mbuf = ctx->mdata.bounce;
             end = mbuf + mlen;
 
-            if (ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT) {
+            if (nvm->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT) {
                 pil = 0;
             }
 
-            for (; mbuf < end; mbuf += ns->lbaf.ms) {
+            for (; mbuf < end; mbuf += nvm->lbaf.ms) {
                 NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
 
                 dif->apptag = cpu_to_be16(apptag);
                 dif->reftag = cpu_to_be32(reftag);
 
-                switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
+                switch (NVME_ID_NS_DPS_TYPE(nvm->id_ns.dps)) {
                 case NVME_ID_NS_DPS_TYPE_1:
                 case NVME_ID_NS_DPS_TYPE_2:
                     reftag++;
@@ -428,7 +433,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
         return NVME_NO_COMPLETE;
     }
 
-    if (nvme_ns_ext(ns) && !(pract && ns->lbaf.ms == 8)) {
+    if (nvme_ns_ext(nvm) && !(pract && nvm->lbaf.ms == 8)) {
         mapped_len += mlen;
     }
 
@@ -462,7 +467,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
     qemu_iovec_init(&ctx->mdata.iov, 1);
     qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
 
-    if (!(pract && ns->lbaf.ms == 8)) {
+    if (!(pract && nvm->lbaf.ms == 8)) {
         status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
                                    NVME_TX_DIRECTION_TO_DEVICE, req);
         if (status) {
@@ -470,18 +475,18 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
         }
     }
 
-    status = nvme_check_prinfo(ns, prinfo, slba, reftag);
+    status = nvme_check_prinfo(nvm, prinfo, slba, reftag);
     if (status) {
         goto err;
     }
 
     if (pract) {
         /* splice generated protection information into the buffer */
-        nvme_dif_pract_generate_dif(ns, ctx->data.bounce, ctx->data.iov.size,
+        nvme_dif_pract_generate_dif(nvm, ctx->data.bounce, ctx->data.iov.size,
                                     ctx->mdata.bounce, ctx->mdata.iov.size,
                                     apptag, &reftag);
     } else {
-        status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
+        status = nvme_dif_check(nvm, ctx->data.bounce, ctx->data.iov.size,
                                 ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
                                 slba, apptag, appmask, &reftag);
         if (status) {
diff --git a/hw/nvme/dif.h b/hw/nvme/dif.h
index e36fea30e71e..7d47299252ae 100644
--- a/hw/nvme/dif.h
+++ b/hw/nvme/dif.h
@@ -37,14 +37,14 @@ static const uint16_t t10_dif_crc_table[256] = {
     0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
 };
 
-uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
-                           uint32_t reftag);
+uint16_t nvme_check_prinfo(NvmeNamespaceNvm *nvm, uint8_t prinfo,
+                           uint64_t slba, uint32_t reftag);
 uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
                                uint64_t slba);
-void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
-                                 uint8_t *mbuf, size_t mlen, uint16_t apptag,
-                                 uint32_t *reftag);
-uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
+void nvme_dif_pract_generate_dif(NvmeNamespaceNvm *nvm, uint8_t *buf,
+                                 size_t len, uint8_t *mbuf, size_t mlen,
+                                 uint16_t apptag, uint32_t *reftag);
+uint16_t nvme_dif_check(NvmeNamespaceNvm *nvm, uint8_t *buf, size_t len,
                         uint8_t *mbuf, size_t mlen, uint8_t prinfo,
                         uint64_t slba, uint16_t apptag,
                         uint16_t appmask, uint32_t *reftag);
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index 183483969088..9b59beb0324d 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -28,14 +28,15 @@
 
 void nvme_ns_init_format(NvmeNamespace *ns)
 {
-    NvmeIdNs *id_ns = &ns->id_ns;
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
+    NvmeIdNs *id_ns = &nvm->id_ns;
     BlockDriverInfo bdi;
     int npdg, nlbas, ret;
 
-    ns->lbaf = id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(id_ns->flbas)];
-    ns->lbasz = 1 << ns->lbaf.ds;
+    nvm->lbaf = id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(id_ns->flbas)];
+    nvm->lbasz = 1 << nvm->lbaf.ds;
 
-    nlbas = ns->size / (ns->lbasz + ns->lbaf.ms);
+    nlbas = nvm->size / (nvm->lbasz + nvm->lbaf.ms);
 
     id_ns->nsze = cpu_to_le64(nlbas);
 
@@ -43,13 +44,13 @@ void nvme_ns_init_format(NvmeNamespace *ns)
     id_ns->ncap = id_ns->nsze;
     id_ns->nuse = id_ns->ncap;
 
-    ns->moff = (int64_t)nlbas << ns->lbaf.ds;
+    nvm->moff = (int64_t)nlbas << nvm->lbaf.ds;
 
-    npdg = ns->blkconf.discard_granularity / ns->lbasz;
+    npdg = nvm->discard_granularity / nvm->lbasz;
 
     ret = bdrv_get_info(blk_bs(ns->blkconf.blk), &bdi);
-    if (ret >= 0 && bdi.cluster_size > ns->blkconf.discard_granularity) {
-        npdg = bdi.cluster_size / ns->lbasz;
+    if (ret >= 0 && bdi.cluster_size > nvm->discard_granularity) {
+        npdg = bdi.cluster_size / nvm->lbasz;
     }
 
     id_ns->npda = id_ns->npdg = npdg - 1;
@@ -57,8 +58,9 @@ void nvme_ns_init_format(NvmeNamespace *ns)
 
 static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
 {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     static uint64_t ns_count;
-    NvmeIdNs *id_ns = &ns->id_ns;
+    NvmeIdNs *id_ns = &nvm->id_ns;
     uint8_t ds;
     uint16_t ms;
     int i;
@@ -66,7 +68,7 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
     ns->csi = NVME_CSI_NVM;
     ns->status = 0x0;
 
-    ns->id_ns.dlfeat = 0x1;
+    nvm->id_ns.dlfeat = 0x1;
 
     /* support DULBE and I/O optimization fields */
     id_ns->nsfeat |= (0x4 | 0x10);
@@ -82,12 +84,12 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
     }
 
     /* simple copy */
-    id_ns->mssrl = cpu_to_le16(ns->params.mssrl);
-    id_ns->mcl = cpu_to_le32(ns->params.mcl);
-    id_ns->msrc = ns->params.msrc;
+    id_ns->mssrl = cpu_to_le16(nvm->mssrl);
+    id_ns->mcl = cpu_to_le32(nvm->mcl);
+    id_ns->msrc = nvm->msrc;
     id_ns->eui64 = cpu_to_be64(ns->params.eui64);
 
-    ds = 31 - clz32(ns->blkconf.logical_block_size);
+    ds = 31 - clz32(nvm->lbasz);
     ms = ns->params.ms;
 
     id_ns->mc = NVME_ID_NS_MC_EXTENDED | NVME_ID_NS_MC_SEPARATE;
@@ -140,6 +142,7 @@ lbaf_found:
 
 static int nvme_ns_init_blk(NvmeNamespace *ns, Error **errp)
 {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     bool read_only;
 
     if (!blkconf_blocksizes(&ns->blkconf, errp)) {
@@ -156,9 +159,14 @@ static int nvme_ns_init_blk(NvmeNamespace *ns, Error **errp)
             MAX(ns->blkconf.logical_block_size, MIN_DISCARD_GRANULARITY);
     }
 
-    ns->size = blk_getlength(ns->blkconf.blk);
-    if (ns->size < 0) {
-        error_setg_errno(errp, -ns->size, "could not get blockdev size");
+    nvm->lbasz = ns->blkconf.logical_block_size;
+    nvm->discard_granularity = ns->blkconf.discard_granularity;
+    nvm->lbaf.ds = 31 - clz32(nvm->lbasz);
+    nvm->lbaf.ms = ns->params.ms;
+
+    nvm->size = blk_getlength(ns->blkconf.blk);
+    if (nvm->size < 0) {
+        error_setg_errno(errp, -nvm->size, "could not get blockdev size");
         return -1;
     }
 
@@ -167,6 +175,7 @@ static int nvme_ns_init_blk(NvmeNamespace *ns, Error **errp)
 
 static int nvme_zns_check_calc_geometry(NvmeNamespace *ns, Error **errp)
 {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
 
     uint64_t zone_size, zone_cap;
@@ -187,14 +196,14 @@ static int nvme_zns_check_calc_geometry(NvmeNamespace *ns, Error **errp)
                    "zone size %"PRIu64"B", zone_cap, zone_size);
         return -1;
     }
-    if (zone_size < ns->lbasz) {
+    if (zone_size < nvm->lbasz) {
         error_setg(errp, "zone size %"PRIu64"B too small, "
-                   "must be at least %zuB", zone_size, ns->lbasz);
+                   "must be at least %zuB", zone_size, nvm->lbasz);
         return -1;
     }
-    if (zone_cap < ns->lbasz) {
+    if (zone_cap < nvm->lbasz) {
         error_setg(errp, "zone capacity %"PRIu64"B too small, "
-                   "must be at least %zuB", zone_cap, ns->lbasz);
+                   "must be at least %zuB", zone_cap, nvm->lbasz);
         return -1;
     }
 
@@ -202,9 +211,9 @@ static int nvme_zns_check_calc_geometry(NvmeNamespace *ns, Error **errp)
      * Save the main zone geometry values to avoid
      * calculating them later again.
      */
-    zoned->zone_size = zone_size / ns->lbasz;
-    zoned->zone_capacity = zone_cap / ns->lbasz;
-    zoned->num_zones = le64_to_cpu(ns->id_ns.nsze) / zoned->zone_size;
+    zoned->zone_size = zone_size / nvm->lbasz;
+    zoned->zone_capacity = zone_cap / nvm->lbasz;
+    zoned->num_zones = le64_to_cpu(nvm->id_ns.nsze) / zoned->zone_size;
 
     /* Do a few more sanity checks of ZNS properties */
     if (!zoned->num_zones) {
@@ -258,6 +267,7 @@ static void nvme_zns_init_state(NvmeNamespaceZoned *zoned)
 
 static void nvme_zns_init(NvmeNamespace *ns)
 {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
     NvmeIdNsZoned *id_ns_z = &zoned->id_ns;
     int i;
@@ -273,16 +283,16 @@ static void nvme_zns_init(NvmeNamespace *ns)
         id_ns_z->ozcs |= NVME_ID_NS_ZONED_OZCS_CROSS_READ;
     }
 
-    for (i = 0; i <= ns->id_ns.nlbaf; i++) {
+    for (i = 0; i <= nvm->id_ns.nlbaf; i++) {
         id_ns_z->lbafe[i].zsze = cpu_to_le64(zoned->zone_size);
         id_ns_z->lbafe[i].zdes =
             zoned->zd_extension_size >> 6; /* Units of 64B */
     }
 
     ns->csi = NVME_CSI_ZONED;
-    ns->id_ns.nsze = cpu_to_le64(zoned->num_zones * zoned->zone_size);
-    ns->id_ns.ncap = ns->id_ns.nsze;
-    ns->id_ns.nuse = ns->id_ns.ncap;
+    nvm->id_ns.nsze = cpu_to_le64(zoned->num_zones * zoned->zone_size);
+    nvm->id_ns.ncap = nvm->id_ns.nsze;
+    nvm->id_ns.nuse = nvm->id_ns.ncap;
 
     /*
      * The device uses the BDRV_BLOCK_ZERO flag to determine the "deallocated"
@@ -291,13 +301,13 @@ static void nvme_zns_init(NvmeNamespace *ns)
      * we can only support DULBE if the zone size is a multiple of the
      * calculated NPDG.
      */
-    if (zoned->zone_size % (ns->id_ns.npdg + 1)) {
+    if (zoned->zone_size % (nvm->id_ns.npdg + 1)) {
         warn_report("the zone size (%"PRIu64" blocks) is not a multiple of "
                     "the calculated deallocation granularity (%d blocks); "
                     "DULBE support disabled",
-                    zoned->zone_size, ns->id_ns.npdg + 1);
+                    zoned->zone_size, nvm->id_ns.npdg + 1);
 
-        ns->id_ns.nsfeat &= ~0x4;
+        nvm->id_ns.nsfeat &= ~0x4;
     }
 }
 
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 9cfb172101a9..c5e08cf9e1c1 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -147,15 +147,32 @@ typedef struct NvmeNamespaceZoned {
     QTAILQ_HEAD(, NvmeZone) full_zones;
 } NvmeNamespaceZoned;
 
+enum {
+    NVME_NS_NVM_EXTENDED_LBA = 1 << 0,
+    NVME_NS_NVM_PI_FIRST     = 1 << 1,
+};
+
+typedef struct NvmeNamespaceNvm {
+    NvmeIdNs id_ns;
+
+    int64_t size;
+    int64_t moff;
+
+    NvmeLBAF lbaf;
+    size_t   lbasz;
+    uint32_t discard_granularity;
+
+    uint16_t mssrl;
+    uint32_t mcl;
+    uint8_t  msrc;
+
+    unsigned long flags;
+} NvmeNamespaceNvm;
+
 typedef struct NvmeNamespace {
     DeviceState  parent_obj;
     BlockConf    blkconf;
     int32_t      bootindex;
-    int64_t      size;
-    int64_t      moff;
-    NvmeIdNs     id_ns;
-    NvmeLBAF     lbaf;
-    size_t       lbasz;
     const uint32_t *iocs;
     uint8_t      csi;
     uint16_t     status;
@@ -169,9 +186,11 @@ typedef struct NvmeNamespace {
         uint32_t err_rec;
     } features;
 
+    NvmeNamespaceNvm   nvm;
     NvmeNamespaceZoned zoned;
 } NvmeNamespace;
 
+#define NVME_NAMESPACE_NVM(ns) (&(ns)->nvm)
 #define NVME_NAMESPACE_ZONED(ns) (&(ns)->zoned)
 
 static inline uint32_t nvme_nsid(NvmeNamespace *ns)
@@ -183,24 +202,24 @@ static inline uint32_t nvme_nsid(NvmeNamespace *ns)
     return 0;
 }
 
-static inline size_t nvme_l2b(NvmeNamespace *ns, uint64_t lba)
+static inline size_t nvme_l2b(NvmeNamespaceNvm *nvm, uint64_t lba)
 {
-    return lba << ns->lbaf.ds;
+    return lba << nvm->lbaf.ds;
 }
 
-static inline size_t nvme_m2b(NvmeNamespace *ns, uint64_t lba)
+static inline size_t nvme_m2b(NvmeNamespaceNvm *nvm, uint64_t lba)
 {
-    return ns->lbaf.ms * lba;
+    return nvm->lbaf.ms * lba;
 }
 
-static inline int64_t nvme_moff(NvmeNamespace *ns, uint64_t lba)
+static inline int64_t nvme_moff(NvmeNamespaceNvm *nvm, uint64_t lba)
 {
-    return ns->moff + nvme_m2b(ns, lba);
+    return nvm->moff + nvme_m2b(nvm, lba);
 }
 
-static inline bool nvme_ns_ext(NvmeNamespace *ns)
+static inline bool nvme_ns_ext(NvmeNamespaceNvm *nvm)
 {
-    return !!NVME_ID_NS_FLBAS_EXTENDED(ns->id_ns.flbas);
+    return !!NVME_ID_NS_FLBAS_EXTENDED(nvm->id_ns.flbas);
 }
 
 void nvme_ns_init_format(NvmeNamespace *ns);
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 07/16] hw/nvme: move BlockBackend to NvmeNamespaceNvm
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (5 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 06/16] hw/nvme: move nvm " Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 08/16] hw/nvme: hoist qdev state from namespace Klaus Jensen
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c | 66 +++++++++++++++++++++++++-------------------------
 hw/nvme/dif.c  | 14 +++++------
 hw/nvme/nvme.h |  6 +++++
 3 files changed, 46 insertions(+), 40 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 026dfaa71bda..3e561a13f76f 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -1465,7 +1465,7 @@ static int nvme_block_status_all(NvmeNamespace *ns, uint64_t slba,
                                  uint32_t nlb, int flags)
 {
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    BlockDriverState *bs = blk_bs(ns->blkconf.blk);
+    BlockDriverState *bs = blk_bs(nvme_blk(ns));
 
     int64_t pnum = 0, bytes = nvme_l2b(nvm, nlb);
     int64_t offset = nvme_l2b(nvm, slba);
@@ -1865,7 +1865,7 @@ void nvme_rw_complete_cb(void *opaque, int ret)
 {
     NvmeRequest *req = opaque;
     NvmeNamespace *ns = req->ns;
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
     BlockAcctCookie *acct = &req->acct;
     BlockAcctStats *stats = blk_get_stats(blk);
 
@@ -1891,7 +1891,7 @@ static void nvme_rw_cb(void *opaque, int ret)
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
 
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
 
     trace_pci_nvme_rw_cb(nvme_cid(req), blk_name(blk));
 
@@ -1942,7 +1942,7 @@ static void nvme_verify_cb(void *opaque, int ret)
     NvmeRequest *req = ctx->req;
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
     BlockAcctCookie *acct = &req->acct;
     BlockAcctStats *stats = blk_get_stats(blk);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
@@ -2000,7 +2000,7 @@ static void nvme_verify_mdata_in_cb(void *opaque, int ret)
     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
     size_t mlen = nvme_m2b(nvm, nlb);
     uint64_t offset = nvme_moff(nvm, slba);
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
 
     trace_pci_nvme_verify_mdata_in_cb(nvme_cid(req), blk_name(blk));
 
@@ -2046,7 +2046,7 @@ static void nvme_compare_mdata_cb(void *opaque, int ret)
     uint32_t reftag = le32_to_cpu(rw->reftag);
     struct nvme_compare_ctx *ctx = req->opaque;
     g_autofree uint8_t *buf = NULL;
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
     BlockAcctCookie *acct = &req->acct;
     BlockAcctStats *stats = blk_get_stats(blk);
     uint16_t status = NVME_SUCCESS;
@@ -2126,7 +2126,7 @@ static void nvme_compare_data_cb(void *opaque, int ret)
     NvmeCtrl *n = nvme_ctrl(req);
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
     BlockAcctCookie *acct = &req->acct;
     BlockAcctStats *stats = blk_get_stats(blk);
 
@@ -2272,7 +2272,7 @@ static void nvme_dsm_md_cb(void *opaque, int ret)
         nvme_dsm_cb(iocb, 0);
     }
 
-    iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk, nvme_moff(nvm, slba),
+    iocb->aiocb = blk_aio_pwrite_zeroes(nvme_blk(ns), nvme_moff(nvm, slba),
                                         nvme_m2b(nvm, nlb), BDRV_REQ_MAY_UNMAP,
                                         nvme_dsm_cb, iocb);
     return;
@@ -2320,7 +2320,7 @@ next:
         goto next;
     }
 
-    iocb->aiocb = blk_aio_pdiscard(ns->blkconf.blk, nvme_l2b(nvm, slba),
+    iocb->aiocb = blk_aio_pdiscard(nvme_blk(ns), nvme_l2b(nvm, slba),
                                    nvme_l2b(nvm, nlb),
                                    nvme_dsm_md_cb, iocb);
     return;
@@ -2341,7 +2341,7 @@ static uint16_t nvme_dsm(NvmeCtrl *n, NvmeRequest *req)
     trace_pci_nvme_dsm(nr, attr);
 
     if (attr & NVME_DSMGMT_AD) {
-        NvmeDSMAIOCB *iocb = blk_aio_get(&nvme_dsm_aiocb_info, ns->blkconf.blk,
+        NvmeDSMAIOCB *iocb = blk_aio_get(&nvme_dsm_aiocb_info, nvme_blk(ns),
                                          nvme_misc_cb, req);
 
         iocb->req = req;
@@ -2371,7 +2371,7 @@ static uint16_t nvme_verify(NvmeCtrl *n, NvmeRequest *req)
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
     uint64_t slba = le64_to_cpu(rw->slba);
     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
     size_t len = nvme_l2b(nvm, nlb);
@@ -2421,7 +2421,7 @@ static uint16_t nvme_verify(NvmeCtrl *n, NvmeRequest *req)
     block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
                      BLOCK_ACCT_READ);
 
-    req->aiocb = blk_aio_preadv(ns->blkconf.blk, offset, &ctx->data.iov, 0,
+    req->aiocb = blk_aio_preadv(nvme_blk(ns), offset, &ctx->data.iov, 0,
                                 nvme_verify_mdata_in_cb, ctx);
     return NVME_NO_COMPLETE;
 }
@@ -2472,7 +2472,7 @@ static void nvme_copy_bh(void *opaque)
     NvmeCopyAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
     NvmeNamespace *ns = req->ns;
-    BlockAcctStats *stats = blk_get_stats(ns->blkconf.blk);
+    BlockAcctStats *stats = blk_get_stats(nvme_blk(ns));
 
     if (iocb->idx != iocb->nr) {
         req->cqe.result = cpu_to_le32(iocb->idx);
@@ -2555,7 +2555,7 @@ static void nvme_copy_out_cb(void *opaque, int ret)
     qemu_iovec_reset(&iocb->iov);
     qemu_iovec_add(&iocb->iov, mbounce, mlen);
 
-    iocb->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_moff(nvm, iocb->slba),
+    iocb->aiocb = blk_aio_pwritev(nvme_blk(ns), nvme_moff(nvm, iocb->slba),
                                   &iocb->iov, 0, nvme_copy_out_completed_cb,
                                   iocb);
 
@@ -2647,7 +2647,7 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret)
     qemu_iovec_reset(&iocb->iov);
     qemu_iovec_add(&iocb->iov, iocb->bounce, len);
 
-    iocb->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_l2b(nvm, iocb->slba),
+    iocb->aiocb = blk_aio_pwritev(nvme_blk(ns), nvme_l2b(nvm, iocb->slba),
                                   &iocb->iov, 0, nvme_copy_out_cb, iocb);
 
     return;
@@ -2695,7 +2695,7 @@ static void nvme_copy_in_cb(void *opaque, int ret)
     qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(nvm, nlb),
                    nvme_m2b(nvm, nlb));
 
-    iocb->aiocb = blk_aio_preadv(ns->blkconf.blk, nvme_moff(nvm, slba),
+    iocb->aiocb = blk_aio_preadv(nvme_blk(ns), nvme_moff(nvm, slba),
                                  &iocb->iov, 0, nvme_copy_in_completed_cb,
                                  iocb);
     return;
@@ -2761,7 +2761,7 @@ static void nvme_copy_cb(void *opaque, int ret)
     qemu_iovec_reset(&iocb->iov);
     qemu_iovec_add(&iocb->iov, iocb->bounce, len);
 
-    iocb->aiocb = blk_aio_preadv(ns->blkconf.blk, nvme_l2b(nvm, slba),
+    iocb->aiocb = blk_aio_preadv(nvme_blk(ns), nvme_l2b(nvm, slba),
                                  &iocb->iov, 0, nvme_copy_in_cb, iocb);
     return;
 
@@ -2780,7 +2780,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeCopyCmd *copy = (NvmeCopyCmd *)&req->cmd;
-    NvmeCopyAIOCB *iocb = blk_aio_get(&nvme_copy_aiocb_info, ns->blkconf.blk,
+    NvmeCopyAIOCB *iocb = blk_aio_get(&nvme_copy_aiocb_info, nvme_blk(ns),
                                       nvme_misc_cb, req);
     uint16_t nr = copy->nr + 1;
     uint8_t format = copy->control[0] & 0xf;
@@ -2847,9 +2847,9 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
 
     qemu_iovec_init(&iocb->iov, 1);
 
-    block_acct_start(blk_get_stats(ns->blkconf.blk), &iocb->acct.read, 0,
+    block_acct_start(blk_get_stats(nvme_blk(ns)), &iocb->acct.read, 0,
                      BLOCK_ACCT_READ);
-    block_acct_start(blk_get_stats(ns->blkconf.blk), &iocb->acct.write, 0,
+    block_acct_start(blk_get_stats(nvme_blk(ns)), &iocb->acct.write, 0,
                      BLOCK_ACCT_WRITE);
 
     req->aiocb = &iocb->common;
@@ -2868,7 +2868,7 @@ static uint16_t nvme_compare(NvmeCtrl *n, NvmeRequest *req)
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
     uint64_t slba = le64_to_cpu(rw->slba);
     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
@@ -2971,7 +2971,7 @@ static void nvme_flush_ns_cb(void *opaque, int ret)
         trace_pci_nvme_flush_ns(iocb->nsid);
 
         iocb->ns = NULL;
-        iocb->aiocb = blk_aio_flush(ns->blkconf.blk, nvme_flush_ns_cb, iocb);
+        iocb->aiocb = blk_aio_flush(nvme_blk(ns), nvme_flush_ns_cb, iocb);
         return;
     }
 
@@ -3073,7 +3073,7 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req)
     uint64_t data_size = nvme_l2b(nvm, nlb);
     uint64_t mapped_size = data_size;
     uint64_t data_offset;
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
     uint16_t status;
 
     if (nvme_ns_ext(nvm)) {
@@ -3151,7 +3151,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
     uint64_t data_offset;
     NvmeZone *zone;
     NvmeZonedResult *res = (NvmeZonedResult *)&req->cqe;
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
     uint16_t status;
 
     if (nvme_ns_ext(nvm)) {
@@ -3542,7 +3542,7 @@ static void nvme_zone_reset_epilogue_cb(void *opaque, int ret)
     moff = nvme_moff(nvm, iocb->zone->d.zslba);
     count = nvme_m2b(nvm, zoned->zone_size);
 
-    iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk, moff, count,
+    iocb->aiocb = blk_aio_pwrite_zeroes(nvme_blk(ns), moff, count,
                                         BDRV_REQ_MAY_UNMAP,
                                         nvme_zone_reset_cb, iocb);
     return;
@@ -3593,7 +3593,7 @@ static void nvme_zone_reset_cb(void *opaque, int ret)
 
         trace_pci_nvme_zns_zone_reset(zone->d.zslba);
 
-        iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk,
+        iocb->aiocb = blk_aio_pwrite_zeroes(nvme_blk(ns),
                                             nvme_l2b(nvm, zone->d.zslba),
                                             nvme_l2b(nvm, zoned->zone_size),
                                             BDRV_REQ_MAY_UNMAP,
@@ -3673,7 +3673,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
     case NVME_ZONE_ACTION_RESET:
         trace_pci_nvme_reset_zone(slba, zone_idx, all);
 
-        iocb = blk_aio_get(&nvme_zone_reset_aiocb_info, ns->blkconf.blk,
+        iocb = blk_aio_get(&nvme_zone_reset_aiocb_info, nvme_blk(ns),
                            nvme_misc_cb, req);
 
         iocb->req = req;
@@ -4076,7 +4076,7 @@ struct nvme_stats {
 
 static void nvme_set_blk_stats(NvmeNamespace *ns, struct nvme_stats *stats)
 {
-    BlockAcctStats *s = blk_get_stats(ns->blkconf.blk);
+    BlockAcctStats *s = blk_get_stats(nvme_blk(ns));
 
     stats->units_read += s->nr_bytes[BLOCK_ACCT_READ] >> BDRV_SECTOR_BITS;
     stats->units_written += s->nr_bytes[BLOCK_ACCT_WRITE] >> BDRV_SECTOR_BITS;
@@ -4942,7 +4942,7 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeRequest *req)
                 continue;
             }
 
-            result = blk_enable_write_cache(ns->blkconf.blk);
+            result = blk_enable_write_cache(nvme_blk(ns));
             if (result) {
                 break;
             }
@@ -5114,11 +5114,11 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
                 continue;
             }
 
-            if (!(dw11 & 0x1) && blk_enable_write_cache(ns->blkconf.blk)) {
-                blk_flush(ns->blkconf.blk);
+            if (!(dw11 & 0x1) && blk_enable_write_cache(nvme_blk(ns))) {
+                blk_flush(nvme_blk(ns));
             }
 
-            blk_set_enable_write_cache(ns->blkconf.blk, dw11 & 1);
+            blk_set_enable_write_cache(nvme_blk(ns), dw11 & 1);
         }
 
         break;
@@ -5368,7 +5368,7 @@ static void nvme_format_ns_cb(void *opaque, int ret)
     if (iocb->offset < nvm->size) {
         bytes = MIN(BDRV_REQUEST_MAX_BYTES, nvm->size - iocb->offset);
 
-        iocb->aiocb = blk_aio_pwrite_zeroes(ns->blkconf.blk, iocb->offset,
+        iocb->aiocb = blk_aio_pwrite_zeroes(nvme_blk(ns), iocb->offset,
                                             bytes, BDRV_REQ_MAY_UNMAP,
                                             nvme_format_ns_cb, iocb);
 
diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c
index 26c7412eb523..1b8f9ba2fb44 100644
--- a/hw/nvme/dif.c
+++ b/hw/nvme/dif.c
@@ -171,7 +171,7 @@ uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
                                uint64_t slba)
 {
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
     BlockDriverState *bs = blk_bs(blk);
 
     int64_t moffset = 0, offset = nvme_l2b(nvm, slba);
@@ -227,7 +227,7 @@ static void nvme_dif_rw_cb(void *opaque, int ret)
     NvmeBounceContext *ctx = opaque;
     NvmeRequest *req = ctx->req;
     NvmeNamespace *ns = req->ns;
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
 
     trace_pci_nvme_dif_rw_cb(nvme_cid(req), blk_name(blk));
 
@@ -311,7 +311,7 @@ static void nvme_dif_rw_mdata_in_cb(void *opaque, int ret)
     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
     size_t mlen = nvme_m2b(nvm, nlb);
     uint64_t offset = nvme_moff(nvm, slba);
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
 
     trace_pci_nvme_dif_rw_mdata_in_cb(nvme_cid(req), blk_name(blk));
 
@@ -341,7 +341,7 @@ static void nvme_dif_rw_mdata_out_cb(void *opaque, int ret)
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     uint64_t slba = le64_to_cpu(rw->slba);
     uint64_t offset = nvme_moff(nvm, slba);
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
 
     trace_pci_nvme_dif_rw_mdata_out_cb(nvme_cid(req), blk_name(blk));
 
@@ -362,7 +362,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    BlockBackend *blk = ns->blkconf.blk;
+    BlockBackend *blk = nvme_blk(ns);
     bool wrz = rw->opcode == NVME_CMD_WRITE_ZEROES;
     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
     uint64_t slba = le64_to_cpu(rw->slba);
@@ -451,7 +451,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
         block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
                          BLOCK_ACCT_READ);
 
-        req->aiocb = blk_aio_preadv(ns->blkconf.blk, offset, &ctx->data.iov, 0,
+        req->aiocb = blk_aio_preadv(nvme_blk(ns), offset, &ctx->data.iov, 0,
                                     nvme_dif_rw_mdata_in_cb, ctx);
         return NVME_NO_COMPLETE;
     }
@@ -497,7 +497,7 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
     block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
                      BLOCK_ACCT_WRITE);
 
-    req->aiocb = blk_aio_pwritev(ns->blkconf.blk, offset, &ctx->data.iov, 0,
+    req->aiocb = blk_aio_pwritev(nvme_blk(ns), offset, &ctx->data.iov, 0,
                                  nvme_dif_rw_mdata_out_cb, ctx);
 
     return NVME_NO_COMPLETE;
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index c5e08cf9e1c1..525bfd0ca831 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -155,6 +155,7 @@ enum {
 typedef struct NvmeNamespaceNvm {
     NvmeIdNs id_ns;
 
+    BlockBackend *blk;
     int64_t size;
     int64_t moff;
 
@@ -193,6 +194,11 @@ typedef struct NvmeNamespace {
 #define NVME_NAMESPACE_NVM(ns) (&(ns)->nvm)
 #define NVME_NAMESPACE_ZONED(ns) (&(ns)->zoned)
 
+static inline BlockBackend *nvme_blk(NvmeNamespace *ns)
+{
+    return NVME_NAMESPACE_NVM(ns)->blk;
+}
+
 static inline uint32_t nvme_nsid(NvmeNamespace *ns)
 {
     if (ns) {
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 08/16] hw/nvme: hoist qdev state from namespace
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (6 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 07/16] hw/nvme: move BlockBackend to NvmeNamespaceNvm Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 09/16] hw/nvme: hoist qdev state from subsystem Klaus Jensen
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c   |  32 +++---
 hw/nvme/ns.c     | 263 +++++++++++++++++++++++++----------------------
 hw/nvme/nvme.h   |  44 +++++---
 hw/nvme/subsys.c |   2 +-
 4 files changed, 186 insertions(+), 155 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 3e561a13f76f..67600d075d32 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -4620,10 +4620,10 @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeRequest *req,
                 continue;
             }
         }
-        if (ns->params.nsid <= min_nsid) {
+        if (ns->nsid <= min_nsid) {
             continue;
         }
-        list_ptr[j++] = cpu_to_le32(ns->params.nsid);
+        list_ptr[j++] = cpu_to_le32(ns->nsid);
         if (j == data_len / sizeof(uint32_t)) {
             break;
         }
@@ -4668,10 +4668,10 @@ static uint16_t nvme_identify_nslist_csi(NvmeCtrl *n, NvmeRequest *req,
                 continue;
             }
         }
-        if (ns->params.nsid <= min_nsid || c->csi != ns->csi) {
+        if (ns->nsid <= min_nsid || c->csi != ns->csi) {
             continue;
         }
-        list_ptr[j++] = cpu_to_le32(ns->params.nsid);
+        list_ptr[j++] = cpu_to_le32(ns->nsid);
         if (j == data_len / sizeof(uint32_t)) {
             break;
         }
@@ -4718,14 +4718,14 @@ static uint16_t nvme_identify_ns_descr_list(NvmeCtrl *n, NvmeRequest *req)
      */
     uuid.hdr.nidt = NVME_NIDT_UUID;
     uuid.hdr.nidl = NVME_NIDL_UUID;
-    memcpy(uuid.v, ns->params.uuid.data, NVME_NIDL_UUID);
+    memcpy(uuid.v, ns->uuid.data, NVME_NIDL_UUID);
     memcpy(pos, &uuid, sizeof(uuid));
     pos += sizeof(uuid);
 
-    if (ns->params.eui64) {
+    if (ns->eui64.v) {
         eui64.hdr.nidt = NVME_NIDT_EUI64;
         eui64.hdr.nidl = NVME_NIDL_EUI64;
-        eui64.v = cpu_to_be64(ns->params.eui64);
+        eui64.v = cpu_to_be64(ns->eui64.v);
         memcpy(pos, &eui64, sizeof(eui64));
         pos += sizeof(eui64);
     }
@@ -5264,7 +5264,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req)
                 return NVME_NS_ALREADY_ATTACHED | NVME_DNR;
             }
 
-            if (ns->attached && !ns->params.shared) {
+            if (ns->attached && !(ns->flags & NVME_NS_SHARED)) {
                 return NVME_NS_PRIVATE | NVME_DNR;
             }
 
@@ -5342,12 +5342,12 @@ static void nvme_format_set(NvmeNamespace *ns, NvmeCmd *cmd)
     uint8_t mset = (dw10 >> 4) & 0x1;
     uint8_t pil = (dw10 >> 8) & 0x1;
 
-    trace_pci_nvme_format_set(ns->params.nsid, lbaf, mset, pi, pil);
+    trace_pci_nvme_format_set(ns->nsid, lbaf, mset, pi, pil);
 
     nvm->id_ns.dps = (pil << 3) | pi;
     nvm->id_ns.flbas = lbaf | (mset << 4);
 
-    nvme_ns_init_format(ns);
+    nvme_ns_nvm_init_format(nvm);
 }
 
 static void nvme_format_ns_cb(void *opaque, int ret)
@@ -6552,7 +6552,7 @@ static int nvme_init_subsys(NvmeCtrl *n, Error **errp)
 void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns)
 {
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    uint32_t nsid = ns->params.nsid;
+    uint32_t nsid = ns->nsid;
     assert(nsid && nsid <= NVME_MAX_NAMESPACES);
 
     n->namespaces[nsid] = ns;
@@ -6565,7 +6565,6 @@ void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns)
 static void nvme_realize(PCIDevice *pci_dev, Error **errp)
 {
     NvmeCtrl *n = NVME(pci_dev);
-    NvmeNamespace *ns;
     Error *local_err = NULL;
 
     nvme_check_constraints(n, &local_err);
@@ -6590,12 +6589,11 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
 
     /* setup a namespace if the controller drive property was given */
     if (n->namespace.blkconf.blk) {
-        ns = &n->namespace;
-        ns->params.nsid = 1;
+        NvmeNamespaceDevice *nsdev = &n->namespace;
+        NvmeNamespace *ns = &nsdev->ns;
+        ns->nsid = 1;
 
-        if (nvme_ns_setup(ns, errp)) {
-            return;
-        }
+        nvme_ns_init(ns);
 
         nvme_attach_ns(n, ns);
     }
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index 9b59beb0324d..582ff7d0236b 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -26,9 +26,8 @@
 
 #define MIN_DISCARD_GRANULARITY (4 * KiB)
 
-void nvme_ns_init_format(NvmeNamespace *ns)
+void nvme_ns_nvm_init_format(NvmeNamespaceNvm *nvm)
 {
-    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeIdNs *id_ns = &nvm->id_ns;
     BlockDriverInfo bdi;
     int npdg, nlbas, ret;
@@ -48,7 +47,7 @@ void nvme_ns_init_format(NvmeNamespace *ns)
 
     npdg = nvm->discard_granularity / nvm->lbasz;
 
-    ret = bdrv_get_info(blk_bs(ns->blkconf.blk), &bdi);
+    ret = bdrv_get_info(blk_bs(nvm->blk), &bdi);
     if (ret >= 0 && bdi.cluster_size > nvm->discard_granularity) {
         npdg = bdi.cluster_size / nvm->lbasz;
     }
@@ -56,53 +55,39 @@ void nvme_ns_init_format(NvmeNamespace *ns)
     id_ns->npda = id_ns->npdg = npdg - 1;
 }
 
-static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
+void nvme_ns_init(NvmeNamespace *ns)
 {
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    static uint64_t ns_count;
     NvmeIdNs *id_ns = &nvm->id_ns;
     uint8_t ds;
     uint16_t ms;
     int i;
 
-    ns->csi = NVME_CSI_NVM;
-    ns->status = 0x0;
-
-    nvm->id_ns.dlfeat = 0x1;
+    id_ns->dlfeat = 0x1;
 
     /* support DULBE and I/O optimization fields */
     id_ns->nsfeat |= (0x4 | 0x10);
 
-    if (ns->params.shared) {
+    if (ns->flags & NVME_NS_SHARED) {
         id_ns->nmic |= NVME_NMIC_NS_SHARED;
     }
 
-    /* Substitute a missing EUI-64 by an autogenerated one */
-    ++ns_count;
-    if (!ns->params.eui64 && ns->params.eui64_default) {
-        ns->params.eui64 = ns_count + NVME_EUI64_DEFAULT;
-    }
-
     /* simple copy */
     id_ns->mssrl = cpu_to_le16(nvm->mssrl);
     id_ns->mcl = cpu_to_le32(nvm->mcl);
     id_ns->msrc = nvm->msrc;
-    id_ns->eui64 = cpu_to_be64(ns->params.eui64);
+    id_ns->eui64 = cpu_to_be64(ns->eui64.v);
 
     ds = 31 - clz32(nvm->lbasz);
-    ms = ns->params.ms;
+    ms = nvm->lbaf.ms;
 
     id_ns->mc = NVME_ID_NS_MC_EXTENDED | NVME_ID_NS_MC_SEPARATE;
 
-    if (ms && ns->params.mset) {
+    if (ms && nvm->flags & NVME_NS_NVM_EXTENDED_LBA) {
         id_ns->flbas |= NVME_ID_NS_FLBAS_EXTENDED;
     }
 
     id_ns->dpc = 0x1f;
-    id_ns->dps = ns->params.pi;
-    if (ns->params.pi && ns->params.pil) {
-        id_ns->dps |= NVME_ID_NS_DPS_FIRST_EIGHT;
-    }
 
     static const NvmeLBAF lbaf[16] = {
         [0] = { .ds =  9           },
@@ -135,59 +120,63 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
     id_ns->flbas |= id_ns->nlbaf;
 
 lbaf_found:
-    nvme_ns_init_format(ns);
-
-    return 0;
+    nvme_ns_nvm_init_format(nvm);
 }
 
-static int nvme_ns_init_blk(NvmeNamespace *ns, Error **errp)
+static int nvme_nsdev_init_blk(NvmeNamespaceDevice *nsdev,
+                               Error **errp)
 {
+    NvmeNamespace *ns = &nsdev->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
+    BlockConf *blkconf = &nsdev->blkconf;
     bool read_only;
 
-    if (!blkconf_blocksizes(&ns->blkconf, errp)) {
+    if (!blkconf_blocksizes(blkconf, errp)) {
         return -1;
     }
 
-    read_only = !blk_supports_write_perm(ns->blkconf.blk);
-    if (!blkconf_apply_backend_options(&ns->blkconf, read_only, false, errp)) {
+    read_only = !blk_supports_write_perm(blkconf->blk);
+    if (!blkconf_apply_backend_options(blkconf, read_only, false, errp)) {
         return -1;
     }
 
-    if (ns->blkconf.discard_granularity == -1) {
-        ns->blkconf.discard_granularity =
-            MAX(ns->blkconf.logical_block_size, MIN_DISCARD_GRANULARITY);
+    if (blkconf->discard_granularity == -1) {
+        blkconf->discard_granularity =
+            MAX(blkconf->logical_block_size, MIN_DISCARD_GRANULARITY);
     }
 
-    nvm->lbasz = ns->blkconf.logical_block_size;
-    nvm->discard_granularity = ns->blkconf.discard_granularity;
+    nvm->lbasz = blkconf->logical_block_size;
+    nvm->discard_granularity = blkconf->discard_granularity;
     nvm->lbaf.ds = 31 - clz32(nvm->lbasz);
-    nvm->lbaf.ms = ns->params.ms;
+    nvm->lbaf.ms = nsdev->params.ms;
+    nvm->blk = blkconf->blk;
 
-    nvm->size = blk_getlength(ns->blkconf.blk);
+    nvm->size = blk_getlength(nvm->blk);
     if (nvm->size < 0) {
-        error_setg_errno(errp, -nvm->size, "could not get blockdev size");
+        error_setg_errno(errp, -(nvm->size), "could not get blockdev size");
         return -1;
     }
 
     return 0;
 }
 
-static int nvme_zns_check_calc_geometry(NvmeNamespace *ns, Error **errp)
+static int nvme_nsdev_zns_check_calc_geometry(NvmeNamespaceDevice *nsdev,
+                                              Error **errp)
 {
+    NvmeNamespace *ns = &nsdev->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
 
     uint64_t zone_size, zone_cap;
 
     /* Make sure that the values of ZNS properties are sane */
-    if (ns->params.zone_size_bs) {
-        zone_size = ns->params.zone_size_bs;
+    if (nsdev->params.zone_size_bs) {
+        zone_size = nsdev->params.zone_size_bs;
     } else {
         zone_size = NVME_DEFAULT_ZONE_SIZE;
     }
-    if (ns->params.zone_cap_bs) {
-        zone_cap = ns->params.zone_cap_bs;
+    if (nsdev->params.zone_cap_bs) {
+        zone_cap = nsdev->params.zone_cap_bs;
     } else {
         zone_cap = zone_size;
     }
@@ -359,46 +348,47 @@ static void nvme_zns_shutdown(NvmeNamespaceZoned *zoned)
     assert(zoned->nr_open_zones == 0);
 }
 
-static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp)
+static int nvme_nsdev_check_constraints(NvmeNamespaceDevice *nsdev,
+                                        Error **errp)
 {
-    if (!ns->blkconf.blk) {
+    if (!nsdev->blkconf.blk) {
         error_setg(errp, "block backend not configured");
         return -1;
     }
 
-    if (ns->params.pi && ns->params.ms < 8) {
+    if (nsdev->params.pi && nsdev->params.ms < 8) {
         error_setg(errp, "at least 8 bytes of metadata required to enable "
                    "protection information");
         return -1;
     }
 
-    if (ns->params.nsid > NVME_MAX_NAMESPACES) {
+    if (nsdev->params.nsid > NVME_MAX_NAMESPACES) {
         error_setg(errp, "invalid namespace id (must be between 0 and %d)",
                    NVME_MAX_NAMESPACES);
         return -1;
     }
 
-    if (ns->params.zoned) {
-        if (ns->params.max_active_zones) {
-            if (ns->params.max_open_zones > ns->params.max_active_zones) {
+    if (nsdev->params.zoned) {
+        if (nsdev->params.max_active_zones) {
+            if (nsdev->params.max_open_zones > nsdev->params.max_active_zones) {
                 error_setg(errp, "max_open_zones (%u) exceeds "
-                           "max_active_zones (%u)", ns->params.max_open_zones,
-                           ns->params.max_active_zones);
+                           "max_active_zones (%u)", nsdev->params.max_open_zones,
+                           nsdev->params.max_active_zones);
                 return -1;
             }
 
-            if (!ns->params.max_open_zones) {
-                ns->params.max_open_zones = ns->params.max_active_zones;
+            if (!nsdev->params.max_open_zones) {
+                nsdev->params.max_open_zones = nsdev->params.max_active_zones;
             }
         }
 
-        if (ns->params.zd_extension_size) {
-            if (ns->params.zd_extension_size & 0x3f) {
+        if (nsdev->params.zd_extension_size) {
+            if (nsdev->params.zd_extension_size & 0x3f) {
                 error_setg(errp, "zone descriptor extension size must be a "
                            "multiple of 64B");
                 return -1;
             }
-            if ((ns->params.zd_extension_size >> 6) > 0xff) {
+            if ((nsdev->params.zd_extension_size >> 6) > 0xff) {
                 error_setg(errp,
                            "zone descriptor extension size is too large");
                 return -1;
@@ -409,35 +399,57 @@ static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp)
     return 0;
 }
 
-int nvme_ns_setup(NvmeNamespace *ns, Error **errp)
+static int nvme_nsdev_setup(NvmeNamespaceDevice *nsdev, Error **errp)
 {
-    if (nvme_ns_check_constraints(ns, errp)) {
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(&nsdev->ns);
+    static uint64_t ns_count;
+
+    if (nvme_nsdev_check_constraints(nsdev, errp)) {
         return -1;
     }
 
-    if (nvme_ns_init_blk(ns, errp)) {
-        return -1;
+    if (nsdev->params.shared) {
+        nsdev->ns.flags |= NVME_NS_SHARED;
     }
 
-    if (nvme_ns_init(ns, errp)) {
-        return -1;
-    }
-    if (ns->params.zoned) {
-        NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
+    nsdev->ns.nsid = nsdev->params.nsid;
+    memcpy(&nsdev->ns.uuid, &nsdev->params.uuid, sizeof(nsdev->ns.uuid));
 
-        if (nvme_zns_check_calc_geometry(ns, errp) != 0) {
+    if (nsdev->params.eui64) {
+        stq_be_p(&nsdev->ns.eui64.v, nsdev->params.eui64);
+    }
+
+    /* Substitute a missing EUI-64 by an autogenerated one */
+    ++ns_count;
+    if (!nsdev->ns.eui64.v && nsdev->params.eui64_default) {
+        nsdev->ns.eui64.v = ns_count + NVME_EUI64_DEFAULT;
+    }
+
+    nvm->id_ns.dps = nsdev->params.pi;
+    if (nsdev->params.pi && nsdev->params.pil) {
+        nvm->id_ns.dps |= NVME_ID_NS_DPS_FIRST_EIGHT;
+    }
+
+    nsdev->ns.csi = NVME_CSI_NVM;
+
+    nvme_ns_init(&nsdev->ns);
+
+    if (nsdev->params.zoned) {
+        NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(&nsdev->ns);
+
+        if (nvme_nsdev_zns_check_calc_geometry(nsdev, errp) != 0) {
             return -1;
         }
 
         /* copy device parameters */
-        zoned->zd_extension_size = ns->params.zd_extension_size;
-        zoned->max_open_zones = ns->params.max_open_zones;
-        zoned->max_active_zones = ns->params.max_open_zones;
-        if (ns->params.cross_zone_read) {
+        zoned->zd_extension_size = nsdev->params.zd_extension_size;
+        zoned->max_open_zones = nsdev->params.max_open_zones;
+        zoned->max_active_zones = nsdev->params.max_open_zones;
+        if (nsdev->params.cross_zone_read) {
             zoned->flags |= NVME_NS_ZONED_CROSS_READ;
         }
 
-        nvme_zns_init(ns);
+        nvme_zns_init(&nsdev->ns);
     }
 
     return 0;
@@ -445,12 +457,12 @@ int nvme_ns_setup(NvmeNamespace *ns, Error **errp)
 
 void nvme_ns_drain(NvmeNamespace *ns)
 {
-    blk_drain(ns->blkconf.blk);
+    blk_drain(nvme_blk(ns));
 }
 
 void nvme_ns_shutdown(NvmeNamespace *ns)
 {
-    blk_flush(ns->blkconf.blk);
+    blk_flush(nvme_blk(ns));
     if (nvme_ns_zoned(ns)) {
         nvme_zns_shutdown(NVME_NAMESPACE_ZONED(ns));
     }
@@ -466,26 +478,28 @@ void nvme_ns_cleanup(NvmeNamespace *ns)
     }
 }
 
-static void nvme_ns_unrealize(DeviceState *dev)
+static void nvme_nsdev_unrealize(DeviceState *dev)
 {
-    NvmeNamespace *ns = NVME_NS(dev);
+    NvmeNamespaceDevice *nsdev = NVME_NAMESPACE_DEVICE(dev);
+    NvmeNamespace *ns = &nsdev->ns;
 
     nvme_ns_drain(ns);
     nvme_ns_shutdown(ns);
     nvme_ns_cleanup(ns);
 }
 
-static void nvme_ns_realize(DeviceState *dev, Error **errp)
+static void nvme_nsdev_realize(DeviceState *dev, Error **errp)
 {
-    NvmeNamespace *ns = NVME_NS(dev);
+    NvmeNamespaceDevice *nsdev = NVME_NAMESPACE_DEVICE(dev);
+    NvmeNamespace *ns = &nsdev->ns;
     BusState *s = qdev_get_parent_bus(dev);
     NvmeCtrl *n = NVME(s->parent);
     NvmeSubsystem *subsys = n->subsys;
-    uint32_t nsid = ns->params.nsid;
+    uint32_t nsid = nsdev->params.nsid;
     int i;
 
     if (!n->subsys) {
-        if (ns->params.detached) {
+        if (nsdev->params.detached) {
             error_setg(errp, "detached requires that the nvme device is "
                        "linked to an nvme-subsys device");
             return;
@@ -500,7 +514,11 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
         }
     }
 
-    if (nvme_ns_setup(ns, errp)) {
+    if (nvme_nsdev_init_blk(nsdev, errp)) {
+        return;
+    }
+
+    if (nvme_nsdev_setup(nsdev, errp)) {
         return;
     }
 
@@ -510,7 +528,7 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
                 continue;
             }
 
-            nsid = ns->params.nsid = i;
+            nsid = ns->nsid = i;
             break;
         }
 
@@ -528,11 +546,11 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
     if (subsys) {
         subsys->namespaces[nsid] = ns;
 
-        if (ns->params.detached) {
+        if (nsdev->params.detached) {
             return;
         }
 
-        if (ns->params.shared) {
+        if (nsdev->params.shared) {
             for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) {
                 NvmeCtrl *ctrl = subsys->ctrls[i];
 
@@ -548,73 +566,74 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
     nvme_attach_ns(n, ns);
 }
 
-static Property nvme_ns_props[] = {
-    DEFINE_BLOCK_PROPERTIES(NvmeNamespace, blkconf),
-    DEFINE_PROP_BOOL("detached", NvmeNamespace, params.detached, false),
-    DEFINE_PROP_BOOL("shared", NvmeNamespace, params.shared, true),
-    DEFINE_PROP_UINT32("nsid", NvmeNamespace, params.nsid, 0),
-    DEFINE_PROP_UUID("uuid", NvmeNamespace, params.uuid),
-    DEFINE_PROP_UINT64("eui64", NvmeNamespace, params.eui64, 0),
-    DEFINE_PROP_UINT16("ms", NvmeNamespace, params.ms, 0),
-    DEFINE_PROP_UINT8("mset", NvmeNamespace, params.mset, 0),
-    DEFINE_PROP_UINT8("pi", NvmeNamespace, params.pi, 0),
-    DEFINE_PROP_UINT8("pil", NvmeNamespace, params.pil, 0),
-    DEFINE_PROP_UINT16("mssrl", NvmeNamespace, params.mssrl, 128),
-    DEFINE_PROP_UINT32("mcl", NvmeNamespace, params.mcl, 128),
-    DEFINE_PROP_UINT8("msrc", NvmeNamespace, params.msrc, 127),
-    DEFINE_PROP_BOOL("zoned", NvmeNamespace, params.zoned, false),
-    DEFINE_PROP_SIZE("zoned.zone_size", NvmeNamespace, params.zone_size_bs,
+static Property nvme_nsdev_props[] = {
+    DEFINE_BLOCK_PROPERTIES(NvmeNamespaceDevice, blkconf),
+    DEFINE_PROP_BOOL("detached", NvmeNamespaceDevice, params.detached, false),
+    DEFINE_PROP_BOOL("shared", NvmeNamespaceDevice, params.shared, true),
+    DEFINE_PROP_UINT32("nsid", NvmeNamespaceDevice, params.nsid, 0),
+    DEFINE_PROP_UUID("uuid", NvmeNamespaceDevice, params.uuid),
+    DEFINE_PROP_UINT64("eui64", NvmeNamespaceDevice, params.eui64, 0),
+    DEFINE_PROP_UINT16("ms", NvmeNamespaceDevice, params.ms, 0),
+    DEFINE_PROP_UINT8("mset", NvmeNamespaceDevice, params.mset, 0),
+    DEFINE_PROP_UINT8("pi", NvmeNamespaceDevice, params.pi, 0),
+    DEFINE_PROP_UINT8("pil", NvmeNamespaceDevice, params.pil, 0),
+    DEFINE_PROP_UINT16("mssrl", NvmeNamespaceDevice, params.mssrl, 128),
+    DEFINE_PROP_UINT32("mcl", NvmeNamespaceDevice, params.mcl, 128),
+    DEFINE_PROP_UINT8("msrc", NvmeNamespaceDevice, params.msrc, 127),
+    DEFINE_PROP_BOOL("zoned", NvmeNamespaceDevice, params.zoned, false),
+    DEFINE_PROP_SIZE("zoned.zone_size", NvmeNamespaceDevice, params.zone_size_bs,
                      NVME_DEFAULT_ZONE_SIZE),
-    DEFINE_PROP_SIZE("zoned.zone_capacity", NvmeNamespace, params.zone_cap_bs,
+    DEFINE_PROP_SIZE("zoned.zone_capacity", NvmeNamespaceDevice, params.zone_cap_bs,
                      0),
-    DEFINE_PROP_BOOL("zoned.cross_read", NvmeNamespace,
+    DEFINE_PROP_BOOL("zoned.cross_read", NvmeNamespaceDevice,
                      params.cross_zone_read, false),
-    DEFINE_PROP_UINT32("zoned.max_active", NvmeNamespace,
+    DEFINE_PROP_UINT32("zoned.max_active", NvmeNamespaceDevice,
                        params.max_active_zones, 0),
-    DEFINE_PROP_UINT32("zoned.max_open", NvmeNamespace,
+    DEFINE_PROP_UINT32("zoned.max_open", NvmeNamespaceDevice,
                        params.max_open_zones, 0),
-    DEFINE_PROP_UINT32("zoned.descr_ext_size", NvmeNamespace,
+    DEFINE_PROP_UINT32("zoned.descr_ext_size", NvmeNamespaceDevice,
                        params.zd_extension_size, 0),
-    DEFINE_PROP_BOOL("eui64-default", NvmeNamespace, params.eui64_default,
+    DEFINE_PROP_BOOL("eui64-default", NvmeNamespaceDevice, params.eui64_default,
                      true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
-static void nvme_ns_class_init(ObjectClass *oc, void *data)
+static void nvme_nsdev_class_init(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
 
     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
 
     dc->bus_type = TYPE_NVME_BUS;
-    dc->realize = nvme_ns_realize;
-    dc->unrealize = nvme_ns_unrealize;
-    device_class_set_props(dc, nvme_ns_props);
+    dc->realize = nvme_nsdev_realize;
+    dc->unrealize = nvme_nsdev_unrealize;
+    device_class_set_props(dc, nvme_nsdev_props);
     dc->desc = "Virtual NVMe namespace";
 }
 
-static void nvme_ns_instance_init(Object *obj)
+static void nvme_nsdev_instance_init(Object *obj)
 {
-    NvmeNamespace *ns = NVME_NS(obj);
-    char *bootindex = g_strdup_printf("/namespace@%d,0", ns->params.nsid);
+    NvmeNamespaceDevice *nsdev = NVME_NAMESPACE_DEVICE(obj);
+    char *bootindex = g_strdup_printf("/namespace@%d,0",
+                                      nsdev->params.nsid);
 
-    device_add_bootindex_property(obj, &ns->bootindex, "bootindex",
+    device_add_bootindex_property(obj, &nsdev->bootindex, "bootindex",
                                   bootindex, DEVICE(obj));
 
     g_free(bootindex);
 }
 
-static const TypeInfo nvme_ns_info = {
-    .name = TYPE_NVME_NS,
+static const TypeInfo nvme_nsdev_info = {
+    .name = TYPE_NVME_NAMESPACE_DEVICE,
     .parent = TYPE_DEVICE,
-    .class_init = nvme_ns_class_init,
-    .instance_size = sizeof(NvmeNamespace),
-    .instance_init = nvme_ns_instance_init,
+    .class_init = nvme_nsdev_class_init,
+    .instance_size = sizeof(NvmeNamespaceDevice),
+    .instance_init = nvme_nsdev_instance_init,
 };
 
-static void nvme_ns_register_types(void)
+static void register_types(void)
 {
-    type_register_static(&nvme_ns_info);
+    type_register_static(&nvme_nsdev_info);
 }
 
-type_init(nvme_ns_register_types)
+type_init(register_types)
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 525bfd0ca831..356d95805f9e 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -80,9 +80,8 @@ static inline NvmeNamespace *nvme_subsys_ns(NvmeSubsystem *subsys,
     return subsys->namespaces[nsid];
 }
 
-#define TYPE_NVME_NS "nvme-ns"
-#define NVME_NS(obj) \
-    OBJECT_CHECK(NvmeNamespace, (obj), TYPE_NVME_NS)
+#define TYPE_NVME_NAMESPACE_DEVICE "nvme-ns"
+OBJECT_DECLARE_SIMPLE_TYPE(NvmeNamespaceDevice, NVME_NAMESPACE_DEVICE)
 
 typedef struct NvmeNamespaceParams {
     bool     detached;
@@ -170,19 +169,25 @@ typedef struct NvmeNamespaceNvm {
     unsigned long flags;
 } NvmeNamespaceNvm;
 
+enum NvmeNamespaceFlags {
+    NVME_NS_SHARED = 1 << 0,
+};
+
 typedef struct NvmeNamespace {
-    DeviceState  parent_obj;
-    BlockConf    blkconf;
-    int32_t      bootindex;
+    uint32_t nsid;
+    uint8_t  csi;
+    QemuUUID uuid;
+    union {
+        uint64_t v;
+        uint8_t  a[8];
+    } eui64;
+
+    unsigned long flags;
+
     const uint32_t *iocs;
-    uint8_t      csi;
     uint16_t     status;
     int          attached;
 
-    QTAILQ_ENTRY(NvmeNamespace) entry;
-
-    NvmeNamespaceParams params;
-
     struct {
         uint32_t err_rec;
     } features;
@@ -199,10 +204,19 @@ static inline BlockBackend *nvme_blk(NvmeNamespace *ns)
     return NVME_NAMESPACE_NVM(ns)->blk;
 }
 
+typedef struct NvmeNamespaceDevice {
+    DeviceState  parent_obj;
+    BlockConf    blkconf;
+    int32_t      bootindex;
+
+    NvmeNamespace       ns;
+    NvmeNamespaceParams params;
+} NvmeNamespaceDevice;
+
 static inline uint32_t nvme_nsid(NvmeNamespace *ns)
 {
     if (ns) {
-        return ns->params.nsid;
+        return ns->nsid;
     }
 
     return 0;
@@ -228,8 +242,8 @@ static inline bool nvme_ns_ext(NvmeNamespaceNvm *nvm)
     return !!NVME_ID_NS_FLBAS_EXTENDED(nvm->id_ns.flbas);
 }
 
-void nvme_ns_init_format(NvmeNamespace *ns);
-int nvme_ns_setup(NvmeNamespace *ns, Error **errp);
+void nvme_ns_nvm_init_format(NvmeNamespaceNvm *nvm);
+void nvme_ns_init(NvmeNamespace *ns);
 void nvme_ns_drain(NvmeNamespace *ns);
 void nvme_ns_shutdown(NvmeNamespace *ns);
 void nvme_ns_cleanup(NvmeNamespace *ns);
@@ -424,7 +438,7 @@ typedef struct NvmeCtrl {
 
     NvmeSubsystem   *subsys;
 
-    NvmeNamespace   namespace;
+    NvmeNamespaceDevice namespace;
     NvmeNamespace   *namespaces[NVME_MAX_NAMESPACES + 1];
     NvmeSQueue      **sq;
     NvmeCQueue      **cq;
diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c
index 6b2e4c975f5b..5a9405d05fbe 100644
--- a/hw/nvme/subsys.c
+++ b/hw/nvme/subsys.c
@@ -31,7 +31,7 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp)
 
     for (nsid = 1; nsid < ARRAY_SIZE(subsys->namespaces); nsid++) {
         NvmeNamespace *ns = subsys->namespaces[nsid];
-        if (ns && ns->params.shared && !ns->params.detached) {
+        if (ns && (ns->flags & NVME_NS_SHARED)) {
             nvme_attach_ns(n, ns);
         }
     }
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 09/16] hw/nvme: hoist qdev state from subsystem
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (7 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 08/16] hw/nvme: hoist qdev state from namespace Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 10/16] hw/nvme: hoist qdev state from controller Klaus Jensen
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c   | 10 +++++++---
 hw/nvme/ns.c     |  2 +-
 hw/nvme/nvme.h   | 24 +++++++++++++++---------
 hw/nvme/subsys.c | 36 ++++++++++++++++++------------------
 4 files changed, 41 insertions(+), 31 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 67600d075d32..af2a783ba4c0 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -6539,7 +6539,7 @@ static int nvme_init_subsys(NvmeCtrl *n, Error **errp)
         return 0;
     }
 
-    cntlid = nvme_subsys_register_ctrl(n, errp);
+    cntlid = nvme_subsys_register_ctrl(n->subsys, n, errp);
     if (cntlid < 0) {
         return -1;
     }
@@ -6567,6 +6567,10 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
     NvmeCtrl *n = NVME(pci_dev);
     Error *local_err = NULL;
 
+    if (n->subsys_dev) {
+        n->subsys = &n->subsys_dev->subsys;
+    }
+
     nvme_check_constraints(n, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
@@ -6637,8 +6641,8 @@ static Property nvme_props[] = {
     DEFINE_BLOCK_PROPERTIES(NvmeCtrl, namespace.blkconf),
     DEFINE_PROP_LINK("pmrdev", NvmeCtrl, pmr.dev, TYPE_MEMORY_BACKEND,
                      HostMemoryBackend *),
-    DEFINE_PROP_LINK("subsys", NvmeCtrl, subsys, TYPE_NVME_SUBSYS,
-                     NvmeSubsystem *),
+    DEFINE_PROP_LINK("subsys", NvmeCtrl, subsys_dev, TYPE_NVME_SUBSYSTEM_DEVICE,
+                     NvmeSubsystemDevice *),
     DEFINE_PROP_STRING("serial", NvmeCtrl, params.serial),
     DEFINE_PROP_UINT32("cmb_size_mb", NvmeCtrl, params.cmb_size_mb, 0),
     DEFINE_PROP_UINT32("num_queues", NvmeCtrl, params.num_queues, 0),
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index 582ff7d0236b..d67632174530 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -509,7 +509,7 @@ static void nvme_nsdev_realize(DeviceState *dev, Error **errp)
          * If this namespace belongs to a subsystem (through a link on the
          * controller device), reparent the device.
          */
-        if (!qdev_set_parent_bus(dev, &subsys->bus.parent_bus, errp)) {
+        if (!qdev_set_parent_bus(dev, &n->subsys_dev->bus.parent_bus, errp)) {
             return;
         }
     }
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 356d95805f9e..e8f4b8e3e2e0 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -40,24 +40,29 @@ typedef struct NvmeBus {
     BusState parent_bus;
 } NvmeBus;
 
-#define TYPE_NVME_SUBSYS "nvme-subsys"
-#define NVME_SUBSYS(obj) \
-    OBJECT_CHECK(NvmeSubsystem, (obj), TYPE_NVME_SUBSYS)
-
 typedef struct NvmeSubsystem {
-    DeviceState parent_obj;
-    NvmeBus     bus;
     uint8_t     subnqn[256];
 
     NvmeCtrl      *ctrls[NVME_MAX_CONTROLLERS];
     NvmeNamespace *namespaces[NVME_MAX_NAMESPACES + 1];
+} NvmeSubsystem;
+
+#define TYPE_NVME_SUBSYSTEM_DEVICE "nvme-subsys"
+OBJECT_DECLARE_SIMPLE_TYPE(NvmeSubsystemDevice, NVME_SUBSYSTEM_DEVICE)
+
+typedef struct NvmeSubsystemDevice {
+    DeviceState parent_obj;
+    NvmeBus     bus;
+
+    NvmeSubsystem subsys;
 
     struct {
         char *nqn;
     } params;
-} NvmeSubsystem;
+} NvmeSubsystemDevice;
 
-int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp);
+int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n,
+                              Error **errp);
 void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n);
 
 static inline NvmeCtrl *nvme_subsys_ctrl(NvmeSubsystem *subsys,
@@ -436,7 +441,8 @@ typedef struct NvmeCtrl {
 #define NVME_CHANGED_NSID_SIZE  (NVME_MAX_NAMESPACES + 1)
     DECLARE_BITMAP(changed_nsids, NVME_CHANGED_NSID_SIZE);
 
-    NvmeSubsystem   *subsys;
+    NvmeSubsystem       *subsys;
+    NvmeSubsystemDevice *subsys_dev;
 
     NvmeNamespaceDevice namespace;
     NvmeNamespace   *namespaces[NVME_MAX_NAMESPACES + 1];
diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c
index 5a9405d05fbe..4d73d14070dc 100644
--- a/hw/nvme/subsys.c
+++ b/hw/nvme/subsys.c
@@ -11,9 +11,8 @@
 
 #include "nvme.h"
 
-int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp)
+int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n, Error **errp)
 {
-    NvmeSubsystem *subsys = n->subsys;
     int cntlid, nsid;
 
     for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) {
@@ -45,53 +44,54 @@ void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n)
     n->cntlid = -1;
 }
 
-static void nvme_subsys_setup(NvmeSubsystem *subsys)
+static void nvme_subsys_device_setup(NvmeSubsystemDevice *dev)
 {
-    const char *nqn = subsys->params.nqn ?
-        subsys->params.nqn : subsys->parent_obj.id;
+    NvmeSubsystem *subsys = &dev->subsys;
+    const char *nqn = dev->params.nqn ?
+        dev->params.nqn : dev->parent_obj.id;
 
     snprintf((char *)subsys->subnqn, sizeof(subsys->subnqn),
              "nqn.2019-08.org.qemu:%s", nqn);
 }
 
-static void nvme_subsys_realize(DeviceState *dev, Error **errp)
+static void nvme_subsys_device_realize(DeviceState *dev, Error **errp)
 {
-    NvmeSubsystem *subsys = NVME_SUBSYS(dev);
+    NvmeSubsystemDevice *subsys = NVME_SUBSYSTEM_DEVICE(dev);
 
     qbus_create_inplace(&subsys->bus, sizeof(NvmeBus), TYPE_NVME_BUS, dev,
                         dev->id);
 
-    nvme_subsys_setup(subsys);
+    nvme_subsys_device_setup(subsys);
 }
 
-static Property nvme_subsystem_props[] = {
-    DEFINE_PROP_STRING("nqn", NvmeSubsystem, params.nqn),
+static Property nvme_subsystem_device_props[] = {
+    DEFINE_PROP_STRING("nqn", NvmeSubsystemDevice, params.nqn),
     DEFINE_PROP_END_OF_LIST(),
 };
 
-static void nvme_subsys_class_init(ObjectClass *oc, void *data)
+static void nvme_subsys_device_class_init(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
 
     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
 
-    dc->realize = nvme_subsys_realize;
+    dc->realize = nvme_subsys_device_realize;
     dc->desc = "Virtual NVMe subsystem";
     dc->hotpluggable = false;
 
-    device_class_set_props(dc, nvme_subsystem_props);
+    device_class_set_props(dc, nvme_subsystem_device_props);
 }
 
-static const TypeInfo nvme_subsys_info = {
-    .name = TYPE_NVME_SUBSYS,
+static const TypeInfo nvme_subsys_device_info = {
+    .name = TYPE_NVME_SUBSYSTEM_DEVICE,
     .parent = TYPE_DEVICE,
-    .class_init = nvme_subsys_class_init,
-    .instance_size = sizeof(NvmeSubsystem),
+    .class_init = nvme_subsys_device_class_init,
+    .instance_size = sizeof(NvmeSubsystemDevice),
 };
 
 static void nvme_subsys_register_types(void)
 {
-    type_register_static(&nvme_subsys_info);
+    type_register_static(&nvme_subsys_device_info);
 }
 
 type_init(nvme_subsys_register_types)
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 10/16] hw/nvme: hoist qdev state from controller
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (8 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 09/16] hw/nvme: hoist qdev state from subsystem Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 11/16] hw/nvme: add experimental object x-nvme-subsystem Klaus Jensen
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Add an abstract object NvmeState.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c   | 382 +++++++++++++++++++++++++----------------------
 hw/nvme/dif.c    |   4 +-
 hw/nvme/dif.h    |   2 +-
 hw/nvme/ns.c     |   4 +-
 hw/nvme/nvme.h   |  52 ++++---
 hw/nvme/subsys.c |   4 +-
 6 files changed, 239 insertions(+), 209 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index af2a783ba4c0..b27bf00f623f 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -323,7 +323,7 @@ static int nvme_zns_aor_check(NvmeNamespaceZoned *zoned, uint32_t act,
     return NVME_SUCCESS;
 }
 
-static bool nvme_addr_is_cmb(NvmeCtrl *n, hwaddr addr)
+static bool nvme_addr_is_cmb(NvmeState *n, hwaddr addr)
 {
     hwaddr hi, lo;
 
@@ -337,13 +337,13 @@ static bool nvme_addr_is_cmb(NvmeCtrl *n, hwaddr addr)
     return addr >= lo && addr < hi;
 }
 
-static inline void *nvme_addr_to_cmb(NvmeCtrl *n, hwaddr addr)
+static inline void *nvme_addr_to_cmb(NvmeState *n, hwaddr addr)
 {
     hwaddr base = n->params.legacy_cmb ? n->cmb.mem.addr : n->cmb.cba;
     return &n->cmb.buf[addr - base];
 }
 
-static bool nvme_addr_is_pmr(NvmeCtrl *n, hwaddr addr)
+static bool nvme_addr_is_pmr(NvmeState *n, hwaddr addr)
 {
     hwaddr hi;
 
@@ -356,12 +356,12 @@ static bool nvme_addr_is_pmr(NvmeCtrl *n, hwaddr addr)
     return addr >= n->pmr.cba && addr < hi;
 }
 
-static inline void *nvme_addr_to_pmr(NvmeCtrl *n, hwaddr addr)
+static inline void *nvme_addr_to_pmr(NvmeState *n, hwaddr addr)
 {
     return memory_region_get_ram_ptr(&n->pmr.dev->mr) + (addr - n->pmr.cba);
 }
 
-static int nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
+static int nvme_addr_read(NvmeState *n, hwaddr addr, void *buf, int size)
 {
     hwaddr hi = addr + size - 1;
     if (hi < addr) {
@@ -381,7 +381,7 @@ static int nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
     return pci_dma_read(&n->parent_obj, addr, buf, size);
 }
 
-static int nvme_addr_write(NvmeCtrl *n, hwaddr addr, void *buf, int size)
+static int nvme_addr_write(NvmeState *n, hwaddr addr, void *buf, int size)
 {
     hwaddr hi = addr + size - 1;
     if (hi < addr) {
@@ -401,18 +401,18 @@ static int nvme_addr_write(NvmeCtrl *n, hwaddr addr, void *buf, int size)
     return pci_dma_write(&n->parent_obj, addr, buf, size);
 }
 
-static bool nvme_nsid_valid(NvmeCtrl *n, uint32_t nsid)
+static bool nvme_nsid_valid(NvmeState *n, uint32_t nsid)
 {
     return nsid &&
         (nsid == NVME_NSID_BROADCAST || nsid <= NVME_MAX_NAMESPACES);
 }
 
-static int nvme_check_sqid(NvmeCtrl *n, uint16_t sqid)
+static int nvme_check_sqid(NvmeState *n, uint16_t sqid)
 {
     return sqid < n->params.max_ioqpairs + 1 && n->sq[sqid] != NULL ? 0 : -1;
 }
 
-static int nvme_check_cqid(NvmeCtrl *n, uint16_t cqid)
+static int nvme_check_cqid(NvmeState *n, uint16_t cqid)
 {
     return cqid < n->params.max_ioqpairs + 1 && n->cq[cqid] != NULL ? 0 : -1;
 }
@@ -441,7 +441,7 @@ static uint8_t nvme_sq_empty(NvmeSQueue *sq)
     return sq->head == sq->tail;
 }
 
-static void nvme_irq_check(NvmeCtrl *n)
+static void nvme_irq_check(NvmeState *n)
 {
     uint32_t intms = ldl_le_p(&n->bar.intms);
 
@@ -455,7 +455,7 @@ static void nvme_irq_check(NvmeCtrl *n)
     }
 }
 
-static void nvme_irq_assert(NvmeCtrl *n, NvmeCQueue *cq)
+static void nvme_irq_assert(NvmeState *n, NvmeCQueue *cq)
 {
     if (cq->irq_enabled) {
         if (msix_enabled(&(n->parent_obj))) {
@@ -472,7 +472,7 @@ static void nvme_irq_assert(NvmeCtrl *n, NvmeCQueue *cq)
     }
 }
 
-static void nvme_irq_deassert(NvmeCtrl *n, NvmeCQueue *cq)
+static void nvme_irq_deassert(NvmeState *n, NvmeCQueue *cq)
 {
     if (cq->irq_enabled) {
         if (msix_enabled(&(n->parent_obj))) {
@@ -496,7 +496,7 @@ static void nvme_req_clear(NvmeRequest *req)
     req->status = NVME_SUCCESS;
 }
 
-static inline void nvme_sg_init(NvmeCtrl *n, NvmeSg *sg, bool dma)
+static inline void nvme_sg_init(NvmeState *n, NvmeSg *sg, bool dma)
 {
     if (dma) {
         pci_dma_sglist_init(&sg->qsg, &n->parent_obj, 0);
@@ -574,7 +574,7 @@ static void nvme_sg_split(NvmeSg *sg, NvmeNamespaceNvm *nvm, NvmeSg *data,
     }
 }
 
-static uint16_t nvme_map_addr_cmb(NvmeCtrl *n, QEMUIOVector *iov, hwaddr addr,
+static uint16_t nvme_map_addr_cmb(NvmeState *n, QEMUIOVector *iov, hwaddr addr,
                                   size_t len)
 {
     if (!len) {
@@ -592,7 +592,7 @@ static uint16_t nvme_map_addr_cmb(NvmeCtrl *n, QEMUIOVector *iov, hwaddr addr,
     return NVME_SUCCESS;
 }
 
-static uint16_t nvme_map_addr_pmr(NvmeCtrl *n, QEMUIOVector *iov, hwaddr addr,
+static uint16_t nvme_map_addr_pmr(NvmeState *n, QEMUIOVector *iov, hwaddr addr,
                                   size_t len)
 {
     if (!len) {
@@ -608,7 +608,7 @@ static uint16_t nvme_map_addr_pmr(NvmeCtrl *n, QEMUIOVector *iov, hwaddr addr,
     return NVME_SUCCESS;
 }
 
-static uint16_t nvme_map_addr(NvmeCtrl *n, NvmeSg *sg, hwaddr addr, size_t len)
+static uint16_t nvme_map_addr(NvmeState *n, NvmeSg *sg, hwaddr addr, size_t len)
 {
     bool cmb = false, pmr = false;
 
@@ -658,12 +658,12 @@ max_mappings_exceeded:
     return NVME_INTERNAL_DEV_ERROR | NVME_DNR;
 }
 
-static inline bool nvme_addr_is_dma(NvmeCtrl *n, hwaddr addr)
+static inline bool nvme_addr_is_dma(NvmeState *n, hwaddr addr)
 {
     return !(nvme_addr_is_cmb(n, addr) || nvme_addr_is_pmr(n, addr));
 }
 
-static uint16_t nvme_map_prp(NvmeCtrl *n, NvmeSg *sg, uint64_t prp1,
+static uint16_t nvme_map_prp(NvmeState *n, NvmeSg *sg, uint64_t prp1,
                              uint64_t prp2, uint32_t len)
 {
     hwaddr trans_len = n->page_size - (prp1 % n->page_size);
@@ -764,7 +764,7 @@ unmap:
  * Map 'nsgld' data descriptors from 'segment'. The function will subtract the
  * number of bytes mapped in len.
  */
-static uint16_t nvme_map_sgl_data(NvmeCtrl *n, NvmeSg *sg,
+static uint16_t nvme_map_sgl_data(NvmeState *n, NvmeSg *sg,
                                   NvmeSglDescriptor *segment, uint64_t nsgld,
                                   size_t *len, NvmeCmd *cmd)
 {
@@ -834,7 +834,7 @@ next:
     return NVME_SUCCESS;
 }
 
-static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl,
+static uint16_t nvme_map_sgl(NvmeState *n, NvmeSg *sg, NvmeSglDescriptor sgl,
                              size_t len, NvmeCmd *cmd)
 {
     /*
@@ -977,7 +977,7 @@ unmap:
     return status;
 }
 
-uint16_t nvme_map_dptr(NvmeCtrl *n, NvmeSg *sg, size_t len,
+uint16_t nvme_map_dptr(NvmeState *n, NvmeSg *sg, size_t len,
                        NvmeCmd *cmd)
 {
     uint64_t prp1, prp2;
@@ -996,7 +996,7 @@ uint16_t nvme_map_dptr(NvmeCtrl *n, NvmeSg *sg, size_t len,
     }
 }
 
-static uint16_t nvme_map_mptr(NvmeCtrl *n, NvmeSg *sg, size_t len,
+static uint16_t nvme_map_mptr(NvmeState *n, NvmeSg *sg, size_t len,
                               NvmeCmd *cmd)
 {
     int psdt = NVME_CMD_FLAGS_PSDT(cmd->flags);
@@ -1027,7 +1027,7 @@ static uint16_t nvme_map_mptr(NvmeCtrl *n, NvmeSg *sg, size_t len,
     return status;
 }
 
-static uint16_t nvme_map_data(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req)
+static uint16_t nvme_map_data(NvmeState *n, uint32_t nlb, NvmeRequest *req)
 {
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(req->ns);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
@@ -1056,7 +1056,7 @@ static uint16_t nvme_map_data(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req)
     return nvme_map_dptr(n, &req->sg, len, &req->cmd);
 }
 
-static uint16_t nvme_map_mdata(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req)
+static uint16_t nvme_map_mdata(NvmeState *n, uint32_t nlb, NvmeRequest *req)
 {
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(req->ns);
     size_t len = nvme_m2b(nvm, nlb);
@@ -1082,7 +1082,7 @@ static uint16_t nvme_map_mdata(NvmeCtrl *n, uint32_t nlb, NvmeRequest *req)
     return nvme_map_mptr(n, &req->sg, len, &req->cmd);
 }
 
-static uint16_t nvme_tx_interleaved(NvmeCtrl *n, NvmeSg *sg, uint8_t *ptr,
+static uint16_t nvme_tx_interleaved(NvmeState *n, NvmeSg *sg, uint8_t *ptr,
                                     uint32_t len, uint32_t bytes,
                                     int32_t skip_bytes, int64_t offset,
                                     NvmeTxDirection dir)
@@ -1144,7 +1144,7 @@ static uint16_t nvme_tx_interleaved(NvmeCtrl *n, NvmeSg *sg, uint8_t *ptr,
     return NVME_SUCCESS;
 }
 
-static uint16_t nvme_tx(NvmeCtrl *n, NvmeSg *sg, uint8_t *ptr, uint32_t len,
+static uint16_t nvme_tx(NvmeState *n, NvmeSg *sg, uint8_t *ptr, uint32_t len,
                         NvmeTxDirection dir)
 {
     assert(sg->flags & NVME_SG_ALLOC);
@@ -1180,7 +1180,7 @@ static uint16_t nvme_tx(NvmeCtrl *n, NvmeSg *sg, uint8_t *ptr, uint32_t len,
     return NVME_SUCCESS;
 }
 
-static inline uint16_t nvme_c2h(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
+static inline uint16_t nvme_c2h(NvmeState *n, uint8_t *ptr, uint32_t len,
                                 NvmeRequest *req)
 {
     uint16_t status;
@@ -1193,7 +1193,7 @@ static inline uint16_t nvme_c2h(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
     return nvme_tx(n, &req->sg, ptr, len, NVME_TX_DIRECTION_FROM_DEVICE);
 }
 
-static inline uint16_t nvme_h2c(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
+static inline uint16_t nvme_h2c(NvmeState *n, uint8_t *ptr, uint32_t len,
                                 NvmeRequest *req)
 {
     uint16_t status;
@@ -1206,7 +1206,7 @@ static inline uint16_t nvme_h2c(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
     return nvme_tx(n, &req->sg, ptr, len, NVME_TX_DIRECTION_TO_DEVICE);
 }
 
-uint16_t nvme_bounce_data(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
+uint16_t nvme_bounce_data(NvmeState *n, uint8_t *ptr, uint32_t len,
                           NvmeTxDirection dir, NvmeRequest *req)
 {
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(req->ns);
@@ -1222,7 +1222,7 @@ uint16_t nvme_bounce_data(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
     return nvme_tx(n, &req->sg, ptr, len, dir);
 }
 
-uint16_t nvme_bounce_mdata(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
+uint16_t nvme_bounce_mdata(NvmeState *n, uint8_t *ptr, uint32_t len,
                            NvmeTxDirection dir, NvmeRequest *req)
 {
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(req->ns);
@@ -1272,7 +1272,7 @@ static inline void nvme_blk_write(BlockBackend *blk, int64_t offset,
 static void nvme_post_cqes(void *opaque)
 {
     NvmeCQueue *cq = opaque;
-    NvmeCtrl *n = cq->ctrl;
+    NvmeState *n = cq->ctrl;
     NvmeRequest *req, *next;
     bool pending = cq->head != cq->tail;
     int ret;
@@ -1332,7 +1332,7 @@ static void nvme_enqueue_req_completion(NvmeCQueue *cq, NvmeRequest *req)
 
 static void nvme_process_aers(void *opaque)
 {
-    NvmeCtrl *n = opaque;
+    NvmeState *n = opaque;
     NvmeAsyncEvent *event, *next;
 
     trace_pci_nvme_process_aers(n->aer_queued);
@@ -1374,7 +1374,7 @@ static void nvme_process_aers(void *opaque)
     }
 }
 
-static void nvme_enqueue_event(NvmeCtrl *n, uint8_t event_type,
+static void nvme_enqueue_event(NvmeState *n, uint8_t event_type,
                                uint8_t event_info, uint8_t log_page)
 {
     NvmeAsyncEvent *event;
@@ -1399,7 +1399,7 @@ static void nvme_enqueue_event(NvmeCtrl *n, uint8_t event_type,
     nvme_process_aers(n);
 }
 
-static void nvme_smart_event(NvmeCtrl *n, uint8_t event)
+static void nvme_smart_event(NvmeState *n, uint8_t event)
 {
     uint8_t aer_info;
 
@@ -1428,7 +1428,7 @@ static void nvme_smart_event(NvmeCtrl *n, uint8_t event)
     nvme_enqueue_event(n, NVME_AER_TYPE_SMART, aer_info, NVME_LOG_SMART_INFO);
 }
 
-static void nvme_clear_events(NvmeCtrl *n, uint8_t event_type)
+static void nvme_clear_events(NvmeState *n, uint8_t event_type)
 {
     n->aer_mask &= ~(1 << event_type);
     if (!QTAILQ_EMPTY(&n->aer_queue)) {
@@ -1436,7 +1436,7 @@ static void nvme_clear_events(NvmeCtrl *n, uint8_t event_type)
     }
 }
 
-static inline uint16_t nvme_check_mdts(NvmeCtrl *n, size_t len)
+static inline uint16_t nvme_check_mdts(NvmeState *n, size_t len)
 {
     uint8_t mdts = n->params.mdts;
 
@@ -1745,7 +1745,7 @@ enum {
     NVME_ZRM_AUTO = 1 << 0,
 };
 
-static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespaceZoned *zoned,
+static uint16_t nvme_zrm_open_flags(NvmeState *n, NvmeNamespaceZoned *zoned,
                                     NvmeZone *zone, int flags)
 {
     int act = 0;
@@ -1796,13 +1796,13 @@ static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespaceZoned *zoned,
     }
 }
 
-static inline uint16_t nvme_zrm_auto(NvmeCtrl *n, NvmeNamespaceZoned *zoned,
+static inline uint16_t nvme_zrm_auto(NvmeState *n, NvmeNamespaceZoned *zoned,
                                      NvmeZone *zone)
 {
     return nvme_zrm_open_flags(n, zoned, zone, NVME_ZRM_AUTO);
 }
 
-static inline uint16_t nvme_zrm_open(NvmeCtrl *n, NvmeNamespaceZoned *zoned,
+static inline uint16_t nvme_zrm_open(NvmeState *n, NvmeNamespaceZoned *zoned,
                                      NvmeZone *zone)
 {
     return nvme_zrm_open_flags(n, zoned, zone, 0);
@@ -1918,7 +1918,7 @@ static void nvme_rw_cb(void *opaque, int ret)
             uint16_t status;
 
             nvme_sg_unmap(&req->sg);
-            status = nvme_map_mdata(nvme_ctrl(req), nlb, req);
+            status = nvme_map_mdata(nvme_state(req), nlb, req);
             if (status) {
                 ret = -EFAULT;
                 goto out;
@@ -2038,7 +2038,7 @@ static void nvme_compare_mdata_cb(void *opaque, int ret)
     NvmeRequest *req = opaque;
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    NvmeCtrl *n = nvme_ctrl(req);
+    NvmeState *n = nvme_state(req);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
     uint16_t apptag = le16_to_cpu(rw->apptag);
@@ -2123,7 +2123,7 @@ out:
 static void nvme_compare_data_cb(void *opaque, int ret)
 {
     NvmeRequest *req = opaque;
-    NvmeCtrl *n = nvme_ctrl(req);
+    NvmeState *n = nvme_state(req);
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     BlockBackend *blk = nvme_blk(ns);
@@ -2286,7 +2286,7 @@ static void nvme_dsm_cb(void *opaque, int ret)
 {
     NvmeDSMAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
-    NvmeCtrl *n = nvme_ctrl(req);
+    NvmeState *n = nvme_state(req);
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeDsmRange *range;
@@ -2330,7 +2330,7 @@ done:
     qemu_bh_schedule(iocb->bh);
 }
 
-static uint16_t nvme_dsm(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_dsm(NvmeState *n, NvmeRequest *req)
 {
     NvmeNamespace *ns = req->ns;
     NvmeDsmCmd *dsm = (NvmeDsmCmd *) &req->cmd;
@@ -2366,7 +2366,7 @@ static uint16_t nvme_dsm(NvmeCtrl *n, NvmeRequest *req)
     return status;
 }
 
-static uint16_t nvme_verify(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_verify(NvmeState *n, NvmeRequest *req)
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
@@ -2774,8 +2774,7 @@ done:
     }
 }
 
-
-static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_copy(NvmeState *n, NvmeRequest *req)
 {
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
@@ -2863,7 +2862,7 @@ invalid:
     return status;
 }
 
-static uint16_t nvme_compare(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_compare(NvmeState *n, NvmeRequest *req)
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
@@ -2984,7 +2983,7 @@ static void nvme_flush_bh(void *opaque)
 {
     NvmeFlushAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
-    NvmeCtrl *n = nvme_ctrl(req);
+    NvmeState *n = nvme_state(req);
     int i;
 
     if (iocb->ret < 0) {
@@ -3019,7 +3018,7 @@ done:
     return;
 }
 
-static uint16_t nvme_flush(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_flush(NvmeState *n, NvmeRequest *req)
 {
     NvmeFlushAIOCB *iocb;
     uint32_t nsid = le32_to_cpu(req->cmd.nsid);
@@ -3062,7 +3061,7 @@ out:
     return status;
 }
 
-static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_read(NvmeState *n, NvmeRequest *req)
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
@@ -3136,7 +3135,7 @@ invalid:
     return status | NVME_DNR;
 }
 
-static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append,
+static uint16_t nvme_do_write(NvmeState *n, NvmeRequest *req, bool append,
                               bool wrz)
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
@@ -3272,17 +3271,17 @@ invalid:
     return status | NVME_DNR;
 }
 
-static inline uint16_t nvme_write(NvmeCtrl *n, NvmeRequest *req)
+static inline uint16_t nvme_write(NvmeState *n, NvmeRequest *req)
 {
     return nvme_do_write(n, req, false, false);
 }
 
-static inline uint16_t nvme_write_zeroes(NvmeCtrl *n, NvmeRequest *req)
+static inline uint16_t nvme_write_zeroes(NvmeState *n, NvmeRequest *req)
 {
     return nvme_do_write(n, req, false, true);
 }
 
-static inline uint16_t nvme_zone_append(NvmeCtrl *n, NvmeRequest *req)
+static inline uint16_t nvme_zone_append(NvmeState *n, NvmeRequest *req)
 {
     return nvme_do_write(n, req, true, false);
 }
@@ -3330,7 +3329,7 @@ enum NvmeZoneProcessingMask {
 static uint16_t nvme_open_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone,
                                NvmeZoneState state, NvmeRequest *req)
 {
-    return nvme_zrm_open(nvme_ctrl(req), zoned, zone);
+    return nvme_zrm_open(nvme_state(req), zoned, zone);
 }
 
 static uint16_t nvme_close_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone,
@@ -3609,7 +3608,7 @@ done:
     }
 }
 
-static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_zone_mgmt_send(NvmeState *n, NvmeRequest *req)
 {
     NvmeCmd *cmd = (NvmeCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
@@ -3758,7 +3757,7 @@ static bool nvme_zone_matches_filter(uint32_t zafs, NvmeZone *zl)
     }
 }
 
-static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_zone_mgmt_recv(NvmeState *n, NvmeRequest *req)
 {
     NvmeCmd *cmd = (NvmeCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
@@ -3866,7 +3865,7 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req)
     return status;
 }
 
-static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_io_cmd(NvmeState *n, NvmeRequest *req)
 {
     NvmeNamespace *ns;
     uint32_t nsid = le32_to_cpu(req->cmd.nsid);
@@ -3949,7 +3948,7 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req)
     return NVME_INVALID_OPCODE | NVME_DNR;
 }
 
-static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n)
+static void nvme_free_sq(NvmeSQueue *sq, NvmeState *n)
 {
     n->sq[sq->sqid] = NULL;
     timer_free(sq->timer);
@@ -3959,7 +3958,7 @@ static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n)
     }
 }
 
-static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_del_sq(NvmeState *n, NvmeRequest *req)
 {
     NvmeDeleteQ *c = (NvmeDeleteQ *)&req->cmd;
     NvmeRequest *r, *next;
@@ -4000,7 +3999,7 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeRequest *req)
     return NVME_SUCCESS;
 }
 
-static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr,
+static void nvme_init_sq(NvmeSQueue *sq, NvmeState *n, uint64_t dma_addr,
                          uint16_t sqid, uint16_t cqid, uint16_t size)
 {
     int i;
@@ -4028,7 +4027,7 @@ static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr,
     n->sq[sqid] = sq;
 }
 
-static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_create_sq(NvmeState *n, NvmeRequest *req)
 {
     NvmeSQueue *sq;
     NvmeCreateSq *c = (NvmeCreateSq *)&req->cmd;
@@ -4084,7 +4083,7 @@ static void nvme_set_blk_stats(NvmeNamespace *ns, struct nvme_stats *stats)
     stats->write_commands += s->nr_ops[BLOCK_ACCT_WRITE];
 }
 
-static uint16_t nvme_smart_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len,
+static uint16_t nvme_smart_info(NvmeState *n, uint8_t rae, uint32_t buf_len,
                                 uint64_t off, NvmeRequest *req)
 {
     uint32_t nsid = le32_to_cpu(req->cmd.nsid);
@@ -4144,7 +4143,7 @@ static uint16_t nvme_smart_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len,
     return nvme_c2h(n, (uint8_t *) &smart + off, trans_len, req);
 }
 
-static uint16_t nvme_fw_log_info(NvmeCtrl *n, uint32_t buf_len, uint64_t off,
+static uint16_t nvme_fw_log_info(NvmeState *n, uint32_t buf_len, uint64_t off,
                                  NvmeRequest *req)
 {
     uint32_t trans_len;
@@ -4162,7 +4161,7 @@ static uint16_t nvme_fw_log_info(NvmeCtrl *n, uint32_t buf_len, uint64_t off,
     return nvme_c2h(n, (uint8_t *) &fw_log + off, trans_len, req);
 }
 
-static uint16_t nvme_error_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len,
+static uint16_t nvme_error_info(NvmeState *n, uint8_t rae, uint32_t buf_len,
                                 uint64_t off, NvmeRequest *req)
 {
     uint32_t trans_len;
@@ -4182,7 +4181,7 @@ static uint16_t nvme_error_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len,
     return nvme_c2h(n, (uint8_t *)&errlog, trans_len, req);
 }
 
-static uint16_t nvme_changed_nslist(NvmeCtrl *n, uint8_t rae, uint32_t buf_len,
+static uint16_t nvme_changed_nslist(NvmeState *n, uint8_t rae, uint32_t buf_len,
                                     uint64_t off, NvmeRequest *req)
 {
     uint32_t nslist[1024];
@@ -4224,7 +4223,7 @@ static uint16_t nvme_changed_nslist(NvmeCtrl *n, uint8_t rae, uint32_t buf_len,
     return nvme_c2h(n, ((uint8_t *)nslist) + off, trans_len, req);
 }
 
-static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len,
+static uint16_t nvme_cmd_effects(NvmeState *n, uint8_t csi, uint32_t buf_len,
                                  uint64_t off, NvmeRequest *req)
 {
     NvmeEffectsLog log = {};
@@ -4264,7 +4263,7 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len,
     return nvme_c2h(n, ((uint8_t *)&log) + off, trans_len, req);
 }
 
-static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_get_log(NvmeState *n, NvmeRequest *req)
 {
     NvmeCmd *cmd = &req->cmd;
 
@@ -4317,7 +4316,7 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req)
     }
 }
 
-static void nvme_free_cq(NvmeCQueue *cq, NvmeCtrl *n)
+static void nvme_free_cq(NvmeCQueue *cq, NvmeState *n)
 {
     n->cq[cq->cqid] = NULL;
     timer_free(cq->timer);
@@ -4329,7 +4328,7 @@ static void nvme_free_cq(NvmeCQueue *cq, NvmeCtrl *n)
     }
 }
 
-static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_del_cq(NvmeState *n, NvmeRequest *req)
 {
     NvmeDeleteQ *c = (NvmeDeleteQ *)&req->cmd;
     NvmeCQueue *cq;
@@ -4356,7 +4355,7 @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeRequest *req)
     return NVME_SUCCESS;
 }
 
-static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr,
+static void nvme_init_cq(NvmeCQueue *cq, NvmeState *n, uint64_t dma_addr,
                          uint16_t cqid, uint16_t vector, uint16_t size,
                          uint16_t irq_enabled)
 {
@@ -4380,7 +4379,7 @@ static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr,
     cq->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, nvme_post_cqes, cq);
 }
 
-static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_create_cq(NvmeState *n, NvmeRequest *req)
 {
     NvmeCQueue *cq;
     NvmeCreateCq *c = (NvmeCreateCq *)&req->cmd;
@@ -4432,21 +4431,21 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRequest *req)
     return NVME_SUCCESS;
 }
 
-static uint16_t nvme_rpt_empty_id_struct(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_rpt_empty_id_struct(NvmeState *n, NvmeRequest *req)
 {
     uint8_t id[NVME_IDENTIFY_DATA_SIZE] = {};
 
     return nvme_c2h(n, id, sizeof(id), req);
 }
 
-static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_identify_ctrl(NvmeState *n, NvmeRequest *req)
 {
     trace_pci_nvme_identify_ctrl();
 
     return nvme_c2h(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), req);
 }
 
-static uint16_t nvme_identify_ctrl_csi(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_identify_ctrl_csi(NvmeState *n, NvmeRequest *req)
 {
     NvmeIdentify *c = (NvmeIdentify *)&req->cmd;
     uint8_t id[NVME_IDENTIFY_DATA_SIZE] = {};
@@ -4471,7 +4470,7 @@ static uint16_t nvme_identify_ctrl_csi(NvmeCtrl *n, NvmeRequest *req)
     return nvme_c2h(n, id, sizeof(id), req);
 }
 
-static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeRequest *req, bool active)
+static uint16_t nvme_identify_ns(NvmeState *n, NvmeRequest *req, bool active)
 {
     NvmeNamespace *ns;
     NvmeIdentify *c = (NvmeIdentify *)&req->cmd;
@@ -4503,7 +4502,7 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeRequest *req, bool active)
     return NVME_INVALID_CMD_SET | NVME_DNR;
 }
 
-static uint16_t nvme_identify_ctrl_list(NvmeCtrl *n, NvmeRequest *req,
+static uint16_t nvme_identify_ctrl_list(NvmeState *n, NvmeRequest *req,
                                         bool attached)
 {
     NvmeIdentify *c = (NvmeIdentify *)&req->cmd;
@@ -4512,7 +4511,7 @@ static uint16_t nvme_identify_ctrl_list(NvmeCtrl *n, NvmeRequest *req,
     uint16_t list[NVME_CONTROLLER_LIST_SIZE] = {};
     uint16_t *ids = &list[1];
     NvmeNamespace *ns;
-    NvmeCtrl *ctrl;
+    NvmeState *ctrl;
     int cntlid, nr_ids = 0;
 
     trace_pci_nvme_identify_ctrl_list(c->cns, min_id);
@@ -4550,7 +4549,7 @@ static uint16_t nvme_identify_ctrl_list(NvmeCtrl *n, NvmeRequest *req,
     return nvme_c2h(n, (uint8_t *)list, sizeof(list), req);
 }
 
-static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req,
+static uint16_t nvme_identify_ns_csi(NvmeState *n, NvmeRequest *req,
                                      bool active)
 {
     NvmeNamespace *ns;
@@ -4585,7 +4584,7 @@ static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req,
     return NVME_INVALID_FIELD | NVME_DNR;
 }
 
-static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeRequest *req,
+static uint16_t nvme_identify_nslist(NvmeState *n, NvmeRequest *req,
                                      bool active)
 {
     NvmeNamespace *ns;
@@ -4632,7 +4631,7 @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeRequest *req,
     return nvme_c2h(n, list, data_len, req);
 }
 
-static uint16_t nvme_identify_nslist_csi(NvmeCtrl *n, NvmeRequest *req,
+static uint16_t nvme_identify_nslist_csi(NvmeState *n, NvmeRequest *req,
                                          bool active)
 {
     NvmeNamespace *ns;
@@ -4680,7 +4679,7 @@ static uint16_t nvme_identify_nslist_csi(NvmeCtrl *n, NvmeRequest *req,
     return nvme_c2h(n, list, data_len, req);
 }
 
-static uint16_t nvme_identify_ns_descr_list(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_identify_ns_descr_list(NvmeState *n, NvmeRequest *req)
 {
     NvmeNamespace *ns;
     NvmeIdentify *c = (NvmeIdentify *)&req->cmd;
@@ -4739,7 +4738,7 @@ static uint16_t nvme_identify_ns_descr_list(NvmeCtrl *n, NvmeRequest *req)
     return nvme_c2h(n, list, sizeof(list), req);
 }
 
-static uint16_t nvme_identify_cmd_set(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_identify_cmd_set(NvmeState *n, NvmeRequest *req)
 {
     uint8_t list[NVME_IDENTIFY_DATA_SIZE] = {};
     static const int data_len = sizeof(list);
@@ -4752,7 +4751,7 @@ static uint16_t nvme_identify_cmd_set(NvmeCtrl *n, NvmeRequest *req)
     return nvme_c2h(n, list, data_len, req);
 }
 
-static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_identify(NvmeState *n, NvmeRequest *req)
 {
     NvmeIdentify *c = (NvmeIdentify *)&req->cmd;
 
@@ -4794,7 +4793,7 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req)
     }
 }
 
-static uint16_t nvme_abort(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_abort(NvmeState *n, NvmeRequest *req)
 {
     uint16_t sqid = le32_to_cpu(req->cmd.cdw10) & 0xffff;
 
@@ -4806,7 +4805,7 @@ static uint16_t nvme_abort(NvmeCtrl *n, NvmeRequest *req)
     return NVME_SUCCESS;
 }
 
-static inline void nvme_set_timestamp(NvmeCtrl *n, uint64_t ts)
+static inline void nvme_set_timestamp(NvmeState *n, uint64_t ts)
 {
     trace_pci_nvme_setfeat_timestamp(ts);
 
@@ -4814,7 +4813,7 @@ static inline void nvme_set_timestamp(NvmeCtrl *n, uint64_t ts)
     n->timestamp_set_qemu_clock_ms = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
 }
 
-static inline uint64_t nvme_get_timestamp(const NvmeCtrl *n)
+static inline uint64_t nvme_get_timestamp(const NvmeState *n)
 {
     uint64_t current_time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
     uint64_t elapsed_time = current_time - n->timestamp_set_qemu_clock_ms;
@@ -4841,14 +4840,14 @@ static inline uint64_t nvme_get_timestamp(const NvmeCtrl *n)
     return cpu_to_le64(ts.all);
 }
 
-static uint16_t nvme_get_feature_timestamp(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_get_feature_timestamp(NvmeState *n, NvmeRequest *req)
 {
     uint64_t timestamp = nvme_get_timestamp(n);
 
     return nvme_c2h(n, (uint8_t *)&timestamp, sizeof(timestamp), req);
 }
 
-static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_get_feature(NvmeState *n, NvmeRequest *req)
 {
     NvmeCmd *cmd = &req->cmd;
     uint32_t dw10 = le32_to_cpu(cmd->cdw10);
@@ -4998,7 +4997,7 @@ out:
     return NVME_SUCCESS;
 }
 
-static uint16_t nvme_set_feature_timestamp(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_set_feature_timestamp(NvmeState *n, NvmeRequest *req)
 {
     uint16_t ret;
     uint64_t timestamp;
@@ -5013,7 +5012,7 @@ static uint16_t nvme_set_feature_timestamp(NvmeCtrl *n, NvmeRequest *req)
     return NVME_SUCCESS;
 }
 
-static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_set_feature(NvmeState *n, NvmeRequest *req)
 {
     NvmeNamespace *ns = NULL;
     NvmeNamespaceNvm *nvm;
@@ -5160,7 +5159,7 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
     return NVME_SUCCESS;
 }
 
-static uint16_t nvme_aer(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_aer(NvmeState *n, NvmeRequest *req)
 {
     trace_pci_nvme_aer(nvme_cid(req));
 
@@ -5179,7 +5178,7 @@ static uint16_t nvme_aer(NvmeCtrl *n, NvmeRequest *req)
     return NVME_NO_COMPLETE;
 }
 
-static void nvme_update_dmrsl(NvmeCtrl *n)
+static void nvme_update_dmrsl(NvmeState *n)
 {
     int nsid;
 
@@ -5197,7 +5196,7 @@ static void nvme_update_dmrsl(NvmeCtrl *n)
     }
 }
 
-static void nvme_select_iocs_ns(NvmeCtrl *n, NvmeNamespace *ns)
+static void nvme_select_iocs_ns(NvmeState *n, NvmeNamespace *ns)
 {
     uint32_t cc = ldl_le_p(&n->bar.cc);
 
@@ -5218,10 +5217,10 @@ static void nvme_select_iocs_ns(NvmeCtrl *n, NvmeNamespace *ns)
     }
 }
 
-static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_ns_attachment(NvmeState *n, NvmeRequest *req)
 {
     NvmeNamespace *ns;
-    NvmeCtrl *ctrl;
+    NvmeState *ctrl;
     uint16_t list[NVME_CONTROLLER_LIST_SIZE] = {};
     uint32_t nsid = le32_to_cpu(req->cmd.nsid);
     uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
@@ -5413,7 +5412,7 @@ static void nvme_format_bh(void *opaque)
 {
     NvmeFormatAIOCB *iocb = opaque;
     NvmeRequest *req = iocb->req;
-    NvmeCtrl *n = nvme_ctrl(req);
+    NvmeState *n = nvme_state(req);
     uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
     uint8_t lbaf = dw10 & 0xf;
     uint8_t pi = (dw10 >> 5) & 0x7;
@@ -5457,7 +5456,7 @@ done:
     qemu_aio_unref(iocb);
 }
 
-static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_format(NvmeState *n, NvmeRequest *req)
 {
     NvmeFormatAIOCB *iocb;
     uint32_t nsid = le32_to_cpu(req->cmd.nsid);
@@ -5498,7 +5497,7 @@ out:
     return status;
 }
 
-static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req)
+static uint16_t nvme_admin_cmd(NvmeState *n, NvmeRequest *req)
 {
     trace_pci_nvme_admin_cmd(nvme_cid(req), nvme_sqid(req), req->cmd.opcode,
                              nvme_adm_opc_str(req->cmd.opcode));
@@ -5552,7 +5551,7 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req)
 static void nvme_process_sq(void *opaque)
 {
     NvmeSQueue *sq = opaque;
-    NvmeCtrl *n = sq->ctrl;
+    NvmeState *n = sq->ctrl;
     NvmeCQueue *cq = n->cq[sq->cqid];
 
     uint16_t status;
@@ -5586,7 +5585,7 @@ static void nvme_process_sq(void *opaque)
     }
 }
 
-static void nvme_ctrl_reset(NvmeCtrl *n)
+static void nvme_ctrl_reset(NvmeState *n)
 {
     NvmeNamespace *ns;
     int i;
@@ -5622,7 +5621,7 @@ static void nvme_ctrl_reset(NvmeCtrl *n)
     n->qs_created = false;
 }
 
-static void nvme_ctrl_shutdown(NvmeCtrl *n)
+static void nvme_ctrl_shutdown(NvmeState *n)
 {
     NvmeNamespace *ns;
     int i;
@@ -5641,7 +5640,7 @@ static void nvme_ctrl_shutdown(NvmeCtrl *n)
     }
 }
 
-static void nvme_select_iocs(NvmeCtrl *n)
+static void nvme_select_iocs(NvmeState *n)
 {
     NvmeNamespace *ns;
     int i;
@@ -5656,7 +5655,7 @@ static void nvme_select_iocs(NvmeCtrl *n)
     }
 }
 
-static int nvme_start_ctrl(NvmeCtrl *n)
+static int nvme_start_ctrl(NvmeState *n)
 {
     uint64_t cap = ldq_le_p(&n->bar.cap);
     uint32_t cc = ldl_le_p(&n->bar.cc);
@@ -5753,7 +5752,7 @@ static int nvme_start_ctrl(NvmeCtrl *n)
     return 0;
 }
 
-static void nvme_cmb_enable_regs(NvmeCtrl *n)
+static void nvme_cmb_enable_regs(NvmeState *n)
 {
     uint32_t cmbloc = ldl_le_p(&n->bar.cmbloc);
     uint32_t cmbsz = ldl_le_p(&n->bar.cmbsz);
@@ -5773,7 +5772,7 @@ static void nvme_cmb_enable_regs(NvmeCtrl *n)
     stl_le_p(&n->bar.cmbsz, cmbsz);
 }
 
-static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
+static void nvme_write_bar(NvmeState *n, hwaddr offset, uint64_t data,
                            unsigned size)
 {
     uint64_t cap = ldq_le_p(&n->bar.cap);
@@ -6023,7 +6022,7 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
 
 static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
 {
-    NvmeCtrl *n = (NvmeCtrl *)opaque;
+    NvmeState *n = (NvmeState *)opaque;
     uint8_t *ptr = (uint8_t *)&n->bar;
 
     trace_pci_nvme_mmio_read(addr, size);
@@ -6061,7 +6060,7 @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
     return ldn_le_p(ptr + addr, size);
 }
 
-static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
+static void nvme_process_db(NvmeState *n, hwaddr addr, int val)
 {
     uint32_t qid;
 
@@ -6193,7 +6192,7 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
 static void nvme_mmio_write(void *opaque, hwaddr addr, uint64_t data,
                             unsigned size)
 {
-    NvmeCtrl *n = (NvmeCtrl *)opaque;
+    NvmeState *n = (NvmeState *)opaque;
 
     trace_pci_nvme_mmio_write(addr, data, size);
 
@@ -6217,13 +6216,13 @@ static const MemoryRegionOps nvme_mmio_ops = {
 static void nvme_cmb_write(void *opaque, hwaddr addr, uint64_t data,
                            unsigned size)
 {
-    NvmeCtrl *n = (NvmeCtrl *)opaque;
+    NvmeState *n = (NvmeState *)opaque;
     stn_le_p(&n->cmb.buf[addr], size, data);
 }
 
 static uint64_t nvme_cmb_read(void *opaque, hwaddr addr, unsigned size)
 {
-    NvmeCtrl *n = (NvmeCtrl *)opaque;
+    NvmeState *n = (NvmeState *)opaque;
     return ldn_le_p(&n->cmb.buf[addr], size);
 }
 
@@ -6237,7 +6236,7 @@ static const MemoryRegionOps nvme_cmb_ops = {
     },
 };
 
-static void nvme_check_constraints(NvmeCtrl *n, Error **errp)
+static int nvme_check_constraints(NvmeState *n, Error **errp)
 {
     NvmeParams *params = &n->params;
 
@@ -6248,41 +6247,35 @@ static void nvme_check_constraints(NvmeCtrl *n, Error **errp)
         params->max_ioqpairs = params->num_queues - 1;
     }
 
-    if (n->namespace.blkconf.blk && n->subsys) {
-        error_setg(errp, "subsystem support is unavailable with legacy "
-                   "namespace ('drive' property)");
-        return;
-    }
-
     if (params->max_ioqpairs < 1 ||
         params->max_ioqpairs > NVME_MAX_IOQPAIRS) {
         error_setg(errp, "max_ioqpairs must be between 1 and %d",
                    NVME_MAX_IOQPAIRS);
-        return;
+        return -1;
     }
 
     if (params->msix_qsize < 1 ||
         params->msix_qsize > PCI_MSIX_FLAGS_QSIZE + 1) {
         error_setg(errp, "msix_qsize must be between 1 and %d",
                    PCI_MSIX_FLAGS_QSIZE + 1);
-        return;
+        return -1;
     }
 
     if (!params->serial) {
         error_setg(errp, "serial property not set");
-        return;
+        return -1;
     }
 
     if (n->pmr.dev) {
         if (host_memory_backend_is_mapped(n->pmr.dev)) {
             error_setg(errp, "can't use already busy memdev: %s",
                        object_get_canonical_path_component(OBJECT(n->pmr.dev)));
-            return;
+            return -1;
         }
 
         if (!is_power_of_2(n->pmr.dev->size)) {
             error_setg(errp, "pmr backend size needs to be power of 2 in size");
-            return;
+            return -1;
         }
 
         host_memory_backend_set_mapped(n->pmr.dev, true);
@@ -6291,16 +6284,18 @@ static void nvme_check_constraints(NvmeCtrl *n, Error **errp)
     if (n->params.zasl > n->params.mdts) {
         error_setg(errp, "zoned.zasl (Zone Append Size Limit) must be less "
                    "than or equal to mdts (Maximum Data Transfer Size)");
-        return;
+        return -1;
     }
 
     if (!n->params.vsl) {
         error_setg(errp, "vsl must be non-zero");
-        return;
+        return -1;
     }
+
+    return 0;
 }
 
-static void nvme_init_state(NvmeCtrl *n)
+static void nvme_init_state(NvmeState *n)
 {
     /* add one to max_ioqpairs to account for the admin queue pair */
     n->reg_size = pow2ceil(sizeof(NvmeBar) +
@@ -6313,7 +6308,7 @@ static void nvme_init_state(NvmeCtrl *n)
     n->aer_reqs = g_new0(NvmeRequest *, n->params.aerl + 1);
 }
 
-static void nvme_init_cmb(NvmeCtrl *n, PCIDevice *pci_dev)
+static void nvme_init_cmb(NvmeState *n, PCIDevice *pci_dev)
 {
     uint64_t cmb_size = n->params.cmb_size_mb * MiB;
     uint64_t cap = ldq_le_p(&n->bar.cap);
@@ -6335,7 +6330,7 @@ static void nvme_init_cmb(NvmeCtrl *n, PCIDevice *pci_dev)
     }
 }
 
-static void nvme_init_pmr(NvmeCtrl *n, PCIDevice *pci_dev)
+static void nvme_init_pmr(NvmeState *n, PCIDevice *pci_dev)
 {
     uint32_t pmrcap = ldl_le_p(&n->bar.pmrcap);
 
@@ -6355,7 +6350,7 @@ static void nvme_init_pmr(NvmeCtrl *n, PCIDevice *pci_dev)
     memory_region_set_enabled(&n->pmr.dev->mr, false);
 }
 
-static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
+static int nvme_init_pci(NvmeState *n, PCIDevice *pci_dev, Error **errp)
 {
     uint8_t *pci_conf = pci_dev->config;
     uint64_t bar_size, msix_table_size, msix_pba_size;
@@ -6420,7 +6415,7 @@ static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
     return 0;
 }
 
-static void nvme_init_subnqn(NvmeCtrl *n)
+static void nvme_init_subnqn(NvmeState *n)
 {
     NvmeSubsystem *subsys = n->subsys;
     NvmeIdCtrl *id = &n->id_ctrl;
@@ -6433,7 +6428,7 @@ static void nvme_init_subnqn(NvmeCtrl *n)
     }
 }
 
-static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev)
+static void nvme_init_ctrl(NvmeState *n, PCIDevice *pci_dev)
 {
     NvmeIdCtrl *id = &n->id_ctrl;
     uint8_t *pci_conf = pci_dev->config;
@@ -6531,7 +6526,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev)
     n->bar.intmc = n->bar.intms = 0;
 }
 
-static int nvme_init_subsys(NvmeCtrl *n, Error **errp)
+static int nvme_init_subsys(NvmeState *n, Error **errp)
 {
     int cntlid;
 
@@ -6549,7 +6544,7 @@ static int nvme_init_subsys(NvmeCtrl *n, Error **errp)
     return 0;
 }
 
-void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns)
+void nvme_attach_ns(NvmeState *n, NvmeNamespace *ns)
 {
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     uint32_t nsid = ns->nsid;
@@ -6564,20 +6559,24 @@ void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns)
 
 static void nvme_realize(PCIDevice *pci_dev, Error **errp)
 {
-    NvmeCtrl *n = NVME(pci_dev);
-    Error *local_err = NULL;
+    NvmeCtrl *ctrl = NVME_DEVICE(pci_dev);
+    NvmeState *n = NVME_STATE(ctrl);
 
     if (n->subsys_dev) {
         n->subsys = &n->subsys_dev->subsys;
     }
 
-    nvme_check_constraints(n, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
+    if (ctrl->namespace.blkconf.blk && n->subsys) {
+        error_setg(errp, "subsystem support is unavailable with legacy "
+                   "namespace ('drive' property)");
         return;
     }
 
-    qbus_create_inplace(&n->bus, sizeof(NvmeBus), TYPE_NVME_BUS,
+    if (nvme_check_constraints(n, errp)) {
+        return;
+    }
+
+    qbus_create_inplace(&ctrl->bus, sizeof(NvmeBus), TYPE_NVME_BUS,
                         &pci_dev->qdev, n->parent_obj.qdev.id);
 
     nvme_init_state(n);
@@ -6586,14 +6585,13 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
     }
 
     if (nvme_init_subsys(n, errp)) {
-        error_propagate(errp, local_err);
         return;
     }
     nvme_init_ctrl(n, pci_dev);
 
     /* setup a namespace if the controller drive property was given */
-    if (n->namespace.blkconf.blk) {
-        NvmeNamespaceDevice *nsdev = &n->namespace;
+    if (ctrl->namespace.blkconf.blk) {
+        NvmeNamespaceDevice *nsdev = &ctrl->namespace;
         NvmeNamespace *ns = &nsdev->ns;
         ns->nsid = 1;
 
@@ -6605,7 +6603,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
 
 static void nvme_exit(PCIDevice *pci_dev)
 {
-    NvmeCtrl *n = NVME(pci_dev);
+    NvmeState *n = NVME_STATE(pci_dev);
     NvmeNamespace *ns;
     int i;
 
@@ -6637,33 +6635,37 @@ static void nvme_exit(PCIDevice *pci_dev)
     memory_region_del_subregion(&n->bar0, &n->iomem);
 }
 
+static Property nvme_state_props[] = {
+    DEFINE_PROP_LINK("pmrdev", NvmeState, pmr.dev, TYPE_MEMORY_BACKEND,
+                     HostMemoryBackend *),
+    DEFINE_PROP_LINK("subsys", NvmeState, subsys_dev,
+                     TYPE_NVME_SUBSYSTEM_DEVICE, NvmeSubsystemDevice *),
+    DEFINE_PROP_STRING("serial", NvmeState, params.serial),
+    DEFINE_PROP_UINT32("cmb_size_mb", NvmeState, params.cmb_size_mb, 0),
+    DEFINE_PROP_UINT32("num_queues", NvmeState, params.num_queues, 0),
+    DEFINE_PROP_UINT32("max_ioqpairs", NvmeState, params.max_ioqpairs, 64),
+    DEFINE_PROP_UINT16("msix_qsize", NvmeState, params.msix_qsize, 65),
+    DEFINE_PROP_UINT8("aerl", NvmeState, params.aerl, 3),
+    DEFINE_PROP_UINT32("aer_max_queued", NvmeState, params.aer_max_queued, 64),
+    DEFINE_PROP_UINT8("mdts", NvmeState, params.mdts, 7),
+    DEFINE_PROP_UINT8("vsl", NvmeState, params.vsl, 7),
+    DEFINE_PROP_BOOL("use-intel-id", NvmeState, params.use_intel_id, false),
+    DEFINE_PROP_BOOL("legacy-cmb", NvmeState, params.legacy_cmb, false),
+    DEFINE_PROP_UINT8("zoned.zasl", NvmeState, params.zasl, 0),
+    DEFINE_PROP_BOOL("zoned.auto_transition", NvmeState,
+                     params.auto_transition_zones, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static Property nvme_props[] = {
     DEFINE_BLOCK_PROPERTIES(NvmeCtrl, namespace.blkconf),
-    DEFINE_PROP_LINK("pmrdev", NvmeCtrl, pmr.dev, TYPE_MEMORY_BACKEND,
-                     HostMemoryBackend *),
-    DEFINE_PROP_LINK("subsys", NvmeCtrl, subsys_dev, TYPE_NVME_SUBSYSTEM_DEVICE,
-                     NvmeSubsystemDevice *),
-    DEFINE_PROP_STRING("serial", NvmeCtrl, params.serial),
-    DEFINE_PROP_UINT32("cmb_size_mb", NvmeCtrl, params.cmb_size_mb, 0),
-    DEFINE_PROP_UINT32("num_queues", NvmeCtrl, params.num_queues, 0),
-    DEFINE_PROP_UINT32("max_ioqpairs", NvmeCtrl, params.max_ioqpairs, 64),
-    DEFINE_PROP_UINT16("msix_qsize", NvmeCtrl, params.msix_qsize, 65),
-    DEFINE_PROP_UINT8("aerl", NvmeCtrl, params.aerl, 3),
-    DEFINE_PROP_UINT32("aer_max_queued", NvmeCtrl, params.aer_max_queued, 64),
-    DEFINE_PROP_UINT8("mdts", NvmeCtrl, params.mdts, 7),
-    DEFINE_PROP_UINT8("vsl", NvmeCtrl, params.vsl, 7),
-    DEFINE_PROP_BOOL("use-intel-id", NvmeCtrl, params.use_intel_id, false),
-    DEFINE_PROP_BOOL("legacy-cmb", NvmeCtrl, params.legacy_cmb, false),
-    DEFINE_PROP_UINT8("zoned.zasl", NvmeCtrl, params.zasl, 0),
-    DEFINE_PROP_BOOL("zoned.auto_transition", NvmeCtrl,
-                     params.auto_transition_zones, true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
 static void nvme_get_smart_warning(Object *obj, Visitor *v, const char *name,
                                    void *opaque, Error **errp)
 {
-    NvmeCtrl *n = NVME(obj);
+    NvmeState *n = NVME_STATE(obj);
     uint8_t value = n->smart_critical_warning;
 
     visit_type_uint8(v, name, &value, errp);
@@ -6672,7 +6674,7 @@ static void nvme_get_smart_warning(Object *obj, Visitor *v, const char *name,
 static void nvme_set_smart_warning(Object *obj, Visitor *v, const char *name,
                                    void *opaque, Error **errp)
 {
-    NvmeCtrl *n = NVME(obj);
+    NvmeState *n = NVME_STATE(obj);
     uint8_t value, old_value, cap = 0, index, event;
 
     if (!visit_type_uint8(v, name, &value, errp)) {
@@ -6707,7 +6709,7 @@ static const VMStateDescription nvme_vmstate = {
     .unmigratable = 1,
 };
 
-static void nvme_class_init(ObjectClass *oc, void *data)
+static void nvme_state_class_init(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
     PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
@@ -6719,35 +6721,54 @@ static void nvme_class_init(ObjectClass *oc, void *data)
 
     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
     dc->desc = "Non-Volatile Memory Express";
-    device_class_set_props(dc, nvme_props);
+    device_class_set_props(dc, nvme_state_props);
     dc->vmsd = &nvme_vmstate;
 }
 
-static void nvme_instance_init(Object *obj)
+static void nvme_state_instance_init(Object *obj)
 {
-    NvmeCtrl *n = NVME(obj);
-
-    device_add_bootindex_property(obj, &n->namespace.blkconf.bootindex,
-                                  "bootindex", "/namespace@1,0",
-                                  DEVICE(obj));
-
     object_property_add(obj, "smart_critical_warning", "uint8",
                         nvme_get_smart_warning,
                         nvme_set_smart_warning, NULL, NULL);
 }
 
-static const TypeInfo nvme_info = {
-    .name          = TYPE_NVME,
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(NvmeCtrl),
-    .instance_init = nvme_instance_init,
-    .class_init    = nvme_class_init,
+static const TypeInfo nvme_state_info = {
+    .name = TYPE_NVME_STATE,
+    .parent = TYPE_PCI_DEVICE,
+    .abstract = true,
+    .class_init = nvme_state_class_init,
+    .instance_size = sizeof(NvmeState),
+    .instance_init = nvme_state_instance_init,
     .interfaces = (InterfaceInfo[]) {
         { INTERFACE_PCIE_DEVICE },
-        { }
+        { },
     },
 };
 
+static void nvme_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    device_class_set_props(dc, nvme_props);
+}
+
+static void nvme_instance_init(Object *obj)
+{
+    NvmeCtrl *ctrl = NVME_DEVICE(obj);
+
+    device_add_bootindex_property(obj, &ctrl->namespace.blkconf.bootindex,
+                                  "bootindex", "/namespace@1,0",
+                                  DEVICE(obj));
+}
+
+static const TypeInfo nvme_info = {
+    .name = TYPE_NVME_DEVICE,
+    .parent = TYPE_NVME_STATE,
+    .class_init = nvme_class_init,
+    .instance_size = sizeof(NvmeCtrl),
+    .instance_init = nvme_instance_init,
+    .class_init = nvme_class_init,
+};
+
 static const TypeInfo nvme_bus_info = {
     .name = TYPE_NVME_BUS,
     .parent = TYPE_BUS,
@@ -6756,6 +6777,7 @@ static const TypeInfo nvme_bus_info = {
 
 static void nvme_register_types(void)
 {
+    type_register_static(&nvme_state_info);
     type_register_static(&nvme_info);
     type_register_static(&nvme_bus_info);
 }
diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c
index 1b8f9ba2fb44..8ad517232c1d 100644
--- a/hw/nvme/dif.c
+++ b/hw/nvme/dif.c
@@ -248,7 +248,7 @@ static void nvme_dif_rw_check_cb(void *opaque, int ret)
     NvmeRequest *req = ctx->req;
     NvmeNamespace *ns = req->ns;
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    NvmeCtrl *n = nvme_ctrl(req);
+    NvmeState *n = nvme_state(req);
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     uint64_t slba = le64_to_cpu(rw->slba);
     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
@@ -357,7 +357,7 @@ out:
     nvme_dif_rw_cb(ctx, ret);
 }
 
-uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
+uint16_t nvme_dif_rw(NvmeState *n, NvmeRequest *req)
 {
     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
     NvmeNamespace *ns = req->ns;
diff --git a/hw/nvme/dif.h b/hw/nvme/dif.h
index 7d47299252ae..53a22bc7c78e 100644
--- a/hw/nvme/dif.h
+++ b/hw/nvme/dif.h
@@ -48,6 +48,6 @@ uint16_t nvme_dif_check(NvmeNamespaceNvm *nvm, uint8_t *buf, size_t len,
                         uint8_t *mbuf, size_t mlen, uint8_t prinfo,
                         uint64_t slba, uint16_t apptag,
                         uint16_t appmask, uint32_t *reftag);
-uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req);
+uint16_t nvme_dif_rw(NvmeState *n, NvmeRequest *req);
 
 #endif /* HW_NVME_DIF_H */
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index d67632174530..efbe9b3808e4 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -493,7 +493,7 @@ static void nvme_nsdev_realize(DeviceState *dev, Error **errp)
     NvmeNamespaceDevice *nsdev = NVME_NAMESPACE_DEVICE(dev);
     NvmeNamespace *ns = &nsdev->ns;
     BusState *s = qdev_get_parent_bus(dev);
-    NvmeCtrl *n = NVME(s->parent);
+    NvmeState *n = NVME_STATE(s->parent);
     NvmeSubsystem *subsys = n->subsys;
     uint32_t nsid = nsdev->params.nsid;
     int i;
@@ -552,7 +552,7 @@ static void nvme_nsdev_realize(DeviceState *dev, Error **errp)
 
         if (nsdev->params.shared) {
             for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) {
-                NvmeCtrl *ctrl = subsys->ctrls[i];
+                NvmeState *ctrl = subsys->ctrls[i];
 
                 if (ctrl) {
                     nvme_attach_ns(ctrl, ns);
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index e8f4b8e3e2e0..a0c34a49772f 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -30,9 +30,14 @@
 
 QEMU_BUILD_BUG_ON(NVME_MAX_NAMESPACES > NVME_NSID_BROADCAST - 1);
 
-typedef struct NvmeCtrl NvmeCtrl;
 typedef struct NvmeNamespace NvmeNamespace;
 
+#define TYPE_NVME_STATE "nvme-state"
+OBJECT_DECLARE_SIMPLE_TYPE(NvmeState, NVME_STATE)
+
+#define TYPE_NVME_DEVICE "nvme"
+OBJECT_DECLARE_SIMPLE_TYPE(NvmeCtrl, NVME_DEVICE)
+
 #define TYPE_NVME_BUS "nvme-bus"
 OBJECT_DECLARE_SIMPLE_TYPE(NvmeBus, NVME_BUS)
 
@@ -43,7 +48,7 @@ typedef struct NvmeBus {
 typedef struct NvmeSubsystem {
     uint8_t     subnqn[256];
 
-    NvmeCtrl      *ctrls[NVME_MAX_CONTROLLERS];
+    NvmeState     *ctrls[NVME_MAX_CONTROLLERS];
     NvmeNamespace *namespaces[NVME_MAX_NAMESPACES + 1];
 } NvmeSubsystem;
 
@@ -61,12 +66,12 @@ typedef struct NvmeSubsystemDevice {
     } params;
 } NvmeSubsystemDevice;
 
-int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n,
+int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeState *n,
                               Error **errp);
-void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n);
+void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeState *n);
 
-static inline NvmeCtrl *nvme_subsys_ctrl(NvmeSubsystem *subsys,
-                                         uint32_t cntlid)
+static inline NvmeState *nvme_subsys_ctrl(NvmeSubsystem *subsys,
+                                          uint32_t cntlid)
 {
     if (!subsys || cntlid >= NVME_MAX_CONTROLLERS) {
         return NULL;
@@ -342,7 +347,7 @@ static inline const char *nvme_io_opc_str(uint8_t opc)
 }
 
 typedef struct NvmeSQueue {
-    struct NvmeCtrl *ctrl;
+    NvmeState   *ctrl;
     uint16_t    sqid;
     uint16_t    cqid;
     uint32_t    head;
@@ -357,7 +362,7 @@ typedef struct NvmeSQueue {
 } NvmeSQueue;
 
 typedef struct NvmeCQueue {
-    struct NvmeCtrl *ctrl;
+    NvmeState   *ctrl;
     uint8_t     phase;
     uint16_t    cqid;
     uint16_t    irq_enabled;
@@ -371,10 +376,6 @@ typedef struct NvmeCQueue {
     QTAILQ_HEAD(, NvmeRequest) req_list;
 } NvmeCQueue;
 
-#define TYPE_NVME "nvme"
-#define NVME(obj) \
-        OBJECT_CHECK(NvmeCtrl, (obj), TYPE_NVME)
-
 typedef struct NvmeParams {
     char     *serial;
     uint32_t num_queues; /* deprecated since 5.1 */
@@ -391,13 +392,12 @@ typedef struct NvmeParams {
     bool     legacy_cmb;
 } NvmeParams;
 
-typedef struct NvmeCtrl {
+typedef struct NvmeState {
     PCIDevice    parent_obj;
     MemoryRegion bar0;
     MemoryRegion iomem;
     NvmeBar      bar;
     NvmeParams   params;
-    NvmeBus      bus;
 
     uint16_t    cntlid;
     bool        qs_created;
@@ -444,7 +444,6 @@ typedef struct NvmeCtrl {
     NvmeSubsystem       *subsys;
     NvmeSubsystemDevice *subsys_dev;
 
-    NvmeNamespaceDevice namespace;
     NvmeNamespace   *namespaces[NVME_MAX_NAMESPACES + 1];
     NvmeSQueue      **sq;
     NvmeCQueue      **cq;
@@ -459,9 +458,18 @@ typedef struct NvmeCtrl {
         };
         uint32_t    async_config;
     } features;
+} NvmeState;
+
+typedef struct NvmeCtrl {
+    NvmeState parent_obj;
+
+    NvmeBus bus;
+
+    /* for use with legacy single namespace (-device nvme,drive=...) setups */
+    NvmeNamespaceDevice namespace;
 } NvmeCtrl;
 
-static inline NvmeNamespace *nvme_ns(NvmeCtrl *n, uint32_t nsid)
+static inline NvmeNamespace *nvme_ns(NvmeState *n, uint32_t nsid)
 {
     if (!nsid || nsid > NVME_MAX_NAMESPACES) {
         return NULL;
@@ -473,12 +481,12 @@ static inline NvmeNamespace *nvme_ns(NvmeCtrl *n, uint32_t nsid)
 static inline NvmeCQueue *nvme_cq(NvmeRequest *req)
 {
     NvmeSQueue *sq = req->sq;
-    NvmeCtrl *n = sq->ctrl;
+    NvmeState *n = sq->ctrl;
 
     return n->cq[sq->cqid];
 }
 
-static inline NvmeCtrl *nvme_ctrl(NvmeRequest *req)
+static inline NvmeState *nvme_state(NvmeRequest *req)
 {
     NvmeSQueue *sq = req->sq;
     return sq->ctrl;
@@ -493,13 +501,13 @@ static inline uint16_t nvme_cid(NvmeRequest *req)
     return le16_to_cpu(req->cqe.cid);
 }
 
-void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns);
-uint16_t nvme_bounce_data(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
+void nvme_attach_ns(NvmeState *n, NvmeNamespace *ns);
+uint16_t nvme_bounce_data(NvmeState *n, uint8_t *ptr, uint32_t len,
                           NvmeTxDirection dir, NvmeRequest *req);
-uint16_t nvme_bounce_mdata(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
+uint16_t nvme_bounce_mdata(NvmeState *n, uint8_t *ptr, uint32_t len,
                            NvmeTxDirection dir, NvmeRequest *req);
 void nvme_rw_complete_cb(void *opaque, int ret);
-uint16_t nvme_map_dptr(NvmeCtrl *n, NvmeSg *sg, size_t len,
+uint16_t nvme_map_dptr(NvmeState *n, NvmeSg *sg, size_t len,
                        NvmeCmd *cmd);
 
 #endif /* HW_NVME_INTERNAL_H */
diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c
index 4d73d14070dc..3e1736d846b7 100644
--- a/hw/nvme/subsys.c
+++ b/hw/nvme/subsys.c
@@ -11,7 +11,7 @@
 
 #include "nvme.h"
 
-int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n, Error **errp)
+int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeState *n, Error **errp)
 {
     int cntlid, nsid;
 
@@ -38,7 +38,7 @@ int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n, Error **errp)
     return cntlid;
 }
 
-void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *n)
+void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeState *n)
 {
     subsys->ctrls[n->cntlid] = NULL;
     n->cntlid = -1;
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 11/16] hw/nvme: add experimental object x-nvme-subsystem
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (9 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 10/16] hw/nvme: hoist qdev state from controller Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 12/16] nvme: add structured type for nguid Klaus Jensen
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Add a basic user creatable object that models an NVMe NVM subsystem.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c   |  14 ++---
 hw/nvme/ns.c     |   5 +-
 hw/nvme/nvme.h   |  10 +++-
 hw/nvme/subsys.c | 144 +++++++++++++++++++++++++++++++++++++++++++++--
 qapi/qom.json    |  17 ++++++
 5 files changed, 175 insertions(+), 15 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index b27bf00f623f..c798aeb095e1 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -6562,14 +6562,14 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
     NvmeCtrl *ctrl = NVME_DEVICE(pci_dev);
     NvmeState *n = NVME_STATE(ctrl);
 
-    if (n->subsys_dev) {
-        n->subsys = &n->subsys_dev->subsys;
-    }
+    if (ctrl->subsys_dev) {
+        if (ctrl->namespace.blkconf.blk) {
+            error_setg(errp, "subsystem support is unavailable with legacy "
+                       "namespace ('drive' property)");
+            return;
+        }
 
-    if (ctrl->namespace.blkconf.blk && n->subsys) {
-        error_setg(errp, "subsystem support is unavailable with legacy "
-                   "namespace ('drive' property)");
-        return;
+        n->subsys = &ctrl->subsys_dev->subsys;
     }
 
     if (nvme_check_constraints(n, errp)) {
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index efbe9b3808e4..09556f0ec7c9 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -493,7 +493,8 @@ static void nvme_nsdev_realize(DeviceState *dev, Error **errp)
     NvmeNamespaceDevice *nsdev = NVME_NAMESPACE_DEVICE(dev);
     NvmeNamespace *ns = &nsdev->ns;
     BusState *s = qdev_get_parent_bus(dev);
-    NvmeState *n = NVME_STATE(s->parent);
+    NvmeCtrl *ctrl = NVME_DEVICE(s->parent);
+    NvmeState *n = NVME_STATE(ctrl);
     NvmeSubsystem *subsys = n->subsys;
     uint32_t nsid = nsdev->params.nsid;
     int i;
@@ -509,7 +510,7 @@ static void nvme_nsdev_realize(DeviceState *dev, Error **errp)
          * If this namespace belongs to a subsystem (through a link on the
          * controller device), reparent the device.
          */
-        if (!qdev_set_parent_bus(dev, &n->subsys_dev->bus.parent_bus, errp)) {
+        if (!qdev_set_parent_bus(dev, &ctrl->subsys_dev->bus.parent_bus, errp)) {
             return;
         }
     }
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index a0c34a49772f..b67e5900a01d 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -45,8 +45,14 @@ typedef struct NvmeBus {
     BusState parent_bus;
 } NvmeBus;
 
+#define TYPE_NVME_SUBSYSTEM "x-nvme-subsystem"
+OBJECT_DECLARE_SIMPLE_TYPE(NvmeSubsystem, NVME_SUBSYSTEM)
+
 typedef struct NvmeSubsystem {
-    uint8_t     subnqn[256];
+    Object parent_obj;
+
+    QemuUUID uuid;
+    uint8_t  subnqn[256];
 
     NvmeState     *ctrls[NVME_MAX_CONTROLLERS];
     NvmeNamespace *namespaces[NVME_MAX_NAMESPACES + 1];
@@ -467,6 +473,8 @@ typedef struct NvmeCtrl {
 
     /* for use with legacy single namespace (-device nvme,drive=...) setups */
     NvmeNamespaceDevice namespace;
+
+    NvmeSubsystemDevice *subsys_dev;
 } NvmeCtrl;
 
 static inline NvmeNamespace *nvme_ns(NvmeState *n, uint32_t nsid)
diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c
index 3e1736d846b7..2599b83c348e 100644
--- a/hw/nvme/subsys.c
+++ b/hw/nvme/subsys.c
@@ -8,10 +8,13 @@
 
 #include "qemu/osdep.h"
 #include "qapi/error.h"
+#include "qapi/qapi-builtin-visit.h"
+#include "qom/object_interfaces.h"
 
 #include "nvme.h"
 
-int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeState *n, Error **errp)
+int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeState *n,
+                              Error **errp)
 {
     int cntlid, nsid;
 
@@ -44,6 +47,136 @@ void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeState *n)
     n->cntlid = -1;
 }
 
+static void get_controllers(Object *obj, Visitor *v, const char *name,
+                            void *opaque, Error **errp)
+{
+    NvmeSubsystem *subsys = NVME_SUBSYSTEM(obj);
+    strList *paths = NULL;
+    strList **tail = &paths;
+    unsigned cntlid;
+
+    for (cntlid = 0; cntlid < NVME_MAX_CONTROLLERS; cntlid++) {
+        NvmeState *n = subsys->ctrls[cntlid];
+        if (!n) {
+            continue;
+        }
+
+        QAPI_LIST_APPEND(tail, object_get_canonical_path(OBJECT(n)));
+    }
+
+    visit_type_strList(v, name, &paths, errp);
+    qapi_free_strList(paths);
+}
+
+static void get_namespaces(Object *obj, Visitor *v, const char *name,
+                           void *opaque, Error **errp)
+{
+    NvmeSubsystem *subsys = NVME_SUBSYSTEM(obj);
+    strList *paths = NULL;
+    strList **tail = &paths;
+    unsigned nsid;
+
+    for (nsid = 1; nsid <= NVME_MAX_NAMESPACES; nsid++) {
+        NvmeNamespace *ns = subsys->namespaces[nsid];
+        if (!ns) {
+            continue;
+        }
+
+        QAPI_LIST_APPEND(tail, object_get_canonical_path(OBJECT(ns)));
+    }
+
+    visit_type_strList(v, name, &paths, errp);
+    qapi_free_strList(paths);
+}
+
+static char *get_subnqn(Object *obj, Error **errp)
+{
+    NvmeSubsystem *subsys = NVME_SUBSYSTEM(obj);
+    return g_strdup((char *)subsys->subnqn);
+}
+
+static void set_subnqn(Object *obj, const char *str, Error **errp)
+{
+    NvmeSubsystem *subsys = NVME_SUBSYSTEM(obj);
+    snprintf((char *)subsys->subnqn, sizeof(subsys->subnqn), "%s", str);
+}
+
+static char *get_uuid(Object *obj, Error **errp)
+{
+    NvmeSubsystem *subsys = NVME_SUBSYSTEM(obj);
+    char buf[UUID_FMT_LEN + 1];
+
+    qemu_uuid_unparse(&subsys->uuid, buf);
+
+    return g_strdup(buf);
+}
+
+static void set_uuid(Object *obj, const char *str, Error **errp)
+{
+    NvmeSubsystem *subsys = NVME_SUBSYSTEM(obj);
+
+    if (!strcmp(str, "auto")) {
+        qemu_uuid_generate(&subsys->uuid);
+    } else if (qemu_uuid_parse(str, &subsys->uuid) < 0) {
+        error_setg(errp, "invalid uuid");
+        return;
+    }
+}
+
+static void nvme_subsys_complete(UserCreatable *uc, Error **errp)
+{
+    NvmeSubsystem *subsys = NVME_SUBSYSTEM(uc);
+
+    if (!strcmp((char *)subsys->subnqn, "")) {
+        char buf[UUID_FMT_LEN + 1];
+
+        qemu_uuid_unparse(&subsys->uuid, buf);
+
+        snprintf((char *)subsys->subnqn, sizeof(subsys->subnqn),
+                 "nqn.2014-08.org.nvmexpress:uuid:%s", buf);
+    }
+}
+
+static void nvme_subsys_instance_init(Object *obj)
+{
+    object_property_add(obj, "controllers", "str", get_controllers, NULL, NULL,
+                        NULL);
+
+    object_property_add(obj, "namespaces", "str", get_namespaces, NULL, NULL,
+                        NULL);
+}
+
+static void nvme_subsys_class_init(ObjectClass *oc, void *data)
+{
+    ObjectProperty *op;
+
+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+    ucc->complete = nvme_subsys_complete;
+
+    object_class_property_add_str(oc, "subnqn", get_subnqn, set_subnqn);
+    object_class_property_set_description(oc, "subnqn", "the NVM Subsystem "
+                                          "NVMe Qualified Name; "
+                                          "(default: \"nqn.2014-08.org.nvmexpress:uuid:<uuid>\")");
+
+    op = object_class_property_add_str(oc, "uuid", get_uuid, set_uuid);
+    object_property_set_default_str(op, "auto");
+    object_class_property_set_description(oc, "subnqn", "NVM Subsystem UUID "
+                                          "(\"auto\" for random value; "
+                                          "default: \"auto\")");
+}
+
+static const TypeInfo nvme_subsys_info = {
+    .name = TYPE_NVME_SUBSYSTEM,
+    .parent = TYPE_OBJECT,
+    .class_init = nvme_subsys_class_init,
+    .instance_init = nvme_subsys_instance_init,
+    .instance_size = sizeof(NvmeSubsystem),
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { },
+    }
+};
+
 static void nvme_subsys_device_setup(NvmeSubsystemDevice *dev)
 {
     NvmeSubsystem *subsys = &dev->subsys;
@@ -64,7 +197,7 @@ static void nvme_subsys_device_realize(DeviceState *dev, Error **errp)
     nvme_subsys_device_setup(subsys);
 }
 
-static Property nvme_subsystem_device_props[] = {
+static Property nvme_subsys_device_props[] = {
     DEFINE_PROP_STRING("nqn", NvmeSubsystemDevice, params.nqn),
     DEFINE_PROP_END_OF_LIST(),
 };
@@ -79,7 +212,7 @@ static void nvme_subsys_device_class_init(ObjectClass *oc, void *data)
     dc->desc = "Virtual NVMe subsystem";
     dc->hotpluggable = false;
 
-    device_class_set_props(dc, nvme_subsystem_device_props);
+    device_class_set_props(dc, nvme_subsys_device_props);
 }
 
 static const TypeInfo nvme_subsys_device_info = {
@@ -89,9 +222,10 @@ static const TypeInfo nvme_subsys_device_info = {
     .instance_size = sizeof(NvmeSubsystemDevice),
 };
 
-static void nvme_subsys_register_types(void)
+static void register_types(void)
 {
+    type_register_static(&nvme_subsys_info);
     type_register_static(&nvme_subsys_device_info);
 }
 
-type_init(nvme_subsys_register_types)
+type_init(register_types)
diff --git a/qapi/qom.json b/qapi/qom.json
index a25616bc7a7d..d4c211fc38b1 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -647,6 +647,21 @@
             '*hugetlbsize': 'size',
             '*seal': 'bool' } }
 
+##
+# @NvmeSubsystemProperties:
+#
+# Properties for nvme-subsys objects.
+#
+# @subnqn: the NVM Subsystem NVMe Qualified Name
+#
+# @uuid: the UUID of the subsystem. Used as default in subnqn.
+#
+# Since: 6.1
+##
+{ 'struct': 'NvmeSubsystemProperties',
+  'data': { '*subnqn': 'str',
+            '*uuid': 'str' } }
+
 ##
 # @PrManagerHelperProperties:
 #
@@ -797,6 +812,7 @@
     { 'name': 'memory-backend-memfd',
       'if': 'CONFIG_LINUX' },
     'memory-backend-ram',
+    'x-nvme-subsystem',
     'pef-guest',
     'pr-manager-helper',
     'qtest',
@@ -855,6 +871,7 @@
       'memory-backend-memfd':       { 'type': 'MemoryBackendMemfdProperties',
                                       'if': 'CONFIG_LINUX' },
       'memory-backend-ram':         'MemoryBackendProperties',
+      'x-nvme-subsystem':           'NvmeSubsystemProperties',
       'pr-manager-helper':          'PrManagerHelperProperties',
       'qtest':                      'QtestProperties',
       'rng-builtin':                'RngProperties',
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 12/16] nvme: add structured type for nguid
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (10 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 11/16] hw/nvme: add experimental object x-nvme-subsystem Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 13/16] hw/nvme: add experimental abstract object x-nvme-ns Klaus Jensen
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Add a structured type for NGUID.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 include/block/nvme.h | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/include/block/nvme.h b/include/block/nvme.h
index 2bcabe561589..f41464ee19bd 100644
--- a/include/block/nvme.h
+++ b/include/block/nvme.h
@@ -1269,6 +1269,11 @@ typedef struct QEMU_PACKED NvmeLBAFE {
 
 #define NVME_NSID_BROADCAST 0xffffffff
 
+typedef struct QEMU_PACKED NvmeNGUID {
+    uint8_t     vspexid[8];
+    uint64_t    eui;
+} NvmeNGUID;
+
 typedef struct QEMU_PACKED NvmeIdNs {
     uint64_t    nsze;
     uint64_t    ncap;
@@ -1300,7 +1305,7 @@ typedef struct QEMU_PACKED NvmeIdNs {
     uint32_t    mcl;
     uint8_t     msrc;
     uint8_t     rsvd81[23];
-    uint8_t     nguid[16];
+    NvmeNGUID   nguid;
     uint64_t    eui64;
     NvmeLBAF    lbaf[16];
     uint8_t     rsvd192[192];
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 13/16] hw/nvme: add experimental abstract object x-nvme-ns
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (11 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 12/16] nvme: add structured type for nguid Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 14/16] hw/nvme: add experimental objects x-nvme-ns-{nvm, zoned} Klaus Jensen
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Add the abstract NvmeNamespace object to base proper namespace types on.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ns.c     | 216 +++++++++++++++++++++++++++++++++++++++++++++++
 hw/nvme/nvme.h   |  22 +++++
 hw/nvme/subsys.c |  31 +++++++
 qapi/qom.json    |  17 ++++
 4 files changed, 286 insertions(+)

diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index 09556f0ec7c9..d75ff4f1cb74 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -13,9 +13,13 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/ctype.h"
 #include "qemu/units.h"
 #include "qemu/error-report.h"
 #include "qapi/error.h"
+#include "qapi/qapi-builtin-visit.h"
+#include "qom/object_interfaces.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/block-backend.h"
 
@@ -632,8 +636,220 @@ static const TypeInfo nvme_nsdev_info = {
     .instance_init = nvme_nsdev_instance_init,
 };
 
+bool nvme_ns_prop_writable(Object *obj, const char *name, Error **errp)
+{
+    NvmeNamespace *ns = NVME_NAMESPACE(obj);
+
+    if (ns->realized) {
+        error_setg(errp, "attempt to set immutable property '%s' on "
+                   "active namespace", name);
+        return false;
+    }
+
+    return true;
+}
+
+static char *nvme_ns_get_nsid(Object *obj, Error **errp)
+{
+    NvmeNamespace *ns = NVME_NAMESPACE(obj);
+
+    return g_strdup_printf("%d\n", ns->nsid);
+}
+
+static void nvme_ns_set_nsid(Object *obj, const char *v, Error **errp)
+{
+    NvmeNamespace *ns = NVME_NAMESPACE(obj);
+    unsigned long nsid;
+
+    if (!nvme_ns_prop_writable(obj, "nsid", errp)) {
+        return;
+    }
+
+    if (!strcmp(v, "auto")) {
+        ns->nsid = 0;
+        return;
+    }
+
+    if (qemu_strtoul(v, NULL, 0, &nsid) < 0 || nsid > NVME_MAX_NAMESPACES) {
+        error_setg(errp, "invalid namespace identifier");
+        return;
+    }
+
+    ns->nsid = nsid;
+}
+
+static char *nvme_ns_get_uuid(Object *obj, Error **errp)
+{
+    NvmeNamespace *ns = NVME_NAMESPACE(obj);
+
+    char *str = g_malloc(UUID_FMT_LEN + 1);
+
+    qemu_uuid_unparse(&ns->uuid, str);
+
+    return str;
+}
+
+static void nvme_ns_set_uuid(Object *obj, const char *v, Error **errp)
+{
+    NvmeNamespace *ns = NVME_NAMESPACE(obj);
+
+    if (!nvme_ns_prop_writable(obj, "uuid", errp)) {
+        return;
+    }
+
+    if (!strcmp(v, "auto")) {
+        qemu_uuid_generate(&ns->uuid);
+    } else if (qemu_uuid_parse(v, &ns->uuid) < 0) {
+        error_setg(errp, "invalid uuid");
+    }
+}
+
+static char *nvme_ns_get_eui64(Object *obj, Error **errp)
+{
+    NvmeNamespace *ns = NVME_NAMESPACE(obj);
+
+    const int len = 2 * 8 + 7 + 1; /* "aa:bb:cc:dd:ee:ff:gg:hh\0" */
+    char *str = g_malloc(len);
+
+    snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+             ns->eui64.a[0], ns->eui64.a[1], ns->eui64.a[2], ns->eui64.a[3],
+             ns->eui64.a[4], ns->eui64.a[5], ns->eui64.a[6], ns->eui64.a[7]);
+
+    return str;
+}
+
+static void nvme_ns_set_eui64(Object *obj, const char *v, Error **errp)
+{
+    NvmeNamespace *ns = NVME_NAMESPACE(obj);
+
+    int i, pos;
+
+    if (!nvme_ns_prop_writable(obj, "eui64", errp)) {
+        return;
+    }
+
+    if (!strcmp(v, "auto")) {
+        ns->eui64.a[0] = 0x52;
+        ns->eui64.a[1] = 0x54;
+        ns->eui64.a[2] = 0x00;
+
+        for (i = 0; i < 5; ++i) {
+            ns->eui64.a[3 + i] = g_random_int();
+        }
+
+        return;
+    }
+
+    for (i = 0, pos = 0; i < 8; i++, pos += 3) {
+        long octet;
+
+        if (!(qemu_isxdigit(v[pos]) && qemu_isxdigit(v[pos + 1]))) {
+            goto invalid;
+        }
+
+        if (i == 7) {
+            if (v[pos + 2] != '\0') {
+                goto invalid;
+            }
+        } else {
+            if (!(v[pos + 2] == ':' || v[pos + 2] == '-')) {
+                goto invalid;
+            }
+        }
+
+        if (qemu_strtol(v + pos, NULL, 16, &octet) < 0 || octet > 0xff) {
+            goto invalid;
+        }
+
+        ns->eui64.a[i] = octet;
+    }
+
+    return;
+
+invalid:
+    error_setg(errp, "invalid ieee extended unique identifier");
+}
+
+static void nvme_ns_set_identifiers_if_unset(NvmeNamespace *ns)
+{
+    ns->nguid.eui = ns->eui64.v;
+}
+
+static void nvme_ns_complete(UserCreatable *uc, Error **errp)
+{
+    NvmeNamespace *ns = NVME_NAMESPACE(uc);
+    NvmeNamespaceClass *nc = NVME_NAMESPACE_GET_CLASS(ns);
+
+    nvme_ns_set_identifiers_if_unset(ns);
+
+    ns->flags |= NVME_NS_SHARED;
+
+    if (nc->check_params && nc->check_params(ns, errp)) {
+        return;
+    }
+
+    if (nvme_subsys_register_ns(ns->subsys, ns, errp)) {
+        return;
+    }
+
+    if (nc->configure && nc->configure(ns, errp)) {
+        return;
+    }
+
+    ns->realized = true;
+}
+
+static void nvme_ns_class_init(ObjectClass *oc, void *data)
+{
+    ObjectProperty *op;
+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+    ucc->complete = nvme_ns_complete;
+
+    op = object_class_property_add_str(oc, "nsid", nvme_ns_get_nsid,
+                                       nvme_ns_set_nsid);
+    object_property_set_default_str(op, "auto");
+    object_class_property_set_description(oc, "nsid", "namespace identifier "
+                                          "(\"auto\": assigned by controller "
+                                          "or subsystem; default: \"auto\")");
+
+    object_class_property_add_link(oc, "subsys", TYPE_NVME_SUBSYSTEM,
+                                   offsetof(NvmeNamespace, subsys),
+                                   object_property_allow_set_link, 0);
+    object_class_property_set_description(oc, "subsys", "link to "
+                                          "x-nvme-subsystem object");
+
+    op = object_class_property_add_str(oc, "uuid", nvme_ns_get_uuid,
+                                       nvme_ns_set_uuid);
+    object_property_set_default_str(op, "auto");
+    object_class_property_set_description(oc, "uuid", "namespace uuid "
+                                          "(\"auto\" for random value; "
+                                          "default: \"auto\")");
+
+    op = object_class_property_add_str(oc, "eui64", nvme_ns_get_eui64,
+                                       nvme_ns_set_eui64);
+    object_property_set_default_str(op, "auto");
+    object_class_property_set_description(oc, "eui64", "IEEE Extended Unique "
+                                          "Identifier (\"auto\" for random "
+                                          "value; default: \"auto\")");
+}
+
+static const TypeInfo nvme_ns_info = {
+    .name = TYPE_NVME_NAMESPACE,
+    .parent = TYPE_OBJECT,
+    .abstract = true,
+    .class_size = sizeof(NvmeNamespaceClass),
+    .class_init = nvme_ns_class_init,
+    .instance_size = sizeof(NvmeNamespace),
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { },
+    },
+};
+
 static void register_types(void)
 {
+    type_register_static(&nvme_ns_info);
     type_register_static(&nvme_nsdev_info);
 }
 
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index b67e5900a01d..627b28649892 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -19,6 +19,8 @@
 #define HW_NVME_INTERNAL_H
 
 #include "qemu/uuid.h"
+#include "qemu/notify.h"
+#include "qapi/qapi-builtin-visit.h"
 #include "hw/pci/pci.h"
 #include "hw/block/block.h"
 
@@ -45,6 +47,16 @@ typedef struct NvmeBus {
     BusState parent_bus;
 } NvmeBus;
 
+#define TYPE_NVME_NAMESPACE "x-nvme-ns"
+OBJECT_DECLARE_TYPE(NvmeNamespace, NvmeNamespaceClass, NVME_NAMESPACE)
+
+struct NvmeNamespaceClass {
+    ObjectClass parent_class;
+
+    int (*check_params)(NvmeNamespace *ns, Error **errp);
+    int (*configure)(NvmeNamespace *ns, Error **errp);
+};
+
 #define TYPE_NVME_SUBSYSTEM "x-nvme-subsystem"
 OBJECT_DECLARE_SIMPLE_TYPE(NvmeSubsystem, NVME_SUBSYSTEM)
 
@@ -75,6 +87,8 @@ typedef struct NvmeSubsystemDevice {
 int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeState *n,
                               Error **errp);
 void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeState *n);
+int nvme_subsys_register_ns(NvmeSubsystem *subsys, NvmeNamespace *ns,
+                            Error **errp);
 
 static inline NvmeState *nvme_subsys_ctrl(NvmeSubsystem *subsys,
                                           uint32_t cntlid)
@@ -190,6 +204,11 @@ enum NvmeNamespaceFlags {
 };
 
 typedef struct NvmeNamespace {
+    Object parent_obj;
+    bool   realized;
+
+    NvmeSubsystem *subsys;
+
     uint32_t nsid;
     uint8_t  csi;
     QemuUUID uuid;
@@ -197,6 +216,7 @@ typedef struct NvmeNamespace {
         uint64_t v;
         uint8_t  a[8];
     } eui64;
+    NvmeNGUID nguid;
 
     unsigned long flags;
 
@@ -212,6 +232,8 @@ typedef struct NvmeNamespace {
     NvmeNamespaceZoned zoned;
 } NvmeNamespace;
 
+bool nvme_ns_prop_writable(Object *obj, const char *name, Error **errp);
+
 #define NVME_NAMESPACE_NVM(ns) (&(ns)->nvm)
 #define NVME_NAMESPACE_ZONED(ns) (&(ns)->zoned)
 
diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c
index 2599b83c348e..e4dcd8fd20a5 100644
--- a/hw/nvme/subsys.c
+++ b/hw/nvme/subsys.c
@@ -47,6 +47,37 @@ void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeState *n)
     n->cntlid = -1;
 }
 
+int nvme_subsys_register_ns(NvmeSubsystem *subsys, NvmeNamespace *ns,
+                            Error **errp)
+{
+    int i;
+
+    if (!ns->nsid) {
+        for (i = 1; i <= NVME_MAX_NAMESPACES; i++) {
+            if (!subsys->namespaces[i]) {
+                ns->nsid = i;
+                break;
+            }
+        }
+
+        if (!ns->nsid) {
+            error_setg(errp, "no free namespace identifiers");
+            return -1;
+        }
+    } else if (ns->nsid > NVME_MAX_NAMESPACES) {
+        error_setg(errp, "invalid namespace identifier '%d'", ns->nsid);
+        return -1;
+    } else if (subsys->namespaces[ns->nsid]) {
+        error_setg(errp, "namespace identifier '%d' already allocated",
+                   ns->nsid);
+        return -1;
+    }
+
+    subsys->namespaces[ns->nsid] = ns;
+
+    return 0;
+}
+
 static void get_controllers(Object *obj, Visitor *v, const char *name,
                             void *opaque, Error **errp)
 {
diff --git a/qapi/qom.json b/qapi/qom.json
index d4c211fc38b1..6d5cef6b92ad 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -662,6 +662,23 @@
   'data': { '*subnqn': 'str',
             '*uuid': 'str' } }
 
+##
+# @NvmeNamespaceProperties:
+#
+# Properties for x-nvme-ns objects.
+#
+# @subsys: nvme controller to attach to
+#
+# @nsid: namespace identifier to assign
+#
+# Since: 6.1
+##
+{ 'struct': 'NvmeNamespaceProperties',
+  'data': { 'subsys': 'str',
+            '*nsid': 'str',
+            '*eui64': 'str',
+            '*uuid': 'str' } }
+
 ##
 # @PrManagerHelperProperties:
 #
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 14/16] hw/nvme: add experimental objects x-nvme-ns-{nvm, zoned}
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (12 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 13/16] hw/nvme: add experimental abstract object x-nvme-ns Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 15/16] hw/nvme: add experimental device x-nvme-ctrl Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 16/16] docs: add documentation for experimental nvme emulation Klaus Jensen
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Add implementations of namespaces that supports the NVM and Zoned
Command Sets.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c      |  11 +-
 hw/nvme/dif.h       |   2 +
 hw/nvme/meson.build |   2 +-
 hw/nvme/ns-nvm.c    | 354 +++++++++++++++++++++++++++++++++++
 hw/nvme/ns-zoned.c  | 443 ++++++++++++++++++++++++++++++++++++++++++++
 hw/nvme/ns.c        | 281 +++-------------------------
 hw/nvme/nvm.h       |  65 +++++++
 hw/nvme/nvme.h      |  96 +---------
 hw/nvme/zns.h       |  48 +++++
 qapi/qom.json       |  48 +++++
 softmmu/vl.c        |   8 +
 11 files changed, 1006 insertions(+), 352 deletions(-)
 create mode 100644 hw/nvme/ns-nvm.c
 create mode 100644 hw/nvme/ns-zoned.c
 create mode 100644 hw/nvme/nvm.h

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index c798aeb095e1..31499b10fc49 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -164,6 +164,7 @@
 
 #include "nvme.h"
 #include "dif.h"
+#include "nvm.h"
 #include "zns.h"
 
 #include "trace.h"
@@ -5346,7 +5347,7 @@ static void nvme_format_set(NvmeNamespace *ns, NvmeCmd *cmd)
     nvm->id_ns.dps = (pil << 3) | pi;
     nvm->id_ns.flbas = lbaf | (mset << 4);
 
-    nvme_ns_nvm_init_format(nvm);
+    nvme_ns_nvm_configure_format(nvm);
 }
 
 static void nvme_format_ns_cb(void *opaque, int ret)
@@ -6592,10 +6593,14 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
     /* setup a namespace if the controller drive property was given */
     if (ctrl->namespace.blkconf.blk) {
         NvmeNamespaceDevice *nsdev = &ctrl->namespace;
-        NvmeNamespace *ns = &nsdev->ns;
+        NvmeNamespace *ns = NVME_NAMESPACE(nsdev->ns);
+        NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
         ns->nsid = 1;
 
-        nvme_ns_init(ns);
+        ns->csi = NVME_CSI_NVM;
+
+        nvme_ns_nvm_configure_identify(ns);
+        nvme_ns_nvm_configure_format(nvm);
 
         nvme_attach_ns(n, ns);
     }
diff --git a/hw/nvme/dif.h b/hw/nvme/dif.h
index 53a22bc7c78e..81efb95cd391 100644
--- a/hw/nvme/dif.h
+++ b/hw/nvme/dif.h
@@ -1,6 +1,8 @@
 #ifndef HW_NVME_DIF_H
 #define HW_NVME_DIF_H
 
+#include "nvm.h"
+
 /* from Linux kernel (crypto/crct10dif_common.c) */
 static const uint16_t t10_dif_crc_table[256] = {
     0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B,
diff --git a/hw/nvme/meson.build b/hw/nvme/meson.build
index 3cf40046eea9..2bb8354bcb57 100644
--- a/hw/nvme/meson.build
+++ b/hw/nvme/meson.build
@@ -1 +1 @@
-softmmu_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c'))
+softmmu_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'ns-nvm.c', 'ns-zoned.c', 'subsys.c'))
diff --git a/hw/nvme/ns-nvm.c b/hw/nvme/ns-nvm.c
new file mode 100644
index 000000000000..0d4f67d481cd
--- /dev/null
+++ b/hw/nvme/ns-nvm.c
@@ -0,0 +1,354 @@
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qom/object_interfaces.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/block-backend.h"
+
+#include "nvme.h"
+#include "nvm.h"
+
+#include "trace.h"
+
+static char *get_blockdev(Object *obj, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    const char *value;
+
+    value = blk_name(nvm->blk);
+    if (strcmp(value, "") == 0) {
+        BlockDriverState *bs = blk_bs(nvm->blk);
+        if (bs) {
+            value = bdrv_get_node_name(bs);
+        }
+    }
+
+    return g_strdup(value);
+}
+
+static void set_blockdev(Object *obj, const char *str, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+
+    g_free(nvm->blk_nodename);
+    nvm->blk_nodename = g_strdup(str);
+}
+
+static void get_lba_size(Object *obj, Visitor *v, const char *name,
+                         void *opaque, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    uint64_t lba_size = nvm->lbasz;
+
+    visit_type_size(v, name, &lba_size, errp);
+}
+
+static void set_lba_size(Object *obj, Visitor *v, const char *name,
+                         void *opaque, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    uint64_t lba_size;
+
+    if (!nvme_ns_prop_writable(obj, name, errp)) {
+        return;
+    }
+
+    if (!visit_type_size(v, name, &lba_size, errp)) {
+        return;
+    }
+
+    nvm->lbasz = lba_size;
+    nvm->lbaf.ds = 31 - clz32(nvm->lbasz);
+}
+
+static void get_metadata_size(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    uint16_t value = nvm->lbaf.ms;
+
+    visit_type_uint16(v, name, &value, errp);
+}
+
+static void set_metadata_size(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    uint16_t value;
+
+    if (!nvme_ns_prop_writable(obj, name, errp)) {
+        return;
+    }
+
+    if (!visit_type_uint16(v, name, &value, errp)) {
+        return;
+    }
+
+    nvm->lbaf.ms = value;
+}
+
+static int get_pi(Object *obj, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    return nvm->id_ns.dps & NVME_ID_NS_DPS_TYPE_MASK;
+}
+
+static void set_pi(Object *obj, int pi_type, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+
+    if (!nvme_ns_prop_writable(obj, "pi-type", errp)) {
+        return;
+    }
+
+    nvm->id_ns.dps |= (nvm->id_ns.dps & ~NVME_ID_NS_DPS_TYPE_MASK) | pi_type;
+}
+
+static bool get_pil(Object *obj, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    return nvm->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT;
+}
+
+static void set_pil(Object *obj, bool first, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+
+    if (!nvme_ns_prop_writable(obj, "pi-first", errp)) {
+        return;
+    }
+
+    if (!first) {
+        return;
+    }
+
+    nvm->id_ns.dps |= NVME_NS_NVM_PI_FIRST;
+}
+
+static bool get_extended_lba(Object *obj, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    return nvm->flags & NVME_NS_NVM_EXTENDED_LBA;
+}
+
+static void set_extended_lba(Object *obj, bool extended, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+
+    if (!nvme_ns_prop_writable(obj, "extended-lba", errp)) {
+        return;
+    }
+
+    if (extended) {
+        nvm->flags |= NVME_NS_NVM_EXTENDED_LBA;
+    } else {
+        nvm->flags &= ~NVME_NS_NVM_EXTENDED_LBA;
+    }
+}
+
+void nvme_ns_nvm_configure_format(NvmeNamespaceNvm *nvm)
+{
+    NvmeIdNs *id_ns = &nvm->id_ns;
+    BlockDriverInfo bdi;
+    int npdg, nlbas, ret;
+    uint32_t discard_granularity = MAX(nvm->lbasz, 4096);
+
+    nvm->lbaf = id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(id_ns->flbas)];
+    nvm->lbasz = 1 << nvm->lbaf.ds;
+
+    if (nvm->lbaf.ms && nvm->flags & NVME_NS_NVM_EXTENDED_LBA) {
+        id_ns->flbas |= NVME_ID_NS_FLBAS_EXTENDED;
+    }
+
+    nlbas = nvm->size / (nvm->lbasz + nvm->lbaf.ms);
+
+    id_ns->nsze = cpu_to_le64(nlbas);
+
+    /* no thin provisioning */
+    id_ns->ncap = id_ns->nsze;
+    id_ns->nuse = id_ns->ncap;
+
+    nvm->moff = nlbas * nvm->lbasz;
+
+    npdg = discard_granularity / nvm->lbasz;
+
+    ret = bdrv_get_info(blk_bs(nvm->blk), &bdi);
+    if (ret >= 0 && bdi.cluster_size > discard_granularity) {
+        npdg = bdi.cluster_size / nvm->lbasz;
+    }
+
+    id_ns->npda = id_ns->npdg = npdg - 1;
+}
+
+void nvme_ns_nvm_configure_identify(NvmeNamespace *ns)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
+    NvmeIdNs *id_ns = &nvm->id_ns;
+
+    static const NvmeLBAF default_lba_formats[16] = {
+        [0] = { .ds =  9           },
+        [1] = { .ds =  9, .ms =  8 },
+        [2] = { .ds =  9, .ms = 16 },
+        [3] = { .ds =  9, .ms = 64 },
+        [4] = { .ds = 12           },
+        [5] = { .ds = 12, .ms =  8 },
+        [6] = { .ds = 12, .ms = 16 },
+        [7] = { .ds = 12, .ms = 64 },
+    };
+
+    id_ns->dlfeat = 0x1;
+
+    /* support DULBE and I/O optimization fields */
+    id_ns->nsfeat = 0x4 | 0x10;
+
+    if (ns->flags & NVME_NS_SHARED) {
+        id_ns->nmic |= NVME_NMIC_NS_SHARED;
+    }
+
+    /* eui64 is always stored in big-endian form */
+    id_ns->eui64 = ns->eui64.v;
+    id_ns->nguid.eui = id_ns->eui64;
+
+    id_ns->mc = NVME_ID_NS_MC_EXTENDED | NVME_ID_NS_MC_SEPARATE;
+
+    id_ns->dpc = 0x1f;
+
+    memcpy(&id_ns->lbaf, &default_lba_formats, sizeof(id_ns->lbaf));
+    id_ns->nlbaf = 7;
+
+    for (int i = 0; i <= id_ns->nlbaf; i++) {
+        NvmeLBAF *lbaf = &id_ns->lbaf[i];
+
+        if (lbaf->ds == nvm->lbaf.ds && lbaf->ms == nvm->lbaf.ms) {
+            id_ns->flbas |= i;
+            return;
+        }
+    }
+
+    /* add non-standard lba format */
+    id_ns->nlbaf++;
+    id_ns->lbaf[id_ns->nlbaf].ds = nvm->lbaf.ds;
+    id_ns->lbaf[id_ns->nlbaf].ms = nvm->lbaf.ms;
+    id_ns->flbas |= id_ns->nlbaf;
+}
+
+int nvme_ns_nvm_configure(NvmeNamespace *ns, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
+    BlockBackend *blk;
+    int ret;
+
+    blk = blk_by_name(nvm->blk_nodename);
+    if (!blk) {
+        BlockDriverState *bs = bdrv_lookup_bs(NULL, nvm->blk_nodename, NULL);
+        if (bs) {
+            blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
+
+            ret = blk_insert_bs(blk, bs, errp);
+            if (ret < 0) {
+                blk_unref(blk);
+                return -1;
+            }
+        }
+    }
+
+    if (!blk) {
+        error_setg(errp, "invalid blockdev '%s'", nvm->blk_nodename);
+        return -1;
+    }
+
+    blk_ref(blk);
+    blk_iostatus_reset(blk);
+
+    nvm->blk = blk;
+
+    nvm->size = blk_getlength(nvm->blk);
+    if (nvm->size < 0) {
+        error_setg_errno(errp, -(nvm->size), "could not get blockdev size");
+        return -1;
+    }
+
+    ns->csi = NVME_CSI_NVM;
+
+    nvme_ns_nvm_configure_identify(ns);
+    nvme_ns_nvm_configure_format(nvm);
+
+    return 0;
+}
+
+int nvme_ns_nvm_check_params(NvmeNamespace *ns, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
+    int pi_type = nvm->id_ns.dps & NVME_ID_NS_DPS_TYPE_MASK;
+
+    if (pi_type && nvm->lbaf.ms < 8) {
+        error_setg(errp, "at least 8 bytes of metadata required to enable "
+                   "protection information");
+        return -1;
+    }
+
+    return 0;
+}
+
+static void nvme_ns_nvm_class_init(ObjectClass *oc, void *data)
+{
+    ObjectProperty *op;
+
+    NvmeNamespaceClass *nc = NVME_NAMESPACE_CLASS(oc);
+
+    object_class_property_add_str(oc, "blockdev", get_blockdev, set_blockdev);
+    object_class_property_set_description(oc, "blockdev",
+                                          "node name or identifier of a "
+                                          "block device to use as a backend");
+
+    op = object_class_property_add(oc, "lba-size", "size",
+                                   get_lba_size, set_lba_size,
+                                   NULL, NULL);
+    object_property_set_default_uint(op, 4096);
+    object_class_property_set_description(oc, "lba-size",
+                                          "logical block size");
+
+    object_class_property_add(oc, "metadata-size", "uint16",
+                              get_metadata_size, set_metadata_size,
+                              NULL, NULL);
+    object_class_property_set_description(oc, "metadata-size",
+                                          "metadata size (default: 0)");
+
+    object_class_property_add_bool(oc, "extended-lba",
+                                   get_extended_lba, set_extended_lba);
+    object_class_property_set_description(oc, "extended-lba",
+                                          "use extended logical blocks "
+                                          "(default: off)");
+
+    object_class_property_add_enum(oc, "pi-type", "NvmeProtInfoType",
+                                   &NvmeProtInfoType_lookup,
+                                   get_pi, set_pi);
+    object_class_property_set_description(oc, "pi-type",
+                                          "protection information type "
+                                          "(default: none)");
+
+    object_class_property_add_bool(oc, "pi-first", get_pil, set_pil);
+    object_class_property_set_description(oc, "pi-first",
+                                          "transfer protection information "
+                                          "as the first eight bytes of "
+                                          "metadata (default: off)");
+
+    nc->check_params = nvme_ns_nvm_check_params;
+    nc->configure = nvme_ns_nvm_configure;
+}
+
+static const TypeInfo nvme_ns_nvm_info = {
+    .name = TYPE_NVME_NAMESPACE_NVM,
+    .parent = TYPE_NVME_NAMESPACE,
+    .class_init = nvme_ns_nvm_class_init,
+    .instance_size = sizeof(NvmeNamespaceNvm),
+};
+
+static void register_types(void)
+{
+    type_register_static(&nvme_ns_nvm_info);
+}
+
+type_init(register_types);
diff --git a/hw/nvme/ns-zoned.c b/hw/nvme/ns-zoned.c
new file mode 100644
index 000000000000..1403911c4f83
--- /dev/null
+++ b/hw/nvme/ns-zoned.c
@@ -0,0 +1,443 @@
+/*
+ * QEMU NVM Express Virtual Zoned Namespace
+ *
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ * Copyright (c) 2021 Samsung Electronics
+ *
+ * Authors:
+ *  Dmitry Fomichev   <dmitry.fomichev@wdc.com>
+ *  Klaus Jensen      <k.jensen@samsung.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See the
+ * COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/block-backend.h"
+
+#include "nvme.h"
+#include "zns.h"
+
+#include "trace.h"
+
+static void get_zone_size(Object *obj, Visitor *v, const char *name,
+                          void *opaque, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+    uint64_t value = zoned->zone_size << nvm->lbaf.ds;
+
+    visit_type_size(v, name, &value, errp);
+}
+
+static void set_zone_size(Object *obj, Visitor *v, const char *name,
+                          void *opaque, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+    uint64_t value;
+
+    if (!nvme_ns_prop_writable(obj, name, errp)) {
+        return;
+    }
+
+    if (!visit_type_size(v, name, &value, errp)) {
+        return;
+    }
+
+    zoned->zone_size = value >> nvm->lbaf.ds;
+}
+
+static void get_zone_capacity(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+    uint64_t value = zoned->zone_capacity << nvm->lbaf.ds;
+
+    visit_type_size(v, name, &value, errp);
+}
+
+static void set_zone_capacity(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(obj);
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+    uint64_t value;
+
+    if (!nvme_ns_prop_writable(obj, name, errp)) {
+        return;
+    }
+
+    if (!visit_type_size(v, name, &value, errp)) {
+        return;
+    }
+
+    zoned->zone_capacity = value >> nvm->lbaf.ds;
+}
+
+static void get_zone_max_active(Object *obj, Visitor *v, const char *name,
+                                void *opaque, Error **errp)
+{
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+
+    visit_type_uint32(v, name, &zoned->max_active_zones, errp);
+}
+
+static void set_zone_max_active(Object *obj, Visitor *v, const char *name,
+                                void *opaque, Error **errp)
+{
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+
+    if (!nvme_ns_prop_writable(obj, name, errp)) {
+        return;
+    }
+
+    if (!visit_type_uint32(v, name, &zoned->max_active_zones, errp)) {
+        return;
+    }
+}
+
+static void get_zone_max_open(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
+{
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+
+    visit_type_uint32(v, name, &zoned->max_open_zones, errp);
+}
+
+static void set_zone_max_open(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
+{
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+
+    if (!nvme_ns_prop_writable(obj, name, errp)) {
+        return;
+    }
+
+    if (!visit_type_uint32(v, name, &zoned->max_open_zones, errp)) {
+        return;
+    }
+}
+
+static bool get_zone_cross_read(Object *obj, Error **errp)
+{
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+    return zoned->flags & NVME_NS_ZONED_CROSS_READ;
+}
+
+static void set_zone_cross_read(Object *obj, bool cross_read, Error **errp)
+{
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+
+    if (!nvme_ns_prop_writable(obj, "zone-cross-read", errp)) {
+        return;
+    }
+
+    if (cross_read) {
+        zoned->flags |= NVME_NS_ZONED_CROSS_READ;
+    } else {
+        zoned->flags &= ~NVME_NS_ZONED_CROSS_READ;
+    }
+}
+
+static void get_zone_descriptor_extension_size(Object *obj, Visitor *v,
+                                               const char *name, void *opaque,
+                                               Error **errp)
+{
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+    uint64_t value = zoned->zd_extension_size;
+
+    visit_type_size(v, name, &value, errp);
+}
+
+static void set_zone_descriptor_extension_size(Object *obj, Visitor *v,
+                                               const char *name, void *opaque,
+                                               Error **errp)
+{
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(obj);
+    uint64_t value;
+
+    if (!nvme_ns_prop_writable(obj, name, errp)) {
+        return;
+    }
+
+    if (!visit_type_size(v, name, &value, errp)) {
+        return;
+    }
+
+    if (value & 0x3f) {
+        error_setg(errp, "zone descriptor extension size must be a "
+                   "multiple of 64 bytes");
+        return;
+    }
+    if ((value >> 6) > 0xff) {
+        error_setg(errp,
+                   "zone descriptor extension size is too large");
+        return;
+    }
+
+    zoned->zd_extension_size = value;
+}
+
+void nvme_zns_init_state(NvmeNamespaceZoned *zoned)
+{
+    uint64_t start = 0, zone_size = zoned->zone_size;
+    uint64_t capacity = zoned->num_zones * zone_size;
+    NvmeZone *zone;
+    int i;
+
+    zoned->zone_array = g_new0(NvmeZone, zoned->num_zones);
+    if (zoned->zd_extension_size) {
+        zoned->zd_extensions = g_malloc0(zoned->zd_extension_size *
+                                         zoned->num_zones);
+    }
+
+    QTAILQ_INIT(&zoned->exp_open_zones);
+    QTAILQ_INIT(&zoned->imp_open_zones);
+    QTAILQ_INIT(&zoned->closed_zones);
+    QTAILQ_INIT(&zoned->full_zones);
+
+    zone = zoned->zone_array;
+    for (i = 0; i < zoned->num_zones; i++, zone++) {
+        if (start + zone_size > capacity) {
+            zone_size = capacity - start;
+        }
+        zone->d.zt = NVME_ZONE_TYPE_SEQ_WRITE;
+        nvme_zns_set_state(zone, NVME_ZONE_STATE_EMPTY);
+        zone->d.za = 0;
+        zone->d.zcap = zoned->zone_capacity;
+        zone->d.zslba = start;
+        zone->d.wp = start;
+        zone->w_ptr = start;
+        start += zone_size;
+    }
+
+    zoned->zone_size_log2 = 0;
+    if (is_power_of_2(zoned->zone_size)) {
+        zoned->zone_size_log2 = 63 - clz64(zoned->zone_size);
+    }
+}
+
+int nvme_zns_configure(NvmeNamespace *ns, Error **errp)
+{
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
+    NvmeIdNsZoned *id_ns_z = &zoned->id_ns;
+    int i;
+
+    if (nvme_ns_nvm_configure(ns, errp)) {
+        return -1;
+    }
+
+    zoned->num_zones = le64_to_cpu(nvm->id_ns.nsze) / zoned->zone_size;
+
+    if (zoned->max_active_zones && !zoned->max_open_zones) {
+        zoned->max_open_zones = zoned->max_active_zones;
+    }
+
+    if (!zoned->num_zones) {
+        error_setg(errp,
+                   "insufficient namespace size; must be at least the size "
+                   "of one zone (%"PRIu64"B)", zoned->zone_size);
+        return -1;
+    }
+
+    nvme_zns_init_state(zoned);
+
+    /* MAR/MOR are zeroes-based, FFFFFFFFFh means no limit */
+    id_ns_z->mar = cpu_to_le32(zoned->max_active_zones - 1);
+    id_ns_z->mor = cpu_to_le32(zoned->max_open_zones - 1);
+    id_ns_z->zoc = 0;
+
+    if (zoned->flags & NVME_NS_ZONED_CROSS_READ) {
+        id_ns_z->ozcs |= NVME_ID_NS_ZONED_OZCS_CROSS_READ;
+    }
+
+    for (i = 0; i <= nvm->id_ns.nlbaf; i++) {
+        id_ns_z->lbafe[i].zsze = cpu_to_le64(zoned->zone_size);
+        id_ns_z->lbafe[i].zdes =
+            zoned->zd_extension_size >> 6; /* Units of 64B */
+    }
+
+    ns->csi = NVME_CSI_ZONED;
+    nvm->id_ns.nsze = cpu_to_le64(zoned->num_zones * zoned->zone_size);
+    nvm->id_ns.ncap = nvm->id_ns.nsze;
+    nvm->id_ns.nuse = nvm->id_ns.ncap;
+
+    /*
+     * The device uses the BDRV_BLOCK_ZERO flag to determine the "deallocated"
+     * status of logical blocks. Since the spec defines that logical blocks
+     * SHALL be deallocated when then zone is in the Empty or Offline states,
+     * we can only support DULBE if the zone size is a multiple of the
+     * calculated NPDG.
+     */
+    if (zoned->zone_size % (nvm->id_ns.npdg + 1)) {
+        warn_report("the zone size (%"PRIu64" blocks) is not a multiple of "
+                    "the calculated deallocation granularity (%d blocks); "
+                    "DULBE support disabled",
+                    zoned->zone_size, nvm->id_ns.npdg + 1);
+
+        nvm->id_ns.nsfeat &= ~0x4;
+    }
+
+    return 0;
+}
+
+void nvme_zns_clear_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone)
+{
+    uint8_t state;
+
+    zone->w_ptr = zone->d.wp;
+    state = nvme_zns_state(zone);
+    if (zone->d.wp != zone->d.zslba ||
+        (zone->d.za & NVME_ZA_ZD_EXT_VALID)) {
+        if (state != NVME_ZONE_STATE_CLOSED) {
+            trace_pci_nvme_clear_ns_close(state, zone->d.zslba);
+            nvme_zns_set_state(zone, NVME_ZONE_STATE_CLOSED);
+        }
+        nvme_zns_aor_inc_active(zoned);
+        QTAILQ_INSERT_HEAD(&zoned->closed_zones, zone, entry);
+    } else {
+        trace_pci_nvme_clear_ns_reset(state, zone->d.zslba);
+        nvme_zns_set_state(zone, NVME_ZONE_STATE_EMPTY);
+    }
+}
+
+/*
+ * Close all the zones that are currently open.
+ */
+void nvme_zns_shutdown(NvmeNamespace *ns)
+{
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
+    NvmeZone *zone, *next;
+
+    QTAILQ_FOREACH_SAFE(zone, &zoned->closed_zones, entry, next) {
+        QTAILQ_REMOVE(&zoned->closed_zones, zone, entry);
+        nvme_zns_aor_dec_active(zoned);
+        nvme_zns_clear_zone(zoned, zone);
+    }
+    QTAILQ_FOREACH_SAFE(zone, &zoned->imp_open_zones, entry, next) {
+        QTAILQ_REMOVE(&zoned->imp_open_zones, zone, entry);
+        nvme_zns_aor_dec_open(zoned);
+        nvme_zns_aor_dec_active(zoned);
+        nvme_zns_clear_zone(zoned, zone);
+    }
+    QTAILQ_FOREACH_SAFE(zone, &zoned->exp_open_zones, entry, next) {
+        QTAILQ_REMOVE(&zoned->exp_open_zones, zone, entry);
+        nvme_zns_aor_dec_open(zoned);
+        nvme_zns_aor_dec_active(zoned);
+        nvme_zns_clear_zone(zoned, zone);
+    }
+
+    assert(zoned->nr_open_zones == 0);
+}
+
+static int nvme_zns_check_params(NvmeNamespace *ns, Error **errp)
+{
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
+    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
+
+    if (nvme_ns_nvm_check_params(ns, errp)) {
+        return -1;
+    }
+
+    if (zoned->zone_size < nvm->lbaf.ds) {
+        error_setg(errp, "'zone-size' must be at least %"PRIu64" bytes",
+                   nvm->lbasz);
+        return -1;
+    }
+
+    if (zoned->zone_capacity < nvm->lbaf.ds) {
+        error_setg(errp, "'zone-capacity' must be at least %"PRIu64" bytes",
+                   nvm->lbasz);
+        return -1;
+    }
+
+    if (zoned->zone_capacity > zoned->zone_size) {
+        error_setg(errp, "'zone-capacity' must not exceed 'zone-size'");
+        return -1;
+    }
+
+    if (zoned->max_active_zones) {
+        if (zoned->max_open_zones > zoned->max_active_zones) {
+            error_setg(errp, "'zone-max-open' must not exceed 'zone-max-active'");
+            return -1;
+        }
+
+        if (!zoned->max_open_zones) {
+            zoned->max_open_zones = zoned->max_active_zones;
+        }
+    }
+
+    return 0;
+}
+
+static void nvme_zns_class_init(ObjectClass *oc, void *data)
+{
+    ObjectProperty *op;
+
+    NvmeNamespaceClass *nc = NVME_NAMESPACE_CLASS(oc);
+
+    op = object_class_property_add(oc, "zone-size", "size",
+                                   get_zone_size, set_zone_size,
+                                   NULL, NULL);
+    object_property_set_default_uint(op, 4096);
+    object_class_property_set_description(oc, "zone-size", "zone size");
+
+    op = object_class_property_add(oc, "zone-capacity", "size",
+                                   get_zone_capacity, set_zone_capacity,
+                                   NULL, NULL);
+    object_property_set_default_uint(op, 4096);
+    object_class_property_set_description(oc, "zone-capacity",
+                                          "zone capacity");
+
+    object_class_property_add_bool(oc, "zone-cross-read",
+                                   get_zone_cross_read, set_zone_cross_read);
+    object_class_property_set_description(oc, "zone-cross-read",
+                                          "allow reads to cross zone "
+                                          "boundaries");
+
+    object_class_property_add(oc, "zone-descriptor-extension-size", "size",
+                              get_zone_descriptor_extension_size,
+                              set_zone_descriptor_extension_size,
+                              NULL, NULL);
+    object_class_property_set_description(oc, "zone-descriptor-extension-size",
+                                          "zone descriptor extension size");
+
+    object_class_property_add(oc, "zone-max-active", "uint32",
+                              get_zone_max_active, set_zone_max_active,
+                              NULL, NULL);
+    object_class_property_set_description(oc, "zone-max-active",
+                                          "maximum number of active zones");
+
+    object_class_property_add(oc, "zone-max-open", "uint32",
+                              get_zone_max_open, set_zone_max_open,
+                              NULL, NULL);
+    object_class_property_set_description(oc, "zone-max-open",
+                                          "maximum number of open zones");
+
+    nc->check_params = nvme_zns_check_params;
+    nc->configure = nvme_zns_configure;
+    nc->shutdown = nvme_zns_shutdown;
+}
+
+static const TypeInfo nvme_zns_info = {
+    .name = TYPE_NVME_NAMESPACE_ZONED,
+    .parent = TYPE_NVME_NAMESPACE_NVM,
+    .class_init = nvme_zns_class_init,
+    .instance_size = sizeof(NvmeNamespaceZoned),
+};
+
+static void register_types(void)
+{
+    type_register_static(&nvme_zns_info);
+}
+
+type_init(register_types);
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index d75ff4f1cb74..757a90b58ea8 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -30,107 +30,10 @@
 
 #define MIN_DISCARD_GRANULARITY (4 * KiB)
 
-void nvme_ns_nvm_init_format(NvmeNamespaceNvm *nvm)
-{
-    NvmeIdNs *id_ns = &nvm->id_ns;
-    BlockDriverInfo bdi;
-    int npdg, nlbas, ret;
-
-    nvm->lbaf = id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(id_ns->flbas)];
-    nvm->lbasz = 1 << nvm->lbaf.ds;
-
-    nlbas = nvm->size / (nvm->lbasz + nvm->lbaf.ms);
-
-    id_ns->nsze = cpu_to_le64(nlbas);
-
-    /* no thin provisioning */
-    id_ns->ncap = id_ns->nsze;
-    id_ns->nuse = id_ns->ncap;
-
-    nvm->moff = (int64_t)nlbas << nvm->lbaf.ds;
-
-    npdg = nvm->discard_granularity / nvm->lbasz;
-
-    ret = bdrv_get_info(blk_bs(nvm->blk), &bdi);
-    if (ret >= 0 && bdi.cluster_size > nvm->discard_granularity) {
-        npdg = bdi.cluster_size / nvm->lbasz;
-    }
-
-    id_ns->npda = id_ns->npdg = npdg - 1;
-}
-
-void nvme_ns_init(NvmeNamespace *ns)
-{
-    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    NvmeIdNs *id_ns = &nvm->id_ns;
-    uint8_t ds;
-    uint16_t ms;
-    int i;
-
-    id_ns->dlfeat = 0x1;
-
-    /* support DULBE and I/O optimization fields */
-    id_ns->nsfeat |= (0x4 | 0x10);
-
-    if (ns->flags & NVME_NS_SHARED) {
-        id_ns->nmic |= NVME_NMIC_NS_SHARED;
-    }
-
-    /* simple copy */
-    id_ns->mssrl = cpu_to_le16(nvm->mssrl);
-    id_ns->mcl = cpu_to_le32(nvm->mcl);
-    id_ns->msrc = nvm->msrc;
-    id_ns->eui64 = cpu_to_be64(ns->eui64.v);
-
-    ds = 31 - clz32(nvm->lbasz);
-    ms = nvm->lbaf.ms;
-
-    id_ns->mc = NVME_ID_NS_MC_EXTENDED | NVME_ID_NS_MC_SEPARATE;
-
-    if (ms && nvm->flags & NVME_NS_NVM_EXTENDED_LBA) {
-        id_ns->flbas |= NVME_ID_NS_FLBAS_EXTENDED;
-    }
-
-    id_ns->dpc = 0x1f;
-
-    static const NvmeLBAF lbaf[16] = {
-        [0] = { .ds =  9           },
-        [1] = { .ds =  9, .ms =  8 },
-        [2] = { .ds =  9, .ms = 16 },
-        [3] = { .ds =  9, .ms = 64 },
-        [4] = { .ds = 12           },
-        [5] = { .ds = 12, .ms =  8 },
-        [6] = { .ds = 12, .ms = 16 },
-        [7] = { .ds = 12, .ms = 64 },
-    };
-
-    memcpy(&id_ns->lbaf, &lbaf, sizeof(lbaf));
-    id_ns->nlbaf = 7;
-
-    for (i = 0; i <= id_ns->nlbaf; i++) {
-        NvmeLBAF *lbaf = &id_ns->lbaf[i];
-        if (lbaf->ds == ds) {
-            if (lbaf->ms == ms) {
-                id_ns->flbas |= i;
-                goto lbaf_found;
-            }
-        }
-    }
-
-    /* add non-standard lba format */
-    id_ns->nlbaf++;
-    id_ns->lbaf[id_ns->nlbaf].ds = ds;
-    id_ns->lbaf[id_ns->nlbaf].ms = ms;
-    id_ns->flbas |= id_ns->nlbaf;
-
-lbaf_found:
-    nvme_ns_nvm_init_format(nvm);
-}
-
 static int nvme_nsdev_init_blk(NvmeNamespaceDevice *nsdev,
                                Error **errp)
 {
-    NvmeNamespace *ns = &nsdev->ns;
+    NvmeNamespace *ns = NVME_NAMESPACE(nsdev->ns);
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     BlockConf *blkconf = &nsdev->blkconf;
     bool read_only;
@@ -167,7 +70,7 @@ static int nvme_nsdev_init_blk(NvmeNamespaceDevice *nsdev,
 static int nvme_nsdev_zns_check_calc_geometry(NvmeNamespaceDevice *nsdev,
                                               Error **errp)
 {
-    NvmeNamespace *ns = &nsdev->ns;
+    NvmeNamespace *ns = NVME_NAMESPACE(nsdev->ns);
     NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
 
@@ -206,152 +109,10 @@ static int nvme_nsdev_zns_check_calc_geometry(NvmeNamespaceDevice *nsdev,
      */
     zoned->zone_size = zone_size / nvm->lbasz;
     zoned->zone_capacity = zone_cap / nvm->lbasz;
-    zoned->num_zones = le64_to_cpu(nvm->id_ns.nsze) / zoned->zone_size;
-
-    /* Do a few more sanity checks of ZNS properties */
-    if (!zoned->num_zones) {
-        error_setg(errp,
-                   "insufficient drive capacity, must be at least the size "
-                   "of one zone (%"PRIu64"B)", zone_size);
-        return -1;
-    }
 
     return 0;
 }
 
-static void nvme_zns_init_state(NvmeNamespaceZoned *zoned)
-{
-    uint64_t start = 0, zone_size = zoned->zone_size;
-    uint64_t capacity = zoned->num_zones * zone_size;
-    NvmeZone *zone;
-    int i;
-
-    zoned->zone_array = g_new0(NvmeZone, zoned->num_zones);
-    if (zoned->zd_extension_size) {
-        zoned->zd_extensions = g_malloc0(zoned->zd_extension_size *
-                                         zoned->num_zones);
-    }
-
-    QTAILQ_INIT(&zoned->exp_open_zones);
-    QTAILQ_INIT(&zoned->imp_open_zones);
-    QTAILQ_INIT(&zoned->closed_zones);
-    QTAILQ_INIT(&zoned->full_zones);
-
-    zone = zoned->zone_array;
-    for (i = 0; i < zoned->num_zones; i++, zone++) {
-        if (start + zone_size > capacity) {
-            zone_size = capacity - start;
-        }
-        zone->d.zt = NVME_ZONE_TYPE_SEQ_WRITE;
-        nvme_zns_set_state(zone, NVME_ZONE_STATE_EMPTY);
-        zone->d.za = 0;
-        zone->d.zcap = zoned->zone_capacity;
-        zone->d.zslba = start;
-        zone->d.wp = start;
-        zone->w_ptr = start;
-        start += zone_size;
-    }
-
-    zoned->zone_size_log2 = 0;
-    if (is_power_of_2(zoned->zone_size)) {
-        zoned->zone_size_log2 = 63 - clz64(zoned->zone_size);
-    }
-}
-
-static void nvme_zns_init(NvmeNamespace *ns)
-{
-    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
-    NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
-    NvmeIdNsZoned *id_ns_z = &zoned->id_ns;
-    int i;
-
-    nvme_zns_init_state(zoned);
-
-    /* MAR/MOR are zeroes-based, FFFFFFFFFh means no limit */
-    id_ns_z->mar = cpu_to_le32(zoned->max_active_zones - 1);
-    id_ns_z->mor = cpu_to_le32(zoned->max_open_zones - 1);
-    id_ns_z->zoc = 0;
-
-    if (zoned->flags & NVME_NS_ZONED_CROSS_READ) {
-        id_ns_z->ozcs |= NVME_ID_NS_ZONED_OZCS_CROSS_READ;
-    }
-
-    for (i = 0; i <= nvm->id_ns.nlbaf; i++) {
-        id_ns_z->lbafe[i].zsze = cpu_to_le64(zoned->zone_size);
-        id_ns_z->lbafe[i].zdes =
-            zoned->zd_extension_size >> 6; /* Units of 64B */
-    }
-
-    ns->csi = NVME_CSI_ZONED;
-    nvm->id_ns.nsze = cpu_to_le64(zoned->num_zones * zoned->zone_size);
-    nvm->id_ns.ncap = nvm->id_ns.nsze;
-    nvm->id_ns.nuse = nvm->id_ns.ncap;
-
-    /*
-     * The device uses the BDRV_BLOCK_ZERO flag to determine the "deallocated"
-     * status of logical blocks. Since the spec defines that logical blocks
-     * SHALL be deallocated when then zone is in the Empty or Offline states,
-     * we can only support DULBE if the zone size is a multiple of the
-     * calculated NPDG.
-     */
-    if (zoned->zone_size % (nvm->id_ns.npdg + 1)) {
-        warn_report("the zone size (%"PRIu64" blocks) is not a multiple of "
-                    "the calculated deallocation granularity (%d blocks); "
-                    "DULBE support disabled",
-                    zoned->zone_size, nvm->id_ns.npdg + 1);
-
-        nvm->id_ns.nsfeat &= ~0x4;
-    }
-}
-
-static void nvme_zns_clear_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone)
-{
-    uint8_t state;
-
-    zone->w_ptr = zone->d.wp;
-    state = nvme_zns_state(zone);
-    if (zone->d.wp != zone->d.zslba ||
-        (zone->d.za & NVME_ZA_ZD_EXT_VALID)) {
-        if (state != NVME_ZONE_STATE_CLOSED) {
-            trace_pci_nvme_clear_ns_close(state, zone->d.zslba);
-            nvme_zns_set_state(zone, NVME_ZONE_STATE_CLOSED);
-        }
-        nvme_zns_aor_inc_active(zoned);
-        QTAILQ_INSERT_HEAD(&zoned->closed_zones, zone, entry);
-    } else {
-        trace_pci_nvme_clear_ns_reset(state, zone->d.zslba);
-        nvme_zns_set_state(zone, NVME_ZONE_STATE_EMPTY);
-    }
-}
-
-/*
- * Close all the zones that are currently open.
- */
-static void nvme_zns_shutdown(NvmeNamespaceZoned *zoned)
-{
-    NvmeZone *zone, *next;
-
-    QTAILQ_FOREACH_SAFE(zone, &zoned->closed_zones, entry, next) {
-        QTAILQ_REMOVE(&zoned->closed_zones, zone, entry);
-        nvme_zns_aor_dec_active(zoned);
-        nvme_zns_clear_zone(zoned, zone);
-    }
-    QTAILQ_FOREACH_SAFE(zone, &zoned->imp_open_zones, entry, next) {
-        QTAILQ_REMOVE(&zoned->imp_open_zones, zone, entry);
-        nvme_zns_aor_dec_open(zoned);
-        nvme_zns_aor_dec_active(zoned);
-        nvme_zns_clear_zone(zoned, zone);
-    }
-    QTAILQ_FOREACH_SAFE(zone, &zoned->exp_open_zones, entry, next) {
-        QTAILQ_REMOVE(&zoned->exp_open_zones, zone, entry);
-        nvme_zns_aor_dec_open(zoned);
-        nvme_zns_aor_dec_active(zoned);
-        nvme_zns_clear_zone(zoned, zone);
-    }
-
-    assert(zoned->nr_open_zones == 0);
-}
-
 static int nvme_nsdev_check_constraints(NvmeNamespaceDevice *nsdev,
                                         Error **errp)
 {
@@ -405,7 +166,8 @@ static int nvme_nsdev_check_constraints(NvmeNamespaceDevice *nsdev,
 
 static int nvme_nsdev_setup(NvmeNamespaceDevice *nsdev, Error **errp)
 {
-    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(&nsdev->ns);
+    NvmeNamespace *ns = NVME_NAMESPACE(nsdev->ns);
+    NvmeNamespaceNvm *nvm = NVME_NAMESPACE_NVM(ns);
     static uint64_t ns_count;
 
     if (nvme_nsdev_check_constraints(nsdev, errp)) {
@@ -413,20 +175,20 @@ static int nvme_nsdev_setup(NvmeNamespaceDevice *nsdev, Error **errp)
     }
 
     if (nsdev->params.shared) {
-        nsdev->ns.flags |= NVME_NS_SHARED;
+        ns->flags |= NVME_NS_SHARED;
     }
 
-    nsdev->ns.nsid = nsdev->params.nsid;
-    memcpy(&nsdev->ns.uuid, &nsdev->params.uuid, sizeof(nsdev->ns.uuid));
+    ns->nsid = nsdev->params.nsid;
+    memcpy(&ns->uuid, &nsdev->params.uuid, sizeof(ns->uuid));
 
     if (nsdev->params.eui64) {
-        stq_be_p(&nsdev->ns.eui64.v, nsdev->params.eui64);
+        stq_be_p(&ns->eui64.v, nsdev->params.eui64);
     }
 
     /* Substitute a missing EUI-64 by an autogenerated one */
     ++ns_count;
-    if (!nsdev->ns.eui64.v && nsdev->params.eui64_default) {
-        nsdev->ns.eui64.v = ns_count + NVME_EUI64_DEFAULT;
+    if (!ns->eui64.v && nsdev->params.eui64_default) {
+        ns->eui64.v = ns_count + NVME_EUI64_DEFAULT;
     }
 
     nvm->id_ns.dps = nsdev->params.pi;
@@ -434,12 +196,13 @@ static int nvme_nsdev_setup(NvmeNamespaceDevice *nsdev, Error **errp)
         nvm->id_ns.dps |= NVME_ID_NS_DPS_FIRST_EIGHT;
     }
 
-    nsdev->ns.csi = NVME_CSI_NVM;
+    ns->csi = NVME_CSI_NVM;
 
-    nvme_ns_init(&nsdev->ns);
+    nvme_ns_nvm_configure_identify(ns);
+    nvme_ns_nvm_configure_format(nvm);
 
     if (nsdev->params.zoned) {
-        NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(&nsdev->ns);
+        NvmeNamespaceZoned *zoned = NVME_NAMESPACE_ZONED(ns);
 
         if (nvme_nsdev_zns_check_calc_geometry(nsdev, errp) != 0) {
             return -1;
@@ -453,7 +216,9 @@ static int nvme_nsdev_setup(NvmeNamespaceDevice *nsdev, Error **errp)
             zoned->flags |= NVME_NS_ZONED_CROSS_READ;
         }
 
-        nvme_zns_init(&nsdev->ns);
+        if (nvme_zns_configure(ns, errp)) {
+            return -1;
+        }
     }
 
     return 0;
@@ -468,7 +233,7 @@ void nvme_ns_shutdown(NvmeNamespace *ns)
 {
     blk_flush(nvme_blk(ns));
     if (nvme_ns_zoned(ns)) {
-        nvme_zns_shutdown(NVME_NAMESPACE_ZONED(ns));
+        nvme_zns_shutdown(ns);
     }
 }
 
@@ -485,7 +250,7 @@ void nvme_ns_cleanup(NvmeNamespace *ns)
 static void nvme_nsdev_unrealize(DeviceState *dev)
 {
     NvmeNamespaceDevice *nsdev = NVME_NAMESPACE_DEVICE(dev);
-    NvmeNamespace *ns = &nsdev->ns;
+    NvmeNamespace *ns = NVME_NAMESPACE(nsdev->ns);
 
     nvme_ns_drain(ns);
     nvme_ns_shutdown(ns);
@@ -495,7 +260,7 @@ static void nvme_nsdev_unrealize(DeviceState *dev)
 static void nvme_nsdev_realize(DeviceState *dev, Error **errp)
 {
     NvmeNamespaceDevice *nsdev = NVME_NAMESPACE_DEVICE(dev);
-    NvmeNamespace *ns = &nsdev->ns;
+    NvmeNamespace *ns = NULL;
     BusState *s = qdev_get_parent_bus(dev);
     NvmeCtrl *ctrl = NVME_DEVICE(s->parent);
     NvmeState *n = NVME_STATE(ctrl);
@@ -519,6 +284,12 @@ static void nvme_nsdev_realize(DeviceState *dev, Error **errp)
         }
     }
 
+    nsdev->ns = nsdev->params.zoned ? object_new(TYPE_NVME_NAMESPACE_ZONED) :
+        object_new(TYPE_NVME_NAMESPACE_NVM);
+
+    ns = NVME_NAMESPACE(nsdev->ns);
+    ns->realized = true;
+
     if (nvme_nsdev_init_blk(nsdev, errp)) {
         return;
     }
diff --git a/hw/nvme/nvm.h b/hw/nvme/nvm.h
new file mode 100644
index 000000000000..c3882ce5c21d
--- /dev/null
+++ b/hw/nvme/nvm.h
@@ -0,0 +1,65 @@
+#ifndef HW_NVME_NVM_H
+#define HW_NVME_NVM_H
+
+#include "nvme.h"
+
+#define TYPE_NVME_NAMESPACE_NVM "x-nvme-ns-nvm"
+OBJECT_DECLARE_SIMPLE_TYPE(NvmeNamespaceNvm, NVME_NAMESPACE_NVM)
+
+enum {
+    NVME_NS_NVM_EXTENDED_LBA = 1 << 0,
+    NVME_NS_NVM_PI_FIRST     = 1 << 1,
+};
+
+typedef struct NvmeNamespaceNvm {
+    NvmeNamespace parent_obj;
+
+    NvmeIdNs id_ns;
+
+    char *blk_nodename;
+    BlockBackend *blk;
+    int64_t size;
+    int64_t moff;
+
+    NvmeLBAF lbaf;
+    size_t   lbasz;
+    uint32_t discard_granularity;
+
+    uint16_t mssrl;
+    uint32_t mcl;
+    uint8_t  msrc;
+
+    unsigned long flags;
+} NvmeNamespaceNvm;
+
+static inline BlockBackend *nvme_blk(NvmeNamespace *ns)
+{
+    return NVME_NAMESPACE_NVM(ns)->blk;
+}
+
+static inline size_t nvme_l2b(NvmeNamespaceNvm *nvm, uint64_t lba)
+{
+    return lba << nvm->lbaf.ds;
+}
+
+static inline size_t nvme_m2b(NvmeNamespaceNvm *nvm, uint64_t lba)
+{
+    return nvm->lbaf.ms * lba;
+}
+
+static inline int64_t nvme_moff(NvmeNamespaceNvm *nvm, uint64_t lba)
+{
+    return nvm->moff + nvme_m2b(nvm, lba);
+}
+
+static inline bool nvme_ns_ext(NvmeNamespaceNvm *nvm)
+{
+    return !!NVME_ID_NS_FLBAS_EXTENDED(nvm->id_ns.flbas);
+}
+
+int nvme_ns_nvm_check_params(NvmeNamespace *ns, Error **errp);
+int nvme_ns_nvm_configure(NvmeNamespace *ns, Error **errp);
+void nvme_ns_nvm_configure_format(NvmeNamespaceNvm *nvm);
+void nvme_ns_nvm_configure_identify(NvmeNamespace *ns);
+
+#endif /* HW_NVME_NVM_H */
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 627b28649892..5e516f8fb377 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -21,6 +21,7 @@
 #include "qemu/uuid.h"
 #include "qemu/notify.h"
 #include "qapi/qapi-builtin-visit.h"
+#include "qapi/qapi-types-qom.h"
 #include "hw/pci/pci.h"
 #include "hw/block/block.h"
 
@@ -55,6 +56,7 @@ struct NvmeNamespaceClass {
 
     int (*check_params)(NvmeNamespace *ns, Error **errp);
     int (*configure)(NvmeNamespace *ns, Error **errp);
+    void (*shutdown)(NvmeNamespace *ns);
 };
 
 #define TYPE_NVME_SUBSYSTEM "x-nvme-subsystem"
@@ -139,66 +141,6 @@ typedef struct NvmeNamespaceParams {
     uint32_t zd_extension_size;
 } NvmeNamespaceParams;
 
-typedef struct NvmeZone {
-    NvmeZoneDescr   d;
-    uint64_t        w_ptr;
-    QTAILQ_ENTRY(NvmeZone) entry;
-} NvmeZone;
-
-enum {
-    NVME_NS_ZONED_CROSS_READ = 1 << 0,
-};
-
-typedef struct NvmeNamespaceZoned {
-    NvmeIdNsZoned id_ns;
-
-    uint32_t num_zones;
-    NvmeZone *zone_array;
-
-    uint64_t zone_size;
-    uint32_t zone_size_log2;
-
-    uint64_t zone_capacity;
-
-    uint32_t zd_extension_size;
-    uint8_t  *zd_extensions;
-
-    uint32_t max_open_zones;
-    int32_t  nr_open_zones;
-    uint32_t max_active_zones;
-    int32_t  nr_active_zones;
-
-    unsigned long flags;
-
-    QTAILQ_HEAD(, NvmeZone) exp_open_zones;
-    QTAILQ_HEAD(, NvmeZone) imp_open_zones;
-    QTAILQ_HEAD(, NvmeZone) closed_zones;
-    QTAILQ_HEAD(, NvmeZone) full_zones;
-} NvmeNamespaceZoned;
-
-enum {
-    NVME_NS_NVM_EXTENDED_LBA = 1 << 0,
-    NVME_NS_NVM_PI_FIRST     = 1 << 1,
-};
-
-typedef struct NvmeNamespaceNvm {
-    NvmeIdNs id_ns;
-
-    BlockBackend *blk;
-    int64_t size;
-    int64_t moff;
-
-    NvmeLBAF lbaf;
-    size_t   lbasz;
-    uint32_t discard_granularity;
-
-    uint16_t mssrl;
-    uint32_t mcl;
-    uint8_t  msrc;
-
-    unsigned long flags;
-} NvmeNamespaceNvm;
-
 enum NvmeNamespaceFlags {
     NVME_NS_SHARED = 1 << 0,
 };
@@ -227,27 +169,16 @@ typedef struct NvmeNamespace {
     struct {
         uint32_t err_rec;
     } features;
-
-    NvmeNamespaceNvm   nvm;
-    NvmeNamespaceZoned zoned;
 } NvmeNamespace;
 
 bool nvme_ns_prop_writable(Object *obj, const char *name, Error **errp);
 
-#define NVME_NAMESPACE_NVM(ns) (&(ns)->nvm)
-#define NVME_NAMESPACE_ZONED(ns) (&(ns)->zoned)
-
-static inline BlockBackend *nvme_blk(NvmeNamespace *ns)
-{
-    return NVME_NAMESPACE_NVM(ns)->blk;
-}
-
 typedef struct NvmeNamespaceDevice {
     DeviceState  parent_obj;
     BlockConf    blkconf;
     int32_t      bootindex;
 
-    NvmeNamespace       ns;
+    Object       *ns;
     NvmeNamespaceParams params;
 } NvmeNamespaceDevice;
 
@@ -260,27 +191,6 @@ static inline uint32_t nvme_nsid(NvmeNamespace *ns)
     return 0;
 }
 
-static inline size_t nvme_l2b(NvmeNamespaceNvm *nvm, uint64_t lba)
-{
-    return lba << nvm->lbaf.ds;
-}
-
-static inline size_t nvme_m2b(NvmeNamespaceNvm *nvm, uint64_t lba)
-{
-    return nvm->lbaf.ms * lba;
-}
-
-static inline int64_t nvme_moff(NvmeNamespaceNvm *nvm, uint64_t lba)
-{
-    return nvm->moff + nvme_m2b(nvm, lba);
-}
-
-static inline bool nvme_ns_ext(NvmeNamespaceNvm *nvm)
-{
-    return !!NVME_ID_NS_FLBAS_EXTENDED(nvm->id_ns.flbas);
-}
-
-void nvme_ns_nvm_init_format(NvmeNamespaceNvm *nvm);
 void nvme_ns_init(NvmeNamespace *ns);
 void nvme_ns_drain(NvmeNamespace *ns);
 void nvme_ns_shutdown(NvmeNamespace *ns);
diff --git a/hw/nvme/zns.h b/hw/nvme/zns.h
index 967adc62d730..1a040d900d5c 100644
--- a/hw/nvme/zns.h
+++ b/hw/nvme/zns.h
@@ -4,9 +4,52 @@
 #include "qemu/units.h"
 
 #include "nvme.h"
+#include "nvm.h"
 
 #define NVME_DEFAULT_ZONE_SIZE   (128 * MiB)
 
+#define TYPE_NVME_NAMESPACE_ZONED "x-nvme-ns-zoned"
+OBJECT_DECLARE_SIMPLE_TYPE(NvmeNamespaceZoned, NVME_NAMESPACE_ZONED)
+
+typedef struct NvmeZone {
+    NvmeZoneDescr   d;
+    uint64_t        w_ptr;
+    QTAILQ_ENTRY(NvmeZone) entry;
+} NvmeZone;
+
+enum {
+    NVME_NS_ZONED_CROSS_READ = 1 << 0,
+};
+
+typedef struct NvmeNamespaceZoned {
+    NvmeNamespaceNvm parent_obj;
+
+    NvmeIdNsZoned id_ns;
+
+    uint32_t num_zones;
+    NvmeZone *zone_array;
+
+    uint64_t zone_size;
+    uint32_t zone_size_log2;
+
+    uint64_t zone_capacity;
+
+    uint32_t zd_extension_size;
+    uint8_t  *zd_extensions;
+
+    uint32_t max_open_zones;
+    int32_t  nr_open_zones;
+    uint32_t max_active_zones;
+    int32_t  nr_active_zones;
+
+    unsigned long flags;
+
+    QTAILQ_HEAD(, NvmeZone) exp_open_zones;
+    QTAILQ_HEAD(, NvmeZone) imp_open_zones;
+    QTAILQ_HEAD(, NvmeZone) closed_zones;
+    QTAILQ_HEAD(, NvmeZone) full_zones;
+} NvmeNamespaceZoned;
+
 static inline NvmeZoneState nvme_zns_state(NvmeZone *zone)
 {
     return zone->d.zs >> 4;
@@ -96,4 +139,9 @@ static inline void nvme_zns_aor_dec_active(NvmeNamespaceZoned *zoned)
     assert(zoned->nr_active_zones >= 0);
 }
 
+void nvme_zns_init_state(NvmeNamespaceZoned *zoned);
+int nvme_zns_configure(NvmeNamespace *ns, Error **errp);
+void nvme_zns_clear_zone(NvmeNamespaceZoned *zoned, NvmeZone *zone);
+void nvme_zns_shutdown(NvmeNamespace *ns);
+
 #endif /* HW_NVME_ZNS_H */
diff --git a/qapi/qom.json b/qapi/qom.json
index 6d5cef6b92ad..84bec3be8493 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -679,6 +679,50 @@
             '*eui64': 'str',
             '*uuid': 'str' } }
 
+##
+# @NvmeProtInfoType:
+#
+# Indicates the namespace protection information type.
+#
+# Since: 6.1
+##
+{ 'enum': 'NvmeProtInfoType',
+  'data': [ 'none', 'type1', 'type2', 'type3' ] }
+
+##
+# @NvmeNamespaceNvmProperties:
+#
+# Properties for x-nvme-ns-nvm objects.
+#
+# @pi-type: protection information type
+#
+# Since: 6.1
+##
+{ 'struct': 'NvmeNamespaceNvmProperties',
+  'base': 'NvmeNamespaceProperties',
+  'data': { 'blockdev': 'str',
+            '*lba-size': 'size',
+            '*metadata-size': 'size',
+            '*extended-lba': 'bool',
+            '*pi-type': 'NvmeProtInfoType',
+            '*pi-first': 'bool' } }
+
+##
+# @NvmeNamespaceZonedProperties:
+#
+# Properties for x-nvme-ns-zoned objects.
+#
+# Since: 6.1
+##
+{ 'struct': 'NvmeNamespaceZonedProperties',
+  'base': 'NvmeNamespaceNvmProperties',
+  'data': { '*zone-size': 'size',
+            '*zone-capacity': 'size',
+            '*zone-cross-read': 'bool',
+            '*zone-descriptor-extension-size': 'size',
+            '*zone-max-active': 'uint32',
+            '*zone-max-open': 'uint32' } }
+
 ##
 # @PrManagerHelperProperties:
 #
@@ -830,6 +874,8 @@
       'if': 'CONFIG_LINUX' },
     'memory-backend-ram',
     'x-nvme-subsystem',
+    'x-nvme-ns-nvm',
+    'x-nvme-ns-zoned',
     'pef-guest',
     'pr-manager-helper',
     'qtest',
@@ -889,6 +935,8 @@
                                       'if': 'CONFIG_LINUX' },
       'memory-backend-ram':         'MemoryBackendProperties',
       'x-nvme-subsystem':           'NvmeSubsystemProperties',
+      'x-nvme-ns-nvm':              'NvmeNamespaceNvmProperties',
+      'x-nvme-ns-zoned':            'NvmeNamespaceZonedProperties',
       'pr-manager-helper':          'PrManagerHelperProperties',
       'qtest':                      'QtestProperties',
       'rng-builtin':                'RngProperties',
diff --git a/softmmu/vl.c b/softmmu/vl.c
index 55ab70eb97fe..24374189b1bf 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -1833,6 +1833,14 @@ static bool object_create_early(const char *type)
         return false;
     }
 
+    /*
+     * Reason: x-nvme-ns-* property "blockdev"
+     */
+    if (g_str_equal(type, "x-nvme-ns-nvm") ||
+        g_str_equal(type, "x-nvme-ns-zoned")) {
+        return false;
+    }
+
     /*
      * Allocation of large amounts of memory may delay
      * chardev initialization for too long, and trigger timeouts
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 15/16] hw/nvme: add experimental device x-nvme-ctrl
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (13 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 14/16] hw/nvme: add experimental objects x-nvme-ns-{nvm, zoned} Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  2021-09-27  5:17 ` [PATCH RFC v2 16/16] docs: add documentation for experimental nvme emulation Klaus Jensen
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Add a new experimental 'x-nvme-ctrl' device which allows us to get rid
of a bunch of legacy options and slightly change others to better use
the qdev property system.

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 hw/nvme/ctrl.c   | 137 +++++++++++++++++++++++++++++++++++------------
 hw/nvme/ns.c     |   2 +-
 hw/nvme/nvme.h   |  11 +++-
 hw/nvme/subsys.c |  27 ++++++----
 4 files changed, 130 insertions(+), 47 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 31499b10fc49..fa2f537f114d 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -6529,19 +6529,14 @@ static void nvme_init_ctrl(NvmeState *n, PCIDevice *pci_dev)
 
 static int nvme_init_subsys(NvmeState *n, Error **errp)
 {
-    int cntlid;
-
     if (!n->subsys) {
         return 0;
     }
 
-    cntlid = nvme_subsys_register_ctrl(n->subsys, n, errp);
-    if (cntlid < 0) {
+    if (nvme_subsys_register_ctrl(n->subsys, n, errp)) {
         return -1;
     }
 
-    n->cntlid = cntlid;
-
     return 0;
 }
 
@@ -6560,8 +6555,39 @@ void nvme_attach_ns(NvmeState *n, NvmeNamespace *ns)
 
 static void nvme_realize(PCIDevice *pci_dev, Error **errp)
 {
-    NvmeCtrl *ctrl = NVME_DEVICE(pci_dev);
-    NvmeState *n = NVME_STATE(ctrl);
+    NvmeState *n = NVME_STATE(pci_dev);
+    const int SN_LEN = 12;
+
+    if (!n->params.serial) {
+        int i;
+
+        n->params.serial = g_malloc0(SN_LEN + 1);
+
+        for (i = 0; i < SN_LEN; i++) {
+             n->params.serial[i] = g_random_int_range(0x41, 0x5A);
+        }
+    }
+
+    if (nvme_check_constraints(n, errp)) {
+        return;
+    }
+
+    nvme_init_state(n);
+    if (nvme_init_pci(n, pci_dev, errp)) {
+        return;
+    }
+
+    if (nvme_init_subsys(n, errp)) {
+        return;
+    }
+
+    nvme_init_ctrl(n, pci_dev);
+}
+
+static void nvme_legacy_realize(PCIDevice *pci_dev, Error **errp)
+{
+    NvmeState *n = NVME_STATE(pci_dev);
+    NvmeCtrlLegacyDevice *ctrl = NVME_DEVICE_LEGACY(n);
 
     if (ctrl->subsys_dev) {
         if (ctrl->namespace.blkconf.blk) {
@@ -6588,6 +6614,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
     if (nvme_init_subsys(n, errp)) {
         return;
     }
+
     nvme_init_ctrl(n, pci_dev);
 
     /* setup a namespace if the controller drive property was given */
@@ -6643,27 +6670,47 @@ static void nvme_exit(PCIDevice *pci_dev)
 static Property nvme_state_props[] = {
     DEFINE_PROP_LINK("pmrdev", NvmeState, pmr.dev, TYPE_MEMORY_BACKEND,
                      HostMemoryBackend *),
-    DEFINE_PROP_LINK("subsys", NvmeState, subsys_dev,
-                     TYPE_NVME_SUBSYSTEM_DEVICE, NvmeSubsystemDevice *),
     DEFINE_PROP_STRING("serial", NvmeState, params.serial),
-    DEFINE_PROP_UINT32("cmb_size_mb", NvmeState, params.cmb_size_mb, 0),
-    DEFINE_PROP_UINT32("num_queues", NvmeState, params.num_queues, 0),
-    DEFINE_PROP_UINT32("max_ioqpairs", NvmeState, params.max_ioqpairs, 64),
-    DEFINE_PROP_UINT16("msix_qsize", NvmeState, params.msix_qsize, 65),
     DEFINE_PROP_UINT8("aerl", NvmeState, params.aerl, 3),
-    DEFINE_PROP_UINT32("aer_max_queued", NvmeState, params.aer_max_queued, 64),
     DEFINE_PROP_UINT8("mdts", NvmeState, params.mdts, 7),
-    DEFINE_PROP_UINT8("vsl", NvmeState, params.vsl, 7),
-    DEFINE_PROP_BOOL("use-intel-id", NvmeState, params.use_intel_id, false),
     DEFINE_PROP_BOOL("legacy-cmb", NvmeState, params.legacy_cmb, false),
-    DEFINE_PROP_UINT8("zoned.zasl", NvmeState, params.zasl, 0),
-    DEFINE_PROP_BOOL("zoned.auto_transition", NvmeState,
-                     params.auto_transition_zones, true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
 static Property nvme_props[] = {
-    DEFINE_BLOCK_PROPERTIES(NvmeCtrl, namespace.blkconf),
+    DEFINE_PROP_LINK("subsys", NvmeState, subsys, TYPE_NVME_SUBSYSTEM,
+                     NvmeSubsystem *),
+
+    DEFINE_PROP_UINT16("cntlid", NvmeState, cntlid, 0),
+    DEFINE_PROP_UINT32("cmb-size-mb", NvmeState, params.cmb_size_mb, 0),
+    DEFINE_PROP_UINT32("max-aen-retention", NvmeState, params.aer_max_queued, 64),
+    DEFINE_PROP_UINT32("max-ioqpairs", NvmeState, params.max_ioqpairs, 64),
+    DEFINE_PROP_UINT16("msix-vectors", NvmeState, params.msix_qsize, 2048),
+
+    /* nvm command set specific properties */
+    DEFINE_PROP_UINT8("nvm-vsl", NvmeState, params.vsl, 7),
+
+    /* zoned command set specific properties */
+    DEFINE_PROP_UINT8("zoned-zasl", NvmeState, params.zasl, 0),
+    DEFINE_PROP_BOOL("zoned-auto-transition-zones", NvmeState,
+                     params.auto_transition_zones, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static Property nvme_legacy_props[] = {
+    DEFINE_BLOCK_PROPERTIES(NvmeCtrlLegacyDevice, namespace.blkconf),
+    DEFINE_PROP_LINK("subsys", NvmeCtrlLegacyDevice, subsys_dev,
+                     TYPE_NVME_SUBSYSTEM_DEVICE, NvmeSubsystemDevice *),
+    DEFINE_PROP_UINT32("cmb_size_mb", NvmeState, params.cmb_size_mb, 0),
+    DEFINE_PROP_UINT32("num_queues", NvmeState, params.num_queues, 0),
+    DEFINE_PROP_UINT32("aer_max_queued", NvmeState, params.aer_max_queued, 64),
+    DEFINE_PROP_UINT32("max_ioqpairs", NvmeState, params.max_ioqpairs, 64),
+    DEFINE_PROP_UINT16("msix_qsize", NvmeState, params.msix_qsize, 65),
+    DEFINE_PROP_BOOL("use-intel-id", NvmeState, params.use_intel_id, false),
+    DEFINE_PROP_UINT8("vsl", NvmeState, params.vsl, 7),
+    DEFINE_PROP_UINT8("zoned.zasl", NvmeState, params.zasl, 0),
+    DEFINE_PROP_BOOL("zoned.auto_transition", NvmeState,
+                     params.auto_transition_zones, true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -6719,7 +6766,6 @@ static void nvme_state_class_init(ObjectClass *oc, void *data)
     DeviceClass *dc = DEVICE_CLASS(oc);
     PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
 
-    pc->realize = nvme_realize;
     pc->exit = nvme_exit;
     pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
     pc->revision = 2;
@@ -6753,25 +6799,45 @@ static const TypeInfo nvme_state_info = {
 static void nvme_class_init(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
+    PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
+
+    pc->realize = nvme_realize;
+
     device_class_set_props(dc, nvme_props);
 }
 
-static void nvme_instance_init(Object *obj)
-{
-    NvmeCtrl *ctrl = NVME_DEVICE(obj);
-
-    device_add_bootindex_property(obj, &ctrl->namespace.blkconf.bootindex,
-                                  "bootindex", "/namespace@1,0",
-                                  DEVICE(obj));
-}
-
 static const TypeInfo nvme_info = {
     .name = TYPE_NVME_DEVICE,
     .parent = TYPE_NVME_STATE,
     .class_init = nvme_class_init,
     .instance_size = sizeof(NvmeCtrl),
-    .instance_init = nvme_instance_init,
-    .class_init = nvme_class_init,
+};
+
+static void nvme_legacy_instance_init(Object *obj)
+{
+    NvmeCtrlLegacyDevice *ctrl = NVME_DEVICE_LEGACY(obj);
+
+    device_add_bootindex_property(obj, &ctrl->namespace.blkconf.bootindex,
+                                  "bootindex", "/namespace@1,0",
+                                  DEVICE(obj));
+}
+
+static void nvme_legacy_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
+
+    pc->realize = nvme_legacy_realize;
+
+    device_class_set_props(dc, nvme_legacy_props);
+}
+
+static const TypeInfo nvme_legacy_info = {
+    .name = TYPE_NVME_DEVICE_LEGACY,
+    .parent = TYPE_NVME_STATE,
+    .class_init = nvme_legacy_class_init,
+    .instance_size = sizeof(NvmeCtrlLegacyDevice),
+    .instance_init = nvme_legacy_instance_init,
 };
 
 static const TypeInfo nvme_bus_info = {
@@ -6780,11 +6846,12 @@ static const TypeInfo nvme_bus_info = {
     .instance_size = sizeof(NvmeBus),
 };
 
-static void nvme_register_types(void)
+static void register_types(void)
 {
     type_register_static(&nvme_state_info);
     type_register_static(&nvme_info);
+    type_register_static(&nvme_legacy_info);
     type_register_static(&nvme_bus_info);
 }
 
-type_init(nvme_register_types)
+type_init(register_types)
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index 757a90b58ea8..90fc8d810ae0 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -262,7 +262,7 @@ static void nvme_nsdev_realize(DeviceState *dev, Error **errp)
     NvmeNamespaceDevice *nsdev = NVME_NAMESPACE_DEVICE(dev);
     NvmeNamespace *ns = NULL;
     BusState *s = qdev_get_parent_bus(dev);
-    NvmeCtrl *ctrl = NVME_DEVICE(s->parent);
+    NvmeCtrlLegacyDevice *ctrl = NVME_DEVICE_LEGACY(s->parent);
     NvmeState *n = NVME_STATE(ctrl);
     NvmeSubsystem *subsys = n->subsys;
     uint32_t nsid = nsdev->params.nsid;
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 5e516f8fb377..613aaab3ccd2 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -38,9 +38,12 @@ typedef struct NvmeNamespace NvmeNamespace;
 #define TYPE_NVME_STATE "nvme-state"
 OBJECT_DECLARE_SIMPLE_TYPE(NvmeState, NVME_STATE)
 
-#define TYPE_NVME_DEVICE "nvme"
+#define TYPE_NVME_DEVICE "x-nvme-ctrl"
 OBJECT_DECLARE_SIMPLE_TYPE(NvmeCtrl, NVME_DEVICE)
 
+#define TYPE_NVME_DEVICE_LEGACY "nvme"
+OBJECT_DECLARE_SIMPLE_TYPE(NvmeCtrlLegacyDevice, NVME_DEVICE_LEGACY)
+
 #define TYPE_NVME_BUS "nvme-bus"
 OBJECT_DECLARE_SIMPLE_TYPE(NvmeBus, NVME_BUS)
 
@@ -400,6 +403,10 @@ typedef struct NvmeState {
 
 typedef struct NvmeCtrl {
     NvmeState parent_obj;
+} NvmeCtrl;
+
+typedef struct NvmeCtrlLegacyDevice {
+    NvmeState parent_obj;
 
     NvmeBus bus;
 
@@ -407,7 +414,7 @@ typedef struct NvmeCtrl {
     NvmeNamespaceDevice namespace;
 
     NvmeSubsystemDevice *subsys_dev;
-} NvmeCtrl;
+} NvmeCtrlLegacyDevice;
 
 static inline NvmeNamespace *nvme_ns(NvmeState *n, uint32_t nsid)
 {
diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c
index e4dcd8fd20a5..48700ca06aa6 100644
--- a/hw/nvme/subsys.c
+++ b/hw/nvme/subsys.c
@@ -16,20 +16,29 @@
 int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeState *n,
                               Error **errp)
 {
-    int cntlid, nsid;
+    int nsid;
 
-    for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) {
-        if (!subsys->ctrls[cntlid]) {
-            break;
+    if (!n->cntlid) {
+        int cntlid;
+
+        for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) {
+            if (!subsys->ctrls[cntlid]) {
+                break;
+            }
         }
-    }
 
-    if (cntlid == ARRAY_SIZE(subsys->ctrls)) {
-        error_setg(errp, "no more free controller id");
+        if (cntlid == ARRAY_SIZE(subsys->ctrls)) {
+            error_setg(errp, "no more free controller identifiers");
+            return -1;
+        }
+
+        n->cntlid = cntlid;
+    } else if (subsys->ctrls[n->cntlid]) {
+        error_setg(errp, "controller identifier already assigned");
         return -1;
     }
 
-    subsys->ctrls[cntlid] = n;
+    subsys->ctrls[n->cntlid] = n;
 
     for (nsid = 1; nsid < ARRAY_SIZE(subsys->namespaces); nsid++) {
         NvmeNamespace *ns = subsys->namespaces[nsid];
@@ -38,7 +47,7 @@ int nvme_subsys_register_ctrl(NvmeSubsystem *subsys, NvmeState *n,
         }
     }
 
-    return cntlid;
+    return 0;
 }
 
 void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeState *n)
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC v2 16/16] docs: add documentation for experimental nvme emulation
  2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
                   ` (14 preceding siblings ...)
  2021-09-27  5:17 ` [PATCH RFC v2 15/16] hw/nvme: add experimental device x-nvme-ctrl Klaus Jensen
@ 2021-09-27  5:17 ` Klaus Jensen
  15 siblings, 0 replies; 17+ messages in thread
From: Klaus Jensen @ 2021-09-27  5:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Fam Zheng, Kevin Wolf, Daniel P. Berrangé,
	Eduardo Habkost, qemu-block, Philippe Mathieu-Daudé,
	Markus Armbruster, Klaus Jensen, Hanna Reitz, Hannes Reinecke,
	Stefan Hajnoczi, Klaus Jensen, Keith Busch, Paolo Bonzini,
	Eric Blake

From: Klaus Jensen <k.jensen@samsung.com>

Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
---
 docs/system/device-emulation.rst          |   1 +
 docs/system/devices/nvme-experimental.rst | 107 ++++++++++++++++++++++
 2 files changed, 108 insertions(+)
 create mode 100644 docs/system/devices/nvme-experimental.rst

diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst
index 7afcfd8064ae..9a4e08e4ea44 100644
--- a/docs/system/device-emulation.rst
+++ b/docs/system/device-emulation.rst
@@ -85,6 +85,7 @@ Emulated Devices
    devices/ivshmem.rst
    devices/net.rst
    devices/nvme.rst
+   devices/nvme-experimental.rst
    devices/usb.rst
    devices/vhost-user.rst
    devices/virtio-pmem.rst
diff --git a/docs/system/devices/nvme-experimental.rst b/docs/system/devices/nvme-experimental.rst
new file mode 100644
index 000000000000..d3b94dc3ef59
--- /dev/null
+++ b/docs/system/devices/nvme-experimental.rst
@@ -0,0 +1,107 @@
+===========================
+Experimental NVMe Emulation
+===========================
+
+QEMU offers experimental NVMe emulation through the ``x-nvme-ctrl`` device and
+the ``x-nvme-subsystem`` and ``x-nvme-ns-{nvm,zoned}`` objects.
+
+
+Adding NVMe Devices
+===================
+
+Controller Emulation
+--------------------
+
+The QEMU emulated NVMe controller implements version 1.4 of the NVM Express
+specification. All mandatory features are implement with a couple of exceptions
+and limitations:
+
+  * Accounting numbers in the SMART/Health log page are reset when the device
+    is power cycled.
+  * Interrupt Coalescing is not supported and is disabled by default.
+
+The simplest way to attach an NVMe controller on the QEMU PCI bus is to add the
+following parameters:
+
+.. code-block:: console
+
+    -object x-nvme-subsystem,id=nvme-subsys-0
+    -device x-nvme-ctrl,subsys=nvme-subsys-0
+
+There are a number of optional general parameters for the ``x-nvme-ctrl``
+device. Some are mentioned here, but see ``-device x-nvme-ctrl,help`` to list
+all possible parameters.
+
+``max-ioqpairs=UINT32`` (default: ``64``)
+  Set the maximum number of allowed I/O queue pairs.
+
+``msix-vectors=UINT16`` (default: ``65``)
+  The number of MSI-X vectors that the device should support.
+
+``mdts=UINT8`` (default: ``7``)
+  Set the Maximum Data Transfer Size of the device.
+
+
+Additional Namespaces
+---------------------
+
+The invocation sketched above does not add any namespaces to the subsystem. To
+add these, add ``x-nvme-ns-NSTYPE`` (where ``NSTYPE`` is either ``nvm`` or
+``zoned``) objects with attached blockdevs and a reference to the subsystem:
+
+.. code-block:: console
+
+    -blockdev file,node-name=blk-file-nvm-1,filename=nvm-1.img
+    -blockdev raw,node-name=blk-nvm-1,file=blk-file-nvm-1
+    -object x-nvme-ns-nvm,id=nvm-1,blockdev=blk-nvm-1,subsys=nvme-subsys-0
+
+There are a number of optional parameters available (common to both the ``nvm``
+and ``zoned`` namespace types):
+
+``nsid`` (default: ``"auto"``)
+  Explicitly set the namespace identifier. If left at the default, the
+  subsystem will allocate the next available identifier.
+
+``uuid`` (default: ``"auto"``)
+  Set the UUID of the namespace. This will be reported as a "Namespace UUID"
+  descriptor in the Namespace Identification Descriptor List. If left at the
+  default, a UUID will be generated.
+
+``eui64`` (default: ``"auto"``)
+  Set the EUI-64 of the namespace. This will be reported as a "IEEE Extended
+  Unique Identifier" descriptor in the Namespace Identification Descriptor
+  List. If left at the default, an identifier prefixed with the QEMU IEEE OUI
+  (``52:54:00``) will be generated.
+
+``lba-size`` (default: ``4096``)
+  Set the logical block size.
+
+Namespaces support LBA metadata in the form separate metadata (``MPTR``-based)
+and extended LBAs.
+
+``metadata-size`` (default: ``0``)
+  Defines the number of metadata bytes per LBA.
+
+``extended-lba`` (default: ``off/false``)
+  Set to ``on/true`` to enable extended LBAs.
+
+With metadata configured, namespaces support DIF- and DIX-based protection
+information (depending on ``extended-lba``).
+
+``pi-type`` (default: ``"none"``)
+  Enable protection information of the specified type (type ``"type1"``,
+  ``"type2"`` or ``"type3"``).
+
+``pi-first`` (default: ``off/false``)
+  Controls the location of the protection information within the metadata. Set
+  to ``on/true`` to transfer protection information as the first eight bytes of
+  metadata. Otherwise, the protection information is transferred as the last
+  eight bytes.
+
+The ``zoned`` namespace type has additional parameters:
+
+``zone-size`` (default: ``4096``)
+  The number of LBAs in a zone.
+
+``zone-capacity`` (default: ``4096``)
+  The number of writable LBAs in a zone.
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2021-09-27  6:02 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-27  5:17 [PATCH RFC v2 00/16] hw/nvme: experimental user-creatable objects Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 01/16] hw/nvme: reattach subsystem namespaces on hotplug Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 02/16] hw/nvme: change nvme-ns 'shared' default Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 03/16] hw/nvme: move dif/pi prototypes into dif.h Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 04/16] hw/nvme: move zns helpers and types into zns.h Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 05/16] hw/nvme: move zoned namespace members to separate struct Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 06/16] hw/nvme: move nvm " Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 07/16] hw/nvme: move BlockBackend to NvmeNamespaceNvm Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 08/16] hw/nvme: hoist qdev state from namespace Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 09/16] hw/nvme: hoist qdev state from subsystem Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 10/16] hw/nvme: hoist qdev state from controller Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 11/16] hw/nvme: add experimental object x-nvme-subsystem Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 12/16] nvme: add structured type for nguid Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 13/16] hw/nvme: add experimental abstract object x-nvme-ns Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 14/16] hw/nvme: add experimental objects x-nvme-ns-{nvm, zoned} Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 15/16] hw/nvme: add experimental device x-nvme-ctrl Klaus Jensen
2021-09-27  5:17 ` [PATCH RFC v2 16/16] docs: add documentation for experimental nvme emulation Klaus Jensen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).