diff options
Diffstat (limited to 'plugins/solidigm/solidigm-telemetry')
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/cod.c | 190 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/cod.h | 9 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/config.c | 76 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/config.h | 19 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/data-area.c | 472 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/data-area.h | 10 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/header.c | 223 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/header.h | 9 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/meson.build | 7 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/nlog.c | 131 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/nlog.h | 11 | ||||
-rw-r--r-- | plugins/solidigm/solidigm-telemetry/telemetry-log.h | 31 |
12 files changed, 1188 insertions, 0 deletions
diff --git a/plugins/solidigm/solidigm-telemetry/cod.c b/plugins/solidigm/solidigm-telemetry/cod.c new file mode 100644 index 0000000..363822a --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/cod.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ +#include "common.h" +#include "cod.h" + +const char *oemDataMapDesc[] = { + "Media Read Count", //Uid 0x00 + "Host Read count", //Uid 0x01 + "Media Write Count", //Uid 0x02 + "Host Write Count", //Uid 0x03 + "Device Model", // 0x04 + "Serial Number", // 0x05 + "Firmware Revision", // 0x06 + "Drive Status", // 0x07 + "Minimum Temperature", // 0x08 + "Maximum Temperature", // 0x09 + "Power Loss Protection Status", // 0x0a + "Lifetime Unsafe Shutdown Count", // 0x0b + "Lifetime Power Cycle Count", // 0x0c + "Minimum Read Latency", // 0x0d + "Maximum Read Latency", // 0x0e + "Average Read Latency", // 0x0f + "Minimum Write Latency", // 0x10 + "Maximum Write Latency", // 0x11 + "Average Write Latency", // 0x12 + "Grown Defects Count", // 0x13 + "DQS Recovery Count", // 0x14 + "Program Fail Count", // 0x15 + "Erase Fail Count", // 0x16 + "Defrag Writes in Progress Count", // 0x17 + "Total Defrag Writes Count", // 0x18 + "Max Die Offline Number", // 0x19 + "Current Die Offline Number", // 0x1A + "XOR Enable Status", // 0x1B + "Media Life Used", // 0x1C + "Uncorrectable Error Count", // 0x1D + "Current Wear Range Delta", // 0x1E + "Read Errors Corrected by XOR", // 0x1F + "Background Data Refresh", // 0x20 + "Pmic Vin History Data 1 Min", // 0x21 + "Pmic Vin History Data 1 Max", // 0x22 + "Pmic Vin History Data 1 Avg", // 0x23 + "Pmic Vin History Data 2 Min", // 0x24 + "Pmic Vin History Data 2 Max", // 0x25 + "Pmic Vin History Data 2 Avg", // 0x26 + "Pmic Vin History Data Total Readings", // 0x27 + "All Time Current Max Wear Level", // 0x28 + "Media Wear Remaining", // 0x29 + "Total Non-Defrag Writes", // 0x2A + "Media Health Relocations" //Uid 0x2B = 43 +}; + +static const char *getOemDataMapDescription(uint32_t id) +{ + if (id < ARRAY_SIZE(oemDataMapDesc)) + return oemDataMapDesc[id]; + return "unknown"; +} + +#define OEMSIGNATURE 0x504D4443 + +#pragma pack(push, cod, 1) +struct cod_header { + uint32_t versionMajor; + uint32_t versionMinor; + uint32_t Signature; //!Fixed signature value (0x504D4443) for identification and validation + uint32_t MapSizeInBytes; //!Total size of the map data structure in bytes + uint32_t EntryCount; //!Total number of entries in the entry list + uint8_t Reserved[12]; +}; + +struct cod_item { + uint32_t DataFieldMapUid; //!The data field unique identifier value + uint32_t reserved1 : 8; + uint32_t dataFieldType : 8; + uint32_t issigned : 1; + uint32_t bigEndian : 1; + uint32_t dataInvalid : 1; + uint32_t reserved2 : 13; + uint32_t DataFieldSizeInBytes; + uint8_t Reserved1[4]; + uint64_t DataFieldOffset; + uint8_t Reserved2[8]; +}; + +struct cod_map { + struct cod_header header; + struct cod_item items[]; +}; + +#pragma pack(pop, cod) + +void solidigm_telemetry_log_cod_parse(struct telemetry_log *tl) +{ + enum cod_field_type { + INTEGER, + FLOAT, + STRING, + TWO_BYTE_ASCII, + FOUR_BYTE_ASCII, + + UNKNOWN = 0xFF, + }; + struct json_object *telemetry_header = NULL; + struct json_object *COD_offset = NULL; + struct json_object *reason_id = NULL; + + if (!json_object_object_get_ex(tl->root, "telemetryHeader", &telemetry_header)) + return; + if (!json_object_object_get_ex(telemetry_header, "reasonIdentifier", &reason_id)) + return; + if (!json_object_object_get_ex(reason_id, "oemDataMapOffset", &COD_offset)) + return; + + uint64_t offset = json_object_get_int(COD_offset); + + if (!offset) + return; + + if ((offset + sizeof(struct cod_header)) > tl->log_size) { + SOLIDIGM_LOG_WARNING("Warning: COD map header out of bounds."); + return; + } + + const struct cod_map *data = (struct cod_map *) (((uint8_t *)tl->log) + offset); + + uint32_t signature = be32_to_cpu(data->header.Signature); + + if (signature != OEMSIGNATURE) { + SOLIDIGM_LOG_WARNING("Warning: Unsupported COD data signature %x!", signature); + return; + } + if ((offset + data->header.MapSizeInBytes) > tl->log_size) { + SOLIDIGM_LOG_WARNING("Warning: COD map data out of bounds."); + return; + } + + struct json_object *cod = json_create_object(); + + json_object_object_add(tl->root, "cod", cod); + + for (uint64_t i = 0; i < data->header.EntryCount; i++) { + if ((offset + sizeof(struct cod_header) + (i + 1) * sizeof(struct cod_item)) > + tl->log_size) { + SOLIDIGM_LOG_WARNING("Warning: COD data out of bounds at item %"PRIu64"!", + i); + return; + } + struct cod_item item = data->items[i]; + + if (item.DataFieldOffset + item.DataFieldOffset > tl->log_size) + continue; + if (item.dataInvalid) + continue; + uint8_t *val = ((uint8_t *)tl->log) + item.DataFieldOffset; + const char *key = getOemDataMapDescription(item.DataFieldMapUid); + + switch (item.dataFieldType) { + case INTEGER: + if (item.issigned) + json_object_object_add(cod, key, + json_object_new_int64(le64_to_cpu(*(uint64_t *)val))); + else + json_object_add_value_uint64(cod, key, le64_to_cpu(*(uint64_t *)val)); + break; + case FLOAT: + json_object_add_value_float(cod, key, *(float *)val); + break; + case STRING: + json_object_object_add(cod, key, + json_object_new_string_len((const char *)val, item.DataFieldSizeInBytes)); + break; + case TWO_BYTE_ASCII: + json_object_object_add(cod, key, + json_object_new_string_len((const char *)val, 2)); + break; + case FOUR_BYTE_ASCII: + json_object_object_add(cod, key, + json_object_new_string_len((const char *)val, 4)); + break; + default: + SOLIDIGM_LOG_WARNING("Warning: Unknown COD field type (%d)", item.DataFieldMapUid); + break; + } + } +} diff --git a/plugins/solidigm/solidigm-telemetry/cod.h b/plugins/solidigm/solidigm-telemetry/cod.h new file mode 100644 index 0000000..032ccdc --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/cod.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "telemetry-log.h" +void solidigm_telemetry_log_cod_parse(struct telemetry_log *tl); diff --git a/plugins/solidigm/solidigm-telemetry/config.c b/plugins/solidigm/solidigm-telemetry/config.c new file mode 100644 index 0000000..eceeede --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/config.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include <stdio.h> +#include <string.h> +#include "config.h" + +// max 16 bit unsigned integer number 65535 +#define MAX_16BIT_NUM_AS_STRING_SIZE 6 + +#define OBJ_NAME_PREFIX "UID_" +#define NLOG_OBJ_PREFIX OBJ_NAME_PREFIX "NLOG_" + +static bool config_get_by_version(const struct json_object *obj, int version_major, + int version_minor, struct json_object **value) +{ + char str_key[MAX_16BIT_NUM_AS_STRING_SIZE]; + char str_subkey[MAX_16BIT_NUM_AS_STRING_SIZE]; + + snprintf(str_key, sizeof(str_key), "%d", version_major); + snprintf(str_subkey, sizeof(str_subkey), "%d", version_minor); + struct json_object *major_obj = NULL; + + if (!json_object_object_get_ex(obj, str_key, &major_obj)) + return false; + if (!json_object_object_get_ex(major_obj, str_subkey, value)) + return false; + return value != NULL; +} + +bool solidigm_config_get_struct_by_token_version(const struct json_object *config, int token_id, + int version_major, int version_minor, + struct json_object **value) +{ + struct json_object *token = NULL; + char str_key[MAX_16BIT_NUM_AS_STRING_SIZE]; + + snprintf(str_key, sizeof(str_key), "%d", token_id); + if (!json_object_object_get_ex(config, str_key, &token)) + return false; + if (!config_get_by_version(token, version_major, version_minor, value)) + return false; + return value != NULL; +} + +const char *solidigm_config_get_nlog_obj_name(const struct json_object *config, uint32_t token) +{ + struct json_object *nlog_names = NULL; + struct json_object *obj_name; + char hex_header[STR_HEX32_SIZE]; + const char *name; + + if (!json_object_object_get_ex(config, "TELEMETRY_OBJECT_UIDS", &nlog_names)) + return NULL; + snprintf(hex_header, STR_HEX32_SIZE, "0x%08X", token); + + if (!json_object_object_get_ex(nlog_names, hex_header, &obj_name)) + return NULL; + name = json_object_get_string(obj_name); + if (strncmp(NLOG_OBJ_PREFIX, name, strlen(NLOG_OBJ_PREFIX))) + return NULL; + + return &name[strlen(OBJ_NAME_PREFIX)]; +} + +struct json_object *solidigm_config_get_nlog_formats(const struct json_object *config) +{ + struct json_object *nlog_formats = NULL; + + json_object_object_get_ex(config, "NLOG_FORMATS", &nlog_formats); + return nlog_formats; +} diff --git a/plugins/solidigm/solidigm-telemetry/config.h b/plugins/solidigm/solidigm-telemetry/config.h new file mode 100644 index 0000000..4e56ba3 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/config.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ +#include <stdbool.h> +#include "util/json.h" + +#define STR_HEX32_SIZE sizeof("0x00000000") + +bool solidigm_config_get_struct_by_token_version(const struct json_object *obj, + int key, int subkey, + int subsubkey, + struct json_object **value); + +const char *solidigm_config_get_nlog_obj_name(const struct json_object *config, uint32_t token); +struct json_object *solidigm_config_get_nlog_formats(const struct json_object *config); + diff --git a/plugins/solidigm/solidigm-telemetry/data-area.c b/plugins/solidigm/solidigm-telemetry/data-area.c new file mode 100644 index 0000000..14d612b --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/data-area.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "common.h" +#include "header.h" +#include "cod.h" +#include "data-area.h" +#include "config.h" +#include "nlog.h" +#include <ctype.h> + +#define SIGNED_INT_PREFIX "int" +#define BITS_IN_BYTE 8 + +#define MAX_WARNING_SIZE 1024 +#define MAX_ARRAY_RANK 16 + +static bool telemetry_log_get_value(const struct telemetry_log *tl, + uint64_t offset_bit, uint32_t size_bit, + bool is_signed, struct json_object **val_obj) +{ + uint32_t offset_bit_from_byte; + uint32_t additional_size_byte; + uint32_t offset_byte; + uint64_t val; + + if (!size_bit) { + char err_msg[MAX_WARNING_SIZE]; + + snprintf(err_msg, MAX_WARNING_SIZE, + "Value with size_bit=0 not supported."); + *val_obj = json_object_new_string(err_msg); + + return false; + } + additional_size_byte = (size_bit - 1) ? (size_bit - 1) / BITS_IN_BYTE : 0; + offset_byte = (uint32_t)offset_bit / BITS_IN_BYTE; + + if (offset_byte > (tl->log_size - additional_size_byte)) { + char err_msg[MAX_WARNING_SIZE]; + + snprintf(err_msg, MAX_WARNING_SIZE, + "Value offset greater than binary size (%u > %zu).", + offset_byte, tl->log_size); + *val_obj = json_object_new_string(err_msg); + + return false; + } + + offset_bit_from_byte = (uint32_t) (offset_bit - ((uint64_t)offset_byte * BITS_IN_BYTE)); + + if ((size_bit + offset_bit_from_byte) > (sizeof(uint64_t) * BITS_IN_BYTE)) { + char err_msg[MAX_WARNING_SIZE]; + + snprintf(err_msg, MAX_WARNING_SIZE, + "Value crossing 64 bit, byte aligned boundary, not supported. size_bit=%u, offset_bit_from_byte=%u.", + size_bit, offset_bit_from_byte); + *val_obj = json_object_new_string(err_msg); + + return false; + } + + val = *(uint64_t *)(((char *)tl->log) + offset_byte); + val >>= offset_bit_from_byte; + if (size_bit < 64) + val &= (1ULL << size_bit) - 1; + if (is_signed) { + if (val >> (size_bit - 1)) + val |= (0ULL - 1) << size_bit; + *val_obj = json_object_new_int64(val); + } else { + *val_obj = json_object_new_uint64(val); + } + + return true; +} + +static int telemetry_log_structure_parse(const struct telemetry_log *tl, + struct json_object *struct_def, + uint64_t parent_offset_bit, + struct json_object *output, + struct json_object *metadata) +{ + struct json_object *obj_arraySizeArray = NULL; + struct json_object *obj = NULL; + struct json_object *obj_memberList; + struct json_object *major_dimension = NULL; + struct json_object *sub_output; + bool is_enumeration = false; + bool has_member_list; + const char *type = ""; + const char *name; + size_t array_rank; + uint64_t offset_bit; + uint32_t size_bit; + uint64_t linear_array_pos_bit; + uint32_t array_size_dimension[MAX_ARRAY_RANK]; + + if (!json_object_object_get_ex(struct_def, "name", &obj)) { + SOLIDIGM_LOG_WARNING("Warning: Structure definition missing property 'name': %s", + json_object_to_json_string(struct_def)); + return -1; + } + + name = json_object_get_string(obj); + + if (metadata) { + json_object_get(obj); + json_object_object_add(metadata, "objName", obj); + } + + if (json_object_object_get_ex(struct_def, "type", &obj)) + type = json_object_get_string(obj); + + if (!json_object_object_get_ex(struct_def, "offsetBit", &obj)) { + SOLIDIGM_LOG_WARNING( + "Warning: Structure definition missing property 'offsetBit': %s", + json_object_to_json_string(struct_def)); + return -1; + } + + offset_bit = json_object_get_uint64(obj); + + if (!json_object_object_get_ex(struct_def, "sizeBit", &obj)) { + SOLIDIGM_LOG_WARNING( + "Warning: Structure definition missing property 'sizeBit': %s", + json_object_to_json_string(struct_def)); + return -1; + } + + size_bit = (uint32_t)json_object_get_uint64(obj); + + if (json_object_object_get_ex(struct_def, "enum", &obj)) + is_enumeration = json_object_get_boolean(obj); + + has_member_list = json_object_object_get_ex(struct_def, + "memberList", + &obj_memberList); + + if (!json_object_object_get_ex(struct_def, "arraySize", + &obj_arraySizeArray)) { + SOLIDIGM_LOG_WARNING( + "Warning: Structure definition missing property 'arraySize': %s", + json_object_to_json_string(struct_def)); + return -1; + } + + array_rank = json_object_array_length(obj_arraySizeArray); + if (!array_rank) { + SOLIDIGM_LOG_WARNING( + "Warning: Structure property 'arraySize' don't support flexible array: %s", + json_object_to_json_string(struct_def)); + return -1; + } + if (array_rank > MAX_ARRAY_RANK) { + SOLIDIGM_LOG_WARNING( + "Warning: Structure property 'arraySize' don't support more than %d dimensions: %s", + MAX_ARRAY_RANK, json_object_to_json_string(struct_def)); + return -1; + } + + for (size_t i = 0; i < array_rank; i++) { + struct json_object *dimension = json_object_array_get_idx(obj_arraySizeArray, i); + + array_size_dimension[i] = json_object_get_int(dimension); + major_dimension = dimension; + } + if (array_rank > 1) { + uint32_t linear_pos_per_index = array_size_dimension[0]; + uint32_t prev_index_offset_bit = 0; + struct json_object *dimension_output; + + for (unsigned int i = 1; i < (array_rank - 1); i++) + linear_pos_per_index *= array_size_dimension[i]; + + dimension_output = json_create_array(); + if (json_object_get_type(output) == json_type_array) + json_object_array_add(output, dimension_output); + else + json_object_add_value_array(output, name, dimension_output); + + /* + * Make sure major_dimension object will not be + * deleted from memory when deleted from array + */ + json_object_get(major_dimension); + json_object_array_del_idx(obj_arraySizeArray, array_rank - 1, 1); + + for (unsigned int i = 0 ; i < array_size_dimension[0]; i++) { + struct json_object *sub_array = json_create_array(); + uint64_t offset; + + offset = parent_offset_bit + prev_index_offset_bit; + + json_object_array_add(dimension_output, sub_array); + telemetry_log_structure_parse(tl, struct_def, + offset, sub_array, NULL); + prev_index_offset_bit += linear_pos_per_index * size_bit; + } + + json_object_array_put_idx(obj_arraySizeArray, array_rank - 1, + major_dimension); + + return 0; + } + + linear_array_pos_bit = 0; + sub_output = output; + + if (array_size_dimension[0] > 1) { + sub_output = json_create_array(); + if (json_object_get_type(output) == json_type_array) + json_object_array_add(output, sub_output); + else + json_object_add_value_array(output, name, sub_output); + } + + for (uint32_t j = 0; j < array_size_dimension[0]; j++) { + if (is_enumeration || !has_member_list) { + bool is_signed = !strncmp(type, SIGNED_INT_PREFIX, sizeof(SIGNED_INT_PREFIX)-1); + struct json_object *val_obj; + uint64_t offset; + + offset = parent_offset_bit + offset_bit + linear_array_pos_bit; + if (telemetry_log_get_value(tl, offset, size_bit, is_signed, &val_obj)) { + if (array_size_dimension[0] > 1) + json_object_array_put_idx(sub_output, j, val_obj); + else + json_object_object_add(sub_output, name, val_obj); + } else { + SOLIDIGM_LOG_WARNING( + "Warning: %s From property '%s', array index %u, structure definition: %s", + json_object_get_string(val_obj), name, j, + json_object_to_json_string(struct_def)); + json_free_object(val_obj); + } + } else { + struct json_object *sub_sub_output = json_create_object(); + int num_members; + + if (array_size_dimension[0] > 1) + json_object_array_put_idx(sub_output, j, sub_sub_output); + else + json_object_add_value_object(sub_output, name, sub_sub_output); + + num_members = json_object_array_length(obj_memberList); + for (int k = 0; k < num_members; k++) { + struct json_object *member = json_object_array_get_idx(obj_memberList, k); + uint64_t offset; + + offset = parent_offset_bit + offset_bit + linear_array_pos_bit; + telemetry_log_structure_parse(tl, member, offset, + sub_sub_output, NULL); + } + } + linear_array_pos_bit += size_bit; + } + return 0; +} + +static int telemetry_log_data_area_get_offset(const struct telemetry_log *tl, + enum nvme_telemetry_da da, + uint32_t *offset, uint32_t *size) +{ + uint32_t offset_blocks = 1; + uint32_t last_block = tl->log->dalb1; + uint32_t last; + + switch (da) { + case NVME_TELEMETRY_DA_1: + offset_blocks = 1; + last_block = tl->log->dalb1; + break; + case NVME_TELEMETRY_DA_2: + offset_blocks = tl->log->dalb1 + 1; + last_block = tl->log->dalb2; + break; + case NVME_TELEMETRY_DA_3: + offset_blocks = tl->log->dalb2 + 1; + last_block = tl->log->dalb3; + break; + case NVME_TELEMETRY_DA_4: + offset_blocks = tl->log->dalb3 + 1; + last_block = tl->log->dalb4; + break; + default: + return -1; + } + + *offset = offset_blocks * NVME_LOG_TELEM_BLOCK_SIZE; + last = (last_block + 1) * NVME_LOG_TELEM_BLOCK_SIZE; + *size = last - *offset; + if ((*offset > tl->log_size) || (last > tl->log_size) || (last <= *offset)) { + SOLIDIGM_LOG_WARNING("Warning: Data Area %d don't fit this Telemetry log.", da); + return -1; + } + + return 0; +} + +static int telemetry_log_nlog_parse(const struct telemetry_log *tl, struct json_object *formats, + uint64_t nlog_file_offset, uint64_t nlog_size, + struct json_object *output, struct json_object *metadata) +{ + /* boundary check */ + if (tl->log_size < (nlog_file_offset + nlog_size)) { + const char *name = ""; + int media_bank = -1; + struct json_object *jobj; + + if (json_object_object_get_ex(metadata, "objName", &jobj)) + name = json_object_get_string(jobj); + if (json_object_object_get_ex(metadata, "mediaBankId", &jobj)) + media_bank = json_object_get_int(jobj); + SOLIDIGM_LOG_WARNING("%s:%d do not fit this log dump.", name, media_bank); + return -1; + } + return solidigm_nlog_parse(((char *) tl->log) + nlog_file_offset, + nlog_size, formats, metadata, output); +} + +struct toc_item { + uint32_t OffsetBytes; + uint32_t ContentSizeBytes; +}; + +struct data_area_header { + uint8_t versionMajor; + uint8_t versionMinor; + uint16_t TableOfContentsCount; + uint32_t DataAreaSize; + uint8_t Reserved[8]; +}; + +struct table_of_contents { + struct data_area_header header; + struct toc_item items[]; +}; + +struct telemetry_object_header { + uint16_t versionMajor; + uint16_t versionMinor; + uint32_t Token; + uint8_t CoreId; + uint8_t Reserved[3]; +}; + +static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, + enum nvme_telemetry_da da, + struct json_object *toc_array, + struct json_object *tele_obj_array) +{ + + const struct telemetry_object_header *header; + const struct table_of_contents *toc; + char *payload; + uint32_t da_offset; + uint32_t da_size; + struct json_object *nlog_formats; + + if (telemetry_log_data_area_get_offset(tl, da, &da_offset, &da_size)) + return; + + toc = (struct table_of_contents *)(((char *)tl->log) + da_offset); + payload = (char *) tl->log; + nlog_formats = solidigm_config_get_nlog_formats(tl->configuration); + + for (int i = 0; i < toc->header.TableOfContentsCount; i++) { + struct json_object *structure_definition = NULL; + struct json_object *toc_item; + uint32_t obj_offset; + bool has_struct; + const char *nlog_name = NULL; + uint32_t header_offset = sizeof(const struct telemetry_object_header); + + if ((char *)&toc->items[i] > + (((char *)toc) + da_size - sizeof(const struct toc_item))) { + SOLIDIGM_LOG_WARNING( + "Warning: Data Area %d, Table of Contents item %d crossed Data Area size.", + da, i); + return; + } + + obj_offset = toc->items[i].OffsetBytes; + if ((obj_offset + sizeof(const struct telemetry_object_header)) > da_size) { + SOLIDIGM_LOG_WARNING( + "Warning: Data Area %d, item %d data, crossed Data Area size.", da, i); + continue; + } + + toc_item = json_create_object(); + json_object_array_add(toc_array, toc_item); + json_object_add_value_uint(toc_item, "dataArea", da); + json_object_add_value_uint(toc_item, "dataAreaIndex", i); + json_object_add_value_uint(toc_item, "dataAreaOffset", obj_offset); + json_object_add_value_uint(toc_item, "fileOffset", obj_offset + da_offset); + json_object_add_value_uint(toc_item, "size", toc->items[i].ContentSizeBytes); + + header = (const struct telemetry_object_header *) (payload + da_offset + obj_offset); + json_object_add_value_uint(toc_item, "telemMajor", header->versionMajor); + json_object_add_value_uint(toc_item, "telemMinor", header->versionMinor); + json_object_add_value_uint(toc_item, "objectId", header->Token); + json_object_add_value_uint(toc_item, "mediaBankId", header->CoreId); + + has_struct = solidigm_config_get_struct_by_token_version(tl->configuration, + header->Token, + header->versionMajor, + header->versionMinor, + &structure_definition); + if (!has_struct) { + if (!nlog_formats) + continue; + nlog_name = solidigm_config_get_nlog_obj_name(tl->configuration, + header->Token); + if (!nlog_name) + continue; + } + struct json_object *tele_obj_item = json_create_object(); + + json_object_array_add(tele_obj_array, tele_obj_item); + json_object_get(toc_item); + json_object_add_value_object(tele_obj_item, "metadata", toc_item); + struct json_object *parsed_struct = json_create_object(); + + json_object_add_value_object(tele_obj_item, "objectData", parsed_struct); + struct json_object *obj_hasTelemObjHdr = NULL; + uint64_t object_file_offset; + + if (json_object_object_get_ex(structure_definition, + "hasTelemObjHdr", + &obj_hasTelemObjHdr)) { + bool hasHeader = json_object_get_boolean(obj_hasTelemObjHdr); + + if (hasHeader) + header_offset = 0; + } + object_file_offset = ((uint64_t)da_offset) + obj_offset + header_offset; + if (has_struct) { + telemetry_log_structure_parse(tl, structure_definition, + BITS_IN_BYTE * object_file_offset, + parsed_struct, toc_item); + } else if (nlog_formats) { + json_object_object_add(toc_item, "objName", + json_object_new_string(nlog_name)); + telemetry_log_nlog_parse(tl, nlog_formats, object_file_offset, + toc->items[i].ContentSizeBytes - header_offset, + parsed_struct, toc_item); + } + } +} + +int solidigm_telemetry_log_data_areas_parse(struct telemetry_log *tl, + enum nvme_telemetry_da last_da) +{ + struct json_object *tele_obj_array = json_create_array(); + struct json_object *toc_array = json_create_array(); + + solidigm_telemetry_log_header_parse(tl); + solidigm_telemetry_log_cod_parse(tl); + if (tl->configuration) { + json_object_add_value_array(tl->root, "tableOfContents", toc_array); + json_object_add_value_array(tl->root, "telemetryObjects", tele_obj_array); + + for (enum nvme_telemetry_da da = NVME_TELEMETRY_DA_1; da <= last_da; da++) + telemetry_log_data_area_toc_parse(tl, da, toc_array, tele_obj_array); + } + return 0; +} diff --git a/plugins/solidigm/solidigm-telemetry/data-area.h b/plugins/solidigm/solidigm-telemetry/data-area.h new file mode 100644 index 0000000..6b690d8 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/data-area.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ +#include "telemetry-log.h" + +int solidigm_telemetry_log_data_areas_parse(struct telemetry_log *tl, + enum nvme_telemetry_da last_da); diff --git a/plugins/solidigm/solidigm-telemetry/header.c b/plugins/solidigm/solidigm-telemetry/header.c new file mode 100644 index 0000000..866ebff --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/header.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "common.h" +#include "header.h" + +#pragma pack(push, reason_indentifier, 1) +struct reason_indentifier_1_0 { + uint16_t versionMajor; + uint16_t versionMinor; + uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue. + char DriveStatus[20]; //! Drive Status String (for example: "Healthy", "*BAD_CONTEXT_2020") + char FirmwareVersion[12]; //! Similar to IdentifyController.FR + char BootloaderVersion[12]; //! Bootloader version string + char SerialNumber[20]; //! Device serial number + uint8_t Reserved[56]; //! Reserved for future usage +}; +static_assert(sizeof(const struct reason_indentifier_1_0) == + MEMBER_SIZE(struct nvme_telemetry_log, rsnident), + "Size mismatch for reason_indentifier_1_0"); + +struct reason_indentifier_1_1 { + uint16_t versionMajor; + uint16_t versionMinor; + uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue. + char DriveStatus[20]; //! Drive Status String (for example: "Healthy", "*BAD_CONTEXT_2020") + char FirmwareVersion[12]; //! Similar to IdentifyController.FR + char BootloaderVersion[12]; //! Bootloader version string + char SerialNumber[20]; //! Device serial number + uint64_t OemDataMapOffset; //! Customer Data Map Object Log Offset + uint8_t TelemetryMajorVersion; //! Shadow of version in TOC + uint8_t TelemetryMinorVersion; //! Shadow of version in TOC + uint8_t Reserved[46]; //! Reserved for future usage +}; +static_assert(sizeof(const struct reason_indentifier_1_1) == + MEMBER_SIZE(struct nvme_telemetry_log, rsnident), + "Size mismatch for reason_indentifier_1_1"); + +struct reason_indentifier_1_2 { + uint16_t versionMajor; + uint16_t versionMinor; + uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue. + char DriveStatus[20]; //! Drive Status String (for example: "Healthy", "*BAD_CONTEXT_2020") + uint8_t Reserved1[24]; //! pad over Fields removed from version 1.1 + char SerialNumber[20]; //! Device serial number + uint64_t OemDataMapOffset; //! Customer Data Map Object Log Offset + uint8_t TelemetryMajorVersion; //! Shadow of version in TOC + uint8_t TelemetryMinorVersion; //! Shadow of version in TOC + uint8_t ProductFamilyId; + uint8_t Reserved2[5]; //! Reserved for future usage + uint8_t DualPortReserved[40]; //! Reserved for dual port +}; +static_assert(sizeof(const struct reason_indentifier_1_2) == + MEMBER_SIZE(struct nvme_telemetry_log, rsnident), + "Size mismatch for reason_indentifier_1_2"); +#pragma pack(pop, reason_indentifier) + +static void telemetry_log_reason_id_parse1_0_ext(const struct telemetry_log *tl, + struct json_object *reason_id) +{ + const struct reason_indentifier_1_0 *ri; + struct json_object *reserved; + + ri = (struct reason_indentifier_1_0 *) tl->log->rsnident; + json_object_object_add(reason_id, "firmwareVersion", + json_object_new_string_len(ri->FirmwareVersion, + sizeof(ri->FirmwareVersion))); + json_object_object_add(reason_id, "bootloaderVersion", + json_object_new_string_len(ri->BootloaderVersion, + sizeof(ri->BootloaderVersion))); + json_object_object_add(reason_id, "serialNumber", + json_object_new_string_len(ri->SerialNumber, + sizeof(ri->SerialNumber))); + + reserved = json_create_array(); + json_object_add_value_array(reason_id, "reserved", reserved); + for (int i = 0; i < sizeof(ri->Reserved); i++) { + struct json_object *val = json_object_new_int(ri->Reserved[i]); + + json_object_array_add(reserved, val); + } +} + +static void telemetry_log_reason_id_parse1_1_ext(const struct telemetry_log *tl, + struct json_object *reason_id) +{ + const struct reason_indentifier_1_1 *ri; + struct json_object *reserved; + + ri = (struct reason_indentifier_1_1 *) tl->log->rsnident; + json_object_object_add(reason_id, "firmwareVersion", + json_object_new_string_len(ri->FirmwareVersion, + sizeof(ri->FirmwareVersion))); + json_object_object_add(reason_id, "bootloaderVersion", + json_object_new_string_len(ri->BootloaderVersion, + sizeof(ri->BootloaderVersion))); + json_object_object_add(reason_id, "serialNumber", + json_object_new_string_len(ri->SerialNumber, + sizeof(ri->SerialNumber))); + json_object_add_value_uint64(reason_id, "oemDataMapOffset", + le64_to_cpu(ri->OemDataMapOffset)); + json_object_add_value_uint(reason_id, "telemetryMajorVersion", + le16_to_cpu(ri->TelemetryMajorVersion)); + json_object_add_value_uint(reason_id, "telemetryMinorVersion", + le16_to_cpu(ri->TelemetryMinorVersion)); + + reserved = json_create_array(); + json_object_add_value_array(reason_id, "reserved", reserved); + for (int i = 0; i < sizeof(ri->Reserved); i++) { + struct json_object *val = json_object_new_int(ri->Reserved[i]); + + json_object_array_add(reserved, val); + } +} + +static void telemetry_log_reason_id_parse1_2_ext(const struct telemetry_log *tl, + struct json_object *reason_id) +{ + const struct reason_indentifier_1_2 *ri; + struct json_object *dp_reserved; + struct json_object *reserved; + + ri = (struct reason_indentifier_1_2 *) tl->log->rsnident; + + json_object_object_add(reason_id, "serialNumber", + json_object_new_string_len(ri->SerialNumber, + sizeof(ri->SerialNumber))); + json_object_add_value_uint64(reason_id, "oemDataMapOffset", + le64_to_cpu(ri->OemDataMapOffset)); + json_object_add_value_uint(reason_id, "telemetryMajorVersion", + le16_to_cpu(ri->TelemetryMajorVersion)); + json_object_add_value_uint(reason_id, "telemetryMinorVersion", + le16_to_cpu(ri->TelemetryMinorVersion)); + json_object_add_value_uint(reason_id, "productFamilyId", ri->ProductFamilyId); + + reserved = json_create_array(); + json_object_add_value_array(reason_id, "reserved2", reserved); + for (int i = 0; i < sizeof(ri->Reserved2); i++) { + struct json_object *val = json_object_new_int(ri->Reserved2[i]); + + json_object_array_add(reserved, val); + } + + dp_reserved = json_create_array(); + json_object_add_value_array(reason_id, "dualPortReserved", dp_reserved); + for (int i = 0; i < sizeof(ri->DualPortReserved); i++) { + struct json_object *val = json_object_new_int(ri->DualPortReserved[i]); + + json_object_array_add(dp_reserved, val); + } +} + +static void solidigm_telemetry_log_reason_id_parse(const struct telemetry_log *tl, struct json_object *reason_id) +{ + const struct reason_indentifier_1_0 *ri1_0 = + (struct reason_indentifier_1_0 *) tl->log->rsnident; + uint16_t version_major = le16_to_cpu(ri1_0->versionMajor); + uint16_t version_minor = le16_to_cpu(ri1_0->versionMinor); + + json_object_add_value_uint(reason_id, "versionMajor", version_major); + json_object_add_value_uint(reason_id, "versionMinor", version_minor); + json_object_add_value_uint(reason_id, "reasonCode", le32_to_cpu(ri1_0->reasonCode)); + json_object_add_value_object(reason_id, "driveStatus", + json_object_new_string_len(ri1_0->DriveStatus, + sizeof(ri1_0->DriveStatus))); + if (version_major == 1) { + switch (version_minor) { + case 0: + telemetry_log_reason_id_parse1_0_ext(tl, reason_id); + break; + case 1: + telemetry_log_reason_id_parse1_1_ext(tl, reason_id); + break; + default: + telemetry_log_reason_id_parse1_2_ext(tl, reason_id); + break; + } + } +} + +bool solidigm_telemetry_log_header_parse(const struct telemetry_log *tl) +{ + const struct nvme_telemetry_log *log; + struct json_object *ieee_oui_id; + struct json_object *reason_id; + struct json_object *header; + + if (tl->log_size < sizeof(const struct nvme_telemetry_log)) { + SOLIDIGM_LOG_WARNING("Telemetry log too short."); + return false; + } + + header = json_create_object(); + + json_object_object_add(tl->root, "telemetryHeader", header); + log = tl->log; + + json_object_add_value_uint(header, "logIdentifier", log->lpi); + ieee_oui_id = json_create_array(); + + json_object_object_add(header, "ieeeOuiIdentifier", ieee_oui_id); + for (int i = 0; i < sizeof(log->ieee); i++) { + struct json_object *val = json_object_new_int(log->ieee[i]); + + json_object_array_add(ieee_oui_id, val); + } + json_object_add_value_uint(header, "dataArea1LastBlock", log->dalb1); + json_object_add_value_uint(header, "dataArea2LastBlock", log->dalb2); + json_object_add_value_uint(header, "dataArea3LastBlock", log->dalb3); + json_object_add_value_uint(header, "hostInitiatedDataGeneration", log->hostdgn); + json_object_add_value_uint(header, "controllerInitiatedDataAvailable", log->ctrlavail); + json_object_add_value_uint(header, "controllerInitiatedDataGeneration", log->ctrldgn); + + reason_id = json_create_object(); + json_object_add_value_object(header, "reasonIdentifier", reason_id); + solidigm_telemetry_log_reason_id_parse(tl, reason_id); + + return true; +} diff --git a/plugins/solidigm/solidigm-telemetry/header.h b/plugins/solidigm/solidigm-telemetry/header.h new file mode 100644 index 0000000..027af55 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/header.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "telemetry-log.h" +bool solidigm_telemetry_log_header_parse(const struct telemetry_log *tl); diff --git a/plugins/solidigm/solidigm-telemetry/meson.build b/plugins/solidigm/solidigm-telemetry/meson.build new file mode 100644 index 0000000..96273b7 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/meson.build @@ -0,0 +1,7 @@ +sources += [ + 'plugins/solidigm/solidigm-telemetry/cod.c', + 'plugins/solidigm/solidigm-telemetry/header.c', + 'plugins/solidigm/solidigm-telemetry/config.c', + 'plugins/solidigm/solidigm-telemetry/data-area.c', + 'plugins/solidigm/solidigm-telemetry/nlog.c', +] diff --git a/plugins/solidigm/solidigm-telemetry/nlog.c b/plugins/solidigm/solidigm-telemetry/nlog.c new file mode 100644 index 0000000..926772b --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/nlog.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "nlog.h" +#include "config.h" +#include <string.h> +#include <stdio.h> + +#include "ccan/ilog/ilog.h" + +#define LOG_ENTRY_HEADER_SIZE 1 +#define LOG_ENTRY_TIMESTAMP_SIZE 2 +#define LOG_ENTRY_NUM_ARGS_MAX 8 +#define LOG_ENTRY_MAX_SIZE (LOG_ENTRY_HEADER_SIZE + LOG_ENTRY_TIMESTAMP_SIZE + \ + LOG_ENTRY_NUM_ARGS_MAX) +#define NUM_ARGS_MASK ((1 << ((int)STATIC_ILOG_32(LOG_ENTRY_NUM_ARGS_MAX))) - 1) +#define MAX_HEADER_MISMATCH_TRACK 10 + +static int formats_find(struct json_object *formats, uint32_t val, struct json_object **format) +{ + char hex_header[STR_HEX32_SIZE]; + + snprintf(hex_header, STR_HEX32_SIZE, "0x%08X", val); + return json_object_object_get_ex(formats, hex_header, format); +} + +static uint32_t nlog_get_pos(const uint32_t *nlog, const uint32_t nlog_size, int pos) +{ + return nlog[pos % nlog_size]; +} + +static uint32_t nlog_get_events(const uint32_t *nlog, const uint32_t nlog_size, int start_offset, + struct json_object *formats, struct json_object *events, uint32_t *tail_mismatches) +{ + uint32_t event_count = 0; + int last_bad_header_pos = nlog_size + 1; // invalid nlog offset + uint32_t tail_count = 0; + + for (int i = nlog_size - start_offset - 1; i >= -start_offset; i--) { + struct json_object *format; + uint32_t header = nlog_get_pos(nlog, nlog_size, i); + uint32_t num_data; + + if (header == 0 || !formats_find(formats, header, &format)) { + if (event_count > 0) { + //check if fould circular buffer tail + if (i != (last_bad_header_pos - 1)) { + if (tail_mismatches && + (tail_count < MAX_HEADER_MISMATCH_TRACK)) + tail_mismatches[tail_count] = header; + tail_count++; + } + last_bad_header_pos = i; + } + continue; + } + num_data = header & NUM_ARGS_MASK; + if (events) { + struct json_object *event = json_object_new_array(); + struct json_object *param = json_object_new_array(); + uint32_t val = nlog_get_pos(nlog, nlog_size, i - 1); + + json_object_array_add(events, event); + json_object_array_add(event, json_object_new_int64(val)); + val = nlog_get_pos(nlog, nlog_size, i - 2); + json_object_array_add(event, json_object_new_int64(val)); + json_object_array_add(event, json_object_new_int64(header)); + json_object_array_add(event, param); + for (uint32_t j = 0; j < num_data; j++) { + val = nlog_get_pos(nlog, nlog_size, i - 3 - j); + json_object_array_add(param, json_object_new_int64(val)); + } + json_object_get(format); + json_object_array_add(event, format); + } + i -= 2 + num_data; + event_count++; + } + return tail_count; +} + +int solidigm_nlog_parse(const char *buffer, uint64_t buff_size, struct json_object *formats, + struct json_object *metadata, struct json_object *output) +{ + uint32_t smaller_tail_count = UINT32_MAX; + int best_offset = 0; + uint32_t offset_tail_mismatches[LOG_ENTRY_MAX_SIZE][MAX_HEADER_MISMATCH_TRACK]; + struct json_object *events = json_object_new_array(); + const uint32_t *nlog = (uint32_t *)buffer; + const uint32_t nlog_size = buff_size / sizeof(uint32_t); + + for (int i = 0; i < LOG_ENTRY_MAX_SIZE; i++) { + uint32_t tail_count = nlog_get_events(nlog, nlog_size, i, formats, NULL, + offset_tail_mismatches[i]); + if (tail_count < smaller_tail_count) { + best_offset = i; + smaller_tail_count = tail_count; + } + if (tail_count == 0) + break; + } + if (smaller_tail_count > 1) { + const char *name = ""; + int media_bank = -1; + char str_mismatches[(STR_HEX32_SIZE + 1) * MAX_HEADER_MISMATCH_TRACK]; + int pos = 0; + int show_mismatch_num = smaller_tail_count < MAX_HEADER_MISMATCH_TRACK ? + smaller_tail_count : MAX_HEADER_MISMATCH_TRACK; + struct json_object *jobj; + + if (json_object_object_get_ex(metadata, "objName", &jobj)) + name = json_object_get_string(jobj); + if (json_object_object_get_ex(metadata, "mediaBankId", &jobj)) + media_bank = json_object_get_int(jobj); + + for (int i = 0; i < show_mismatch_num; i++) + pos += snprintf(&str_mismatches[pos], STR_HEX32_SIZE + 1, "0x%08X ", + offset_tail_mismatches[best_offset][i]); + + SOLIDIGM_LOG_WARNING("%s:%d with %d header mismatches ( %s). Configuration file may be missing format headers.", + name, media_bank, smaller_tail_count, str_mismatches); + } + nlog_get_events(nlog, nlog_size, best_offset, formats, events, NULL); + + json_object_object_add(output, "events", events); + return 0; +} diff --git a/plugins/solidigm/solidigm-telemetry/nlog.h b/plugins/solidigm/solidigm-telemetry/nlog.h new file mode 100644 index 0000000..f33aa45 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/nlog.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ +#include "telemetry-log.h" + +int solidigm_nlog_parse(const char *buffer, uint64_t bufer_size, + struct json_object *formats, struct json_object *metadata, + struct json_object *output); diff --git a/plugins/solidigm/solidigm-telemetry/telemetry-log.h b/plugins/solidigm/solidigm-telemetry/telemetry-log.h new file mode 100644 index 0000000..e9eff73 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/telemetry-log.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#ifndef _SOLIDIGM_TELEMETRY_LOG_H +#define _SOLIDIGM_TELEMETRY_LOG_H + +#include "libnvme.h" +#include "util/json.h" +#include <assert.h> + +#if !defined __cplusplus +#define static_assert _Static_assert +#endif + +#define VA_ARGS(...), ##__VA_ARGS__ +#define SOLIDIGM_LOG_WARNING(format, ...) fprintf(stderr, format"\n" VA_ARGS(__VA_ARGS__)) + +#define MEMBER_SIZE(type, member) sizeof(((type *)0)->member) + +struct telemetry_log { + struct nvme_telemetry_log *log; + size_t log_size; + struct json_object *root; + struct json_object *configuration; +}; + +#endif /* _SOLIDIGM_TELEMETRY_LOG_H */ |