diff options
Diffstat (limited to 'plugins')
44 files changed, 8059 insertions, 2429 deletions
diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c index 2a221f8..1efdd76 100644 --- a/plugins/fdp/fdp.c +++ b/plugins/fdp/fdp.c @@ -25,7 +25,7 @@ static int fdp_configs(int argc, char **argv, struct command *cmd, const char *human_readable = "show log in readable format"; const char *raw = "use binary output"; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_dev *dev; struct nvme_fdp_config_log hdr; void *log = NULL; @@ -107,7 +107,7 @@ static int fdp_usage(int argc, char **argv, struct command *cmd, struct plugin * const char *egid = "Endurance group identifier"; const char *raw = "use binary output"; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_dev *dev; struct nvme_fdp_ruhu_log hdr; size_t len; @@ -180,7 +180,7 @@ static int fdp_stats(int argc, char **argv, struct command *cmd, struct plugin * const char *egid = "Endurance group identifier"; const char *raw = "use binary output"; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_dev *dev; struct nvme_fdp_stats_log stats; int err; @@ -238,7 +238,7 @@ static int fdp_events(int argc, char **argv, struct command *cmd, struct plugin const char *host_events = "Get host events"; const char *raw = "use binary output"; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_dev *dev; struct nvme_fdp_events_log events; int err; @@ -299,7 +299,7 @@ static int fdp_status(int argc, char **argv, struct command *cmd, struct plugin const char *namespace_id = "Namespace identifier"; const char *raw = "use binary output"; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_dev *dev; struct nvme_fdp_ruh_status hdr; size_t len; diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index 0272dea..c09d8d3 100644 --- a/plugins/huawei/huawei-nvme.c +++ b/plugins/huawei/huawei-nvme.c @@ -296,7 +296,7 @@ static int huawei_list(int argc, char **argv, struct command *command, struct huawei_list_item *list_items; unsigned int i, n, ret; unsigned int huawei_num = 0; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; const char *desc = "Retrieve basic information for the given huawei device"; struct config { char *output_format; diff --git a/plugins/innogrit/typedef.h b/plugins/innogrit/typedef.h index f2a59b4..7220d38 100644 --- a/plugins/innogrit/typedef.h +++ b/plugins/innogrit/typedef.h @@ -53,14 +53,14 @@ struct vsc_smart_log { unsigned int low_pwr_cnt; unsigned int wa; unsigned int ps3_entry_cnt; - u_char highest_temp[4]; + unsigned char highest_temp[4]; unsigned int weight_ec; unsigned int slc_cap_mb; unsigned long long nand_page_write_cnt; unsigned int program_error_cnt; unsigned int erase_error_cnt; - u_char flash_type; - u_char reserved2[3]; + unsigned char flash_type; + unsigned char reserved2[3]; unsigned int hs_crc_err_cnt; unsigned int ddr_ecc_err_cnt; unsigned int reserved3[44]; diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index b215125..c189f1b 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -65,7 +65,7 @@ static int compare_fw_version(const char *fw1, const char *fw2) * 0: old memblaze format * *******************************************************/ #define MEMBLAZE_FORMAT (0) -#define INTEL_FORMAT (1) +#define INTEL_FORMAT (1) /* 2.13 = papaya */ #define IS_PAPAYA(str) (!strcmp(str, "2.13")) @@ -107,14 +107,15 @@ static __u64 raw_2_u64(const __u8 *buf, size_t len) return le64_to_cpu(val); } -static void get_memblaze_new_smart_info(struct nvme_p4_smart_log *smart, int index, __u8 *nm_val, __u8 *raw_val) +static void get_memblaze_new_smart_info(struct nvme_p4_smart_log *smart, int index, __u8 *nm_val, + __u8 *raw_val) { memcpy(nm_val, smart->itemArr[index].nmVal, NM_SIZE); memcpy(raw_val, smart->itemArr[index].rawVal, RAW_SIZE); } -static void show_memblaze_smart_log_new(struct nvme_memblaze_smart_log *s, - unsigned int nsid, const char *devname) +static void show_memblaze_smart_log_new(struct nvme_memblaze_smart_log *s, unsigned int nsid, + const char *devname) { struct nvme_p4_smart_log *smart = (struct nvme_p4_smart_log *)s; __u8 *nm = malloc(NM_SIZE * sizeof(__u8)); @@ -130,7 +131,8 @@ static void show_memblaze_smart_log_new(struct nvme_memblaze_smart_log *s, return; } - printf("%s:%s %s:%x\n", "Additional Smart Log for NVME device", devname, "namespace-id", nsid); + printf("%s:%s %s:%x\n", "Additional Smart Log for NVME device", devname, "namespace-id", + nsid); printf("%-34s%-11s%s\n", "key", "normalized", "raw"); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PROGRAM_FAIL, nm, raw); @@ -140,42 +142,51 @@ static void show_memblaze_smart_log_new(struct nvme_memblaze_smart_log *s, printf("%-32s: %3d%% %"PRIu64"\n", "erase_fail_count", *nm, int48_to_long(raw)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_WEARLEVELING_COUNT, nm, raw); - printf("%-31s : %3d%% %s%u%s%u%s%u\n", "wear_leveling", *nm, - "min: ", *(__u16 *)raw, ", max: ", *(__u16 *)(raw+2), ", avg: ", *(__u16 *)(raw+4)); + printf("%-31s : %3d%% %s%u%s%u%s%u\n", "wear_leveling", *nm, "min: ", *(__u16 *)raw, + ", max: ", *(__u16 *)(raw+2), ", avg: ", *(__u16 *)(raw+4)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_E2E_DECTECTION_COUNT, nm, raw); - printf("%-31s: %3d%% %"PRIu64"\n", "end_to_end_error_detection_count", *nm, int48_to_long(raw)); + printf("%-31s: %3d%% %"PRIu64"\n", "end_to_end_error_detection_count", *nm, + int48_to_long(raw)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PCIE_CRC_ERR_COUNT, nm, raw); printf("%-32s: %3d%% %"PRIu64"\n", "crc_error_count", *nm, int48_to_long(raw)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR, nm, raw); - printf("%-32s: %3d%% %.3f%%\n", "timed_workload_media_wear", *nm, ((float)int48_to_long(raw))/1000); + printf("%-32s: %3d%% %.3f%%\n", "timed_workload_media_wear", *nm, + ((float)int48_to_long(raw))/1000); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ, nm, raw); - printf("%-32s: %3d%% %"PRIu64"%%\n", "timed_workload_host_reads", *nm, int48_to_long(raw)); + printf("%-32s: %3d%% %"PRIu64"%%\n", "timed_workload_host_reads", *nm, + int48_to_long(raw)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_TIMER, nm, raw); - printf("%-32s: %3d%% %"PRIu64"%s\n", "timed_workload_timer", *nm, int48_to_long(raw), " min"); + printf("%-32s: %3d%% %"PRIu64"%s\n", "timed_workload_timer", *nm, + int48_to_long(raw), " min"); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_THERMAL_THROTTLE_STATUS, nm, raw); - printf("%-32s: %3d%% %u%%%s%"PRIu64"\n", "thermal_throttle_status", *nm, - *raw, ", cnt: ", int48_to_long(raw+1)); + printf("%-32s: %3d%% %u%%%s%"PRIu64"\n", "thermal_throttle_status", *nm, *raw, + ", cnt: ", int48_to_long(raw+1)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT, nm, raw); - printf("%-32s: %3d%% %"PRIu64"\n", "retry_buffer_overflow_count", *nm, int48_to_long(raw)); + printf("%-32s: %3d%% %"PRIu64"\n", "retry_buffer_overflow_count", *nm, + int48_to_long(raw)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT, nm, raw); - printf("%-32s: %3d%% %"PRIu64"\n", "pll_lock_loss_count", *nm, int48_to_long(raw)); + printf("%-32s: %3d%% %"PRIu64"\n", "pll_lock_loss_count", *nm, + int48_to_long(raw)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TOTAL_WRITE, nm, raw); - printf("%-32s: %3d%% %s%"PRIu64"\n", "nand_bytes_written", *nm, "sectors: ", int48_to_long(raw)); + printf("%-32s: %3d%% %s%"PRIu64"\n", "nand_bytes_written", *nm, "sectors: ", + int48_to_long(raw)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_HOST_WRITE, nm, raw); - printf("%-32s: %3d%% %s%"PRIu64"\n", "host_bytes_written", *nm, "sectors: ", int48_to_long(raw)); + printf("%-32s: %3d%% %s%"PRIu64"\n", "host_bytes_written", *nm, "sectors: ", + int48_to_long(raw)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT, nm, raw); - printf("%-32s: %3d%% %"PRIu64"\n", "system_area_life_left", *nm, int48_to_long(raw)); + printf("%-32s: %3d%% %"PRIu64"\n", "system_area_life_left", *nm, + int48_to_long(raw)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TOTAL_READ, nm, raw); printf("%-32s: %3d%% %"PRIu64"\n", "total_read", *nm, int48_to_long(raw)); @@ -189,8 +200,8 @@ static void show_memblaze_smart_log_new(struct nvme_memblaze_smart_log *s, "max: ", *(__u16 *)raw, ", min: ", *(__u16 *)(raw+2), ", curr: ", *(__u16 *)(raw+4)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TEMPT_SINCE_BOOTUP, nm, raw); - printf("%-32s: %3d%% %s%u%s%u%s%u\n", "tempt_since_bootup", *nm, "max: ", *(__u16 *)raw, - ", min: ", *(__u16 *)(raw+2), ", curr: ", *(__u16 *)(raw+4)); + printf("%-32s: %3d%% %s%u%s%u%s%u\n", "tempt_since_bootup", *nm, "max: ", + *(__u16 *)raw, ", min: ", *(__u16 *)(raw+2), ", curr: ", *(__u16 *)(raw+4)); get_memblaze_new_smart_info(smart, RAISIN_SI_VD_READ_FAIL, nm, raw); printf("%-32s: %3d%% %"PRIu64"\n", "read_fail_count", *nm, int48_to_long(raw)); @@ -319,23 +330,24 @@ static void show_memblaze_smart_log_old(struct nvme_memblaze_smart_log *smart, } get_memblaze_new_smart_info(s, PROGRAM_FAIL, nm, raw); printf("%-32s : %3d%% %"PRIu64"\n", - "program_fail_count", *nm, int48_to_long(raw)); + "program_fail_count", *nm, int48_to_long(raw)); get_memblaze_new_smart_info(s, ERASE_FAIL, nm, raw); printf("%-32s : %3d%% %"PRIu64"\n", - "erase_fail_count", *nm, int48_to_long(raw)); + "erase_fail_count", *nm, int48_to_long(raw)); get_memblaze_new_smart_info(s, WEARLEVELING_COUNT, nm, raw); printf("%-31s : %3d%% %s%u%s%u%s%u\n", - "wear_leveling", *nm, "min: ", *(__u16 *)raw, ", max: ", *(__u16 *)(raw+2), ", avg: ", *(__u16 *)(raw+4)); + "wear_leveling", *nm, "min: ", *(__u16 *)raw, ", max: ", *(__u16 *)(raw+2), + ", avg: ", *(__u16 *)(raw+4)); get_memblaze_new_smart_info(s, TOTAL_WRITE, nm, raw); printf("%-32s : %3d%% %"PRIu64"\n", - "nand_bytes_written", *nm, 32*int48_to_long(raw)); + "nand_bytes_written", *nm, 32*int48_to_long(raw)); get_memblaze_new_smart_info(s, HOST_WRITE, nm, raw); printf("%-32s : %3d%% %"PRIu64"\n", - "host_bytes_written", *nm, 32*int48_to_long(raw)); + "host_bytes_written", *nm, 32*int48_to_long(raw)); free(nm); free(raw); @@ -402,14 +414,15 @@ int parse_params(char *str, int number, ...) return 0; } -static int mb_get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int mb_get_additional_smart_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { struct nvme_memblaze_smart_log smart_log; char *desc = - "Get Memblaze vendor specific additional smart log (optionally, for the specified namespace), and show it."; + "Get Memblaze vendor specific additional smart log, and show it."; const char *namespace = "(optional) desired namespace"; const char *raw = "dump output in binary format"; - struct nvme_dev *dev; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; struct config { __u32 namespace_id; bool raw_binary; @@ -442,7 +455,6 @@ static int mb_get_additional_smart_log(int argc, char **argv, struct command *cm if (err > 0) nvme_show_status(err); - dev_close(dev); return err; } @@ -460,12 +472,13 @@ static char *mb_feature_to_string(int feature) } } -static int mb_get_powermanager_status(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int mb_get_powermanager_status(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { const char *desc = "Get Memblaze power management ststus\n (value 0 - 25w, 1 - 20w, 2 - 15w)"; __u32 result; __u32 feature_id = MB_FEAT_POWER_MGMT; - struct nvme_dev *dev; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; int err; OPT_ARGS(opts) = { @@ -477,16 +490,16 @@ static int mb_get_powermanager_status(int argc, char **argv, struct command *cmd return err; struct nvme_get_features_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), + .args_size = sizeof(args), + .fd = dev_fd(dev), .fid = feature_id, .nsid = 0, .sel = 0, .cdw11 = 0, .uuidx = 0, - .data_len = 0, + .data_len = 0, .data = NULL, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .result = &result, }; err = nvme_get_features(&args); @@ -497,16 +510,16 @@ static int mb_get_powermanager_status(int argc, char **argv, struct command *cmd mb_feature_to_string(feature_id), nvme_select_to_string(0), result); else if (err > 0) nvme_show_status(err); - dev_close(dev); return err; } -static int mb_set_powermanager_status(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int mb_set_powermanager_status(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { const char *desc = "Set Memblaze power management status\n (value 0 - 25w, 1 - 20w, 2 - 15w)"; const char *value = "new value of feature (required)"; const char *save = "specifies that the controller shall save the attribute"; - struct nvme_dev *dev; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; __u32 result; int err; @@ -533,8 +546,8 @@ static int mb_set_powermanager_status(int argc, char **argv, struct command *cmd return err; struct nvme_set_features_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), + .args_size = sizeof(args), + .fd = dev_fd(dev), .fid = cfg.feature_id, .nsid = 0, .cdw11 = cfg.value, @@ -542,9 +555,9 @@ static int mb_set_powermanager_status(int argc, char **argv, struct command *cmd .save = cfg.save, .uuidx = 0, .cdw15 = 0, - .data_len = 0, + .data_len = 0, .data = NULL, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .result = &result, }; err = nvme_set_features(&args); @@ -556,14 +569,14 @@ static int mb_set_powermanager_status(int argc, char **argv, struct command *cmd else if (err > 0) nvme_show_status(err); - dev_close(dev); return err; } -#define P2MIN (1) -#define P2MAX (5000) -#define MB_FEAT_HIGH_LATENCY_VALUE_SHIFT (15) -static int mb_set_high_latency_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +#define P2MIN (1) +#define P2MAX (5000) +#define MB_FEAT_HIGH_LATENCY_VALUE_SHIFT (15) +static int mb_set_high_latency_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { const char *desc = "Set Memblaze high latency log\n" " input parameter p1,p2\n" @@ -571,7 +584,7 @@ static int mb_set_high_latency_log(int argc, char **argv, struct command *cmd, s " p2 value: 1 .. 5000 ms"; const char *param = "input parameters"; int param1 = 0, param2 = 0; - struct nvme_dev *dev; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; __u32 result; int err; @@ -598,12 +611,10 @@ static int mb_set_high_latency_log(int argc, char **argv, struct command *cmd, s if (parse_params(cfg.param, 2, ¶m1, ¶m2)) { printf("setfeature: invalid formats %s\n", cfg.param); - dev_close(dev); return -EINVAL; } if ((param1 == 1) && (param2 < P2MIN || param2 > P2MAX)) { printf("setfeature: invalid high io latency threshold %d\n", param2); - dev_close(dev); return -EINVAL; } cfg.value = (param1 << MB_FEAT_HIGH_LATENCY_VALUE_SHIFT) | param2; @@ -632,7 +643,6 @@ static int mb_set_high_latency_log(int argc, char **argv, struct command *cmd, s else if (err > 0) nvme_show_status(err); - dev_close(dev); return err; } @@ -672,7 +682,7 @@ static int find_deadbeef(char *buf) return 0; } -#define TIME_STR_SIZE (44) +#define TIME_STR_SIZE (44) static int glp_high_latency(FILE *fdi, char *buf, int buflen, int print) { struct log_page_high_latency *logEntry; @@ -723,11 +733,12 @@ static int glp_high_latency(FILE *fdi, char *buf, int buflen, int print) return 1; } -static int mb_high_latency_log_print(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int mb_high_latency_log_print(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { const char *desc = "Get Memblaze high latency log"; char buf[LOG_PAGE_SIZE]; - struct nvme_dev *dev; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; FILE *fdi = NULL; int err; @@ -742,14 +753,13 @@ static int mb_high_latency_log_print(int argc, char **argv, struct command *cmd, fdi = fopen(FID_C3_LOG_FILENAME, "w+"); glp_high_latency_show_bar(fdi, DO_PRINT_FLAG); - err = nvme_get_log_simple(dev_fd(dev), GLP_ID_VU_GET_HIGH_LATENCY_LOG, - sizeof(buf), &buf); + err = nvme_get_log_simple(dev_fd(dev), GLP_ID_VU_GET_HIGH_LATENCY_LOG, sizeof(buf), &buf); while (1) { if (!glp_high_latency(fdi, buf, LOG_PAGE_SIZE, DO_PRINT_FLAG)) break; - err = nvme_get_log_simple(dev_fd(dev), GLP_ID_VU_GET_HIGH_LATENCY_LOG, - sizeof(buf), &buf); + err = nvme_get_log_simple(dev_fd(dev), GLP_ID_VU_GET_HIGH_LATENCY_LOG, sizeof(buf), + &buf); if (err) { nvme_show_status(err); break; @@ -758,7 +768,6 @@ static int mb_high_latency_log_print(int argc, char **argv, struct command *cmd, if (fdi) fclose(fdi); - dev_close(dev); return err; } @@ -787,7 +796,7 @@ static int mb_selective_download(int argc, char **argv, struct command *cmd, str int xfer = 4096; void *fw_buf; int selectNo, fw_fd, fw_size, err, offset = 0; - struct nvme_dev *dev; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; struct stat sb; int i; @@ -901,12 +910,11 @@ out_free: out_close: close(fw_fd); out: - dev_close(dev); return err; } static void ioLatencyHistogramOutput(FILE *fd, int index, int start, int end, char *unit0, - char *unit1, unsigned int *pHistogram, int print) + char *unit1, unsigned int *pHistogram, int print) { int len; char string[64], subString0[12], subString1[12]; @@ -917,8 +925,7 @@ static void ioLatencyHistogramOutput(FILE *fd, int index, int start, int end, ch else snprintf(subString1, sizeof(subString1), "%s", "+INF"); len = snprintf(string, sizeof(string), "%-11d %-11s %-11s %-11u\n", - index, subString0, subString1, - pHistogram[index]); + index, subString0, subString1, pHistogram[index]); fwrite(string, 1, len, fd); if (print) printf("%s", string); @@ -960,7 +967,8 @@ int io_latency_histogram(char *file, char *buf, int print, int logid) strcpy(unit[0], "ms"); strcpy(unit[1], "ms"); for (i = 1; i < 32; i++, index++) - ioLatencyHistogramOutput(fdi, index, i, i + 1, unit[0], unit[1], (unsigned int *)buf, print); + ioLatencyHistogramOutput(fdi, index, i, i + 1, unit[0], unit[1], + (unsigned int *)buf, print); for (i = 1; i < 32; i++, index++) { if (i == 31) { @@ -976,9 +984,11 @@ int io_latency_histogram(char *file, char *buf, int print, int logid) strcpy(unit[0], "s"); strcpy(unit[1], "s"); for (i = 1; i < 4; i++, index++) - ioLatencyHistogramOutput(fdi, index, i, i + 1, unit[0], unit[1], (unsigned int *)buf, print); + ioLatencyHistogramOutput(fdi, index, i, i + 1, unit[0], unit[1], + (unsigned int *)buf, print); - ioLatencyHistogramOutput(fdi, index, i, 0x7FFFFFFF, unit[0], unit[1], (unsigned int *)buf, print); + ioLatencyHistogramOutput(fdi, index, i, 0x7FFFFFFF, unit[0], unit[1], + (unsigned int *)buf, print); } else { fPRINT_PARAM1("Unsupported io latency histogram revision\n"); } @@ -993,7 +1003,7 @@ static int mb_lat_stats_log_print(int argc, char **argv, struct command *cmd, st char stats[LOG_PAGE_SIZE]; char f1[] = FID_C1_LOG_FILENAME; char f2[] = FID_C2_LOG_FILENAME; - struct nvme_dev *dev; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; int err; const char *desc = "Get Latency Statistics log and show it."; @@ -1015,8 +1025,7 @@ static int mb_lat_stats_log_print(int argc, char **argv, struct command *cmd, st if (err) return err; - err = nvme_get_log_simple(dev_fd(dev), cfg.write ? 0xc2 : 0xc1, - sizeof(stats), &stats); + err = nvme_get_log_simple(dev_fd(dev), cfg.write ? 0xc2 : 0xc1, sizeof(stats), &stats); if (!err) io_latency_histogram(cfg.write ? f2 : f1, stats, DO_PRINT_FLAG, cfg.write ? GLP_ID_VU_GET_WRITE_LATENCY_HISTOGRAM : @@ -1024,14 +1033,14 @@ static int mb_lat_stats_log_print(int argc, char **argv, struct command *cmd, st else nvme_show_status(err); - dev_close(dev); return err; } -static int memblaze_clear_error_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int memblaze_clear_error_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { char *desc = "Clear Memblaze devices error log."; - struct nvme_dev *dev; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; int err; __u32 result; @@ -1043,9 +1052,9 @@ static int memblaze_clear_error_log(int argc, char **argv, struct command *cmd, }; struct config cfg = { - .feature_id = 0xf7, - .value = 0x534d0001, - .save = 0, + .feature_id = 0xf7, + .value = 0x534d0001, + .save = 0, }; OPT_ARGS(opts) = { @@ -1075,16 +1084,15 @@ static int memblaze_clear_error_log(int argc, char **argv, struct command *cmd, if (err < 0) perror("set-feature"); if (!err) - printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, mb_feature_to_string(cfg.feature_id), cfg.value); + printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id, + mb_feature_to_string(cfg.feature_id), cfg.value); else if (err > 0) nvme_show_status(err); - dev_close(dev); return err; } -static int mb_set_lat_stats(int argc, char **argv, - struct command *command, struct plugin *plugin) +static int mb_set_lat_stats(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = ( "Enable/Disable Latency Statistics Tracking.\n" @@ -1098,7 +1106,7 @@ static int mb_set_lat_stats(int argc, char **argv, const __u32 cdw12 = 0x0; const __u32 data_len = 32; const __u32 save = 0; - struct nvme_dev *dev; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; void *buf = NULL; __u32 result; int err; @@ -1168,12 +1176,10 @@ static int mb_set_lat_stats(int argc, char **argv, case None: err = nvme_get_features(&args_get); if (!err) { - printf( - "Latency Statistics Tracking (FID 0x%X) is currently (%i).\n", - fid, result); + printf("Latency Statistics Tracking (FID 0x%X) is currently (%i).\n", fid, + result); } else { printf("Could not read feature id 0xE2.\n"); - dev_close(dev); return err; } break; @@ -1186,15 +1192,13 @@ static int mb_set_lat_stats(int argc, char **argv, perror("Enable latency tracking"); fprintf(stderr, "Command failed while parsing.\n"); } else { - printf("Successfully set enable bit for FID (0x%X) to %i.\n", - 0xe2, option); + printf("Successfully set enable bit for FID (0x%X) to %i.\n", 0xe2, option); } break; default: printf("%d not supported.\n", option); err = EINVAL; } - dev_close(dev); return err; } @@ -1238,17 +1242,17 @@ struct __packed smart_log_add_item_12 { uint8_t rsvd1; union { struct wear_level wear_level; // 0xad - struct temp_since_born { // 0xe7 + struct __packed temp_since_born { // 0xe7 __le16 max; __le16 min; __le16 curr; } temp_since_born; - struct power_consumption { // 0xe8 + struct __packed power_consumption { // 0xe8 __le16 max; __le16 min; __le16 curr; } power_consumption; - struct temp_since_power_on { // 0xaf + struct __packed temp_since_power_on { // 0xaf __le16 max; __le16 min; __le16 curr; @@ -1268,10 +1272,10 @@ struct __packed smart_log_add_item_10 { uint8_t rsvd[2]; }; -struct smart_log_add { +struct __packed smart_log_add { union { union { - struct smart_log_add_v0 { + struct __packed smart_log_add_v0 { struct smart_log_add_item_12 program_fail_count; struct smart_log_add_item_12 erase_fail_count; struct smart_log_add_item_12 wear_leveling_count; @@ -1300,7 +1304,7 @@ struct smart_log_add { }; union { - struct smart_log_add_v2 { + struct __packed smart_log_add_v2 { struct smart_log_add_item_12 program_fail_count; struct smart_log_add_item_12 erase_fail_count; struct smart_log_add_item_12 wear_leveling_count; @@ -1322,7 +1326,7 @@ struct smart_log_add { struct smart_log_add_item_12 xor_fail_count; struct smart_log_add_item_12 xor_invoked_count; struct smart_log_add_item_12 inflight_read_io_cmd; - struct smart_log_add_item_12 flash_error_media_count; + struct smart_log_add_item_12 inflight_write_io_cmd; struct smart_log_add_item_12 nand_bytes_read; struct smart_log_add_item_12 temp_since_born; struct smart_log_add_item_12 power_consumption; @@ -1334,7 +1338,7 @@ struct smart_log_add { }; union { - struct smart_log_add_v3 { + struct __packed smart_log_add_v3 { struct smart_log_add_item_10 program_fail_count; struct smart_log_add_item_10 erase_fail_count; struct smart_log_add_item_10 wear_leveling_count; @@ -1401,33 +1405,33 @@ static void smart_log_add_v0_print(struct smart_log_add_item_12 *item, int item_ switch (item->id) { case 0xad: printf("min: %d, max: %d, avg: %d\n", - le16_to_cpu(item->wear_level.min), - le16_to_cpu(item->wear_level.max), - le16_to_cpu(item->wear_level.avg)); + le16_to_cpu(item->wear_level.min), + le16_to_cpu(item->wear_level.max), + le16_to_cpu(item->wear_level.avg)); break; case 0xe7: printf("max: %d °C (%d K), min: %d °C (%d K), curr: %d °C (%d K)\n", - K2C(le16_to_cpu(item->temp_since_born.max)), - le16_to_cpu(item->temp_since_born.max), - K2C(le16_to_cpu(item->temp_since_born.min)), - le16_to_cpu(item->temp_since_born.min), - K2C(le16_to_cpu(item->temp_since_born.curr)), - le16_to_cpu(item->temp_since_born.curr)); + K2C(le16_to_cpu(item->temp_since_born.max)), + le16_to_cpu(item->temp_since_born.max), + K2C(le16_to_cpu(item->temp_since_born.min)), + le16_to_cpu(item->temp_since_born.min), + K2C(le16_to_cpu(item->temp_since_born.curr)), + le16_to_cpu(item->temp_since_born.curr)); break; case 0xe8: printf("max: %d, min: %d, curr: %d\n", - le16_to_cpu(item->power_consumption.max), - le16_to_cpu(item->power_consumption.min), - le16_to_cpu(item->power_consumption.curr)); + le16_to_cpu(item->power_consumption.max), + le16_to_cpu(item->power_consumption.min), + le16_to_cpu(item->power_consumption.curr)); break; case 0xaf: printf("max: %d °C (%d K), min: %d °C (%d K), curr: %d °C (%d K)\n", - K2C(le16_to_cpu(item->temp_since_power_on.max)), - le16_to_cpu(item->temp_since_power_on.max), - K2C(le16_to_cpu(item->temp_since_power_on.min)), - le16_to_cpu(item->temp_since_power_on.min), - K2C(le16_to_cpu(item->temp_since_power_on.curr)), - le16_to_cpu(item->temp_since_power_on.curr)); + K2C(le16_to_cpu(item->temp_since_power_on.max)), + le16_to_cpu(item->temp_since_power_on.max), + K2C(le16_to_cpu(item->temp_since_power_on.min)), + le16_to_cpu(item->temp_since_power_on.min), + K2C(le16_to_cpu(item->temp_since_power_on.curr)), + le16_to_cpu(item->temp_since_power_on.curr)); break; default: printf("%" PRIu64 "\n", int48_to_long(item->raw)); @@ -1460,7 +1464,7 @@ static void smart_log_add_v2_print(struct smart_log_add_item_12 *item, int item_ [0xfd] = {18, "xor_fail_count" }, [0xfe] = {19, "xor_invoked_count" }, [0xe5] = {20, "inflight_read_io_cmd" }, - [0xe6] = {21, "flash_error_media_count" }, + [0xe6] = {21, "inflight_write_io_cmd" }, [0xf8] = {22, "nand_bytes_read" }, [0xe7] = {23, "temp_since_born" }, [0xe8] = {24, "power_consumption" }, @@ -1476,33 +1480,33 @@ static void smart_log_add_v2_print(struct smart_log_add_item_12 *item, int item_ switch (item->id) { case 0xad: printf("min: %d, max: %d, avg: %d\n", - le16_to_cpu(item->wear_level.min), - le16_to_cpu(item->wear_level.max), - le16_to_cpu(item->wear_level.avg)); + le16_to_cpu(item->wear_level.min), + le16_to_cpu(item->wear_level.max), + le16_to_cpu(item->wear_level.avg)); break; case 0xe7: printf("max: %d °C (%d K), min: %d °C (%d K), curr: %d °C (%d K)\n", - K2C(le16_to_cpu(item->temp_since_born.max)), - le16_to_cpu(item->temp_since_born.max), - K2C(le16_to_cpu(item->temp_since_born.min)), - le16_to_cpu(item->temp_since_born.min), - K2C(le16_to_cpu(item->temp_since_born.curr)), - le16_to_cpu(item->temp_since_born.curr)); + K2C(le16_to_cpu(item->temp_since_born.max)), + le16_to_cpu(item->temp_since_born.max), + K2C(le16_to_cpu(item->temp_since_born.min)), + le16_to_cpu(item->temp_since_born.min), + K2C(le16_to_cpu(item->temp_since_born.curr)), + le16_to_cpu(item->temp_since_born.curr)); break; case 0xe8: printf("max: %d, min: %d, curr: %d\n", - le16_to_cpu(item->power_consumption.max), - le16_to_cpu(item->power_consumption.min), - le16_to_cpu(item->power_consumption.curr)); + le16_to_cpu(item->power_consumption.max), + le16_to_cpu(item->power_consumption.min), + le16_to_cpu(item->power_consumption.curr)); break; case 0xaf: printf("max: %d °C (%d K), min: %d °C (%d K), curr: %d °C (%d K)\n", - K2C(le16_to_cpu(item->temp_since_power_on.max)), - le16_to_cpu(item->temp_since_power_on.max), - K2C(le16_to_cpu(item->temp_since_power_on.min)), - le16_to_cpu(item->temp_since_power_on.min), - K2C(le16_to_cpu(item->temp_since_power_on.curr)), - le16_to_cpu(item->temp_since_power_on.curr)); + K2C(le16_to_cpu(item->temp_since_power_on.max)), + le16_to_cpu(item->temp_since_power_on.max), + K2C(le16_to_cpu(item->temp_since_power_on.min)), + le16_to_cpu(item->temp_since_power_on.min), + K2C(le16_to_cpu(item->temp_since_power_on.curr)), + le16_to_cpu(item->temp_since_power_on.curr)); break; default: printf("%" PRIu64 "\n", int48_to_long(item->raw)); @@ -1546,9 +1550,9 @@ static void smart_log_add_v3_print(struct smart_log_add_item_10 *item, int item_ switch (item->id) { case 0xad: printf("min: %d, max: %d, avg: %d\n", - le16_to_cpu(item->wear_level.min), - le16_to_cpu(item->wear_level.max), - le16_to_cpu(item->wear_level.avg)); + le16_to_cpu(item->wear_level.min), + le16_to_cpu(item->wear_level.max), + le16_to_cpu(item->wear_level.avg)); break; default: printf("%" PRIu64 "\n", int48_to_long(item->raw)); @@ -1571,13 +1575,13 @@ static void smart_log_add_print(struct smart_log_add *log, const char *devname) switch (version) { case 0: return smart_log_add_v0_print(&log->v0_raw[0], - sizeof(struct smart_log_add_v0) / sizeof(struct smart_log_add_item_12)); + sizeof(struct smart_log_add_v0) / sizeof(struct smart_log_add_item_12)); case 2: return smart_log_add_v2_print(&log->v2_raw[0], - sizeof(struct smart_log_add_v2) / sizeof(struct smart_log_add_item_12)); + sizeof(struct smart_log_add_v2) / sizeof(struct smart_log_add_item_12)); case 3: return smart_log_add_v3_print(&log->v3_raw[0], - sizeof(struct smart_log_add_v3) / sizeof(struct smart_log_add_item_10)); + sizeof(struct smart_log_add_v3) / sizeof(struct smart_log_add_item_10)); case 1: fprintf(stderr, "Version %d: N/A\n", version); @@ -1604,9 +1608,7 @@ static int mb_get_smart_log_add(int argc, char **argv, struct command *cmd, stru OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, "dump the whole log buffer in binary format"), OPT_END()}; - // Open device - - struct nvme_dev *dev = NULL; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; err = parse_and_open(&dev, argc, argv, cmd->help, opts); if (err) @@ -1616,7 +1618,8 @@ static int mb_get_smart_log_add(int argc, char **argv, struct command *cmd, stru struct smart_log_add log = {0}; - err = nvme_get_log_simple(dev_fd(dev), LID_SMART_LOG_ADD, sizeof(struct smart_log_add), &log); + err = nvme_get_log_simple(dev_fd(dev), LID_SMART_LOG_ADD, sizeof(struct smart_log_add), + &log); if (!err) { if (!cfg.raw_binary) smart_log_add_print(&log, dev->name); @@ -1628,9 +1631,6 @@ static int mb_get_smart_log_add(int argc, char **argv, struct command *cmd, stru nvme_show_error("%s: %s", cmd->name, nvme_strerror(errno)); } - // Close device - - dev_close(dev); return err; } @@ -1643,7 +1643,7 @@ struct latency_stats_bucket { struct __packed latency_stats { union { - struct latency_stats_v2_0 { + struct __packed latency_stats_v2_0 { uint32_t minor_version; uint32_t major_version; uint32_t bucket_read_data[32]; @@ -1660,9 +1660,9 @@ struct __packed latency_stats { struct __packed high_latency_log { union { - struct high_latency_log_v1 { + struct __packed high_latency_log_v1 { uint32_t version; - struct high_latency_log_entry { + struct __packed high_latency_log_entry { uint64_t timestamp; // ms uint32_t latency; uint32_t qid; @@ -1688,12 +1688,12 @@ struct __packed high_latency_log { struct __packed performance_stats { union { - struct performance_stats_v1 { + struct __packed performance_stats_v1 { uint8_t version; uint8_t rsvd[3]; - struct performance_stats_timestamp { + struct __packed performance_stats_timestamp { uint8_t timestamp[6]; - struct performance_stats_entry { + struct __packed performance_stats_entry { uint16_t read_iops; // K IOPS uint16_t read_bandwidth; // MiB uint32_t read_latency; // us @@ -1705,12 +1705,36 @@ struct __packed performance_stats { } entries[3600]; } timestamps[24]; } v1; + struct __packed performance_stats_v2 { + uint8_t version; + uint8_t rsvd[3]; + struct __packed performance_stats_timestamp_v2 { + uint8_t timestamp[6]; + struct __packed performance_stats_entry_v2 { + uint16_t read_iops; + uint16_t read_bandwidth; + uint16_t read_latency_avg; + uint16_t read_latency_max; + uint8_t scale_of_read_iops; + uint8_t scale_of_read_bandwidth; + uint8_t scale_of_read_latency_avg; + uint8_t scale_of_read_latency_max; + uint16_t write_iops; + uint16_t write_bandwidth; + uint16_t write_latency_avg; + uint16_t write_latency_max; + uint8_t scale_of_write_iops; + uint8_t scale_of_write_bandwidth; + uint8_t scale_of_write_latency_avg; + uint8_t scale_of_write_latency_max; + } entries[3600]; + } timestamps[24]; + } v2; uint8_t raw[4 + 24 * (6 + 3600 * 24)]; }; }; -static int mb_set_latency_feature(int argc, char **argv, struct command *cmd, - struct plugin *plugin) +static int mb_set_latency_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) { int err = 0; @@ -1728,26 +1752,24 @@ static int mb_set_latency_feature(int argc, char **argv, struct command *cmd, OPT_ARGS(opts) = { OPT_UINT("sel-perf-log", 's', &cfg.perf_monitor, - "Select features to turn on, default: Disable\n" - " bit 0: latency statistics\n" - " bit 1: high latency log\n" - " bit 2: Performance stat"), + "Select features to turn on, default: Disable\n" + " bit 0: latency statistics\n" + " bit 1: high latency log\n" + " bit 2: Performance stat"), OPT_UINT("set-commands-mask", 'm', &cfg.cmd_mask, - "Set Enable, default: Disable\n" - " bit 0: Read commands\n" - " bit 1: high Write commands\n" - " bit 2: De-allocate/TRIM (this bit is not worked for Performance stat.)"), + "Set Enable, default: Disable\n" + " bit 0: Read commands\n" + " bit 1: high Write commands\n" + " bit 2: De-allocate/TRIM (this bit is not worked for Performance stat.)"), OPT_UINT("set-read-threshold", 'r', &cfg.read_threshold, - "set read high latency log threshold, it's a 0-based value and unit is 10ms"), + "set read high latency log threshold, it's a 0-based value and unit is 10ms"), OPT_UINT("set-write-threshold", 'w', &cfg.write_threshold, - "set write high latency log threshold, it's a 0-based value and unit is 10ms"), + "set write high latency log threshold, it's a 0-based value and unit is 10ms"), OPT_UINT("set-trim-threshold", 't', &cfg.de_allocate_trim_threshold, - "set trim high latency log threshold, it's a 0-based value and unit is 10ms"), + "set trim high latency log threshold, it's a 0-based value and unit is 10ms"), OPT_END()}; - // Open device - - struct nvme_dev *dev = NULL; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; err = parse_and_open(&dev, argc, argv, cmd->help, opts); if (err) @@ -1766,9 +1788,9 @@ static int mb_set_latency_feature(int argc, char **argv, struct command *cmd, .cdw11 = 0 | cfg.perf_monitor, .cdw12 = 0 | cfg.cmd_mask, .cdw13 = 0 | - (cfg.read_threshold & 0xff) | - ((cfg.write_threshold & 0xff) << 8) | - ((cfg.de_allocate_trim_threshold & 0xff) << 16), + (cfg.read_threshold & 0xff) | + ((cfg.write_threshold & 0xff) << 8) | + ((cfg.de_allocate_trim_threshold & 0xff) << 16), .cdw15 = 0, .save = 0, .uuidx = 0, @@ -1786,14 +1808,10 @@ static int mb_set_latency_feature(int argc, char **argv, struct command *cmd, else nvme_show_error("%s: %s", cmd->name, nvme_strerror(errno)); - // Close device - - dev_close(dev); return err; } -static int mb_get_latency_feature(int argc, char **argv, struct command *cmd, - struct plugin *plugin) +static int mb_get_latency_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) { int err = 0; @@ -1802,9 +1820,7 @@ static int mb_get_latency_feature(int argc, char **argv, struct command *cmd, OPT_ARGS(opts) = { OPT_END()}; - // Open device - - struct nvme_dev *dev = NULL; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; err = parse_and_open(&dev, argc, argv, cmd->help, opts); if (err) @@ -1835,8 +1851,512 @@ static int mb_get_latency_feature(int argc, char **argv, struct command *cmd, nvme_show_error("%s: %s", cmd->name, nvme_strerror(errno)); } - // Close device + return err; +} + +static void latency_stats_v2_0_print(struct latency_stats *log, int size) +{ + static const struct latency_stats_bucket buckets[0xff] = { + [1] = {"0us", "50us" }, + [2] = {"50us", "100us"}, + [3] = {"100us", "150us"}, + [4] = {"150us", "200us"}, + [5] = {"200us", "300us"}, + [6] = {"300us", "400us"}, + [7] = {"400us", "500us"}, + [8] = {"500us", "600us"}, + [9] = {"600us", "700us"}, + [10] = {"700us", "800us"}, + [11] = {"800us", "900us"}, + [12] = {"900us", "1ms" }, + [13] = {"1ms", "5ms" }, + [14] = {"5ms", "10ms" }, + [15] = {"10ms", "20ms" }, + [16] = {"20ms", "50ms" }, + [17] = {"50ms", "100ms"}, + [18] = {"100ms", "200ms"}, + [19] = {"200ms", "300ms"}, + [20] = {"300ms", "400ms"}, + [21] = {"400ms", "500ms"}, + [22] = {"500ms", "600ms"}, + [23] = {"600ms", "700ms"}, + [24] = {"700ms", "800ms"}, + [25] = {"800ms", "900ms"}, + [26] = {"900ms", "1s" }, + [27] = {"1s", "2s" }, + [28] = {"2s", "3s" }, + [29] = {"3s", "4s" }, + [30] = {"4s", "5s" }, + [31] = {"5s", "8s" }, + [32] = {"8s", "INF" }, + }; + + printf("Bucket 1-32 IO Read Command Data\n"); + printf("-------------------------------------------\n"); + printf("%-12s%-12s%-12s%-12s\n", "Bucket", "Start(>=)", "End(<)", "Value"); + int bucket_count = sizeof(log->v2_0.bucket_read_data) / sizeof(uint32_t); + + for (int i = 0; i < bucket_count; i++) { + printf("%-12u%-12s%-12s%-12u\n", i + 1, buckets[i + 1].start_threshold, + buckets[i + 1].end_threshold, log->v2_0.bucket_read_data[i]); + } + printf("\n"); + + printf("Bucket 1-32 IO Write Command Data\n"); + printf("-------------------------------------------\n"); + printf("%-12s%-12s%-12s%-12s\n", "Bucket", "Start(>=)", "End(<)", "Value"); + bucket_count = sizeof(log->v2_0.bucket_write_data) / sizeof(uint32_t); + + for (int i = 0; i < bucket_count; i++) { + printf("%-12u%-12s%-12s%-12u\n", i + 1, buckets[i + 1].start_threshold, + buckets[i + 1].end_threshold, log->v2_0.bucket_write_data[i]); + } + printf("\n"); + + printf("Bucket 1-32 IO Trim Command Data\n"); + printf("-------------------------------------------\n"); + printf("%-12s%-12s%-12s%-12s\n", "Bucket", "Start(>=)", "End(<)", "Value"); + bucket_count = sizeof(log->v2_0.bucket_trim_data) / sizeof(uint32_t); + + for (int i = 0; i < bucket_count; i++) { + printf("%-12u%-12s%-12s%-12u\n", i + 1, buckets[i + 1].start_threshold, + buckets[i + 1].end_threshold, log->v2_0.bucket_trim_data[i]); + } + printf("\n"); +} + +static void latency_stats_print(struct latency_stats *log, const char *devname) +{ + uint32_t minor_version = *(uint32_t *)&log->raw[0]; + uint32_t major_version = *(uint32_t *)&log->raw[4]; + + printf("Major Version: %u, Minor Version: %u\n", major_version, minor_version); + printf("\n"); + printf("Latency Statistics Log for NVMe device: %s\n", devname); + printf("\n"); + + switch (major_version) { + case 2: + switch (minor_version) { + case 0: + latency_stats_v2_0_print(log, sizeof(struct latency_stats)); + break; + default: + fprintf(stderr, "Major Version %u, Minor Version %u: Not supported yet\n", + major_version, minor_version); + break; + } + break; + + default: + fprintf(stderr, "Major Version %u: Not supported yet\n", major_version); + break; + } +} + +static int mb_get_latency_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + // Get the configuration + + struct config { + bool raw_binary; + }; + + struct config cfg = {0}; + + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", + 'b', + &cfg.raw_binary, + "dump the whole log buffer in binary format"), + OPT_END()}; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + + int err = parse_and_open(&dev, argc, argv, cmd->help, opts); + + if (err) + return err; + + // Get log + + struct latency_stats log = {0}; + + err = nvme_get_log_simple(dev_fd(dev), LID_LATENCY_STATISTICS, sizeof(struct latency_stats), + &log); + if (!err) { + if (!cfg.raw_binary) + latency_stats_print(&log, dev->name); + else + d_raw((unsigned char *)&log, sizeof(struct latency_stats)); + } else if (err > 0) { + nvme_show_status(err); + } else { + nvme_show_error("%s: %s", cmd->name, nvme_strerror(errno)); + } + + return err; +} + +static void high_latency_log_v1_print(struct high_latency_log *log, int size) +{ + printf("%-24s%-12s%-12s%-6s%-6s%-6s%-6s%-12s%-24s%-6s%-6s%-6s%-6s%-6s\n", + "Timestamp", "Latency(us)", "QID", "OpC", "Fuse", "PSDT", "CID", "NSID", "SLBA", + "NLB", "DType", "PInfo", "FUA", "LR"); + + for (int i = 0; i < 1024; i++) { + if (log->v1.entries[i].timestamp == 0) + break; + + // Get the timestamp + + time_t timestamp_ms = log->v1.entries[i].timestamp; + time_t timestamp_s = timestamp_ms / 1000; + int time_ms = timestamp_ms % 1000; + char str_time_s[20] = {0}; + char str_time_ms[32] = {0}; + + strftime(str_time_s, sizeof(str_time_s), "%Y-%m-%d %H:%M:%S", + localtime(×tamp_s)); + snprintf(str_time_ms, sizeof(str_time_ms), "%s.%03d", str_time_s, time_ms); + printf("%-24s", str_time_ms); + + // + printf("%-12" PRIu32, log->v1.entries[i].latency); + printf("%-12" PRIu32, log->v1.entries[i].qid); + printf("%#-6" PRIx32, log->v1.entries[i].opcode); + printf("%-6" PRIu32, log->v1.entries[i].fuse); + printf("%-6" PRIu32, log->v1.entries[i].psdt); + printf("%-6" PRIu32, log->v1.entries[i].cid); + printf("%-12" PRIu32, log->v1.entries[i].nsid); + printf("%-24" PRIu64, log->v1.entries[i].slba); + printf("%-6" PRIu32, log->v1.entries[i].nlb); + printf("%-6" PRIu32, log->v1.entries[i].dtype); + printf("%-6" PRIu32, log->v1.entries[i].pinfo); + printf("%-6" PRIu32, log->v1.entries[i].fua); + printf("%-6" PRIu32, log->v1.entries[i].lr); + printf("\n"); + } +} + +static void high_latency_log_print(struct high_latency_log *log, const char *devname) +{ + uint32_t version = *(uint32_t *)&log->raw[0]; + + printf("Version: %u\n", version); + printf("\n"); + printf("High Latency Log for NVMe device: %s\n", devname); + printf("\n"); + + switch (version) { + case 1: + high_latency_log_v1_print(log, sizeof(struct high_latency_log)); + break; + + default: + fprintf(stderr, "Version %u: Not supported yet\n", version); + break; + } +} + +static int mb_get_high_latency_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + // Get the configuration + + struct config { + bool raw_binary; + }; + + struct config cfg = {0}; + + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", + 'b', + &cfg.raw_binary, + "dump the whole log buffer in binary format"), + OPT_END()}; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + + int err = parse_and_open(&dev, argc, argv, cmd->help, opts); + + if (err) + return err; + + // Get log + + struct high_latency_log log = {0}; + + err = nvme_get_log_simple(dev_fd(dev), LID_HIGH_LATENCY_LOG, + sizeof(struct high_latency_log), &log); + if (!err) { + if (!cfg.raw_binary) + high_latency_log_print(&log, dev->name); + else + d_raw((unsigned char *)&log, sizeof(struct high_latency_log)); + } else if (err > 0) { + nvme_show_status(err); + } else { + nvme_show_error("%s: %s", cmd->name, nvme_strerror(errno)); + } + + return err; +} + +static void performance_stats_v1_print(struct performance_stats *log, int duration) +{ + for (int i = 0; i < duration; i++) { + // Print timestamp + + time_t timestamp_ms = int48_to_long(log->v1.timestamps[i].timestamp); + time_t timestamp_s = timestamp_ms / 1000; + int time_ms = timestamp_ms % 1000; + char time_s[32] = {0}; + + strftime(time_s, sizeof(time_s), "%Y-%m-%d %H:%M:%S", localtime(×tamp_s)); + printf("Timestamp %2d: %s.%03d\n", i + 1, time_s, time_ms); + + // Print entry title + + printf("%-8s%-14s%-21s%-22s%-22s%-15s%-22s%-23s%-23s\n", "Entry", "Read-IOs(K)", + "Read-Bandwidth(MiB)", "Avg-Read-Latency(us)", "Max-Read-Latency(us)", + "Write-IOs(K)", "Write-Bandwidth(MiB)", "Avg-Write-Latency(us)", + "Max-Write-Latency(us)"); + + // Print all entries content + + struct performance_stats_entry entry = {0}; + + for (int j = 0; j < 3600; j++) { + entry.read_iops = + log->v1.timestamps[i].entries[j].read_iops; + entry.read_bandwidth = + log->v1.timestamps[i].entries[j].read_bandwidth; + entry.read_latency = + log->v1.timestamps[i].entries[j].read_latency; + entry.read_latency_max = + log->v1.timestamps[i].entries[j].read_latency_max; + entry.write_iops = + log->v1.timestamps[i].entries[j].write_iops; + entry.write_bandwidth = + log->v1.timestamps[i].entries[j].write_bandwidth; + entry.write_latency = + log->v1.timestamps[i].entries[j].write_latency; + entry.write_latency_max = + log->v1.timestamps[i].entries[j].write_latency_max; + + if (entry.read_iops == 0 && entry.write_iops == 0) + continue; + + printf("%-8u%-14u%-21u%-22u%-22u%-15u%-22u%-23u%-23u\n", + j + 1, + entry.read_iops, + entry.read_bandwidth, + entry.read_iops == 0 ? + 0 : entry.read_latency / (1000 * entry.read_iops), + entry.read_latency_max, + entry.write_iops, + entry.write_bandwidth, + entry.write_iops == 0 ? + 0 : entry.write_latency / (1000 * entry.write_iops), + entry.write_latency_max); + usleep(100); + } + printf("\n"); + } +} + +static void performance_stats_v2_print(struct performance_stats *log, int duration) +{ + for (int i = 0; i < duration; i++) { + // Print timestamp + + time_t timestamp_ms = int48_to_long(log->v2.timestamps[i].timestamp); + time_t timestamp_s = timestamp_ms / 1000; + int time_ms = timestamp_ms % 1000; + char time_s[32] = {0}; + + strftime(time_s, sizeof(time_s), "%Y-%m-%d %H:%M:%S", localtime(×tamp_s)); + printf("Timestamp %2d: %s.%03d\n", i + 1, time_s, time_ms); + + // Print entry title + + printf("%-8s%-23s%-23s%-23s%-23s%-23s%-23s%-23s%-23s\n", + "Entry", + "Read-IOs(IOPS)", "Read-Bandwidth(KiB)", + "Avg-Read-Latency(us)", "Max-Read-Latency(us)", + "Write-IOs(IOPS)", "Write-Bandwidth(KiB)", + "Avg-Write-Latency(us)", "Max-Write-Latency(us)"); + + // Print all entries content + for (int j = 0; j < 3600; j++) { + uint32_t read_iops = + log->v2.timestamps[i].entries[j].read_iops; + uint32_t read_bandwidth = + log->v2.timestamps[i].entries[j].read_bandwidth; + uint32_t read_latency_avg = + log->v2.timestamps[i].entries[j].read_latency_avg; + uint32_t read_latency_max = + log->v2.timestamps[i].entries[j].read_latency_max; + uint32_t scale_of_read_iops = + log->v2.timestamps[i].entries[j].scale_of_read_iops; + uint32_t scale_of_read_bandwidth = + log->v2.timestamps[i].entries[j].scale_of_read_bandwidth; + uint32_t scale_of_read_latency_avg = + log->v2.timestamps[i].entries[j].scale_of_read_latency_avg; + uint32_t scale_of_read_latency_max = + log->v2.timestamps[i].entries[j].scale_of_read_latency_max; + + uint32_t write_iops = + log->v2.timestamps[i].entries[j].write_iops; + uint32_t write_bandwidth = + log->v2.timestamps[i].entries[j].write_bandwidth; + uint32_t write_latency_avg = + log->v2.timestamps[i].entries[j].write_latency_avg; + uint32_t write_latency_max = + log->v2.timestamps[i].entries[j].write_latency_max; + uint32_t scale_of_write_iops = + log->v2.timestamps[i].entries[j].scale_of_write_iops; + uint32_t scale_of_write_bandwidth = + log->v2.timestamps[i].entries[j].scale_of_write_bandwidth; + uint32_t scale_of_write_latency_avg = + log->v2.timestamps[i].entries[j].scale_of_write_latency_avg; + uint32_t scale_of_write_latency_max = + log->v2.timestamps[i].entries[j].scale_of_write_latency_max; + + if (read_iops == 0 && write_iops == 0) + continue; + + while (scale_of_read_iops < 4 && scale_of_read_iops) { + read_iops *= 10; + scale_of_read_iops--; + } + while (scale_of_read_bandwidth < 3 && scale_of_read_bandwidth) { + read_bandwidth *= 1024; + scale_of_read_bandwidth--; + } + while (scale_of_read_latency_avg < 3 && scale_of_read_latency_avg) { + read_latency_avg *= 1000; + scale_of_read_latency_avg--; + } + while (scale_of_read_latency_max < 3 && scale_of_read_latency_max) { + read_latency_max *= 1000; + scale_of_read_latency_max--; + } + + while (scale_of_write_iops < 4 && scale_of_write_iops) { + write_iops *= 10; + scale_of_write_iops--; + } + while (scale_of_write_bandwidth < 3 && scale_of_write_bandwidth) { + write_bandwidth *= 1024; + scale_of_write_bandwidth--; + } + while (scale_of_write_latency_avg < 3 && scale_of_write_latency_avg) { + write_latency_avg *= 1000; + scale_of_write_latency_avg--; + } + while (scale_of_write_latency_max < 3 && scale_of_write_latency_max) { + write_latency_max *= 1000; + scale_of_write_latency_max--; + } + + printf("%-8u%-23u%-23u%-23u%-23u%-23u%-23u%-23u%-23u\n", + j + 1, + read_iops, + read_bandwidth, + read_latency_avg, + read_latency_max, + write_iops, + write_bandwidth, + write_latency_avg, + write_latency_max); + usleep(100); + } + printf("\n"); + } +} + +static void performance_stats_print(struct performance_stats *log, const char *devname, + int duration) +{ + uint8_t version = *(uint8_t *)&log->raw[0]; + + printf("Version: %u\n", version); + printf("\n"); + printf("Performance Stat log for NVMe device: %s\n", devname); + printf("\n"); + + switch (version) { + case 1: + performance_stats_v1_print(log, duration); + break; + case 2: + performance_stats_v2_print(log, duration); + break; + default: + fprintf(stderr, "Version %u: Not supported yet\n", version); + break; + } +} + +static int mb_get_performance_stats(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + // Get the configuration + + struct config { + int duration; + bool raw_binary; + }; + + struct config cfg = {.duration = 1, .raw_binary = false}; + + OPT_ARGS(opts) = { + OPT_UINT("duration", + 'd', + &cfg.duration, + "[1-24] hours: duration of the log to be printed, default is 1 hour"), + OPT_FLAG("raw-binary", + 'b', + &cfg.raw_binary, + "dump the whole log buffer in binary format"), + OPT_END()}; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + + int err = parse_and_open(&dev, argc, argv, cmd->help, opts); + + if (err) + return err; + + // Check parameters + if (cfg.duration < 1 || cfg.duration > 24) { + fprintf(stderr, "duration must be between 1 and 24.\n"); + exit(1); + } + + // Get log + + struct performance_stats log = {0}; + + int log_size = 4 + cfg.duration * sizeof(struct performance_stats_timestamp); + // Get one more timestamp if duration is odd number to avoid non-dw alignment issues + int xfer_size = (cfg.duration % 2) > 0 ? + (4 + (cfg.duration + 1) * sizeof(struct performance_stats_timestamp)) : log_size; + + err = nvme_get_log_simple(dev_fd(dev), LID_PERFORMANCE_STATISTICS, xfer_size, &log); + if (!err) { + if (!cfg.raw_binary) + performance_stats_print(&log, dev->name, cfg.duration); + else + d_raw((unsigned char *)&log, log_size); + } else if (err > 0) { + nvme_show_status(err); + } else { + nvme_show_error("%s: %s", cmd->name, nvme_strerror(errno)); + } - dev_close(dev); return err; } diff --git a/plugins/memblaze/memblaze-nvme.h b/plugins/memblaze/memblaze-nvme.h index e25267b..6c7462f 100644 --- a/plugins/memblaze/memblaze-nvme.h +++ b/plugins/memblaze/memblaze-nvme.h @@ -9,18 +9,36 @@ PLUGIN(NAME("memblaze", "Memblaze vendor specific extensions", NVME_VERSION), COMMAND_LIST( - ENTRY("smart-log-add", "Retrieve Memblaze SMART Log, show it", mb_get_additional_smart_log) - ENTRY("get-pm-status", "Get Memblaze Power Manager Status", mb_get_powermanager_status) - ENTRY("set-pm-status", "Set Memblaze Power Manager Status", mb_set_powermanager_status) - ENTRY("select-download", "Selective Firmware Download", mb_selective_download) - ENTRY("lat-stats", "Enable and disable Latency Statistics logging", mb_set_lat_stats) - ENTRY("lat-stats-print", "Retrieve IO Latency Statistics log, show it", mb_lat_stats_log_print) - ENTRY("lat-log", "Set Memblaze High Latency Log", mb_set_high_latency_log) - ENTRY("lat-log-print", "Output Memblaze High Latency Log", mb_high_latency_log_print) - ENTRY("clear-error-log", "Clear error log", memblaze_clear_error_log) - ENTRY("smart-log-add-x", "Retrieve Memblaze SMART Log, show it", mb_get_smart_log_add) - ENTRY("lat-set-feature-x", "Set Enable/Disable for Latency Monitor feature", mb_set_latency_feature) - ENTRY("lat-get-feature-x", "Get Enabled/Disabled of Latency Monitor feature", mb_get_latency_feature) + ENTRY("smart-log-add", "Retrieve Memblaze SMART Log, show it", + mb_get_additional_smart_log) + ENTRY("get-pm-status", "Get Memblaze Power Manager Status", + mb_get_powermanager_status) + ENTRY("set-pm-status", "Set Memblaze Power Manager Status", + mb_set_powermanager_status) + ENTRY("select-download", "Selective Firmware Download", + mb_selective_download) + ENTRY("lat-stats", "Enable and disable Latency Statistics logging", + mb_set_lat_stats) + ENTRY("lat-stats-print", "Retrieve IO Latency Statistics log, show it", + mb_lat_stats_log_print) + ENTRY("lat-log", "Set Memblaze High Latency Log", + mb_set_high_latency_log) + ENTRY("lat-log-print", "Output Memblaze High Latency Log", + mb_high_latency_log_print) + ENTRY("clear-error-log", "Clear error log", + memblaze_clear_error_log) + ENTRY("smart-log-add-x", "Retrieve Memblaze SMART Log, show it", + mb_get_smart_log_add) + ENTRY("lat-set-feature-x", "Set Enable/Disable for Latency Monitor feature", + mb_set_latency_feature) + ENTRY("lat-get-feature-x", "Get Enabled/Disabled of Latency Monitor feature", + mb_get_latency_feature) + ENTRY("lat-stats-print-x", "Get Latency Statistics log and show it.", + mb_get_latency_stats) + ENTRY("lat-log-print-x", "Get High Latency log and show it.", + mb_get_high_latency_log) + ENTRY("perf-stats-print-x", "Get Performance Stat log and show it.", + mb_get_performance_stats) ) ); diff --git a/plugins/meson.build b/plugins/meson.build index bb4c9ad..146fa2a 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -26,10 +26,11 @@ if json_c_dep.found() 'plugins/wdc/wdc-utils.c', 'plugins/ymtc/ymtc-nvme.c', 'plugins/zns/zns.c', + 'plugins/ssstc/ssstc-nvme.c', ] subdir('solidigm') subdir('ocp') - if conf.has('HAVE_SED_OPAL') + if conf.get('HAVE_SED_OPAL') != 0 subdir('sed') endif endif diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 63a7a79..2782595 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -1,4 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Micron, Inc 2024. + * + * @file: micron-nvme.c + * @brief: This module contains all the constructs needed for micron nvme-cli plugin. + * @authors:Chaithanya Shoba <ashoba@micron.com>, + */ + #include <stdio.h> #include <string.h> #include <ctype.h> @@ -9,6 +17,8 @@ #include <time.h> #include <string.h> #include <libgen.h> +#include <stddef.h> +#include <sys/types.h> #include <sys/stat.h> #include "common.h" #include "nvme.h" @@ -17,6 +27,7 @@ #include "linux/types.h" #include "nvme-print.h" #include "util/cleanup.h" +#include "util/utils.h" #define CREATE_CMD #include "micron-nvme.h" @@ -132,34 +143,24 @@ static enum eDriveModel GetDriveModel(int idx) if (vendor_id == MICRON_VENDOR_ID) { switch (device_id) { case 0x5196: - fallthrough; case 0x51A0: - fallthrough; case 0x51A1: - fallthrough; case 0x51A2: eModel = M51AX; break; case 0x51B0: - fallthrough; case 0x51B1: - fallthrough; case 0x51B2: eModel = M51BX; break; case 0x51C0: - fallthrough; case 0x51C1: - fallthrough; case 0x51C2: - fallthrough; case 0x51C3: eModel = M51CX; break; case 0x5405: - fallthrough; case 0x5406: - fallthrough; case 0x5407: eModel = M5407; break; @@ -226,7 +227,6 @@ static int SetupDebugDataDirectories(char *strSN, char *strFilePath, int length = 0; int nIndex = 0; char *strTemp = NULL; - struct stat dirStat; int j; int k = 0; int i = 0; @@ -304,18 +304,17 @@ static int SetupDebugDataDirectories(char *strSN, char *strFilePath, strMainDirName[nIndex] = '\0'; j = 1; - while (!stat(strMainDirName, &dirStat)) { + while (mkdir(strMainDirName, 0777) < 0) { + if (errno != EEXIST) { + err = -1; + goto exit_status; + } strMainDirName[nIndex] = '\0'; sprintf(strAppend, "-%d", j); strcat(strMainDirName, strAppend); j++; } - if (mkdir(strMainDirName, 0777) < 0) { - err = -1; - goto exit_status; - } - if (strOSDirName) { sprintf(strOSDirName, "%s/%s", strMainDirName, "OS"); if (mkdir(strOSDirName, 0777) < 0) { @@ -331,7 +330,7 @@ static int SetupDebugDataDirectories(char *strSN, char *strFilePath, rmdir(strOSDirName); rmdir(strMainDirName); err = -1; - } + } } exit_status: @@ -1203,14 +1202,8 @@ static void init_d0_log_page(__u8 *buf, __u8 nsze) sprintf(d0_log_page[6].datastr, "0x%x", logD0[1]); } -/* OCP and Vendor specific log data format */ -struct micron_vs_logpage { - char *field; - int size; /* FB client spec version 1.0 sizes - M5410 models */ - int size2; /* FB client spec version 0.7 sizes - M5407 models */ -} /* Smart Health Log information as per OCP spec M51CX models */ -ocp_c0_log_page[] = { +struct request_data ocp_c0_log_page[] = { { "Physical Media Units Written", 16}, { "Physical Media Units Read", 16 }, { "Raw Bad User NAND Block Count", 6}, @@ -1329,82 +1322,6 @@ fb_log_page[] = { { "Log Page GUID", 0, 16}, }; -/* - * Common function to print Micron VS log pages - * - buf: raw log data - * - log_page: format of the data - * - field_count: log field count - * - stats: json object to add fields - * - spec: ocp spec index - */ -static void print_micron_vs_logs(__u8 *buf, struct micron_vs_logpage *log_page, int field_count, - struct json_object *stats, __u8 spec) -{ - __u64 lval_lo, lval_hi; - __u32 ival; - __u16 sval; - __u8 cval, lval[8] = { 0 }; - int field; - int offset = 0; - - for (field = 0; field < field_count; field++) { - char datastr[1024] = { 0 }; - char *sfield = NULL; - int size = !spec ? log_page[field].size : log_page[field].size2; - - if (!size) - continue; - sfield = log_page[field].field; - if (size == 16) { - if (strstr(sfield, "GUID")) { - sprintf(datastr, "0x%"PRIx64"%"PRIx64"", - (uint64_t)le64_to_cpu(*(uint64_t *)(&buf[offset + 8])), - (uint64_t)le64_to_cpu(*(uint64_t *)(&buf[offset]))); - } else { - lval_lo = *((__u64 *)(&buf[offset])); - lval_hi = *((__u64 *)(&buf[offset + 8])); - if (lval_hi) - sprintf(datastr, "0x%"PRIx64"%016"PRIx64"", - le64_to_cpu(lval_hi), le64_to_cpu(lval_lo)); - else - sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); - } - } else if (size == 8) { - lval_lo = *((__u64 *)(&buf[offset])); - sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); - } else if (size == 7) { - /* 7 bytes will be in little-endian format, with last byte as MSB */ - memcpy(&lval[0], &buf[offset], 7); - memcpy((void *)&lval_lo, lval, 8); - sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); - } else if (size == 6) { - ival = *((__u32 *)(&buf[offset])); - sval = *((__u16 *)(&buf[offset + 4])); - lval_lo = (((__u64)sval << 32) | ival); - sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); - } else if (size == 4) { - ival = *((__u32 *)(&buf[offset])); - sprintf(datastr, "0x%x", le32_to_cpu(ival)); - } else if (size == 2) { - sval = *((__u16 *)(&buf[offset])); - sprintf(datastr, "0x%04x", le16_to_cpu(sval)); - } else if (size == 1) { - cval = buf[offset]; - sprintf(datastr, "0x%02x", cval); - } else { - sprintf(datastr, "0"); - } - offset += size; - /* do not print reserved values */ - if (strstr(sfield, "Reserved")) - continue; - if (stats) - json_object_add_value_string(stats, sfield, datastr); - else - printf("%-40s : %-4s\n", sfield, datastr); - } -} - static void print_smart_cloud_health_log(__u8 *buf, bool is_json) { struct json_object *root; @@ -1420,7 +1337,7 @@ static void print_smart_cloud_health_log(__u8 *buf, bool is_json) logPages); } - print_micron_vs_logs(buf, ocp_c0_log_page, field_count, stats, 0); + generic_structure_parser(buf, ocp_c0_log_page, field_count, stats, 0, NULL); if (is_json) { json_array_add_value_object(logPages, stats); @@ -1445,7 +1362,7 @@ static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json, logPages); } - print_micron_vs_logs(buf, fb_log_page, field_count, stats, spec); + generic_structure_parser(buf, fb_log_page, field_count, stats, spec, NULL); /* print last three entries from D0 log page */ if (buf2) { @@ -1599,7 +1516,7 @@ static void print_ext_smart_logs_e1(__u8 *buf, bool is_json) printf("SMART Extended Log:0xE1\n"); } - print_micron_vs_logs(buf, e1_log_page, field_count, stats, 0); + generic_structure_parser(buf, e1_log_page, field_count, stats, 0, NULL); if (is_json) { json_array_add_value_object(logPages, stats); @@ -1768,6 +1685,7 @@ static void GetGenericLogs(int fd, const char *dir) struct nvme_firmware_slot fw_log; struct nvme_cmd_effects_log effects; struct nvme_persistent_event_log pevent_log; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; void *pevent_log_info = NULL; __u32 log_len = 0; @@ -3217,28 +3135,20 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, err = -1; switch (aVendorLogs[i].ucLogPage) { case 0xE1: - fallthrough; case 0xE5: - fallthrough; case 0xE9: err = 1; break; case 0xE2: - fallthrough; case 0xE3: - fallthrough; case 0xE4: - fallthrough; case 0xE8: - fallthrough; case 0xEA: err = get_common_log(dev_fd(dev), aVendorLogs[i].ucLogPage, &dataBuffer, &bSize); break; case 0xC1: - fallthrough; case 0xC2: - fallthrough; case 0xC4: err = GetLogPageSize(dev_fd(dev), aVendorLogs[i].ucLogPage, &bSize); @@ -3247,7 +3157,6 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, &dataBuffer, bSize); break; case 0xE6: - fallthrough; case 0xE7: puiIDDBuf = (unsigned int *)&ctrl; uiMask = puiIDDBuf[1015]; @@ -3273,11 +3182,8 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, } break; case 0xF7: - fallthrough; case 0xF9: - fallthrough; case 0xFC: - fallthrough; case 0xFD: if (eModel == M51BX) (void)NVMEResetLog(dev_fd(dev), aVendorLogs[i].ucLogPage, diff --git a/plugins/micron/micron-nvme.h b/plugins/micron/micron-nvme.h index 4f7b892..c9e3ca7 100644 --- a/plugins/micron/micron-nvme.h +++ b/plugins/micron/micron-nvme.h @@ -1,4 +1,11 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) Micron, Inc 2024. + * + * @file: micron-nvme.h + * @brief: This module contains all the constructs needed for micron nvme-cli plugin. + * @authors:Chaithanya Shoba <ashoba@micron.com>, + */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/micron/micron-nvme diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index 2193ffb..f25941a 100644 --- a/plugins/nbft/nbft-plugin.c +++ b/plugins/nbft/nbft-plugin.c @@ -4,11 +4,12 @@ #include <stdio.h> #include <fnmatch.h> +#include <libnvme.h> #include "nvme-print.h" #include "nvme.h" #include "nbft.h" -#include "libnvme.h" #include "fabrics.h" +#include "util/logging.h" #define CREATE_CMD #include "nbft-plugin.h" @@ -168,7 +169,11 @@ static json_object *ssns_to_json(struct nbft_info_subsystem_ns *ss) || json_object_add_value_int(ss_json, "pdu_header_digest_required", ss->pdu_header_digest_required) || json_object_add_value_int(ss_json, "data_digest_required", - ss->data_digest_required)) + ss->data_digest_required) + || json_object_add_value_int(ss_json, "discovered", + ss->discovered) + || json_object_add_value_int(ss_json, "unavailable", + ss->unavailable)) goto fail; return ss_json; @@ -319,7 +324,7 @@ static int json_show_nbfts(struct list_head *nbft_list, bool show_subsys, bool show_hfi, bool show_discovery) { struct json_object *nbft_json_array, *nbft_json; - struct nbft_file_entry *entry; + struct nbft_file_entry *entry = NULL; nbft_json_array = json_create_array(); if (!nbft_json_array) @@ -510,7 +515,7 @@ static void normal_show_nbfts(struct list_head *nbft_list, bool show_subsys, bool show_hfi, bool show_discovery) { bool not_first = false; - struct nbft_file_entry *entry; + struct nbft_file_entry *entry = NULL; list_for_each(nbft_list, entry, node) { if (not_first) @@ -526,9 +531,10 @@ int show_nbft(int argc, char **argv, struct command *cmd, struct plugin *plugin) struct list_head nbft_list; char *format = "normal"; char *nbft_path = NBFT_SYSFS_PATH; - enum nvme_print_flags flags; + nvme_print_flags_t flags; int ret; bool show_subsys = false, show_hfi = false, show_discovery = false; + unsigned int verbose = 0; OPT_ARGS(opts) = { OPT_FMT("output-format", 'o', &format, "Output format: normal|json"), @@ -536,6 +542,7 @@ int show_nbft(int argc, char **argv, struct command *cmd, struct plugin *plugin) OPT_FLAG("hfi", 'H', &show_hfi, "show NBFT HFIs"), OPT_FLAG("discovery", 'd', &show_discovery, "show NBFT discovery controllers"), OPT_STRING("nbft-path", 0, "STR", &nbft_path, "user-defined path for NBFT tables"), + OPT_INCR("verbose", 'v', &verbose, "Increase logging verbosity"), OPT_END() }; @@ -543,6 +550,9 @@ int show_nbft(int argc, char **argv, struct command *cmd, struct plugin *plugin) if (ret) return ret; + log_level = map_log_level(verbose, false /* quiet */); + nvme_init_default_logging(stderr, log_level, false, false); + ret = validate_output_format(format, &flags); if (ret < 0) return ret; diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index 2ecdcc5..99f0a20 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -46,12 +46,14 @@ enum { enum { ONTAP_C2_LOG_SUPPORTED_LSP = 0x0, ONTAP_C2_LOG_NSINFO_LSP = 0x1, + ONTAP_C2_LOG_PLATFORM_LSP = 0x2, }; enum { - ONTAP_VSERVER_TLV = 0x11, - ONTAP_VOLUME_TLV = 0x12, - ONTAP_NS_TLV = 0x13, + ONTAP_VSERVER_NAME_TLV = 0x11, + ONTAP_VOLUME_NAME_TLV = 0x12, + ONTAP_NS_NAME_TLV = 0x13, + ONTAP_NS_PATH_TLV = 0x14, }; static const char *dev_path = "/dev/"; @@ -134,8 +136,10 @@ static void netapp_get_ontap_labels(char *vsname, char *nspath, unsigned char *log_data) { int lsp, tlv, label_len; - char *vserver_name, *volume_name, *namespace_name; + char *vserver_name, *volume_name, *namespace_name, *namespace_path; char vol_name[ONTAP_LABEL_LEN], ns_name[ONTAP_LABEL_LEN]; + char ns_path[ONTAP_LABEL_LEN]; + bool nspath_tlv_available = false; const char *ontap_vol = "/vol/"; int i, j; @@ -145,9 +149,9 @@ static void netapp_get_ontap_labels(char *vsname, char *nspath, /* lsp not related to nsinfo */ return; - /* get the vserver tlv and name */ + /* get the vserver name tlv */ tlv = *(__u8 *)&log_data[32]; - if (tlv == ONTAP_VSERVER_TLV) { + if (tlv == ONTAP_VSERVER_NAME_TLV) { label_len = (*(__u16 *)&log_data[34]) * 4; vserver_name = (char *)&log_data[36]; ontap_labels_to_str(vsname, vserver_name, label_len); @@ -159,9 +163,9 @@ static void netapp_get_ontap_labels(char *vsname, char *nspath, i = 36 + label_len; j = i + 2; - /* get the volume tlv and name */ + /* get the volume name tlv */ tlv = *(__u8 *)&log_data[i]; - if (tlv == ONTAP_VOLUME_TLV) { + if (tlv == ONTAP_VOLUME_NAME_TLV) { label_len = (*(__u16 *)&log_data[j]) * 4; volume_name = (char *)&log_data[j + 2]; ontap_labels_to_str(vol_name, volume_name, label_len); @@ -173,9 +177,9 @@ static void netapp_get_ontap_labels(char *vsname, char *nspath, i += 4 + label_len; j += 4 + label_len; - /* get the namespace tlv and name */ + /* get the namespace name tlv */ tlv = *(__u8 *)&log_data[i]; - if (tlv == ONTAP_NS_TLV) { + if (tlv == ONTAP_NS_NAME_TLV) { label_len = (*(__u16 *)&log_data[j]) * 4; namespace_name = (char *)&log_data[j + 2]; ontap_labels_to_str(ns_name, namespace_name, label_len); @@ -185,8 +189,25 @@ static void netapp_get_ontap_labels(char *vsname, char *nspath, return; } - snprintf(nspath, ONTAP_NS_PATHLEN, "%s%s%s%s", ontap_vol, + i += 4 + label_len; + j += 4 + label_len; + /* get the namespace path tlv if available */ + tlv = *(__u8 *)&log_data[i]; + if (tlv == ONTAP_NS_PATH_TLV) { + nspath_tlv_available = true; + label_len = (*(__u16 *)&log_data[j]) * 4; + namespace_path = (char *)&log_data[j + 2]; + ontap_labels_to_str(ns_path, namespace_path, label_len); + } + + if (nspath_tlv_available) { + /* set nspath from the corresponding ns_path string */ + snprintf(nspath, ONTAP_NS_PATHLEN, "%s", ns_path); + } else { + /* set nspath by concatenating ontap_vol with ns_name */ + snprintf(nspath, ONTAP_NS_PATHLEN, "%s%s%s%s", ontap_vol, vol_name, "/", ns_name); + } } static void netapp_smdevice_json(struct json_object *devices, char *devname, diff --git a/plugins/ocp/meson.build b/plugins/ocp/meson.build index 64447ff..7835444 100644 --- a/plugins/ocp/meson.build +++ b/plugins/ocp/meson.build @@ -4,5 +4,6 @@ sources += [ 'plugins/ocp/ocp-clear-features.c', 'plugins/ocp/ocp-smart-extended-log.c', 'plugins/ocp/ocp-fw-activation-history.c', + 'plugins/ocp/ocp-telemetry-decode.c', ] diff --git a/plugins/ocp/ocp-clear-features.c b/plugins/ocp/ocp-clear-features.c index 0f49584..731dfea 100644 --- a/plugins/ocp/ocp-clear-features.c +++ b/plugins/ocp/ocp-clear-features.c @@ -18,7 +18,7 @@ static int ocp_clear_feature(int argc, char **argv, const char *desc, const __u8 __u32 result = 0; __u32 clear = 1 << 31; struct nvme_dev *dev; - int uuid_index = 0; + __u8 uuid_index = 0; bool uuid = true; int err; diff --git a/plugins/ocp/ocp-fw-activation-history.c b/plugins/ocp/ocp-fw-activation-history.c index ad96c6b..543042f 100644 --- a/plugins/ocp/ocp-fw-activation-history.c +++ b/plugins/ocp/ocp-fw-activation-history.c @@ -66,7 +66,7 @@ static void ocp_fw_activation_history_normal(const struct fw_activation_history printf(" %-22s%d\n", "activation count:", le16_to_cpu(entry->activation_count)); printf(" %-22s%"PRIu64"\n", "timestamp:", - le64_to_cpu(entry->timestamp)); + (0x0000FFFFFFFFFFFF & le64_to_cpu(entry->timestamp))); printf(" %-22s%"PRIu64"\n", "power cycle count:", le64_to_cpu(entry->power_cycle_count)); printf(" %-22s%.*s\n", "previous firmware:", (int)sizeof(entry->previous_fw), @@ -106,7 +106,7 @@ static void ocp_fw_activation_history_json(const struct fw_activation_history *f json_object_add_value_uint(entry_obj, "activation count", le16_to_cpu(entry->activation_count)); json_object_add_value_uint64(entry_obj, "timestamp", - le64_to_cpu(entry->timestamp)); + (0x0000FFFFFFFFFFFF & le64_to_cpu(entry->timestamp))); json_object_add_value_uint(entry_obj, "power cycle count", le64_to_cpu(entry->power_cycle_count)); @@ -162,7 +162,7 @@ int ocp_fw_activation_history_log(int argc, char **argv, struct command *cmd, if (err) return err; - int uuid_index = 0; + __u8 uuid_index = 0; /* * Best effort attempt at uuid. Otherwise, assume no index (i.e. 0) @@ -207,7 +207,7 @@ int ocp_fw_activation_history_log(int argc, char **argv, struct command *cmd, } if (!err) { - enum nvme_print_flags print_flag; + nvme_print_flags_t print_flag; err = validate_output_format(format, &print_flag); if (err < 0) { diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 53ae0f4..91f4083 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -23,10 +23,12 @@ #include "linux/types.h" #include "util/types.h" #include "nvme-print.h" +#include "nvme-wrap.h" #include "ocp-smart-extended-log.h" #include "ocp-clear-features.h" #include "ocp-fw-activation-history.h" +#include "ocp-telemetry-decode.h" #define CREATE_CMD #include "ocp-nvme.h" @@ -112,6 +114,93 @@ struct __packed feature_latency_monitor { __u8 reserved[4083]; }; +struct erri_entry { + union { + __u8 flags; + struct { + __u8 enable:1; + __u8 single:1; + __u8 rsvd2:6; + }; + }; + __u8 rsvd1; + __le16 type; + union { + __u8 specific[28]; + struct { + __le16 nrtdp; + __u8 rsvd4[26]; + }; + }; +}; + +#define ERRI_ENTRIES_MAX 127 + +enum erri_type { + ERRI_TYPE_CPU_CTRL_HANG = 1, + ERRI_TYPE_NAND_HANG, + ERRI_TYPE_PLP_DEFECT, + ERRI_TYPE_LOGICAL_FIRMWARE_ERROR, + ERRI_TYPE_DRAM_CORRUPT_CRIT, + ERRI_TYPE_DRAM_CORRUPT_NON_CRIT, + ERRI_TYPE_NAND_CORRUPT, + ERRI_TYPE_SRAM_CORRUPT, + ERRI_TYPE_HW_MALFUNCTION, + ERRI_TYPE_NO_MORE_NAND_SPARES, + ERRI_TYPE_INCOMPLETE_SHUTDOWN, +}; + +const char *erri_type_to_string(__le16 type) +{ + switch (type) { + case ERRI_TYPE_CPU_CTRL_HANG: + return "CPU/controller hang"; + case ERRI_TYPE_NAND_HANG: + return "NAND hang"; + case ERRI_TYPE_PLP_DEFECT: + return "PLP defect"; + case ERRI_TYPE_LOGICAL_FIRMWARE_ERROR: + return "logical firmware error"; + case ERRI_TYPE_DRAM_CORRUPT_CRIT: + return "DRAM corruption critical path"; + case ERRI_TYPE_DRAM_CORRUPT_NON_CRIT: + return "DRAM corruption non-critical path"; + case ERRI_TYPE_NAND_CORRUPT: + return "NAND corruption"; + case ERRI_TYPE_SRAM_CORRUPT: + return "SRAM corruption"; + case ERRI_TYPE_HW_MALFUNCTION: + return "HW malfunction"; + case ERRI_TYPE_NO_MORE_NAND_SPARES: + return "no more NAND spares available"; + case ERRI_TYPE_INCOMPLETE_SHUTDOWN: + return "incomplete shutdown"; + default: + break; + } + + return "unknown"; +} + +struct erri_get_cq_entry { + __u32 nume:7; + __u32 rsvd7:25; +}; + +struct erri_config { + char *file; + __u8 number; + __u16 type; + __u16 nrtdp; +}; + +static const char *sel = "[0-3]: current/default/saved/supported"; +static const char *no_uuid = "Skip UUID index search (UUID index not required for OCP 1.0)"; +const char *data = "Error injection data structure entries"; +const char *number = "Number of valid error injection data entries"; +static const char *type = "Error injection type"; +static const char *nrtdp = "Number of reads to trigger device panic"; + static int ocp_print_C3_log_normal(struct nvme_dev *dev, struct ssd_latency_monitor_log *log_data) { @@ -140,7 +229,7 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, printf(" Active Threshold D %d ms\n", C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_d+1)); - printf(" Active Latency Configuration 0x%x \n", + printf(" Active Latency Configuration 0x%x\n", le16_to_cpu(log_data->active_latency_config)); printf(" Active Latency Minimum Window %d ms\n", C3_MINIMUM_WINDOW_INCREMENT * @@ -397,7 +486,7 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) static int get_c3_log_page(struct nvme_dev *dev, char *format) { struct ssd_latency_monitor_log *log_data; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; int ret; __u8 *data; int i; @@ -664,8 +753,8 @@ static const char *eol_plp_failure_mode_to_string(__u8 mode) return "Reserved"; } -static int eol_plp_failure_mode_get(struct nvme_dev *dev, const __u32 nsid, - const __u8 fid, __u8 sel) +static int eol_plp_failure_mode_get(struct nvme_dev *dev, const __u32 nsid, const __u8 fid, + __u8 sel, bool uuid) { __u32 result; int err; @@ -684,6 +773,15 @@ static int eol_plp_failure_mode_get(struct nvme_dev *dev, const __u32 nsid, .result = &result, }; + if (uuid) { + /* OCP 2.0 requires UUID index support */ + err = ocp_get_uuid_index(dev, &args.uuidx); + if (err || !args.uuidx) { + nvme_show_error("ERROR: No OCP UUID index found"); + return err; + } + } + err = nvme_get_features(&args); if (!err) { nvme_show_result("End of Life Behavior (feature: %#0*x): %#0*x (%s: %s)", @@ -705,7 +803,7 @@ static int eol_plp_failure_mode_set(struct nvme_dev *dev, const __u32 nsid, { __u32 result; int err; - int uuid_index = 0; + __u8 uuid_index = 0; if (uuid) { /* OCP 2.0 requires UUID index support */ @@ -716,7 +814,6 @@ static int eol_plp_failure_mode_set(struct nvme_dev *dev, const __u32 nsid, } } - struct nvme_set_features_args args = { .args_size = sizeof(args), .fd = dev_fd(dev), @@ -756,7 +853,7 @@ static int eol_plp_failure_mode(int argc, char **argv, struct command *cmd, "No argument prints current mode."; const char *mode = "[0-3]: default/rom/wtm/normal"; const char *save = "Specifies that the controller shall save the attribute"; - const char *sel = "[0-3,8]: current/default/saved/supported/changed"; + const char *sel = "[0-3]: current/default/saved/supported"; const __u32 nsid = 0; const __u8 fid = 0xc2; struct nvme_dev *dev; @@ -774,14 +871,12 @@ static int eol_plp_failure_mode(int argc, char **argv, struct command *cmd, .sel = 0, }; - OPT_ARGS(opts) = { - OPT_BYTE("mode", 'm', &cfg.mode, mode), - OPT_FLAG("save", 's', &cfg.save, save), - OPT_BYTE("sel", 'S', &cfg.sel, sel), - OPT_FLAG("no-uuid", 'n', NULL, - "Skip UUID index search (UUID index not required for OCP 1.0)"), - OPT_END() - }; + NVME_ARGS(opts, + OPT_BYTE("mode", 'm', &cfg.mode, mode), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_FLAG("no-uuid", 'n', NULL, + "Skip UUID index search (UUID index not required for OCP 1.0)")); err = parse_and_open(&dev, argc, argv, desc, opts); if (err) @@ -792,7 +887,8 @@ static int eol_plp_failure_mode(int argc, char **argv, struct command *cmd, cfg.save, !argconfig_parse_seen(opts, "no-uuid")); else - err = eol_plp_failure_mode_get(dev, nsid, fid, cfg.sel); + err = eol_plp_failure_mode_get(dev, nsid, fid, cfg.sel, + !argconfig_parse_seen(opts, "no-uuid")); dev_close(dev); @@ -804,58 +900,15 @@ static int eol_plp_failure_mode(int argc, char **argv, struct command *cmd, /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Telemetry Log +//global buffers +static __le64 total_log_page_sz; +static __u8 *header_data; +static struct telemetry_str_log_format *log_data; -#define TELEMETRY_HEADER_SIZE 512 -#define TELEMETRY_BYTE_PER_BLOCK 512 -#define TELEMETRY_TRANSFER_SIZE 1024 -#define FILE_NAME_SIZE 2048 - -enum TELEMETRY_TYPE { - TELEMETRY_TYPE_NONE = 0, - TELEMETRY_TYPE_HOST = 7, - TELEMETRY_TYPE_CONTROLLER = 8, - TELEMETRY_TYPE_HOST_0 = 9, - TELEMETRY_TYPE_HOST_1 = 10, -}; +__u8 *ptelemetry_buffer; +__u8 *pstring_buffer; +__u8 *pC9_string_buffer; -struct telemetry_initiated_log { - __u8 LogIdentifier; - __u8 Reserved1[4]; - __u8 IEEE[3]; - __le16 DataArea1LastBlock; - __le16 DataArea2LastBlock; - __le16 DataArea3LastBlock; - __u8 Reserved2[368]; - __u8 DataAvailable; - __u8 DataGenerationNumber; - __u8 ReasonIdentifier[128]; -}; - -struct telemetry_data_area_1 { - __le16 major_version; - __le16 minor_version; - __u8 reserved1[4]; - __le64 timestamp; - __u8 log_page_guid[16]; - __u8 no_of_tps_supp; - __u8 tps; - __u8 reserved2[6]; - __le16 sls; - __u8 reserved3[8]; - __le16 fw_revision; - __u8 reserved4[32]; - __le16 da1_stat_start; - __le16 da1_stat_size; - __le16 da2_stat_start; - __le16 da2_stat_size; - __u8 reserved5[32]; - __u8 event_fifo_da[16]; - __le64 event_fifo_start[16]; - __le64 event_fifo_size[16]; - __u8 reserved6[80]; - __u8 smart_health_info[512]; - __u8 smart_health_info_extended[512]; -}; static void get_serial_number(struct nvme_id_ctrl *ctrl, char *sn) { int i; @@ -867,39 +920,20 @@ static void get_serial_number(struct nvme_id_ctrl *ctrl, char *sn) } } -static int get_telemetry_header(struct nvme_dev *dev, __u32 ns, __u8 tele_type, - __u32 data_len, void *data, __u8 nLSP, __u8 nRAE) -{ - struct nvme_passthru_cmd cmd = { - .opcode = nvme_admin_get_log_page, - .nsid = ns, - .addr = (__u64)(uintptr_t) data, - .data_len = data_len, - }; - - __u32 numd = (data_len >> 2) - 1; - __u16 numdu = numd >> 16; - __u16 numdl = numd & 0xffff; - - cmd.cdw10 = tele_type | (nLSP & 0x0F) << 8 | (nRAE & 0x01) << 15 | (numdl & 0xFFFF) << 16; - cmd.cdw11 = numdu; - cmd.cdw12 = 0; - cmd.cdw13 = 0; - cmd.cdw14 = 0; - - return nvme_submit_admin_passthru(dev_fd(dev), &cmd, NULL); -} - static void print_telemetry_header(struct telemetry_initiated_log *logheader, int tele_type) { if (logheader) { unsigned int i = 0, j = 0; + __u8 dataGenNum; - if (tele_type == TELEMETRY_TYPE_HOST) + if (tele_type == TELEMETRY_TYPE_HOST) { printf("============ Telemetry Host Header ============\n"); - else + dataGenNum = logheader->DataHostGenerationNumber; + } else { printf("========= Telemetry Controller Header =========\n"); + dataGenNum = logheader->DataCtlrGenerationNumber; + } printf("Log Identifier : 0x%02X\n", logheader->LogIdentifier); printf("IEEE : 0x%02X%02X%02X\n", @@ -910,8 +944,10 @@ static void print_telemetry_header(struct telemetry_initiated_log *logheader, le16_to_cpu(logheader->DataArea2LastBlock)); printf("Data Area 3 Last Block : 0x%04X\n", le16_to_cpu(logheader->DataArea3LastBlock)); - printf("Data Available : 0x%02X\n", logheader->DataAvailable); - printf("Data Generation Number : 0x%02X\n", logheader->DataGenerationNumber); + printf("Data Available : 0x%02X\n", + logheader->CtlrDataAvailable); + printf("Data Generation Number : 0x%02X\n", + dataGenNum); printf("Reason Identifier :\n"); for (i = 0; i < 8; i++) { @@ -922,6 +958,7 @@ static void print_telemetry_header(struct telemetry_initiated_log *logheader, printf("===============================================\n\n"); } } + static int get_telemetry_data(struct nvme_dev *dev, __u32 ns, __u8 tele_type, __u32 data_len, void *data, __u8 nLSP, __u8 nRAE, __u64 offset) @@ -935,10 +972,14 @@ static int get_telemetry_data(struct nvme_dev *dev, __u32 ns, __u8 tele_type, __u32 numd = (data_len >> 2) - 1; __u16 numdu = numd >> 16; __u16 numdl = numd & 0xffff; - cmd.cdw10 = tele_type | (nLSP & 0x0F) << 8 | (nRAE & 0x01) << 15 | (numdl & 0xFFFF) << 16; + + cmd.cdw10 = tele_type | + (nLSP & 0x0F) << 8 | + (nRAE & 0x01) << 15 | + (numdl & 0xFFFF) << 16; cmd.cdw11 = numdu; - cmd.cdw12 = offset; - cmd.cdw13 = 0; + cmd.cdw12 = (__u32)(0x00000000FFFFFFFF & offset); + cmd.cdw13 = (__u32)((0xFFFFFFFF00000000 & offset) >> 8); cmd.cdw14 = 0; return nvme_submit_admin_passthru(dev_fd(dev), &cmd, NULL); } @@ -946,121 +987,117 @@ static void print_telemetry_data_area_1(struct telemetry_data_area_1 *da1, int tele_type) { if (da1) { - unsigned int i = 0; + int i = 0; + if (tele_type == TELEMETRY_TYPE_HOST) printf("============ Telemetry Host Data area 1 ============\n"); else printf("========= Telemetry Controller Data area 1 =========\n"); - printf("Major Version : 0x%x\n", le16_to_cpu(da1->major_version)); - printf("Minor Version : 0x%x\n", le16_to_cpu(da1->minor_version)); - for (i = 0; i < 4; i++) - printf("reserved1 : 0x%x\n", da1->reserved1[i]); + printf("Major Version : 0x%x\n", le16_to_cpu(da1->major_version)); + printf("Minor Version : 0x%x\n", le16_to_cpu(da1->minor_version)); printf("Timestamp : %"PRIu64"\n", le64_to_cpu(da1->timestamp)); - for (i = 15; i >= 0; i--) - printf("%x", da1->log_page_guid[i]); - printf("Number Telemetry Profiles Supported : 0x%x\n", da1->no_of_tps_supp); - printf("Telemetry Profile Selected (TPS) : 0x%x\n", da1->tps); - for (i = 0; i < 6; i++) - printf("reserved2 : 0x%x\n", da1->reserved2[i]); - printf("Telemetry String Log Size (SLS) : 0x%x\n", le16_to_cpu(da1->sls)); + printf("Log Page GUID : 0x"); + for (int j = 15; j >= 0; j--) + printf("%02x", da1->log_page_guid[j]); + printf("\n"); + printf("Number Telemetry Profiles Supported : 0x%x\n", + da1->no_of_tps_supp); + printf("Telemetry Profile Selected (TPS) : 0x%x\n", + da1->tps); + printf("Telemetry String Log Size (SLS) : 0x%lx\n", + le64_to_cpu(da1->sls)); + printf("Firmware Revision : "); for (i = 0; i < 8; i++) - printf("reserved3 : 0x%x\n", da1->reserved3[i]); - printf("Firmware Revision : 0x%x\n", le16_to_cpu(da1->fw_revision)); - for (i = 0; i < 32; i++) - printf("reserved4 : 0x%x\n", da1->reserved4[i]); - printf("Data Area 1 Statistic Start : 0x%x\n", le16_to_cpu(da1->da1_stat_start)); - printf("Data Area 1 Statistic Size : 0x%x\n", le16_to_cpu(da1->da1_stat_size)); - printf("Data Area 2 Statistic Start : 0x%x\n", le16_to_cpu(da1->da2_stat_start)); - printf("Data Area 2 Statistic Size : 0x%x\n", le16_to_cpu(da1->da2_stat_size)); - for (i = 0; i < 32; i++) - printf("reserved5 : 0x%x\n", da1->reserved5[i]); - for (i = 0; i < 17; i++){ - printf("Event FIFO %d Data Area : 0x%x\n", i, da1->event_fifo_da[i]); - printf("Event FIFO %d Start : %"PRIu64"\n", i, le64_to_cpu(da1->event_fifo_start[i])); - printf("Event FIFO %d Size : %"PRIu64"\n", i, le64_to_cpu(da1->event_fifo_size[i])); - } - for (i = 0; i < 80; i++) - printf("reserved6 : 0x%x\n", da1->reserved6[i]); - for (i = 0; i < 512; i++){ - printf("SMART / Health Information : 0x%x\n", da1->smart_health_info[i]); - printf("SMART / Health Information Extended : 0x%x\n", da1->smart_health_info_extended[i]); - } - printf("===============================================\n\n"); - } -} -static void print_telemetry_da1_stat(__u8 *da1_stat, int tele_type, __u16 buf_size) -{ - if (da1_stat) { - unsigned int i = 0; - if (tele_type == TELEMETRY_TYPE_HOST) - printf("============ Telemetry Host Data area 1 Statistics ============\n"); - else - printf("========= Telemetry Controller Data area 1 Statistics =========\n"); - while((i + 8) < buf_size) { - printf("Statistics Identifier : 0x%x\n", (da1_stat[i] | da1_stat[i+1] << 8)); - printf("Statistics info : 0x%x\n", da1_stat[i+2]); - printf("NS info : 0x%x\n", da1_stat[i+3]); - printf("Statistic Data Size : 0x%x\n", (da1_stat[i+4] | da1_stat[i+5] << 8)); - printf("Reserved : 0x%x\n", (da1_stat[i+6] | da1_stat[i+7] << 8)); - i = 8 + ((da1_stat[i+4] | da1_stat[i+5] << 8) * 4); - } - printf("===============================================\n\n"); - } -} -static void print_telemetry_da1_fifo(__u8 *da1_fifo, int tele_type, __u16 buf_size) -{ - if (da1_fifo) { - unsigned int i = 0; - if (tele_type == TELEMETRY_TYPE_HOST) - printf("============ Telemetry Host Data area 1 FIFO ============\n"); - else - printf("========= Telemetry Controller Data area 1 FIFO =========\n"); - while((i + 4) < buf_size) { - printf("Debug Event Class Type : 0x%x\n", da1_fifo[i]); - printf("Event ID : 0x%x\n", (da1_fifo[i+1] | da1_fifo[i+2] << 8)); - printf("Event Data Size : 0x%x\n", da1_fifo[3]); - i = 4 + ((da1_fifo[3]) * 4); + printf("%c", (char)da1->fw_revision[i]); + printf("\n"); + printf("Data Area 1 Statistic Start : 0x%lx\n", + le64_to_cpu(da1->da1_stat_start)); + printf("Data Area 1 Statistic Size : 0x%lx\n", + le64_to_cpu(da1->da1_stat_size)); + printf("Data Area 2 Statistic Start : 0x%lx\n", + le64_to_cpu(da1->da2_stat_start)); + printf("Data Area 2 Statistic Size : 0x%lx\n", + le64_to_cpu(da1->da2_stat_size)); + for (i = 0; i < 16; i++) { + printf("Event FIFO %d Data Area : 0x%x\n", + i, da1->event_fifo_da[i]); + printf("Event FIFO %d Start : 0x%"PRIx64"\n", + i, le64_to_cpu(da1->event_fifos[i].start)); + printf("Event FIFO %d Size : 0x%"PRIx64"\n", + i, le64_to_cpu(da1->event_fifos[i].size)); } + printf("SMART / Health Information :\n"); + printf("0x"); + for (i = 0; i < 512; i++) + printf("%02x", da1->smart_health_info[i]); + printf("\n"); + + printf("SMART / Health Information Extended :\n"); + printf("0x"); + for (i = 0; i < 512; i++) + printf("%02x", da1->smart_health_info_extended[i]); + printf("\n"); + printf("===============================================\n\n"); } } -static void print_telemetry_da2_stat(__u8 *da1_stat, int tele_type, __u16 buf_size) +static void print_telemetry_da_stat(struct telemetry_stats_desc *da_stat, + int tele_type, + __u16 buf_size, + __u8 data_area) { - if (da1_stat) { + if (da_stat) { unsigned int i = 0; + struct telemetry_stats_desc *next_da_stat = da_stat; + if (tele_type == TELEMETRY_TYPE_HOST) - printf("============ Telemetry Host Data area 1 Statistics ============\n"); + printf("============ Telemetry Host Data Area %d Statistics ============\n", + data_area); else - printf("========= Telemetry Controller Data area 1 Statistics =========\n"); - while((i + 8) < buf_size) { - printf("Statistics Identifier : 0x%x\n", (da1_stat[i] | da1_stat[i+1] << 8)); - printf("Statistics info : 0x%x\n", da1_stat[i+2]); - printf("NS info : 0x%x\n", da1_stat[i+3]); - printf("Statistic Data Size : 0x%x\n", (da1_stat[i+4] | da1_stat[i+5] << 8)); - printf("Reserved : 0x%x\n", (da1_stat[i+6] | da1_stat[i+7] << 8)); - i = 8 + ((da1_stat[i+4] | da1_stat[i+5] << 8) * 4); + printf("========= Telemetry Controller Data Area %d Statistics =========\n", + data_area); + while ((i + 8) < buf_size) { + print_stats_desc(next_da_stat); + i += 8 + ((next_da_stat->size) * 4); + next_da_stat = (struct telemetry_stats_desc *)((__u64)da_stat + i); + + if ((next_da_stat->id == 0) && (next_da_stat->size == 0)) + break; } printf("===============================================\n\n"); } } -static void print_telemetry_da2_fifo(__u8 *da1_fifo, int tele_type, __u16 buf_size) +static void print_telemetry_da_fifo(struct telemetry_event_desc *da_fifo, + __le64 buf_size, + int tele_type, + int da, + int index) { - if (da1_fifo) { + if (da_fifo) { unsigned int i = 0; + struct telemetry_event_desc *next_da_fifo = da_fifo; + if (tele_type == TELEMETRY_TYPE_HOST) - printf("============ Telemetry Host Data area 1 Statistics ============\n"); + printf("========= Telemetry Host Data area %d Event FIFO %d =========\n", + da, index); else - printf("========= Telemetry Controller Data area 1 Statistics =========\n"); - while((i + 4) < buf_size) { - printf("Debug Event Class Type : 0x%x\n", da1_fifo[i]); - printf("Event ID : 0x%x\n", (da1_fifo[i+1] | da1_fifo[i+2] << 8)); - printf("Event Data Size : 0x%x\n", da1_fifo[3]); - i = 4 + ((da1_fifo[3]) * 4); + printf("====== Telemetry Controller Data area %d Event FIFO %d ======\n", + da, index); + + + while ((i + 4) < buf_size) { + /* Print Event Data */ + print_telemetry_fifo_event(next_da_fifo->class, /* Event class type */ + next_da_fifo->id, /* Event ID */ + next_da_fifo->size, /* Event data size */ + (__u8 *)&next_da_fifo->data); /* Event data */ + + i += (4 + (next_da_fifo->size * 4)); + next_da_fifo = (struct telemetry_event_desc *)((__u64)da_fifo + i); } printf("===============================================\n\n"); } } - static int extract_dump_get_log(struct nvme_dev *dev, char *featurename, char *filename, char *sn, int dumpsize, int transfersize, __u32 nsid, __u8 log_id, __u8 lsp, __u64 offset, bool rae) @@ -1146,10 +1183,12 @@ static int get_telemetry_dump(struct nvme_dev *dev, char *filename, char *sn, enum TELEMETRY_TYPE tele_type, int data_area, bool header_print) { __u32 err = 0, nsid = 0; - __u8 lsp = 0, rae = 0; + __le64 da1_sz = 512, m_512_sz = 0, da1_off = 0, m_512_off = 0, diff = 0, + temp_sz = 0, temp_ofst = 0; + __u8 lsp = 0, rae = 0, flag = 0; + __u8 data[TELEMETRY_HEADER_SIZE] = { 0 }; unsigned int i = 0; - char data[TELEMETRY_TRANSFER_SIZE] = { 0 }; - char data1[1536] = { 0 }; + char data1[TELEMETRY_DATA_SIZE] = { 0 }; char *featurename = 0; struct telemetry_initiated_log *logheader = (struct telemetry_initiated_log *)data; struct telemetry_data_area_1 *da1 = (struct telemetry_data_area_1 *)data1; @@ -1172,51 +1211,245 @@ static int get_telemetry_dump(struct nvme_dev *dev, char *filename, char *sn, rae = 1; } - err = get_telemetry_header(dev, nsid, tele_type, TELEMETRY_HEADER_SIZE, - (void *)data, lsp, rae); - if (err) + /* Get the telemetry header */ + err = get_telemetry_data(dev, nsid, tele_type, TELEMETRY_HEADER_SIZE, + (void *)data, lsp, rae, 0); + if (err) { + printf("get_telemetry_header failed, err: %d.\n", err); return err; + } if (header_print) print_telemetry_header(logheader, tele_type); - err = get_telemetry_data(dev, nsid, tele_type, 1536, + + /* Get the telemetry data */ + err = get_telemetry_data(dev, nsid, tele_type, TELEMETRY_DATA_SIZE, (void *)data1, lsp, rae, 512); - if (err) + if (err) { + printf("get_telemetry_data failed for type: 0x%x, err: %d.\n", tele_type, err); return err; + } + print_telemetry_data_area_1(da1, tele_type); - char *da1_stat = calloc((da1->da1_stat_size * 4), sizeof(char)); - err = get_telemetry_data(dev, nsid, tele_type, (da1->da1_stat_size) * 4, - (void *)da1_stat, lsp, rae, (da1->da1_stat_start) * 4); - if (err) - return err; - print_telemetry_da1_stat((void *)da1_stat, tele_type, (da1->da1_stat_size) * 4); - for (i = 0; i < 17 ; i++){ - if (da1->event_fifo_da[i] == 1){ - char *da1_fifo = calloc((da1->event_fifo_size[i]) * 4, sizeof(char)); - err = get_telemetry_data(dev, nsid, tele_type, (da1->event_fifo_size[i]) * 4, - (void *)da1_stat, lsp, rae, (da1->event_fifo_start[i]) * 4); - if (err) + + /* Print the Data Area 1 Stats */ + if (da1->da1_stat_size != 0) { + diff = 0; + da1_sz = (da1->da1_stat_size) * 4; + m_512_sz = (da1->da1_stat_size) * 4; + da1_off = (da1->da1_stat_start) * 4; + m_512_off = (da1->da1_stat_start) * 4; + temp_sz = (da1->da1_stat_size) * 4; + temp_ofst = (da1->da1_stat_start) * 4; + flag = 0; + + if ((da1_off % 512) > 0) { + m_512_off = (__le64) ((da1_off / 512)); + da1_off = m_512_off * 512; + diff = temp_ofst - da1_off; + flag = 1; + } + + if (da1_sz < 512) + da1_sz = 512; + else if ((da1_sz % 512) > 0) { + if (flag == 0) { + m_512_sz = (__le64) ((da1_sz / 512) + 1); + da1_sz = m_512_sz * 512; + } else { + if (diff < 512) + diff = 1; + else + diff = (diff / 512) * 512; + + m_512_sz = (__le64) ((da1_sz / 512) + 1 + diff + 1); + da1_sz = m_512_sz * 512; + } + } + + char *da1_stat = calloc(da1_sz, sizeof(char)); + + err = get_telemetry_data(dev, nsid, tele_type, da1_sz, + (void *)da1_stat, lsp, rae, da1_off); + if (err) { + printf("get_telemetry_data da1 stats failed, err: %d.\n", err); + return err; + } + + print_telemetry_da_stat((void *)(da1_stat + (temp_ofst - da1_off)), + tele_type, (da1->da1_stat_size) * 4, 1); + } + + /* Print the Data Area 1 Event FIFO's */ + for (i = 0; i < 16 ; i++) { + if ((da1->event_fifo_da[i] == 1) && (da1->event_fifos[i].size != 0)) { + diff = 0; + da1_sz = da1->event_fifos[i].size * 4; + m_512_sz = da1->event_fifos[i].size * 4; + da1_off = da1->event_fifos[i].start * 4; + m_512_off = da1->event_fifos[i].start * 4; + temp_sz = da1->event_fifos[i].size * 4; + temp_ofst = da1->event_fifos[i].start * 4; + flag = 0; + + if ((da1_off % 512) > 0) { + m_512_off = (__le64) ((da1_off / 512)); + da1_off = m_512_off * 512; + diff = temp_ofst - da1_off; + flag = 1; + } + + if (da1_sz < 512) + da1_sz = 512; + else if ((da1_sz % 512) > 0) { + if (flag == 0) { + m_512_sz = (__le64) ((da1_sz / 512) + 1); + da1_sz = m_512_sz * 512; + } else { + if (diff < 512) + diff = 1; + else + diff = (diff / 512) * 512; + + m_512_sz = (__le64) ((da1_sz / 512) + 1 + diff + 1); + da1_sz = m_512_sz * 512; + } + } + + char *da1_fifo = calloc(da1_sz, sizeof(char)); + + err = get_telemetry_data(dev, nsid, tele_type, + (da1->event_fifos[i].size) * 4, + (void *)da1_fifo, lsp, rae, da1_off); + if (err) { + printf("get_telemetry_data da1 event fifos failed, err: %d.\n", + err); return err; - print_telemetry_da1_fifo((void *)da1_fifo, tele_type, (da1->event_fifo_size[i]) * 4); + } + print_telemetry_da_fifo((void *)(da1_fifo + (temp_ofst - da1_off)), + temp_sz, + tele_type, + da1->event_fifo_da[i], + i); } } - char *da2_stat = calloc((da1->da2_stat_size * 4), sizeof(char)); - err = get_telemetry_data(dev, nsid, tele_type, (da1->da2_stat_size) * 4, - (void *)da2_stat, lsp, rae, (da1->da2_stat_start) * 4); - if (err) - return err; - print_telemetry_da2_stat((void *)da2_stat, tele_type, (da1->da2_stat_size) * 4); - for (i = 0; i < 17 ; i++){ - if (da1->event_fifo_da[i] == 2){ - char *da1_fifo = calloc((da1->event_fifo_size[i]) * 4, sizeof(char)); - err = get_telemetry_data(dev, nsid, tele_type, (da1->event_fifo_size[i]) * 4, - (void *)da1_stat, lsp, rae, (da1->event_fifo_start[i]) * 4); - if (err) + + /* Print the Data Area 2 Stats */ + if (da1->da2_stat_size != 0) { + da1_off = (da1->da2_stat_start) * 4; + temp_ofst = (da1->da2_stat_start) * 4; + da1_sz = (da1->da2_stat_size) * 4; + diff = 0; + flag = 0; + + if (da1->da2_stat_start == 0) { + da1_off = 512 + (logheader->DataArea1LastBlock * 512); + temp_ofst = 512 + (le16_to_cpu(logheader->DataArea1LastBlock) * 512); + if ((da1_off % 512) == 0) { + m_512_off = (__le64) (((da1_off) / 512)); + da1_off = m_512_off * 512; + diff = temp_ofst - da1_off; + flag = 1; + } + } else { + + if (((da1_off * 4) % 512) > 0) { + m_512_off = (__le64) ((((da1->da2_stat_start) * 4) / 512)); + da1_off = m_512_off * 512; + diff = ((da1->da2_stat_start) * 4) - da1_off; + flag = 1; + } + } + + if (da1_sz < 512) + da1_sz = 512; + else if ((da1_sz % 512) > 0) { + if (flag == 0) { + m_512_sz = (__le64) ((da1->da2_stat_size / 512) + 1); + da1_sz = m_512_sz * 512; + } else { + if (diff < 512) + diff = 1; + else + diff = (diff / 512) * 512; + m_512_sz = (__le64) ((da1->da2_stat_size / 512) + 1 + diff + 1); + da1_sz = m_512_sz * 512; + } + } + + char *da2_stat = calloc(da1_sz, sizeof(char)); + + err = get_telemetry_data(dev, nsid, tele_type, da1_sz, + (void *)da2_stat, lsp, rae, da1_off); + if (err) { + printf("get_telemetry_data da2 stats failed, err: %d.\n", err); + return err; + } + + print_telemetry_da_stat((void *)(da2_stat + (temp_ofst - da1_off)), + tele_type, + (da1->da2_stat_size) * 4, + 2); + } + + /* Print the Data Area 2 Event FIFO's */ + for (i = 0; i < 16 ; i++) { + if ((da1->event_fifo_da[i] == 2) && (da1->event_fifos[i].size != 0)) { + diff = 0; + da1_sz = da1->event_fifos[i].size * 4; + m_512_sz = da1->event_fifos[i].size * 4; + da1_off = da1->event_fifos[i].start * 4; + m_512_off = da1->event_fifos[i].start * 4; + temp_sz = da1->event_fifos[i].size * 4; + temp_ofst = da1->event_fifos[i].start * 4; + flag = 0; + + if ((da1_off % 512) > 0) { + m_512_off = (__le64) ((da1_off / 512)); + da1_off = m_512_off * 512; + diff = temp_ofst - da1_off; + flag = 1; + } + + if (da1_sz < 512) + da1_sz = 512; + else if ((da1_sz % 512) > 0) { + if (flag == 0) { + m_512_sz = (__le64) ((da1_sz / 512) + 1); + da1_sz = m_512_sz * 512; + } + + else { + if (diff < 512) + diff = 1; + else + diff = (diff / 512) * 512; + + m_512_sz = (__le64) ((da1_sz / 512) + 1 + diff + 1); + da1_sz = m_512_sz * 512; + } + } + + char *da1_fifo = calloc(da1_sz, sizeof(char)); + + err = get_telemetry_data(dev, nsid, tele_type, + (da1->event_fifos[i].size) * 4, + (void *)da1_fifo, lsp, rae, da1_off); + if (err) { + printf("get_telemetry_data da2 event fifos failed, err: %d.\n", + err); return err; - print_telemetry_da2_fifo((void *)da1_fifo, tele_type, (da1->event_fifo_size[i]) * 4); + } + print_telemetry_da_fifo((void *)(da1_fifo + (temp_ofst - da1_off)), + temp_sz, + tele_type, + da1->event_fifo_da[i], + i); } } + printf("------------------------------FIFO End---------------------------\n"); + switch (data_area) { case 1: offset = TELEMETRY_HEADER_SIZE; @@ -1252,43 +1485,283 @@ static int get_telemetry_dump(struct nvme_dev *dev, char *filename, char *sn, return err; } +static int get_telemetry_log_page_data(struct nvme_dev *dev, int tele_type) +{ + char file_path[PATH_MAX]; + void *telemetry_log; + const size_t bs = 512; + struct nvme_telemetry_log *hdr; + size_t full_size, offset = bs; + int err, fd; + + if ((tele_type == TELEMETRY_TYPE_HOST_0) || (tele_type == TELEMETRY_TYPE_HOST_1)) + tele_type = TELEMETRY_TYPE_HOST; + + int log_id = (tele_type == TELEMETRY_TYPE_HOST ? NVME_LOG_LID_TELEMETRY_HOST : + NVME_LOG_LID_TELEMETRY_CTRL); + + hdr = malloc(bs); + telemetry_log = malloc(bs); + if (!hdr || !telemetry_log) { + fprintf(stderr, "Failed to allocate %zu bytes for log: %s\n", + bs, strerror(errno)); + err = -ENOMEM; + goto exit_status; + } + memset(hdr, 0, bs); + + sprintf(file_path, DEFAULT_TELEMETRY_BIN); + fd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + fprintf(stderr, "Failed to open output file %s: %s!\n", + file_path, strerror(errno)); + err = fd; + goto exit_status; + } + + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = hdr, + .args_size = sizeof(args), + .fd = dev_fd(dev), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = log_id, + .len = bs, + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_TELEM_HOST_LSP_CREATE, + .uuidx = NVME_UUID_NONE, + .rae = true, + .ot = false, + }; + + err = nvme_get_log(&args); + if (err < 0) + nvme_show_error("Failed to fetch the log from drive.\n"); + else if (err > 0) { + nvme_show_status(err); + nvme_show_error("Failed to fetch telemetry-header. Error:%d.\n", err); + goto close_fd; + } + + err = write(fd, (void *)hdr, bs); + if (err != bs) { + nvme_show_error("Failed to write data to file.\n"); + goto close_fd; + } + + full_size = (le16_to_cpu(hdr->dalb3) * bs) + offset; + + while (offset != full_size) { + args.log = telemetry_log; + args.lpo = offset; + args.lsp = NVME_LOG_LSP_NONE; + err = nvme_get_log(&args); + if (err < 0) { + nvme_show_error("Failed to fetch the log from drive.\n"); + break; + } else if (err > 0) { + nvme_show_error("Failed to fetch telemetry-log.\n"); + nvme_show_status(err); + break; + } + + err = write(fd, (void *)telemetry_log, bs); + if (err != bs) { + nvme_show_error("Failed to write data to file.\n"); + break; + } + err = 0; + offset += bs; + } + +close_fd: + close(fd); +exit_status: + free(hdr); + free(telemetry_log); + + return err; +} + +static int get_c9_log_page_data(struct nvme_dev *dev, int print_data, int save_bin) +{ + int ret = 0, fd; + __le64 stat_id_str_table_ofst = 0; + __le64 event_str_table_ofst = 0; + __le64 vu_event_str_table_ofst = 0; + __le64 ascii_table_ofst = 0; + char file_path[PATH_MAX]; + + header_data = (__u8 *)malloc(sizeof(__u8) * C9_TELEMETRY_STR_LOG_LEN); + if (!header_data) { + fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); + return -1; + } + memset(header_data, 0, sizeof(__u8) * C9_TELEMETRY_STR_LOG_LEN); + + ret = nvme_get_log_simple(dev_fd(dev), C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE, + C9_TELEMETRY_STR_LOG_LEN, header_data); + + if (!ret) { + log_data = (struct telemetry_str_log_format *)header_data; + if (print_data) { + printf("Statistics Identifier String Table Size = %lld\n", + log_data->sitsz); + printf("Event String Table Size = %lld\n", log_data->estsz); + printf("VU Event String Table Size = %lld\n", log_data->vu_eve_st_sz); + printf("ASCII Table Size = %lld\n", log_data->asctsz); + } + + //Calculating the offset for dynamic fields. + + stat_id_str_table_ofst = log_data->sits * 4; + event_str_table_ofst = log_data->ests * 4; + vu_event_str_table_ofst = log_data->vu_eve_sts * 4; + ascii_table_ofst = log_data->ascts * 4; + total_log_page_sz = C9_TELEMETRY_STR_LOG_LEN + + (log_data->sitsz * 4) + (log_data->estsz * 4) + + (log_data->vu_eve_st_sz * 4) + (log_data->asctsz * 4); + + if (print_data) { + printf("stat_id_str_table_ofst = %lld\n", stat_id_str_table_ofst); + printf("event_str_table_ofst = %lld\n", event_str_table_ofst); + printf("vu_event_str_table_ofst = %lld\n", vu_event_str_table_ofst); + printf("ascii_table_ofst = %lld\n", ascii_table_ofst); + printf("total_log_page_sz = %lld\n", total_log_page_sz); + } + + pC9_string_buffer = (__u8 *)malloc(sizeof(__u8) * total_log_page_sz); + if (!pC9_string_buffer) { + fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); + return -1; + } + memset(pC9_string_buffer, 0, sizeof(__u8) * total_log_page_sz); + + ret = nvme_get_log_simple(dev_fd(dev), C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE, + total_log_page_sz, pC9_string_buffer); + } else + fprintf(stderr, "ERROR : OCP : Unable to read C9 data.\n"); + + if (save_bin) { + sprintf(file_path, DEFAULT_STRING_BIN); + fd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + fprintf(stderr, "Failed to open output file %s: %s!\n", + file_path, strerror(errno)); + goto exit_status; + } + + ret = write(fd, (void *)pC9_string_buffer, total_log_page_sz); + if (ret != total_log_page_sz) + fprintf(stderr, "Failed to flush all data to file!\n"); + + close(fd); + } + +exit_status: + return 0; +} + +int parse_ocp_telemetry_log(struct ocp_telemetry_parse_options *options) +{ + int status = 0; + long telemetry_buffer_size = 0; + long string_buffer_size = 0; + enum nvme_print_flags fmt; + unsigned char log_id; + + if (options->telemetry_log) { + if (strstr((const char *)options->telemetry_log, "bin")) { + // Read the data from the telemetry binary file + ptelemetry_buffer = + read_binary_file(NULL, (const char *)options->telemetry_log, + &telemetry_buffer_size, 1); + if (ptelemetry_buffer == NULL) { + nvme_show_error("Failed to read telemetry-log.\n"); + return -1; + } + } + } else { + nvme_show_error("telemetry-log is empty.\n"); + return -1; + } + + log_id = ptelemetry_buffer[0]; + if ((log_id != NVME_LOG_LID_TELEMETRY_HOST) && (log_id != NVME_LOG_LID_TELEMETRY_CTRL)) { + nvme_show_error("Invalid LogPageId [0x%02X]\n", log_id); + return -1; + } + + if (options->string_log) { + // Read the data from the string binary file + if (strstr((const char *)options->string_log, "bin")) { + pstring_buffer = read_binary_file(NULL, (const char *)options->string_log, + &string_buffer_size, 1); + if (pstring_buffer == NULL) { + nvme_show_error("Failed to read string-log.\n"); + return -1; + } + } + } else { + nvme_show_error("string-log is empty.\n"); + return -1; + } + + status = validate_output_format(options->output_format, &fmt); + if (status < 0) { + nvme_show_error("Invalid output format\n"); + return status; + } + + switch (fmt) { + case NORMAL: + print_ocp_telemetry_normal(options); + break; + case JSON: + print_ocp_telemetry_json(options); + break; + default: + break; + } + + return 0; +} + static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - struct nvme_dev *dev; - int err = 0; - const char *desc = "Retrieve and save telemetry log."; - const char *type = "Telemetry Type; 'host[Create bit]' or 'controller'"; - const char *area = "Telemetry Data Area; 1 or 3"; - const char *file = "Output file name with path;\n" + const char *desc = "Retrieve and parse OCP Telemetry log."; + const char *telemetry_log = "Telemetry log binary;\n 'host.bin' or 'controller.bin'"; + const char *string_log = "String log binary; 'C9.bin'"; + const char *output_file = "Output file name with path;\n" "e.g. '-o ./path/name'\n'-o ./path1/path2/';\n" "If requested path does not exist, the directory will be newly created."; + const char *output_format = "output format normal|json"; + const char *data_area = "Telemetry Data Area; 1 or 2;\n" + "e.g. '-a 1 for Data Area 1.'\n'-a 2 for Data Areas 1 and 2.';\n"; + const char *telemetry_type = "Telemetry Type; 'host' or 'controller'"; + struct nvme_dev *dev; + int err = 0; __u32 nsid = NVME_NSID_ALL; struct stat nvme_stat; char sn[21] = {0,}; struct nvme_id_ctrl ctrl; bool is_support_telemetry_controller; - + struct ocp_telemetry_parse_options opt; int tele_type = 0; int tele_area = 0; - struct config { - char *type; - int area; - char *file; - }; - - struct config cfg = { - .type = NULL, - .area = 0, - .file = NULL, - }; - OPT_ARGS(opts) = { - OPT_STR("telemetry_type", 't', &cfg.type, type), - OPT_INT("telemetry_data_area", 'a', &cfg.area, area), - OPT_FILE("output-file", 'o', &cfg.file, file), + OPT_STR("telemetry-log", 'l', &opt.telemetry_log, telemetry_log), + OPT_STR("string-log", 's', &opt.string_log, string_log), + OPT_FILE("output-file", 'o', &opt.output_file, output_file), + OPT_FMT("output-format", 'f', &opt.output_format, output_format), + OPT_INT("data-area", 'a', &opt.data_area, data_area), + OPT_STR("telemetry-type", 't', &opt.telemetry_type, telemetry_type), OPT_END() }; @@ -1314,36 +1787,84 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, is_support_telemetry_controller = ((ctrl.lpa & 0x8) >> 3); - if (!cfg.type && !cfg.area) { - tele_type = TELEMETRY_TYPE_NONE; - tele_area = 0; - } else if (cfg.type && cfg.area) { - if (!strcmp(cfg.type, "host0")) + if (!opt.data_area) { + nvme_show_result("Missing data-area. Using default data area 1.\n"); + opt.data_area = DATA_AREA_1;//Default data area 1 + } else if (opt.data_area != 1 && opt.data_area != 2) { + nvme_show_result("Invalid data-area specified. Please specify 1 or 2.\n"); + goto out; + } + + tele_area = opt.data_area; + + if (opt.telemetry_type) { + if (!strcmp(opt.telemetry_type, "host0")) tele_type = TELEMETRY_TYPE_HOST_0; - else if (!strcmp(cfg.type, "host1")) + else if (!strcmp(opt.telemetry_type, "host1")) tele_type = TELEMETRY_TYPE_HOST_1; - else if (!strcmp(cfg.type, "controller")) + else if (!strcmp(opt.telemetry_type, "host")) + tele_type = TELEMETRY_TYPE_HOST; + else if (!strcmp(opt.telemetry_type, "controller")) tele_type = TELEMETRY_TYPE_CONTROLLER; + else { + nvme_show_error("telemetry-type should be host or controller.\n"); + goto out; + } + } else { + tele_type = TELEMETRY_TYPE_HOST; //Default Type - Host + nvme_show_result("Missing telemetry-type. Using default - host.\n"); + } - tele_area = cfg.area; + if (!opt.telemetry_log) { + nvme_show_result("\nMissing telemetry-log. Fetching from drive...\n"); + err = get_telemetry_log_page_data(dev, tele_type);//Pull Telemetry log + if (err) { + nvme_show_error("Failed to fetch telemetry-log from the drive.\n"); + goto out; + } + nvme_show_result("telemetry.bin generated. Proceeding with next steps.\n"); + opt.telemetry_log = DEFAULT_TELEMETRY_BIN; + } - if ((tele_area != 1 && tele_area != 3) || - (tele_type == TELEMETRY_TYPE_CONTROLLER && tele_area != 3)) { - printf("\nUnsupported parameters entered.\n"); - printf("Possible combinations; {'host0',1}, {'host0',3}, {'host1',1}, {'host1',3}, {'controller',3}\n"); - return err; + if (!opt.string_log) { + nvme_show_result("Missing string-log. Fetching from drive...\n"); + err = get_c9_log_page_data(dev, 0, 1); //Pull String log + if (err) { + nvme_show_error("Failed to fetch string-log from the drive.\n"); + goto out; } - } else { - printf("\nShould provide these all; 'telemetry_type' and 'telemetry_data_area'\n"); - return err; + nvme_show_result("string.bin generated. Proceeding with next steps.\n"); + opt.string_log = DEFAULT_STRING_BIN; + } + + if (!opt.output_format) { + nvme_show_result("Missing format. Using default format - JSON.\n"); + opt.output_format = DEFAULT_OUTPUT_FORMAT_JSON; } - if (tele_type == TELEMETRY_TYPE_NONE) { + switch (tele_type) { + case TELEMETRY_TYPE_HOST: { + printf("Extracting Telemetry Host Dump (Data Area %d)...\n", tele_area); + err = parse_ocp_telemetry_log(&opt); + if (err) + nvme_show_result("Status:(%x)\n", err); + } + break; + case TELEMETRY_TYPE_CONTROLLER: { + printf("Extracting Telemetry Controller Dump (Data Area %d)...\n", tele_area); + if (is_support_telemetry_controller == true) { + err = parse_ocp_telemetry_log(&opt); + if (err) + nvme_show_result("Status:(%x)\n", err); + } + } + break; + case TELEMETRY_TYPE_NONE: { printf("\n-------------------------------------------------------------\n"); /* Host 0 (lsp == 0) must be executed before Host 1 (lsp == 1). */ printf("\nExtracting Telemetry Host 0 Dump (Data Area 1)...\n"); - err = get_telemetry_dump(dev, cfg.file, sn, + err = get_telemetry_dump(dev, opt.output_file, sn, TELEMETRY_TYPE_HOST_0, 1, true); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); @@ -1352,7 +1873,7 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, printf("\nExtracting Telemetry Host 0 Dump (Data Area 3)...\n"); - err = get_telemetry_dump(dev, cfg.file, sn, + err = get_telemetry_dump(dev, opt.output_file, sn, TELEMETRY_TYPE_HOST_0, 3, false); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); @@ -1361,7 +1882,7 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, printf("\nExtracting Telemetry Host 1 Dump (Data Area 1)...\n"); - err = get_telemetry_dump(dev, cfg.file, sn, + err = get_telemetry_dump(dev, opt.output_file, sn, TELEMETRY_TYPE_HOST_1, 1, true); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); @@ -1370,7 +1891,7 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, printf("\nExtracting Telemetry Host 1 Dump (Data Area 3)...\n"); - err = get_telemetry_dump(dev, cfg.file, sn, + err = get_telemetry_dump(dev, opt.output_file, sn, TELEMETRY_TYPE_HOST_1, 3, false); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); @@ -1380,35 +1901,35 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, printf("\nExtracting Telemetry Controller Dump (Data Area 3)...\n"); if (is_support_telemetry_controller == true) { - err = get_telemetry_dump(dev, cfg.file, sn, + err = get_telemetry_dump(dev, opt.output_file, sn, TELEMETRY_TYPE_CONTROLLER, 3, true); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); } printf("\n-------------------------------------------------------------\n"); - } else if (tele_type == TELEMETRY_TYPE_CONTROLLER) { - printf("Extracting Telemetry Controller Dump (Data Area %d)...\n", tele_area); - - if (is_support_telemetry_controller == true) { - err = get_telemetry_dump(dev, cfg.file, sn, tele_type, tele_area, true); - if (err) - fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); - } - } else { + } + break; + case TELEMETRY_TYPE_HOST_0: + case TELEMETRY_TYPE_HOST_1: + default: { printf("Extracting Telemetry Host(%d) Dump (Data Area %d)...\n", (tele_type == TELEMETRY_TYPE_HOST_0) ? 0 : 1, tele_area); - err = get_telemetry_dump(dev, cfg.file, sn, tele_type, tele_area, true); + err = get_telemetry_dump(dev, opt.output_file, sn, tele_type, tele_area, true); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); } + break; + } - printf("telemetry-log done.\n"); - -return err; + printf("ocp internal-log command completed.\n"); +out: + dev_close(dev); + return err; } + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -1511,7 +2032,7 @@ static void ocp_print_c5_log_binary(struct unsupported_requirement_log *log_data static int get_c5_log_page(struct nvme_dev *dev, char *format) { - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; int ret; __u8 *data; int i; @@ -1585,7 +2106,6 @@ out: return ret; } - static int ocp_unsupported_requirements_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { @@ -1738,7 +2258,7 @@ static void ocp_print_c1_log_binary(struct ocp_error_recovery_log_page *log_data static int get_c1_log_page(struct nvme_dev *dev, char *format) { struct ocp_error_recovery_log_page *log_data; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; int ret; __u8 *data; int i, j; @@ -1954,7 +2474,7 @@ static void ocp_print_c4_log_binary(struct ocp_device_capabilities_log_page *log static int get_c4_log_page(struct nvme_dev *dev, char *format) { struct ocp_device_capabilities_log_page *log_data; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; int ret; __u8 *data; int i, j; @@ -2060,6 +2580,90 @@ static int ocp_device_capabilities_log(int argc, char **argv, struct command *cm /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// +/// Set Telemetry Profile (Feature Identifier C8h) Set Feature + +static int ocp_set_telemetry_profile(struct nvme_dev *dev, __u8 tps) +{ + __u32 result; + int err; + __u8 uuid_index = 0; + + /* OCP 2.0 requires UUID index support */ + err = ocp_get_uuid_index(dev, &uuid_index); + if (err || !uuid_index) { + nvme_show_error("ERROR: No OCP UUID index found"); + return err; + } + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = 0xC8, + .nsid = 0xFFFFFFFF, + .cdw11 = tps, + .cdw12 = 0, + .save = true, + .uuidx = uuid_index, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_set_features(&args); + if (err > 0) { + nvme_show_status(err); + } else if (err < 0) { + nvme_show_perror("Set Telemetry Profile"); + fprintf(stderr, "Command failed while parsing.\n"); + } else { + printf("Successfully Set Telemetry Profile (feature: 0xC8) to below values\n"); + printf("Telemetry Profile Select: 0x%x\n", tps); + } + + return err; +} + +static int ocp_set_telemetry_profile_feature(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Set Telemetry Profile (Feature Identifier C8h) Set Feature."; + const char *tps = "Telemetry Profile Select for device debug data collection"; + struct nvme_dev *dev; + int err; + + struct config { + __u8 tps; + }; + + struct config cfg = { + .tps = 0, + }; + + OPT_ARGS(opts) = { + OPT_BYTE("telemetry-profile-select", 't', &cfg.tps, tps), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + if (argconfig_parse_seen(opts, "telemetry-profile-select")) + err = ocp_set_telemetry_profile(dev, cfg.tps); + else + nvme_show_error("Telemetry Profile Select is a required argument"); + + dev_close(dev); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// /// DSSD Power State (Feature Identifier C7h) Set Feature static int set_dssd_power_state(struct nvme_dev *dev, const __u32 nsid, @@ -2068,7 +2672,7 @@ static int set_dssd_power_state(struct nvme_dev *dev, const __u32 nsid, { __u32 result; int err; - int uuid_index = 0; + __u8 uuid_index = 0; if (uuid) { /* OCP 2.0 requires UUID index support */ @@ -2143,7 +2747,7 @@ static int set_dssd_power_state_feature(int argc, char **argv, struct command *c if (err) return err; - if (argconfig_parse_seen(opts, "power state")) + if (argconfig_parse_seen(opts, "power-state")) err = set_dssd_power_state(dev, nsid, fid, cfg.power_state, cfg.save, !argconfig_parse_seen(opts, "no-uuid")); @@ -2157,140 +2761,374 @@ static int set_dssd_power_state_feature(int argc, char **argv, struct command *c /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// +/// DSSD Power State (Feature Identifier C7h) Get Feature + +static int get_dssd_power_state(struct nvme_dev *dev, const __u32 nsid, + const __u8 fid, __u8 sel, bool uuid) +{ + __u32 result; + int err; + __u8 uuid_index = 0; + + if (uuid) { + /* OCP 2.0 requires UUID index support */ + err = ocp_get_uuid_index(dev, &uuid_index); + if (err || !uuid_index) { + nvme_show_error("ERROR: No OCP UUID index found"); + return err; + } + } + + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = fid, + .nsid = nsid, + .sel = sel, + .cdw11 = 0, + .uuidx = uuid_index, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_get_features(&args); + if (!err) { + printf("get-feature:0xC7 %s value: %#08x\n", nvme_select_to_string(sel), result); + + if (sel == NVME_GET_FEATURES_SEL_SUPPORTED) + nvme_show_select_result(fid, result); + } else { + nvme_show_error("Could not get feature: 0xC7 with sel: %d\n", sel); + } + + return err; +} + +static int get_dssd_power_state_feature(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Define DSSD Power State (Feature Identifier C7h) Get Feature."; + const char *all = "Print out all 3 values at once - Current, Default, and Saved"; + const char *sel = "[0-3]: current/default/saved/supported/"; + const __u32 nsid = 0; + const __u8 fid = 0xC7; + struct nvme_dev *dev; + int i, err; + + struct config { + __u8 sel; + bool all; + }; + + struct config cfg = { + .sel = 0, + .all = false, + }; + + OPT_ARGS(opts) = { + OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_FLAG("all", 'a', NULL, all), + OPT_FLAG("no-uuid", 'n', NULL, + "Skip UUID index search (UUID index not required for OCP 1.0)"), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + if (argconfig_parse_seen(opts, "all")) { + for (i = 0; i < 3; i++) { + err = get_dssd_power_state(dev, nsid, fid, i, + !argconfig_parse_seen(opts, "no-uuid")); + if (err) + break; + } + } else if (argconfig_parse_seen(opts, "sel")) + err = get_dssd_power_state(dev, nsid, fid, cfg.sel, + !argconfig_parse_seen(opts, "no-uuid")); + else + nvme_show_error("Required to have --sel as an argument, or pass the --all flag."); + + dev_close(dev); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// /// plp_health_check_interval static int set_plp_health_check_interval(int argc, char **argv, struct command *cmd, - struct plugin *plugin) -{ - - const char *desc = "Define Issue Set Feature command (FID : 0xC6) PLP Health Check Interval"; - const char *plp_health_interval = "[31:16]:PLP Health Check Interval"; - const char *save = "Specifies that the controller shall save the attribute"; - const __u32 nsid = 0; - const __u8 fid = 0xc6; - struct nvme_dev *dev; - int err; - __u32 result; - int uuid_index = 0; - - struct config { - __le16 plp_health_interval; - bool save; - }; - - struct config cfg = { - .plp_health_interval = 0, - .save = false, - }; - - OPT_ARGS(opts) = { - OPT_BYTE("plp_health_interval", 'p', &cfg.plp_health_interval, plp_health_interval), - OPT_FLAG("save", 's', &cfg.save, save), - OPT_FLAG("no-uuid", 'n', NULL, - "Skip UUID index search (UUID index not required for OCP 1.0)"), - OPT_END() - }; - - err = parse_and_open(&dev, argc, argv, desc, opts); - if (err) - return err; - - - if (!argconfig_parse_seen(opts, "no-uuid")) { - /* OCP 2.0 requires UUID index support */ - err = ocp_get_uuid_index(dev, &uuid_index); - if (err || !uuid_index) { - printf("ERROR: No OCP UUID index found"); - return err; - } - } - - - struct nvme_set_features_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .fid = fid, - .nsid = nsid, - .cdw11 = cfg.plp_health_interval << 16, - .cdw12 = 0, - .save = cfg.save, - .uuidx = uuid_index, - .cdw15 = 0, - .data_len = 0, - .data = NULL, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = &result, - }; - - err = nvme_set_features(&args); - if (err > 0) { - nvme_show_status(err); - } else if (err < 0) { - nvme_show_perror("Define PLP Health Check Interval"); - fprintf(stderr, "Command failed while parsing.\n"); - } else { - printf("Successfully set the PLP Health Check Interval"); - printf("PLP Health Check Interval: 0x%x\n", cfg.plp_health_interval); - printf("Save bit Value: 0x%x\n", cfg.save); - } - return err; + struct plugin *plugin) +{ + + const char *desc = "Define Issue Set Feature command (FID : 0xC6) PLP Health Check Interval"; + const char *plp_health_interval = "[31:16]:PLP Health Check Interval"; + const char *save = "Specifies that the controller shall save the attribute"; + const __u32 nsid = 0; + const __u8 fid = 0xc6; + struct nvme_dev *dev; + int err; + __u32 result; + __u8 uuid_index = 0; + + struct config { + __le16 plp_health_interval; + bool save; + }; + + struct config cfg = { + .plp_health_interval = 0, + .save = false, + }; + + OPT_ARGS(opts) = { + OPT_BYTE("plp_health_interval", 'p', &cfg.plp_health_interval, plp_health_interval), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_FLAG("no-uuid", 'n', NULL, + "Skip UUID index search (UUID index not required for OCP 1.0)"), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + + if (!argconfig_parse_seen(opts, "no-uuid")) { + /* OCP 2.0 requires UUID index support */ + err = ocp_get_uuid_index(dev, &uuid_index); + if (err || !uuid_index) { + printf("ERROR: No OCP UUID index found"); + return err; + } + } + + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = fid, + .nsid = nsid, + .cdw11 = cfg.plp_health_interval << 16, + .cdw12 = 0, + .save = cfg.save, + .uuidx = uuid_index, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_set_features(&args); + if (err > 0) { + nvme_show_status(err); + } else if (err < 0) { + nvme_show_perror("Define PLP Health Check Interval"); + fprintf(stderr, "Command failed while parsing.\n"); + } else { + printf("Successfully set the PLP Health Check Interval"); + printf("PLP Health Check Interval: 0x%x\n", cfg.plp_health_interval); + printf("Save bit Value: 0x%x\n", cfg.save); + } + return err; } static int get_plp_health_check_interval(int argc, char **argv, struct command *cmd, - struct plugin *plugin) -{ - - const char *desc = "Define Issue Get Feature command (FID : 0xC6) PLP Health Check Interval"; - const char *sel = "[0-3,8]: current/default/saved/supported/changed"; - const __u32 nsid = 0; - const __u8 fid = 0xc6; - struct nvme_dev *dev; - __u32 result; - int err; - - struct config { - __u8 sel; - }; - - struct config cfg = { - .sel = 0, - }; - - OPT_ARGS(opts) = { - OPT_BYTE("sel", 'S', &cfg.sel, sel), - OPT_END() - }; - - err = parse_and_open(&dev, argc, argv, desc, opts); - if (err) - return err; - - - struct nvme_get_features_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .fid = fid, - .nsid = nsid, - .sel = cfg.sel, - .cdw11 = 0, - .uuidx = 0, - .data_len = 0, - .data = NULL, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = &result, - }; - - err = nvme_get_features(&args); - if (!err) { - printf("get-feature:0xC6 %s value: %#08x\n", nvme_select_to_string(cfg.sel), result); - - if (cfg.sel == NVME_GET_FEATURES_SEL_SUPPORTED) - nvme_show_select_result(fid, result); - } else { - nvme_show_error("Could not get feature: 0xC6"); - } - - return err; + struct plugin *plugin) +{ + + const char *desc = "Define Issue Get Feature command (FID : 0xC6) PLP Health Check Interval"; + const char *sel = "[0-3,8]: current/default/saved/supported/changed"; + const __u32 nsid = 0; + const __u8 fid = 0xc6; + struct nvme_dev *dev; + __u32 result; + int err; + + struct config { + __u8 sel; + }; + + struct config cfg = { + .sel = 0, + }; + + OPT_ARGS(opts) = { + OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = fid, + .nsid = nsid, + .sel = cfg.sel, + .cdw11 = 0, + .uuidx = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_get_features(&args); + if (!err) { + printf("get-feature:0xC6 %s value: %#08x\n", nvme_select_to_string(cfg.sel), result); + + if (cfg.sel == NVME_GET_FEATURES_SEL_SUPPORTED) + nvme_show_select_result(fid, result); + } else { + nvme_show_error("Could not get feature: 0xC6"); + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/// dssd_async_event_config + +static int set_dssd_async_event_config(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + + const char *desc = "Issue Set Feature command (FID : 0xC9) DSSD Async Event Config"; + const char *epn = "[0]:Enable Panic Notices"; + const char *save = "Specifies that the controller shall save the attribute"; + const __u32 nsid = 0; + const __u8 fid = 0xc9; + struct nvme_dev *dev; + int err; + __u32 result; + __u8 uuid_index = 0; + + struct config { + bool epn; + bool save; + }; + + struct config cfg = { + .epn = false, + .save = false, + }; + + OPT_ARGS(opts) = { + OPT_FLAG("enable-panic-notices", 'e', &cfg.epn, epn), + OPT_FLAG("save", 's', &cfg.save, save), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + /* OCP 2.0 requires UUID index support */ + err = ocp_get_uuid_index(dev, &uuid_index); + if (err || !uuid_index) { + printf("ERROR: No OCP UUID index found\n"); + return err; + } + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = fid, + .nsid = nsid, + .cdw11 = cfg.epn ? 1 : 0, + .cdw12 = 0, + .save = cfg.save, + .uuidx = uuid_index, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_set_features(&args); + if (err > 0) { + nvme_show_status(err); + } else if (err < 0) { + nvme_show_perror("Set DSSD Asynchronous Event Configuration\n"); + fprintf(stderr, "Command failed while parsing.\n"); + } else { + printf("Successfully set the DSSD Asynchronous Event Configuration\n"); + printf("Enable Panic Notices bit Value: 0x%x\n", cfg.epn); + printf("Save bit Value: 0x%x\n", cfg.save); + } + return err; +} + +static int get_dssd_async_event_config(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + + const char *desc = "Issue Get Feature command (FID : 0xC9) DSSD Async Event Config"; + const char *sel = "[0-3]: current/default/saved/supported"; + const __u32 nsid = 0; + const __u8 fid = 0xc9; + struct nvme_dev *dev; + __u32 result; + int err; + + struct config { + __u8 sel; + }; + + struct config cfg = { + .sel = 0, + }; + + OPT_ARGS(opts) = { + OPT_BYTE("sel", 'S', &cfg.sel, sel), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = fid, + .nsid = nsid, + .sel = cfg.sel, + .cdw11 = 0, + .uuidx = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_get_features(&args); + if (!err) { + printf("get-feature:0xC9 %s value: %#08x\n", nvme_select_to_string(cfg.sel), result); + + if (cfg.sel == NVME_GET_FEATURES_SEL_SUPPORTED) + nvme_show_select_result(fid, result); + } else { + nvme_show_error("Could not get feature: 0xC9\n"); + } + + return err; } /////////////////////////////////////////////////////////////////////////////// @@ -2299,645 +3137,807 @@ static int get_plp_health_check_interval(int argc, char **argv, struct command * /////////////////////////////////////////////////////////////////////////////// /// Telemetry String Log Format Log Page (LID : C9h) -/* C9 Telemetry String Log Format Log Page */ -#define C9_GUID_LENGTH 16 -#define C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE 0xC9 -#define C9_TELEMETRY_STR_LOG_LEN 432 -#define C9_TELEMETRY_STR_LOG_SIST_OFST 431 +/* Function declaration for Telemetry String Log Format (LID:C9h) */ +static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *cmd, + struct plugin *plugin); -/** - * struct telemetry_str_log_format - Telemetry String Log Format - * @log_page_version: indicates the version of the mapping this log page uses - * Shall be set to 01h. - * @reserved1: Reserved. - * @log_page_guid: Shall be set to B13A83691A8F408B9EA495940057AA44h. - * @sls: Shall be set to the number of DWORDS in the String Log. - * @reserved2: reserved. - * @sits: shall be set to the number of DWORDS in the Statistics - * Identifier String Table - * @ests: Shall be set to the number of DWORDS from byte 0 of this - * log page to the start of the Event String Table - * @estsz: shall be set to the number of DWORDS in the Event String Table - * @vu_eve_sts: Shall be set to the number of DWORDS from byte 0 of this - * log page to the start of the VU Event String Table - * @vu_eve_st_sz: shall be set to the number of DWORDS in the VU Event String Table - * @ascts: the number of DWORDS from byte 0 of this log page until the ASCII Table Starts. - * @asctsz: the number of DWORDS in the ASCII Table - * @fifo1: FIFO 0 ASCII String - * @fifo2: FIFO 1 ASCII String - * @fifo3: FIFO 2 ASCII String - * @fifo4: FIFO 3 ASCII String - * @fif05: FIFO 4 ASCII String - * @fifo6: FIFO 5 ASCII String - * @fifo7: FIFO 6 ASCII String - * @fifo8: FIFO 7 ASCII String - * @fifo9: FIFO 8 ASCII String - * @fifo10: FIFO 9 ASCII String - * @fif011: FIFO 10 ASCII String - * @fif012: FIFO 11 ASCII String - * @fifo13: FIFO 12 ASCII String - * @fif014: FIFO 13 ASCII String - * @fif015: FIFO 14 ASCII String - * @fif016: FIFO 15 ASCII String - * @reserved3: reserved - */ -struct __attribute__((__packed__)) telemetry_str_log_format { - __u8 log_page_version; - __u8 reserved1[15]; - __u8 log_page_guid[C9_GUID_LENGTH]; - __le64 sls; - __u8 reserved2[24]; - __le64 sits; - __le64 sitsz; - __le64 ests; - __le64 estsz; - __le64 vu_eve_sts; - __le64 vu_eve_st_sz; - __le64 ascts; - __le64 asctsz; - __u8 fifo1[16]; - __u8 fifo2[16]; - __u8 fifo3[16]; - __u8 fifo4[16]; - __u8 fifo5[16]; - __u8 fifo6[16]; - __u8 fifo7[16]; - __u8 fifo8[16]; - __u8 fifo9[16]; - __u8 fifo10[16]; - __u8 fifo11[16]; - __u8 fifo12[16]; - __u8 fifo13[16]; - __u8 fifo14[16]; - __u8 fifo15[16]; - __u8 fifo16[16]; - __u8 reserved3[48]; -}; -/* - * struct statistics_id_str_table_entry - Statistics Identifier String Table Entry - * @vs_si: Shall be set the Vendor Unique Statistic Identifier number. - * @reserved1: Reserved - * @ascii_id_len: Shall be set the number of ASCII Characters that are valid. - * @ascii_id_ofst: Shall be set to the offset from DWORD 0/Byte 0 of the Start - * of the ASCII Table to the first character of the string for - * this Statistic Identifier string.. - * @reserved2 reserved - */ -struct __attribute__((__packed__)) statistics_id_str_table_entry { - __le16 vs_si; - __u8 reserved1; - __u8 ascii_id_len; - __le64 ascii_id_ofst; - __le32 reserved2; -}; +static int ocp_print_C9_log_normal(struct telemetry_str_log_format *log_data, __u8 *log_data_buf) +{ + //calculating the index value for array + __le64 stat_id_index = (log_data->sitsz * 4) / 16; + __le64 eve_id_index = (log_data->estsz * 4) / 16; + __le64 vu_eve_index = (log_data->vu_eve_st_sz * 4) / 16; + __le64 ascii_table_index = (log_data->asctsz * 4); + //Calculating the offset for dynamic fields. + __le64 stat_id_str_table_ofst = log_data->sits * 4; + __le64 event_str_table_ofst = log_data->ests * 4; + __le64 vu_event_str_table_ofst = log_data->vu_eve_sts * 4; + __le64 ascii_table_ofst = log_data->ascts * 4; + struct statistics_id_str_table_entry stat_id_str_table_arr[stat_id_index]; + struct event_id_str_table_entry event_id_str_table_arr[eve_id_index]; + struct vu_event_id_str_table_entry vu_event_id_str_table_arr[vu_eve_index]; + int j; -/* - * struct event_id_str_table_entry - Event Identifier String Table Entry - * @deb_eve_class: Shall be set the Debug Class. - * @ei: Shall be set to the Event Identifier - * @ascii_id_len: Shall be set the number of ASCII Characters that are valid. - * @ascii_id_ofst: This is the offset from DWORD 0/ Byte 0 of the start of the - * ASCII table to the ASCII data for this identifier - * @reserved2 reserved - */ -struct __attribute__((__packed__)) event_id_str_table_entry { - __u8 deb_eve_class; - __le16 ei; - __u8 ascii_id_len; - __le64 ascii_id_ofst; - __le32 reserved2; + printf(" Log Page Version : 0x%x\n", log_data->log_page_version); + + printf(" Reserved : "); + for (j = 0; j < 15; j++) + printf("%d", log_data->reserved1[j]); + printf("\n"); + + printf(" Log page GUID : 0x"); + for (j = C9_GUID_LENGTH - 1; j >= 0; j--) + printf("%x", log_data->log_page_guid[j]); + printf("\n"); + + printf(" Telemetry String Log Size : 0x%lx\n", le64_to_cpu(log_data->sls)); + + printf(" Reserved : "); + for (j = 0; j < 24; j++) + printf("%d", log_data->reserved2[j]); + printf("\n"); + + printf(" Statistics Identifier String Table Start : 0x%lx\n", le64_to_cpu(log_data->sits)); + printf(" Statistics Identifier String Table Size : 0x%lx\n", le64_to_cpu(log_data->sitsz)); + printf(" Event String Table Start : 0x%lx\n", le64_to_cpu(log_data->ests)); + printf(" Event String Table Size : 0x%lx\n", le64_to_cpu(log_data->estsz)); + printf(" VU Event String Table Start : 0x%lx\n", le64_to_cpu(log_data->vu_eve_sts)); + printf(" VU Event String Table Size : 0x%lx\n", le64_to_cpu(log_data->vu_eve_st_sz)); + printf(" ASCII Table Start : 0x%lx\n", le64_to_cpu(log_data->ascts)); + printf(" ASCII Table Size : 0x%lx\n", le64_to_cpu(log_data->asctsz)); + + printf(" FIFO 1 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo1[j], log_data->fifo1[j]); + + printf(" FIFO 2 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo2[j], log_data->fifo2[j]); + + printf(" FIFO 3 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo3[j], log_data->fifo3[j]); + + printf(" FIFO 4 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo4[j], log_data->fifo4[j]); + + printf(" FIFO 5 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo5[j], log_data->fifo5[j]); + + printf(" FIFO 6 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo6[j], log_data->fifo6[j]); + + printf(" FIFO 7 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo7[j], log_data->fifo7[j]); + + printf(" FIFO 8 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo8[j], log_data->fifo8[j]); + + printf(" FIFO 9 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo9[j], log_data->fifo9[j]); + + printf(" FIFO 10 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo10[j], log_data->fifo10[j]); + + printf(" FIFO 11 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo11[j], log_data->fifo11[j]); + + printf(" FIFO 12 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo12[j], log_data->fifo12[j]); + + printf(" FIFO 13 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo13[j], log_data->fifo13[j]); + + printf(" FIFO 14 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo14[j], log_data->fifo14[j]); + + printf(" FIFO 15 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo15[j], log_data->fifo16[j]); + + printf(" FIFO 16 ASCII String\n"); + printf(" index value ascii_val\n"); + for (j = 0; j < 16; j++) + printf(" %d %d %c \n", j, log_data->fifo16[j], log_data->fifo16[j]); + + printf(" Reserved : "); + for (j = 0; j < 48; j++) + printf("%d", log_data->reserved3[j]); + printf("\n"); + + + if (log_data->sitsz != 0) { + memcpy(stat_id_str_table_arr, + (__u8 *)log_data_buf + stat_id_str_table_ofst, + (log_data->sitsz * 4)); + printf(" Statistics Identifier String Table\n"); + for (j = 0; j < stat_id_index; j++) { + printf(" Vendor Specific Statistic Identifier : 0x%x\n", + le16_to_cpu(stat_id_str_table_arr[j].vs_si)); + printf(" Reserved : 0x%x\n", + stat_id_str_table_arr[j].reserved1); + printf(" ASCII ID Length : 0x%x\n", + stat_id_str_table_arr[j].ascii_id_len); + printf(" ASCII ID offset : 0x%lx\n", + le64_to_cpu(stat_id_str_table_arr[j].ascii_id_ofst)); + printf(" Reserved : 0x%x\n", + stat_id_str_table_arr[j].reserved2); + } + } + + + if (log_data->estsz != 0) { + memcpy(event_id_str_table_arr, (__u8 *)log_data_buf + + event_str_table_ofst, (log_data->estsz * 4)); + printf(" Event Identifier String Table Entry\n"); + for (j = 0; j < eve_id_index; j++) { + printf(" Debug Event Class : 0x%x\n", + event_id_str_table_arr[j].deb_eve_class); + printf(" Event Identifier : 0x%x\n", + le16_to_cpu(event_id_str_table_arr[j].ei)); + printf(" ASCII ID Length : 0x%x\n", + event_id_str_table_arr[j].ascii_id_len); + printf(" ASCII ID offset : 0x%lx\n", + le64_to_cpu(event_id_str_table_arr[j].ascii_id_ofst)); + printf(" Reserved : 0x%x\n", + event_id_str_table_arr[j].reserved2); + + } + } + + if (log_data->vu_eve_st_sz != 0) { + memcpy(vu_event_id_str_table_arr, (__u8 *)log_data_buf + + vu_event_str_table_ofst, (log_data->vu_eve_st_sz * 4)); + printf(" VU Event Identifier String Table Entry\n"); + for (j = 0; j < vu_eve_index; j++) { + printf(" Debug Event Class : 0x%x\n", + vu_event_id_str_table_arr[j].deb_eve_class); + printf(" VU Event Identifier : 0x%x\n", + le16_to_cpu(vu_event_id_str_table_arr[j].vu_ei)); + printf(" ASCII ID Length : 0x%x\n", + vu_event_id_str_table_arr[j].ascii_id_len); + printf(" ASCII ID offset : 0x%lx\n", + le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_ofst)); + printf(" Reserved : 0x%x\n", + vu_event_id_str_table_arr[j].reserved); + + } + } + + if (log_data->asctsz != 0) { + printf(" ASCII Table\n"); + printf(" Byte Data_Byte ASCII_Character\n"); + for (j = 0; j < ascii_table_index; j++) + printf(" %lld %d %c\n", + ascii_table_ofst+j, log_data_buf[ascii_table_ofst + j], + (char)log_data_buf[ascii_table_ofst + j]); + } + + return 0; +} + +static int ocp_print_C9_log_json(struct telemetry_str_log_format *log_data, __u8 *log_data_buf) +{ + struct json_object *root = json_create_object(); + char res_arr[48]; + char *res = res_arr; + char guid_buf[C9_GUID_LENGTH]; + char *guid = guid_buf; + char fifo_arr[16]; + char *fifo = fifo_arr; + char buf[128]; + //calculating the index value for array + __le64 stat_id_index = (log_data->sitsz * 4) / 16; + __le64 eve_id_index = (log_data->estsz * 4) / 16; + __le64 vu_eve_index = (log_data->vu_eve_st_sz * 4) / 16; + __le64 ascii_table_index = (log_data->asctsz * 4); + //Calculating the offset for dynamic fields. + __le64 stat_id_str_table_ofst = log_data->sits * 4; + __le64 event_str_table_ofst = log_data->ests * 4; + __le64 vu_event_str_table_ofst = log_data->vu_eve_sts * 4; + __le64 ascii_table_ofst = log_data->ascts * 4; + struct statistics_id_str_table_entry stat_id_str_table_arr[stat_id_index]; + struct event_id_str_table_entry event_id_str_table_arr[eve_id_index]; + struct vu_event_id_str_table_entry vu_event_id_str_table_arr[vu_eve_index]; + __u8 ascii_table_info_arr[ascii_table_index]; + char ascii_buf[ascii_table_index]; + char *ascii = ascii_buf; + int j; + + json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); + + memset((__u8 *)res, 0, 15); + for (j = 0; j < 15; j++) + res += sprintf(res, "%d", log_data->reserved1[j]); + json_object_add_value_string(root, "Reserved", res_arr); + + memset((void *)guid, 0, C9_GUID_LENGTH); + for (j = C9_GUID_LENGTH - 1; j >= 0; j--) + guid += sprintf(guid, "%02x", log_data->log_page_guid[j]); + json_object_add_value_string(root, "Log page GUID", guid_buf); + + json_object_add_value_int(root, "Telemetry String Log Size", le64_to_cpu(log_data->sls)); + + memset((__u8 *)res, 0, 24); + for (j = 0; j < 24; j++) + res += sprintf(res, "%d", log_data->reserved2[j]); + json_object_add_value_string(root, "Reserved", res_arr); + + json_object_add_value_int(root, "Statistics Identifier String Table Start", le64_to_cpu(log_data->sits)); + json_object_add_value_int(root, "Event String Table Start", le64_to_cpu(log_data->ests)); + json_object_add_value_int(root, "Event String Table Size", le64_to_cpu(log_data->estsz)); + json_object_add_value_int(root, "VU Event String Table Start", le64_to_cpu(log_data->vu_eve_sts)); + json_object_add_value_int(root, "VU Event String Table Size", le64_to_cpu(log_data->vu_eve_st_sz)); + json_object_add_value_int(root, "ASCII Table Start", le64_to_cpu(log_data->ascts)); + json_object_add_value_int(root, "ASCII Table Size", le64_to_cpu(log_data->asctsz)); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo1[j]); + json_object_add_value_string(root, "FIFO 1 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo2[j]); + json_object_add_value_string(root, "FIFO 2 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo3[j]); + json_object_add_value_string(root, "FIFO 3 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo4[j]); + json_object_add_value_string(root, "FIFO 4 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo5[j]); + json_object_add_value_string(root, "FIFO 5 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo6[j]); + json_object_add_value_string(root, "FIFO 6 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo7[j]); + json_object_add_value_string(root, "FIFO 7 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo8[j]); + json_object_add_value_string(root, "FIFO 8 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo9[j]); + json_object_add_value_string(root, "FIFO 9 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo10[j]); + json_object_add_value_string(root, "FIFO 10 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo11[j]); + json_object_add_value_string(root, "FIFO 11 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo12[j]); + json_object_add_value_string(root, "FIFO 12 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo13[j]); + json_object_add_value_string(root, "FIFO 13 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo14[j]); + json_object_add_value_string(root, "FIFO 14 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo15[j]); + json_object_add_value_string(root, "FIFO 15 ASCII String", fifo_arr); + + memset((void *)fifo, 0, 16); + for (j = 0; j < 16; j++) + fifo += sprintf(fifo, "%c", log_data->fifo16[j]); + json_object_add_value_string(root, "FIFO 16 ASCII String", fifo_arr); + + memset((__u8 *)res, 0, 48); + for (j = 0; j < 48; j++) + res += sprintf(res, "%d", log_data->reserved3[j]); + json_object_add_value_string(root, "Reserved", res_arr); + + if (log_data->sitsz != 0) { + + memcpy(stat_id_str_table_arr, + (__u8 *)log_data_buf + stat_id_str_table_ofst, + (log_data->sitsz * 4)); + struct json_object *stat_table = json_create_object(); + + for (j = 0; j < stat_id_index; j++) { + struct json_object *entry = json_create_object(); + + json_object_add_value_uint(entry, "Vendor Specific Statistic Identifier", + le16_to_cpu(stat_id_str_table_arr[j].vs_si)); + json_object_add_value_uint(entry, "Reserved", + le64_to_cpu(stat_id_str_table_arr[j].reserved1)); + json_object_add_value_uint(entry, "ASCII ID Length", + le64_to_cpu(stat_id_str_table_arr[j].ascii_id_len)); + json_object_add_value_uint(entry, "ASCII ID offset", + le64_to_cpu(stat_id_str_table_arr[j].ascii_id_ofst)); + json_object_add_value_uint(entry, "Reserved2", + le64_to_cpu(stat_id_str_table_arr[j].reserved2)); + sprintf(buf, "Statistics Identifier String Table %d", j); + json_object_add_value_object(stat_table, buf, entry); + } + + json_object_add_value_object(root, + "Statistics Identifier String Table", stat_table); + } + + if (log_data->estsz != 0) { + struct json_object *eve_table = json_create_object(); + + memcpy(event_id_str_table_arr, + (__u8 *)log_data_buf + event_str_table_ofst, + (log_data->estsz * 4)); + for (j = 0; j < eve_id_index; j++) { + struct json_object *entry = json_create_object(); + + json_object_add_value_int(entry, "Debug Event Class", + le16_to_cpu(event_id_str_table_arr[j].deb_eve_class)); + json_object_add_value_int(entry, "Event Identifier", + le16_to_cpu(event_id_str_table_arr[j].ei)); + json_object_add_value_int(entry, "ASCII ID Length", + le64_to_cpu(event_id_str_table_arr[j].ascii_id_len)); + json_object_add_value_int(entry, "ASCII ID offset", + le64_to_cpu(event_id_str_table_arr[j].ascii_id_ofst)); + json_object_add_value_int(entry, "Reserved", + le64_to_cpu(event_id_str_table_arr[j].reserved2)); + sprintf(buf, "Event Identifier String Table Entry %d", j); + json_object_add_value_object(eve_table, buf, entry); + } + json_object_add_value_object(root, + "Event Identifier String Table Entry", + eve_table); + } + + if (log_data->vu_eve_st_sz != 0) { + struct json_object *vu_eve_table = json_create_object(); + + memcpy(vu_event_id_str_table_arr, + (__u8 *)log_data_buf + vu_event_str_table_ofst, + (log_data->vu_eve_st_sz * 4)); + for (j = 0; j < vu_eve_index; j++) { + struct json_object *entry = json_create_object(); + + json_object_add_value_int(entry, "Debug Event Class", + le16_to_cpu(vu_event_id_str_table_arr[j].deb_eve_class)); + json_object_add_value_int(entry, "VU Event Identifier", + le16_to_cpu(vu_event_id_str_table_arr[j].vu_ei)); + json_object_add_value_int(entry, "ASCII ID Length", + le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_len)); + json_object_add_value_int(entry, "ASCII ID offset", + le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_ofst)); + json_object_add_value_int(entry, "Reserved", + le64_to_cpu(vu_event_id_str_table_arr[j].reserved)); + sprintf(buf, "VU Event Identifier String Table Entry %d", j); + json_object_add_value_object(vu_eve_table, buf, entry); + } + json_object_add_value_object(root, + "VU Event Identifier String Table Entry", + vu_eve_table); + } + + if (log_data->asctsz != 0) { + memcpy(ascii_table_info_arr, + (__u8 *)log_data_buf + ascii_table_ofst, + (log_data->asctsz * 4)); + memset((void *)ascii, 0, ascii_table_index); + for (j = 0; j < ascii_table_index; j++) + ascii += sprintf(ascii, "%c", ascii_table_info_arr[j]); + json_object_add_value_string(root, "ASCII Table", ascii_buf); + } + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + + return 0; +} + +static void ocp_print_c9_log_binary(__u8 *log_data_buf, int total_log_page_size) +{ + return d_raw((unsigned char *)log_data_buf, total_log_page_size); +} + +static int get_c9_log_page(struct nvme_dev *dev, char *format) +{ + + int ret = 0; + + nvme_print_flags_t fmt; + + ret = validate_output_format(format, &fmt); + if (ret < 0) { + fprintf(stderr, "ERROR : OCP : invalid output format\n"); + return ret; + } + + get_c9_log_page_data(dev, 1, 0); + + if (!ret) { + switch (fmt) { + case NORMAL: + ocp_print_C9_log_normal(log_data, pC9_string_buffer); + break; + case JSON: + ocp_print_C9_log_json(log_data, pC9_string_buffer); + break; + case BINARY: + ocp_print_c9_log_binary(pC9_string_buffer, total_log_page_sz); + break; + default: + fprintf(stderr, "unhandled output format\n"); + break; + } + } else + fprintf(stderr, "ERROR : OCP : Unable to read C9 data from buffer\n"); + free(header_data); + return ret; +} + +static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + struct nvme_dev *dev; + int ret = 0; + const char *desc = "Retrieve telemetry string log format"; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "output Format: normal|json"), + OPT_END() + }; + + ret = parse_and_open(&dev, argc, argv, desc, opts); + if (ret) + return ret; + + ret = get_c9_log_page(dev, cfg.output_format); + if (ret) + fprintf(stderr, "ERROR : OCP : Failure reading the C9 Log Page, ret = %d\n", ret); + + dev_close(dev); + + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/// TCG Configuration Log Page (LID : C7h) + +/* C7 TCG Configuration Log Page */ +#define C7_GUID_LENGTH 16 +#define C7_TCG_CONFIGURATION_LEN 512 +#define C7_TCG_CONFIGURATION_OPCODE 0xC7 +#define C7_TCG_CONFIGURATION_LOG_VERSION 0x1 + +static __u8 tcg_configuration_guid[C7_GUID_LENGTH] = { + 0x06, 0x40, 0x24, 0xBD, + 0x7E, 0xE0, 0xE6, 0x83, + 0xC0, 0x47, 0x54, 0xFA, + 0x9D, 0x2A, 0xE0, 0x54 }; /* - * struct vu_event_id_str_table_entry - VU Event Identifier String Table Entry - * @deb_eve_class: Shall be set the Debug Class. - * @vu_ei: Shall be set to the VU Event Identifier - * @ascii_id_len: Shall be set the number of ASCII Characters that are valid. - * @ascii_id_ofst: This is the offset from DWORD 0/ Byte 0 of the start of the - * ASCII table to the ASCII data for this identifier - * @reserved reserved + * struct tcg_configuration_log - TCG Configuration Log Page Structure + * @state: state + * @rsvd1: Reserved1 + * @locking_sp_act_count: Locking SP Activation Count + * @type_rev_count: Tper Revert Count + * @locking_sp_rev_count: Locking SP Revert Count. + * @no_of_locking_obj: Number of Locking Objects + * @no_of_single_um_locking_obj: Number of Single User Mode Locking Objects + * @no_of_range_prov_locking_obj: Number of Range Provisioned Locking Objects + * @no_of_ns_prov_locking_obj: Number of Namespace Provisioned Locking Objects + * @no_of_read_lock_locking_obj: Number of Read Locked Locking Objects + * @no_of_write_lock_locking_obj: Number of Write Locked Locking Objects + * @no_of_read_unlock_locking_obj: Number of Read Unlocked Locking Objects + * @no_of_read_unlock_locking_obj: Number of Write Unlocked Locking Objects + * @rsvd2: Reserved2 + * @sid_auth_try_count: SID Authentication Try Count + * @sid_auth_try_limit: SID Authentication Try Limit + * @pro_tcg_rc: Programmatic TCG Reset Count + * @pro_rlc: Programmatic Reset Lock Count + * @tcg_ec: TCG Error Count + * @rsvd3: Reserved3 + * @log_page_version: Log Page Version */ -struct __attribute__((__packed__)) vu_event_id_str_table_entry { - __u8 deb_eve_class; - __le16 vu_ei; - __u8 ascii_id_len; - __le64 ascii_id_ofst; - __le32 reserved; +struct __packed tcg_configuration_log { + __u8 state; + __u8 rsvd1[3]; + __u8 locking_sp_act_count; + __u8 type_rev_count; + __u8 locking_sp_rev_count; + __u8 no_of_locking_obj; + __u8 no_of_single_um_locking_obj; + __u8 no_of_range_prov_locking_obj; + __u8 no_of_ns_prov_locking_obj; + __u8 no_of_read_lock_locking_obj; + __u8 no_of_write_lock_locking_obj; + __u8 no_of_read_unlock_locking_obj; + __u8 no_of_write_unlock_locking_obj; + __u8 rsvd2; + __u32 sid_auth_try_count; + __u32 sid_auth_try_limit; + __u32 pro_tcg_rc; + __u32 pro_rlc; + __u32 tcg_ec; + __u8 rsvd3[458]; + __le16 log_page_version; + __u8 log_page_guid[C7_GUID_LENGTH]; + }; -/* Function declaration for Telemetry String Log Format (LID:C9h) */ -static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *cmd, - struct plugin *plugin); - - -static int ocp_print_C9_log_normal(struct telemetry_str_log_format *log_data,__u8 *log_data_buf) -{ - //calculating the index value for array - __le64 stat_id_index = (log_data->sitsz * 4) / 16; - __le64 eve_id_index = (log_data->estsz * 4) / 16; - __le64 vu_eve_index = (log_data->vu_eve_st_sz * 4) / 16; - __le64 ascii_table_index = (log_data->asctsz * 4); - //Calculating the offset for dynamic fields. - __le64 stat_id_str_table_ofst = C9_TELEMETRY_STR_LOG_SIST_OFST + (log_data->sitsz * 4); - __le64 event_str_table_ofst = stat_id_str_table_ofst + (log_data->estsz * 4); - __le64 vu_event_str_table_ofst = event_str_table_ofst + (log_data->vu_eve_st_sz * 4); - __le64 ascii_table_ofst = vu_event_str_table_ofst + (log_data->asctsz * 4); - struct statistics_id_str_table_entry stat_id_str_table_arr[stat_id_index]; - struct event_id_str_table_entry event_id_str_table_arr[eve_id_index]; - struct vu_event_id_str_table_entry vu_event_id_str_table_arr[vu_eve_index]; - __u8 ascii_table_info_arr[ascii_table_index]; - int j; - - printf(" Log Page Version : 0x%x\n", log_data->log_page_version); - - printf(" Reserved : "); - for (j = 0; j < 15; j++) - printf("%d", log_data->reserved1[j]); - printf("\n"); - - printf(" Log page GUID : 0x"); - for (j = C9_GUID_LENGTH - 1; j >= 0; j--) - printf("%x", log_data->log_page_guid[j]); - printf("\n"); - - printf(" Telemetry String Log Size : 0x%lx\n", le64_to_cpu(log_data->sls)); - - printf(" Reserved : "); - for (j = 0; j < 24; j++) - printf("%d", log_data->reserved2[j]); - printf("\n"); - - printf(" Statistics Identifier String Table Start : 0x%lx\n", le64_to_cpu(log_data->sits)); - printf(" Statistics Identifier String Table Size : 0x%lx\n", le64_to_cpu(log_data->sitsz)); - printf(" Event String Table Start : 0x%lx\n", le64_to_cpu(log_data->ests)); - printf(" Event String Table Size : 0x%lx\n", le64_to_cpu(log_data->estsz)); - printf(" VU Event String Table Start : 0x%lx\n", le64_to_cpu(log_data->vu_eve_sts)); - printf(" VU Event String Table Size : 0x%lx\n", le64_to_cpu(log_data->vu_eve_st_sz)); - printf(" ASCII Table Start : 0x%lx\n", le64_to_cpu(log_data->ascts)); - printf(" ASCII Table Size : 0x%lx\n", le64_to_cpu(log_data->asctsz)); - - printf(" FIFO 1 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo1[j], log_data->fifo1[j]); - } - - printf(" FIFO 2 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo2[j], log_data->fifo2[j]); - } - - printf(" FIFO 3 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo3[j], log_data->fifo3[j]); - } - - printf(" FIFO 4 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - - printf(" %d %d %c \n", j, log_data->fifo4[j], log_data->fifo4[j]); - } - - printf(" FIFO 5 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo5[j], log_data->fifo5[j]); - } - - printf(" FIFO 6 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo6[j], log_data->fifo6[j]); - } - - printf(" FIFO 7 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo7[j], log_data->fifo7[j]); - } - - printf(" FIFO 8 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf("index value ascii_val"); - printf(" %d %d %c \n", j, log_data->fifo8[j], log_data->fifo8[j]); - } - - printf(" FIFO 9 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo9[j], log_data->fifo9[j]); - } - - printf(" FIFO 10 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo10[j], log_data->fifo10[j]); - } - - printf(" FIFO 11 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo11[j], log_data->fifo11[j]); - } - - printf(" FIFO 12 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo12[j], log_data->fifo12[j]); - } - - printf(" FIFO 13 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo13[j], log_data->fifo13[j]); - } - - printf(" FIFO 14 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo14[j], log_data->fifo14[j]); - } - - printf(" FIFO 15 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo15[j], log_data->fifo16[j]); - } - - printf(" FIFO 16 ASCII String\n"); - printf(" index value ascii_val\n"); - for (j = 0; j < 16; j++){ - printf(" %d %d %c \n", j, log_data->fifo16[j], log_data->fifo16[j]); - } - - printf(" Reserved : "); - for (j = 0; j < 48; j++) - printf("%d", log_data->reserved3[j]); - printf("\n"); - - memcpy(stat_id_str_table_arr, (__u8*)log_data_buf + stat_id_str_table_ofst, (log_data->sitsz * 4)); - memcpy(event_id_str_table_arr, (__u8*)log_data_buf + event_str_table_ofst, (log_data->estsz * 4)); - memcpy(vu_event_id_str_table_arr, (__u8*)log_data_buf + vu_event_str_table_ofst, (log_data->vu_eve_st_sz * 4)); - memcpy(ascii_table_info_arr, (__u8*)log_data_buf + ascii_table_ofst, (log_data->asctsz * 4)); - - printf(" Statistics Identifier String Table\n"); - for (j = 0; j < stat_id_index; j++){ - printf(" Vendor Specific Statistic Identifier : 0x%x\n",le16_to_cpu(stat_id_str_table_arr[j].vs_si)); - printf(" Reserved : 0x%d",stat_id_str_table_arr[j].reserved1); - printf(" ASCII ID Length : 0x%x\n",stat_id_str_table_arr[j].ascii_id_len); - printf(" ASCII ID offset : 0x%lx\n",le64_to_cpu(stat_id_str_table_arr[j].ascii_id_ofst)); - printf(" Reserved : 0x%d\n",stat_id_str_table_arr[j].reserved2); - } - - printf(" Event Identifier String Table Entry\n"); - for (j = 0; j < eve_id_index; j++){ - printf(" Debug Event Class : 0x%x\n",event_id_str_table_arr[j].deb_eve_class); - printf(" Event Identifier : 0x%x\n",le16_to_cpu(event_id_str_table_arr[j].ei)); - printf(" ASCII ID Length : 0x%x\n",event_id_str_table_arr[j].ascii_id_len); - printf(" ASCII ID offset : 0x%lx\n",le64_to_cpu(event_id_str_table_arr[j].ascii_id_ofst)); - printf(" Reserved : 0x%d\n",event_id_str_table_arr[j].reserved2); - - } - - printf(" VU Event Identifier String Table Entry\n"); - for (j = 0; j < vu_eve_index; j++){ - printf(" Debug Event Class : 0x%x\n",vu_event_id_str_table_arr[j].deb_eve_class); - printf(" VU Event Identifier : 0x%x\n",le16_to_cpu(vu_event_id_str_table_arr[j].vu_ei)); - printf(" ASCII ID Length : 0x%x\n",vu_event_id_str_table_arr[j].ascii_id_len); - printf(" ASCII ID offset : 0x%lx\n",le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_ofst)); - printf(" Reserved : 0x%d\n",vu_event_id_str_table_arr[j].reserved); - - } - - printf(" ASCII Table\n"); - printf(" Byte Data_Byte ASCII_Character\n"); - for (j = 0; j < ascii_table_index; j++){ - printf(" %lld 0x%x %c \n",ascii_table_ofst+j,ascii_table_info_arr[j],ascii_table_info_arr[j]); - } - return 0; -} - -static int ocp_print_C9_log_json(struct telemetry_str_log_format *log_data,__u8 *log_data_buf) -{ - struct json_object *root = json_create_object(); - struct json_object *stat_table = json_create_object(); - struct json_object *eve_table = json_create_object(); - struct json_object *vu_eve_table = json_create_object(); - struct json_object *entry = json_create_object(); - char res_arr[48]; - char *res = res_arr; - char guid_buf[C9_GUID_LENGTH]; - char *guid = guid_buf; - char fifo_arr[16]; - char *fifo = fifo_arr; - //calculating the index value for array - __le64 stat_id_index = (log_data->sitsz * 4) / 16; - __le64 eve_id_index = (log_data->estsz * 4) / 16; - __le64 vu_eve_index = (log_data->vu_eve_st_sz * 4) / 16; - __le64 ascii_table_index = (log_data->asctsz * 4); - //Calculating the offset for dynamic fields. - __le64 stat_id_str_table_ofst = C9_TELEMETRY_STR_LOG_SIST_OFST + (log_data->sitsz * 4); - __le64 event_str_table_ofst = stat_id_str_table_ofst + (log_data->estsz * 4); - __le64 vu_event_str_table_ofst = event_str_table_ofst + (log_data->vu_eve_st_sz * 4); - __le64 ascii_table_ofst = vu_event_str_table_ofst + (log_data->asctsz * 4); - struct statistics_id_str_table_entry stat_id_str_table_arr[stat_id_index]; - struct event_id_str_table_entry event_id_str_table_arr[eve_id_index]; - struct vu_event_id_str_table_entry vu_event_id_str_table_arr[vu_eve_index]; - __u8 ascii_table_info_arr[ascii_table_index]; - char ascii_buf[ascii_table_index]; - char *ascii = ascii_buf; - int j; - - json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); - - memset((__u8 *)res, 0, 15); - for (j = 0; j < 15; j++) - res += sprintf(res, "%d", log_data->reserved1[j]); - json_object_add_value_string(root, "Reserved", res_arr); - - memset((void *)guid, 0, C9_GUID_LENGTH); - for (j = C9_GUID_LENGTH - 1; j >= 0; j--) - guid += sprintf(guid, "%02x", log_data->log_page_guid[j]); - json_object_add_value_string(root, "Log page GUID", guid_buf); - - json_object_add_value_int(root, "Telemetry String Log Size", le64_to_cpu(log_data->sls)); - - memset((__u8 *)res, 0, 24); - for (j = 0; j < 24; j++) - res += sprintf(res, "%d", log_data->reserved2[j]); - json_object_add_value_string(root, "Reserved", res_arr); - - json_object_add_value_int(root, "Statistics Identifier String Table Start", le64_to_cpu(log_data->sits)); - json_object_add_value_int(root, "Event String Table Start", le64_to_cpu(log_data->ests)); - json_object_add_value_int(root, "Event String Table Size", le64_to_cpu(log_data->estsz)); - json_object_add_value_int(root, "VU Event String Table Start", le64_to_cpu(log_data->vu_eve_sts)); - json_object_add_value_int(root, "VU Event String Table Size", le64_to_cpu(log_data->vu_eve_st_sz)); - json_object_add_value_int(root, "ASCII Table Start", le64_to_cpu(log_data->ascts)); - json_object_add_value_int(root, "ASCII Table Size", le64_to_cpu(log_data->asctsz)); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo1[j]); - json_object_add_value_string(root, "FIFO 1 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo2[j]); - json_object_add_value_string(root, "FIFO 2 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo3[j]); - json_object_add_value_string(root, "FIFO 3 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo4[j]); - json_object_add_value_string(root, "FIFO 4 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo5[j]); - json_object_add_value_string(root, "FIFO 5 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo6[j]); - json_object_add_value_string(root, "FIFO 6 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo7[j]); - json_object_add_value_string(root, "FIFO 7 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo8[j]); - json_object_add_value_string(root, "FIFO 8 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo9[j]); - json_object_add_value_string(root, "FIFO 9 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo10[j]); - json_object_add_value_string(root, "FIFO 10 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo11[j]); - json_object_add_value_string(root, "FIFO 11 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo12[j]); - json_object_add_value_string(root, "FIFO 12 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo13[j]); - json_object_add_value_string(root, "FIFO 13 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo14[j]); - json_object_add_value_string(root, "FIFO 14 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo15[j]); - json_object_add_value_string(root, "FIFO 15 ASCII String", fifo_arr); - - memset((void *)fifo, 0, 16); - for (j = 0; j < 16; j++) - fifo += sprintf(fifo, "%c", log_data->fifo16[j]); - json_object_add_value_string(root, "FIFO 16 ASCII String", fifo_arr); - - memset((__u8 *)res, 0, 48); - for (j = 0; j < 48; j++) - res += sprintf(res, "%d", log_data->reserved3[j]); - json_object_add_value_string(root, "Reserved", res_arr); - - memcpy(stat_id_str_table_arr, (__u8*)log_data_buf + stat_id_str_table_ofst, (log_data->sitsz * 4)); - memcpy(event_id_str_table_arr, (__u8*)log_data_buf + event_str_table_ofst, (log_data->estsz * 4)); - memcpy(vu_event_id_str_table_arr, (__u8*)log_data_buf + vu_event_str_table_ofst, (log_data->vu_eve_st_sz * 4)); - memcpy(ascii_table_info_arr, (__u8*)log_data_buf + ascii_table_ofst, (log_data->asctsz * 4)); - - for (j = 0; j < stat_id_index; j++){ - json_object_add_value_int(entry, "Vendor Specific Statistic Identifier", le16_to_cpu(stat_id_str_table_arr[j].vs_si)); - json_object_add_value_int(entry, "Reserved", le64_to_cpu(stat_id_str_table_arr[j].reserved1)); - json_object_add_value_int(entry, "ASCII ID Length", le64_to_cpu(stat_id_str_table_arr[j].ascii_id_len)); - json_object_add_value_int(entry, "ASCII ID offset", le64_to_cpu(stat_id_str_table_arr[j].ascii_id_ofst)); - json_object_add_value_int(entry, "Reserved", le64_to_cpu(stat_id_str_table_arr[j].reserved2)); - json_array_add_value_object(stat_table, entry); - } - json_object_add_value_array(root, "Statistics Identifier String Table", stat_table); - - for (j = 0; j < eve_id_index; j++){ - json_object_add_value_int(entry, "Debug Event Class", le16_to_cpu(event_id_str_table_arr[j].deb_eve_class)); - json_object_add_value_int(entry, "Event Identifier", le16_to_cpu(event_id_str_table_arr[j].ei)); - json_object_add_value_int(entry, "ASCII ID Length", le64_to_cpu(event_id_str_table_arr[j].ascii_id_len)); - json_object_add_value_int(entry, "ASCII ID offset", le64_to_cpu(event_id_str_table_arr[j].ascii_id_ofst)); - json_object_add_value_int(entry, "Reserved", le64_to_cpu(event_id_str_table_arr[j].reserved2)); - json_array_add_value_object(eve_table, entry); - } - json_object_add_value_array(root, "Event Identifier String Table Entry", eve_table); - - for (j = 0; j < vu_eve_index; j++){ - json_object_add_value_int(entry, "Debug Event Class", le16_to_cpu(vu_event_id_str_table_arr[j].deb_eve_class)); - json_object_add_value_int(entry, "VU Event Identifier", le16_to_cpu(vu_event_id_str_table_arr[j].vu_ei)); - json_object_add_value_int(entry, "ASCII ID Length", le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_len)); - json_object_add_value_int(entry, "ASCII ID offset", le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_ofst)); - json_object_add_value_int(entry, "Reserved", le64_to_cpu(vu_event_id_str_table_arr[j].reserved)); - json_array_add_value_object(vu_eve_table, entry); - } - json_object_add_value_array(root, "VU Event Identifier String Table Entry", vu_eve_table); - - memset((void *)ascii, 0, ascii_table_index); - for (j = 0; j < ascii_table_index; j++) - ascii += sprintf(ascii, "%c", ascii_table_info_arr[j]); - json_object_add_value_string(root, "ASCII Table", ascii_buf); - - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); - json_free_object(stat_table); - json_free_object(eve_table); - json_free_object(vu_eve_table); - - return 0; -} - -static void ocp_print_c9_log_binary(__u8 *log_data_buf,int total_log_page_size) -{ - return d_raw((unsigned char *)log_data_buf, total_log_page_size); +/* Function declaration for TCG Configuration log page (LID:C7h) */ +static int ocp_tcg_configuration_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin); + +static int ocp_print_C7_log_normal(struct nvme_dev *dev, + struct tcg_configuration_log *log_data) +{ + int j; + + printf("TCG Configuration C7 Log Page Data-\n"); + + printf(" State : 0x%x\n", log_data->state); + printf(" Reserved1 : 0x"); + for (j = 0; j < 3; j++) + printf("%d", log_data->rsvd1[j]); + printf("\n"); + printf(" Locking SP Activation Count : 0x%x\n", log_data->locking_sp_act_count); + printf(" Tper Revert Count : 0x%x\n", log_data->type_rev_count); + printf(" Locking SP Revert Count : 0x%x\n", log_data->locking_sp_rev_count); + printf(" Number of Locking Objects : 0x%x\n", log_data->no_of_locking_obj); + printf(" Number of Single User Mode Locking Objects : 0x%x\n", log_data->no_of_single_um_locking_obj); + printf(" Number of Range Provisioned Locking Objects : 0x%x\n", log_data->no_of_range_prov_locking_obj); + printf(" Number of Namespace Provisioned Locking Objects : 0x%x\n", log_data->no_of_ns_prov_locking_obj); + printf(" Number of Read Locked Locking Objects : 0x%x\n", log_data->no_of_read_lock_locking_obj); + printf(" Number of Write Locked Locking Objects : 0x%x\n", log_data->no_of_write_lock_locking_obj); + printf(" Number of Read Unlocked Locking Objects : 0x%x\n", log_data->no_of_read_unlock_locking_obj); + printf(" Number of Write Unlocked Locking Objects : 0x%x\n", log_data->no_of_write_unlock_locking_obj); + printf(" Reserved2 : 0x%x\n", log_data->rsvd2); + + printf(" SID Authentication Try Count : 0x%x\n", le32_to_cpu(log_data->sid_auth_try_count)); + printf(" SID Authentication Try Limit : 0x%x\n", le32_to_cpu(log_data->sid_auth_try_limit)); + printf(" Programmatic TCG Reset Count : 0x%x\n", le32_to_cpu(log_data->pro_tcg_rc)); + printf(" Programmatic Reset Lock Count : 0x%x\n", le32_to_cpu(log_data->pro_rlc)); + printf(" TCG Error Count : 0x%x\n", le32_to_cpu(log_data->tcg_ec)); + + printf(" Reserved3 : 0x"); + for (j = 0; j < 458; j++) + printf("%d", log_data->rsvd3[j]); + printf("\n"); + + printf(" Log Page Version : 0x%x\n", le16_to_cpu(log_data->log_page_version)); + printf(" Log page GUID : 0x"); + for (j = C7_GUID_LENGTH - 1; j >= 0; j--) + printf("%x", log_data->log_page_guid[j]); + printf("\n"); + + return 0; } -static int get_c9_log_page(struct nvme_dev *dev, char *format) +static void ocp_print_C7_log_json(struct tcg_configuration_log *log_data) { - int ret = 0; - __u8 *header_data; - struct telemetry_str_log_format *log_data; - enum nvme_print_flags fmt; - __u8 *full_log_buf_data = NULL; - __le64 stat_id_str_table_ofst = 0; - __le64 event_str_table_ofst = 0; - __le64 vu_event_str_table_ofst = 0; - __le64 ascii_table_ofst = 0; - __le64 total_log_page_sz = 0; - - ret = validate_output_format(format, &fmt); - if (ret < 0) { - fprintf(stderr, "ERROR : OCP : invalid output format\n"); - return ret; - } - - header_data = (__u8 *)malloc(sizeof(__u8) * C9_TELEMETRY_STR_LOG_LEN); - if (!header_data) { - fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); - return -1; - } - memset(header_data, 0, sizeof(__u8) * C9_TELEMETRY_STR_LOG_LEN); - - ret = nvme_get_log_simple(dev_fd(dev), C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE, - C9_TELEMETRY_STR_LOG_LEN, header_data); - - if (!ret) { - log_data = (struct telemetry_str_log_format *)header_data; - printf("Statistics Identifier String Table Size = %lld\n",log_data->sitsz); - printf("Event String Table Size = %lld\n",log_data->estsz); - printf("VU Event String Table Size = %lld\n",log_data->vu_eve_st_sz); - printf("ASCII Table Size = %lld\n",log_data->asctsz); - - //Calculating the offset for dynamic fields. - stat_id_str_table_ofst = C9_TELEMETRY_STR_LOG_SIST_OFST + (log_data->sitsz * 4); - event_str_table_ofst = stat_id_str_table_ofst + (log_data->estsz * 4); - vu_event_str_table_ofst = event_str_table_ofst + (log_data->vu_eve_st_sz * 4); - ascii_table_ofst = vu_event_str_table_ofst + (log_data->asctsz * 4); - total_log_page_sz = stat_id_str_table_ofst + event_str_table_ofst + vu_event_str_table_ofst + ascii_table_ofst; - - printf("stat_id_str_table_ofst = %lld\n",stat_id_str_table_ofst); - printf("event_str_table_ofst = %lld\n",event_str_table_ofst); - printf("vu_event_str_table_ofst = %lld\n",vu_event_str_table_ofst); - printf("ascii_table_ofst = %lld\n",ascii_table_ofst); - printf("total_log_page_sz = %lld\n",total_log_page_sz); - - full_log_buf_data = (__u8 *)malloc(sizeof(__u8) * total_log_page_sz); - if (!full_log_buf_data) { - fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); - return -1; - } - memset(full_log_buf_data, 0, sizeof(__u8) * total_log_page_sz); - - ret = nvme_get_log_simple(dev_fd(dev), C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE, - total_log_page_sz, full_log_buf_data); - - if (!ret) { - switch (fmt) { - case NORMAL: - ocp_print_C9_log_normal(log_data,full_log_buf_data); - break; - case JSON: - ocp_print_C9_log_json(log_data,full_log_buf_data); - break; - case BINARY: - ocp_print_c9_log_binary(full_log_buf_data,total_log_page_sz); - break; - default: - fprintf(stderr, "unhandled output format\n"); - break; - } - } else{ - fprintf(stderr, "ERROR : OCP : Unable to read C9 data from buffer\n"); - } - } else { - fprintf(stderr, "ERROR : OCP : Unable to read C9 data from buffer\n"); - } - - free(header_data); - free(full_log_buf_data); - - return ret; + int j; + struct json_object *root; + char guid_buf[C7_GUID_LENGTH]; + char *guid = guid_buf; + char res_arr[458]; + char *res = res_arr; + + root = json_create_object(); + + json_object_add_value_int(root, "State", le16_to_cpu(log_data->state)); + memset((__u8 *)res, 0, 3); + for (j = 0; j < 3; j++) + res += sprintf(res, "%d", log_data->rsvd1[j]); + json_object_add_value_string(root, "Reserved1", res_arr); + json_object_add_value_int(root, "Locking SP Activation Count", le16_to_cpu(log_data->locking_sp_act_count)); + json_object_add_value_int(root, "Tper Revert Count", le16_to_cpu(log_data->locking_sp_rev_count)); + json_object_add_value_int(root, "Number of Locking Objects", le16_to_cpu(log_data->no_of_locking_obj)); + json_object_add_value_int(root, "Number of Single User Mode Locking Objects", le16_to_cpu(log_data->no_of_single_um_locking_obj)); + json_object_add_value_int(root, "Number of Range Provisioned Locking Objects", le16_to_cpu(log_data->no_of_range_prov_locking_obj)); + json_object_add_value_int(root, "Number of Namespace Provisioned Locking Objects", le16_to_cpu(log_data->no_of_ns_prov_locking_obj)); + json_object_add_value_int(root, "Number of Read Locked Locking Objects", le16_to_cpu(log_data->no_of_read_lock_locking_obj)); + json_object_add_value_int(root, "Number of Write Locked Locking Objects", le16_to_cpu(log_data->no_of_write_lock_locking_obj)); + json_object_add_value_int(root, "Number of Read Unlocked Locking Objects", le16_to_cpu(log_data->no_of_read_unlock_locking_obj)); + json_object_add_value_int(root, "Number of Write Unlocked Locking Objects", le16_to_cpu(log_data->no_of_write_unlock_locking_obj)); + json_object_add_value_int(root, "Reserved2", le16_to_cpu(log_data->rsvd2)); + + json_object_add_value_int(root, "SID Authentication Try Count", le16_to_cpu(log_data->sid_auth_try_count)); + json_object_add_value_int(root, "SID Authentication Try Limit", le16_to_cpu(log_data->sid_auth_try_limit)); + json_object_add_value_int(root, "Programmatic TCG Reset Count", le16_to_cpu(log_data->pro_tcg_rc)); + json_object_add_value_int(root, "Programmatic Reset Lock Count", le16_to_cpu(log_data->pro_rlc)); + json_object_add_value_int(root, "TCG Error Count", le16_to_cpu(log_data->tcg_ec)); + + memset((__u8 *)res, 0, 458); + for (j = 0; j < 458; j++) + res += sprintf(res, "%d", log_data->rsvd3[j]); + json_object_add_value_string(root, "Reserved3", res_arr); + + json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); + + memset((void *)guid, 0, C7_GUID_LENGTH); + for (j = C7_GUID_LENGTH - 1; j >= 0; j--) + guid += sprintf(guid, "%02x", log_data->log_page_guid[j]); + json_object_add_value_string(root, "Log page GUID", guid_buf); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); } -static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *cmd, - struct plugin *plugin) +static void ocp_print_c7_log_binary(struct tcg_configuration_log *log_data) { - struct nvme_dev *dev; - int ret = 0; - const char *desc = "Retrieve telemetry string log format"; + return d_raw((unsigned char *)log_data, sizeof(*log_data)); +} - struct config { - char *output_format; - }; +static int get_c7_log_page(struct nvme_dev *dev, char *format) +{ + nvme_print_flags_t fmt; + int ret; + __u8 *data; + int i; + struct tcg_configuration_log *log_data; + int j; - struct config cfg = { - .output_format = "normal", - }; + ret = validate_output_format(format, &fmt); + if (ret < 0) { + fprintf(stderr, "ERROR : OCP : invalid output format\n"); + return ret; + } - OPT_ARGS(opts) = { - OPT_FMT("output-format", 'o', &cfg.output_format, "output Format: normal|json"), - OPT_END() - }; + data = (__u8 *)malloc(sizeof(__u8) * C7_TCG_CONFIGURATION_LEN); + if (!data) { + fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); + return -1; + } + memset(data, 0, sizeof(__u8) * C7_TCG_CONFIGURATION_LEN); - ret = parse_and_open(&dev, argc, argv, desc, opts); - if (ret) - return ret; + ret = nvme_get_log_simple(dev_fd(dev), C7_TCG_CONFIGURATION_OPCODE, + C7_TCG_CONFIGURATION_LEN, data); + if (!ret) { + log_data = (struct tcg_configuration_log *)data; - ret = get_c9_log_page(dev, cfg.output_format); - if (ret) - fprintf(stderr, "ERROR : OCP : Failure reading the C9 Log Page, ret = %d\n", ret); + /* check log page version */ + if (log_data->log_page_version != C7_TCG_CONFIGURATION_LOG_VERSION) { + fprintf(stderr, "ERROR : OCP : invalid TCG Configuration Log Page version\n"); + ret = -1; + goto out; + } - dev_close(dev); + /* + * check log page guid + * Verify GUID matches + */ + for (i = 0; i < 16; i++) { + if (tcg_configuration_guid[i] != log_data->log_page_guid[i]) { + fprintf(stderr, "ERROR : OCP : Unknown GUID in C7 Log Page data\n"); + fprintf(stderr, "ERROR : OCP : Expected GUID: 0x"); + for (j = 0; j < 16; j++) + fprintf(stderr, "%x", tcg_configuration_guid[j]); + fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x"); + for (j = 0; j < 16; j++) + fprintf(stderr, "%x", log_data->log_page_guid[j]); + fprintf(stderr, "\n"); + + ret = -1; + goto out; + } + } + + switch (fmt) { + case NORMAL: + ocp_print_C7_log_normal(dev, log_data); + break; + case JSON: + ocp_print_C7_log_json(log_data); + break; + case BINARY: + ocp_print_c7_log_binary(log_data); + break; + default: + break; + } + } else { + fprintf(stderr, "ERROR : OCP : Unable to read C7 data from buffer\n"); + } - return ret; +out: + free(data); + return ret; +} + + +static int ocp_tcg_configuration_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve TCG Configuration Log Page Data"; + struct nvme_dev *dev; + int ret = 0; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "output Format: normal|json"), + OPT_END() + }; + + ret = parse_and_open(&dev, argc, argv, desc, opts); + if (ret) + return ret; + + ret = get_c7_log_page(dev, cfg.output_format); + if (ret) + fprintf(stderr, "ERROR : OCP : Failure reading the C7 Log Page, ret = %d\n", ret); + + dev_close(dev); + return ret; } /////////////////////////////////////////////////////////////////////////////// @@ -2969,3 +3969,177 @@ static int fw_activation_history_log(int argc, char **argv, struct command *cmd, { return ocp_fw_activation_history_log(argc, argv, cmd, plugin); } + +static int error_injection_get(struct nvme_dev *dev, const __u8 sel, bool uuid) +{ + struct erri_get_cq_entry cq_entry; + int err; + int i; + const __u8 fid = 0xc0; + + _cleanup_free_ struct erri_entry *entry = NULL; + + struct nvme_get_features_args args = { + .result = (__u32 *)&cq_entry, + .data = entry, + .args_size = sizeof(args), + .fd = dev_fd(dev), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .sel = sel, + .data_len = sizeof(*entry) * ERRI_ENTRIES_MAX, + .fid = fid, + }; + + if (uuid) { + /* OCP 2.0 requires UUID index support */ + err = ocp_get_uuid_index(dev, &args.uuidx); + if (err || !args.uuidx) { + nvme_show_error("ERROR: No OCP UUID index found"); + return err; + } + } + + entry = nvme_alloc(args.data_len); + if (!entry) { + nvme_show_error("malloc: %s", strerror(errno)); + return -errno; + } + + err = nvme_cli_get_features(dev, &args); + if (!err) { + nvme_show_result("Number of Error Injecttions (feature: %#0*x): %#0*x (%s: %d)", + fid ? 4 : 2, fid, cq_entry.nume ? 10 : 8, cq_entry.nume, + nvme_select_to_string(sel), cq_entry.nume); + if (sel == NVME_GET_FEATURES_SEL_SUPPORTED) + nvme_show_select_result(fid, *args.result); + for (i = 0; i < cq_entry.nume; i++) { + printf("Entry: %d, Flags: %x (%s%s), Type: %x (%s), NRTDP: %d\n", i, + entry->flags, entry->enable ? "Enabled" : "Disabled", + entry->single ? ", Single instance" : "", entry->type, + erri_type_to_string(entry->type), entry->nrtdp); + } + } else { + nvme_show_error("Could not get feature: %#0*x.", fid ? 4 : 2, fid); + } + + return err; +} + +static int get_error_injection(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Return set of error injection"; + int err; + struct config { + __u8 sel; + }; + struct config cfg = { 0 }; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + + OPT_ARGS(opts) = { + OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_FLAG("no-uuid", 'n', NULL, no_uuid), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + return error_injection_get(dev, cfg.sel, !argconfig_parse_seen(opts, "no-uuid")); +} + +static int error_injection_set(struct nvme_dev *dev, struct erri_config *cfg, bool uuid) +{ + int err; + __u32 result; + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .fid = 0xc0, + .cdw11 = cfg->number, + .data_len = cfg->number * sizeof(struct erri_entry), + .timeout = nvme_cfg.timeout, + .result = &result, + }; + + _cleanup_fd_ int ffd = -1; + + _cleanup_free_ struct erri_entry *entry = NULL; + + if (uuid) { + /* OCP 2.0 requires UUID index support */ + err = ocp_get_uuid_index(dev, &args.uuidx); + if (err || !args.uuidx) { + nvme_show_error("ERROR: No OCP UUID index found"); + return err; + } + } + + entry = nvme_alloc(args.data_len); + if (!entry) { + nvme_show_error("malloc: %s", strerror(errno)); + return -errno; + } + + if (cfg->file && strlen(cfg->file)) { + ffd = open(cfg->file, O_RDONLY); + if (ffd < 0) { + nvme_show_error("Failed to open file %s: %s", cfg->file, strerror(errno)); + return -EINVAL; + } + err = read(ffd, entry, args.data_len); + if (err < 0) { + nvme_show_error("failed to read data buffer from input file: %s", + strerror(errno)); + return -errno; + } + } else { + entry->enable = 1; + entry->single = 1; + entry->type = cfg->type; + entry->nrtdp = cfg->nrtdp; + } + + args.data = entry; + + err = nvme_set_features(&args); + if (err) { + if (err < 0) + nvme_show_error("set-error-injection: %s", nvme_strerror(errno)); + else if (err > 0) + nvme_show_status(err); + return err; + } + + printf("set-error-injection, data: %s, number: %d, uuid: %d, type: %d, nrtdp: %d\n", + cfg->file, cfg->number, args.uuidx, cfg->type, cfg->nrtdp); + if (args.data) + d(args.data, args.data_len, 16, 1); + + return 0; +} + +static int set_error_injection(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Inject error conditions"; + int err; + struct erri_config cfg = { + .number = 1, + }; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + + NVME_ARGS(opts, + OPT_FILE("data", 'd', &cfg.file, data), + OPT_BYTE("number", 'n', &cfg.number, number), + OPT_FLAG("no-uuid", 'N', NULL, no_uuid), + OPT_SHRT("type", 't', &cfg.type, type), + OPT_SHRT("nrtdp", 'r', &cfg.nrtdp, nrtdp)); + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + return error_injection_set(dev, &cfg, !argconfig_parse_seen(opts, "no-uuid")); +} diff --git a/plugins/ocp/ocp-nvme.h b/plugins/ocp/ocp-nvme.h index 95539b0..16d929d 100644 --- a/plugins/ocp/ocp-nvme.h +++ b/plugins/ocp/ocp-nvme.h @@ -11,9 +11,10 @@ #if !defined(OCP_NVME) || defined(CMD_HEADER_MULTI_READ) #define OCP_NVME +#define OCP_PLUGIN_VERSION "2.9.0" #include "cmd.h" -PLUGIN(NAME("ocp", "OCP cloud SSD extensions", NVME_VERSION), +PLUGIN(NAME("ocp", "OCP cloud SSD extensions", OCP_PLUGIN_VERSION), COMMAND_LIST( ENTRY("smart-add-log", "Retrieve extended SMART Information", smart_add_log) ENTRY("latency-monitor-log", "Get Latency Monitor Log Page", ocp_latency_monitor_log) @@ -26,10 +27,17 @@ PLUGIN(NAME("ocp", "OCP cloud SSD extensions", NVME_VERSION), ENTRY("unsupported-reqs-log", "Get Unsupported Requirements Log Page", ocp_unsupported_requirements_log) ENTRY("error-recovery-log", "Retrieve Error Recovery Log Page", ocp_error_recovery_log) ENTRY("device-capability-log", "Get Device capabilities Requirements Log Page", ocp_device_capabilities_log) - ENTRY("set-dssd-power-state-feature", "Get Device capabilities Requirements Log Page", set_dssd_power_state_feature) + ENTRY("set-dssd-power-state-feature", "Set DSSD Power State feature", set_dssd_power_state_feature) + ENTRY("get-dssd-power-state-feature", "Get DSSD Power State feature", get_dssd_power_state_feature) ENTRY("set-plp-health-check-interval", "Set PLP Health Check Interval", set_plp_health_check_interval) ENTRY("get-plp-health-check-interval", "Get PLP Health Check Interval", get_plp_health_check_interval) ENTRY("telemetry-string-log", "Retrieve Telemetry string Log Page", ocp_telemetry_str_log_format) + ENTRY("set-telemetry-profile", "Set Telemetry Profile Feature", ocp_set_telemetry_profile_feature) + ENTRY("set-dssd-async-event-config", "Set DSSD Async Event Config", set_dssd_async_event_config) + ENTRY("get-dssd-async-event-config", "Get DSSD Async Event Config", get_dssd_async_event_config) + ENTRY("tcg-configuration-log", "Retrieve TCG Configuration Log Page", ocp_tcg_configuration_log) + ENTRY("get-error-injection", "Return set of error injection", get_error_injection) + ENTRY("set-error-injection", "Inject error conditions", set_error_injection) ) ); diff --git a/plugins/ocp/ocp-smart-extended-log.c b/plugins/ocp/ocp-smart-extended-log.c index 0d8ba81..6a524d3 100644 --- a/plugins/ocp/ocp-smart-extended-log.c +++ b/plugins/ocp/ocp-smart-extended-log.c @@ -252,7 +252,7 @@ static void ocp_print_C0_log_json(void *data) static int get_c0_log_page(int fd, char *format) { - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; __u8 *data; int i; int ret; diff --git a/plugins/ocp/ocp-telemetry-decode.c b/plugins/ocp/ocp-telemetry-decode.c new file mode 100644 index 0000000..11963be --- /dev/null +++ b/plugins/ocp/ocp-telemetry-decode.c @@ -0,0 +1,1566 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (c) 2024 Western Digital Corporation or its affiliates. + * + * Authors: Jeff Lien <jeff.lien@wdc.com>, + */ + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "linux/types.h" +#include "util/types.h" +#include "nvme-print.h" + +#include "ocp-telemetry-decode.h" + + +void print_vu_event_data(__u32 size, __u8 *data) +{ + int j; + __u16 vu_event_id = *(__u16 *)data; + + printf(" VU Event ID : 0x%02x\n", le16_to_cpu(vu_event_id)); + printf(" VU Data : 0x"); + for (j = 2; j < size; j++) + printf("%x", data[j]); + printf("\n\n"); +} + +void print_stats_desc(struct telemetry_stats_desc *stat_desc) +{ + int j; + /* Get the statistics Identifier string name and data size */ + __u16 stat_id = stat_desc->id; + __u32 stat_data_sz = ((stat_desc->size) * 4); + + printf("Statistics Identifier : 0x%x, %s\n", + stat_id, telemetry_stat_id_to_string(stat_id)); + printf("Statistics info : 0x%x\n", stat_desc->info); + printf("NS info : 0x%x\n", stat_desc->ns_info); + printf("Statistic Data Size : 0x%x\n", le16_to_cpu(stat_data_sz)); + + if (stat_data_sz > 0) { + printf("%s : 0x", + telemetry_stat_id_to_string(stat_id)); + for (j = 0; j < stat_data_sz; j++) + printf("%02x", stat_desc->data[j]); + printf("\n"); + } + printf("\n"); +} + +void print_telemetry_fifo_event(__u8 class_type, + __u16 id, __u8 size_dw, __u8 *data) +{ + int j; + const char *class_str = NULL; + __u32 size = size_dw * 4; + char time_str[40]; + uint64_t timestamp = 0; + + memset((void *)time_str, '\0', 40); + + if (class_type) { + class_str = telemetry_event_class_to_string(class_type); + printf("Event Class : %s\n", class_str); + } + + switch (class_type) { + case TELEMETRY_TIMESTAMP_CLASS: + timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(*(uint64_t *)data)); + + memset((void *)time_str, 0, 9); + sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(timestamp)/3600), + (int)((le64_to_cpu(timestamp%3600)/60)), + (int)(le64_to_cpu(timestamp%60))); + + printf(" Event ID : 0x%02x %s\n", id, telemetry_ts_event_to_string(id)); + printf(" Timestamp : %s\n", time_str); + printf(" Size : %d\n", size); + if (size > 8) { + printf(" VU Data : 0x"); + for (j = 8; j < size; j++) + printf("%02x", data[j]); + printf("\n\n"); + } + break; + + case TELEMETRY_PCIE_CLASS: + printf(" Event ID : 0x%02x %s\n", + id, telemetry_pcie_event_id_to_string(id)); + printf(" State : 0x%02x %s\n", + data[0], telemetry_pcie_state_data_to_string(data[0])); + printf(" Speed : 0x%02x %s\n", + data[1], telemetry_pcie_speed_data_to_string(data[1])); + printf(" Width : 0x%02x %s\n", + data[2], telemetry_pcie_width_data_to_string(data[2])); + if (size > 4) { + printf(" VU Data : "); + for (j = 4; j < size; j++) + printf("%x", data[j]); + printf("\n\n"); + } + break; + + case TELEMETRY_NVME_CLASS: + printf(" Event ID : 0x%02x %s\n", + id, telemetry_nvme_event_id_to_string(id)); + if ((id == ADMIN_QUEUE_NONZERO_STATUS) || + (id == IO_QUEUE_NONZERO_STATUS)) { + printf(" Cmd Op Code : 0x%02x\n", data[0]); + __u16 status = *(__u16 *)&data[1]; + __u16 cmd_id = *(__u16 *)&data[3]; + __u16 sq_id = *(__u16 *)&data[5]; + + printf(" Status Code : 0x%04x\n", le16_to_cpu(status)); + printf(" Cmd ID : 0x%04x\n", le16_to_cpu(cmd_id)); + printf(" SQ ID : 0x%04x\n", le16_to_cpu(sq_id)); + } else if (id == CC_REGISTER_CHANGED) { + __u32 cc_reg_data = *(__u32 *)data; + + printf(" CC Reg Data : 0x%08x\n", + le32_to_cpu(cc_reg_data)); + } else if (id == CSTS_REGISTER_CHANGED) { + __u32 csts_reg_data = *(__u32 *)data; + + printf(" CSTS Reg Data : 0x%08x\n", + le32_to_cpu(csts_reg_data)); + } + if (size > 8) + print_vu_event_data(size, (__u8 *)&data[8]); + break; + + case TELEMETRY_RESET_CLASS: + printf(" Event ID : 0x%02x %s\n", + id, telemetry_reset_event_id_to_string(id)); + if (size) + print_vu_event_data(size, data); + break; + + case TELEMETRY_BOOT_SEQ_CLASS: + printf(" Event ID : 0x%02x %s\n", + id, telemetry_boot_seq_event_id_to_string(id)); + if (size) + print_vu_event_data(size, data); + break; + + case TELEMETRY_FW_ASSERT_CLASS: + printf(" Event ID : 0x%02x %s\n", + id, telemetry_fw_assert_event_id_to_string(id)); + if (size) + print_vu_event_data(size, data); + break; + + case TELEMETRY_TEMPERATURE_CLASS: + printf(" Event ID : 0x%02x %s\n", + id, telemetry_temperature_event_id_to_string(id)); + if (size) + print_vu_event_data(size, data); + break; + + case TELEMETRY_MEDIA_DBG_CLASS: + printf(" Event ID : 0x%02x %s\n", + id, telemetry_media_debug_event_id_to_string(id)); + if (size) + print_vu_event_data(size, data); + break; + + case TELEMETRY_MEDIA_WEAR_CLASS: + printf(" Event ID : 0x%02x %s\n", + id, telemetry_media_debug_event_id_to_string(id)); + __u32 host_tb_written = *(__u32 *)&data[0]; + __u32 media_tb_written = *(__u32 *)&data[4]; + __u32 media_tb_erased = *(__u32 *)&data[8]; + + printf(" Host TB Written : 0x%04x\n", + le16_to_cpu(host_tb_written)); + printf(" Media TB Written : 0x%04x\n", + le16_to_cpu(media_tb_written)); + printf(" Media TB Erased : 0x%04x\n", + le16_to_cpu(media_tb_erased)); + + if (size > 12) + print_vu_event_data(size, (__u8 *)&data[12]); + break; + + case TELEMETRY_STAT_SNAPSHOT_CLASS: + printf(" Statistic ID : 0x%02x %s\n", + id, telemetry_stat_id_to_string(id)); + print_stats_desc((struct telemetry_stats_desc *)data); + break; + + default: + /* + * printf("Unknown Event Class Type\n"); + * printf("Data : 0x"); + * for (j = 0; j < size; j++) + * printf("%x", data[j]); + * printf("\n\n"); + */ + break; + } +} + +struct statistic_entry statistic_identifiers_map[] = { + { 0x00, "Error, this entry does not exist." }, + { 0x01, "Outstanding Admin Commands" }, + { 0x02, "Host Write Bandwidth"}, + { 0x03, "GC Write Bandwidth"}, + { 0x04, "Active Namespaces"}, + { 0x05, "Internal Write Workload"}, + { 0x06, "Internal Read Workload"}, + { 0x07, "Internal Write Queue Depth"}, + { 0x08, "Internal Read Queue Depth"}, + { 0x09, "Pending Trim LBA Count"}, + { 0x0A, "Host Trim LBA Request Count"}, + { 0x0B, "Current NVMe Power State"}, + { 0x0C, "Current DSSD Power State"}, + { 0x0D, "Program Fail Count"}, + { 0x0E, "Erase Fail Count"}, + { 0x0F, "Read Disturb Writes"}, + { 0x10, "Retention Writes"}, + { 0x11, "Wear Leveling Writes"}, + { 0x12, "Read Recovery Writes"}, + { 0x13, "GC Writes"}, + { 0x14, "SRAM Correctable Count"}, + { 0x15, "DRAM Correctable Count"}, + { 0x16, "SRAM Uncorrectable Count"}, + { 0x17, "DRAM Uncorrectable Count"}, + { 0x18, "Data Integrity Error Count"}, + { 0x19, "Read Retry Error Count"}, + { 0x1A, "PERST Events Count"}, + { 0x1B, "Max Die Bad Block"}, + { 0x1C, "Max NAND Channel Bad Block"}, + { 0x1D, "Minimum NAND Channel Bad Block"} +}; + +struct request_data host_log_page_header[] = { + { "LogIdentifier", 1 }, + { "Reserved1", 4 }, + { "IEEE OUI Identifier", 3 }, + { "Telemetry Host-Initiated Data Area 1 Last Block", 2 }, + { "Telemetry Host-Initiated Data Area 2 Last Block", 2 }, + { "Telemetry Host-Initiated Data Area 3 Last Block", 2 }, + { "Reserved2", 2 }, + { "Telemetry Host-Initiated Data Area 4 Last Block", 4 }, + { "Reserved3", 360 }, + { "Telemetry Host-Initiated Scope", 1 }, + { "Telemetry Host Initiated Generation Number", 1 }, + { "Telemetry Host-Initiated Data Available", 1 }, + { "Telemetry Controller-Initiated Data Generation Number", 1 } +}; + +struct request_data controller_log_page_header[] = { + { "LogIdentifier", 1 }, + { "Reserved1", 4 }, + { "IEEE OUI Identifier", 3 }, + { "Telemetry Host-Initiated Data Area 1 Last Block", 2 }, + { "Telemetry Host-Initiated Data Area 2 Last Block", 2 }, + { "Telemetry Host-Initiated Data Area 3 Last Block", 2 }, + { "Reserved2", 2 }, + { "Telemetry Host-Initiated Data Area 4 Last Block", 4 }, + { "Reserved3", 361 }, + { "Telemetry Controller-Initiated Scope", 1 }, + { "Telemetry Controller-Initiated Data Available", 1 }, + { "Telemetry Controller-Initiated Data Generation Number", 1 } +}; + +struct request_data reason_identifier[] = { + { "Error ID", 64 }, + { "File ID", 8 }, + { "Line Number", 2 }, + { "Valid Flags", 1 }, + { "Reserved", 21 }, + { "VU Reason Extension", 32 } +}; + +struct request_data ocp_header_in_da1[] = { + { "Major Version", 2 }, + { "Minor Version", 2 }, + { "Reserved1", 4 }, + { "Timestamp", 8 }, + { "Log page GUID", 16 }, + { "Number Telemetry Profiles Supported", 1 }, + { "Telemetry Profile Selected", 1 }, + { "Reserved2", 6 }, + { "Telemetry String Log Size", 8 }, + { "Reserved3", 8 }, + { "Firmware Revision", 8 }, + { "Reserved4", 32 }, + { "Data Area 1 Statistic Start", 8 }, + { "Data Area 1 Statistic Size", 8 }, + { "Data Area 2 Statistic Start", 8 }, + { "Data Area 2 Statistic Size", 8 }, + { "Reserved5", 32 }, + { "Event FIFO 1 Data Area", 1 }, + { "Event FIFO 2 Data Area", 1 }, + { "Event FIFO 3 Data Area", 1 }, + { "Event FIFO 4 Data Area", 1 }, + { "Event FIFO 5 Data Area", 1 }, + { "Event FIFO 6 Data Area", 1 }, + { "Event FIFO 7 Data Area", 1 }, + { "Event FIFO 8 Data Area", 1 }, + { "Event FIFO 9 Data Area", 1 }, + { "Event FIFO 10 Data Area", 1 }, + { "Event FIFO 11 Data Area", 1 }, + { "Event FIFO 12 Data Area", 1 }, + { "Event FIFO 13 Data Area", 1 }, + { "Event FIFO 14 Data Area", 1 }, + { "Event FIFO 15 Data Area", 1 }, + { "Event FIFO 16 Data Area", 1 }, + { "Event FIFO 1 Start", 8 }, + { "Event FIFO 1 Size", 8 }, + { "Event FIFO 2 Start", 8 }, + { "Event FIFO 2 Size", 8 }, + { "Event FIFO 3 Start", 8 }, + { "Event FIFO 3 Size", 8 }, + { "Event FIFO 4 Start", 8 }, + { "Event FIFO 4 Size", 8 }, + { "Event FIFO 5 Start", 8 }, + { "Event FIFO 5 Size", 8 }, + { "Event FIFO 6 Start", 8 }, + { "Event FIFO 6 Size", 8 }, + { "Event FIFO 7 Start", 8 }, + { "Event FIFO 7 Size", 8 }, + { "Event FIFO 8 Start", 8 }, + { "Event FIFO 8 Size", 8 }, + { "Event FIFO 9 Start", 8 }, + { "Event FIFO 9 Size", 8 }, + { "Event FIFO 10 Start", 8 }, + { "Event FIFO 10 Size", 8 }, + { "Event FIFO 11 Start", 8 }, + { "Event FIFO 11 Size", 8 }, + { "Event FIFO 12 Start", 8 }, + { "Event FIFO 12 Size", 8 }, + { "Event FIFO 13 Start", 8 }, + { "Event FIFO 13 Size", 8 }, + { "Event FIFO 14 Start", 8 }, + { "Event FIFO 14 Size", 8 }, + { "Event FIFO 15 Start", 8 }, + { "Event FIFO 15 Size", 8 }, + { "Event FIFO 16 Start", 8 }, + { "Event FIFO 16 Size", 8 }, + { "Reserved6", 80 } +}; + +struct request_data smart[] = { + { "Critical Warning", 1 }, + { "Composite Temperature", 2 }, + { "Available Spare", 1 }, + { "Available Spare Threshold", 1 }, + { "Percentage Used", 1 }, + { "Reserved1", 26 }, + { "Data Units Read", 16 }, + { "Data Units Written", 16 }, + { "Host Read Commands", 16 }, + { "Host Write Commands", 16 }, + { "Controller Busy Time", 16 }, + { "Power Cycles", 16 }, + { "Power On Hours", 16 }, + { "Unsafe Shutdowns", 16 }, + { "Media and Data Integrity Errors", 16 }, + { "Number of Error Information Log Entries", 16 }, + { "Warning Composite Temperature Time", 4 }, + { "Critical Composite Temperature Time", 4 }, + { "Temperature Sensor 1", 2 }, + { "Temperature Sensor 2", 2 }, + { "Temperature Sensor 3", 2 }, + { "Temperature Sensor 4", 2 }, + { "Temperature Sensor 5", 2 }, + { "Temperature Sensor 6", 2 }, + { "Temperature Sensor 7", 2 }, + { "Temperature Sensor 8", 2 }, + { "Thermal Management Temperature 1 Transition Count", 4 }, + { "Thermal Management Temperature 2 Transition Count", 4 }, + { "Total Time for Thermal Management Temperature 1", 4 }, + { "Total Time for Thermal Management Temperature 2", 4 }, + { "Reserved2", 280 } +}; + +struct request_data smart_extended[] = { + { "Physical Media Units Written", 16 }, + { "Physical Media Units Read", 16 }, + { "Bad User NAND Blocks Raw Count", 6 }, + { "Bad User NAND Blocks Normalized Value", 2 }, + { "Bad System NAND Blocks Raw Count", 6 }, + { "Bad System NAND Blocks Normalized Value", 2 }, + { "XOR Recovery Count", 8 }, + { "Uncorrectable Read Error Count", 8 }, + { "Soft ECC Error Count", 8 }, + { "End to End Correction Counts Detected Errors", 4 }, + { "End to End Correction Counts Corrected Errors", 4 }, + { "System Data Percent Used", 1 }, + { "Refresh Counts", 7 }, + { "Maximum User Data Erase Count", 4 }, + { "Minimum User Data Erase Count", 4 }, + { "Number of thermal throttling events", 1 }, + { "Current Throttling Status", 1 }, + { "Errata Version Field", 1 }, + { "Point Version Field", 2 }, + { "Minor Version Field", 2 }, + { "Major Version Field", 1 }, + { "PCIe Correctable Error Count", 8 }, + { "Incomplete Shutdowns", 4 }, + { "Reserved1", 4 }, + { "Percent Free Blocks", 1 }, + { "Reserved2", 7 }, + { "Capacitor Health", 2 }, + { "NVMe Base Errata Version", 1 }, + { "NVMe Command Set Errata Version", 1 }, + { "Reserved3", 4 }, + { "Unaligned IO", 8 }, + { "Security Version Number", 8 }, + { "Total NUSE", 8 }, + { "PLP Start Count", 16 }, + { "Endurance Estimate", 16 }, + { "PCIe Link Retraining Count", 8 }, + { "Power State Change Count", 8 }, + { "Lowest Permitted Firmware Revision", 8 }, + { "Reserved4", 278 }, + { "Log Page Version", 2 }, + { "Log page GUID", 16 } +}; + +void json_add_formatted_u32_str(struct json_object *pobject, const char *msg, unsigned int pdata) +{ + char data_str[70] = { 0 }; + + sprintf(data_str, "0x%x", pdata); + json_object_add_value_string(pobject, msg, data_str); +} + +void json_add_formatted_var_size_str(struct json_object *pobject, const char *msg, __u8 *pdata, + unsigned int data_size) +{ + char description_str[256] = ""; + char temp_buffer[3] = { 0 }; + + for (size_t i = 0; i < data_size; ++i) { + sprintf(temp_buffer, "%02X", pdata[i]); + strcat(description_str, temp_buffer); + } + + json_object_add_value_string(pobject, msg, description_str); +} + +int get_telemetry_das_offset_and_size( + struct nvme_ocp_telemetry_common_header *ptelemetry_common_header, + struct nvme_ocp_telemetry_offsets *ptelemetry_das_offset) +{ + if (NULL == ptelemetry_common_header || NULL == ptelemetry_das_offset) { + nvme_show_error("Invalid input arguments."); + return -1; + } + + if (ptelemetry_common_header->log_id == NVME_LOG_LID_TELEMETRY_HOST) + ptelemetry_das_offset->header_size = + sizeof(struct nvme_ocp_telemetry_host_initiated_header); + else if (ptelemetry_common_header->log_id == NVME_LOG_LID_TELEMETRY_CTRL) + ptelemetry_das_offset->header_size = + sizeof(struct nvme_ocp_telemetry_controller_initiated_header); + else + return -1; + + ptelemetry_das_offset->da1_start_offset = ptelemetry_das_offset->header_size; + ptelemetry_das_offset->da1_size = ptelemetry_common_header->da1_last_block * + OCP_TELEMETRY_DATA_BLOCK_SIZE; + + ptelemetry_das_offset->da2_start_offset = ptelemetry_das_offset->da1_start_offset + + ptelemetry_das_offset->da1_size; + ptelemetry_das_offset->da2_size = + (ptelemetry_common_header->da2_last_block - + ptelemetry_common_header->da1_last_block) * OCP_TELEMETRY_DATA_BLOCK_SIZE; + + ptelemetry_das_offset->da3_start_offset = ptelemetry_das_offset->da2_start_offset + + ptelemetry_das_offset->da2_size; + ptelemetry_das_offset->da3_size = + (ptelemetry_common_header->da3_last_block - + ptelemetry_common_header->da2_last_block) * OCP_TELEMETRY_DATA_BLOCK_SIZE; + + ptelemetry_das_offset->da4_start_offset = ptelemetry_das_offset->da3_start_offset + + ptelemetry_das_offset->da3_size; + ptelemetry_das_offset->da4_size = + (ptelemetry_common_header->da4_last_block - + ptelemetry_common_header->da3_last_block) * OCP_TELEMETRY_DATA_BLOCK_SIZE; + + return 0; +} + +int get_static_id_ascii_string(int identifier, char *description) +{ + if (pstring_buffer == NULL) + return -1; + + struct nvme_ocp_telemetry_string_header *pocp_ts_header = + (struct nvme_ocp_telemetry_string_header *)pstring_buffer; + + //Calculating the sizes of the tables. Note: Data is present in the form of DWORDS, + //So multiplying with sizeof(DWORD) + unsigned long long sits_table_size = (pocp_ts_header->sitsz) * SIZE_OF_DWORD; + + //Calculating number of entries present in all 3 tables + int sits_entries = (int)sits_table_size / + sizeof(struct nvme_ocp_statistics_identifier_string_table); + + for (int sits_entry = 0; sits_entry < sits_entries; sits_entry++) { + struct nvme_ocp_statistics_identifier_string_table + *peach_statistic_entry = + (struct nvme_ocp_statistics_identifier_string_table *) + (pstring_buffer + (pocp_ts_header->sits * SIZE_OF_DWORD) + + (sits_entry * + sizeof(struct nvme_ocp_statistics_identifier_string_table))); + + if (identifier == (int)peach_statistic_entry->vs_statistic_identifier) { + char *pdescription = (char *)(pstring_buffer + + (pocp_ts_header->ascts * SIZE_OF_DWORD) + + (peach_statistic_entry->ascii_id_offset * + SIZE_OF_DWORD)); + + memcpy(description, pdescription, + peach_statistic_entry->ascii_id_length + 1); + + // If ASCII string isn't found, see in our internal Map + // for 2.5 Spec defined strings (id < 0x1D). + if ((description == NULL) && (identifier < 0x1D)) + memcpy(description, + statistic_identifiers_map[identifier].description, + peach_statistic_entry->ascii_id_length + 1); + return 0; + } + } + + return -1; +} + +int get_event_id_ascii_string(int identifier, int debug_event_class, char *description) +{ + if (pstring_buffer == NULL) + return -1; + + struct nvme_ocp_telemetry_string_header *pocp_ts_header = + (struct nvme_ocp_telemetry_string_header *)pstring_buffer; + + //Calculating the sizes of the tables. Note: Data is present in the form of DWORDS, + //So multiplying with sizeof(DWORD) + unsigned long long ests_table_size = (pocp_ts_header->estsz) * SIZE_OF_DWORD; + + //Calculating number of entries present in all 3 tables + int ests_entries = (int)ests_table_size / sizeof(struct nvme_ocp_event_string_table); + + for (int ests_entry = 0; ests_entry < ests_entries; ests_entry++) { + struct nvme_ocp_event_string_table *peach_event_entry = + (struct nvme_ocp_event_string_table *) + (pstring_buffer + (pocp_ts_header->ests * SIZE_OF_DWORD) + + (ests_entry * sizeof(struct nvme_ocp_event_string_table))); + + if (identifier == (int)peach_event_entry->event_identifier && + debug_event_class == (int)peach_event_entry->debug_event_class) { + char *pdescription = (char *)(pstring_buffer + + (pocp_ts_header->ascts * SIZE_OF_DWORD) + + (peach_event_entry->ascii_id_offset * SIZE_OF_DWORD)); + + memcpy(description, pdescription, + peach_event_entry->ascii_id_length + 1); + return 0; + } + } + + return -1; +} + +int get_vu_event_id_ascii_string(int identifier, int debug_event_class, char *description) +{ + if (pstring_buffer == NULL) + return -1; + + struct nvme_ocp_telemetry_string_header *pocp_ts_header = + (struct nvme_ocp_telemetry_string_header *)pstring_buffer; + + //Calculating the sizes of the tables. Note: Data is present in the form of DWORDS, + //So multiplying with sizeof(DWORD) + unsigned long long vuests_table_size = (pocp_ts_header->vu_estsz) * SIZE_OF_DWORD; + + //Calculating number of entries present in all 3 tables + int vu_ests_entries = (int)vuests_table_size / + sizeof(struct nvme_ocp_vu_event_string_table); + + for (int vu_ests_entry = 0; vu_ests_entry < vu_ests_entries; vu_ests_entry++) { + struct nvme_ocp_vu_event_string_table *peach_vu_event_entry = + (struct nvme_ocp_vu_event_string_table *) + (pstring_buffer + (pocp_ts_header->vu_ests * SIZE_OF_DWORD) + + (vu_ests_entry * sizeof(struct nvme_ocp_vu_event_string_table))); + + if (identifier == (int)peach_vu_event_entry->vu_event_identifier && + debug_event_class == + (int)peach_vu_event_entry->debug_event_class) { + char *pdescription = (char *)(pstring_buffer + + (pocp_ts_header->ascts * SIZE_OF_DWORD) + + (peach_vu_event_entry->ascii_id_offset * SIZE_OF_DWORD)); + + memcpy(description, pdescription, + peach_vu_event_entry->ascii_id_length + 1); + return 0; + } + } + + return -1; +} + +int parse_ocp_telemetry_string_log(int event_fifo_num, int identifier, int debug_event_class, + enum ocp_telemetry_string_tables string_table, char *description) +{ + if (pstring_buffer == NULL) + return -1; + + if (event_fifo_num != 0) { + struct nvme_ocp_telemetry_string_header *pocp_ts_header = + (struct nvme_ocp_telemetry_string_header *)pstring_buffer; + + if (*pocp_ts_header->fifo_ascii_string[event_fifo_num-1] != '\0') + memcpy(description, pocp_ts_header->fifo_ascii_string[event_fifo_num-1], + 16); + else + description = ""; + + return 0; + } + + if (string_table == STATISTICS_IDENTIFIER_STRING) + get_static_id_ascii_string(identifier, description); + else if (string_table == EVENT_STRING) + get_event_id_ascii_string(identifier, debug_event_class, description); + else if (string_table == VU_EVENT_STRING) + get_vu_event_id_ascii_string(identifier, debug_event_class, description); + + return 0; +} + +void parse_time_stamp_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, + struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, + struct json_object *pevent_fifos_object, FILE *fp) +{ + struct nvme_ocp_time_stamp_dbg_evt_class_format *ptime_stamp_event = + (struct nvme_ocp_time_stamp_dbg_evt_class_format *) pevent_specific_data; + + int vu_event_id = (int)ptime_stamp_event->vu_event_identifier; + + unsigned int data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD)- + sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format)); + + __u8 *pdata = (__u8 *)ptime_stamp_event + + sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format); + + char description_str[256] = ""; + + parse_ocp_telemetry_string_log(0, ptime_stamp_event->vu_event_identifier, + pevent_descriptor->debug_event_class_type, + VU_EVENT_STRING, description_str); + + if (pevent_fifos_object != NULL) { + json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA, + ptime_stamp_event->time_stamp, DATA_SIZE_8); + json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, + vu_event_id); + json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING, + description_str); + json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata, + data_size); + } else { + if (fp) { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, + ptime_stamp_event->time_stamp, DATA_SIZE_8, fp); + fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } else { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, + ptime_stamp_event->time_stamp, DATA_SIZE_8, fp); + printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + printf("%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + } +} + +void parse_pcie_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, + struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, + struct json_object *pevent_fifos_object, FILE *fp) +{ + struct nvme_ocp_pcie_dbg_evt_class_format *ppcie_event = + (struct nvme_ocp_pcie_dbg_evt_class_format *) pevent_specific_data; + int vu_event_id = (int) ppcie_event->vu_event_identifier; + unsigned int data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - + sizeof(struct nvme_ocp_pcie_dbg_evt_class_format)); + __u8 *pdata = (__u8 *) ppcie_event + sizeof(struct nvme_ocp_pcie_dbg_evt_class_format); + char description_str[256] = ""; + + parse_ocp_telemetry_string_log(0, ppcie_event->vu_event_identifier, + pevent_descriptor->debug_event_class_type, VU_EVENT_STRING, description_str); + + if (pevent_fifos_object != NULL) { + json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA, + ppcie_event->pCIeDebugEventData, DATA_SIZE_4); + json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, + vu_event_id); + json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING, + description_str); + json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata, + data_size); + } else { + if (fp) { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, + ppcie_event->pCIeDebugEventData, DATA_SIZE_4, fp); + fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } else { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, + ppcie_event->pCIeDebugEventData, DATA_SIZE_4, fp); + printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + printf("%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + } +} + +void parse_nvme_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, + struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, + struct json_object *pevent_fifos_object, FILE *fp) +{ + struct nvme_ocp_nvme_dbg_evt_class_format *pnvme_event = + (struct nvme_ocp_nvme_dbg_evt_class_format *) pevent_specific_data; + int vu_event_id = (int) pnvme_event->vu_event_identifier; + unsigned int data_size = ((pevent_descriptor->event_data_size * + SIZE_OF_DWORD) - sizeof(struct nvme_ocp_nvme_dbg_evt_class_format)); + __u8 *pdata = (__u8 *) pnvme_event + sizeof(struct nvme_ocp_nvme_dbg_evt_class_format); + char description_str[256] = ""; + + parse_ocp_telemetry_string_log(0, pnvme_event->vu_event_identifier, + pevent_descriptor->debug_event_class_type, VU_EVENT_STRING, + description_str); + + if (pevent_fifos_object != NULL) { + json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA, + pnvme_event->nvmeDebugEventData, DATA_SIZE_8); + json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, + vu_event_id); + json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING, + description_str); + json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata, + data_size); + } else { + if (fp) { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, + pnvme_event->nvmeDebugEventData, DATA_SIZE_8, fp); + fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } else { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, + pnvme_event->nvmeDebugEventData, DATA_SIZE_8, fp); + printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + printf("%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + } +} + +void parse_common_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, + struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, + struct json_object *pevent_fifos_object, FILE *fp) +{ + struct nvme_ocp_common_dbg_evt_class_format *pcommon_debug_event = + (struct nvme_ocp_common_dbg_evt_class_format *) pevent_specific_data; + int vu_event_id = (int) pcommon_debug_event->vu_event_identifier; + unsigned int data_size = ((pevent_descriptor->event_data_size * + SIZE_OF_DWORD) - sizeof(struct nvme_ocp_common_dbg_evt_class_format)); + __u8 *pdata = (__u8 *) pcommon_debug_event + + sizeof(struct nvme_ocp_common_dbg_evt_class_format); + char description_str[256] = ""; + + parse_ocp_telemetry_string_log(0, pcommon_debug_event->vu_event_identifier, + pevent_descriptor->debug_event_class_type, VU_EVENT_STRING, description_str); + + if (pevent_fifos_object != NULL) { + json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, + vu_event_id); + json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING, + description_str); + json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata, + data_size); + } else { + if (fp) { + fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } else { + printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + printf("%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + } +} + +void parse_media_wear_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, + struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, + struct json_object *pevent_fifos_object, FILE *fp) +{ + struct nvme_ocp_media_wear_dbg_evt_class_format *pmedia_wear_event = + (struct nvme_ocp_media_wear_dbg_evt_class_format *) pevent_specific_data; + int vu_event_id = (int) pmedia_wear_event->vu_event_identifier; + unsigned int data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - + sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format)); + __u8 *pdata = (__u8 *) pmedia_wear_event + + sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format); + char description_str[256] = ""; + + parse_ocp_telemetry_string_log(0, pmedia_wear_event->vu_event_identifier, + pevent_descriptor->debug_event_class_type, VU_EVENT_STRING, + description_str); + + if (pevent_fifos_object != NULL) { + json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA, + pmedia_wear_event->currentMediaWear, DATA_SIZE_12); + json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, + vu_event_id); + json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING, + description_str); + json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata, + data_size); + } else { + if (fp) { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, + pmedia_wear_event->currentMediaWear, DATA_SIZE_12, fp); + fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } else { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, + pmedia_wear_event->currentMediaWear, DATA_SIZE_12, NULL); + printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + printf("%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + } +} + +int parse_event_fifo(unsigned int fifo_num, unsigned char *pfifo_start, + struct json_object *pevent_fifos_object, unsigned char *pstring_buffer, + struct nvme_ocp_telemetry_offsets *poffsets, __u64 fifo_size, FILE *fp) +{ + if (NULL == pfifo_start || NULL == poffsets) { + nvme_show_error("Input buffer was NULL"); + return -1; + } + + int status = 0; + unsigned int event_fifo_number = fifo_num + 1; + char *description = (char *)malloc((40 + 1) * sizeof(char)); + + memset(description, 0, sizeof(40)); + + status = + parse_ocp_telemetry_string_log(event_fifo_number, 0, 0, EVENT_STRING, description); + + if (status != 0) { + nvme_show_error("Failed to get C9 String. status: %d\n", status); + return -1; + } + + char event_fifo_name[100] = {0}; + + snprintf(event_fifo_name, sizeof(event_fifo_name), "%s%d%s%s", "EVENT FIFO ", + event_fifo_number, " - ", description); + + struct json_object *pevent_fifo_array = NULL; + + if (pevent_fifos_object != NULL) + pevent_fifo_array = json_create_array(); + else { + char buffer[1024] = {0}; + + sprintf(buffer, "%s%s\n%s", STR_LINE, event_fifo_name, STR_LINE); + if (fp) + fprintf(fp, "%s", buffer); + else + printf("%s", buffer); + } + + int offset_to_move = 0; + unsigned int event_des_size = sizeof(struct nvme_ocp_telemetry_event_descriptor); + + while ((fifo_size > 0) && (offset_to_move < fifo_size)) { + struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor = + (struct nvme_ocp_telemetry_event_descriptor *) + (pfifo_start + offset_to_move); + + if (pevent_descriptor != NULL && pevent_descriptor->event_data_size >= 0) { + //Data is present in the form of DWORDS, So multiplying with sizeof(DWORD) + unsigned int data_size = pevent_descriptor->event_data_size * + SIZE_OF_DWORD; + + __u8 *pevent_specific_data = (__u8 *)pevent_descriptor + event_des_size; + + char description_str[256] = ""; + + parse_ocp_telemetry_string_log(0, pevent_descriptor->event_id, + pevent_descriptor->debug_event_class_type, EVENT_STRING, + description_str); + + struct json_object *pevent_descriptor_obj = + ((pevent_fifos_object != NULL)?json_create_object():NULL); + + if (pevent_descriptor_obj != NULL) { + json_add_formatted_u32_str(pevent_descriptor_obj, + STR_DBG_EVENT_CLASS_TYPE, + pevent_descriptor->debug_event_class_type); + json_add_formatted_u32_str(pevent_descriptor_obj, + STR_EVENT_IDENTIFIER, pevent_descriptor->event_id); + json_object_add_value_string(pevent_descriptor_obj, + STR_EVENT_STRING, description_str); + json_add_formatted_u32_str(pevent_descriptor_obj, + STR_EVENT_DATA_SIZE, pevent_descriptor->event_data_size); + + if (pevent_descriptor->debug_event_class_type >= 0x80) + json_add_formatted_var_size_str(pevent_descriptor_obj, + STR_VU_DATA, pevent_specific_data, data_size); + } else { + if (fp) { + fprintf(fp, "%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE, + pevent_descriptor->debug_event_class_type); + fprintf(fp, "%s: 0x%x\n", STR_EVENT_IDENTIFIER, + pevent_descriptor->event_id); + fprintf(fp, "%s: %s\n", STR_EVENT_STRING, description_str); + fprintf(fp, "%s: 0x%x\n", STR_EVENT_DATA_SIZE, + pevent_descriptor->event_data_size); + } else { + printf("%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE, + pevent_descriptor->debug_event_class_type); + printf("%s: 0x%x\n", STR_EVENT_IDENTIFIER, + pevent_descriptor->event_id); + printf("%s: %s\n", STR_EVENT_STRING, description_str); + printf("%s: 0x%x\n", STR_EVENT_DATA_SIZE, + pevent_descriptor->event_data_size); + } + + if (pevent_descriptor->debug_event_class_type >= 0x80) + print_formatted_var_size_str(STR_VU_DATA, + pevent_specific_data, data_size, fp); + } + + switch (pevent_descriptor->debug_event_class_type) { + case TIME_STAMP_CLASS_TYPE: + parse_time_stamp_event(pevent_descriptor, pevent_descriptor_obj, + pevent_specific_data, pevent_fifos_object, fp); + break; + case PCIE_CLASS_TYPE: + parse_pcie_event(pevent_descriptor, pevent_descriptor_obj, + pevent_specific_data, pevent_fifos_object, fp); + break; + case NVME_CLASS_TYPE: + parse_nvme_event(pevent_descriptor, pevent_descriptor_obj, + pevent_specific_data, pevent_fifos_object, fp); + break; + case RESET_CLASS_TYPE: + case BOOT_SEQUENCE_CLASS_TYPE: + case FIRMWARE_ASSERT_CLASS_TYPE: + case TEMPERATURE_CLASS_TYPE: + case MEDIA_CLASS_TYPE: + parse_common_event(pevent_descriptor, pevent_descriptor_obj, + pevent_specific_data, pevent_fifos_object, fp); + break; + case MEDIA_WEAR_CLASS_TYPE: + parse_media_wear_event(pevent_descriptor, pevent_descriptor_obj, + pevent_specific_data, pevent_fifos_object, fp); + break; + case STATISTIC_SNAPSHOT_CLASS_TYPE: { + struct nvme_ocp_statistic_snapshot_evt_class_format + *pStaticSnapshotEvent = + (struct nvme_ocp_statistic_snapshot_evt_class_format *) + pevent_specific_data; + struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry = + (struct nvme_ocp_telemetry_statistic_descriptor *) + (&pStaticSnapshotEvent->statisticDescriptorData); + + parse_statistic(pstatistic_entry, pevent_descriptor_obj, fp); + break; + } + case RESERVED_CLASS_TYPE: + default: + break; + } + + if (pevent_descriptor_obj != NULL && pevent_fifo_array != NULL) + json_array_add_value_object(pevent_fifo_array, pevent_descriptor_obj); + else { + if (fp) + fprintf(fp, STR_LINE2); + else + printf(STR_LINE2); + } + } else + break; + + offset_to_move += (pevent_descriptor->event_data_size * SIZE_OF_DWORD + event_des_size); + } + + if (pevent_fifos_object != NULL && pevent_fifo_array != NULL) + json_object_add_value_array(pevent_fifos_object, event_fifo_name, + pevent_fifo_array); + + free(description); + return 0; +} + +int parse_event_fifos(struct json_object *root, struct nvme_ocp_telemetry_offsets *poffsets, + FILE *fp) +{ + if (poffsets == NULL) { + nvme_show_error("Input buffer was NULL"); + return -1; + } + + struct json_object *pevent_fifos_object = NULL; + + if (root != NULL) + pevent_fifos_object = json_create_object(); + + __u8 *pda1_header_offset = ptelemetry_buffer + poffsets->da1_start_offset;//512 + __u8 *pda2_offset = ptelemetry_buffer + poffsets->da2_start_offset; + struct nvme_ocp_header_in_da1 *pda1_header = (struct nvme_ocp_header_in_da1 *) + pda1_header_offset; + struct nvme_ocp_event_fifo_data event_fifo[MAX_NUM_FIFOS]; + + for (int fifo_num = 0; fifo_num < MAX_NUM_FIFOS; fifo_num++) { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_da[fifo_num]; + event_fifo[fifo_num].event_fifo_start = + pda1_header->fifo_offsets[fifo_num].event_fifo_start; + event_fifo[fifo_num].event_fifo_size = + pda1_header->fifo_offsets[fifo_num].event_fifo_size; + } + + //Parse all the FIFOs DA wise + for (int fifo_no = 0; fifo_no < MAX_NUM_FIFOS; fifo_no++) { + if (event_fifo[fifo_no].event_fifo_da == poffsets->data_area) { + __u64 fifo_offset = + (event_fifo[fifo_no].event_fifo_start * SIZE_OF_DWORD); + __u64 fifo_size = + (event_fifo[fifo_no].event_fifo_size * SIZE_OF_DWORD); + __u8 *pfifo_start = NULL; + + if (event_fifo[fifo_no].event_fifo_da == 1) + pfifo_start = pda1_header_offset + fifo_offset; + else if (event_fifo[fifo_no].event_fifo_da == 2) + pfifo_start = pda2_offset + fifo_offset; + else { + nvme_show_error("Unsupported Data Area:[%d]", poffsets->data_area); + return -1; + } + + int status = parse_event_fifo(fifo_no, pfifo_start, pevent_fifos_object, + pstring_buffer, poffsets, fifo_size, fp); + + if (status != 0) { + nvme_show_error("Failed to parse Event FIFO. status:%d\n", status); + return -1; + } + } + } + + if (pevent_fifos_object != NULL && root != NULL) { + const char *data_area = (poffsets->data_area == 1 ? STR_DA_1_EVENT_FIFO_INFO : + STR_DA_2_EVENT_FIFO_INFO); + + json_object_add_value_array(root, data_area, pevent_fifos_object); + } + + return 0; +} + +int parse_statistic(struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry, + struct json_object *pstats_array, FILE *fp) +{ + if (pstatistic_entry == NULL) { + nvme_show_error("Input buffer was NULL"); + return -1; + } + + unsigned int data_size = pstatistic_entry->statistic_data_size * SIZE_OF_DWORD; + __u8 *pdata = (__u8 *)pstatistic_entry + + sizeof(struct nvme_ocp_telemetry_statistic_descriptor); + char description_str[256] = ""; + + parse_ocp_telemetry_string_log(0, pstatistic_entry->statistic_id, 0, + STATISTICS_IDENTIFIER_STRING, description_str); + + if (pstats_array != NULL) { + struct json_object *pstatistics_object = json_create_object(); + + json_add_formatted_u32_str(pstatistics_object, STR_STATISTICS_IDENTIFIER, + pstatistic_entry->statistic_id); + json_object_add_value_string(pstatistics_object, STR_STATISTICS_IDENTIFIER_STR, + description_str); + json_add_formatted_u32_str(pstatistics_object, + STR_STATISTICS_INFO_BEHAVIOUR_TYPE, + pstatistic_entry->statistic_info_behaviour_type); + json_add_formatted_u32_str(pstatistics_object, STR_STATISTICS_INFO_RESERVED, + pstatistic_entry->statistic_info_reserved); + json_add_formatted_u32_str(pstatistics_object, STR_NAMESPACE_IDENTIFIER, + pstatistic_entry->ns_info_nsid); + json_add_formatted_u32_str(pstatistics_object, STR_NAMESPACE_INFO_VALID, + pstatistic_entry->ns_info_ns_info_valid); + json_add_formatted_u32_str(pstatistics_object, STR_STATISTICS_DATA_SIZE, + pstatistic_entry->statistic_data_size); + json_add_formatted_u32_str(pstatistics_object, STR_RESERVED, + pstatistic_entry->reserved); + json_add_formatted_var_size_str(pstatistics_object, STR_STATISTICS_SPECIFIC_DATA, + pdata, data_size); + + if (pstatistics_object != NULL) + json_array_add_value_object(pstats_array, pstatistics_object); + } else { + if (fp) { + fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_IDENTIFIER, + pstatistic_entry->statistic_id); + fprintf(fp, "%s: %s\n", STR_STATISTICS_IDENTIFIER_STR, description_str); + fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_INFO_BEHAVIOUR_TYPE, + pstatistic_entry->statistic_info_behaviour_type); + fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_INFO_RESERVED, + pstatistic_entry->statistic_info_reserved); + fprintf(fp, "%s: 0x%x\n", STR_NAMESPACE_IDENTIFIER, + pstatistic_entry->ns_info_nsid); + fprintf(fp, "%s: 0x%x\n", STR_NAMESPACE_INFO_VALID, + pstatistic_entry->ns_info_ns_info_valid); + fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_DATA_SIZE, + pstatistic_entry->statistic_data_size); + fprintf(fp, "%s: 0x%x\n", STR_RESERVED, pstatistic_entry->reserved); + print_formatted_var_size_str(STR_STATISTICS_SPECIFIC_DATA, pdata, + data_size, fp); + fprintf(fp, STR_LINE2); + } else { + printf("%s: 0x%x\n", STR_STATISTICS_IDENTIFIER, + pstatistic_entry->statistic_id); + printf("%s: %s\n", STR_STATISTICS_IDENTIFIER_STR, description_str); + printf("%s: 0x%x\n", STR_STATISTICS_INFO_BEHAVIOUR_TYPE, + pstatistic_entry->statistic_info_behaviour_type); + printf("%s: 0x%x\n", STR_STATISTICS_INFO_RESERVED, + pstatistic_entry->statistic_info_reserved); + printf("%s: 0x%x\n", STR_NAMESPACE_IDENTIFIER, + pstatistic_entry->ns_info_nsid); + printf("%s: 0x%x\n", STR_NAMESPACE_INFO_VALID, + pstatistic_entry->ns_info_ns_info_valid); + printf("%s: 0x%x\n", STR_STATISTICS_DATA_SIZE, + pstatistic_entry->statistic_data_size); + printf("%s: 0x%x\n", STR_RESERVED, pstatistic_entry->reserved); + print_formatted_var_size_str(STR_STATISTICS_SPECIFIC_DATA, pdata, + data_size, fp); + printf(STR_LINE2); + } + } + + return 0; +} + +int parse_statistics(struct json_object *root, struct nvme_ocp_telemetry_offsets *poffsets, + FILE *fp) +{ + if (poffsets == NULL) { + nvme_show_error("Input buffer was NULL"); + return -1; + } + + __u8 *pda1_ocp_header_offset = ptelemetry_buffer + poffsets->header_size;//512 + __u32 statistics_size = 0; + __u32 stats_da_1_start_dw = 0, stats_da_1_size_dw = 0; + __u32 stats_da_2_start_dw = 0, stats_da_2_size_dw = 0; + __u8 *pstats_offset = NULL; + + if (poffsets->data_area == 1) { + __u32 stats_da_1_start = *(__u32 *)(pda1_ocp_header_offset + + offsetof(struct nvme_ocp_header_in_da1, da1_statistic_start)); + __u32 stats_da_1_size = *(__u32 *)(pda1_ocp_header_offset + + offsetof(struct nvme_ocp_header_in_da1, da1_statistic_size)); + + //Data is present in the form of DWORDS, So multiplying with sizeof(DWORD) + stats_da_1_start_dw = (stats_da_1_start * SIZE_OF_DWORD); + stats_da_1_size_dw = (stats_da_1_size * SIZE_OF_DWORD); + + pstats_offset = pda1_ocp_header_offset + stats_da_1_start_dw; + statistics_size = stats_da_1_size_dw; + } else if (poffsets->data_area == 2) { + __u32 stats_da_2_start = *(__u32 *)(pda1_ocp_header_offset + + offsetof(struct nvme_ocp_header_in_da1, da2_statistic_start)); + __u32 stats_da_2_size = *(__u32 *)(pda1_ocp_header_offset + + offsetof(struct nvme_ocp_header_in_da1, da2_statistic_size)); + + stats_da_2_start_dw = (stats_da_2_start * SIZE_OF_DWORD); + stats_da_2_size_dw = (stats_da_2_size * SIZE_OF_DWORD); + + pstats_offset = pda1_ocp_header_offset + poffsets->da1_size + stats_da_2_start_dw; + statistics_size = stats_da_2_size_dw; + } else { + nvme_show_error("Unsupported Data Area:[%d]", poffsets->data_area); + return -1; + } + + struct json_object *pstats_array = ((root != NULL) ? json_create_array() : NULL); + + __u32 stat_des_size = sizeof(struct nvme_ocp_telemetry_statistic_descriptor);//8 + __u32 offset_to_move = 0; + + while (((statistics_size > 0) && (offset_to_move < statistics_size))) { + struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry = + (struct nvme_ocp_telemetry_statistic_descriptor *) + (pstats_offset + offset_to_move); + + parse_statistic(pstatistic_entry, pstats_array, fp); + offset_to_move += (pstatistic_entry->statistic_data_size * SIZE_OF_DWORD + + stat_des_size); + } + + if (root != NULL && pstats_array != NULL) { + const char *pdata_area = + (poffsets->data_area == 1 ? STR_DA_1_STATS : STR_DA_2_STATS); + + json_object_add_value_array(root, pdata_area, pstats_array); + } + + return 0; +} + +int print_ocp_telemetry_normal(struct ocp_telemetry_parse_options *options) +{ + int status = 0; + + if (options->output_file != NULL) { + FILE *fp = fopen(options->output_file, "w"); + + if (fp) { + fprintf(fp, STR_LINE); + fprintf(fp, "%s\n", STR_LOG_PAGE_HEADER); + fprintf(fp, STR_LINE); + if (!strcmp(options->telemetry_type, "host")) + generic_structure_parser(ptelemetry_buffer, host_log_page_header, + ARRAY_SIZE(host_log_page_header), NULL, 0, fp); + else if (!strcmp(options->telemetry_type, "controller")) + generic_structure_parser(ptelemetry_buffer, + controller_log_page_header, + ARRAY_SIZE(controller_log_page_header), NULL, 0, fp); + fprintf(fp, STR_LINE); + fprintf(fp, "%s\n", STR_REASON_IDENTIFIER); + fprintf(fp, STR_LINE); + __u8 *preason_identifier_offset = ptelemetry_buffer + + offsetof(struct nvme_ocp_telemetry_host_initiated_header, + reason_id); + + generic_structure_parser(preason_identifier_offset, reason_identifier, + ARRAY_SIZE(reason_identifier), NULL, 0, fp); + + fprintf(fp, STR_LINE); + fprintf(fp, "%s\n", STR_TELEMETRY_HOST_DATA_BLOCK_1); + fprintf(fp, STR_LINE); + + //Set DA to 1 and get offsets + struct nvme_ocp_telemetry_offsets offsets = { 0 }; + + offsets.data_area = 1;// Default DA - DA1 + + struct nvme_ocp_telemetry_common_header *ptelemetry_common_header = + (struct nvme_ocp_telemetry_common_header *) ptelemetry_buffer; + + get_telemetry_das_offset_and_size(ptelemetry_common_header, &offsets); + + __u8 *pda1_header_offset = ptelemetry_buffer + + offsets.da1_start_offset;//512 + + generic_structure_parser(pda1_header_offset, ocp_header_in_da1, + ARRAY_SIZE(ocp_header_in_da1), NULL, 0, fp); + + fprintf(fp, STR_LINE); + fprintf(fp, "%s\n", STR_SMART_HEALTH_INFO); + fprintf(fp, STR_LINE); + __u8 *pda1_smart_offset = pda1_header_offset + + offsetof(struct nvme_ocp_header_in_da1, smart_health_info); + //512+512 =1024 + + generic_structure_parser(pda1_smart_offset, smart, ARRAY_SIZE(smart), + NULL, 0, fp); + + fprintf(fp, STR_LINE); + fprintf(fp, "%s\n", STR_SMART_HEALTH_INTO_EXTENDED); + fprintf(fp, STR_LINE); + __u8 *pda1_smart_ext_offset = pda1_header_offset + + offsetof(struct nvme_ocp_header_in_da1, + smart_health_info_extended); + + generic_structure_parser(pda1_smart_ext_offset, smart_extended, + ARRAY_SIZE(smart_extended), NULL, 0, fp); + + fprintf(fp, STR_LINE); + fprintf(fp, "%s\n", STR_DA_1_STATS); + fprintf(fp, STR_LINE); + + status = parse_statistics(NULL, &offsets, fp); + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + + fprintf(fp, STR_LINE); + fprintf(fp, "%s\n", STR_DA_1_EVENT_FIFO_INFO); + fprintf(fp, STR_LINE); + status = parse_event_fifos(NULL, &offsets, fp); + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + + //Set the DA to 2 + if (options->data_area == 2) { + offsets.data_area = 2; + fprintf(fp, STR_LINE); + fprintf(fp, "%s\n", STR_DA_2_STATS); + fprintf(fp, STR_LINE); + status = parse_statistics(NULL, &offsets, fp); + + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + + fprintf(fp, STR_LINE); + fprintf(fp, "%s\n", STR_DA_2_EVENT_FIFO_INFO); + fprintf(fp, STR_LINE); + status = parse_event_fifos(NULL, &offsets, fp); + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + } + + fprintf(fp, STR_LINE); + fclose(fp); + } else { + nvme_show_error("Failed to open %s file.\n", options->output_file); + return -1; + } + } else { + printf(STR_LINE); + printf("%s\n", STR_LOG_PAGE_HEADER); + printf(STR_LINE); + if (!strcmp(options->telemetry_type, "host")) + generic_structure_parser(ptelemetry_buffer, host_log_page_header, + ARRAY_SIZE(host_log_page_header), NULL, 0, NULL); + else if (!strcmp(options->telemetry_type, "controller")) + generic_structure_parser(ptelemetry_buffer, controller_log_page_header, + ARRAY_SIZE(controller_log_page_header), NULL, 0, NULL); + + printf(STR_LINE); + printf("%s\n", STR_REASON_IDENTIFIER); + printf(STR_LINE); + __u8 *preason_identifier_offset = ptelemetry_buffer + + offsetof(struct nvme_ocp_telemetry_host_initiated_header, reason_id); + generic_structure_parser(preason_identifier_offset, reason_identifier, + ARRAY_SIZE(reason_identifier), NULL, 0, NULL); + + printf(STR_LINE); + printf("%s\n", STR_TELEMETRY_HOST_DATA_BLOCK_1); + printf(STR_LINE); + + //Set DA to 1 and get offsets + struct nvme_ocp_telemetry_offsets offsets = { 0 }; + + offsets.data_area = 1; + + struct nvme_ocp_telemetry_common_header *ptelemetry_common_header = + (struct nvme_ocp_telemetry_common_header *) ptelemetry_buffer; + + get_telemetry_das_offset_and_size(ptelemetry_common_header, &offsets); + + __u8 *pda1_header_offset = ptelemetry_buffer + offsets.da1_start_offset;//512 + + generic_structure_parser(pda1_header_offset, ocp_header_in_da1, + ARRAY_SIZE(ocp_header_in_da1), NULL, 0, NULL); + + printf(STR_LINE); + printf("%s\n", STR_SMART_HEALTH_INFO); + printf(STR_LINE); + __u8 *pda1_smart_offset = pda1_header_offset + + offsetof(struct nvme_ocp_header_in_da1, smart_health_info); + + generic_structure_parser(pda1_smart_offset, smart, ARRAY_SIZE(smart), NULL, 0, + NULL); + + printf(STR_LINE); + printf("%s\n", STR_SMART_HEALTH_INTO_EXTENDED); + printf(STR_LINE); + __u8 *pda1_smart_ext_offset = pda1_header_offset + + offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended); + + generic_structure_parser(pda1_smart_ext_offset, smart_extended, + ARRAY_SIZE(smart_extended), NULL, 0, NULL); + + printf(STR_LINE); + printf("%s\n", STR_DA_1_STATS); + printf(STR_LINE); + status = parse_statistics(NULL, &offsets, NULL); + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + + printf(STR_LINE); + printf("%s\n", STR_DA_1_EVENT_FIFO_INFO); + printf(STR_LINE); + status = parse_event_fifos(NULL, &offsets, NULL); + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + + //Set the DA to 2 + if (options->data_area == 2) { + offsets.data_area = 2; + printf(STR_LINE); + printf("%s\n", STR_DA_2_STATS); + printf(STR_LINE); + status = parse_statistics(NULL, &offsets, NULL); + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + + printf(STR_LINE); + printf("%s\n", STR_DA_2_EVENT_FIFO_INFO); + printf(STR_LINE); + status = parse_event_fifos(NULL, &offsets, NULL); + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + } + + printf(STR_LINE); + } + + return status; +} + +int print_ocp_telemetry_json(struct ocp_telemetry_parse_options *options) +{ + int status = 0; + + //create json objects + struct json_object *root, *pheader, *preason_identifier, *da1_header, *smart_obj, + *ext_smart_obj; + + root = json_create_object(); + + //Add data to root json object + + //"Log Page Header" + pheader = json_create_object(); + + generic_structure_parser(ptelemetry_buffer, host_log_page_header, + ARRAY_SIZE(host_log_page_header), pheader, 0, NULL); + json_object_add_value_object(root, STR_LOG_PAGE_HEADER, pheader); + + //"Reason Identifier" + preason_identifier = json_create_object(); + + __u8 *preason_identifier_offset = ptelemetry_buffer + + offsetof(struct nvme_ocp_telemetry_host_initiated_header, reason_id); + + generic_structure_parser(preason_identifier_offset, reason_identifier, + ARRAY_SIZE(reason_identifier), preason_identifier, 0, NULL); + json_object_add_value_object(pheader, STR_REASON_IDENTIFIER, preason_identifier); + + struct nvme_ocp_telemetry_offsets offsets = { 0 }; + + //Set DA to 1 and get offsets + offsets.data_area = 1; + struct nvme_ocp_telemetry_common_header *ptelemetry_common_header = + (struct nvme_ocp_telemetry_common_header *) ptelemetry_buffer; + + get_telemetry_das_offset_and_size(ptelemetry_common_header, &offsets); + + //"Telemetry Host-Initiated Data Block 1" + __u8 *pda1_header_offset = ptelemetry_buffer + offsets.da1_start_offset;//512 + + da1_header = json_create_object(); + + generic_structure_parser(pda1_header_offset, ocp_header_in_da1, + ARRAY_SIZE(ocp_header_in_da1), da1_header, 0, NULL); + json_object_add_value_object(root, STR_TELEMETRY_HOST_DATA_BLOCK_1, da1_header); + + //"SMART / Health Information Log(LID-02h)" + __u8 *pda1_smart_offset = pda1_header_offset + offsetof(struct nvme_ocp_header_in_da1, + smart_health_info); + smart_obj = json_create_object(); + + generic_structure_parser(pda1_smart_offset, smart, ARRAY_SIZE(smart), smart_obj, 0, NULL); + json_object_add_value_object(da1_header, STR_SMART_HEALTH_INFO, smart_obj); + + //"SMART / Health Information Extended(LID-C0h)" + __u8 *pda1_smart_ext_offset = pda1_header_offset + offsetof(struct nvme_ocp_header_in_da1, + smart_health_info_extended); + ext_smart_obj = json_create_object(); + + generic_structure_parser(pda1_smart_ext_offset, smart_extended, ARRAY_SIZE(smart_extended), + ext_smart_obj, 0, NULL); + json_object_add_value_object(da1_header, STR_SMART_HEALTH_INTO_EXTENDED, ext_smart_obj); + + //Data Area 1 Statistics + status = parse_statistics(root, &offsets, NULL); + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + + //Data Area 1 Event FIFOs + status = parse_event_fifos(root, &offsets, NULL); + if (status != 0) { + nvme_show_error("status: %d\n", status, NULL); + return -1; + } + + if (options->data_area == 2) { + //Set the DA to 2 + offsets.data_area = 2; + //Data Area 2 Statistics + status = parse_statistics(root, &offsets, NULL); + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + + //Data Area 2 Event FIFOs + status = parse_event_fifos(root, &offsets, NULL); + if (status != 0) { + nvme_show_error("status: %d\n", status); + return -1; + } + } + + if (options->output_file != NULL) { + const char *json_string = json_object_to_json_string(root); + FILE *fp = fopen(options->output_file, "w"); + + if (fp) { + fputs(json_string, fp); + fclose(fp); + } else { + nvme_show_error("Failed to open %s file.\n", options->output_file); + return -1; + } + } else { + //Print root json object + json_print_object(root, NULL); + nvme_show_result("\n"); + json_free_object(root); + } + + return status; +} diff --git a/plugins/ocp/ocp-telemetry-decode.h b/plugins/ocp/ocp-telemetry-decode.h new file mode 100644 index 0000000..ed31a6c --- /dev/null +++ b/plugins/ocp/ocp-telemetry-decode.h @@ -0,0 +1,1228 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) 2024 Western Digital Corporation or its affiliates. + * + * Authors: Jeff Lien <jeff.lien@wdc.com>, + */ + +#include "nvme.h" +#include "nvme-print.h" +#include "util/utils.h" +#include "common.h" + +extern __u8 *ptelemetry_buffer; +extern __u8 *pstring_buffer; + +/***************************************************************************** + * Telemetry Statistics ID's and Strings + *****************************************************************************/ +enum TELEMETRY_STATISTIC_ID { + TELEMETRY_STAT_ID_OAC = 0x1, /* Outstanding Admin Commands */ + TELEMETRY_STAT_ID_HWB = 0x2, /* Host Write Bandwidth */ + TELEMETRY_STAT_ID_GCWB = 0x3, /* Garbage Collection Write Bandwidth */ + TELEMETRY_STAT_ID_AN = 0x4, /* Active Namespaces */ + TELEMETRY_STAT_ID_IWW = 0x5, /* Internal Write Workload */ + TELEMETRY_STAT_ID_IRW = 0x6, /* Internal Read Workload */ + TELEMETRY_STAT_ID_IWQD = 0x7, /* Internal Write Queue Depth */ + TELEMETRY_STAT_ID_IRQD = 0x8, /* Internal Read Queue Depth */ + TELEMETRY_STAT_ID_PTC = 0x9, /* Pending Trim LBA Count */ + TELEMETRY_STAT_ID_HTRC = 0xA, /* Host Trim LBA Request Count */ + TELEMETRY_STAT_ID_CNPS = 0xB, /* Current NVMe Power State */ + TELEMETRY_STAT_ID_CDPS = 0xC, /* Current DSSD Power State */ + TELEMETRY_STAT_ID_PFC = 0xD, /* Program Fail Count */ + TELEMETRY_STAT_ID_EFC = 0xE, /* Erase Fail Count */ + TELEMETRY_STAT_ID_RDW = 0xF, /* Read Disturb Write */ + TELEMETRY_STAT_ID_RW = 0x10, /* Retention Writes */ + TELEMETRY_STAT_ID_WLW = 0x11, /* Wear Leveling Writes */ + TELEMETRY_STAT_ID_RRW = 0x12, /* Read Recovery Writes */ + TELEMETRY_STAT_ID_GCW = 0x13, /* Garbage Collection Writes */ + TELEMETRY_STAT_ID_SCC = 0x14, /* SRAM Correctable Count */ + TELEMETRY_STAT_ID_DCC = 0x15, /* DRAM Uncorrectable Count */ + TELEMETRY_STAT_ID_SUC = 0x16, /* SRAM Correctable Count */ + TELEMETRY_STAT_ID_DUC = 0x17, /* DRAM Uncorrectable Count */ + TELEMETRY_STAT_ID_DIEC = 0x18, /* Data Integrity Error Count */ + TELEMETRY_STAT_ID_RREC = 0x19, /* Read Retry Error Count */ + TELEMETRY_STAT_ID_PEC = 0x1A, /* PERST Events Count */ + TELEMETRY_STAT_ID_MAXDBB = 0x1B, /* Max Die Bad Block */ + TELEMETRY_STAT_ID_MAXCBB = 0x1C, /* Max NAND Channel Bad Block */ + TELEMETRY_STAT_ID_MINCBB = 0x1D, /* Min NAND Channel Bad Block */ +}; + +static const char * const telemetry_stat_id_str[] = { + [TELEMETRY_STAT_ID_OAC] = "Outstanding Admin Commands", + [TELEMETRY_STAT_ID_HWB] = "Host Write Bandwidth", + [TELEMETRY_STAT_ID_GCWB] = "Garbage Collection Write Bandwidth", + [TELEMETRY_STAT_ID_AN] = "Active Namespaces", + [TELEMETRY_STAT_ID_IWW] = "Internal Write Workload", + [TELEMETRY_STAT_ID_IRW] = "Internal Read Workload", + [TELEMETRY_STAT_ID_IWQD] = "Internal Write Queue Depth", + [TELEMETRY_STAT_ID_IRQD] = "Internal Read Queue Depth", + [TELEMETRY_STAT_ID_PTC] = "Pending Trim LBA Count", + [TELEMETRY_STAT_ID_HTRC] = "Host Trim LBA Request Count", + [TELEMETRY_STAT_ID_CNPS] = "Current NVMe Power State", + [TELEMETRY_STAT_ID_CDPS] = "Current DSSD Power State", + [TELEMETRY_STAT_ID_PFC] = "Program Fail Count", + [TELEMETRY_STAT_ID_EFC] = "Erase Fail Count", + [TELEMETRY_STAT_ID_RDW] = "Read Disturb Write", + [TELEMETRY_STAT_ID_RW] = "Retention Writes", + [TELEMETRY_STAT_ID_WLW] = "Wear Leveling Writes", + [TELEMETRY_STAT_ID_RRW] = "Read Recovery Writes", + [TELEMETRY_STAT_ID_GCW] = "Garbage Collection Writes", + [TELEMETRY_STAT_ID_SCC] = "SRAM Correctable Count", + [TELEMETRY_STAT_ID_DCC] = "DRAM Correctable Count", + [TELEMETRY_STAT_ID_SUC] = "SRAM Uncorrectable Count", + [TELEMETRY_STAT_ID_DUC] = "DRAM Uncorrectable Count", + [TELEMETRY_STAT_ID_DIEC] = "Data Integrity Error Count", + [TELEMETRY_STAT_ID_RREC] = "Read Retry Error Count", + [TELEMETRY_STAT_ID_PEC] = "PERST Events Count", + [TELEMETRY_STAT_ID_MAXDBB] = "Max Die Bad Block", + [TELEMETRY_STAT_ID_MAXCBB] = "Max NAND Channel Bad Block", + [TELEMETRY_STAT_ID_MINCBB] = "Min NAND Channel Bad Block", +}; + +/***************************************************************************** + * Telemetry FIFO Event Class ID's and Strings + *****************************************************************************/ +enum TELEMETRY_EVENT_CLASS_TYPE { + TELEMETRY_TIMESTAMP_CLASS = 0x1, + TELEMETRY_PCIE_CLASS = 0x2, + TELEMETRY_NVME_CLASS = 0x3, + TELEMETRY_RESET_CLASS = 0x4, + TELEMETRY_BOOT_SEQ_CLASS = 0x5, + TELEMETRY_FW_ASSERT_CLASS = 0x6, + TELEMETRY_TEMPERATURE_CLASS = 0x7, + TELEMETRY_MEDIA_DBG_CLASS = 0x8, + TELEMETRY_MEDIA_WEAR_CLASS = 0x9, + TELEMETRY_STAT_SNAPSHOT_CLASS = 0xA, +}; + +static const char * const telemetry_event_class_str[] = { + [TELEMETRY_TIMESTAMP_CLASS] = "Timestamp Class", + [TELEMETRY_PCIE_CLASS] = "PCIe Class", + [TELEMETRY_NVME_CLASS] = "NVMe Class", + [TELEMETRY_RESET_CLASS] = "Reset Class", + [TELEMETRY_BOOT_SEQ_CLASS] = "Boot Sequence Class", + [TELEMETRY_FW_ASSERT_CLASS] = "FW Assert Class", + [TELEMETRY_TEMPERATURE_CLASS] = "Temperature Class", + [TELEMETRY_MEDIA_DBG_CLASS] = "Media Debug Class", + [TELEMETRY_MEDIA_WEAR_CLASS] = "Media Wear Class", + [TELEMETRY_STAT_SNAPSHOT_CLASS] = "Statistic Snapshot Class", +}; + +/***************************************************************************** + * Telemetry Timestamp Class (01h) Event ID's and Strings + *****************************************************************************/ +enum TELEMETRY_TIMESTAMP_EVENT_ID { + TIMESTAMP_HOST_CMD_ISSUED = 0x0000, + TIMESTAMP_SNAPSHOT = 0x0001, + TIMESTAMP_POWER_ON_HOURS = 0x0002, +}; + +static const char * const telemetry_timestamp_event_id_str[] = { + [TIMESTAMP_HOST_CMD_ISSUED] = "Timestamp Host Cmd Issued", + [TIMESTAMP_SNAPSHOT] = "Timestamp Snapshot", + [TIMESTAMP_POWER_ON_HOURS] = "Timestamp Power on Hours", +}; + +/***************************************************************************** + * Telemetry PCIE Class (02h) Event ID's and Strings + *****************************************************************************/ +enum TELEMETRY_PCIE_EVENT_ID { + PCIE_LINK_UP = 0x0000, + PCIE_LINK_DOWN = 0x0001, + PCIE_ERROR_DETECTED = 0x0002, + PCIE_PERST_ASSERTED = 0x0003, + PCIE_PERST_DEASSERTED = 0x0004, + PCIE_REFCLK_STABLE = 0x0005, + PCIE_VMAIN_STABLE = 0x0006, + PCIE_LINK_NEGOTIATED = 0x0007, +}; + +static const char * const telemetry_pcie_event_id_str[] = { + [PCIE_LINK_UP] = "PCIe Link Up", + [PCIE_LINK_DOWN] = "PCIe Link Down", + [PCIE_ERROR_DETECTED] = "PCIe Error Detected", + [PCIE_PERST_ASSERTED] = "PCIe PERST Asserted", + [PCIE_PERST_DEASSERTED] = "PCIe PERST Deasserted", + [PCIE_REFCLK_STABLE] = "PCIe Refclk Stable", + [PCIE_VMAIN_STABLE] = "PCIe Vmain Stable", + [PCIE_LINK_NEGOTIATED] = "PCIe Link Negotiated", +}; + +enum TELEMETRY_PCIE_STATE_DATA { + PCIE_STATE_UNCHANGED = 0x00, + PCIE_SPEED_CHANGED = 0x01, + PCIE_WIDTH_CHANGED = 0x02, +}; + +static const char * const telemetry_pcie_state_data_str[] = { + [PCIE_STATE_UNCHANGED] = "PCIe State Unchained", + [PCIE_SPEED_CHANGED] = "PCIe Speed Changed", + [PCIE_WIDTH_CHANGED] = "PCIe Width Changed", +}; + +enum TELEMETRY_PCIE_SPEED_DATA { + PCIE_LINK_GEN1 = 0x01, + PCIE_LINK_GEN2 = 0x02, + PCIE_LINK_GEN3 = 0x03, + PCIE_LINK_GEN4 = 0x04, + PCIE_LINK_GEN5 = 0x05, + PCIE_LINK_GEN6 = 0x06, + PCIE_LINK_GEN7 = 0x07, +}; + +static const char * const telemetry_pcie_speed_data_str[] = { + [PCIE_LINK_GEN1] = "PCIe Link Speed Gen1", + [PCIE_LINK_GEN2] = "PCIe Link Speed Gen2", + [PCIE_LINK_GEN3] = "PCIe Link Speed Gen3", + [PCIE_LINK_GEN4] = "PCIe Link Speed Gen4", + [PCIE_LINK_GEN5] = "PCIe Link Speed Gen5", + [PCIE_LINK_GEN6] = "PCIe Link Speed Gen6", + [PCIE_LINK_GEN7] = "PCIe Link Speed Gen7", +}; + +enum TELEMETRY_PCIE_WIDTH_DATA { + PCIE_LINK_X1 = 0x01, + PCIE_LINK_X2 = 0x02, + PCIE_LINK_X4 = 0x03, + PCIE_LINK_X8 = 0x04, + PCIE_LINK_X16 = 0x05, +}; + +static const char * const telemetry_pcie_width_data_str[] = { + [PCIE_LINK_X1] = "PCIe Link Width x1", + [PCIE_LINK_X2] = "PCIe Link Width x2", + [PCIE_LINK_X4] = "PCIe Link Width x4", + [PCIE_LINK_X8] = "PCIe Link Width x8", + [PCIE_LINK_X16] = "PCIe Link Width x16", +}; + +/***************************************************************************** + * Telemetry NVMe Class (03h) Event ID's and Strings + *****************************************************************************/ +enum TELEMETRY_NVME_EVENT_ID { + CC_EN_0_TO_1 = 0x0000, + CC_EN_1_TO_0 = 0x0001, + CSTS_RDY_0_TO_1 = 0x0002, + CSTS_RDY_1_TO_0 = 0x0003, + NVME_EVENT_ID_RESERVED = 0x0004, + CREATE_IO_QUEUE_PROCESSED = 0x0005, + ADMIN_QUEUE_CMD_PROCESSED = 0x0006, + ADMIN_QUEUE_NONZERO_STATUS = 0x0007, + IO_QUEUE_NONZERO_STATUS = 0x0008, + CSTS_CFS_0_TO_1 = 0x0009, + ADMIN_QUEUE_BASE_WRITTEN = 0x000A, + CC_REGISTER_CHANGED = 0x000B, + CSTS_REGISTER_CHANGED = 0x000C, + DELETE_IO_QUEUE_PROCESSED = 0x000D, +}; + +static const char * const telemetry_nvme_event_id_str[] = { + [CC_EN_0_TO_1] = "CC.EN Transitions from 0 to 1", + [CC_EN_1_TO_0] = "CC.EN Transitions from 1 to 0", + [CSTS_RDY_0_TO_1] = "CSTS.RDY Transitions from 0 to 1", + [CSTS_RDY_1_TO_0] = "CSTS.RDY Transitions from 1 to 0", + [NVME_EVENT_ID_RESERVED] = "Reserved NVMe Event ID", + [CREATE_IO_QUEUE_PROCESSED] = "Create IO SQ or CQ Command Processed", + [ADMIN_QUEUE_CMD_PROCESSED] = "Other Admin Queue Command Processed", + [ADMIN_QUEUE_NONZERO_STATUS] = "Admin Command Returned Non-zero Status", + [IO_QUEUE_NONZERO_STATUS] = "IO Command Returned Non-zero Status", + [CSTS_CFS_0_TO_1] = "CSTS.CFS Transitions from 0 to 1", + [ADMIN_QUEUE_BASE_WRITTEN] = "Admin SQ or CQ Base Address Written", + [CC_REGISTER_CHANGED] = "CC Register Changed", + [CSTS_REGISTER_CHANGED] = "CTS Register Changed", + [DELETE_IO_QUEUE_PROCESSED] = "Delete IO SQ or CQ Command Processed", +}; + +/***************************************************************************** + * Telemetry Reset Class (04h) Event ID's and Strings + *****************************************************************************/ +enum TELEMETRY_RESET_EVENT_ID { + PCIE_CONVENTIONAL_HOT_RESET = 0x0000, + MAIN_POWER_CYCLE = 0x0001, + PERST = 0x0002, + PCIE_FUNCTION_LEVEL_RESET = 0x0003, + NVME_SUBSYSTEM_RESET = 0x0004, +}; + +static const char * const telemetry_reset_event_id_str[] = { + [PCIE_CONVENTIONAL_HOT_RESET] = "PCIE Conventional Hot Reset", + [MAIN_POWER_CYCLE] = "Main Power_Cycle", + [PERST] = "PERST", + [PCIE_FUNCTION_LEVEL_RESET] = "PCIE Function Level Reset", + [NVME_SUBSYSTEM_RESET] = "NVMe Subsytem Reset", +}; + +/***************************************************************************** + * Telemetry Boot Sequence Class (05h) Event ID's and Strings + *****************************************************************************/ +enum TELEMETRY_BOOT_SEQ_EVENT_ID { + MAIN_FW_BOOT_COMPLETE = 0x0000, + FTL_LOAD_FROM_NVM_COMPLETE = 0x0001, + FTL_REBUILD_STARTED = 0x0002, + FTL_REBUILD_COMPLETE = 0x0003, +}; + +static const char * const telemetry_boot_seq_event_id_str[] = { + [MAIN_FW_BOOT_COMPLETE] = "Main Firmware Boot Complete", + [FTL_LOAD_FROM_NVM_COMPLETE] = "FTL Load from NVM Complete", + [FTL_REBUILD_STARTED] = "FTL Rebuild Started", + [FTL_REBUILD_COMPLETE] = "FTL Rebuild Complete", +}; + +/***************************************************************************** + * Telemetry Firmware Assert Class (06h) Event ID's and Strings + *****************************************************************************/ +enum TELEMETRY_FW_ASSERT_EVENT_ID { + ASSERT_NVME_CODE = 0x0000, + ASSERT_MEDIA_CODE = 0x0001, + ASSERT_SECURITY_CODE = 0x0002, + ASSERT_BACKGROUND_CODE = 0x0003, + FTL_REBUILD_FAILED = 0x0004, + FTL_DATA_MISMATCH = 0x0005, + ASSERT_OTHER_CODE = 0x0006, +}; + +static const char * const telemetry_fw_assert_event_id_str[] = { + [ASSERT_NVME_CODE] = "Assert in NVMe Processing Code", + [ASSERT_MEDIA_CODE] = "Assert in Media Code", + [ASSERT_SECURITY_CODE] = "Assert in Security Code", + [ASSERT_BACKGROUND_CODE] = "Assert in Background Services Code", + [FTL_REBUILD_FAILED] = "FTL Rebuild Failed", + [FTL_DATA_MISMATCH] = "FTL Data Mismatch", + [ASSERT_OTHER_CODE] = "FTL Other Code", +}; + +/***************************************************************************** + * Telemetry Temperature Class (07h) Event ID's and Strings + *****************************************************************************/ +enum TELEMETRY_TEMPERATURE_EVENT_ID { + COMPOSITE_TEMP_DECREASE = 0x0000, + COMPOSITE_TEMP_INCREASE_WCTEMP = 0x0001, + COMPOSITE_TEMP_INCREASE_CCTEMP = 0x0002, +}; + +static const char * const telemetry_temperature_event_id_str[] = { + [COMPOSITE_TEMP_DECREASE] = "Composite Temp Decreases to (WCTEMP-2)", + [COMPOSITE_TEMP_INCREASE_WCTEMP] = "Composite Temp Increases to WCTEMP", + [COMPOSITE_TEMP_INCREASE_CCTEMP] = "Composite Temp Increases to CCTEMP", +}; + +/***************************************************************************** + * Telemetry Media Debug Class (08h) Event ID's and Strings + *****************************************************************************/ +enum TELEMETRY_MEDIA_DEBUG_EVENT_ID { + XOR_RECOVERY_INVOKED = 0x0000, + UNCORRECTABLE_MEDIA_ERROR = 0x0001, + BAD_BLOCK_PROGRAM_ERROR = 0x0002, + BAD_BLOCK_ERASE_ERROR = 0x0003, + BAD_BLOCK_READ_ERROR = 0x0004, + PLANE_FAILURE_EVENT = 0x0005, +}; + +static const char * const telemetry_media_debug_event_id_str[] = { + [XOR_RECOVERY_INVOKED] = "XOR Recovery Invoked", + [UNCORRECTABLE_MEDIA_ERROR] = "Uncorrectable Media Error", + [BAD_BLOCK_PROGRAM_ERROR] = "Block Marked Bad Due to Program Error", + [BAD_BLOCK_ERASE_ERROR] = "Block Marked Bad Due to Erase Error", + [BAD_BLOCK_READ_ERROR] = "Block Marked Bad Due to Read Error", + [PLANE_FAILURE_EVENT] = "Plane Failure Event", +}; + +/***************************************************************************** + * Telemetry Media Wear Class (09h) Event ID's and Strings + *****************************************************************************/ +enum TELEMETRY_MEDIA_WEAR_EVENT_ID { + MEDIA_WEAR = 0x0000, +}; + +static const char * const telemetry_media_wear_event_id_str[] = { + [MEDIA_WEAR] = "Media Wear", +}; + + +/***************************************************************************** + * Telemetry Data Structures + *****************************************************************************/ +#define TELEMETRY_HEADER_SIZE 512 +#define TELEMETRY_DATA_SIZE 1536 +#define TELEMETRY_BYTE_PER_BLOCK 512 +#define TELEMETRY_TRANSFER_SIZE 1024 +#define FILE_NAME_SIZE 2048 + +enum TELEMETRY_TYPE { + TELEMETRY_TYPE_NONE = 0, + TELEMETRY_TYPE_HOST = 7, + TELEMETRY_TYPE_CONTROLLER = 8, + TELEMETRY_TYPE_HOST_0 = 9, + TELEMETRY_TYPE_HOST_1 = 10, +}; + +struct telemetry_initiated_log { + __u8 LogIdentifier; + __u8 Reserved1[4]; + __u8 IEEE[3]; + __le16 DataArea1LastBlock; + __le16 DataArea2LastBlock; + __le16 DataArea3LastBlock; + __u8 Reserved2[2]; + __le32 DataArea4LastBlock; + __u8 Reserved3[361]; + __u8 DataHostGenerationNumber; + __u8 CtlrDataAvailable; + __u8 DataCtlrGenerationNumber; + __u8 ReasonIdentifier[128]; +}; + +struct telemetry_stats_desc { + __le16 id; + __u8 info; + __u8 ns_info; + __le16 size; + __le16 rsvd1; + __u8 data[]; +}; + +struct telemetry_event_desc { + __u8 class; + __le16 id; + __u8 size; + __u8 data[]; +}; + +struct event_fifo { + __le64 start; + __le64 size; +}; + +struct telemetry_data_area_1 { + __le16 major_version; + __le16 minor_version; + __u8 reserved1[4]; + __le64 timestamp; + __u8 log_page_guid[16]; + __u8 no_of_tps_supp; + __u8 tps; + __u8 reserved2[6]; + __le64 sls; + __u8 reserved3[8]; + __u8 fw_revision[8]; + __u8 reserved4[32]; + __le64 da1_stat_start; + __le64 da1_stat_size; + __le64 da2_stat_start; + __le64 da2_stat_size; + __u8 reserved5[32]; + __u8 event_fifo_da[16]; + struct event_fifo event_fifos[16]; + __u8 reserved6[80]; + __u8 smart_health_info[512]; + __u8 smart_health_info_extended[512]; +}; + +#define DATA_SIZE_12 12 +#define DATA_SIZE_8 8 +#define DATA_SIZE_4 4 +#define MAX_BUFFER_32_KB 0x8000 +#define OCP_TELEMETRY_DATA_BLOCK_SIZE 512 +#define SIZE_OF_DWORD 4 +#define MAX_NUM_FIFOS 16 +#define DA1_OFFSET 512 +#define DEFAULT_ASCII_STRING_SIZE 16 + +#define DEFAULT_TELEMETRY_BIN "telemetry.bin" +#define DEFAULT_STRING_BIN "string.bin" +#define DEFAULT_OUTPUT_FORMAT_JSON "json" + +/* C9 Telemetry String Log Format Log Page */ +#define C9_GUID_LENGTH 16 +#define C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE 0xC9 +#define C9_TELEMETRY_STR_LOG_LEN 432 +#define C9_TELEMETRY_STR_LOG_SIST_OFST 431 + +#define STR_LOG_PAGE_HEADER "Log Page Header" +#define STR_REASON_IDENTIFIER "Reason Identifier" +#define STR_TELEMETRY_HOST_DATA_BLOCK_1 "Telemetry Host-Initiated Data Block 1" +#define STR_SMART_HEALTH_INFO "SMART / Health Information Log(LID-02h)" +#define STR_SMART_HEALTH_INTO_EXTENDED "SMART / Health Information Extended(LID-C0h)" +#define STR_DA_1_STATS "Data Area 1 Statistics" +#define STR_DA_2_STATS "Data Area 2 Statistics" +#define STR_DA_1_EVENT_FIFO_INFO "Data Area 1 Event FIFO info" +#define STR_DA_2_EVENT_FIFO_INFO "Data Area 2 Event FIFO info" +#define STR_STATISTICS_IDENTIFIER "Statistics Identifier" +#define STR_STATISTICS_IDENTIFIER_STR "Statistic Identifier String" +#define STR_STATISTICS_INFO_BEHAVIOUR_TYPE "Statistics Info Behavior Type" +#define STR_STATISTICS_INFO_RESERVED "Statistics Info Reserved" +#define STR_NAMESPACE_IDENTIFIER "Namespace Identifier" +#define STR_NAMESPACE_INFO_VALID "Namespace Information Valid" +#define STR_STATISTICS_DATA_SIZE "Statistic Data Size" +#define STR_RESERVED "Reserved" +#define STR_STATISTICS_SPECIFIC_DATA "Statistic Specific Data" +#define STR_CLASS_SPECIFIC_DATA "Class Specific Data" +#define STR_DBG_EVENT_CLASS_TYPE "Debug Event Class type" +#define STR_EVENT_IDENTIFIER "Event Identifier" +#define STR_EVENT_STRING "Event String" +#define STR_EVENT_DATA_SIZE "Event Data Size" +#define STR_VU_EVENT_STRING "VU Event String" +#define STR_VU_EVENT_ID_STRING "VU Event Identifier" +#define STR_VU_DATA "VU Data" +#define STR_LINE "==============================================================================\n" +#define STR_LINE2 "-----------------------------------------------------------------------------\n" + +/** + * enum ocp_telemetry_data_area - Telemetry Data Areas + * @DATA_AREA_1: Data Area 1 + * @DATA_AREA_2: Data Area 2 + * @DATA_AREA_3: Data Area 3 + * @DATA_AREA_4: Data Area 4 + */ +enum ocp_telemetry_data_area { + DATA_AREA_1 = 0x01, + DATA_AREA_2 = 0x02, + DATA_AREA_3 = 0x03, + DATA_AREA_4 = 0x04, +}; + +/** + * enum ocp_telemetry_string_tables - OCP telemetry string tables + * @STATISTICS_IDENTIFIER_STRING: Statistic Identifier string + * @EVENT_STRING: Event String + * @VU_EVENT_STRING: VU Event String + */ +enum ocp_telemetry_string_tables { + STATISTICS_IDENTIFIER_STRING = 0, + EVENT_STRING, + VU_EVENT_STRING +}; + +/** + * enum ocp_telemetry_debug_event_class_types - OCP Debug Event Class types + * @RESERVED_CLASS_TYPE: Reserved class + * @TIME_STAMP_CLASS_TYPE: Time stamp class + * @PCIE_CLASS_TYPE: PCIe class + * @NVME_CLASS_TYPE: NVME class + * @RESET_CLASS_TYPE: Reset class + * @BOOT_SEQUENCE_CLASS_TYPE: Boot Sequence class + * @FIRMWARE_ASSERT_CLASS_TYPE: Firmware Assert class + * @TEMPERATURE_CLASS_TYPE: Temperature class + * @MEDIA_CLASS_TYPE: Media class + * @MEDIA_WEAR_CLASS_TYPE: Media wear class + * @STATISTIC_SNAPSHOT_CLASS_TYPE: Statistic snapshot class + * @RESERVED: Reserved class + * @VENDOR_UNIQUE_CLASS_TYPE: Vendor Unique class + */ +enum ocp_telemetry_debug_event_class_types { + RESERVED_CLASS_TYPE = 0x00, + TIME_STAMP_CLASS_TYPE = 0x01, + PCIE_CLASS_TYPE = 0x02, + NVME_CLASS_TYPE = 0x03, + RESET_CLASS_TYPE = 0x04, + BOOT_SEQUENCE_CLASS_TYPE = 0x05, + FIRMWARE_ASSERT_CLASS_TYPE = 0x06, + TEMPERATURE_CLASS_TYPE = 0x07, + MEDIA_CLASS_TYPE = 0x08, + MEDIA_WEAR_CLASS_TYPE = 0x09, + STATISTIC_SNAPSHOT_CLASS_TYPE = 0x0A, + //RESERVED = 7Fh-0Bh, + //VENDOR_UNIQUE_CLASS_TYPE = FFh-80h, +}; + +/** + * struct telemetry_str_log_format - Telemetry String Log Format + * @log_page_version: indicates the version of the mapping this log page uses + * Shall be set to 01h. + * @reserved1: Reserved. + * @log_page_guid: Shall be set to B13A83691A8F408B9EA495940057AA44h. + * @sls: Shall be set to the number of DWORDS in the String Log. + * @reserved2: reserved. + * @sits: shall be set to the number of DWORDS in the Statistics + * Identifier String Table + * @ests: Shall be set to the number of DWORDS from byte 0 of this + * log page to the start of the Event String Table + * @estsz: shall be set to the number of DWORDS in the Event String Table + * @vu_eve_sts: Shall be set to the number of DWORDS from byte 0 of this + * log page to the start of the VU Event String Table + * @vu_eve_st_sz: shall be set to the number of DWORDS in the VU Event String Table + * @ascts: the number of DWORDS from byte 0 of this log page until the + * ASCII Table Starts. + * @asctsz: the number of DWORDS in the ASCII Table + * @fifo1: FIFO 0 ASCII String + * @fifo2: FIFO 1 ASCII String + * @fifo3: FIFO 2 ASCII String + * @fifo4: FIFO 3 ASCII String + * @fif05: FIFO 4 ASCII String + * @fifo6: FIFO 5 ASCII String + * @fifo7: FIFO 6 ASCII String + * @fifo8: FIFO 7 ASCII String + * @fifo9: FIFO 8 ASCII String + * @fifo10: FIFO 9 ASCII String + * @fif011: FIFO 10 ASCII String + * @fif012: FIFO 11 ASCII String + * @fifo13: FIFO 12 ASCII String + * @fif014: FIFO 13 ASCII String + * @fif015: FIFO 14 ASCII String + * @fif016: FIFO 15 ASCII String + * @reserved3: reserved + */ +struct __packed telemetry_str_log_format { + __u8 log_page_version; + __u8 reserved1[15]; + __u8 log_page_guid[C9_GUID_LENGTH]; + __le64 sls; + __u8 reserved2[24]; + __le64 sits; + __le64 sitsz; + __le64 ests; + __le64 estsz; + __le64 vu_eve_sts; + __le64 vu_eve_st_sz; + __le64 ascts; + __le64 asctsz; + __u8 fifo1[16]; + __u8 fifo2[16]; + __u8 fifo3[16]; + __u8 fifo4[16]; + __u8 fifo5[16]; + __u8 fifo6[16]; + __u8 fifo7[16]; + __u8 fifo8[16]; + __u8 fifo9[16]; + __u8 fifo10[16]; + __u8 fifo11[16]; + __u8 fifo12[16]; + __u8 fifo13[16]; + __u8 fifo14[16]; + __u8 fifo15[16]; + __u8 fifo16[16]; + __u8 reserved3[48]; +}; + +/* + * struct statistics_id_str_table_entry - Statistics Identifier String Table Entry + * @vs_si: Shall be set the Vendor Unique Statistic Identifier number. + * @reserved1: Reserved + * @ascii_id_len: Shall be set the number of ASCII Characters that are valid. + * @ascii_id_ofst: Shall be set to the offset from DWORD 0/Byte 0 of the Start + * of the ASCII Table to the first character of the string for + * this Statistic Identifier string.. + * @reserved2 reserved + */ +struct __packed statistics_id_str_table_entry { + __le16 vs_si; + __u8 reserved1; + __u8 ascii_id_len; + __le64 ascii_id_ofst; + __le32 reserved2; +}; + +/* + * struct event_id_str_table_entry - Event Identifier String Table Entry + * @deb_eve_class: Shall be set the Debug Class. + * @ei: Shall be set to the Event Identifier + * @ascii_id_len: Shall be set the number of ASCII Characters that are valid. + * @ascii_id_ofst: This is the offset from DWORD 0/ Byte 0 of the start of the + * ASCII table to the ASCII data for this identifier + * @reserved2 reserved + */ +struct __packed event_id_str_table_entry { + __u8 deb_eve_class; + __le16 ei; + __u8 ascii_id_len; + __le64 ascii_id_ofst; + __le32 reserved2; +}; + +/* + * struct vu_event_id_str_table_entry - VU Event Identifier String Table Entry + * @deb_eve_class: Shall be set the Debug Class. + * @vu_ei: Shall be set to the VU Event Identifier + * @ascii_id_len: Shall be set the number of ASCII Characters that are valid. + * @ascii_id_ofst: This is the offset from DWORD 0/ Byte 0 of the start of the + * ASCII table to the ASCII data for this identifier + * @reserved reserved + */ +struct __packed vu_event_id_str_table_entry { + __u8 deb_eve_class; + __le16 vu_ei; + __u8 ascii_id_len; + __le64 ascii_id_ofst; + __le32 reserved; +}; + + +struct __packed ocp_telemetry_parse_options { + char *telemetry_log; + char *string_log; + char *output_file; + char *output_format; + int data_area; + char *telemetry_type; +}; + +struct __packed nvme_ocp_telemetry_reason_id +{ + __u8 error_id[64]; // Bytes 63:00 + __u8 file_id[8]; // Bytes 71:64 + __le16 line_number; // Bytes 73:72 + __u8 valid_flags; // Bytes 74 + __u8 reserved[21]; // Bytes 95:75 + __u8 vu_reason_ext[32]; // Bytes 127:96 +}; + +struct __packed nvme_ocp_telemetry_common_header +{ + __u8 log_id; // Byte 00 + __le32 reserved1; // Bytes 04:01 + __u8 ieee_oui_id[3]; // Bytes 07:05 + __le16 da1_last_block; // Bytes 09:08 + __le16 da2_last_block; // Bytes 11:10 + __le16 da3_last_block; // Bytes 13:12 + __le16 reserved2; // Bytes 15:14 + __le32 da4_last_block; // Bytes 19:16 +}; + +struct __packed nvme_ocp_telemetry_host_initiated_header +{ + struct nvme_ocp_telemetry_common_header commonHeader; // Bytes 19:00 + __u8 reserved3[360]; // Bytes 379:20 + __u8 host_initiated_scope; // Byte 380 + __u8 host_initiated_gen_number; // Byte 381 + __u8 host_initiated_data_available; // Byte 382 + __u8 ctrl_initiated_gen_number; // Byte 383 + struct nvme_ocp_telemetry_reason_id reason_id; // Bytes 511:384 +}; + +struct __packed nvme_ocp_telemetry_controller_initiated_header +{ + struct nvme_ocp_telemetry_common_header commonHeader; // Bytes 19:00 + __u8 reserved3[361]; // Bytes 380:20 + __u8 ctrl_initiated_scope; // Byte 381 + __u8 ctrl_initiated_data_available; // Byte 382 + __u8 ctrl_initiated_gen_number; // Byte 383 + struct nvme_ocp_telemetry_reason_id reason_id; // Bytes 511:384 +}; + +struct __packed nvme_ocp_telemetry_smart +{ + __u8 critical_warning; // Byte 0 + __le16 composite_temperature; // Bytes 2:1 + __u8 available_spare; // Bytes 3 + __u8 available_spare_threshold; // Bytes 4 + __u8 percentage_used; // Bytes 5 + __u8 reserved1[26]; // Bytes 31:6 + __u8 data_units_read[16]; // Bytes 47:32 + __u8 data_units_written[16]; // Bytes 63:48 + __u8 host_read_commands[16]; // Byte 79:64 + __u8 host_write_commands[16]; // Bytes 95:80 + __u8 controller_busy_time[16]; // Bytes 111:96 + __u8 power_cycles[16]; // Bytes 127:112 + __u8 power_on_hours[16]; // Bytes 143:128 + __u8 unsafe_shutdowns[16]; // Bytes 159:144 + __u8 media_and_data_integrity_errors[16]; // Bytes 175:160 + __u8 number_of_error_information_log_entries[16]; // Bytes 191:176 + __le32 warning_composite_temperature_time; // Byte 195:192 + __le32 critical_composite_temperature_time; // Bytes 199:196 + __le16 temperature_sensor1; // Bytes 201:200 + __le16 temperature_sensor2; // Byte 203:202 + __le16 temperature_sensor3; // Byte 205:204 + __le16 temperature_sensor4; // Bytes 207:206 + __le16 temperature_sensor5; // Bytes 209:208 + __le16 temperature_sensor6; // Bytes 211:210 + __le16 temperature_sensor7; // Bytes 213:212 + __le16 temperature_sensor8; // Bytes 215:214 + __le32 thermal_management_temperature1_transition_count; // Bytes 219:216 + __le32 thermal_management_temperature2_transition_count; // Bytes 223:220 + __le32 total_time_for_thermal_management_temperature1; // Bytes 227:224 + __le32 total_time_for_thermal_management_temperature2; // Bytes 231:228 + __u8 reserved2[280]; // Bytes 511:232 +}; + +struct __packed nvme_ocp_telemetry_smart_extended +{ + __u8 physical_media_units_written[16]; // Bytes 15:0 + __u8 physical_media_units_read[16]; // Bytes 31:16 + __u8 bad_user_nand_blocks_raw_count[6]; // Bytes 37:32 + __le16 bad_user_nand_blocks_normalized_value; // Bytes 39:38 + __u8 bad_system_nand_blocks_raw_count[6]; // Bytes 45:40 + __le16 bad_system_nand_blocks_normalized_value; // Bytes 47:46 + __le64 xor_recovery_count; // Bytes 55:48 + __le64 uncorrectable_read_error_count; // Bytes 63:56 + __le64 soft_ecc_error_count; // Bytes 71:64 + __le32 end_to_end_correction_counts_detected_errors; // Bytes 75:72 + __le32 end_to_end_correction_counts_corrected_errors; // Bytes 79:76 + __u8 system_data_percent_used; // Byte 80 + __u8 refresh_counts[7]; // Bytes 87:81 + __le32 max_user_data_erase_count; // Bytes 91:88 + __le32 min_user_data_erase_count; // Bytes 95:92 + __u8 num_thermal_throttling_events; // Bytes 96 + __u8 current_throttling_status; // Bytes 97 + __u8 errata_version_field; // Byte 98 + __le16 point_version_field; // Byte 100:99 + __le16 minor_version_field; // Byte 102:101 + __u8 major_version_field; // Byte 103 + __le64 pcie_correctable_error_count; // Bytes 111:104 + __le32 incomplete_shutdowns; // Bytes 115:112 + __le32 reserved1; // Bytes 119:116 + __u8 percent_free_blocks; // Byte 120 + __u8 reserved2[7]; // Bytes 127:121 + __le16 capacitor_health; // Bytes 129:128 + __u8 nvme_base_errata_version; // Byte 130 + __u8 nvme_command_set_errata_version; // Byte 131 + __le32 reserved3; // Bytes 135:132 + __le64 unaligned_io; // Bytes 143:136 + __le64 security_version_number; // Bytes 151:144 + __le64 total_nuse; // Bytes 159:152 + __u8 plp_start_count[16]; // Bytes 175:160 + __u8 endurance_estimate[16]; // Bytes 191:176 + __le64 pcie_link_retraining_count; // Bytes 199:192 + __le64 power_state_change_count; // Bytes 207:200 + __le64 lowest_permitted_firmware_revision; // Bytes 215:208 + __u8 reserved4[278]; // Bytes 493:216 + __le16 log_page_version; // Bytes 495:494 + __u8 log_page_guid[16]; // Bytes 511:496 +}; + +struct __packed nvme_ocp_event_fifo_data +{ + __le32 event_fifo_num; + __u8 event_fifo_da; + __le64 event_fifo_start; + __le64 event_fifo_size; +}; + +struct __packed nvme_ocp_telemetry_offsets +{ + __le32 data_area; + __le32 header_size; + __le32 da1_start_offset; + __le32 da1_size; + __le32 da2_start_offset; + __le32 da2_size; + __le32 da3_start_offset; + __le32 da3_size; + __le32 da4_start_offset; + __le32 da4_size; +}; + +struct __packed nvme_ocp_event_fifo_offsets +{ + __le64 event_fifo_start; + __le64 event_fifo_size; +}; + +struct __packed nvme_ocp_header_in_da1 +{ + __le16 major_version; // Bytes 1:0 + __le16 minor_version; // Bytes 3:2 + __le32 reserved1; // Bytes 7:4 + __le64 time_stamp; // Bytes 15:8 + __u8 log_page_guid[16]; // Bytes 31:16 + __u8 num_telemetry_profiles_supported; // Byte 32 + __u8 telemetry_profile_selected; // Byte 33 + __u8 reserved2[6]; // Bytes 39:34 + __le64 string_log_size; // Bytes 47:40 + __le64 reserved3; // Bytes 55:48 + __le64 firmware_revision; // Bytes 63:56 + __u8 reserved4[32]; // Bytes 95:64 + __le64 da1_statistic_start; // Bytes 103:96 + __le64 da1_statistic_size; // Bytes 111:104 + __le64 da2_statistic_start; // Bytes 119:112 + __le64 da2_statistic_size; // Bytes 127:120 + __u8 reserved5[32]; // Bytes 159:128 + __u8 event_fifo_da[16]; // Bytes 175:160 + struct nvme_ocp_event_fifo_offsets fifo_offsets[16]; // Bytes 431:176 + __u8 reserved6[80]; // Bytes 511:432 + struct nvme_ocp_telemetry_smart smart_health_info; // Bytes 1023:512 + struct nvme_ocp_telemetry_smart_extended smart_health_info_extended; // Bytes 1535:1024 +}; + +struct __packed nvme_ocp_telemetry_statistic_descriptor +{ + __le16 statistic_id; // Bytes 1:0 + __u8 statistic_info_behaviour_type : 4; // Byte 2(3:0) + __u8 statistic_info_reserved : 4; // Byte 2(7:4) + __u8 ns_info_nsid : 7; // Bytes 3(6:0) + __u8 ns_info_ns_info_valid : 1; // Bytes 3(7) + __le16 statistic_data_size; // Bytes 5:4 + __le16 reserved; // Bytes 7:6 +}; + +struct __packed nvme_ocp_telemetry_event_descriptor +{ + __u8 debug_event_class_type; // Byte 0 + __le16 event_id; // Bytes 2:1 + __u8 event_data_size; // Byte 3 +}; + +struct __packed nvme_ocp_time_stamp_dbg_evt_class_format +{ + __u8 time_stamp[DATA_SIZE_8]; // Bytes 11:4 + __le16 vu_event_identifier; // Bytes 13:12 +}; + +struct __packed nvme_ocp_pcie_dbg_evt_class_format +{ + __u8 pCIeDebugEventData[DATA_SIZE_4]; // Bytes 7:4 + __le16 vu_event_identifier; // Bytes 9:8 +}; + +struct __packed nvme_ocp_nvme_dbg_evt_class_format +{ + __u8 nvmeDebugEventData[DATA_SIZE_8]; // Bytes 11:4 + __le16 vu_event_identifier; // Bytes 13:12 +}; + +struct __packed nvme_ocp_common_dbg_evt_class_format +{ + __le16 vu_event_identifier; // Bytes 5:4 +}; + +struct __packed nvme_ocp_media_wear_dbg_evt_class_format +{ + __u8 currentMediaWear[DATA_SIZE_12]; // Bytes 15:4 + __le16 vu_event_identifier; // Bytes 17:16 +}; + +struct __packed nvme_ocp_statistic_snapshot_evt_class_format +{ + struct nvme_ocp_telemetry_statistic_descriptor statisticDescriptorData; // Bytes 11:10 +}; + +struct __packed nvme_ocp_statistics_identifier_string_table +{ + __le16 vs_statistic_identifier; //1:0 + __u8 reserved1; //2 + __u8 ascii_id_length; //3 + __le64 ascii_id_offset; //11:4 + __le32 reserved2; //15:12 +}; + +struct __packed nvme_ocp_event_string_table +{ + __u8 debug_event_class; //0 + __le16 event_identifier; //2:1 + __u8 ascii_id_length; //3 + __le64 ascii_id_offset; //11:4 + __le32 reserved; //15:12 +}; + +struct __packed nvme_ocp_vu_event_string_table +{ + __u8 debug_event_class; //0 + __le16 vu_event_identifier; //2:1 + __u8 ascii_id_length; //3 + __le64 ascii_id_offset; //11:4 + __le32 reserved; //15:12 +}; + +struct __packed nvme_ocp_telemetry_string_header +{ + __u8 version; //0:0 + __u8 reserved1[15]; //15:1 + __u8 guid[16]; //32:16 + __le64 string_log_size; //39:32 + __u8 reserved2[24]; //63:40 + __le64 sits; //71:64 Statistics Identifier String Table Start(SITS) + __le64 sitsz; //79:72 Statistics Identifier String Table Size (SITSZ) + __le64 ests; //87:80 Event String Table Start(ESTS) + __le64 estsz; //95:88 Event String Table Size(ESTSZ) + __le64 vu_ests; //103:96 VU Event String Table Start + __le64 vu_estsz; //111:104 VU Event String Table Size + __le64 ascts; //119:112 ASCII Table start + __le64 asctsz; //127:120 ASCII Table Size + __u8 fifo_ascii_string[16][16]; //383:128 + __u8 reserved3[48]; //431:384 +}; + +struct __packed statistic_entry { + int identifier; + char *description; +}; + +/************************************************************ + * Telemetry Parsing Function Prototypes + ************************************************************/ +void print_vu_event_data(__u32 size, __u8 *data); +void print_stats_desc(struct telemetry_stats_desc *stat_desc); +void print_telemetry_fifo_event(__u8 class_type, + __u16 id, __u8 size, __u8 *data); + + +/************************************************************ + * Telemetry ID to String Conversion Functions + ************************************************************/ +static inline const char *arg_str(const char * const *strings, + size_t array_size, size_t idx) +{ + if (idx < array_size && strings[idx]) + return strings[idx]; + return "unrecognized"; +} + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define ARGSTR(s, i) arg_str(s, ARRAY_SIZE(s), i) + +static inline const char *telemetry_stat_id_to_string(int stat_id) +{ + return ARGSTR(telemetry_stat_id_str, stat_id); +} +static inline const char *telemetry_event_class_to_string(int class) +{ + return ARGSTR(telemetry_event_class_str, class); +} +static inline const char *telemetry_ts_event_to_string(int event_id) +{ + return ARGSTR(telemetry_timestamp_event_id_str, event_id); +} +static inline const char *telemetry_pcie_event_id_to_string(int event_id) +{ + return ARGSTR(telemetry_pcie_event_id_str, event_id); +} +static inline const char *telemetry_pcie_state_data_to_string(int pcie_state) +{ + return ARGSTR(telemetry_pcie_state_data_str, pcie_state); +} +static inline const char *telemetry_pcie_speed_data_to_string(int pcie_speed) +{ + return ARGSTR(telemetry_pcie_speed_data_str, pcie_speed); +} +static inline const char *telemetry_pcie_width_data_to_string(int pcie_width) +{ + return ARGSTR(telemetry_pcie_width_data_str, pcie_width); +} +static inline const char *telemetry_nvme_event_id_to_string(int event_id) +{ + return ARGSTR(telemetry_nvme_event_id_str, event_id); +} +static inline const char *telemetry_reset_event_id_to_string(int event_id) +{ + return ARGSTR(telemetry_reset_event_id_str, event_id); +} +static inline const char *telemetry_boot_seq_event_id_to_string(int event_id) +{ + return ARGSTR(telemetry_boot_seq_event_id_str, event_id); +} +static inline const char *telemetry_fw_assert_event_id_to_string(int event_id) +{ + return ARGSTR(telemetry_fw_assert_event_id_str, event_id); +} +static inline const char *telemetry_temperature_event_id_to_string(int event_id) +{ + return ARGSTR(telemetry_temperature_event_id_str, event_id); +} +static inline const char *telemetry_media_debug_event_id_to_string(int event_id) +{ + return ARGSTR(telemetry_media_debug_event_id_str, event_id); +} +static inline const char *telemetry_media_wear_event_id_to_string(int event_id) +{ + return ARGSTR(telemetry_media_wear_event_id_str, event_id); +} + +/** + * @brief parse the ocp telemetry host or controller log binary file + * into json or text + * + * @param options, input pointer for inputs like telemetry log bin file, + * string log bin file and output file etc. + * + * @return 0 success + */ +int parse_ocp_telemetry_log(struct ocp_telemetry_parse_options *options); + +/** + * @brief parse the ocp telemetry string log binary file to json or text + * + * @param event_fifo_num, input event FIFO number + * @param debug_event_class, input debug event class id + * @param string_table, input string table + * @param description, input description string + * + * @return 0 success + */ +int parse_ocp_telemetry_string_log(int event_fifo_num, int identifier, int debug_event_class, + enum ocp_telemetry_string_tables string_table, char *description); + +/** + * @brief gets the telemetry datas areas, offsets and sizes information + * + * @param ptelemetry_common_header, input telemetry common header pointer + * @param ptelemetry_das_offset, input telemetry offsets pointer + * + * @return 0 success + */ +int get_telemetry_das_offset_and_size( + struct nvme_ocp_telemetry_common_header *ptelemetry_common_header, + struct nvme_ocp_telemetry_offsets *ptelemetry_das_offset); + +/** + * @brief parses statistics data to text or json formats + * + * @param root, input time json root object pointer + * @param ptelemetry_das_offset, input telemetry offsets pointer + * @param fp, input file pointer + * + * @return 0 success + */ +int parse_statistics(struct json_object *root, struct nvme_ocp_telemetry_offsets *pOffsets, + FILE *fp); + +/** + * @brief parses a single statistic data to text or json formats + * + * @param pstatistic_entry, statistic entry pointer + * @param pstats_array, stats array pointer + * @param fp, input file pointer + * + * @return 0 success + */ +int parse_statistic(struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry, + struct json_object *pstats_array, FILE *fp); + +/** + * @brief parses event fifos data to text or json formats + * + * @param root, input time json root object pointer + * @param poffsets, input telemetry offsets pointer + * @param fp, input file pointer + * + * @return 0 success + */ +int parse_event_fifos(struct json_object *root, struct nvme_ocp_telemetry_offsets *poffsets, + FILE *fp); + +/** + * @brief parses a single event fifo data to text or json formats + * + * @param fifo_num, input event fifo number + * @param pfifo_start, event fifo start pointer + * @param pevent_fifos_object, event fifos json object pointer + * @param ptelemetry_das_offset, input telemetry offsets pointer + * @param fifo_size, input event fifo size + * @param fp, input file pointer + * + * @return 0 success + */ +int parse_event_fifo(unsigned int fifo_num, unsigned char *pfifo_start, + struct json_object *pevent_fifos_object, unsigned char *pstring_buffer, + struct nvme_ocp_telemetry_offsets *poffsets, __u64 fifo_size, FILE *fp); + +/** + * @brief parses event fifos data to text or json formats + * + * @return 0 success + */ +int print_ocp_telemetry_normal(struct ocp_telemetry_parse_options *options); + +/** + * @brief parses event fifos data to text or json formats + * + * @return 0 success + */ +int print_ocp_telemetry_json(struct ocp_telemetry_parse_options *options); + +/** + * @brief gets statistic id ascii string + * + * @param identifier, string id + * @param description, string description + * + * @return 0 success + */ +int get_static_id_ascii_string(int identifier, char *description); + +/** + * @brief gets event id ascii string + * + * @param identifier, string id + * @param debug_event_class, debug event class + * @param description, string description + * + * @return 0 success + */ +int get_event_id_ascii_string(int identifier, int debug_event_class, char *description); + +/** + * @brief gets vu event id ascii string + * + * @param identifier, string id + * @param debug_event_class, debug event class + * @param description, string description + * + * @return 0 success + */ +int get_vu_event_id_ascii_string(int identifier, int debug_event_class, char *description); + +/** + * @brief parses a time-stamp event fifo data to text or json formats + * + * @param pevent_descriptor, input event descriptor data + * @param pevent_descriptor_obj, event descriptor json object pointer + * @param pevent_specific_data, input event specific data + * @param pevent_fifos_object, event fifos json object pointer + * @param fp, input file pointer + * + * @return + */ +void parse_time_stamp_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, + struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, + struct json_object *pevent_fifos_object, FILE *fp); + +/** + * @brief parses a pcie event fifo data to text or json formats + * + * @param pevent_descriptor, input event descriptor data + * @param pevent_descriptor_obj, event descriptor json object pointer + * @param pevent_specific_data, input event specific data + * @param pevent_fifos_object, event fifos json object pointer + * @param fp, input file pointer + * + * @return + */ +void parse_pcie_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, + struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, + struct json_object *pevent_fifos_object, FILE *fp); + +/** + * @brief parses a nvme event fifo data to text or json formats + * + * @param pevent_descriptor, input event descriptor data + * @param pevent_descriptor_obj, event descriptor json object pointer + * @param pevent_specific_data, input event specific data + * @param pevent_fifos_object, event fifos json object pointer + * @param fp, input file pointer + * + * @return + */ +void parse_nvme_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, + struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, + struct json_object *pevent_fifos_object, FILE *fp); + +/** + * @brief parses common event fifo data to text or json formats + * + * @param pevent_descriptor, input event descriptor data + * @param pevent_descriptor_obj, event descriptor json object pointer + * @param pevent_specific_data, input event specific data + * @param pevent_fifos_object, event fifos json object pointer + * @param fp, input file pointer + * + * @return + */ +void parse_common_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, + struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, + struct json_object *pevent_fifos_object, FILE *fp); + +/** + * @brief parses a media-wear event fifo data to text or json formats + * + * @param pevent_descriptor, input event descriptor data + * @param pevent_descriptor_obj, event descriptor json object pointer + * @param pevent_specific_data, input event specific data + * @param pevent_fifos_object, event fifos json object pointer + * @param fp, input file pointer + * + * @return + */ +void parse_media_wear_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, + struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, + struct json_object *pevent_fifos_object, FILE *fp); diff --git a/plugins/ocp/ocp-utils.c b/plugins/ocp/ocp-utils.c index 1257b30..8a1462e 100644 --- a/plugins/ocp/ocp-utils.c +++ b/plugins/ocp/ocp-utils.c @@ -1,19 +1,32 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2022 Solidigm. + * Copyright (c) 2022-2024 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ #include <unistd.h> +#include <errno.h> #include "ocp-utils.h" -#include "nvme-print.h" const unsigned char ocp_uuid[NVME_UUID_LEN] = { 0xc1, 0x94, 0xd5, 0x5b, 0xe0, 0x94, 0x47, 0x94, 0xa2, 0x1d, 0x29, 0x99, 0x8f, 0x56, 0xbe, 0x6f }; -int ocp_get_uuid_index(struct nvme_dev *dev, int *index) +int ocp_find_uuid_index(struct nvme_id_uuid_list *uuid_list, __u8 *index) +{ + int i = nvme_uuid_find(uuid_list, ocp_uuid); + + *index = 0; + if (i > 0) + *index = i; + else + return -errno; + + return 0; +} + +int ocp_get_uuid_index(struct nvme_dev *dev, __u8 *index) { struct nvme_id_uuid_list uuid_list; int err = nvme_identify_uuid(dev_fd(dev), &uuid_list); @@ -22,11 +35,5 @@ int ocp_get_uuid_index(struct nvme_dev *dev, int *index) if (err) return err; - for (int i = 0; i < NVME_ID_UUID_LIST_MAX; i++) { - if (memcmp(ocp_uuid, &uuid_list.entry[i].uuid, NVME_UUID_LEN) == 0) { - *index = i + 1; - break; - } - } - return err; + return ocp_find_uuid_index(&uuid_list, index); } diff --git a/plugins/ocp/ocp-utils.h b/plugins/ocp/ocp-utils.h index d02bea9..1512db8 100644 --- a/plugins/ocp/ocp-utils.h +++ b/plugins/ocp/ocp-utils.h @@ -1,18 +1,32 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (c) 2022 Solidigm. + * Copyright (c) 2022-2024 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ #include "nvme.h" +/* + * UUID assigned for OCP. + */ +extern const unsigned char ocp_uuid[NVME_UUID_LEN]; + /** * ocp_get_uuid_index() - Get OCP UUID index * @dev: nvme device * @index: integer pointer to here to save the index - * @result: The command completion result from CQE dword0 * - * Return: Zero if nvme device has UUID list log page, or result of get uuid list otherwise. + * Return: Zero if nvme device has UUID list identify page, or positive result of get uuid list + * or negative POSIX error code otherwise. + */ +int ocp_get_uuid_index(struct nvme_dev *dev, __u8 *index); + +/** + * ocp_find_uuid_index() - Find OCP UUID index in UUID list + * @uuid_list: uuid_list retrieved from Identify UUID List (CNS 0x17) + * @index: integer pointer to here to save the index + * + * Return: Zero if nvme device has UUID list log page, Negative POSIX error code otherwise. */ -int ocp_get_uuid_index(struct nvme_dev *dev, int *index); +int ocp_find_uuid_index(struct nvme_id_uuid_list *uuid_list, __u8 *index); diff --git a/plugins/sed/sedopal_cmd.c b/plugins/sed/sedopal_cmd.c index 649e0b2..d9a789c 100644 --- a/plugins/sed/sedopal_cmd.c +++ b/plugins/sed/sedopal_cmd.c @@ -169,8 +169,10 @@ int sedopal_cmd_initialize(int fd) struct opal_key key; struct opal_lr_act lr_act = {}; struct opal_user_lr_setup lr_setup = {}; + struct opal_new_pw new_pw = {}; sedopal_ask_key = true; + sedopal_ask_new_key = true; rc = sedopal_set_key(&key); if (rc != 0) return rc; @@ -217,6 +219,21 @@ int sedopal_cmd_initialize(int fd) return rc; } + /* + * set password + */ + new_pw.new_user_pw.who = OPAL_ADMIN1; + new_pw.new_user_pw.opal_key.lr = 0; + new_pw.session.who = OPAL_ADMIN1; + new_pw.session.sum = 0; + new_pw.session.opal_key.lr = 0; + new_pw.session.opal_key = key; + new_pw.new_user_pw.opal_key = key; + + rc = ioctl(fd, IOC_OPAL_SET_PW, &new_pw); + if (rc != 0) + fprintf(stderr, "Error: failed setting password - %d\n", rc); + return rc; } @@ -234,8 +251,21 @@ int sedopal_cmd_lock(int fd) */ int sedopal_cmd_unlock(int fd) { + int rc; + + rc = sedopal_lock_unlock(fd, OPAL_RW); - return sedopal_lock_unlock(fd, OPAL_RW); + /* + * If the unlock was successful, force a re-read of the + * partition table. Return rc of unlock operation. + */ + if (rc == 0) { + if (ioctl(fd, BLKRRPART, 0) != 0) + fprintf(stderr, + "Warning: failed re-reading partition\n"); + } + + return rc; } /* @@ -258,18 +288,6 @@ int sedopal_lock_unlock(int fd, int lock_state) if (rc != 0) fprintf(stderr, "Error: failed locking or unlocking - %d\n", rc); - - /* - * If the unlock was successful, force a re-read of the - * partition table. - */ - if (rc == 0) { - rc = ioctl(fd, BLKRRPART, 0); - if (rc != 0) - fprintf(stderr, - "Error: failed re-reading partition\n"); - } - return rc; } @@ -380,6 +398,14 @@ int sedopal_cmd_revert(int fd) revert_lsp.__pad = 0; rc = ioctl(fd, IOC_OPAL_REVERT_LSP, &revert_lsp); + if (rc == 0) { + /* + * TPER must also be reverted. + */ + rc = ioctl(fd, IOC_OPAL_REVERT_TPR, &revert_lsp.key); + if (rc != 0) + fprintf(stderr, "Error: revert TPR - %d\n", rc); + } #else rc = -EOPNOTSUPP; #endif @@ -448,14 +474,14 @@ void sedopal_print_locking_features(uint8_t features) int sedopal_cmd_discover(int fd) { #ifdef IOC_OPAL_DISCOVERY - int rc; + int rc, feat_length; bool sedopal_locking_supported = false; struct opal_discovery discover; struct level_0_discovery_header *dh; struct level_0_discovery_features *feat; struct level_0_discovery_features *feat_end; uint16_t code; - uint8_t locking_flags; + uint8_t locking_flags = 0; char buf[4096]; discover.data = (__u64)buf; @@ -483,6 +509,7 @@ int sedopal_cmd_discover(int fd) */ while (feat < feat_end) { code = be16toh(feat->code); + feat_length = feat->length + 4 /* hdr */; switch (code) { case OPAL_FEATURE_CODE_LOCKING: locking_flags = feat->feature; @@ -494,7 +521,7 @@ int sedopal_cmd_discover(int fd) break; } - feat++; + feat = (struct level_0_discovery_features *)((char *)feat + feat_length); } rc = 0; diff --git a/plugins/solidigm/meson.build b/plugins/solidigm/meson.build index 052afa1..df2dc57 100644 --- a/plugins/solidigm/meson.build +++ b/plugins/solidigm/meson.build @@ -11,6 +11,7 @@ sources += [ 'plugins/solidigm/solidigm-temp-stats.c', 'plugins/solidigm/solidigm-get-drive-info.c', 'plugins/solidigm/solidigm-ocp-version.c', + 'plugins/solidigm/solidigm-workload-tracker.c', ] subdir('solidigm-telemetry') diff --git a/plugins/solidigm/solidigm-garbage-collection.c b/plugins/solidigm/solidigm-garbage-collection.c index a37e9c5..3c046b0 100644 --- a/plugins/solidigm/solidigm-garbage-collection.c +++ b/plugins/solidigm/solidigm-garbage-collection.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2022 Solidigm. + * Copyright (c) 2022-2024 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ @@ -68,7 +68,7 @@ static void vu_gc_log_show(struct garbage_control_collection_log *payload, const int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Get and parse Solidigm vendor specific garbage collection event log."; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_dev *dev; int err; __u8 uuid_index; @@ -97,7 +97,7 @@ int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *c return -EINVAL; } - uuid_index = solidigm_get_vu_uuid_index(dev); + sldgm_get_uuid_index(dev, &uuid_index); struct garbage_control_collection_log gc_log; const int solidigm_vu_gc_log_id = 0xfd; diff --git a/plugins/solidigm/solidigm-get-drive-info.c b/plugins/solidigm/solidigm-get-drive-info.c index 21f59bb..c783fa8 100644 --- a/plugins/solidigm/solidigm-get-drive-info.c +++ b/plugins/solidigm/solidigm-get-drive-info.c @@ -16,7 +16,7 @@ int sldgm_get_drive_info(int argc, char **argv, struct command *cmd, struct plug const char *desc = "Get drive HW information"; const char *FTL_unit_size_str = "FTL_unit_size"; char *output_format = "normal"; - enum nvme_print_flags flags; + nvme_print_flags_t flags; nvme_root_t r; nvme_ctrl_t c; nvme_ns_t n; diff --git a/plugins/solidigm/solidigm-id-ctrl.c b/plugins/solidigm/solidigm-id-ctrl.c index f45e758..67dc7b7 100644 --- a/plugins/solidigm/solidigm-id-ctrl.c +++ b/plugins/solidigm/solidigm-id-ctrl.c @@ -9,7 +9,7 @@ #include "common.h" #include "solidigm-id-ctrl.h" -struct __packed nvme_vu_id_ctrl_field { /* CDR MR5 */ +struct __packed nvme_vu_id_ctrl_field { // CPC __u8 rsvd1[3]; __u8 ss; char health[20]; @@ -22,6 +22,26 @@ struct __packed nvme_vu_id_ctrl_field { /* CDR MR5 */ __le64 ww; char mic_bl[4]; char mic_fw[4]; + __u8 rsvd3[678]; + __u32 signature; + __u8 version; + __u8 product_type; + __u8 nand_type; + __u8 form_factor; + __u32 fw_status; + __u32 p4_revision; // git hash first 8 characters + __u32 customer_id; + __u32 usage_model; + struct{ + __u32 zns_nvme : 1; // bit 0 + __u32 mfnd_nvme : 1; // bit 1 + __u32 cdw1413 : 1; // bit 2: CDW14 remapping into CDW13 + __u32 vpd_avail : 1; // bit 3: VPD EEPROM is available + //at moment of id-ctrl response + __u32 rsvd : 28; // bit 4..31 are unused + } + command_set; + }; void sldgm_id_ctrl(uint8_t *vs, struct json_object *root) @@ -37,6 +57,19 @@ void sldgm_id_ctrl(uint8_t *vs, struct json_object *root) const char *str_ww = "wwid"; const char *str_mic_bl = "bwLimGran"; const char *str_mic_fw = "ioLimGran"; + const char *str_signature = "signature"; + const char *str_version = "version"; + const char *str_product_type = "prodType"; + const char *str_nand_type = "nandType"; + const char *str_form_factor = "formFactor"; + const char *str_fw_status = "fwStatus"; + const char *str_p4_revision = "P4Revision"; + const char *str_customer_id = "customerID"; + const char *str_usage_model = "usageModel"; + const char *str_zns_nvme = "znsNVMe"; + const char *str_mfnd_nvme = "mfndNVMe"; + const char *str_cdw14_cdw13 = "cdw14map13"; + const char *str_vpd_avail = "vpdAvail"; struct nvme_vu_id_ctrl_field *id = (struct nvme_vu_id_ctrl_field *)vs; @@ -54,12 +87,25 @@ void sldgm_id_ctrl(uint8_t *vs, struct json_object *root) printf("%-10s: 0x%016"PRIx64"\n", str_ww, le64_to_cpu(id->ww)); printf("%-10s: %.*s\n", str_mic_bl, (int)sizeof(id->mic_bl), id->mic_bl); printf("%-10s: %.*s\n", str_mic_fw, (int)sizeof(id->mic_fw), id->mic_fw); + printf("%-10s: 0x%08X\n", str_signature, id->signature); + printf("%-10s: 0x%02X\n", str_version, id->version); + printf("%-10s: %u\n", str_product_type, id->product_type); + printf("%-10s: %u\n", str_nand_type, id->nand_type); + printf("%-10s: %u\n", str_form_factor, id->form_factor); + printf("%-10s: %u\n", str_fw_status, id->fw_status); + printf("%-10s: 0x%08X\n", str_p4_revision, id->p4_revision); + printf("%-10s: 0x%08X\n", str_customer_id, id->customer_id); + printf("%-10s: %u\n", str_usage_model, id->usage_model); + printf("%-10s: %u\n", str_zns_nvme, id->command_set.zns_nvme); + printf("%-10s: %u\n", str_mfnd_nvme, id->command_set.mfnd_nvme); + printf("%-10s: %u\n", str_cdw14_cdw13, id->command_set.cdw1413); + printf("%-10s: %u\n", str_vpd_avail, id->command_set.vpd_avail); return; } json_object_add_value_uint(root, str_ss, id->ss); json_object_object_add(root, str_health, - json_object_new_string_len(health, sizeof(id->health))); + json_object_new_string_len(health, sizeof(id->health))); json_object_add_value_uint(root, str_cls, id->cls); json_object_add_value_uint(root, str_nlw, id->nlw); json_object_add_value_uint(root, str_scap, id->scap); @@ -67,7 +113,20 @@ void sldgm_id_ctrl(uint8_t *vs, struct json_object *root) json_object_object_add(root, str_bl, json_object_new_string_len(id->bl, sizeof(id->bl))); json_object_add_value_uint64(root, str_ww, le64_to_cpu(id->ww)); json_object_object_add(root, str_mic_bl, - json_object_new_string_len(id->mic_bl, sizeof(id->mic_bl))); + json_object_new_string_len(id->mic_bl, sizeof(id->mic_bl))); json_object_object_add(root, str_mic_fw, - json_object_new_string_len(id->mic_fw, sizeof(id->mic_fw))); + json_object_new_string_len(id->mic_fw, sizeof(id->mic_fw))); + json_object_add_value_uint(root, str_signature, id->signature); + json_object_add_value_uint(root, str_version, id->version); + json_object_add_value_uint(root, str_product_type, id->product_type); + json_object_add_value_uint(root, str_nand_type, id->nand_type); + json_object_add_value_uint(root, str_form_factor, id->form_factor); + json_object_add_value_uint(root, str_fw_status, id->fw_status); + json_object_add_value_uint(root, str_p4_revision, id->p4_revision); + json_object_add_value_uint(root, str_customer_id, id->customer_id); + json_object_add_value_uint(root, str_usage_model, id->usage_model); + json_object_add_value_uint(root, str_zns_nvme, id->command_set.zns_nvme); + json_object_add_value_uint(root, str_mfnd_nvme, id->command_set.mfnd_nvme); + json_object_add_value_uint(root, str_cdw14_cdw13, id->command_set.cdw1413); + json_object_add_value_uint(root, str_vpd_avail, id->command_set.vpd_avail); } diff --git a/plugins/solidigm/solidigm-internal-logs.c b/plugins/solidigm/solidigm-internal-logs.c index c604761..f5b57f3 100644 --- a/plugins/solidigm/solidigm-internal-logs.c +++ b/plugins/solidigm/solidigm-internal-logs.c @@ -12,7 +12,6 @@ #include <stdlib.h> #include <unistd.h> #include <inttypes.h> -#include <linux/limits.h> #include <time.h> #include "common.h" @@ -23,11 +22,15 @@ #include "solidigm-util.h" #define DWORD_SIZE 4 +#define LOG_FILE_PERMISSION 0644 enum log_type { NLOG = 0, EVENTLOG = 1, ASSERTLOG = 2, + HIT, + CIT, + ALL }; #pragma pack(push, internal_logs, 1) @@ -122,12 +125,20 @@ struct nlog_dump_header4_1 { #pragma pack(pop, internal_logs) struct config { - __u32 namespace_id; - char *dir_prefix; + char *out_dir; char *type; bool verbose; }; +struct ilog { + struct nvme_dev *dev; + struct config *cfg; + int count; + struct nvme_id_ctrl id_ctrl; + enum nvme_telemetry_da max_da; + __u32 max_tx; +}; + static void print_nlog_header(__u8 *buffer) { struct nlog_dump_header_common *nlog_header = (struct nlog_dump_header_common *) buffer; @@ -218,29 +229,29 @@ static int get_serial_number(char *str, int fd) return err; } -static int dump_assert_logs(struct nvme_dev *dev, struct config cfg) +static int ilog_dump_assert_logs(struct ilog *ilog) { __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER]; __u8 head_buf[INTERNAL_LOG_MAX_BYTE_TRANSFER]; - char file_path[PATH_MAX]; + char file_path[PATH_MAX] = {0}; char file_name[] = "AssertLog.bin"; struct assert_dump_header *ad = (struct assert_dump_header *) head_buf; struct nvme_passthru_cmd cmd = { .opcode = 0xd2, - .nsid = cfg.namespace_id, + .nsid = NVME_NSID_ALL, .addr = (unsigned long)(void *)head_buf, .cdw12 = ASSERTLOG, .cdw13 = 0, }; int output, err; - err = read_header(&cmd, dev_fd(dev)); + err = read_header(&cmd, dev_fd(ilog->dev)); if (err) return err; snprintf(file_path, sizeof(file_path), "%.*s/%s", - (int) (sizeof(file_path) - sizeof(file_name) - 1), cfg.dir_prefix, file_name); - output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + (int) (sizeof(file_path) - sizeof(file_name) - 1), ilog->cfg->out_dir, file_name); + output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, LOG_FILE_PERMISSION); if (output < 0) return -errno; err = write_header((__u8 *)ad, output, ad->header.header_size * DWORD_SIZE); @@ -251,7 +262,7 @@ static int dump_assert_logs(struct nvme_dev *dev, struct config cfg) } cmd.addr = (unsigned long)(void *)buf; - if (cfg.verbose) { + if (ilog->cfg->verbose) { printf("Assert Log, cores: %d log size: %d header size: %d\n", ad->header.numcores, ad->header.log_size * DWORD_SIZE, ad->header.header_size * DWORD_SIZE); for (__u32 i = 0; i < ad->header.numcores; i++) @@ -262,28 +273,27 @@ static int dump_assert_logs(struct nvme_dev *dev, struct config cfg) if (!ad->core[i].assertvalid) continue; cmd.cdw13 = ad->core[i].coreoffset; - err = cmd_dump_repeat(&cmd, ad->core[i].assertsize, - output, - dev_fd(dev), false); + err = cmd_dump_repeat(&cmd, ad->core[i].assertsize, output, + dev_fd(ilog->dev), false); if (err) { close(output); return err; } } close(output); - printf("Successfully wrote log to %s\n", file_path); + printf("Successfully wrote Assert to %s\n", file_path); return err; } -static int dump_event_logs(struct nvme_dev *dev, struct config cfg) +static int ilog_dump_event_logs(struct ilog *ilog) { __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER]; __u8 head_buf[INTERNAL_LOG_MAX_BYTE_TRANSFER]; - char file_path[PATH_MAX]; + char file_path[PATH_MAX] = {0}; struct event_dump_header *ehdr = (struct event_dump_header *) head_buf; struct nvme_passthru_cmd cmd = { .opcode = 0xd2, - .nsid = cfg.namespace_id, + .nsid = NVME_NSID_ALL, .addr = (unsigned long)(void *)head_buf, .cdw12 = EVENTLOG, .cdw13 = 0, @@ -291,11 +301,11 @@ static int dump_event_logs(struct nvme_dev *dev, struct config cfg) int output; int core_num, err; - err = read_header(&cmd, dev_fd(dev)); + err = read_header(&cmd, dev_fd(ilog->dev)); if (err) return err; - snprintf(file_path, sizeof(file_path), "%s/EventLog.bin", cfg.dir_prefix); - output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + snprintf(file_path, sizeof(file_path) - 1, "%s/EventLog.bin", ilog->cfg->out_dir); + output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, LOG_FILE_PERMISSION); if (output < 0) return -errno; err = write_header(head_buf, output, INTERNAL_LOG_MAX_BYTE_TRANSFER); @@ -308,11 +318,11 @@ static int dump_event_logs(struct nvme_dev *dev, struct config cfg) } cmd.addr = (unsigned long)(void *)buf; - if (cfg.verbose) + if (ilog->cfg->verbose) printf("Event Log, cores: %d log size: %d\n", core_num, ehdr->header.log_size * 4); for (__u32 j = 0; j < core_num; j++) { - if (cfg.verbose) { + if (ilog->cfg->verbose) { for (int k = 0 ; k < 16; k++) { printf("core: %d event: %d ", j, k); printf("validity: %d ", ehdr->edumps[j].eventIdValidity[k]); @@ -321,14 +331,14 @@ static int dump_event_logs(struct nvme_dev *dev, struct config cfg) } cmd.cdw13 = ehdr->edumps[j].coreoffset; err = cmd_dump_repeat(&cmd, ehdr->edumps[j].coresize, - output, dev_fd(dev), false); + output, dev_fd(ilog->dev), false); if (err) { close(output); return err; } } close(output); - printf("Successfully wrote log to %s\n", file_path); + printf("Successfully wrote Events to %s\n", file_path); return err; } @@ -348,16 +358,16 @@ static size_t get_nlog_header_size(struct nlog_dump_header_common *nlog_header) } /* dumps nlogs from specified core or all cores when core = -1 */ -static int dump_nlogs(struct nvme_dev *dev, struct config cfg, int core) +static int ilog_dump_nlogs(struct ilog *ilog, int core) { int err = 0; __u32 count, core_num; __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER]; - char file_path[PATH_MAX]; + char file_path[PATH_MAX] = {0}; struct nlog_dump_header_common *nlog_header = (struct nlog_dump_header_common *)buf; struct nvme_passthru_cmd cmd = { .opcode = 0xd2, - .nsid = cfg.namespace_id, + .nsid = NVME_NSID_ALL, .addr = (unsigned long)(void *)buf }; @@ -381,7 +391,7 @@ static int dump_nlogs(struct nvme_dev *dev, struct config cfg, int core) do { cmd.cdw13 = 0; cmd.cdw12 = log_select.raw; - err = read_header(&cmd, dev_fd(dev)); + err = read_header(&cmd, dev_fd(ilog->dev)); if (err) { if (is_open) close(output); @@ -390,9 +400,10 @@ static int dump_nlogs(struct nvme_dev *dev, struct config cfg, int core) count = nlog_header->totalnlogs; core_num = core < 0 ? nlog_header->corecount : 0; if (!header_size) { - snprintf(file_path, sizeof(file_path), "%s/NLog.bin", - cfg.dir_prefix); - output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + snprintf(file_path, sizeof(file_path) - 1, "%s/NLog.bin", + ilog->cfg->out_dir); + output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, + LOG_FILE_PERMISSION); if (output < 0) return -errno; header_size = get_nlog_header_size(nlog_header); @@ -401,11 +412,11 @@ static int dump_nlogs(struct nvme_dev *dev, struct config cfg, int core) err = write_header(buf, output, header_size); if (err) break; - if (cfg.verbose) + if (ilog->cfg->verbose) print_nlog_header(buf); cmd.cdw13 = 0x400; err = cmd_dump_repeat(&cmd, nlog_header->nlogbytesize / 4, - output, dev_fd(dev), true); + output, dev_fd(ilog->dev), true); if (err) break; } while (++log_select.selectNlog < count); @@ -414,244 +425,552 @@ static int dump_nlogs(struct nvme_dev *dev, struct config cfg, int core) } while (++log_select.selectCore < core_num); if (is_open) { close(output); - printf("Successfully wrote log to %s\n", file_path); + printf("Successfully wrote Nlog to %s\n", file_path); } return err; } -enum telemetry_type { - HOSTGENOLD, - HOSTGENNEW, - CONTROLLER +int ensure_dir(const char *parent_dir_name, const char *name) +{ + char file_path[PATH_MAX] = {0}; + struct stat sb; + + snprintf(file_path, sizeof(file_path) - 1, "%s/%s", parent_dir_name, name); + if (!(stat(file_path, &sb) == 0 && S_ISDIR(sb.st_mode))) { + if (mkdir(file_path, 777) != 0) { + perror(file_path); + return -errno; + } + } + return 0; +} + +struct log { + __u8 id; + const char *desc; + size_t buffer_size; + __u8 *buffer; }; -static int dump_telemetry(struct nvme_dev *dev, struct config cfg, enum telemetry_type ttype) +static int log_save(struct log *log, const char *parent_dir_name, const char *subdir_name, + const char *file_name, __u8 *buffer, size_t buf_size) { - _cleanup_free_ struct nvme_telemetry_log *log = NULL; - size_t log_size = 0; - int err = 0; - __u8 *buffer = NULL; + _cleanup_fd_ int output = -1; + char file_path[PATH_MAX] = {0}; size_t bytes_remaining = 0; + int err = 0; + + ensure_dir(parent_dir_name, subdir_name); + + snprintf(file_path, sizeof(file_path) - 1, "%s/%s/%s", parent_dir_name, subdir_name, + file_name); + output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, LOG_FILE_PERMISSION); + if (output < 0) + return -errno; + + bytes_remaining = buf_size; + + while (bytes_remaining) { + ssize_t bytes_written = write(output, buffer, bytes_remaining); + + if (bytes_written < 0) { + err = -errno; + goto log_save_close_output; + } + + bytes_remaining -= bytes_written; + buffer += bytes_written; + } + printf("Successfully wrote %s to %s\n", log->desc, file_path); + +log_save_close_output: + close(output); + return err; +} + +static int ilog_dump_identify_page(struct ilog *ilog, struct log *cns, __u32 nsid) +{ + __u8 data[NVME_IDENTIFY_DATA_SIZE]; + __u8 *buff = cns->buffer ? cns->buffer : data; + char filename[sizeof( + "cntid_XXXXX_cns_XXX_nsid_XXXXXXXXXX_nvmsetid_XXXXX_csi_XXX.bin")] = {0}; + int err = nvme_identify_cns_nsid(dev_fd(ilog->dev), cns->id, nsid, buff); + + if (err) + return err; + + snprintf(filename, sizeof(filename) - 1, "cntid_0_cns_%d_nsid_%d_nvmsetid_0_csi_0.bin", + cns->id, nsid); + return log_save(cns, ilog->cfg->out_dir, "identify", filename, buff, sizeof(data)); +} + +static int ilog_ensure_dump_id_ctrl(struct ilog *ilog) +{ + static bool first = true; + static int err; + struct log idctrl = {NVME_IDENTIFY_CNS_CTRL, "Id Controller Data", sizeof(ilog->id_ctrl), + (__u8 *) &ilog->id_ctrl}; + + if (!first) + return err; + + first = false; + err = ilog_dump_identify_page(ilog, &idctrl, 0); + + if (err) + return err; + + ilog->count++; + + if (ilog->id_ctrl.lpa & 0x8) + ilog->max_da = NVME_TELEMETRY_DA_3; + if (ilog->id_ctrl.lpa & 0x40) + ilog->max_da = NVME_TELEMETRY_DA_4; + + /* assuming CAP.MPSMIN is zero minimum Memory Page Size is at least 4096 bytes */ + ilog->max_tx = (1 << ilog->id_ctrl.mdts) * NVME_LOG_PAGE_PDU_SIZE; + if (ilog->max_tx > DRIVER_MAX_TX_256K) + ilog->max_tx = DRIVER_MAX_TX_256K; + + return err; +} + +static int ilog_dump_telemetry(struct ilog *ilog, enum log_type ttype) +{ + int err = 0; enum nvme_telemetry_da da; size_t max_data_tx; - char file_path[PATH_MAX]; - char *file_name; - char *log_descr; - struct stat sb; + const char *file_name; + struct nvme_feat_host_behavior prev = {0}; + bool host_behavior_changed = false; + struct log log = {0}; + + err = ilog_ensure_dump_id_ctrl(ilog); + if (err) + return err; - _cleanup_file_ int output = -1; + da = ilog->max_da; + max_data_tx = ilog->max_tx; + + if (da == 4) { + __u32 result; + int err = nvme_get_features_host_behavior(dev_fd(ilog->dev), 0, &prev, &result); + + if (!err && !prev.etdas) { + struct nvme_feat_host_behavior da4_enable = prev; + + da4_enable.etdas = 1; + nvme_set_features_host_behavior(dev_fd(ilog->dev), 0, &da4_enable); + host_behavior_changed = true; + } + } switch (ttype) { - case HOSTGENNEW: + case HIT: file_name = "lid_0x07_lsp_0x01_lsi_0x0000.bin"; - log_descr = "Generated Host Initiated"; - break; - case HOSTGENOLD: - file_name = "lid_0x07_lsp_0x00_lsi_0x0000.bin"; - log_descr = "Existing Host Initiated"; + log.desc = "Host Initiated Telemetry"; + err = nvme_get_telemetry_log(dev_fd(ilog->dev), true, false, false, max_data_tx, da, + (struct nvme_telemetry_log **) &log.buffer, + &log.buffer_size); break; - case CONTROLLER: + case CIT: file_name = "lid_0x08_lsp_0x00_lsi_0x0000.bin"; - log_descr = "Controller Initiated"; + log.desc = "Controller Initiated Telemetry"; + err = nvme_get_telemetry_log(dev_fd(ilog->dev), false, true, true, max_data_tx, da, + (struct nvme_telemetry_log **) &log.buffer, + &log.buffer_size); break; default: return -EINVAL; } - err = nvme_get_telemetry_max(dev_fd(dev), &da, &max_data_tx); + + if (host_behavior_changed) + nvme_set_features_host_behavior(dev_fd(ilog->dev), 0, &prev); + if (err) return err; - if (max_data_tx > DRIVER_MAX_TX_256K) - max_data_tx = DRIVER_MAX_TX_256K; + err = log_save(&log, ilog->cfg->out_dir, "log_pages", file_name, log.buffer, + log.buffer_size); + return err; +} - switch (ttype) { - case HOSTGENNEW: - err = nvme_get_telemetry_log(dev_fd(dev), true, false, false, max_data_tx, da, - &log, &log_size); - break; - case HOSTGENOLD: - err = nvme_get_telemetry_log(dev_fd(dev), false, false, false, max_data_tx, da, - &log, &log_size); - break; - case CONTROLLER: - err = nvme_get_telemetry_log(dev_fd(dev), false, true, true, max_data_tx, da, &log, - &log_size); - break; +static int ilog_dump_identify_pages(struct ilog *ilog) +{ + struct nvme_ns_list ns_list; + __u32 j = 0; + struct log identify_base_list[] = { + {NVME_IDENTIFY_CNS_NS_ACTIVE_LIST, "Id Active Namespace ID list", + sizeof(ns_list), (__u8 *) &ns_list}, + {NVME_IDENTIFY_CNS_NVMSET_LIST, "Id NVM Set List"}, + {NVME_IDENTIFY_CNS_CSI_CTRL, "Id I/O Command Set specific"}, + {NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST, "Id Allocated Namespace ID list"}, + {NVME_IDENTIFY_CNS_CTRL_LIST, "Id Controller List"} + }; + struct log identify_ns_required_list[] = { + {NVME_IDENTIFY_CNS_NS, "Id Namespace data"}, + {NVME_IDENTIFY_CNS_NS_DESC_LIST, "Id Namespace Id Descriptor list"}, + {NVME_IDENTIFY_CNS_CSI_NS, "Id Namespace ID I/O Command Set specific"}, + {NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS, + "I/O Command Set Independent Identify Namespace Data"}, + {NVME_IDENTIFY_CNS_ALLOCATED_NS, "Id Namespace data "}, + {NVME_IDENTIFY_CNS_NS_CTRL_LIST, "Id Namespace Id Controller List"}, + }; + + ilog_ensure_dump_id_ctrl(ilog); + + for (int i = 0; i < ARRAY_SIZE(identify_base_list); i++) { + int err = ilog_dump_identify_page(ilog, &identify_base_list[i], 0); + + if (err == 0) + ilog->count++; } - if (err) - return err; + while (ns_list.ns[j]) { + for (int i = 0; i < ARRAY_SIZE(identify_ns_required_list); i++) { + int err = ilog_dump_identify_page(ilog, &identify_ns_required_list[i], + ns_list.ns[j]); - snprintf(file_path, sizeof(file_path), "%s/log_pages", cfg.dir_prefix); - if (!(stat(file_path, &sb) == 0 && S_ISDIR(sb.st_mode))) { - if (mkdir(file_path, 777) != 0) { - perror(file_path); - return -errno; + if (err == 0) + ilog->count++; } + j++; } - snprintf(file_path, sizeof(file_path), "%s/log_pages/%s", cfg.dir_prefix, file_name); - output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (output < 0) - return -errno; + return 0; +} - bytes_remaining = log_size; - buffer = (__u8 *)log; +static int ilog_dump_log_page(struct ilog *ilog, struct log *lp, __u32 nsid) +{ + __u8 *buff = lp->buffer; + char filename[sizeof("lid_0xXX_lsp_0xXX_lsi_0xXXXX.bin")] = {0}; + int err; - while (bytes_remaining) { - ssize_t bytes_written = write(output, buffer, bytes_remaining); + if (!lp->buffer_size) + return -EINVAL; + if (!buff) { + buff = nvme_alloc(lp->buffer_size); + if (!buff) + return -ENOMEM; + } + err = nvme_get_nsid_log(dev_fd(ilog->dev), 0, lp->id, 0, lp->buffer_size, buff); + if (err) + return err; - if (bytes_written < 0) { - err = -errno; - goto tele_close_output; - } + snprintf(filename, sizeof(filename), "lid_0x%02x_lsp_0x00_lsi_0x0000.bin", + lp->id); + return log_save(lp, ilog->cfg->out_dir, "log_pages", filename, buff, lp->buffer_size); +} - bytes_remaining -= bytes_written; - buffer += bytes_written; +static int ilog_dump_no_lsp_log_pages(struct ilog *ilog) +{ + struct lba_status_info { + __u32 lslplen; + __u32 nlslne; + __u32 estulb; + __u16 rsvd; + __u16 lsgc; + } lba_status = {}; + __u64 num_entries = 0; + struct log log_page_dependent_list[] = { + {NVME_LOG_LID_LBA_STATUS}, + {NVME_LOG_LID_ENDURANCE_GRP_EVT}, + }; + struct log log_page_base_list[] = { + {NVME_LOG_LID_SUPPORTED_LOG_PAGES, NULL, sizeof(struct nvme_supported_log_pages)}, + {NVME_LOG_LID_ERROR, NULL, + (ilog->id_ctrl.elpe + 1) * sizeof(struct nvme_error_log_page)}, + {NVME_LOG_LID_SMART, NULL, sizeof(struct nvme_smart_log)}, + {NVME_LOG_LID_FW_SLOT, NULL, sizeof(struct nvme_firmware_slot)}, + {NVME_LOG_LID_CHANGED_NS, NULL, sizeof(struct nvme_ns_list)}, + {NVME_LOG_LID_CMD_EFFECTS, NULL, sizeof(struct nvme_cmd_effects_log)}, + {NVME_LOG_LID_DEVICE_SELF_TEST, NULL, sizeof(struct nvme_self_test_log)}, + {NVME_LOG_LID_LBA_STATUS, NULL, sizeof(lba_status), (__u8 *) &lba_status}, + {NVME_LOG_LID_ENDURANCE_GRP_EVT, NULL, sizeof(num_entries), (__u8 *) &num_entries}, + {NVME_LOG_LID_FID_SUPPORTED_EFFECTS, NULL, + sizeof(struct nvme_fid_supported_effects_log)}, + {NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS, NULL, + sizeof(struct nvme_mi_cmd_supported_effects_log)}, + {NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN, NULL, 512}, + {NVME_LOG_LID_PHY_RX_EOM, NULL, 512}, + {NVME_LOG_LID_SANITIZE, NULL, sizeof(struct nvme_sanitize_log_page)}, + {0xC0, "OCP or VU SMART / Health Information Extended", 512}, + {0xC1, "OCP Error Recovery or VU Latency Reads", 512}, + {0xC2, "OCP Firmware Activation History or VU Latency Writes", 4096}, + {0xC3, "OCP Latency Monitor", 512}, + {0xC4, "OCP Device Capabilities or VU Endurance Manager Statistics", 4096}, + {0xC5, "OCP Unsupported Requirements or VU Tempeture Statistics", 4096}, + {0xC7, "OCP TCG Configuration", 512}, + {0xCA, "SMART Attributes", 512}, + {0xd5, "Tempeture Statistics", 512}, + {0xfe, "Latency Outlier", 8192}, + }; + + for (int i = 0; i < ARRAY_SIZE(log_page_base_list); i++) { + log_page_base_list[i].desc = log_page_base_list[i].desc ? + log_page_base_list[i].desc : + nvme_log_to_string(log_page_base_list[i].id); + if (!ilog_dump_log_page(ilog, &log_page_base_list[i], 0)) + ilog->count++; } - printf("Successfully wrote %s Telemetry log to %s\n", log_descr, file_path); -tele_close_output: - close(output); + /* if needed, patch logs based on retrieved log size */ + if (lba_status.lslplen > sizeof(lba_status)) + log_page_dependent_list[0].buffer_size = lba_status.lslplen; + if (num_entries) + log_page_dependent_list[1].buffer_size = sizeof(num_entries) + + (num_entries * sizeof(__u16)); + + for (int i = 0; i < ARRAY_SIZE(log_page_dependent_list); i++) { + log_page_dependent_list[i].desc = log_page_dependent_list[i].desc ? + log_page_dependent_list[i].desc : + nvme_log_to_string(log_page_dependent_list[i].id); + ilog_dump_log_page(ilog, &log_page_dependent_list[i], 0); + } + + return 0; +} + +static int ilog_dump_pel(struct ilog *ilog) +{ + struct log lp = { + NVME_LOG_LID_PERSISTENT_EVENT, + nvme_log_to_string(NVME_LOG_LID_PERSISTENT_EVENT) + }; + void *pevent_log_full; + int err; + struct nvme_get_log_args args; + + _cleanup_free_ struct nvme_persistent_event_log *pevent = NULL; + + _cleanup_huge_ struct nvme_mem_huge mh = {0}; + + err = nvme_get_log_persistent_event(dev_fd(ilog->dev), NVME_PEVENT_LOG_RELEASE_CTX, + sizeof(*pevent), pevent); + if (err) + return err; + + + pevent = nvme_alloc(sizeof(*pevent)); + if (!pevent) + return -ENOMEM; + + err = nvme_get_log_persistent_event(dev_fd(ilog->dev), NVME_PEVENT_LOG_EST_CTX_AND_READ, + sizeof(*pevent), pevent); + if (err) + return err; + + lp.buffer_size = le64_to_cpu(pevent->tll); + + pevent_log_full = nvme_alloc_huge(lp.buffer_size, &mh); + if (!pevent_log_full) + return -ENOMEM; + + err = nvme_get_log_persistent_event(dev_fd(ilog->dev), NVME_PEVENT_LOG_READ, + lp.buffer_size, pevent_log_full); + args = (struct nvme_get_log_args) { + .lpo = 0, + .result = NULL, + .log = pevent_log_full, + .args_size = sizeof(args), + .fd = dev_fd(ilog->dev), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = NVME_LOG_LID_PERSISTENT_EVENT, + .len = lp.buffer_size, + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_PEVENT_LOG_READ, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + err = nvme_get_log_page(dev_fd(ilog->dev), ilog->max_tx, &args); + if (err) + return err; + + err = log_save(&lp, ilog->cfg->out_dir, "log_pages", "lid_0x0d_lsp_0x00_lsi_0x0000.bin", + pevent_log_full, lp.buffer_size); + + nvme_get_log_persistent_event(dev_fd(ilog->dev), NVME_PEVENT_LOG_RELEASE_CTX, + sizeof(*pevent), pevent); + return err; } int solidigm_get_internal_log(int argc, char **argv, struct command *command, struct plugin *plugin) { - char folder[PATH_MAX]; - char zip_name[PATH_MAX]; - char *output_path; char sn_prefix[sizeof(((struct nvme_id_ctrl *)0)->sn)+1]; - int log_count = 0; + char date_str[sizeof("-YYYYMMDDHHMMSS")]; + char full_folder[PATH_MAX] = {0}; + char unique_folder[sizeof(sn_prefix)+sizeof(date_str)-1] = {0}; + char *initial_folder; + char zip_name[PATH_MAX] = {0}; + char *output_path; + struct ilog ilog = {0}; int err; _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; - bool all = false; - time_t t; - struct tm tm; + enum log_type log_type = ALL; + char type_ALL[] = "ALL"; + time_t current_time; + DIR *dir; const char *desc = "Get Debug Firmware Logs and save them."; - const char *type = - "Log type: ALL, CONTROLLERINITTELEMETRY, HOSTINITTELEMETRY, HOSTINITTELEMETRYNOGEN, NLOG, ASSERT, EVENT. Defaults to ALL."; - const char *prefix = "Output dir prefix; defaults to device serial number."; + const char *type = "Log type; Defaults to ALL."; + const char *out_dir = "Output directory; defaults to current working directory."; const char *verbose = "To print out verbose info."; - const char *namespace_id = "Namespace to get logs from."; - struct config cfg = { - .namespace_id = NVME_NSID_ALL, - .dir_prefix = NULL, - .type = NULL, + .out_dir = ".", + .type = type_ALL, }; OPT_ARGS(opts) = { - OPT_STR("type", 't', &cfg.type, type), - OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), - OPT_FILE("dir-prefix", 'p', &cfg.dir_prefix, prefix), - OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), + OPT_STRING("type", 't', "ALL|CIT|HIT|NLOG|ASSERT|EVENT", &cfg.type, type), + OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.out_dir, out_dir), + OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), OPT_END() }; err = parse_and_open(&dev, argc, argv, desc, opts); if (err) return err; - - if (!cfg.dir_prefix) { - err = get_serial_number(sn_prefix, dev_fd(dev)); - if (err) - return err; - cfg.dir_prefix = sn_prefix; + ilog.dev = dev; + ilog.cfg = &cfg; + + for (char *p = cfg.type; *p; ++p) + *p = toupper(*p); + + if (!strcmp(cfg.type, "ALL")) + log_type = ALL; + else if (!strcmp(cfg.type, "HIT")) + log_type = HIT; + else if (!strcmp(cfg.type, "CIT")) + log_type = CIT; + else if (!strcmp(cfg.type, "NLOG")) + log_type = NLOG; + else if (!strcmp(cfg.type, "ASSERT")) + log_type = ASSERTLOG; + else if (!strcmp(cfg.type, "EVENT")) + log_type = EVENTLOG; + else { + fprintf(stderr, "Invalid log type: %s\n", cfg.type); + return -EINVAL; } - t = time(NULL); - tm = *localtime(&t); - snprintf(folder, sizeof(folder), "%s-%d%02d%02d%02d%02d%02d", cfg.dir_prefix, - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - if (mkdir(folder, 0777) != 0) { - perror("mkdir"); + + dir = opendir(cfg.out_dir); + if (dir) + closedir(dir); + else { + perror(cfg.out_dir); return -errno; } - cfg.dir_prefix = folder; - output_path = folder; - if (!cfg.type) - cfg.type = "ALL"; - else { - for (char *p = cfg.type; *p; ++p) - *p = toupper(*p); - } + initial_folder = cfg.out_dir; - if (!strcmp(cfg.type, "ALL")) { - all = true; - } - if (all || !strcmp(cfg.type, "ASSERT")) { - err = dump_assert_logs(dev, cfg); - if (err == 0) - log_count++; - else if (err < 0) - perror("Error retrieving Assert log"); + err = get_serial_number(sn_prefix, dev_fd(dev)); + if (err) + return err; + + current_time = time(NULL); + strftime(date_str, sizeof(date_str), "-%Y%m%d%H%M%S", localtime(¤t_time)); + snprintf(unique_folder, sizeof(unique_folder), "%s%s", sn_prefix, date_str); + snprintf(full_folder, sizeof(full_folder) - 1, "%s/%s", cfg.out_dir, unique_folder); + if (mkdir(full_folder, 0755) != 0) { + perror("mkdir"); + return -errno; } - if (all || !strcmp(cfg.type, "EVENT")) { - err = dump_event_logs(dev, cfg); + cfg.out_dir = full_folder; + output_path = full_folder; + + /* Retrieve first logs that records actions to retrieve other logs */ + if (log_type == ALL || log_type == HIT) { + err = ilog_dump_telemetry(&ilog, HIT); if (err == 0) - log_count++; + ilog.count++; else if (err < 0) - perror("Error retrieving Event log"); + perror("Error retrieving Host Initiated Telemetry"); } - if (all || !strcmp(cfg.type, "NLOG")) { - err = dump_nlogs(dev, cfg, -1); + if (log_type == ALL || log_type == NLOG) { + err = ilog_dump_nlogs(&ilog, -1); if (err == 0) - log_count++; + ilog.count++; else if (err < 0) perror("Error retrieving Nlog"); } - if (all || !strcmp(cfg.type, "CONTROLLERINITTELEMETRY")) { - err = dump_telemetry(dev, cfg, CONTROLLER); + if (log_type == ALL || log_type == CIT) { + err = ilog_dump_telemetry(&ilog, CIT); if (err == 0) - log_count++; + ilog.count++; else if (err < 0) - perror("Error retrieving Telemetry Controller Initiated"); + perror("Error retrieving Controller Initiated Telemetry"); } - if (all || !strcmp(cfg.type, "HOSTINITTELEMETRYNOGEN")) { - err = dump_telemetry(dev, cfg, HOSTGENOLD); + if (log_type == ALL || log_type == ASSERTLOG) { + err = ilog_dump_assert_logs(&ilog); if (err == 0) - log_count++; + ilog.count++; else if (err < 0) - perror("Error retrieving previously existing Telemetry Host Initiated"); + perror("Error retrieving Assert log"); } - if (all || !strcmp(cfg.type, "HOSTINITTELEMETRY")) { - err = dump_telemetry(dev, cfg, HOSTGENNEW); + if (log_type == ALL || log_type == EVENTLOG) { + err = ilog_dump_event_logs(&ilog); if (err == 0) - log_count++; + ilog.count++; else if (err < 0) - perror("Error retrieving Telemetry Host Initiated"); + perror("Error retrieving Event log"); + } + if (log_type == ALL) { + err = ilog_dump_identify_pages(&ilog); + if (err < 0) + perror("Error retrieving Identify pages"); + + err = ilog_dump_pel(&ilog); + if (err < 0) + perror("Error retrieving Persistent Event Log page"); + + err = ilog_dump_no_lsp_log_pages(&ilog); + if (err < 0) + perror("Error retrieving no LSP Log pages"); } - if (log_count > 0) { + if (ilog.count > 0) { int ret_cmd; - char cmd[ARG_MAX]; - char *where_err = cfg.verbose ? "" : ">/dev/null 2>&1"; - - snprintf(zip_name, sizeof(zip_name), "%s.zip", cfg.dir_prefix); - snprintf(cmd, sizeof(cmd), "cd \"%s\" && zip -r \"../%s\" ./* %s", cfg.dir_prefix, - zip_name, where_err); + char *cmd; + char *quiet = cfg.verbose ? "" : " -q"; + + snprintf(zip_name, sizeof(zip_name) - 1, "%s.zip", unique_folder); + if (asprintf(&cmd, "cd \"%s\" && zip -MM -r \"../%s\" ./* %s", cfg.out_dir, + zip_name, quiet) < 0) { + err = errno; + perror("Can't allocate string for zip command"); + goto out; + } printf("Compressing logs to %s\n", zip_name); ret_cmd = system(cmd); - if (ret_cmd == -1) + if (ret_cmd) perror(cmd); else { output_path = zip_name; - snprintf(cmd, sizeof(cmd), "rm -rf %s", cfg.dir_prefix); - printf("Removing %s\n", cfg.dir_prefix); + free(cmd); + if (asprintf(&cmd, "rm -rf %s", cfg.out_dir) < 0) { + err = errno; + perror("Can't allocate string for cleanup"); + goto out; + } if (system(cmd) != 0) perror("Failed removing logs folder"); } + free(cmd); } - if (log_count == 0) { +out: + if (ilog.count == 0) { if (err > 0) nvme_show_status(err); - } else if ((log_count > 1) || cfg.verbose) - printf("Total: %d log files in %s\n", log_count, output_path); + + } else if ((ilog.count > 1) || cfg.verbose) + printf("Total: %d log files in %s/%s\n", ilog.count, initial_folder, output_path); return err; } diff --git a/plugins/solidigm/solidigm-latency-tracking.c b/plugins/solidigm/solidigm-latency-tracking.c index 66f3c56..899075d 100644 --- a/plugins/solidigm/solidigm-latency-tracking.c +++ b/plugins/solidigm/solidigm-latency-tracking.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2022 Solidigm. + * Copyright (c) 2022-2024 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ @@ -45,7 +45,7 @@ struct latency_tracker { int fd; __u8 uuid_index; struct config cfg; - enum nvme_print_flags print_flags; + nvme_print_flags_t print_flags; struct latency_statistics stats; struct json_object *bucket_list; __u32 bucket_list_size; @@ -430,7 +430,7 @@ int solidigm_get_latency_tracking_log(int argc, char **argv, struct command *cmd return -EINVAL; } - lt.uuid_index = solidigm_get_vu_uuid_index(dev); + sldgm_get_uuid_index(dev, <.uuid_index); err = latency_tracking_enable(<); if (err) { diff --git a/plugins/solidigm/solidigm-log-page-dir.c b/plugins/solidigm/solidigm-log-page-dir.c index bf272f8..f8d1974 100644 --- a/plugins/solidigm/solidigm-log-page-dir.c +++ b/plugins/solidigm/solidigm-log-page-dir.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2023 Solidigm. + * Copyright (c) 2023-2024 Solidigm. * * Author: karl.dedow@solidigm.com */ @@ -15,6 +15,7 @@ #include "nvme-print.h" #include "plugins/ocp/ocp-utils.h" +#include "solidigm-util.h" #define MIN_VENDOR_LID 0xC0 #define SOLIDIGM_MAX_UUID 2 @@ -38,41 +39,9 @@ static void init_lid_dir(struct lid_dir *lid_dir) } } -static bool is_invalid_uuid(const struct nvme_id_uuid_list_entry entry) -{ - static const unsigned char ALL_ZERO_UUID[NVME_UUID_LEN] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - return memcmp(ALL_ZERO_UUID, entry.uuid, NVME_UUID_LEN) == 0; -} - -static bool is_solidigm_uuid(const struct nvme_id_uuid_list_entry entry) -{ - static const unsigned char SOLIDIGM_UUID[NVME_UUID_LEN] = { - 0x96, 0x19, 0x58, 0x6e, 0xc1, 0x1b, 0x43, 0xad, - 0xaa, 0xaa, 0x65, 0x41, 0x87, 0xf6, 0xbb, 0xb2 - }; - - return memcmp(SOLIDIGM_UUID, entry.uuid, NVME_UUID_LEN) == 0; -} - -static bool is_ocp_uuid(const struct nvme_id_uuid_list_entry entry) -{ - static const unsigned char OCP_UUID[NVME_UUID_LEN] = { - 0xc1, 0x94, 0xd5, 0x5b, 0xe0, 0x94, 0x47, 0x94, - 0xa2, 0x1d, 0x29, 0x99, 0x8f, 0x56, 0xbe, 0x6f - }; - - return memcmp(OCP_UUID, entry.uuid, NVME_UUID_LEN) == 0; -} - static int get_supported_log_pages_log(struct nvme_dev *dev, int uuid_index, struct nvme_supported_log_pages *supported) { - static const __u8 LID; - memset(supported, 0, sizeof(*supported)); struct nvme_get_log_args args = { .lpo = 0, @@ -81,7 +50,7 @@ static int get_supported_log_pages_log(struct nvme_dev *dev, int uuid_index, .args_size = sizeof(args), .fd = dev_fd(dev), .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .lid = LID, + .lid = NVME_LOG_LID_SUPPORTED_LOG_PAGES, .len = sizeof(*supported), .nsid = NVME_NSID_ALL, .csi = NVME_CSI_NVM, @@ -101,8 +70,8 @@ static struct lid_dir *get_standard_lids(struct nvme_supported_log_pages *suppor init_lid_dir(&standard_dir); - for (int lid = 0; lid < NVME_LOG_SUPPORTED_LOG_PAGES_MAX; lid++) { - if (!supported->lid_support[lid] || lid >= MIN_VENDOR_LID) + for (int lid = 0; lid < MIN_VENDOR_LID; lid++) { + if (!supported->lid_support[lid]) continue; standard_dir.lid[lid].supported = true; @@ -128,12 +97,15 @@ static struct lid_dir *get_solidigm_lids(struct nvme_supported_log_pages *suppor static struct lid_dir solidigm_dir = { 0 }; init_lid_dir(&solidigm_dir); + solidigm_dir.lid[0xC0].str = "OCP SMART / Health Information Extended"; solidigm_dir.lid[0xC1].str = "Read Commands Latency Statistics"; solidigm_dir.lid[0xC2].str = "Write Commands Latency Statistics"; + solidigm_dir.lid[0xC3].str = "OCP Latency Monitor"; solidigm_dir.lid[0xC4].str = "Endurance Manager Statistics"; solidigm_dir.lid[0xC5].str = "Temperature Statistics"; solidigm_dir.lid[0xCA].str = "SMART Attributes"; solidigm_dir.lid[0xCB].str = "VU NVMe IO Queue Metrics Log Page"; + solidigm_dir.lid[0xD5].str = solidigm_dir.lid[0xC5].str; solidigm_dir.lid[0xDD].str = "VU Marketing Description Log Page"; solidigm_dir.lid[0xEF].str = "Performance Rating and LBA Access Histogram"; solidigm_dir.lid[0xF2].str = "Get Power Usage Log Page"; @@ -222,7 +194,7 @@ int solidigm_get_log_page_directory_log(int argc, char **argv, struct command *c OPT_END() }; - struct nvme_dev *dev = NULL; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; int err = parse_and_open(&dev, argc, argv, description, options); if (err) @@ -247,16 +219,21 @@ int solidigm_get_log_page_directory_log(int argc, char **argv, struct command *c lid_dirs[NO_UUID_INDEX]->lid[lid] = solidigm_lid_dir->lid[lid]; } } else { - for (int uuid_index = 1; uuid_index <= SOLIDIGM_MAX_UUID; uuid_index++) { - if (is_invalid_uuid(uuid_list.entry[uuid_index - 1])) - break; - else if (get_supported_log_pages_log(dev, uuid_index, &supported)) - continue; - - if (is_solidigm_uuid(uuid_list.entry[uuid_index - 1])) - lid_dirs[uuid_index] = get_solidigm_lids(&supported); - else if (is_ocp_uuid(uuid_list.entry[uuid_index - 1])) - lid_dirs[uuid_index] = get_ocp_lids(&supported); + __u8 sldgm_idx; + __u8 ocp_idx; + + sldgm_find_uuid_index(&uuid_list, &sldgm_idx); + ocp_find_uuid_index(&uuid_list, &ocp_idx); + + if (sldgm_idx && (sldgm_idx <= SOLIDIGM_MAX_UUID)) { + err = get_supported_log_pages_log(dev, sldgm_idx, &supported); + if (!err) + lid_dirs[sldgm_idx] = get_solidigm_lids(&supported); + } + if (ocp_idx && (ocp_idx <= SOLIDIGM_MAX_UUID)) { + err = get_supported_log_pages_log(dev, ocp_idx, &supported); + if (!err) + lid_dirs[ocp_idx] = get_ocp_lids(&supported); } } } else { @@ -264,7 +241,7 @@ int solidigm_get_log_page_directory_log(int argc, char **argv, struct command *c } if (!err) { - enum nvme_print_flags print_flag; + nvme_print_flags_t print_flag; err = validate_output_format(format, &print_flag); if (err < 0) { @@ -279,8 +256,5 @@ int solidigm_get_log_page_directory_log(int argc, char **argv, struct command *c } } - /* Redundant close() to make static code analysis happy */ - close(dev->direct.fd); - dev_close(dev); return err; } diff --git a/plugins/solidigm/solidigm-market-log.c b/plugins/solidigm/solidigm-market-log.c index d7d38da..e7e8728 100644 --- a/plugins/solidigm/solidigm-market-log.c +++ b/plugins/solidigm/solidigm-market-log.c @@ -12,7 +12,6 @@ #include <stdlib.h> #include <unistd.h> #include <inttypes.h> -#include <linux/limits.h> #include "common.h" #include "nvme.h" diff --git a/plugins/solidigm/solidigm-nvme.c b/plugins/solidigm/solidigm-nvme.c index 3fb86f5..8a7db07 100644 --- a/plugins/solidigm/solidigm-nvme.c +++ b/plugins/solidigm/solidigm-nvme.c @@ -21,6 +21,7 @@ #include "solidigm-temp-stats.h" #include "solidigm-get-drive-info.h" #include "solidigm-ocp-version.h" +#include "solidigm-workload-tracker.h" #include "plugins/ocp/ocp-clear-features.h" #include "plugins/ocp/ocp-smart-extended-log.h" @@ -107,3 +108,9 @@ static int get_cloud_SSDplugin_version(int argc, char **argv, struct command *cm { return sldgm_ocp_version(argc, argv, cmd, plugin); } + +static int get_workload_tracker(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return sldgm_get_workload_tracker(argc, argv, cmd, plugin); +} diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h index bee8266..2b74a02 100644 --- a/plugins/solidigm/solidigm-nvme.h +++ b/plugins/solidigm/solidigm-nvme.h @@ -13,7 +13,7 @@ #include "cmd.h" -#define SOLIDIGM_PLUGIN_VERSION "1.1" +#define SOLIDIGM_PLUGIN_VERSION "1.6" PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_VERSION), COMMAND_LIST( @@ -32,6 +32,8 @@ PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_V ENTRY("temp-stats", "Retrieve Temperature Statistics log", get_temp_stats_log) ENTRY("vs-drive-info", "Retrieve drive information", get_drive_info) ENTRY("cloud-SSDplugin-version", "Prints plug-in OCP version", get_cloud_SSDplugin_version) + ENTRY("workload-tracker", "Real Time capture Workload Tracker samples", + get_workload_tracker) ) ); diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c index 62245fa..002753a 100644 --- a/plugins/solidigm/solidigm-smart.c +++ b/plugins/solidigm/solidigm-smart.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2022 Solidigm. + * Copyright (c) 2022-2024 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ @@ -197,7 +197,7 @@ int solidigm_get_additional_smart_log(int argc, char **argv, struct command *cmd "Get Solidigm vendor specific smart log (optionally, for the specified namespace), and show it."; const int solidigm_vu_smart_log_id = 0xCA; struct vu_smart_log smart_log_payload; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_dev *dev; int err; __u8 uuid_index; @@ -229,7 +229,7 @@ int solidigm_get_additional_smart_log(int argc, char **argv, struct command *cmd return err; } - uuid_index = solidigm_get_vu_uuid_index(dev); + sldgm_get_uuid_index(dev, &uuid_index); struct nvme_get_log_args args = { .lpo = 0, diff --git a/plugins/solidigm/solidigm-temp-stats.c b/plugins/solidigm/solidigm-temp-stats.c index 85a3c37..7f385db 100644 --- a/plugins/solidigm/solidigm-temp-stats.c +++ b/plugins/solidigm/solidigm-temp-stats.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2023 Solidigm. + * Copyright (c) 2023-2024 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ @@ -11,7 +11,8 @@ #include "nvme-print.h" #include "solidigm-util.h" -#define SLDGM_TEMP_STATS_LID 0xC5 +#define SLDGM_LEGACY_TEMP_STATS_LID 0xC5 +#define SLDGM_TEMP_STATS_LID 0xD5 struct temp_stats { __le64 curr; @@ -40,7 +41,7 @@ static void show_temp_stats(struct temp_stats *stats) int sldgm_get_temp_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { unsigned char buffer[4096] = {0}; - struct nvme_dev *dev; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; __u8 uuid_idx; int err; @@ -63,7 +64,7 @@ int sldgm_get_temp_stats_log(int argc, char **argv, struct command *cmd, struct if (err) return err; - uuid_idx = solidigm_get_vu_uuid_index(dev); + sldgm_get_uuid_index(dev, &uuid_idx); struct nvme_get_log_args args = { .lpo = 0, @@ -84,25 +85,26 @@ int sldgm_get_temp_stats_log(int argc, char **argv, struct command *cmd, struct }; err = nvme_get_log(&args); - if (!err) { - uint64_t *guid = (uint64_t *)&buffer[4080]; + if (err > 0) { + args.lid = SLDGM_LEGACY_TEMP_STATS_LID; + err = nvme_get_log(&args); + if (!err) { + uint64_t *guid = (uint64_t *)&buffer[4080]; - if (guid[1] == 0xC7BB98B7D0324863 && guid[0] == 0xBB2C23990E9C722F) { - fprintf(stderr, "Error: Log page has 'OCP unsupported Requirements' GUID\n"); - err = -EBADMSG; - goto closefd; + if (guid[1] == 0xC7BB98B7D0324863 && guid[0] == 0xBB2C23990E9C722F) { + fprintf(stderr, + "Error: Log page has OCP unsupported Requirements GUID\n"); + return -EBADMSG; + } } + } + if (!err) { if (!cfg.raw_binary) show_temp_stats((struct temp_stats *) buffer); else d_raw(buffer, sizeof(struct temp_stats)); - } else if (err > 0) { + } else if (err > 0) nvme_show_status(err); - } -closefd: - /* Redundant close() to make static code analysis happy */ - close(dev->direct.fd); - dev_close(dev); return err; } diff --git a/plugins/solidigm/solidigm-util.c b/plugins/solidigm/solidigm-util.c index 0171a49..05d1537 100644 --- a/plugins/solidigm/solidigm-util.c +++ b/plugins/solidigm/solidigm-util.c @@ -1,20 +1,39 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2023 Solidigm. + * Copyright (c) 2023-2024 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ -#include "plugins/ocp/ocp-utils.h" +#include <errno.h> #include "solidigm-util.h" -__u8 solidigm_get_vu_uuid_index(struct nvme_dev *dev) +const unsigned char solidigm_uuid[NVME_UUID_LEN] = { + 0x96, 0x19, 0x58, 0x6e, 0xc1, 0x1b, 0x43, 0xad, + 0xaa, 0xaa, 0x65, 0x41, 0x87, 0xf6, 0xbb, 0xb2 +}; + +int sldgm_find_uuid_index(struct nvme_id_uuid_list *uuid_list, __u8 *index) { - int ocp_uuid_index = 0; + int i = nvme_uuid_find(uuid_list, solidigm_uuid); - if (ocp_get_uuid_index(dev, &ocp_uuid_index) == 0) - if (ocp_uuid_index == 2) - return 1; + *index = 0; + if (i > 0) + *index = i; + else + return -errno; return 0; } + +int sldgm_get_uuid_index(struct nvme_dev *dev, __u8 *index) +{ + struct nvme_id_uuid_list uuid_list; + int err = nvme_identify_uuid(dev_fd(dev), &uuid_list); + + *index = 0; + if (err) + return err; + + return sldgm_find_uuid_index(&uuid_list, index); +} diff --git a/plugins/solidigm/solidigm-util.h b/plugins/solidigm/solidigm-util.h index fa5032f..ed7bf0f 100644 --- a/plugins/solidigm/solidigm-util.h +++ b/plugins/solidigm/solidigm-util.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (c) 2023 Solidigm. + * Copyright (c) 2023-2024 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ @@ -9,4 +9,5 @@ #define DRIVER_MAX_TX_256K (256 * 1024) -__u8 solidigm_get_vu_uuid_index(struct nvme_dev *dev); +int sldgm_find_uuid_index(struct nvme_id_uuid_list *uuid_list, __u8 *index); +int sldgm_get_uuid_index(struct nvme_dev *dev, __u8 *index); diff --git a/plugins/solidigm/solidigm-workload-tracker.c b/plugins/solidigm/solidigm-workload-tracker.c new file mode 100644 index 0000000..73bb3c3 --- /dev/null +++ b/plugins/solidigm/solidigm-workload-tracker.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2024 Solidigm. + * + * Authors: leonardo.da.cunha@solidigm.com + */ + +#include "common.h" +#include "nvme-print.h" +#include <errno.h> +#include <time.h> + +#define LID 0xf9 +#define FID 0xf1 +#define WLT2MS 25000 +#define MAX_WORKLOAD_LOG_ENTRIES 126 +#define MAX_WORKLOAD_LOG_ENTRY_SIZE 32 +#define MAX_FIELDS 15 + +char const *samplet[] = { + "default", + "1ms", + "5ms", + "10ms", + "50ms", + "100ms", + "500ms", + "1s", + "5s", + "10s", + "30s", + "1m", + "5m", + "10m", + "30m", + "1h" +}; + +char const *trk_types[] = { + "Base", + "CmdQ", + "Pattern", + "RandSeq", + "Throttle", + "Power", + "Defrag" +}; + +struct field { + __u8 size; + char *name; + char *desc; +}; + +struct field group_fields[][MAX_FIELDS] = { +{ // Base, group 0 + {4, "hostReads", "Host Read Count in Sectors"}, + {4, "hostWrites", "Host Write Count in Sectors"}, + {4, "nandWrites", "Nand Write Count in Sectors"}, + {1, "misalignment%", "% of Misaligned Sectors"}, + {1, "collision%", "% of Colliding Sectors"}, + {1, "randomWrite%", "% of Random Write Sectors vs. Sequential"}, + {1, "randomRead%", "% of Random Read Sectors vs. Sequential"}, + {4, "xorInvokedCount", "Count of XOR Operations Invoked"}, + {4, "hostSoftReadSuccess", "Count of Soft Reads Completed Successfully."}, + {4, "bandDefragRelocation", "Count of BDRs"}, + {1, "pwrThrottle%", "% of Throttle Period due to Power Regulation"}, + {1, "thmThrottle%", "% of Throttle Period due to Thermal Levels"}, + {1, "tbufBg%", "% of Background TBUF Work vs. All Available Work"}, + {1, "tbufHost%", "% of Host Requested TBUF Work vs. All Available Work"}, + {0} +}, +{ //CmdQ stats, group 1 + {4, "CmdQ_InternalReadQDepth", "Snapshot of the Internal Read Queue Depth"}, + {4, "CmdQ_DetectedWriteQDepth", "Snapshot of the Internal Write Queue Depth"}, + {4, "CmdQ_ReadCmdsPending", "Snapshot of the Internal Read Commands Pending"}, + {1, "misalignment%", "% of Misaligned Sectors"}, + {1, "collision%", "% of Colliding Sectors"}, + {1, "randomWrite%", "% of Random Write Sectors vs. Sequential"}, + {1, "randomRead%", "% of Random Read Sectors vs. Sequential"}, + {4, "CmdQ_WriteCmdsPending", "Snapshot of the Internal Write Commands Pending"}, + {4, "CmdQ_ReadCmdsOutstanding", "Snapshot of the Internal Read Commands Outstanding"}, + {4, "CmdQ_WriteCmdsOutstanding", "Snapshot of the Internal Read Commands Outstanding"}, + {1, "pwrThrottle%", "% of Throttle Period due to Power Regulation"}, + {1, "thmThrottle%", "% of Throttle Period due to Thermal Levels"}, + {1, "tbufBg%", "% of Background TBUF Work vs. All Available Work"}, + {1, "tbufHost%", "% of Host Requested TBUF Work vs. All Available Work"}, + {0} +}, +{ // test pattern, group 2 + {4, "x11223300"}, + {4, "x44556600_"}, + {4, "x77889900_"}, + {4, "xAABBCC00_"}, + {2, "xDD00"}, + {2, "xEE00"}, + {2, "xFF00"}, + {2, "x0_"}, + {1, "x00"}, + {1, "x80"}, + {1, "x__"}, + {1, "x8_"}, + {4, "x33322100"}, + {0} +}, +{ // Random vs. Sequential Data, group 3 + {4, "hostReads", "Host Read Count in Sectors"}, + {4, "hostWrites", "Host Write Count in Sectors"}, + {4, "nandWrites", "Nand Write Count in Sectors"}, + {4, "randomReadCmd", "Count of Random Read Commands (vs. Sequential)"}, + {4, "randomWriteCmd", "Count of Random Write Commands (vs. Sequential)"}, + {4, "hostReadCmd", "Count of Total Host Read Commands (vs. Sequential)"}, + {4, "hostWriteCmd", "Count of Total Host Read Commands (vs. Sequential)"}, + {1, NULL}, + {1, NULL}, + {1, "randomWrite%", "% of Random Write Sectors vs. Sequential"}, + {1, "randomThrottleRead%", "% of Random Read Sectors vs. Sequential"}, + {0} +}, +{ //Detailed Throttle Data, group 4 + {4, "pwrThrottleOn_ms", "Duration of Power Throttling in mS."}, + {4, "thmThrottleOn_ms", "Duration of Thermal Throttling in mS."}, + {4, "powerOn_us", "Duration of Power-on in uS."}, + {4, NULL}, + {4, NULL}, + {4, NULL}, + {4, NULL}, + {1, "pwrThrottle%", "% of Throttle Period due to Power Regulation"}, + {1, "thmThrottle%", "% of Throttle Period due to Thermal Levels"}, + {0} +}, +{ // Detailed Power Data, group 5 + // PMIC and/or Input Voltage Power + {4, "vin1Power", "in uW"}, + {4, "vin2Power"}, + // NAND Workload + {4, "nandWrites", "Nand Write Count in Sectors"}, + {4, "nandReads", "Nand Read Count in Sectors"}, + // Power Governor (if not enabled, all-0s) + {4, "lastNandAvgPwr"}, + {4, "lastDriveAvgPwr"}, + {4, "NscPwgSysCreditCnt"}, + {4, "burstPowerBudget"}, + {0} +}, +{ // Defrag, group 6 + {4, "hostReads", "Host Read Count in Sectors"}, + {4, "hostWrites", "Host Write Count in Sectors"}, + {4, "nandWrites", "Nand Write Count in Sectors"}, + {4, "defragSlots", "Current defragSlots"}, + {4, "hostSlots", "hostSlots"}, + {4, "totalSlots", "Total slots"}, + {1, "hostBufferUse%", "% of WCM_GetHostBuffersInUse to WCM_GetDesiredHostBuffer"}, + {1, "defragBufferUse%", "% of defragBuffer to Desired defrag buffer %"}, + {1, "defragSlotsUse%", "defragSlots to Total defrag slots %"}, + {1, "hostSlotsUse%", "hostSlots to Total defrag slots %"}, + {1, "aiuUse%", "% of AvailableIndirectionUnits to Start Setpoint IU"}, + {1, "isImminentFRorWL", "defrag/Wear leveling is imminent"}, + {1, "defragType", "defrag type"}, + {0} +}}; + +#pragma pack(push, 1) +union WorkloadLogEnable { + struct { + __u32 trackerEnable : 1; + __u32 triggerEnable : 1; + __u32 triggerSynchronous : 1; // trigger mode, 1=Synchronous,0=ASynchronous(Latency) + __u32 triggerDelta : 1; // trigger value mode, 1=delta, 0=current value + __u32 triggerDwordIndex : 3; // trigger dword index, 0~7 of a log entry + __u32 triggerByteWordIndex : 2; // trigger byte or word index,byte=0~3, word=0~1 + __u32 triggerSize : 2; // trigger size, 1=byte, 2=word, 3=dword as a trigger + __u32 sampleTime : 4; // trigger sample time + __u32 contentGroup : 4; // content group select + __u32 stopCount : 12;// event limit,if<>0,stop tracker after stopCount events + __u32 eventDumpEnable : 1; // trigger event dump enable + } field; + __u32 dword; +}; + +struct workloadLogHeader { + __u16 majorVersion; // Major Version + __u16 minorVersion; // Minor Version + __u32 workloadLogCount; // Number of Entries in the Workload Log + __u32 reserved; // reserve for future + __u32 triggeredEvents; // Count of events triggered + __u32 samplePeriodInMilliseconds; // Sample Period In Milliseconds + __u64 timestamp_lastEntry; // Timestamp for the last full entry + __u64 timestamp_triggered; // Timestamp at the point of trigger + __u32 trackerEnable; // Workload trigger and enable settings + __u32 triggerthreshold; // Trigger threshold + __u32 triggeredValue; // Actual value fired the trigger +}; + + +struct workloadLog { // Full WL Log Structure + struct workloadLogHeader header; + __u8 entry[MAX_WORKLOAD_LOG_ENTRIES][MAX_WORKLOAD_LOG_ENTRY_SIZE]; +}; +#pragma pack(pop) + +struct wltracker { + int fd; + struct workloadLog workload_log; + size_t entry_count; + unsigned int verbose; +}; + +static void wltracker_print_field_names(struct wltracker *wlt) +{ + struct workloadLog *log = &wlt->workload_log; + __u8 cnt = log->header.workloadLogCount; + union WorkloadLogEnable workloadEnable = (union WorkloadLogEnable)log->header.trackerEnable; + __u8 content_group = workloadEnable.field.contentGroup; + + if (cnt == 0) + return; + + printf("%-16s", "timestamp"); + + for (int i = 0 ; i < MAX_FIELDS; i++) { + struct field f = group_fields[content_group][i]; + + if (f.size == 0) + break; + if (f.name == NULL) + continue; + printf("%s ", f.name); + } + + if (wlt->verbose > 1) + printf("%s", "entry#"); + + printf("\n"); +} + +static void wltracker_print_header(struct wltracker *wlt) +{ + struct workloadLog *log = &wlt->workload_log; + __u8 cnt = log->header.workloadLogCount; + union WorkloadLogEnable workloadEnable = (union WorkloadLogEnable)log->header.trackerEnable; + __u8 content_group = workloadEnable.field.contentGroup; + + printf("%-20s %u.%u\n", "Log page version:", le16_to_cpu(log->header.majorVersion), + le16_to_cpu(log->header.minorVersion)); + printf("%-20s %u\n", "Sample period(ms):", + le32_to_cpu(log->header.samplePeriodInMilliseconds)); + printf("%-20s %lu\n", "timestamp_lastEntry:", + le64_to_cpu(log->header.timestamp_lastEntry) / WLT2MS); + printf("%-20s %lu\n", "timestamp_triggered:", + le64_to_cpu(log->header.timestamp_triggered/1000)); + printf("%-20s 0x%x\n", "trackerEnable:", le32_to_cpu(log->header.trackerEnable)); + printf("%-20s %u\n", "Triggerthreshold:", + le32_to_cpu(log->header.triggerthreshold)); + printf("%-20s %u\n", "ValueTriggered:", le32_to_cpu(log->header.triggeredValue)); + printf("%-20s %s\n", "Tracker Type:", trk_types[content_group]); + printf("%-30s %u\n", "Total workload log entries:", le16_to_cpu(cnt)); + printf("%-20s %ld\n\n", "Sample count:", wlt->entry_count); + if (wlt->entry_count != 0) + wltracker_print_field_names(wlt); +} + +static int wltracker_show_newer_entries(struct wltracker *wlt) +{ + struct workloadLog *log = &wlt->workload_log; + __u8 cnt; + __u8 content_group; + static __u64 last_timestamp_ms; + __u64 timestamp = 0; + union WorkloadLogEnable workloadEnable; + + int err = nvme_get_log_simple(wlt->fd, LID, sizeof(struct workloadLog), log); + + if (err > 0) { + nvme_show_status(err); + return err; + } + if (err < 0) + return err; + + if (wlt->verbose) + wltracker_print_header(wlt); + + cnt = log->header.workloadLogCount; + workloadEnable = (union WorkloadLogEnable)log->header.trackerEnable; + content_group = workloadEnable.field.contentGroup; + + if (cnt == 0) { + nvme_show_error("Warning : No valid workload log data\n"); + return 0; + } + + timestamp = (le64_to_cpu(log->header.timestamp_lastEntry) / WLT2MS) - + (log->header.samplePeriodInMilliseconds * (cnt - 1)); + + + if (wlt->entry_count == 0) + wltracker_print_field_names(wlt); + + for (int i = cnt - 1; i >= 0; i--) { + int offset = 0; + __u8 *entry = (__u8 *) &log->entry[i]; + bool is_old = timestamp <= last_timestamp_ms; + + if (is_old) { + timestamp += log->header.samplePeriodInMilliseconds; + continue; + } + printf("%-16llu", timestamp); + for (int j = 0; j < MAX_FIELDS; j++) { + __u32 val = 0; + struct field f = group_fields[content_group][j]; + + if (f.size == 0) { + if (wlt->verbose > 1) + printf("%-*i", (int)sizeof("entry#"), i); + printf("\n"); + break; + } + if (f.name == NULL) + continue; + + switch (f.size) { + case 1: + val = *(entry+offset); + break; + case 2: + val = *(__u16 *)(entry + offset); + break; + case 4: + val = *(__u32 *)(entry + offset); + break; + default: + nvme_show_error("Bad field size"); + } + offset += f.size; + + printf("%-*u ", (int)strlen(f.name), val); + } + wlt->entry_count++; + timestamp += log->header.samplePeriodInMilliseconds; + } + last_timestamp_ms = log->header.timestamp_lastEntry / WLT2MS; + return 0; +} + +int wltracker_config(struct wltracker *wlt, union WorkloadLogEnable *we) +{ + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = wlt->fd, + .fid = FID, + .cdw11 = we->dword, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + }; + + return nvme_set_features(&args); +} + +static int stricmp(char const *a, char const *b) +{ + for (; *a || *b; a++, b++) + if (tolower((unsigned char)*a) != tolower((unsigned char)*b)) + return 1; + return 0; +} + +static int find_option(char const *list[], int size, const char *val) +{ + for (int i = 0; i < size; i++) { + if (!stricmp(val, list[i])) + return i; + } + return -EINVAL; +} + +static void join(char *dest, char const *list[], size_t list_size) +{ + strcat(dest, list[0]); + for (int i = 1; i < list_size; i++) { + strcat(dest, "|"); + strcat(dest, list[i]); + } +} + +__u64 micros(void) +{ + struct timespec ts; + __u64 us; + + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + us = (((__u64)ts.tv_sec)*1000000) + (((__u64)ts.tv_nsec)/1000); + return us; +} + +int sldgm_get_workload_tracker(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + struct wltracker wlt = {0}; + union WorkloadLogEnable we = {0}; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + const char *desc = "Real Time capture Workload Tracker samples"; + const char *sample_interval = "Sample interval"; + const char *run_time = "Limit runtime capture time in seconds"; + const char *flush_frequency = + "Samples (1 to 126) to wait for extracting data. Default 100 samples"; + char type_options[80] = {0}; + char sample_options[80] = {0}; + __u64 us_start; + __u64 run_time_us; + __u64 elapsed_run_time_us = 0; + __u64 next_sample_us = 0; + int opt; + int err; + + struct config { + bool enable; + bool disable; + const char *tracker_type; + const char *sample_time; + int run_time_s; + int flush_frequency; + }; + + struct config cfg = { + .sample_time = samplet[0], + .flush_frequency = 100, + .tracker_type = trk_types[0], + }; + + join(type_options, trk_types, ARRAY_SIZE(trk_types)); + join(sample_options, samplet, ARRAY_SIZE(samplet)); + + OPT_ARGS(opts) = { + OPT_FLAG("enable", 'e', &cfg.enable, "tracker enable"), + OPT_FLAG("disable", 'd', &cfg.disable, "tracker disable"), + OPT_STRING("sample-time", 's', sample_options, &cfg.sample_time, sample_interval), + OPT_STRING("type", 't', type_options, &cfg.tracker_type, "Tracker type"), + OPT_INT("run-time", 'r', &cfg.run_time_s, run_time), + OPT_INT("flush-freq", 'f', &cfg.flush_frequency, flush_frequency), + OPT_INCR("verbose", 'v', &wlt.verbose, "Increase logging verbosity"), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + wlt.fd = dev_fd(dev); + + if ((cfg.flush_frequency < 1) || (cfg.flush_frequency > MAX_WORKLOAD_LOG_ENTRIES)) { + nvme_show_error("Invalid number of samples: %s. Valid values: 1-%d", + cfg.flush_frequency, MAX_WORKLOAD_LOG_ENTRIES); + return -EINVAL; + } + + opt = find_option(samplet, ARRAY_SIZE(samplet), cfg.sample_time); + if (opt < 0) { + nvme_show_error("invalid Sample interval: %s. Valid values: %s", + cfg.sample_time, sample_options); + return -EINVAL; + } + we.field.sampleTime = opt; + + opt = find_option(trk_types, ARRAY_SIZE(trk_types), cfg.tracker_type); + if (opt < 0) { + nvme_show_error("Invalid tracker type: %s. Valid types: %s", + cfg.tracker_type, type_options); + return -EINVAL; + } + we.field.contentGroup = opt; + + if (cfg.enable && cfg.disable) { + nvme_show_error("Can't enable disable simultaneously"); + return -EINVAL; + } + + if (cfg.enable || cfg.disable) { + we.field.trackerEnable = cfg.enable; + err = wltracker_config(&wlt, &we); + if (err < 0) { + nvme_show_error("tracker set-feature: %s", nvme_strerror(errno)); + return err; + } else if (err > 0) { + nvme_show_status(err); + return err; + } + } + + if (cfg.disable && !cfg.enable) { + printf("Tracker disabled\n"); + return 0; + } + + us_start = micros(); + run_time_us = cfg.run_time_s * 1000000; + while (elapsed_run_time_us < run_time_us) { + __u64 interval; + __u64 elapsed; + __u64 prev_elapsed_run_time_us = elapsed_run_time_us; + + err = wltracker_show_newer_entries(&wlt); + + if (err > 0) { + nvme_show_status(err); + return err; + } + interval = ((__u64)wlt.workload_log.header.samplePeriodInMilliseconds) * 1000 * + cfg.flush_frequency; + next_sample_us += interval; + elapsed_run_time_us = micros() - us_start; + elapsed = elapsed_run_time_us - prev_elapsed_run_time_us; + if (wlt.verbose > 1) + printf("elapsed_run_time: %lluus\n", elapsed_run_time_us); + if (interval > elapsed) { + __u64 period_us = min(next_sample_us - elapsed_run_time_us, + run_time_us - elapsed_run_time_us); + if (wlt.verbose > 1) + printf("Sleeping %lluus..\n", period_us); + usleep(period_us); + } + elapsed_run_time_us = micros() - us_start; + } + + err = wltracker_show_newer_entries(&wlt); + + elapsed_run_time_us = micros() - us_start; + if (wlt.verbose > 0) + printf("elapsed_run_time: %lluus\n", elapsed_run_time_us); + + if (err > 0) { + nvme_show_status(err); + return err; + } + return err; +} diff --git a/plugins/solidigm/solidigm-workload-tracker.h b/plugins/solidigm/solidigm-workload-tracker.h new file mode 100644 index 0000000..d3ecc16 --- /dev/null +++ b/plugins/solidigm/solidigm-workload-tracker.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2024 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +int sldgm_get_workload_tracker(int argc, char **argv, struct command *cmd, struct plugin *plugin); diff --git a/plugins/ssstc/ssstc-nvme.c b/plugins/ssstc/ssstc-nvme.c new file mode 100644 index 0000000..302df98 --- /dev/null +++ b/plugins/ssstc/ssstc-nvme.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "linux/types.h" +#include "nvme-print.h" + +#define CREATE_CMD +#include "ssstc-nvme.h" + +struct __packed nvme_additional_smart_log_item +{ + __u8 key; + __u8 norm; + union __packed { + __u8 raw[6]; + struct __packed wear_level + { + __le16 min; + __le16 max; + __le16 avg; + } wear_level; + }; + __u8 _rp[2]; +}; + +struct nvme_additional_smart_log { + struct nvme_additional_smart_log_item program_fail_cnt; + struct nvme_additional_smart_log_item erase_fail_cnt; + struct nvme_additional_smart_log_item wear_leveling_cnt; + struct nvme_additional_smart_log_item e2e_err_cnt; + struct nvme_additional_smart_log_item crc_err_cnt; + struct nvme_additional_smart_log_item nand_bytes_written; + struct nvme_additional_smart_log_item host_bytes_written; + struct nvme_additional_smart_log_item reallocated_sector_count; + struct nvme_additional_smart_log_item uncorrectable_sector_count; + struct nvme_additional_smart_log_item NAND_ECC_Detection_Count; + struct nvme_additional_smart_log_item NAND_ECC_Correction_Count; + struct nvme_additional_smart_log_item Bad_Block_Failure_Rate; + struct nvme_additional_smart_log_item GC_Count; + struct nvme_additional_smart_log_item DRAM_UECC_Detection_Count; + struct nvme_additional_smart_log_item SRAM_UECC_Detection_Count; + struct nvme_additional_smart_log_item Raid_Recovery_Fail_Count; + struct nvme_additional_smart_log_item Inflight_Command; + struct nvme_additional_smart_log_item Internal_End_to_End_Dect_Count; + struct nvme_additional_smart_log_item PCIe_Correctable_Error_Count; + struct nvme_additional_smart_log_item die_fail_count; + struct nvme_additional_smart_log_item wear_leveling_exec_count; + struct nvme_additional_smart_log_item read_disturb_count; + struct nvme_additional_smart_log_item data_retention_count; +}; + + +static +void show_ssstc_add_smart_log_jsn(struct nvme_additional_smart_log *smart, + unsigned int nsid, const char *devname) +{ + struct json_object *root, *entry_stats, *dev_stats, *multi; + uint16_t wear_level_min = 0; + uint16_t wear_level_max = 0; + uint16_t wear_level_avg = 0; + uint64_t raw_val = 0; + + root = json_create_object(); + json_object_add_value_string(root, "SSSTC Smart log", devname); + + dev_stats = json_create_object(); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->program_fail_cnt.key); + json_object_add_value_int(entry_stats, "normalized", smart->program_fail_cnt.norm); + raw_val = int48_to_long(smart->program_fail_cnt.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "program_fail_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->erase_fail_cnt.key); + json_object_add_value_int(entry_stats, "normalized", smart->erase_fail_cnt.norm); + raw_val = int48_to_long(smart->erase_fail_cnt.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "erase_fail_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->wear_leveling_cnt.key); + json_object_add_value_int(entry_stats, "normalized", smart->wear_leveling_cnt.norm); + multi = json_create_object(); + wear_level_min = le16_to_cpu(smart->wear_leveling_cnt.wear_level.min); + wear_level_max = le16_to_cpu(smart->wear_leveling_cnt.wear_level.max); + wear_level_avg = le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg); + json_object_add_value_int(multi, "min", wear_level_min); + json_object_add_value_int(multi, "max", wear_level_max); + json_object_add_value_int(multi, "avg", wear_level_avg); + json_object_add_value_object(entry_stats, "raw", multi); + json_object_add_value_object(dev_stats, "wear_leveling", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->e2e_err_cnt.key); + json_object_add_value_int(entry_stats, "normalized", smart->e2e_err_cnt.norm); + multi = json_create_object(); + wear_level_min = le16_to_cpu(smart->e2e_err_cnt.wear_level.min); + wear_level_max = le16_to_cpu(smart->e2e_err_cnt.wear_level.max); + wear_level_avg = le16_to_cpu(smart->e2e_err_cnt.wear_level.avg); + json_object_add_value_int(multi, "guard check error", wear_level_min); + json_object_add_value_int(multi, "application tag check error", wear_level_max); + json_object_add_value_int(multi, "reference tag check error", wear_level_avg); + json_object_add_value_object(entry_stats, "raw", multi); + json_object_add_value_object(dev_stats, "end_to_end_error_dect_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->crc_err_cnt.key); + json_object_add_value_int(entry_stats, "normalized", smart->crc_err_cnt.norm); + raw_val = int48_to_long(smart->crc_err_cnt.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "crc_error_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->nand_bytes_written.key); + json_object_add_value_int(entry_stats, "normalized", smart->nand_bytes_written.norm); + raw_val = int48_to_long(smart->nand_bytes_written.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "nand_bytes_written", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->host_bytes_written.key); + json_object_add_value_int(entry_stats, "normalized", smart->host_bytes_written.norm); + raw_val = int48_to_long(smart->host_bytes_written.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "host_bytes_written", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->reallocated_sector_count.key); + json_object_add_value_int(entry_stats, "normalized", smart->reallocated_sector_count.norm); + raw_val = int48_to_long(smart->reallocated_sector_count.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "reallocated_sector_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->uncorrectable_sector_count.key); + json_object_add_value_int(entry_stats, "normalized", + smart->uncorrectable_sector_count.norm); + raw_val = int48_to_long(smart->uncorrectable_sector_count.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "uncorrectable_sector_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->NAND_ECC_Detection_Count.key); + json_object_add_value_int(entry_stats, "normalized", smart->NAND_ECC_Detection_Count.norm); + raw_val = int48_to_long(smart->NAND_ECC_Detection_Count.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "NAND_ECC_detection_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->NAND_ECC_Correction_Count.key); + json_object_add_value_int(entry_stats, "normalized", smart->NAND_ECC_Correction_Count.norm); + raw_val = int48_to_long(smart->NAND_ECC_Correction_Count.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "NAND_ECC_correction_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->GC_Count.key); + json_object_add_value_int(entry_stats, "normalized", smart->GC_Count.norm); + raw_val = int48_to_long(smart->GC_Count.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "GC_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->DRAM_UECC_Detection_Count.key); + json_object_add_value_int(entry_stats, "normalized", smart->DRAM_UECC_Detection_Count.norm); + multi = json_create_object(); + wear_level_max = le16_to_cpu(smart->DRAM_UECC_Detection_Count.wear_level.max); + wear_level_avg = le16_to_cpu(smart->DRAM_UECC_Detection_Count.wear_level.avg); + json_object_add_value_int(multi, "1-Bit Err", wear_level_max); + json_object_add_value_int(multi, "2-Bit Err", wear_level_avg); + json_object_add_value_object(entry_stats, "raw", multi); + json_object_add_value_object(dev_stats, "DRAM_UECC_detection_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->SRAM_UECC_Detection_Count.key); + json_object_add_value_int(entry_stats, "normalized", smart->SRAM_UECC_Detection_Count.norm); + multi = json_create_object(); + wear_level_min = le16_to_cpu(smart->SRAM_UECC_Detection_Count.wear_level.min); + wear_level_max = le16_to_cpu(smart->SRAM_UECC_Detection_Count.wear_level.max); + wear_level_avg = le16_to_cpu(smart->SRAM_UECC_Detection_Count.wear_level.avg); + json_object_add_value_int(multi, "parity error detected", wear_level_min); + json_object_add_value_int(multi, "ecc error detection", wear_level_max); + json_object_add_value_int(multi, "axi data parity errors", wear_level_avg); + json_object_add_value_object(entry_stats, "raw", multi); + json_object_add_value_object(dev_stats, "SRAM_UECC_Detection_Count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->Raid_Recovery_Fail_Count.key); + json_object_add_value_int(entry_stats, "normalized", smart->Raid_Recovery_Fail_Count.norm); + raw_val = int48_to_long(smart->Raid_Recovery_Fail_Count.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "raid_Recovery_fail_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->Inflight_Command.key); + json_object_add_value_int(entry_stats, "normalized", smart->Inflight_Command.norm); + multi = json_create_object(); + wear_level_min = le16_to_cpu(smart->Inflight_Command.wear_level.min); + wear_level_max = le16_to_cpu(smart->Inflight_Command.wear_level.max); + wear_level_avg = le16_to_cpu(smart->Inflight_Command.wear_level.avg); + json_object_add_value_int(multi, "Read Cmd", wear_level_min); + json_object_add_value_int(multi, "Write Cmd", wear_level_max); + json_object_add_value_int(multi, "Admin Cmd", wear_level_avg); + json_object_add_value_object(entry_stats, "raw", multi); + json_object_add_value_object(dev_stats, "Inflight_Command", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->Internal_End_to_End_Dect_Count.key); + json_object_add_value_int(entry_stats, "normalized", 100); + multi = json_create_object(); + wear_level_min = le16_to_cpu(smart->Internal_End_to_End_Dect_Count.wear_level.min); + wear_level_max = le16_to_cpu(smart->Internal_End_to_End_Dect_Count.wear_level.max); + wear_level_avg = le16_to_cpu(smart->Internal_End_to_End_Dect_Count.wear_level.avg); + json_object_add_value_int(multi, "read hcrc", wear_level_min); + json_object_add_value_int(multi, "write hcrc", wear_level_max); + json_object_add_value_int(multi, "reserved", wear_level_avg); + json_object_add_value_object(entry_stats, "raw", multi); + json_object_add_value_object(dev_stats, "internal_end_to_end_dect_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->die_fail_count.key); + json_object_add_value_int(entry_stats, "normalized", smart->die_fail_count.norm); + raw_val = int48_to_long(smart->die_fail_count.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "die_fail_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->wear_leveling_exec_count.key); + json_object_add_value_int(entry_stats, "normalized", smart->wear_leveling_exec_count.norm); + raw_val = int48_to_long(smart->wear_leveling_exec_count.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "wear_leveling_exec_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->read_disturb_count.key); + json_object_add_value_int(entry_stats, "normalized", smart->read_disturb_count.norm); + raw_val = int48_to_long(smart->read_disturb_count.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "read_disturb_count", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "#id", smart->data_retention_count.key); + json_object_add_value_int(entry_stats, "normalized", smart->data_retention_count.norm); + raw_val = int48_to_long(smart->data_retention_count.raw); + json_object_add_value_int(entry_stats, "raw", raw_val); + json_object_add_value_object(dev_stats, "data_retention_count", entry_stats); + + json_object_add_value_object(root, "Device stats", dev_stats); + + json_print_object(root, NULL); + json_free_object(root); +} + +static +void show_ssstc_add_smart_log(struct nvme_additional_smart_log *smart, + unsigned int nsid, const char *devname) +{ + printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", + devname, nsid); + printf("key #id normalized raw\n"); + printf("program_fail_count : %03d %3d%% %"PRIu64"\n", + smart->program_fail_cnt.key, + smart->program_fail_cnt.norm, + int48_to_long(smart->program_fail_cnt.raw)); + printf("erase_fail_count : %03d %3d%% %"PRIu64"\n", + smart->erase_fail_cnt.key, + smart->erase_fail_cnt.norm, + int48_to_long(smart->erase_fail_cnt.raw)); + printf("wear_leveling : %03d %3d%% min: %u, max: %u, avg: %u\n", + smart->wear_leveling_cnt.key, + smart->wear_leveling_cnt.norm, + le16_to_cpu(smart->wear_leveling_cnt.wear_level.min), + le16_to_cpu(smart->wear_leveling_cnt.wear_level.max), + le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg)); + printf("end_to_end_error_dect_count : %03d %3d%% " + "guard check error: %u, " + "application tag check error: %u, " + "reference tag check error: %u\n", + smart->e2e_err_cnt.key, + smart->e2e_err_cnt.norm, + le16_to_cpu(smart->e2e_err_cnt.wear_level.min), + le16_to_cpu(smart->e2e_err_cnt.wear_level.max), + le16_to_cpu(smart->e2e_err_cnt.wear_level.avg)); + printf("crc_error_count : %03d %3d%% %"PRIu64"\n", + smart->crc_err_cnt.key, + smart->crc_err_cnt.norm, + int48_to_long(smart->crc_err_cnt.raw)); + printf("nand_bytes_written : %03d %3d%% sectors: %"PRIu64"\n", + smart->nand_bytes_written.key, + smart->nand_bytes_written.norm, + int48_to_long(smart->nand_bytes_written.raw)); + printf("host_bytes_written : %3d %3d%% sectors: %"PRIu64"\n", + smart->host_bytes_written.key, + smart->host_bytes_written.norm, + int48_to_long(smart->host_bytes_written.raw)); + printf("reallocated_sector_count : %03d %3d%% %"PRIu64"\n", + smart->reallocated_sector_count.key, + smart->reallocated_sector_count.norm, + int48_to_long(smart->reallocated_sector_count.raw)); + printf("uncorrectable_sector_count : %03d %3d%% %"PRIu64"\n", + smart->uncorrectable_sector_count.key, + smart->uncorrectable_sector_count.norm, + int48_to_long(smart->uncorrectable_sector_count.raw)); + printf("NAND_ECC_detection_count : %03d %3d%% %"PRIu64"\n", + smart->NAND_ECC_Detection_Count.key, + smart->NAND_ECC_Detection_Count.norm, + int48_to_long(smart->NAND_ECC_Detection_Count.raw)); + printf("NAND_ECC_correction_count : %03d %3d%% %"PRIu64"\n", + smart->NAND_ECC_Correction_Count.key, + smart->NAND_ECC_Correction_Count.norm, + int48_to_long(smart->NAND_ECC_Correction_Count.raw)); + printf("GC_count : %03d %3d%% %"PRIu64"\n", + smart->GC_Count.key, + smart->GC_Count.norm, + int48_to_long(smart->GC_Count.raw)); + printf("DRAM_UECC_detection_count : %03d %3d%% 1-Bit Err: %u, 2-Bit Err: %u\n", + smart->DRAM_UECC_Detection_Count.key, + smart->DRAM_UECC_Detection_Count.norm, + le16_to_cpu(smart->DRAM_UECC_Detection_Count.wear_level.max), + le16_to_cpu(smart->DRAM_UECC_Detection_Count.wear_level.avg)); + printf("SRAM_UECC_Detection_Count : %03d %3d%% " + "parity error detected: %u, " + "ecc error detection: %u, " + "axi data parity errors: %u\n", + smart->SRAM_UECC_Detection_Count.key, + smart->SRAM_UECC_Detection_Count.norm, + le16_to_cpu(smart->SRAM_UECC_Detection_Count.wear_level.min), + le16_to_cpu(smart->SRAM_UECC_Detection_Count.wear_level.max), + le16_to_cpu(smart->SRAM_UECC_Detection_Count.wear_level.avg)); + printf("raid_recovery_fail_count : %03d %3d%% %"PRIu64"\n", + smart->Raid_Recovery_Fail_Count.key, + smart->Raid_Recovery_Fail_Count.norm, + int48_to_long(smart->Raid_Recovery_Fail_Count.raw)); + printf("Inflight_Command : %03d %3d%% " + "Read Cmd: %u, Write Cmd: %u, Admin Cmd: %u\n", + smart->Inflight_Command.key, + smart->Inflight_Command.norm, + le16_to_cpu(smart->Inflight_Command.wear_level.min), + le16_to_cpu(smart->Inflight_Command.wear_level.max), + le16_to_cpu(smart->Inflight_Command.wear_level.avg)); + printf("internal_end_to_end_dect_count : %03d %3d%% " + "read hcrc: %u, write hcrc: %u, reserved: %u\n", + smart->Internal_End_to_End_Dect_Count.key, + 100, + le16_to_cpu(smart->Internal_End_to_End_Dect_Count.wear_level.min), + le16_to_cpu(smart->Internal_End_to_End_Dect_Count.wear_level.max), + le16_to_cpu(smart->Internal_End_to_End_Dect_Count.wear_level.avg)); + printf("die_fail_count : %03d %3d%% %"PRIu64"\n", + smart->die_fail_count.key, + smart->die_fail_count.norm, + int48_to_long(smart->die_fail_count.raw)); + printf("wear_leveling_exec_count : %03d %3d%% %"PRIu64"\n", + smart->wear_leveling_exec_count.key, + smart->wear_leveling_exec_count.norm, + int48_to_long(smart->wear_leveling_exec_count.raw)); + printf("read_disturb_count : %03d %3d%% %"PRIu64"\n", + smart->read_disturb_count.key, + smart->read_disturb_count.norm, + int48_to_long(smart->read_disturb_count.raw)); + printf("data_retention_count : %03d %3d%% %"PRIu64"\n", + smart->data_retention_count.key, + smart->data_retention_count.norm, + int48_to_long(smart->data_retention_count.raw)); +} + +static +int ssstc_get_add_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + + const char *desc = + "Get SSSTC vendor specific additional smart log\n" + "(optionally, for the specified namespace), and show it."; + const char *namespace = "(optional) desired namespace"; + const char *raw = "Dump output in binary format"; + const char *json = "Dump output in json format"; + + struct nvme_additional_smart_log smart_log_add; + struct nvme_dev *dev; + int err; + + struct config { + __u32 namespace_id; + bool raw_binary; + bool json; + }; + + struct config cfg = { + .namespace_id = NVME_NSID_ALL, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_FLAG("json", 'j', &cfg.json, json), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + err = nvme_get_log_simple(dev_fd(dev), 0xca, sizeof(smart_log_add), + &smart_log_add); + if (!err) { + if (cfg.json) + show_ssstc_add_smart_log_jsn(&smart_log_add, cfg.namespace_id, + dev->name); + else if (!cfg.raw_binary) + show_ssstc_add_smart_log(&smart_log_add, cfg.namespace_id, + dev->name); + else + d_raw((unsigned char *)&smart_log_add, sizeof(smart_log_add)); + } else if (err > 0) { + nvme_show_status(err); + } + dev_close(dev); + return err; + +} diff --git a/plugins/ssstc/ssstc-nvme.h b/plugins/ssstc/ssstc-nvme.h new file mode 100644 index 0000000..e34fa50 --- /dev/null +++ b/plugins/ssstc/ssstc-nvme.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/ssstc/ssstc-nvme + +#if !defined(SSSTC_NVME) || defined(CMD_HEADER_MULTI_READ) +#define SSSTC_NVME + +#include "cmd.h" +PLUGIN(NAME("ssstc", "SSSTC vendor specific extensions", NVME_VERSION), + COMMAND_LIST( + ENTRY("smart-log-add", "Retrieve ssstc SMART Log, show it", ssstc_get_add_smart_log) + ) +); +#endif + +#include "define_cmd.h" diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c index 0ba4b15..ad9938d 100644 --- a/plugins/virtium/virtium-nvme.c +++ b/plugins/virtium/virtium-nvme.c @@ -32,14 +32,14 @@ static char vt_default_log_file_name[256]; struct vtview_log_header { char path[256]; char test_name[256]; - long time_stamp; + time_t time_stamp; struct nvme_id_ctrl raw_ctrl; struct nvme_firmware_slot raw_fw; }; struct vtview_smart_log_entry { char path[256]; - long time_stamp; + time_t time_stamp; struct nvme_id_ns raw_ns; struct nvme_id_ctrl raw_ctrl; struct nvme_smart_log raw_smart; diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 8cbcf2e..fa4157d 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -681,44 +681,6 @@ struct __packed wdc_nvme_ext_smart_log { __u8 ext_smart_lpg[16]; /* 496 Log page GUID */ }; -enum { - SCAO_PMUW = 0, /* Physical media units written */ - SCAO_PMUR = 16, /* Physical media units read */ - SCAO_BUNBR = 32, /* Bad user nand blocks raw */ - SCAO_BUNBN = 38, /* Bad user nand blocks normalized */ - SCAO_BSNBR = 40, /* Bad system nand blocks raw */ - SCAO_BSNBN = 46, /* Bad system nand blocks normalized */ - SCAO_XRC = 48, /* XOR recovery count */ - SCAO_UREC = 56, /* Uncorrectable read error count */ - SCAO_SEEC = 64, /* Soft ecc error count */ - SCAO_EECE = 72, /* End to end corrected errors */ - SCAO_EEDC = 76, /* End to end detected errors */ - SCAO_SDPU = 80, /* System data percent used */ - SCAO_RFSC = 81, /* Refresh counts */ - SCAO_MXUDEC = 88, /* Max User data erase counts */ - SCAO_MNUDEC = 92, /* Min User data erase counts */ - SCAO_NTTE = 96, /* Number of Thermal throttling events */ - SCAO_CTS = 97, /* Current throttling status */ - SCAO_EVF = 98, /* Errata Version Field */ - SCAO_PVF = 99, /* Point Version Field */ - SCAO_MIVF = 101, /* Minor Version Field */ - SCAO_MAVF = 103, /* Major Version Field */ - SCAO_PCEC = 104, /* PCIe correctable error count */ - SCAO_ICS = 112, /* Incomplete shutdowns */ - SCAO_PFB = 120, /* Percent free blocks */ - SCAO_CPH = 128, /* Capacitor health */ - SCAO_NEV = 130, /* NVMe Errata Version */ - SCAO_UIO = 136, /* Unaligned I/O */ - SCAO_SVN = 144, /* Security Version Number */ - SCAO_NUSE = 152, /* NUSE - Namespace utilization */ - SCAO_PSC = 160, /* PLP start count */ - SCAO_EEST = 176, /* Endurance estimate */ - SCAO_PLRC = 192, /* PCIe Link Retraining Count */ - SCAO_PSCC = 200, /* Power State Change Count */ - SCAO_LPV = 494, /* Log page version */ - SCAO_LPG = 496, /* Log page GUID */ -}; - struct ocp_bad_nand_block_count { __u64 raw : 48; __u16 normalized : 16; @@ -766,8 +728,9 @@ struct ocp_cloud_smart_log { __u8 percent_free_blocks; __u8 rsvd121[7]; __u16 capacitor_health; - __u8 nvme_errata_ver; - __u8 rsvd131[5]; + __u8 nvme_base_errata_ver; + __u8 nvme_cmd_set_errata_ver; + __u8 rsvd132[4]; __u64 unaligned_io; __u64 security_version_number; __u64 total_nuse; @@ -775,7 +738,8 @@ struct ocp_cloud_smart_log { __u8 endurance_estimate[16]; __u64 pcie_link_retraining_cnt; __u64 power_state_change_cnt; - __u8 rsvd208[286]; + char lowest_permitted_fw_rev[8]; + __u8 rsvd216[278]; __u16 log_page_version; __u8 log_page_guid[16]; }; @@ -1004,6 +968,13 @@ static int wdc_enc_submit_move_data(struct nvme_dev *dev, char *cmd, int len, in static bool get_dev_mgment_cbs_data(nvme_root_t r, struct nvme_dev *dev, __u8 log_id, void **cbs_data); static __u32 wdc_get_fw_cust_id(nvme_root_t r, struct nvme_dev *dev); +static int wdc_print_c0_cloud_attr_log(void *data, + int fmt, + struct nvme_dev *dev); +static int wdc_print_c0_eol_log(void *data, int fmt); +static void wdc_show_cloud_smart_log_normal(struct ocp_cloud_smart_log *log, + struct nvme_dev *dev); +static void wdc_show_cloud_smart_log_json(struct ocp_cloud_smart_log *log); /* Drive log data size */ struct wdc_log_size { @@ -1148,14 +1119,14 @@ struct __packed wdc_bd_ca_log_format { __u8 raw_value[8]; }; -#define LATENCY_LOG_BUCKET_READ 3 -#define LATENCY_LOG_BUCKET_WRITE 2 -#define LATENCY_LOG_BUCKET_TRIM 1 -#define LATENCY_LOG_BUCKET_RESERVED 0 +#define WDC_LATENCY_LOG_BUCKET_READ 3 +#define WDC_LATENCY_LOG_BUCKET_WRITE 2 +#define WDC_LATENCY_LOG_BUCKET_TRIM 1 +#define WDC_LATENCY_LOG_BUCKET_RESERVED 0 -#define LATENCY_LOG_MEASURED_LAT_READ 2 -#define LATENCY_LOG_MEASURED_LAT_WRITE 1 -#define LATENCY_LOG_MEASURED_LAT_TRIM 0 +#define WDC_LATENCY_LOG_MEASURED_LAT_READ 2 +#define WDC_LATENCY_LOG_MEASURED_LAT_WRITE 1 +#define WDC_LATENCY_LOG_MEASURED_LAT_TRIM 0 struct __packed wdc_ssd_latency_monitor_log { __u8 feature_status; /* 0x00 */ @@ -1180,8 +1151,9 @@ struct __packed wdc_ssd_latency_monitor_log { __le64 static_latency_timestamp[4][3]; /* 0x130 - 0x18F */ __le16 static_measured_latency[4][3]; /* 0x190 - 0x1A7 */ __le16 static_latency_stamp_units; /* 0x1A8 */ - __u8 rsvd4[0x16]; /* 0x1AA */ + __u8 rsvd4[10]; /* 0x1AA */ + __u8 debug_telemetry_log_size[12]; /* 0x1B4 */ __le16 debug_log_trigger_enable; /* 0x1C0 */ __le16 debug_log_measured_latency; /* 0x1C2 */ __le64 debug_log_latency_stamp; /* 0x1C4 */ @@ -1249,25 +1221,29 @@ struct __packed wdc_ssd_d0_smart_log { #define WDC_OCP_C1_GUID_LENGTH 16 #define WDC_ERROR_REC_LOG_BUF_LEN 512 #define WDC_ERROR_REC_LOG_ID 0xC1 -#define WDC_ERROR_REC_LOG_VERSION1 0001 -#define WDC_ERROR_REC_LOG_VERSION2 0002 struct __packed wdc_ocp_c1_error_recovery_log { - __le16 panic_reset_wait_time; /* 000 - Panic Reset Wait Time */ - __u8 panic_reset_action; /* 002 - Panic Reset Action */ - __u8 dev_recovery_action1; /* 003 - Device Recovery Action 1 */ - __le64 panic_id; /* 004 - Panic ID */ - __le32 dev_capabilities; /* 012 - Device Capabilities */ - __u8 vs_recovery_opc; /* 016 - Vendor Specific Recovery Opcode */ - __u8 rsvd1[3]; /* 017 - 3 Reserved Bytes */ - __le32 vs_cmd_cdw12; /* 020 - Vendor Specific Command CDW12 */ - __le32 vs_cmd_cdw13; /* 024 - Vendor Specific Command CDW13 */ - __u8 vs_cmd_to; /* 028 - Vendor Specific Command Timeout V2 */ - __u8 dev_recovery_action2; /* 029 - Device Recovery Action 2 V2 */ - __u8 dev_recovery_action2_to; /* 030 - Device Recovery Action 2 Timeout V2 */ - __u8 rsvd2[463]; /* 031 - 463 Reserved Bytes */ - __le16 log_page_version; /* 494 - Log Page Version */ - __u8 log_page_guid[WDC_OCP_C1_GUID_LENGTH]; /* 496 - Log Page GUID */ + __le16 panic_reset_wait_time; /* 000 - Panic Reset Wait Time */ + __u8 panic_reset_action; /* 002 - Panic Reset Action */ + __u8 dev_recovery_action1; /* 003 - Device Recovery Action 1 */ + __le64 panic_id; /* 004 - Panic ID */ + __le32 dev_capabilities; /* 012 - Device Capabilities */ + __u8 vs_recovery_opc; /* 016 - Vendor Specific Recovery Opcode */ + __u8 rsvd1[3]; /* 017 - 3 Reserved Bytes */ + __le32 vs_cmd_cdw12; /* 020 - Vendor Specific Command CDW12 */ + __le32 vs_cmd_cdw13; /* 024 - Vendor Specific Command CDW13 */ + __u8 vs_cmd_to; /* 028 - Vendor Specific Command Timeout V2 */ + __u8 dev_recovery_action2; /* 029 - Device Recovery Action 2 V2 */ + __u8 dev_recovery_action2_to; /* 030 - Device Recovery Action 2 Timeout V2 */ + __u8 panic_count; /* 031 - Number of panics encountered */ + __le64 prev_panic_ids[4]; /* 032 - 063 Previous Panic ID's */ + __u8 rsvd2[430]; /* 064 - 493 Reserved Bytes */ + /* 430 reserved bytes aligns with the rest */ + /* of the data structure. The size of 463 */ + /* bytes mentioned in the OCP spec */ + /* (version 2.5) would not fit here. */ + __le16 log_page_version; /* 494 - Log Page Version */ + __u8 log_page_guid[WDC_OCP_C1_GUID_LENGTH]; /* 496 - Log Page GUID */ }; static __u8 wdc_ocp_c1_guid[WDC_OCP_C1_GUID_LENGTH] = { 0x44, 0xD9, 0x31, 0x21, 0xFE, 0x30, 0x34, 0xAE, @@ -1383,6 +1359,11 @@ struct __packed wdc_fw_act_history_log_format_c2 { __u8 log_page_guid[WDC_C2_GUID_LENGTH]; }; +static __u8 ocp_C2_guid[WDC_C2_GUID_LENGTH] = { + 0x6D, 0x79, 0x9A, 0x76, 0xB4, 0xDA, 0xF6, 0xA3, + 0xE2, 0x4D, 0xB2, 0x8A, 0xAC, 0xF3, 0x1C, 0xD1 +}; + #define WDC_OCP_C4_GUID_LENGTH 16 #define WDC_DEV_CAP_LOG_BUF_LEN 4096 #define WDC_DEV_CAP_LOG_ID 0xC4 @@ -1726,7 +1707,6 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) case WDC_NVME_VID_2: switch (read_device_id) { case WDC_NVME_SN630_DEV_ID: - fallthrough; case WDC_NVME_SN630_DEV_ID_1: capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | @@ -1743,19 +1723,12 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) break; case WDC_NVME_SN640_DEV_ID: - fallthrough; case WDC_NVME_SN640_DEV_ID_1: - fallthrough; case WDC_NVME_SN640_DEV_ID_2: - fallthrough; case WDC_NVME_SN640_DEV_ID_3: - fallthrough; case WDC_NVME_SN560_DEV_ID_1: - fallthrough; case WDC_NVME_SN560_DEV_ID_2: - fallthrough; case WDC_NVME_SN560_DEV_ID_3: - fallthrough; case WDC_NVME_SN660_DEV_ID: /* verify the 0xC0 log page is supported */ if (wdc_nvme_check_supported_log_page(r, dev, @@ -1816,9 +1789,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) break; case WDC_NVME_SN840_DEV_ID: - fallthrough; case WDC_NVME_SN840_DEV_ID_1: - fallthrough; case WDC_NVME_SN860_DEV_ID: /* verify the 0xC0 log page is supported */ if (wdc_nvme_check_supported_log_page(r, dev, @@ -1826,7 +1797,6 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE; fallthrough; case WDC_NVME_ZN540_DEV_ID: - fallthrough; case WDC_NVME_SN540_DEV_ID: capabilities |= (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | @@ -1847,17 +1817,11 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) break; case WDC_NVME_SN650_DEV_ID: - fallthrough; case WDC_NVME_SN650_DEV_ID_1: - fallthrough; case WDC_NVME_SN650_DEV_ID_2: - fallthrough; case WDC_NVME_SN650_DEV_ID_3: - fallthrough; case WDC_NVME_SN650_DEV_ID_4: - fallthrough; case WDC_NVME_SN655_DEV_ID: - fallthrough; case WDC_NVME_SN550_DEV_ID: /* verify the 0xC0 log page is supported */ if (wdc_nvme_check_supported_log_page(r, dev, @@ -1907,7 +1871,6 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) break; case WDC_NVME_SN861_DEV_ID: - fallthrough; case WDC_NVME_SN861_DEV_ID_1: capabilities |= (WDC_DRIVE_CAP_C0_LOG_PAGE | WDC_DRIVE_CAP_C3_LOG_PAGE | @@ -1921,6 +1884,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) WDC_DRIVE_CAP_INFO | WDC_DRIVE_CAP_CLOUD_SSD_VERSION | WDC_DRIVE_CAP_LOG_PAGE_DIR | + WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_SET_LATENCY_MONITOR); break; @@ -1936,11 +1900,8 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) break; case WDC_NVME_SN520_DEV_ID: - fallthrough; case WDC_NVME_SN520_DEV_ID_1: - fallthrough; case WDC_NVME_SN520_DEV_ID_2: - fallthrough; case WDC_NVME_SN810_DEV_ID: capabilities = WDC_DRIVE_CAP_DUI_DATA; break; @@ -2010,19 +1971,14 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) case WDC_NVME_SN8000S_DEV_ID: fallthrough; case WDC_NVME_SN740_DEV_ID: - fallthrough; case WDC_NVME_SN740_DEV_ID_1: - fallthrough; case WDC_NVME_SN740_DEV_ID_2: - fallthrough; case WDC_NVME_SN740_DEV_ID_3: - fallthrough; case WDC_NVME_SN340_DEV_ID: capabilities = WDC_DRIVE_CAP_DUI; break; case WDC_NVME_ZN350_DEV_ID: - fallthrough; case WDC_NVME_ZN350_DEV_ID_1: capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | WDC_DRIVE_CAP_C0_LOG_PAGE | @@ -2450,23 +2406,32 @@ static bool get_dev_mgment_cbs_data(nvme_root_t r, struct nvme_dev *dev, uuid_index = index + 1; } - if (!uuid_index && needs_c2_log_page_check(device_id)) { - /* In certain devices that don't support UUID lists, there are multiple - * definitions of the C2 logpage. In those cases, the code - * needs to try two UUID indexes and use an identification algorithm - * to determine which is returning the correct log page data. - */ - uuid_ix = 1; - } + if (uuid_present) { + /* use the uuid index found above */ + found = get_dev_mgmt_log_page_lid_data(dev, cbs_data, lid, log_id, uuid_index); + } else if (device_id == WDC_NVME_ZN350_DEV_ID || device_id == WDC_NVME_ZN350_DEV_ID_1) { + uuid_index = 0; + found = get_dev_mgmt_log_page_lid_data(dev, cbs_data, lid, log_id, uuid_index); + } else { + if (!uuid_index && needs_c2_log_page_check(device_id)) { + /* In certain devices that don't support UUID lists, there are multiple + * definitions of the C2 logpage. In those cases, the code + * needs to try two UUID indexes and use an identification algorithm + * to determine which is returning the correct log page data. + */ + uuid_ix = 1; + } - found = get_dev_mgmt_log_page_lid_data(dev, cbs_data, lid, log_id, uuid_ix); + found = get_dev_mgmt_log_page_lid_data(dev, cbs_data, lid, log_id, uuid_ix); - if (!found) { - /* not found with uuid = 1 try with uuid = 0 */ - uuid_ix = 0; - fprintf(stderr, "Not found, requesting log page with uuid_index %d\n", uuid_index); + if (!found) { + /* not found with uuid = 1 try with uuid = 0 */ + uuid_ix = 0; + fprintf(stderr, "Not found, requesting log page with uuid_index %d\n", + uuid_index); - found = get_dev_mgmt_log_page_lid_data(dev, cbs_data, lid, log_id, uuid_ix); + found = get_dev_mgmt_log_page_lid_data(dev, cbs_data, lid, log_id, uuid_ix); + } } return found; @@ -3722,7 +3687,7 @@ free_buf: static int dump_internal_logs(struct nvme_dev *dev, char *dir_name, int verbose) { - char file_path[128]; + char file_path[PATH_MAX]; void *telemetry_log; const size_t bs = 512; struct nvme_telemetry_log *hdr; @@ -4692,20 +4657,30 @@ static int wdc_print_latency_monitor_log_normal(struct nvme_dev *dev, printf(" Active Latency Minimum Window %d ms\n", 100*log_data->active_latency_min_window); printf(" Active Latency Stamp Units %d\n", le16_to_cpu(log_data->active_latency_stamp_units)); printf(" Static Latency Stamp Units %d\n", le16_to_cpu(log_data->static_latency_stamp_units)); - printf(" Debug Log Trigger Enable %d\n", le16_to_cpu(log_data->debug_log_trigger_enable)); + if (le16_to_cpu(log_data->log_page_version) >= 4) + printf(" Debug Telemetry Log Size %"PRIu64"\n", + le64_to_cpu(*(uint64_t *)log_data->debug_telemetry_log_size)); + printf(" Debug Log Trigger Enable %d\n", + le16_to_cpu(log_data->debug_log_trigger_enable)); + printf(" Log Page Version %d\n", + le16_to_cpu(log_data->log_page_version)); + printf(" Log page GUID 0x"); + for (j = 0; j < WDC_C3_GUID_LENGTH; j++) + printf("%x", log_data->log_page_guid[j]); + printf("\n"); printf(" Read Write Deallocate/Trim\n"); for (i = 0; i <= 3; i++) printf(" Active Bucket Counter: Bucket %d %27d %27d %27d\n", - i, le32_to_cpu(log_data->active_bucket_counter[i][LATENCY_LOG_BUCKET_READ]), - le32_to_cpu(log_data->active_bucket_counter[i][LATENCY_LOG_BUCKET_WRITE]), - le32_to_cpu(log_data->active_bucket_counter[i][LATENCY_LOG_BUCKET_TRIM])); + i, le32_to_cpu(log_data->active_bucket_counter[i][WDC_LATENCY_LOG_BUCKET_READ]), + le32_to_cpu(log_data->active_bucket_counter[i][WDC_LATENCY_LOG_BUCKET_WRITE]), + le32_to_cpu(log_data->active_bucket_counter[i][WDC_LATENCY_LOG_BUCKET_TRIM])); for (i = 3; i >= 0; i--) printf(" Active Measured Latency: Bucket %d %27d ms %27d ms %27d ms\n", - 3-i, le16_to_cpu(log_data->active_measured_latency[i][LATENCY_LOG_MEASURED_LAT_READ]), - le16_to_cpu(log_data->active_measured_latency[i][LATENCY_LOG_MEASURED_LAT_WRITE]), - le16_to_cpu(log_data->active_measured_latency[i][LATENCY_LOG_MEASURED_LAT_TRIM])); + 3-i, le16_to_cpu(log_data->active_measured_latency[i][WDC_LATENCY_LOG_MEASURED_LAT_READ]), + le16_to_cpu(log_data->active_measured_latency[i][WDC_LATENCY_LOG_MEASURED_LAT_WRITE]), + le16_to_cpu(log_data->active_measured_latency[i][WDC_LATENCY_LOG_MEASURED_LAT_TRIM])); for (i = 3; i >= 0; i--) { printf(" Active Latency Time Stamp: Bucket %d ", 3-i); @@ -4722,15 +4697,15 @@ static int wdc_print_latency_monitor_log_normal(struct nvme_dev *dev, for (i = 0; i <= 3; i++) printf(" Static Bucket Counter: Bucket %d %27d %27d %27d\n", - i, le32_to_cpu(log_data->static_bucket_counter[i][LATENCY_LOG_BUCKET_READ]), - le32_to_cpu(log_data->static_bucket_counter[i][LATENCY_LOG_BUCKET_WRITE]), - le32_to_cpu(log_data->static_bucket_counter[i][LATENCY_LOG_BUCKET_TRIM])); + i, le32_to_cpu(log_data->static_bucket_counter[i][WDC_LATENCY_LOG_BUCKET_READ]), + le32_to_cpu(log_data->static_bucket_counter[i][WDC_LATENCY_LOG_BUCKET_WRITE]), + le32_to_cpu(log_data->static_bucket_counter[i][WDC_LATENCY_LOG_BUCKET_TRIM])); for (i = 3; i >= 0; i--) printf(" Static Measured Latency: Bucket %d %27d ms %27d ms %27d ms\n", - 3-i, le16_to_cpu(log_data->static_measured_latency[i][LATENCY_LOG_MEASURED_LAT_READ]), - le16_to_cpu(log_data->static_measured_latency[i][LATENCY_LOG_MEASURED_LAT_WRITE]), - le16_to_cpu(log_data->static_measured_latency[i][LATENCY_LOG_MEASURED_LAT_TRIM])); + 3-i, le16_to_cpu(log_data->static_measured_latency[i][WDC_LATENCY_LOG_MEASURED_LAT_READ]), + le16_to_cpu(log_data->static_measured_latency[i][WDC_LATENCY_LOG_MEASURED_LAT_WRITE]), + le16_to_cpu(log_data->static_measured_latency[i][WDC_LATENCY_LOG_MEASURED_LAT_TRIM])); for (i = 3; i >= 0; i--) { printf(" Static Latency Time Stamp: Bucket %d ", 3-i); @@ -4766,7 +4741,22 @@ static void wdc_print_latency_monitor_log_json(struct wdc_ssd_latency_monitor_lo json_object_add_value_int(root, "Active Lantency Minimum Window", 100*log_data->active_latency_min_window); json_object_add_value_int(root, "Active Latency Stamp Units", le16_to_cpu(log_data->active_latency_stamp_units)); json_object_add_value_int(root, "Static Latency Stamp Units", le16_to_cpu(log_data->static_latency_stamp_units)); - json_object_add_value_int(root, "Debug Log Trigger Enable", le16_to_cpu(log_data->debug_log_trigger_enable)); + if (le16_to_cpu(log_data->log_page_version) >= 4) { + json_object_add_value_int(root, "Debug Telemetry Log Size", + le64_to_cpu(*(uint64_t *)log_data->debug_telemetry_log_size)); + } + json_object_add_value_int(root, "Debug Log Trigger Enable", + le16_to_cpu(log_data->debug_log_trigger_enable)); + json_object_add_value_int(root, "Log Page Version", + le16_to_cpu(log_data->log_page_version)); + + char guid[40]; + + memset((void *)guid, 0, 40); + sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]), + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0])); + json_object_add_value_string(root, "Log page GUID", guid); for (i = 0; i <= 3; i++) { for (j = 2; j >= 0; j--) { @@ -4825,13 +4815,20 @@ static void wdc_print_error_rec_log_normal(struct wdc_ocp_c1_error_recovery_log printf(" Vendor Specific Recovery Opcode : 0x%x\n", log_data->vs_recovery_opc); printf(" Vendor Specific Command CDW12 : 0x%x\n", le32_to_cpu(log_data->vs_cmd_cdw12)); printf(" Vendor Specific Command CDW13 : 0x%x\n", le32_to_cpu(log_data->vs_cmd_cdw13)); - if (le16_to_cpu(log_data->log_page_version) == WDC_ERROR_REC_LOG_VERSION2) { + if (le16_to_cpu(log_data->log_page_version) >= 2) { printf(" Vendor Specific Command Timeout : 0x%x\n", log_data->vs_cmd_to); printf(" Device Recovery Action 2 : 0x%x\n", log_data->dev_recovery_action2); printf(" Device Recovery Action 2 Timeout : 0x%x\n", log_data->dev_recovery_action2_to); } - printf(" Log Page Version : 0x%x\n", le16_to_cpu(log_data->log_page_version)); - printf(" Log page GUID : 0x"); + if (le16_to_cpu(log_data->log_page_version) >= 3) { + printf(" Panic Count : 0x%x\n", log_data->panic_count); + for (j = 0; j < 4; j++) + printf(" Previous Panic ID N-%d : 0x%"PRIx64"\n", + j+1, le64_to_cpu(log_data->prev_panic_ids[j])); + } + printf(" Log Page Version : 0x%x\n", + le16_to_cpu(log_data->log_page_version)); + printf(" Log page GUID : 0x"); for (j = 0; j < WDC_OCP_C1_GUID_LENGTH; j++) printf("%x", log_data->log_page_guid[j]); printf("\n"); @@ -4839,6 +4836,8 @@ static void wdc_print_error_rec_log_normal(struct wdc_ocp_c1_error_recovery_log static void wdc_print_error_rec_log_json(struct wdc_ocp_c1_error_recovery_log *log_data) { + int j; + char buf[128]; struct json_object *root = json_create_object(); json_object_add_value_int(root, "Panic Reset Wait Time", le16_to_cpu(log_data->panic_reset_wait_time)); @@ -4849,12 +4848,21 @@ static void wdc_print_error_rec_log_json(struct wdc_ocp_c1_error_recovery_log *l json_object_add_value_int(root, "Vendor Specific Recovery Opcode", log_data->vs_recovery_opc); json_object_add_value_int(root, "Vendor Specific Command CDW12", le32_to_cpu(log_data->vs_cmd_cdw12)); json_object_add_value_int(root, "Vendor Specific Command CDW13", le32_to_cpu(log_data->vs_cmd_cdw13)); - if (le16_to_cpu(log_data->log_page_version) == WDC_ERROR_REC_LOG_VERSION2) { + if (le16_to_cpu(log_data->log_page_version) >= 2) { json_object_add_value_int(root, "Vendor Specific Command Timeout", log_data->vs_cmd_to); json_object_add_value_int(root, "Device Recovery Action 2", log_data->dev_recovery_action2); json_object_add_value_int(root, "Device Recovery Action 2 Timeout", log_data->dev_recovery_action2_to); } - json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); + if (le16_to_cpu(log_data->log_page_version) >= 3) { + json_object_add_value_int(root, "Panic Count", log_data->panic_count); + for (j = 0; j < 4; j++) { + sprintf(buf, "Previous Panic ID N-%d", j+1); + json_object_add_value_int(root, buf, + le64_to_cpu(log_data->prev_panic_ids[j])); + } + } + json_object_add_value_int(root, "Log Page Version", + le16_to_cpu(log_data->log_page_version)); char guid[40]; @@ -5538,17 +5546,18 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, char previous_fw[9]; char new_fw[9]; char commit_action_bin[8]; - char time_str[11]; + char time_str[100]; __u16 oldestEntryIdx = 0, entryIdx = 0; + uint64_t timestamp; + __u64 timestamp_sec; char *null_fw = "--------"; - memset((void *)time_str, 0, 11); + memset((void *)time_str, '\0', 100); if (data[0] == WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { printf(" Firmware Activate History Log\n"); if (cust_id == WDC_CUSTOMER_ID_0x1005 || - vendor_id == WDC_NVME_SNDK_VID || - wdc_is_sn861(device_id)) { + vendor_id == WDC_NVME_SNDK_VID) { printf(" Power on Hour Power Cycle Previous New\n"); printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result\n"); printf(" ----- ----------------- ----------------- --------- --------- ----- ------ -------\n"); @@ -5589,48 +5598,33 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, memcpy(new_fw, null_fw, 8); printf("%5"PRIu16"", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[entryIdx].fw_act_hist_entries)); + + timestamp = (0x0000FFFFFFFFFFFF & + le64_to_cpu( + fw_act_history_entry->entry[entryIdx].timestamp)); + timestamp_sec = timestamp / 1000; if (cust_id == WDC_CUSTOMER_ID_0x1005) { printf(" "); memset((void *)time_str, 0, 9); - sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)/3600), - (int)((le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%3600)/60)), - (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%60))); + sprintf((char *)time_str, "%"PRIu32":%u:%u", + (__u32)(timestamp_sec/3600), + (__u8)(timestamp_sec%3600/60), + (__u8)(timestamp_sec%60)); printf("%s", time_str); printf(" "); } else if (vendor_id == WDC_NVME_SNDK_VID) { printf(" "); - uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)); memset((void *)time_str, 0, 9); - sprintf((char *)time_str, "%04d:%02d:%02d", (int)((timestamp/(3600*1000))%24), (int)((timestamp/(1000*60))%60), - (int)((timestamp/1000)%60)); + sprintf((char *)time_str, "%"PRIu32":%u:%u", + (__u32)((timestamp_sec/3600)%24), + (__u8)((timestamp_sec/60)%60), + (__u8)(timestamp_sec%60)); printf("%s", time_str); printf(" "); - } else if (wdc_is_sn861(device_id)) { - printf(" "); - char timestamp[20]; - __u64 hour; - __u8 min; - __u8 sec; - __u64 timestamp_sec; - - timestamp_sec = - le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp) - / 1000; - hour = timestamp_sec / 3600; - min = (timestamp_sec % 3600) / 60; - sec = timestamp_sec % 60; - - sprintf(timestamp, - "%"PRIu64":%02"PRIu8":%02"PRIu8, - (uint64_t)hour, min, sec); - printf("%-11s", timestamp); - printf(" "); } else { printf(" "); - uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)); - printf("%16"PRIu64"", timestamp); printf(" "); } @@ -5735,13 +5729,15 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, char new_fw[9]; char commit_action_bin[8]; char fail_str[32]; - char time_str[11]; + char time_str[100]; char ext_time_str[20]; + uint64_t timestamp; + __u64 timestamp_sec; memset((void *)previous_fw, 0, 9); memset((void *)new_fw, 0, 9); memset((void *)commit_action_bin, 0, 8); - memset((void *)time_str, 0, 11); + memset((void *)time_str, '\0', 100); memset((void *)ext_time_str, 0, 20); memset((void *)fail_str, 0, 11); char *null_fw = "--------"; @@ -5781,33 +5777,25 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, json_object_add_value_int(root, "Entry", le16_to_cpu(fw_act_history_entry->entry[entryIdx].fw_act_hist_entries)); + timestamp = (0x0000FFFFFFFFFFFF & + le64_to_cpu( + fw_act_history_entry->entry[entryIdx].timestamp)); + timestamp_sec = timestamp / 1000; if (cust_id == WDC_CUSTOMER_ID_0x1005) { - sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)/3600), - (int)((le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%3600)/60)), - (int)(le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp%60))); + sprintf((char *)time_str, "%"PRIu32":%u:%u", + (__u32)(timestamp_sec/3600), + (__u8)(timestamp_sec%3600/60), + (__u8)(timestamp_sec%60)); json_object_add_value_string(root, "Power on Hour", time_str); } else if (vendor_id == WDC_NVME_SNDK_VID) { - uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)); - - sprintf((char *)time_str, "%04d:%02d:%02d", (int)((timestamp/(3600*1000))%24), (int)((timestamp/(1000*60))%60), - (int)((timestamp/1000)%60)); + sprintf((char *)time_str, "%"PRIu32":%u:%u", + (__u32)((timestamp_sec/3600)%24), + (__u8)((timestamp_sec/60)%60), + (__u8)(timestamp_sec%60)); json_object_add_value_string(root, "Power on Hour", time_str); - } else if (wdc_is_sn861(device_id)) { - __u64 timestamp_sec = - le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp) - / 1000; - - sprintf((char *)ext_time_str, - "%"PRIu64":%02"PRIu8":%02"PRIu8, - (uint64_t)(__u64)(timestamp_sec/3600), - (__u8)((timestamp_sec%3600)/60), - (__u8)(timestamp_sec%60)); - json_object_add_value_string(root, "Power on Hour", ext_time_str); } else { - uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp)); - json_object_add_value_uint64(root, "Timestamp", timestamp); } @@ -5906,6 +5894,141 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, json_free_object(root); } +static int nvme_get_print_ocp_cloud_smart_log(struct nvme_dev *dev, + int uuid_index, + __u32 namespace_id, + int fmt) +{ + struct ocp_cloud_smart_log *log_ptr = NULL; + int ret, i; + __u32 length = WDC_NVME_SMART_CLOUD_ATTR_LEN; + int fd = dev_fd(dev); + + log_ptr = (struct ocp_cloud_smart_log *)malloc(sizeof(__u8) * length); + if (!log_ptr) { + fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); + return -1; + } + + if (namespace_id == NVME_NSID_ALL) { + ret = nvme_get_nsid(fd, &namespace_id); + if (ret < 0) + namespace_id = NVME_NSID_ALL; + } + + /* Get the 0xC0 log data */ + struct nvme_get_log_args args = { + .args_size = sizeof(args), + .fd = fd, + .lid = WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID, + .nsid = namespace_id, + .lpo = 0, + .lsp = NVME_LOG_LSP_NONE, + .lsi = 0, + .rae = false, + .uuidx = uuid_index, + .csi = NVME_CSI_NVM, + .ot = false, + .len = length, + .log = log_ptr, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + ret = nvme_get_log(&args); + + if (fmt == JSON) + nvme_show_status(ret); + + if (!ret) { + /* Verify GUID matches */ + for (i = 0; i < 16; i++) { + if (scao_guid[i] != log_ptr->log_page_guid[i]) { + fprintf(stderr, "ERROR: WDC: Unknown GUID in C0 Log Page data\n"); + int j; + + fprintf(stderr, "ERROR: WDC: Expected GUID: 0x"); + for (j = 0; j < 16; j++) + fprintf(stderr, "%x", scao_guid[j]); + fprintf(stderr, "\nERROR: WDC: Actual GUID: 0x"); + for (j = 0; j < 16; j++) + fprintf(stderr, "%x", log_ptr->log_page_guid[j]); + fprintf(stderr, "\n"); + + ret = -1; + break; + } + } + + if (!ret) + /* parse the data */ + wdc_print_c0_cloud_attr_log(log_ptr, fmt, dev); + } else { + fprintf(stderr, "ERROR: WDC: Unable to read C0 Log Page data\n"); + ret = -1; + } + + free(log_ptr); + return ret; +} + +static int nvme_get_print_c0_eol_log(struct nvme_dev *dev, + int uuid_index, + __u32 namespace_id, + int fmt) +{ + void *log_ptr = NULL; + int ret; + __u32 length = WDC_NVME_EOL_STATUS_LOG_LEN; + int fd = dev_fd(dev); + + log_ptr = (void *)malloc(sizeof(__u8) * length); + if (!log_ptr) { + fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); + return -1; + } + + if (namespace_id == NVME_NSID_ALL) { + ret = nvme_get_nsid(fd, &namespace_id); + if (ret < 0) + namespace_id = NVME_NSID_ALL; + } + + /* Get the 0xC0 log data */ + struct nvme_get_log_args args = { + .args_size = sizeof(args), + .fd = fd, + .lid = WDC_NVME_GET_EOL_STATUS_LOG_OPCODE, + .nsid = namespace_id, + .lpo = 0, + .lsp = NVME_LOG_LSP_NONE, + .lsi = 0, + .rae = false, + .uuidx = uuid_index, + .csi = NVME_CSI_NVM, + .ot = false, + .len = length, + .log = log_ptr, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + ret = nvme_get_log(&args); + + if (fmt == JSON) + nvme_show_status(ret); + + if (!ret) { + /* parse the data */ + wdc_print_c0_eol_log(log_ptr, fmt); + } else { + fprintf(stderr, "ERROR: WDC: Unable to read C0 Log Page data "); + fprintf(stderr, "with uuid index %d\n", uuid_index); + ret = -1; + } + + free(log_ptr); + return ret; +} + static int nvme_get_ext_smart_cloud_log(int fd, __u8 **data, int uuid_index, __u32 namespace_id) { int ret, i; @@ -6587,177 +6710,6 @@ static void wdc_print_ext_smart_cloud_log_json(void *data, int mask) json_free_object(root); } -static void wdc_print_smart_cloud_attr_C0_normal(void *data) -{ - __u8 *log_data = (__u8 *)data; - uint16_t smart_log_ver = 0; - - printf(" SMART Cloud Attributes :-\n"); - - printf(" Physical media units written : %s\n", - uint128_t_to_string(le128_to_cpu(&log_data[SCAO_PMUW]))); - printf(" Physical media units read : %s\n", - uint128_t_to_string(le128_to_cpu(&log_data[SCAO_PMUR]))); - printf(" Bad user nand blocks Raw : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF)); - printf(" Bad user nand blocks Normalized : %d\n", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN])); - printf(" Bad system nand blocks Raw : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF)); - printf(" Bad system nand blocks Normalized : %d\n", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN])); - printf(" XOR recovery count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC])); - printf(" Uncorrectable read error count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC])); - printf(" Soft ecc error count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC])); - printf(" End to end corrected errors : %"PRIu32"\n", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE])); - printf(" End to end detected errors : %"PRIu32"\n", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC])); - printf(" System data percent used : %d\n", (__u8)log_data[SCAO_SDPU]); - printf(" Refresh counts : %"PRIu64"\n", - (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC]) & 0x00FFFFFFFFFFFFFF)); - printf(" Max User data erase counts : %"PRIu32"\n", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC])); - printf(" Min User data erase counts : %"PRIu32"\n", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); - printf(" Number of Thermal throttling events : %d\n", (__u8)log_data[SCAO_NTTE]); - printf(" Current throttling status : 0x%x\n", (__u8)log_data[SCAO_CTS]); - printf(" PCIe correctable error count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); - printf(" Incomplete shutdowns : %"PRIu32"\n", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS])); - printf(" Percent free blocks : %d\n", (__u8)log_data[SCAO_PFB]); - printf(" Capacitor health : %"PRIu16"\n", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH])); - printf(" Unaligned I/O : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO])); - printf(" Security Version Number : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN])); - printf(" NUSE Namespace utilization : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE])); - printf(" PLP start count : %s\n", - uint128_t_to_string(le128_to_cpu(&log_data[SCAO_PSC]))); - printf(" Endurance estimate : %s\n", - uint128_t_to_string(le128_to_cpu(&log_data[SCAO_EEST]))); - smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]); - printf(" Log page version : %"PRIu16"\n", smart_log_ver); - printf(" Log page GUID : 0x"); - printf("%"PRIx64"%"PRIx64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG])); - if (smart_log_ver > 2) { - printf(" Errata Version Field : %d\n", - (__u8)log_data[SCAO_EVF]); - printf(" Point Version Field : %"PRIu16"\n", - (uint16_t)log_data[SCAO_PVF]); - printf(" Minor Version Field : %"PRIu16"\n", - (uint16_t)log_data[SCAO_MIVF]); - printf(" Major Version Field : %d\n", - (__u8)log_data[SCAO_MAVF]); - printf(" NVMe Errata Version : %d\n", - (__u8)log_data[SCAO_NEV]); - printf(" PCIe Link Retraining Count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC])); - } - if (smart_log_ver > 3) { - printf(" Power State Change Count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PSCC])); - } - printf("\n"); -} - -static void wdc_print_smart_cloud_attr_C0_json(void *data) -{ - __u8 *log_data = (__u8 *)data; - struct json_object *root = json_create_object(); - uint16_t smart_log_ver = 0; - - json_object_add_value_uint128(root, "Physical media units written", - le128_to_cpu(&log_data[SCAO_PMUW])); - json_object_add_value_uint128(root, "Physical media units read", - le128_to_cpu(&log_data[SCAO_PMUR])); - json_object_add_value_uint64(root, "Bad user nand blocks - Raw", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF)); - json_object_add_value_uint(root, "Bad user nand blocks - Normalized", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN])); - json_object_add_value_uint64(root, "Bad system nand blocks - Raw", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF)); - json_object_add_value_uint(root, "Bad system nand blocks - Normalized", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN])); - json_object_add_value_uint64(root, "XOR recovery count", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC])); - json_object_add_value_uint64(root, "Uncorrectable read error count", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC])); - json_object_add_value_uint64(root, "Soft ecc error count", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC])); - json_object_add_value_uint(root, "End to end corrected errors", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE])); - json_object_add_value_uint(root, "End to end detected errors", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC])); - json_object_add_value_uint(root, "System data percent used", - (__u8)log_data[SCAO_SDPU]); - json_object_add_value_uint64(root, "Refresh counts", - (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC]) & 0x00FFFFFFFFFFFFFF)); - json_object_add_value_uint(root, "Max User data erase counts", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC])); - json_object_add_value_uint(root, "Min User data erase counts", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); - json_object_add_value_uint(root, "Number of Thermal throttling events", - (__u8)log_data[SCAO_NTTE]); - json_object_add_value_uint(root, "Current throttling status", - (__u8)log_data[SCAO_CTS]); - json_object_add_value_uint64(root, "PCIe correctable error count", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); - json_object_add_value_uint(root, "Incomplete shutdowns", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS])); - json_object_add_value_uint(root, "Percent free blocks", - (__u8)log_data[SCAO_PFB]); - json_object_add_value_uint(root, "Capacitor health", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH])); - json_object_add_value_uint64(root, "Unaligned I/O", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO])); - json_object_add_value_uint64(root, "Security Version Number", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN])); - json_object_add_value_uint64(root, "NUSE - Namespace utilization", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE])); - json_object_add_value_uint128(root, "PLP start count", - le128_to_cpu(&log_data[SCAO_PSC])); - json_object_add_value_uint128(root, "Endurance estimate", - le128_to_cpu(&log_data[SCAO_EEST])); - smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]); - json_object_add_value_uint(root, "Log page version", smart_log_ver); - char guid[40]; - - memset((void *)guid, 0, 40); - sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG])); - json_object_add_value_string(root, "Log page GUID", guid); - if (smart_log_ver > 2) { - json_object_add_value_uint(root, "Errata Version Field", - (__u8)log_data[SCAO_EVF]); - json_object_add_value_uint(root, "Point Version Field", - (uint16_t)log_data[SCAO_PVF]); - json_object_add_value_uint(root, "Minor Version Field", - (uint16_t)log_data[SCAO_MIVF]); - json_object_add_value_uint(root, "Major Version Field", - (__u8)log_data[SCAO_MAVF]); - json_object_add_value_uint(root, "NVMe Errata Version", - (__u8)log_data[SCAO_NEV]); - json_object_add_value_uint64(root, "PCIe Link Retraining Count", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC])); - } - if (smart_log_ver > 3) { - json_object_add_value_uint64(root, "Power State Change Count", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PSCC])); - } - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); -} static void wdc_print_eol_c0_normal(void *data) { @@ -6825,18 +6777,26 @@ static int wdc_print_ext_smart_cloud_log(void *data, int fmt) return 0; } -static int wdc_print_c0_cloud_attr_log(void *data, int fmt) +static int wdc_print_c0_cloud_attr_log(void *data, + int fmt, + struct nvme_dev *dev) { + struct ocp_cloud_smart_log *log = (struct ocp_cloud_smart_log *)data; + if (!data) { fprintf(stderr, "ERROR: WDC: Invalid buffer to read 0xC0 log\n"); return -1; } + switch (fmt) { + case BINARY: + d_raw((unsigned char *)log, sizeof(struct ocp_cloud_smart_log)); + break; case NORMAL: - wdc_print_smart_cloud_attr_C0_normal(data); + wdc_show_cloud_smart_log_normal(log, dev); break; case JSON: - wdc_print_smart_cloud_attr_C0_json(data); + wdc_show_cloud_smart_log_json(log); break; } return 0; @@ -6849,6 +6809,9 @@ static int wdc_print_c0_eol_log(void *data, int fmt) return -1; } switch (fmt) { + case BINARY: + d_raw((unsigned char *)data, WDC_NVME_EOL_STATUS_LOG_LEN); + break; case NORMAL: wdc_print_eol_c0_normal(data); break; @@ -6863,113 +6826,17 @@ static int wdc_get_c0_log_page_sn_customer_id_0x100X(struct nvme_dev *dev, int u char *format, __u32 namespace_id, int fmt) { int ret; - __u8 *data; - int i; if (!uuid_index) { - data = (__u8 *)malloc(sizeof(__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN); - if (!data) { - fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); - return -1; - } - - if (namespace_id == NVME_NSID_ALL) { - ret = nvme_get_nsid(dev_fd(dev), &namespace_id); - if (ret < 0) - namespace_id = NVME_NSID_ALL; - } - - /* Get the 0xC0 log data */ - struct nvme_get_log_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .lid = WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID, - .nsid = namespace_id, - .lpo = 0, - .lsp = NVME_LOG_LSP_NONE, - .lsi = 0, - .rae = false, - .uuidx = uuid_index, - .csi = NVME_CSI_NVM, - .ot = false, - .len = WDC_NVME_SMART_CLOUD_ATTR_LEN, - .log = data, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - }; - ret = nvme_get_log(&args); - - if (strcmp(format, "json")) - nvme_show_status(ret); - - if (!ret) { - /* Verify GUID matches */ - for (i = 0; i < 16; i++) { - if (scao_guid[i] != data[SCAO_LPG + i]) { - fprintf(stderr, "ERROR: WDC: Unknown GUID in C0 Log Page data\n"); - int j; - - fprintf(stderr, "ERROR: WDC: Expected GUID: 0x"); - for (j = 0; j < 16; j++) - fprintf(stderr, "%x", scao_guid[j]); - fprintf(stderr, "\nERROR: WDC: Actual GUID: 0x"); - for (j = 0; j < 16; j++) - fprintf(stderr, "%x", data[SCAO_LPG + j]); - fprintf(stderr, "\n"); - - ret = -1; - break; - } - } - - if (!ret) - /* parse the data */ - wdc_print_c0_cloud_attr_log(data, fmt); - } else { - fprintf(stderr, "ERROR: WDC: Unable to read C0 Log Page data\n"); - ret = -1; - } - - free(data); + ret = nvme_get_print_ocp_cloud_smart_log(dev, + uuid_index, + namespace_id, + fmt); } else if (uuid_index == 1) { - data = (__u8 *)malloc(sizeof(__u8) * WDC_NVME_EOL_STATUS_LOG_LEN); - if (!data) { - fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); - return -1; - } - - /* Get the 0xC0 log data */ - struct nvme_get_log_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .lid = WDC_NVME_GET_EOL_STATUS_LOG_OPCODE, - .nsid = NVME_NSID_ALL, - .lpo = 0, - .lsp = NVME_LOG_LSP_NONE, - .lsi = 0, - .rae = false, - .uuidx = uuid_index, - .csi = NVME_CSI_NVM, - .ot = false, - .len = WDC_NVME_EOL_STATUS_LOG_LEN, - .log = data, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - }; - ret = nvme_get_log(&args); - - if (strcmp(format, "json")) - nvme_show_status(ret); - - if (!ret) { - /* parse the data */ - wdc_print_c0_eol_log(data, fmt); - } else { - fprintf(stderr, "ERROR: WDC: Unable to read C0 Log Page data\n"); - ret = -1; - } - - free(data); + ret = nvme_get_print_c0_eol_log(dev, + uuid_index, + namespace_id, + fmt); } else { fprintf(stderr, "ERROR: WDC: Unknown uuid index\n"); ret = -1; @@ -6983,7 +6850,6 @@ static int wdc_get_c0_log_page_sn(nvme_root_t r, struct nvme_dev *dev, int uuid_ { int ret = 0; __u32 cust_id; - __u8 *data; cust_id = wdc_get_fw_cust_id(r, dev); if (cust_id == WDC_INVALID_CUSTOMER_ID) { @@ -6996,30 +6862,10 @@ static int wdc_get_c0_log_page_sn(nvme_root_t r, struct nvme_dev *dev, int uuid_ ret = wdc_get_c0_log_page_sn_customer_id_0x100X(dev, uuid_index, format, namespace_id, fmt); } else { - data = (__u8 *)malloc(sizeof(__u8) * WDC_NVME_EOL_STATUS_LOG_LEN); - if (!data) { - fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); - return -1; - } - - /* Get the 0xC0 log data */ - ret = nvme_get_log_simple(dev_fd(dev), - WDC_NVME_GET_EOL_STATUS_LOG_OPCODE, - WDC_NVME_EOL_STATUS_LOG_LEN, - data); - - if (strcmp(format, "json")) - nvme_show_status(ret); - - if (!ret) { - /* parse the data */ - wdc_print_c0_eol_log(data, fmt); - } else { - fprintf(stderr, "ERROR: WDC: Unable to read C0 Log Page data\n"); - ret = -1; - } - - free(data); + ret = nvme_get_print_c0_eol_log(dev, + 0, + namespace_id, + fmt); } return ret; @@ -7029,11 +6875,9 @@ static int wdc_get_c0_log_page(nvme_root_t r, struct nvme_dev *dev, char *format __u32 namespace_id) { uint32_t device_id, read_vendor_id; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; int ret; __u8 *data; - __u8 log_id; - __u32 length; if (!wdc_check_device(r, dev)) return -1; @@ -7047,123 +6891,42 @@ static int wdc_get_c0_log_page(nvme_root_t r, struct nvme_dev *dev, char *format switch (device_id) { case WDC_NVME_SN640_DEV_ID: - fallthrough; case WDC_NVME_SN640_DEV_ID_1: - fallthrough; case WDC_NVME_SN640_DEV_ID_2: - fallthrough; case WDC_NVME_SN640_DEV_ID_3: - fallthrough; case WDC_NVME_SN840_DEV_ID: - fallthrough; case WDC_NVME_SN840_DEV_ID_1: - fallthrough; case WDC_NVME_SN860_DEV_ID: - fallthrough; case WDC_NVME_SN560_DEV_ID_1: - fallthrough; case WDC_NVME_SN560_DEV_ID_2: - fallthrough; case WDC_NVME_SN560_DEV_ID_3: - fallthrough; case WDC_NVME_SN550_DEV_ID: ret = wdc_get_c0_log_page_sn(r, dev, uuid_index, format, namespace_id, fmt); break; - case WDC_NVME_SN650_DEV_ID: - fallthrough; case WDC_NVME_SN650_DEV_ID_1: - fallthrough; case WDC_NVME_SN650_DEV_ID_2: - fallthrough; case WDC_NVME_SN650_DEV_ID_3: - fallthrough; case WDC_NVME_SN650_DEV_ID_4: - fallthrough; case WDC_NVME_SN655_DEV_ID: if (uuid_index == 0) { - log_id = WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID; - length = WDC_NVME_SMART_CLOUD_ATTR_LEN; - } else { - log_id = WDC_NVME_GET_EOL_STATUS_LOG_OPCODE; - length = WDC_NVME_EOL_STATUS_LOG_LEN; - } - - data = (__u8 *)malloc(sizeof(__u8) * length); - if (!data) { - fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); - return -1; - } - - if (namespace_id == NVME_NSID_ALL) { - ret = nvme_get_nsid(dev_fd(dev), &namespace_id); - if (ret < 0) - namespace_id = NVME_NSID_ALL; - } - - /* Get the 0xC0 log data */ - struct nvme_get_log_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .lid = log_id, - .nsid = namespace_id, - .lpo = 0, - .lsp = NVME_LOG_LSP_NONE, - .lsi = 0, - .rae = false, - .uuidx = uuid_index, - .csi = NVME_CSI_NVM, - .ot = false, - .len = length, - .log = data, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - }; - ret = nvme_get_log(&args); - - if (strcmp(format, "json")) - nvme_show_status(ret); - - if (!ret) { - /* parse the data */ - if (uuid_index == 0) - wdc_print_c0_cloud_attr_log(data, fmt); - else - wdc_print_c0_eol_log(data, fmt); + ret = nvme_get_print_ocp_cloud_smart_log(dev, + uuid_index, + namespace_id, + fmt); } else { - fprintf(stderr, "ERROR: WDC: Unable to read C0 Log Page data "); - fprintf(stderr, "with uuid index %d\n", uuid_index); - ret = -1; + ret = nvme_get_print_c0_eol_log(dev, + uuid_index, + namespace_id, + fmt); } - free(data); break; - case WDC_NVME_ZN350_DEV_ID: - fallthrough; case WDC_NVME_ZN350_DEV_ID_1: - data = (__u8 *)malloc(sizeof(__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN); - if (!data) { - fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); - return -1; - } - - /* Get the 0xC0 log data */ - ret = nvme_get_log_simple(dev_fd(dev), - WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID, - WDC_NVME_SMART_CLOUD_ATTR_LEN, data); - - if (strcmp(format, "json")) - nvme_show_status(ret); - - if (!ret) { - /* parse the data */ - wdc_print_c0_cloud_attr_log(data, fmt); - } else { - fprintf(stderr, "ERROR: WDC: Unable to read C0 Log Page data\n"); - ret = -1; - } - - free(data); + ret = nvme_get_print_ocp_cloud_smart_log(dev, + 0, + NVME_NSID_ALL, + fmt); break; case WDC_NVME_SN820CL_DEV_ID: /* Get the 0xC0 Extended Smart Cloud Attribute log data */ @@ -7345,7 +7108,7 @@ static int wdc_get_ca_log_page(nvme_root_t r, struct nvme_dev *dev, char *format { uint32_t read_device_id, read_vendor_id; struct wdc_ssd_ca_perf_stats *perf; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; __u32 cust_id; __u8 *data; int ret; @@ -7405,17 +7168,11 @@ static int wdc_get_ca_log_page(nvme_root_t r, struct nvme_dev *dev, char *format } break; case WDC_NVME_SN640_DEV_ID: - fallthrough; case WDC_NVME_SN640_DEV_ID_1: - fallthrough; case WDC_NVME_SN640_DEV_ID_2: - fallthrough; case WDC_NVME_SN640_DEV_ID_3: - fallthrough; case WDC_NVME_SN840_DEV_ID: - fallthrough; case WDC_NVME_SN840_DEV_ID_1: - fallthrough; case WDC_NVME_SN860_DEV_ID: if (cust_id == WDC_CUSTOMER_ID_0x1005) { data = (__u8 *)malloc(sizeof(__u8) * WDC_FB_CA_LOG_BUF_LEN); @@ -7482,7 +7239,7 @@ static int wdc_get_c1_log_page(nvme_root_t r, struct nvme_dev *dev, struct wdc_log_page_subpage_header *sph; struct wdc_ssd_perf_stats *perf; struct wdc_log_page_header *l; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; int total_subpages; int skip_cnt = 4; __u8 *data; @@ -7539,7 +7296,7 @@ static int wdc_get_c1_log_page(nvme_root_t r, struct nvme_dev *dev, static int wdc_get_c3_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { struct wdc_ssd_latency_monitor_log *log_data; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; __u8 *data; int ret; int i; @@ -7611,7 +7368,7 @@ out: static int wdc_get_ocp_c1_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { struct wdc_ocp_c1_error_recovery_log *log_data; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; __u8 *data; int ret; int i; @@ -7642,9 +7399,10 @@ static int wdc_get_ocp_c1_log_page(nvme_root_t r, struct nvme_dev *dev, char *fo log_data = (struct wdc_ocp_c1_error_recovery_log *)data; /* check log page version */ - if ((log_data->log_page_version != WDC_ERROR_REC_LOG_VERSION1) && - (log_data->log_page_version != WDC_ERROR_REC_LOG_VERSION2)) { - fprintf(stderr, "ERROR: WDC: invalid error recovery log version - %d\n", log_data->log_page_version); + if ((log_data->log_page_version < 1) || + (log_data->log_page_version > 3)) { + fprintf(stderr, "ERROR: WDC: invalid error recovery log version - %d\n", + log_data->log_page_version); ret = -1; goto out; } @@ -7682,7 +7440,7 @@ out: static int wdc_get_ocp_c4_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { struct wdc_ocp_C4_dev_cap_log *log_data; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; __u8 *data; int ret; int i; @@ -7752,7 +7510,7 @@ out: static int wdc_get_ocp_c5_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { struct wdc_ocp_C5_unsupported_reqs *log_data; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; int ret; __u8 *data; int i; @@ -7822,7 +7580,7 @@ out: static int wdc_get_d0_log_page(nvme_root_t r, struct nvme_dev *dev, char *format) { struct wdc_ssd_d0_smart_log *perf; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; int ret = 0; __u8 *data; @@ -7907,7 +7665,7 @@ static const char *stringify_cloud_smart_log_thermal_status(__u8 status) return "unrecognized"; } -static void show_cloud_smart_log_json(struct ocp_cloud_smart_log *log) +static void wdc_show_cloud_smart_log_json(struct ocp_cloud_smart_log *log) { struct json_object *root; struct json_object *bad_user_nand_blocks; @@ -7917,6 +7675,8 @@ static void show_cloud_smart_log_json(struct ocp_cloud_smart_log *log) struct json_object *thermal_status; struct json_object *dssd_specific_ver; char buf[2 * sizeof(log->log_page_guid) + 3]; + char lowest_fr[sizeof(log->lowest_permitted_fw_rev) + 1]; + uint16_t smart_log_ver = (uint16_t)le16_to_cpu(log->log_page_version); bad_user_nand_blocks = json_create_object(); json_object_add_value_uint(bad_user_nand_blocks, "normalized", @@ -7982,7 +7742,8 @@ static void show_cloud_smart_log_json(struct ocp_cloud_smart_log *log) json_object_add_value_object(root, "user_data_erase_counts", user_data_erase_counts); json_object_add_value_object(root, "thermal_status", thermal_status); - json_object_add_value_object(root, "dssd_specific_ver", + if (smart_log_ver >= 3) + json_object_add_value_object(root, "dssd_specific_ver", dssd_specific_ver); json_object_add_value_uint(root, "pcie_correctable_error_count", le64_to_cpu(log->pcie_correctable_error_count)); @@ -7992,8 +7753,18 @@ static void show_cloud_smart_log_json(struct ocp_cloud_smart_log *log) log->percent_free_blocks); json_object_add_value_uint(root, "capacitor_health", le16_to_cpu(log->capacitor_health)); - sprintf(buf, "%c", log->nvme_errata_ver); - json_object_add_value_string(root, "nvme_errata_version", buf); + if (smart_log_ver >= 3) { + if (smart_log_ver >= 4) { + sprintf(buf, "%c", log->nvme_base_errata_ver); + json_object_add_value_string(root, "nvme_base_errata_version", buf); + sprintf(buf, "%c", log->nvme_cmd_set_errata_ver); + json_object_add_value_string(root, "nvme_cmd_set_errata_version", buf); + } else { + sprintf(buf, "%c", log->nvme_base_errata_ver); + json_object_add_value_string(root, "nvme_errata_version", buf); + } + } + json_object_add_value_uint(root, "unaligned_io", le64_to_cpu(log->unaligned_io)); json_object_add_value_uint(root, "security_version_number", @@ -8004,12 +7775,22 @@ static void show_cloud_smart_log_json(struct ocp_cloud_smart_log *log) le_to_float(log->plp_start_count, 16)); json_object_add_value_uint64(root, "endurance_estimate", le_to_float(log->endurance_estimate, 16)); - json_object_add_value_uint(root, "pcie_link_retraining_count", - le64_to_cpu(log->pcie_link_retraining_cnt)); - json_object_add_value_uint(root, "power_state_change_count", - le64_to_cpu(log->power_state_change_cnt)); + if (smart_log_ver >= 3) { + json_object_add_value_uint(root, "pcie_link_retraining_count", + le64_to_cpu(log->pcie_link_retraining_cnt)); + json_object_add_value_uint(root, "power_state_change_count", + le64_to_cpu(log->power_state_change_cnt)); + if (smart_log_ver >= 4) { + snprintf(lowest_fr, sizeof(lowest_fr), "%-.*s", + (int)sizeof(log->lowest_permitted_fw_rev), + log->lowest_permitted_fw_rev); + json_object_add_value_string(root, "lowest_permitted_fw_rev", lowest_fr); + } else + json_object_add_value_uint128(root, "hardware_revision", + le128_to_cpu((__u8 *)&log->lowest_permitted_fw_rev[0])); + } json_object_add_value_uint(root, "log_page_version", - le16_to_cpu(log->log_page_version)); + smart_log_ver); stringify_log_page_guid(log->log_page_guid, buf); json_object_add_value_string(root, "log_page_guid", buf); @@ -8018,11 +7799,13 @@ static void show_cloud_smart_log_json(struct ocp_cloud_smart_log *log) json_free_object(root); } -static void show_cloud_smart_log_normal(struct ocp_cloud_smart_log *log, struct nvme_dev *dev) +static void wdc_show_cloud_smart_log_normal(struct ocp_cloud_smart_log *log, + struct nvme_dev *dev) { char buf[2 * sizeof(log->log_page_guid) + 3]; + uint16_t smart_log_ver = (uint16_t)le16_to_cpu(log->log_page_version); - printf("Smart Extended Log for NVME device:%s\n", dev->name); + printf("SMART Cloud Attributes for NVMe device : %s\n", dev->name); printf("Physical Media Units Written : %'.0Lf\n", le_to_float(log->physical_media_units_written, 16)); printf("Physical Media Units Read : %'.0Lf\n", @@ -8057,14 +7840,16 @@ static void show_cloud_smart_log_normal(struct ocp_cloud_smart_log *log, struct stringify_cloud_smart_log_thermal_status(log->thermal_status.current_status)); printf("Thermal Throttling Status (Number of Events) : %" PRIu8 "\n", log->thermal_status.num_events); - printf("NVMe Major Version : %" PRIu8 "\n", - log->dssd_specific_ver.major_ver); - printf(" Minor Version : %" PRIu16 "\n", - le16_to_cpu(log->dssd_specific_ver.minor_ver)); - printf(" Point Version : %" PRIu16 "\n", - le16_to_cpu(log->dssd_specific_ver.point_ver)); - printf(" Errata Version : %" PRIu8 "\n", - log->dssd_specific_ver.errata_ver); + if (smart_log_ver >= 3) { + printf("NVMe Major Version : %" PRIu8 "\n", + log->dssd_specific_ver.major_ver); + printf(" Minor Version : %" PRIu16 "\n", + le16_to_cpu(log->dssd_specific_ver.minor_ver)); + printf(" Point Version : %" PRIu16 "\n", + le16_to_cpu(log->dssd_specific_ver.point_ver)); + printf(" Errata Version : %" PRIu8 "\n", + log->dssd_specific_ver.errata_ver); + } printf("PCIe Correctable Error Count : %" PRIu64 "\n", le64_to_cpu(log->pcie_correctable_error_count)); printf("Incomplete Shutdowns : %" PRIu32 "\n", @@ -8073,8 +7858,17 @@ static void show_cloud_smart_log_normal(struct ocp_cloud_smart_log *log, struct log->percent_free_blocks); printf("Capacitor Health : %" PRIu16 "%%\n", le16_to_cpu(log->capacitor_health)); - printf("NVMe Errata Version : %c\n", - log->nvme_errata_ver); + if (smart_log_ver >= 3) { + if (smart_log_ver >= 4) { + printf("NVMe Base Errata Version : %c\n", + log->nvme_base_errata_ver); + printf("NVMe Command Set Errata Version : %c\n", + log->nvme_cmd_set_errata_ver); + } else { + printf("NVMe Errata Version : %c\n", + log->nvme_base_errata_ver); + } + } printf("Unaligned IO : %" PRIu64 "\n", le64_to_cpu(log->unaligned_io)); printf("Security Version Number : %" PRIu64 "\n", @@ -8085,12 +7879,22 @@ static void show_cloud_smart_log_normal(struct ocp_cloud_smart_log *log, struct le_to_float(log->plp_start_count, 16)); printf("Endurance Estimate : %'.0Lf\n", le_to_float(log->endurance_estimate, 16)); - printf("PCIe Link Retraining Count : %" PRIu64 "\n", - le64_to_cpu(log->pcie_link_retraining_cnt)); - printf("Power State Change Count : %" PRIu64 "\n", - le64_to_cpu(log->power_state_change_cnt)); + if (smart_log_ver >= 3) { + printf("PCIe Link Retraining Count : %" PRIu64 "\n", + le64_to_cpu(log->pcie_link_retraining_cnt)); + printf("Power State Change Count : %" PRIu64 "\n", + le64_to_cpu(log->power_state_change_cnt)); + if (smart_log_ver >= 4) + printf("Lowest Permitted FW Revision : %-.*s\n", + (int)sizeof(log->lowest_permitted_fw_rev), + log->lowest_permitted_fw_rev); + else + printf("Hardware Revision : %s\n", + uint128_t_to_string(le128_to_cpu( + (__u8 *)&log->lowest_permitted_fw_rev[0]))); + } printf("Log Page Version : %" PRIu16 "\n", - le16_to_cpu(log->log_page_version)); + smart_log_ver); stringify_log_page_guid(log->log_page_guid, buf); printf("Log Page GUID : %s\n", buf); printf("\n\n"); @@ -8104,7 +7908,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, const char *log_page_version = "Log Page Version: 0 = vendor, 1 = WDC"; const char *log_page_mask = "Log Page Mask, comma separated list: 0xC0, 0xC1, 0xCA, 0xD0"; const char *namespace_id = "desired namespace id"; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; struct nvme_dev *dev; nvme_root_t r; int ret = 0; @@ -8200,43 +8004,16 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, "ERROR: WDC: Failure reading the C0 Log Page, ret = %d\n", ret); } else { - struct ocp_cloud_smart_log log; - char buf[2 * sizeof(log.log_page_guid) + 3]; - ret = validate_output_format(cfg.output_format, &fmt); if (ret < 0) { fprintf(stderr, "Invalid output format: %s\n", cfg.output_format); goto out; } - ret = nvme_get_log_simple(dev_fd(dev), - WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID, - sizeof(log), &log); - if (!ret) { - char *ptr = buf; - int i; - __u8 *guid = log.log_page_guid; - - memset(buf, 0, sizeof(char) * 19); - - ptr += sprintf(ptr, "0x"); - for (i = 0; i < 16; i++) - ptr += sprintf(ptr, "%x", guid[15 - i]); - if (strcmp(buf, "0xafd514c97c6f4f9ca4f2bfea2810afc5")) - fprintf(stderr, "Invalid GUID: %s\n", buf); - else { - if (fmt == BINARY) - d_raw((unsigned char *)&log, sizeof(log)); - else if (fmt == JSON) - show_cloud_smart_log_json(&log); - else - show_cloud_smart_log_normal(&log, dev); - } - } else if (ret > 0) { - nvme_show_status(ret); - } else { - perror("vs-smart-add-log"); - } + ret = nvme_get_print_ocp_cloud_smart_log(dev, + 0, + NVME_NSID_ALL, + fmt); } } if (((capabilities & (WDC_DRIVE_CAP_CA_LOG_PAGE)) == (WDC_DRIVE_CAP_CA_LOG_PAGE)) && @@ -8274,7 +8051,7 @@ static int wdc_vs_cloud_log(int argc, char **argv, struct command *command, { const char *desc = "Retrieve Cloud Log Smart/Health Information"; const char *namespace_id = "desired namespace id"; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; __u64 capabilities = 0; struct nvme_dev *dev; nvme_root_t r; @@ -8345,7 +8122,7 @@ static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *command, { const char *desc = "Retrieve Hardware Revision Log Information"; const char *namespace_id = "desired namespace id"; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; __u64 capabilities = 0; struct nvme_dev *dev; int ret; @@ -8430,7 +8207,7 @@ static int wdc_vs_device_waf(int argc, char **argv, struct command *command, const char *desc = "Retrieve Device Write Amplication Factor"; const char *namespace_id = "desired namespace id"; struct nvme_smart_log smart_log; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; struct nvme_dev *dev; __u8 *data; nvme_root_t r; @@ -8987,7 +8764,7 @@ static int wdc_get_fw_act_history(nvme_root_t r, struct nvme_dev *dev, char *format) { struct wdc_fw_act_history_log_hdr *fw_act_history_hdr; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; int ret; __u8 *data; @@ -9071,9 +8848,10 @@ static int wdc_get_fw_act_history_C2(nvme_root_t r, struct nvme_dev *dev, __u32 tot_entries = 0, num_entries = 0; __u32 vendor_id = 0, device_id = 0; __u32 cust_id = 0; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; __u8 *data; int ret; + bool c2GuidMatch = false; if (!wdc_check_device(r, dev)) return -1; @@ -9102,29 +8880,40 @@ static int wdc_get_fw_act_history_C2(nvme_root_t r, struct nvme_dev *dev, nvme_show_status(ret); if (!ret) { - /* parse the data */ + /* Get the log page data and verify the GUID */ fw_act_history_log = (struct wdc_fw_act_history_log_format_c2 *)(data); - tot_entries = le32_to_cpu(fw_act_history_log->num_entries); - if (tot_entries > 0) { - /* get the FW customer id */ - if (!wdc_is_sn861(device_id)) { - cust_id = wdc_get_fw_cust_id(r, dev); - if (cust_id == WDC_INVALID_CUSTOMER_ID) { - fprintf(stderr, - "%s: ERROR: WDC: invalid customer id\n", - __func__); - ret = -1; - goto freeData; + c2GuidMatch = !memcmp(ocp_C2_guid, + fw_act_history_log->log_page_guid, + WDC_C2_GUID_LENGTH); + + if (c2GuidMatch) { + /* parse the data */ + tot_entries = le32_to_cpu(fw_act_history_log->num_entries); + + if (tot_entries > 0) { + /* get the FW customer id */ + if (!wdc_is_sn861(device_id)) { + cust_id = wdc_get_fw_cust_id(r, dev); + if (cust_id == WDC_INVALID_CUSTOMER_ID) { + fprintf(stderr, + "%s: ERROR: WDC: invalid customer id\n", + __func__); + ret = -1; + goto freeData; + } } + num_entries = (tot_entries < WDC_MAX_NUM_ACT_HIST_ENTRIES) ? + tot_entries : WDC_MAX_NUM_ACT_HIST_ENTRIES; + ret = wdc_print_fw_act_history_log(data, num_entries, + fmt, cust_id, vendor_id, device_id); + } else { + fprintf(stderr, "INFO: WDC: No entries found.\n"); + ret = 0; } - num_entries = (tot_entries < WDC_MAX_NUM_ACT_HIST_ENTRIES) ? tot_entries : - WDC_MAX_NUM_ACT_HIST_ENTRIES; - ret = wdc_print_fw_act_history_log(data, num_entries, - fmt, cust_id, vendor_id, device_id); - } else { - fprintf(stderr, "INFO: WDC: No FW Activate History entries found.\n"); - ret = 0; + } else { + fprintf(stderr, "ERROR: WDC: Invalid C2 log page GUID\n"); + ret = -1; } } else { fprintf(stderr, "ERROR: WDC: Unable to read FW Activate History Log Page data\n"); @@ -9143,7 +8932,7 @@ static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *com __u64 capabilities = 0; struct nvme_dev *dev; nvme_root_t r; - int ret; + int ret = -1; struct config { char *output_format; @@ -9171,61 +8960,23 @@ static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *com } if (capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) { - int uuid_index = 0; - bool c0GuidMatch = false; - __u8 *data; - int i; - - /* - * check for the GUID in the 0xC0 log page to determine which log page to use to - * retrieve fw activate history data - */ - data = (__u8 *)malloc(sizeof(__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN); - if (!data) { - fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno)); + __u32 cust_fw_id = 0; + /* get the FW customer id */ + cust_fw_id = wdc_get_fw_cust_id(r, dev); + if (cust_fw_id == WDC_INVALID_CUSTOMER_ID) { + fprintf(stderr, "%s: ERROR: WDC: invalid customer id\n", __func__); ret = -1; goto out; } - /* Get the 0xC0 log data */ - struct nvme_get_log_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .lid = WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID, - .nsid = 0xFFFFFFFF, - .lpo = 0, - .lsp = NVME_LOG_LSP_NONE, - .lsi = 0, - .rae = false, - .uuidx = uuid_index, - .csi = NVME_CSI_NVM, - .ot = false, - .len = WDC_NVME_SMART_CLOUD_ATTR_LEN, - .log = data, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - }; - ret = nvme_get_log(&args); - - if (!ret) { - /* Verify GUID matches */ - for (i = 0; i < 16; i++) { - if (scao_guid[i] != data[SCAO_LPG + i]) { - c0GuidMatch = false; - break; - } - } - - if (i == 16) - c0GuidMatch = true; - } - - free(data); - if (c0GuidMatch) + if ((cust_fw_id == WDC_CUSTOMER_ID_0x1004) || + (cust_fw_id == WDC_CUSTOMER_ID_0x1008) || + (cust_fw_id == WDC_CUSTOMER_ID_0x1005) || + (cust_fw_id == WDC_CUSTOMER_ID_0x1304)) ret = wdc_get_fw_act_history_C2(r, dev, cfg.output_format); else ret = wdc_get_fw_act_history(r, dev, cfg.output_format); - } else { + } else if (capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2) { ret = wdc_get_fw_act_history_C2(r, dev, cfg.output_format); } @@ -9648,10 +9399,10 @@ static int wdc_fetch_log_file_from_device(struct nvme_dev *dev, __u32 fileId, __u16 spiDestn, __u64 fileSize, __u8 *dataBuffer) { int ret = WDC_STATUS_FAILURE; - __u32 chunckSize = WDC_DE_VU_READ_BUFFER_STANDARD_OFFSET; - __u32 maximumTransferLength = 0; - __u32 buffSize = 0; - __u64 offsetIdx = 0; + __u32 chunckSize = WDC_DE_VU_READ_BUFFER_STANDARD_OFFSET; + __u32 maximumTransferLength = 0; + __u32 buffSize = 0; + __u64 offsetIdx = 0; if (!dev || !dataBuffer || !fileSize) { ret = WDC_STATUS_INVALID_PARAMETER; @@ -9699,18 +9450,17 @@ end: static int wdc_de_get_dump_trace(struct nvme_dev *dev, char *filePath, __u16 binFileNameLen, char *binFileName) { - int ret = WDC_STATUS_FAILURE; - __u8 *readBuffer = NULL; - __u32 readBufferLen = 0; - __u32 lastPktReadBufferLen = 0; - __u32 maxTransferLen = 0; - __u32 dumptraceSize = 0; - __u32 chunkSize = 0; - __u32 chunks = 0; - __u32 offset = 0; - __u8 loop = 0; - __u16 i = 0; - __u32 maximumTransferLength = 0; + int ret = WDC_STATUS_FAILURE; + __u8 *readBuffer = NULL; + __u32 readBufferLen = 0; + __u32 lastPktReadBufferLen = 0; + __u32 maxTransferLen = 0; + __u32 dumptraceSize = 0; + __u32 chunkSize; + __u32 chunks; + __u32 offset; + __u32 i; + __u32 maximumTransferLength = 0; if (!dev || !binFileName || !filePath) { ret = WDC_STATUS_INVALID_PARAMETER; @@ -9759,7 +9509,7 @@ static int wdc_de_get_dump_trace(struct nvme_dev *dev, char *filePath, __u16 bin } for (i = 0; i < chunks; i++) { - offset = ((i*chunkSize) / 4); + offset = (i * chunkSize) / 4; /* Last loop call, Assign readBufferLen to read only left over bytes */ if (i == (chunks - 1)) @@ -9774,7 +9524,7 @@ static int wdc_de_get_dump_trace(struct nvme_dev *dev, char *filePath, __u16 bin break; } } - } while (loop); + } while (0); if (ret == WDC_STATUS_SUCCESS) { ret = wdc_WriteToFile(binFileName, (char *)readBuffer, dumptraceSize); @@ -10580,7 +10330,7 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command struct plugin *plugin) { const char *desc = "Retrieve Log Page Directory."; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; struct nvme_dev *dev; int ret = 0; nvme_root_t r; @@ -11240,7 +10990,7 @@ static void wdc_print_pcie_stats_json(struct wdc_vs_pcie_stats *pcie_stats) static int wdc_do_vs_nand_stats_sn810_2(struct nvme_dev *dev, char *format) { - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; uint8_t *data = NULL; int ret; @@ -11279,7 +11029,7 @@ out: static int wdc_do_vs_nand_stats(struct nvme_dev *dev, char *format) { - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; uint8_t *output = NULL; __u16 version = 0; int ret; @@ -11403,7 +11153,7 @@ static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Retrieve PCIE statistics."; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; struct nvme_dev *dev; nvme_root_t r; int ret; @@ -11478,7 +11228,7 @@ static int wdc_vs_drive_info(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Send a vs-drive-info command."; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; nvme_root_t r; uint64_t capabilities = 0; struct nvme_dev *dev; @@ -11678,7 +11428,6 @@ static int wdc_vs_drive_info(int argc, char **argv, break; case WDC_NVME_SN861_DEV_ID: - fallthrough; case WDC_NVME_SN861_DEV_ID_1: data_len = sizeof(info); num_dwords = data_len / 4; @@ -11746,7 +11495,7 @@ static int wdc_vs_temperature_stats(int argc, char **argv, const char *desc = "Send a vs-temperature-stats command."; struct nvme_smart_log smart_log; struct nvme_id_ctrl id_ctrl; - enum nvme_print_flags fmt; + nvme_print_flags_t fmt; struct nvme_dev *dev; nvme_root_t r; uint64_t capabilities = 0; diff --git a/plugins/wdc/wdc-nvme.h b/plugins/wdc/wdc-nvme.h index d3692bc..a18da16 100644 --- a/plugins/wdc/wdc-nvme.h +++ b/plugins/wdc/wdc-nvme.h @@ -5,7 +5,7 @@ #if !defined(WDC_NVME) || defined(CMD_HEADER_MULTI_READ) #define WDC_NVME -#define WDC_PLUGIN_VERSION "2.7.0" +#define WDC_PLUGIN_VERSION "2.9.1" #include "cmd.h" PLUGIN(NAME("wdc", "Western Digital vendor specific extensions", WDC_PLUGIN_VERSION), diff --git a/plugins/wdc/wdc-utils.c b/plugins/wdc/wdc-utils.c index 414a06a..1b52e7c 100644 --- a/plugins/wdc/wdc-utils.c +++ b/plugins/wdc/wdc-utils.c @@ -192,5 +192,5 @@ bool wdc_CheckUuidListSupport(struct nvme_dev *dev, struct nvme_id_uuid_list *uu bool wdc_UuidEqual(struct nvme_id_uuid_list_entry *entry1, struct nvme_id_uuid_list_entry *entry2) { - return !memcmp(entry1, entry2, NVME_UUID_LEN); + return !memcmp(entry1->uuid, entry2->uuid, NVME_UUID_LEN); } diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index a7a3766..94336d7 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -114,7 +114,7 @@ static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *pl "the given device and report information about the specified\n" "controller in various formats."; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_zns_id_ctrl ctrl; struct nvme_dev *dev; int err = -1; @@ -160,7 +160,7 @@ static int id_ns(int argc, char **argv, struct command *cmd, struct plugin *plug const char *vendor_specific = "dump binary vendor fields"; const char *human_readable = "show identify in readable format"; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_zns_id_ns ns; struct nvme_id_ns id_ns; struct nvme_dev *dev; @@ -732,7 +732,7 @@ static int zone_mgmt_recv(int argc, char **argv, struct command *cmd, struct plu const char *partial = "Zone Receive Action Specific Features(Partial Report)"; const char *data_len = "length of data in bytes"; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_dev *dev; void *data = NULL; int err = -1; @@ -830,7 +830,7 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi const char *part = "set to use the partial report"; const char *verbose = "show report zones verbosity"; - enum nvme_print_flags flags; + nvme_print_flags_t flags; int zdes = 0, err = -1; struct nvme_dev *dev; __u32 report_size; @@ -1222,7 +1222,7 @@ static int changed_zone_list(int argc, char **argv, struct command *cmd, struct const char *rae = "retain an asynchronous event"; struct nvme_zns_changed_zone_log log; - enum nvme_print_flags flags; + nvme_print_flags_t flags; struct nvme_dev *dev; int err = -1; |