diff --git a/Documentation/nvme-solidigm-parse-telemetry-log.txt b/Documentation/nvme-solidigm-parse-telemetry-log.txt index 38a678bd80..d152e4482a 100644 --- a/Documentation/nvme-solidigm-parse-telemetry-log.txt +++ b/Documentation/nvme-solidigm-parse-telemetry-log.txt @@ -33,17 +33,18 @@ OPTIONS -d :: --data-area=:: - Pick which telemetry data area to report. Default is 3 to fetch areas 1-3. + Pick which telemetry data area to report. Default is 1, or 3 if a config file is specified. Valid options are 1, 2, 3, 4. -j :: --config-file=:: Specify a JSON configuration file for custom parsing of the telemetry log. --s:: ---source-file:: - Indicates that the argument is a binary file containing a log dump - instead of a block or character device. +-s :: +--source-file=:: + Specifies a binary file containing a telemetry log dump to parse instead of + retrieving the log from a device. When this option is used, no device argument + should be provided. EXAMPLES -------- @@ -74,7 +75,7 @@ EXAMPLES * Parse a telemetry log from a binary file: + ------------ -# nvme solidigm parse-telemetry-log telemetry_dump.bin -s +# nvme solidigm parse-telemetry-log -s telemetry_dump.bin ------------ OUTPUT diff --git a/completions/_nvme b/completions/_nvme index 157555b461..f0aa427ecd 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -509,9 +509,7 @@ _nvme () { -d':alias for --data-area' --config-file':JSON configuration file' -j':alias for --config-file' - --source-file':data source is binary - file containing log dump instead - of block or character device' + --source-file':binary file containing log dump to parse' -s':alias for --source-file' ) _arguments '*:: :->subcmds' diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h index e2f416e618..edb1e76263 100644 --- a/plugins/solidigm/solidigm-nvme.h +++ b/plugins/solidigm/solidigm-nvme.h @@ -13,7 +13,7 @@ #include "cmd.h" -#define SOLIDIGM_PLUGIN_VERSION "1.9" +#define SOLIDIGM_PLUGIN_VERSION "1.10" PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_VERSION), COMMAND_LIST( diff --git a/plugins/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c index 12cb6c62d5..24926e46d4 100644 --- a/plugins/solidigm/solidigm-telemetry.c +++ b/plugins/solidigm/solidigm-telemetry.c @@ -29,7 +29,7 @@ static int read_file2buffer(char *file_name, char **buffer, size_t *length) FILE *fd = fopen(file_name, "rb"); if (!fd) - return errno; + return -errno; fseek(fd, 0, SEEK_END); size_t length_bytes = ftell(fd); @@ -39,7 +39,7 @@ static int read_file2buffer(char *file_name, char **buffer, size_t *length) *buffer = malloc(length_bytes); if (!*buffer) { fclose(fd); - return errno; + return -errno; } *length = fread(*buffer, 1, length_bytes, fd); fclose(fd); @@ -51,9 +51,15 @@ struct config { bool ctrl_init; int data_area; char *cfg_file; - bool is_input_file; + char *binary_file; }; +static void cleanup_json_object(struct json_object **jobj_ptr) +{ + json_free_object(*jobj_ptr); + *jobj_ptr = NULL; +} + int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Parse Solidigm Telemetry log"; @@ -61,20 +67,25 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc const char *cgen = "Gather report generated by the controller."; const char *dgen = "Pick which telemetry data area to report. Default is 3 to fetch areas 1-3. Valid options are 1, 2, 3, 4."; const char *cfile = "JSON configuration file"; - const char *sfile = "data source is binary file containing log dump instead of block or character device"; - struct nvme_dev *dev; + const char *sfile = "binary file containing log dump"; + bool has_binary_file = false; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + + _cleanup_free_ struct nvme_telemetry_log *tlog = NULL; + + __attribute__((cleanup(cleanup_json_object))) struct json_object *configuration = NULL; + + __attribute__((cleanup(cleanup_json_object))) struct json_object *root = + json_create_object(); struct telemetry_log tl = { - .root = json_create_object(), - .log = NULL, + .root = root, }; struct config cfg = { .host_gen = 1, .ctrl_init = false, - .data_area = -1, - .cfg_file = NULL, - .is_input_file = false, }; OPT_ARGS(opts) = { @@ -82,54 +93,61 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc OPT_FLAG("controller-init", 'c', &cfg.ctrl_init, cgen), OPT_UINT("data-area", 'd', &cfg.data_area, dgen), OPT_FILE("config-file", 'j', &cfg.cfg_file, cfile), - OPT_FLAG("source-file", 's', &cfg.is_input_file, sfile), + OPT_FILE("source-file", 's', &cfg.binary_file, sfile), + OPT_INCR("verbose", 'v', &nvme_cfg.verbose, verbose), OPT_END() }; int err = argconfig_parse(argc, argv, desc, opts); - if (err) - goto ret; + if (err) { + nvme_show_status(err); + return err; + } /* When not selected on the command line, get minimum data area required */ - if (cfg.data_area == -1) - cfg.data_area = cfg.cfg_file ? 3 : 1; - - if (cfg.is_input_file) { - if (optind >= argc) { - err = errno = EINVAL; - perror(argv[0]); - goto ret; + if (!argconfig_parse_seen(opts, "data-area")) + cfg.data_area = argconfig_parse_seen(opts, "config-file") ? 3 : 1; + + has_binary_file = argconfig_parse_seen(opts, "source-file"); + if (has_binary_file) { + // If a binary file is provided, we don't want to open a device. + // GNU getopt() permutes the contents of argv as it scans, + // so that eventually all the nonoptions are at the end. + if (argc > optind) { + errno = EINVAL; + err = -errno; + nvme_show_status(err); + return err; } - char *binary_file_name = argv[optind]; - - err = read_file2buffer(binary_file_name, (char **)&tl.log, &tl.log_size); + err = read_file2buffer(cfg.binary_file, (char **)&tlog, &tl.log_size); } else { err = parse_and_open(&dev, argc, argv, desc, opts); } - if (err) - goto ret; + if (err) { + nvme_show_status(err); + return err; + } if (cfg.host_gen > 1) { SOLIDIGM_LOG_WARNING("Invalid host-generate value '%d'", cfg.host_gen); - err = EINVAL; - goto close_fd; + err = -EINVAL; + nvme_show_status(err); + return err; } - if (cfg.cfg_file) { - char *conf_str = NULL; + if (argconfig_parse_seen(opts, "config-file")) { + _cleanup_free_ char *conf_str = NULL; size_t length = 0; err = read_file2buffer(cfg.cfg_file, &conf_str, &length); if (err) { - SOLIDIGM_LOG_WARNING("Failed to open JSON configuration file %s: %s!", - cfg.cfg_file, strerror(err)); - goto close_fd; + nvme_show_status(err); + return err; } struct json_tokener *jstok = json_tokener_new(); - tl.configuration = json_tokener_parse_ex(jstok, conf_str, length); - free(conf_str); + configuration = json_tokener_parse_ex(jstok, conf_str, length); if (jstok->err != json_tokener_success) { SOLIDIGM_LOG_WARNING("Parsing error on JSON configuration file %s: %s (at offset %d)", cfg.cfg_file, @@ -137,12 +155,13 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc jstok->char_offset); json_tokener_free(jstok); err = EINVAL; - goto close_fd; + return err; } json_tokener_free(jstok); + tl.configuration = configuration; } - if (!cfg.is_input_file) { + if (!has_binary_file) { size_t max_data_tx; size_t power2; __u8 mdts = 0; @@ -151,11 +170,11 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc if (err < 0) { SOLIDIGM_LOG_WARNING("identify_ctrl: %s", nvme_strerror(errno)); - goto close_fd; + return err; } else if (err > 0) { nvme_show_status(err); SOLIDIGM_LOG_WARNING("Failed to acquire identify ctrl %d!", err); - goto close_fd; + return err; } power2 = max_data_tx / NVME_LOG_PAGE_PDU_SIZE; while (power2 && !(1 & power2)) { @@ -164,31 +183,22 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc } err = sldgm_dynamic_telemetry(dev_fd(dev), cfg.host_gen, cfg.ctrl_init, true, - mdts, cfg.data_area, &tl.log, &tl.log_size); + mdts, cfg.data_area, &tlog, &tl.log_size); if (err < 0) { SOLIDIGM_LOG_WARNING("get-telemetry-log: %s", nvme_strerror(errno)); - goto close_fd; + return err; } else if (err > 0) { nvme_show_status(err); SOLIDIGM_LOG_WARNING("Failed to acquire telemetry log %d!", err); - goto close_fd; + return err; } } + tl.log = tlog; solidigm_telemetry_log_data_areas_parse(&tl, cfg.data_area); json_print_object(tl.root, NULL); - json_free_object(tl.root); printf("\n"); -close_fd: - if (!cfg.is_input_file) { - /* Redundant close() to make static code analysis happy */ - close(dev->direct.fd); - dev_close(dev); - } -ret: - json_free_object(tl.configuration); - free(tl.log); return err; } diff --git a/plugins/solidigm/solidigm-telemetry/data-area.c b/plugins/solidigm/solidigm-telemetry/data-area.c index 14d612bcf2..b30aac421a 100644 --- a/plugins/solidigm/solidigm-telemetry/data-area.c +++ b/plugins/solidigm/solidigm-telemetry/data-area.c @@ -18,6 +18,23 @@ #define MAX_WARNING_SIZE 1024 #define MAX_ARRAY_RANK 16 +#define NLOG_HEADER_ID 101 + + +static void reverse_string(char *buff, size_t len) +{ + char *start = buff; + char *end = buff + len - 1; + char temp; + + while (end > start) { + temp = *end; + *end = *start; + *start = temp; + start++; + end--; + } +} static bool telemetry_log_get_value(const struct telemetry_log *tl, uint64_t offset_bit, uint32_t size_bit, @@ -418,6 +435,13 @@ static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, header->Token); if (!nlog_name) continue; + + // NLOGs have different parser from other Telemetry objects + has_struct = solidigm_config_get_struct_by_token_version(tl->configuration, + NLOG_HEADER_ID, + header->versionMajor, + header->versionMinor, + &structure_definition); } struct json_object *tele_obj_item = json_create_object(); @@ -443,29 +467,72 @@ static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, telemetry_log_structure_parse(tl, structure_definition, BITS_IN_BYTE * object_file_offset, parsed_struct, toc_item); - } else if (nlog_formats) { + } + // NLOGs have different parser from other Telemetry objects + if (nlog_name) { + if (has_struct) { + struct json_object *header_sizeBits = NULL; + struct json_object *header_nlogSelect = NULL; + struct json_object *header_nlogName = NULL; + + if (json_object_object_get_ex(structure_definition, "sizeBit", + &header_sizeBits)) + header_offset = json_object_get_int(header_sizeBits) / + BITS_IN_BYTE; + // Overwrite nlogName with correct type + if (json_object_object_get_ex(parsed_struct, "nlogSelect", + &header_nlogSelect) && + json_object_object_get_ex(header_nlogSelect, "nlogName", + &header_nlogName)) { + int nlogName = json_object_get_int(header_nlogName); + char *name = (char *)&nlogName; + + reverse_string(name, sizeof(uint32_t)); + json_object_object_add(header_nlogSelect, "nlogName", + json_object_new_string_len(name, + sizeof(uint32_t))); + } + } + // Overwrite the object name json_object_object_add(toc_item, "objName", json_object_new_string(nlog_name)); - telemetry_log_nlog_parse(tl, nlog_formats, object_file_offset, + + telemetry_log_nlog_parse(tl, nlog_formats, + object_file_offset + header_offset, toc->items[i].ContentSizeBytes - header_offset, parsed_struct, toc_item); } } } +void solidigm_telemetry_log_da1_check_ocp(struct telemetry_log *tl) +{ + const uint64_t ocp_telemetry_uuid[] = {0xBC73719D87E64EFA, 0xBA560A9C3043424C}; + const uint64_t *log_uuid = (uint64_t *) &tl->log->data_area[16]; + + tl->is_ocp = tl->log_size >= (&tl->log->data_area[32] - (uint8_t *) tl->log) && + log_uuid[0] == ocp_telemetry_uuid[0] && log_uuid[1] == ocp_telemetry_uuid[1]; +} + 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_da1_check_ocp(tl); solidigm_telemetry_log_header_parse(tl); solidigm_telemetry_log_cod_parse(tl); if (tl->configuration) { + enum nvme_telemetry_da first_da = NVME_TELEMETRY_DA_1; + + if (tl->is_ocp) + first_da = NVME_TELEMETRY_DA_3; + 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++) + for (enum nvme_telemetry_da da = first_da; 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 index 6b690d880e..5ae7efcdf7 100644 --- a/plugins/solidigm/solidigm-telemetry/data-area.h +++ b/plugins/solidigm/solidigm-telemetry/data-area.h @@ -8,3 +8,4 @@ int solidigm_telemetry_log_data_areas_parse(struct telemetry_log *tl, enum nvme_telemetry_da last_da); +void solidigm_telemetry_log_da1_check_ocp(struct telemetry_log *tl); diff --git a/plugins/solidigm/solidigm-telemetry/header.c b/plugins/solidigm/solidigm-telemetry/header.c index 866ebfff58..0a8361df20 100644 --- a/plugins/solidigm/solidigm-telemetry/header.c +++ b/plugins/solidigm/solidigm-telemetry/header.c @@ -57,6 +57,28 @@ struct reason_indentifier_1_2 { static_assert(sizeof(const struct reason_indentifier_1_2) == MEMBER_SIZE(struct nvme_telemetry_log, rsnident), "Size mismatch for reason_indentifier_1_2"); + +struct reason_identifier_ocp_2_5 { + char errorId[64]; + char fileId[8]; + uint16_t lineNum; + union { + struct { + uint8_t validLineNum:1; + uint8_t validFileId:1; + uint8_t validErrorId:1; + uint8_t validVuExtension:1; + uint8_t reservedBits:4; + }; + uint8_t raw; + } validFlags; + uint8_t reserved[21]; + uint8_t vuExtension[32]; +}; +static_assert(sizeof(const struct reason_identifier_ocp_2_5) == + MEMBER_SIZE(struct nvme_telemetry_log, rsnident), + "Size mismatch for reason_identifier_ocp_2_5"); + #pragma pack(pop, reason_indentifier) static void telemetry_log_reason_id_parse1_0_ext(const struct telemetry_log *tl, @@ -153,6 +175,43 @@ static void telemetry_log_reason_id_parse1_2_ext(const struct telemetry_log *tl, json_object_array_add(dp_reserved, val); } } +static void telemetry_log_reason_id_parse_ocp_2_5(const struct telemetry_log *tl, + struct json_object *reason_id) +{ + const struct reason_identifier_ocp_2_5 *ri; + struct json_object *reserved; + struct json_object *vu_extension; + + ri = (struct reason_identifier_ocp_2_5 *) tl->log->rsnident; + + json_object_object_add(reason_id, "errorId", + json_object_new_string_len(ri->errorId, + sizeof(ri->errorId))); + json_object_object_add(reason_id, "fileId", + json_object_new_string_len(ri->fileId, + sizeof(ri->fileId))); + json_object_add_value_uint(reason_id, "lineNum", le16_to_cpu(ri->lineNum)); + json_object_add_value_uint(reason_id, "validLineNum", ri->validFlags.validLineNum); + json_object_add_value_uint(reason_id, "validFileId", ri->validFlags.validFileId); + json_object_add_value_uint(reason_id, "validErrorId", ri->validFlags.validErrorId); + json_object_add_value_uint(reason_id, "validVuExtension", ri->validFlags.validVuExtension); + + 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); + } + + vu_extension = json_create_array(); + json_object_add_value_array(reason_id, "vuExtension", vu_extension); + for (int i = 0; i < sizeof(ri->vuExtension); i++) { + struct json_object *val = json_object_new_int(ri->vuExtension[i]); + + json_object_array_add(vu_extension, val); + } +} static void solidigm_telemetry_log_reason_id_parse(const struct telemetry_log *tl, struct json_object *reason_id) { @@ -161,12 +220,18 @@ static void solidigm_telemetry_log_reason_id_parse(const struct telemetry_log *t uint16_t version_major = le16_to_cpu(ri1_0->versionMajor); uint16_t version_minor = le16_to_cpu(ri1_0->versionMinor); + if (tl->is_ocp) { + telemetry_log_reason_id_parse_ocp_2_5(tl, reason_id); + return; + } + 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: @@ -211,6 +276,7 @@ bool solidigm_telemetry_log_header_parse(const struct telemetry_log *tl) 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, "dataArea4LastBlock", log->dalb4); 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); diff --git a/plugins/solidigm/solidigm-telemetry/nlog.c b/plugins/solidigm/solidigm-telemetry/nlog.c index 926772b26c..366abaefd3 100644 --- a/plugins/solidigm/solidigm-telemetry/nlog.c +++ b/plugins/solidigm/solidigm-telemetry/nlog.c @@ -10,14 +10,12 @@ #include #include -#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_NUM_ARGS_MASK 0xF #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) @@ -41,13 +39,13 @@ static uint32_t nlog_get_events(const uint32_t *nlog, const uint32_t nlog_size, uint32_t tail_count = 0; for (int i = nlog_size - start_offset - 1; i >= -start_offset; i--) { - struct json_object *format; + struct json_object *format = NULL; 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 + //check if found circular buffer tail if (i != (last_bad_header_pos - 1)) { if (tail_mismatches && (tail_count < MAX_HEADER_MISMATCH_TRACK)) @@ -58,7 +56,7 @@ static uint32_t nlog_get_events(const uint32_t *nlog, const uint32_t nlog_size, } continue; } - num_data = header & NUM_ARGS_MASK; + num_data = header & LOG_ENTRY_NUM_ARGS_MASK; if (events) { struct json_object *event = json_object_new_array(); struct json_object *param = json_object_new_array(); diff --git a/plugins/solidigm/solidigm-telemetry/telemetry-log.h b/plugins/solidigm/solidigm-telemetry/telemetry-log.h index e9eff7340d..5edd487f77 100644 --- a/plugins/solidigm/solidigm-telemetry/telemetry-log.h +++ b/plugins/solidigm/solidigm-telemetry/telemetry-log.h @@ -11,6 +11,7 @@ #include "libnvme.h" #include "util/json.h" #include +#include #if !defined __cplusplus #define static_assert _Static_assert @@ -26,6 +27,7 @@ struct telemetry_log { size_t log_size; struct json_object *root; struct json_object *configuration; + bool is_ocp; }; #endif /* _SOLIDIGM_TELEMETRY_LOG_H */