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;
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
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.
*/
} else {
atomic->atomic_writes = 1;
}
+ atomic->atomic_boundary = 0;
+ atomic->atomic_nabo = 0;
}
}
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) {
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 */
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),
};