From: Alan Adamson Date: Mon, 2 Jun 2025 23:04:58 +0000 (-0700) Subject: hw/nvme: add atomic boundary support X-Git-Tag: v10.2.0-rc1~32^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bce51b83709b548fbecbe64acd65225587c5b803;p=thirdparty%2Fqemu.git hw/nvme: add atomic boundary support 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 Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 121a95b2e3..4d150c7206 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 28aacb8db5..86f5ab0a75 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 7d01080fc1..a7d225d2d8 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;