]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
nvme-cli: Implement get_telemetry_log from 1.3 spec
authorScott Bauer <scott.bauer@intel.com>
Wed, 17 Jan 2018 17:39:20 +0000 (10:39 -0700)
committerScott Bauer <scott.bauer@intel.com>
Thu, 18 Jan 2018 01:49:03 +0000 (18:49 -0700)
Signed-off-by: Scott Bauer <scott.bauer@intel.com>
linux/nvme.h
nvme-builtin.h
nvme-ioctl.c
nvme-ioctl.h
nvme-print.c
nvme.c

index 4c02f382fe005cfb473ceb8c3c25b3679b8ae4df..9c152e4a398b5c39800ba1bcfd2539844fd15172 100644 (file)
@@ -377,6 +377,28 @@ enum {
        NVME_NIDT_UUID          = 0x03,
 };
 
+/* Derived from 1.3a Figure 101: Get Log Page – Telemetry Host
+ * -Initiated Log (Log Identifier 07h)
+ */
+struct nvme_telemetry_log_page_hdr {
+       __u8    lpi; /* Log page identifier */
+       __u8    rsvd[4];
+       __u8    iee_oui[3];
+       __u16   dalb1; /* Data area 1 last block */
+       __u16   dalb2; /* Data area 2 last block */
+       __u16   dalb3; /* Data area 3 last block */
+       __u8    rsvd1[368]; /* TODO verify */
+       __u8    ctrlavail; /* Controller initiated data avail?*/
+       __u8    ctrldgn; /* Controller initiated telemetry Data Gen # */
+       __u8    rsnident[128];
+       /* We'll have to double fetch so we can get the header,
+        * parse dalb1->3 determine how much size we need for the
+        * log then alloc below. Or just do a secondary non-struct
+        * allocation.
+        */
+       __u8    telemetry_dataarea[0];
+};
+
 struct nvme_smart_log {
        __u8                    critical_warning;
        __u8                    temperature[2];
@@ -768,6 +790,7 @@ enum {
        NVME_LOG_SMART          = 0x02,
        NVME_LOG_FW_SLOT        = 0x03,
        NVME_LOG_CMD_EFFECTS    = 0x05,
+       NVME_LOG_TELEMETRY_HOST = 0x07,
        NVME_LOG_DISC           = 0x70,
        NVME_LOG_RESERVATION    = 0x80,
        NVME_LOG_SANITIZE       = 0x81,
@@ -779,6 +802,7 @@ enum {
 enum {
        NVME_NO_LOG_LSP       = 0x0,
        NVME_NO_LOG_LPO       = 0x0,
+       NVME_TELEM_LSP_CREATE = 0x1,
 };
 
 /* Sanitize and Sanitize Monitor/Log */
index 85f9f0fd364eea493c939c1cd56f24801d5785ef..4adfa78ea95b104a9d1b3d2448fe18ec28a32e2b 100644 (file)
@@ -20,6 +20,7 @@ COMMAND_LIST(
        ENTRY("list-ctrl", "Send NVMe Identify Controller List, display structure", list_ctrl)
        ENTRY("get-ns-id", "Retrieve the namespace ID of opened block device", get_ns_id)
        ENTRY("get-log", "Generic NVMe get log, returns log in raw format", get_log)
+       ENTRY("telemetry_log", "Retrieve FW Telemetry log write to file", get_telemetry_log)
        ENTRY("fw-log", "Retrieve FW Log, show it", get_fw_log)
        ENTRY("smart-log", "Retrieve SMART Log, show it", get_smart_log)
        ENTRY("error-log", "Retrieve Error Log, show it", get_error_log)
index 9e9189eb175a47a22e0829c0efd74d9ac3568567..5c900b72775052540b9167a386387953ad14a66f 100644 (file)
@@ -404,6 +404,19 @@ int nvme_get_log(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo,
        return nvme_submit_admin_passthru(fd, &cmd);
 }
 
+int nvme_get_telemetry_log(int fd, void *lp, int generate_report,
+                          size_t log_page_size, __u64 offset)
+{
+       if (generate_report)
+               return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_TELEMETRY_HOST,
+                                   NVME_TELEM_LSP_CREATE, offset,
+                                   log_page_size, lp);
+       else
+               return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_TELEMETRY_HOST,
+                                   NVME_NO_LOG_LSP, offset,
+                                   log_page_size, lp);
+}
+
 int nvme_fw_log(int fd, struct nvme_firmware_log_page *fw_log)
 {
        return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_FW_SLOT,
index cf5e693158aba2d6c716bd402eaad4b891a893a7..4031c7086a236e52751c944e79612b294b63da4b 100644 (file)
@@ -81,6 +81,8 @@ int nvme_identify_ns_descs(int fd, __u32 nsid, void *data);
 int nvme_get_log(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo,
                  __u32 data_len, void *data);
 
+int nvme_get_telemetry_log(int fd, void *lp, int generate_report,
+                          size_t log_page_size, __u64 offset);
 int nvme_fw_log(int fd, struct nvme_firmware_log_page *fw_log);
 int nvme_error_log(int fd, int entries, struct nvme_error_log_page *err_log);
 int nvme_smart_log(int fd, __u32 nsid, struct nvme_smart_log *smart_log);
index a414f035825b388c9026192bf791ae92581294a8..1ca4b711af2ece52c6253d0e760625fa01064e16 100644 (file)
@@ -174,12 +174,15 @@ static void show_nvme_id_ctrl_frmw(__u8 frmw)
 
 static void show_nvme_id_ctrl_lpa(__u8 lpa)
 {
-       __u8 rsvd = (lpa & 0xF8) >> 3;
+       __u8 rsvd = (lpa & 0xF0) >> 4;
+       __u8 telem = (lpa & 0x8) >> 3;
        __u8 ed = (lpa & 0x4) >> 2;
        __u8 celp = (lpa & 0x2) >> 1;
        __u8 smlp = lpa & 0x1;
        if (rsvd)
-               printf("  [7:3] : %#x\tReserved\n", rsvd);
+               printf("  [7:4] : %#x\tReserved\n", rsvd);
+       printf("  [3:3] : %#x\tTelemetry host/controller initiated log page %sSuporrted\n",
+              telem, telem ? "" : "Not ");
        printf("  [2:2] : %#x\tExtended data for Get Log Page %sSupported\n",
                ed, ed ? "" : "Not ");
        printf("  [1:1] : %#x\tCommand Effects Log Page %sSupported\n",
diff --git a/nvme.c b/nvme.c
index f4e9e44a58845abff8a073e89911504522887155..d94e7a9750917599c18a992b1b5c20ea776a09f6 100644 (file)
--- a/nvme.c
+++ b/nvme.c
@@ -223,6 +223,112 @@ static int get_smart_log(int argc, char **argv, struct command *cmd, struct plug
        return err;
 }
 
+static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+       const char *desc = "Retrieve telemetry log and write to binary file";
+       const char *fname = "File name to save raw binary, includes header";
+       const char *hgen = "Have the host tell the controller to generate the report";
+       struct nvme_telemetry_log_page_hdr *hdr;
+       void *page_log;
+       size_t full_size, offset, num_blocks;
+       int err = 0 , fd, output;
+
+       struct config {
+               char *file_name;
+               __u32 host_gen;
+       };
+       struct config cfg = {
+               .file_name = NULL,
+               .host_gen = 1,
+       };
+
+       const struct argconfig_commandline_options command_line_options[] = {
+               {"output-file", 'o', "FILE", CFG_STRING, &cfg.file_name, required_argument, fname},
+               {"host-generate", 'h', "NUM", CFG_POSITIVE, &cfg.host_gen, required_argument, hgen},
+               {NULL}
+       };
+
+       fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+       if (fd < 0) {
+               fprintf(stderr, "parse and open failed\n");
+               return fd;
+       }
+
+       if (!cfg.file_name) {
+               fprintf(stderr, "Please provide an output file!\n");
+               close(fd);
+               return EINVAL;
+       }
+
+       output = open(cfg.file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+       if (output < 0) {
+               fprintf(stderr, "Failed to open output file!\n");
+               return output;
+       }
+
+       cfg.host_gen = !!cfg.host_gen;
+
+       hdr = malloc(4096);
+       memset(hdr, 0, 4096);
+
+       err = nvme_get_telemetry_log(fd, hdr, cfg.host_gen, 4096, 0);
+       if (err) {
+               fprintf(stderr, "NVMe Status:%s(%x)\n",
+                       nvme_status_to_string(err), err);
+               fprintf(stderr, "Failed to aquire telemetry header %d!\n", err);
+               free(hdr);
+               goto close_out;
+       }
+
+       err = write(output, (void *) hdr, 4096);
+       if (err != 4096) {
+               fprintf(stderr, "Failed to flush all data to file!");
+               goto close_out;
+       }
+
+       num_blocks = max(hdr->dalb1, max(hdr->dalb2, hdr->dalb3));
+
+       free(hdr);
+       full_size = num_blocks * 512;
+       /* Round to page boundary */
+       full_size += (4096 - (full_size % 4096));
+       /* We've already pulled 1 page worth of data, lets remove that. */
+       full_size -= 4096;
+       offset = 4096;
+
+       page_log = malloc(4096);
+       if (!page_log) {
+               fprintf(stderr, "Failed to allocate %zu bytes for log\n",
+                       full_size);
+               err = ENOMEM;
+               goto close_out;
+       }
+
+       while (full_size) {
+               err = nvme_get_telemetry_log(fd, page_log, 0, 4096, offset);
+               if (err) {
+                       fprintf(stderr, "Failed to aquire full telemetry log!\n");
+                       fprintf(stderr, "NVMe Status:%s(%x)\n",
+                               nvme_status_to_string(err), err);
+                       goto out;
+               }
+
+               err = write(output, (void *) page_log, 4096);
+               if (err != 4096) {
+                       fprintf(stderr, "Failed to flush all data to file!");
+                       goto out;
+               }
+               full_size -= 4096;
+               offset += 4096;
+       }
+ out:
+       free(page_log);
+ close_out:
+       close(fd);
+       close(output);
+       return err;
+}
+
 static int get_effects_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
 {
        const char *desc = "Retrieve command effects log page and print the table.";