// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2023 Meta Platforms, Inc. * * Authors: Arthur Shau , * Wei Zhang , * Venkat Ramesh */ #include #include #include #include #include #include #include #include #include #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-smart-extended-log.h" #include "ocp-clear-features.h" #include "ocp-fw-activation-history.h" #define CREATE_CMD #include "ocp-nvme.h" #include "ocp-utils.h" /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Latency Monitor Log #define C3_LATENCY_MON_LOG_BUF_LEN 0x200 #define C3_LATENCY_MON_OPCODE 0xC3 #define C3_LATENCY_MON_VERSION 0x0001 #define C3_GUID_LENGTH 16 #define NVME_FEAT_OCP_LATENCY_MONITOR 0xC5 #define C3_ACTIVE_BUCKET_TIMER_INCREMENT 5 #define C3_ACTIVE_THRESHOLD_INCREMENT 5 #define C3_MINIMUM_WINDOW_INCREMENT 100 #define C3_BUCKET_NUM 4 static __u8 lat_mon_guid[C3_GUID_LENGTH] = { 0x92, 0x7a, 0xc0, 0x8c, 0xd0, 0x84, 0x6c, 0x9c, 0x70, 0x43, 0xe6, 0xd4, 0x58, 0x5e, 0xd4, 0x85 }; #define READ 3 #define WRITE 2 #define TRIM 1 #define RESERVED 0 struct __packed ssd_latency_monitor_log { __u8 feature_status; /* 0x00 */ __u8 rsvd1; /* 0x01 */ __le16 active_bucket_timer; /* 0x02 */ __le16 active_bucket_timer_threshold; /* 0x04 */ __u8 active_threshold_a; /* 0x06 */ __u8 active_threshold_b; /* 0x07 */ __u8 active_threshold_c; /* 0x08 */ __u8 active_threshold_d; /* 0x09 */ __le16 active_latency_config; /* 0x0A */ __u8 active_latency_min_window; /* 0x0C */ __u8 rsvd2[0x13]; /* 0x0D */ __le32 active_bucket_counter[4][4]; /* 0x20 - 0x5F */ __le64 active_latency_timestamp[4][3]; /* 0x60 - 0xBF */ __le16 active_measured_latency[4][3]; /* 0xC0 - 0xD7 */ __le16 active_latency_stamp_units; /* 0xD8 */ __u8 rsvd3[0x16]; /* 0xDA */ __le32 static_bucket_counter[4][4]; /* 0x0F0 - 0x12F */ __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 */ __le16 debug_log_trigger_enable; /* 0x1C0 */ __le16 debug_log_measured_latency; /* 0x1C2 */ __le64 debug_log_latency_stamp; /* 0x1C4 */ __le16 debug_log_ptr; /* 0x1CC */ __le16 debug_log_counter_trigger; /* 0x1CE */ __u8 debug_log_stamp_units; /* 0x1D0 */ __u8 rsvd5[0x1D]; /* 0x1D1 */ __le16 log_page_version; /* 0x1EE */ __u8 log_page_guid[0x10]; /* 0x1F0 */ }; struct __packed feature_latency_monitor { __u16 active_bucket_timer_threshold; __u8 active_threshold_a; __u8 active_threshold_b; __u8 active_threshold_c; __u8 active_threshold_d; __u16 active_latency_config; __u8 active_latency_minimum_window; __u16 debug_log_trigger_enable; __u8 discard_debug_log; __u8 latency_monitor_feature_enable; __u8 reserved[4083]; }; static int ocp_print_C3_log_normal(struct nvme_dev *dev, struct ssd_latency_monitor_log *log_data) { char ts_buf[128]; int i, j; printf("-Latency Monitor/C3 Log Page Data-\n"); printf(" Controller : %s\n", dev->name); printf(" Feature Status 0x%x\n", log_data->feature_status); printf(" Active Bucket Timer %d min\n", C3_ACTIVE_BUCKET_TIMER_INCREMENT * le16_to_cpu(log_data->active_bucket_timer)); printf(" Active Bucket Timer Threshold %d min\n", C3_ACTIVE_BUCKET_TIMER_INCREMENT * le16_to_cpu(log_data->active_bucket_timer_threshold)); printf(" Active Threshold A %d ms\n", C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_a+1)); printf(" Active Threshold B %d ms\n", C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_b+1)); printf(" Active Threshold C %d ms\n", C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_c+1)); 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", le16_to_cpu(log_data->active_latency_config)); printf(" Active Latency Minimum Window %d ms\n", C3_MINIMUM_WINDOW_INCREMENT * le16_to_cpu(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)); printf(" Debug Log Measured Latency %d\n", le16_to_cpu(log_data->debug_log_measured_latency)); if (le64_to_cpu(log_data->debug_log_latency_stamp) == -1) { printf(" Debug Log Latency Time Stamp N/A\n"); } else { convert_ts(le64_to_cpu(log_data->debug_log_latency_stamp), ts_buf); printf(" Debug Log Latency Time Stamp %s\n", ts_buf); } printf(" Debug Log Pointer %d\n", le16_to_cpu(log_data->debug_log_ptr)); printf(" Debug Counter Trigger Source %d\n", le16_to_cpu(log_data->debug_log_counter_trigger)); printf(" Debug Log Stamp Units %d\n", le16_to_cpu(log_data->debug_log_stamp_units)); printf(" Log Page Version %d\n", le16_to_cpu(log_data->log_page_version)); char guid[(C3_GUID_LENGTH * 2) + 1]; char *ptr = &guid[0]; for (i = C3_GUID_LENGTH - 1; i >= 0; i--) ptr += sprintf(ptr, "%02X", log_data->log_page_guid[i]); printf(" Log Page GUID %s\n", guid); printf("\n"); printf(" Read Write Deallocate/Trim\n"); for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Active Bucket Counter: Bucket %d %27d %27d %27d\n", i, le32_to_cpu(log_data->active_bucket_counter[i][READ]), le32_to_cpu(log_data->active_bucket_counter[i][WRITE]), le32_to_cpu(log_data->active_bucket_counter[i][TRIM])); } for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Active Latency Time Stamp: Bucket %d ", i); for (j = 2; j >= 0; j--) { if (le64_to_cpu(log_data->active_latency_timestamp[3-i][j]) == -1) { printf(" N/A "); } else { convert_ts(le64_to_cpu(log_data->active_latency_timestamp[3-i][j]), ts_buf); printf("%s ", ts_buf); } } printf("\n"); } for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Active Measured Latency: Bucket %d %27d ms %27d ms %27d ms\n", i, le16_to_cpu(log_data->active_measured_latency[3-i][READ-1]), le16_to_cpu(log_data->active_measured_latency[3-i][WRITE-1]), le16_to_cpu(log_data->active_measured_latency[3-i][TRIM-1])); } printf("\n"); for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Static Bucket Counter: Bucket %d %27d %27d %27d\n", i, le32_to_cpu(log_data->static_bucket_counter[i][READ]), le32_to_cpu(log_data->static_bucket_counter[i][WRITE]), le32_to_cpu(log_data->static_bucket_counter[i][TRIM])); } for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Static Latency Time Stamp: Bucket %d ", i); for (j = 2; j >= 0; j--) { if (le64_to_cpu(log_data->static_latency_timestamp[3-i][j]) == -1) { printf(" N/A "); } else { convert_ts(le64_to_cpu(log_data->static_latency_timestamp[3-i][j]), ts_buf); printf("%s ", ts_buf); } } printf("\n"); } for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Static Measured Latency: Bucket %d %27d ms %27d ms %27d ms\n", i, le16_to_cpu(log_data->static_measured_latency[3-i][READ-1]), le16_to_cpu(log_data->static_measured_latency[3-i][WRITE-1]), le16_to_cpu(log_data->static_measured_latency[3-i][TRIM-1])); } return 0; } static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) { struct json_object *root; char ts_buf[128]; char buf[128]; int i, j; char *operation[3] = {"Trim", "Write", "Read"}; root = json_create_object(); json_object_add_value_uint(root, "Feature Status", log_data->feature_status); json_object_add_value_uint(root, "Active Bucket Timer", C3_ACTIVE_BUCKET_TIMER_INCREMENT * le16_to_cpu(log_data->active_bucket_timer)); json_object_add_value_uint(root, "Active Bucket Timer Threshold", C3_ACTIVE_BUCKET_TIMER_INCREMENT * le16_to_cpu(log_data->active_bucket_timer_threshold)); json_object_add_value_uint(root, "Active Threshold A", C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_a + 1)); json_object_add_value_uint(root, "Active Threshold B", C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_b + 1)); json_object_add_value_uint(root, "Active Threshold C", C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_c + 1)); json_object_add_value_uint(root, "Active Threshold D", C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_d + 1)); json_object_add_value_uint(root, "Active Latency Configuration", le16_to_cpu(log_data->active_latency_config)); json_object_add_value_uint(root, "Active Latency Minimum Window", C3_MINIMUM_WINDOW_INCREMENT * le16_to_cpu(log_data->active_latency_min_window)); for (i = 0; i < C3_BUCKET_NUM; i++) { struct json_object *bucket; bucket = json_create_object(); sprintf(buf, "Active Bucket Counter: Bucket %d", i); for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], le32_to_cpu(log_data->active_bucket_counter[i][j+1])); } json_object_add_value_object(root, buf, bucket); } for (i = 0; i < C3_BUCKET_NUM; i++) { struct json_object *bucket; bucket = json_create_object(); sprintf(buf, "Active Latency Time Stamp: Bucket %d", i); for (j = 2; j >= 0; j--) { if (le64_to_cpu(log_data->active_latency_timestamp[3-i][j]) == -1) { json_object_add_value_string(bucket, operation[j], "NA"); } else { convert_ts(le64_to_cpu(log_data->active_latency_timestamp[3-i][j]), ts_buf); json_object_add_value_string(bucket, operation[j], ts_buf); } } json_object_add_value_object(root, buf, bucket); } for (i = 0; i < C3_BUCKET_NUM; i++) { struct json_object *bucket; bucket = json_create_object(); sprintf(buf, "Active Measured Latency: Bucket %d", i); for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], le16_to_cpu(log_data->active_measured_latency[3-i][j])); } json_object_add_value_object(root, buf, bucket); } json_object_add_value_uint(root, "Active Latency Stamp Units", le16_to_cpu(log_data->active_latency_stamp_units)); for (i = 0; i < C3_BUCKET_NUM; i++) { struct json_object *bucket; bucket = json_create_object(); sprintf(buf, "Static Bucket Counter: Bucket %d", i); for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], le32_to_cpu(log_data->static_bucket_counter[i][j+1])); } json_object_add_value_object(root, buf, bucket); } for (i = 0; i < C3_BUCKET_NUM; i++) { struct json_object *bucket; bucket = json_create_object(); sprintf(buf, "Static Latency Time Stamp: Bucket %d", i); for (j = 2; j >= 0; j--) { if (le64_to_cpu(log_data->static_latency_timestamp[3-i][j]) == -1) { json_object_add_value_string(bucket, operation[j], "NA"); } else { convert_ts(le64_to_cpu(log_data->static_latency_timestamp[3-i][j]), ts_buf); json_object_add_value_string(bucket, operation[j], ts_buf); } } json_object_add_value_object(root, buf, bucket); } for (i = 0; i < C3_BUCKET_NUM; i++) { struct json_object *bucket; bucket = json_create_object(); sprintf(buf, "Static Measured Latency: Bucket %d", i); for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], le16_to_cpu(log_data->static_measured_latency[3-i][j])); } json_object_add_value_object(root, buf, bucket); } json_object_add_value_uint(root, "Static Latency Stamp Units", le16_to_cpu(log_data->static_latency_stamp_units)); json_object_add_value_uint(root, "Debug Log Trigger Enable", le16_to_cpu(log_data->debug_log_trigger_enable)); json_object_add_value_uint(root, "Debug Log Measured Latency", le16_to_cpu(log_data->debug_log_measured_latency)); if (le64_to_cpu(log_data->debug_log_latency_stamp) == -1) { json_object_add_value_string(root, "Debug Log Latency Time Stamp", "NA"); } else { convert_ts(le64_to_cpu(log_data->debug_log_latency_stamp), ts_buf); json_object_add_value_string(root, "Debug Log Latency Time Stamp", ts_buf); } json_object_add_value_uint(root, "Debug Log Pointer", le16_to_cpu(log_data->debug_log_ptr)); json_object_add_value_uint(root, "Debug Counter Trigger Source", le16_to_cpu(log_data->debug_log_counter_trigger)); json_object_add_value_uint(root, "Debug Log Stamp Units", le16_to_cpu(log_data->debug_log_stamp_units)); json_object_add_value_uint(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); char guid[(C3_GUID_LENGTH * 2) + 1]; char *ptr = &guid[0]; for (i = C3_GUID_LENGTH - 1; i >= 0; i--) ptr += sprintf(ptr, "%02X", log_data->log_page_guid[i]); json_object_add_value_string(root, "Log Page GUID", guid); json_print_object(root, NULL); printf("\n"); json_free_object(root); } static int get_c3_log_page(struct nvme_dev *dev, char *format) { struct ssd_latency_monitor_log *log_data; enum nvme_print_flags fmt; int ret; __u8 *data; int i; ret = validate_output_format(format, &fmt); if (ret < 0) { fprintf(stderr, "ERROR : OCP : invalid output format\n"); return ret; } data = malloc(sizeof(__u8) * C3_LATENCY_MON_LOG_BUF_LEN); if (!data) { fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); return -1; } memset(data, 0, sizeof(__u8) * C3_LATENCY_MON_LOG_BUF_LEN); ret = nvme_get_log_simple(dev_fd(dev), C3_LATENCY_MON_OPCODE, C3_LATENCY_MON_LOG_BUF_LEN, data); if (strcmp(format, "json")) fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret); if (!ret) { log_data = (struct ssd_latency_monitor_log *)data; /* check log page version */ if (log_data->log_page_version != C3_LATENCY_MON_VERSION) { fprintf(stderr, "ERROR : OCP : invalid latency monitor version\n"); ret = -1; goto out; } /* * check log page guid * Verify GUID matches */ for (i = 0; i < 16; i++) { if (lat_mon_guid[i] != log_data->log_page_guid[i]) { int j; fprintf(stderr, "ERROR : OCP : Unknown GUID in C3 Log Page data\n"); fprintf(stderr, "ERROR : OCP : Expected GUID: 0x"); for (j = 0; j < 16; j++) fprintf(stderr, "%x", lat_mon_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_C3_log_normal(dev, log_data); break; case JSON: ocp_print_C3_log_json(log_data); break; default: fprintf(stderr, "unhandled output format\n"); } } else { fprintf(stderr, "ERROR : OCP : Unable to read C3 data from buffer\n"); } out: free(data); return ret; } static int ocp_latency_monitor_log(int argc, char **argv, struct command *command, struct plugin *plugin) { const char *desc = "Retrieve latency monitor log 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_c3_log_page(dev, cfg.output_format); if (ret) fprintf(stderr, "ERROR : OCP : Failure reading the C3 Log Page, ret = %d\n", ret); dev_close(dev); return ret; } int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) { int err = -1; struct nvme_dev *dev; __u32 result; struct feature_latency_monitor buf = {0,}; __u32 nsid = NVME_NSID_ALL; struct stat nvme_stat; struct nvme_id_ctrl ctrl; const char *desc = "Set Latency Monitor feature."; const char *active_bucket_timer_threshold = "This is the value that loads the Active Bucket Timer Threshold."; const char *active_threshold_a = "This is the value that loads into the Active Threshold A."; const char *active_threshold_b = "This is the value that loads into the Active Threshold B."; const char *active_threshold_c = "This is the value that loads into the Active Threshold C."; const char *active_threshold_d = "This is the value that loads into the Active Threshold D."; const char *active_latency_config = "This is the value that loads into the Active Latency Configuration."; const char *active_latency_minimum_window = "This is the value that loads into the Active Latency Minimum Window."; const char *debug_log_trigger_enable = "This is the value that loads into the Debug Log Trigger Enable."; const char *discard_debug_log = "Discard Debug Log."; const char *latency_monitor_feature_enable = "Latency Monitor Feature Enable."; struct config { __u16 active_bucket_timer_threshold; __u8 active_threshold_a; __u8 active_threshold_b; __u8 active_threshold_c; __u8 active_threshold_d; __u16 active_latency_config; __u8 active_latency_minimum_window; __u16 debug_log_trigger_enable; __u8 discard_debug_log; __u8 latency_monitor_feature_enable; }; struct config cfg = { .active_bucket_timer_threshold = 0x7E0, .active_threshold_a = 0x5, .active_threshold_b = 0x13, .active_threshold_c = 0x1E, .active_threshold_d = 0x2E, .active_latency_config = 0xFFF, .active_latency_minimum_window = 0xA, .debug_log_trigger_enable = 0, .discard_debug_log = 0, .latency_monitor_feature_enable = 0x7, }; OPT_ARGS(opts) = { OPT_UINT("active_bucket_timer_threshold", 't', &cfg.active_bucket_timer_threshold, active_bucket_timer_threshold), OPT_UINT("active_threshold_a", 'a', &cfg.active_threshold_a, active_threshold_a), OPT_UINT("active_threshold_b", 'b', &cfg.active_threshold_b, active_threshold_b), OPT_UINT("active_threshold_c", 'c', &cfg.active_threshold_c, active_threshold_c), OPT_UINT("active_threshold_d", 'd', &cfg.active_threshold_d, active_threshold_d), OPT_UINT("active_latency_config", 'f', &cfg.active_latency_config, active_latency_config), OPT_UINT("active_latency_minimum_window", 'w', &cfg.active_latency_minimum_window, active_latency_minimum_window), OPT_UINT("debug_log_trigger_enable", 'r', &cfg.debug_log_trigger_enable, debug_log_trigger_enable), OPT_UINT("discard_debug_log", 'l', &cfg.discard_debug_log, discard_debug_log), OPT_UINT("latency_monitor_feature_enable", 'e', &cfg.latency_monitor_feature_enable, latency_monitor_feature_enable), OPT_END() }; err = parse_and_open(&dev, argc, argv, desc, opts); if (err) return err; err = fstat(dev_fd(dev), &nvme_stat); if (err < 0) return err; if (S_ISBLK(nvme_stat.st_mode)) { err = nvme_get_nsid(dev_fd(dev), &nsid); if (err < 0) { perror("invalid-namespace-id"); return err; } } err = nvme_identify_ctrl(dev_fd(dev), &ctrl); if (err) return err; memset(&buf, 0, sizeof(struct feature_latency_monitor)); buf.active_bucket_timer_threshold = cfg.active_bucket_timer_threshold; buf.active_threshold_a = cfg.active_threshold_a; buf.active_threshold_b = cfg.active_threshold_b; buf.active_threshold_c = cfg.active_threshold_c; buf.active_threshold_d = cfg.active_threshold_d; buf.active_latency_config = cfg.active_latency_config; buf.active_latency_minimum_window = cfg.active_latency_minimum_window; buf.debug_log_trigger_enable = cfg.debug_log_trigger_enable; buf.discard_debug_log = cfg.discard_debug_log; buf.latency_monitor_feature_enable = cfg.latency_monitor_feature_enable; struct nvme_set_features_args args = { .args_size = sizeof(args), .fd = dev_fd(dev), .fid = NVME_FEAT_OCP_LATENCY_MONITOR, .nsid = 0, .cdw12 = 0, .save = 1, .data_len = sizeof(struct feature_latency_monitor), .data = (void *)&buf, .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .result = &result, }; err = nvme_set_features(&args); if (err < 0) { perror("set-feature"); } else if (!err) { printf("NVME_FEAT_OCP_LATENCY_MONITOR: 0x%02x\n", NVME_FEAT_OCP_LATENCY_MONITOR); printf("active bucket timer threshold: 0x%x\n", buf.active_bucket_timer_threshold); printf("active threshold a: 0x%x\n", buf.active_threshold_a); printf("active threshold b: 0x%x\n", buf.active_threshold_b); printf("active threshold c: 0x%x\n", buf.active_threshold_c); printf("active threshold d: 0x%x\n", buf.active_threshold_d); printf("active latency config: 0x%x\n", buf.active_latency_config); printf("active latency minimum window: 0x%x\n", buf.active_latency_minimum_window); printf("debug log trigger enable: 0x%x\n", buf.debug_log_trigger_enable); printf("discard debug log: 0x%x\n", buf.discard_debug_log); printf("latency monitor feature enable: 0x%x\n", buf.latency_monitor_feature_enable); } else if (err > 0) { fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err, false), err); } return err; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// EOL/PLP Failure Mode static const char *eol_plp_failure_mode_to_string(__u8 mode) { switch (mode) { case 1: return "Read only mode (ROM)"; case 2: return "Write through mode (WTM)"; case 3: return "Normal mode"; default: break; } return "Reserved"; } static int eol_plp_failure_mode_get(struct nvme_dev *dev, const __u32 nsid, const __u8 fid, __u8 sel) { __u32 result; int err; struct nvme_get_features_args args = { .args_size = sizeof(args), .fd = dev_fd(dev), .fid = fid, .nsid = nsid, .sel = sel, .cdw11 = 0, .uuidx = 0, .data_len = 0, .data = NULL, .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .result = &result, }; err = nvme_get_features(&args); if (!err) { nvme_show_result("End of Life Behavior (feature: %#0*x): %#0*x (%s: %s)", fid ? 4 : 2, fid, result ? 10 : 8, result, nvme_select_to_string(sel), eol_plp_failure_mode_to_string(result)); if (sel == NVME_GET_FEATURES_SEL_SUPPORTED) nvme_show_select_result(fid, result); } else { nvme_show_error("Could not get feature: %#0*x.", fid ? 4 : 2, fid); } return err; } static int eol_plp_failure_mode_set(struct nvme_dev *dev, const __u32 nsid, const __u8 fid, __u8 mode, bool save, 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_set_features_args args = { .args_size = sizeof(args), .fd = dev_fd(dev), .fid = fid, .nsid = nsid, .cdw11 = mode << 30, .cdw12 = 0, .save = 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 EOL/PLP failure mode"); fprintf(stderr, "Command failed while parsing.\n"); } else { nvme_show_result("Successfully set mode (feature: %#0*x): %#0*x (%s: %s).", fid ? 4 : 2, fid, mode ? 10 : 8, mode, save ? "Save" : "Not save", eol_plp_failure_mode_to_string(mode)); } return err; } static int eol_plp_failure_mode(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Define EOL or PLP circuitry failure mode.\n" "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 __u32 nsid = 0; const __u8 fid = 0xc2; struct nvme_dev *dev; int err; struct config { __u8 mode; bool save; __u8 sel; }; struct config cfg = { .mode = 0, .save = false, .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() }; err = parse_and_open(&dev, argc, argv, desc, opts); if (err) return err; if (argconfig_parse_seen(opts, "mode")) err = eol_plp_failure_mode_set(dev, nsid, fid, cfg.mode, cfg.save, !argconfig_parse_seen(opts, "no-uuid")); else err = eol_plp_failure_mode_get(dev, nsid, fid, cfg.sel); dev_close(dev); return err; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Telemetry Log #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, }; 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; /* Remove trailing spaces from the name */ for (i = 0; i < sizeof(ctrl->sn); i++) { if (ctrl->sn[i] == ' ') break; sn[i] = ctrl->sn[i]; } } 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; if (tele_type == TELEMETRY_TYPE_HOST) printf("============ Telemetry Host Header ============\n"); else printf("========= Telemetry Controller Header =========\n"); printf("Log Identifier : 0x%02X\n", logheader->LogIdentifier); printf("IEEE : 0x%02X%02X%02X\n", logheader->IEEE[0], logheader->IEEE[1], logheader->IEEE[2]); printf("Data Area 1 Last Block : 0x%04X\n", le16_to_cpu(logheader->DataArea1LastBlock)); printf("Data Area 2 Last Block : 0x%04X\n", 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("Reason Identifier :\n"); for (i = 0; i < 8; i++) { for (j = 0; j < 16; j++) printf("%02X ", logheader->ReasonIdentifier[127 - ((i * 16) + j)]); printf("\n"); } 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) { 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 = offset; cmd.cdw13 = 0; cmd.cdw14 = 0; return nvme_submit_admin_passthru(dev_fd(dev), &cmd, NULL); } static void print_telemetry_data_area_1(struct telemetry_data_area_1 *da1, int tele_type) { if (da1) { unsigned 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("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)); 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("===============================================\n\n"); } } static void print_telemetry_da2_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_da2_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 Statistics ============\n"); 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("===============================================\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) { int i = 0, err = 0; char *data = calloc(transfersize, sizeof(char)); char filepath[FILE_NAME_SIZE] = {0,}; int output = 0; int total_loop_cnt = dumpsize / transfersize; int last_xfer_size = dumpsize % transfersize; if (last_xfer_size) total_loop_cnt++; else last_xfer_size = transfersize; if (filename == 0) snprintf(filepath, FILE_NAME_SIZE, "%s_%s.bin", featurename, sn); else snprintf(filepath, FILE_NAME_SIZE, "%s%s_%s.bin", filename, featurename, sn); for (i = 0; i < total_loop_cnt; i++) { memset(data, 0, transfersize); struct nvme_get_log_args args = { .lpo = offset, .result = NULL, .log = (void *)data, .args_size = sizeof(args), .fd = dev_fd(dev), .lid = log_id, .len = transfersize, .nsid = nsid, .lsp = lsp, .uuidx = 0, .rae = rae, .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .csi = NVME_CSI_NVM, .ot = false, }; err = nvme_get_log(&args); if (err) { if (i > 0) goto close_output; else goto end; } if (i != total_loop_cnt - 1) { if (!i) { output = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (output < 0) { err = -13; goto end; } } if (write(output, data, transfersize) < 0) { err = -10; goto close_output; } } else { if (write(output, data, last_xfer_size) < 0) { err = -10; goto close_output; } } offset += transfersize; printf("%d%%\r", (i + 1) * 100 / total_loop_cnt); } printf("100%%\nThe log file was saved at \"%s\"\n", filepath); close_output: close(output); end: free(data); return err; } 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; unsigned int i = 0; char data[TELEMETRY_TRANSFER_SIZE] = { 0 }; char data1[1536] = { 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; __u64 offset = 0, size = 0; char dumpname[FILE_NAME_SIZE] = { 0 }; if (tele_type == TELEMETRY_TYPE_HOST_0) { featurename = "Host(0)"; lsp = 0; rae = 0; tele_type = TELEMETRY_TYPE_HOST; } else if (tele_type == TELEMETRY_TYPE_HOST_1) { featurename = "Host(1)"; lsp = 1; rae = 0; tele_type = TELEMETRY_TYPE_HOST; } else { featurename = "Controller"; lsp = 0; rae = 1; } err = get_telemetry_header(dev, nsid, tele_type, TELEMETRY_HEADER_SIZE, (void *)data, lsp, rae); if (err) return err; if (header_print) print_telemetry_header(logheader, tele_type); err = get_telemetry_data(dev, nsid, tele_type, 1536, (void *)data1, lsp, rae, 512); if (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) return err; print_telemetry_da1_fifo((void *)da1_fifo, tele_type, (da1->event_fifo_size[i]) * 4); } } 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) return err; print_telemetry_da2_fifo((void *)da1_fifo, tele_type, (da1->event_fifo_size[i]) * 4); } } switch (data_area) { case 1: offset = TELEMETRY_HEADER_SIZE; size = le16_to_cpu(logheader->DataArea1LastBlock); break; case 2: offset = TELEMETRY_HEADER_SIZE + (le16_to_cpu(logheader->DataArea1LastBlock) * TELEMETRY_BYTE_PER_BLOCK); size = le16_to_cpu(logheader->DataArea2LastBlock) - le16_to_cpu(logheader->DataArea1LastBlock); break; case 3: offset = TELEMETRY_HEADER_SIZE + (le16_to_cpu(logheader->DataArea2LastBlock) * TELEMETRY_BYTE_PER_BLOCK); size = le16_to_cpu(logheader->DataArea3LastBlock) - le16_to_cpu(logheader->DataArea2LastBlock); break; default: break; } if (!size) { printf("Telemetry %s Area %d is empty.\n", featurename, data_area); return err; } snprintf(dumpname, FILE_NAME_SIZE, "Telemetry_%s_Area_%d", featurename, data_area); err = extract_dump_get_log(dev, dumpname, filename, sn, size * TELEMETRY_BYTE_PER_BLOCK, TELEMETRY_TRANSFER_SIZE, nsid, tele_type, 0, offset, rae); return err; } 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" "e.g. '-o ./path/name'\n'-o ./path1/path2/';\n" "If requested path does not exist, the directory will be newly created."; __u32 nsid = NVME_NSID_ALL; struct stat nvme_stat; char sn[21] = {0,}; struct nvme_id_ctrl ctrl; bool is_support_telemetry_controller; 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_END() }; err = parse_and_open(&dev, argc, argv, desc, opts); if (err) return err; err = fstat(dev_fd(dev), &nvme_stat); if (err < 0) return err; if (S_ISBLK(nvme_stat.st_mode)) { err = nvme_get_nsid(dev_fd(dev), &nsid); if (err < 0) return err; } err = nvme_identify_ctrl(dev_fd(dev), &ctrl); if (err) return err; get_serial_number(&ctrl, sn); 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")) tele_type = TELEMETRY_TYPE_HOST_0; else if (!strcmp(cfg.type, "host1")) tele_type = TELEMETRY_TYPE_HOST_1; else if (!strcmp(cfg.type, "controller")) tele_type = TELEMETRY_TYPE_CONTROLLER; tele_area = cfg.area; 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; } } else { printf("\nShould provide these all; 'telemetry_type' and 'telemetry_data_area'\n"); return err; } if (tele_type == 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, TELEMETRY_TYPE_HOST_0, 1, true); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); printf("\n-------------------------------------------------------------\n"); printf("\nExtracting Telemetry Host 0 Dump (Data Area 3)...\n"); err = get_telemetry_dump(dev, cfg.file, sn, TELEMETRY_TYPE_HOST_0, 3, false); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); printf("\n-------------------------------------------------------------\n"); printf("\nExtracting Telemetry Host 1 Dump (Data Area 1)...\n"); err = get_telemetry_dump(dev, cfg.file, sn, TELEMETRY_TYPE_HOST_1, 1, true); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); printf("\n-------------------------------------------------------------\n"); printf("\nExtracting Telemetry Host 1 Dump (Data Area 3)...\n"); err = get_telemetry_dump(dev, cfg.file, sn, TELEMETRY_TYPE_HOST_1, 3, false); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); printf("\n-------------------------------------------------------------\n"); printf("\nExtracting Telemetry Controller Dump (Data Area 3)...\n"); if (is_support_telemetry_controller == true) { err = get_telemetry_dump(dev, cfg.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 { 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); if (err) fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err); } printf("telemetry-log done.\n"); return err; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Unsupported Requirement Log Page (LID : C5h) /* C5 Unsupported Requirement Log Page */ #define C5_GUID_LENGTH 16 #define C5_UNSUPPORTED_REQS_LEN 4096 #define C5_UNSUPPORTED_REQS_OPCODE 0xC5 #define C5_UNSUPPORTED_REQS_LOG_VERSION 0x1 #define C5_NUM_UNSUPPORTED_REQ_ENTRIES 253 static __u8 unsupported_req_guid[C5_GUID_LENGTH] = { 0x2F, 0x72, 0x9C, 0x0E, 0x99, 0x23, 0x2C, 0xBB, 0x63, 0x48, 0x32, 0xD0, 0xB7, 0x98, 0xBB, 0xC7 }; /* * struct unsupported_requirement_log - unsupported requirement list * @unsupported_count: Number of Unsupported Requirement IDs * @rsvd1: Reserved * @unsupported_req_list: Unsupported Requirements lists upto 253. * @rsvd2: Reserved * @log_page_version: indicates the version of the mapping this log page uses. * Shall be set to 0001h * @log_page_guid: Shall be set to C7BB98B7D0324863BB2C23990E9C722Fh. */ struct __packed unsupported_requirement_log { __le16 unsupported_count; __u8 rsvd1[14]; __u8 unsupported_req_list[C5_NUM_UNSUPPORTED_REQ_ENTRIES][16]; __u8 rsvd2[14]; __le16 log_page_version; __u8 log_page_guid[C5_GUID_LENGTH]; }; /* Function declaration for unsupported requirement log page (LID:C5h) */ static int ocp_unsupported_requirements_log(int argc, char **argv, struct command *cmd, struct plugin *plugin); static int ocp_print_C5_log_normal(struct nvme_dev *dev, struct unsupported_requirement_log *log_data) { int j; printf("Unsupported Requirement-C5 Log Page Data-\n"); printf(" Number Unsupported Req IDs : 0x%x\n", le16_to_cpu(log_data->unsupported_count)); for (j = 0; j < le16_to_cpu(log_data->unsupported_count); j++) printf(" Unsupported Requirement List %d : %s\n", j, log_data->unsupported_req_list[j]); printf(" Log Page Version : 0x%x\n", le16_to_cpu(log_data->log_page_version)); printf(" Log page GUID : 0x"); for (j = C5_GUID_LENGTH - 1; j >= 0; j--) printf("%x", log_data->log_page_guid[j]); printf("\n"); return 0; } static void ocp_print_C5_log_json(struct unsupported_requirement_log *log_data) { int j; struct json_object *root; char unsup_req_list_str[40]; char guid_buf[C5_GUID_LENGTH]; char *guid = guid_buf; root = json_create_object(); json_object_add_value_int(root, "Number Unsupported Req IDs", le16_to_cpu(log_data->unsupported_count)); memset((void *)unsup_req_list_str, 0, 40); for (j = 0; j < le16_to_cpu(log_data->unsupported_count); j++) { sprintf((char *)unsup_req_list_str, "Unsupported Requirement List %d", j); json_object_add_value_string(root, unsup_req_list_str, (char *)log_data->unsupported_req_list[j]); } json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); memset((void *)guid, 0, C5_GUID_LENGTH); for (j = C5_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 void ocp_print_c5_log_binary(struct unsupported_requirement_log *log_data) { return d_raw((unsigned char *)log_data, sizeof(*log_data)); } static int get_c5_log_page(struct nvme_dev *dev, char *format) { enum nvme_print_flags fmt; int ret; __u8 *data; int i; struct unsupported_requirement_log *log_data; int j; ret = validate_output_format(format, &fmt); if (ret < 0) { fprintf(stderr, "ERROR : OCP : invalid output format\n"); return ret; } data = (__u8 *)malloc(sizeof(__u8) * C5_UNSUPPORTED_REQS_LEN); if (!data) { fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); return -1; } memset(data, 0, sizeof(__u8) * C5_UNSUPPORTED_REQS_LEN); ret = nvme_get_log_simple(dev_fd(dev), C5_UNSUPPORTED_REQS_OPCODE, C5_UNSUPPORTED_REQS_LEN, data); if (!ret) { log_data = (struct unsupported_requirement_log *)data; /* check log page version */ if (log_data->log_page_version != C5_UNSUPPORTED_REQS_LOG_VERSION) { fprintf(stderr, "ERROR : OCP : invalid unsupported requirement version\n"); ret = -1; goto out; } /* * check log page guid * Verify GUID matches */ for (i = 0; i < 16; i++) { if (unsupported_req_guid[i] != log_data->log_page_guid[i]) { fprintf(stderr, "ERROR : OCP : Unknown GUID in C5 Log Page data\n"); fprintf(stderr, "ERROR : OCP : Expected GUID: 0x"); for (j = 0; j < 16; j++) fprintf(stderr, "%x", unsupported_req_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_C5_log_normal(dev, log_data); break; case JSON: ocp_print_C5_log_json(log_data); break; case BINARY: ocp_print_c5_log_binary(log_data); break; default: break; } } else { fprintf(stderr, "ERROR : OCP : Unable to read C3 data from buffer\n"); } out: free(data); return ret; } static int ocp_unsupported_requirements_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve unsupported requirements log 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_c5_log_page(dev, cfg.output_format); if (ret) fprintf(stderr, "ERROR : OCP : Failure reading the C5 Log Page, ret = %d\n", ret); dev_close(dev); return ret; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Error Recovery Log Page(0xC1) #define C1_ERROR_RECOVERY_LOG_BUF_LEN 0x200 #define C1_ERROR_RECOVERY_OPCODE 0xC1 #define C1_ERROR_RECOVERY_VERSION 0x0002 #define C1_GUID_LENGTH 16 static __u8 error_recovery_guid[C1_GUID_LENGTH] = { 0x44, 0xd9, 0x31, 0x21, 0xfe, 0x30, 0x34, 0xae, 0xab, 0x4d, 0xfd, 0x3d, 0xba, 0x83, 0x19, 0x5a }; /** * struct ocp_error_recovery_log_page - Error Recovery Log Page * @panic_reset_wait_time: Panic Reset Wait Time * @panic_reset_action: Panic Reset Action * @device_recover_action_1: Device Recovery Action 1 * @panic_id: Panic ID * @device_capabilities: Device Capabilities * @vendor_specific_recovery_opcode: Vendor Specific Recovery Opcode * @reserved: Reserved * @vendor_specific_command_cdw12: Vendor Specific Command CDW12 * @vendor_specific_command_cdw13: Vendor Specific Command CDW13 * @vendor_specific_command_timeout: Vendor Specific Command Timeout * @device_recover_action_2: Device Recovery Action 2 * @device_recover_action_2_timeout: Device Recovery Action 2 Timeout * @reserved2: Reserved * @log_page_version: Log Page Version * @log_page_guid: Log Page GUID */ struct __packed ocp_error_recovery_log_page { __le16 panic_reset_wait_time; /* 2 bytes - 0x00 - 0x01 */ __u8 panic_reset_action; /* 1 byte - 0x02 */ __u8 device_recover_action_1; /* 1 byte - 0x03 */ __le64 panic_id; /* 8 bytes - 0x04 - 0x0B */ __le32 device_capabilities; /* 4 bytes - 0x0C - 0x0F */ __u8 vendor_specific_recovery_opcode; /* 1 byte - 0x10 */ __u8 reserved[0x3]; /* 3 bytes - 0x11 - 0x13 */ __le32 vendor_specific_command_cdw12; /* 4 bytes - 0x14 - 0x17 */ __le32 vendor_specific_command_cdw13; /* 4 bytes - 0x18 - 0x1B */ __u8 vendor_specific_command_timeout; /* 1 byte - 0x1C */ __u8 device_recover_action_2; /* 1 byte - 0x1D */ __u8 device_recover_action_2_timeout; /* 1 byte - 0x1E */ __u8 reserved2[0x1cf]; /* 463 bytes - 0x1F - 0x1ED */ __le16 log_page_version; /* 2 bytes - 0x1EE - 0x1EF */ __u8 log_page_guid[0x10]; /* 16 bytes - 0x1F0 - 0x1FF */ }; static void ocp_print_c1_log_normal(struct ocp_error_recovery_log_page *log_data); static void ocp_print_c1_log_json(struct ocp_error_recovery_log_page *log_data); 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); static int ocp_error_recovery_log(int argc, char **argv, struct command *cmd, struct plugin *plugin); static void ocp_print_c1_log_normal(struct ocp_error_recovery_log_page *log_data) { int i; printf(" Error Recovery/C1 Log Page Data\n"); printf(" Panic Reset Wait Time : 0x%x\n", le16_to_cpu(log_data->panic_reset_wait_time)); printf(" Panic Reset Action : 0x%x\n", log_data->panic_reset_action); printf(" Device Recovery Action 1 : 0x%x\n", log_data->device_recover_action_1); printf(" Panic ID : 0x%x\n", le32_to_cpu(log_data->panic_id)); printf(" Device Capabilities : 0x%x\n", le32_to_cpu(log_data->device_capabilities)); printf(" Vendor Specific Recovery Opcode : 0x%x\n", log_data->vendor_specific_recovery_opcode); printf(" Vendor Specific Command CDW12 : 0x%x\n", le32_to_cpu(log_data->vendor_specific_command_cdw12)); printf(" Vendor Specific Command CDW13 : 0x%x\n", le32_to_cpu(log_data->vendor_specific_command_cdw13)); printf(" Vendor Specific Command Timeout : 0x%x\n", log_data->vendor_specific_command_timeout); printf(" Device Recovery Action 2 : 0x%x\n", log_data->device_recover_action_2); printf(" Device Recovery Action 2 Timeout : 0x%x\n", log_data->device_recover_action_2_timeout); printf(" Log Page Version : 0x%x\n", le16_to_cpu(log_data->log_page_version)); printf(" Log page GUID : 0x"); for (i = C1_GUID_LENGTH - 1; i >= 0; i--) printf("%x", log_data->log_page_guid[i]); printf("\n"); } static void ocp_print_c1_log_json(struct ocp_error_recovery_log_page *log_data) { struct json_object *root; root = json_create_object(); char guid[64]; json_object_add_value_int(root, "Panic Reset Wait Time", le16_to_cpu(log_data->panic_reset_wait_time)); json_object_add_value_int(root, "Panic Reset Action", log_data->panic_reset_action); json_object_add_value_int(root, "Device Recovery Action 1", log_data->device_recover_action_1); json_object_add_value_int(root, "Panic ID", le32_to_cpu(log_data->panic_id)); json_object_add_value_int(root, "Device Capabilities", le32_to_cpu(log_data->device_capabilities)); json_object_add_value_int(root, "Vendor Specific Recovery Opcode", log_data->vendor_specific_recovery_opcode); json_object_add_value_int(root, "Vendor Specific Command CDW12", le32_to_cpu(log_data->vendor_specific_command_cdw12)); json_object_add_value_int(root, "Vendor Specific Command CDW13", le32_to_cpu(log_data->vendor_specific_command_cdw13)); json_object_add_value_int(root, "Vendor Specific Command Timeout", log_data->vendor_specific_command_timeout); json_object_add_value_int(root, "Device Recovery Action 2", log_data->device_recover_action_2); json_object_add_value_int(root, "Device Recovery Action 2 Timeout", log_data->device_recover_action_2_timeout); json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); memset((void *)guid, 0, 64); 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); json_print_object(root, NULL); printf("\n"); json_free_object(root); } static void ocp_print_c1_log_binary(struct ocp_error_recovery_log_page *log_data) { return d_raw((unsigned char *)log_data, sizeof(*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; int ret; __u8 *data; int i, j; ret = validate_output_format(format, &fmt); if (ret < 0) { fprintf(stderr, "ERROR : OCP : invalid output format\n"); return ret; } data = (__u8 *)malloc(sizeof(__u8) * C1_ERROR_RECOVERY_LOG_BUF_LEN); if (!data) { fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); return -1; } memset(data, 0, sizeof(__u8) * C1_ERROR_RECOVERY_LOG_BUF_LEN); ret = nvme_get_log_simple(dev_fd(dev), C1_ERROR_RECOVERY_OPCODE, C1_ERROR_RECOVERY_LOG_BUF_LEN, data); if (!ret) { log_data = (struct ocp_error_recovery_log_page *)data; /* check log page version */ if (log_data->log_page_version != C1_ERROR_RECOVERY_VERSION) { fprintf(stderr, "ERROR : OCP : invalid error recovery log page version\n"); ret = -1; goto out; } /* * check log page guid * Verify GUID matches */ for (i = 0; i < 16; i++) { if (error_recovery_guid[i] != log_data->log_page_guid[i]) { fprintf(stderr, "ERROR : OCP : Unknown GUID in C1 Log Page data\n"); fprintf(stderr, "ERROR : OCP : Expected GUID: 0x"); for (j = 0; j < 16; j++) fprintf(stderr, "%x", error_recovery_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_c1_log_normal(log_data); break; case JSON: ocp_print_c1_log_json(log_data); break; case BINARY: ocp_print_c1_log_binary(log_data); break; default: break; } } else { fprintf(stderr, "ERROR : OCP : Unable to read C1 data from buffer\n"); } out: free(data); return ret; } static int ocp_error_recovery_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve C1h Error Recovery Log 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|binary"), OPT_END() }; ret = parse_and_open(&dev, argc, argv, desc, opts); if (ret) return ret; ret = get_c1_log_page(dev, cfg.output_format); if (ret) fprintf(stderr, "ERROR : OCP : Failure reading the C1h Log Page, ret = %d\n", ret); dev_close(dev); return ret; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Device Capabilities (Log Identifier C4h) Requirements #define C4_DEV_CAP_REQ_LEN 0x1000 #define C4_DEV_CAP_REQ_OPCODE 0xC4 #define C4_DEV_CAP_REQ_VERSION 0x0001 #define C4_GUID_LENGTH 16 static __u8 dev_cap_req_guid[C4_GUID_LENGTH] = { 0x97, 0x42, 0x05, 0x0d, 0xd1, 0xe1, 0xc9, 0x98, 0x5d, 0x49, 0x58, 0x4b, 0x91, 0x3c, 0x05, 0xb7 }; /** * struct ocp_device_capabilities_log_page - Device Capability Log page * @pcie_exp_port: PCI Express Ports * @oob_management_support: OOB Management Support * @wz_cmd_support: Write Zeroes Command Support * @sanitize_cmd_support: Sanitize Command Support * @dsm_cmd_support: Dataset Management Command Support * @wu_cmd_support: Write Uncorrectable Command Support * @fused_operation_support: Fused Operation Support * @min_valid_dssd_pwr_state: Minimum Valid DSSD Power State * @dssd_pwr_state_desc: DSSD Power State Descriptors * @vendor_specific_command_timeout: Vendor Specific Command Timeout * @reserved: Reserved * @log_page_version: Log Page Version * @log_page_guid: Log Page GUID */ struct __packed ocp_device_capabilities_log_page { __le16 pcie_exp_port; __le16 oob_management_support; __le16 wz_cmd_support; __le16 sanitize_cmd_support; __le16 dsm_cmd_support; __le16 wu_cmd_support; __le16 fused_operation_support; __le16 min_valid_dssd_pwr_state; __u8 dssd_pwr_state_desc[128]; __u8 reserved[3934]; __le16 log_page_version; __u8 log_page_guid[16]; }; static void ocp_print_c4_log_normal(struct ocp_device_capabilities_log_page *log_data); static void ocp_print_c4_log_json(struct ocp_device_capabilities_log_page *log_data); static void ocp_print_c4_log_binary(struct ocp_device_capabilities_log_page *log_data); static int get_c4_log_page(struct nvme_dev *dev, char *format); static int ocp_device_capabilities_log(int argc, char **argv, struct command *cmd, struct plugin *plugin); static void ocp_print_c4_log_normal(struct ocp_device_capabilities_log_page *log_data) { int i; printf(" Device Capability/C4 Log Page Data\n"); printf(" PCI Express Ports : 0x%x\n", le16_to_cpu(log_data->pcie_exp_port)); printf(" OOB Management Support : 0x%x\n", le16_to_cpu(log_data->oob_management_support)); printf(" Write Zeroes Command Support : 0x%x\n", le16_to_cpu(log_data->wz_cmd_support)); printf(" Sanitize Command Support : 0x%x\n", le16_to_cpu(log_data->sanitize_cmd_support)); printf(" Dataset Management Command Support : 0x%x\n", le16_to_cpu(log_data->dsm_cmd_support)); printf(" Write Uncorrectable Command Support : 0x%x\n", le16_to_cpu(log_data->wu_cmd_support)); printf(" Fused Operation Support : 0x%x\n", le16_to_cpu(log_data->fused_operation_support)); printf(" Minimum Valid DSSD Power State : 0x%x\n", le16_to_cpu(log_data->min_valid_dssd_pwr_state)); printf(" DSSD Power State Descriptors : 0x"); for (i = 0; i <= 127; i++) printf("%x", log_data->dssd_pwr_state_desc[i]); printf("\n"); printf(" Log Page Version : 0x%x\n", le16_to_cpu(log_data->log_page_version)); printf(" Log page GUID : 0x"); for (i = C4_GUID_LENGTH - 1; i >= 0; i--) printf("%x", log_data->log_page_guid[i]); printf("\n"); } static void ocp_print_c4_log_json(struct ocp_device_capabilities_log_page *log_data) { struct json_object *root = json_create_object(); char guid[64]; int i; json_object_add_value_int(root, "PCI Express Ports", le16_to_cpu(log_data->pcie_exp_port)); json_object_add_value_int(root, "OOB Management Support", le16_to_cpu(log_data->oob_management_support)); json_object_add_value_int(root, "Write Zeroes Command Support", le16_to_cpu(log_data->wz_cmd_support)); json_object_add_value_int(root, "Sanitize Command Support", le16_to_cpu(log_data->sanitize_cmd_support)); json_object_add_value_int(root, "Dataset Management Command Support", le16_to_cpu(log_data->dsm_cmd_support)); json_object_add_value_int(root, "Write Uncorrectable Command Support", le16_to_cpu(log_data->wu_cmd_support)); json_object_add_value_int(root, "Fused Operation Support", le16_to_cpu(log_data->fused_operation_support)); json_object_add_value_int(root, "Minimum Valid DSSD Power State", le16_to_cpu(log_data->min_valid_dssd_pwr_state)); for (i = 0; i <= 127; i++) json_object_add_value_int(root, "DSSD Power State Descriptors", log_data->dssd_pwr_state_desc[i]); json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version)); memset((void *)guid, 0, 64); 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); json_print_object(root, NULL); printf("\n"); json_free_object(root); } static void ocp_print_c4_log_binary(struct ocp_device_capabilities_log_page *log_data) { return d_raw((unsigned char *)log_data, sizeof(*log_data)); } 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; int ret; __u8 *data; int i, j; ret = validate_output_format(format, &fmt); if (ret < 0) { fprintf(stderr, "ERROR : OCP : invalid output format\n"); return ret; } data = (__u8 *)malloc(sizeof(__u8) * C4_DEV_CAP_REQ_LEN); if (!data) { fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno)); return -1; } memset(data, 0, sizeof(__u8) * C4_DEV_CAP_REQ_LEN); ret = nvme_get_log_simple(dev_fd(dev), C4_DEV_CAP_REQ_OPCODE, C4_DEV_CAP_REQ_LEN, data); if (!ret) { log_data = (struct ocp_device_capabilities_log_page *)data; /* check log page version */ if (log_data->log_page_version != C4_DEV_CAP_REQ_VERSION) { fprintf(stderr, "ERROR : OCP : invalid device capabilities log page version\n"); ret = -1; goto out; } /* * check log page guid * Verify GUID matches */ for (i = 0; i < 16; i++) { if (dev_cap_req_guid[i] != log_data->log_page_guid[i]) { fprintf(stderr, "ERROR : OCP : Unknown GUID in C4 Log Page data\n"); fprintf(stderr, "ERROR : OCP : Expected GUID: 0x"); for (j = 0; j < 16; j++) fprintf(stderr, "%x", dev_cap_req_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_c4_log_normal(log_data); break; case JSON: ocp_print_c4_log_json(log_data); break; case BINARY: ocp_print_c4_log_binary(log_data); break; default: break; } } else { fprintf(stderr, "ERROR : OCP : Unable to read C4 data from buffer\n"); } out: free(data); return ret; } static int ocp_device_capabilities_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve C4h Device Capabilities Log 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|binary"), OPT_END() }; ret = parse_and_open(&dev, argc, argv, desc, opts); if (ret) return ret; ret = get_c4_log_page(dev, cfg.output_format); if (ret) fprintf(stderr, "ERROR : OCP : Failure reading the C4h Log Page, ret = %d\n", ret); dev_close(dev); return ret; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// 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, const __u8 fid, __u8 power_state, bool save, 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_set_features_args args = { .args_size = sizeof(args), .fd = dev_fd(dev), .fid = fid, .nsid = nsid, .cdw11 = power_state, .cdw12 = 0, .save = 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 DSSD Power State"); fprintf(stderr, "Command failed while parsing.\n"); } else { printf("Successfully set DSSD Power State (feature: 0xC7) to below values\n"); printf("DSSD Power State: 0x%x\n", power_state); printf("Save bit Value: 0x%x\n", save); } return err; } static int set_dssd_power_state_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Define DSSD Power State (Feature Identifier C7h) Set Feature."; const char *power_state = "DSSD Power State to set in watts"; const char *save = "Specifies that the controller shall save the attribute"; const __u32 nsid = 0; const __u8 fid = 0xC7; struct nvme_dev *dev; int err; struct config { __u8 power_state; bool save; }; struct config cfg = { .power_state = 0, .save = false, }; OPT_ARGS(opts) = { OPT_BYTE("power-state", 'p', &cfg.power_state, power_state), 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, "power-state")) err = set_dssd_power_state(dev, nsid, fid, cfg.power_state, cfg.save, !argconfig_parse_seen(opts, "no-uuid")); 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; __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; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// 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; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// 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 /** * 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; }; /* * 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; }; /* * 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 __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; }; /* 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); } static int get_c9_log_page(struct nvme_dev *dev, char *format) { 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; } 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; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /// Misc static int clear_fw_update_history(int argc, char **argv, struct command *cmd, struct plugin *plugin) { return ocp_clear_fw_update_history(argc, argv, cmd, plugin); } static int smart_add_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { return ocp_smart_add_log(argc, argv, cmd, plugin); } static int clear_pcie_correctable_error_counters(int argc, char **argv, struct command *cmd, struct plugin *plugin) { return ocp_clear_pcie_correctable_errors(argc, argv, cmd, plugin); } static int fw_activation_history_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { return ocp_fw_activation_history_log(argc, argv, cmd, plugin); }