]> www.infradead.org Git - users/sagi/libnvme.git/commitdiff
mi: Add Admin channel Get Log Page command
authorJeremy Kerr <jk@codeconstruct.com.au>
Tue, 26 Oct 2021 08:31:14 +0000 (16:31 +0800)
committerJeremy Kerr <jk@codeconstruct.com.au>
Tue, 21 Jun 2022 03:52:55 +0000 (11:52 +0800)
This change add support for the Get Log Page command over the MI Admin
channel:

  int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl,
 struct nvme_get_log_args *args)

Since the log data can be large, we implement this as chunked transfers
of >4096 bytes.

Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
src/libnvme-mi.map
src/nvme/mi.c
src/nvme/mi.h

index 9e3b96ccc45d44acf73b7301a0b8685eaaac9449..d9e9097f25cc4cee5958343a60b530ef5fd734a9 100644 (file)
@@ -11,6 +11,7 @@ LIBNVME_MI_1_1 {
                nvme_mi_mi_read_mi_data_ctrl;
                nvme_mi_mi_subsystem_health_status_poll;
                nvme_mi_admin_identify_partial;
+               nvme_mi_admin_get_log_page;
                nvme_mi_open_mctp;
        local:
                *;
index 244d400ce6e0d13af0fcbd93a9c61f92db73cfba..9634c9633c199e66244a1d31f021742528660178 100644 (file)
@@ -196,6 +196,108 @@ int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl,
        return 0;
 }
 
+/* retrieves a MCTP-messsage-sized chunk of log page data. offset and len are
+ * specified within the args->data area */
+static int __nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl,
+                                       const struct nvme_get_log_args *args,
+                                       off_t offset, size_t *lenp, bool final)
+{
+       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;
+       size_t len;
+       __u32 ndw;
+       int rc;
+
+       /* MI spec requires that the data length field is less than or equal
+        * to 4096 */
+       len = *lenp;
+       if (!len || len > 4096 || len < 4)
+               return -EINVAL;
+
+       if (offset < 0 || offset >= len)
+               return -EINVAL;
+
+       ndw = (len >> 2) - 1;
+
+       nvme_mi_admin_init_req(&req, &req_hdr, ctrl->id, nvme_admin_get_log_page);
+       req_hdr.cdw1 = cpu_to_le32(args->nsid);
+       req_hdr.cdw10 = cpu_to_le32((ndw & 0xffff) << 16 |
+                                   ((!final || args->rae) ? 1 : 0) << 15 |
+                                   args->lsp << 8 |
+                                   (args->lid & 0xff));
+       req_hdr.cdw11 = cpu_to_le32(args->lsi << 16 |
+                                   ndw >> 16);
+       req_hdr.cdw12 = cpu_to_le32(args->lpo & 0xffffffff);
+       req_hdr.cdw13 = cpu_to_le32(args->lpo >> 32);
+       req_hdr.cdw14 = cpu_to_le32(args->csi << 24 |
+                                   (args->ot ? 1 : 0) << 23 |
+                                   args->uuidx);
+       req_hdr.flags = 0x1;
+       req_hdr.dlen = cpu_to_le32(len & 0xffffffff);
+       if (offset) {
+               req_hdr.flags |= 0x2;
+               req_hdr.doff = cpu_to_le32(offset);
+       }
+
+       nvme_mi_calc_req_mic(&req);
+
+       nvme_mi_admin_init_resp(&resp, &resp_hdr);
+       resp.data = args->log + offset;
+       resp.data_len = len;
+
+       rc = nvme_mi_submit(ctrl->ep, &req, &resp);
+       if (rc)
+               return rc;
+
+       if (resp_hdr.status)
+               return resp_hdr.status;
+
+       *lenp = resp.data_len;
+
+       return 0;
+}
+
+int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl,
+                              struct nvme_get_log_args *args)
+{
+       const size_t xfer_size = 4096;
+       off_t xfer_offset;
+       int rc = 0;
+
+       if (args->args_size < sizeof(*args))
+               return -EINVAL;
+
+       for (xfer_offset = 0; xfer_offset < args->len;) {
+               size_t tmp, cur_xfer_size = xfer_size;
+               bool final;
+
+               if (xfer_offset + cur_xfer_size > args->len)
+                       cur_xfer_size = args->len - xfer_offset;
+
+               tmp = cur_xfer_size;
+
+               final = xfer_offset + cur_xfer_size >= args->len;
+
+               rc = __nvme_mi_admin_get_log_page(ctrl, args, xfer_offset,
+                                                 &tmp, final);
+               if (rc)
+                       break;
+
+               xfer_offset += tmp;
+               /* if we returned less data than expected, consider that
+                * the end of the log page */
+               if (tmp != cur_xfer_size)
+                       break;
+       }
+
+       if (!rc)
+               args->len = xfer_offset;
+
+       return rc;
+}
+
 static int nvme_mi_read_data(nvme_mi_ep_t ep, __u32 cdw0,
                             void *data, size_t *data_len)
 {
index 56b8b5c9e91f824d8fc93e276a2dc6b84d4edbe8..0eb816a8b5aa8a0199a76fc9abb44391987777b8 100644 (file)
@@ -181,4 +181,18 @@ static inline int nvme_mi_admin_identify_ctrl_list(nvme_mi_ctrl_t ctrl,
        return nvme_mi_admin_identify(ctrl, &args);
 }
 
+/**
+ * Retreives log page data as specified by @args. On return, @args->len is
+ * updated to the amount of log data written to @args->log.
+ *
+ * This request may be implemented as multiple log page commands, in order
+ * to fit within MI message-size limits.
+ *
+ * @ctrl:      MI controller
+ * @args:      Log page request parameters
+ */
+int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl,
+                              struct nvme_get_log_args *args);
+
+
 #endif /* _LIBNVME_MI_MI_H */