1
0
Fork 0
nvme-cli/plugins/ocp/ocp-telemetry-decode.c
Daniel Baumann 0f2367f2fa
Merging upstream version 2.11.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-02-16 12:28:30 +01:00

1701 lines
56 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/* Copyright (c) 2024 Western Digital Corporation or its affiliates.
*
* Authors: Jeff Lien <jeff.lien@wdc.com>,
*/
#include "common.h"
#include "nvme.h"
#include "libnvme.h"
#include "plugin.h"
#include "linux/types.h"
#include "util/types.h"
#include "nvme-print.h"
#include "ocp-telemetry-decode.h"
void print_vu_event_data(__u32 size, __u8 *data)
{
int j;
__u16 vu_event_id = *(__u16 *)data;
printf(" VU Event ID : 0x%02x\n", le16_to_cpu(vu_event_id));
printf(" VU Data : 0x");
for (j = 2; j < size; j++)
printf("%x", data[j]);
printf("\n\n");
}
void print_stats_desc(struct telemetry_stats_desc *stat_desc)
{
int j;
/* Get the statistics Identifier string name and data size */
__u16 stat_id = stat_desc->id;
__u32 stat_data_sz = ((stat_desc->size) * 4);
printf("Statistics Identifier : 0x%x, %s\n",
stat_id, telemetry_stat_id_to_string(stat_id));
printf("Statistics info : 0x%x\n", stat_desc->info);
printf("NS info : 0x%x\n", stat_desc->ns_info);
printf("Statistic Data Size : 0x%x\n", le16_to_cpu(stat_data_sz));
if (stat_data_sz > 0) {
printf("%s : 0x",
telemetry_stat_id_to_string(stat_id));
for (j = 0; j < stat_data_sz; j++)
printf("%02x", stat_desc->data[j]);
printf("\n");
}
printf("\n");
}
void print_telemetry_fifo_event(__u8 class_type,
__u16 id, __u8 size_dw, __u8 *data)
{
int j;
const char *class_str = NULL;
__u32 size = size_dw * 4;
char time_str[40];
uint64_t timestamp = 0;
memset((void *)time_str, '\0', 40);
if (class_type) {
class_str = telemetry_event_class_to_string(class_type);
printf("Event Class : %s\n", class_str);
printf(" Size : 0x%02x\n", size);
}
switch (class_type) {
case TELEMETRY_TIMESTAMP_CLASS:
timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(*(uint64_t *)data));
memset((void *)time_str, 0, 9);
sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(timestamp)/3600),
(int)((le64_to_cpu(timestamp%3600)/60)),
(int)(le64_to_cpu(timestamp%60)));
printf(" Event ID : 0x%04x %s\n", id, telemetry_ts_event_to_string(id));
printf(" Timestamp : %s\n", time_str);
if (size > 8) {
printf(" VU Data : 0x");
for (j = 8; j < size; j++)
printf("%02x", data[j]);
printf("\n\n");
}
break;
case TELEMETRY_PCIE_CLASS:
printf(" Event ID : 0x%04x %s\n",
id, telemetry_pcie_event_id_to_string(id));
printf(" State : 0x%02x %s\n",
data[0], telemetry_pcie_state_data_to_string(data[0]));
printf(" Speed : 0x%02x %s\n",
data[1], telemetry_pcie_speed_data_to_string(data[1]));
printf(" Width : 0x%02x %s\n",
data[2], telemetry_pcie_width_data_to_string(data[2]));
if (size > 4) {
printf(" VU Data : ");
for (j = 4; j < size; j++)
printf("%x", data[j]);
printf("\n\n");
}
break;
case TELEMETRY_NVME_CLASS:
printf(" Event ID : 0x%04x %s\n",
id, telemetry_nvme_event_id_to_string(id));
if ((id == ADMIN_QUEUE_NONZERO_STATUS) ||
(id == IO_QUEUE_NONZERO_STATUS)) {
printf(" Cmd Op Code : 0x%02x\n", data[0]);
__u16 status = *(__u16 *)&data[1];
__u16 cmd_id = *(__u16 *)&data[3];
__u16 sq_id = *(__u16 *)&data[5];
printf(" Status Code : 0x%04x\n", le16_to_cpu(status));
printf(" Cmd ID : 0x%04x\n", le16_to_cpu(cmd_id));
printf(" SQ ID : 0x%04x\n", le16_to_cpu(sq_id));
} else if (id == CC_REGISTER_CHANGED) {
__u32 cc_reg_data = *(__u32 *)data;
printf(" CC Reg Data : 0x%08x\n",
le32_to_cpu(cc_reg_data));
} else if (id == CSTS_REGISTER_CHANGED) {
__u32 csts_reg_data = *(__u32 *)data;
printf(" CSTS Reg Data : 0x%08x\n",
le32_to_cpu(csts_reg_data));
}
if (size > 8)
print_vu_event_data((size-8), (__u8 *)&data[8]);
break;
case TELEMETRY_RESET_CLASS:
printf(" Event ID : 0x%04x %s\n",
id, telemetry_reset_event_id_to_string(id));
if (size)
print_vu_event_data(size, data);
break;
case TELEMETRY_BOOT_SEQ_CLASS:
printf(" Event ID : 0x%04x %s\n",
id, telemetry_boot_seq_event_id_to_string(id));
if (size)
print_vu_event_data(size, data);
break;
case TELEMETRY_FW_ASSERT_CLASS:
printf(" Event ID : 0x%04x %s\n",
id, telemetry_fw_assert_event_id_to_string(id));
if (size)
print_vu_event_data(size, data);
break;
case TELEMETRY_TEMPERATURE_CLASS:
printf(" Event ID : 0x%04x %s\n",
id, telemetry_temperature_event_id_to_string(id));
if (size)
print_vu_event_data(size, data);
break;
case TELEMETRY_MEDIA_DBG_CLASS:
printf(" Event ID : 0x%04x %s\n",
id, telemetry_media_debug_event_id_to_string(id));
if (size)
print_vu_event_data(size, data);
break;
case TELEMETRY_MEDIA_WEAR_CLASS:
printf(" Event ID : 0x%04x %s\n",
id, telemetry_media_debug_event_id_to_string(id));
__u32 host_tb_written = *(__u32 *)&data[0];
__u32 media_tb_written = *(__u32 *)&data[4];
__u32 media_tb_erased = *(__u32 *)&data[8];
printf(" Host TB Written : 0x%04x\n",
le16_to_cpu(host_tb_written));
printf(" Media TB Written : 0x%04x\n",
le16_to_cpu(media_tb_written));
printf(" Media TB Erased : 0x%04x\n",
le16_to_cpu(media_tb_erased));
if (size > 12)
print_vu_event_data((size-12), (__u8 *)&data[12]);
break;
case TELEMETRY_STAT_SNAPSHOT_CLASS:
printf(" Statistic ID : 0x%02x %s\n",
id, telemetry_stat_id_to_string(id));
print_stats_desc((struct telemetry_stats_desc *)data);
break;
default:
/*
* printf("Unknown Event Class Type\n");
* printf("Data : 0x");
* for (j = 0; j < size; j++)
* printf("%x", data[j]);
* printf("\n\n");
*/
break;
}
}
struct statistic_entry statistic_identifiers_map[] = {
{ 0x00, "Error, this entry does not exist." },
{ 0x01, "Outstanding Admin Commands" },
{ 0x02, "Host Write Bandwidth"},
{ 0x03, "GC Write Bandwidth"},
{ 0x04, "Active Namespaces"},
{ 0x05, "Internal Write Workload"},
{ 0x06, "Internal Read Workload"},
{ 0x07, "Internal Write Queue Depth"},
{ 0x08, "Internal Read Queue Depth"},
{ 0x09, "Pending Trim LBA Count"},
{ 0x0A, "Host Trim LBA Request Count"},
{ 0x0B, "Current NVMe Power State"},
{ 0x0C, "Current DSSD Power State"},
{ 0x0D, "Program Fail Count"},
{ 0x0E, "Erase Fail Count"},
{ 0x0F, "Read Disturb Writes"},
{ 0x10, "Retention Writes"},
{ 0x11, "Wear Leveling Writes"},
{ 0x12, "Read Recovery Writes"},
{ 0x13, "GC Writes"},
{ 0x14, "SRAM Correctable Count"},
{ 0x15, "DRAM Correctable Count"},
{ 0x16, "SRAM Uncorrectable Count"},
{ 0x17, "DRAM Uncorrectable Count"},
{ 0x18, "Data Integrity Error Count"},
{ 0x19, "Read Retry Error Count"},
{ 0x1A, "PERST Events Count"},
{ 0x1B, "Max Die Bad Block"},
{ 0x1C, "Max NAND Channel Bad Block"},
{ 0x1D, "Minimum NAND Channel Bad Block"}
};
struct request_data host_log_page_header[] = {
{ "LogIdentifier", 1 },
{ "Reserved1", 4 },
{ "IEEE OUI Identifier", 3 },
{ "Telemetry Host-Initiated Data Area 1 Last Block", 2 },
{ "Telemetry Host-Initiated Data Area 2 Last Block", 2 },
{ "Telemetry Host-Initiated Data Area 3 Last Block", 2 },
{ "Reserved2", 2 },
{ "Telemetry Host-Initiated Data Area 4 Last Block", 4 },
{ "Reserved3", 360 },
{ "Telemetry Host-Initiated Scope", 1 },
{ "Telemetry Host Initiated Generation Number", 1 },
{ "Telemetry Host-Initiated Data Available", 1 },
{ "Telemetry Controller-Initiated Data Generation Number", 1 }
};
struct request_data controller_log_page_header[] = {
{ "LogIdentifier", 1 },
{ "Reserved1", 4 },
{ "IEEE OUI Identifier", 3 },
{ "Telemetry Host-Initiated Data Area 1 Last Block", 2 },
{ "Telemetry Host-Initiated Data Area 2 Last Block", 2 },
{ "Telemetry Host-Initiated Data Area 3 Last Block", 2 },
{ "Reserved2", 2 },
{ "Telemetry Host-Initiated Data Area 4 Last Block", 4 },
{ "Reserved3", 361 },
{ "Telemetry Controller-Initiated Scope", 1 },
{ "Telemetry Controller-Initiated Data Available", 1 },
{ "Telemetry Controller-Initiated Data Generation Number", 1 }
};
struct request_data reason_identifier[] = {
{ "Error ID", 64 },
{ "File ID", 8 },
{ "Line Number", 2 },
{ "Valid Flags", 1 },
{ "Reserved", 21 },
{ "VU Reason Extension", 32 }
};
struct request_data ocp_header_in_da1[] = {
{ "Major Version", 2 },
{ "Minor Version", 2 },
{ "Reserved1", 4 },
{ "Timestamp", 8 },
{ "Log page GUID", GUID_LEN },
{ "Number Telemetry Profiles Supported", 1 },
{ "Telemetry Profile Selected", 1 },
{ "Reserved2", 6 },
{ "Telemetry String Log Size", 8 },
{ "Reserved3", 8 },
{ "Firmware Revision", 8 },
{ "Reserved4", 32 },
{ "Data Area 1 Statistic Start", 8 },
{ "Data Area 1 Statistic Size", 8 },
{ "Data Area 2 Statistic Start", 8 },
{ "Data Area 2 Statistic Size", 8 },
{ "Reserved5", 32 },
{ "Event FIFO 1 Data Area", 1 },
{ "Event FIFO 2 Data Area", 1 },
{ "Event FIFO 3 Data Area", 1 },
{ "Event FIFO 4 Data Area", 1 },
{ "Event FIFO 5 Data Area", 1 },
{ "Event FIFO 6 Data Area", 1 },
{ "Event FIFO 7 Data Area", 1 },
{ "Event FIFO 8 Data Area", 1 },
{ "Event FIFO 9 Data Area", 1 },
{ "Event FIFO 10 Data Area", 1 },
{ "Event FIFO 11 Data Area", 1 },
{ "Event FIFO 12 Data Area", 1 },
{ "Event FIFO 13 Data Area", 1 },
{ "Event FIFO 14 Data Area", 1 },
{ "Event FIFO 15 Data Area", 1 },
{ "Event FIFO 16 Data Area", 1 },
{ "Event FIFO 1 Start", 8 },
{ "Event FIFO 1 Size", 8 },
{ "Event FIFO 2 Start", 8 },
{ "Event FIFO 2 Size", 8 },
{ "Event FIFO 3 Start", 8 },
{ "Event FIFO 3 Size", 8 },
{ "Event FIFO 4 Start", 8 },
{ "Event FIFO 4 Size", 8 },
{ "Event FIFO 5 Start", 8 },
{ "Event FIFO 5 Size", 8 },
{ "Event FIFO 6 Start", 8 },
{ "Event FIFO 6 Size", 8 },
{ "Event FIFO 7 Start", 8 },
{ "Event FIFO 7 Size", 8 },
{ "Event FIFO 8 Start", 8 },
{ "Event FIFO 8 Size", 8 },
{ "Event FIFO 9 Start", 8 },
{ "Event FIFO 9 Size", 8 },
{ "Event FIFO 10 Start", 8 },
{ "Event FIFO 10 Size", 8 },
{ "Event FIFO 11 Start", 8 },
{ "Event FIFO 11 Size", 8 },
{ "Event FIFO 12 Start", 8 },
{ "Event FIFO 12 Size", 8 },
{ "Event FIFO 13 Start", 8 },
{ "Event FIFO 13 Size", 8 },
{ "Event FIFO 14 Start", 8 },
{ "Event FIFO 14 Size", 8 },
{ "Event FIFO 15 Start", 8 },
{ "Event FIFO 15 Size", 8 },
{ "Event FIFO 16 Start", 8 },
{ "Event FIFO 16 Size", 8 },
{ "Reserved6", 80 }
};
struct request_data smart[] = {
{ "Critical Warning", 1 },
{ "Composite Temperature", 2 },
{ "Available Spare", 1 },
{ "Available Spare Threshold", 1 },
{ "Percentage Used", 1 },
{ "Reserved1", 26 },
{ "Data Units Read", 16 },
{ "Data Units Written", 16 },
{ "Host Read Commands", 16 },
{ "Host Write Commands", 16 },
{ "Controller Busy Time", 16 },
{ "Power Cycles", 16 },
{ "Power On Hours", 16 },
{ "Unsafe Shutdowns", 16 },
{ "Media and Data Integrity Errors", 16 },
{ "Number of Error Information Log Entries", 16 },
{ "Warning Composite Temperature Time", 4 },
{ "Critical Composite Temperature Time", 4 },
{ "Temperature Sensor 1", 2 },
{ "Temperature Sensor 2", 2 },
{ "Temperature Sensor 3", 2 },
{ "Temperature Sensor 4", 2 },
{ "Temperature Sensor 5", 2 },
{ "Temperature Sensor 6", 2 },
{ "Temperature Sensor 7", 2 },
{ "Temperature Sensor 8", 2 },
{ "Thermal Management Temperature 1 Transition Count", 4 },
{ "Thermal Management Temperature 2 Transition Count", 4 },
{ "Total Time for Thermal Management Temperature 1", 4 },
{ "Total Time for Thermal Management Temperature 2", 4 },
{ "Reserved2", 280 }
};
struct request_data smart_extended[] = {
{ "Physical Media Units Written", 16 },
{ "Physical Media Units Read", 16 },
{ "Bad User NAND Blocks Raw Count", 6 },
{ "Bad User NAND Blocks Normalized Value", 2 },
{ "Bad System NAND Blocks Raw Count", 6 },
{ "Bad System NAND Blocks Normalized Value", 2 },
{ "XOR Recovery Count", 8 },
{ "Uncorrectable Read Error Count", 8 },
{ "Soft ECC Error Count", 8 },
{ "End to End Correction Counts Detected Errors", 4 },
{ "End to End Correction Counts Corrected Errors", 4 },
{ "System Data Percent Used", 1 },
{ "Refresh Counts", 7 },
{ "Maximum User Data Erase Count", 4 },
{ "Minimum User Data Erase Count", 4 },
{ "Number of thermal throttling events", 1 },
{ "Current Throttling Status", 1 },
{ "Errata Version Field", 1 },
{ "Point Version Field", 2 },
{ "Minor Version Field", 2 },
{ "Major Version Field", 1 },
{ "PCIe Correctable Error Count", 8 },
{ "Incomplete Shutdowns", 4 },
{ "Reserved1", 4 },
{ "Percent Free Blocks", 1 },
{ "Reserved2", 7 },
{ "Capacitor Health", 2 },
{ "NVMe Base Errata Version", 1 },
{ "NVMe Command Set Errata Version", 1 },
{ "Reserved3", 4 },
{ "Unaligned IO", 8 },
{ "Security Version Number", 8 },
{ "Total NUSE", 8 },
{ "PLP Start Count", 16 },
{ "Endurance Estimate", 16 },
{ "PCIe Link Retraining Count", 8 },
{ "Power State Change Count", 8 },
{ "Lowest Permitted Firmware Revision", 8 },
{ "Reserved4", 278 },
{ "Log Page Version", 2 },
{ "Log page GUID", GUID_LEN }
};
#ifdef CONFIG_JSONC
void json_add_formatted_u32_str(struct json_object *pobject, const char *msg, unsigned int pdata)
{
char data_str[70] = { 0 };
sprintf(data_str, "0x%x", pdata);
json_object_add_value_string(pobject, msg, data_str);
}
void json_add_formatted_var_size_str(struct json_object *pobject, const char *msg, __u8 *pdata,
unsigned int data_size)
{
char description_str[256] = "";
char temp_buffer[3] = { 0 };
for (size_t i = 0; i < data_size; ++i) {
sprintf(temp_buffer, "%02X", pdata[i]);
strcat(description_str, temp_buffer);
}
json_object_add_value_string(pobject, msg, description_str);
}
#endif /* CONFIG_JSONC */
int get_telemetry_das_offset_and_size(
struct nvme_ocp_telemetry_common_header *ptelemetry_common_header,
struct nvme_ocp_telemetry_offsets *ptelemetry_das_offset)
{
if (NULL == ptelemetry_common_header || NULL == ptelemetry_das_offset) {
nvme_show_error("Invalid input arguments.");
return -1;
}
if (ptelemetry_common_header->log_id == NVME_LOG_LID_TELEMETRY_HOST)
ptelemetry_das_offset->header_size =
sizeof(struct nvme_ocp_telemetry_host_initiated_header);
else if (ptelemetry_common_header->log_id == NVME_LOG_LID_TELEMETRY_CTRL)
ptelemetry_das_offset->header_size =
sizeof(struct nvme_ocp_telemetry_controller_initiated_header);
else
return -1;
ptelemetry_das_offset->da1_start_offset = ptelemetry_das_offset->header_size;
ptelemetry_das_offset->da1_size = ptelemetry_common_header->da1_last_block *
OCP_TELEMETRY_DATA_BLOCK_SIZE;
ptelemetry_das_offset->da2_start_offset = ptelemetry_das_offset->da1_start_offset +
ptelemetry_das_offset->da1_size;
ptelemetry_das_offset->da2_size =
(ptelemetry_common_header->da2_last_block -
ptelemetry_common_header->da1_last_block) * OCP_TELEMETRY_DATA_BLOCK_SIZE;
ptelemetry_das_offset->da3_start_offset = ptelemetry_das_offset->da2_start_offset +
ptelemetry_das_offset->da2_size;
ptelemetry_das_offset->da3_size =
(ptelemetry_common_header->da3_last_block -
ptelemetry_common_header->da2_last_block) * OCP_TELEMETRY_DATA_BLOCK_SIZE;
ptelemetry_das_offset->da4_start_offset = ptelemetry_das_offset->da3_start_offset +
ptelemetry_das_offset->da3_size;
ptelemetry_das_offset->da4_size =
(ptelemetry_common_header->da4_last_block -
ptelemetry_common_header->da3_last_block) * OCP_TELEMETRY_DATA_BLOCK_SIZE;
return 0;
}
int get_static_id_ascii_string(int identifier, char *description)
{
if (pstring_buffer == NULL)
return -1;
struct nvme_ocp_telemetry_string_header *pocp_ts_header =
(struct nvme_ocp_telemetry_string_header *)pstring_buffer;
//Calculating the sizes of the tables. Note: Data is present in the form of DWORDS,
//So multiplying with sizeof(DWORD)
unsigned long long sits_table_size = (pocp_ts_header->sitsz) * SIZE_OF_DWORD;
//Calculating number of entries present in all 3 tables
int sits_entries = (int)sits_table_size /
sizeof(struct nvme_ocp_statistics_identifier_string_table);
for (int sits_entry = 0; sits_entry < sits_entries; sits_entry++) {
struct nvme_ocp_statistics_identifier_string_table
*peach_statistic_entry =
(struct nvme_ocp_statistics_identifier_string_table *)
(pstring_buffer + (pocp_ts_header->sits * SIZE_OF_DWORD) +
(sits_entry *
sizeof(struct nvme_ocp_statistics_identifier_string_table)));
if (identifier == (int)peach_statistic_entry->vs_statistic_identifier) {
char *pdescription = (char *)(pstring_buffer +
(pocp_ts_header->ascts * SIZE_OF_DWORD) +
(peach_statistic_entry->ascii_id_offset *
SIZE_OF_DWORD));
memcpy(description, pdescription,
peach_statistic_entry->ascii_id_length + 1);
// If ASCII string isn't found, see in our internal Map
// for 2.5 Spec defined strings (id < 0x1D).
if ((description == NULL) && (identifier < 0x1D))
memcpy(description,
statistic_identifiers_map[identifier].description,
peach_statistic_entry->ascii_id_length + 1);
return 0;
}
}
return -1;
}
int get_event_id_ascii_string(int identifier, int debug_event_class, char *description)
{
if (pstring_buffer == NULL)
return -1;
struct nvme_ocp_telemetry_string_header *pocp_ts_header =
(struct nvme_ocp_telemetry_string_header *)pstring_buffer;
//Calculating the sizes of the tables. Note: Data is present in the form of DWORDS,
//So multiplying with sizeof(DWORD)
unsigned long long ests_table_size = (pocp_ts_header->estsz) * SIZE_OF_DWORD;
//Calculating number of entries present in all 3 tables
int ests_entries = (int)ests_table_size / sizeof(struct nvme_ocp_event_string_table);
for (int ests_entry = 0; ests_entry < ests_entries; ests_entry++) {
struct nvme_ocp_event_string_table *peach_event_entry =
(struct nvme_ocp_event_string_table *)
(pstring_buffer + (pocp_ts_header->ests * SIZE_OF_DWORD) +
(ests_entry * sizeof(struct nvme_ocp_event_string_table)));
if (identifier == (int)peach_event_entry->event_identifier &&
debug_event_class == (int)peach_event_entry->debug_event_class) {
char *pdescription = (char *)(pstring_buffer +
(pocp_ts_header->ascts * SIZE_OF_DWORD) +
(peach_event_entry->ascii_id_offset * SIZE_OF_DWORD));
memcpy(description, pdescription,
peach_event_entry->ascii_id_length + 1);
return 0;
}
}
return -1;
}
int get_vu_event_id_ascii_string(int identifier, int debug_event_class, char *description)
{
if (pstring_buffer == NULL)
return -1;
struct nvme_ocp_telemetry_string_header *pocp_ts_header =
(struct nvme_ocp_telemetry_string_header *)pstring_buffer;
//Calculating the sizes of the tables. Note: Data is present in the form of DWORDS,
//So multiplying with sizeof(DWORD)
unsigned long long vuests_table_size = (pocp_ts_header->vu_estsz) * SIZE_OF_DWORD;
//Calculating number of entries present in all 3 tables
int vu_ests_entries = (int)vuests_table_size /
sizeof(struct nvme_ocp_vu_event_string_table);
for (int vu_ests_entry = 0; vu_ests_entry < vu_ests_entries; vu_ests_entry++) {
struct nvme_ocp_vu_event_string_table *peach_vu_event_entry =
(struct nvme_ocp_vu_event_string_table *)
(pstring_buffer + (pocp_ts_header->vu_ests * SIZE_OF_DWORD) +
(vu_ests_entry * sizeof(struct nvme_ocp_vu_event_string_table)));
if (identifier == (int)peach_vu_event_entry->vu_event_identifier &&
debug_event_class ==
(int)peach_vu_event_entry->debug_event_class) {
char *pdescription = (char *)(pstring_buffer +
(pocp_ts_header->ascts * SIZE_OF_DWORD) +
(peach_vu_event_entry->ascii_id_offset * SIZE_OF_DWORD));
memcpy(description, pdescription,
peach_vu_event_entry->ascii_id_length + 1);
return 0;
}
}
return -1;
}
int parse_ocp_telemetry_string_log(int event_fifo_num, int identifier, int debug_event_class,
enum ocp_telemetry_string_tables string_table, char *description)
{
if (pstring_buffer == NULL)
return -1;
if (event_fifo_num != 0) {
struct nvme_ocp_telemetry_string_header *pocp_ts_header =
(struct nvme_ocp_telemetry_string_header *)pstring_buffer;
if (*pocp_ts_header->fifo_ascii_string[event_fifo_num-1] != '\0')
memcpy(description, pocp_ts_header->fifo_ascii_string[event_fifo_num-1],
16);
else
description = "";
return 0;
}
if (string_table == STATISTICS_IDENTIFIER_STRING)
get_static_id_ascii_string(identifier, description);
else if (string_table == EVENT_STRING)
get_event_id_ascii_string(identifier, debug_event_class, description);
else if (string_table == VU_EVENT_STRING)
get_vu_event_id_ascii_string(identifier, debug_event_class, description);
return 0;
}
#ifdef CONFIG_JSONC
void parse_time_stamp_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor,
struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data,
struct json_object *pevent_fifos_object, FILE *fp)
{
struct nvme_ocp_time_stamp_dbg_evt_class_format *ptime_stamp_event =
(struct nvme_ocp_time_stamp_dbg_evt_class_format *) pevent_specific_data;
struct nvme_ocp_common_dbg_evt_class_vu_data *ptime_stamp_event_vu_data = NULL;
__u16 vu_event_id = 0;
__u8 *pdata = NULL;
char description_str[256] = "";
unsigned int vu_data_size = 0;
bool vu_data_present = false;
if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) >
sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format)) {
vu_data_present = true;
vu_data_size =
((pevent_descriptor->event_data_size * SIZE_OF_DWORD) -
(sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format) +
SIZE_OF_VU_EVENT_ID));
ptime_stamp_event_vu_data =
(struct nvme_ocp_common_dbg_evt_class_vu_data *)((__u64)ptime_stamp_event +
sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format));
vu_event_id = le16_to_cpu(ptime_stamp_event_vu_data->vu_event_identifier);
pdata = (__u8 *)&(ptime_stamp_event_vu_data->data);
parse_ocp_telemetry_string_log(0, vu_event_id,
pevent_descriptor->debug_event_class_type,
VU_EVENT_STRING, description_str);
}
if (pevent_fifos_object != NULL) {
json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA,
ptime_stamp_event->time_stamp, DATA_SIZE_8);
if (vu_data_present) {
json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING,
vu_event_id);
json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING,
description_str);
json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata,
vu_data_size);
}
} else {
if (fp) {
print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA,
ptime_stamp_event->time_stamp, DATA_SIZE_8, fp);
if (vu_data_present) {
fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id);
fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str);
print_formatted_var_size_str(STR_VU_DATA, pdata, vu_data_size, fp);
}
} else {
print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA,
ptime_stamp_event->time_stamp, DATA_SIZE_8, fp);
if (vu_data_present) {
printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id);
printf("%s: %s\n", STR_VU_EVENT_STRING, description_str);
print_formatted_var_size_str(STR_VU_DATA, pdata, vu_data_size, fp);
}
}
}
}
void parse_pcie_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor,
struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data,
struct json_object *pevent_fifos_object, FILE *fp)
{
struct nvme_ocp_pcie_dbg_evt_class_format *ppcie_event =
(struct nvme_ocp_pcie_dbg_evt_class_format *) pevent_specific_data;
struct nvme_ocp_common_dbg_evt_class_vu_data *ppcie_event_vu_data = NULL;
__u16 vu_event_id = 0;
__u8 *pdata = NULL;
char description_str[256] = "";
unsigned int vu_data_size = 0;
bool vu_data_present = false;
if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) >
sizeof(struct nvme_ocp_pcie_dbg_evt_class_format)) {
vu_data_present = true;
vu_data_size =
((pevent_descriptor->event_data_size * SIZE_OF_DWORD) -
(sizeof(struct nvme_ocp_pcie_dbg_evt_class_format) +
SIZE_OF_VU_EVENT_ID));
ppcie_event_vu_data =
(struct nvme_ocp_common_dbg_evt_class_vu_data *)((__u64)ppcie_event +
sizeof(struct nvme_ocp_pcie_dbg_evt_class_format));
vu_event_id = le16_to_cpu(ppcie_event_vu_data->vu_event_identifier);
pdata = (__u8 *)&(ppcie_event_vu_data->data);
parse_ocp_telemetry_string_log(0, vu_event_id,
pevent_descriptor->debug_event_class_type,
VU_EVENT_STRING, description_str);
}
if (pevent_fifos_object != NULL) {
json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA,
ppcie_event->pCIeDebugEventData, DATA_SIZE_4);
if (vu_data_present) {
json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING,
vu_event_id);
json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING,
description_str);
json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata,
vu_data_size);
}
} else {
if (fp) {
print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA,
ppcie_event->pCIeDebugEventData, DATA_SIZE_4, fp);
if (vu_data_present) {
fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id);
fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str);
print_formatted_var_size_str(STR_VU_DATA, pdata, vu_data_size, fp);
}
} else {
print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA,
ppcie_event->pCIeDebugEventData, DATA_SIZE_4, fp);
if (vu_data_present) {
printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id);
printf("%s: %s\n", STR_VU_EVENT_STRING, description_str);
print_formatted_var_size_str(STR_VU_DATA, pdata, vu_data_size, fp);
}
}
}
}
void parse_nvme_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor,
struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data,
struct json_object *pevent_fifos_object, FILE *fp)
{
struct nvme_ocp_nvme_dbg_evt_class_format *pnvme_event =
(struct nvme_ocp_nvme_dbg_evt_class_format *) pevent_specific_data;
struct nvme_ocp_common_dbg_evt_class_vu_data *pnvme_event_vu_data = NULL;
__u16 vu_event_id = 0;
__u8 *pdata = NULL;
char description_str[256] = "";
unsigned int vu_data_size = 0;
bool vu_data_present = false;
if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) >
sizeof(struct nvme_ocp_nvme_dbg_evt_class_format)) {
vu_data_present = true;
vu_data_size =
((pevent_descriptor->event_data_size * SIZE_OF_DWORD) -
(sizeof(struct nvme_ocp_nvme_dbg_evt_class_format) +
SIZE_OF_VU_EVENT_ID));
pnvme_event_vu_data =
(struct nvme_ocp_common_dbg_evt_class_vu_data *)((__u64)pnvme_event +
sizeof(struct nvme_ocp_nvme_dbg_evt_class_format));
vu_event_id = le16_to_cpu(pnvme_event_vu_data->vu_event_identifier);
pdata = (__u8 *)&(pnvme_event_vu_data->data);
parse_ocp_telemetry_string_log(0, vu_event_id,
pevent_descriptor->debug_event_class_type,
VU_EVENT_STRING,
description_str);
}
if (pevent_fifos_object != NULL) {
json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA,
pnvme_event->nvmeDebugEventData, DATA_SIZE_8);
if (vu_data_present) {
json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING,
vu_event_id);
json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING,
description_str);
json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata,
vu_data_size);
}
} else {
if (fp) {
print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA,
pnvme_event->nvmeDebugEventData, DATA_SIZE_8, fp);
if (vu_data_present) {
fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id);
fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str);
print_formatted_var_size_str(STR_VU_DATA, pdata, vu_data_size, fp);
}
} else {
print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA,
pnvme_event->nvmeDebugEventData, DATA_SIZE_8, fp);
if (vu_data_present) {
printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id);
printf("%s: %s\n", STR_VU_EVENT_STRING, description_str);
print_formatted_var_size_str(STR_VU_DATA, pdata, vu_data_size, fp);
}
}
}
}
void parse_common_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor,
struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data,
struct json_object *pevent_fifos_object, FILE *fp)
{
if (pevent_specific_data) {
struct nvme_ocp_common_dbg_evt_class_vu_data *pcommon_debug_event_vu_data =
(struct nvme_ocp_common_dbg_evt_class_vu_data *) pevent_specific_data;
__u16 vu_event_id = le16_to_cpu(pcommon_debug_event_vu_data->vu_event_identifier);
char description_str[256] = "";
__u8 *pdata = (__u8 *)&(pcommon_debug_event_vu_data->data);
unsigned int vu_data_size = ((pevent_descriptor->event_data_size *
SIZE_OF_DWORD) - SIZE_OF_VU_EVENT_ID);
parse_ocp_telemetry_string_log(0, vu_event_id,
pevent_descriptor->debug_event_class_type,
VU_EVENT_STRING, description_str);
if (pevent_fifos_object != NULL) {
json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING,
vu_event_id);
json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING,
description_str);
json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata,
vu_data_size);
} else {
if (fp) {
fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id);
fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str);
print_formatted_var_size_str(STR_VU_DATA, pdata, vu_data_size, fp);
} else {
printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id);
printf("%s: %s\n", STR_VU_EVENT_STRING, description_str);
print_formatted_var_size_str(STR_VU_DATA, pdata, vu_data_size, fp);
}
}
}
}
void parse_media_wear_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor,
struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data,
struct json_object *pevent_fifos_object, FILE *fp)
{
struct nvme_ocp_media_wear_dbg_evt_class_format *pmedia_wear_event =
(struct nvme_ocp_media_wear_dbg_evt_class_format *) pevent_specific_data;
struct nvme_ocp_common_dbg_evt_class_vu_data *pmedia_wear_event_vu_data = NULL;
__u16 vu_event_id = 0;
__u8 *pdata = NULL;
char description_str[256] = "";
unsigned int vu_data_size = 0;
bool vu_data_present = false;
if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) >
sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format)) {
vu_data_present = true;
vu_data_size =
((pevent_descriptor->event_data_size * SIZE_OF_DWORD) -
(sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format) +
SIZE_OF_VU_EVENT_ID));
pmedia_wear_event_vu_data =
(struct nvme_ocp_common_dbg_evt_class_vu_data *)((__u64)pmedia_wear_event +
sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format));
vu_event_id = le16_to_cpu(pmedia_wear_event_vu_data->vu_event_identifier);
pdata = (__u8 *)&(pmedia_wear_event_vu_data->data);
parse_ocp_telemetry_string_log(0, vu_event_id,
pevent_descriptor->debug_event_class_type,
VU_EVENT_STRING,
description_str);
}
if (pevent_fifos_object != NULL) {
json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA,
pmedia_wear_event->currentMediaWear, DATA_SIZE_12);
if (vu_data_present) {
json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING,
vu_event_id);
json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING,
description_str);
json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata,
vu_data_size);
}
} else {
if (fp) {
print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA,
pmedia_wear_event->currentMediaWear, DATA_SIZE_12, fp);
if (vu_data_present) {
fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id);
fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str);
print_formatted_var_size_str(STR_VU_DATA, pdata, vu_data_size, fp);
}
} else {
print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA,
pmedia_wear_event->currentMediaWear, DATA_SIZE_12, NULL);
if (vu_data_present) {
printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id);
printf("%s: %s\n", STR_VU_EVENT_STRING, description_str);
print_formatted_var_size_str(STR_VU_DATA, pdata, vu_data_size, fp);
}
}
}
}
int parse_event_fifo(unsigned int fifo_num, unsigned char *pfifo_start,
struct json_object *pevent_fifos_object, unsigned char *pstring_buffer,
struct nvme_ocp_telemetry_offsets *poffsets, __u64 fifo_size, FILE *fp)
{
if (NULL == pfifo_start || NULL == poffsets) {
nvme_show_error("Input buffer was NULL");
return -1;
}
int status = 0;
unsigned int event_fifo_number = fifo_num + 1;
char *description = (char *)malloc((40 + 1) * sizeof(char));
memset(description, 0, sizeof(40));
status =
parse_ocp_telemetry_string_log(event_fifo_number, 0, 0, EVENT_STRING, description);
if (status != 0) {
nvme_show_error("Failed to get C9 String. status: %d\n", status);
return -1;
}
char event_fifo_name[100] = {0};
snprintf(event_fifo_name, sizeof(event_fifo_name), "%s%d%s%s", "EVENT FIFO ",
event_fifo_number, " - ", description);
struct json_object *pevent_fifo_array = NULL;
if (pevent_fifos_object != NULL)
pevent_fifo_array = json_create_array();
else {
char buffer[1024] = {0};
sprintf(buffer, "%s%s\n%s", STR_LINE, event_fifo_name, STR_LINE);
if (fp)
fprintf(fp, "%s", buffer);
else
printf("%s", buffer);
}
int offset_to_move = 0;
unsigned int event_des_size = sizeof(struct nvme_ocp_telemetry_event_descriptor);
while ((fifo_size > 0) && (offset_to_move < fifo_size)) {
struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor =
(struct nvme_ocp_telemetry_event_descriptor *)
(pfifo_start + offset_to_move);
/* check if at the end of the list */
if (pevent_descriptor->debug_event_class_type == RESERVED_CLASS_TYPE)
break;
if (pevent_descriptor != NULL && pevent_descriptor->event_data_size >= 0) {
/* Data is present in the form of DWORDS,
* So multiplying with sizeof(DWORD)
*/
unsigned int data_size = pevent_descriptor->event_data_size *
SIZE_OF_DWORD;
__u8 *pevent_specific_data = NULL;
__u16 event_id = 0;
char description_str[256] = "";
if (pevent_descriptor != NULL && pevent_descriptor->event_data_size > 0)
pevent_specific_data = (__u8 *)pevent_descriptor + event_des_size;
event_id = le16_to_cpu(pevent_descriptor->event_id);
parse_ocp_telemetry_string_log(0, event_id,
pevent_descriptor->debug_event_class_type, EVENT_STRING,
description_str);
struct json_object *pevent_descriptor_obj =
((pevent_fifos_object != NULL)?json_create_object():NULL);
if (pevent_descriptor_obj != NULL) {
json_add_formatted_u32_str(pevent_descriptor_obj,
STR_DBG_EVENT_CLASS_TYPE,
pevent_descriptor->debug_event_class_type);
json_add_formatted_u32_str(pevent_descriptor_obj,
STR_EVENT_IDENTIFIER, event_id);
json_object_add_value_string(pevent_descriptor_obj,
STR_EVENT_STRING, description_str);
json_add_formatted_u32_str(pevent_descriptor_obj,
STR_EVENT_DATA_SIZE, pevent_descriptor->event_data_size);
if (pevent_descriptor->debug_event_class_type >= 0x80)
json_add_formatted_var_size_str(pevent_descriptor_obj,
STR_VU_DATA, pevent_specific_data, data_size);
} else {
if (fp) {
fprintf(fp, "%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE,
pevent_descriptor->debug_event_class_type);
fprintf(fp, "%s: 0x%x\n", STR_EVENT_IDENTIFIER,
event_id);
fprintf(fp, "%s: %s\n", STR_EVENT_STRING, description_str);
fprintf(fp, "%s: 0x%x\n", STR_EVENT_DATA_SIZE,
pevent_descriptor->event_data_size);
} else {
printf("%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE,
pevent_descriptor->debug_event_class_type);
printf("%s: 0x%x\n", STR_EVENT_IDENTIFIER,
event_id);
printf("%s: %s\n", STR_EVENT_STRING, description_str);
printf("%s: 0x%x\n", STR_EVENT_DATA_SIZE,
pevent_descriptor->event_data_size);
}
if (pevent_descriptor->debug_event_class_type >= 0x80)
print_formatted_var_size_str(STR_VU_DATA,
pevent_specific_data, data_size, fp);
}
switch (pevent_descriptor->debug_event_class_type) {
case TIME_STAMP_CLASS_TYPE:
parse_time_stamp_event(pevent_descriptor,
pevent_descriptor_obj,
pevent_specific_data,
pevent_fifos_object,
fp);
break;
case PCIE_CLASS_TYPE:
parse_pcie_event(pevent_descriptor,
pevent_descriptor_obj,
pevent_specific_data,
pevent_fifos_object,
fp);
break;
case NVME_CLASS_TYPE:
parse_nvme_event(pevent_descriptor,
pevent_descriptor_obj,
pevent_specific_data,
pevent_fifos_object,
fp);
break;
case RESET_CLASS_TYPE:
case BOOT_SEQUENCE_CLASS_TYPE:
case FIRMWARE_ASSERT_CLASS_TYPE:
case TEMPERATURE_CLASS_TYPE:
case MEDIA_CLASS_TYPE:
parse_common_event(pevent_descriptor,
pevent_descriptor_obj,
pevent_specific_data,
pevent_fifos_object,
fp);
break;
case MEDIA_WEAR_CLASS_TYPE:
parse_media_wear_event(pevent_descriptor,
pevent_descriptor_obj,
pevent_specific_data,
pevent_fifos_object,
fp);
break;
case STATISTIC_SNAPSHOT_CLASS_TYPE: {
struct nvme_ocp_statistic_snapshot_evt_class_format
*pStaticSnapshotEvent =
(struct nvme_ocp_statistic_snapshot_evt_class_format *)
pevent_specific_data;
struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry =
(struct nvme_ocp_telemetry_statistic_descriptor *)
(&pStaticSnapshotEvent->statisticDescriptorData);
parse_statistic(pstatistic_entry, pevent_descriptor_obj, fp);
break;
}
case RESERVED_CLASS_TYPE:
default:
break;
}
if (pevent_descriptor_obj != NULL && pevent_fifo_array != NULL)
json_array_add_value_object(pevent_fifo_array,
pevent_descriptor_obj);
else {
if (fp)
fprintf(fp, STR_LINE2);
else
printf(STR_LINE2);
}
} else
break;
offset_to_move += (pevent_descriptor->event_data_size * SIZE_OF_DWORD +
event_des_size);
}
if (pevent_fifos_object != NULL && pevent_fifo_array != NULL)
json_object_add_value_array(pevent_fifos_object, event_fifo_name,
pevent_fifo_array);
free(description);
return 0;
}
int parse_event_fifos(struct json_object *root, struct nvme_ocp_telemetry_offsets *poffsets,
FILE *fp)
{
if (poffsets == NULL) {
nvme_show_error("Input buffer was NULL");
return -1;
}
struct json_object *pevent_fifos_object = NULL;
if (root != NULL)
pevent_fifos_object = json_create_object();
__u8 *pda1_header_offset = ptelemetry_buffer + poffsets->da1_start_offset;//512
__u8 *pda2_offset = ptelemetry_buffer + poffsets->da2_start_offset;
struct nvme_ocp_header_in_da1 *pda1_header = (struct nvme_ocp_header_in_da1 *)
pda1_header_offset;
struct nvme_ocp_event_fifo_data event_fifo[MAX_NUM_FIFOS];
for (int fifo_num = 0; fifo_num < MAX_NUM_FIFOS; fifo_num++) {
event_fifo[fifo_num].event_fifo_num = fifo_num;
event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_da[fifo_num];
event_fifo[fifo_num].event_fifo_start =
pda1_header->fifo_offsets[fifo_num].event_fifo_start;
event_fifo[fifo_num].event_fifo_size =
pda1_header->fifo_offsets[fifo_num].event_fifo_size;
}
//Parse all the FIFOs DA wise
for (int fifo_no = 0; fifo_no < MAX_NUM_FIFOS; fifo_no++) {
if (event_fifo[fifo_no].event_fifo_da == poffsets->data_area) {
__u64 fifo_offset =
(event_fifo[fifo_no].event_fifo_start * SIZE_OF_DWORD);
__u64 fifo_size =
(event_fifo[fifo_no].event_fifo_size * SIZE_OF_DWORD);
__u8 *pfifo_start = NULL;
if (event_fifo[fifo_no].event_fifo_da == 1)
pfifo_start = pda1_header_offset + fifo_offset;
else if (event_fifo[fifo_no].event_fifo_da == 2)
pfifo_start = pda2_offset + fifo_offset;
else {
nvme_show_error("Unsupported Data Area:[%d]", poffsets->data_area);
return -1;
}
int status = parse_event_fifo(fifo_no, pfifo_start, pevent_fifos_object,
pstring_buffer, poffsets, fifo_size, fp);
if (status != 0) {
nvme_show_error("Failed to parse Event FIFO. status:%d\n", status);
return -1;
}
}
}
if (pevent_fifos_object != NULL && root != NULL) {
const char *data_area = (poffsets->data_area == 1 ? STR_DA_1_EVENT_FIFO_INFO :
STR_DA_2_EVENT_FIFO_INFO);
json_object_add_value_array(root, data_area, pevent_fifos_object);
}
return 0;
}
int parse_statistic(struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry,
struct json_object *pstats_array, FILE *fp)
{
if (pstatistic_entry == NULL) {
nvme_show_error("Input buffer was NULL");
return -1;
}
unsigned int data_size = pstatistic_entry->statistic_data_size * SIZE_OF_DWORD;
__u8 *pdata = (__u8 *)pstatistic_entry +
sizeof(struct nvme_ocp_telemetry_statistic_descriptor);
char description_str[256] = "";
parse_ocp_telemetry_string_log(0, pstatistic_entry->statistic_id, 0,
STATISTICS_IDENTIFIER_STRING, description_str);
if (pstats_array != NULL) {
struct json_object *pstatistics_object = json_create_object();
json_add_formatted_u32_str(pstatistics_object, STR_STATISTICS_IDENTIFIER,
pstatistic_entry->statistic_id);
json_object_add_value_string(pstatistics_object, STR_STATISTICS_IDENTIFIER_STR,
description_str);
json_add_formatted_u32_str(pstatistics_object,
STR_STATISTICS_INFO_BEHAVIOUR_TYPE,
pstatistic_entry->statistic_info_behaviour_type);
json_add_formatted_u32_str(pstatistics_object, STR_STATISTICS_INFO_RESERVED,
pstatistic_entry->statistic_info_reserved);
json_add_formatted_u32_str(pstatistics_object, STR_NAMESPACE_IDENTIFIER,
pstatistic_entry->ns_info_nsid);
json_add_formatted_u32_str(pstatistics_object, STR_NAMESPACE_INFO_VALID,
pstatistic_entry->ns_info_ns_info_valid);
json_add_formatted_u32_str(pstatistics_object, STR_STATISTICS_DATA_SIZE,
pstatistic_entry->statistic_data_size);
json_add_formatted_u32_str(pstatistics_object, STR_RESERVED,
pstatistic_entry->reserved);
json_add_formatted_var_size_str(pstatistics_object, STR_STATISTICS_SPECIFIC_DATA,
pdata, data_size);
if (pstatistics_object != NULL)
json_array_add_value_object(pstats_array, pstatistics_object);
} else {
if (fp) {
fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_IDENTIFIER,
pstatistic_entry->statistic_id);
fprintf(fp, "%s: %s\n", STR_STATISTICS_IDENTIFIER_STR, description_str);
fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_INFO_BEHAVIOUR_TYPE,
pstatistic_entry->statistic_info_behaviour_type);
fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_INFO_RESERVED,
pstatistic_entry->statistic_info_reserved);
fprintf(fp, "%s: 0x%x\n", STR_NAMESPACE_IDENTIFIER,
pstatistic_entry->ns_info_nsid);
fprintf(fp, "%s: 0x%x\n", STR_NAMESPACE_INFO_VALID,
pstatistic_entry->ns_info_ns_info_valid);
fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_DATA_SIZE,
pstatistic_entry->statistic_data_size);
fprintf(fp, "%s: 0x%x\n", STR_RESERVED, pstatistic_entry->reserved);
print_formatted_var_size_str(STR_STATISTICS_SPECIFIC_DATA, pdata,
data_size, fp);
fprintf(fp, STR_LINE2);
} else {
printf("%s: 0x%x\n", STR_STATISTICS_IDENTIFIER,
pstatistic_entry->statistic_id);
printf("%s: %s\n", STR_STATISTICS_IDENTIFIER_STR, description_str);
printf("%s: 0x%x\n", STR_STATISTICS_INFO_BEHAVIOUR_TYPE,
pstatistic_entry->statistic_info_behaviour_type);
printf("%s: 0x%x\n", STR_STATISTICS_INFO_RESERVED,
pstatistic_entry->statistic_info_reserved);
printf("%s: 0x%x\n", STR_NAMESPACE_IDENTIFIER,
pstatistic_entry->ns_info_nsid);
printf("%s: 0x%x\n", STR_NAMESPACE_INFO_VALID,
pstatistic_entry->ns_info_ns_info_valid);
printf("%s: 0x%x\n", STR_STATISTICS_DATA_SIZE,
pstatistic_entry->statistic_data_size);
printf("%s: 0x%x\n", STR_RESERVED, pstatistic_entry->reserved);
print_formatted_var_size_str(STR_STATISTICS_SPECIFIC_DATA, pdata,
data_size, fp);
printf(STR_LINE2);
}
}
return 0;
}
int parse_statistics(struct json_object *root, struct nvme_ocp_telemetry_offsets *poffsets,
FILE *fp)
{
if (poffsets == NULL) {
nvme_show_error("Input buffer was NULL");
return -1;
}
__u8 *pda1_ocp_header_offset = ptelemetry_buffer + poffsets->header_size;//512
__u32 statistics_size = 0;
__u32 stats_da_1_start_dw = 0, stats_da_1_size_dw = 0;
__u32 stats_da_2_start_dw = 0, stats_da_2_size_dw = 0;
__u8 *pstats_offset = NULL;
if (poffsets->data_area == 1) {
__u32 stats_da_1_start = *(__u32 *)(pda1_ocp_header_offset +
offsetof(struct nvme_ocp_header_in_da1, da1_statistic_start));
__u32 stats_da_1_size = *(__u32 *)(pda1_ocp_header_offset +
offsetof(struct nvme_ocp_header_in_da1, da1_statistic_size));
//Data is present in the form of DWORDS, So multiplying with sizeof(DWORD)
stats_da_1_start_dw = (stats_da_1_start * SIZE_OF_DWORD);
stats_da_1_size_dw = (stats_da_1_size * SIZE_OF_DWORD);
pstats_offset = pda1_ocp_header_offset + stats_da_1_start_dw;
statistics_size = stats_da_1_size_dw;
} else if (poffsets->data_area == 2) {
__u32 stats_da_2_start = *(__u32 *)(pda1_ocp_header_offset +
offsetof(struct nvme_ocp_header_in_da1, da2_statistic_start));
__u32 stats_da_2_size = *(__u32 *)(pda1_ocp_header_offset +
offsetof(struct nvme_ocp_header_in_da1, da2_statistic_size));
stats_da_2_start_dw = (stats_da_2_start * SIZE_OF_DWORD);
stats_da_2_size_dw = (stats_da_2_size * SIZE_OF_DWORD);
pstats_offset = pda1_ocp_header_offset + poffsets->da1_size + stats_da_2_start_dw;
statistics_size = stats_da_2_size_dw;
} else {
nvme_show_error("Unsupported Data Area:[%d]", poffsets->data_area);
return -1;
}
struct json_object *pstats_array = ((root != NULL) ? json_create_array() : NULL);
__u32 stat_des_size = sizeof(struct nvme_ocp_telemetry_statistic_descriptor);//8
__u32 offset_to_move = 0;
while (((statistics_size > 0) && (offset_to_move < statistics_size))) {
struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry =
(struct nvme_ocp_telemetry_statistic_descriptor *)
(pstats_offset + offset_to_move);
parse_statistic(pstatistic_entry, pstats_array, fp);
offset_to_move += (pstatistic_entry->statistic_data_size * SIZE_OF_DWORD +
stat_des_size);
}
if (root != NULL && pstats_array != NULL) {
const char *pdata_area =
(poffsets->data_area == 1 ? STR_DA_1_STATS : STR_DA_2_STATS);
json_object_add_value_array(root, pdata_area, pstats_array);
}
return 0;
}
int print_ocp_telemetry_normal(struct ocp_telemetry_parse_options *options)
{
int status = 0;
if (options->output_file != NULL) {
FILE *fp = fopen(options->output_file, "w");
if (fp) {
fprintf(fp, STR_LINE);
fprintf(fp, "%s\n", STR_LOG_PAGE_HEADER);
fprintf(fp, STR_LINE);
if (!strcmp(options->telemetry_type, "host")) {
if ((ptelemetry_buffer == NULL) ||
(ARRAY_SIZE(host_log_page_header) == 0))
printf("skip generic_structure_parser\n");
else
generic_structure_parser(ptelemetry_buffer,
host_log_page_header,
ARRAY_SIZE(host_log_page_header),
NULL, 0, fp);
}
else if (!strcmp(options->telemetry_type, "controller"))
generic_structure_parser(ptelemetry_buffer,
controller_log_page_header,
ARRAY_SIZE(controller_log_page_header), NULL, 0, fp);
fprintf(fp, STR_LINE);
fprintf(fp, "%s\n", STR_REASON_IDENTIFIER);
fprintf(fp, STR_LINE);
__u8 *preason_identifier_offset = ptelemetry_buffer +
offsetof(struct nvme_ocp_telemetry_host_initiated_header,
reason_id);
generic_structure_parser(preason_identifier_offset, reason_identifier,
ARRAY_SIZE(reason_identifier), NULL, 0, fp);
fprintf(fp, STR_LINE);
fprintf(fp, "%s\n", STR_TELEMETRY_HOST_DATA_BLOCK_1);
fprintf(fp, STR_LINE);
//Set DA to 1 and get offsets
struct nvme_ocp_telemetry_offsets offsets = { 0 };
offsets.data_area = 1;// Default DA - DA1
struct nvme_ocp_telemetry_common_header *ptelemetry_common_header =
(struct nvme_ocp_telemetry_common_header *) ptelemetry_buffer;
get_telemetry_das_offset_and_size(ptelemetry_common_header, &offsets);
__u8 *pda1_header_offset = ptelemetry_buffer +
offsets.da1_start_offset;//512
generic_structure_parser(pda1_header_offset, ocp_header_in_da1,
ARRAY_SIZE(ocp_header_in_da1), NULL, 0, fp);
fprintf(fp, STR_LINE);
fprintf(fp, "%s\n", STR_SMART_HEALTH_INFO);
fprintf(fp, STR_LINE);
__u8 *pda1_smart_offset = pda1_header_offset +
offsetof(struct nvme_ocp_header_in_da1, smart_health_info);
//512+512 =1024
generic_structure_parser(pda1_smart_offset, smart, ARRAY_SIZE(smart),
NULL, 0, fp);
fprintf(fp, STR_LINE);
fprintf(fp, "%s\n", STR_SMART_HEALTH_INTO_EXTENDED);
fprintf(fp, STR_LINE);
__u8 *pda1_smart_ext_offset = pda1_header_offset +
offsetof(struct nvme_ocp_header_in_da1,
smart_health_info_extended);
generic_structure_parser(pda1_smart_ext_offset, smart_extended,
ARRAY_SIZE(smart_extended), NULL, 0, fp);
fprintf(fp, STR_LINE);
fprintf(fp, "%s\n", STR_DA_1_STATS);
fprintf(fp, STR_LINE);
status = parse_statistics(NULL, &offsets, fp);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
fprintf(fp, STR_LINE);
fprintf(fp, "%s\n", STR_DA_1_EVENT_FIFO_INFO);
fprintf(fp, STR_LINE);
status = parse_event_fifos(NULL, &offsets, fp);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
//Set the DA to 2
if (options->data_area == 2) {
offsets.data_area = 2;
fprintf(fp, STR_LINE);
fprintf(fp, "%s\n", STR_DA_2_STATS);
fprintf(fp, STR_LINE);
status = parse_statistics(NULL, &offsets, fp);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
fprintf(fp, STR_LINE);
fprintf(fp, "%s\n", STR_DA_2_EVENT_FIFO_INFO);
fprintf(fp, STR_LINE);
status = parse_event_fifos(NULL, &offsets, fp);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
}
fprintf(fp, STR_LINE);
fclose(fp);
} else {
nvme_show_error("Failed to open %s file.\n", options->output_file);
return -1;
}
} else {
printf(STR_LINE);
printf("%s\n", STR_LOG_PAGE_HEADER);
printf(STR_LINE);
if (!strcmp(options->telemetry_type, "host")) {
if ((ptelemetry_buffer == NULL) ||
(ARRAY_SIZE(host_log_page_header) == 0))
printf("skip generic_structure_parser\n");
else {
generic_structure_parser(ptelemetry_buffer, host_log_page_header,
ARRAY_SIZE(host_log_page_header), NULL, 0, NULL);
}
}
else if (!strcmp(options->telemetry_type, "controller"))
generic_structure_parser(ptelemetry_buffer, controller_log_page_header,
ARRAY_SIZE(controller_log_page_header), NULL, 0, NULL);
printf(STR_LINE);
printf("%s\n", STR_REASON_IDENTIFIER);
printf(STR_LINE);
__u8 *preason_identifier_offset = ptelemetry_buffer +
offsetof(struct nvme_ocp_telemetry_host_initiated_header, reason_id);
generic_structure_parser(preason_identifier_offset, reason_identifier,
ARRAY_SIZE(reason_identifier), NULL, 0, NULL);
printf(STR_LINE);
printf("%s\n", STR_TELEMETRY_HOST_DATA_BLOCK_1);
printf(STR_LINE);
//Set DA to 1 and get offsets
struct nvme_ocp_telemetry_offsets offsets = { 0 };
offsets.data_area = 1;
struct nvme_ocp_telemetry_common_header *ptelemetry_common_header =
(struct nvme_ocp_telemetry_common_header *) ptelemetry_buffer;
get_telemetry_das_offset_and_size(ptelemetry_common_header, &offsets);
__u8 *pda1_header_offset = ptelemetry_buffer + offsets.da1_start_offset;//512
generic_structure_parser(pda1_header_offset, ocp_header_in_da1,
ARRAY_SIZE(ocp_header_in_da1), NULL, 0, NULL);
printf(STR_LINE);
printf("%s\n", STR_SMART_HEALTH_INFO);
printf(STR_LINE);
__u8 *pda1_smart_offset = pda1_header_offset +
offsetof(struct nvme_ocp_header_in_da1, smart_health_info);
generic_structure_parser(pda1_smart_offset, smart, ARRAY_SIZE(smart), NULL, 0,
NULL);
printf(STR_LINE);
printf("%s\n", STR_SMART_HEALTH_INTO_EXTENDED);
printf(STR_LINE);
__u8 *pda1_smart_ext_offset = pda1_header_offset +
offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended);
generic_structure_parser(pda1_smart_ext_offset, smart_extended,
ARRAY_SIZE(smart_extended), NULL, 0, NULL);
printf(STR_LINE);
printf("%s\n", STR_DA_1_STATS);
printf(STR_LINE);
status = parse_statistics(NULL, &offsets, NULL);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
printf(STR_LINE);
printf("%s\n", STR_DA_1_EVENT_FIFO_INFO);
printf(STR_LINE);
status = parse_event_fifos(NULL, &offsets, NULL);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
//Set the DA to 2
if (options->data_area == 2) {
offsets.data_area = 2;
printf(STR_LINE);
printf("%s\n", STR_DA_2_STATS);
printf(STR_LINE);
status = parse_statistics(NULL, &offsets, NULL);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
printf(STR_LINE);
printf("%s\n", STR_DA_2_EVENT_FIFO_INFO);
printf(STR_LINE);
status = parse_event_fifos(NULL, &offsets, NULL);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
}
printf(STR_LINE);
}
return status;
}
int print_ocp_telemetry_json(struct ocp_telemetry_parse_options *options)
{
int status = 0;
//create json objects
struct json_object *root, *pheader, *preason_identifier, *da1_header, *smart_obj,
*ext_smart_obj;
root = json_create_object();
//Add data to root json object
//"Log Page Header"
pheader = json_create_object();
generic_structure_parser(ptelemetry_buffer, host_log_page_header,
ARRAY_SIZE(host_log_page_header), pheader, 0, NULL);
json_object_add_value_object(root, STR_LOG_PAGE_HEADER, pheader);
//"Reason Identifier"
preason_identifier = json_create_object();
__u8 *preason_identifier_offset = ptelemetry_buffer +
offsetof(struct nvme_ocp_telemetry_host_initiated_header, reason_id);
generic_structure_parser(preason_identifier_offset, reason_identifier,
ARRAY_SIZE(reason_identifier), preason_identifier, 0, NULL);
json_object_add_value_object(pheader, STR_REASON_IDENTIFIER, preason_identifier);
struct nvme_ocp_telemetry_offsets offsets = { 0 };
//Set DA to 1 and get offsets
offsets.data_area = 1;
struct nvme_ocp_telemetry_common_header *ptelemetry_common_header =
(struct nvme_ocp_telemetry_common_header *) ptelemetry_buffer;
get_telemetry_das_offset_and_size(ptelemetry_common_header, &offsets);
//"Telemetry Host-Initiated Data Block 1"
__u8 *pda1_header_offset = ptelemetry_buffer + offsets.da1_start_offset;//512
da1_header = json_create_object();
generic_structure_parser(pda1_header_offset, ocp_header_in_da1,
ARRAY_SIZE(ocp_header_in_da1), da1_header, 0, NULL);
json_object_add_value_object(root, STR_TELEMETRY_HOST_DATA_BLOCK_1, da1_header);
//"SMART / Health Information Log(LID-02h)"
__u8 *pda1_smart_offset = pda1_header_offset + offsetof(struct nvme_ocp_header_in_da1,
smart_health_info);
smart_obj = json_create_object();
generic_structure_parser(pda1_smart_offset, smart, ARRAY_SIZE(smart), smart_obj, 0, NULL);
json_object_add_value_object(da1_header, STR_SMART_HEALTH_INFO, smart_obj);
//"SMART / Health Information Extended(LID-C0h)"
__u8 *pda1_smart_ext_offset = pda1_header_offset + offsetof(struct nvme_ocp_header_in_da1,
smart_health_info_extended);
ext_smart_obj = json_create_object();
generic_structure_parser(pda1_smart_ext_offset, smart_extended, ARRAY_SIZE(smart_extended),
ext_smart_obj, 0, NULL);
json_object_add_value_object(da1_header, STR_SMART_HEALTH_INTO_EXTENDED, ext_smart_obj);
//Data Area 1 Statistics
status = parse_statistics(root, &offsets, NULL);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
//Data Area 1 Event FIFOs
status = parse_event_fifos(root, &offsets, NULL);
if (status != 0) {
nvme_show_error("status: %d\n", status, NULL);
return -1;
}
if (options->data_area == 2) {
//Set the DA to 2
offsets.data_area = 2;
//Data Area 2 Statistics
status = parse_statistics(root, &offsets, NULL);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
//Data Area 2 Event FIFOs
status = parse_event_fifos(root, &offsets, NULL);
if (status != 0) {
nvme_show_error("status: %d\n", status);
return -1;
}
}
if (options->output_file != NULL) {
const char *json_string = json_object_to_json_string(root);
FILE *fp = fopen(options->output_file, "w");
if (fp) {
fputs(json_string, fp);
fclose(fp);
} else {
nvme_show_error("Failed to open %s file.\n", options->output_file);
return -1;
}
} else {
//Print root json object
json_print_object(root, NULL);
nvme_show_result("\n");
json_free_object(root);
}
return status;
}
#endif /* CONFIG_JSONC */