diff --git a/plugins/ibm/ibm-nvme.c b/plugins/ibm/ibm-nvme.c new file mode 100644 index 0000000000..5ee38354a7 --- /dev/null +++ b/plugins/ibm/ibm-nvme.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include + +#include "common.h" +#include "nvme-print.h" +#include "plugin.h" + +#define CREATE_CMD +#include "ibm-nvme.h" + +#pragma pack(push, 1) +struct nvme_ibm_log_f0_item { + __le16 attr; + union { + __le64 raw1; + struct split_raw1 { + __le32 lower; + __le32 upper; + } split_raw1; + }; + union { + __le64 raw2; + struct split_raw2 { + __le32 lower; + __le32 upper; + } split_raw2; + }; +}; +#pragma pack(pop) + +struct nvme_ibm_additional_smart_log { + __le16 logid; + __le16 len; + struct nvme_ibm_log_f0_item read_err_rate; + struct nvme_ibm_log_f0_item retired_clk_cnt; + struct nvme_ibm_log_f0_item power_on_hours; + struct nvme_ibm_log_f0_item power_cycle_cnt; + struct nvme_ibm_log_f0_item ecc_rate; + struct nvme_ibm_log_f0_item mb_erased; + struct nvme_ibm_log_f0_item unused_rsvd_blk_cnt_percent; + struct nvme_ibm_log_f0_item progrm_fail_cnt; + struct nvme_ibm_log_f0_item erase_fail_cnt; + struct nvme_ibm_log_f0_item drive_life_remain_percent; + struct nvme_ibm_log_f0_item io_err_det_code_events; + struct nvme_ibm_log_f0_item reported_uc_errs; + struct nvme_ibm_log_f0_item drive_temperature; + struct nvme_ibm_log_f0_item thermal_throt; + struct nvme_ibm_log_f0_item drive_life_temp; + struct nvme_ibm_log_f0_item int_raid_correct_err_cnt; + struct nvme_ibm_log_f0_item ssd_life_used; + struct nvme_ibm_log_f0_item ssd_life_used_accurate; + struct nvme_ibm_log_f0_item lifetime_wr_to_flash_mb; + struct nvme_ibm_log_f0_item lifetime_rd_from_flash_mb; + struct nvme_ibm_log_f0_item lifetime_wr_from_host_mb; + struct nvme_ibm_log_f0_item lifetime_rd_to_host_mb; + struct nvme_ibm_log_f0_item vol_mem_backup_fail; + struct nvme_ibm_log_f0_item security_wear_indicator; + struct nvme_ibm_log_f0_item device_pcie_received_errors; +}; + +static void show_ibm_smart_log(struct nvme_ibm_additional_smart_log *smart, const char *devname) +{ + int i, entries = (sizeof(struct nvme_ibm_additional_smart_log) - 4) + / sizeof(struct nvme_ibm_log_f0_item); + struct nvme_ibm_log_f0_item *entry; + + entry = &smart->read_err_rate; + + printf("Additional IBM Smart Log for NVME device:%s\n", devname); + + for (i = 0; i < entries; i++, entry++) { + switch (le16_to_cpu(entry->attr)) { + case 0x0001: + printf("Total UC Read Errors : %"PRIu64"\n", + le64_to_cpu(smart->read_err_rate.raw1)); + printf("Total Reads vs Read Errors : %"PRIu64"\n", + le64_to_cpu(smart->read_err_rate.raw2)); + break; + case 0x0005: + printf("Total Retired Blks : %"PRIu64"\n", + le64_to_cpu(smart->retired_clk_cnt.raw1)); + break; + case 0x0009: + printf("Total Power On Hours : %"PRIu64"\n", + le64_to_cpu(smart->power_on_hours.raw1)); + printf("Time since Last P/C(ms) : %"PRIu64"\n", + le64_to_cpu(smart->power_on_hours.raw2)); + break; + case 0x000c: + printf("Total Number of Power Cycles : %"PRIu64"\n", + le64_to_cpu(smart->power_cycle_cnt.raw1)); + break; + case 0x000d: + printf("Read (ECC) Errors recov nodelay : %"PRIu64"\n", + le64_to_cpu(smart->ecc_rate.raw1)); + printf("Total Reads vs Read Errs nodelay : %"PRIu64"\n", + le64_to_cpu(smart->ecc_rate.raw2)); + break; + case 0x0064: + printf("Total MB Erased : %"PRIu64"\n", + le64_to_cpu(smart->mb_erased.raw1)); + break; + case 0x00aa: + printf("Unused Rsv Blk 100*Cur/Mfg Spares : %"PRIu64"\n", + le64_to_cpu(smart->unused_rsvd_blk_cnt_percent.raw1)); + printf("Current Spares : %"PRIu32"\n", + le32_to_cpu(smart->unused_rsvd_blk_cnt_percent.split_raw2.upper)); + printf("Total Spares @ Mfg : %"PRIu32"\n", + le32_to_cpu(smart->unused_rsvd_blk_cnt_percent.split_raw2.lower)); + break; + case 0x00ab: + printf("Total Number of Program Fails : %"PRIu64"\n", + le64_to_cpu(smart->progrm_fail_cnt.raw1)); + printf("Program fails since Power Cycle : %"PRIu64"\n", + le64_to_cpu(smart->progrm_fail_cnt.raw2)); + break; + case 0x00ac: + printf("Total Number of Erase Fails : %"PRIu64"\n", + le64_to_cpu(smart->erase_fail_cnt.raw1)); + printf("Erase fails since Power Cycle : %"PRIu64"\n", + le64_to_cpu(smart->erase_fail_cnt.raw2)); + break; + case 0x00b1: + printf("Life remaining percent : %"PRIu64"\n", + le64_to_cpu(smart->drive_life_remain_percent.raw1)); + printf("PE Cycles most : %"PRIu32"\n", + le32_to_cpu(smart->drive_life_remain_percent.split_raw2.upper)); + printf("PE Cycles least : %"PRIu32"\n", + le32_to_cpu(smart->drive_life_remain_percent.split_raw2.lower)); + break; + case 0x00b8: + printf("Total number of IOEDC : %"PRIu64"\n", + le64_to_cpu(smart->io_err_det_code_events.raw1)); + break; + case 0x00bb: + printf("Total number of UC Errors : %"PRIu64"\n", + le64_to_cpu(smart->reported_uc_errs.raw1)); + break; + case 0x00be: + printf("Current Temperature (in C) : %"PRIu64"\n", + le64_to_cpu(smart->drive_temperature.raw1)); + printf("Highest Temperature since Power ON : %"PRIu32"\n", + le32_to_cpu(smart->drive_temperature.split_raw2.upper)); + printf("Lowest Temperature since Power ON : %"PRIu32"\n", + le32_to_cpu(smart->drive_temperature.split_raw2.lower)); + break; + case 0x00bf: + printf("Percentage throttled : %"PRIu64"\n", + le64_to_cpu(smart->thermal_throt.raw1)); + printf("Thermal throttling starts : %"PRIu32"\n", + le32_to_cpu(smart->thermal_throt.split_raw2.upper)); + printf("Thermal throttling stops : %"PRIu32"\n", + le32_to_cpu(smart->thermal_throt.split_raw2.lower)); + break; + case 0x00c2: + printf("PON Time in mins Highest Temperature: %"PRIu32"\n", + le32_to_cpu(smart->drive_life_temp.split_raw1.upper)); + printf("PON Time in mins Lowest Temperature : %"PRIu32"\n", + le32_to_cpu(smart->drive_life_temp.split_raw1.lower)); + printf("Highest Lifetime Temperature (in C) : %"PRIu32"\n", + le32_to_cpu(smart->drive_life_temp.split_raw2.upper)); + printf("Lowest Lifetime Temperature (in C) : %"PRIu32"\n", + le32_to_cpu(smart->drive_life_temp.split_raw2.lower)); + break; + case 0x00c3: + printf("Internal RAID Correctable Error : %"PRIu64"\n", + le64_to_cpu(smart->int_raid_correct_err_cnt.raw1)); + break; + case 0x00e7: + printf("Life used in percentage : %"PRIu64"\n", + le64_to_cpu(smart->ssd_life_used.raw1)); + printf("Average PE Cycles of Flash : %"PRIu64"\n", + le64_to_cpu(smart->ssd_life_used.raw2)); + break; + case 0x00e8: + printf("Accurate Life used in percentage : %"PRIu64".%"PRIu64"\n", + le64_to_cpu(smart->ssd_life_used_accurate.raw1)/100, + le64_to_cpu(smart->ssd_life_used_accurate.raw1)%100); + break; + case 0x00e9: + printf("Lifetime Writes to flash in MB : %"PRIu64"\n", + le64_to_cpu(smart->lifetime_wr_to_flash_mb.raw1)); + break; + case 0x00ea: + printf("Lifetime Read from flash in MB : %"PRIu64"\n", + le64_to_cpu(smart->lifetime_rd_from_flash_mb.raw1)); + break; + case 0x00f1: + printf("Lifetime Writes from Host in MB : %"PRIu64"\n", + le64_to_cpu(smart->lifetime_wr_from_host_mb.raw1)); + break; + case 0x00f2: + printf("Lifetime Read to Host in MB : %"PRIu64"\n", + le64_to_cpu(smart->lifetime_rd_to_host_mb.raw1)); + break; + case 0x00f3: + printf("Vol. Memory Backup Failures : %"PRIu64"\n", + le64_to_cpu(smart->vol_mem_backup_fail.raw1)); + break; + case 0x00f4: + printf("Security Wear Indicator : %"PRIu64"\n", + le64_to_cpu(smart->security_wear_indicator.raw1)); + break; + case 0x00f5: + printf("PCIe Received Errors : %"PRIu32"\n", + le32_to_cpu(smart->device_pcie_received_errors.split_raw1.upper)); + printf("PCIe Received Bad TLP : %"PRIu32"\n", + le32_to_cpu(smart->device_pcie_received_errors.split_raw1.lower)); + printf("PCIe Received Bad DLLP : %"PRIu32"\n", + le32_to_cpu(smart->device_pcie_received_errors.split_raw2.upper)); + printf("PCIe Recd Transitions to Recoveries : %"PRIu32"\n", + le32_to_cpu(smart->device_pcie_received_errors.split_raw2.lower)); + default: + break; + } + } +} + +static int get_ibm_addi_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Get IBM specific additional smart log and show it."; + const char *raw = "Dump output in binary format"; + + struct nvme_ibm_additional_smart_log smart_log; + struct nvme_dev *dev; + int err; + + struct config { + bool raw_binary; + }; + + struct config cfg = { + .raw_binary = 0, + }; + + 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), 0xf0, sizeof(smart_log), &smart_log); + + if (!err) { + if (!cfg.raw_binary) + show_ibm_smart_log(&smart_log, dev->name); + else + d_raw((unsigned char *)&smart_log, sizeof(smart_log)); + } else if (err > 0) + nvme_show_status(err); + else + nvme_show_error("ibm additional smart log: %s\n", nvme_strerror(errno)); + + dev_close(dev); + return err; +} + +#pragma pack(push, 1) +struct nvme_ibm_vpd_log { + char version[4]; + char description[40]; + char masterpn[12]; + char ec[10]; + char fru[12]; + char finalasm[12]; + char fc[4]; + char ccin[4]; + char ibm11s[8]; + char ssid[8]; + char endurance[4]; + char capacity[10]; + char warranty[12]; + char encryption[1]; + char rctt[2]; + char loadid[8]; + char mfgloc[3]; + char ffc[5]; + char iotimeout[2]; + char formattimeout[4]; + char ioqs[4]; + char mediatype[2]; + char mfgsn[20]; + char firmware[8]; + char pad[4]; +}; +#pragma pack(pop) + +#define MEDIATYPE 3 +static void show_ibm_vpd_log(struct nvme_ibm_vpd_log *vpd, const char *devname) +{ + struct nvme_ibm_vpd_log vpdlog; + char *mediatype[MEDIATYPE][2] = { + { "00", "NAND TLC" }, + { "01", "3DXP" }, + { "02", "LL NAND" } + }; + + printf("IBM VPD for NVME device:%s\n", devname); + printf("VPD Log Page Version : %.*s\n", (int)sizeof(vpdlog.version), + vpd->version); + printf("Description or ID : %.*s\n", (int)sizeof(vpdlog.description), + vpd->description); + printf("Master Part Number : %.*s\n", (int)sizeof(vpdlog.masterpn), + vpd->masterpn); + printf("EC Level : %.*s\n", (int)sizeof(vpdlog.ec), vpd->ec); + printf("FRU Part Number : %.*s\n", (int)sizeof(vpdlog.fru), vpd->fru); + printf("Final Assembly PN : %.*s\n", (int)sizeof(vpdlog.finalasm), + vpd->finalasm); + printf("Feature Code : %.*s\n", (int)sizeof(vpdlog.fc), vpd->fc); + printf("CCIN : %.*s\n", (int)sizeof(vpdlog.ccin), vpd->ccin); + printf("11S Serial Number : 11S%.*sY%.*s%.*s\n", 7, vpd->masterpn, + (int)sizeof(vpdlog.mfgloc), vpd->mfgloc, (int)sizeof(vpdlog.ibm11s), + vpd->ibm11s); + printf("PCI SSID : %.*s\n", (int)sizeof(vpdlog.ssid), vpd->ssid); + printf("Endurance (DWPD) : %.*s\n", (int)sizeof(vpdlog.endurance), + vpd->endurance); + printf("Capacity (GB) : %.*s\n", (int)sizeof(vpdlog.capacity), + vpd->capacity); + printf("Warranty (Peta Bytes Written) : %.*s\n", (int)sizeof(vpdlog.warranty), + vpd->warranty); + printf("Encryption (0=not supported) : %.*s\n", (int)sizeof(vpdlog.encryption), + vpd->encryption); + printf("RCTT : %.*s\n", (int)sizeof(vpdlog.rctt), vpd->rctt); + printf("Load ID : %.*s\n", (int)sizeof(vpdlog.loadid), vpd->loadid); + printf("MFG Location : %.*s\n", (int)sizeof(vpdlog.mfgloc), + vpd->mfgloc); + printf("FFC : %.*s\n", (int)sizeof(vpdlog.ffc), vpd->ffc); + printf("IO Timeout in Seconds : 0x%.*s\n", (int)sizeof(vpdlog.iotimeout), + vpd->iotimeout); + printf("Format Timeout in Seconds : 0x%.*s\n", (int)sizeof(vpdlog.formattimeout), + vpd->formattimeout); + printf("Optimal Number of IO Queues : 0x%.*s\n", (int)sizeof(vpdlog.ioqs), + vpd->ioqs); + + for (int i = 0; i < MEDIATYPE; i++) { + if (!strncmp(mediatype[i][0], vpd->mediatype, (int)sizeof(vpdlog.mediatype))) + printf("Media Type : %.*s (%.*s)\n", + (int)sizeof(mediatype[i][1]), mediatype[i][1], + (int)sizeof(vpdlog.mediatype), vpd->mediatype); + } + + printf("Manufacturer Serial Number : %.*s\n", (int) sizeof(vpdlog.mfgsn), vpd->mfgsn); + printf("Firmware version : %.*s\n", (int) sizeof(vpdlog.firmware), + vpd->firmware); +} + +static int get_ibm_vpd_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + struct nvme_ibm_vpd_log vpd_log; + int err; + struct nvme_dev *dev; + + const char *desc = "Get IBM vendor specific VPD log"; + const char *raw = "dump output in binary format"; + + struct config { + int raw_binary; + }; + + struct config cfg = { + .raw_binary = 0, + }; + + 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 < 0) + return err; + + bzero(&vpd_log, sizeof(vpd_log)); + err = nvme_get_log_simple(dev_fd(dev), 0xf1, sizeof(vpd_log), &vpd_log); + + if (!err) { + if (!cfg.raw_binary) + show_ibm_vpd_log(&vpd_log, dev->name); + else + d_raw((unsigned char *)&vpd_log, sizeof(vpd_log)); + } else if (err > 0) + nvme_show_status(err); + else + nvme_show_error("ibm vpd log: %s\n", nvme_strerror(errno)); + + dev_close(dev); + return err; +} diff --git a/plugins/ibm/ibm-nvme.h b/plugins/ibm/ibm-nvme.h new file mode 100644 index 0000000000..4979f2a056 --- /dev/null +++ b/plugins/ibm/ibm-nvme.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later*/ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/ibm/ibm-nvme + +#if !defined(IBM_NVME) || defined(CMD_HEADER_MULTI_READ) +#define IBM_NVME + +#include "cmd.h" + +#define PLUGIN_VERSION "nvme ibm plugin version 0.1" + +PLUGIN(NAME("ibm", "IBM vendor specific extensions", NVME_VERSION), + COMMAND_LIST( + ENTRY("crit-log", "Display IBM Smart Log Information", get_ibm_addi_smart_log) + ENTRY("vpd", "Display IBM VPD Information", get_ibm_vpd_log) + ) +); +#endif + +#include "define_cmd.h" diff --git a/plugins/meson.build b/plugins/meson.build index 97e155a0cd..7795e07d32 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -7,6 +7,7 @@ sources += [ 'plugins/dera/dera-nvme.c', 'plugins/fdp/fdp.c', 'plugins/huawei/huawei-nvme.c', + 'plugins/ibm/ibm-nvme.c', 'plugins/innogrit/innogrit-nvme.c', 'plugins/inspur/inspur-nvme.c', 'plugins/intel/intel-nvme.c',