[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];
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;
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));
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);
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,
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);
}
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;
/*
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'"