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];
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,
enum {
NVME_NO_LOG_LSP = 0x0,
NVME_NO_LOG_LPO = 0x0,
+ NVME_TELEM_LSP_CREATE = 0x1,
};
/* Sanitize and Sanitize Monitor/Log */
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)
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,
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);
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",
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.";