diff options
Diffstat (limited to '')
24 files changed, 1627 insertions, 278 deletions
diff --git a/plugins/solidigm/meson.build b/plugins/solidigm/meson.build index 526fb02..84495a1 100644 --- a/plugins/solidigm/meson.build +++ b/plugins/solidigm/meson.build @@ -1,8 +1,13 @@ sources += [ + 'plugins/solidigm/solidigm-id-ctrl.c', 'plugins/solidigm/solidigm-util.c', 'plugins/solidigm/solidigm-smart.c', 'plugins/solidigm/solidigm-garbage-collection.c', 'plugins/solidigm/solidigm-latency-tracking.c', + 'plugins/solidigm/solidigm-log-page-dir.c', 'plugins/solidigm/solidigm-telemetry.c', + 'plugins/solidigm/solidigm-internal-logs.c', + 'plugins/solidigm/solidigm-market-log.c', ] subdir('solidigm-telemetry') + diff --git a/plugins/solidigm/solidigm-garbage-collection.c b/plugins/solidigm/solidigm-garbage-collection.c index 3828b9e..b26d754 100644 --- a/plugins/solidigm/solidigm-garbage-collection.c +++ b/plugins/solidigm/solidigm-garbage-collection.c @@ -21,26 +21,27 @@ #include "solidigm-garbage-collection.h" #include "solidigm-util.h" -typedef struct __attribute__((packed)) gc_item { +struct __packed gc_item { __le32 timer_type; __le64 timestamp; -} gc_item_t; +}; #define VU_GC_MAX_ITEMS 100 -typedef struct garbage_control_collection_log { +struct garbage_control_collection_log { __le16 version_major; __le16 version_minor; - gc_item_t item[VU_GC_MAX_ITEMS]; + struct __packed gc_item item[VU_GC_MAX_ITEMS]; __u8 reserved[2892]; -} garbage_control_collection_log_t; +}; -static void vu_gc_log_show_json(garbage_control_collection_log_t *payload, const char *devname) +static void vu_gc_log_show_json(struct garbage_control_collection_log *payload, const char *devname) { struct json_object *gc_entries = json_create_array(); for (int i = 0; i < VU_GC_MAX_ITEMS; i++) { - gc_item_t item = payload->item[i]; + struct __packed gc_item item = payload->item[i]; struct json_object *entry = json_create_object(); + json_object_add_value_int(entry, "timestamp", le64_to_cpu(item.timestamp)); json_object_add_value_int(entry, "timer_type", le32_to_cpu(item.timer_type)); json_array_add_value_object(gc_entries, entry); @@ -50,7 +51,7 @@ static void vu_gc_log_show_json(garbage_control_collection_log_t *payload, const json_free_object(gc_entries); } -static void vu_gc_log_show(garbage_control_collection_log_t *payload, const char *devname, +static void vu_gc_log_show(struct garbage_control_collection_log *payload, const char *devname, __u8 uuid_index) { printf("Solidigm Garbage Collection Log for NVME device:%s UUID-idx:%d\n", devname, @@ -58,7 +59,8 @@ static void vu_gc_log_show(garbage_control_collection_log_t *payload, const char printf("Timestamp Timer Type\n"); for (int i = 0; i < VU_GC_MAX_ITEMS; i++) { - gc_item_t item = payload->item[i]; + struct __packed gc_item item = payload->item[i]; + printf("%-13" PRIu64 " %d\n", le64_to_cpu(item.timestamp), le32_to_cpu(item.timer_type)); } } @@ -88,15 +90,16 @@ int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *c return err; enum nvme_print_flags flags = validate_output_format(cfg.output_format); + if (flags == -EINVAL) { fprintf(stderr, "Invalid output format '%s'\n", cfg.output_format); dev_close(dev); - return EINVAL; + return -EINVAL; } uuid_index = solidigm_get_vu_uuid_index(dev); - garbage_control_collection_log_t gc_log; + struct garbage_control_collection_log gc_log; const int solidigm_vu_gc_log_id = 0xfd; struct nvme_get_log_args args = { .lpo = 0, @@ -118,15 +121,13 @@ int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *c err = nvme_get_log(&args); if (!err) { - if (flags & BINARY) { + if (flags & BINARY) d_raw((unsigned char *)&gc_log, sizeof(gc_log)); - } else if (flags & JSON) { + else if (flags & JSON) vu_gc_log_show_json(&gc_log, dev->name); - } else { + else vu_gc_log_show(&gc_log, dev->name, uuid_index); - } - } - else if (err > 0) { + } else if (err > 0) { nvme_show_status(err); } diff --git a/plugins/solidigm/solidigm-id-ctrl.c b/plugins/solidigm/solidigm-id-ctrl.c new file mode 100644 index 0000000..f45e758 --- /dev/null +++ b/plugins/solidigm/solidigm-id-ctrl.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include <inttypes.h> +#include "common.h" +#include "solidigm-id-ctrl.h" + +struct __packed nvme_vu_id_ctrl_field { /* CDR MR5 */ + __u8 rsvd1[3]; + __u8 ss; + char health[20]; + __u8 cls; + __u8 nlw; + __u8 scap; + __u8 sstat; + char bl[8]; + __u8 rsvd2[38]; + __le64 ww; + char mic_bl[4]; + char mic_fw[4]; +}; + +void sldgm_id_ctrl(uint8_t *vs, struct json_object *root) +{ + // text output aligns nicely with property name up to 10 chars + const char *str_ss = "stripeSize"; + const char *str_health = "health"; + const char *str_cls = "linkSpeed"; + const char *str_nlw = "negLnkWdth"; + const char *str_scap = "secCapab"; + const char *str_sstat = "secStatus"; + const char *str_bl = "bootLoader"; + const char *str_ww = "wwid"; + const char *str_mic_bl = "bwLimGran"; + const char *str_mic_fw = "ioLimGran"; + + struct nvme_vu_id_ctrl_field *id = (struct nvme_vu_id_ctrl_field *)vs; + + const char str_heathy[sizeof(id->health)] = "healthy"; + const char *health = id->health[0] ? id->health : str_heathy; + + if (root == NULL) { + printf("%-10s: %u\n", str_ss, id->ss); + printf("%-10s: %.*s\n", str_health, (int)sizeof(id->health), health); + printf("%-10s: %u\n", str_cls, id->cls); + printf("%-10s: %u\n", str_nlw, id->nlw); + printf("%-10s: %u\n", str_scap, id->scap); + printf("%-10s: %u\n", str_sstat, id->sstat); + printf("%-10s: %.*s\n", str_bl, (int)sizeof(id->bl), id->bl); + printf("%-10s: 0x%016"PRIx64"\n", str_ww, le64_to_cpu(id->ww)); + printf("%-10s: %.*s\n", str_mic_bl, (int)sizeof(id->mic_bl), id->mic_bl); + printf("%-10s: %.*s\n", str_mic_fw, (int)sizeof(id->mic_fw), id->mic_fw); + return; + } + + json_object_add_value_uint(root, str_ss, id->ss); + json_object_object_add(root, str_health, + json_object_new_string_len(health, sizeof(id->health))); + json_object_add_value_uint(root, str_cls, id->cls); + json_object_add_value_uint(root, str_nlw, id->nlw); + json_object_add_value_uint(root, str_scap, id->scap); + json_object_add_value_uint(root, str_sstat, id->sstat); + json_object_object_add(root, str_bl, json_object_new_string_len(id->bl, sizeof(id->bl))); + json_object_add_value_uint64(root, str_ww, le64_to_cpu(id->ww)); + json_object_object_add(root, str_mic_bl, + json_object_new_string_len(id->mic_bl, sizeof(id->mic_bl))); + json_object_object_add(root, str_mic_fw, + json_object_new_string_len(id->mic_fw, sizeof(id->mic_fw))); +} diff --git a/plugins/solidigm/solidigm-id-ctrl.h b/plugins/solidigm/solidigm-id-ctrl.h new file mode 100644 index 0000000..ed6e438 --- /dev/null +++ b/plugins/solidigm/solidigm-id-ctrl.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include <inttypes.h> +#include "util/json.h" +void sldgm_id_ctrl(uint8_t *vs, struct json_object *root); diff --git a/plugins/solidigm/solidigm-internal-logs.c b/plugins/solidigm/solidigm-internal-logs.c new file mode 100644 index 0000000..4730443 --- /dev/null +++ b/plugins/solidigm/solidigm-internal-logs.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Solidigm. + * + * Authors: leonardo.da.cunha@solidigm.com + * shankaralingegowda.singonahalli@solidigm.com + */ + +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <linux/limits.h> + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "nvme-print.h" + +#define DWORD_SIZE 4 + +enum log_type { + NLOG = 0, + EVENTLOG = 1, + ASSERTLOG = 2, +}; + +#pragma pack(push, internal_logs, 1) +struct version { + __u16 major; + __u16 minor; +}; + +struct event_dump_instance { + __u32 numeventdumps; + __u32 coresize; + __u32 coreoffset; + __u32 eventidoffset[16]; + __u8 eventIdValidity[16]; +}; + +struct commom_header { + struct version ver; + __u32 header_size; + __u32 log_size; + __u32 numcores; +}; + +struct event_dump_header { + struct commom_header header; + __u32 eventidsize; + struct event_dump_instance edumps[0]; +}; + +struct assert_dump_core { + __u32 coreoffset; + __u32 assertsize; + __u8 assertdumptype; + __u8 assertvalid; + __u8 reserved[2]; +}; + +struct assert_dump_header { + struct commom_header header; + struct assert_dump_core core[]; +}; + +struct nlog_dump_header_common { + struct version ver; + __u32 logselect; + __u32 totalnlogs; + __u32 nlognum; + char nlogname[4]; + __u32 nlogbytesize; + __u32 nlogprimarybuffsize; + __u32 tickspersecond; + __u32 corecount; +}; + +struct nlog_dump_header3_0 { + struct nlog_dump_header_common common; + __u32 nlogpausestatus; + __u32 selectoffsetref; + __u32 selectnlogpause; + __u32 selectaddedoffset; + __u32 nlogbufnum; + __u32 nlogbufnummax; +}; + +struct nlog_dump_header4_0 { + struct nlog_dump_header_common common; + __u64 nlogpausestatus; + __u32 selectoffsetref; + __u32 selectnlogpause; + __u32 selectaddedoffset; + __u32 nlogbufnum; + __u32 nlogbufnummax; + __u32 coreselected; + __u32 reserved[2]; +}; + +struct nlog_dump_header4_1 { + struct nlog_dump_header_common common; + __u64 nlogpausestatus; + __u32 selectoffsetref; + __u32 selectnlogpause; + __u32 selectaddedoffset; + __u32 nlogbufnum; + __u32 nlogbufnummax; + __u32 coreselected; + __u32 lpaPointer1High; + __u32 lpaPointer1Low; + __u32 lpaPointer2High; + __u32 lpaPointer2Low; +}; + +#pragma pack(pop, internal_logs) + +struct config { + __u32 namespace_id; + char *file_prefix; + char *type; + bool verbose; +}; + +static void print_nlog_header(__u8 *buffer) +{ + struct nlog_dump_header_common *nlog_header = (struct nlog_dump_header_common *) buffer; + + if (nlog_header->ver.major >= 3) { + printf("Version Major %u\n", nlog_header->ver.major); + printf("Version Minor %u\n", nlog_header->ver.minor); + printf("Log_select %u\n", nlog_header->logselect); + printf("totalnlogs %u\n", nlog_header->totalnlogs); + printf("nlognum %u\n", nlog_header->nlognum); + printf("nlogname %c%c%c%c\n", nlog_header->nlogname[3], nlog_header->nlogname[2], + nlog_header->nlogname[1], nlog_header->nlogname[0]); + printf("nlogbytesize %u\n", nlog_header->nlogbytesize); + printf("nlogprimarybuffsize %u\n", nlog_header->nlogprimarybuffsize); + printf("tickspersecond %u\n", nlog_header->tickspersecond); + printf("corecount %u\n", nlog_header->corecount); + } + if (nlog_header->ver.major >= 4) { + struct nlog_dump_header4_0 *nlog_header = (struct nlog_dump_header4_0 *) buffer; + + printf("nlogpausestatus %"PRIu64"\n", (uint64_t)nlog_header->nlogpausestatus); + printf("selectoffsetref %u\n", nlog_header->selectoffsetref); + printf("selectnlogpause %u\n", nlog_header->selectnlogpause); + printf("selectaddedoffset %u\n", nlog_header->selectaddedoffset); + printf("nlogbufnum %u\n", nlog_header->nlogbufnum); + printf("nlogbufnummax %u\n", nlog_header->nlogbufnummax); + printf("coreselected %u\n\n", nlog_header->coreselected); + } +} + +#define INTERNAL_LOG_MAX_BYTE_TRANSFER 4096 +#define INTERNAL_LOG_MAX_DWORD_TRANSFER (INTERNAL_LOG_MAX_BYTE_TRANSFER / 4) + +static int cmd_dump_repeat(struct nvme_passthru_cmd *cmd, __u32 total_dw_size, + int out_fd, int ioctl_fd, bool force_max_transfer) +{ + int err = 0; + + while (total_dw_size > 0) { + size_t dword_tfer = min(INTERNAL_LOG_MAX_DWORD_TRANSFER, total_dw_size); + + cmd->cdw10 = force_max_transfer ? INTERNAL_LOG_MAX_DWORD_TRANSFER : dword_tfer; + cmd->data_len = dword_tfer * 4; + err = nvme_submit_admin_passthru(ioctl_fd, cmd, NULL); + if (err) + return err; + + if (out_fd > 0) { + err = write(out_fd, (const void *)(uintptr_t)cmd->addr, cmd->data_len); + if (err < 0) { + perror("write failure"); + return err; + } + err = 0; + } + total_dw_size -= dword_tfer; + cmd->cdw13 += dword_tfer; + } + return err; +} + +static int write_header(__u8 *buf, int fd, size_t amnt) +{ + if (write(fd, buf, amnt) < 0) + return 1; + return 0; +} + +static int read_header(struct nvme_passthru_cmd *cmd, int ioctl_fd) +{ + memset((void *)(uintptr_t)cmd->addr, 0, INTERNAL_LOG_MAX_BYTE_TRANSFER); + return cmd_dump_repeat(cmd, INTERNAL_LOG_MAX_DWORD_TRANSFER, -1, ioctl_fd, false); +} + +static int get_serial_number(char *str, int fd) +{ + struct nvme_id_ctrl ctrl = {0}; + int err; + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + return err; + + /* Remove trailing spaces */ + for (int i = sizeof(ctrl.sn) - 1; i && ctrl.sn[i] == ' '; i--) + ctrl.sn[i] = '\0'; + sprintf(str, "%-.*s", (int)sizeof(ctrl.sn), ctrl.sn); + return err; +} + +static int dump_assert_logs(struct nvme_dev *dev, struct config cfg) +{ + __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER]; + __u8 head_buf[INTERNAL_LOG_MAX_BYTE_TRANSFER]; + char file_path[PATH_MAX]; + struct assert_dump_header *ad = (struct assert_dump_header *) head_buf; + struct nvme_passthru_cmd cmd = { + .opcode = 0xd2, + .nsid = cfg.namespace_id, + .addr = (unsigned long)(void *)head_buf, + .cdw12 = ASSERTLOG, + .cdw13 = 0, + }; + int output, err; + + err = read_header(&cmd, dev_fd(dev)); + if (err) + return err; + + sprintf(file_path, "%s_AssertLog.bin", cfg.file_prefix); + output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (output < 0) + return -errno; + err = write_header((__u8 *)ad, output, ad->header.header_size * DWORD_SIZE); + if (err) { + perror("write failure"); + close(output); + return err; + } + cmd.addr = (unsigned long)(void *)buf; + + if (cfg.verbose) { + printf("Assert Log, cores: %d log size: %d header size: %d\n", ad->header.numcores, + ad->header.log_size * DWORD_SIZE, ad->header.header_size * DWORD_SIZE); + for (__u32 i = 0; i < ad->header.numcores; i++) + printf("core %d assert size: %d\n", i, ad->core[i].assertsize * DWORD_SIZE); + } + + for (__u32 i = 0; i < ad->header.numcores; i++) { + if (!ad->core[i].assertvalid) + continue; + cmd.cdw13 = ad->core[i].coreoffset; + err = cmd_dump_repeat(&cmd, ad->core[i].assertsize, + output, + dev_fd(dev), false); + if (err) { + close(output); + return err; + } + } + close(output); + printf("Successfully wrote log to %s\n", file_path); + return err; +} + +static int dump_event_logs(struct nvme_dev *dev, struct config cfg) +{ + __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER]; + __u8 head_buf[INTERNAL_LOG_MAX_BYTE_TRANSFER]; + char file_path[PATH_MAX]; + struct event_dump_header *ehdr = (struct event_dump_header *) head_buf; + struct nvme_passthru_cmd cmd = { + .opcode = 0xd2, + .nsid = cfg.namespace_id, + .addr = (unsigned long)(void *)head_buf, + .cdw12 = EVENTLOG, + .cdw13 = 0, + }; + int output; + int core_num, err; + + err = read_header(&cmd, dev_fd(dev)); + if (err) + return err; + sprintf(file_path, "%s_EventLog.bin", cfg.file_prefix); + output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (output < 0) + return -errno; + err = write_header(head_buf, output, INTERNAL_LOG_MAX_BYTE_TRANSFER); + + core_num = ehdr->header.numcores; + + if (err) { + close(output); + return err; + } + cmd.addr = (unsigned long)(void *)buf; + + if (cfg.verbose) + printf("Event Log, cores: %d log size: %d\n", core_num, ehdr->header.log_size * 4); + + for (__u32 j = 0; j < core_num; j++) { + if (cfg.verbose) { + for (int k = 0 ; k < 16; k++) { + printf("core: %d event: %d ", j, k); + printf("validity: %d ", ehdr->edumps[j].eventIdValidity[k]); + printf("offset: %d\n", ehdr->edumps[j].eventidoffset[k]); + } + } + cmd.cdw13 = ehdr->edumps[j].coreoffset; + err = cmd_dump_repeat(&cmd, ehdr->edumps[j].coresize, + output, dev_fd(dev), false); + if (err) { + close(output); + return err; + } + } + close(output); + printf("Successfully wrote log to %s\n", file_path); + return err; +} + +static size_t get_nlog_header_size(struct nlog_dump_header_common *nlog_header) +{ + switch (nlog_header->ver.major) { + case 3: + return sizeof(struct nlog_dump_header3_0); + case 4: + if (nlog_header->ver.minor == 0) + return sizeof(struct nlog_dump_header4_0); + return sizeof(struct nlog_dump_header4_1); + default: + return INTERNAL_LOG_MAX_BYTE_TRANSFER; + } + +} + +/* dumps nlogs from specified core or all cores when core = -1 */ +static int dump_nlogs(struct nvme_dev *dev, struct config cfg, int core) +{ + int err = 0; + __u32 count, core_num; + __u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER]; + char file_path[PATH_MAX]; + struct nlog_dump_header_common *nlog_header = (struct nlog_dump_header_common *)buf; + struct nvme_passthru_cmd cmd = { + .opcode = 0xd2, + .nsid = cfg.namespace_id, + .addr = (unsigned long)(void *)buf + }; + + struct dump_select { + union { + struct { + __u32 selectLog : 3; + __u32 selectCore : 2; + __u32 selectNlog : 8; + }; + __u32 raw; + }; + } log_select; + int output; + bool is_open = false; + size_t header_size = 0; + + log_select.selectCore = core < 0 ? 0 : core; + do { + log_select.selectNlog = 0; + do { + cmd.cdw13 = 0; + cmd.cdw12 = log_select.raw; + err = read_header(&cmd, dev_fd(dev)); + if (err) { + if (is_open) + close(output); + return err; + } + count = nlog_header->totalnlogs; + core_num = core < 0 ? nlog_header->corecount : 0; + if (!header_size) { + sprintf(file_path, "%s_NLog.bin", cfg.file_prefix); + output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (output < 0) + return -errno; + header_size = get_nlog_header_size(nlog_header); + is_open = true; + } + err = write_header(buf, output, header_size); + if (err) + break; + if (cfg.verbose) + print_nlog_header(buf); + cmd.cdw13 = 0x400; + err = cmd_dump_repeat(&cmd, nlog_header->nlogbytesize / 4, + output, dev_fd(dev), true); + if (err) + break; + } while (++log_select.selectNlog < count); + if (err) + break; + } while (++log_select.selectCore < core_num); + if (is_open) { + close(output); + printf("Successfully wrote log to %s\n", file_path); + } + return err; +} + +enum telemetry_type { + HOSTGENOLD, + HOSTGENNEW, + CONTROLLER +}; + +static int dump_telemetry(struct nvme_dev *dev, struct config cfg, enum telemetry_type ttype) +{ + struct nvme_telemetry_log *log = NULL; + size_t log_size = 0; + int err = 0, output; + __u8 *buffer = NULL; + size_t bytes_remaining = 0; + int data_area = NVME_TELEMETRY_DA_3; + char file_path[PATH_MAX]; + char *log_name; + + switch (ttype) { + case HOSTGENNEW: + log_name = "TelemetryHostGenNew"; + break; + case HOSTGENOLD: + log_name = "TelemetryHostGenOld"; + break; + case CONTROLLER: + log_name = "TelemetryController"; + break; + default: + return -EINVAL; + } + + sprintf(file_path, "%s_%s.bin", cfg.file_prefix, log_name); + output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (output < 0) + return -errno; + + switch (ttype) { + case HOSTGENNEW: + err = nvme_get_new_host_telemetry(dev_fd(dev), &log, + data_area, &log_size); + break; + case HOSTGENOLD: + err = nvme_get_host_telemetry(dev_fd(dev), &log, + data_area, &log_size); + break; + case CONTROLLER: + err = nvme_get_ctrl_telemetry(dev_fd(dev), true, &log, + data_area, &log_size); + break; + } + + if (err) + goto tele_close_output; + + bytes_remaining = log_size; + buffer = (__u8 *)log; + + while (bytes_remaining) { + ssize_t bytes_written = write(output, buffer, bytes_remaining); + + if (bytes_written < 0) { + err = -errno; + goto tele_close_output; + } + bytes_remaining -= bytes_written; + buffer += bytes_written; + } + printf("Successfully wrote log to %s\n", file_path); + +tele_close_output: + free(log); + close(output); + + return err; +} + +int solidigm_get_internal_log(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + char sn_prefix[sizeof(((struct nvme_id_ctrl *)0)->sn)+1]; + int log_count = 0; + int err; + struct nvme_dev *dev; + bool all = false; + + const char *desc = "Get Debug Firmware Logs and save them."; + const char *type = + "Log type: ALL, CONTROLLERINITTELEMETRY, HOSTINITTELEMETRY, HOSTINITTELEMETRYNOGEN, NLOG, ASSERT, EVENT. Defaults to ALL."; + const char *prefix = "Output file prefix; defaults to device serial number."; + const char *verbose = "To print out verbose info."; + const char *namespace_id = "Namespace to get logs from."; + + + struct config cfg = { + .namespace_id = NVME_NSID_ALL, + .file_prefix = NULL, + .type = NULL, + }; + + OPT_ARGS(opts) = { + OPT_STR("type", 't', &cfg.type, type), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_FILE("file-prefix", 'p', &cfg.file_prefix, prefix), + OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + if (!cfg.file_prefix) { + err = get_serial_number(sn_prefix, dev_fd(dev)); + if (err) + goto out_dev; + cfg.file_prefix = sn_prefix; + } + + if (!cfg.type) + cfg.type = "ALL"; + else { + for (char *p = cfg.type; *p; ++p) + *p = toupper(*p); + } + + if (!strcmp(cfg.type, "ALL")) + all = true; + if (all || !strcmp(cfg.type, "ASSERT")) { + err = dump_assert_logs(dev, cfg); + if (err == 0) + log_count++; + else if (err < 0) + perror("Assert log"); + } + if (all || !strcmp(cfg.type, "EVENT")) { + err = dump_event_logs(dev, cfg); + if (err == 0) + log_count++; + else if (err < 0) + perror("Eventt log"); + } + if (all || !strcmp(cfg.type, "NLOG")) { + err = dump_nlogs(dev, cfg, -1); + if (err == 0) + log_count++; + else if (err < 0) + perror("Nlog"); + } + if (all || !strcmp(cfg.type, "CONTROLLERINITTELEMETRY")) { + err = dump_telemetry(dev, cfg, CONTROLLER); + if (err == 0) + log_count++; + else if (err < 0) + perror("Telemetry Controller Initated"); + } + if (all || !strcmp(cfg.type, "HOSTINITTELEMETRYNOGEN")) { + err = dump_telemetry(dev, cfg, HOSTGENOLD); + if (err == 0) + log_count++; + else if (err < 0) + perror("Previously existing Telemetry Host Initated"); + } + if (all || !strcmp(cfg.type, "HOSTINITTELEMETRY")) { + err = dump_telemetry(dev, cfg, HOSTGENNEW); + if (err == 0) + log_count++; + else if (err < 0) + perror("Telemetry Host Initated"); + } + + if (log_count == 0) { + if (err > 0) + nvme_show_status(err); + } else if ((log_count > 1) || cfg.verbose) + printf("Total: %d log files with prefix: %s\n", log_count, cfg.file_prefix); +out_dev: + /* Redundant close() to make static code analysis happy */ + close(dev->direct.fd); + dev_close(dev); + return err; +} diff --git a/plugins/solidigm/solidigm-internal-logs.h b/plugins/solidigm/solidigm-internal-logs.h new file mode 100644 index 0000000..801af24 --- /dev/null +++ b/plugins/solidigm/solidigm-internal-logs.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +int solidigm_get_internal_log(int argc, char **argv, struct command *cmd, struct plugin *plugin); diff --git a/plugins/solidigm/solidigm-latency-tracking.c b/plugins/solidigm/solidigm-latency-tracking.c index 40edcfa..481a831 100644 --- a/plugins/solidigm/solidigm-latency-tracking.c +++ b/plugins/solidigm/solidigm-latency-tracking.c @@ -94,7 +94,6 @@ static void latency_tracker_bucket_parse(const struct latency_tracker *lt, int i __u32 bucket_data = le32_to_cpu(lt->stats.data[id]); if (lt->print_flags == NORMAL) { - printf("%-*d", COL_WIDTH, id); get_time_unit_label(buffer, lower_us, true); @@ -137,12 +136,10 @@ static void latency_tracker_parse_linear(const struct latency_tracker *lt, __u32 bytes_per, __u32 us_step, bool nonzero_print) { - for (int i = (start_offset / bytes_per) - 1; - i < end_offset / bytes_per; i++) { - if (nonzero_print && lt->stats.data[i] == 0) + for (int i = (start_offset / bytes_per) - 1; i < end_offset / bytes_per; i++) { + if (nonzero_print && !lt->stats.data[i]) continue; - latency_tracker_bucket_parse(lt, i, us_step * i, - us_step * (i + 1), true); + latency_tracker_bucket_parse(lt, i, us_step * i, us_step * (i + 1), true); } } @@ -153,6 +150,7 @@ static void latency_tracker_parse_linear(const struct latency_tracker *lt, static int latency_tracker_bucket_pos2us(const struct latency_tracker *lt, int i) { __u32 base_val = 1 << lt->base_range_bits; + if (i < (base_val << 1)) return i; @@ -171,15 +169,15 @@ static int latency_tracker_bucket_pos2us(const struct latency_tracker *lt, int i * "values" : { */ static void latency_tracker_populate_json_root(const struct latency_tracker *lt, - struct json_object *root) + struct json_object *root) { struct json_object *subroot = json_create_object(); json_object_add_value_object(root, "latstats", subroot); json_object_add_value_string(subroot, "type", lt->cfg.write ? "write" : "read"); - if (lt->has_average_latency_field) { - json_object_add_value_uint64(subroot, "average_latency", le64_to_cpu(lt->stats.average_latency)); - } + if (lt->has_average_latency_field) + json_object_add_value_uint64(subroot, "average_latency", + le64_to_cpu(lt->stats.average_latency)); json_object_add_value_object(subroot, "values", lt->bucket_list); } @@ -199,13 +197,12 @@ static void latency_tracker_parse_4_0(const struct latency_tracker *lt) int lower_us = latency_tracker_bucket_pos2us(lt, i); int upper_us = latency_tracker_bucket_pos2us(lt, i + 1); - latency_tracker_bucket_parse(lt, i, lower_us, - upper_us, + latency_tracker_bucket_parse(lt, i, lower_us, upper_us, i < (lt->bucket_list_size - 1)); } } -static void print_dash_separator() +static void print_dash_separator(void) { printf("--------------------------------------------------\n"); } @@ -218,16 +215,14 @@ static void latency_tracker_pre_parse(struct latency_tracker *lt) printf("UUID-idx: %d\n", lt->uuid_index); printf("Major Revision: %u\nMinor Revision: %u\n", le16_to_cpu(lt->stats.version_major), le16_to_cpu(lt->stats.version_minor)); - if (lt->has_average_latency_field) { + if (lt->has_average_latency_field) printf("Average Latency: %" PRIu64 "\n", le64_to_cpu(lt->stats.average_latency)); - } print_dash_separator(); printf("%-12s%-12s%-12s%-20s\n", "Bucket", "Start", "End", "Value"); print_dash_separator(); } - if (lt->print_flags == JSON) { + if (lt->print_flags == JSON) lt->bucket_list = json_object_new_array(); - } } static void latency_tracker_post_parse(struct latency_tracker *lt) @@ -253,11 +248,10 @@ static void latency_tracker_parse(struct latency_tracker *lt) latency_tracker_parse_3_0(lt); break; case 4: - if (version_minor >= 8){ + if (version_minor >= 8) lt->has_average_latency_field = true; - } latency_tracker_pre_parse(lt); - if (version_minor == 0){ + if (!version_minor) { lt->base_range_bits = BASE_RANGE_BITS_4_0; lt->bucket_list_size = BUCKET_LIST_SIZE_4_0; } @@ -275,7 +269,7 @@ static void latency_tracker_parse(struct latency_tracker *lt) #define LATENCY_TRACKING_FID 0xe2 #define LATENCY_TRACKING_FID_DATA_LEN 32 -static int latency_tracking_is_enable(struct latency_tracker *lt, __u32 * enabled) +static int latency_tracking_is_enable(struct latency_tracker *lt, __u32 *enabled) { struct nvme_get_features_args args_get = { .args_size = sizeof(args_get), @@ -298,13 +292,12 @@ static int latency_tracking_enable(struct latency_tracker *lt) __u32 result; int err; - if (!(lt->cfg.enable || lt->cfg.disable)){ + if (!(lt->cfg.enable || lt->cfg.disable)) return 0; - } - if (lt->cfg.enable && lt->cfg.disable){ - fprintf(stderr,"Cannot enable and disable simultaneously.\n"); - return EINVAL; + if (lt->cfg.enable && lt->cfg.disable) { + fprintf(stderr, "Cannot enable and disable simultaneously.\n"); + return -EINVAL; } struct nvme_set_features_args args_set = { @@ -345,9 +338,9 @@ static int latency_tracker_get_log(struct latency_tracker *lt) { int err; - if (lt->cfg.read && lt->cfg.write){ - fprintf(stderr,"Cannot capture read and write logs simultaneously.\n"); - return EINVAL; + if (lt->cfg.read && lt->cfg.write) { + fprintf(stderr, "Cannot capture read and write logs simultaneously.\n"); + return -EINVAL; } if (!(lt->cfg.read || lt->cfg.write)) @@ -422,31 +415,31 @@ int solidigm_get_latency_tracking_log(int argc, char **argv, struct command *cmd if (lt.print_flags == -EINVAL) { fprintf(stderr, "Invalid output format '%s'\n", lt.cfg.output_format); dev_close(dev); - return EINVAL; + return -EINVAL; } if (lt.cfg.type > 0xf) { fprintf(stderr, "Invalid Log type value '%d'\n", lt.cfg.type); dev_close(dev); - return EINVAL; + return -EINVAL; } if (lt.cfg.type && !(lt.cfg.read || lt.cfg.write)) { fprintf(stderr, "Log type option valid only when retrieving statistics\n"); dev_close(dev); - return EINVAL; + return -EINVAL; } lt.uuid_index = solidigm_get_vu_uuid_index(dev); err = latency_tracking_enable(<); - if (err){ + if (err) { dev_close(dev); return err; } err = latency_tracker_get_log(<); - if (err){ + if (err) { dev_close(dev); return err; } @@ -460,16 +453,16 @@ int solidigm_get_latency_tracking_log(int argc, char **argv, struct command *cmd if (!err) { if (lt.print_flags == JSON) { struct json_object *root = json_create_object(); - json_object_add_value_int(root,"enabled", enabled); + + json_object_add_value_int(root, "enabled", enabled); json_print_object(root, NULL); json_free_object(root); printf("\n"); } else if (lt.print_flags == BINARY) { putchar(enabled); } else { - printf( - "Latency Statistics Tracking (UUID-idx:%d, FID:0x%X) is currently %i.\n", - lt.uuid_index, LATENCY_TRACKING_FID, enabled); + printf("Latency Statistics Tracking (UUID-idx:%d, FID:0x%X) is currently %i.\n", + lt.uuid_index, LATENCY_TRACKING_FID, enabled); } } else { fprintf(stderr, "Could not read feature id 0xE2.\n"); diff --git a/plugins/solidigm/solidigm-log-page-dir.c b/plugins/solidigm/solidigm-log-page-dir.c new file mode 100644 index 0000000..111a433 --- /dev/null +++ b/plugins/solidigm/solidigm-log-page-dir.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Solidigm. + * + * Author: karl.dedow@solidigm.com + */ + +#include "solidigm-log-page-dir.h" + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +#include "common.h" +#include "nvme-print.h" + +#include "plugins/ocp/ocp-utils.h" + +#define MIN_VENDOR_LID 0xC0 +#define SOLIDIGM_MAX_UUID 2 + +static const char dash[100] = {[0 ... 99] = '-'}; + +struct lid_dir { + struct __packed { + bool supported; + const char *str; + } lid[NVME_LOG_SUPPORTED_LOG_PAGES_MAX]; +}; + +static void init_lid_dir(struct lid_dir *lid_dir) +{ + static const char *unknown_str = "Unknown"; + + for (int lid = 0; lid < NVME_LOG_SUPPORTED_LOG_PAGES_MAX; lid++) { + lid_dir->lid[lid].supported = false; + lid_dir->lid[lid].str = unknown_str; + } +} + +static bool is_invalid_uuid(const struct nvme_id_uuid_list_entry entry) +{ + static const unsigned char ALL_ZERO_UUID[NVME_UUID_LEN] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + return memcmp(ALL_ZERO_UUID, entry.uuid, NVME_UUID_LEN) == 0; +} + +static bool is_solidigm_uuid(const struct nvme_id_uuid_list_entry entry) +{ + static const unsigned char SOLIDIGM_UUID[NVME_UUID_LEN] = { + 0x96, 0x19, 0x58, 0x6e, 0xc1, 0x1b, 0x43, 0xad, + 0xaa, 0xaa, 0x65, 0x41, 0x87, 0xf6, 0xbb, 0xb2 + }; + + return memcmp(SOLIDIGM_UUID, entry.uuid, NVME_UUID_LEN) == 0; +} + +static bool is_ocp_uuid(const struct nvme_id_uuid_list_entry entry) +{ + static const unsigned char OCP_UUID[NVME_UUID_LEN] = { + 0xc1, 0x94, 0xd5, 0x5b, 0xe0, 0x94, 0x47, 0x94, + 0xa2, 0x1d, 0x29, 0x99, 0x8f, 0x56, 0xbe, 0x6f + }; + + return memcmp(OCP_UUID, entry.uuid, NVME_UUID_LEN) == 0; +} + +static int get_supported_log_pages_log(struct nvme_dev *dev, int uuid_index, + struct nvme_supported_log_pages *supported) +{ + static const __u8 LID; + + memset(supported, 0, sizeof(*supported)); + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = supported, + .args_size = sizeof(args), + .fd = dev_fd(dev), + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = LID, + .len = sizeof(*supported), + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = 0, + .uuidx = uuid_index, + .rae = false, + .ot = false, + }; + + return nvme_get_log(&args); +} + +static struct lid_dir *get_standard_lids(struct nvme_supported_log_pages *supported) +{ + static struct lid_dir standard_dir = { 0 }; + + init_lid_dir(&standard_dir); + standard_dir.lid[0x00].str = "Supported Log Pages"; + standard_dir.lid[0x01].str = "Error Information"; + standard_dir.lid[0x02].str = "SMART / Health Information"; + standard_dir.lid[0x03].str = "Firmware Slot Information"; + standard_dir.lid[0x04].str = "Changed Namespace List"; + standard_dir.lid[0x05].str = "Commands Supported and Effects"; + standard_dir.lid[0x06].str = "Device Self Test"; + standard_dir.lid[0x07].str = "Telemetry Host-Initiated"; + standard_dir.lid[0x08].str = "Telemetry Controller-Initiated"; + standard_dir.lid[0x09].str = "Endurance Group Information"; + standard_dir.lid[0x0A].str = "Predictable Latency Per NVM Set"; + standard_dir.lid[0x0B].str = "Predictable Latency Event Aggregate"; + standard_dir.lid[0x0C].str = "Asymmetric Namespace Access"; + standard_dir.lid[0x0D].str = "Persistent Event Log"; + standard_dir.lid[0x0E].str = "Predictable Latency Event Aggregate"; + standard_dir.lid[0x0F].str = "Endurance Group Event Aggregate"; + standard_dir.lid[0x10].str = "Media Unit Status"; + standard_dir.lid[0x11].str = "Supported Capacity Configuration List"; + standard_dir.lid[0x12].str = "Feature Identifiers Supported and Effects"; + standard_dir.lid[0x13].str = "NVMe-MI Commands Supported and Effects"; + standard_dir.lid[0x14].str = "Command and Feature lockdown"; + standard_dir.lid[0x15].str = "Boot Partition"; + standard_dir.lid[0x16].str = "Rotational Media Information"; + standard_dir.lid[0x70].str = "Discovery"; + standard_dir.lid[0x80].str = "Reservation Notification"; + standard_dir.lid[0x81].str = "Sanitize Status"; + + for (int lid = 0; lid < NVME_LOG_SUPPORTED_LOG_PAGES_MAX; lid++) { + if (!supported->lid_support[lid] || lid >= MIN_VENDOR_LID) + continue; + + standard_dir.lid[lid].supported = true; + } + + return &standard_dir; +} + +static void update_vendor_lid_supported(struct nvme_supported_log_pages *supported, + struct lid_dir *lid_dir) +{ + for (int lid = 0; lid < NVME_LOG_SUPPORTED_LOG_PAGES_MAX; lid++) { + if (!supported->lid_support[lid] || lid < MIN_VENDOR_LID) + continue; + + lid_dir->lid[lid].supported = true; + } +} + +static struct lid_dir *get_solidigm_lids(struct nvme_supported_log_pages *supported) +{ + static struct lid_dir solidigm_dir = { 0 }; + + init_lid_dir(&solidigm_dir); + solidigm_dir.lid[0xC1].str = "Read Commands Latency Statistics"; + solidigm_dir.lid[0xC2].str = "Write Commands Latency Statistics"; + solidigm_dir.lid[0xC4].str = "Endurance Manager Statistics"; + solidigm_dir.lid[0xC5].str = "Temperature Statistics"; + solidigm_dir.lid[0xCA].str = "SMART Attributes"; + + update_vendor_lid_supported(supported, &solidigm_dir); + + return &solidigm_dir; +} + +static struct lid_dir *get_ocp_lids(struct nvme_supported_log_pages *supported) +{ + static struct lid_dir ocp_dir = { 0 }; + + init_lid_dir(&ocp_dir); + ocp_dir.lid[0xC0].str = "OCP SMART / Health Information Extended"; + ocp_dir.lid[0xC1].str = "OCP Error Recovery"; + ocp_dir.lid[0xC2].str = "OCP Firmware Activation History"; + ocp_dir.lid[0xC3].str = "OCP Latency Monitor"; + ocp_dir.lid[0xC4].str = "OCP Device Capabilities"; + ocp_dir.lid[0xC5].str = "OCP Unsupported Requirements"; + + update_vendor_lid_supported(supported, &ocp_dir); + + return &ocp_dir; +} + +static void supported_log_pages_normal(struct lid_dir *lid_dir[SOLIDIGM_MAX_UUID + 1]) +{ + printf("%-5s %-4s %-42s\n", "uuidx", "LID", "Description"); + printf("%-.5s %-.4s %-.42s\n", dash, dash, dash); + + for (int uuid_index = 0; uuid_index <= SOLIDIGM_MAX_UUID; uuid_index++) { + if (!lid_dir[uuid_index]) + continue; + + for (int lid = 0; lid < NVME_LOG_SUPPORTED_LOG_PAGES_MAX; lid++) { + if (!lid_dir[uuid_index]->lid[lid].supported) + continue; + + printf("%-5d 0x%02x %s\n", le32_to_cpu(uuid_index), le32_to_cpu(lid), + lid_dir[uuid_index]->lid[lid].str); + } + } +} + +static void supported_log_pages_json(struct lid_dir *lid_dir[SOLIDIGM_MAX_UUID + 1]) +{ + struct json_object *root = json_create_array(); + + for (int uuid_index = 0; uuid_index <= SOLIDIGM_MAX_UUID; uuid_index++) { + if (!lid_dir[uuid_index]) + continue; + + for (int lid = 0; lid < NVME_LOG_SUPPORTED_LOG_PAGES_MAX; lid++) { + if (!lid_dir[uuid_index]->lid[lid].supported) + continue; + + struct json_object *lid_obj = json_create_object(); + + json_object_add_value_uint(lid_obj, "uuidx", le32_to_cpu(uuid_index)); + json_object_add_value_uint(lid_obj, "lid", le32_to_cpu(lid)); + json_object_add_value_string(lid_obj, "description", + lid_dir[uuid_index]->lid[lid].str); + json_array_add_value_object(root, lid_obj); + } + } + + json_print_object(root, NULL); + json_free_object(root); + printf("\n"); +} + +int solidigm_get_log_page_directory_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const int NO_UUID_INDEX = 0; + const char *description = "Retrieves list of supported log pages for each UUID index."; + char *format = "normal"; + + OPT_ARGS(options) = { + OPT_FMT("output-format", 'o', &format, "output format : normal | json"), + OPT_END() + }; + + struct nvme_dev *dev = NULL; + int err = parse_and_open(&dev, argc, argv, description, options); + + if (err) + return err; + + struct lid_dir *lid_dirs[SOLIDIGM_MAX_UUID + 1] = { 0 }; + struct nvme_id_uuid_list uuid_list = { 0 }; + struct nvme_supported_log_pages supported = { 0 }; + + err = get_supported_log_pages_log(dev, NO_UUID_INDEX, &supported); + + if (!err) { + lid_dirs[NO_UUID_INDEX] = get_standard_lids(&supported); + + // Assume VU logs are the Solidigm log pages if UUID not supported. + if (nvme_identify_uuid(dev_fd(dev), &uuid_list)) { + struct lid_dir *solidigm_lid_dir = get_solidigm_lids(&supported); + + // Transfer supported Solidigm lids to lid directory at UUID index 0 + for (int lid = 0; lid < NVME_LOG_SUPPORTED_LOG_PAGES_MAX; lid++) { + if (solidigm_lid_dir->lid[lid].supported) + lid_dirs[NO_UUID_INDEX]->lid[lid] = solidigm_lid_dir->lid[lid]; + } + } else { + for (int uuid_index = 1; uuid_index <= SOLIDIGM_MAX_UUID; uuid_index++) { + if (is_invalid_uuid(uuid_list.entry[uuid_index - 1])) + break; + else if (get_supported_log_pages_log(dev, uuid_index, &supported)) + continue; + + if (is_solidigm_uuid(uuid_list.entry[uuid_index - 1])) + lid_dirs[uuid_index] = get_solidigm_lids(&supported); + else if (is_ocp_uuid(uuid_list.entry[uuid_index - 1])) + lid_dirs[uuid_index] = get_ocp_lids(&supported); + } + } + } else { + nvme_show_status(err); + } + + if (!err) { + const enum nvme_print_flags print_flag = validate_output_format(format); + + if (print_flag == NORMAL) { + supported_log_pages_normal(lid_dirs); + } else if (print_flag == JSON) { + supported_log_pages_json(lid_dirs); + } else { + fprintf(stderr, "Error: Invalid output format specified: %s.\n", format); + err = -EINVAL; + } + } + + /* Redundant close() to make static code analysis happy */ + close(dev->direct.fd); + dev_close(dev); + return err; +} diff --git a/plugins/solidigm/solidigm-log-page-dir.h b/plugins/solidigm/solidigm-log-page-dir.h new file mode 100644 index 0000000..48777df --- /dev/null +++ b/plugins/solidigm/solidigm-log-page-dir.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Solidigm. + * + * Authors: karl.dedow@solidigm.com + */ + +#ifndef SOLIDIGM_LOG_PAGE_DIRECTORY_H +#define SOLIDIGM_LOG_PAGE_DIRECTORY_H + +struct command; +struct plugin; + +int solidigm_get_log_page_directory_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin); + +#endif diff --git a/plugins/solidigm/solidigm-market-log.c b/plugins/solidigm/solidigm-market-log.c new file mode 100644 index 0000000..d7d38da --- /dev/null +++ b/plugins/solidigm/solidigm-market-log.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Solidigm. + * + * Authors: leonardo.da.cunha@solidigm.com + * Hardeep.Dhillon@solidigm.com + */ + +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <linux/limits.h> + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "nvme-print.h" + +#define MARKET_LOG_MAX_SIZE 512 + +int sldgm_get_market_log(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + const char *desc = "Get Solidigm Marketing Name log and show it."; + const char *raw = "dump output in binary format"; + struct nvme_dev *dev; + char log[MARKET_LOG_MAX_SIZE]; + int err; + + struct config { + bool raw_binary; + }; + + struct config cfg = { + }; + + OPT_ARGS(opts) = { + OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + err = nvme_get_log_simple(dev_fd(dev), 0xdd, sizeof(log), log); + if (!err) { + if (!cfg.raw_binary) + printf("Solidigm Marketing Name Log:\n%s\n", log); + else + d_raw((unsigned char *)&log, sizeof(log)); + } else if (err > 0) + + nvme_show_status(err); + /* Redundant close() to make static code analysis happy */ + close(dev->direct.fd); + dev_close(dev); + return err; +} diff --git a/plugins/solidigm/solidigm-market-log.h b/plugins/solidigm/solidigm-market-log.h new file mode 100644 index 0000000..6f808c4 --- /dev/null +++ b/plugins/solidigm/solidigm-market-log.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Solidigm. + * + * Author: hardeep.dhillon@solidigm.com + */ + +int sldgm_get_market_log(int argc, char **argv, struct command *cmd, struct plugin *plugin); diff --git a/plugins/solidigm/solidigm-nvme.c b/plugins/solidigm/solidigm-nvme.c index 0e42bd6..b0db1ea 100644 --- a/plugins/solidigm/solidigm-nvme.c +++ b/plugins/solidigm/solidigm-nvme.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2022 Solidigm. + * Copyright (c) 2022-2023 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ @@ -10,19 +10,34 @@ #define CREATE_CMD #include "solidigm-nvme.h" +#include "solidigm-id-ctrl.h" #include "solidigm-smart.h" +#include "solidigm-internal-logs.h" #include "solidigm-garbage-collection.h" #include "solidigm-latency-tracking.h" #include "solidigm-telemetry.h" +#include "solidigm-log-page-dir.h" +#include "solidigm-market-log.h" #include "plugins/ocp/ocp-clear-fw-update-history.h" #include "plugins/ocp/ocp-smart-extended-log.h" +#include "plugins/ocp/ocp-fw-activation-history.h" + +static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + return __id_ctrl(argc, argv, cmd, plugin, sldgm_id_ctrl); +} static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { return solidigm_get_additional_smart_log(argc, argv, cmd, plugin); } +static int get_internal_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + return solidigm_get_internal_log(argc, argv, cmd, plugin); +} + static int get_garbage_collection_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { return solidigm_get_garbage_collection_log(argc, argv, cmd, plugin); @@ -49,3 +64,21 @@ static int smart_cloud(int argc, char **argv, struct command *cmd, { return ocp_smart_add_log(argc, argv, cmd, plugin); } + +static int fw_activation_history(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return ocp_fw_activation_history_log(argc, argv, cmd, plugin); +} + +static int get_log_page_directory_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return solidigm_get_log_page_directory_log(argc, argv, cmd, plugin); +} + +static int get_market_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return sldgm_get_market_log(argc, argv, cmd, plugin); +} diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h index 1fdc6a6..69b63e5 100644 --- a/plugins/solidigm/solidigm-nvme.h +++ b/plugins/solidigm/solidigm-nvme.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (c) 2022 Solidigm. + * Copyright (c) 2022-2023 Solidigm. * * Author: leonardo.da.cunha@solidigm.com */ @@ -13,18 +13,21 @@ #include "cmd.h" -#define SOLIDIGM_PLUGIN_VERSION "0.8" +#define SOLIDIGM_PLUGIN_VERSION "0.14" PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_VERSION), COMMAND_LIST( + ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl) ENTRY("smart-log-add", "Retrieve Solidigm SMART Log", get_additional_smart_log) ENTRY("vs-smart-add-log", "Get SMART / health extended log (redirects to ocp plug-in)", smart_cloud) + ENTRY("vs-internal-log", "Retrieve Debug log binaries", get_internal_log) ENTRY("garbage-collect-log", "Retrieve Garbage Collection Log", get_garbage_collection_log) + ENTRY("market-log", "Retrieve Market Log", get_market_log) ENTRY("latency-tracking-log", "Enable/Retrieve Latency tracking Log", get_latency_tracking_log) ENTRY("parse-telemetry-log", "Parse Telemetry Log binary", get_telemetry_log) - ENTRY("clear-fw-activate-history", - "Clear firmware update history log (redirects to ocp plug-in)", - clear_fw_update_history) + ENTRY("clear-fw-activate-history", "Clear firmware update history log (redirects to ocp plug-in)", clear_fw_update_history) + ENTRY("vs-fw-activate-history", "Get firmware activation history log (redirects to ocp plug-in)", fw_activation_history) + ENTRY("log-page-directory", "Retrieve log page directory", get_log_page_directory_log) ) ); diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c index 568d3ab..e3d468a 100644 --- a/plugins/solidigm/solidigm-smart.c +++ b/plugins/solidigm/solidigm-smart.c @@ -21,32 +21,31 @@ #include "solidigm-smart.h" #include "solidigm-util.h" -struct __attribute__((packed)) nvme_additional_smart_log_item { +struct __packed nvme_additional_smart_log_item { __u8 id; __u8 _kp[2]; __u8 normalized; __u8 _np; - union __attribute__((packed)) { + union __packed { __u8 raw[6]; - struct __attribute__((packed)) wear_level { + struct __packed wear_level { __le16 min; __le16 max; __le16 avg; } wear_level; - struct __attribute__((packed)) thermal_throttle { + struct __packed thermal_throttle { __u8 pct; __u32 count; } thermal_throttle; - } ; + }; __u8 _rp; -} ; -typedef struct nvme_additional_smart_log_item smart_log_item_t; +}; #define VU_SMART_PAGE_SIZE 512 -#define VU_SMART_MAX_ITEMS VU_SMART_PAGE_SIZE / sizeof(smart_log_item_t) -typedef struct vu_smart_log { - smart_log_item_t item[VU_SMART_MAX_ITEMS]; -} vu_smart_log_t; +#define VU_SMART_MAX_ITEMS (VU_SMART_PAGE_SIZE / sizeof(struct nvme_additional_smart_log_item)) +struct vu_smart_log { + struct nvme_additional_smart_log_item item[VU_SMART_MAX_ITEMS]; +}; static char *id_to_name(__u8 id) { @@ -110,11 +109,10 @@ static char *id_to_name(__u8 id) } } -static void smart_log_item_print(smart_log_item_t *item) +static void smart_log_item_print(struct nvme_additional_smart_log_item *item) { - if (!item->id) { + if (!item->id) return; - } printf("%#x %-45s %3d ", item->id, id_to_name(item->id), item->normalized); @@ -136,13 +134,12 @@ static void smart_log_item_print(smart_log_item_t *item) } } -static void smart_log_item_add_json(smart_log_item_t *item, struct json_object *dev_stats) +static void smart_log_item_add_json(struct nvme_additional_smart_log_item *item, struct json_object *dev_stats) { struct json_object *entry_stats = json_create_object(); - if (!item->id) { + if (!item->id) return; - } json_object_add_value_int(entry_stats, "normalized", item->normalized); @@ -162,15 +159,14 @@ static void smart_log_item_add_json(smart_log_item_t *item, struct json_object * json_object_add_value_object(dev_stats, id_to_name(item->id), entry_stats); } -static void vu_smart_log_show_json(vu_smart_log_t *payload, unsigned int nsid, const char *devname) +static void vu_smart_log_show_json(struct vu_smart_log *payload, unsigned int nsid, const char *devname) { struct json_object *dev_stats = json_create_object(); - smart_log_item_t *item = payload->item; + struct nvme_additional_smart_log_item *item = payload->item; struct json_object *root; - for (int i = 0; i < VU_SMART_MAX_ITEMS; i++) { + for (int i = 0; i < VU_SMART_MAX_ITEMS; i++) smart_log_item_add_json(&item[i], dev_stats); - } root = json_create_object(); json_object_add_value_string(root, "Solidigm SMART log", devname); @@ -180,26 +176,25 @@ static void vu_smart_log_show_json(vu_smart_log_t *payload, unsigned int nsid, c json_free_object(root); } -static void vu_smart_log_show(vu_smart_log_t *payload, unsigned int nsid, const char *devname, +static void vu_smart_log_show(struct vu_smart_log *payload, unsigned int nsid, const char *devname, __u8 uuid_index) { - smart_log_item_t *item = payload->item; + struct nvme_additional_smart_log_item *item = payload->item; printf("Additional Smart Log for NVMe device:%s namespace-id:%x UUID-idx:%d\n", devname, nsid, uuid_index); printf("ID KEY Normalized Raw\n"); - for (int i = 0; i < VU_SMART_MAX_ITEMS; i++) { + for (int i = 0; i < VU_SMART_MAX_ITEMS; i++) smart_log_item_print(&item[i]); - } } int solidigm_get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - const char *desc = "Get Solidigm vendor specific smart log (optionally, "\ - "for the specified namespace), and show it."; + const char *desc = + "Get Solidigm vendor specific smart log (optionally, for the specified namespace), and show it."; const int solidigm_vu_smart_log_id = 0xCA; - vu_smart_log_t smart_log_payload; + struct vu_smart_log smart_log_payload; enum nvme_print_flags flags; struct nvme_dev *dev; int err; @@ -254,15 +249,14 @@ int solidigm_get_additional_smart_log(int argc, char **argv, struct command *cmd err = nvme_get_log(&args); if (!err) { - if (flags & JSON) { + if (flags & JSON) vu_smart_log_show_json(&smart_log_payload, cfg.namespace_id, dev->name); - } else if (flags & BINARY) { + else if (flags & BINARY) d_raw((unsigned char *)&smart_log_payload, sizeof(smart_log_payload)); - } else { + else vu_smart_log_show(&smart_log_payload, cfg.namespace_id, dev->name, uuid_index); - } } else if (err > 0) { nvme_show_status(err); } diff --git a/plugins/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c index 9946991..472284a 100644 --- a/plugins/solidigm/solidigm-telemetry.c +++ b/plugins/solidigm/solidigm-telemetry.c @@ -112,7 +112,7 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc } if (cfg.cfg_file) { - char *conf_str = 0; + char *conf_str = NULL; size_t length = 0; err = read_file2buffer(cfg.cfg_file, &conf_str, &length); @@ -121,9 +121,10 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc cfg.cfg_file, strerror(err)); goto close_fd; } - struct json_tokener * jstok = json_tokener_new(); + 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, @@ -160,11 +161,7 @@ int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struc 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); + solidigm_telemetry_log_data_areas_parse(&tl, cfg.data_area); json_print_object(tl.root, NULL); json_free_object(tl.root); diff --git a/plugins/solidigm/solidigm-telemetry/cod.c b/plugins/solidigm/solidigm-telemetry/cod.c index 7accc53..363822a 100644 --- a/plugins/solidigm/solidigm-telemetry/cod.c +++ b/plugins/solidigm/solidigm-telemetry/cod.c @@ -51,22 +51,20 @@ const char *oemDataMapDesc[] = { "All Time Current Max Wear Level", // 0x28 "Media Wear Remaining", // 0x29 "Total Non-Defrag Writes", // 0x2A - "Number of sectors relocated in reaction to an error" //Uid 0x2B = 43 + "Media Health Relocations" //Uid 0x2B = 43 }; -static const char * getOemDataMapDescription(__u32 id) +static const char *getOemDataMapDescription(uint32_t id) { - if (id < (sizeof(oemDataMapDesc) / sizeof(oemDataMapDesc[0]))) { + if (id < ARRAY_SIZE(oemDataMapDesc)) return oemDataMapDesc[id]; - } return "unknown"; } #define OEMSIGNATURE 0x504D4443 #pragma pack(push, cod, 1) -struct cod_header -{ +struct cod_header { uint32_t versionMajor; uint32_t versionMinor; uint32_t Signature; //!Fixed signature value (0x504D4443) for identification and validation @@ -75,8 +73,7 @@ struct cod_header uint8_t Reserved[12]; }; -struct cod_item -{ +struct cod_item { uint32_t DataFieldMapUid; //!The data field unique identifier value uint32_t reserved1 : 8; uint32_t dataFieldType : 8; @@ -90,8 +87,7 @@ struct cod_item uint8_t Reserved2[8]; }; -struct cod_map -{ +struct cod_map { struct cod_header header; struct cod_item items[]; }; @@ -100,8 +96,7 @@ struct cod_map void solidigm_telemetry_log_cod_parse(struct telemetry_log *tl) { - enum cod_field_type - { + enum cod_field_type { INTEGER, FLOAT, STRING, @@ -118,77 +113,78 @@ void solidigm_telemetry_log_cod_parse(struct telemetry_log *tl) return; if (!json_object_object_get_ex(telemetry_header, "reasonIdentifier", &reason_id)) return; - if (!json_object_object_get_ex(reason_id, "OemDataMapOffset", &COD_offset)) + if (!json_object_object_get_ex(reason_id, "oemDataMapOffset", &COD_offset)) return; - __u64 offset = json_object_get_int(COD_offset); + uint64_t offset = json_object_get_int(COD_offset); - if (offset == 0) { + if (!offset) return; - } if ((offset + sizeof(struct cod_header)) > tl->log_size) { SOLIDIGM_LOG_WARNING("Warning: COD map header out of bounds."); return; } - const struct cod_map *data = (struct cod_map *) (((__u8 *)tl->log ) + offset); + const struct cod_map *data = (struct cod_map *) (((uint8_t *)tl->log) + offset); uint32_t signature = be32_to_cpu(data->header.Signature); - if ( signature != OEMSIGNATURE){ + + if (signature != OEMSIGNATURE) { SOLIDIGM_LOG_WARNING("Warning: Unsupported COD data signature %x!", signature); return; } - if ((offset + data->header.MapSizeInBytes) > tl->log_size){ + if ((offset + data->header.MapSizeInBytes) > tl->log_size) { SOLIDIGM_LOG_WARNING("Warning: COD map data out of bounds."); return; } struct json_object *cod = json_create_object(); + json_object_object_add(tl->root, "cod", cod); - for (int i =0 ; i < data->header.EntryCount; i++) { + for (uint64_t i = 0; i < data->header.EntryCount; i++) { if ((offset + sizeof(struct cod_header) + (i + 1) * sizeof(struct cod_item)) > - tl->log_size){ - SOLIDIGM_LOG_WARNING("Warning: COD data out of bounds at item %d!", i); + tl->log_size) { + SOLIDIGM_LOG_WARNING("Warning: COD data out of bounds at item %"PRIu64"!", + i); return; } struct cod_item item = data->items[i]; - if (item.DataFieldOffset + item.DataFieldOffset > tl->log_size) { + + if (item.DataFieldOffset + item.DataFieldOffset > tl->log_size) continue; - } - if (item.dataInvalid) { + if (item.dataInvalid) continue; - } - uint8_t *val = ((uint8_t *)tl->log )+ item.DataFieldOffset; + uint8_t *val = ((uint8_t *)tl->log) + item.DataFieldOffset; const char *key = getOemDataMapDescription(item.DataFieldMapUid); - switch(item.dataFieldType){ - case(INTEGER): - if (item.issigned) { - json_object_object_add(cod, key, - json_object_new_int64(le64_to_cpu(*(uint64_t *)val))); - } else { - json_object_add_value_uint64(cod, key, le64_to_cpu(*(uint64_t *)val)); - } - break; - case(FLOAT): - json_object_add_value_float(cod, key, *(float *) val); - break; - case(STRING): - json_object_object_add(cod, key, - json_object_new_string_len((const char *)val, item.DataFieldSizeInBytes)); - break; - case(TWO_BYTE_ASCII): - json_object_object_add(cod, key, - json_object_new_string_len((const char *)val,2)); - break; - case(FOUR_BYTE_ASCII): + + switch (item.dataFieldType) { + case INTEGER: + if (item.issigned) json_object_object_add(cod, key, - json_object_new_string_len((const char *)val, 4)); - break; - default: - SOLIDIGM_LOG_WARNING("Warning: Unknown COD field type (%d)", item.DataFieldMapUid); - + json_object_new_int64(le64_to_cpu(*(uint64_t *)val))); + else + json_object_add_value_uint64(cod, key, le64_to_cpu(*(uint64_t *)val)); + break; + case FLOAT: + json_object_add_value_float(cod, key, *(float *)val); + break; + case STRING: + json_object_object_add(cod, key, + json_object_new_string_len((const char *)val, item.DataFieldSizeInBytes)); + break; + case TWO_BYTE_ASCII: + json_object_object_add(cod, key, + json_object_new_string_len((const char *)val, 2)); + break; + case FOUR_BYTE_ASCII: + json_object_object_add(cod, key, + json_object_new_string_len((const char *)val, 4)); + break; + default: + SOLIDIGM_LOG_WARNING("Warning: Unknown COD field type (%d)", item.DataFieldMapUid); + break; } } } diff --git a/plugins/solidigm/solidigm-telemetry/config.c b/plugins/solidigm/solidigm-telemetry/config.c index 5111703..cc2a8bb 100644 --- a/plugins/solidigm/solidigm-telemetry/config.c +++ b/plugins/solidigm/solidigm-telemetry/config.c @@ -4,13 +4,17 @@ * * Author: leonardo.da.cunha@solidigm.com */ -#include <stdbool.h> -#include "util/json.h" + #include <stdio.h> +#include <string.h> +#include "config.h" // max 16 bit unsigned integer nummber 65535 #define MAX_16BIT_NUM_AS_STRING_SIZE 6 +#define OBJ_NAME_PREFIX "UID_" +#define NLOG_OBJ_PREFIX OBJ_NAME_PREFIX "NLOG_" + static bool config_get_by_version(const struct json_object *obj, int version_major, int version_minor, struct json_object **value) { @@ -28,17 +32,45 @@ static bool config_get_by_version(const struct json_object *obj, int version_maj return value != NULL; } -bool solidigm_config_get_by_token_version(const struct json_object *obj, int token_id, +bool solidigm_config_get_struct_by_token_version(const struct json_object *config, int token_id, int version_major, int version_minor, struct json_object **value) { - struct json_object *token_obj = NULL; + struct json_object *token = NULL; char str_key[MAX_16BIT_NUM_AS_STRING_SIZE]; snprintf(str_key, sizeof(str_key), "%d", token_id); - if (!json_object_object_get_ex(obj, str_key, &token_obj)) + if (!json_object_object_get_ex(config, str_key, &token)) return false; - if (!config_get_by_version(token_obj, version_major, version_minor, value)) + if (!config_get_by_version(token, version_major, version_minor, value)) return false; return value != NULL; } + +const char *solidigm_config_get_nlog_obj_name(const struct json_object *config, uint32_t token) +{ + struct json_object *nlog_names = NULL; + struct json_object *obj_name; + char hex_header[STR_HEX32_SIZE]; + const char *name; + + if (!json_object_object_get_ex(config, "TELEMETRY_OBJECT_UIDS", &nlog_names)) + return NULL; + snprintf(hex_header, STR_HEX32_SIZE, "0x%08X", token); + + if (!json_object_object_get_ex(nlog_names, hex_header, &obj_name)) + return NULL; + name = json_object_get_string(obj_name); + if (strncmp(NLOG_OBJ_PREFIX, name, strlen(NLOG_OBJ_PREFIX))) + return NULL; + + return &name[strlen(OBJ_NAME_PREFIX)]; +} + +struct json_object *solidigm_config_get_nlog_formats(const struct json_object *config) +{ + struct json_object *nlog_formats = NULL; + + json_object_object_get_ex(config, "NLOG_FORMATS", &nlog_formats); + return nlog_formats; +} diff --git a/plugins/solidigm/solidigm-telemetry/config.h b/plugins/solidigm/solidigm-telemetry/config.h index 30e61ff..4e56ba3 100644 --- a/plugins/solidigm/solidigm-telemetry/config.h +++ b/plugins/solidigm/solidigm-telemetry/config.h @@ -7,7 +7,13 @@ #include <stdbool.h> #include "util/json.h" -bool solidigm_config_get_by_token_version(const struct json_object *obj, +#define STR_HEX32_SIZE sizeof("0x00000000") + +bool solidigm_config_get_struct_by_token_version(const struct json_object *obj, int key, int subkey, int subsubkey, struct json_object **value); + +const char *solidigm_config_get_nlog_obj_name(const struct json_object *config, uint32_t token); +struct json_object *solidigm_config_get_nlog_formats(const struct json_object *config); + diff --git a/plugins/solidigm/solidigm-telemetry/data-area.c b/plugins/solidigm/solidigm-telemetry/data-area.c index 2f18ea2..0cfa56c 100644 --- a/plugins/solidigm/solidigm-telemetry/data-area.c +++ b/plugins/solidigm/solidigm-telemetry/data-area.c @@ -6,25 +6,29 @@ */ #include "common.h" +#include "header.h" +#include "cod.h" #include "data-area.h" #include "config.h" +#include "nlog.h" #include <ctype.h> #define SIGNED_INT_PREFIX "int" #define BITS_IN_BYTE 8 #define MAX_WARNING_SIZE 1024 +#define MAX_ARRAY_RANK 16 static bool telemetry_log_get_value(const struct telemetry_log *tl, - uint32_t offset_bit, uint32_t size_bit, + uint64_t offset_bit, uint32_t size_bit, bool is_signed, struct json_object **val_obj) { uint32_t offset_bit_from_byte; uint32_t additional_size_byte; uint32_t offset_byte; - uint32_t val; + uint64_t val; - if (size_bit == 0) { + if (!size_bit) { char err_msg[MAX_WARNING_SIZE]; snprintf(err_msg, MAX_WARNING_SIZE, @@ -34,7 +38,7 @@ static bool telemetry_log_get_value(const struct telemetry_log *tl, return false; } additional_size_byte = (size_bit - 1) ? (size_bit - 1) / BITS_IN_BYTE : 0; - offset_byte = offset_bit / BITS_IN_BYTE; + offset_byte = (uint32_t)offset_bit / BITS_IN_BYTE; if (offset_byte > (tl->log_size - additional_size_byte)) { char err_msg[MAX_WARNING_SIZE]; @@ -47,15 +51,14 @@ static bool telemetry_log_get_value(const struct telemetry_log *tl, return false; } - offset_bit_from_byte = offset_bit - (offset_byte * BITS_IN_BYTE); + offset_bit_from_byte = (uint32_t) (offset_bit - ((uint64_t)offset_byte * BITS_IN_BYTE)); if ((size_bit + offset_bit_from_byte) > (sizeof(uint64_t) * BITS_IN_BYTE)) { char err_msg[MAX_WARNING_SIZE]; snprintf(err_msg, MAX_WARNING_SIZE, - "Value crossing 64 bit, byte aligned bounday, " - "not supported. size_bit=%u, offset_bit_from_byte=%u.", - size_bit, offset_bit_from_byte); + "Value crossing 64 bit, byte aligned bounday, not supported. size_bit=%u, offset_bit_from_byte=%u.", + size_bit, offset_bit_from_byte); *val_obj = json_object_new_string(err_msg); return false; @@ -67,7 +70,7 @@ static bool telemetry_log_get_value(const struct telemetry_log *tl, val &= (1ULL << size_bit) - 1; if (is_signed) { if (val >> (size_bit - 1)) - val |= -1ULL << size_bit; + val |= (0ULL - 1) << size_bit; *val_obj = json_object_new_int64(val); } else { *val_obj = json_object_new_uint64(val); @@ -78,23 +81,24 @@ static bool telemetry_log_get_value(const struct telemetry_log *tl, static int telemetry_log_structure_parse(const struct telemetry_log *tl, struct json_object *struct_def, - size_t parent_offset_bit, + uint64_t parent_offset_bit, struct json_object *output, struct json_object *metadata) { struct json_object *obj_arraySizeArray = NULL; struct json_object *obj = NULL; struct json_object *obj_memberList; - struct json_object *major_dimension; + struct json_object *major_dimension = NULL; struct json_object *sub_output; bool is_enumeration = false; bool has_member_list; const char *type = ""; const char *name; size_t array_rank; - size_t offset_bit; - size_t size_bit; - uint32_t linear_array_pos_bit; + uint64_t offset_bit; + uint32_t size_bit; + uint64_t linear_array_pos_bit; + uint32_t array_size_dimension[MAX_ARRAY_RANK]; if (!json_object_object_get_ex(struct_def, "name", &obj)) { SOLIDIGM_LOG_WARNING("Warning: Structure definition missing property 'name': %s", @@ -113,22 +117,22 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, type = json_object_get_string(obj); if (!json_object_object_get_ex(struct_def, "offsetBit", &obj)) { - SOLIDIGM_LOG_WARNING("Warning: Structure definition missing " - "property 'offsetBit': %s", - json_object_to_json_string(struct_def)); + SOLIDIGM_LOG_WARNING( + "Warning: Structure definition missing property 'offsetBit': %s", + json_object_to_json_string(struct_def)); return -1; } offset_bit = json_object_get_uint64(obj); if (!json_object_object_get_ex(struct_def, "sizeBit", &obj)) { - SOLIDIGM_LOG_WARNING("Warning: Structure definition missing " - "property 'sizeBit': %s", - json_object_to_json_string(struct_def)); + SOLIDIGM_LOG_WARNING( + "Warning: Structure definition missing property 'sizeBit': %s", + json_object_to_json_string(struct_def)); return -1; } - size_bit = json_object_get_uint64(obj); + size_bit = (uint32_t)json_object_get_uint64(obj); if (json_object_object_get_ex(struct_def, "enum", &obj)) is_enumeration = json_object_get_boolean(obj); @@ -139,25 +143,30 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, if (!json_object_object_get_ex(struct_def, "arraySize", &obj_arraySizeArray)) { - SOLIDIGM_LOG_WARNING("Warning: Structure definition missing " - "property 'arraySize': %s", - json_object_to_json_string(struct_def)); + SOLIDIGM_LOG_WARNING( + "Warning: Structure definition missing property 'arraySize': %s", + json_object_to_json_string(struct_def)); return -1; } array_rank = json_object_array_length(obj_arraySizeArray); - if (array_rank == 0) { - SOLIDIGM_LOG_WARNING("Warning: Structure property 'arraySize' " - "don't support flexible array: %s", - json_object_to_json_string(struct_def)); + if (!array_rank) { + SOLIDIGM_LOG_WARNING( + "Warning: Structure property 'arraySize' don't support flexible array: %s", + json_object_to_json_string(struct_def)); + return -1; + } + if (array_rank > MAX_ARRAY_RANK) { + SOLIDIGM_LOG_WARNING( + "Warning: Structure property 'arraySize' don't support more than %d dimensions: %s", + MAX_ARRAY_RANK, json_object_to_json_string(struct_def)); return -1; } - uint32_t array_size_dimension[array_rank]; for (size_t i = 0; i < array_rank; i++) { struct json_object *dimension = json_object_array_get_idx(obj_arraySizeArray, i); - array_size_dimension[i] = json_object_get_uint64(dimension); + array_size_dimension[i] = json_object_get_int(dimension); major_dimension = dimension; } if (array_rank > 1) { @@ -165,7 +174,7 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, uint32_t prev_index_offset_bit = 0; struct json_object *dimension_output; - for (int i = 1; i < (array_rank - 1); i++) + for (unsigned int i = 1; i < (array_rank - 1); i++) linear_pos_per_index *= array_size_dimension[i]; dimension_output = json_create_array(); @@ -181,9 +190,9 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, json_object_get(major_dimension); json_object_array_del_idx(obj_arraySizeArray, array_rank - 1, 1); - for (int i = 0 ; i < array_size_dimension[0]; i++) { + for (unsigned int i = 0 ; i < array_size_dimension[0]; i++) { struct json_object *sub_array = json_create_array(); - size_t offset; + uint64_t offset; offset = parent_offset_bit + prev_index_offset_bit; @@ -214,7 +223,7 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, if (is_enumeration || !has_member_list) { bool is_signed = !strncmp(type, SIGNED_INT_PREFIX, sizeof(SIGNED_INT_PREFIX)-1); struct json_object *val_obj; - size_t offset; + uint64_t offset; offset = parent_offset_bit + offset_bit + linear_array_pos_bit; if (telemetry_log_get_value(tl, offset, size_bit, is_signed, &val_obj)) { @@ -223,10 +232,10 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, else json_object_object_add(sub_output, name, val_obj); } else { - SOLIDIGM_LOG_WARNING("Warning: %s From property '%s', " - "array index %u, structure definition: %s", - json_object_get_string(val_obj), - name, j, json_object_to_json_string(struct_def)); + SOLIDIGM_LOG_WARNING( + "Warning: %s From property '%s', array index %u, structure definition: %s", + json_object_get_string(val_obj), name, j, + json_object_to_json_string(struct_def)); json_free_object(val_obj); } } else { @@ -241,7 +250,7 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, num_members = json_object_array_length(obj_memberList); for (int k = 0; k < num_members; k++) { struct json_object *member = json_object_array_get_idx(obj_memberList, k); - size_t offset; + uint64_t offset; offset = parent_offset_bit + offset_bit + linear_array_pos_bit; telemetry_log_structure_parse(tl, member, offset, @@ -293,6 +302,27 @@ static int telemetry_log_data_area_get_offset(const struct telemetry_log *tl, return 0; } +static int telemetry_log_nlog_parse(const struct telemetry_log *tl, struct json_object *formats, + uint64_t nlog_file_offset, uint64_t nlog_size, + struct json_object *output, struct json_object *metadata) +{ + /* boundary check */ + if (tl->log_size < (nlog_file_offset + nlog_size)) { + const char *name = ""; + int media_bank = -1; + struct json_object *jobj; + + if (json_object_object_get_ex(metadata, "objName", &jobj)) + name = json_object_get_string(jobj); + if (json_object_object_get_ex(metadata, "mediaBankId", &jobj)) + media_bank = json_object_get_int(jobj); + SOLIDIGM_LOG_WARNING("%s:%d do not fit this log dump.", name, media_bank); + return -1; + } + return solidigm_nlog_parse(((char *) tl->log) + nlog_file_offset, + nlog_size, formats, metadata, output); +} + struct toc_item { uint32_t OffsetBytes; uint32_t ContentSizeBytes; @@ -319,7 +349,6 @@ struct telemetry_object_header { uint8_t Reserved[3]; }; - static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, enum nvme_telemetry_da da, struct json_object *toc_array, @@ -331,30 +360,35 @@ static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, char *payload; uint32_t da_offset; uint32_t da_size; + struct json_object *nlog_formats; if (telemetry_log_data_area_get_offset(tl, da, &da_offset, &da_size)) return; toc = (struct table_of_contents *)(((char *)tl->log) + da_offset); payload = (char *) tl->log; + nlog_formats = solidigm_config_get_nlog_formats(tl->configuration); for (int i = 0; i < toc->header.TableOfContentsCount; i++) { struct json_object *structure_definition = NULL; struct json_object *toc_item; uint32_t obj_offset; bool has_struct; - - if ((char *)&toc->items[i] > (((char *)toc) + da_size - sizeof(const struct toc_item))) { - SOLIDIGM_LOG_WARNING("Warning: Data Area %d, " - "Table of Contents item %d " - "crossed Data Area size.", da, i); + const char *nlog_name = NULL; + uint32_t header_offset = sizeof(const struct telemetry_object_header); + + if ((char *)&toc->items[i] > + (((char *)toc) + da_size - sizeof(const struct toc_item))) { + SOLIDIGM_LOG_WARNING( + "Warning: Data Area %d, Table of Contents item %d crossed Data Area size.", + da, i); return; } obj_offset = toc->items[i].OffsetBytes; if ((obj_offset + sizeof(const struct telemetry_object_header)) > da_size) { - SOLIDIGM_LOG_WARNING("Warning: Data Area %d, item %d " - "data, crossed Data Area size.", da, i); + SOLIDIGM_LOG_WARNING( + "Warning: Data Area %d, item %d data, crossed Data Area size.", da, i); continue; } @@ -372,53 +406,67 @@ static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, json_object_add_value_uint(toc_item, "objectId", header->Token); json_object_add_value_uint(toc_item, "mediaBankId", header->CoreId); - has_struct = solidigm_config_get_by_token_version(tl->configuration, - header->Token, - header->versionMajor, - header->versionMinor, - &structure_definition); - - if (has_struct) { - struct json_object *tele_obj_item = json_create_object(); + has_struct = solidigm_config_get_struct_by_token_version(tl->configuration, + header->Token, + header->versionMajor, + header->versionMinor, + &structure_definition); + if (!has_struct) { + if (!nlog_formats) + continue; + nlog_name = solidigm_config_get_nlog_obj_name(tl->configuration, + header->Token); + if (!nlog_name) + continue; + } + struct json_object *tele_obj_item = json_create_object(); - json_object_array_add(tele_obj_array, tele_obj_item); - json_object_get(toc_item); - json_object_add_value_object(tele_obj_item, "metadata", toc_item); - struct json_object *parsed_struct = json_create_object(); + json_object_array_add(tele_obj_array, tele_obj_item); + json_object_get(toc_item); + json_object_add_value_object(tele_obj_item, "metadata", toc_item); + struct json_object *parsed_struct = json_create_object(); - json_object_add_value_object(tele_obj_item, "objectData", parsed_struct); - struct json_object *obj_hasTelemObjHdr = NULL; - uint32_t header_offset = sizeof(const struct telemetry_object_header); - uint32_t file_offset; + json_object_add_value_object(tele_obj_item, "objectData", parsed_struct); + struct json_object *obj_hasTelemObjHdr = NULL; + uint64_t object_file_offset; - if (json_object_object_get_ex(structure_definition, - "hasTelemObjHdr", - &obj_hasTelemObjHdr)) { - bool hasHeader = json_object_get_boolean(obj_hasTelemObjHdr); + if (json_object_object_get_ex(structure_definition, + "hasTelemObjHdr", + &obj_hasTelemObjHdr)) { + bool hasHeader = json_object_get_boolean(obj_hasTelemObjHdr); - if (hasHeader) - header_offset = 0; - } - - file_offset = da_offset + obj_offset + header_offset; + if (hasHeader) + header_offset = 0; + } + object_file_offset = ((uint64_t)da_offset) + obj_offset + header_offset; + if (has_struct) { telemetry_log_structure_parse(tl, structure_definition, - BITS_IN_BYTE * file_offset, - parsed_struct, toc_item); + BITS_IN_BYTE * object_file_offset, + parsed_struct, toc_item); + } else if (nlog_formats) { + json_object_object_add(toc_item, "objName", + json_object_new_string(nlog_name)); + telemetry_log_nlog_parse(tl, nlog_formats, object_file_offset, + toc->items[i].ContentSizeBytes - header_offset, + parsed_struct, toc_item); } } } -int solidigm_telemetry_log_data_areas_parse(const struct telemetry_log *tl, +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(); - 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++) - telemetry_log_data_area_toc_parse(tl, da, toc_array, tele_obj_array); + solidigm_telemetry_log_header_parse(tl); + solidigm_telemetry_log_cod_parse(tl); + if (tl->configuration) { + 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++) + telemetry_log_data_area_toc_parse(tl, da, toc_array, tele_obj_array); + } return 0; } diff --git a/plugins/solidigm/solidigm-telemetry/data-area.h b/plugins/solidigm/solidigm-telemetry/data-area.h index 095eb64..6b690d8 100644 --- a/plugins/solidigm/solidigm-telemetry/data-area.h +++ b/plugins/solidigm/solidigm-telemetry/data-area.h @@ -4,8 +4,7 @@ * * Author: leonardo.da.cunha@solidigm.com */ -#include "common.h" #include "telemetry-log.h" -int solidigm_telemetry_log_data_areas_parse(const struct telemetry_log *tl, +int solidigm_telemetry_log_data_areas_parse(struct telemetry_log *tl, enum nvme_telemetry_da last_da); diff --git a/plugins/solidigm/solidigm-telemetry/header.c b/plugins/solidigm/solidigm-telemetry/header.c index d085c24..866ebff 100644 --- a/plugins/solidigm/solidigm-telemetry/header.c +++ b/plugins/solidigm/solidigm-telemetry/header.c @@ -9,8 +9,7 @@ #include "header.h" #pragma pack(push, reason_indentifier, 1) -struct reason_indentifier_1_0 -{ +struct reason_indentifier_1_0 { uint16_t versionMajor; uint16_t versionMinor; uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue. @@ -24,8 +23,7 @@ static_assert(sizeof(const struct reason_indentifier_1_0) == MEMBER_SIZE(struct nvme_telemetry_log, rsnident), "Size mismatch for reason_indentifier_1_0"); -struct reason_indentifier_1_1 -{ +struct reason_indentifier_1_1 { uint16_t versionMajor; uint16_t versionMinor; uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue. @@ -42,8 +40,7 @@ static_assert(sizeof(const struct reason_indentifier_1_1) == MEMBER_SIZE(struct nvme_telemetry_log, rsnident), "Size mismatch for reason_indentifier_1_1"); -struct reason_indentifier_1_2 -{ +struct reason_indentifier_1_2 { uint16_t versionMajor; uint16_t versionMinor; uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue. @@ -69,14 +66,21 @@ static void telemetry_log_reason_id_parse1_0_ext(const struct telemetry_log *tl, struct json_object *reserved; ri = (struct reason_indentifier_1_0 *) tl->log->rsnident; - json_object_object_add(reason_id, "FirmwareVersion", json_object_new_string_len(ri->FirmwareVersion, sizeof(ri->FirmwareVersion))); - json_object_object_add(reason_id, "BootloaderVersion", json_object_new_string_len(ri->BootloaderVersion, sizeof(ri->BootloaderVersion))); - json_object_object_add(reason_id, "SerialNumber", json_object_new_string_len(ri->SerialNumber, sizeof(ri->SerialNumber))); + json_object_object_add(reason_id, "firmwareVersion", + json_object_new_string_len(ri->FirmwareVersion, + sizeof(ri->FirmwareVersion))); + json_object_object_add(reason_id, "bootloaderVersion", + json_object_new_string_len(ri->BootloaderVersion, + sizeof(ri->BootloaderVersion))); + json_object_object_add(reason_id, "serialNumber", + json_object_new_string_len(ri->SerialNumber, + sizeof(ri->SerialNumber))); reserved = json_create_array(); - json_object_add_value_array(reason_id, "Reserved", reserved); - for ( int i=0; i < sizeof(ri->Reserved); i++) { + json_object_add_value_array(reason_id, "reserved", reserved); + for (int i = 0; i < sizeof(ri->Reserved); i++) { struct json_object *val = json_object_new_int(ri->Reserved[i]); + json_object_array_add(reserved, val); } } @@ -88,17 +92,27 @@ static void telemetry_log_reason_id_parse1_1_ext(const struct telemetry_log *tl, struct json_object *reserved; ri = (struct reason_indentifier_1_1 *) tl->log->rsnident; - json_object_object_add(reason_id, "FirmwareVersion", json_object_new_string_len(ri->FirmwareVersion, sizeof(ri->FirmwareVersion))); - json_object_object_add(reason_id, "BootloaderVersion", json_object_new_string_len(ri->BootloaderVersion, sizeof(ri->BootloaderVersion))); - json_object_object_add(reason_id, "SerialNumber", json_object_new_string_len(ri->SerialNumber, sizeof(ri->SerialNumber))); - json_object_add_value_uint64(reason_id, "OemDataMapOffset", le64_to_cpu(ri->OemDataMapOffset)); - json_object_add_value_uint(reason_id, "TelemetryMajorVersion", le16_to_cpu(ri->TelemetryMajorVersion)); - json_object_add_value_uint(reason_id, "TelemetryMinorVersion", le16_to_cpu(ri->TelemetryMinorVersion)); + json_object_object_add(reason_id, "firmwareVersion", + json_object_new_string_len(ri->FirmwareVersion, + sizeof(ri->FirmwareVersion))); + json_object_object_add(reason_id, "bootloaderVersion", + json_object_new_string_len(ri->BootloaderVersion, + sizeof(ri->BootloaderVersion))); + json_object_object_add(reason_id, "serialNumber", + json_object_new_string_len(ri->SerialNumber, + sizeof(ri->SerialNumber))); + json_object_add_value_uint64(reason_id, "oemDataMapOffset", + le64_to_cpu(ri->OemDataMapOffset)); + json_object_add_value_uint(reason_id, "telemetryMajorVersion", + le16_to_cpu(ri->TelemetryMajorVersion)); + json_object_add_value_uint(reason_id, "telemetryMinorVersion", + le16_to_cpu(ri->TelemetryMinorVersion)); reserved = json_create_array(); - json_object_add_value_array(reason_id, "Reserved", reserved); + json_object_add_value_array(reason_id, "reserved", reserved); for (int i = 0; i < sizeof(ri->Reserved); i++) { struct json_object *val = json_object_new_int(ri->Reserved[i]); + json_object_array_add(reserved, val); } } @@ -112,23 +126,30 @@ static void telemetry_log_reason_id_parse1_2_ext(const struct telemetry_log *tl, ri = (struct reason_indentifier_1_2 *) tl->log->rsnident; - json_object_object_add(reason_id, "SerialNumber", json_object_new_string_len(ri->SerialNumber, sizeof(ri->SerialNumber))); - json_object_add_value_uint64(reason_id, "OemDataMapOffset", le64_to_cpu(ri->OemDataMapOffset)); - json_object_add_value_uint(reason_id, "TelemetryMajorVersion", le16_to_cpu(ri->TelemetryMajorVersion)); - json_object_add_value_uint(reason_id, "TelemetryMinorVersion", le16_to_cpu(ri->TelemetryMinorVersion)); - json_object_add_value_uint(reason_id, "ProductFamilyId", ri->ProductFamilyId); + json_object_object_add(reason_id, "serialNumber", + json_object_new_string_len(ri->SerialNumber, + sizeof(ri->SerialNumber))); + json_object_add_value_uint64(reason_id, "oemDataMapOffset", + le64_to_cpu(ri->OemDataMapOffset)); + json_object_add_value_uint(reason_id, "telemetryMajorVersion", + le16_to_cpu(ri->TelemetryMajorVersion)); + json_object_add_value_uint(reason_id, "telemetryMinorVersion", + le16_to_cpu(ri->TelemetryMinorVersion)); + json_object_add_value_uint(reason_id, "productFamilyId", ri->ProductFamilyId); reserved = json_create_array(); - json_object_add_value_array(reason_id, "Reserved2", reserved); + json_object_add_value_array(reason_id, "reserved2", reserved); for (int i = 0; i < sizeof(ri->Reserved2); i++) { struct json_object *val = json_object_new_int(ri->Reserved2[i]); + json_object_array_add(reserved, val); } dp_reserved = json_create_array(); - json_object_add_value_array(reason_id, "DualPortReserved", dp_reserved); + json_object_add_value_array(reason_id, "dualPortReserved", dp_reserved); for (int i = 0; i < sizeof(ri->DualPortReserved); i++) { struct json_object *val = json_object_new_int(ri->DualPortReserved[i]); + json_object_array_add(dp_reserved, val); } } @@ -137,23 +158,26 @@ static void solidigm_telemetry_log_reason_id_parse(const struct telemetry_log *t { const struct reason_indentifier_1_0 *ri1_0 = (struct reason_indentifier_1_0 *) tl->log->rsnident; - __u16 version_major = le16_to_cpu(ri1_0->versionMajor); - __u16 version_minor = le16_to_cpu(ri1_0->versionMinor); + uint16_t version_major = le16_to_cpu(ri1_0->versionMajor); + uint16_t version_minor = le16_to_cpu(ri1_0->versionMinor); json_object_add_value_uint(reason_id, "versionMajor", version_major); json_object_add_value_uint(reason_id, "versionMinor", version_minor); json_object_add_value_uint(reason_id, "reasonCode", le32_to_cpu(ri1_0->reasonCode)); - json_object_add_value_object(reason_id, "DriveStatus", json_object_new_string_len(ri1_0->DriveStatus, sizeof(ri1_0->DriveStatus))); + json_object_add_value_object(reason_id, "driveStatus", + json_object_new_string_len(ri1_0->DriveStatus, + sizeof(ri1_0->DriveStatus))); if (version_major == 1) { switch (version_minor) { - case 0: - telemetry_log_reason_id_parse1_0_ext(tl, reason_id); - break; - case 1: - telemetry_log_reason_id_parse1_1_ext(tl, reason_id); - break; - default: - telemetry_log_reason_id_parse1_2_ext(tl, reason_id); + case 0: + telemetry_log_reason_id_parse1_0_ext(tl, reason_id); + break; + case 1: + telemetry_log_reason_id_parse1_1_ext(tl, reason_id); + break; + default: + telemetry_log_reason_id_parse1_2_ext(tl, reason_id); + break; } } } diff --git a/plugins/solidigm/solidigm-telemetry/meson.build b/plugins/solidigm/solidigm-telemetry/meson.build index 53ab452..96273b7 100644 --- a/plugins/solidigm/solidigm-telemetry/meson.build +++ b/plugins/solidigm/solidigm-telemetry/meson.build @@ -3,4 +3,5 @@ sources += [ 'plugins/solidigm/solidigm-telemetry/header.c', 'plugins/solidigm/solidigm-telemetry/config.c', 'plugins/solidigm/solidigm-telemetry/data-area.c', + 'plugins/solidigm/solidigm-telemetry/nlog.c', ] diff --git a/plugins/solidigm/solidigm-telemetry/nlog.c b/plugins/solidigm/solidigm-telemetry/nlog.c new file mode 100644 index 0000000..43b8918 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/nlog.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "nlog.h" +#include "config.h" +#include <string.h> +#include <math.h> +#include <stdio.h> + +#define LOG_ENTRY_HEADER_SIZE 1 +#define LOG_ENTRY_TIMESTAMP_SIZE 2 +#define LOG_ENTRY_NUM_ARGS_MAX 8 +#define LOG_ENTRY_MAX_SIZE (LOG_ENTRY_HEADER_SIZE + LOG_ENTRY_TIMESTAMP_SIZE + \ + LOG_ENTRY_NUM_ARGS_MAX) +#define NUM_ARGS_MASK ((1 << ((int)log2(LOG_ENTRY_NUM_ARGS_MAX)+1)) - 1) +#define MAX_HEADER_MISMATCH_TRACK 10 + +static int formats_find(struct json_object *formats, uint32_t val, struct json_object **format) +{ + char hex_header[STR_HEX32_SIZE]; + + snprintf(hex_header, STR_HEX32_SIZE, "0x%08X", val); + return json_object_object_get_ex(formats, hex_header, format); +} + +static uint32_t nlog_get_pos(const uint32_t *nlog, const uint32_t nlog_size, int pos) +{ + return nlog[pos % nlog_size]; +} + +static uint32_t nlog_get_events(const uint32_t *nlog, const uint32_t nlog_size, int start_offset, + struct json_object *formats, struct json_object *events, uint32_t *tail_mismatches) +{ + uint32_t event_count = 0; + int last_bad_header_pos = nlog_size + 1; // invalid nlog offset + uint32_t tail_count = 0; + + for (int i = nlog_size - start_offset - 1; i >= -start_offset; i--) { + struct json_object *format; + uint32_t header = nlog_get_pos(nlog, nlog_size, i); + uint32_t num_data; + + if (header == 0 || !formats_find(formats, header, &format)) { + if (event_count > 0) { + //check if fould circular buffer tail + if (i != (last_bad_header_pos - 1)) { + if (tail_mismatches && + (tail_count < MAX_HEADER_MISMATCH_TRACK)) + tail_mismatches[tail_count] = header; + tail_count++; + } + last_bad_header_pos = i; + } + continue; + } + num_data = header & NUM_ARGS_MASK; + if (events) { + struct json_object *event = json_object_new_array(); + struct json_object *param = json_object_new_array(); + uint32_t val = nlog_get_pos(nlog, nlog_size, i - 1); + + json_object_array_add(events, event); + json_object_array_add(event, json_object_new_int64(val)); + val = nlog_get_pos(nlog, nlog_size, i - 2); + json_object_array_add(event, json_object_new_int64(val)); + json_object_array_add(event, json_object_new_int64(header)); + json_object_array_add(event, param); + for (uint32_t j = 0; j < num_data; j++) { + val = nlog_get_pos(nlog, nlog_size, i - 3 - j); + json_object_array_add(param, json_object_new_int64(val)); + } + json_object_get(format); + json_object_array_add(event, format); + } + i -= 2 + num_data; + event_count++; + } + return tail_count; +} + +int solidigm_nlog_parse(const char *buffer, uint64_t buff_size, struct json_object *formats, + struct json_object *metadata, struct json_object *output) +{ + uint32_t smaller_tail_count = UINT32_MAX; + int best_offset = 0; + uint32_t offset_tail_mismatches[LOG_ENTRY_MAX_SIZE][MAX_HEADER_MISMATCH_TRACK]; + struct json_object *events = json_object_new_array(); + const uint32_t *nlog = (uint32_t *)buffer; + const uint32_t nlog_size = buff_size / sizeof(uint32_t); + + for (int i = 0; i < LOG_ENTRY_MAX_SIZE; i++) { + uint32_t tail_count = nlog_get_events(nlog, nlog_size, i, formats, NULL, + offset_tail_mismatches[i]); + if (tail_count < smaller_tail_count) { + best_offset = i; + smaller_tail_count = tail_count; + } + if (tail_count == 0) + break; + } + if (smaller_tail_count > 1) { + const char *name = ""; + int media_bank = -1; + char str_mismatches[(STR_HEX32_SIZE + 1) * MAX_HEADER_MISMATCH_TRACK]; + int pos = 0; + int show_mismatch_num = smaller_tail_count < MAX_HEADER_MISMATCH_TRACK ? + smaller_tail_count : MAX_HEADER_MISMATCH_TRACK; + struct json_object *jobj; + + if (json_object_object_get_ex(metadata, "objName", &jobj)) + name = json_object_get_string(jobj); + if (json_object_object_get_ex(metadata, "mediaBankId", &jobj)) + media_bank = json_object_get_int(jobj); + + for (int i = 0; i < show_mismatch_num; i++) + pos += snprintf(&str_mismatches[pos], STR_HEX32_SIZE + 1, "0x%08X ", + offset_tail_mismatches[best_offset][i]); + + SOLIDIGM_LOG_WARNING("%s:%d with %d header mismatches ( %s). Configuration file may be missing format headers.", + name, media_bank, smaller_tail_count, str_mismatches); + } + nlog_get_events(nlog, nlog_size, best_offset, formats, events, NULL); + + json_object_object_add(output, "events", events); + return 0; +} diff --git a/plugins/solidigm/solidigm-telemetry/nlog.h b/plugins/solidigm/solidigm-telemetry/nlog.h new file mode 100644 index 0000000..f33aa45 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/nlog.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2023 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ +#include "telemetry-log.h" + +int solidigm_nlog_parse(const char *buffer, uint64_t bufer_size, + struct json_object *formats, struct json_object *metadata, + struct json_object *output); |