]> www.infradead.org Git - users/sagi/libnvme.git/commitdiff
mi: Add direct admin transfer API
authorJeremy Kerr <jk@codeconstruct.com.au>
Tue, 2 Nov 2021 02:48:09 +0000 (10:48 +0800)
committerJeremy Kerr <jk@codeconstruct.com.au>
Tue, 21 Jun 2022 04:08:02 +0000 (12:08 +0800)
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 <jk@codeconstruct.com.au>
examples/mi-mctp.c
src/libnvme-mi.map
src/nvme/mi.c
src/nvme/mi.h

index 1028c20315c1b778a43bd0cec6d4c77e48a9b93b..d8149ddefbe79b4a1516ab0342de85627e75a60a 100644 (file)
@@ -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 <controller-id> [--partial]\n"
                        "  get-log-page <controller-id> [<log-id>]\n"
+                       "  admin <controller-id> <opcode> [<cdw10>, <cdw11>, ...]\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);
index d9e9097f25cc4cee5958343a60b530ef5fd734a9..94c5b456dd982e37e6890354ba12923ae0720b62 100644 (file)
@@ -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:
                *;
index 9634c9633c199e66244a1d31f021742528660178..40cae1bd33c53720845f6da0ee3ce1ea27177326 100644 (file)
@@ -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)
index 0eb816a8b5aa8a0199a76fc9abb44391987777b8..9b8e881b0d6eb0f35ffd10a412c138be49f0d344 100644 (file)
@@ -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);