From 84707e77c1bb0b09e2ce7150a68b619256ac4725 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Tue, 19 Jul 2022 17:47:04 +0800 Subject: [PATCH] mi: Implement Format NVM command Add support for the Format NVM command, using the existing struct nvme_format_nvm_args, plus a small test. Signed-off-by: Jeremy Kerr --- src/libnvme-mi.map | 1 + src/nvme/mi.c | 40 ++++++++++++++++++++++++ src/nvme/mi.h | 13 ++++++++ test/mi.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+) diff --git a/src/libnvme-mi.map b/src/libnvme-mi.map index d20766a0..42b7ada8 100644 --- a/src/libnvme-mi.map +++ b/src/libnvme-mi.map @@ -18,6 +18,7 @@ LIBNVME_MI_1_1 { nvme_mi_admin_set_features; nvme_mi_admin_ns_mgmt; nvme_mi_admin_ns_attach; + nvme_mi_admin_format_nvm; nvme_mi_admin_xfer; nvme_mi_admin_security_send; nvme_mi_admin_security_recv; diff --git a/src/nvme/mi.c b/src/nvme/mi.c index 5440f7e5..7a3fd852 100644 --- a/src/nvme/mi.c +++ b/src/nvme/mi.c @@ -810,6 +810,46 @@ int nvme_mi_admin_ns_attach(nvme_mi_ctrl_t ctrl, return 0; } +int nvme_mi_admin_format_nvm(nvme_mi_ctrl_t ctrl, + struct nvme_format_nvm_args *args) +{ + struct nvme_mi_admin_resp_hdr resp_hdr; + struct nvme_mi_admin_req_hdr req_hdr; + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (args->args_size < sizeof(*args)) + return -EINVAL; + + nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, + nvme_admin_format_nvm); + + req_hdr.cdw1 = cpu_to_le32(args->nsid); + req_hdr.cdw10 = cpu_to_le32(((args->lbafu & 0x3) << 12) + | ((args->ses & 0x7) << 9) + | ((args->pil & 0x1) << 8) + | ((args->pi & 0x7) << 5) + | ((args->mset & 0x1) << 4) + | ((args->lbaf & 0xf) << 0)); + + nvme_mi_calc_req_mic(&req); + + nvme_mi_admin_init_resp(&resp, &resp_hdr); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + if (resp_hdr.status) + return resp_hdr.status; + + if (args->result) + *args->result = le32_to_cpu(resp_hdr.cdw0); + + return 0; +} + static int nvme_mi_read_data(nvme_mi_ep_t ep, __u32 cdw0, void *data, size_t *data_len) { diff --git a/src/nvme/mi.h b/src/nvme/mi.h index a0e63942..9a2b68e1 100644 --- a/src/nvme/mi.h +++ b/src/nvme/mi.h @@ -1496,4 +1496,17 @@ static inline int nvme_mi_admin_ns_detach_ctrls(nvme_mi_ctrl_t ctrl, __u32 nsid, return nvme_mi_admin_ns_attach(ctrl, &args); } +/** + * nvme_mi_admin_format_nvm() - Format NVMe namespace + * @ctrl: Controller to send command to + * @args: Format NVM command arguments + * + * Perform a low-level format to set the LBA data & metadata size. May destroy + * data & metadata on the specified namespaces + * + * Return: 0 on success, non-zero on failure + */ +int nvme_mi_admin_format_nvm(nvme_mi_ctrl_t ctrl, + struct nvme_format_nvm_args *args); + #endif /* _LIBNVME_MI_MI_H */ diff --git a/test/mi.c b/test/mi.c index aecbb566..bf4fb07e 100644 --- a/test/mi.c +++ b/test/mi.c @@ -1350,6 +1350,83 @@ static void test_admin_ns_detach(struct nvme_mi_ep *ep) assert(!rc); } +struct format_data { + __u32 nsid; + __u8 lbafu; + __u8 ses; + __u8 pil; + __u8 pi; + __u8 mset; + __u8 lbafl; +}; + +static int test_admin_format_nvm_cb(struct nvme_mi_ep *ep, + struct nvme_mi_req *req, + struct nvme_mi_resp *resp, + void *data) +{ + struct nvme_format_nvm_args *args = data; + __u8 *rq_hdr; + __u32 nsid; + + assert(req->data_len == 0); + + rq_hdr = (__u8 *)req->hdr; + + assert(rq_hdr[4] == nvme_admin_format_nvm); + + nsid = rq_hdr[11] << 24 | rq_hdr[10] << 16 | rq_hdr[9] << 8 | rq_hdr[8]; + assert(nsid == args->nsid); + + assert(((rq_hdr[44] >> 0) & 0xf) == args->lbaf); + assert(((rq_hdr[44] >> 4) & 0x1) == args->mset); + assert(((rq_hdr[44] >> 5) & 0x7) == args->pi); + + assert(((rq_hdr[45] >> 0) & 0x1) == args->pil); + assert(((rq_hdr[45] >> 1) & 0x7) == args->ses); + assert(((rq_hdr[45] >> 4) & 0x3) == args->lbafu); + + test_transport_resp_calc_mic(resp); + + return 0; +} + +static void test_admin_format_nvm(struct nvme_mi_ep *ep) +{ + struct nvme_format_nvm_args args = { 0 }; + nvme_mi_ctrl_t ctrl; + int rc; + + ctrl = nvme_mi_init_ctrl(ep, 5); + assert(ctrl); + + test_set_transport_callback(ep, test_admin_format_nvm_cb, &args); + + /* ensure we have the cdw0 bit field encoding correct, by testing twice + * with inverted bit values */ + args.args_size = sizeof(args); + args.nsid = 0x04030201; + args.lbafu = 0x3; + args.ses = 0x0; + args.pil = 0x1; + args.pi = 0x0; + args.mset = 0x1; + args.lbaf = 0x0; + + rc = nvme_mi_admin_format_nvm(ctrl, &args); + assert(!rc); + + args.nsid = ~args.nsid; + args.lbafu = 0; + args.ses = 0x7; + args.pil = 0x0; + args.pi = 0x7; + args.mset = 0x0; + args.lbaf = 0xf; + + rc = nvme_mi_admin_format_nvm(ctrl, &args); + assert(!rc); +} #define DEFINE_TEST(name) { #name, test_ ## name } struct test { @@ -1385,6 +1462,7 @@ struct test { DEFINE_TEST(admin_ns_mgmt_delete), DEFINE_TEST(admin_ns_attach), DEFINE_TEST(admin_ns_detach), + DEFINE_TEST(admin_format_nvm), }; static void run_test(struct test *test, FILE *logfd, nvme_mi_ep_t ep) -- 2.50.1