From de534bce9806b23b809ff8c6802eefad26d38cc7 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Tue, 2 Nov 2021 10:48:09 +0800 Subject: [PATCH] mi: Add direct admin transfer API Callers may want to invoke arbitrary admin commands; so implement a direct admin API, passing just the Admin request/response header pointers: int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, struct nvme_mi_admin_req_hdr *admin_req, size_t req_data_size, struct nvme_mi_admin_resp_hdr *admin_resp, off_t resp_data_offset, size_t *resp_data_size); Signed-off-by: Jeremy Kerr --- examples/mi-mctp.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++ src/libnvme-mi.map | 1 + src/nvme/mi.c | 47 ++++++++++++++++++++++ src/nvme/mi.h | 12 ++++++ 4 files changed, 159 insertions(+) diff --git a/examples/mi-mctp.c b/examples/mi-mctp.c index 1028c203..d8149dde 100644 --- a/examples/mi-mctp.c +++ b/examples/mi-mctp.c @@ -323,11 +323,104 @@ int do_get_log_page(nvme_mi_ep_t ep, int argc, char **argv) return 0; } +int do_admin_raw(nvme_mi_ep_t ep, int argc, char **argv) +{ + struct nvme_mi_admin_req_hdr req; + struct nvme_mi_admin_resp_hdr *resp; + struct nvme_mi_ctrl *ctrl; + size_t resp_data_len; + unsigned long tmp; + uint8_t buf[512]; + uint16_t ctrl_id; + uint8_t opcode; + __le32 *cdw; + int i, rc; + + if (argc < 2) { + fprintf(stderr, "no controller ID specified\n"); + return -1; + } + + if (argc < 3) { + fprintf(stderr, "no opcode specified\n"); + return -1; + } + + tmp = atoi(argv[1]); + if (tmp < 0 || tmp > 0xffff) { + fprintf(stderr, "invalid controller ID\n"); + return -1; + } + ctrl_id = tmp & 0xffff; + + tmp = atoi(argv[2]); + if (tmp < 0 || tmp > 0xff) { + fprintf(stderr, "invalid opcode\n"); + return -1; + } + opcode = tmp & 0xff; + + memset(&req, 0, sizeof(req)); + req.opcode = opcode; + req.ctrl_id = cpu_to_le16(ctrl_id); + + /* The cdw10 - cdw16 fields are contiguous in req; set from argv. */ + cdw = (void *)&req + offsetof(typeof(req), cdw10); + for (i = 0; i < 6; i++) { + if (argc >= 4 + i) + tmp = strtoul(argv[3 + i], NULL, 0); + else + tmp = 0; + *cdw = cpu_to_le32(tmp & 0xffffffff); + cdw++; + } + + printf("Admin request:\n"); + printf(" opcode: 0x%02x\n", req.opcode); + printf(" ctrl: 0x%04x\n", le16_to_cpu(req.ctrl_id)); + printf(" cdw10: 0x%08x\n", le32_to_cpu(req.cdw10)); + printf(" cdw11: 0x%08x\n", le32_to_cpu(req.cdw11)); + printf(" cdw12: 0x%08x\n", le32_to_cpu(req.cdw12)); + printf(" cdw13: 0x%08x\n", le32_to_cpu(req.cdw13)); + printf(" cdw14: 0x%08x\n", le32_to_cpu(req.cdw14)); + printf(" cdw15: 0x%08x\n", le32_to_cpu(req.cdw15)); + printf(" raw:\n"); + hexdump((void *)&req, sizeof(req)); + + memset(buf, 0, sizeof(buf)); + resp = (void *)buf; + + ctrl = nvme_mi_init_ctrl(ep, ctrl_id); + if (!ctrl) { + warn("can't create controller"); + return -1; + } + + resp_data_len = sizeof(buf) - sizeof(*resp); + + rc = nvme_mi_admin_xfer(ctrl, &req, 0, resp, 0, &resp_data_len); + if (rc) { + warn("nvme_admin_xfer failed: %d", rc); + return -1; + } + + printf("Admin response:\n"); + printf(" Status: 0x%02x\n", resp->status); + printf(" cdw0: 0x%08x\n", le32_to_cpu(resp->cdw0)); + printf(" cdw1: 0x%08x\n", le32_to_cpu(resp->cdw1)); + printf(" cdw3: 0x%08x\n", le32_to_cpu(resp->cdw3)); + printf(" data [%zd bytes]\n", resp_data_len); + + hexdump(buf + sizeof(*resp), resp_data_len); + return 0; +} + enum action { ACTION_INFO, ACTION_CONTROLLERS, ACTION_IDENTIFY, ACTION_GET_LOG_PAGE, + ACTION_ADMIN_RAW, }; int main(int argc, char **argv) @@ -347,6 +440,7 @@ int main(int argc, char **argv) " controllers\n" " identify [--partial]\n" " get-log-page []\n" + " admin [, , ...]\n" ); return EXIT_FAILURE; } @@ -371,6 +465,8 @@ int main(int argc, char **argv) action = ACTION_IDENTIFY; } else if (!strcmp(action_str, "get-log-page")) { action = ACTION_GET_LOG_PAGE; + } else if (!strcmp(action_str, "admin")) { + action = ACTION_ADMIN_RAW; } else { fprintf(stderr, "invalid action '%s'\n", action_str); return EXIT_FAILURE; @@ -398,6 +494,9 @@ int main(int argc, char **argv) case ACTION_GET_LOG_PAGE: rc = do_get_log_page(ep, argc, argv); break; + case ACTION_ADMIN_RAW: + rc = do_admin_raw(ep, argc, argv); + break; } nvme_mi_close(ep); diff --git a/src/libnvme-mi.map b/src/libnvme-mi.map index d9e9097f..94c5b456 100644 --- a/src/libnvme-mi.map +++ b/src/libnvme-mi.map @@ -12,6 +12,7 @@ LIBNVME_MI_1_1 { nvme_mi_mi_subsystem_health_status_poll; nvme_mi_admin_identify_partial; nvme_mi_admin_get_log_page; + nvme_mi_admin_xfer; nvme_mi_open_mctp; local: *; diff --git a/src/nvme/mi.c b/src/nvme/mi.c index 9634c963..40cae1bd 100644 --- a/src/nvme/mi.c +++ b/src/nvme/mi.c @@ -146,6 +146,53 @@ static void nvme_mi_admin_init_resp(struct nvme_mi_resp *resp, resp->hdr_len = sizeof(*hdr); } +int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, + struct nvme_mi_admin_req_hdr *admin_req, + size_t req_data_size, + struct nvme_mi_admin_resp_hdr *admin_resp, + off_t resp_data_offset, + size_t *resp_data_size) +{ + struct nvme_mi_resp resp; + struct nvme_mi_req req; + int rc; + + if (*resp_data_size > 0xffffffff) + return -EINVAL; + if (resp_data_offset > 0xffffffff) + return -EINVAL; + + admin_req->hdr.type = NVME_MI_MSGTYPE_NVME; + admin_req->hdr.nmp = (NVME_MI_ROR_REQ << 7) | + (NVME_MI_MT_ADMIN << 3); + memset(&req, 0, sizeof(req)); + req.hdr = &admin_req->hdr; + req.hdr_len = sizeof(*admin_req); + req.data = admin_req + 1; + req.data_len = req_data_size; + + nvme_mi_calc_req_mic(&req); + + memset(&resp, 0, sizeof(resp)); + resp.hdr = &admin_resp->hdr; + resp.hdr_len = sizeof(*admin_resp); + resp.data = admin_resp + 1; + resp.data_len = *resp_data_size; + + /* limit the response size, specify offset */ + admin_req->flags = 0x3; + admin_req->dlen = cpu_to_le32(resp.data_len & 0xffffffff); + admin_req->doff = cpu_to_le32(resp_data_offset & 0xffffffff); + + rc = nvme_mi_submit(ctrl->ep, &req, &resp); + if (rc) + return rc; + + *resp_data_size = resp.data_len; + + return 0; +} + int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl, struct nvme_identify_args *args, off_t offset, size_t size) diff --git a/src/nvme/mi.h b/src/nvme/mi.h index 0eb816a8..9b8e881b 100644 --- a/src/nvme/mi.h +++ b/src/nvme/mi.h @@ -124,6 +124,18 @@ int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear, struct nvme_mi_nvm_ss_health_status *nshds); /* Admin channel functions */ + +/* "raw" admin transfer. req_data_size and resp_data_size are the sizes of + * the data portion of the payload, so do not include the length of + * the header, and start at 0 for no payload. + */ +int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl, + struct nvme_mi_admin_req_hdr *admin_req, + size_t req_data_size, + struct nvme_mi_admin_resp_hdr *admin_resp, + off_t resp_data_offset, + size_t *resp_data_size); + int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl, struct nvme_identify_args *args, off_t offset, size_t size); -- 2.50.1