qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Klaus Jensen <its@irrelevant.dk>
To: qemu-devel@nongnu.org
Cc: Peter Maydell <peter.maydell@linaro.org>,
	Alan Adamson <alan.adamson@oracle.com>,
	Jesper Wendel Devantier <foss@defmacro.it>,
	Klaus Jensen <k.jensen@samsung.com>,
	Keith Busch <kbusch@kernel.org>, Klaus Jensen <its@irrelevant.dk>,
	qemu-block@nongnu.org
Subject: [PULL 7/7] hw/nvme: add atomic boundary support
Date: Thu, 30 Oct 2025 08:29:55 +0100	[thread overview]
Message-ID: <20251030072956.1194-8-its@irrelevant.dk> (raw)
In-Reply-To: <20251030072956.1194-1-its@irrelevant.dk>

From: Alan Adamson <alan.adamson@oracle.com>

Add support for the namespace atomic boundary paramters: NABO, NABSN, and NABSPF.

Writes that cross an atomic boundary whose size is less than or equal to values
reported by AWUN/AWUPF are guaranteed to be atomic. If AWUN/AWUPF is set to zero,
writes that cross an atomic boundary are not guaranteed to be atomic.

The value reported by NABO field indicates the LBA on this namespace where the
first atomic boundary starts.

New NVMe QEMU Paramters (See NVMe Specification for details):
        atomic.nabo=UINT16 (default: 0)
        atomic.nabsn=UINT16 (default: 0)
        atomic.nabspf=UINT16 (default: 0)

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 | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/nvme/ns.c   | 36 ++++++++++++++++++++++++++++++++++
 hw/nvme/nvme.h |  8 ++++++++
 3 files changed, 97 insertions(+)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 121a95b2e373..4d150c7206ad 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -6711,9 +6711,21 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req)
                 if (n->dn) {
                     ns->atomic.atomic_max_write_size =
                         le16_to_cpu(ns->id_ns.nawupf) + 1;
+                    if (ns->id_ns.nabspf) {
+                        ns->atomic.atomic_boundary =
+                            le16_to_cpu(ns->id_ns.nabspf) + 1;
+                    } else {
+                        ns->atomic.atomic_boundary = 0;
+                    }
                 } else {
                     ns->atomic.atomic_max_write_size =
                         le16_to_cpu(ns->id_ns.nawun) + 1;
+                    if (ns->id_ns.nabsn) {
+                        ns->atomic.atomic_boundary =
+                            le16_to_cpu(ns->id_ns.nabsn) + 1;
+                    } else {
+                        ns->atomic.atomic_boundary = 0;
+                    }
                 }
                 if (ns->atomic.atomic_max_write_size == 1) {
                     ns->atomic.atomic_writes = 0;
@@ -7636,6 +7648,36 @@ static void nvme_update_sq_tail(NvmeSQueue *sq)
     trace_pci_nvme_update_sq_tail(sq->sqid, sq->tail);
 }
 
+static int nvme_atomic_boundary_check(NvmeCtrl *n, NvmeCmd *cmd,
+    NvmeAtomic *atomic)
+{
+    NvmeRwCmd *rw = (NvmeRwCmd *)cmd;
+
+    if (atomic->atomic_boundary) {
+        uint64_t slba = le64_to_cpu(rw->slba);
+        uint32_t nlb = (uint32_t)le16_to_cpu(rw->nlb);
+        uint64_t elba = slba + nlb;
+        uint64_t imask;
+
+        if ((slba < atomic->atomic_nabo) || (elba < atomic->atomic_nabo)) {
+            return 0;
+        }
+
+        /* Update slba/elba based on boundary offset */
+        slba = slba - atomic->atomic_nabo;
+        elba = slba + nlb;
+
+        imask = ~(atomic->atomic_boundary - 1);
+        if ((slba & imask) != (elba & imask)) {
+            if (n->atomic.atomic_max_write_size &&
+                ((nlb + 1) <= n->atomic.atomic_max_write_size)) {
+                return 1;
+            }
+            return 0;
+        }
+    }
+    return 1;
+}
 #define NVME_ATOMIC_NO_START        0
 #define NVME_ATOMIC_START_ATOMIC    1
 #define NVME_ATOMIC_START_NONATOMIC 2
@@ -7655,6 +7697,15 @@ static int nvme_atomic_write_check(NvmeCtrl *n, NvmeCmd *cmd,
         cmd_atomic_wr = false;
     }
 
+    /*
+     * Check if a write crosses an atomic boundary.
+     */
+    if (cmd->opcode == NVME_CMD_WRITE) {
+        if (!nvme_atomic_boundary_check(n, cmd, atomic)) {
+            cmd_atomic_wr = false;
+        }
+    }
+
     /*
      * Walk the queues to see if there are any atomic conflicts.
      */
@@ -8741,6 +8792,8 @@ static void nvme_init_state(NvmeCtrl *n)
         } else {
             atomic->atomic_writes = 1;
         }
+        atomic->atomic_boundary = 0;
+        atomic->atomic_nabo = 0;
     }
 }
 
diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c
index 28aacb8db59a..86f5ab0a7572 100644
--- a/hw/nvme/ns.c
+++ b/hw/nvme/ns.c
@@ -747,6 +747,28 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
             error_report("Invalid: NAWUN=%x NAWUPF=%x",
                 id_ns->nawun, id_ns->nawupf);
         }
