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;