summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hw/block/nvme-ns.c3
-rw-r--r--hw/block/nvme-ns.h7
-rw-r--r--hw/block/nvme.c175
-rw-r--r--hw/block/nvme.h1
-rw-r--r--hw/block/trace-events3
-rw-r--r--include/block/nvme.h1
6 files changed, 188 insertions, 2 deletions
diff --git a/hw/block/nvme-ns.c b/hw/block/nvme-ns.c
index 1e8ef36ba5..7f8d139a86 100644
--- a/hw/block/nvme-ns.c
+++ b/hw/block/nvme-ns.c
@@ -32,7 +32,7 @@
#define MIN_DISCARD_GRANULARITY (4 * KiB)
-static void nvme_ns_init_format(NvmeNamespace *ns)
+void nvme_ns_init_format(NvmeNamespace *ns)
{
NvmeIdNs *id_ns = &ns->id_ns;
BlockDriverInfo bdi;
@@ -66,6 +66,7 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp)
int i;
ns->csi = NVME_CSI_NVM;
+ ns->status = 0x0;
ns->id_ns.dlfeat = 0x1;
diff --git a/hw/block/nvme-ns.h b/hw/block/nvme-ns.h
index 07e1688080..9ab7894fc8 100644
--- a/hw/block/nvme-ns.h
+++ b/hw/block/nvme-ns.h
@@ -59,6 +59,7 @@ typedef struct NvmeNamespace {
NvmeIdNs id_ns;
const uint32_t *iocs;
uint8_t csi;
+ uint16_t status;
NvmeSubsystem *subsys;
QTAILQ_ENTRY(NvmeNamespace) entry;
@@ -84,6 +85,11 @@ typedef struct NvmeNamespace {
} features;
} NvmeNamespace;
+static inline uint16_t nvme_ns_status(NvmeNamespace *ns)
+{
+ return ns->status;
+}
+
static inline uint32_t nvme_nsid(NvmeNamespace *ns)
{
if (ns) {
@@ -218,6 +224,7 @@ static inline void nvme_aor_dec_active(NvmeNamespace *ns)
assert(ns->nr_active_zones >= 0);
}
+void nvme_ns_init_format(NvmeNamespace *ns);
int nvme_ns_setup(NvmeNamespace *ns, Error **errp);
void nvme_ns_drain(NvmeNamespace *ns);
void nvme_ns_shutdown(NvmeNamespace *ns);
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index 64cb966ab6..6842b01ab5 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -213,6 +213,7 @@ static const uint32_t nvme_cse_acs[256] = {
[NVME_ADM_CMD_GET_FEATURES] = NVME_CMD_EFF_CSUPP,
[NVME_ADM_CMD_ASYNC_EV_REQ] = NVME_CMD_EFF_CSUPP,
[NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC,
+ [NVME_ADM_CMD_FORMAT_NVM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC,
};
static const uint32_t nvme_cse_iocs_none[256];
@@ -1866,6 +1867,42 @@ out:
nvme_rw_complete_cb(req, ret);
}
+struct nvme_aio_format_ctx {
+ NvmeRequest *req;
+ NvmeNamespace *ns;
+
+ /* number of outstanding write zeroes for this namespace */
+ int *count;
+};
+
+static void nvme_aio_format_cb(void *opaque, int ret)
+{
+ struct nvme_aio_format_ctx *ctx = opaque;
+ NvmeRequest *req = ctx->req;
+ NvmeNamespace *ns = ctx->ns;
+ uintptr_t *num_formats = (uintptr_t *)&req->opaque;
+ int *count = ctx->count;
+
+ g_free(ctx);
+
+ if (ret) {
+ nvme_aio_err(req, ret);
+ }
+
+ if (--(*count)) {
+ return;
+ }
+
+ g_free(count);
+ ns->status = 0x0;
+
+ if (--(*num_formats)) {
+ return;
+ }
+
+ nvme_enqueue_req_completion(nvme_cq(req), req);
+}
+
struct nvme_aio_flush_ctx {
NvmeRequest *req;
NvmeNamespace *ns;
@@ -3556,6 +3593,7 @@ static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req)
static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req)
{
uint32_t nsid = le32_to_cpu(req->cmd.nsid);
+ uint16_t status;
trace_pci_nvme_io_cmd(nvme_cid(req), nsid, nvme_sqid(req),
req->cmd.opcode, nvme_io_opc_str(req->cmd.opcode));
@@ -3597,6 +3635,11 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req)
return NVME_INVALID_OPCODE | NVME_DNR;
}
+ status = nvme_ns_status(req->ns);
+ if (unlikely(status)) {
+ return status;
+ }
+
switch (req->cmd.opcode) {
case NVME_CMD_WRITE_ZEROES:
return nvme_write_zeroes(n, req);
@@ -4898,6 +4941,134 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req)
return NVME_SUCCESS;
}
+static uint16_t nvme_format_ns(NvmeCtrl *n, NvmeNamespace *ns, uint8_t lbaf,
+ uint8_t mset, uint8_t pi, uint8_t pil,
+ NvmeRequest *req)
+{
+ int64_t len, offset;
+ struct nvme_aio_format_ctx *ctx;
+ BlockBackend *blk = ns->blkconf.blk;
+ uint16_t ms;
+ uintptr_t *num_formats = (uintptr_t *)&req->opaque;
+ int *count;
+
+ if (ns->params.zoned) {
+ return NVME_INVALID_FORMAT | NVME_DNR;
+ }
+
+ trace_pci_nvme_format_ns(nvme_cid(req), nvme_nsid(ns), lbaf, mset, pi, pil);
+
+ if (lbaf > ns->id_ns.nlbaf) {
+ return NVME_INVALID_FORMAT | NVME_DNR;
+ }
+
+ ms = ns->id_ns.lbaf[lbaf].ms;
+
+ if (pi && (ms < sizeof(NvmeDifTuple))) {
+ return NVME_INVALID_FORMAT | NVME_DNR;
+ }
+
+ if (pi && pi > NVME_ID_NS_DPS_TYPE_3) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+
+ nvme_ns_drain(ns);
+ nvme_ns_shutdown(ns);
+ nvme_ns_cleanup(ns);
+
+ ns->id_ns.dps = (pil << 3) | pi;
+ ns->id_ns.flbas = lbaf | (mset << 4);
+
+ nvme_ns_init_format(ns);
+
+ ns->status = NVME_FORMAT_IN_PROGRESS;
+
+ len = ns->size;
+ offset = 0;
+
+ count = g_new(int, 1);
+ *count = 1;
+
+ (*num_formats)++;
+
+ while (len) {
+ ctx = g_new(struct nvme_aio_format_ctx, 1);
+ ctx->req = req;
+ ctx->ns = ns;
+ ctx->count = count;
+
+ size_t bytes = MIN(BDRV_REQUEST_MAX_BYTES, len);
+
+ (*count)++;
+
+ blk_aio_pwrite_zeroes(blk, offset, bytes, BDRV_REQ_MAY_UNMAP,
+ nvme_aio_format_cb, ctx);
+
+ offset += bytes;
+ len -= bytes;
+
+ }
+
+ (*count)--;
+
+ return NVME_NO_COMPLETE;
+}
+
+static uint16_t nvme_format(NvmeCtrl *n, NvmeRequest *req)
+{
+ NvmeNamespace *ns;
+ uint32_t dw10 = le32_to_cpu(req->cmd.cdw10);
+ uint32_t nsid = le32_to_cpu(req->cmd.nsid);
+ uint8_t lbaf = dw10 & 0xf;
+ uint8_t mset = (dw10 >> 4) & 0x1;
+ uint8_t pi = (dw10 >> 5) & 0x7;
+ uint8_t pil = (dw10 >> 8) & 0x1;
+ uintptr_t *num_formats = (uintptr_t *)&req->opaque;
+ uint16_t status;
+ int i;
+
+ trace_pci_nvme_format(nvme_cid(req), nsid, lbaf, mset, pi, pil);
+
+ /* 1-initialize; see the comment in nvme_dsm */
+ *num_formats = 1;
+
+ if (nsid != NVME_NSID_BROADCAST) {
+ if (!nvme_nsid_valid(n, nsid)) {
+ return NVME_INVALID_NSID | NVME_DNR;
+ }
+
+ ns = nvme_ns(n, nsid);
+ if (!ns) {
+ return NVME_INVALID_FIELD | NVME_DNR;
+ }
+
+ status = nvme_format_ns(n, ns, lbaf, mset, pi, pil, req);
+ if (status && status != NVME_NO_COMPLETE) {
+ req->status = status;
+ }
+ } else {
+ for (i = 1; i <= n->num_namespaces; i++) {
+ ns = nvme_ns(n, i);
+ if (!ns) {
+ continue;
+ }
+
+ status = nvme_format_ns(n, ns, lbaf, mset, pi, pil, req);
+ if (status && status != NVME_NO_COMPLETE) {
+ req->status = status;
+ break;
+ }
+ }
+ }
+
+ /* account for the 1-initialization */
+ if (--(*num_formats)) {
+ return NVME_NO_COMPLETE;
+ }
+
+ return req->status;
+}
+
static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req)
{
trace_pci_nvme_admin_cmd(nvme_cid(req), nvme_sqid(req), req->cmd.opcode,
@@ -4936,6 +5107,8 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req)
return nvme_aer(n, req);
case NVME_ADM_CMD_NS_ATTACHMENT:
return nvme_ns_attachment(n, req);
+ case NVME_ADM_CMD_FORMAT_NVM:
+ return nvme_format(n, req);
default:
assert(false);
}
@@ -5912,7 +6085,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev)
id->mdts = n->params.mdts;
id->ver = cpu_to_le32(NVME_SPEC_VER);
- id->oacs = cpu_to_le16(NVME_OACS_NS_MGMT);
+ id->oacs = cpu_to_le16(NVME_OACS_NS_MGMT | NVME_OACS_FORMAT);
id->cntrltype = 0x1;
/*
diff --git a/hw/block/nvme.h b/hw/block/nvme.h
index 7b082212db..5b0031b11d 100644
--- a/hw/block/nvme.h
+++ b/hw/block/nvme.h
@@ -86,6 +86,7 @@ static inline const char *nvme_adm_opc_str(uint8_t opc)
case NVME_ADM_CMD_SET_FEATURES: return "NVME_ADM_CMD_SET_FEATURES";
case NVME_ADM_CMD_GET_FEATURES: return "NVME_ADM_CMD_GET_FEATURES";
case NVME_ADM_CMD_ASYNC_EV_REQ: return "NVME_ADM_CMD_ASYNC_EV_REQ";
+ case NVME_ADM_CMD_FORMAT_NVM: return "NVME_ADM_CMD_FORMAT_NVM";
default: return "NVME_ADM_CMD_UNKNOWN";
}
}
diff --git a/hw/block/trace-events b/hw/block/trace-events
index 72114a5946..b71cf7a087 100644
--- a/hw/block/trace-events
+++ b/hw/block/trace-events
@@ -41,6 +41,9 @@ pci_nvme_map_sgl(uint8_t typ, uint64_t len) "type 0x%"PRIx8" len %"PRIu64""
pci_nvme_io_cmd(uint16_t cid, uint32_t nsid, uint16_t sqid, uint8_t opcode, const char *opname) "cid %"PRIu16" nsid %"PRIu32" sqid %"PRIu16" opc 0x%"PRIx8" opname '%s'"
pci_nvme_admin_cmd(uint16_t cid, uint16_t sqid, uint8_t opcode, const char *opname) "cid %"PRIu16" sqid %"PRIu16" opc 0x%"PRIx8" opname '%s'"
pci_nvme_flush(uint16_t cid, uint32_t nsid) "cid %"PRIu16" nsid %"PRIu32""
+pci_nvme_format(uint16_t cid, uint32_t nsid, uint8_t lbaf, uint8_t mset, uint8_t pi, uint8_t pil) "cid %"PRIu16" nsid %"PRIu32" lbaf %"PRIu8" mset %"PRIu8" pi %"PRIu8" pil %"PRIu8""
+pci_nvme_format_ns(uint16_t cid, uint32_t nsid, uint8_t lbaf, uint8_t mset, uint8_t pi, uint8_t pil) "cid %"PRIu16" nsid %"PRIu32" lbaf %"PRIu8" mset %"PRIu8" pi %"PRIu8" pil %"PRIu8""
+pci_nvme_format_cb(uint16_t cid, uint32_t nsid) "cid %"PRIu16" nsid %"PRIu32""
pci_nvme_read(uint16_t cid, uint32_t nsid, uint32_t nlb, uint64_t count, uint64_t lba) "cid %"PRIu16" nsid %"PRIu32" nlb %"PRIu32" count %"PRIu64" lba 0x%"PRIx64""
pci_nvme_write(uint16_t cid, const char *verb, uint32_t nsid, uint32_t nlb, uint64_t count, uint64_t lba) "cid %"PRIu16" opname '%s' nsid %"PRIu32" nlb %"PRIu32" count %"PRIu64" lba 0x%"PRIx64""
pci_nvme_rw_cb(uint16_t cid, const char *blkname) "cid %"PRIu16" blk '%s'"
diff --git a/include/block/nvme.h b/include/block/nvme.h
index ba757b32db..b0a4e42916 100644
--- a/include/block/nvme.h
+++ b/include/block/nvme.h
@@ -828,6 +828,7 @@ enum NvmeStatusCodes {
NVME_CAP_EXCEEDED = 0x0081,
NVME_NS_NOT_READY = 0x0082,
NVME_NS_RESV_CONFLICT = 0x0083,
+ NVME_FORMAT_IN_PROGRESS = 0x0084,
NVME_INVALID_CQID = 0x0100,
NVME_INVALID_QID = 0x0101,
NVME_MAX_QSIZE_EXCEEDED = 0x0102,