]> git.ipfire.org Git - thirdparty/qemu.git/commitdiff
hw/nvme: enable ns atomic writes
authorAlan Adamson <alan.adamson@oracle.com>
Mon, 2 Jun 2025 23:04:57 +0000 (16:04 -0700)
committerKlaus Jensen <k.jensen@samsung.com>
Thu, 30 Oct 2025 06:07:14 +0000 (07:07 +0100)
Add support for the namespace atomic paramters: NAWUN and NAWUN. Namespace
Atomic Compare and Write Unit (NACWU) is not currently supported.

Writes that adhere to the NACWU and NAWUPF parameters are guaranteed to be
atomic.

New NVMe QEMU Paramters (See NVMe Specification for details):
        atomic.nawun=UINT16 (default: 0)
        atomic.nawupf=UINT16 (default: 0)
        atomic.nsfeat (default off) - Set Namespace Supported Atomic Boundary &
                Power (NSABP) bit in Namespace Features (NSFEAT) in the Identify
                Namespace Data Structure

See the NVMe Specification for more information.

Signed-off-by: Alan Adamson <alan.adamson@oracle.com>
Reviewed-by: Jesper Wendel Devantier <foss@defmacro.it>
Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
hw/nvme/ctrl.c
hw/nvme/ns.c
hw/nvme/nvme.h

index fa003031e719cc2c3a369cdc4496936844952c92..121a95b2e3735fb5ab2483e49a049cebaf697e89 100644 (file)
@@ -6705,6 +6705,23 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
         } else {
             atomic->atomic_writes = 1;
         }
+        for (i = 1; i <= NVME_MAX_NAMESPACES; i++) {
+            ns = nvme_ns(n, i);
+            if (ns && ns->atomic.atomic_writes) {
+                if (n->dn) {
+                    ns->atomic.atomic_max_write_size =
+                        le16_to_cpu(ns->id_ns.nawupf) + 1;
+                } else {
+                    ns->atomic.atomic_max_write_size =
+                        le16_to_cpu(ns->id_ns.nawun) + 1;
+                }
+                if (ns->atomic.atomic_max_write_size == 1) {
+                    ns->atomic.atomic_writes = 0;
+                } else {
+                    ns->atomic.atomic_writes = 1;
+                }
+            }
+        }
         break;
     default:
         return NVME_FEAT_NOT_CHANGEABLE | NVME_DNR;
@@ -7688,6 +7705,12 @@ static int nvme_atomic_write_check(NvmeCtrl *n, NvmeCmd *cmd,
 
 static NvmeAtomic *nvme_get_atomic(NvmeCtrl *n, NvmeCmd *cmd)
 {
+    NvmeNamespace *ns = nvme_ns(n, cmd->nsid);
+
+    if (ns && ns->atomic.atomic_writes) {
+        return &ns->atomic;
+    }
+
     if (n->atomic.atomic_writes) {
         return &n->atomic;
     }
index 6df2e8e7c5acd69a3cc8548cb5844c4c84ff00fb..28aacb8db59a9639e03c19dd1f61447eb0819928 100644 (file)
@@ -724,11 +724,46 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
     BusState *s = qdev_get_parent_bus(dev);
     NvmeCtrl *n = NVME(s->parent);
     NvmeSubsystem *subsys = n->subsys;
+    NvmeIdCtrl *id = &n->id_ctrl;
+    NvmeIdNs *id_ns = &ns->id_ns;
     uint32_t nsid = ns->params.nsid;
     int i;
 
     assert(subsys);
 
+    /* Set atomic write parameters */
+    if (ns->params.atomic_nsfeat) {
+        id_ns->nsfeat |= NVME_ID_NS_NSFEAT_NSABPNS;
+        id_ns->nawun = cpu_to_le16(ns->params.atomic_nawun);
+        if (!id->awupf || (id_ns->nawun && (id_ns->nawun < id->awun))) {
+            error_report("Invalid NAWUN: %x AWUN=%x", id_ns->nawun, id->awun);
+        }
+        id_ns->nawupf = cpu_to_le16(ns->params.atomic_nawupf);
+        if (!id->awupf || (id_ns->nawupf && (id_ns->nawupf < id->awupf))) {
+            error_report("Invalid NAWUPF: %x AWUPF=%x",
+                id_ns->nawupf, id->awupf);
+        }
+        if (id_ns->nawupf > id_ns->nawun) {
+            error_report("Invalid: NAWUN=%x NAWUPF=%x",
+                id_ns->nawun, id_ns->nawupf);
+        }
+    }
+
+    if (id_ns->nawun || id_ns->nawupf) {
+        NvmeAtomic *atomic = &ns->atomic;
+
+        if (n->dn) {
+            atomic->atomic_max_write_size = cpu_to_le16(id_ns->nawupf) + 1;
+        } else {
+            atomic->atomic_max_write_size = cpu_to_le16(id_ns->nawun) + 1;
+        }
+        if (atomic->atomic_max_write_size == 1) {
+            atomic->atomic_writes = 0;
+        } else {
+            atomic->atomic_writes = 1;
+        }
+    }
+
     /* reparent to subsystem bus */
     if (!qdev_set_parent_bus(dev, &subsys->bus.parent_bus, errp)) {
         return;
@@ -804,6 +839,9 @@ static const Property nvme_ns_props[] = {
     DEFINE_PROP_BOOL("eui64-default", NvmeNamespace, params.eui64_default,
                      false),
     DEFINE_PROP_STRING("fdp.ruhs", NvmeNamespace, params.fdp.ruhs),
+    DEFINE_PROP_UINT16("atomic.nawun", NvmeNamespace, params.atomic_nawun, 0),
+    DEFINE_PROP_UINT16("atomic.nawupf", NvmeNamespace, params.atomic_nawupf, 0),
+    DEFINE_PROP_BOOL("atomic.nsfeat", NvmeNamespace, params.atomic_nsfeat, 0),
 };
 
 static void nvme_ns_class_init(ObjectClass *oc, const void *data)
index 67ed562e0086d88491745a9d961ff57d3c2f3d6d..7d01080fc1f9bfd9eda5b87b6c064b40db8b46c0 100644 (file)
@@ -218,6 +218,9 @@ typedef struct NvmeNamespaceParams {
     struct {
         char *ruhs;
     } fdp;
+    uint16_t atomic_nawun;
+    uint16_t atomic_nawupf;
+    bool     atomic_nsfeat;
 } NvmeNamespaceParams;
 
 typedef struct NvmeAtomic {
@@ -280,6 +283,9 @@ typedef struct NvmeNamespace {
         /* reclaim unit handle identifiers indexed by placement handle */
         uint16_t *phs;
     } fdp;
+    uint16_t  atomic_nawun;
+    uint16_t  atomic_nawupf;
+    NvmeAtomic  atomic;
 } NvmeNamespace;
 
 static inline uint32_t nvme_nsid(NvmeNamespace *ns)