Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions Documentation/nvme-solidigm-parse-telemetry-log.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,18 @@ OPTIONS

-d <num>::
--data-area=<num>::
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 <file>::
--config-file=<file>::
Specify a JSON configuration file for custom parsing of the telemetry log.

-s::
--source-file::
Indicates that the <device> argument is a binary file containing a log dump
instead of a block or character device.
-s <file>::
--source-file=<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
--------
Expand Down Expand Up @@ -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
Expand Down
4 changes: 1 addition & 3 deletions completions/_nvme
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,7 @@ _nvme () {
-d':alias for --data-area'
--config-file':JSON configuration file'
-j':alias for --config-file'
--source-file':data source <device> 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'
Expand Down
2 changes: 1 addition & 1 deletion plugins/solidigm/solidigm-nvme.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
114 changes: 62 additions & 52 deletions plugins/solidigm/solidigm-telemetry.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -51,98 +51,117 @@ 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";
const char *hgen = "Controls when to generate new host initiated report. Default value '1' generates new host initiated report, value '0' causes retrieval of existing log.";
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 <device> 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) = {
OPT_UINT("host-generate", 'g', &cfg.host_gen, hgen),
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,
json_tokener_error_desc(jstok->err),
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;
Expand All @@ -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)) {
Expand All @@ -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;
}
73 changes: 70 additions & 3 deletions plugins/solidigm/solidigm-telemetry/data-area.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();

Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions plugins/solidigm/solidigm-telemetry/data-area.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Loading