From c0ffe32bbf39f8957227cace026524835990a499 Mon Sep 17 00:00:00 2001 From: jeff-lien-sndk Date: Mon, 18 Aug 2025 12:59:57 -0500 Subject: [PATCH 1/2] ocp: Add support for retrieving telemetry data area 4 The current ocp internal-log command will only retrieve up to data area 3. This change will enable retrieving of all 4 telemetry log data areas. It will also refactor the code used to save off the 2 files collected: telemetry log and string log along with the 1 generated file: the parsed da 1 and 2 data. Swap -o and -f on output-format and output-file parameters. Signed-off-by: jeff-lien-sndk Reviewed-by: brandon-paupore-sndk --- plugins/ocp/ocp-nvme.c | 110 +++++++++++++++++++++++------ plugins/ocp/ocp-nvme.h | 2 +- plugins/ocp/ocp-telemetry-decode.c | 12 ++-- plugins/ocp/ocp-telemetry-decode.h | 2 +- 4 files changed, 98 insertions(+), 28 deletions(-) diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 35d27f4755..28aed8631e 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -1221,6 +1221,12 @@ static int get_telemetry_dump(struct nvme_dev *dev, char *filename, char *sn, size = le16_to_cpu(logheader->DataArea3LastBlock) - le16_to_cpu(logheader->DataArea2LastBlock); break; + case 4: + offset = TELEMETRY_HEADER_SIZE + + (le16_to_cpu(logheader->DataArea3LastBlock) * TELEMETRY_BYTE_PER_BLOCK); + size = le16_to_cpu(logheader->DataArea4LastBlock) - + le16_to_cpu(logheader->DataArea3LastBlock); + break; default: break; } @@ -1237,13 +1243,15 @@ static int get_telemetry_dump(struct nvme_dev *dev, char *filename, char *sn, return err; } -static int get_telemetry_log_page_data(struct nvme_dev *dev, int tele_type) +static int get_telemetry_log_page_data(struct nvme_dev *dev, + int tele_type, + int tele_area, + const char *output_file) { - char file_path[PATH_MAX]; void *telemetry_log; const size_t bs = 512; struct nvme_telemetry_log *hdr; - size_t full_size, offset = bs; + size_t full_size = 0, offset = bs; int err, fd; if ((tele_type == TELEMETRY_TYPE_HOST_0) || (tele_type == TELEMETRY_TYPE_HOST_1)) @@ -1262,11 +1270,10 @@ static int get_telemetry_log_page_data(struct nvme_dev *dev, int tele_type) } memset(hdr, 0, bs); - sprintf(file_path, DEFAULT_TELEMETRY_BIN); - fd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { fprintf(stderr, "Failed to open output file %s: %s!\n", - file_path, strerror(errno)); + output_file, strerror(errno)); err = fd; goto exit_status; } @@ -1304,9 +1311,25 @@ static int get_telemetry_log_page_data(struct nvme_dev *dev, int tele_type) goto close_fd; } - full_size = (le16_to_cpu(hdr->dalb3) * bs) + offset; + switch (tele_area) { + case 1: + full_size = (le16_to_cpu(hdr->dalb1) * bs) + offset; + break; + case 2: + full_size = (le16_to_cpu(hdr->dalb2) * bs) + offset; + break; + case 3: + full_size = (le16_to_cpu(hdr->dalb3) * bs) + offset; + break; + case 4: + full_size = (le32_to_cpu(hdr->dalb4) * bs) + offset; + break; + default: + full_size = offset; + break; + } - while (offset != full_size) { + while (offset < full_size) { args.log = telemetry_log; args.lpo = offset; args.lsp = NVME_LOG_LSP_NONE; @@ -1431,7 +1454,7 @@ int parse_ocp_telemetry_log(struct ocp_telemetry_parse_options *options) if (options->telemetry_log) { if (strstr((const char *)options->telemetry_log, "bin")) { - // Read the data from the telemetry binary file + /* Read the data from the telemetry binary file */ ptelemetry_buffer = read_binary_file(NULL, (const char *)options->telemetry_log, &telemetry_buffer_size, 1); @@ -1452,7 +1475,7 @@ int parse_ocp_telemetry_log(struct ocp_telemetry_parse_options *options) } if (options->string_log) { - // Read the data from the string binary file + /* Read the data from the string binary file */ if (strstr((const char *)options->string_log, "bin")) { pstring_buffer = read_binary_file(NULL, (const char *)options->string_log, &string_buffer_size, 1); @@ -1483,11 +1506,15 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, struct const char *telemetry_log = "Telemetry log binary;\n 'host.bin' or 'controller.bin'"; const char *string_log = "String log binary; 'C9.bin'"; const char *output_file = "Output file name with path;\n" - "e.g. '-o ./path/name'\n'-o ./path1/path2/';\n" + "e.g. '-f ./path/name'\n'-f ./path1/path2/';\n" "If requested path does not exist, the directory will be newly created."; const char *output_format = "output format normal|json"; - const char *data_area = "Telemetry Data Area; 1 or 2;\n" - "e.g. '-a 1 for Data Area 1.'\n'-a 2 for Data Areas 1 and 2.';\n"; + const char *data_area = "Telemetry Data Area; 1, 2, 3, or 4;\n" + "e.g. '-a 1 for Data Area 1.'\n" + "e.g. '-a 2 for Data Areas 1 and 2.'\n" + "e.g. '-a 3 for Data Areas 1, 2, and 3.'\n" + "e.g. '-a 4 for Data Areas 1, 2, 3, and 4.';\n"; + const char *telemetry_type = "Telemetry Type; 'host', 'host0', 'host1' or 'controller'"; struct nvme_dev *dev; @@ -1500,12 +1527,16 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, struct struct ocp_telemetry_parse_options opt; int tele_type = 0; int tele_area = 0; + char file_path_telemetry[PATH_MAX], file_path_string[PATH_MAX]; + const char *string_suffix = "string.bin"; + const char *tele_log_suffix = "telemetry.bin"; + bool host_behavior_changed = false; OPT_ARGS(opts) = { OPT_STR("telemetry-log", 'l', &opt.telemetry_log, telemetry_log), OPT_STR("string-log", 's', &opt.string_log, string_log), - OPT_FILE("output-file", 'o', &opt.output_file, output_file), - OPT_FMT("output-format", 'f', &opt.output_format, output_format), + OPT_FILE("output-file", 'f', &opt.output_file, output_file), + OPT_FMT("output-format", 'o', &opt.output_format, output_format), OPT_INT("data-area", 'a', &opt.data_area, data_area), OPT_STR("telemetry-type", 't', &opt.telemetry_type, telemetry_type), OPT_END() @@ -1536,11 +1567,15 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, struct is_support_telemetry_controller = ((ctrl.lpa & 0x8) >> 3); + if (opt.output_file == NULL) + opt.output_file = DEFAULT_TELEMETRY_LOG; + if (!opt.data_area) { nvme_show_result("Missing data-area. Using default data area 1.\n"); opt.data_area = DATA_AREA_1;//Default data area 1 - } else if (opt.data_area != 1 && opt.data_area != 2) { - nvme_show_result("Invalid data-area specified. Please specify 1 or 2.\n"); + } else if (opt.data_area != 1 && opt.data_area != 2 && + opt.data_area != 3 && opt.data_area != 4) { + nvme_show_result("Invalid data-area specified. Please specify 1, 2, 3, or 4.\n"); goto out; } @@ -1568,25 +1603,55 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, struct if (!opt.telemetry_log) { nvme_show_result("\nMissing telemetry-log. Fetching from drive...\n"); - err = get_telemetry_log_page_data(dev, tele_type);//Pull Telemetry log + + if (tele_area == 4) { + if (!(ctrl.lpa & 0x40)) { + nvme_show_error("Telemetry data area 4 not supported by device.\n"); + goto out; + } + + err = nvme_set_etdas(dev_fd(dev), &host_behavior_changed); + if (err) { + fprintf(stderr, "%s: Failed to set ETDAS bit\n", __func__); + return err; + } + } + + /* Pull the Telemetry log */ + sprintf(file_path_telemetry, "%s-%s", opt.output_file, tele_log_suffix); + err = get_telemetry_log_page_data(dev, + tele_type, + tele_area, + (const char *)file_path_telemetry); if (err) { nvme_show_error("Failed to fetch telemetry-log from the drive.\n"); goto out; } nvme_show_result("telemetry.bin generated. Proceeding with next steps.\n"); - opt.telemetry_log = DEFAULT_TELEMETRY_BIN; + opt.telemetry_log = file_path_telemetry; + + if (host_behavior_changed) { + host_behavior_changed = false; + err = nvme_clear_etdas(dev_fd(dev), &host_behavior_changed); + if (err) { + /* Continue on if this fails, it's not a fatal condition */ + nvme_show_error("Failed to clear ETDAS bit.\n"); + } + } } if (!opt.string_log) { nvme_show_result("Missing string-log. Fetching from drive...\n"); + /* Pull String log */ - err = get_c9_log_page_data(dev, 0, 1, (const char *)opt.output_file); + sprintf(file_path_string, "%s-%s", opt.output_file, string_suffix); + err = get_c9_log_page_data(dev, 0, 1, (const char *)file_path_string); if (err) { nvme_show_error("Failed to fetch string-log from the drive.\n"); goto out; } nvme_show_result("string.bin generated. Proceeding with next steps.\n"); - opt.string_log = DEFAULT_STRING_BIN; + opt.string_log = file_path_string; } if (!opt.output_format) { @@ -1597,6 +1662,7 @@ static int ocp_telemetry_log(int argc, char **argv, struct command *cmd, struct switch (tele_type) { case TELEMETRY_TYPE_HOST: printf("Extracting Telemetry Host Dump (Data Area %d)...\n", tele_area); + err = parse_ocp_telemetry_log(&opt); if (err) nvme_show_result("Status:(%x)\n", err); @@ -2634,7 +2700,7 @@ static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *c return ret; if (cfg.output_file != NULL) - sprintf(file_path, "%s%s", cfg.output_file, string_suffix); + sprintf(file_path, "%s-%s", cfg.output_file, string_suffix); else sprintf(file_path, "%s", DEFAULT_STRING_BIN); diff --git a/plugins/ocp/ocp-nvme.h b/plugins/ocp/ocp-nvme.h index 986b081b74..5408c21f8f 100644 --- a/plugins/ocp/ocp-nvme.h +++ b/plugins/ocp/ocp-nvme.h @@ -11,7 +11,7 @@ #if !defined(OCP_NVME) || defined(CMD_HEADER_MULTI_READ) #define OCP_NVME -#define OCP_PLUGIN_VERSION "2.15.2" +#define OCP_PLUGIN_VERSION "2.15.3" #include "cmd.h" PLUGIN(NAME("ocp", "OCP cloud SSD extensions", OCP_PLUGIN_VERSION), diff --git a/plugins/ocp/ocp-telemetry-decode.c b/plugins/ocp/ocp-telemetry-decode.c index 4bf70ad364..ac39afe56e 100644 --- a/plugins/ocp/ocp-telemetry-decode.c +++ b/plugins/ocp/ocp-telemetry-decode.c @@ -1522,9 +1522,11 @@ int parse_statistics(struct json_object *root, struct nvme_ocp_telemetry_offsets int print_ocp_telemetry_normal(struct ocp_telemetry_parse_options *options) { int status = 0; + char file_path[PATH_MAX]; if (options->output_file != NULL) { - FILE *fp = fopen(options->output_file, "w"); + sprintf(file_path, "%s.%s", options->output_file, "txt"); + FILE *fp = fopen(file_path, "w"); if (fp) { fprintf(fp, STR_LINE); @@ -1639,7 +1641,7 @@ int print_ocp_telemetry_normal(struct ocp_telemetry_parse_options *options) fprintf(fp, STR_LINE); fclose(fp); } else { - nvme_show_error("Failed to open %s file.\n", options->output_file); + nvme_show_error("Failed to open %s file.\n", file_path); return -1; } } else { @@ -1753,6 +1755,7 @@ int print_ocp_telemetry_normal(struct ocp_telemetry_parse_options *options) int print_ocp_telemetry_json(struct ocp_telemetry_parse_options *options) { int status = 0; + char file_path[PATH_MAX]; //create json objects struct json_object *root, *pheader, *preason_identifier, *da1_header, *smart_obj, @@ -1848,13 +1851,14 @@ int print_ocp_telemetry_json(struct ocp_telemetry_parse_options *options) if (options->output_file != NULL) { const char *json_string = json_object_to_json_string(root); - FILE *fp = fopen(options->output_file, "w"); + sprintf(file_path, "%s.%s", options->output_file, "json"); + FILE *fp = fopen(file_path, "w"); if (fp) { fputs(json_string, fp); fclose(fp); } else { - nvme_show_error("Failed to open %s file.\n", options->output_file); + nvme_show_error("Failed to open %s file.\n", file_path); return -1; } } else { diff --git a/plugins/ocp/ocp-telemetry-decode.h b/plugins/ocp/ocp-telemetry-decode.h index e48095cda7..60eba714bd 100644 --- a/plugins/ocp/ocp-telemetry-decode.h +++ b/plugins/ocp/ocp-telemetry-decode.h @@ -604,7 +604,7 @@ struct telemetry_data_area_1 { #define DEFAULT_ASCII_STRING_SIZE 16 #define SIZE_OF_VU_EVENT_ID 2 -#define DEFAULT_TELEMETRY_BIN "telemetry.bin" +#define DEFAULT_TELEMETRY_LOG "telemetry-log" #define DEFAULT_STRING_BIN "string.bin" #define DEFAULT_OUTPUT_FORMAT_JSON "json" From 573b2371f67c2ad68788a823413ef30230369e01 Mon Sep 17 00:00:00 2001 From: jeff-lien-sndk Date: Mon, 25 Aug 2025 16:26:05 -0500 Subject: [PATCH 2/2] ocp: Update man page documentation for internal-log command The man page documentation has been updated to document the change of -o for output-format and -f for output-file. Also there have been updates made to note new supported data areas. Signed-off-by: jeff-lien-sndk Reviewed-by: brandon-paupore-sndk --- Documentation/nvme-ocp-internal-log.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Documentation/nvme-ocp-internal-log.txt b/Documentation/nvme-ocp-internal-log.txt index 26d78ac351..ed9330fe37 100644 --- a/Documentation/nvme-ocp-internal-log.txt +++ b/Documentation/nvme-ocp-internal-log.txt @@ -14,8 +14,8 @@ SYNOPSIS 'nvme ocp internal-log' [--telemetry-log= | -l ] [--string-log= | -s ] - [--output-file= | -o ] - [--output-format= | -f ] + [--output-file= | -f ] + [--output-format= | -o ] [--data-area= | -a ] [--telemetry-type= | -t ] @@ -48,19 +48,20 @@ OPTIONS is specified, a live retrieval of payload on will be performed. --o :: +-f :: --output-file=:: - Filepath name to where human-readable output data will be saved to. + Filepath name used as the prefix for the telemetry and string log bin files + along with the human-readable parsed output file. --f :: +-o :: --output-format=:: Set the reporting format to 'normal', 'json'. Only one output format can be used at a time, the default value is 'json'. -a :: --data-area=:: - Retrieves the specific data area requested. Valid inputs are 1,2. If this - option is not specified, the default value is 1. + Retrieves the specific data area requested. Valid inputs are 1, 2, 3, or 4. + If this option is not specified, the default value is 1. -t :: --telemetry-type=::