mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-27 13:30:52 +00:00
hw/block/nvme: add support for the format nvm command
Format NVM admin command can make a namespace or namespaces to be with different LBA size and metadata size with protection information types. This patch introduces Format NVM command with LBA format, Metadata, and Protection Information for the device. The secure erase operation things and support for formatting zoned namespaces are yet to be added. The parameter checks inside of this patch has been referred from Keith's old branch. Signed-off-by: Minwoo Im <minwoo.im@samsung.com> [anaidu.gollu: rebased on e2e] Signed-off-by: Gollu Appalanaidu <anaidu.gollu@samsung.com> [k.jensen: rebased for reworked aio tracking] Signed-off-by: Klaus Jensen <k.jensen@samsung.com> Reviewed-by: Keith Busch <kbusch@kernel.org>
This commit is contained in:
parent
516990f4df
commit
dc04d25e2f
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
175
hw/block/nvme.c
175
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;
|
||||
|
||||
/*
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -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'"
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user