// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2022 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ #include #include #include #include #include #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; 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 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 = -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_END() }; int err = argconfig_parse(argc, argv, desc, opts); if (err) goto ret; /* 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; } 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 = 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; } struct json_tokener *jstok = json_tokener_new(); tl.configuration = json_tokener_parse_ex(jstok, conf_str, length); free(conf_str); 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) { size_t max_data_tx; err = nvme_get_telemetry_max(dev_fd(dev), NULL, &max_data_tx); if (err < 0) { SOLIDIGM_LOG_WARNING("identify_ctrl: %s", nvme_strerror(errno)); goto close_fd; } else if (err > 0) { nvme_show_status(err); SOLIDIGM_LOG_WARNING("Failed to acquire identify ctrl %d!", err); goto close_fd; } if (max_data_tx > DRIVER_MAX_TX_256K) max_data_tx = DRIVER_MAX_TX_256K; err = nvme_get_telemetry_log(dev_fd(dev), cfg.host_gen, cfg.ctrl_init, true, max_data_tx, cfg.data_area, &tl.log, &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_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; }