diff options
Diffstat (limited to 'plugins/ocp')
-rw-r--r-- | plugins/ocp/meson.build | 1 | ||||
-rw-r--r-- | plugins/ocp/ocp-clear-fw-update-history.c | 3 | ||||
-rw-r--r-- | plugins/ocp/ocp-fw-activation-history.c | 223 | ||||
-rw-r--r-- | plugins/ocp/ocp-fw-activation-history.h | 17 | ||||
-rw-r--r-- | plugins/ocp/ocp-nvme.c | 1550 | ||||
-rw-r--r-- | plugins/ocp/ocp-nvme.h | 6 | ||||
-rw-r--r-- | plugins/ocp/ocp-smart-extended-log.c | 16 |
7 files changed, 1656 insertions, 160 deletions
diff --git a/plugins/ocp/meson.build b/plugins/ocp/meson.build index 641239a..405ee51 100644 --- a/plugins/ocp/meson.build +++ b/plugins/ocp/meson.build @@ -3,5 +3,6 @@ sources += [ 'plugins/ocp/ocp-nvme.c', 'plugins/ocp/ocp-clear-fw-update-history.c', 'plugins/ocp/ocp-smart-extended-log.c', + 'plugins/ocp/ocp-fw-activation-history.c', ] diff --git a/plugins/ocp/ocp-clear-fw-update-history.c b/plugins/ocp/ocp-clear-fw-update-history.c index b9235b8..6b256b1 100644 --- a/plugins/ocp/ocp-clear-fw-update-history.c +++ b/plugins/ocp/ocp-clear-fw-update-history.c @@ -16,6 +16,5 @@ int ocp_clear_fw_update_history(int argc, char **argv, struct command *cmd, stru { const char *desc = "OCP Clear Firmware Update History"; - return ocp_clear_feature(argc, argv, desc, - OCP_FID_CLEAR_FW_ACTIVATION_HISTORY); + return ocp_clear_feature(argc, argv, desc, OCP_FID_CLEAR_FW_ACTIVATION_HISTORY); } diff --git a/plugins/ocp/ocp-fw-activation-history.c b/plugins/ocp/ocp-fw-activation-history.c new file mode 100644 index 0000000..b067346 --- /dev/null +++ b/plugins/ocp/ocp-fw-activation-history.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: karl.dedow@solidigm.com + */ + +#include "ocp-fw-activation-history.h" + +#include <errno.h> +#include <stdio.h> + +#include "common.h" +#include "nvme-print.h" + +#include "ocp-utils.h" + +static const unsigned char ocp_fw_activation_history_guid[16] = { + 0x6D, 0x79, 0x9a, 0x76, + 0xb4, 0xda, 0xf6, 0xa3, + 0xe2, 0x4d, 0xb2, 0x8a, + 0xac, 0xf3, 0x1c, 0xd1 +}; + +struct __packed fw_activation_history_entry { + __u8 ver_num; + __u8 entry_length; + __u16 reserved1; + __u16 activation_count; + __u64 timestamp; + __u64 reserved2; + __u64 power_cycle_count; + char previous_fw[8]; + char new_fw[8]; + __u8 slot_number; + __u8 commit_action; + __u16 result; + __u8 reserved3[14]; +}; + +struct __packed fw_activation_history { + __u8 log_id; + __u8 reserved1[3]; + __u32 valid_entries; + struct fw_activation_history_entry entries[20]; + __u8 reserved2[2790]; + __u16 log_page_version; + __u64 log_page_guid[2]; +}; + +static void ocp_fw_activation_history_normal(const struct fw_activation_history *fw_history) +{ + printf("Firmware History Log:\n"); + + printf(" %-26s%d\n", "log identifier:", fw_history->log_id); + printf(" %-26s%d\n", "valid entries:", le32_to_cpu(fw_history->valid_entries)); + + printf(" entries:\n"); + + for (int index = 0; index < fw_history->valid_entries; index++) { + const struct fw_activation_history_entry *entry = &fw_history->entries[index]; + + printf(" entry[%d]:\n", le32_to_cpu(index)); + printf(" %-22s%d\n", "version number:", entry->ver_num); + printf(" %-22s%d\n", "entry length:", entry->entry_length); + printf(" %-22s%d\n", "activation count:", + le16_to_cpu(entry->activation_count)); + printf(" %-22s%"PRIu64"\n", "timestamp:", + le64_to_cpu(entry->timestamp)); + printf(" %-22s%"PRIu64"\n", "power cycle count:", + le64_to_cpu(entry->power_cycle_count)); + printf(" %-22s%.*s\n", "previous firmware:", (int)sizeof(entry->previous_fw), + entry->previous_fw); + printf(" %-22s%.*s\n", "new firmware:", (int)sizeof(entry->new_fw), + entry->new_fw); + printf(" %-22s%d\n", "slot number:", entry->slot_number); + printf(" %-22s%d\n", "commit action type:", entry->commit_action); + printf(" %-22s%d\n", "result:", le16_to_cpu(entry->result)); + } + + printf(" %-26s%d\n", "log page version:", + le16_to_cpu(fw_history->log_page_version)); + + printf(" %-26s0x%"PRIx64"%"PRIx64"\n", "log page guid:", + le64_to_cpu(fw_history->log_page_guid[1]), + le64_to_cpu(fw_history->log_page_guid[0])); + + printf("\n"); +} + +static void ocp_fw_activation_history_json(const struct fw_activation_history *fw_history) +{ + struct json_object *root = json_create_object(); + + json_object_add_value_uint(root, "log identifier", fw_history->log_id); + json_object_add_value_uint(root, "valid entries", le32_to_cpu(fw_history->valid_entries)); + + struct json_object *entries = json_create_array(); + + for (int index = 0; index < fw_history->valid_entries; index++) { + const struct fw_activation_history_entry *entry = &fw_history->entries[index]; + struct json_object *entry_obj = json_create_object(); + + json_object_add_value_uint(entry_obj, "version number", entry->ver_num); + json_object_add_value_uint(entry_obj, "entry length", entry->entry_length); + json_object_add_value_uint(entry_obj, "activation count", + le16_to_cpu(entry->activation_count)); + json_object_add_value_uint64(entry_obj, "timestamp", + le64_to_cpu(entry->timestamp)); + json_object_add_value_uint(entry_obj, "power cycle count", + le64_to_cpu(entry->power_cycle_count)); + + struct json_object *fw = json_object_new_string_len(entry->previous_fw, + sizeof(entry->previous_fw)); + + json_object_add_value_object(entry_obj, "previous firmware", fw); + + fw = json_object_new_string_len(entry->new_fw, sizeof(entry->new_fw)); + + json_object_add_value_object(entry_obj, "new firmware", fw); + json_object_add_value_uint(entry_obj, "slot number", entry->slot_number); + json_object_add_value_uint(entry_obj, "commit action type", entry->commit_action); + json_object_add_value_uint(entry_obj, "result", le16_to_cpu(entry->result)); + + json_array_add_value_object(entries, entry_obj); + } + + json_object_add_value_array(root, "entries", entries); + + json_object_add_value_uint(root, "log page version", + le16_to_cpu(fw_history->log_page_version)); + + char guid[2 * sizeof(fw_history->log_page_guid) + 3] = { 0 }; + + sprintf(guid, "0x%"PRIx64"%"PRIx64"", + le64_to_cpu(fw_history->log_page_guid[1]), + le64_to_cpu(fw_history->log_page_guid[0])); + json_object_add_value_string(root, "log page guid", guid); + + json_print_object(root, NULL); + json_free_object(root); + + printf("\n"); +} + +int ocp_fw_activation_history_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const __u8 log_id = 0xC2; + const char *description = "Retrieves the OCP firmware activation history log."; + + char *format = "normal"; + + OPT_ARGS(options) = { + OPT_FMT("output-format", 'o', &format, "output format : normal | json"), + OPT_END() + }; + + struct nvme_dev *dev = NULL; + int err = parse_and_open(&dev, argc, argv, description, options); + + if (err) + return err; + + int uuid_index = 0; + + /* + * Best effort attempt at uuid. Otherwise, assume no index (i.e. 0) + * Log GUID check will ensure correctness of returned data + */ + ocp_get_uuid_index(dev, &uuid_index); + + struct fw_activation_history fw_history = { 0 }; + + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = &fw_history, + .args_size = sizeof(args), + .fd = dev_fd(dev), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = log_id, + .len = sizeof(fw_history), + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = 0, + .uuidx = uuid_index, + .rae = false, + .ot = false, + }; + + err = nvme_get_log(&args); + + if (err) + nvme_show_status(err); + + dev_close(dev); + + int guid_cmp_res = memcmp(fw_history.log_page_guid, ocp_fw_activation_history_guid, + sizeof(ocp_fw_activation_history_guid)); + + if (!err && guid_cmp_res) { + fprintf(stderr, + "Error: Unexpected data. Log page guid does not match with expected.\n"); + err = -EINVAL; + } + + if (!err) { + const enum nvme_print_flags print_flag = validate_output_format(format); + + if (print_flag == JSON) + ocp_fw_activation_history_json(&fw_history); + else if (print_flag == NORMAL) + ocp_fw_activation_history_normal(&fw_history); + else { + fprintf(stderr, "Error: Invalid output format.\n"); + err = -EINVAL; + } + } + + return err; +} diff --git a/plugins/ocp/ocp-fw-activation-history.h b/plugins/ocp/ocp-fw-activation-history.h new file mode 100644 index 0000000..a7f9058 --- /dev/null +++ b/plugins/ocp/ocp-fw-activation-history.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022 Solidigm. + * + * Authors: karl.dedow@solidigm.com + */ + +#ifndef OCP_FIRMWARE_ACTIVATION_HISTORY_H +#define OCP_FIRMWARE_ACTIVATION_HISTORY_H + +struct command; +struct plugin; + +int ocp_fw_activation_history_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin); + +#endif diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index a864363..edf75fc 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* Copyright (c) 2022 Meta Platforms, Inc. +/* + * Copyright (c) 2023 Meta Platforms, Inc. * - * Authors: Arthur Shau <arthurshau@fb.com>, - * Wei Zhang <wzhang@fb.com>, - * Venkat Ramesh <venkatraghavan@fb.com> + * Authors: Arthur Shau <arthurshau@meta.com>, + * Wei Zhang <wzhang@meta.com>, + * Venkat Ramesh <venkatraghavan@meta.com> */ #include <stdio.h> #include <string.h> @@ -25,20 +26,29 @@ #include "ocp-smart-extended-log.h" #include "ocp-clear-fw-update-history.h" +#include "ocp-fw-activation-history.h" #define CREATE_CMD #include "ocp-nvme.h" #include "ocp-utils.h" -#define C0_ACTIVE_BUCKET_TIMER_INCREMENT 5 -#define C0_ACTIVE_THRESHOLD_INCREMENT 5 -#define C0_MINIMUM_WINDOW_INCREMENT 100 +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/// Latency Monitor Log -/* C3 Latency Monitor Log Page */ #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, @@ -46,12 +56,12 @@ static __u8 lat_mon_guid[C3_GUID_LENGTH] = { 0x58, 0x5e, 0xd4, 0x85 }; -#define READ 0 -#define WRITE 1 -#define TRIM 2 -#define RESERVED 3 +#define READ 3 +#define WRITE 2 +#define TRIM 1 +#define RESERVED 0 -struct __attribute__((__packed__)) ssd_latency_monitor_log { +struct __packed ssd_latency_monitor_log { __u8 feature_status; /* 0x00 */ __u8 rsvd1; /* 0x01 */ __le16 active_bucket_timer; /* 0x02 */ @@ -88,24 +98,19 @@ struct __attribute__((__packed__)) ssd_latency_monitor_log { __u8 log_page_guid[0x10]; /* 0x1F0 */ }; -static const __u8 OCP_FID_CLEAR_PCIE_CORRECTABLE_ERROR_COUNTERS = 0xC3; - -static int convert_ts(time_t time, char *ts_buf) -{ - struct tm gmTimeInfo; - time_t time_Human, time_ms; - char buf[80]; - - time_Human = time/1000; - time_ms = time % 1000; - - gmtime_r((const time_t *)&time_Human, &gmTimeInfo); - - strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &gmTimeInfo); - sprintf(ts_buf, "%s.%03ld GMT", buf, time_ms); - - return 0; -} +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) @@ -119,25 +124,25 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, printf(" Feature Status 0x%x\n", log_data->feature_status); printf(" Active Bucket Timer %d min\n", - C0_ACTIVE_BUCKET_TIMER_INCREMENT * + C3_ACTIVE_BUCKET_TIMER_INCREMENT * le16_to_cpu(log_data->active_bucket_timer)); printf(" Active Bucket Timer Threshold %d min\n", - C0_ACTIVE_BUCKET_TIMER_INCREMENT * + C3_ACTIVE_BUCKET_TIMER_INCREMENT * le16_to_cpu(log_data->active_bucket_timer_threshold)); printf(" Active Threshold A %d ms\n", - C0_ACTIVE_THRESHOLD_INCREMENT * + C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_a+1)); printf(" Active Threshold B %d ms\n", - C0_ACTIVE_THRESHOLD_INCREMENT * + C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_b+1)); printf(" Active Threshold C %d ms\n", - C0_ACTIVE_THRESHOLD_INCREMENT * + C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_c+1)); printf(" Active Threshold D %d ms\n", - C0_ACTIVE_THRESHOLD_INCREMENT * + C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_d+1)); printf(" Active Latency Minimum Window %d ms\n", - C0_MINIMUM_WINDOW_INCREMENT * + 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)); @@ -145,39 +150,55 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, 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 <= 3; i++) { + printf(" Read Write Deallocate/Trim\n"); + for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Active Latency Mode: Bucket %d %27d %27d %27d\n", i, log_data->active_latency_config & (1 << pos), log_data->active_latency_config & (1 << pos), log_data->active_latency_config & (1 << pos)); } - printf("\n"); - for (i = 0; i <= 3; i++) { - printf(" Active Bucket Counter: Bucket %d %27d %27d %27d \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 <= 3; i++) { - printf(" Active Measured Latency: Bucket %d %27d ms %27d ms %27d ms\n", - i, - le16_to_cpu(log_data->active_measured_latency[i][READ]), - le16_to_cpu(log_data->active_measured_latency[i][WRITE]), - le16_to_cpu(log_data->active_measured_latency[i][TRIM])); - } - - for (i = 0; i <= 3; i++) { + for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Active Latency Time Stamp: Bucket %d ", i); - for (j = 0; j <= 2; j++) { - if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1) + for (j = 2; j >= 0; j--) { + if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1) { printf(" N/A "); - else { + } else { convert_ts(le64_to_cpu(log_data->active_latency_timestamp[i][j]), ts_buf); printf("%s ", ts_buf); } @@ -185,7 +206,16 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, printf("\n"); } - for (i = 0; i <= 3; i++) { + 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[i][READ-1]), + le16_to_cpu(log_data->active_measured_latency[i][WRITE-1]), + le16_to_cpu(log_data->active_measured_latency[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]), @@ -193,20 +223,12 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, le32_to_cpu(log_data->static_bucket_counter[i][TRIM])); } - for (i = 0; i <= 3; i++) { - printf(" Static Measured Latency: Bucket %d %27d ms %27d ms %27d ms \n", - i, - le16_to_cpu(log_data->static_measured_latency[i][READ]), - le16_to_cpu(log_data->static_measured_latency[i][WRITE]), - le16_to_cpu(log_data->static_measured_latency[i][TRIM])); - } - - for (i = 0; i <= 3; i++) { + for (i = 0; i < C3_BUCKET_NUM; i++) { printf(" Static Latency Time Stamp: Bucket %d ", i); - for (j = 0; j <= 2; j++) { - if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1) + for (j = 2; j >= 0; j--) { + if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1) { printf(" N/A "); - else { + } else { convert_ts(le64_to_cpu(log_data->static_latency_timestamp[i][j]), ts_buf); printf("%s ", ts_buf); } @@ -214,6 +236,14 @@ static int ocp_print_C3_log_normal(struct nvme_dev *dev, 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[i][READ-1]), + le16_to_cpu(log_data->static_measured_latency[i][WRITE-1]), + le16_to_cpu(log_data->static_measured_latency[i][TRIM-1])); + } + return 0; } @@ -224,85 +254,68 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) char buf[128]; int i, j; int pos = 0; - char *operation[3] = {"Read", "Write", "Trim"}; + 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", - C0_ACTIVE_BUCKET_TIMER_INCREMENT * + C3_ACTIVE_BUCKET_TIMER_INCREMENT * le16_to_cpu(log_data->active_bucket_timer)); json_object_add_value_uint(root, "Active Bucket Timer Threshold", - C0_ACTIVE_BUCKET_TIMER_INCREMENT * + C3_ACTIVE_BUCKET_TIMER_INCREMENT * le16_to_cpu(log_data->active_bucket_timer_threshold)); json_object_add_value_uint(root, "Active Threshold A", - C0_ACTIVE_THRESHOLD_INCREMENT * + C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_a + 1)); json_object_add_value_uint(root, "Active Threshold B", - C0_ACTIVE_THRESHOLD_INCREMENT * + C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_b + 1)); json_object_add_value_uint(root, "Active Threshold C", - C0_ACTIVE_THRESHOLD_INCREMENT * + C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_c + 1)); json_object_add_value_uint(root, "Active Threshold D", - C0_ACTIVE_THRESHOLD_INCREMENT * + C3_ACTIVE_THRESHOLD_INCREMENT * le16_to_cpu(log_data->active_threshold_d + 1)); - json_object_add_value_uint(root, "Active Lantency Minimum Window", - C0_MINIMUM_WINDOW_INCREMENT * - le16_to_cpu(log_data->active_latency_min_window)); - json_object_add_value_uint(root, "Active Latency Stamp Units", - le16_to_cpu(log_data->active_latency_stamp_units)); - 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)); - for (i = 0; i <= 3; i++) { + for (i = 0; i < C3_BUCKET_NUM; i++) { struct json_object *bucket; bucket = json_create_object(); sprintf(buf, "Active Latency Mode: Bucket %d", i); - for (j = 0; j <= 2; j++) { + for (j = 2; j >= 0; j--) { json_object_add_value_uint(bucket, operation[j], log_data->active_latency_config & (1 << pos)); } json_object_add_value_object(root, buf, bucket); } - for (i = 0; i <= 3; i++) { - struct json_object *bucket; - - bucket = json_create_object(); - sprintf(buf, "Active Bucket Counter: Bucket %d", i); - for (j = 0; j <= 2; j++) { - json_object_add_value_uint(bucket, operation[j], - le32_to_cpu(log_data->active_bucket_counter[i][j])); - } - json_object_add_value_object(root, buf, bucket); - } + 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 <= 3; i++) { + 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 = 0; j <= 2; j++) { + sprintf(buf, "Active Bucket Counter: 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[i][j])); + le32_to_cpu(log_data->active_bucket_counter[i][j+1])); } json_object_add_value_object(root, buf, bucket); } - for (i = 0; i <= 3; i++) { + 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 = 0; j <= 2; j++) { - if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1) + for (j = 2; j >= 0; j--) { + if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1) { json_object_add_value_string(bucket, operation[j], "NA"); - else { + } else { convert_ts(le64_to_cpu(log_data->active_latency_timestamp[i][j]), ts_buf); json_object_add_value_string(bucket, operation[j], ts_buf); } @@ -310,39 +323,42 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) json_object_add_value_object(root, buf, bucket); } - for (i = 0; i <= 3; i++) { + 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 = 0; j <= 2; j++) { + sprintf(buf, "Active Measured Latency: 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])); + le16_to_cpu(log_data->active_measured_latency[i][j])); } json_object_add_value_object(root, buf, bucket); } - for (i = 0; i <= 3; i++) { + 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 Measured Latency: Bucket %d", i); - for (j = 0; j <= 2; j++) { + sprintf(buf, "Static Bucket Counter: 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[i][j])); + le32_to_cpu(log_data->static_bucket_counter[i][j+1])); } json_object_add_value_object(root, buf, bucket); } - for (i = 0; i <= 3; i++) { + 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 = 0; j <= 2; j++) { - if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1) + for (j = 2; j >= 0; j--) { + if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1) { json_object_add_value_string(bucket, operation[j], "NA"); - else { + } else { convert_ts(le64_to_cpu(log_data->static_latency_timestamp[i][j]), ts_buf); json_object_add_value_string(bucket, operation[j], ts_buf); } @@ -350,6 +366,47 @@ static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data) 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[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"); @@ -381,12 +438,9 @@ static int get_c3_log_page(struct nvme_dev *dev, char *format) 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); + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret); - if (ret == 0) { + if (!ret) { log_data = (struct ssd_latency_monitor_log *)data; /* check log page version */ @@ -397,22 +451,22 @@ static int get_c3_log_page(struct nvme_dev *dev, char *format) goto out; } - /* check log page guid */ - /* Verify GUID matches */ + /* + * 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++) { + 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++) { + for (j = 0; j < 16; j++) fprintf(stderr, "%x", log_data->log_page_guid[j]); - } fprintf(stderr, "\n"); ret = -1; @@ -438,12 +492,6 @@ out: return ret; } -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 ocp_latency_monitor_log(int argc, char **argv, struct command *command, struct plugin *plugin) @@ -480,12 +528,142 @@ static int ocp_latency_monitor_log(int argc, char **argv, return ret; } -static int clear_fw_update_history(int argc, char **argv, - struct command *cmd, struct plugin *plugin) +int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - return ocp_clear_fw_update_history(argc, argv, cmd, 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) { @@ -524,14 +702,14 @@ static int eol_plp_failure_mode_get(struct nvme_dev *dev, const __u32 nsid, err = nvme_get_features(&args); if (!err) { - printf("End of Life Behavior (feature: %#0*x): %#0*x (%s: %s)\n", - fid ? 4 : 2, fid, result ? 10 : 8, result, - nvme_select_to_string(sel), - eol_plp_failure_mode_to_string(result)); + 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(result); } else { - printf("Could not get feature: %#0*x.\n", fid ? 4 : 2, fid); + nvme_show_error("Could not get feature: %#0*x.", fid ? 4 : 2, fid); } return err; @@ -549,7 +727,7 @@ static int eol_plp_failure_mode_set(struct nvme_dev *dev, const __u32 nsid, /* OCP 2.0 requires UUID index support */ err = ocp_get_uuid_index(dev, &uuid_index); if (err || !uuid_index) { - fprintf(stderr, "ERROR: No OCP UUID index found\n"); + nvme_show_error("ERROR: No OCP UUID index found"); return err; } } @@ -575,13 +753,13 @@ static int eol_plp_failure_mode_set(struct nvme_dev *dev, const __u32 nsid, if (err > 0) { nvme_show_status(err); } else if (err < 0) { - perror("Define EOL/PLP failure mode"); + nvme_show_perror("Define EOL/PLP failure mode"); fprintf(stderr, "Command failed while parsing.\n"); } else { - printf("Successfully set mode (feature: %#0*x): %#0*x (%s: %s).\n", - fid ? 4 : 2, fid, mode ? 10 : 8, mode, - save ? "Save" : "Not save", - eol_plp_failure_mode_to_string(mode)); + 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; @@ -590,7 +768,7 @@ static int eol_plp_failure_mode_set(struct nvme_dev *dev, const __u32 nsid, 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"\ + 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"; @@ -637,6 +815,1074 @@ static int eol_plp_failure_mode(int argc, char **argv, struct command *cmd, 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]; +}; + +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 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; + char data[TELEMETRY_TRANSFER_SIZE] = { 0 }; + char *featurename = 0; + struct telemetry_initiated_log *logheader = (struct telemetry_initiated_log *)data; + __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); + + 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) +{ + int ret = 0; + int fmt = -1; + __u8 *data; + int i; + struct unsupported_requirement_log *log_data; + int j; + + fmt = validate_output_format(format); + if (fmt < 0) { + fprintf(stderr, "ERROR : OCP : invalid output format\n"); + return fmt; + } + + 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; + } + } 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) +{ + int ret = 0; + int fmt = -1; + __u8 *data; + int i, j; + struct ocp_error_recovery_log_page *log_data; + + fmt = validate_output_format(format); + if (fmt < 0) { + fprintf(stderr, "ERROR : OCP : invalid output format\n"); + return fmt; + } + + 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; + } + } 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) +{ + int ret = 0; + int fmt = -1; + __u8 *data; + int i, j; + struct ocp_device_capabilities_log_page *log_data; + + fmt = validate_output_format(format); + if (fmt < 0) { + fprintf(stderr, "ERROR : OCP : invalid output format\n"); + return fmt; + } + + 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; + } + } 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; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/// Misc + +static const __u8 OCP_FID_CLEAR_PCIE_CORRECTABLE_ERROR_COUNTERS = 0xC3; + +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_corectable_error_counters(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -646,3 +1892,9 @@ static int clear_pcie_corectable_error_counters(int argc, char **argv, return ocp_clear_feature(argc, argv, desc, OCP_FID_CLEAR_PCIE_CORRECTABLE_ERROR_COUNTERS); } + +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); +} diff --git a/plugins/ocp/ocp-nvme.h b/plugins/ocp/ocp-nvme.h index dc9e154..74dd0ef 100644 --- a/plugins/ocp/ocp-nvme.h +++ b/plugins/ocp/ocp-nvme.h @@ -17,9 +17,15 @@ PLUGIN(NAME("ocp", "OCP cloud SSD extensions", NVME_VERSION), COMMAND_LIST( ENTRY("smart-add-log", "Retrieve extended SMART Information", smart_add_log) ENTRY("latency-monitor-log", "Get Latency Monitor Log Page", ocp_latency_monitor_log) + ENTRY("set-latency-monitor-feature", "Set Latency Monitor feature", ocp_set_latency_monitor_feature) + ENTRY("internal-log", "Retrieve and save internal device telemetry log", ocp_telemetry_log) ENTRY("clear-fw-activate-history", "Clear firmware update history log", clear_fw_update_history) ENTRY("eol-plp-failure-mode", "Define EOL or PLP circuitry failure mode.", eol_plp_failure_mode) ENTRY("clear-pcie-correctable-error-counters", "Clear PCIe correctable error counters", clear_pcie_corectable_error_counters) + ENTRY("vs-fw-activate-history", "Get firmware activation history log", fw_activation_history_log) + ENTRY("unsupported-reqs-log", "Get Unsupported Requirements Log Page", ocp_unsupported_requirements_log) + ENTRY("error-recovery-log", "Retrieve Error Recovery Log Page", ocp_error_recovery_log) + ENTRY("device-capability-log", "Get Device capabilities Requirements Log Page", ocp_device_capabilities_log) ) ); diff --git a/plugins/ocp/ocp-smart-extended-log.c b/plugins/ocp/ocp-smart-extended-log.c index 37b62e9..c989d34 100644 --- a/plugins/ocp/ocp-smart-extended-log.c +++ b/plugins/ocp/ocp-smart-extended-log.c @@ -26,7 +26,7 @@ static __u8 scao_guid[C0_GUID_LENGTH] = { 0xC9, 0x14, 0xD5, 0xAF }; -typedef enum { +enum { SCAO_PMUW = 0, /* Physical media units written */ SCAO_PMUR = 16, /* Physical media units read */ SCAO_BUNBR = 32, /* Bad user nand blocks raw */ @@ -62,7 +62,7 @@ typedef enum { SCAO_PSCC = 200, /* Power State Change Count */ SCAO_LPV = 494, /* Log page version */ SCAO_LPG = 496, /* Log page GUID */ -} SMART_CLOUD_ATTRIBUTE_OFFSETS; +}; static void ocp_print_C0_log_normal(void *data) { @@ -71,10 +71,10 @@ static void ocp_print_C0_log_normal(void *data) printf("SMART Cloud Attributes :-\n"); - printf(" Physical media units written - %"PRIu64" %"PRIu64"\n", + printf(" Physical media units written - %"PRIu64" %"PRIu64"\n", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW + 8] & 0xFFFFFFFFFFFFFFFF), (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF)); - printf(" Physical media units read - %"PRIu64" %"PRIu64"\n", + printf(" Physical media units read - %"PRIu64" %"PRIu64"\n", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR + 8] & 0xFFFFFFFFFFFFFFFF), (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF)); printf(" Bad user nand blocks - Raw %"PRIu64"\n", @@ -105,7 +105,7 @@ static void ocp_print_C0_log_normal(void *data) (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); printf(" Number of Thermal throttling events %d\n", (__u8)log_data[SCAO_NTTE]); - printf(" Current throttling status 0x%x\n", + printf(" Current throttling status 0x%x\n", (__u8)log_data[SCAO_CTS]); printf(" PCIe correctable error count %"PRIu64"\n", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); @@ -286,14 +286,12 @@ static int get_c0_log_page(int fd, char *format) fprintf(stderr, "ERROR : OCP : Unknown GUID in C0 Log Page data\n"); fprintf(stderr, "ERROR : OCP : Expected GUID: 0x"); - for (j = 0; j < 16; j++) { + for (j = 0; j < 16; j++) fprintf(stderr, "%x", scao_guid[j]); - } fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x"); - for (j = 0; j < 16; j++) { + for (j = 0; j < 16; j++) fprintf(stderr, "%x", data[SCAO_LPG + j]); - } fprintf(stderr, "\n"); ret = -1; |