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