From: Daniel Gomez <[email protected]> NVMe spec defines Multiple Atomicity Mode (MAM) as a superset of Single Atomicity Mode (SAM) where the controller works in either mode. In this mode, a write command that crosses the Namespace Atomic Boundaries is divided at those boundaries into atomic LBA subranges, each written atomically, although the full command is not atomic as a whole.
Add an "atomic.mam" nvme-ns boolean that sets the existing NVME_ID_NS_NSFEAT_MAM bit once the configured geometry satisfies the mode's requirements: NSABP, with a nonzero NABSN/NABSPF and == NAWUN/NAWUPF. This lets a guest use the atomic-write/MAM path with the following emulated NVMe option atomic.mam=on, e.g.: -device nvme,serial=foo,id=nvme0\ -drive file=nvme0.qcow2,if=none,format=qcow2,id=nvme0-ns0\ -device nvme-ns,drive=nvme0-ns0,bus=nvme0,atomic.nawun=7,\ atomic.nawupf=7,atomic.nabsn=7,atomic.nabspf=7,atomic.mam=on Note that for MAM emulation, the atomic boundaries limits are skipped and the whole command is serialized as one atomic write. Also, fix a typo in the error message reported when nabspf and nawupf are checked when setting the Namespace Atomic Boundaries. Signed-off-by: Daniel Gomez <[email protected]> --- hw/nvme/ctrl.c | 7 +++++-- hw/nvme/ns.c | 27 ++++++++++++++++++++++++++- hw/nvme/nvme.h | 1 + 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 3baf57af634..c912a4df8da 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -56,6 +56,7 @@ * zoned.max_active=<N[optional]>, \ * zoned.max_open=<N[optional]>, \ * zoned.cross_read=<true|false[optional]>, \ + * atomic.mam=<on|off[optional]>, \ * atomic.nabo=<N[optional]>, \ * atomic.nabsn=<N[optional]>, \ * atomic.nabspf=<N[optional]>, \ @@ -7830,6 +7831,8 @@ static int nvme_atomic_write_check(NvmeCtrl *n, NvmeCmd *cmd, NvmeAtomic *atomic) { NvmeRwCmd *rw = (NvmeRwCmd *)cmd; + NvmeNamespace *ns = nvme_ns(n, le32_to_cpu(cmd->nsid)); + bool mam = ns && (ns->id_ns.nsfeat & NVME_ID_NS_NSFEAT_MAM); uint64_t slba = le64_to_cpu(rw->slba); uint32_t nlb = (uint32_t)le16_to_cpu(rw->nlb); uint64_t elba = slba + nlb; @@ -7837,14 +7840,14 @@ static int nvme_atomic_write_check(NvmeCtrl *n, NvmeCmd *cmd, int i; if ((cmd->opcode == NVME_CMD_READ) || ((cmd->opcode == NVME_CMD_WRITE) && - ((nlb + 1) > atomic->atomic_max_write_size))) { + !mam && ((nlb + 1) > atomic->atomic_max_write_size))) { cmd_atomic_wr = false; } /* * Check if a write crosses an atomic boundary. */ - if (cmd->opcode == NVME_CMD_WRITE) { + if (cmd->opcode == NVME_CMD_WRITE && !mam) { if (!nvme_atomic_boundary_check(n, cmd, atomic)) { cmd_atomic_wr = false; } diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 7f0f9ac7662..344c16d1aa0 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -769,7 +769,7 @@ static bool nvme_ns_set_nab(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) } if (nabspf && nabspf < le16_to_cpu(id_ns->nawupf)) { - error_setg(errp, "nabspf must be great than or equal to nawupf"); + error_setg(errp, "nabspf must be greater than or equal to nawupf"); return false; } } @@ -787,6 +787,30 @@ static bool nvme_ns_set_nab(NvmeCtrl *n, NvmeNamespace *ns, Error **errp) nvme_ns_atomic_configure_boundary(n->dn, nabsn, nabspf, &ns->atomic); + if (ns->params.atomic.mam) { + uint16_t nawupf = le16_to_cpu(id_ns->nawupf); + uint16_t nawun = le16_to_cpu(id_ns->nawun); + + if (!(id_ns->nsfeat & NVME_ID_NS_NSFEAT_NSABP)) { + error_setg(errp, "atomic.mam requires per-namespace atomic writes"); + return false; + } + + if (!nabsn || !nabspf) { + error_setg(errp, "atomic.mam requires a nonzero atomic.nabsn and " + "atomic.nabspf"); + return false; + } + + if (nabsn != nawun || nabspf != nawupf || nawun != nawupf) { + error_setg(errp, "atomic.mam requires atomic.nabsn == atomic.nawun " + "== atomic.nabspf == atomic.nawupf"); + return false; + } + + id_ns->nsfeat |= NVME_ID_NS_NSFEAT_MAM; + } + return true; } @@ -1096,6 +1120,7 @@ static const Property nvme_ns_props[] = { DEFINE_PROP_UINT16("atomic.nabsn", NvmeNamespace, params.atomic.nabsn, 0), DEFINE_PROP_UINT16("atomic.nabspf", NvmeNamespace, params.atomic.nabspf, 0), DEFINE_PROP_UINT16("atomic.nabo", NvmeNamespace, params.atomic.nabo, 0), + DEFINE_PROP_BOOL("atomic.mam", NvmeNamespace, params.atomic.mam, 0), }; static void nvme_ns_class_init(ObjectClass *oc, const void *data) diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 43e3c916f73..edc84f347b3 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -232,6 +232,7 @@ typedef struct NvmeNamespaceParams { uint16_t nabsn; uint16_t nabspf; uint16_t nabo; + bool mam; } atomic; } NvmeNamespaceParams; -- 2.55.0