+        id_ns->nabsn = cpu_to_le16(ns->params.atomic_nabsn);
+        id_ns->nabspf = cpu_to_le16(ns->params.atomic_nabspf);
+        id_ns->nabo = cpu_to_le16(ns->params.atomic_nabo);
+        if (!id->awun || (id_ns->nabsn && ((id_ns->nabsn < id_ns->nawun) ||
+            (id_ns->nabsn < id->awun)))) {
+            error_report("Invalid NABSN: %x NAWUN=%x AWUN=%x",
+                id_ns->nabsn, id_ns->nawun, id->awun);
+        }
+        if (!id->awupf || (id_ns->nabspf && ((id_ns->nabspf < id_ns->nawupf) ||
+            (id_ns->nawupf < id->awupf)))) {
+            error_report("Invalid NABSPF: %x NAWUPF=%x AWUPF=%x",
+                id_ns->nabspf, id_ns->nawupf, id->awupf);
+        }
+        if (id_ns->nabo && ((id_ns->nabo > id_ns->nabsn) ||
+            (id_ns->nabo > id_ns->nabspf))) {
+            error_report("Invalid NABO: %x NABSN=%x NABSPF=%x",
+                id_ns->nabo, id_ns->nabsn, id_ns->nabspf);
+        }
+        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) {
@@ -754,14 +776,25 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
 
         if (n->dn) {
             atomic->atomic_max_write_size = cpu_to_le16(id_ns->nawupf) + 1;
+            if (id_ns->nabspf) {
+                atomic->atomic_boundary = cpu_to_le16(id_ns->nabspf) + 1;
+            } else {
+                atomic->atomic_boundary = 0;
+            }
         } else {
             atomic->atomic_max_write_size = cpu_to_le16(id_ns->nawun) + 1;
+            if (id_ns->nabsn) {
+                atomic->atomic_boundary = cpu_to_le16(id_ns->nabsn) + 1;
+            } else {
+                atomic->atomic_boundary = 0;
+            }
         }
         if (atomic->atomic_max_write_size == 1) {
             atomic->atomic_writes = 0;
         } else {
             atomic->atomic_writes = 1;
         }
+        atomic->atomic_nabo = cpu_to_le16(id_ns->nabo);
     }
 
     /* reparent to subsystem bus */
@@ -841,6 +874,9 @@ static const Property nvme_ns_props[] = {
     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_UINT16("atomic.nabspf", NvmeNamespace, params.atomic_nabspf, 0),
+    DEFINE_PROP_UINT16("atomic.nabsn", NvmeNamespace, params.atomic_nabsn, 0),
+    DEFINE_PROP_UINT16("atomic.nabo", NvmeNamespace, params.atomic_nabo, 0),
     DEFINE_PROP_BOOL("atomic.nsfeat", NvmeNamespace, params.atomic_nsfeat, 0),
 };
 
diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h
index 7d01080fc1f9..a7d225d2d80b 100644
--- a/hw/nvme/nvme.h
+++ b/hw/nvme/nvme.h
@@ -220,11 +220,16 @@ typedef struct NvmeNamespaceParams {
     } fdp;
     uint16_t atomic_nawun;
     uint16_t atomic_nawupf;
+    uint16_t atomic_nabsn;
+    uint16_t atomic_nabspf;
+    uint16_t atomic_nabo;
     bool     atomic_nsfeat;
 } NvmeNamespaceParams;
 
 typedef struct NvmeAtomic {
     uint32_t    atomic_max_write_size;
+    uint64_t    atomic_boundary;
+    uint64_t    atomic_nabo;
     bool        atomic_writes;
 } NvmeAtomic;
 
@@ -285,6 +290,9 @@ typedef struct NvmeNamespace {
     } fdp;
     uint16_t  atomic_nawun;
     uint16_t  atomic_nawupf;
+    uint16_t  atomic_nabsn;
+    uint16_t  atomic_nabspf;
+    uint16_t  atomic_nabo;
     NvmeAtomic  atomic;
 } NvmeNamespace;
 
-- 
2.51.0



  parent reply	other threads:[~2025-10-30  7:31 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-30  7:29 [PULL 0/7] nvme queue Klaus Jensen
2025-10-30  7:29 ` [PULL 1/7] spdm-socket: add seperate send/recv functions Klaus Jensen
2025-10-30  7:29 ` [PULL 2/7] spdm: add spdm storage transport virtual header Klaus Jensen
2025-10-30  7:29 ` [PULL 3/7] hw/nvme: add NVMe Admin Security SPDM support Klaus Jensen
2025-10-30  7:29 ` [PULL 4/7] spdm: define SPDM transport enum types Klaus Jensen
2025-10-30  7:29 ` [PULL 5/7] hw/nvme: connect SPDM over NVMe Security Send/Recv Klaus Jensen
2025-10-30  7:29 ` [PULL 6/7] hw/nvme: enable ns atomic writes Klaus Jensen
2025-11-02 11:50   ` Peter Maydell
2025-11-03 12:50     ` Klaus Jensen
2025-10-30  7:29 ` Klaus Jensen [this message]
2025-11-01  8:36 ` [PULL 0/7] nvme queue Richard Henderson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251030072956.1194-8-its@irrelevant.dk \
    --to=its@irrelevant.dk \
    --cc=alan.adamson@oracle.com \
    --cc=foss@defmacro.it \
    --cc=k.jensen@samsung.com \
    --cc=kbusch@kernel.org \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).