From: Jeff Lien Date: Wed, 11 Dec 2019 19:54:57 +0000 (-0600) Subject: [NVMe-CLI] Add support to retrieve telemetry data in vs-internal-log X-Git-Tag: v1.10~19 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=c0118e963798ae59c44d4b92dd45875e9a03277d;p=users%2Fhch%2Fnvme-cli.git [NVMe-CLI] Add support to retrieve telemetry data in vs-internal-log --- diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index c319027..3ee7cd3 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -66,6 +66,7 @@ #define WDC_NVME_SN640_DEV_ID 0x2400 #define WDC_NVME_SN640_DEV_ID_1 0x2401 #define WDC_NVME_SN640_DEV_ID_2 0x2402 +#define WDC_NVME_SN740_DEV_ID 0x2600 #define WDC_NVME_SXSLCL_DEV_ID 0x2001 #define WDC_NVME_SN520_DEV_ID 0x5003 #define WDC_NVME_SN520_DEV_ID_1 0x5004 @@ -136,6 +137,13 @@ #define WDC_NVME_DUI_MAX_SECTION_V2 0x26 #define WDC_NVME_DUI_MAX_DATA_AREA 0x05 +/* Telemtery types for vs-internal-log command */ +#define WDC_TELEMETRY_TYPE_NONE 0x0 +#define WDC_TELEMETRY_TYPE_HOST 0x1 +#define WDC_TELEMETRY_TYPE_CONTROLLER 0x2 +#define WDC_TELEMETRY_HEADER_LENGTH 512 +#define WDC_TELEMETRY_BLOCK_SIZE 512 + /* Crash dump */ #define WDC_NVME_CRASH_DUMP_SIZE_DATA_LEN WDC_NVME_LOG_SIZE_DATA_LEN #define WDC_NVME_CRASH_DUMP_SIZE_NDT 0x02 @@ -821,6 +829,8 @@ static __u64 wdc_get_drive_capabilities(int fd) { case WDC_NVME_SN840_DEV_ID: /* FALLTHRU */ case WDC_NVME_SN840_DEV_ID_1: + /* FALLTHRU */ + case WDC_NVME_SN740_DEV_ID: capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_CLEAR_PCIE | @@ -1306,9 +1316,146 @@ static int wdc_do_dump_e6(int fd, __u32 opcode,__u32 data_len, return ret; } -static int wdc_do_cap_diag(int fd, char *file, __u32 xfer_size) +static int wdc_do_cap_telemetry_log(int fd, char *file, __u32 bs, int type, int data_area) { - int ret; + struct nvme_telemetry_log_page_hdr *hdr; + size_t full_size, offset = WDC_TELEMETRY_HEADER_LENGTH; + int err = 0, output; + void *page_log; + __u32 host_gen = 1; + int ctrl_init = 0; + __u32 result; + void *buf = NULL; + + + if (type == WDC_TELEMETRY_TYPE_HOST) { + host_gen = 1; + ctrl_init = 0; + } else if (type == WDC_TELEMETRY_TYPE_CONTROLLER) { + /* Verify the Controller Initiated Option is enabled */ + err = nvme_get_feature(fd, 0, WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, 0, 0, + 4, buf, &result); + if (err == 0) { + if (result) { + host_gen = 0; + ctrl_init = 1; + } + else { + fprintf(stderr, "%s: Controller initiated option telemetry log page disabled\n", __func__); + err = -EINVAL; + goto close_fd; + } + } else { + fprintf(stderr, "ERROR : WDC: Get telemetry option feature failed. NVMe Status:%s(%x)\n", + nvme_status_to_string(err), err); + err = -EPERM; + goto close_fd; + } + } else { + fprintf(stderr, "%s: Invalid type parameter; type = %d\n", __func__, type); + err = -EINVAL; + goto close_fd; + } + + if (!file) { + fprintf(stderr, "%s: Please provide an output file!\n", __func__); + err = -EINVAL; + goto close_fd; + } + + hdr = malloc(bs); + page_log = malloc(bs); + if (!hdr || !page_log) { + fprintf(stderr, "%s: Failed to allocate 0x%x bytes for log: %s\n", + __func__, bs, strerror(errno)); + err = -ENOMEM; + goto free_mem; + } + memset(hdr, 0, bs); + + output = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (output < 0) { + fprintf(stderr, "%s: Failed to open output file %s: %s!\n", + __func__, file, strerror(errno)); + err = output; + goto free_mem; + } + + err = nvme_get_telemetry_log(fd, hdr, host_gen, ctrl_init, WDC_TELEMETRY_HEADER_LENGTH, 0); + if (err < 0) + perror("get-telemetry-log"); + else if (err > 0) { + nvme_show_status(err); + fprintf(stderr, "%s: Failed to acquire telemetry header!\n", __func__); + goto close_output; + } + + err = write(output, (void *) hdr, WDC_TELEMETRY_HEADER_LENGTH); + if (err != WDC_TELEMETRY_HEADER_LENGTH) { + fprintf(stderr, "%s: Failed to flush header data to file!, err = %d\n", __func__, err); + goto close_output; + } + + switch (data_area) { + case 1: + full_size = (le16_to_cpu(hdr->dalb1) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH; + break; + case 2: + full_size = (le16_to_cpu(hdr->dalb2) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH; + break; + case 3: + full_size = (le16_to_cpu(hdr->dalb3) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH; + break; + default: + fprintf(stderr, "%s: Invalid data area requested, data area = %d\n", __func__, data_area); + err = -EINVAL; + goto close_output; + } + + /* + * Continuously pull data until the offset hits the end of the last + * block. + */ + while (offset < full_size) { + if ((full_size - offset) < bs) + bs = (full_size - offset); + + + err = nvme_get_telemetry_log(fd, page_log, 0, ctrl_init, bs, offset); + if (err < 0) { + perror("get-telemetry-log"); + break; + } else if (err > 0) { + nvme_show_status(err); + fprintf(stderr, "%s: Failed to acquire full telemetry log!\n", __func__); + nvme_show_status(err); + break; + } + + err = write(output, (void *) page_log, bs); + if (err != bs) { + fprintf(stderr, "%s: Failed to flush telemetry data to file!, err = %d\n", __func__, err); + break; + } + err = 0; + offset += bs; + } + +close_output: + close(output); +free_mem: + free(hdr); + free(page_log); +close_fd: + close(fd); + + return err; + +} + +static int wdc_do_cap_diag(int fd, char *file, __u32 xfer_size, int type, int data_area) +{ + int ret = -1; __u32 e6_log_hdr_size = WDC_NVME_CAP_DIAG_HEADER_TOC_SIZE; struct wdc_e6_log_hdr *log_hdr; __u32 cap_diag_length; @@ -1321,27 +1468,34 @@ static int wdc_do_cap_diag(int fd, char *file, __u32 xfer_size) } memset(log_hdr, 0, e6_log_hdr_size); - ret = wdc_dump_length_e6(fd, WDC_NVME_CAP_DIAG_OPCODE, - WDC_NVME_CAP_DIAG_HEADER_TOC_SIZE>>2, - 0x00, - log_hdr); - if (ret == -1) { - ret = -1; - goto out; - } + if (type == WDC_TELEMETRY_TYPE_NONE) { + ret = wdc_dump_length_e6(fd, WDC_NVME_CAP_DIAG_OPCODE, + WDC_NVME_CAP_DIAG_HEADER_TOC_SIZE>>2, + 0x00, + log_hdr); + if (ret == -1) { + ret = -1; + goto out; + } - cap_diag_length = (log_hdr->log_size[0] << 24 | log_hdr->log_size[1] << 16 | - log_hdr->log_size[2] << 8 | log_hdr->log_size[3]); + cap_diag_length = (log_hdr->log_size[0] << 24 | log_hdr->log_size[1] << 16 | + log_hdr->log_size[2] << 8 | log_hdr->log_size[3]); - if (cap_diag_length == 0) { - fprintf(stderr, "INFO : WDC : Capture Diagnostics log is empty\n"); - } else { - ret = wdc_do_dump_e6(fd, WDC_NVME_CAP_DIAG_OPCODE, cap_diag_length, - (WDC_NVME_CAP_DIAG_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | WDC_NVME_CAP_DIAG_CMD, - file, xfer_size, (__u8 *)log_hdr); + if (cap_diag_length == 0) { + fprintf(stderr, "INFO : WDC : Capture Diagnostics log is empty\n"); + } else { + ret = wdc_do_dump_e6(fd, WDC_NVME_CAP_DIAG_OPCODE, cap_diag_length, + (WDC_NVME_CAP_DIAG_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | WDC_NVME_CAP_DIAG_CMD, + file, xfer_size, (__u8 *)log_hdr); - fprintf(stderr, "INFO : WDC : Capture Diagnostics log, length = 0x%x\n", cap_diag_length); - } + fprintf(stderr, "INFO : WDC : Capture Diagnostics log, length = 0x%x\n", cap_diag_length); + } + } else if ((type == WDC_TELEMETRY_TYPE_HOST) || + (type == WDC_TELEMETRY_TYPE_CONTROLLER)) { + /* Get the desired telemetry log page */ + ret = wdc_do_cap_telemetry_log(fd, file, xfer_size, type, data_area); + } else + fprintf(stderr, "%s: ERROR : Invalid type : %d\n", __func__, type); out: free(log_hdr); @@ -1634,7 +1788,7 @@ static int wdc_cap_diag(int argc, char **argv, struct command *command, capabilities = wdc_get_drive_capabilities(fd); if ((capabilities & WDC_DRIVE_CAP_CAP_DIAG) == WDC_DRIVE_CAP_CAP_DIAG) - return wdc_do_cap_diag(fd, f, xfer_size); + return wdc_do_cap_diag(fd, f, xfer_size, 0, 0); fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); return 0; @@ -1876,14 +2030,16 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command char *desc = "Internal Firmware Log."; char *file = "Output file pathname."; char *size = "Data retrieval transfer size."; - char *data_area = "Data area to retrieve up to."; - char *file_size = "Output file size."; - char *offset = "Output file data offset."; + char *data_area = "Data area to retrieve up to. Currently only supported on the SN340, SN640, and SN840 devices."; + char *file_size = "Output file size. Currently only supported on the SN340 device."; + char *offset = "Output file data offset. Currently only supported on the SN340 device."; + char *type = "Telemetry type - NONE, HOST, or CONTROLLER. Currently only supported on the SN640 and SN840 devices."; char *verbose = "Display more debug messages."; char f[PATH_MAX] = {0}; char fileSuffix[PATH_MAX] = {0}; __u32 xfer_size = 0; int fd; + int telemetry_type = 0, telemetry_data_area = 0; UtilsTimeInfo timeInfo; __u8 timeStamp[MAX_PATH_LEN]; __u64 capabilities = 0; @@ -1894,6 +2050,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command int data_area; __u64 file_size; __u64 offset; + char *type; int verbose; }; @@ -1903,6 +2060,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command .data_area = 2, .file_size = 0, .offset = 0, + .type = NULL, .verbose = 0, }; @@ -1911,7 +2069,8 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), OPT_UINT("data-area", 'd', &cfg.data_area, data_area), OPT_LONG("file-size", 'f', &cfg.file_size, file_size), - OPT_LONG("offset", 't', &cfg.offset, offset), + OPT_LONG("offset", 'e', &cfg.offset, offset), + OPT_FILE("type", 't', &cfg.type, type), OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), OPT_END() }; @@ -1964,12 +2123,28 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command } capabilities = wdc_get_drive_capabilities(fd); - if ((capabilities & WDC_DRIVE_CAP_INTERNAL_LOG) == WDC_DRIVE_CAP_INTERNAL_LOG) - return wdc_do_cap_diag(fd, f, xfer_size); + if ((capabilities & WDC_DRIVE_CAP_INTERNAL_LOG) == WDC_DRIVE_CAP_INTERNAL_LOG) { + if ((cfg.type == NULL) || + (!strcmp(cfg.type, "NONE")) || + (!strcmp(cfg.type, "none"))) { + telemetry_type = WDC_TELEMETRY_TYPE_NONE; + data_area = 0; + } else if ((!strcmp(cfg.type, "HOST")) || + (!strcmp(cfg.type, "host"))) { + telemetry_type = WDC_TELEMETRY_TYPE_HOST; + telemetry_data_area = cfg.data_area; + } else if ((!strcmp(cfg.type, "CONTROLLER")) || + (!strcmp(cfg.type, "controller"))) { + telemetry_type = WDC_TELEMETRY_TYPE_CONTROLLER; + telemetry_data_area = cfg.data_area; + } + + return wdc_do_cap_diag(fd, f, xfer_size, telemetry_type, telemetry_data_area); + } if ((capabilities & WDC_DRIVE_CAP_SN340_DUI) == WDC_DRIVE_CAP_SN340_DUI) { - /* FW requirement - xfer size must be 500k for data area 4 */ + /* FW requirement - xfer size must be 256k for data area 4 */ if (cfg.data_area >= 4) - xfer_size = 0x80000; + xfer_size = 0x40000; return wdc_do_cap_dui(fd, f, xfer_size, cfg.data_area, cfg.verbose, cfg.file_size, cfg.offset); } if ((capabilities & WDC_DRIVE_CAP_DUI_DATA) == WDC_DRIVE_CAP_DUI_DATA)