184 lines
4.6 KiB
C
184 lines
4.6 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
/*
|
||
|
* Copyright (c) 2022 Solidigm.
|
||
|
*
|
||
|
* Author: leonardo.da.cunha@solidigm.com
|
||
|
*/
|
||
|
|
||
|
#include <fcntl.h>
|
||
|
#include <errno.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "nvme.h"
|
||
|
#include "libnvme.h"
|
||
|
#include "plugin.h"
|
||
|
#include "nvme-print.h"
|
||
|
#include "solidigm-telemetry.h"
|
||
|
#include "solidigm-telemetry/telemetry-log.h"
|
||
|
#include "solidigm-telemetry/cod.h"
|
||
|
#include "solidigm-telemetry/header.h"
|
||
|
#include "solidigm-telemetry/config.h"
|
||
|
#include "solidigm-telemetry/data-area.h"
|
||
|
|
||
|
static int read_file2buffer(char *file_name, char **buffer, size_t *length)
|
||
|
{
|
||
|
FILE *fd = fopen(file_name, "rb");
|
||
|
|
||
|
if (!fd)
|
||
|
return errno;
|
||
|
|
||
|
fseek(fd, 0, SEEK_END);
|
||
|
size_t length_bytes = ftell(fd);
|
||
|
|
||
|
fseek(fd, 0, SEEK_SET);
|
||
|
|
||
|
*buffer = malloc(length_bytes);
|
||
|
if (!*buffer) {
|
||
|
fclose(fd);
|
||
|
return errno;
|
||
|
}
|
||
|
*length = fread(*buffer, 1, length_bytes, fd);
|
||
|
fclose(fd);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
struct config {
|
||
|
__u32 host_gen;
|
||
|
bool ctrl_init;
|
||
|
int data_area;
|
||
|
char *cfg_file;
|
||
|
bool is_input_file;
|
||
|
};
|
||
|
|
||
|
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;
|
||
|
|
||
|
struct telemetry_log tl = {
|
||
|
.root = json_create_object(),
|
||
|
.log = NULL,
|
||
|
};
|
||
|
|
||
|
struct config cfg = {
|
||
|
.host_gen = 1,
|
||
|
.ctrl_init = false,
|
||
|
.data_area = 3,
|
||
|
.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_END()
|
||
|
};
|
||
|
|
||
|
int err = argconfig_parse(argc, argv, desc, opts);
|
||
|
|
||
|
if (err)
|
||
|
goto ret;
|
||
|
|
||
|
if (cfg.is_input_file) {
|
||
|
if (optind >= argc) {
|
||
|
err = errno = EINVAL;
|
||
|
perror(argv[0]);
|
||
|
goto ret;
|
||
|
}
|
||
|
char *binary_file_name = argv[optind];
|
||
|
|
||
|
err = read_file2buffer(binary_file_name, (char **)&tl.log, &tl.log_size);
|
||
|
} else {
|
||
|
err = parse_and_open(&dev, argc, argv, desc, opts);
|
||
|
}
|
||
|
if (err)
|
||
|
goto ret;
|
||
|
|
||
|
if (cfg.host_gen > 1) {
|
||
|
SOLIDIGM_LOG_WARNING("Invalid host-generate value '%d'", cfg.host_gen);
|
||
|
err = EINVAL;
|
||
|
goto close_fd;
|
||
|
}
|
||
|
|
||
|
if (cfg.cfg_file) {
|
||
|
char *conf_str = 0;
|
||
|
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;
|
||
|
}
|
||
|
json_tokener * jstok = json_tokener_new();
|
||
|
|
||
|
tl.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;
|
||
|
}
|
||
|
json_tokener_free(jstok);
|
||
|
}
|
||
|
|
||
|
if (!cfg.is_input_file) {
|
||
|
if (cfg.ctrl_init)
|
||
|
err = nvme_get_ctrl_telemetry(dev_fd(dev), true,
|
||
|
&tl.log, cfg.data_area,
|
||
|
&tl.log_size);
|
||
|
else if (cfg.host_gen)
|
||
|
err = nvme_get_new_host_telemetry(dev_fd(dev), &tl.log,
|
||
|
cfg.data_area,
|
||
|
&tl.log_size);
|
||
|
else
|
||
|
err = nvme_get_host_telemetry(dev_fd(dev), &tl.log,
|
||
|
cfg.data_area,
|
||
|
&tl.log_size);
|
||
|
|
||
|
if (err < 0) {
|
||
|
SOLIDIGM_LOG_WARNING("get-telemetry-log: %s",
|
||
|
nvme_strerror(errno));
|
||
|
goto close_fd;
|
||
|
} else if (err > 0) {
|
||
|
nvme_show_status(err);
|
||
|
SOLIDIGM_LOG_WARNING("Failed to acquire telemetry log %d!", err);
|
||
|
goto close_fd;
|
||
|
}
|
||
|
}
|
||
|
solidigm_telemetry_log_header_parse(&tl);
|
||
|
if (cfg.cfg_file)
|
||
|
solidigm_telemetry_log_data_areas_parse(&tl, cfg.data_area);
|
||
|
else
|
||
|
solidigm_telemetry_log_cod_parse(&tl);
|
||
|
|
||
|
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;
|
||
|
}
|