diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-29 04:41:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-29 04:41:05 +0000 |
commit | 1cc8413aaf5f8fa6595aece1933462c096e88639 (patch) | |
tree | e97b4f25c511372d73bdd96c389c5f468d99138a /plugins/solidigm | |
parent | Initial commit. (diff) | |
download | nvme-cli-1cc8413aaf5f8fa6595aece1933462c096e88639.tar.xz nvme-cli-1cc8413aaf5f8fa6595aece1933462c096e88639.zip |
Adding upstream version 2.4+really2.3.upstream/2.4+really2.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/solidigm')
21 files changed, 2078 insertions, 0 deletions
diff --git a/plugins/solidigm/meson.build b/plugins/solidigm/meson.build new file mode 100644 index 0000000..bca13bb --- /dev/null +++ b/plugins/solidigm/meson.build @@ -0,0 +1,7 @@ +sources += [ + 'plugins/solidigm/solidigm-smart.c', + 'plugins/solidigm/solidigm-garbage-collection.c', + 'plugins/solidigm/solidigm-latency-tracking.c', + 'plugins/solidigm/solidigm-telemetry.c', +] +subdir('solidigm-telemetry') diff --git a/plugins/solidigm/solidigm-garbage-collection.c b/plugins/solidigm/solidigm-garbage-collection.c new file mode 100644 index 0000000..8e2eccc --- /dev/null +++ b/plugins/solidigm/solidigm-garbage-collection.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "linux/types.h" +#include "nvme-print.h" +#include "solidigm-garbage-collection.h" + +typedef struct __attribute__((packed)) gc_item { + __le32 timer_type; + __le64 timestamp; +} gc_item_t; + +#define VU_GC_MAX_ITEMS 100 +typedef struct garbage_control_collection_log { + __le16 version_major; + __le16 version_minor; + gc_item_t 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) +{ + 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 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); + } + + json_print_object(gc_entries, NULL); + json_free_object(gc_entries); +} + +static void vu_gc_log_show(garbage_control_collection_log_t *payload, const char *devname) +{ + printf("Solidigm Garbage Collection Log for NVME device: %s\n", devname); + printf("Timestamp Timer Type\n"); + + for (int i = 0; i < VU_GC_MAX_ITEMS; i++) { + gc_item_t item = payload->item[i]; + printf("%-13" PRIu64 " %d\n", le64_to_cpu(item.timestamp), le32_to_cpu(item.timer_type)); + } +} + +int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Get and parse Solidigm vendor specific garbage collection event log."; + struct nvme_dev *dev; + int err; + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + 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; + } + + garbage_control_collection_log_t gc_log; + const int solidigm_vu_gc_log_id = 0xfd; + + err = nvme_get_log_simple(dev_fd(dev), solidigm_vu_gc_log_id, + sizeof(gc_log), &gc_log); + if (!err) { + if (flags & BINARY) { + d_raw((unsigned char *)&gc_log, sizeof(gc_log)); + } else if (flags & JSON) { + vu_gc_log_show_json(&gc_log, dev->name); + } else { + vu_gc_log_show(&gc_log, dev->name); + } + } + 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-garbage-collection.h b/plugins/solidigm/solidigm-garbage-collection.h new file mode 100644 index 0000000..a3e34b2 --- /dev/null +++ b/plugins/solidigm/solidigm-garbage-collection.h @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +int solidigm_get_garbage_collection_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 new file mode 100644 index 0000000..1013ae8 --- /dev/null +++ b/plugins/solidigm/solidigm-latency-tracking.c @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "linux/types.h" +#include "nvme-print.h" + +#define BUCKET_LIST_SIZE_4_0 152 +#define BUCKET_LIST_SIZE_4_1 1216 + +#define BASE_RANGE_BITS_4_0 3 +#define BASE_RANGE_BITS_4_1 6 + +struct latency_statistics { + __u16 version_major; + __u16 version_minor; + __u32 data[BUCKET_LIST_SIZE_4_1]; + __u64 average_latency; +}; + +struct config { + bool enable; + bool disable; + bool read; + bool write; + unsigned char type; + char *output_format; +}; + +struct latency_tracker { + int fd; + struct config cfg; + enum nvme_print_flags print_flags; + struct latency_statistics stats; + struct json_object *bucket_list; + __u32 bucket_list_size; + __u8 base_range_bits; + bool has_average_latency_field; +}; + +/* COL_WIDTH controls width of columns in NORMAL output. */ +#define COL_WIDTH 12 +#define BUCKET_LABEL_MAX_SIZE 10 + +#define US_IN_S 1000000 +#define US_IN_MS 1000 + +/* + * Edge buckets may have range [#s, inf) in some + * latency statistics formats. + */ +static void get_time_unit_label(char *label, __u32 microseconds, + bool bonded) +{ + char *string = "us"; + int divisor = 1; + + if (!bonded) { + snprintf(label, BUCKET_LABEL_MAX_SIZE, "%s", "+INF"); + return; + } + + if (microseconds > US_IN_S) { + string = "s"; + divisor = US_IN_S; + } else if (microseconds > US_IN_MS) { + string = "ms"; + divisor = US_IN_MS; + } + + snprintf(label, BUCKET_LABEL_MAX_SIZE, "%4.2f%s", (float) microseconds / divisor, + string); +} + +static void latency_tracker_bucket_parse(const struct latency_tracker *lt, int id, + __u32 lower_us, __u32 upper_us, bool upper_bounded) +{ + char buffer[BUCKET_LABEL_MAX_SIZE] = ""; + __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); + printf("%-*s", COL_WIDTH, buffer); + + get_time_unit_label(buffer, upper_us, upper_bounded); + printf("%-*s", COL_WIDTH, buffer); + + printf("%-*d\n", COL_WIDTH, bucket_data); + } + + if (lt->print_flags == JSON) { + /* + * Creates a bucket under the "values" json_object. Format is: + * "values" : { + * "bucket" : { + * "id" : #, + * "start" : string, + * "end" : string, + * "value" : 0, + * }, + */ + struct json_object *bucket = json_create_object(); + + json_object_array_add(lt->bucket_list, bucket); + json_object_add_value_int(bucket, "id", id); + + get_time_unit_label(buffer, lower_us, true); + json_object_add_value_string(bucket, "start", buffer); + + get_time_unit_label(buffer, upper_us, upper_bounded); + json_object_add_value_string(bucket, "end", buffer); + + json_object_add_value_int(bucket, "value", bucket_data); + } +} + +static void latency_tracker_parse_linear(const struct latency_tracker *lt, + __u32 start_offset, __u32 end_offset, + __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) + continue; + latency_tracker_bucket_parse(lt, i, us_step * i, + us_step * (i + 1), true); + } +} + +/* + * Calculates bucket time slot. Valid starting on 4.0 revision. + */ + +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; + + int error_bits = (i >> lt->base_range_bits) - 1; + int base = 1 << (error_bits + lt->base_range_bits); + int k = i % base_val; + + return base + ((k + 0.5) * (1 << error_bits)); +} + +/* + * Creates a subroot in the following manner: + * { + * "latstats" : { + * "type" : "write" or "read", + * "values" : { + */ +static void latency_tracker_populate_json_root(const struct latency_tracker *lt, + 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)); + } + json_object_add_value_object(subroot, "values", lt->bucket_list); +} + +static void latency_tracker_parse_3_0(const struct latency_tracker *lt) +{ + latency_tracker_parse_linear(lt, 4, 131, 4, 32, false); + latency_tracker_parse_linear(lt, 132, 255, 4, 1024, false); + latency_tracker_parse_linear(lt, 256, 379, 4, 32768, false); + latency_tracker_parse_linear(lt, 380, 383, 4, 32, true); + latency_tracker_parse_linear(lt, 384, 387, 4, 32, true); + latency_tracker_parse_linear(lt, 388, 391, 4, 32, true); +} + +static void latency_tracker_parse_4_0(const struct latency_tracker *lt) +{ + for (unsigned int i = 0; i < lt->bucket_list_size; i++) { + 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, + i < (lt->bucket_list_size - 1)); + } +} + +static void print_dash_separator() +{ + printf("--------------------------------------------------\n"); +} + +static void latency_tracker_pre_parse(struct latency_tracker *lt) +{ + if (lt->print_flags == NORMAL) { + printf("Solidigm IO %s Command Latency Tracking Statistics type %d\n", + lt->cfg.write ? "Write" : "Read", lt->cfg.type); + 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) { + 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) { + lt->bucket_list = json_object_new_array(); + } +} + +static void latency_tracker_post_parse(struct latency_tracker *lt) +{ + if (lt->print_flags == JSON) { + struct json_object *root = json_create_object(); + + latency_tracker_populate_json_root(lt, root); + json_print_object(root, NULL); + json_free_object(root); + printf("\n"); + } +} + +static void latency_tracker_parse(struct latency_tracker *lt) +{ + __u16 version_major = le16_to_cpu(lt->stats.version_major); + __u16 version_minor = le16_to_cpu(lt->stats.version_minor); + + switch (version_major) { + case 3: + latency_tracker_pre_parse(lt); + latency_tracker_parse_3_0(lt); + break; + case 4: + if (version_minor >= 8){ + lt->has_average_latency_field = true; + } + latency_tracker_pre_parse(lt); + if (version_minor == 0){ + lt->base_range_bits = BASE_RANGE_BITS_4_0; + lt->bucket_list_size = BUCKET_LIST_SIZE_4_0; + } + latency_tracker_parse_4_0(lt); + break; + default: + printf("Unsupported revision (%u.%u)\n", + version_major, version_minor); + break; + } + + latency_tracker_post_parse(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) +{ + struct nvme_get_features_args args_get = { + .args_size = sizeof(args_get), + .fd = lt->fd, + .fid = LATENCY_TRACKING_FID, + .nsid = 0, + .sel = 0, + .cdw11 = 0, + .uuidx = 0, + .data_len = LATENCY_TRACKING_FID_DATA_LEN, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = enabled, + }; + return nvme_get_features(&args_get); +} + +static int latency_tracking_enable(struct latency_tracker *lt) +{ + __u32 result; + int err; + + 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; + } + + struct nvme_set_features_args args_set = { + .args_size = sizeof(args_set), + .fd = lt->fd, + .fid = LATENCY_TRACKING_FID, + .nsid = 0, + .cdw11 = lt->cfg.enable, + .cdw12 = 0, + .save = 0, + .uuidx = 0, + .cdw15 = 0, + .data_len = LATENCY_TRACKING_FID_DATA_LEN, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_set_features(&args_set); + if (err > 0) { + nvme_show_status(err); + } else if (err < 0) { + perror("Enable latency tracking"); + fprintf(stderr, "Command failed while parsing.\n"); + } else { + if (lt->print_flags == NORMAL) { + printf("Successfully set enable bit for FID (0x%X) to %i.\n", + LATENCY_TRACKING_FID, lt->cfg.enable); + } + } + return err; +} + +#define READ_LOG_ID 0xc1 +#define WRITE_LOG_ID 0xc2 + +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)) + return 0; + + struct nvme_get_log_args args = { + .lpo = 0, + .result = NULL, + .log = <->stats, + .args_size = sizeof(args), + .fd = lt->fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = lt->cfg.write ? WRITE_LOG_ID : READ_LOG_ID, + .len = sizeof(lt->stats), + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = lt->cfg.type, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + + err = nvme_get_log(&args); + if (err) + return err; + + if (lt->print_flags & BINARY) + d_raw((unsigned char *)<->stats, + sizeof(lt->stats)); + else { + latency_tracker_parse(lt); + } + return err; +} + +int solidigm_get_latency_tracking_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Get and Parse Solidigm Latency Tracking Statistics log."; + struct nvme_dev *dev; + __u32 enabled; + int err; + + struct latency_tracker lt = { + .cfg = { + .output_format = "normal", + }, + .base_range_bits = BASE_RANGE_BITS_4_1, + .bucket_list_size = BUCKET_LIST_SIZE_4_1, + .has_average_latency_field = false, + }; + + OPT_ARGS(opts) = { + OPT_FLAG("enable", 'e', <.cfg.enable, "Enable Latency Tracking"), + OPT_FLAG("disable", 'd', <.cfg.disable, "Disable Latency Tracking"), + OPT_FLAG("read", 'r', <.cfg.read, "Get read statistics"), + OPT_FLAG("write", 'w', <.cfg.write, "Get write statistics"), + OPT_BYTE("type", 't', <.cfg.type, "Log type to get"), + OPT_FMT("output-format", 'o', <.cfg.output_format, output_format), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + lt.fd = dev_fd(dev); + + lt.print_flags = validate_output_format(lt.cfg.output_format); + if (lt.print_flags == -EINVAL) { + fprintf(stderr, "Invalid output format '%s'\n", lt.cfg.output_format); + dev_close(dev); + return EINVAL; + } + + if (lt.cfg.type > 0xf) { + fprintf(stderr, "Invalid Log type value '%d'\n", lt.cfg.type); + dev_close(dev); + 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; + } + + err = latency_tracking_enable(<); + if (err){ + dev_close(dev); + return err; + } + + err = latency_tracker_get_log(<); + if (err){ + dev_close(dev); + return err; + } + + if ((lt.cfg.read || lt.cfg.write || lt.cfg.enable || lt.cfg.disable)) { + dev_close(dev); + return 0; + } + + err = latency_tracking_is_enable(<, &enabled); + if (!err) { + if (lt.print_flags == JSON) { + struct json_object *root = json_create_object(); + 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 (FID 0x%X) is currently (%i).\n", + LATENCY_TRACKING_FID, enabled); + } + } else { + fprintf(stderr, "Could not read feature id 0xE2.\n"); + } + /* Redundant close() to make static code analysis happy */ + close(dev->direct.fd); + dev_close(dev); + return err; +} diff --git a/plugins/solidigm/solidigm-latency-tracking.h b/plugins/solidigm/solidigm-latency-tracking.h new file mode 100644 index 0000000..9a763a9 --- /dev/null +++ b/plugins/solidigm/solidigm-latency-tracking.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +int solidigm_get_latency_tracking_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 new file mode 100644 index 0000000..b547035 --- /dev/null +++ b/plugins/solidigm/solidigm-nvme.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "nvme.h" + +#define CREATE_CMD +#include "solidigm-nvme.h" + +#include "solidigm-smart.h" +#include "solidigm-garbage-collection.h" +#include "solidigm-latency-tracking.h" +#include "solidigm-telemetry.h" +#include "plugins/ocp/ocp-clear-fw-update-history.h" + +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_garbage_collection_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + return solidigm_get_garbage_collection_log(argc, argv, cmd, plugin); +} + +static int get_latency_tracking_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + return solidigm_get_latency_tracking_log(argc, argv, cmd, plugin); +} + +static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + return solidigm_get_telemetry_log(argc, argv, cmd, plugin); +} + +static int clear_fw_update_history(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return ocp_clear_fw_update_history(argc, argv, cmd, plugin); +} diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h new file mode 100644 index 0000000..778dbf9 --- /dev/null +++ b/plugins/solidigm/solidigm-nvme.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/solidigm/solidigm-nvme + +#if !defined(SOLIDIGM_NVME) || defined(CMD_HEADER_MULTI_READ) +#define SOLIDIGM_NVME + +#include "cmd.h" + +#define SOLIDIGM_PLUGIN_VERSION "0.7" + +PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_VERSION), + COMMAND_LIST( + ENTRY("smart-log-add", "Retrieve Solidigm SMART Log", get_additional_smart_log) + ENTRY("garbage-collect-log", "Retrieve Garbage Collection Log", get_garbage_collection_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) + ) +); + +#endif + +#include "define_cmd.h" diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c new file mode 100644 index 0000000..77c26ac --- /dev/null +++ b/plugins/solidigm/solidigm-smart.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "linux/types.h" +#include "nvme-print.h" + +#include "solidigm-smart.h" + +struct __attribute__((packed)) nvme_additional_smart_log_item { + __u8 id; + __u8 _kp[2]; + __u8 normalized; + __u8 _np; + union __attribute__((packed)) { + __u8 raw[6]; + struct __attribute__((packed)) wear_level { + __le16 min; + __le16 max; + __le16 avg; + } wear_level; + struct __attribute__((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; + +static char *id_to_name(__u8 id) +{ + switch (id) { + case 0x0D: + return "soft_ecc_error_rate"; + case 0x05: + return "relocatable_sector_count"; + case 0xAB: + return "program_fail_count"; + case 0xAC: + return "erase_fail_count"; + case 0xAD: + return "wear_leveling_count"; + case 0xAE: + return "unexpected_power_loss"; + case 0xB8: + return "e2e_error_detect_count"; + case 0xC7: + return "crc_error_count"; + case 0xE2: + return "media_wear_percentage"; + case 0xE3: + return "host_reads"; + case 0xE4: + return "timed_work_load"; + case 0xE5: + return "read_commands_in_flight_counter"; + case 0xE6: + return "write_commands_in_flight_counter"; + case 0xEA: + return "thermal_throttle_status"; + case 0xF0: + return "retry_buffer_overflow_counter"; + case 0xF3: + return "pll_lock_loss_counter"; + case 0xF4: + return "nand_bytes_written"; + case 0xF5: + return "host_bytes_written"; + case 0xF6: + return "host_context_wear_used"; + case 0xF7: + return "performance_status_indicator"; + case 0xF8: + return "media_bytes_read"; + case 0xF9: + return "available_fw_downgrades"; + case 0xFA: + return "host_read_collision_count"; + case 0xFB: + return "host_write_collision_count"; + case 0xFC: + return "xor_pass_count"; + case 0xFD: + return "xor_fail_count"; + case 0xFE: + return "xor_invoked_count"; + default: + return "unknown"; + } +} + +static void smart_log_item_print(smart_log_item_t *item) +{ + if (!item->id) { + return; + } + + printf("%#x %-45s %3d ", + item->id, id_to_name(item->id), item->normalized); + + switch (item->id) { + case 0xAD: + printf("min: %u, max: %u, avg: %u\n", + le16_to_cpu(item->wear_level.min), + le16_to_cpu(item->wear_level.max), + le16_to_cpu(item->wear_level.avg)); + return; + case 0xEA: + printf("%u%%, cnt: %u\n", + item->thermal_throttle.pct, + le32_to_cpu(item->thermal_throttle.count)); + return; + default: + printf("%"PRIu64"\n", int48_to_long(item->raw)); + } +} + +static void smart_log_item_add_json(smart_log_item_t *item, struct json_object *dev_stats) +{ + struct json_object *entry_stats = json_create_object(); + + if (!item->id) { + return; + } + + json_object_add_value_int(entry_stats, "normalized", item->normalized); + + switch (item->id) { + case 0xAD: + json_object_add_value_int(entry_stats, "min", le16_to_cpu(item->wear_level.min)); + json_object_add_value_int(entry_stats, "max", le16_to_cpu(item->wear_level.max)); + json_object_add_value_int(entry_stats, "avg", le16_to_cpu(item->wear_level.avg)); + break; + case 0xEA: + json_object_add_value_int(entry_stats, "percentage", item->thermal_throttle.pct); + json_object_add_value_int(entry_stats, "count", le32_to_cpu(item->thermal_throttle.count)); + break; + default: + json_object_add_value_int(entry_stats, "raw", int48_to_long(item->raw)); + } + 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) +{ + struct json_object *dev_stats = json_create_object(); + smart_log_item_t *item = payload->item; + struct json_object *root; + + 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); + json_object_add_value_object(root, "Device stats", dev_stats); + + json_print_object(root, NULL); + json_free_object(root); +} + +static void vu_smart_log_show(vu_smart_log_t *payload, unsigned int nsid, const char *devname) +{ + smart_log_item_t *item = payload->item; + + printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", + devname, nsid); + printf("ID KEY Normalized Raw\n"); + + 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 int solidigm_vu_smart_log_id = 0xCA; + vu_smart_log_t smart_log_payload; + enum nvme_print_flags flags; + struct nvme_dev *dev; + int err; + + struct config { + __u32 namespace_id; + char *output_format; + }; + + struct config cfg = { + .namespace_id = NVME_NSID_ALL, + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, "(optional) desired namespace"), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + flags = validate_output_format(cfg.output_format); + if (flags == -EINVAL) { + fprintf(stderr, "Invalid output format '%s'\n", cfg.output_format); + dev_close(dev); + return flags; + } + + err = nvme_get_log_simple(dev_fd(dev), solidigm_vu_smart_log_id, + sizeof(smart_log_payload), &smart_log_payload); + if (!err) { + if (flags & JSON) { + vu_smart_log_show_json(&smart_log_payload, + cfg.namespace_id, dev->name); + } else if (flags & BINARY) { + d_raw((unsigned char *)&smart_log_payload, sizeof(smart_log_payload)); + } else { + vu_smart_log_show(&smart_log_payload, cfg.namespace_id, + dev->name); + } + } 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-smart.h b/plugins/solidigm/solidigm-smart.h new file mode 100644 index 0000000..e19ebe5 --- /dev/null +++ b/plugins/solidigm/solidigm-smart.h @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +int solidigm_get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin); diff --git a/plugins/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c new file mode 100644 index 0000000..84a4e2a --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "nvme-print.h" +#include "solidigm-telemetry.h" +#include "solidigm-telemetry/telemetry-log.h" +#include "solidigm-telemetry/cod.h" +#include "solidigm-telemetry/header.h" +#include "solidigm-telemetry/config.h" +#include "solidigm-telemetry/data-area.h" + +static int read_file2buffer(char *file_name, char **buffer, size_t *length) +{ + FILE *fd = fopen(file_name, "rb"); + + if (!fd) + return errno; + + fseek(fd, 0, SEEK_END); + size_t length_bytes = ftell(fd); + + fseek(fd, 0, SEEK_SET); + + *buffer = malloc(length_bytes); + if (!*buffer) { + fclose(fd); + return errno; + } + *length = fread(*buffer, 1, length_bytes, fd); + fclose(fd); + return 0; +} + +struct config { + __u32 host_gen; + bool ctrl_init; + int data_area; + char *cfg_file; + bool is_input_file; +}; + +int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Parse Solidigm Telemetry log"; + const char *hgen = "Controls when to generate new host initiated report. Default value '1' generates new host initiated report, value '0' causes retrieval of existing log."; + const char *cgen = "Gather report generated by the controller."; + const char *dgen = "Pick which telemetry data area to report. Default is 3 to fetch areas 1-3. Valid options are 1, 2, 3, 4."; + const char *cfile = "JSON configuration file"; + const char *sfile = "data source <device> is binary file containing log dump instead of block or character device"; + struct nvme_dev *dev; + + struct telemetry_log tl = { + .root = json_create_object(), + .log = NULL, + }; + + struct config cfg = { + .host_gen = 1, + .ctrl_init = false, + .data_area = 3, + .cfg_file = NULL, + .is_input_file = false, + }; + + OPT_ARGS(opts) = { + OPT_UINT("host-generate", 'g', &cfg.host_gen, hgen), + OPT_FLAG("controller-init", 'c', &cfg.ctrl_init, cgen), + OPT_UINT("data-area", 'd', &cfg.data_area, dgen), + OPT_FILE("config-file", 'j', &cfg.cfg_file, cfile), + OPT_FLAG("source-file", 's', &cfg.is_input_file, sfile), + OPT_END() + }; + + int err = argconfig_parse(argc, argv, desc, opts); + + if (err) + goto ret; + + if (cfg.is_input_file) { + if (optind >= argc) { + err = errno = EINVAL; + perror(argv[0]); + goto ret; + } + char *binary_file_name = argv[optind]; + + err = read_file2buffer(binary_file_name, (char **)&tl.log, &tl.log_size); + } else { + err = parse_and_open(&dev, argc, argv, desc, opts); + } + if (err) + goto ret; + + if (cfg.host_gen > 1) { + SOLIDIGM_LOG_WARNING("Invalid host-generate value '%d'", cfg.host_gen); + err = EINVAL; + goto close_fd; + } + + if (cfg.cfg_file) { + char *conf_str = 0; + size_t length = 0; + + err = read_file2buffer(cfg.cfg_file, &conf_str, &length); + if (err) { + SOLIDIGM_LOG_WARNING("Failed to open JSON configuration file %s: %s!", + cfg.cfg_file, strerror(err)); + goto close_fd; + } + json_tokener * jstok = json_tokener_new(); + + tl.configuration = json_tokener_parse_ex(jstok, conf_str, length); + if (jstok->err != json_tokener_success) { + SOLIDIGM_LOG_WARNING("Parsing error on JSON configuration file %s: %s (at offset %d)", + cfg.cfg_file, + json_tokener_error_desc(jstok->err), + jstok->char_offset); + json_tokener_free(jstok); + err = EINVAL; + goto close_fd; + } + json_tokener_free(jstok); + } + + if (!cfg.is_input_file) { + if (cfg.ctrl_init) + err = nvme_get_ctrl_telemetry(dev_fd(dev), true, + &tl.log, cfg.data_area, + &tl.log_size); + else if (cfg.host_gen) + err = nvme_get_new_host_telemetry(dev_fd(dev), &tl.log, + cfg.data_area, + &tl.log_size); + else + err = nvme_get_host_telemetry(dev_fd(dev), &tl.log, + cfg.data_area, + &tl.log_size); + + if (err < 0) { + SOLIDIGM_LOG_WARNING("get-telemetry-log: %s", + nvme_strerror(errno)); + goto close_fd; + } else if (err > 0) { + nvme_show_status(err); + SOLIDIGM_LOG_WARNING("Failed to acquire telemetry log %d!", err); + goto close_fd; + } + } + solidigm_telemetry_log_header_parse(&tl); + if (cfg.cfg_file) + solidigm_telemetry_log_data_areas_parse(&tl, cfg.data_area); + else + solidigm_telemetry_log_cod_parse(&tl); + + json_print_object(tl.root, NULL); + json_free_object(tl.root); + printf("\n"); + +close_fd: + if (!cfg.is_input_file) { + /* Redundant close() to make static code analysis happy */ + close(dev->direct.fd); + dev_close(dev); + } +ret: + json_free_object(tl.configuration); + free(tl.log); + return err; +} diff --git a/plugins/solidigm/solidigm-telemetry.h b/plugins/solidigm/solidigm-telemetry.h new file mode 100644 index 0000000..971ee2a --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry.h @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin); diff --git a/plugins/solidigm/solidigm-telemetry/cod.c b/plugins/solidigm/solidigm-telemetry/cod.c new file mode 100644 index 0000000..be5685b --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/cod.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ +#include "common.h" +#include "cod.h" + +const char *oemDataMapDesc[] = { + "Media Read Count", //Uid 0x00 + "Host Read count", //Uid 0x01 + "Media Write Count", //Uid 0x02 + "Host Write Count", //Uid 0x03 + "Device Model", // 0x04 + "Serial Number", // 0x05 + "Firmware Revision", // 0x06 + "Drive Status", // 0x07 + "Minimum Temperature", // 0x08 + "Maximum Temperature", // 0x09 + "Power Loss Protection Status", // 0x0a + "Lifetime Unsafe Shutdown Count", // 0x0b + "Lifetime Power Cycle Count", // 0x0c + "Minimum Read Latency", // 0x0d + "Maximum Read Latency", // 0x0e + "Average Read Latency", // 0x0f + "Minimum Write Latency", // 0x10 + "Maximum Write Latency", // 0x11 + "Average Write Latency", // 0x12 + "Grown Defects Count", // 0x13 + "DQS Recovery Count", // 0x14 + "Program Fail Count", // 0x15 + "Erase Fail Count", // 0x16 + "Defrag Writes in Progress Count", // 0x17 + "Total Defrag Writes Count", // 0x18 + "Max Die Offline Number", // 0x19 + "Current Die Offline Number", // 0x1A + "XOR Enable Status", // 0x1B + "Media Life Used", // 0x1C + "Uncorrectable Error Count", // 0x1D + "Current Wear Range Delta", // 0x1E + "Read Errors Corrected by XOR", // 0x1F + "Background Data Refresh", // 0x20 + "Pmic Vin History Data 1 Min", // 0x21 + "Pmic Vin History Data 1 Max", // 0x22 + "Pmic Vin History Data 1 Avg", // 0x23 + "Pmic Vin History Data 2 Min", // 0x24 + "Pmic Vin History Data 2 Max", // 0x25 + "Pmic Vin History Data 2 Avg", // 0x26 + "Pmic Vin History Data Total Readings", // 0x27 + "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 +}; + +static const char * getOemDataMapDescription(__u32 id) +{ + if (id < (sizeof(oemDataMapDesc) / sizeof(oemDataMapDesc[0]))) { + return oemDataMapDesc[id]; + } + return "unknown"; +} + +#define OEMSIGNATURE 0x504D4443 + +#pragma pack(push, cod, 1) +struct cod_header +{ + uint32_t versionMajor; + uint32_t versionMinor; + uint32_t Signature; //!Fixed signature value (0x504D4443) for identification and validation + uint32_t MapSizeInBytes; //!Total size of the map data structure in bytes + uint32_t EntryCount; //!Total number of entries in the entry list + uint8_t Reserved[12]; +}; + +struct cod_item +{ + uint32_t DataFieldMapUid; //!The data field unique identifier value + uint32_t reserved1 : 8; + uint32_t dataFieldType : 8; + uint32_t issigned : 1; + uint32_t bigEndian : 1; + uint32_t dataInvalid : 1; + uint32_t reserved2 : 13; + uint32_t DataFieldSizeInBytes; + uint8_t Reserved1[4]; + uint64_t DataFieldOffset; + uint8_t Reserved2[8]; +}; + +struct cod_map +{ + struct cod_header header; + struct cod_item items[]; +}; + +#pragma pack(pop, cod) + +void solidigm_telemetry_log_cod_parse(struct telemetry_log *tl) +{ + enum cod_field_type + { + INTEGER, + FLOAT, + STRING, + TWO_BYTE_ASCII, + FOUR_BYTE_ASCII, + + UNKNOWN = 0xFF, + }; + json_object *telemetry_header = NULL; + json_object *COD_offset = NULL; + json_object *reason_id = NULL; + + if (!json_object_object_get_ex(tl->root, "telemetryHeader", &telemetry_header)) + return; + if (!json_object_object_get_ex(telemetry_header, "reasonIdentifier", &reason_id)) + return; + if (!json_object_object_get_ex(reason_id, "OemDataMapOffset", &COD_offset)) + return; + + __u64 offset = json_object_get_int(COD_offset); + + if (offset == 0) { + 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); + + uint32_t signature = be32_to_cpu(data->header.Signature); + if ( signature != OEMSIGNATURE){ + SOLIDIGM_LOG_WARNING("Warning: Unsupported COD data signature %x!", signature); + return; + } + if ((offset + data->header.MapSizeInBytes) > tl->log_size){ + SOLIDIGM_LOG_WARNING("Warning: COD map data out of bounds."); + return; + } + + json_object *cod = json_create_object(); + json_object_object_add(tl->root, "cod", cod); + + for (int 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); + return; + } + struct cod_item item = data->items[i]; + if (item.DataFieldOffset + item.DataFieldOffset > tl->log_size) { + continue; + } + if (item.dataInvalid) { + continue; + } + 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): + 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); + + } + } +} diff --git a/plugins/solidigm/solidigm-telemetry/cod.h b/plugins/solidigm/solidigm-telemetry/cod.h new file mode 100644 index 0000000..032ccdc --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/cod.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "telemetry-log.h" +void solidigm_telemetry_log_cod_parse(struct telemetry_log *tl); diff --git a/plugins/solidigm/solidigm-telemetry/config.c b/plugins/solidigm/solidigm-telemetry/config.c new file mode 100644 index 0000000..781d786 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/config.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ +#include <stdbool.h> +#include "util/json.h" +#include <stdio.h> + +// max 16 bit unsigned integer nummber 65535 +#define MAX_16BIT_NUM_AS_STRING_SIZE 6 + +static bool config_get_by_version(const json_object *obj, int version_major, + int version_minor, json_object **value) +{ + char str_key[MAX_16BIT_NUM_AS_STRING_SIZE]; + char str_subkey[MAX_16BIT_NUM_AS_STRING_SIZE]; + + snprintf(str_key, sizeof(str_key), "%d", version_major); + snprintf(str_subkey, sizeof(str_subkey), "%d", version_minor); + json_object *major_obj = NULL; + + if (!json_object_object_get_ex(obj, str_key, &major_obj)) + return false; + if (!json_object_object_get_ex(major_obj, str_subkey, value)) + return false; + return value != NULL; +} + +bool solidigm_config_get_by_token_version(const json_object *obj, int token_id, + int version_major, int version_minor, + json_object **value) +{ + json_object *token_obj = 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)) + return false; + if (!config_get_by_version(token_obj, version_major, version_minor, value)) + return false; + return value != NULL; +} diff --git a/plugins/solidigm/solidigm-telemetry/config.h b/plugins/solidigm/solidigm-telemetry/config.h new file mode 100644 index 0000000..bea84fb --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/config.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ +#include <stdbool.h> +#include "util/json.h" + +bool solidigm_config_get_by_token_version(const json_object *obj, int key, int subkey, int subsubkey, json_object **value); diff --git a/plugins/solidigm/solidigm-telemetry/data-area.c b/plugins/solidigm/solidigm-telemetry/data-area.c new file mode 100644 index 0000000..7233e8f --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/data-area.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "common.h" +#include "data-area.h" +#include "config.h" +#include <ctype.h> + +#define SIGNED_INT_PREFIX "int" +#define BITS_IN_BYTE 8 + +#define MAX_WARNING_SIZE 1024 + +static bool telemetry_log_get_value(const struct telemetry_log *tl, + uint32_t offset_bit, uint32_t size_bit, + bool is_signed, json_object **val_obj) +{ + uint32_t offset_bit_from_byte; + uint32_t additional_size_byte; + uint32_t offset_byte; + uint32_t val; + + if (size_bit == 0) { + char err_msg[MAX_WARNING_SIZE]; + + snprintf(err_msg, MAX_WARNING_SIZE, + "Value with size_bit=0 not supported."); + *val_obj = json_object_new_string(err_msg); + + return false; + } + additional_size_byte = (size_bit - 1) ? (size_bit - 1) / BITS_IN_BYTE : 0; + offset_byte = offset_bit / BITS_IN_BYTE; + + if (offset_byte > (tl->log_size - additional_size_byte)) { + char err_msg[MAX_WARNING_SIZE]; + + snprintf(err_msg, MAX_WARNING_SIZE, + "Value offset greater than binary size (%u > %zu).", + offset_byte, tl->log_size); + *val_obj = json_object_new_string(err_msg); + + return false; + } + + offset_bit_from_byte = offset_bit - (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); + *val_obj = json_object_new_string(err_msg); + + return false; + } + + val = *(uint64_t *)(((char *)tl->log) + offset_byte); + val >>= offset_bit_from_byte; + if (size_bit < 64) + val &= (1ULL << size_bit) - 1; + if (is_signed) { + if (val >> (size_bit - 1)) + val |= -1ULL << size_bit; + *val_obj = json_object_new_int64(val); + } else { + *val_obj = json_object_new_uint64(val); + } + + return true; +} + +static int telemetry_log_structure_parse(const struct telemetry_log *tl, + json_object *struct_def, + size_t parent_offset_bit, + json_object *output, + json_object *metadata) +{ + json_object *obj_arraySizeArray = NULL; + json_object *obj = NULL; + json_object *obj_memberList; + json_object *major_dimension; + 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; + + if (!json_object_object_get_ex(struct_def, "name", &obj)) { + SOLIDIGM_LOG_WARNING("Warning: Structure definition missing property 'name': %s", + json_object_to_json_string(struct_def)); + return -1; + } + + name = json_object_get_string(obj); + + if (metadata) { + json_object_get(obj); + json_object_object_add(metadata, "objName", obj); + } + + if (json_object_object_get_ex(struct_def, "type", &obj)) + 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)); + 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)); + return -1; + } + + size_bit = json_object_get_uint64(obj); + + if (json_object_object_get_ex(struct_def, "enum", &obj)) + is_enumeration = json_object_get_boolean(obj); + + has_member_list = json_object_object_get_ex(struct_def, + "memberList", + &obj_memberList); + + 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)); + 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)); + return -1; + } + uint32_t array_size_dimension[array_rank]; + + for (size_t i = 0; i < array_rank; i++) { + json_object *dimension = json_object_array_get_idx(obj_arraySizeArray, i); + + array_size_dimension[i] = json_object_get_uint64(dimension); + major_dimension = dimension; + } + if (array_rank > 1) { + uint32_t linear_pos_per_index = array_size_dimension[0]; + uint32_t prev_index_offset_bit = 0; + json_object *dimension_output; + + for (int i = 1; i < (array_rank - 1); i++) + linear_pos_per_index *= array_size_dimension[i]; + + dimension_output = json_create_array(); + if (json_object_get_type(output) == json_type_array) + json_object_array_add(output, dimension_output); + else + json_object_add_value_array(output, name, dimension_output); + + /* + * Make sure major_dimension object will not be + * deleted from memory when deleted from array + */ + 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++) { + json_object *sub_array = json_create_array(); + size_t offset; + + offset = parent_offset_bit + prev_index_offset_bit; + + json_object_array_add(dimension_output, sub_array); + telemetry_log_structure_parse(tl, struct_def, + offset, sub_array, NULL); + prev_index_offset_bit += linear_pos_per_index * size_bit; + } + + json_object_array_put_idx(obj_arraySizeArray, array_rank - 1, + major_dimension); + + return 0; + } + + linear_array_pos_bit = 0; + sub_output = output; + + if (array_size_dimension[0] > 1) { + sub_output = json_create_array(); + if (json_object_get_type(output) == json_type_array) + json_object_array_add(output, sub_output); + else + json_object_add_value_array(output, name, sub_output); + } + + for (uint32_t j = 0; j < array_size_dimension[0]; j++) { + if (is_enumeration || !has_member_list) { + bool is_signed = !strncmp(type, SIGNED_INT_PREFIX, sizeof(SIGNED_INT_PREFIX)-1); + json_object *val_obj; + size_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)) { + if (array_size_dimension[0] > 1) + json_object_array_put_idx(sub_output, j, val_obj); + 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)); + json_free_object(val_obj); + } + } else { + json_object *sub_sub_output = json_object_new_object(); + int num_members; + + if (array_size_dimension[0] > 1) + json_object_array_put_idx(sub_output, j, sub_sub_output); + else + json_object_add_value_object(sub_output, name, sub_sub_output); + + num_members = json_object_array_length(obj_memberList); + for (int k = 0; k < num_members; k++) { + json_object *member = json_object_array_get_idx(obj_memberList, k); + size_t offset; + + offset = parent_offset_bit + offset_bit + linear_array_pos_bit; + telemetry_log_structure_parse(tl, member, offset, + sub_sub_output, NULL); + } + } + linear_array_pos_bit += size_bit; + } + return 0; +} + +static int telemetry_log_data_area_get_offset(const struct telemetry_log *tl, + enum nvme_telemetry_da da, + uint32_t *offset, uint32_t *size) +{ + uint32_t offset_blocks = 1; + uint32_t last_block = tl->log->dalb1; + uint32_t last; + + switch (da) { + case NVME_TELEMETRY_DA_1: + offset_blocks = 1; + last_block = tl->log->dalb1; + break; + case NVME_TELEMETRY_DA_2: + offset_blocks = tl->log->dalb1 + 1; + last_block = tl->log->dalb2; + break; + case NVME_TELEMETRY_DA_3: + offset_blocks = tl->log->dalb2 + 1; + last_block = tl->log->dalb3; + break; + case NVME_TELEMETRY_DA_4: + offset_blocks = tl->log->dalb3 + 1; + last_block = tl->log->dalb4; + break; + default: + return -1; + } + + *offset = offset_blocks * NVME_LOG_TELEM_BLOCK_SIZE; + last = (last_block + 1) * NVME_LOG_TELEM_BLOCK_SIZE; + *size = last - *offset; + if ((*offset > tl->log_size) || (last > tl->log_size) || (last <= *offset)) { + SOLIDIGM_LOG_WARNING("Warning: Data Area %d don't fit this Telemetry log.", da); + return -1; + } + + return 0; +} + +struct toc_item { + uint32_t OffsetBytes; + uint32_t ContentSizeBytes; +}; + +struct data_area_header { + uint8_t versionMajor; + uint8_t versionMinor; + uint16_t TableOfContentsCount; + uint32_t DataAreaSize; + uint8_t Reserved[8]; +}; + +struct table_of_contents { + struct data_area_header header; + struct toc_item items[]; +}; + +struct telemetry_object_header { + uint16_t versionMajor; + uint16_t versionMinor; + uint32_t Token; + uint8_t CoreId; + uint8_t Reserved[3]; +}; + + +static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, + enum nvme_telemetry_da da, + json_object *toc_array, + json_object *tele_obj_array) +{ + + const struct telemetry_object_header *header; + const struct table_of_contents *toc; + char *payload; + uint32_t da_offset; + uint32_t da_size; + + 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; + + for (int i = 0; i < toc->header.TableOfContentsCount; i++) { + json_object *structure_definition = NULL; + 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); + 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); + continue; + } + + toc_item = json_create_object(); + json_object_array_add(toc_array, toc_item); + json_object_add_value_uint(toc_item, "dataArea", da); + json_object_add_value_uint(toc_item, "dataAreaIndex", i); + json_object_add_value_uint(toc_item, "dataAreaOffset", obj_offset); + json_object_add_value_uint(toc_item, "fileOffset", obj_offset + da_offset); + json_object_add_value_uint(toc_item, "size", toc->items[i].ContentSizeBytes); + + header = (const struct telemetry_object_header *) (payload + da_offset + obj_offset); + json_object_add_value_uint(toc_item, "telemMajor", header->versionMajor); + json_object_add_value_uint(toc_item, "telemMinor", header->versionMinor); + 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) { + 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); + json_object *parsed_struct = json_object_new_object(); + + json_object_add_value_object(tele_obj_item, "objectData", parsed_struct); + json_object *obj_hasTelemObjHdr = NULL; + uint32_t header_offset = sizeof(const struct telemetry_object_header); + uint32_t file_offset; + + 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; + telemetry_log_structure_parse(tl, structure_definition, + BITS_IN_BYTE * file_offset, + parsed_struct, toc_item); + } + } +} + +int solidigm_telemetry_log_data_areas_parse(const struct telemetry_log *tl, + enum nvme_telemetry_da last_da) +{ + json_object *tele_obj_array = json_create_array(); + 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); + + return 0; +} diff --git a/plugins/solidigm/solidigm-telemetry/data-area.h b/plugins/solidigm/solidigm-telemetry/data-area.h new file mode 100644 index 0000000..095eb64 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/data-area.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022 Solidigm. + * + * 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, + enum nvme_telemetry_da last_da); diff --git a/plugins/solidigm/solidigm-telemetry/header.c b/plugins/solidigm/solidigm-telemetry/header.c new file mode 100644 index 0000000..72b2d97 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/header.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "common.h" +#include "header.h" + +#pragma pack(push, reason_indentifier, 1) +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. + char DriveStatus[20]; //! Drive Status String (for example: "Healthy", "*BAD_CONTEXT_2020") + char FirmwareVersion[12]; //! Similar to IdentifyController.FR + char BootloaderVersion[12]; //! Bootloader version string + char SerialNumber[20]; //! Device serial number + uint8_t Reserved[56]; //! Reserved for future usage +}; +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 +{ + uint16_t versionMajor; + uint16_t versionMinor; + uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue. + char DriveStatus[20]; //! Drive Status String (for example: "Healthy", "*BAD_CONTEXT_2020") + char FirmwareVersion[12]; //! Similar to IdentifyController.FR + char BootloaderVersion[12]; //! Bootloader version string + char SerialNumber[20]; //! Device serial number + uint64_t OemDataMapOffset; //! Customer Data Map Object Log Offset + uint8_t TelemetryMajorVersion; //! Shadow of version in TOC + uint8_t TelemetryMinorVersion; //! Shadow of version in TOC + uint8_t Reserved[46]; //! Reserved for future usage +}; +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 +{ + uint16_t versionMajor; + uint16_t versionMinor; + uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue. + char DriveStatus[20]; //! Drive Status String (for example: "Healthy", "*BAD_CONTEXT_2020") + uint8_t Reserved1[24]; //! pad over Fields removed from version 1.1 + char SerialNumber[20]; //! Device serial number + uint64_t OemDataMapOffset; //! Customer Data Map Object Log Offset + uint8_t TelemetryMajorVersion; //! Shadow of version in TOC + uint8_t TelemetryMinorVersion; //! Shadow of version in TOC + uint8_t ProductFamilyId; + uint8_t Reserved2[5]; //! Reserved for future usage + uint8_t DualPortReserved[40]; //! Reserved for dual port +}; +static_assert(sizeof(const struct reason_indentifier_1_2) == + MEMBER_SIZE(struct nvme_telemetry_log, rsnident), + "Size mismatch for reason_indentifier_1_2"); +#pragma pack(pop, reason_indentifier) + +static void telemetry_log_reason_id_parse1_0_ext(const struct telemetry_log *tl, + json_object *reason_id) +{ + const struct reason_indentifier_1_0 *ri; + 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))); + + reserved = json_create_array(); + json_object_add_value_array(reason_id, "Reserved", reserved); + for ( int i=0; i < sizeof(ri->Reserved); i++) { + json_object *val = json_object_new_int(ri->Reserved[i]); + json_object_array_add(reserved, val); + } +} + +static void telemetry_log_reason_id_parse1_1_ext(const struct telemetry_log *tl, + json_object *reason_id) +{ + const struct reason_indentifier_1_1 *ri; + 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)); + + reserved = json_create_array(); + json_object_add_value_array(reason_id, "Reserved", reserved); + for (int i = 0; i < sizeof(ri->Reserved); i++) { + json_object *val = json_object_new_int(ri->Reserved[i]); + json_object_array_add(reserved, val); + } +} + +static void telemetry_log_reason_id_parse1_2_ext(const struct telemetry_log *tl, + json_object *reason_id) +{ + const struct reason_indentifier_1_2 *ri; + json_object *dp_reserved; + json_object *reserved; + + 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); + + reserved = json_create_array(); + json_object_add_value_array(reason_id, "Reserved2", reserved); + for (int i = 0; i < sizeof(ri->Reserved2); i++) { + 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); + for (int i = 0; i < sizeof(ri->DualPortReserved); i++) { + json_object *val = json_object_new_int(ri->DualPortReserved[i]); + json_object_array_add(dp_reserved, val); + } +} + +static void solidigm_telemetry_log_reason_id_parse(const struct telemetry_log *tl, json_object *reason_id) +{ + 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); + + 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))); + 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); + } + } +} + +bool solidigm_telemetry_log_header_parse(const struct telemetry_log *tl) +{ + const struct nvme_telemetry_log *log; + json_object *ieee_oui_id; + json_object *reason_id; + json_object *header; + + if (tl->log_size < sizeof(const struct nvme_telemetry_log)) { + SOLIDIGM_LOG_WARNING("Telemetry log too short."); + return false; + } + + header = json_create_object(); + + json_object_object_add(tl->root, "telemetryHeader", header); + log = tl->log; + + json_object_add_value_uint(header, "logIdentifier", log->lpi); + ieee_oui_id = json_create_array(); + + json_object_object_add(header, "ieeeOuiIdentifier", ieee_oui_id); + for (int i = 0; i < sizeof(log->ieee); i++) { + json_object *val = json_object_new_int(log->ieee[i]); + + json_object_array_add(ieee_oui_id, val); + } + json_object_add_value_uint(header, "dataArea1LastBlock", log->dalb1); + json_object_add_value_uint(header, "dataArea2LastBlock", log->dalb2); + json_object_add_value_uint(header, "dataArea3LastBlock", log->dalb3); + json_object_add_value_uint(header, "hostInitiatedDataGeneration", log->hostdgn); + json_object_add_value_uint(header, "controllerInitiatedDataAvailable", log->ctrlavail); + json_object_add_value_uint(header, "controllerInitiatedDataGeneration", log->ctrldgn); + + reason_id = json_create_object(); + json_object_add_value_object(header, "reasonIdentifier", reason_id); + solidigm_telemetry_log_reason_id_parse(tl, reason_id); + + return true; +} diff --git a/plugins/solidigm/solidigm-telemetry/header.h b/plugins/solidigm/solidigm-telemetry/header.h new file mode 100644 index 0000000..027af55 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/header.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "telemetry-log.h" +bool solidigm_telemetry_log_header_parse(const struct telemetry_log *tl); diff --git a/plugins/solidigm/solidigm-telemetry/meson.build b/plugins/solidigm/solidigm-telemetry/meson.build new file mode 100644 index 0000000..53ab452 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/meson.build @@ -0,0 +1,6 @@ +sources += [ + 'plugins/solidigm/solidigm-telemetry/cod.c', + 'plugins/solidigm/solidigm-telemetry/header.c', + 'plugins/solidigm/solidigm-telemetry/config.c', + 'plugins/solidigm/solidigm-telemetry/data-area.c', +] diff --git a/plugins/solidigm/solidigm-telemetry/telemetry-log.h b/plugins/solidigm/solidigm-telemetry/telemetry-log.h new file mode 100644 index 0000000..ef4ec5d --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/telemetry-log.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#ifndef _SOLIDIGM_TELEMETRY_LOG_H +#define _SOLIDIGM_TELEMETRY_LOG_H + +#include "libnvme.h" +#include "util/json.h" +#include <assert.h> + +#if !defined __cplusplus +#define static_assert _Static_assert +#endif + +#define VA_ARGS(...), ##__VA_ARGS__ +#define SOLIDIGM_LOG_WARNING(format, ...) fprintf(stderr, format"\n" VA_ARGS(__VA_ARGS__)) + +#define MEMBER_SIZE(type, member) sizeof(((type *)0)->member) + +struct telemetry_log { + struct nvme_telemetry_log *log; + size_t log_size; + json_object *root; + json_object *configuration; +}; + +#endif /* _SOLIDIGM_TELEMETRY_LOG_H */
\ No newline at end of file |