From: da Cunha, Leonardo Date: Fri, 18 Mar 2022 19:45:43 +0000 (-0400) Subject: solidigm: Add Solidigm pluging for vendor specific SMART fields. X-Git-Tag: v2.1-rc0~53^2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=fadb20e17e71b618cc2632c4c262396a81b5587b;p=users%2Fsagi%2Fnvme-cli.git solidigm: Add Solidigm pluging for vendor specific SMART fields. --- diff --git a/plugins/meson.build b/plugins/meson.build index 6f213620..6783c0b0 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -10,6 +10,7 @@ sources += [ 'plugins/scaleflux/sfx-nvme.c', 'plugins/seagate/seagate-nvme.c', 'plugins/shannon/shannon-nvme.c', + 'plugins/solidigm/solidigm-smart.c', 'plugins/toshiba/toshiba-nvme.c', 'plugins/transcend/transcend-nvme.c', 'plugins/virtium/virtium-nvme.c', diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h new file mode 100644 index 00000000..d7035cf7 --- /dev/null +++ b/plugins/solidigm/solidigm-nvme.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Solidigm. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * 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.1" + +PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_VERSION), + COMMAND_LIST( + ENTRY("smart-log-add", "Retrieve Solidigm SMART Log", get_additional_smart_log) + ) +); + +#endif + +#include "define_cmd.h" diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c new file mode 100644 index 00000000..d056369d --- /dev/null +++ b/plugins/solidigm/solidigm-smart.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2022 Solidigm. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "linux/types.h" +#include "nvme-print.h" + +#define CREATE_CMD +#include "solidigm-nvme.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]); + } +} + +static int 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; + int fd, err; + + struct config { + __u32 namespace_id; + char *output_format; + int raw_binary; + }; + + struct config cfg = { + .namespace_id = NVME_NSID_ALL, + .output_format = "normal", + .raw_binary = 0 + }; + + 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_FLAG("raw-binary", 'b', &cfg.raw_binary, "Dump output in binary format"), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + return fd; + } + + flags = validate_output_format(cfg.output_format); + if (flags == -EINVAL) { + fprintf(stderr, "Invalid output format '%s'\n", cfg.output_format); + close(fd); + return fd; + } + if (cfg.raw_binary) { + flags = BINARY; + } + + err = nvme_get_log_simple(fd, 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, devicename); + } 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, devicename); + } + } else if (err > 0) { + nvme_show_status(err); + } + + close(fd); + return err; +} +