1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
// 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"
#include "solidigm-util.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;
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 = "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 = root,
};
struct config cfg = {
.host_gen = 1,
.ctrl_init = 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_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) {
nvme_show_status(err);
return err;
}
/* When not selected on the command line, get minimum data area required */
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;
}
err = read_file2buffer(cfg.binary_file, (char **)&tlog, &tl.log_size);
} else {
err = parse_and_open(&dev, argc, argv, desc, opts);
}
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;
nvme_show_status(err);
return err;
}
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) {
nvme_show_status(err);
return err;
}
struct json_tokener *jstok = json_tokener_new();
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;
return err;
}
json_tokener_free(jstok);
tl.configuration = configuration;
}
if (!has_binary_file) {
size_t max_data_tx;
size_t power2;
__u8 mdts = 0;
err = nvme_get_telemetry_max(dev_fd(dev), NULL, &max_data_tx);
if (err < 0) {
SOLIDIGM_LOG_WARNING("identify_ctrl: %s",
nvme_strerror(errno));
return err;
} else if (err > 0) {
nvme_show_status(err);
SOLIDIGM_LOG_WARNING("Failed to acquire identify ctrl %d!", err);
return err;
}
power2 = max_data_tx / NVME_LOG_PAGE_PDU_SIZE;
while (power2 && !(1 & power2)) {
power2 >>= 1;
mdts++;
}
err = sldgm_dynamic_telemetry(dev_fd(dev), cfg.host_gen, cfg.ctrl_init, true,
mdts, cfg.data_area, &tlog, &tl.log_size);
if (err < 0) {
SOLIDIGM_LOG_WARNING("get-telemetry-log: %s",
nvme_strerror(errno));
return err;
} else if (err > 0) {
nvme_show_status(err);
SOLIDIGM_LOG_WARNING("Failed to acquire telemetry log %d!", err);
return err;
}
}
tl.log = tlog;
solidigm_telemetry_log_data_areas_parse(&tl, cfg.data_area);
json_print_object(tl.root, NULL);
printf("\n");
return err;
}
|