From 2e650c1f5f2f79e7db10dec5dcdd1cffcaf47891 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 26 Jul 2022 07:11:40 +0200 Subject: Merging upstream version 2.1~rc0 (Closes: #1015722). Signed-off-by: Daniel Baumann --- plugins/amzn/amzn-nvme.c | 1 + plugins/amzn/amzn-nvme.h | 1 + plugins/dell/dell-nvme.c | 54 + plugins/dell/dell-nvme.h | 18 + plugins/dera/dera-nvme.c | 4 +- plugins/dera/dera-nvme.h | 1 + plugins/huawei/huawei-nvme.c | 64 +- plugins/huawei/huawei-nvme.h | 1 + plugins/innogrit/innogrit-nvme.c | 402 +++++ plugins/innogrit/innogrit-nvme.h | 20 + plugins/innogrit/typedef.h | 72 + plugins/intel/intel-nvme.c | 51 +- plugins/intel/intel-nvme.h | 1 + plugins/memblaze/memblaze-nvme.c | 97 +- plugins/memblaze/memblaze-nvme.h | 1 + plugins/memblaze/memblaze-utils.h | 37 +- plugins/meson.build | 6 + plugins/micron/micron-nvme.c | 593 +++++-- plugins/micron/micron-nvme.h | 1 + plugins/netapp/netapp-nvme.c | 38 +- plugins/netapp/netapp-nvme.h | 1 + plugins/nvidia/nvidia-nvme.c | 1 + plugins/nvidia/nvidia-nvme.h | 1 + plugins/ocp/ocp-nvme.c | 18 +- plugins/ocp/ocp-nvme.h | 2 +- plugins/scaleflux/sfx-nvme.c | 328 +++- plugins/scaleflux/sfx-nvme.h | 1 + plugins/seagate/seagate-diag.h | 1 + plugins/seagate/seagate-nvme.c | 85 +- plugins/seagate/seagate-nvme.h | 1 + plugins/shannon/shannon-nvme.c | 6 +- plugins/shannon/shannon-nvme.h | 1 + plugins/solidigm/meson.build | 5 + plugins/solidigm/solidigm-garbage-collection.c | 111 ++ plugins/solidigm/solidigm-garbage-collection.h | 8 + plugins/solidigm/solidigm-latency-tracking.c | 470 ++++++ plugins/solidigm/solidigm-latency-tracking.h | 9 + plugins/solidigm/solidigm-nvme.c | 30 + plugins/solidigm/solidigm-nvme.h | 28 + plugins/solidigm/solidigm-smart.c | 250 +++ plugins/solidigm/solidigm-smart.h | 8 + plugins/toshiba/toshiba-nvme.c | 5 + plugins/toshiba/toshiba-nvme.h | 1 + plugins/transcend/transcend-nvme.c | 5 +- plugins/transcend/transcend-nvme.h | 1 + plugins/virtium/virtium-nvme.c | 37 +- plugins/virtium/virtium-nvme.h | 1 + plugins/wdc/wdc-nvme.c | 2140 ++++++++++++++++++++---- plugins/wdc/wdc-nvme.h | 7 +- plugins/wdc/wdc-utils.c | 1 + plugins/wdc/wdc-utils.h | 1 + plugins/ymtc/ymtc-nvme.c | 35 +- plugins/ymtc/ymtc-nvme.h | 1 + plugins/ymtc/ymtc-utils.h | 1 + plugins/zns/zns.c | 16 +- plugins/zns/zns.h | 1 + 56 files changed, 4409 insertions(+), 672 deletions(-) create mode 100644 plugins/dell/dell-nvme.c create mode 100644 plugins/dell/dell-nvme.h create mode 100644 plugins/innogrit/innogrit-nvme.c create mode 100644 plugins/innogrit/innogrit-nvme.h create mode 100644 plugins/innogrit/typedef.h create mode 100644 plugins/solidigm/meson.build create mode 100644 plugins/solidigm/solidigm-garbage-collection.c create mode 100644 plugins/solidigm/solidigm-garbage-collection.h create mode 100644 plugins/solidigm/solidigm-latency-tracking.c create mode 100644 plugins/solidigm/solidigm-latency-tracking.h create mode 100644 plugins/solidigm/solidigm-nvme.c create mode 100644 plugins/solidigm/solidigm-nvme.h create mode 100644 plugins/solidigm/solidigm-smart.c create mode 100644 plugins/solidigm/solidigm-smart.h (limited to 'plugins') diff --git a/plugins/amzn/amzn-nvme.c b/plugins/amzn/amzn-nvme.c index cd7d555..e04aa53 100644 --- a/plugins/amzn/amzn-nvme.c +++ b/plugins/amzn/amzn-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include diff --git a/plugins/amzn/amzn-nvme.h b/plugins/amzn/amzn-nvme.h index f969c0e..f6c4f8b 100644 --- a/plugins/amzn/amzn-nvme.h +++ b/plugins/amzn/amzn-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/amzn/amzn-nvme diff --git a/plugins/dell/dell-nvme.c b/plugins/dell/dell-nvme.c new file mode 100644 index 0000000..8ed10e7 --- /dev/null +++ b/plugins/dell/dell-nvme.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright © 2022 Dell Inc. or its subsidiaries. All Rights Reserved. +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" + +#define CREATE_CMD +#include "dell-nvme.h" + +#define ARRAY_NAME_LEN 80 + +struct nvme_vu_id_ctrl_field { + __u16 dell_mjr; + __u16 dell_mnr; + __u16 dell_ter; + __u8 reserved0[1018]; +}; + +static void dell_id_ctrl(__u8 *vs, struct json_object *root) +{ + struct nvme_vu_id_ctrl_field *id = (struct nvme_vu_id_ctrl_field *)vs; + char array_ver[16] = { 0 }; + char array_name[ARRAY_NAME_LEN + 1] = {0}; + + snprintf(array_ver, sizeof(array_ver), "0x%04x%04x%04x", + le16_to_cpu(id->dell_mjr), + le16_to_cpu(id->dell_mnr), + le16_to_cpu(id->dell_ter)); + + memcpy(array_name, vs + sizeof(array_ver), ARRAY_NAME_LEN); + + if (root) { + json_object_add_value_string(root, "array_name", strlen(array_name) > 1 ? array_name : "NULL"); + json_object_add_value_string(root, "array_ver", array_ver); + return; + } + + printf("array_name : %s\n", strlen(array_name) > 1 ? array_name : "NULL"); + printf("array_ver : %s\n", array_ver); +} + +static int id_ctrl(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + return __id_ctrl(argc, argv, cmd, plugin, dell_id_ctrl); +} diff --git a/plugins/dell/dell-nvme.h b/plugins/dell/dell-nvme.h new file mode 100644 index 0000000..aaf0de1 --- /dev/null +++ b/plugins/dell/dell-nvme.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/dell/dell-nvme + +#if !defined(DELL_NVME) || defined(CMD_HEADER_MULTI_READ) +#define DELL_NVME + +#include "cmd.h" + +PLUGIN(NAME("dell", "DELL vendor specific extensions", NVME_VERSION), + COMMAND_LIST( + ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl) + ) +); + +#endif + +#include "define_cmd.h" diff --git a/plugins/dera/dera-nvme.c b/plugins/dera/dera-nvme.c index f36fc67..1390be0 100644 --- a/plugins/dera/dera-nvme.c +++ b/plugins/dera/dera-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -186,7 +187,7 @@ static int get_status(int argc, char **argv, struct command *cmd, struct plugin printf("fw_loader_version : %.*s\n", 8, log.fw_loader_version); printf("uefi_driver_version : %.*s\n", 8, log.uefi_driver_version); - if (log.pcie_volt_status <= sizeof(volt_status) / sizeof(const char *)){ + if (log.pcie_volt_status < sizeof(volt_status) / sizeof(const char *)){ printf("pcie_volt_status : %s\n", volt_status[log.pcie_volt_status]); } else{ @@ -204,6 +205,7 @@ exit: if (err > 0) nvme_show_status(err); + close(fd); return err; } diff --git a/plugins/dera/dera-nvme.h b/plugins/dera/dera-nvme.h index d3a8b0b..a5bb0ae 100644 --- a/plugins/dera/dera-nvme.h +++ b/plugins/dera/dera-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/dera/dera-nvme diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index 116025b..572086c 100644 --- a/plugins/huawei/huawei-nvme.c +++ b/plugins/huawei/huawei-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017-2019 Huawei Corporation or its affiliates. * @@ -92,7 +93,8 @@ static int huawei_get_nvme_info(int fd, struct huawei_list_item *item, const cha if (err < 0) return err; - strcpy(item->node, node); + strncpy(item->node, node, sizeof(item->node)); + item->node[sizeof(item->node) - 1] = '\0'; item->block = S_ISBLK(nvme_stat_info.st_mode); if (item->ns.vs[0] == 0) { @@ -204,20 +206,20 @@ static void huawei_print_list_head(struct huawei_list_element_len element_len) element_len.usage, dash, element_len.array_name, dash); } -static void huawei_print_list_item(struct huawei_list_item list_item, - struct huawei_list_element_len element_len) +static void huawei_print_list_item(struct huawei_list_item *list_item, + struct huawei_list_element_len element_len) { __u8 lba_index; - nvme_id_ns_flbas_to_lbaf_inuse(list_item.ns.flbas, &lba_index); - long long int lba = 1 << list_item.ns.lbaf[lba_index].ds; - double nsze = le64_to_cpu(list_item.ns.nsze) * lba; - double nuse = le64_to_cpu(list_item.ns.nuse) * lba; + nvme_id_ns_flbas_to_lbaf_inuse(list_item->ns.flbas, &lba_index); + unsigned long long int lba = 1ULL << list_item->ns.lbaf[lba_index].ds; + double nsze = le64_to_cpu(list_item->ns.nsze) * lba; + double nuse = le64_to_cpu(list_item->ns.nuse) * lba; const char *s_suffix = suffix_si_get(&nsze); const char *u_suffix = suffix_si_get(&nuse); char usage[128]; - char nguid_buf[2 * sizeof(list_item.ns.nguid) + 1]; + char nguid_buf[2 * sizeof(list_item->ns.nguid) + 1]; char *nguid = nguid_buf; int i; @@ -225,16 +227,17 @@ static void huawei_print_list_item(struct huawei_list_item list_item, nsze, s_suffix); memset(nguid, 0, sizeof(nguid_buf)); - for (i = 0; i < sizeof(list_item.ns.nguid); i++) - nguid += sprintf(nguid, "%02x", list_item.ns.nguid[i]); + for (i = 0; i < sizeof(list_item->ns.nguid); i++) + nguid += sprintf(nguid, "%02x", list_item->ns.nguid[i]); printf("%-*.*s %-*.*s %-*.*s %-*d %-*.*s %-*.*s\n", - element_len.node, element_len.node, list_item.node, - element_len.ns_name, element_len.ns_name, list_item.ns_name, + element_len.node, element_len.node, list_item->node, + element_len.ns_name, element_len.ns_name, list_item->ns_name, element_len.nguid, element_len.nguid, nguid_buf, - element_len.ns_id, list_item.nsid, + element_len.ns_id, list_item->nsid, element_len.usage, element_len.usage, usage, - element_len.array_name, element_len.array_name, list_item.array_name); + element_len.array_name, element_len.array_name, + list_item->array_name); } @@ -287,7 +290,7 @@ static void huawei_print_list_items(struct huawei_list_item *list_items, unsigne huawei_print_list_head(element_len); for (i = 0 ; i < len ; i++) - huawei_print_list_item(list_items[i], element_len); + huawei_print_list_item(&list_items[i], element_len); } static int huawei_list(int argc, char **argv, struct command *command, @@ -296,7 +299,7 @@ static int huawei_list(int argc, char **argv, struct command *command, char path[264]; struct dirent **devices; struct huawei_list_item *list_items; - unsigned int i, n, fd, ret; + unsigned int i, n, ret; unsigned int huawei_num = 0; int fmt; const char *desc = "Retrieve basic information for the given huawei device"; @@ -313,7 +316,10 @@ static int huawei_list(int argc, char **argv, struct command *command, OPT_END() }; - argconfig_parse(argc, argv, desc, opts); + ret = argconfig_parse(argc, argv, desc, opts); + if (ret) + return ret; + fmt = validate_output_format(cfg.output_format); if (fmt != JSON && fmt != NORMAL) return -EINVAL; @@ -325,18 +331,29 @@ static int huawei_list(int argc, char **argv, struct command *command, list_items = calloc(n, sizeof(*list_items)); if (!list_items) { fprintf(stderr, "can not allocate controller list payload\n"); - return ENOMEM; + ret = ENOMEM; + goto out_free_devices; } for (i = 0; i < n; i++) { + int fd; + snprintf(path, sizeof(path), "/dev/%s", devices[i]->d_name); fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open device %s: %s\n", + path, strerror(errno)); + continue; + } ret = huawei_get_nvme_info(fd, &list_items[huawei_num], path); - if (ret) - return ret; + if (ret) { + close(fd); + goto out_free_list_items; + } if (list_items[huawei_num].huawei_device == true) { huawei_num++; } + close(fd); } if (huawei_num > 0){ @@ -345,13 +362,14 @@ static int huawei_list(int argc, char **argv, struct command *command, else huawei_print_list_items(list_items, huawei_num); } - +out_free_list_items: + free(list_items); +out_free_devices: for (i = 0; i < n; i++) free(devices[i]); free(devices); - free(list_items); - return 0; + return ret; } static void huawei_do_id_ctrl(__u8 *vs, struct json_object *root) diff --git a/plugins/huawei/huawei-nvme.h b/plugins/huawei/huawei-nvme.h index 175ddd5..f49e6fd 100644 --- a/plugins/huawei/huawei-nvme.h +++ b/plugins/huawei/huawei-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/huawei/huawei-nvme diff --git a/plugins/innogrit/innogrit-nvme.c b/plugins/innogrit/innogrit-nvme.c new file mode 100644 index 0000000..214fe72 --- /dev/null +++ b/plugins/innogrit/innogrit-nvme.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "nvme-print.h" +#include "typedef.h" + +#define CREATE_CMD +#include "innogrit-nvme.h" + +static int innogrit_smart_log_additional(int argc, char **argv, + struct command *command, + struct plugin *plugin) +{ + struct nvme_smart_log smart_log = { 0 }; + int fd, i, iindex; + struct vsc_smart_log *pvsc_smart = (struct vsc_smart_log *)smart_log.rsvd232; + const char *desc = "Retrieve additional SMART log for the given device "; + const char *namespace = "(optional) desired namespace"; + + struct config { + __u32 namespace_id; + }; + + struct config cfg = { + .namespace_id = NVME_NSID_ALL, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + nvme_get_log_smart(fd, cfg.namespace_id, true, &smart_log); + nvme_show_smart_log(&smart_log, cfg.namespace_id, devicename, NORMAL); + + printf("DW0[0-1] Defect Cnt : %u\n", pvsc_smart->defect_cnt); + printf("DW0[2-3] Slc Spb Cnt : %u\n", pvsc_smart->slc_spb_cnt); + printf("DW1 Slc Total Ec Cnt : %u\n", pvsc_smart->slc_total_ec_cnt); + printf("DW2 Slc Max Ec Cnt : %u\n", pvsc_smart->slc_max_ec_cnt); + printf("DW3 Slc Min Ec Cnt : %u\n", pvsc_smart->slc_min_ec_cnt); + printf("DW4 Slc Avg Ec Cnt : %u\n", pvsc_smart->slc_avg_ec_cnt); + printf("DW5 Total Ec Cnt : %u\n", pvsc_smart->total_ec_cnt); + printf("DW6 Max Ec Cnt : %u\n", pvsc_smart->max_ec_cnt); + printf("DW7 Min Ec Cnt : %u\n", pvsc_smart->min_ec_cnt); + printf("DW8 Avg Ec Cnt : %u\n", pvsc_smart->avg_ec_cnt); + printf("DW9 Mrd Rr Good Cnt : %u\n", pvsc_smart->mrd_rr_good_cnt); + printf("DW10 Ard Rr Good Cnt : %u\n", pvsc_smart->ard_rr_good_cnt); + printf("DW11 Preset Cnt : %u\n", pvsc_smart->preset_cnt); + printf("DW12 Nvme Reset Cnt : %u\n", pvsc_smart->nvme_reset_cnt); + printf("DW13 Low Pwr Cnt : %u\n", pvsc_smart->low_pwr_cnt); + printf("DW14 Wa : %u\n", pvsc_smart->wa); + printf("DW15 Ps3 Entry Cnt : %u\n", pvsc_smart->ps3_entry_cnt); + printf("DW16[0] highest_temp[0] : %u\n", pvsc_smart->highest_temp[0]); + printf("DW16[1] highest_temp[1] : %u\n", pvsc_smart->highest_temp[1]); + printf("DW16[2] highest_temp[2] : %u\n", pvsc_smart->highest_temp[2]); + printf("DW16[3] highest_temp[3] : %u\n", pvsc_smart->highest_temp[3]); + printf("DW17 weight_ec : %u\n", pvsc_smart->weight_ec); + printf("DW18 slc_cap_mb : %u\n", pvsc_smart->slc_cap_mb); + printf("DW19-20 nand_page_write_cnt : %llu\n", pvsc_smart->nand_page_write_cnt); + + iindex = 21; + for (i = 0; i < (sizeof(pvsc_smart->reserved2)/4); i++) { + if (pvsc_smart->reserved2[i] != 0) + printf("DW%-37d : %u\n", iindex, pvsc_smart->reserved2[i]); + iindex++; + } + + return 0; +} + +static int sort_eventlog_fn(const void *a, const void *b) +{ + const struct eventlog_addindex *l = a; + const struct eventlog_addindex *r = b; + int rc; + + if (l->ms > r->ms) { + rc = 1; + } else if (l->ms < r->ms) { + rc = -1; + } else { + if (l->iindex < r->iindex) + rc = -1; + else + rc = 1; + } + + return rc; +} + +static void sort_eventlog(struct eventlog *data16ksrc, unsigned int icount) +{ + struct eventlog_addindex peventlogadd[512]; + unsigned int i; + + for (i = 0; i < icount; i++) { + memcpy(&peventlogadd[i], &data16ksrc[i], sizeof(struct eventlog)); + peventlogadd[i].iindex = i; + } + + qsort(peventlogadd, icount, sizeof(struct eventlog_addindex), sort_eventlog_fn); + + for (i = 0; i < icount; i++) + memcpy(&data16ksrc[i], &peventlogadd[i], sizeof(struct eventlog)); +} + +static unsigned char setfilecontent(char *filenamea, unsigned char *buffer, + unsigned int buffersize) +{ + FILE *fp = NULL; + int rc; + + if (buffersize == 0) + return true; + fp = fopen(filenamea, "a+"); + rc = fwrite(buffer, 1, buffersize, fp); + fclose(fp); + if (rc != buffersize) + return false; + return true; +} + +static int nvme_vucmd(int fd, unsigned char opcode, unsigned int cdw12, + unsigned int cdw13, unsigned int cdw14, + unsigned int cdw15, char *data, int data_len) +{ + struct nvme_passthru_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = opcode; + cmd.cdw12 = cdw12; + cmd.cdw13 = cdw13; + cmd.cdw14 = cdw14; + cmd.cdw15 = cdw15; + cmd.nsid = 0; + cmd.addr = (__u64)(__u64)(uintptr_t)data; + cmd.data_len = data_len; + return nvme_submit_admin_passthru(fd, &cmd, NULL); +} + +static int innogrit_vsc_geteventlog(int argc, char **argv, + struct command *command, + struct plugin *plugin) +{ + time_t timep; + struct tm *logtime; + int icount, ioffset16k, iblock; + char currentdir[128], filename[512]; + unsigned char data[4096], data16k[SIZE_16K], zerob[32]; + unsigned int *pcheckdata; + unsigned int isize, icheck_stopvalue, iend; + unsigned char bSortLog = false, bget_nextlog = true; + struct evlg_flush_hdr *pevlog = (struct evlg_flush_hdr *)data; + int fd, ret = -1; + const char *desc = "Recrieve event log for the given device "; + const char *clean_opt = "(optional) 1 for clean event log"; + + struct config { + __u32 clean_flg; + }; + + struct config cfg = { + .clean_flg = 0, + }; + + OPT_ARGS(opts) = { + OPT_UINT("clean_flg", 'c', &cfg.clean_flg, clean_opt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + + if (getcwd(currentdir, 128) == NULL) + return -1; + + time(&timep); + logtime = localtime(&timep); + sprintf(filename, "%s/eventlog_%02d%02d-%02d%02d%02d.elog", currentdir, logtime->tm_mon+1, + logtime->tm_mday, logtime->tm_hour, logtime->tm_min, logtime->tm_sec); + + iblock = 0; + ioffset16k = 0; + memset(data16k, 0, SIZE_16K); + memset(zerob, 0, 32); + + icount = 0; + while (bget_nextlog) { + if (icount % 100 == 0) { + printf("\rWait for Dump EventLog " XCLEAN_LINE); + fflush(stdout); + icount = 0; + } else if (icount % 5 == 0) { + printf("."); + fflush(stdout); + } + icount++; + + memset(data, 0, 4096); + ret = nvme_vucmd(fd, NVME_VSC_GET_EVENT_LOG, 0, 0, (SRB_SIGNATURE >> 32), + (SRB_SIGNATURE & 0xFFFFFFFF), (char *)data, 4096); + if (ret == -1) + return ret; + + pcheckdata = (unsigned int *)&data[4096 - 32]; + icheck_stopvalue = pcheckdata[1]; + + if (icheck_stopvalue == 0xFFFFFFFF) { + isize = pcheckdata[0]; + if (isize == 0) { + /* Finish Log */ + bget_nextlog = false; + } else if (bSortLog) { + /* No Full 4K Package */ + for (iend = 0; iend < isize - 32; iend += sizeof(struct eventlog)) { + if (memcmp(&data[iend], zerob, sizeof(struct eventlog)) != 0) { + memcpy(&data16k[ioffset16k], &data[iend], sizeof(struct eventlog)); + ioffset16k += sizeof(struct eventlog); + } + } + } else { + setfilecontent(filename, data, isize); + } + } else { + /* Full 4K Package */ + if ((pevlog->signature == EVLOG_SIG) && (pevlog->log_type == 1)) + bSortLog = true; + + if (bSortLog) { + for (iend = 0; iend < SIZE_4K; iend += sizeof(struct eventlog)) { + if (memcmp(&data[iend], zerob, sizeof(struct eventlog)) != 0) { + memcpy(&data16k[ioffset16k], &data[iend], sizeof(struct eventlog)); + ioffset16k += sizeof(struct eventlog); + } + } + + iblock++; + if (iblock == 4) { + sort_eventlog((struct eventlog *)(data16k + sizeof(struct evlg_flush_hdr)), + (ioffset16k - sizeof(struct evlg_flush_hdr))/sizeof(struct eventlog)); + setfilecontent(filename, data16k, ioffset16k); + ioffset16k = 0; + iblock = 0; + memset(data16k, 0, SIZE_16K); + } + } else { + setfilecontent(filename, data, SIZE_4K); + } + + } + } + + if (bSortLog) { + if (ioffset16k > 0) { + sort_eventlog((struct eventlog *)(data16k + sizeof(struct evlg_flush_hdr)), + (ioffset16k - sizeof(struct evlg_flush_hdr))/sizeof(struct eventlog)); + setfilecontent(filename, data16k, ioffset16k); + } + } + + printf("\r" XCLEAN_LINE "Dump eventLog finish to %s\n", filename); + chmod(filename, 0666); + + if (cfg.clean_flg == 1) { + printf("Clean eventlog\n"); + nvme_vucmd(fd, NVME_VSC_CLEAN_EVENT_LOG, 0, 0, (SRB_SIGNATURE >> 32), + (SRB_SIGNATURE & 0xFFFFFFFF), (char *)NULL, 0); + } + + return ret; +} + +static int innogrit_vsc_getcdump(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + time_t timep; + struct tm *logtime; + char currentdir[128], filename[512], fname[128]; + unsigned int itotal, icur; + unsigned char data[4096]; + struct cdumpinfo cdumpinfo; + unsigned char busevsc = false; + unsigned int ipackcount, ipackindex; + char fwvera[32]; + int fd, ret = -1; + const char *desc = "Recrieve cdump data for the given device "; + + OPT_ARGS(opts) = { + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + if (getcwd(currentdir, 128) == NULL) + return -1; + + time(&timep); + logtime = localtime(&timep); + + ipackindex = 0; + memset(data, 0, 4096); + if (nvme_vucmd(fd, NVME_VSC_GET, VSC_FN_GET_CDUMP, 0x00, (SRB_SIGNATURE >> 32), + (SRB_SIGNATURE & 0xFFFFFFFF), (char *)data, 4096) == 0) { + memcpy(&cdumpinfo, &data[3072], sizeof(cdumpinfo)); + if (cdumpinfo.sig == 0x5a5b5c5d) { + busevsc = true; + ipackcount = cdumpinfo.ipackcount; + if (ipackcount == 0) { + itotal = 0; + } else { + itotal = cdumpinfo.cdumppack[ipackindex].ilenth; + memset(fwvera, 0, sizeof(fwvera)); + memcpy(fwvera, cdumpinfo.cdumppack[ipackindex].fwver, 8); + sprintf(fname, "cdump_%02d%02d-%02d%02d%02d_%d_%s.cdp", logtime->tm_mon+1, + logtime->tm_mday, logtime->tm_hour, logtime->tm_min, logtime->tm_sec, + ipackindex, fwvera); + sprintf(filename, "%s/%s", currentdir, fname); + } + } + } + + if (busevsc == false) { + memset(data, 0, 4096); + ret = nvme_get_nsid_log(fd, true, 0x07, NVME_NSID_ALL, 4096, data); + if (ret != 0) + return ret; + + ipackcount = 1; + memcpy(&itotal, &data[4092], 4); + sprintf(fname, "cdump_%02d%02d-%02d%02d%02d.cdp", logtime->tm_mon+1, logtime->tm_mday, + logtime->tm_hour, logtime->tm_min, logtime->tm_sec); + sprintf(filename, "%s/%s", currentdir, fname); + } + + if (itotal == 0) { + printf("no cdump data\n"); + return 0; + } + + while (ipackindex < ipackcount) { + memset(data, 0, 4096); + strcpy((char *)data, "cdumpstart"); + setfilecontent(filename, data, strlen((char *)data)); + for (icur = 0; icur < itotal; icur += 4096) { + memset(data, 0, 4096); + if (busevsc) + ret = nvme_vucmd(fd, NVME_VSC_GET, VSC_FN_GET_CDUMP, 0x00, (SRB_SIGNATURE >> 32), + (SRB_SIGNATURE & 0xFFFFFFFF), (char *)data, 4096); + else + ret = nvme_get_nsid_log(fd, true, 0x07, NVME_NSID_ALL, 4096, data); + if (ret != 0) + return ret; + + setfilecontent(filename, data, 4096); + + printf("\rWait for dump data %d%%" XCLEAN_LINE, ((icur+4096) * 100/itotal)); + } + memset(data, 0, 4096); + strcpy((char *)data, "cdumpend"); + setfilecontent(filename, data, strlen((char *)data)); + printf("\r%s\n", fname); + ipackindex++; + if (ipackindex != ipackcount) { + memset(data, 0, 4096); + if (busevsc) + ret = nvme_vucmd(fd, NVME_VSC_GET, VSC_FN_GET_CDUMP, 0x00, (SRB_SIGNATURE >> 32), + (SRB_SIGNATURE & 0xFFFFFFFF), (char *)data, 4096); + else + ret = nvme_get_nsid_log(fd, true, 0x07, NVME_NSID_ALL, 4096, data); + if (ret != 0) + return ret; + + itotal = cdumpinfo.cdumppack[ipackindex].ilenth; + memset(fwvera, 0, sizeof(fwvera)); + memcpy(fwvera, cdumpinfo.cdumppack[ipackindex].fwver, 8); + sprintf(fname, "cdump_%02d%02d-%02d%02d%02d_%d_%s.cdp", logtime->tm_mon+1, + logtime->tm_mday, logtime->tm_hour, logtime->tm_min, logtime->tm_sec, + ipackindex, fwvera); + sprintf(filename, "%s/%s", currentdir, fname); + } + + } + + printf("\n"); + return ret; +} diff --git a/plugins/innogrit/innogrit-nvme.h b/plugins/innogrit/innogrit-nvme.h new file mode 100644 index 0000000..2de0502 --- /dev/null +++ b/plugins/innogrit/innogrit-nvme.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/innogrit/innogrit-nvme + +#if !defined(INNOGRIT_NVME) || defined(CMD_HEADER_MULTI_READ) +#define INNOGRIT_NVME + +#include "cmd.h" + +PLUGIN(NAME("innogrit", "innogrit vendor specific extensions", NVME_VERSION), + COMMAND_LIST( + ENTRY("smart-log-add", "Retrieve innogrit SMART Log, show it", innogrit_smart_log_additional) + ENTRY("get-eventlog", "get event log", innogrit_vsc_geteventlog) + ENTRY("get-cdump", "get cdump data", innogrit_vsc_getcdump) + ) +); + +#endif + +#include "define_cmd.h" diff --git a/plugins/innogrit/typedef.h b/plugins/innogrit/typedef.h new file mode 100644 index 0000000..d4ea269 --- /dev/null +++ b/plugins/innogrit/typedef.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#define SIZE_4K 4096 +#define SIZE_16K 16384 + +#define NVME_VSC_GET_EVENT_LOG 0xC2 +#define NVME_VSC_CLEAN_EVENT_LOG 0xD8 +#define NVME_VSC_GET 0xE6 +#define VSC_FN_GET_CDUMP 0x08 +#define EVLOG_SIG 0x65766C67 +#define SRB_SIGNATURE 0x544952474F4E4E49ULL +#define XCLEAN_LINE "\033[K" + +struct evlg_flush_hdr { + unsigned int signature; + unsigned int fw_ver[2]; + unsigned int fw_type : 8; + unsigned int log_type : 8; + unsigned int project : 16; + unsigned int trace_cnt; + unsigned int sout_crc; + unsigned int reserved[2]; +}; + +struct eventlog { + unsigned int ms; + unsigned int param[7]; +}; + +struct eventlog_addindex { + unsigned int ms; + unsigned int param[7]; + unsigned int iindex; +}; + +#pragma pack(push) +#pragma pack(1) +struct vsc_smart_log { + unsigned short defect_cnt; + unsigned short slc_spb_cnt; + unsigned int slc_total_ec_cnt; + unsigned int slc_max_ec_cnt; + unsigned int slc_min_ec_cnt; + unsigned int slc_avg_ec_cnt; + unsigned int total_ec_cnt; + unsigned int max_ec_cnt; + unsigned int min_ec_cnt; + unsigned int avg_ec_cnt; + unsigned int mrd_rr_good_cnt; + unsigned int ard_rr_good_cnt; + unsigned int preset_cnt; + unsigned int nvme_reset_cnt; + unsigned int low_pwr_cnt; + unsigned int wa; + unsigned int ps3_entry_cnt; + u_char highest_temp[4]; + unsigned int weight_ec; + unsigned int slc_cap_mb; + unsigned long long nand_page_write_cnt; + unsigned int reserved2[49]; +}; +#pragma pack(pop) + +struct cdump_pack { + unsigned int ilenth; + char fwver[8]; +}; + +struct cdumpinfo { + unsigned int sig; + unsigned int ipackcount; + struct cdump_pack cdumppack[32]; +}; diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c index 80d218c..1bf6627 100644 --- a/plugins/intel/intel-nvme.c +++ b/plugins/intel/intel-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -179,7 +180,7 @@ static void show_intel_smart_log_jsn(struct nvme_additional_smart_log *smart, entry_stats = json_create_object(); json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_media_wear.norm); - json_object_add_value_float(entry_stats, "raw", ((long double)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024); + json_object_add_value_double(entry_stats, "raw", ((long double)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024); json_object_add_value_object(dev_stats, "timed_workload_media_wear", entry_stats); entry_stats = json_create_object(); @@ -377,6 +378,7 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, } else if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -412,6 +414,7 @@ static int get_market_log(int argc, char **argv, struct command *cmd, struct plu d_raw((unsigned char *)&log, sizeof(log)); } else if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -472,6 +475,7 @@ static int get_temp_stats_log(int argc, char **argv, struct command *cmd, struct d_raw((unsigned char *)&stats, sizeof(stats)); } else if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -1105,21 +1109,19 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct sizeof(struct intel_lat_stats)); } - if (!err) { - if (cfg.json) - json_lat_stats(cfg.write); - else if (!cfg.raw_binary) - show_lat_stats(cfg.write); - else { - if (media_version[0] == 1000) - d_raw((unsigned char *)&v1000_stats, - sizeof(v1000_stats)); - else - d_raw((unsigned char *)&stats, - sizeof(stats)); - } - } else if (err > 0) - nvme_show_status(err); + if (cfg.json) + json_lat_stats(cfg.write); + else if (!cfg.raw_binary) + show_lat_stats(cfg.write); + else { + if (media_version[0] == 1000) + d_raw((unsigned char *)&v1000_stats, + sizeof(v1000_stats)); + else + d_raw((unsigned char *)&stats, + sizeof(stats)); + } + close_fd: close(fd); return err; @@ -1385,14 +1387,14 @@ static int get_internal_log(int argc, char **argv, struct command *command, } if (cfg.log > 2 || cfg.core > 4 || cfg.lnum > 255) { - free(intel); - return EINVAL; + err = -EINVAL; + goto out_free; } if (!cfg.file) { err = setup_file(f, cfg.file, fd, cfg.log); if (err) - goto out; + goto out_free; cfg.file = f; } @@ -1403,6 +1405,10 @@ static int get_internal_log(int argc, char **argv, struct command *command, cdlog.u.fields.selectNlog = cfg.lnum < 0 ? 0 : cfg.lnum; output = open(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (output < 0) { + err = output; + goto out_free; + } err = read_header(&cmd, buf, fd, cdlog.u.entireDword, cfg.namespace_id); if (err) @@ -1494,7 +1500,7 @@ static int get_internal_log(int argc, char **argv, struct command *command, } } err = 0; - out: +out: if (err > 0) { nvme_show_status(err); } else if (err < 0) { @@ -1502,7 +1508,10 @@ static int get_internal_log(int argc, char **argv, struct command *command, err = EIO; } else printf("Successfully wrote log to %s\n", cfg.file); + close(output); +out_free: free(intel); + close(fd); return err; } @@ -1597,6 +1606,7 @@ static int enable_lat_stats_tracking(int argc, char **argv, fid, result); } else { printf("Could not read feature id 0xE2.\n"); + close(fd); return err; } break; @@ -1617,6 +1627,7 @@ static int enable_lat_stats_tracking(int argc, char **argv, printf("%d not supported.\n", option); return EINVAL; } + close(fd); return fd; } diff --git a/plugins/intel/intel-nvme.h b/plugins/intel/intel-nvme.h index af1231a..165048a 100644 --- a/plugins/intel/intel-nvme.h +++ b/plugins/intel/intel-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/intel/intel-nvme diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c index c0f4d66..a6a8ddf 100644 --- a/plugins/memblaze/memblaze-nvme.c +++ b/plugins/memblaze/memblaze-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -161,6 +162,15 @@ static void show_memblaze_smart_log_new(struct nvme_memblaze_smart_log *s, u8 *nm = malloc(NM_SIZE * sizeof(u8)); u8 *raw = malloc(RAW_SIZE * sizeof(u8)); + if (!nm) { + if (raw) + free(raw); + return; + } + if (!raw) { + free(nm); + return; + } /* Table Title */ printf("%s:%s %s:%x\n", STRN2_01, devname, STRN2_02, nsid); /* Clumn Name*/ @@ -242,11 +252,11 @@ static void show_memblaze_smart_log_new(struct nvme_memblaze_smart_log *s, static void show_memblaze_smart_log_old(struct nvme_memblaze_smart_log *smart, unsigned int nsid, const char *devname, const char *fw_ver) { - char fw_ver_local[STR_VER_SIZE]; + char fw_ver_local[STR_VER_SIZE + 1]; struct nvme_memblaze_smart_log_item *item; strncpy(fw_ver_local, fw_ver, STR_VER_SIZE); - *(fw_ver_local + STR_VER_SIZE - 1) = '\0'; + *(fw_ver_local + STR_VER_SIZE) = '\0'; printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", devname, nsid); @@ -342,6 +352,15 @@ static void show_memblaze_smart_log_old(struct nvme_memblaze_smart_log *smart, u8 *nm = malloc(NM_SIZE * sizeof(u8)); u8 *raw = malloc(RAW_SIZE * sizeof(u8)); + if (!nm) { + if (raw) + free(raw); + return; + } + if (!raw) { + free(nm); + return; + } /* 00 RAISIN_SI_VD_PROGRAM_FAIL */ get_memblaze_new_smart_info(s, PROGRAM_FAIL, nm, raw); printf("%-32s : %3d%% %"PRIu64"\n", @@ -407,11 +426,13 @@ int parse_params(char *str, int number, ...) c = strtok(str, ","); if ( c == NULL) { printf("No enough parameters. abort...\n"); - exit(EINVAL); + va_end(argp); + return 1; } if (isalnum((int)*c) == 0) { printf("%s is not a valid number\n", c); + va_end(argp); return 1; } value = atoi(c); @@ -467,6 +488,7 @@ static int mb_get_additional_smart_log(int argc, char **argv, struct command *cm if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -517,6 +539,7 @@ static int mb_get_powermanager_status(int argc, char **argv, struct command *cmd nvme_select_to_string(0), result); } else if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -574,6 +597,7 @@ static int mb_set_powermanager_status(int argc, char **argv, struct command *cmd } else if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -613,11 +637,13 @@ static int mb_set_high_latency_log(int argc, char **argv, struct command *cmd, s if (parse_params(cfg.param, 2, ¶m1, ¶m2)) { printf("setfeature: invalid formats %s\n", cfg.param); - exit(EINVAL); + close(fd); + return EINVAL; } if ((param1 == 1) && (param2 < P2MIN || param2 > P2MAX)) { printf("setfeature: invalid high io latency threshold %d\n", param2); - exit(EINVAL); + close(fd); + return EINVAL; } cfg.value = (param1 << MB_FEAT_HIGH_LATENCY_VALUE_SHIFT) | param2; @@ -646,6 +672,7 @@ static int mb_set_high_latency_log(int argc, char **argv, struct command *cmd, s } else if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -726,9 +753,14 @@ static int glp_high_latency(FILE *fdi, char *buf, int buflen, int print) 1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, millisec); } - fprintf(fdi, "%-32s %-7x %-6x %-6x %-8x %4x%08x %-8x %-d\n", - string, logEntry->opcode, logEntry->sqe, logEntry->cid, logEntry->nsid, - (__u32)(logEntry->sLBA >> 32), (__u32)logEntry->sLBA, logEntry->numLBA, logEntry->latency); + if (fdi) { + fprintf(fdi, "%-32s %-7x %-6x %-6x %-8x %4x%08x %-8x %-d\n", + string, logEntry->opcode, logEntry->sqe, + logEntry->cid, logEntry->nsid, + (__u32)(logEntry->sLBA >> 32), + (__u32)logEntry->sLBA, logEntry->numLBA, + logEntry->latency); + } if (print) { printf("%-32s %-7x %-6x %-6x %-8x %4x%08x %-8x %-d\n", @@ -746,7 +778,6 @@ static int mb_high_latency_log_print(int argc, char **argv, struct command *cmd, char buf[LOG_PAGE_SIZE]; FILE *fdi = NULL; - fdi = fopen(FID_C3_LOG_FILENAME, "w+"); OPT_ARGS(opts) = { OPT_END() }; @@ -754,6 +785,8 @@ static int mb_high_latency_log_print(int argc, char **argv, struct command *cmd, fd = parse_and_open(argc, argv, desc, opts); if (fd < 0) return fd; + fdi = fopen(FID_C3_LOG_FILENAME, "w+"); + glp_high_latency_show_bar(fdi, DO_PRINT_FLAG); err = nvme_get_log_simple(fd, GLP_ID_VU_GET_HIGH_LATENCY_LOG, sizeof(buf), &buf); @@ -767,6 +800,7 @@ static int mb_high_latency_log_print(int argc, char **argv, struct command *cmd, } if (NULL != fdi) fclose(fdi); + close(fd); return err; } @@ -852,23 +886,26 @@ static int mb_selective_download(int argc, char **argv, struct command *cmd, str if (err < 0) { perror("fstat"); err = errno; + goto out_close; } fw_size = sb.st_size; if (fw_size & 0x3) { fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); err = EINVAL; - goto out; + goto out_close; } if (posix_memalign(&fw_buf, getpagesize(), fw_size)) { fprintf(stderr, "No memory for f/w size:%d\n", fw_size); err = ENOMEM; - goto out; + goto out_close; } - if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size))) - return EIO; + if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size))) { + err = errno; + goto out_free; + } while (fw_size > 0) { xfer = min(xfer, fw_size); @@ -885,10 +922,10 @@ static int mb_selective_download(int argc, char **argv, struct command *cmd, str err = nvme_fw_download(&args); if (err < 0) { perror("fw-download"); - goto out; + goto out_free; } else if (err != 0) { nvme_show_status(err); - goto out; + goto out_free; } fw_buf += xfer; fw_size -= xfer; @@ -902,7 +939,12 @@ static int mb_selective_download(int argc, char **argv, struct command *cmd, str fprintf(stderr, "Update successful! Please power cycle for changes to take effect\n"); } +out_free: + free(fw_buf); +out_close: + close(fw_fd); out: + close(fd); return err; } @@ -912,22 +954,17 @@ static void ioLatencyHistogramOutput(FILE *fd, int index, int start, int end, ch int len; char string[64], subString0[12], subString1[12]; - len = snprintf(subString0, sizeof(subString0), "%d%s", start, unit0); + snprintf(subString0, sizeof(subString0), "%d%s", start, unit0); if (end != 0x7FFFFFFF) - { - len = snprintf(subString1, sizeof(subString1), "%d%s", end, unit1); - } + snprintf(subString1, sizeof(subString1), "%d%s", end, unit1); else - { - len = snprintf(subString1, sizeof(subString1), "%s", "+INF"); - } - len = snprintf(string, sizeof(string), "%-11d %-11s %-11s %-11u\n", index, subString0, subString1, + snprintf(subString1, sizeof(subString1), "%s", "+INF"); + len = snprintf(string, sizeof(string), "%-11d %-11s %-11s %-11u\n", + index, subString0, subString1, pHistogram[index]); fwrite(string, 1, len, fd); if (print) - { printf("%s", string); - } } int io_latency_histogram(char *file, char *buf, int print, int logid) @@ -1006,7 +1043,8 @@ int io_latency_histogram(char *file, char *buf, int print, int logid) fPRINT_PARAM1("Unsupported io latency histogram revision\n"); } - fclose(fdi); + if (fdi) + fclose(fdi); return 1; } @@ -1116,6 +1154,7 @@ static int memblaze_clear_error_log(int argc, char **argv, struct command *cmd, printf("NVMe Status:%s(%x)\n", nvme_status_to_string(err), err); }; */ + close(fd); return err; } @@ -1208,6 +1247,7 @@ static int mb_set_lat_stats(int argc, char **argv, fid, result); } else { printf("Could not read feature id 0xE2.\n"); + close(fd); return err; } break; @@ -1226,8 +1266,9 @@ static int mb_set_lat_stats(int argc, char **argv, break; default: printf("%d not supported.\n", option); - return EINVAL; + err = EINVAL; } - return fd; + close(fd); + return err; } diff --git a/plugins/memblaze/memblaze-nvme.h b/plugins/memblaze/memblaze-nvme.h index 6f10bd7..87314c6 100644 --- a/plugins/memblaze/memblaze-nvme.h +++ b/plugins/memblaze/memblaze-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/memblaze/memblaze-nvme diff --git a/plugins/memblaze/memblaze-utils.h b/plugins/memblaze/memblaze-utils.h index 6fdee39..84263f3 100644 --- a/plugins/memblaze/memblaze-utils.h +++ b/plugins/memblaze/memblaze-utils.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __MEMBLAZE_UTILS_H__ #define __MEMBLAZE_UTILS_H__ @@ -199,28 +200,24 @@ struct nvme_p4_smart_log printf("=Memblaze= %s[%d]-%s():%d=%s\n", \ __FILE__, __LINE__, __func__, ip, argv[ip]); }while(0) -#define fPRINT_PARAM1(format) \ - { \ - do \ - { \ - fprintf(fdi, format);\ - if (print) \ - { \ - printf(format); \ - } \ - } while (0); \ +#define fPRINT_PARAM1(format) \ + { \ + do { \ + if (fdi) \ + fprintf(fdi, format); \ + if (print) \ + printf(format); \ + } while (0); \ } -#define fPRINT_PARAM2(format, value) \ - { \ - do \ - { \ - fprintf(fdi, format, value);\ - if (print) \ - { \ - printf(format, value); \ - } \ - } while (0); \ +#define fPRINT_PARAM2(format, value) \ + { \ + do { \ + if (fdi) \ + fprintf(fdi, format, value); \ + if (print) \ + printf(format, value); \ + } while (0); \ } #endif // __MEMBLAZE_UTILS_H__ diff --git a/plugins/meson.build b/plugins/meson.build index 6f21362..b3af39b 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,8 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + sources += [ 'plugins/amzn/amzn-nvme.c', + 'plugins/dell/dell-nvme.c', 'plugins/dera/dera-nvme.c', 'plugins/huawei/huawei-nvme.c', 'plugins/intel/intel-nvme.c', + 'plugins/innogrit/innogrit-nvme.c', 'plugins/memblaze/memblaze-nvme.c', 'plugins/micron/micron-nvme.c', 'plugins/netapp/netapp-nvme.c', @@ -10,6 +14,7 @@ sources += [ 'plugins/scaleflux/sfx-nvme.c', 'plugins/seagate/seagate-nvme.c', 'plugins/shannon/shannon-nvme.c', + 'plugins/solidigm/solidigm-nvme.c', 'plugins/toshiba/toshiba-nvme.c', 'plugins/transcend/transcend-nvme.c', 'plugins/virtium/virtium-nvme.c', @@ -19,3 +24,4 @@ sources += [ 'plugins/zns/zns.c', 'plugins/ocp/ocp-nvme.c', ] +subdir('solidigm') diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index d333c4c..d7ac3d4 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -21,7 +22,7 @@ /* Supported Vendor specific feature ids */ #define MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS 0xC3 -#define MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY 0xCE +#define MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY 0xC1 #define MICRON_FEATURE_TELEMETRY_CONTROL_OPTION 0xCF #define MICRON_FEATURE_SMBUS_OPTION 0xD5 @@ -36,12 +37,12 @@ #define CommonChunkSize 16 * 4096 #define min(x, y) ((x) > (y) ? (y) : (x)) -#define SensorCount 2 +#define SensorCount 8 /* Plugin version major_number.minor_number.patch */ static const char *__version_major = "1"; static const char *__version_minor = "0"; -static const char *__version_patch = "8"; +static const char *__version_patch = "14"; /* supported models of micron plugin; new models should be added at the end * before UNKNOWN_MODEL. Make sure M5410 is first in the list ! @@ -87,13 +88,15 @@ static int ReadSysFile(const char *file, unsigned short *id) char idstr[32] = { '\0' }; int fd = open(file, O_RDONLY); - if (fd > 0) { - ret = read(fd, idstr, sizeof(idstr)); - close(fd); + if (fd < 0) { + perror(file); + return fd; } - if (fd < 0 || ret < 0) - perror(file); + ret = read(fd, idstr, sizeof(idstr)); + close(fd); + if (ret < 0) + perror("read"); else *id = strtol(idstr, NULL, 16); @@ -249,6 +252,7 @@ static int SetupDebugDataDirectories(char *strSN, char *strFilePath, if (':' == fileLocation[length - 1]) { if ((strTemp = (char *)malloc(length + 2)) == NULL) { + free(fileLocation); goto exit_status; } strcpy(strTemp, fileLocation); @@ -257,6 +261,7 @@ static int SetupDebugDataDirectories(char *strSN, char *strFilePath, length = (int)strlen(strTemp); if ((fileLocation = (char *)malloc(length + 1)) == NULL) { + free(strTemp); goto exit_status; } @@ -290,17 +295,27 @@ static int SetupDebugDataDirectories(char *strSN, char *strFilePath, j++; } - mkdir(strMainDirName, 0777); + if (mkdir(strMainDirName, 0777) < 0) { + err = -1; + goto exit_status; + } if (strOSDirName != NULL) { sprintf(strOSDirName, "%s/%s", strMainDirName, "OS"); - mkdir(strOSDirName, 0777); - + if (mkdir(strOSDirName, 0777) < 0) { + rmdir(strMainDirName); + err = -1; + goto exit_status; + } } if (strCtrlDirName != NULL) { sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller"); - mkdir(strCtrlDirName, 0777); - + if (mkdir(strCtrlDirName, 0777) < 0) { + if (strOSDirName != NULL) + rmdir(strOSDirName); + rmdir(strMainDirName); + err = -1; + } } exit_status: @@ -327,7 +342,8 @@ static int GetLogPageSize(int nFD, unsigned char ucLogID, int *nLogSize) err = -1; } } else { - printf ("Getting size of log page : 0x%X failed with %d\n", ucLogID, err); + printf ("Getting size of log page : 0x%X failed with %d (ignored)!\n", + ucLogID, err); *nLogSize = 0; } } @@ -402,8 +418,10 @@ static int NVMEResetLog(int nFD, unsigned char ucLogID, int nBufferSize, while (err == 0 && llMaxSize > 0) { err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize); - if (err) + if (err) { + free(pBuffer); return err; + } if (pBuffer[0] == 0xdeadbeef) break; @@ -498,14 +516,13 @@ static int micron_selective_download(int argc, char **argv, }; fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) return fd; if (strlen(cfg.select) != 3) { fprintf(stderr, "Invalid select flag\n"); - err = EINVAL; - goto out; + close(fd); + return EINVAL; } for (int i = 0; i < 3; i++) { @@ -520,21 +537,22 @@ static int micron_selective_download(int argc, char **argv, selectNo = 26; } else { fprintf(stderr, "Invalid select flag\n"); - err = EINVAL; - goto out; + close(fd); + return EINVAL; } fw_fd = open(cfg.fw, O_RDONLY); if (fw_fd < 0) { fprintf(stderr, "no firmware file provided\n"); - err = EINVAL; - goto out; + close(fd); + return EINVAL; } err = fstat(fw_fd, &sb); if (err < 0) { perror("fstat"); err = errno; + goto out; } fw_size = sb.st_size; @@ -550,8 +568,10 @@ static int micron_selective_download(int argc, char **argv, goto out; } - if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) - return EIO; + if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) { + err = errno; + goto out_free; + } while (fw_size > 0) { xfer = min(xfer, fw_size); @@ -568,10 +588,10 @@ static int micron_selective_download(int argc, char **argv, err = nvme_fw_download(&args); if (err < 0) { perror("fw-download"); - goto out; + goto out_free; } else if (err != 0) { nvme_show_status(err); - goto out; + goto out_free; } fw_buf += xfer; fw_size -= xfer; @@ -586,7 +606,11 @@ static int micron_selective_download(int argc, char **argv, "Update successful! Power cycle for changes to take effect\n"); } +out_free: + free(fw_buf); out: + close(fw_fd); + close(fd); return err; } @@ -723,7 +747,7 @@ static int micron_temp_stats(int argc, char **argv, struct command *cmd, if (!err) { temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]); temperature = temperature ? temperature - 273 : 0; - for (i = 0; i < SensorCount; i++) { + for (i = 0; i < SensorCount && tempSensors[i] != 0; i++) { tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]); tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0; } @@ -735,7 +759,7 @@ static int micron_temp_stats(int argc, char **argv, struct command *cmd, json_object_add_value_array(root, "Micron temperature information", logPages); sprintf(tempstr, "%u C", temperature); json_object_add_value_string(stats, "Current Composite Temperature", tempstr); - for (i = 0; i < SensorCount; i++) { + for (i = 0; i < SensorCount && tempSensors[i] != 0; i++) { char sensor_str[256] = { 0 }; char datastr[64] = { 0 }; sprintf(sensor_str, "Temperature Sensor #%d", (i + 1)); @@ -749,11 +773,12 @@ static int micron_temp_stats(int argc, char **argv, struct command *cmd, } else { printf("Micron temperature information:\n"); printf("%-10s : %u C\n", "Current Composite Temperature", temperature); - for (i = 0; i < SensorCount; i++) { + for (i = 0; i < SensorCount && tempSensors[i] != 0; i++) { printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]); } } } + close(fd); return err; } @@ -769,9 +794,11 @@ static int micron_pcie_stats(int argc, char **argv, FILE *fp; char correctable[8] = { 0 }; char uncorrectable[8] = { 0 }; + struct nvme_passthru_cmd admin_cmd = { 0 }; eDriveModel eModel = UNKNOWN_MODEL; char *res; bool is_json = true; + bool counters = false; struct format { char *fmt; }; @@ -780,29 +807,64 @@ static int micron_pcie_stats(int argc, char **argv, struct format cfg = { .fmt = "json", }; + struct pcie_error_counters { + __u16 receiver_error; + __u16 bad_tlp; + __u16 bad_dllp; + __u16 replay_num_rollover; + __u16 replay_timer_timeout; + __u16 advisory_non_fatal_error; + __u16 DLPES; + __u16 poisoned_tlp; + __u16 FCPC; + __u16 completion_timeout; + __u16 completion_abort; + __u16 unexpected_completion; + __u16 receiver_overflow; + __u16 malformed_tlp; + __u16 ecrc_error; + __u16 unsupported_request_error; + } pcie_error_counters = { 0 }; + struct { char *err; int bit; int val; } pcie_correctable_errors[] = { - { "Unsupported Request Error Status (URES)", 20}, - { "ECRC Error Status (ECRCES)", 19}, - { "Malformed TLP Status (MTS)", 18}, - { "Receiver Overflow Status (ROS)", 17}, - { "Unexpected Completion Status (UCS)", 16}, - { "Completer Abort Status (CAS)", 15}, - { "Completion Timeout Stats (CTS)", 14}, - { "Flow Control Protocol Error Status (FCPES)", 13}, - { "Poisoned TLP Status (PTS)", 12}, - { "Data Link Protocol Error Status (DLPES)", 4}, + { "Unsupported Request Error Status (URES)", 20, + offsetof(struct pcie_error_counters, unsupported_request_error)}, + { "ECRC Error Status (ECRCES)", 19, + offsetof(struct pcie_error_counters, ecrc_error)}, + { "Malformed TLP Status (MTS)", 18, + offsetof(struct pcie_error_counters, malformed_tlp)}, + { "Receiver Overflow Status (ROS)", 17, + offsetof(struct pcie_error_counters, receiver_overflow)}, + { "Unexpected Completion Status (UCS)", 16, + offsetof(struct pcie_error_counters, unexpected_completion)}, + { "Completer Abort Status (CAS)", 15, + offsetof(struct pcie_error_counters, completion_abort)}, + { "Completion Timeout Status (CTS)", 14, + offsetof(struct pcie_error_counters, completion_timeout)}, + { "Flow Control Protocol Error Status (FCPES)", 13, + offsetof(struct pcie_error_counters, FCPC)}, + { "Poisoned TLP Status (PTS)", 12, + offsetof(struct pcie_error_counters, poisoned_tlp)}, + { "Data Link Protocol Error Status (DLPES)", 4, + offsetof(struct pcie_error_counters, DLPES)}, }, pcie_uncorrectable_errors[] = { - { "Advisory Non-Fatal Error Status (ANFES)", 13}, - { "Replay Timer Timeout Status (RTS)", 12}, - { "REPLY NUM Rollover Status (RRS)", 8}, - { "Bad DLLP Status (BDS)", 7}, - { "Bad TLP Status (BTS)", 6}, - { "Receiver Error Status (RES)", 0}, + { "Advisory Non-Fatal Error Status (ANFES)", 13, + offsetof(struct pcie_error_counters, advisory_non_fatal_error)}, + { "Replay Timer Timeout Status (RTS)", 12, + offsetof(struct pcie_error_counters, replay_timer_timeout)}, + { "REPLAY_NUM Rollover Status (RRS)", 8, + offsetof(struct pcie_error_counters, replay_num_rollover)}, + { "Bad DLLP Status (BDS)", 7, + offsetof(struct pcie_error_counters, bad_dllp)}, + { "Bad TLP Status (BTS)", 6, + offsetof(struct pcie_error_counters, bad_tlp)}, + { "Receiver Error Status (RES)", 0, + offsetof(struct pcie_error_counters, receiver_error)}, }; __u32 correctable_errors; @@ -823,13 +885,26 @@ static int micron_pcie_stats(int argc, char **argv, sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { printf ("Unsupported drive model for vs-pcie-stats command\n"); - close(fd); goto out; } if (strcmp(cfg.fmt, "normal") == 0) is_json = false; + if (eModel == M5407) { + admin_cmd.opcode = 0xD6; + admin_cmd.addr = (__u64)(uintptr_t)&pcie_error_counters; + admin_cmd.data_len = sizeof(pcie_error_counters); + admin_cmd.cdw10 = 1; + err = nvme_submit_admin_passthru(fd, &admin_cmd, NULL); + if (!err) { + counters = true; + correctable_errors = 10; + uncorrectable_errors = 6; + goto print_stats; + } + } + if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { devicename = strrchr(argv[optind], '/'); } else if (strstr(argv[optind], "/dev/nvme")) { @@ -841,7 +916,8 @@ static int micron_pcie_stats(int argc, char **argv, goto out; } sprintf(strTempFile, "/sys/block/%s/device", devicename); - sLinkSize = readlink(strTempFile, strTempFile2, 1024); + memset(strTempFile2, 0x0, 1024); + sLinkSize = readlink(strTempFile, strTempFile2, 1023); if (sLinkSize < 0) { err = -errno; printf("Failed to read device\n"); @@ -849,7 +925,8 @@ static int micron_pcie_stats(int argc, char **argv, } if (strstr(strTempFile2, "../../nvme")) { sprintf(strTempFile, "/sys/block/%s/device/device", devicename); - sLinkSize = readlink(strTempFile, strTempFile2, 1024); + memset(strTempFile2, 0x0, 1024); + sLinkSize = readlink(strTempFile, strTempFile2, 1023); if (sLinkSize < 0) { err = -errno; printf("Failed to read device\n"); @@ -868,6 +945,7 @@ static int micron_pcie_stats(int argc, char **argv, res = fgets(correctable, sizeof(correctable), fp); if (res == NULL) { printf("Failed to retrieve error count\n"); + pclose(fp); goto out; } pclose(fp); @@ -882,6 +960,7 @@ static int micron_pcie_stats(int argc, char **argv, res = fgets(uncorrectable, sizeof(uncorrectable), fp); if (res == NULL) { printf("Failed to retrieve error count\n"); + pclose(fp); goto out; } pclose(fp); @@ -889,32 +968,46 @@ static int micron_pcie_stats(int argc, char **argv, correctable_errors = (__u32)strtol(correctable, NULL, 16); uncorrectable_errors = (__u32)strtol(uncorrectable, NULL, 16); +print_stats: if (is_json) { struct json_object *root = json_create_object(); struct json_object *pcieErrors = json_create_array(); struct json_object *stats = json_create_object(); + __u8 *pcounter = (__u8 *)&pcie_error_counters; json_object_add_value_array(root, "PCIE Stats", pcieErrors); for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { - json_object_add_value_int(stats, pcie_correctable_errors[i].err, - ((correctable_errors >> pcie_correctable_errors[i].bit) & 1)); + __u16 val = counters ? *(__u16 *)(pcounter + pcie_correctable_errors[i].val) : + (correctable_errors >> pcie_correctable_errors[i].bit) & 1; + json_object_add_value_int(stats, pcie_correctable_errors[i].err, val); } for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { - json_object_add_value_int(stats, pcie_uncorrectable_errors[i].err, - ((uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1)); + __u16 val = counters ? *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val) : + (uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1; + json_object_add_value_int(stats, pcie_uncorrectable_errors[i].err, val); } json_array_add_value_object(pcieErrors, stats); json_print_object(root, NULL); printf("\n"); json_free_object(root); + } else if (counters == true) { + __u8 *pcounter = (__u8 *)&pcie_error_counters; + for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { + printf("%-42s : %-1hu\n", pcie_correctable_errors[i].err, + *(__u16 *)(pcounter + pcie_correctable_errors[i].val)); + } + for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { + printf("%-42s : %-1hu\n", pcie_uncorrectable_errors[i].err, + *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val)); + } } else if (eModel == M5407 || eModel == M5410) { for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { - printf("%-40s : %-1d\n", pcie_correctable_errors[i].err, + printf("%-42s : %-1d\n", pcie_correctable_errors[i].err, ((correctable_errors >> pcie_correctable_errors[i].bit) & 1)); } for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { - printf("%-40s : %-1d\n", pcie_uncorrectable_errors[i].err, + printf("%-42s : %-1d\n", pcie_uncorrectable_errors[i].err, ((uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1)); } } else { @@ -924,6 +1017,8 @@ static int micron_pcie_stats(int argc, char **argv, } out: + if (fd > 0) + close(fd); return err; } @@ -938,6 +1033,7 @@ static int micron_clear_pcie_correctable_errors(int argc, char **argv, char tdevice[PATH_MAX] = { 0 }; ssize_t sLinkSize = 0; eDriveModel model = UNKNOWN_MODEL; + struct nvme_passthru_cmd admin_cmd = { 0 }; char correctable[8] = { 0 }; int fd = -1; FILE *fp; @@ -955,13 +1051,22 @@ static int micron_clear_pcie_correctable_errors(int argc, char **argv, /* For M51CX models, PCIe errors are cleared using 0xC3 feature */ if (model == M51CX) { err = nvme_set_features_simple(fd, fid, 0, (1 << 31), false, &result); - if (err == 0 && (err = (int)result) == 0) - printf("Device correctable errors cleared!\n"); - else if (err > 0) - nvme_show_status(err); - else - printf("Error clearing Device correctable errors = 0x%x\n", err); - goto out; + if (err == 0 && (err = (int)result) == 0) { + printf("Device correctable errors are cleared!\n"); + goto out; + } + } else if (model == M5407) { + admin_cmd.opcode = 0xD6; + admin_cmd.addr = 0; + admin_cmd.cdw10 = 0; + err = nvme_submit_admin_passthru(fd, &admin_cmd, NULL); + if (err == 0) { + printf("Device correctable error counters are cleared!\n"); + goto out; + } else { + /* proceed to clear status bits using sysfs interface + printf("Error clearing PCIe correctable errors = 0x%x\n", err); */ + } } if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { @@ -979,7 +1084,8 @@ static int micron_clear_pcie_correctable_errors(int argc, char **argv, if (err < 0) goto out; - sLinkSize = readlink(strTempFile, strTempFile2, 1024); + memset(strTempFile2, 0x0, 1024); + sLinkSize = readlink(strTempFile, strTempFile2, 1023); if (sLinkSize < 0) { err = -errno; printf("Failed to read device\n"); @@ -990,7 +1096,8 @@ static int micron_clear_pcie_correctable_errors(int argc, char **argv, "/sys/block/%s/device/device", devicename); if (err < 0) goto out; - sLinkSize = readlink(strTempFile, strTempFile2, 1024); + memset(strTempFile2, 0x0, 1024); + sLinkSize = readlink(strTempFile, strTempFile2, 1023); if (sLinkSize < 0) { err = -errno; printf("Failed to read device\n"); @@ -1019,6 +1126,7 @@ static int micron_clear_pcie_correctable_errors(int argc, char **argv, res = fgets(correctable, sizeof(correctable), fp); if (res == NULL) { printf("Failed to retrieve error count\n"); + pclose(fp); goto out; } pclose(fp); @@ -1026,8 +1134,7 @@ static int micron_clear_pcie_correctable_errors(int argc, char **argv, printf("Device correctable errors detected: %s\n", correctable); err = 0; out: - if (fd > 0) - close(fd); + close(fd); return err; } @@ -1323,13 +1430,13 @@ static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json, init_d0_log_page(buf2, nsze); if (is_json) { - for (int i = 4; i < 7; i++) { + for (int i = 0; i < 7; i++) { json_object_add_value_string(stats, d0_log_page[i].field, d0_log_page[i].datastr); } } else { - for (int i = 4; i < 7; i++) { + for (int i = 0; i < 7; i++) { printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); } } @@ -1670,14 +1777,14 @@ static void GetGenericLogs(int fd, const char *dir) err = nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_EST_CTX_AND_READ, sizeof(pevent_log), &pevent_log); if (err) { - fprintf(stderr, "Failed to set persistent event log read context"); + fprintf(stderr, "Setting persistent event log read ctx failed (ignored)!\n"); return; } log_len = le64_to_cpu(pevent_log.tll); pevent_log_info = nvme_alloc(log_len, &huge); if (!pevent_log_info) { - perror("could not alloc buffer for persistent event log page\n"); + perror("could not alloc buffer for persistent event log page (ignored)!\n"); return; } err = nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_READ, @@ -1704,7 +1811,7 @@ static void GetNSIDDInfo(int fd, const char *dir, int nsid) static void GetOSConfig(const char *strOSDirName) { FILE *fpOSConfig = NULL; - char strBuffer[1024], strTemp[1024]; + char strBuffer[1024]; char strFileName[PATH_MAX]; int i; @@ -1727,15 +1834,15 @@ static void GetOSConfig(const char *strOSDirName) for (i = 0; i < 7; i++) { fpOSConfig = fopen(strFileName, "a+"); - fprintf(fpOSConfig, + if (NULL != fpOSConfig) { + fprintf(fpOSConfig, "\n\n\n\n%s\n-----------------------------------------------\n", cmdArray[i].strcmdHeader); - if (NULL != fpOSConfig) { fclose(fpOSConfig); fpOSConfig = NULL; } - strcpy(strTemp, cmdArray[i].strCommand); - sprintf(strBuffer, strTemp, strFileName); + snprintf(strBuffer, sizeof(strBuffer) - 1, + cmdArray[i].strCommand, strFileName); if (system(strBuffer)) fprintf(stderr, "Failed to send \"%s\"\n", strBuffer); } @@ -1821,10 +1928,11 @@ static int GetTelemetryData(int fd, const char *dir) if (err == 0 && logSize > 0 && buffer != NULL) { sprintf(msg, "telemetry log: 0x%X", tmap[i].log); WriteData(buffer, logSize, dir, tmap[i].file, msg); - if (buffer != NULL) - free(buffer); } - buffer = NULL; + if (buffer) { + free(buffer); + buffer = NULL; + } logSize = 0; } return err; @@ -1885,7 +1993,7 @@ static int GetFeatureSettings(int fd, const char *dir) WriteData(bufp, len, dir, fmap[i].file, msg); } } else { - printf("Feature 0x%x data not retrieved, error %d (ignored)!\n", + fprintf(stderr, "Feature 0x%x data not retrieved, error %d (ignored)!\n", fmap[i].id, err); errcnt++; } @@ -1929,6 +2037,7 @@ static int micron_drive_info(int argc, char **argv, struct command *cmd, if (model == UNKNOWN_MODEL) { fprintf(stderr, "ERROR : Unsupported drive for vs-drive-info cmd"); + close(fd); return -1; } @@ -1943,16 +2052,19 @@ static int micron_drive_info(int argc, char **argv, struct command *cmd, err = nvme_submit_admin_passthru(fd, &admin_cmd, NULL); if (err) { fprintf(stderr, "ERROR : drive-info opcode failed with 0x%x\n", err); + close(fd); return -1; } } else { err = nvme_identify_ctrl(fd, &ctrl); if (err) { fprintf(stderr, "ERROR : identify_ctrl() failed with 0x%x\n", err); + close(fd); return -1; } dinfo.hw_ver_major = ctrl.vs[820]; dinfo.hw_ver_minor = ctrl.vs[821]; + dinfo.ftl_unit_size = ctrl.vs[822]; } if (is_json) { @@ -1991,6 +2103,7 @@ static int micron_drive_info(int argc, char **argv, struct command *cmd, } } + close(fd); return 0; } @@ -2010,12 +2123,6 @@ static int micron_plugin_version(int argc, char **argv, struct command *cmd, return 0; } -static int micron_logpage_dir(int argc, char **argv, struct command *cmd, - struct plugin *plugin) -{ - printf("This command is not implemented for the drive\n"); - return 0; -} /* Binary format of firmware activation history entry */ struct __attribute__((__packed__)) fw_activation_history_entry { __u8 version; @@ -2050,8 +2157,8 @@ struct __attribute__((__packed__)) micron_fw_activation_history_table { const char *fw_activation_history_table_header = "\ __________________________________________________________________________________\n\ | | | | | | | \n\ -Firmware | Power | Power | Previous | New FW | Slot | Commit | Result \n\ -Activation| On Hour | cycle | firmware | activated | number | Action | \n\ +Firmware | Power On | Power | Previous | New FW | Slot | Commit | Result \n\ +Activation| Hour | cycle | firmware | activated | number | Action | \n\ Counter | | count | | | | Type | \n\ __________|___________|_________|__________|___________|________|________|________\n"; @@ -2069,9 +2176,9 @@ static int display_fw_activate_entry ( char *ptr = formatted_entry; int index = 0, entry_size = 82; - if (entry->version != 1 || entry->length != 64) { - fprintf(stderr, "unsupported entry ! version: %x with length: %d\n", - entry->version, entry->length); + if ((entry->version != 1 && entry->version != 2) || entry->length != 64) { + /*fprintf(stderr, "unsupported entry ! version: %x with length: %d\n", + entry->version, entry->length); */ return -EINVAL; } @@ -2178,7 +2285,8 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *c (struct micron_fw_activation_history_table *)logC2; /* check version and log page */ - if (table->version != 2 || table->log_page != 0xC2) { + if (table->log_page != 0xC2 || (table->version != 2 && table->version != 1)) + { fprintf(stderr, "Unsupported fw activation history page: %x, version: %x\n", table->log_page, table->version); goto out; @@ -2200,8 +2308,7 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *c } } out: - if (fd > 0) - close(fd); + close(fd); return err; } @@ -2254,6 +2361,7 @@ static int micron_latency_stats_track(int argc, char **argv, struct command *cmd enable = 0; } else if (strcmp(opt.option, "status")) { printf("Invalid control option %s specified\n", opt.option); + close(fd); return -1; } @@ -2274,6 +2382,7 @@ static int micron_latency_stats_track(int argc, char **argv, struct command *cmd err = nvme_get_features(&g_args); if (err != 0) { printf("Failed to retrieve latency monitoring feature status\n"); + close(fd); return err; } @@ -2298,6 +2407,7 @@ static int micron_latency_stats_track(int argc, char **argv, struct command *cmd } else if (result == 0) { printf("\n"); } + close(fd); return err; } @@ -2305,11 +2415,13 @@ static int micron_latency_stats_track(int argc, char **argv, struct command *cmd if (enable == 1) { if (opt.threshold > 2550) { printf("The maximum threshold value cannot be more than 2550 ms\n"); + close(fd); return -1; } /* timing mask is in terms of 10ms units, so min allowed is 10ms */ else if ((opt.threshold % 10) != 0) { printf("The threshold value should be multiple of 10 ms\n"); + close(fd); return -1; } opt.threshold /= 10; @@ -2322,12 +2434,13 @@ static int micron_latency_stats_track(int argc, char **argv, struct command *cmd } else if (!strcmp(opt.command, "write")) { command_mask = 0x2; timing_mask = (opt.threshold << 16); - } else if (!strcmp(opt.command, "read")) { + } else if (!strcmp(opt.command, "trim")) { command_mask = 0x4; timing_mask = (opt.threshold << 8); } else if (strcmp(opt.command, "all")) { printf("Invalid command %s specified for option %s\n", opt.command, opt.option); + close(fd); return -1; } @@ -2349,11 +2462,11 @@ static int micron_latency_stats_track(int argc, char **argv, struct command *cmd }; err = nvme_set_features(&args); if (err == 0) { - printf("Successfully %sed latency monitoring for %s commands\n", - opt.option, opt.command); + printf("Successfully %sd latency monitoring for %s commands with %dms threshold\n", + opt.option, opt.command, opt.threshold == 0 ? 800 : opt.threshold * 10); } else { - printf("Failed to %s latency monitoring for %s commands\n", - opt.option, opt.command); + printf("Failed to %s latency monitoring for %s commands with %dms threshold\n", + opt.option, opt.command, opt.threshold == 0 ? 800 : opt.threshold * 10); } close(fd); @@ -2411,6 +2524,7 @@ static int micron_latency_stats_logs(int argc, char **argv, struct command *cmd, if (err) { if (err < 0) printf("Unable to retrieve latency stats log the drive\n"); + close(fd); return err; } /* print header and each log entry */ @@ -2424,6 +2538,7 @@ static int micron_latency_stats_logs(int argc, char **argv, struct command *cmd, log[i].deac, log[i].prinfo, log[i].fua, log[i].lr); } printf("\n"); + close(fd); return err; } @@ -2437,13 +2552,14 @@ static int micron_latency_stats_info(int argc, char **argv, struct command *cmd, int fd = -1; eDriveModel model = UNKNOWN_MODEL; #define LATENCY_BUCKET_COUNT 32 + #define LATENCY_BUCKET_RSVD 32 struct micron_latency_stats { uint64_t version; /* major << 32 | minior */ - uint64_t all_cmds[LATENCY_BUCKET_COUNT]; - uint64_t read_cmds[LATENCY_BUCKET_COUNT]; - uint64_t write_cmds[LATENCY_BUCKET_COUNT]; - uint64_t trim_cmds[LATENCY_BUCKET_COUNT]; - uint32_t reserved[765]; /* round up to 4K */ + uint64_t all_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; + uint64_t read_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; + uint64_t write_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; + uint64_t trim_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; + uint32_t reserved[255]; /* round up to 4K */ } log; struct latency_thresholds { @@ -2489,6 +2605,7 @@ static int micron_latency_stats_info(int argc, char **argv, struct command *cmd, cmd_str = "Trim"; } else if (strcmp(opt.command, "all")) { printf("Invalid command option %s to display latency stats\n", opt.command); + close(fd); return -1; } @@ -2497,6 +2614,7 @@ static int micron_latency_stats_info(int argc, char **argv, struct command *cmd, if (err) { if (err < 0) printf("Unable to retrieve latency stats log the drive\n"); + close(fd); return err; } printf("Micron IO %s Command Latency Statistics\n" @@ -2518,6 +2636,7 @@ static int micron_latency_stats_info(int argc, char **argv, struct command *cmd, printf("%2d %8s %8s %8"PRIu64"\n", bucket, start, end, cmd_stats[b]); } + close(fd); return err; } @@ -2613,8 +2732,11 @@ static int micron_clr_fw_activation_history(int argc, char **argv, return err; } - err = nvme_set_features_simple(fd, fid, 1, 0, 0, &result); + err = nvme_set_features_simple(fd, fid, 1 << 31, 0, 0, &result); if (err == 0) err = (int)result; + else printf ("Failed to clear fw activation history, error = 0x%x\n", err); + + close(fd); return err; } @@ -2733,6 +2855,157 @@ static int micron_telemetry_cntrl_option(int argc, char **argv, return err; } +/* M51XX models log page header */ +struct micron_common_log_header { + uint8_t id; + uint8_t version; + uint16_t pn; + uint32_t log_size; + uint32_t max_size; + uint32_t write_pointer; + uint32_t next_pointer; + uint32_t overwritten_bytes; + uint8_t flags; + uint8_t reserved[7]; +}; + +/* helper function to retrieve logs with specific offset and max chunk size */ +int nvme_get_log_lpo(int fd, __u8 log_id, __u32 lpo, __u32 chunk, + __u32 data_len, void *data) +{ + __u32 offset = lpo, xfer_len = data_len; + void *ptr = data; + struct nvme_get_log_args args = { + .lpo = offset, + .result = NULL, + .log = ptr, + .args_size = sizeof(args), + .fd = fd, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .lid = log_id, + .len = xfer_len, + .nsid = NVME_NSID_ALL, + .csi = NVME_CSI_NVM, + .lsi = NVME_LOG_LSI_NONE, + .lsp = NVME_LOG_LSP_NONE, + .uuidx = NVME_UUID_NONE, + .rae = false, + .ot = false, + }; + int ret = 0; + + /* divide data into multiple chunks */ + do { + xfer_len = data_len - offset; + if (xfer_len > chunk) + xfer_len = chunk; + + args.lpo = offset; + args.log = ptr; + args.len = xfer_len; + ret = nvme_get_log(&args); + if (ret) + return ret; + offset += xfer_len; + ptr += xfer_len; + } while (offset < data_len); + return ret; +} + +/* retrieves logs with common log format */ +static int get_common_log(int fd, uint8_t id, uint8_t **buf, int *size) +{ + struct micron_common_log_header hdr = { 0 }; + int log_size = sizeof(hdr), first = 0, second = 0; + uint8_t *buffer = NULL; + int ret = -1; + int chunk = 0x4000; /* max chunk size to be used for these logs */ + + ret = nvme_get_log_simple(fd, id, sizeof(hdr), &hdr); + if (ret) { + fprintf(stderr, "pull hdr failed for %hhu with error: 0x%x\n", id, ret); + return ret; + } + + if (hdr.id != id || + hdr.log_size == 0 || + hdr.max_size == 0 || + hdr.write_pointer < sizeof(hdr)) + { + fprintf(stderr, "invalid log data for LOG: 0x%X, id: 0x%X, size: %u, " + "max: %u, wp: %u, flags: %hhu, np: %u\n", id, + hdr.id, hdr.log_size, hdr.max_size, hdr.write_pointer, + hdr.flags, hdr.next_pointer); + return 1; + } + + /* we may have just 32-bytes for some models; write to wfile if log hasn't + * yet reached its max size + */ + if (hdr.log_size == sizeof(hdr)) { + buffer = (uint8_t *)malloc(sizeof(hdr)); + if (buffer == NULL) { + fprintf(stderr, "malloc of %lu bytes failed for log: 0x%X\n", + sizeof(hdr), id); + return -ENOMEM; + } + memcpy(buffer,(uint8_t *)&hdr, sizeof(hdr)); + } else if (hdr.log_size < hdr.max_size) { + buffer = (uint8_t *)malloc(sizeof(hdr) + hdr.log_size); + if (buffer == NULL) { + fprintf(stderr, "malloc of %lu bytes failed for log: 0x%X\n", + hdr.log_size + sizeof(hdr), id); + return -ENOMEM; + } + memcpy(buffer, &hdr, sizeof(hdr)); + ret = nvme_get_log_lpo(fd, id, sizeof(hdr), chunk, hdr.log_size, + buffer + sizeof(hdr)); + if (ret == 0) { + log_size += hdr.log_size; + } + } else if (hdr.log_size >= hdr.max_size) { + /* reached maximum, to maintain, sequence we need to depend on write + * pointer to detect wrap-overs. FW doesn't yet implement the condition + * hdr.log_size > hdr.max_size; also ignore over-written log data; we + * also ignore collisions for now + */ + buffer = (uint8_t *)malloc(hdr.max_size + sizeof(hdr)); + if (buffer == NULL) { + fprintf(stderr, "malloc of %lu bytes failed for log: 0x%X\n", + hdr.max_size + sizeof(hdr), id); + return -ENOMEM; + } + memcpy(buffer, &hdr, sizeof(hdr)); + + first = hdr.max_size - hdr.write_pointer; + second = hdr.write_pointer - sizeof(hdr); + + if (first) { + ret = nvme_get_log_lpo(fd, id, hdr.write_pointer, chunk, first, + buffer + sizeof(hdr)); + if (ret) { + free(buffer); + fprintf(stderr, "failed to get log: 0x%X\n", id); + return ret; + } + log_size += first; + } + if (second) { + ret = nvme_get_log_lpo(fd, id, sizeof(hdr), chunk, second, + buffer + sizeof(hdr) + first); + if (ret) { + fprintf(stderr, "failed to get log: 0x%X\n", id); + free(buffer); + return ret; + } + log_size += second; + } + } + *buf = buffer; + *size = log_size; + return ret; +} + static int micron_internal_logs(int argc, char **argv, struct command *cmd, struct plugin *plugin) { @@ -2747,7 +3020,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, struct nvme_id_ctrl ctrl; char sn[20] = { 0 }; char msg[256] = { 0 }; - + int c_logs_index = 8; /* should be current size of aVendorLogs */ struct { unsigned char ucLogPage; const char *strFileName; @@ -2788,6 +3061,16 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 }, { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 }, { 0xCA, "nvmelog_CA.bin", 512, 1024 } + }, + aM51CXLogs[] = { + { 0xE1, "nvmelog_E1.bin", 0, 0 }, + { 0xE2, "nvmelog_E2.bin", 0, 0 }, + { 0xE3, "nvmelog_E3.bin", 0, 0 }, + { 0xE4, "nvmelog_E4.bin", 0, 0 }, + { 0xE5, "nvmelog_E5.bin", 0, 0 }, + { 0xE8, "nvmelog_E8.bin", 0, 0 }, + { 0xE9, "nvmelog_E9.bin", 0, 0 }, + { 0xEA, "nvmelog_EA.bin", 0, 0 }, }; eDriveModel eModel; @@ -2824,7 +3107,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, fd = parse_and_open(argc, argv, desc, opts); if (fd < 0) - goto out; + return fd; /* if telemetry type is specified, check for data area */ if (strlen(cfg.type) != 0) { @@ -2832,19 +3115,16 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, cfg.log = 0x08; } else if (strcmp(cfg.type, "host")) { printf ("telemetry type (host or controller) should be specified i.e. -t=host\n"); - close(fd); goto out; } if (cfg.data_area <= 0 || cfg.data_area > 3) { printf ("data area must be selected using -d option ie --d=1,2,3\n"); - close(fd); goto out; } telemetry_option = 1; } else if (cfg.data_area > 0) { printf ("data area option is valid only for telemetry option (i.e --type=host|controller)\n"); - close(fd); goto out; } @@ -2860,7 +3140,6 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { printf ("Unsupported drive model for vs-internal-log collection\n"); - close(fd); goto out; } @@ -2882,7 +3161,6 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, WriteData(buffer, logSize, dir, cfg.package, msg); free(buffer); } - close(fd); goto out; } @@ -2918,17 +3196,34 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, GetFeatureSettings(fd, strCtrlDirName); if (eModel != M5410 && eModel != M5407) { - memcpy(aVendorLogs, aM51XXLogs, sizeof(aM51XXLogs)); + memcpy(&aVendorLogs[c_logs_index], aM51XXLogs, sizeof(aM51XXLogs)); + c_logs_index += sizeof(aM51XXLogs)/sizeof(aM51XXLogs[0]); if (eModel == M51AX) - memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51AXLogs, sizeof(aM51AXLogs)); - else - memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51BXLogs, sizeof(aM51BXLogs)); + memcpy((char *)&aVendorLogs[c_logs_index], aM51AXLogs, sizeof(aM51AXLogs)); + else if (eModel == M51BX) + memcpy((char *)&aVendorLogs[c_logs_index], aM51BXLogs, sizeof(aM51BXLogs)); + else if (eModel == M51CX) + memcpy((char *)&aVendorLogs[c_logs_index], aM51CXLogs, sizeof(aM51CXLogs)); } for (int i = 0; i < (int)(sizeof(aVendorLogs) / sizeof(aVendorLogs[0])) && aVendorLogs[i].ucLogPage != 0; i++) { err = -1; switch (aVendorLogs[i].ucLogPage) { + case 0xE1: + case 0xE5: + case 0xE9: + err = 1; + break; + + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE8: + case 0xEA: + err = get_common_log(fd, aVendorLogs[i].ucLogPage, &dataBuffer, &bSize); + break; + case 0xC1: case 0xC2: case 0xC4: @@ -2964,9 +3259,11 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, case 0xF9: case 0xFC: case 0xFD: - if (eModel == M51BX) + if (eModel == M51BX) { (void)NVMEResetLog(fd, aVendorLogs[i].ucLogPage, aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize); + } + /* fallthrough */ default: bSize = aVendorLogs[i].nLogSize; dataBuffer = (unsigned char *)malloc(bSize); @@ -3002,5 +3299,71 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, err = ZipAndRemoveDir(strMainDirName, cfg.package); out: + close(fd); + return err; +} + +#define MIN_LOG_SIZE 512 +static int micron_logpage_dir(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + int err = -1; + int fd = -1; + const char *desc = "List the supported log pages"; + eDriveModel model = UNKNOWN_MODEL; + char logbuf[MIN_LOG_SIZE]; + int i; + + OPT_ARGS(opts) = { + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + + struct nvme_supported_logs { + uint8_t log_id; + uint8_t supported; + char *desc; + } log_list[] = { + {0x00, 0, "Support Log Pages"}, + {0x01, 0, "Error Information"}, + {0x02, 0, "SMART / Health Information"}, + {0x03, 0, "Firmware Slot Information"}, + {0x04, 0, "Changed Namespace List"}, + {0x05, 0, "Commands Supported and Effects"}, + {0x06, 0, "Device Self Test"}, + {0x07, 0, "Telemetry Host-Initiated"}, + {0x08, 0, "Telemetry Controller-Initiated"}, + {0x09, 0, "Endurance Group Information"}, + {0x0A, 0, "Predictable Latency Per NVM Set"}, + {0x0B, 0, "Predictable Latency Event Aggregate"}, + {0x0C, 0, "Asymmetric Namespace Access"}, + {0x0D, 0, "Persistent Event Log"}, + {0x0E, 0, "Predictable Latency Event Aggregate"}, + {0x0F, 0, "Endurance Group Event Aggregate"}, + {0x10, 0, "Media Unit Status"}, + {0x11, 0, "Supported Capacity Configuration List"}, + {0x12, 0, "Feature Identifiers Supported and Effects"}, + {0x13, 0, "NVMe-MI Commands Supported and Effects"}, + {0x14, 0, "Command and Feature lockdown"}, + {0x15, 0, "Boot Partition"}, + {0x16, 0, "Rotational Media Information"}, + {0x70, 0, "Discovery"}, + {0x80, 0, "Reservation Notification"}, + {0x81, 0, "Sanitize Status"}, + {0xC0, 0, "SMART Cloud Health Log"}, + {0xC2, 0, "Firmware Activation History"}, + {0xC3, 0, "Latency Monitor Log"}, + }; + + printf("Supported log page list\nLog ID : Description\n"); + for (i = 0; i < sizeof(log_list)/sizeof(log_list[0]); i++) { + err = nvme_get_log_simple(fd, log_list[i].log_id, + MIN_LOG_SIZE, &logbuf[0]); + if (err) continue; + printf("%02Xh : %s\n", log_list[i].log_id, log_list[i].desc); + } + return err; } diff --git a/plugins/micron/micron-nvme.h b/plugins/micron/micron-nvme.h index c1b224e..4f7b892 100644 --- a/plugins/micron/micron-nvme.h +++ b/plugins/micron/micron-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/micron/micron-nvme diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c index 9630442..b6bd3f6 100644 --- a/plugins/netapp/netapp-nvme.c +++ b/plugins/netapp/netapp-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2018 NetApp, Inc. * @@ -103,12 +104,12 @@ static void netapp_nguid_to_str(char *str, __u8 *nguid) str += sprintf(str, "%02x", nguid[i]); } -static void netapp_get_ns_size(char *size, long long *lba, +static void netapp_get_ns_size(char *size, unsigned long long *lba, struct nvme_id_ns *ns) { __u8 lba_index; nvme_id_ns_flbas_to_lbaf_inuse(ns->flbas, &lba_index); - *lba = 1 << ns->lbaf[lba_index].ds; + *lba = 1ULL << ns->lbaf[lba_index].ds; double nsze = le64_to_cpu(ns->nsze) * (*lba); const char *s_suffix = suffix_si_get(&nsze); @@ -263,7 +264,7 @@ static void netapp_smdevices_print(struct smdevice_info *devices, int count, int for (i = 0; i < count; i++) { nvme_id_ns_flbas_to_lbaf_inuse(devices[i].ns.flbas, &lba_index); - long long int lba = 1 << devices[i].ns.lbaf[lba_index].ds; + unsigned long long int lba = 1ULL << devices[i].ns.lbaf[lba_index].ds; double nsze = le64_to_cpu(devices[i].ns.nsze) * lba; const char *s_suffix = suffix_si_get(&nsze); char size[128]; @@ -302,7 +303,7 @@ static void netapp_ontapdevices_print(struct ontapdevice_info *devices, struct json_object *json_devices = NULL; char vsname[ONTAP_LABEL_LEN] = " "; char nspath[ONTAP_NS_PATHLEN] = " "; - long long lba; + unsigned long long lba; char size[128]; char uuid_str[37] = " "; int i; @@ -392,8 +393,10 @@ static int netapp_smdevices_get_info(int fd, struct smdevice_info *item, err = nvme_identify_ctrl(fd, &item->ctrl); if (err) { - fprintf(stderr, "Identify Controller failed to %s (%s)\n", dev, - strerror(err)); + fprintf(stderr, + "Identify Controller failed to %s (%s)\n", dev, + err < 0 ? strerror(-err) : + nvme_status_to_string(err, false)); return 0; } @@ -403,11 +406,13 @@ static int netapp_smdevices_get_info(int fd, struct smdevice_info *item, err = nvme_get_nsid(fd, &item->nsid); err = nvme_identify_ns(fd, item->nsid, &item->ns); if (err) { - fprintf(stderr, "Unable to identify namespace for %s (%s)\n", - dev, strerror(err)); + fprintf(stderr, + "Unable to identify namespace for %s (%s)\n", + dev, err < 0 ? strerror(-err) : + nvme_status_to_string(err, false)); return 0; } - strncpy(item->dev, dev, sizeof(item->dev)); + strncpy(item->dev, dev, sizeof(item->dev) - 1); return 1; } @@ -421,7 +426,8 @@ static int netapp_ontapdevices_get_info(int fd, struct ontapdevice_info *item, err = nvme_identify_ctrl(fd, &item->ctrl); if (err) { fprintf(stderr, "Identify Controller failed to %s (%s)\n", - dev, strerror(err)); + dev, err < 0 ? strerror(-err) : + nvme_status_to_string(err, false)); return 0; } @@ -434,7 +440,8 @@ static int netapp_ontapdevices_get_info(int fd, struct ontapdevice_info *item, err = nvme_identify_ns(fd, item->nsid, &item->ns); if (err) { fprintf(stderr, "Unable to identify namespace for %s (%s)\n", - dev, strerror(err)); + dev, err < 0 ? strerror(-err) : + nvme_status_to_string(err, false)); return 0; } @@ -446,7 +453,9 @@ static int netapp_ontapdevices_get_info(int fd, struct ontapdevice_info *item, err = nvme_identify_ns_descs(fd, item->nsid, nsdescs); if (err) { fprintf(stderr, "Unable to identify namespace descriptor for %s (%s)\n", - dev, strerror(err)); + dev, err < 0 ? strerror(-err) : + nvme_status_to_string(err, false)); + free(nsdescs); return 0; } @@ -456,11 +465,12 @@ static int netapp_ontapdevices_get_info(int fd, struct ontapdevice_info *item, err = nvme_get_ontap_c2_log(fd, item->nsid, item->log_data, ONTAP_C2_LOG_SIZE); if (err) { fprintf(stderr, "Unable to get log page data for %s (%s)\n", - dev, strerror(err)); + dev, err < 0 ? strerror(-err): + nvme_status_to_string(err, false)); return 0; } - strncpy(item->dev, dev, sizeof(item->dev)); + strncpy(item->dev, dev, sizeof(item->dev) - 1); return 1; } diff --git a/plugins/netapp/netapp-nvme.h b/plugins/netapp/netapp-nvme.h index 2599db0..73de4b4 100644 --- a/plugins/netapp/netapp-nvme.h +++ b/plugins/netapp/netapp-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/netapp/netapp-nvme diff --git a/plugins/nvidia/nvidia-nvme.c b/plugins/nvidia/nvidia-nvme.c index 8ddd16f..71e0bc3 100644 --- a/plugins/nvidia/nvidia-nvme.c +++ b/plugins/nvidia/nvidia-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include diff --git a/plugins/nvidia/nvidia-nvme.h b/plugins/nvidia/nvidia-nvme.h index 74a20b8..3d870e1 100644 --- a/plugins/nvidia/nvidia-nvme.h +++ b/plugins/nvidia/nvidia-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/nvidia/nvidia-nvme diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c index 6b7fc9d..56cfdff 100644 --- a/plugins/ocp/ocp-nvme.c +++ b/plugins/ocp/ocp-nvme.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0-or-later /* Copyright (c) 2022 Meta Platforms, Inc. * * Authors: Arthur Shau , @@ -380,11 +380,6 @@ static int get_c0_log_page(int fd, char *format) } /* print the data */ - if (!data) { - fprintf(stderr, "ERROR : OCP : Invalid buffer to read 0xC0 log\n"); - ret = -1; - goto out; - } switch (fmt) { case NORMAL: ocp_print_C0_log_normal(data); @@ -430,7 +425,7 @@ static int ocp_smart_add_log(int argc, char **argv, struct command *cmd, if (ret) fprintf(stderr, "ERROR : OCP : Failure reading the C0 Log Page, ret = %d\n", ret); - + close(fd); return ret; } @@ -728,13 +723,6 @@ static int get_c3_log_page(int fd, char *format) } } - /* print the data */ - if (!log_data) { - fprintf(stderr, - "ERROR : OCP : Invalid C3 log data buffer\n"); - ret = -1; - goto out; - } switch (fmt) { case NORMAL: ocp_print_C3_log_normal(fd, log_data); @@ -783,6 +771,6 @@ static int ocp_latency_monitor_log(int argc, char **argv, struct command *comman fprintf(stderr, "ERROR : OCP : Failure reading the C3 Log Page, ret = %d\n", ret); - + close(fd); return ret; } diff --git a/plugins/ocp/ocp-nvme.h b/plugins/ocp/ocp-nvme.h index 19378cd..3e3f437 100644 --- a/plugins/ocp/ocp-nvme.h +++ b/plugins/ocp/ocp-nvme.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Copyright (c) 2022 Meta Platforms, Inc. * * Authors: Arthur Shau , diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index a6aaad5..0740e43 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -29,6 +30,12 @@ #define IDEMA_CAP(exp_GB) (((__u64)exp_GB - 50ULL) * 1953504ULL + 97696368ULL) #define IDEMA_CAP2GB(exp_sector) (((__u64)exp_sector - 97696368ULL) / 1953504ULL + 50ULL) +#define VANDA_MAJOR_IDX 0 +#define VANDA_MINOR_IDX 0 + +#define MYRTLE_MAJOR_IDX 4 +#define MYRTLE_MINOR_IDX 1 + enum { SFX_LOG_LATENCY_READ_STATS = 0xc1, SFX_LOG_SMART = 0xc2, @@ -58,6 +65,7 @@ struct sfx_freespace_ctx __u64 user_space; /* user required space, in unit of sector*/ __u64 hw_used; /* hw space used in 4K */ __u64 app_written; /* app data written in 4K */ + __u64 out_of_space; }; struct nvme_capacity_info { @@ -66,25 +74,26 @@ struct nvme_capacity_info { __u64 used_space; __u64 free_space; }; -struct __attribute__((packed)) nvme_additional_smart_log_item { - uint8_t key; - uint8_t _kp[2]; - uint8_t norm; - uint8_t _np; - union { - uint8_t raw[6]; - struct wear_level { - uint16_t min; - uint16_t max; - uint16_t avg; - } wear_level ; - struct thermal_throttle { - uint8_t pct; - uint32_t count; + +struct __attribute__((packed)) nvme_additional_smart_log_item { + __u8 key; + __u8 _kp[2]; + __u8 norm; + __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; - }; - uint8_t _rp; -}; + } ; + __u8 _rp; +} ; struct nvme_additional_smart_log { struct nvme_additional_smart_log_item program_fail_cnt; @@ -105,8 +114,26 @@ struct nvme_additional_smart_log { struct nvme_additional_smart_log_item erase_timeout_cnt; struct nvme_additional_smart_log_item read_timeout_cnt; struct nvme_additional_smart_log_item read_ecc_cnt;//retry cnt + struct nvme_additional_smart_log_item non_media_crc_err_cnt; + struct nvme_additional_smart_log_item compression_path_err_cnt; + struct nvme_additional_smart_log_item out_of_space_flag; + struct nvme_additional_smart_log_item physical_usage_ratio; + struct nvme_additional_smart_log_item grown_bb; //grown bad block }; +int nvme_query_cap(int fd, __u32 nsid, __u32 data_len, void *data) +{ + int rc = 0; + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_query_cap_info, + .nsid = nsid, + .addr = (__u64)(uintptr_t) data, + .data_len = data_len, + }; + + rc = ioctl(fd, SFX_GET_FREESPACE, data); + return rc == 0 ? 0 : nvme_submit_admin_passthru(fd, &cmd, NULL); +} int nvme_change_cap(int fd, __u32 nsid, __u64 capacity) { struct nvme_passthru_cmd cmd = { @@ -255,6 +282,31 @@ static void show_sfx_smart_log_jsn(struct nvme_additional_smart_log *smart, json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->read_ecc_cnt.raw)); json_object_add_value_object(dev_stats, "read_ecc_cnt", entry_stats); + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->non_media_crc_err_cnt.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->non_media_crc_err_cnt.raw)); + json_object_add_value_object(dev_stats, "non_media_crc_err_cnt", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->compression_path_err_cnt.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->compression_path_err_cnt.raw)); + json_object_add_value_object(dev_stats, "compression_path_err_cnt", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->out_of_space_flag.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->out_of_space_flag.raw)); + json_object_add_value_object(dev_stats, "out_of_space_flag", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->physical_usage_ratio.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->physical_usage_ratio.raw)); + json_object_add_value_object(dev_stats, "physical_usage_ratio", entry_stats); + + entry_stats = json_create_object(); + json_object_add_value_int(entry_stats, "normalized", smart->grown_bb.norm); + json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->grown_bb.raw)); + json_object_add_value_object(dev_stats, "grown_bb", entry_stats); + json_object_add_value_object(root, "Device stats", dev_stats); json_print_object(root, NULL); @@ -325,6 +377,22 @@ static void show_sfx_smart_log(struct nvme_additional_smart_log *smart, printf("read_timeout_cnt : %3d%% %"PRIu64"\n", smart->read_timeout_cnt.norm, int48_to_long(smart->read_timeout_cnt.raw)); + printf("non_media_crc_err_cnt : %3d%% %" PRIu64 "\n", + smart->non_media_crc_err_cnt.norm, + int48_to_long(smart->non_media_crc_err_cnt.raw)); + printf("compression_path_err_cnt : %3d%% %" PRIu64 "\n", + smart->compression_path_err_cnt.norm, + int48_to_long(smart->compression_path_err_cnt.raw)); + printf("out_of_space_flag : %3d%% %" PRIu64 "\n", + smart->out_of_space_flag.norm, + int48_to_long(smart->out_of_space_flag.raw)); + printf("phy_capacity_used_ratio : %3d%% %" PRIu64 "\n", + smart->physical_usage_ratio.norm, + int48_to_long(smart->physical_usage_ratio.raw)); + printf("grown_bb_count : %3d%% %" PRIu64 "\n", + smart->grown_bb.norm, int48_to_long(smart->grown_bb.raw)); + + } static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -355,6 +423,9 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + return fd; + } err = nvme_get_nsid_log(fd, false, 0xca, cfg.namespace_id, sizeof(smart_log), (void *)&smart_log); @@ -368,12 +439,13 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, } else if (err > 0) nvme_show_status(err); + close(fd); return err; } -struct sfx_lat_stats { - __u16 maj; - __u16 min; +struct __attribute__((__packed__)) sfx_lat_stats_vanda { + __u16 maj; + __u16 min; __u32 bucket_1[32]; /* 0~1ms, step 32us */ __u32 bucket_2[31]; /* 1~32ms, step 1ms */ __u32 bucket_3[31]; /* 32ms~1s, step 32ms */ @@ -382,11 +454,50 @@ struct sfx_lat_stats { __u32 bucket_6[1]; /* 4s+, specifically 4096ms+ */ }; -static void show_lat_stats(struct sfx_lat_stats *stats, int write) +struct __attribute__((__packed__)) sfx_lat_stats_myrtle { + __u16 maj; + __u16 min; + __u32 bucket_1[64]; /* 0us~63us, step 1us */ + __u32 bucket_2[64]; /* 63us~127us, step 1us */ + __u32 bucket_3[64]; /* 127us~255us, step 2us */ + __u32 bucket_4[64]; /* 255us~510us, step 4us */ + __u32 bucket_5[64]; /* 510us~1.02ms step 8us */ + __u32 bucket_6[64]; /* 1.02ms~2.04ms step 16us */ + __u32 bucket_7[64]; /* 2.04ms~4.08ms step 32us */ + __u32 bucket_8[64]; /* 4.08ms~8.16ms step 64us */ + __u32 bucket_9[64]; /* 8.16ms~16.32ms step 128us */ + __u32 bucket_10[64]; /* 16.32ms~32.64ms step 256us */ + __u32 bucket_11[64]; /* 32.64ms~65.28ms step 512us */ + __u32 bucket_12[64]; /* 65.28ms~130.56ms step 1.024ms */ + __u32 bucket_13[64]; /* 130.56ms~261.12ms step 2.048ms */ + __u32 bucket_14[64]; /* 261.12ms~522.24ms step 4.096ms */ + __u32 bucket_15[64]; /* 522.24ms~1.04s step 8.192ms */ + __u32 bucket_16[64]; /* 1.04s~2.09s step 16.384ms */ + __u32 bucket_17[64]; /* 2.09s~4.18s step 32.768ms */ + __u32 bucket_18[64]; /* 4.18s~8.36s step 65.536ms */ + __u32 bucket_19[64]; /* 8.36s~ step 131.072ms */ + __u64 average; /* average latency statistics */ +}; + + +struct __attribute__((__packed__)) sfx_lat_status_ver { + __u16 maj; + __u16 min; +}; + +struct sfx_lat_stats { + union { + struct sfx_lat_status_ver ver; + struct sfx_lat_stats_vanda vanda; + struct sfx_lat_stats_myrtle myrtle; + }; +}; + +static void show_lat_stats_vanda(struct sfx_lat_stats_vanda *stats, int write) { int i; - printf(" ScaleFlux IO %s Command Latency Statistics\n", write ? "Write" : "Read"); + printf("ScaleFlux IO %s Command Latency Statistics\n", write ? "Write" : "Read"); printf("-------------------------------------\n"); printf("Major Revision : %u\n", stats->maj); printf("Minor Revision : %u\n", stats->min); @@ -413,6 +524,95 @@ static void show_lat_stats(struct sfx_lat_stats *stats, int write) printf("Bucket %2d: %u\n", 0, stats->bucket_6[0]); } +static void show_lat_stats_myrtle(struct sfx_lat_stats_myrtle *stats, int write) +{ + int i; + + printf("ScaleFlux IO %s Command Latency Statistics\n", write ? "Write" : "Read"); + printf("-------------------------------------\n"); + printf("Major Revision : %u\n", stats->maj); + printf("Minor Revision : %u\n", stats->min); + + printf("\nGroup 1: Range is 0us~63us, step 1us\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_1[i]); + + printf("\nGroup 2: Range is 63us~127us, step 1us\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_2[i]); + + printf("\nGroup 3: Range is 127us~255us, step 2us\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_3[i]); + + printf("\nGroup 4: Range is 255us~510us, step 4us\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_4[i]); + + printf("\nGroup 5: Range is 510us~1.02ms step\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_5[i]); + + printf("\nGroup 6: Range is 1.02ms~2.04ms step 16us\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_6[i]); + + printf("\nGroup 7: Range is 2.04ms~4.08ms step 32us\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_7[i]); + + printf("\nGroup 8: Range is 4.08ms~8.16ms step 64us\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_8[i]); + + printf("\nGroup 9: Range is 8.16ms~16.32ms step 128us\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_9[i]); + + printf("\nGroup 10: Range is 16.32ms~32.64ms step 256us\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_10[i]); + + printf("\nGroup 11: Range is 32.64ms~65.28ms step 512us\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_11[i]); + + printf("\nGroup 12: Range is 65.28ms~130.56ms step 1.024ms\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_12[i]); + + printf("\nGroup 13: Range is 130.56ms~261.12ms step 2.048ms\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_13[i]); + + printf("\nGroup 14: Range is 261.12ms~522.24ms step 4.096ms\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_14[i]); + + printf("\nGroup 15: Range is 522.24ms~1.04s step 8.192ms\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_15[i]); + + printf("\nGroup 16: Range is 1.04s~2.09s step 16.384ms\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_16[i]); + + printf("\nGroup 17: Range is 2.09s~4.18s step 32.768ms\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_17[i]); + + printf("\nGroup 18: Range is 4.18s~8.36s step 65.536ms\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_18[i]); + + printf("\nGroup 19: Range is 8.36s~ step 131.072ms\n"); + for (i = 0; i < 64; i++) + printf("Bucket %2d: %u\n", i, stats->bucket_19[i]); + + printf("\nAverage latency statistics %lld\n", stats->average); +} + + static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { struct sfx_lat_stats stats; @@ -436,15 +636,31 @@ static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct }; fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + return fd; + } err = nvme_get_log_simple(fd, cfg.write ? 0xc3 : 0xc1, sizeof(stats), (void *)&stats); if (!err) { - if (!cfg.raw_binary) - show_lat_stats(&stats, cfg.write); - else - d_raw((unsigned char *)&stats, sizeof(stats)); + if ((stats.ver.maj == VANDA_MAJOR_IDX) && (stats.ver.min == VANDA_MINOR_IDX)) { + if (!cfg.raw_binary) { + show_lat_stats_vanda(&stats.vanda, cfg.write); + } else { + d_raw((unsigned char *)&stats.vanda, sizeof(struct sfx_lat_stats_vanda)); + } + } else if ((stats.ver.maj == MYRTLE_MAJOR_IDX) && (stats.ver.min == MYRTLE_MINOR_IDX)) { + if (!cfg.raw_binary) { + show_lat_stats_myrtle(&stats.myrtle, cfg.write); + } else { + d_raw((unsigned char *)&stats.myrtle, sizeof(struct sfx_lat_stats_myrtle)); + } + } else { + printf("ScaleFlux IO %s Command Latency Statistics Invalid Version Maj %d Min %d\n", + write ? "Write" : "Read", stats.ver.maj, stats.ver.min); + } } else if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -560,7 +776,6 @@ static int sfx_get_bad_block(int argc, char **argv, struct command *cmd, struct }; fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) { return fd; } @@ -568,6 +783,7 @@ static int sfx_get_bad_block(int argc, char **argv, struct command *cmd, struct data_buf = malloc(buf_size); if (!data_buf) { fprintf(stderr, "malloc fail, errno %d\r\n", errno); + close(fd); return -1; } @@ -582,6 +798,7 @@ static int sfx_get_bad_block(int argc, char **argv, struct command *cmd, struct } free(data_buf); + close(fd); return 0; } @@ -603,18 +820,15 @@ static int query_cap_info(int argc, char **argv, struct command *cmd, struct plu { struct sfx_freespace_ctx ctx = { 0 }; int err = 0, fd; - char *desc = "query current capacity info of vanda"; + char *desc = "query current capacity info"; const char *raw = "dump output in binary format"; - const char *json= "Dump output in json format"; struct config { bool raw_binary; - bool json; }; struct config cfg; OPT_ARGS(opts) = { OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_FLAG("json", 'j', &cfg.json, json), OPT_END() }; @@ -623,12 +837,19 @@ static int query_cap_info(int argc, char **argv, struct command *cmd, struct plu return fd; } - if (ioctl(fd, SFX_GET_FREESPACE, &ctx)) { - fprintf(stderr, "vu ioctl fail, errno %d\r\n", errno); - return -1; + if (nvme_query_cap(fd, 0xffffffff, sizeof(ctx), &ctx)) { + perror("sfx-query-cap"); + err = -1; } - show_cap_info(&ctx); + if (!err) { + if (!cfg.raw_binary) { + show_cap_info(&ctx); + } else { + d_raw((unsigned char *)&ctx, sizeof(ctx)); + } + } + close(fd); return err; } @@ -639,14 +860,10 @@ static int change_sanity_check(int fd, __u64 trg_in_4k, int *shrink) __u64 mem_need = 0; __u64 cur_in_4k = 0; __u64 provisoned_cap_4k = 0; - __u32 cnt_ms = 0; int extend = 0; - while (ioctl(fd, SFX_GET_FREESPACE, &freespace_ctx)) { - if (cnt_ms++ > 600) {//1min - return -1; - } - usleep(100000); + if (nvme_query_cap(fd, 0xffffffff, sizeof(freespace_ctx), &freespace_ctx)) { + return -1; } /* @@ -706,12 +923,12 @@ static int change_sanity_check(int fd, __u64 trg_in_4k, int *shrink) */ static int sfx_confirm_change(const char *str) { - char confirm; + unsigned char confirm; fprintf(stderr, "WARNING: %s.\n" "Use the force [--force] option to suppress this warning.\n", str); fprintf(stderr, "Confirm Y/y, Others cancel:\n"); - confirm = fgetc(stdin); + confirm = (unsigned char)fgetc(stdin); if (confirm != 'y' && confirm != 'Y') { fprintf(stderr, "Cancled.\n"); return 0; @@ -723,9 +940,7 @@ static int sfx_confirm_change(const char *str) static int change_cap(int argc, char **argv, struct command *cmd, struct plugin *plugin) { int err = -1, fd; - char *desc = "query current capacity info of vanda"; - const char *raw = "dump output in binary format"; - const char *json= "Dump output in json format"; + char *desc = "dynamic change capacity"; const char *cap_gb = "cap size in GB"; const char *cap_byte = "cap size in byte"; const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command"; @@ -736,8 +951,6 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin struct config { __u64 cap_in_byte; __u32 capacity_in_gb; - bool raw_binary; - bool json; bool force; }; @@ -751,8 +964,6 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin OPT_UINT("cap", 'c', &cfg.capacity_in_gb, cap_gb), OPT_SUFFIX("cap-byte", 'z', &cfg.cap_in_byte, cap_byte), OPT_FLAG("force", 'f', &cfg.force, force), - OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), - OPT_FLAG("json", 'j', &cfg.json, json), OPT_END() }; @@ -770,10 +981,12 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin if (change_sanity_check(fd, cap_in_4k, &shrink)) { printf("ScaleFlux change-capacity: fail\n"); + close(fd); return err; } if (!cfg.force && shrink && !sfx_confirm_change("Changing Cap may irrevocably delete this device's data")) { + close(fd); return 0; } @@ -786,6 +999,7 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin printf("ScaleFlux change-capacity: success\n"); ioctl(fd, BLKRRPART); } + close(fd); return err; } @@ -877,12 +1091,14 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl if (!cfg.feature_id) { fprintf(stderr, "feature-id required param\n"); + close(fd); return EINVAL; } if (cfg.feature_id == SFX_FEAT_CLR_CARD) { /*Warning for clean card*/ if (!cfg.force && !sfx_confirm_change("Going to clean device's data, confirm umount fs and try again")) { + close(fd); return 0; } else { return sfx_clean_card(fd); @@ -898,6 +1114,7 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl perror("identify-namespace"); else nvme_show_status(err); + close(fd); return err; } /* @@ -905,17 +1122,20 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl */ if ((ns.flbas & 0xf) != 1) { printf("Please change-sector size to 4K, then retry\n"); + close(fd); return EFAULT; } } } else if (cfg.feature_id == SFX_FEAT_UP_P_CAP) { if (cfg.value <= 0) { fprintf(stderr, "Invalid Param\n"); + close(fd); return EINVAL; } /*Warning for change pacp by GB*/ if (!cfg.force && !sfx_confirm_change("Changing physical capacity may irrevocably delete this device's data")) { + close(fd); return 0; } } @@ -924,6 +1144,7 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl if (err < 0) { perror("ScaleFlux-set-feature"); + close(fd); return errno; } else if (!err) { printf("ScaleFlux set-feature:%#02x (%s), value:%d\n", cfg.feature_id, @@ -931,6 +1152,7 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl } else if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -959,19 +1181,20 @@ static int sfx_get_feature(int argc, char **argv, struct command *cmd, struct pl }; fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) { return fd; } if (!cfg.feature_id) { fprintf(stderr, "feature-id required param\n"); + close(fd); return EINVAL; } err = nvme_sfx_get_features(fd, cfg.namespace_id, cfg.feature_id, &result); if (err < 0) { perror("ScaleFlux-get-feature"); + close(fd); return errno; } else if (!err) { printf("ScaleFlux get-feature:%02x (%s), value:%d\n", cfg.feature_id, @@ -979,6 +1202,7 @@ static int sfx_get_feature(int argc, char **argv, struct command *cmd, struct pl } else if (err > 0) nvme_show_status(err); + close(fd); return err; } diff --git a/plugins/scaleflux/sfx-nvme.h b/plugins/scaleflux/sfx-nvme.h index fde5ba3..0b95d92 100644 --- a/plugins/scaleflux/sfx-nvme.h +++ b/plugins/scaleflux/sfx-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/scaleflux/sfx-nvme diff --git a/plugins/seagate/seagate-diag.h b/plugins/seagate/seagate-diag.h index 3dc2df7..139901d 100644 --- a/plugins/seagate/seagate-diag.h +++ b/plugins/seagate/seagate-diag.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Do NOT modify or remove this copyright and license * diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c index 5f6ce90..516cb1e 100644 --- a/plugins/seagate/seagate-nvme.c +++ b/plugins/seagate/seagate-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Do NOT modify or remove this copyright and license * @@ -147,6 +148,7 @@ static void json_log_pages_supp(log_page_map *logPageMap) } json_print_object(root, NULL); printf("\n"); + json_free_object(root); } static int log_pages_supp(int argc, char **argv, struct command *cmd, @@ -174,6 +176,8 @@ static int log_pages_supp(int argc, char **argv, struct command *cmd, }; fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; err = nvme_get_log_simple(fd, 0xc5, sizeof(logPageMap), &logPageMap); if (!err) { if (strcmp(cfg.output_format,"json")) { @@ -199,6 +203,7 @@ static int log_pages_supp(int argc, char **argv, struct command *cmd, if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -732,6 +737,11 @@ static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugi }; fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf ("\nDevice not found \n"); + return -1; + } + if (strcmp(cfg.output_format,"json")) printf("Seagate Extended SMART Information :\n"); @@ -774,6 +784,7 @@ static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugi } else if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -889,6 +900,7 @@ static int temp_stats(int argc, char **argv, struct command *cmd, struct plugin if(!strcmp(cfg.output_format,"json")) json_temp_stats(temperature, PcbTemp, SocTemp, maxTemperature, MaxSocTemp, cf_err, scCurrentTemp, scMaxTemp); + close(fd); return err; } /* EOF Temperature Stats information */ @@ -1000,6 +1012,11 @@ static int vs_pcie_error_log(int argc, char **argv, struct command *cmd, struct }; fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf ("\nDevice not found \n");; + return -1; + } + if(strcmp(cfg.output_format,"json")) printf("Seagate PCIe error counters Information :\n"); @@ -1013,6 +1030,7 @@ static int vs_pcie_error_log(int argc, char **argv, struct command *cmd, struct } else if (err > 0) nvme_show_status(err); + close(fd); return err; } /* EOF PCIE error-log information */ @@ -1038,14 +1056,18 @@ static int vs_clr_pcie_correctable_errs(int argc, char **argv, struct command *c }; fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf ("\nDevice not found \n");; + return -1; + } err = nvme_set_features_simple(fd, 0xE1, 0, 0xCB, cfg.save, &result); if (err < 0) { perror("set-feature"); - return errno; } + close(fd); return err; } @@ -1113,19 +1135,25 @@ static int get_host_tele(int argc, char **argv, struct command *cmd, struct plug blkCnt = 0; while(blkCnt < maxBlk) { + unsigned long long bytesToGet; + blksToGet = ((maxBlk - blkCnt) >= TELEMETRY_BLOCKS_TO_READ) ? TELEMETRY_BLOCKS_TO_READ : (maxBlk - blkCnt); - if(blksToGet == 0) + if(blksToGet == 0) { + close(fd); return err; + } - log = malloc(blksToGet * 512); + bytesToGet = (unsigned long long)blksToGet * 512; + log = malloc(bytesToGet); if (!log) { fprintf(stderr, "could not alloc buffer for log\n"); + close(fd); return EINVAL; } - memset(log, 0, blksToGet * 512); + memset(log, 0, bytesToGet); struct nvme_get_log_args args = { .args_size = sizeof(args), @@ -1139,21 +1167,21 @@ static int get_host_tele(int argc, char **argv, struct command *cmd, struct plug .uuidx = 0, .csi = NVME_CSI_NVM, .ot = false, - .len = blksToGet * 512, + .len = bytesToGet, .log = (void *)log, .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .result = NULL, }; err = nvme_get_log(&args); if (!err) { - offset += blksToGet * 512; + offset += (__le64)bytesToGet; if (!cfg.raw_binary) { printf("\nBlock # :%d to %d\n", blkCnt + 1, blkCnt + blksToGet); - d((unsigned char *)log, blksToGet * 512, 16, 1); + d((unsigned char *)log, bytesToGet, 16, 1); } else - seaget_d_raw((unsigned char *)log, blksToGet * 512, dump_fd); + seaget_d_raw((unsigned char *)log, bytesToGet, dump_fd); } else if (err > 0) nvme_show_status(err); else @@ -1164,6 +1192,7 @@ static int get_host_tele(int argc, char **argv, struct command *cmd, struct plug free(log); } + close(fd); return err; } @@ -1225,19 +1254,22 @@ static int get_ctrl_tele(int argc, char **argv, struct command *cmd, struct plug blkCnt = 0; while(blkCnt < maxBlk) { + unsigned long long bytesToGet; + blksToGet = ((maxBlk - blkCnt) >= TELEMETRY_BLOCKS_TO_READ) ? TELEMETRY_BLOCKS_TO_READ : (maxBlk - blkCnt); if(blksToGet == 0) return err; - log = malloc(blksToGet * 512); + bytesToGet = (unsigned long long)blksToGet * 512; + log = malloc(bytesToGet); if (!log) { fprintf(stderr, "could not alloc buffer for log\n"); return EINVAL; } - memset(log, 0, blksToGet * 512); + memset(log, 0, bytesToGet); struct nvme_get_log_args args = { .args_size = sizeof(args), @@ -1251,21 +1283,21 @@ static int get_ctrl_tele(int argc, char **argv, struct command *cmd, struct plug .uuidx = 0, .csi = NVME_CSI_NVM, .ot = false, - .len = blksToGet * 512, + .len = bytesToGet, .log = (void *)log, .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .result = NULL, }; err = nvme_get_log(&args); if (!err) { - offset += blksToGet * 512; + offset += (__le64)bytesToGet; if (!cfg.raw_binary) { printf("\nBlock # :%d to %d\n", blkCnt + 1, blkCnt + blksToGet); - d((unsigned char *)log, blksToGet * 512, 16, 1); + d((unsigned char *)log, bytesToGet, 16, 1); } else - seaget_d_raw((unsigned char *)log, blksToGet * 512, dump_fd); + seaget_d_raw((unsigned char *)log, bytesToGet, dump_fd); } else if (err > 0) nvme_show_status(err); else @@ -1275,8 +1307,9 @@ static int get_ctrl_tele(int argc, char **argv, struct command *cmd, struct plug free(log); } - return err; + close(fd); + return err; } void seaget_d_raw(unsigned char *buf, int len, int fd) @@ -1334,6 +1367,7 @@ static int vs_internal_log(int argc, char **argv, struct command *cmd, struct pl dump_fd = open(cfg.file, flags, mode); if (dump_fd < 0) { perror(cfg.file); + close(fd); return EINVAL; } } @@ -1358,20 +1392,24 @@ static int vs_internal_log(int argc, char **argv, struct command *cmd, struct pl blkCnt = 0; while(blkCnt < maxBlk) { + unsigned long long bytesToGet; + blksToGet = ((maxBlk - blkCnt) >= TELEMETRY_BLOCKS_TO_READ) ? TELEMETRY_BLOCKS_TO_READ : (maxBlk - blkCnt); if(blksToGet == 0) { - return err; + goto out; } - log = malloc(blksToGet * 512); + bytesToGet = (unsigned long long)blksToGet * 512; + log = malloc(bytesToGet); if (!log) { fprintf(stderr, "could not alloc buffer for log\n"); - return EINVAL; + err = EINVAL; + goto out; } - memset(log, 0, blksToGet * 512); + memset(log, 0, bytesToGet); struct nvme_get_log_args args = { .args_size = sizeof(args), @@ -1385,16 +1423,16 @@ static int vs_internal_log(int argc, char **argv, struct command *cmd, struct pl .uuidx = 0, .csi = NVME_CSI_NVM, .ot = false, - .len = blksToGet * 512, + .len = bytesToGet, .log = (void *)log, .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .result = NULL, }; err = nvme_get_log(&args); if (!err) { - offset += blksToGet * 512; + offset += (__le64)bytesToGet; - seaget_d_raw((unsigned char *)log, blksToGet * 512, dump_fd); + seaget_d_raw((unsigned char *)log, bytesToGet, dump_fd); } else if (err > 0) nvme_show_status(err); @@ -1405,10 +1443,11 @@ static int vs_internal_log(int argc, char **argv, struct command *cmd, struct pl free(log); } - +out: if(strlen(cfg.file)) close(dump_fd); + close(fd); return err; } diff --git a/plugins/seagate/seagate-nvme.h b/plugins/seagate/seagate-nvme.h index a4989f1..6df1331 100644 --- a/plugins/seagate/seagate-nvme.h +++ b/plugins/seagate/seagate-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Do NOT modify or remove this copyright and license * diff --git a/plugins/shannon/shannon-nvme.c b/plugins/shannon/shannon-nvme.c index d79b119..220638f 100644 --- a/plugins/shannon/shannon-nvme.c +++ b/plugins/shannon/shannon-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -137,6 +138,8 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, }; fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; err = nvme_get_nsid_log(fd, false, 0xca, cfg.namespace_id, sizeof(smart_log), &smart_log); if (!err) { @@ -147,6 +150,7 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, } else if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -156,7 +160,7 @@ static int get_additional_feature(int argc, char **argv, struct command *cmd, st "specified controller. Operating parameters are grouped "\ "and identified by Feature Identifiers; each Feature "\ "Identifier contains one or more attributes that may affect "\ - "behaviour of the feature. Each Feature has three possible "\ + "behavior of the feature. Each Feature has three possible "\ "settings: default, saveable, and current. If a Feature is "\ "saveable, it may be modified by set-feature. Default values "\ "are vendor-specific and not changeable. Use set-feature to "\ diff --git a/plugins/shannon/shannon-nvme.h b/plugins/shannon/shannon-nvme.h index 46fc697..255bb6b 100644 --- a/plugins/shannon/shannon-nvme.h +++ b/plugins/shannon/shannon-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/shannon/shannon-nvme diff --git a/plugins/solidigm/meson.build b/plugins/solidigm/meson.build new file mode 100644 index 0000000..fb0f6a9 --- /dev/null +++ b/plugins/solidigm/meson.build @@ -0,0 +1,5 @@ +sources += [ + 'plugins/solidigm/solidigm-smart.c', + 'plugins/solidigm/solidigm-garbage-collection.c', + 'plugins/solidigm/solidigm-latency-tracking.c', +] diff --git a/plugins/solidigm/solidigm-garbage-collection.c b/plugins/solidigm/solidigm-garbage-collection.c new file mode 100644 index 0000000..c7f1286 --- /dev/null +++ b/plugins/solidigm/solidigm-garbage-collection.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * 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" +#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("%-13lu %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 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() + }; + + int fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + return fd; + } + + enum nvme_print_flags flags = validate_output_format(cfg.output_format); + if (flags == -EINVAL) { + fprintf(stderr, "Invalid output format '%s'\n", cfg.output_format); + close(fd); + return fd; + } + + garbage_control_collection_log_t gc_log; + const int solidigm_vu_gc_log_id = 0xfd; + + int err = nvme_get_log_simple(fd, 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, devicename); + } else { + vu_gc_log_show(&gc_log, devicename); + } + } + else if (err > 0) { + nvme_show_status(err); + } + + close(fd); + 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..0bfd611 --- /dev/null +++ b/plugins/solidigm/solidigm-latency-tracking.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "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: %lu\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."; + __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() + }; + + lt.fd = parse_and_open(argc, argv, desc, opts); + if (lt.fd < 0) + return lt.fd; + + 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); + close(lt.fd); + return EINVAL; + } + + if (lt.cfg.type > 0xf) { + fprintf(stderr, "Invalid Log type value '%d'\n", lt.cfg.type); + close(lt.fd); + return EINVAL; + } + + if (lt.cfg.type && !(lt.cfg.read || lt.cfg.write)) { + fprintf(stderr, "Log type option valid only when retrieving statistics\n"); + close(lt.fd); + return EINVAL; + } + + err = latency_tracking_enable(<); + if (err){ + close(lt.fd); + return err; + } + + err = latency_tracker_get_log(<); + if (err){ + close(lt.fd); + return err; + } + + if ((lt.cfg.read || lt.cfg.write || lt.cfg.enable || lt.cfg.disable)) { + close(lt.fd); + 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"); + } + close(lt.fd); + 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..3eb3cc9 --- /dev/null +++ b/plugins/solidigm/solidigm-nvme.c @@ -0,0 +1,30 @@ +// 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" + +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); +} \ No newline at end of file diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h new file mode 100644 index 0000000..9fc8b45 --- /dev/null +++ b/plugins/solidigm/solidigm-nvme.h @@ -0,0 +1,28 @@ +// 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.4" + +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) + ) +); + +#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..4b30cab --- /dev/null +++ b/plugins/solidigm/solidigm-smart.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include +#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" + +#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; + int fd, 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() + }; + + 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; + } + + 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; +} + 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/toshiba/toshiba-nvme.c b/plugins/toshiba/toshiba-nvme.c index fd2e9c1..cf19352 100644 --- a/plugins/toshiba/toshiba-nvme.c +++ b/plugins/toshiba/toshiba-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -148,6 +149,7 @@ static int nvme_sct_command_transfer_log(int fd, bool current) memcpy(data + 2, &function_code, sizeof(function_code)); err = nvme_sct_op(fd, OP_SCT_COMMAND_TRANSFER, DW10_SCT_COMMAND_TRANSFER, DW11_SCT_COMMAND_TRANSFER, data, data_len); + free(data); return err; } @@ -474,6 +476,7 @@ static int vendor_log(int argc, char **argv, struct command *cmd, struct plugin end: if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -517,6 +520,7 @@ static int internal_log(int argc, char **argv, struct command *cmd, struct plugi if (err > 0) nvme_show_status(err); + close(fd); return err; } @@ -569,5 +573,6 @@ static int clear_correctable_errors(int argc, char **argv, struct command *cmd, end: if (err > 0) nvme_show_status(err); + close(fd); return err; } diff --git a/plugins/toshiba/toshiba-nvme.h b/plugins/toshiba/toshiba-nvme.h index ebd7575..6208f5d 100644 --- a/plugins/toshiba/toshiba-nvme.h +++ b/plugins/toshiba/toshiba-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/toshiba/toshiba-nvme diff --git a/plugins/transcend/transcend-nvme.c b/plugins/transcend/transcend-nvme.c index 85d3cac..0a3d852 100644 --- a/plugins/transcend/transcend-nvme.c +++ b/plugins/transcend/transcend-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -49,7 +50,7 @@ static int getHealthValue(int argc, char **argv, struct command *cmd, struct plu } } - + close(fd); return result; } @@ -83,6 +84,6 @@ static int getBadblock(int argc, char **argv, struct command *cmd, struct plugin int badblock = data[0]; printf("Transcend NVME badblock count: %d\n",badblock); } - + close(fd); return result; } diff --git a/plugins/transcend/transcend-nvme.h b/plugins/transcend/transcend-nvme.h index 317793a..9c89883 100644 --- a/plugins/transcend/transcend-nvme.h +++ b/plugins/transcend/transcend-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/transcend/transcend-nvme diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c index 7c2ebec..d9fc54e 100644 --- a/plugins/virtium/virtium-nvme.c +++ b/plugins/virtium/virtium-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -132,7 +133,7 @@ static void vt_convert_smart_data_to_human_readable_format(struct vtview_smart_l setlocale(LC_ALL, "C"); - long long int lba = 1 << smart->raw_ns.lbaf[lba_index].ds; + unsigned long long int lba = 1ULL << smart->raw_ns.lbaf[lba_index].ds; capacity = le64_to_cpu(smart->raw_ns.nsze) * lba; snprintf(tempbuff, sizeof(tempbuff), "log;%s;%lu;%s;%s;%-.*s;", smart->raw_ctrl.sn, smart->time_stamp, smart->path, @@ -270,16 +271,16 @@ static void vt_process_string(char *str, const size_t size) static int vt_add_entry_to_log(const int fd, const char *path, const struct vtview_save_log_settings *cfg) { struct vtview_smart_log_entry smart; - char filename[256] = ""; + const char *filename; int ret = 0; unsigned nsid = 0; memset(smart.path, 0, sizeof(smart.path)); - strcpy(smart.path, path); + strncpy(smart.path, path, sizeof(smart.path) - 1); if(NULL == cfg->output_file) - strcpy(filename, vt_default_log_file_name); + filename = vt_default_log_file_name; else - strcpy(filename, cfg->output_file); + filename = cfg->output_file; smart.time_stamp = time(NULL); ret = nvme_get_nsid(fd, &nsid); @@ -317,21 +318,32 @@ static int vt_add_entry_to_log(const int fd, const char *path, const struct vtvi static int vt_update_vtview_log_header(const int fd, const char *path, const struct vtview_save_log_settings *cfg) { struct vtview_log_header header; - char filename[256] = ""; + const char *filename; int ret = 0; vt_initialize_header_buffer(&header); + if (strlen(path) > sizeof(header.path)) { + printf("filename too long\n"); + errno = EINVAL; + return -1; + } strcpy(header.path, path); if (NULL == cfg->test_name) strcpy(header.test_name, DEFAULT_TEST_NAME); - else + else { + if (strlen(cfg->test_name) > sizeof(header.test_name)) { + printf("test name too long\n"); + errno = EINVAL; + return -1; + } strcpy(header.test_name, cfg->test_name); + } if(NULL == cfg->output_file) - strcpy(filename, vt_default_log_file_name); + filename = vt_default_log_file_name; else - strcpy(filename, cfg->output_file); + filename = cfg->output_file; printf("Log file: %s\n", filename); header.time_stamp = time(NULL); @@ -955,8 +967,13 @@ static int vt_save_smart_to_vtview_log(int argc, char **argv, struct command *cm vt_generate_vtview_log_file_name(vt_default_log_file_name); - if (argc >= 2) + if (argc >= 2) { + if (strlen(argv[1]) > sizeof(path) - 1) { + printf("Filename too long\n"); + return -1; + } strcpy(path, argv[1]); + } fd = parse_and_open(argc, argv, desc, opts); if (fd < 0) { diff --git a/plugins/virtium/virtium-nvme.h b/plugins/virtium/virtium-nvme.h index 124ab18..0a04a35 100644 --- a/plugins/virtium/virtium-nvme.h +++ b/plugins/virtium/virtium-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/virtium/virtium-nvme diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 486ee36..5be7e11 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2015-2018 Western Digital Corporation or its affiliates. * @@ -79,6 +80,8 @@ #define WDC_NVME_SN560_DEV_ID_1 0x2712 #define WDC_NVME_SN560_DEV_ID_2 0x2713 #define WDC_NVME_SN560_DEV_ID_3 0x2714 +#define WDC_NVME_SN860_DEV_ID 0x2730 +#define WDC_NVME_SN550_DEV_ID 0x2708 #define WDC_NVME_SXSLCL_DEV_ID 0x2001 #define WDC_NVME_SN520_DEV_ID 0x5003 #define WDC_NVME_SN520_DEV_ID_1 0x5004 @@ -96,6 +99,7 @@ #define WDC_NVME_ZN350_DEV_ID 0x5010 #define WDC_NVME_ZN350_DEV_ID_1 0x5018 #define WDC_NVME_SN810_DEV_ID 0x5011 +#define WDC_NVME_SN820CL_DEV_ID 0x5037 #define WDC_DRIVE_CAP_CAP_DIAG 0x0000000000000001 #define WDC_DRIVE_CAP_INTERNAL_LOG 0x0000000000000002 @@ -125,8 +129,10 @@ #define WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY 0x0000000002000000 #define WDC_DRIVE_CAP_CLOUD_SSD_VERSION 0x0000000004000000 #define WDC_DRIVE_CAP_PCIE_STATS 0x0000000008000000 -#define WDC_DRIVE_CAP_INFO_2 0x0000000010000000 +#define WDC_DRIVE_CAP_HW_REV_LOG_PAGE 0x0000000010000000 #define WDC_DRIVE_CAP_C3_LOG_PAGE 0x0000000020000000 +#define WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION 0x0000000040000000 +#define WDC_DRIVE_CAP_CLOUD_LOG_PAGE 0x0000000080000000 #define WDC_DRIVE_CAP_DRIVE_ESSENTIALS 0x0000000100000000 #define WDC_DRIVE_CAP_DUI_DATA 0x0000000200000000 @@ -136,6 +142,7 @@ #define WDC_DRIVE_CAP_OCP_C1_LOG_PAGE 0x0000002000000000 #define WDC_DRIVE_CAP_OCP_C4_LOG_PAGE 0x0000004000000000 #define WDC_DRIVE_CAP_OCP_C5_LOG_PAGE 0x0000008000000000 +#define WDC_DRIVE_CAP_DEVICE_WAF 0x0000010000000000 #define WDC_DRIVE_CAP_SMART_LOG_MASK (WDC_DRIVE_CAP_C0_LOG_PAGE | WDC_DRIVE_CAP_C1_LOG_PAGE | \ WDC_DRIVE_CAP_CA_LOG_PAGE | WDC_DRIVE_CAP_D0_LOG_PAGE) #define WDC_DRIVE_CAP_CLEAR_PCIE_MASK (WDC_DRIVE_CAP_CLEAR_PCIE | \ @@ -350,6 +357,7 @@ static __u8 wdc_lat_mon_guid[WDC_C3_GUID_LENGTH] = { 0x92, 0x7a, 0xc0, 0x8c, #define WDC_LOG_ID_C0 0xC0 #define WDC_LOG_ID_C1 0xC1 #define WDC_LOG_ID_C2 WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE +#define WDC_LOG_ID_C3 0xC3 #define WDC_LOG_ID_C4 0xC4 #define WDC_LOG_ID_C5 0xC5 #define WDC_LOG_ID_C6 0xC6 @@ -487,6 +495,121 @@ typedef enum WDC_DE_TYPE_ALL = 0xFFFFFFF, } WDC_DRIVE_ESSENTIAL_TYPE; +#define WDC_C0_GUID_LENGTH 16 +#define WDC_SCA_V1_NAND_STATS 0x1 +#define WDC_SCA_V1_ALL 0xF +typedef enum +{ + SCAO_V1_PMUWT = 0, /* Physical media units written TLC */ + SCAO_V1_PMUWS = 16, /* Physical media units written SLC */ + SCAO_V1_BUNBN = 32, /* Bad user nand blocks normalized */ + SCAO_V1_BUNBR = 34, /* Bad user nand blocks raw */ + SCAO_V1_XRC = 40, /* XOR recovery count */ + SCAO_V1_UREC = 48, /* Uncorrectable read error count */ + SCAO_V1_EECE = 56, /* End to end corrected errors */ + SCAO_V1_EEDE = 64, /* End to end detected errors */ + SCAO_V1_EEUE = 72, /* End to end uncorrected errors */ + SCAO_V1_SDPU = 80, /* System data percent used */ + SCAO_V1_MNUDEC = 84, /* Min User data erase counts (TLC) */ + SCAO_V1_MXUDEC = 92, /* Max User data erase counts (TLC) */ + SCAO_V1_AVUDEC = 100, /* Average User data erase counts (TLC) */ + SCAO_V1_MNEC = 108, /* Min Erase counts (SLC) */ + SCAO_V1_MXEC = 116, /* Max Erase counts (SLC) */ + SCAO_V1_AVEC = 124, /* Average Erase counts (SLC) */ + SCAO_V1_PFCN = 132, /* Program fail count normalized */ + SCAO_V1_PFCR = 134, /* Program fail count raw */ + SCAO_V1_EFCN = 140, /* Erase fail count normalized */ + SCAO_V1_EFCR = 142, /* Erase fail count raw */ + SCAO_V1_PCEC = 148, /* PCIe correctable error count */ + SCAO_V1_PFBU = 156, /* Percent free blocks (User) */ + SCAO_V1_SVN = 160, /* Security Version Number */ + SCAO_V1_PFBS = 168, /* Percent free blocks (System) */ + SCAO_V1_DCC = 172, /* Deallocate Commands Completed */ + SCAO_V1_TNU = 188, /* Total Namespace Utilization */ + SCAO_V1_FCC = 196, /* Format NVM Commands Completed */ + SCAO_V1_BBPG = 198, /* Background Back-Pressure Gauge */ + SCAO_V1_SEEC = 202, /* Soft ECC error count */ + SCAO_V1_RFSC = 210, /* Refresh count */ + SCAO_V1_BSNBN = 218, /* Bad system nand blocks normalized */ + SCAO_V1_BSNBR = 220, /* Bad system nand blocks raw */ + SCAO_V1_EEST = 226, /* Endurance estimate */ + SCAO_V1_TTC = 242, /* Thermal throttling count */ + SCAO_V1_UIO = 244, /* Unaligned I/O */ + SCAO_V1_PMUR = 252, /* Physical media units read */ + SCAO_V1_RTOC = 268, /* Read command timeout count */ + SCAO_V1_WTOC = 272, /* Write command timeout count */ + SCAO_V1_TTOC = 276, /* Trim command timeout count */ + SCAO_V1_PLRC = 284, /* PCIe Link Retraining Count */ + SCAO_V1_PSCC = 292, /* Power State Change Count */ + SCAO_V1_MAVF = 300, /* Boot SSD major version field */ + SCAO_V1_MIVF = 302, /* Boot SSD minor version field */ + SCAO_V1_PVF = 304, /* Boot SSD point version field */ + SCAO_V1_EVF = 306, /* Boot SSD errata version field */ + SCAO_V1_FTLUS = 308, /* FTL Unit Size */ + SCAO_V1_TCGOS = 312, /* TCG Ownership Status */ + + SCAO_V1_LPV = 494, /* Log page version - 0x0001 */ + SCAO_V1_LPG = 496, /* Log page GUID */ +} SMART_CLOUD_ATTRIBUTE_OFFSETS_V1; + +static __u8 ext_smart_guid[WDC_C0_GUID_LENGTH] = { 0x65, 0x43, 0x88, 0x78, 0xAC, 0xD8, 0x78, 0xA1, + 0x66, 0x42, 0x1E, 0x0F, 0x92, 0xD7, 0x6D, 0xC4 }; + +typedef struct __attribute__((__packed__)) wdc_nvme_ext_smart_log +{ + __u8 ext_smart_pmuwt[16]; /* 000 Physical media units written TLC */ + __u8 ext_smart_pmuws[16]; /* 016 Physical media units written SLC */ + __u8 ext_smart_bunbc[8]; /* 032 Bad user nand block count */ + __u64 ext_smart_xrc; /* 040 XOR recovery count */ + __u64 ext_smart_urec; /* 048 Uncorrectable read error count */ + __u64 ext_smart_eece; /* 056 End to end corrected errors */ + __u64 ext_smart_eede; /* 064 End to end detected errors */ + __u64 ext_smart_eeue; /* 072 End to end uncorrected errors */ + __u8 ext_smart_sdpu; /* 080 System data percent used */ + __u8 ext_smart_rsvd1[3]; /* 081 reserved */ + __u64 ext_smart_mnudec; /* 084 Min User data erase counts (TLC) */ + __u64 ext_smart_mxudec; /* 092 Max User data erase counts (TLC) */ + __u64 ext_smart_avudec; /* 100 Average User data erase counts (TLC) */ + __u64 ext_smart_mnec; /* 108 Min Erase counts (SLC) */ + __u64 ext_smart_mxec; /* 116 Max Erase counts (SLC) */ + __u64 ext_smart_avec; /* 124 Average Erase counts (SLC) */ + __u8 ext_smart_pfc[8]; /* 132 Program fail count */ + __u8 ext_smart_efc[8]; /* 140 Erase fail count */ + __u64 ext_smart_pcec; /* 148 PCIe correctable error count */ + __u8 ext_smart_pfbu; /* 156 Percent free blocks (User) */ + __u8 ext_smart_rsvd2[3]; /* 157 reserved */ + __u64 ext_smart_svn; /* 160 Security Version Number */ + __u8 ext_smart_pfbs; /* 168 Percent free blocks (System) */ + __u8 ext_smart_rsvd3[3]; /* 169 reserved */ + __u8 ext_smart_dcc[16]; /* 172 Deallocate Commands Completed */ + __u64 ext_smart_tnu; /* 188 Total Namespace Utilization */ + __u16 ext_smart_fcc; /* 196 Format NVM Commands Completed */ + __u8 ext_smart_bbpg; /* 198 Background Back-Pressure Gauge */ + __u8 ext_smart_rsvd4[3]; /* 199 reserved */ + __u64 ext_smart_seec; /* 202 Soft ECC error count */ + __u64 ext_smart_rfsc; /* 210 Refresh count */ + __u8 ext_smart_bsnbc[8]; /* 218 Bad system nand block count */ + __u8 ext_smart_eest[16]; /* 226 Endurance estimate */ + __u16 ext_smart_ttc; /* 242 Thermal throttling count */ + __u64 ext_smart_uio; /* 244 Unaligned I/O */ + __u8 ext_smart_pmur[16]; /* 252 Physical media units read */ + __u32 ext_smart_rtoc; /* 268 Read command timeout count */ + __u32 ext_smart_wtoc; /* 272 Write command timeout count */ + __u32 ext_smart_ttoc; /* 276 Trim command timeout count */ + __u8 ext_smart_rsvd5[4]; /* 280 reserved */ + __u64 ext_smart_plrc; /* 284 PCIe Link Retraining Count */ + __u64 ext_smart_pscc; /* 292 Power State Change Count */ + __u16 ext_smart_maj; /* 300 Boot SSD major version field */ + __u16 ext_smart_min; /* 302 Boot SSD minor version field */ + __u16 ext_smart_pt; /* 304 Boot SSD point version field */ + __u16 ext_smart_err; /* 306 Boot SSD errata version field */ + __u32 ext_smart_ftlus; /* 308 FTL Unit Size */ + __u32 ext_smart_tcgos; /* 312 TCG Ownership Status */ + __u8 ext_smart_rsvd6[178]; /* 316 reserved */ + __u16 ext_smart_lpv; /* 494 Log page version - 0x0001 */ + __u8 ext_smart_lpg[16]; /* 496 Log page GUID */ +} wdc_nvme_ext_smart_log; + typedef enum { SCAO_PMUW = 0, /* Physical media units written */ @@ -520,12 +643,11 @@ typedef enum SCAO_NUSE = 152, /* NUSE - Namespace utilization */ SCAO_PSC = 160, /* PLP start count */ SCAO_EEST = 176, /* Endurance estimate */ - SCAO_PLRC = 192, /* PCIe Link Retraining Count */ + SCAO_PLRC = 192, /* PCIe Link Retraining Count */ + SCAO_PSCC = 200, /* Power State Change Count */ SCAO_LPV = 494, /* Log page version */ SCAO_LPG = 496, /* Log page GUID */ -} SMART_CLOUD_ATTRIBUTE_OFFSETS; - -#define WDC_C0_GUID_LENGTH 16 +} SMART_CLOUD_ATTRIBUTE_OFFSETS_V3; static __u8 scao_guid[WDC_C0_GUID_LENGTH] = { 0xC5, 0xAF, 0x10, 0x28, 0xEA, 0xBF, 0xF2, 0xA4, 0x9C, 0x4F, 0x6F, 0x7C, 0xC9, 0x14, 0xD5, 0xAF }; @@ -542,6 +664,54 @@ typedef enum EOL_RRER = 108, /* Raw Read Error Rate */ } EOL_LOG_PAGE_C0_OFFSETS; +#define WDC_NVME_C6_GUID_LENGTH 16 +#define WDC_NVME_GET_HW_REV_LOG_OPCODE 0xc6 +#define WDC_NVME_HW_REV_LOG_PAGE_LEN 512 + +typedef struct __attribute__((__packed__)) wdc_nvme_hw_rev_log +{ + __u8 hw_rev_gdr; /* 0 Global Device HW Revision */ + __u8 hw_rev_ar; /* 1 ASIC HW Revision */ + __u8 hw_rev_pbc_mc; /* 2 PCB Manufacturer Code */ + __u8 hw_rev_dram_mc; /* 3 DRAM Manufacturer Code */ + __u8 hw_rev_nand_mc; /* 4 NAND Manufacturer Code */ + __u8 hw_rev_pmic1_mc; /* 5 PMIC 1 Manufacturer Code */ + __u8 hw_rev_pmic2_mc; /* 6 PMIC 2 Manufacturer Code */ + __u8 hw_rev_c1_mc; /* 7 Other Component 1 Manf Code */ + __u8 hw_rev_c2_mc; /* 8 Other Component 2 Manf Code */ + __u8 hw_rev_c3_mc; /* 9 Other Component 3 Manf Code */ + __u8 hw_rev_c4_mc; /* 10 Other Component 4 Manf Code */ + __u8 hw_rev_c5_mc; /* 11 Other Component 5 Manf Code */ + __u8 hw_rev_c6_mc; /* 12 Other Component 6 Manf Code */ + __u8 hw_rev_c7_mc; /* 13 Other Component 7 Manf Code */ + __u8 hw_rev_c8_mc; /* 14 Other Component 8 Manf Code */ + __u8 hw_rev_c9_mc; /* 15 Other Component 9 Manf Code */ + __u8 hw_rev_rsrvd1[48]; /* 16 Reserved 48 bytes */ + __u8 hw_rev_dev_mdi[16]; /* 64 Device Manf Detailed Info */ + __u8 hw_rev_asic_di[16]; /* 80 ASIC Detailed Info */ + __u8 hw_rev_pcb_di[16]; /* 96 PCB Detailed Info */ + __u8 hw_rev_dram_di[16]; /* 112 DRAM Detailed Info */ + __u8 hw_rev_nand_di[16]; /* 128 NAND Detailed Info */ + __u8 hw_rev_pmic1_di[16]; /* 144 PMIC1 Detailed Info */ + __u8 hw_rev_pmic2_di[16]; /* 160 PMIC2 Detailed Info */ + __u8 hw_rev_c1_di[16]; /* 176 Component 1 Detailed Info */ + __u8 hw_rev_c2_di[16]; /* 192 Component 2 Detailed Info */ + __u8 hw_rev_c3_di[16]; /* 208 Component 3 Detailed Info */ + __u8 hw_rev_c4_di[16]; /* 224 Component 4 Detailed Info */ + __u8 hw_rev_c5_di[16]; /* 240 Component 5 Detailed Info */ + __u8 hw_rev_c6_di[16]; /* 256 Component 6 Detailed Info */ + __u8 hw_rev_c7_di[16]; /* 272 Component 7 Detailed Info */ + __u8 hw_rev_c8_di[16]; /* 288 Component 8 Detailed Info */ + __u8 hw_rev_c9_di[16]; /* 304 Component 9 Detailed Info */ + __u8 hw_rev_sn[32]; /* 320 Serial Number */ + __u8 hw_rev_rsrvd2[142]; /* 352 Reserved 143 bytes */ + __u16 hw_rev_version; /* 494 Log Page Version */ + __u8 hw_rev_guid[16]; /* 496 Log Page GUID */ +} wdc_nvme_hw_rev_log; + +static __u8 hw_rev_log_guid[WDC_NVME_C6_GUID_LENGTH] = { 0xAA, 0xB0, 0x05, 0xF5, 0x13, 0x5E, 0x48, 0x15, + 0xAB, 0x89, 0x05, 0xBA, 0x8B, 0xE2, 0xBF, 0x3C }; + typedef struct __attribute__((__packed__)) _WDC_DE_VU_FILE_META_DATA { __u8 fileName[WDC_DE_FILE_NAME_SIZE]; @@ -1161,7 +1331,7 @@ static int wdc_get_pci_ids(nvme_root_t r, uint32_t *device_id, fprintf(stderr, "%s: Read of pci vendor id failed\n", __func__); return -1; } - + id[ret < 32 ? ret : 31] = '\0'; if (id[strlen(id) - 1] == '\n') id[strlen(id) - 1] = '\0'; @@ -1181,7 +1351,7 @@ static int wdc_get_pci_ids(nvme_root_t r, uint32_t *device_id, fprintf(stderr, "%s: Read of pci device id failed\n", __func__); return -1; } - + id[ret < 32 ? ret : 31] = '\0'; if (id[strlen(id) - 1] == '\n') id[strlen(id) - 1] = '\0'; @@ -1348,6 +1518,12 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, int fd) { case WDC_NVME_SN640_DEV_ID_2: /* FALLTHRU */ case WDC_NVME_SN640_DEV_ID_3: + /* FALLTHRU */ + case WDC_NVME_SN560_DEV_ID_1: + /* FALLTHRU */ + case WDC_NVME_SN560_DEV_ID_2: + /* FALLTHRU */ + case WDC_NVME_SN560_DEV_ID_3: /* verify the 0xC0 log page is supported */ if (wdc_nvme_check_supported_log_page(r, fd, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE) == true) { capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE; @@ -1400,6 +1576,8 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, int fd) { case WDC_NVME_SN840_DEV_ID: /* FALLTHRU */ case WDC_NVME_SN840_DEV_ID_1: + /* FALLTHRU */ + case WDC_NVME_SN860_DEV_ID: /* verify the 0xC0 log page is supported */ if (wdc_nvme_check_supported_log_page(r, fd, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE) == true) { capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE; @@ -1430,9 +1608,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, int fd) { case WDC_NVME_SN650_DEV_ID_3: case WDC_NVME_SN650_DEV_ID_4: case WDC_NVME_SN655_DEV_ID: - case WDC_NVME_SN560_DEV_ID_1: - case WDC_NVME_SN560_DEV_ID_2: - case WDC_NVME_SN560_DEV_ID_3: + case WDC_NVME_SN550_DEV_ID: /* verify the 0xC0 log page is supported */ if (wdc_nvme_check_supported_log_page(r, fd, WDC_NVME_GET_EOL_STATUS_LOG_OPCODE) == true) { capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE; @@ -1440,7 +1616,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, int fd) { capabilities |= (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | - WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | + WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_CLEAR_PCIE | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG | WDC_DRIVE_CAP_REASON_ID | WDC_DRIVE_CAP_LOG_PAGE_DIR | WDC_DRIVE_CAP_INFO | @@ -1470,12 +1646,19 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, int fd) { case WDC_NVME_SN810_DEV_ID: capabilities = WDC_DRIVE_CAP_DUI_DATA; break; + case WDC_NVME_SN820CL_DEV_ID: + capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION | + WDC_DRIVE_CAP_CLOUD_LOG_PAGE | WDC_DRIVE_CAP_C0_LOG_PAGE | + WDC_DRIVE_CAP_HW_REV_LOG_PAGE | WDC_DRIVE_CAP_INFO | + WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | WDC_DRIVE_CAP_NAND_STATS | + WDC_DRIVE_CAP_DEVICE_WAF | WDC_DRIVE_CAP_TEMP_STATS; + break; case WDC_NVME_SN720_DEV_ID: capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_NAND_STATS | WDC_DRIVE_CAP_NS_RESIZE; break; case WDC_NVME_SN730A_DEV_ID: - capabilities = WDC_DRIVE_CAP_DUI | WDC_DRIVE_CAP_NAND_STATS | WDC_DRIVE_CAP_INFO_2 - | WDC_DRIVE_CAP_TEMP_STATS | WDC_DRIVE_CAP_VUC_CLEAR_PCIE | WDC_DRIVE_CAP_PCIE_STATS; + capabilities = WDC_DRIVE_CAP_DUI | WDC_DRIVE_CAP_NAND_STATS | WDC_DRIVE_CAP_INFO | + WDC_DRIVE_CAP_TEMP_STATS | WDC_DRIVE_CAP_VUC_CLEAR_PCIE | WDC_DRIVE_CAP_PCIE_STATS; break; case WDC_NVME_SN740_DEV_ID: case WDC_NVME_SN740_DEV_ID_1: @@ -1628,6 +1811,7 @@ static int wdc_create_log_file(char *file, __u8 *drive_log_data, ret = write(fd, drive_log_data, WRITE_SIZE); if (ret < 0) { fprintf (stderr, "ERROR : WDC: write : %s\n", strerror(errno)); + close(fd); return -1; } drive_log_data += WRITE_SIZE; @@ -1637,11 +1821,13 @@ static int wdc_create_log_file(char *file, __u8 *drive_log_data, ret = write(fd, drive_log_data, drive_log_length); if (ret < 0) { fprintf(stderr, "ERROR : WDC : write : %s\n", strerror(errno)); + close(fd); return -1; } if (fsync(fd) < 0) { fprintf(stderr, "ERROR : WDC : fsync : %s\n", strerror(errno)); + close(fd); return -1; } close(fd); @@ -2204,13 +2390,12 @@ static int wdc_do_cap_telemetry_log(int fd, char *file, __u32 bs, int type, int if (err) { fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed " "0x%x\n", err); - goto close_fd; + return err; } if (!(ctrl.lpa & 0x8)) { fprintf(stderr, "Telemetry Host-Initiated and Telemetry Controller-Initiated log pages not supported\n"); - err = -EINVAL; - goto close_fd; + return -EINVAL; } r = nvme_scan(NULL); @@ -2232,15 +2417,13 @@ static int wdc_do_cap_telemetry_log(int fd, char *file, __u32 bs, int type, int } else { fprintf(stderr, "%s: Controller initiated option telemetry log page disabled\n", __func__); - err = -EINVAL; - goto close_fd; + return -EINVAL; } } else { fprintf(stderr, "ERROR : WDC: Get telemetry option feature failed."); nvme_show_status(err); - err = -EPERM; - goto close_fd; + return -EPERM; } } else { @@ -2249,22 +2432,19 @@ static int wdc_do_cap_telemetry_log(int fd, char *file, __u32 bs, int type, int } } else { fprintf(stderr, "%s: Invalid type parameter; type = %d\n", __func__, type); - err = -EINVAL; - goto close_fd; + return -EINVAL; } if (!file) { fprintf(stderr, "%s: Please provide an output file!\n", __func__); - err = -EINVAL; - goto close_fd; + return -EINVAL; } output = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (output < 0) { fprintf(stderr, "%s: Failed to open output file %s: %s!\n", __func__, file, strerror(errno)); - err = output; - goto close_fd; + return output; } if (ctrl_init) @@ -2274,9 +2454,10 @@ static int wdc_do_cap_telemetry_log(int fd, char *file, __u32 bs, int type, int else err = nvme_get_host_telemetry(fd, &log, data_area, &full_size); - if (err < 0) + if (err < 0) { perror("get-telemetry-log"); - else if (err > 0) { + goto close_output; + } else if (err > 0) { nvme_show_status(err); fprintf(stderr, "%s: Failed to acquire telemetry header!\n", __func__); goto close_output; @@ -2309,17 +2490,13 @@ static int wdc_do_cap_telemetry_log(int fd, char *file, __u32 bs, int type, int if (fsync(output) < 0) { fprintf(stderr, "ERROR : %s: fsync : %s\n", __func__, strerror(errno)); - return -1; + err = -1; } free(log); close_output: close(output); -close_fd: - close(fd); - return err; - } static int wdc_do_cap_diag(nvme_root_t r, int fd, char *file, @@ -2757,7 +2934,7 @@ static int wdc_cap_diag(int argc, char **argv, struct command *command, char *size = "Data retrieval transfer size."; char f[PATH_MAX] = {0}; __u32 xfer_size = 0; - int fd; + int fd, ret = 0; __u64 capabilities = 0; struct config { @@ -2776,30 +2953,40 @@ static int wdc_cap_diag(int argc, char **argv, struct command *command, OPT_END() }; - r = nvme_scan(NULL); - fd = parse_and_open(argc, argv, desc, opts); if (fd < 0) return fd; + r = nvme_scan(NULL); + if (cfg.file != NULL) strncpy(f, cfg.file, PATH_MAX - 1); if (cfg.xfer_size != 0) xfer_size = cfg.xfer_size; - if (wdc_get_serial_name(fd, f, PATH_MAX, "cap_diag") == -1) { + ret = wdc_get_serial_name(fd, f, PATH_MAX, "cap_diag"); + if (ret) { fprintf(stderr, "ERROR : WDC: failed to generate file name\n"); - return -1; + goto out; + } + if (cfg.file == NULL) { + if (strlen(f) > PATH_MAX - 5) { + fprintf(stderr, "ERROR : WDC: file name overflow\n"); + ret = -1; + goto out; + } + strcat(f, ".bin"); } - if (cfg.file == NULL) - snprintf(f + strlen(f), PATH_MAX, "%s", ".bin"); capabilities = wdc_get_drive_capabilities(r, fd); if ((capabilities & WDC_DRIVE_CAP_CAP_DIAG) == WDC_DRIVE_CAP_CAP_DIAG) - return wdc_do_cap_diag(r, fd, f, xfer_size, 0, 0); - - fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = wdc_do_cap_diag(r, fd, f, xfer_size, 0, 0); + else + fprintf(stderr, + "ERROR : WDC: unsupported device for this command\n"); +out: nvme_free_tree(r); - return 0; + close(fd); + return ret; } static int wdc_do_get_sn730_log_len(int fd, uint32_t *len_buf, uint32_t subopcode) @@ -3047,7 +3234,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command char fileSuffix[PATH_MAX] = {0}; nvme_root_t r; __u32 xfer_size = 0; - int fd; + int fd, ret = -1; int telemetry_type = 0, telemetry_data_area = 0; UtilsTimeInfo timeInfo; __u8 timeStamp[MAX_PATH_LEN]; @@ -3089,16 +3276,14 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command return fd; r = nvme_scan(NULL); - if (!wdc_check_device(r, fd)) { - nvme_free_tree(r); - return -1; - } + if (!wdc_check_device(r, fd)) + goto out; + if (cfg.xfer_size != 0) xfer_size = cfg.xfer_size; else { fprintf(stderr, "ERROR : WDC : Invalid length\n"); - nvme_free_tree(r); - return -1; + goto out; } if (cfg.file != NULL) { @@ -3108,7 +3293,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command verify_file = open(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (verify_file < 0) { fprintf(stderr, "ERROR : WDC: open : %s\n", strerror(errno)); - return -1; + goto out; } close(verify_file); strncpy(f, cfg.file, PATH_MAX - 1); @@ -3120,20 +3305,28 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command timeInfo.hour, timeInfo.minute, timeInfo.second); snprintf(fileSuffix, PATH_MAX, "_internal_fw_log_%s", (char*)timeStamp); - if (wdc_get_serial_name(fd, f, PATH_MAX, fileSuffix) == -1) { + ret = wdc_get_serial_name(fd, f, PATH_MAX, fileSuffix); + if (ret) { fprintf(stderr, "ERROR : WDC: failed to generate file name\n"); - return -1; + goto out; } } - if (cfg.file == NULL) - snprintf(f + strlen(f), PATH_MAX, "%s", ".bin"); + if (cfg.file == NULL) { + if (strlen(f) > PATH_MAX - 5) { + fprintf(stderr, "ERROR : WDC: file name overflow\n"); + ret = -1; + goto out; + } + strcat(f, ".bin"); + } fprintf(stderr, "%s: filename = %s\n", __func__, f); if (cfg.data_area) { if (cfg.data_area > 5 || cfg.data_area < 1) { fprintf(stderr, "ERROR : WDC: Data area must be 1-5\n"); - return -1; + ret = -1; + goto out; } } @@ -3152,7 +3345,8 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command telemetry_data_area = cfg.data_area; } else { fprintf(stderr, "ERROR : WDC: Invalid type - Must be NONE, HOST or CONTROLLER\n"); - return -1; + ret = -1; + goto out; } capabilities = wdc_get_drive_capabilities(r, fd); @@ -3160,8 +3354,9 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command if (telemetry_data_area == 0) telemetry_data_area = 3; /* Set the default DA to 3 if not specified */ - return wdc_do_cap_diag(r, fd, f, xfer_size, - telemetry_type, telemetry_data_area); + ret = wdc_do_cap_diag(r, fd, f, xfer_size, + telemetry_type, telemetry_data_area); + goto out; } if ((capabilities & WDC_DRIVE_CAP_DUI) == WDC_DRIVE_CAP_DUI) { if ((telemetry_type == WDC_TELEMETRY_TYPE_HOST) || @@ -3169,18 +3364,20 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command if (telemetry_data_area == 0) telemetry_data_area = 3; /* Set the default DA to 3 if not specified */ /* Get the desired telemetry log page */ - return wdc_do_cap_telemetry_log(fd, f, xfer_size, telemetry_type, telemetry_data_area); - } - else { - if (cfg.data_area == 0) { + ret = wdc_do_cap_telemetry_log(fd, f, xfer_size, + telemetry_type, telemetry_data_area); + goto out; + } else { + if (cfg.data_area == 0) cfg.data_area = 1; - } /* FW requirement - xfer size must be 256k for data area 4 */ if (cfg.data_area >= 4) xfer_size = 0x40000; - return wdc_do_cap_dui(fd, f, xfer_size, cfg.data_area, - cfg.verbose, cfg.file_size, cfg.offset); + ret = wdc_do_cap_dui(fd, f, xfer_size, cfg.data_area, + cfg.verbose, cfg.file_size, + cfg.offset); + goto out; } } if ((capabilities & WDC_DRIVE_CAP_DUI_DATA) == WDC_DRIVE_CAP_DUI_DATA){ @@ -3189,17 +3386,26 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command if (telemetry_data_area == 0) telemetry_data_area = 3; /* Set the default DA to 3 if not specified */ /* Get the desired telemetry log page */ - return wdc_do_cap_telemetry_log(fd, f, xfer_size, telemetry_type, telemetry_data_area); - } - else { - return wdc_do_cap_dui(fd, f, xfer_size, WDC_NVME_DUI_MAX_DATA_AREA, cfg.verbose, 0, 0); + ret = wdc_do_cap_telemetry_log(fd, f, xfer_size, + telemetry_type, telemetry_data_area); + goto out; + } else { + ret = wdc_do_cap_dui(fd, f, xfer_size, + WDC_NVME_DUI_MAX_DATA_AREA, + cfg.verbose, 0, 0); + goto out; } } if ((capabilities & WDC_SN730B_CAP_VUC_LOG) == WDC_SN730B_CAP_VUC_LOG) - return wdc_do_sn730_get_and_tar(fd, f); - - fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); - return -1; + ret = wdc_do_sn730_get_and_tar(fd, f); + else { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + } +out: + nvme_free_tree(r); + close(fd); + return ret; } static int wdc_do_crash_dump(int fd, char *file, int type) @@ -3277,6 +3483,7 @@ static int wdc_crash_dump(int fd, char *file, int type) { char f[PATH_MAX] = {0}; const char *dump_type; + int ret; if (file != NULL) { strncpy(f, file, PATH_MAX - 1); @@ -3287,11 +3494,13 @@ static int wdc_crash_dump(int fd, char *file, int type) else dump_type = "_crash_dump"; - if (wdc_get_serial_name(fd, f, PATH_MAX, dump_type) == -1) { + ret = wdc_get_serial_name(fd, f, PATH_MAX, dump_type); + if (ret) fprintf(stderr, "ERROR : WDC : failed to generate file name\n"); - return -1; - } - return wdc_do_crash_dump(fd, f, type); + else + ret = wdc_do_crash_dump(fd, f, type); \ + close(fd); + return ret; } static int wdc_do_drive_log(int fd, char *file) @@ -3365,6 +3574,7 @@ static int wdc_drive_log(int argc, char **argv, struct command *command, if (!wdc_check_device(r, fd)) { nvme_free_tree(r); + close(fd); return -1; } capabilities = wdc_get_drive_capabilities(r, fd); @@ -3376,14 +3586,14 @@ static int wdc_drive_log(int argc, char **argv, struct command *command, if (cfg.file != NULL) { strncpy(f, cfg.file, PATH_MAX - 1); } - if (wdc_get_serial_name(fd, f, PATH_MAX, "drive_log") == -1) { + ret = wdc_get_serial_name(fd, f, PATH_MAX, "drive_log"); + if (ret) fprintf(stderr, "ERROR : WDC : failed to generate file name\n"); - nvme_free_tree(r); - return -1; - } - ret = wdc_do_drive_log(fd, f); + else + ret = wdc_do_drive_log(fd, f); } nvme_free_tree(r); + close(fd); return ret; } @@ -3417,6 +3627,7 @@ static int wdc_get_crash_dump(int argc, char **argv, struct command *command, if (!wdc_check_device(r, fd)) { nvme_free_tree(r); + close(fd); return -1; } @@ -3433,6 +3644,7 @@ static int wdc_get_crash_dump(int argc, char **argv, struct command *command, } } nvme_free_tree(r); + close(fd); return ret; } @@ -3466,6 +3678,7 @@ static int wdc_get_pfail_dump(int argc, char **argv, struct command *command, if (!wdc_check_device(r, fd)) { nvme_free_tree(r); + close(fd); return -1; } @@ -3480,6 +3693,7 @@ static int wdc_get_pfail_dump(int argc, char **argv, struct command *command, } } nvme_free_tree(r); + close(fd); return ret; } @@ -3553,6 +3767,7 @@ static int wdc_purge(int argc, char **argv, if (!wdc_check_device(r, fd)) { nvme_free_tree(r); + close(fd); return -1; } @@ -3584,6 +3799,7 @@ static int wdc_purge(int argc, char **argv, nvme_show_status(ret); } nvme_free_tree(r); + close(fd); return ret; } @@ -3610,6 +3826,7 @@ static int wdc_purge_monitor(int argc, char **argv, r = nvme_scan(NULL); if (!wdc_check_device(r, fd)) { nvme_free_tree(r); + close(fd); return -1; } @@ -3642,6 +3859,7 @@ static int wdc_purge_monitor(int argc, char **argv, nvme_show_status(ret); } nvme_free_tree(r); + close(fd); return ret; } @@ -4234,7 +4452,7 @@ static void wdc_print_bd_ca_log_normal(void *data) __u8 *byte_raw; if (bd_data->field_id == 0x00) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", devicename, WDC_DE_GLOBAL_NSID); printf("key normalized raw\n"); @@ -4245,7 +4463,7 @@ static void wdc_print_bd_ca_log_normal(void *data) } bd_data++; if (bd_data->field_id == 0x01) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("erase_fail_count : %3"PRIu8"%% %"PRIu64"\n", bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { @@ -4266,7 +4484,7 @@ static void wdc_print_bd_ca_log_normal(void *data) } bd_data++; if (bd_data->field_id == 0x03) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("end_to_end_error_detection_count: %3"PRIu8"%% %"PRIu64"\n", bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { @@ -4274,7 +4492,7 @@ static void wdc_print_bd_ca_log_normal(void *data) } bd_data++; if (bd_data->field_id == 0x04) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("crc_error_count : %3"PRIu8"%% %"PRIu64"\n", bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { @@ -4282,7 +4500,7 @@ static void wdc_print_bd_ca_log_normal(void *data) } bd_data++; if (bd_data->field_id == 0x05) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("timed_workload_media_wear : %3"PRIu8"%% %-.3f%%\n", bd_data->normalized_value, safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0)); @@ -4291,7 +4509,7 @@ static void wdc_print_bd_ca_log_normal(void *data) } bd_data++; if (bd_data->field_id == 0x06) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("timed_workload_host_reads : %3"PRIu8"%% %"PRIu64"%%\n", bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { @@ -4299,7 +4517,7 @@ static void wdc_print_bd_ca_log_normal(void *data) } bd_data++; if (bd_data->field_id == 0x07) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("timed_workload_timer : %3"PRIu8"%% %"PRIu64"\n", bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { @@ -4316,7 +4534,7 @@ static void wdc_print_bd_ca_log_normal(void *data) } bd_data++; if (bd_data->field_id == 0x09) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("retry_buffer_overflow_count : %3"PRIu8"%% %"PRIu64"\n", bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { @@ -4324,7 +4542,7 @@ static void wdc_print_bd_ca_log_normal(void *data) } bd_data++; if (bd_data->field_id == 0x0A) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("pll_lock_loss_count : %3"PRIu8"%% %"PRIu64"\n", bd_data->normalized_value, le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF)); } else { @@ -4332,7 +4550,7 @@ static void wdc_print_bd_ca_log_normal(void *data) } bd_data++; if (bd_data->field_id == 0x0B) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("nand_bytes_written : %3"PRIu8"%% sectors: %.f\n", bd_data->normalized_value, safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); } else { @@ -4340,7 +4558,7 @@ static void wdc_print_bd_ca_log_normal(void *data) } bd_data++; if (bd_data->field_id == 0x0C) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; printf("host_bytes_written : %3"PRIu8"%% sectors: %.f\n", bd_data->normalized_value, safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); } else { @@ -4368,7 +4586,7 @@ static void wdc_print_bd_ca_log_json(void *data) root = json_create_object(); if (bd_data->field_id == 0x00) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "program_fail_count normalized", bd_data->normalized_value); json_object_add_value_int(root, "program_fail_count raw", @@ -4378,7 +4596,7 @@ static void wdc_print_bd_ca_log_json(void *data) } bd_data++; if (bd_data->field_id == 0x01) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "erase_fail_count normalized", bd_data->normalized_value); json_object_add_value_int(root, "erase_fail_count raw", @@ -4400,7 +4618,7 @@ static void wdc_print_bd_ca_log_json(void *data) } bd_data++; if (bd_data->field_id == 0x03) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "end_to_end_error_detection_count normalized", bd_data->normalized_value); json_object_add_value_int(root, "end_to_end_error_detection_count raw", @@ -4410,7 +4628,7 @@ static void wdc_print_bd_ca_log_json(void *data) } bd_data++; if (bd_data->field_id == 0x04) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "crc_error_count normalized", bd_data->normalized_value); json_object_add_value_int(root, "crc_error_count raw", @@ -4420,17 +4638,17 @@ static void wdc_print_bd_ca_log_json(void *data) } bd_data++; if (bd_data->field_id == 0x05) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "timed_workload_media_wear normalized", bd_data->normalized_value); - json_object_add_value_float(root, "timed_workload_media_wear raw", + json_object_add_value_double(root, "timed_workload_media_wear raw", safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x06) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "timed_workload_host_reads normalized", bd_data->normalized_value); json_object_add_value_int(root, "timed_workload_host_reads raw", @@ -4440,7 +4658,7 @@ static void wdc_print_bd_ca_log_json(void *data) } bd_data++; if (bd_data->field_id == 0x07) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "timed_workload_timer normalized", bd_data->normalized_value); json_object_add_value_int(root, "timed_workload_timer", @@ -4461,7 +4679,7 @@ static void wdc_print_bd_ca_log_json(void *data) } bd_data++; if (bd_data->field_id == 0x09) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "retry_buffer_overflow_count normalized", bd_data->normalized_value); json_object_add_value_int(root, "retry_buffer_overflow_count raw", @@ -4471,7 +4689,7 @@ static void wdc_print_bd_ca_log_json(void *data) } bd_data++; if (bd_data->field_id == 0x0A) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "pll_lock_loss_count normalized", bd_data->normalized_value); json_object_add_value_int(root, "pll_lock_loss_count raw", @@ -4481,20 +4699,20 @@ static void wdc_print_bd_ca_log_json(void *data) } bd_data++; if (bd_data->field_id == 0x0B) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "nand_bytes_written normalized", bd_data->normalized_value); - json_object_add_value_float(root, "nand_bytes_written raw", + json_object_add_value_double(root, "nand_bytes_written raw", safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); } else { goto invalid_id; } bd_data++; if (bd_data->field_id == 0x0C) { - raw = (__u64*)&bd_data->raw_value[1]; + raw = (__u64*)&bd_data->raw_value[0]; json_object_add_value_int(root, "host_bytes_written normalized", bd_data->normalized_value); - json_object_add_value_float(root, "host_bytes_written raw", + json_object_add_value_double(root, "host_bytes_written raw", safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF)); } else { goto invalid_id; @@ -4678,6 +4896,7 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, __u struct wdc_fw_act_history_log_format_c2 *fw_act_history_entry = (struct wdc_fw_act_history_log_format_c2 *)(data); + oldestEntryIdx = WDC_MAX_NUM_ACT_HIST_ENTRIES; if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { /* find lowest/oldest entry */ for (i = 0; i < num_entries; i++) { @@ -4761,6 +4980,7 @@ static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, __u struct wdc_fw_act_history_log_entry *fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); + oldestEntryIdx = WDC_MAX_NUM_ACT_HIST_ENTRIES; if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { /* find lowest/oldest entry */ for (i = 0; i < num_entries; i++) { @@ -4840,6 +5060,7 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, __u32 if(data[0] == WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) { struct wdc_fw_act_history_log_format_c2 *fw_act_history_entry = (struct wdc_fw_act_history_log_format_c2 *)(data); + oldestEntryIdx = WDC_MAX_NUM_ACT_HIST_ENTRIES; if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { /* find lowest/oldest entry */ for (i = 0; i < num_entries; i++) { @@ -4910,6 +5131,7 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, __u32 else { struct wdc_fw_act_history_log_entry *fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); + oldestEntryIdx = WDC_MAX_NUM_ACT_HIST_ENTRIES; if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) { /* find lowest/oldest entry */ for (i = 0; i < num_entries; i++) { @@ -4970,145 +5192,817 @@ static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries, __u32 json_free_object(root); } -static void wdc_print_smart_cloud_attr_C0_normal(void *data) +static int nvme_get_ext_smart_cloud_log(int fd, __u8 **data, int uuid_index, __u32 namespace_id) { - __u8 *log_data = (__u8*)data; - uint16_t smart_log_ver = 0; + int ret, i; + __u8 *log_ptr = NULL; - printf(" SMART Cloud Attributes :- \n"); + if ((log_ptr = (__u8*) malloc(sizeof (__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } - printf(" Physical media units written : %"PRIu64" %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW+8] & 0xFFFFFFFFFFFFFFFF), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF)); - printf(" Physical media units read : %"PRIu64" %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR+8] & 0xFFFFFFFFFFFFFFFF), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF)); - printf(" Bad user nand blocks Raw : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF)); - printf(" Bad user nand blocks Normalized : %d\n", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN])); - printf(" Bad system nand blocks Raw : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF)); - printf(" Bad system nand blocks Normalized : %d\n", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN])); - printf(" XOR recovery count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC])); - printf(" Uncorrectable read error count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC])); - printf(" Soft ecc error count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC])); - printf(" End to end corrected errors : %"PRIu32"\n", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE])); - printf(" End to end detected errors : %"PRIu32"\n", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC])); - printf(" System data percent used : %d\n", (__u8)log_data[SCAO_SDPU]); - printf(" Refresh counts : %"PRIu64"\n", - (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC])& 0x00FFFFFFFFFFFFFF)); - printf(" Max User data erase counts : %"PRIu32"\n", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC])); - printf(" Min User data erase counts : %"PRIu32"\n", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); - printf(" Number of Thermal throttling events : %d\n", (__u8)log_data[SCAO_NTTE]); - printf(" Current throttling status : 0x%x\n", (__u8)log_data[SCAO_CTS]); - printf(" PCIe correctable error count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); - printf(" Incomplete shutdowns : %"PRIu32"\n", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS])); - printf(" Percent free blocks : %d\n", (__u8)log_data[SCAO_PFB]); - printf(" Capacitor health : %"PRIu16"\n", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH])); - printf(" Unaligned I/O : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO])); - printf(" Security Version Number : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN])); - printf(" NUSE Namespace utilization : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE])); - printf(" PLP start count : %.0Lf\n", int128_to_double(&log_data[SCAO_PSC])); - printf(" Endurance estimate : %.0Lf\n", int128_to_double(&log_data[SCAO_EEST])); - smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]); - printf(" Log page version : %"PRIu16"\n",smart_log_ver); - printf(" Log page GUID : 0x"); - printf("0x%"PRIx64"%"PRIx64"\n",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]), - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG])); - if(smart_log_ver > 2) { - printf(" Errata Version Field : %d\n", - (__u8)log_data[SCAO_EVF]); - printf(" Point Version Field : %"PRIu16"\n", - (uint16_t)log_data[SCAO_PVF]); - printf(" Minor Version Field : %"PRIu16"\n", - (uint16_t)log_data[SCAO_MIVF]); - printf(" Major Version Field : %d\n", - (__u8)log_data[SCAO_MAVF]); - printf(" NVMe Errata Version : %d\n", - (__u8)log_data[SCAO_NEV]); - printf(" PCIe Link Retraining Count : %"PRIu64"\n", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC])); + /* Get the 0xC0 log data */ + struct nvme_get_log_args args = { + .args_size = sizeof(args), + .fd = fd, + .lid = WDC_NVME_GET_EOL_STATUS_LOG_OPCODE, + .nsid = namespace_id, + .lpo = 0, + .lsp = NVME_LOG_LSP_NONE, + .lsi = 0, + .rae = false, + .uuidx = uuid_index, + .csi = NVME_CSI_NVM, + .ot = false, + .len = WDC_NVME_SMART_CLOUD_ATTR_LEN, + .log = log_ptr, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + ret = nvme_get_log(&args); + + if (ret == 0) { + + /* Verify GUID matches */ + for (i = 0; i < WDC_C0_GUID_LENGTH; i++) { + if (ext_smart_guid[i] != *&log_ptr[SCAO_V1_LPG + i]) { + fprintf(stderr, "ERROR : WDC : Unknown GUID in C0 Log Page V1 data\n"); + int j; + fprintf(stderr, "ERROR : WDC : Expected GUID: 0x"); + for (j = 0; j < WDC_C0_GUID_LENGTH; j++) { + fprintf(stderr, "%x", ext_smart_guid[j]); + } + fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x"); + for (j = 0; j < WDC_C0_GUID_LENGTH; j++) { + fprintf(stderr, "%x", *&log_ptr[SCAO_V1_LPG + j]); + } + fprintf(stderr, "\n"); + + ret = -1; + break; + } + } } - printf("\n"); + + *data = log_ptr; + + return ret; } -static void wdc_print_smart_cloud_attr_C0_json(void *data) + +static int nvme_get_hw_rev_log(int fd, __u8 **data, int uuid_index, __u32 namespace_id) { - __u8 *log_data = (__u8*)data; - struct json_object *root; - uint16_t smart_log_ver = 0; + int ret, i; + wdc_nvme_hw_rev_log *log_ptr = NULL; - root = json_create_object(); - json_object_add_value_uint64(root, "Physical media units written hi", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW+8] & 0xFFFFFFFFFFFFFFFF)); - json_object_add_value_uint64(root, "Physical media units written lo", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF)); - json_object_add_value_uint64(root, "Physical media units read hi", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR+8] & 0xFFFFFFFFFFFFFFFF)); - json_object_add_value_uint64(root, "Physical media units read lo", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF)); - json_object_add_value_uint64(root, "Bad user nand blocks - Raw", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF)); - json_object_add_value_uint(root, "Bad user nand blocks - Normalized", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN])); - json_object_add_value_uint64(root, "Bad system nand blocks - Raw", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF)); - json_object_add_value_uint(root, "Bad system nand blocks - Normalized", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN])); - json_object_add_value_uint64(root, "XOR recovery count", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC])); - json_object_add_value_uint64(root, "Uncorrectable read error count", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC])); - json_object_add_value_uint64(root, "Soft ecc error count", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC])); - json_object_add_value_uint(root, "End to end corrected errors", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE])); - json_object_add_value_uint(root, "End to end detected errors", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC])); - json_object_add_value_uint(root, "System data percent used", - (__u8)log_data[SCAO_SDPU]); - json_object_add_value_uint64(root, "Refresh counts", - (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC])& 0x00FFFFFFFFFFFFFF)); - json_object_add_value_uint(root, "Max User data erase counts", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC])); - json_object_add_value_uint(root, "Min User data erase counts", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); - json_object_add_value_uint(root, "Number of Thermal throttling events", - (__u8)log_data[SCAO_NTTE]); - json_object_add_value_uint(root, "Current throttling status", - (__u8)log_data[SCAO_CTS]); - json_object_add_value_uint64(root, "PCIe correctable error count", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); - json_object_add_value_uint(root, "Incomplete shutdowns", - (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS])); - json_object_add_value_uint(root, "Percent free blocks", - (__u8)log_data[SCAO_PFB]); - json_object_add_value_uint(root, "Capacitor health", - (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH])); - json_object_add_value_uint64(root, "Unaligned I/O", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO])); - json_object_add_value_uint64(root, "Security Version Number", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN])); - json_object_add_value_uint64(root, "NUSE - Namespace utilization", - (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE])); - json_object_add_value_uint(root, "PLP start count", + if ((log_ptr = (wdc_nvme_hw_rev_log *)malloc(sizeof (__u8) * WDC_NVME_HW_REV_LOG_PAGE_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + /* Get the 0xC0 log data */ + struct nvme_get_log_args args = { + .args_size = sizeof(args), + .fd = fd, + .lid = WDC_NVME_GET_HW_REV_LOG_OPCODE, + .nsid = namespace_id, + .lpo = 0, + .lsp = NVME_LOG_LSP_NONE, + .lsi = 0, + .rae = false, + .uuidx = uuid_index, + .csi = NVME_CSI_NVM, + .ot = false, + .len = WDC_NVME_HW_REV_LOG_PAGE_LEN, + .log = log_ptr, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + ret = nvme_get_log(&args); + + if (ret == 0) { + + /* Verify GUID matches */ + for (i = 0; i < WDC_NVME_C6_GUID_LENGTH; i++) { + if (hw_rev_log_guid[i] != log_ptr->hw_rev_guid[i]) { + fprintf(stderr, "ERROR : WDC : Unknown GUID in HW Revision Log Page data\n"); + int j; + fprintf(stderr, "ERROR : WDC : Expected GUID: 0x"); + for (j = 0; j < WDC_NVME_C6_GUID_LENGTH; j++) { + fprintf(stderr, "%x", hw_rev_log_guid[j]); + } + fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x"); + for (j = 0; j < WDC_NVME_C6_GUID_LENGTH; j++) { + fprintf(stderr, "%x", log_ptr->hw_rev_guid[j]); + } + fprintf(stderr, "\n"); + + ret = -1; + break; + } + } + } + + *data = (__u8 *)log_ptr; + + return ret; +} + + +static void wdc_print_hw_rev_log_normal(void *data) +{ + int i; + wdc_nvme_hw_rev_log *log_data = (wdc_nvme_hw_rev_log *)data; + + printf(" Hardware Revision Log:- \n"); + + printf(" Global Device HW Revision : %d\n", + log_data->hw_rev_gdr); + printf(" ASIC HW Revision : %d\n", + log_data->hw_rev_ar); + printf(" PCB Manufacturer Code : %d\n", + log_data->hw_rev_pbc_mc); + printf(" DRAM Manufacturer Code : %d\n", + log_data->hw_rev_dram_mc); + printf(" NAND Manufacturer Code : %d\n", + log_data->hw_rev_nand_mc); + printf(" PMIC 1 Manufacturer Code : %d\n", + log_data->hw_rev_pmic1_mc); + printf(" PMIC 2 Manufacturer Code : %d\n", + log_data->hw_rev_pmic2_mc); + printf(" Other Component 1 Manf Code : %d\n", + log_data->hw_rev_c1_mc); + printf(" Other Component 2 Manf Code : %d\n", + log_data->hw_rev_c2_mc); + printf(" Other Component 3 Manf Code : %d\n", + log_data->hw_rev_c3_mc); + printf(" Other Component 4 Manf Code : %d\n", + log_data->hw_rev_c4_mc); + printf(" Other Component 5 Manf Code : %d\n", + log_data->hw_rev_c5_mc); + printf(" Other Component 6 Manf Code : %d\n", + log_data->hw_rev_c6_mc); + printf(" Other Component 7 Manf Code : %d\n", + log_data->hw_rev_c7_mc); + printf(" Other Component 8 Manf Code : %d\n", + log_data->hw_rev_c8_mc); + printf(" Other Component 9 Manf Code : %d\n", + log_data->hw_rev_c9_mc); + + printf(" Device Manf Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_dev_mdi[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" ASIC Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_asic_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" PCB Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_pcb_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" DRAM Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_dram_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" NAND Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_nand_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" PMIC 1 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_pmic1_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" PMIC 2 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_pmic2_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" Component 1 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_c1_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" Component 2 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_c2_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" Component 3 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_c3_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" Component 4 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_c4_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" Component 5 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_c5_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" Component 6 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_c6_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" Component 7 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_c7_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" Component 8 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_c8_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" Component 9 Detailed Info : 0x"); + for (i = 0; i < 16; i++) { + printf("%02x", log_data->hw_rev_c9_di[i]); + if (i == 7) + printf(" 0x"); + } + printf("\n"); + printf(" Serial Number : 0x"); + for (i = 0; i < 32; i++) { + if ((i > 1) & !(i % 8)) + printf(" 0x"); + printf("%02x", log_data->hw_rev_sn[i]); + } + printf("\n"); + + printf(" Log Page Version : %d\n", + log_data->hw_rev_version); + printf(" Log page GUID : 0x"); + printf("%"PRIx64"%"PRIx64"\n",le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[0])); + printf("\n"); +} + +static void wdc_print_hw_rev_log_json(void *data) +{ + wdc_nvme_hw_rev_log *log_data = (wdc_nvme_hw_rev_log *)data; + struct json_object *root; + char json_data[80]; + + root = json_create_object(); + json_object_add_value_uint(root, "Global Device HW Revision", + log_data->hw_rev_gdr); + json_object_add_value_uint(root, "ASIC HW Revision", + log_data->hw_rev_ar); + json_object_add_value_uint(root, "PCB Manufacturer Code", + log_data->hw_rev_pbc_mc); + json_object_add_value_uint(root, "DRAM Manufacturer Code", + log_data->hw_rev_dram_mc); + json_object_add_value_uint(root, "NAND Manufacturer Code", + log_data->hw_rev_nand_mc); + json_object_add_value_uint(root, "PMIC 1 Manufacturer Code", + log_data->hw_rev_pmic1_mc); + json_object_add_value_uint(root, "PMIC 2 Manufacturer Code", + log_data->hw_rev_pmic2_mc); + json_object_add_value_uint(root, "Other Component 1 Manf Code", + log_data->hw_rev_c1_mc); + json_object_add_value_uint(root, "Other Component 2 Manf Code", + log_data->hw_rev_c2_mc); + json_object_add_value_uint(root, "Other Component 3 Manf Code", + log_data->hw_rev_c3_mc); + json_object_add_value_uint(root, "Other Component 4 Manf Code", + log_data->hw_rev_c4_mc); + json_object_add_value_uint(root, "Other Component 5 Manf Code", + log_data->hw_rev_c5_mc); + json_object_add_value_uint(root, "Other Component 6 Manf Code", + log_data->hw_rev_c6_mc); + json_object_add_value_uint(root, "Other Component 7 Manf Code", + log_data->hw_rev_c7_mc); + json_object_add_value_uint(root, "Other Component 8 Manf Code", + log_data->hw_rev_c8_mc); + json_object_add_value_uint(root, "Other Component 9 Manf Code", + log_data->hw_rev_c9_mc); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dev_mdi[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dev_mdi[0])); + json_object_add_value_string(root, "Device Manf Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_asic_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_asic_di[0])); + json_object_add_value_string(root, "ASIC Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pcb_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pcb_di[0])); + json_object_add_value_string(root, "PCB Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dram_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dram_di[0])); + json_object_add_value_string(root, "DRAM Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_nand_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_nand_di[0])); + json_object_add_value_string(root, "NAND Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic1_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic1_di[0])); + json_object_add_value_string(root, "PMIC 1 Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic2_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic2_di[0])); + json_object_add_value_string(root, "PMIC 2 Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c1_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c1_di[0])); + json_object_add_value_string(root, "Component 1 Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c2_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c2_di[0])); + json_object_add_value_string(root, "Component 2 Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c3_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c3_di[0])); + json_object_add_value_string(root, "Component 3 Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c4_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c4_di[0])); + json_object_add_value_string(root, "Component 4 Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c5_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c5_di[0])); + json_object_add_value_string(root, "Component 5 Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c6_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c6_di[0])); + json_object_add_value_string(root, "Component 6 Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c7_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c7_di[0])); + json_object_add_value_string(root, "Component 7 Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c8_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c8_di[0])); + json_object_add_value_string(root, "Component 8 Detailed Info", json_data); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c9_di[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c9_di[0])); + json_object_add_value_string(root, "Component 9 Detailed Info", json_data); + + memset((void*)json_data, 0, 80); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"%"PRIx64"%"PRIx64"", + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[0]), le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[16]), le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[24])); + json_object_add_value_string(root, "Serial Number", json_data); + + json_object_add_value_uint(root, "Log Page Version", + le16_to_cpu(log_data->hw_rev_version)); + + memset((void*)json_data, 0, 40); + sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[8]), + le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[0])); + json_object_add_value_string(root, "Log Page GUID", json_data); + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +static void wdc_print_ext_smart_cloud_log_normal(void *data, int mask) +{ + int i; + wdc_nvme_ext_smart_log *ext_smart_log_ptr = (wdc_nvme_ext_smart_log *)data; + + if (mask == WDC_SCA_V1_NAND_STATS) + printf(" NAND Statistics :- \n"); + else + printf(" SMART Cloud Attributes :- \n"); + + printf(" Physical Media Units Written TLC (Bytes) : %'.0Lf\n", + int128_to_double(ext_smart_log_ptr->ext_smart_pmuwt)); + printf(" Physical Media Units Written SLC (Bytes) : %'.0Lf\n", + int128_to_double(ext_smart_log_ptr->ext_smart_pmuws)); + printf(" Bad User NAND Block Count (Normalized) (Int) : %d\n", + le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_bunbc)); + printf(" Bad User NAND Block Count (Raw) (Int) : %"PRIu64"\n", + le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_bunbc & 0xFFFFFFFFFFFF0000)); + printf(" XOR Recovery Count (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_xrc)); + printf(" Uncorrectable Read Error Count (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_urec)); + if (mask == WDC_SCA_V1_ALL) { + printf(" SSD End to End correction counts (Corrected Errors) (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_eece)); + printf(" SSD End to End correction counts (Detected Errors) (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_eede)); + printf(" SSD End to End correction counts (Uncorrected E2E Errors) (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_eeue)); + printf(" System Data %% life-used : %d %%\n", + ext_smart_log_ptr->ext_smart_sdpu); + } + printf(" User data erase counts (Minimum TLC) (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_mnudec)); + printf(" User data erase counts (Maximum TLC) (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_mxudec)); + printf(" User data erase counts (Minimum SLC) (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_mnec)); + printf(" User data erase counts (Maximum SLC) (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_mxec)); + printf(" User data erase counts (Average SLC) (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_avec)); + printf(" User data erase counts (Average TLC) (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_avudec)); + printf(" Program Fail Count (Normalized) (Int) : %d\n", + le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_pfc)); + printf(" Program Fail Count (Raw) (Int) : %"PRIu64"\n", + le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_pfc & 0xFFFFFFFFFFFF0000)); + printf(" Erase Fail Count (Normalized) (Int) : %d\n", + le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_efc)); + printf(" Erase Fail Count (Raw) (Int) : %"PRIu64"\n", + le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_efc & 0xFFFFFFFFFFFF0000)); + if (mask == WDC_SCA_V1_ALL) { + printf(" PCIe Correctable Error Count (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_pcec)); + printf(" %% Free Blocks (User) (Int) : %d %%\n", + ext_smart_log_ptr->ext_smart_pfbu); + printf(" Security Version Number (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_svn)); + printf(" %% Free Blocks (System) (Int) : %d %%\n", + ext_smart_log_ptr->ext_smart_pfbs); + printf(" NVMe Stats (# Data Set Management/TRIM Commands Completed) (Int) : %'.0Lf\n", + int128_to_double(ext_smart_log_ptr->ext_smart_dcc)); + printf(" Total Namespace Utilization (nvme0n1 NUSE) (Bytes) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_tnu)); + printf(" NVMe Stats (# NVMe Format Commands Completed) (Int) : %d\n", + le16_to_cpu(ext_smart_log_ptr->ext_smart_fcc)); + printf(" Background Back-Pressure Gauge(%%) (Int) : %d\n", + ext_smart_log_ptr->ext_smart_bbpg); + } + printf(" Total # of Soft ECC Error Count (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_seec)); + if (mask == WDC_SCA_V1_ALL) { + printf(" Total # of Read Refresh Count (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_rfsc)); + } + printf(" Bad System NAND Block Count (Normalized) (Int) : %d\n", + le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_bsnbc)); + printf(" Bad System NAND Block Count (Raw) (Int) : %"PRIu64"\n", + le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_bsnbc & 0xFFFFFFFFFFFF0000)); + printf(" Endurance Estimate (Total Writable Lifetime Bytes) (Bytes) : %'.0Lf\n", + int128_to_double(ext_smart_log_ptr->ext_smart_eest)); + if (mask == WDC_SCA_V1_ALL) { + printf(" Thermal Throttling Status & Count (Number of thermal throttling events) (Int) : %d\n", + le16_to_cpu(ext_smart_log_ptr->ext_smart_ttc)); + printf(" Total # Unaligned I/O (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_uio)); + } + printf(" Total Physical Media Units Read (Bytes) (Int) : %'.0Lf\n", + int128_to_double(ext_smart_log_ptr->ext_smart_pmur)); + if (mask == WDC_SCA_V1_ALL) { + printf(" Command Timeout (# of READ Commands > 5 Seconds) (Int) : %"PRIu32"\n", + le32_to_cpu(ext_smart_log_ptr->ext_smart_rtoc)); + printf(" Command Timeout (# of WRITE Commands > 5 Seconds) (Int) : %"PRIu32"\n", + le32_to_cpu(ext_smart_log_ptr->ext_smart_wtoc)); + printf(" Command Timeout (# of TRIM Commands > 5 Seconds) (Int) : %"PRIu32"\n", + le32_to_cpu(ext_smart_log_ptr->ext_smart_ttoc)); + printf(" Total PCIe Link Retraining Count (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_plrc)); + printf(" Active Power State Change Count (Int) : %"PRIu64"\n", + le64_to_cpu(ext_smart_log_ptr->ext_smart_pscc)); + } + printf(" Cloud Boot SSD Spec Version (Int) : %d.%d.%d.%d\n", + le16_to_cpu(ext_smart_log_ptr->ext_smart_maj), + le16_to_cpu(ext_smart_log_ptr->ext_smart_min), + le16_to_cpu(ext_smart_log_ptr->ext_smart_pt), + le16_to_cpu(ext_smart_log_ptr->ext_smart_err)); + printf(" Cloud Boot SSD HW Revision (Int) : %d.%d.%d.%d\n", + 0, 0, 0, 0); + if (mask == WDC_SCA_V1_ALL) { + printf(" FTL Unit Size : %"PRIu32"\n", + le32_to_cpu(ext_smart_log_ptr->ext_smart_ftlus)); + printf(" TCG Ownership Status : %"PRIu32"\n", + le32_to_cpu(ext_smart_log_ptr->ext_smart_tcgos)); + printf(" Log Page Version (Int) : %d\n", + le16_to_cpu(ext_smart_log_ptr->ext_smart_lpv)); + printf(" Log page GUID (Hex) : 0x"); + for (i = WDC_C0_GUID_LENGTH; i > 0; i--) + printf("%02x", ext_smart_log_ptr->ext_smart_lpg[i-1]); + printf("\n"); + } + printf("\n"); +} + +static void wdc_print_ext_smart_cloud_log_json(void *data, int mask) +{ + wdc_nvme_ext_smart_log *ext_smart_log_ptr = (wdc_nvme_ext_smart_log *)data; + struct json_object *root; + + root = json_create_object(); + json_object_add_value_double(root, "physical_media_units_bytes_tlc", + int128_to_double(ext_smart_log_ptr->ext_smart_pmuwt)); + json_object_add_value_double(root, "physical_media_units_bytes_slc", + int128_to_double(ext_smart_log_ptr->ext_smart_pmuws)); + json_object_add_value_uint(root, "bad_user_blocks_normalized", + le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_bunbc)); + json_object_add_value_uint64(root, "bad_user_blocks_raw", + le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_bunbc & 0xFFFFFFFFFFFF0000)); + json_object_add_value_uint64(root, "xor_recovery_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_xrc)); + json_object_add_value_uint64(root, "uncorrectable_read_errors", + le64_to_cpu(ext_smart_log_ptr->ext_smart_urec)); + if (mask == WDC_SCA_V1_ALL) { + json_object_add_value_uint64(root, "corrected_e2e_errors", + le64_to_cpu(ext_smart_log_ptr->ext_smart_eece)); + json_object_add_value_uint64(root, "detected_e2e_errors", + le64_to_cpu(ext_smart_log_ptr->ext_smart_eede)); + json_object_add_value_uint64(root, "uncorrected_e2e_errors", + le64_to_cpu(ext_smart_log_ptr->ext_smart_eeue)); + json_object_add_value_uint(root, "system_data_life_used_pct", + (__u8)ext_smart_log_ptr->ext_smart_sdpu); + } + json_object_add_value_uint64(root, "min_slc_user_data_erase_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_mnec)); + json_object_add_value_uint64(root, "min_tlc_user_data_erase_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_mnudec)); + json_object_add_value_uint64(root, "max_slc_user_data_erase_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_mxec)); + json_object_add_value_uint64(root, "max_tlc_user_data_erase_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_mxudec)); + json_object_add_value_uint64(root, "avg_slc_user_data_erase_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_avec)); + json_object_add_value_uint64(root, "avg_tlc_user_data_erase_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_avudec)); + json_object_add_value_uint(root, "program_fail_count_normalized", + le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_pfc)); + json_object_add_value_uint64(root, "program_fail_count_raw", + le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_pfc & 0xFFFFFFFFFFFF0000)); + json_object_add_value_uint(root, "erase_fail_count_normalized", + le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_efc)); + json_object_add_value_uint64(root, "erase_fail_count_raw", + le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_efc & 0xFFFFFFFFFFFF0000)); + if (mask == WDC_SCA_V1_ALL) { + json_object_add_value_uint64(root, "pcie_correctable_errors", + le64_to_cpu(ext_smart_log_ptr->ext_smart_pcec)); + json_object_add_value_uint(root, "pct_free_blocks_user", + (__u8)ext_smart_log_ptr->ext_smart_pfbu); + json_object_add_value_uint64(root, "security_version", + le64_to_cpu(ext_smart_log_ptr->ext_smart_svn)); + json_object_add_value_uint(root, "pct_free_blocks_system", + (__u8)ext_smart_log_ptr->ext_smart_pfbs); + json_object_add_value_double(root, "num_of_trim_commands", + int128_to_double(ext_smart_log_ptr->ext_smart_dcc)); + json_object_add_value_uint64(root, "total_nuse_bytes", + le64_to_cpu(ext_smart_log_ptr->ext_smart_tnu)); + json_object_add_value_uint(root, "num_of_format_commands", + le16_to_cpu(ext_smart_log_ptr->ext_smart_fcc)); + json_object_add_value_uint(root, "background_pressure_gauge", + (__u8)ext_smart_log_ptr->ext_smart_bbpg); + } + json_object_add_value_uint64(root, "soft_ecc_error_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_seec)); + if (mask == WDC_SCA_V1_ALL) { + json_object_add_value_uint64(root, "read_refresh_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_rfsc)); + } + json_object_add_value_uint(root, "bad_system_block_normalized", + le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_bsnbc)); + json_object_add_value_uint64(root, "bad_system_block_raw", + le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_bsnbc & 0xFFFFFFFFFFFF0000)); + json_object_add_value_double(root, "endurance_est_bytes", + int128_to_double(ext_smart_log_ptr->ext_smart_eest)); + if (mask == WDC_SCA_V1_ALL) { + json_object_add_value_uint(root, "num_throttling_events", + le16_to_cpu(ext_smart_log_ptr->ext_smart_ttc)); + json_object_add_value_uint64(root, "total_unaligned_io", + le64_to_cpu(ext_smart_log_ptr->ext_smart_uio)); + } + json_object_add_value_double(root, "physical_media_units_read_bytes", + int128_to_double(ext_smart_log_ptr->ext_smart_pmur)); + if (mask == WDC_SCA_V1_ALL) { + json_object_add_value_uint(root, "num_read_timeouts", + le32_to_cpu(ext_smart_log_ptr->ext_smart_rtoc)); + json_object_add_value_uint(root, "num_write_timeouts", + le32_to_cpu(ext_smart_log_ptr->ext_smart_wtoc)); + json_object_add_value_uint(root, "num_trim_timeouts", + le32_to_cpu(ext_smart_log_ptr->ext_smart_ttoc)); + json_object_add_value_uint64(root, "pcie_link_retrain_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_plrc)); + json_object_add_value_uint64(root, "active_power_state_change_count", + le64_to_cpu(ext_smart_log_ptr->ext_smart_pscc)); + } + char vers_str[40]; + memset((void*)vers_str, 0, 40); + sprintf((char*)vers_str, "%d.%d.%d.%d", + le16_to_cpu(ext_smart_log_ptr->ext_smart_maj), le16_to_cpu(ext_smart_log_ptr->ext_smart_min), + le16_to_cpu(ext_smart_log_ptr->ext_smart_pt), le16_to_cpu(ext_smart_log_ptr->ext_smart_err)); + json_object_add_value_string(root, "cloud_boot_ssd_spec_ver", vers_str); + memset((void*)vers_str, 0, 40); + sprintf((char*)vers_str, "%d.%d.%d.%d", 0, 0, 0, 0); + json_object_add_value_string(root, "cloud_boot_ssd_hw_ver", vers_str); + + if (mask == WDC_SCA_V1_ALL) { + json_object_add_value_uint(root, "ftl_unit_size", + le32_to_cpu(ext_smart_log_ptr->ext_smart_ftlus)); + json_object_add_value_uint(root, "tcg_ownership_status", + le32_to_cpu(ext_smart_log_ptr->ext_smart_tcgos)); + json_object_add_value_uint(root, "log_page_ver", + le16_to_cpu(ext_smart_log_ptr->ext_smart_lpv)); + char guid[40]; + memset((void*)guid, 0, 40); + sprintf((char*)guid, "0x%"PRIx64"%"PRIx64"",le64_to_cpu(*(uint64_t *)&ext_smart_log_ptr->ext_smart_lpg[8]), + le64_to_cpu(*(uint64_t *)&ext_smart_log_ptr->ext_smart_lpg[0])); + json_object_add_value_string(root, "log_page_guid", guid); + } + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +static void wdc_print_smart_cloud_attr_C0_normal(void *data) +{ + __u8 *log_data = (__u8*)data; + uint16_t smart_log_ver = 0; + + printf(" SMART Cloud Attributes :- \n"); + + printf(" Physical media units written : %'.0Lf\n", int128_to_double(&log_data[SCAO_PMUW])); + printf(" Physical media units read : %'.0Lf\n", int128_to_double(&log_data[SCAO_PMUR])); + printf(" Bad user nand blocks Raw : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF)); + printf(" Bad user nand blocks Normalized : %d\n", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN])); + printf(" Bad system nand blocks Raw : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF)); + printf(" Bad system nand blocks Normalized : %d\n", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN])); + printf(" XOR recovery count : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC])); + printf(" Uncorrectable read error count : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC])); + printf(" Soft ecc error count : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC])); + printf(" End to end corrected errors : %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE])); + printf(" End to end detected errors : %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC])); + printf(" System data percent used : %d\n", (__u8)log_data[SCAO_SDPU]); + printf(" Refresh counts : %"PRIu64"\n", + (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC])& 0x00FFFFFFFFFFFFFF)); + printf(" Max User data erase counts : %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC])); + printf(" Min User data erase counts : %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); + printf(" Number of Thermal throttling events : %d\n", (__u8)log_data[SCAO_NTTE]); + printf(" Current throttling status : 0x%x\n", (__u8)log_data[SCAO_CTS]); + printf(" PCIe correctable error count : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); + printf(" Incomplete shutdowns : %"PRIu32"\n", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS])); + printf(" Percent free blocks : %d\n", (__u8)log_data[SCAO_PFB]); + printf(" Capacitor health : %"PRIu16"\n", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH])); + printf(" Unaligned I/O : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO])); + printf(" Security Version Number : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN])); + printf(" NUSE Namespace utilization : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE])); + printf(" PLP start count : %'.0Lf\n", int128_to_double(&log_data[SCAO_PSC])); + printf(" Endurance estimate : %'.0Lf\n", int128_to_double(&log_data[SCAO_EEST])); + smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]); + printf(" Log page version : %"PRIu16"\n",smart_log_ver); + printf(" Log page GUID : 0x"); + printf("%"PRIx64"%"PRIx64"\n",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]), + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG])); + if(smart_log_ver > 2) { + printf(" Errata Version Field : %d\n", + (__u8)log_data[SCAO_EVF]); + printf(" Point Version Field : %"PRIu16"\n", + (uint16_t)log_data[SCAO_PVF]); + printf(" Minor Version Field : %"PRIu16"\n", + (uint16_t)log_data[SCAO_MIVF]); + printf(" Major Version Field : %d\n", + (__u8)log_data[SCAO_MAVF]); + printf(" NVMe Errata Version : %d\n", + (__u8)log_data[SCAO_NEV]); + printf(" PCIe Link Retraining Count : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC])); + } + if (smart_log_ver > 3) { + printf(" Power State Change Count : %"PRIu64"\n", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PSCC])); + } + printf("\n"); +} + +static void wdc_print_smart_cloud_attr_C0_json(void *data) +{ + __u8 *log_data = (__u8*)data; + struct json_object *root; + uint16_t smart_log_ver = 0; + + root = json_create_object(); + json_object_add_value_double(root, "Physical media units written", + int128_to_double(&log_data[SCAO_PMUW])); + json_object_add_value_double(root, "Physical media units read", + int128_to_double(&log_data[SCAO_PMUR])); + json_object_add_value_uint64(root, "Bad user nand blocks - Raw", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF)); + json_object_add_value_uint(root, "Bad user nand blocks - Normalized", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN])); + json_object_add_value_uint64(root, "Bad system nand blocks - Raw", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF)); + json_object_add_value_uint(root, "Bad system nand blocks - Normalized", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN])); + json_object_add_value_uint64(root, "XOR recovery count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC])); + json_object_add_value_uint64(root, "Uncorrectable read error count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC])); + json_object_add_value_uint64(root, "Soft ecc error count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC])); + json_object_add_value_uint(root, "End to end corrected errors", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE])); + json_object_add_value_uint(root, "End to end detected errors", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC])); + json_object_add_value_uint(root, "System data percent used", + (__u8)log_data[SCAO_SDPU]); + json_object_add_value_uint64(root, "Refresh counts", + (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC])& 0x00FFFFFFFFFFFFFF)); + json_object_add_value_uint(root, "Max User data erase counts", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC])); + json_object_add_value_uint(root, "Min User data erase counts", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC])); + json_object_add_value_uint(root, "Number of Thermal throttling events", + (__u8)log_data[SCAO_NTTE]); + json_object_add_value_uint(root, "Current throttling status", + (__u8)log_data[SCAO_CTS]); + json_object_add_value_uint64(root, "PCIe correctable error count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC])); + json_object_add_value_uint(root, "Incomplete shutdowns", + (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS])); + json_object_add_value_uint(root, "Percent free blocks", + (__u8)log_data[SCAO_PFB]); + json_object_add_value_uint(root, "Capacitor health", + (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH])); + json_object_add_value_uint64(root, "Unaligned I/O", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO])); + json_object_add_value_uint64(root, "Security Version Number", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN])); + json_object_add_value_uint64(root, "NUSE - Namespace utilization", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE])); + json_object_add_value_double(root, "PLP start count", int128_to_double(&log_data[SCAO_PSC])); - json_object_add_value_uint(root, "Endurance estimate", + json_object_add_value_double(root, "Endurance estimate", int128_to_double(&log_data[SCAO_EEST])); smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]); json_object_add_value_uint(root, "Log page version", smart_log_ver); @@ -5131,6 +6025,10 @@ static void wdc_print_smart_cloud_attr_C0_json(void *data) json_object_add_value_uint64(root, "PCIe Link Retraining Count", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC])); } + if(smart_log_ver > 3) { + json_object_add_value_uint64(root, "Power State Change Count", + (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PSCC])); + } json_print_object(root, NULL); printf("\n"); json_free_object(root); @@ -5187,6 +6085,23 @@ static void wdc_print_eol_c0_json(void *data) json_free_object(root); } +static int wdc_print_ext_smart_cloud_log(void *data, int fmt) +{ + if (!data) { + fprintf(stderr, "ERROR : WDC : Invalid buffer to read 0xC0 V1 log\n"); + return -1; + } + switch (fmt) { + case NORMAL: + wdc_print_ext_smart_cloud_log_normal(data, WDC_SCA_V1_ALL); + break; + case JSON: + wdc_print_ext_smart_cloud_log_json(data, WDC_SCA_V1_ALL); + break; + } + return 0; +} + static int wdc_print_c0_cloud_attr_log(void *data, int fmt) { if (!data) { @@ -5249,12 +6164,16 @@ static int wdc_get_c0_log_page(nvme_root_t r, int fd, char *format, case WDC_NVME_SN640_DEV_ID_3: case WDC_NVME_SN840_DEV_ID: case WDC_NVME_SN840_DEV_ID_1: + case WDC_NVME_SN860_DEV_ID: case WDC_NVME_SN650_DEV_ID: case WDC_NVME_SN650_DEV_ID_1: case WDC_NVME_SN650_DEV_ID_2: case WDC_NVME_SN650_DEV_ID_3: case WDC_NVME_SN650_DEV_ID_4: case WDC_NVME_SN655_DEV_ID: + case WDC_NVME_SN560_DEV_ID_1: + case WDC_NVME_SN560_DEV_ID_2: + case WDC_NVME_SN560_DEV_ID_3: cust_id = wdc_get_fw_cust_id(r, fd); if (cust_id == WDC_INVALID_CUSTOMER_ID) { fprintf(stderr, "%s: ERROR : WDC : invalid customer id\n", __func__); @@ -5427,9 +6346,28 @@ static int wdc_get_c0_log_page(nvme_root_t r, int fd, char *format, free(data); break; + case WDC_NVME_SN820CL_DEV_ID: + /* Get the 0xC0 Extended Smart Cloud Attribute log data */ + data = NULL; + ret = nvme_get_ext_smart_cloud_log(fd, &data, uuid_index, namespace_id); + + if (strcmp(format, "json")) + nvme_show_status(ret); + + if (ret == 0) { + /* parse the data */ + wdc_print_ext_smart_cloud_log(data, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page V1 data\n"); + ret = -1; + } + + if (data) + free(data); + break; + default: fprintf(stderr, "ERROR : WDC : Unknown device id - 0x%x\n", device_id); - ret = -1; break; @@ -5649,7 +6587,7 @@ static int wdc_get_ca_log_page(nvme_root_t r, int fd, char *format) case WDC_NVME_SN640_DEV_ID_3: case WDC_NVME_SN840_DEV_ID: case WDC_NVME_SN840_DEV_ID_1: - + case WDC_NVME_SN860_DEV_ID: if (cust_id == WDC_CUSTOMER_ID_0x1005) { if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN)) == NULL) { @@ -6206,23 +7144,302 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command, if (ret) fprintf(stderr, "ERROR : WDC : Failure reading the CA Log Page, ret = %d\n", ret); } - if (((capabilities & WDC_DRIVE_CAP_C1_LOG_PAGE) == WDC_DRIVE_CAP_C1_LOG_PAGE) && - (page_mask & WDC_C1_PAGE_MASK)) { - /* Get the C1 Log Page */ - ret = wdc_get_c1_log_page(r, fd, cfg.output_format, cfg.interval); - if (ret) - fprintf(stderr, "ERROR : WDC : Failure reading the C1 Log Page, ret = %d\n", ret); + if (((capabilities & WDC_DRIVE_CAP_C1_LOG_PAGE) == WDC_DRIVE_CAP_C1_LOG_PAGE) && + (page_mask & WDC_C1_PAGE_MASK)) { + /* Get the C1 Log Page */ + ret = wdc_get_c1_log_page(r, fd, cfg.output_format, cfg.interval); + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading the C1 Log Page, ret = %d\n", ret); + } + if (((capabilities & WDC_DRIVE_CAP_D0_LOG_PAGE) == WDC_DRIVE_CAP_D0_LOG_PAGE) && + (page_mask & WDC_D0_PAGE_MASK)) { + /* Get the D0 Log Page */ + ret = wdc_get_d0_log_page(r, fd, cfg.output_format); + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading the D0 Log Page, ret = %d\n", ret); + } + +out: + nvme_free_tree(r); + close(fd); + return ret; +} + +static int wdc_vs_cloud_log(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + const char *desc = "Retrieve Cloud Log Smart/Health Information"; + const char *namespace_id = "desired namespace id"; + int fd; + nvme_root_t r; + int ret = 0; + __u64 capabilities = 0; + __u8 *data; + int fmt = -1; + + struct config { + char *output_format; + __u32 namespace_id; + }; + + struct config cfg = { + .output_format = "normal", + .namespace_id = NVME_NSID_ALL, + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + r = nvme_scan(NULL); + + capabilities = wdc_get_drive_capabilities(r, fd); + + if ((capabilities & WDC_DRIVE_CAP_CLOUD_LOG_PAGE) == 0) { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + goto out; + } + + data = NULL; + ret = nvme_get_ext_smart_cloud_log(fd, &data, 0, cfg.namespace_id); + + if (strcmp(cfg.output_format, "json")) + nvme_show_status(ret); + + if (ret == 0) { + fmt = validate_output_format(cfg.output_format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC %s: invalid output format\n", __func__); + ret = fmt; + } + + /* parse the data */ + wdc_print_ext_smart_cloud_log(data, fmt); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page V1 data\n"); + ret = -1; + } + + if (data) + free(data); + +out: + nvme_free_tree(r); + close(fd); + return ret; +} + +static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + const char *desc = "Retrieve Hardware Revision Log Information"; + const char *namespace_id = "desired namespace id"; + int fd; + nvme_root_t r; + int ret = 0; + __u64 capabilities = 0; + __u8 *data = NULL; + int fmt = -1; + + struct config { + char *output_format; + __u32 namespace_id; + }; + + struct config cfg = { + .output_format = "normal", + .namespace_id = NVME_NSID_ALL, + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + r = nvme_scan(NULL); + + capabilities = wdc_get_drive_capabilities(r, fd); + + if ((capabilities & WDC_DRIVE_CAP_HW_REV_LOG_PAGE) == 0) { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + goto out; + } + + ret = nvme_get_hw_rev_log(fd, &data, 0, cfg.namespace_id); + + if (strcmp(cfg.output_format, "json")) + nvme_show_status(ret); + + if (ret == 0) { + fmt = validate_output_format(cfg.output_format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC %s: invalid output format\n", __func__); + ret = fmt; + goto free_buf; + } + + if (!data) { + fprintf(stderr, "ERROR : WDC : Invalid buffer to read Hardware Revision log\n"); + ret = -1; + goto out; + } + switch (fmt) { + case NORMAL: + wdc_print_hw_rev_log_normal(data); + break; + case JSON: + wdc_print_hw_rev_log_json(data); + break; + } + } else { + fprintf(stderr, "ERROR : WDC : Unable to read Hardware Revision Log Page data\n"); + ret = -1; + } + +free_buf: + if (data) + free(data); + +out: + nvme_free_tree(r); + close(fd); + return ret; +} + +static int wdc_vs_device_waf(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + const char *desc = "Retrieve Device Write Amplication Factor"; + const char *namespace_id = "desired namespace id"; + struct nvme_smart_log smart_log; + __u8 *data; + int fd; + nvme_root_t r; + int ret = 0; + int fmt = -1; + __u64 capabilities = 0; + wdc_nvme_ext_smart_log *ext_smart_log_ptr; + long double data_units_written = 0, + phys_media_units_written_tlc = 0, + phys_media_units_written_slc = 0; + struct json_object *root = NULL; + char tlc_waf_str[32] = { 0 }, + slc_waf_str[32] = { 0 }; + + struct config { + char *output_format; + __u32 namespace_id; + }; + + struct config cfg = { + .output_format = "normal", + .namespace_id = NVME_NSID_ALL, + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + r = nvme_scan(NULL); + + capabilities = wdc_get_drive_capabilities(r, fd); + + if ((capabilities & WDC_DRIVE_CAP_DEVICE_WAF) == 0) { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + goto out; + } + + /* get data units written from the smart log page */ + ret = nvme_get_log_smart(fd, cfg.namespace_id, true, &smart_log); + if (!ret) { + data_units_written = int128_to_double(smart_log.data_units_written); + } + else if (ret > 0) { + nvme_show_status(ret); + ret = -1; + goto out; + } else { + fprintf(stderr, "smart log: %s\n", nvme_strerror(errno)); + ret = -1; + goto out; + } + + /* get Physical Media Units Written from extended smart/C0 log page */ + data = NULL; + ret = nvme_get_ext_smart_cloud_log(fd, &data, 0, cfg.namespace_id); + + if (ret == 0) { + ext_smart_log_ptr = (wdc_nvme_ext_smart_log *)data; + phys_media_units_written_tlc = int128_to_double(ext_smart_log_ptr->ext_smart_pmuwt); + phys_media_units_written_slc = int128_to_double(ext_smart_log_ptr->ext_smart_pmuws); + + if (data) + free(data); + } else { + fprintf(stderr, "ERROR : WDC %s: get smart cloud log failure\n", __func__); + ret = -1; + goto out; + } + + if (strcmp(cfg.output_format, "json")) + nvme_show_status(ret); + + fmt = validate_output_format(cfg.output_format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC %s: invalid output format\n", __func__); + ret = fmt; + goto out; + } + + if (data_units_written == 0) { + fprintf(stderr, "ERROR : WDC %s: 0 data units written\n", __func__); + ret = -1; + goto out; + } + + if (fmt == NORMAL) { + printf("Device Write Amplification Factor TLC : %4.2Lf\n", + (phys_media_units_written_tlc/data_units_written)); + printf("Device Write Amplification Factor SLC : %4.2Lf\n", + (phys_media_units_written_slc/data_units_written)); } - if (((capabilities & WDC_DRIVE_CAP_D0_LOG_PAGE) == WDC_DRIVE_CAP_D0_LOG_PAGE) && - (page_mask & WDC_D0_PAGE_MASK)) { - /* Get the D0 Log Page */ - ret = wdc_get_d0_log_page(r, fd, cfg.output_format); - if (ret) - fprintf(stderr, "ERROR : WDC : Failure reading the D0 Log Page, ret = %d\n", ret); + else if (fmt == JSON) { + root = json_create_object(); + sprintf(tlc_waf_str, "%4.2Lf", (phys_media_units_written_tlc/data_units_written)); + sprintf(slc_waf_str, "%4.2Lf", (phys_media_units_written_slc/data_units_written)); + + json_object_add_value_string(root, "Device Write Amplification Factor TLC", tlc_waf_str); + json_object_add_value_string(root, "Device Write Amplification Factor SLC", slc_waf_str); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); } out: nvme_free_tree(r); + close(fd); return ret; } @@ -6266,6 +7483,8 @@ static int wdc_get_latency_monitor_log(int argc, char **argv, struct command *co fprintf(stderr, "ERROR : WDC : Failure reading the Latency Monitor (C3) Log Page, ret = %d\n", ret); out: + nvme_free_tree(r); + close(fd); return ret; } @@ -6309,6 +7528,8 @@ static int wdc_get_error_recovery_log(int argc, char **argv, struct command *com fprintf(stderr, "ERROR : WDC : Failure reading the Error Recovery (C1) Log Page, ret = 0x%x\n", ret); out: + nvme_free_tree(r); + close(fd); return ret; } @@ -6352,6 +7573,8 @@ static int wdc_get_dev_capabilities_log(int argc, char **argv, struct command *c fprintf(stderr, "ERROR : WDC : Failure reading the Device Capabilities (C4) Log Page, ret = 0x%x\n", ret); out: + nvme_free_tree(r); + close(fd); return ret; } @@ -6395,6 +7618,8 @@ static int wdc_get_unsupported_reqs_log(int argc, char **argv, struct command *c fprintf(stderr, "ERROR : WDC : Failure reading the Unsupported Requirements (C5) Log Page, ret = 0x%x\n", ret); out: + nvme_free_tree(r); + close(fd); return ret; } @@ -6480,6 +7705,7 @@ static int wdc_clear_pcie_correctable_errors(int argc, char **argv, struct comma out: nvme_free_tree(r); + close(fd); return ret; } @@ -6597,6 +7823,7 @@ static int wdc_drive_status(int argc, char **argv, struct command *command, out: nvme_free_tree(r); + close(fd); return ret; } @@ -6647,6 +7874,7 @@ static int wdc_clear_assert_dump(int argc, char **argv, struct command *command, out: nvme_free_tree(r); + close(fd); return ret; } @@ -6834,7 +8062,8 @@ static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *com /* to retrieve fw activate history data */ if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_SMART_CLOUD_ATTR_LEN)) == NULL) { fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); - return -1; + ret = -1; + goto out; } /* Get the 0xC0 log data */ @@ -6887,6 +8116,7 @@ static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *com fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret); out: nvme_free_tree(r); + close(fd); return ret; } @@ -6944,15 +8174,14 @@ static int wdc_clear_fw_activate_history(int argc, char **argv, struct command * goto out; } - if (capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) { + if (capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) ret = wdc_do_clear_fw_activate_history_vuc(fd); - } - else { + else ret = wdc_do_clear_fw_activate_history_fid(fd); - } out: nvme_free_tree(r); + close(fd); return ret; } @@ -7043,6 +8272,7 @@ static int wdc_vs_telemetry_controller_option(int argc, char **argv, struct comm out: nvme_free_tree(r); + close(fd); return ret; } @@ -7179,7 +8409,7 @@ static int wdc_get_log_dir_max_entries(int fd, __u32* maxNumOfEntries) { fprintf(stderr, "ERROR : WDC : %s: Failed to get headerPayloadSize from file directory 0x%x\n", __func__, ret); - goto end; + return ret; } fileIdOffsetsBufferSize = WDC_DE_FILE_HEADER_SIZE + (headerPayloadSize * WDC_DE_FILE_OFFSET_SIZE); @@ -7201,9 +8431,8 @@ static int wdc_get_log_dir_max_entries(int fd, __u32* maxNumOfEntries) continue; (*maxNumOfEntries)++; } - end: - if (!fileIdOffsetsBuffer) - free(fileIdOffsetsBuffer); +end: + free(fileIdOffsetsBuffer); return ret; } @@ -7319,7 +8548,10 @@ static int wdc_fetch_log_file_from_device(int fd, __u32 fileId, __u16 spiDestn, goto end; } - wdc_get_max_transfer_len(fd, &maximumTransferLength); + if (wdc_get_max_transfer_len(fd, &maximumTransferLength) < 0) { + ret = WDC_STATUS_FAILURE; + goto end; + } /* Fetch Log File Data */ if ((fileSize >= maximumTransferLength) || (fileSize > 0xFFFFFFFF)) @@ -7379,7 +8611,8 @@ static int wdc_de_get_dump_trace(int fd, char * filePath, __u16 binFileNameLen, return ret; } - wdc_get_max_transfer_len(fd, &maximumTransferLength); + if (wdc_get_max_transfer_len(fd, &maximumTransferLength) < 0) + return WDC_STATUS_FAILURE; do { @@ -7757,7 +8990,7 @@ static int wdc_drive_essentials(int argc, char **argv, struct command *command, char d[PATH_MAX] = {0}; char k[PATH_MAX] = {0}; char *d_ptr; - int fd; + int fd, ret; nvme_root_t r; __u64 capabilities = 0; @@ -7783,8 +9016,8 @@ static int wdc_drive_essentials(int argc, char **argv, struct command *command, capabilities = wdc_get_drive_capabilities(r, fd); if ((capabilities & WDC_DRIVE_CAP_DRIVE_ESSENTIALS) != WDC_DRIVE_CAP_DRIVE_ESSENTIALS) { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); - nvme_free_tree(r); - return -1; + ret = -1; + goto out; } if (cfg.dirName != NULL) { @@ -7794,7 +9027,11 @@ static int wdc_drive_essentials(int argc, char **argv, struct command *command, d_ptr = NULL; } - return wdc_do_drive_essentials(r, fd, d_ptr, k); + ret = wdc_do_drive_essentials(r, fd, d_ptr, k); +out: + nvme_free_tree(r); + close(fd); + return ret; } static int wdc_do_drive_resize(int fd, uint64_t new_size) @@ -7885,6 +9122,7 @@ static int wdc_drive_resize(int argc, char **argv, nvme_show_status(ret); nvme_free_tree(r); + close(fd); return ret; } @@ -7924,6 +9162,7 @@ static int wdc_namespace_resize(int argc, char **argv, (cfg.op_option != 0xF)) { fprintf(stderr, "ERROR : WDC: unsupported OP option parameter\n"); + close(fd); return -1; } @@ -7942,6 +9181,7 @@ static int wdc_namespace_resize(int argc, char **argv, nvme_show_status(ret); nvme_free_tree(r); + close(fd); return ret; } @@ -8017,8 +9257,12 @@ static int wdc_reason_identifier(int argc, char **argv, ret = -1; goto close_fd; } - - snprintf(f + strlen(f), PATH_MAX, "%s", ".bin"); + if (strlen(f) > PATH_MAX - 5) { + fprintf(stderr, "ERROR : WDC: file name overflow\n"); + ret = -1; + goto close_fd; + } + strcat(f, ".bin"); } fprintf(stderr, "%s: filename = %s\n", __func__, f); @@ -8060,6 +9304,7 @@ static const char *nvme_log_id_to_string(__u8 log_id) case WDC_LOG_ID_C0: return "WDC Vendor Unique Log ID C0"; case WDC_LOG_ID_C1: return "WDC Vendor Unique Log ID C1"; case WDC_LOG_ID_C2: return "WDC Vendor Unique Log ID C2"; + case WDC_LOG_ID_C3: return "WDC Vendor Unique Log ID C3"; case WDC_LOG_ID_C4: return "WDC Vendor Unique Log ID C4"; case WDC_LOG_ID_C5: return "WDC Vendor Unique Log ID C5"; case WDC_LOG_ID_C6: return "WDC Vendor Unique Log ID C6"; @@ -8114,6 +9359,7 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command ret = validate_output_format(cfg.output_format); if (ret < 0) { fprintf(stderr, "%s: ERROR : WDC : invalid output format\n", __func__); + close(fd); return ret; } ret = 0; @@ -8172,6 +9418,7 @@ static int wdc_log_page_directory(int argc, char **argv, struct command *command out: nvme_free_tree(r); + close(fd); return ret; } @@ -8229,7 +9476,11 @@ static int wdc_save_reason_id(int fd, __u8 *rsn_ident, int size) /* make the nvmecli dir in /usr/local if it doesn't already exist */ if (stat(reason_id_path, &st) == -1) { - mkdir(reason_id_path, 0700); + if (mkdir(reason_id_path, 0700) < 0) { + fprintf(stderr, "%s: ERROR : failed to mkdir %s : %s\n", + __func__, reason_id_path, strerror(errno)); + return -1; + } } if (asprintf(&reason_id_file, "%s/%s%s", reason_id_path, @@ -8472,9 +9723,9 @@ static void wdc_print_nand_stats_json(__u16 version, void *data) case 0: - json_object_add_value_float(root, "NAND Writes TLC (Bytes)", + json_object_add_value_double(root, "NAND Writes TLC (Bytes)", int128_to_double(nand_stats->nand_write_tlc)); - json_object_add_value_float(root, "NAND Writes SLC (Bytes)", + json_object_add_value_double(root, "NAND Writes SLC (Bytes)", int128_to_double(nand_stats->nand_write_slc)); json_object_add_value_uint(root, "NAND Program Failures", le32_to_cpu(nand_stats->nand_prog_failure)); @@ -8495,9 +9746,9 @@ static void wdc_print_nand_stats_json(__u16 version, void *data) case 3: - json_object_add_value_float(root, "NAND Writes TLC (Bytes)", + json_object_add_value_double(root, "NAND Writes TLC (Bytes)", int128_to_double(nand_stats_v3->nand_write_tlc)); - json_object_add_value_float(root, "NAND Writes SLC (Bytes)", + json_object_add_value_double(root, "NAND Writes SLC (Bytes)", int128_to_double(nand_stats_v3->nand_write_slc)); temp_ptr = (__u64 *)nand_stats_v3->bad_nand_block_count; temp_norm = (__u16)(*temp_ptr & 0x000000000000FFFF); @@ -8548,7 +9799,7 @@ static void wdc_print_nand_stats_json(__u16 version, void *data) le64_to_cpu(nand_stats_v3->security_version_number)); json_object_add_value_uint(root, "% Free Blocks (System)", nand_stats_v3->percent_free_blocks_system); - json_object_add_value_float(root, "Data Set Management Commands", + json_object_add_value_double(root, "Data Set Management Commands", int128_to_double(nand_stats_v3->trim_completions)); json_object_add_value_uint64(root, "Estimate of Incomplete Trim Data", le64_to_cpu(nand_stats_v3->trim_completions[16])); @@ -8567,7 +9818,7 @@ static void wdc_print_nand_stats_json(__u16 version, void *data) le16_to_cpu(temp_norm)); json_object_add_value_uint64(root, "Bad System Nand Block Count - Raw", le64_to_cpu(temp_raw)); - json_object_add_value_float(root, "Endurance Estimate", + json_object_add_value_double(root, "Endurance Estimate", int128_to_double(nand_stats_v3->endurance_estimate)); json_object_add_value_uint(root, "Thermal Throttling Status", nand_stats_v3->thermal_throttling_st_ct[0]); @@ -8575,7 +9826,7 @@ static void wdc_print_nand_stats_json(__u16 version, void *data) nand_stats_v3->thermal_throttling_st_ct[1]); json_object_add_value_uint64(root, "Unaligned I/O", le64_to_cpu(nand_stats_v3->unaligned_IO)); - json_object_add_value_float(root, "Physical Media Units Read", + json_object_add_value_double(root, "Physical Media Units Read", int128_to_double(nand_stats_v3->physical_media_units)); json_object_add_value_uint(root, "log page version", le16_to_cpu(nand_stats_v3->log_page_version)); @@ -8677,6 +9928,43 @@ static void wdc_print_pcie_stats_json(struct wdc_vs_pcie_stats *pcie_stats) json_free_object(root); } +static int wdc_do_vs_nand_stats_sn810_2(int fd, char *format) +{ + int ret; + int fmt = -1; + uint8_t *data = NULL; + + data = NULL; + ret = nvme_get_ext_smart_cloud_log(fd, &data, 0, NVME_NSID_ALL); + + if (ret) { + fprintf(stderr, "ERROR : WDC : %s : Failed to retreive NAND stats\n", __func__); + goto out; + } else { + fmt = validate_output_format(format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : %s : invalid output format\n", __func__); + ret = fmt; + goto out; + } + + /* parse the data */ + switch (fmt) { + case NORMAL: + wdc_print_ext_smart_cloud_log_normal(data, WDC_SCA_V1_NAND_STATS); + break; + case JSON: + wdc_print_ext_smart_cloud_log_json(data, WDC_SCA_V1_NAND_STATS); + break; + } + } + +out: + if (data) + free(data); + return ret; +} + static int wdc_do_vs_nand_stats(int fd, char *format) { int ret; @@ -8730,6 +10018,7 @@ static int wdc_vs_nand_stats(int argc, char **argv, struct command *command, int ret = 0; nvme_root_t r; __u64 capabilities = 0; + uint32_t read_device_id = 0, read_vendor_id = 0; struct config { char *output_format; @@ -8755,12 +10044,28 @@ static int wdc_vs_nand_stats(int argc, char **argv, struct command *command, fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); ret = -1; } else { - ret = wdc_do_vs_nand_stats(fd, cfg.output_format); - if (ret) - fprintf(stderr, "ERROR : WDC : Failure reading NAND statistics, ret = %d\n", ret); + ret = wdc_get_pci_ids(r, &read_device_id, &read_vendor_id); + if (ret < 0) + { + fprintf(stderr, "ERROR : WDC: %s: failure to get pci ids, ret = %d\n", __func__, ret); + return -1; + } + + switch (read_device_id) { + case WDC_NVME_SN820CL_DEV_ID: + ret = wdc_do_vs_nand_stats_sn810_2(fd, cfg.output_format); + break; + default: + ret = wdc_do_vs_nand_stats(fd, cfg.output_format); + break; + } } + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading NAND statistics, ret = %d\n", ret); + nvme_free_tree(r); + close(fd); return ret; } @@ -8856,6 +10161,7 @@ static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command, out: nvme_free_tree(r); + close(fd); return ret; } @@ -8872,10 +10178,15 @@ static int wdc_vs_drive_info(int argc, char **argv, struct nvme_id_ctrl ctrl; char vsData[32] = {0}; char major_rev = 0, minor_rev = 0; + __u8 *data = NULL; + __u32 ftl_unit_size = 0, tcg_dev_ownership = 0; + __u16 boot_spec_major = 0, boot_spec_minor = 0; int fmt = -1; struct json_object *root = NULL; char formatter[41] = { 0 }; - char rev_str[8] = { 0 }; + char rev_str[16] = { 0 }; + uint32_t read_device_id = -1, read_vendor_id = -1; + wdc_nvme_ext_smart_log *ext_smart_log_ptr = NULL; struct config { char *output_format; @@ -8897,6 +10208,7 @@ static int wdc_vs_drive_info(int argc, char **argv, fmt = validate_output_format(cfg.output_format); if (fmt < 0) { fprintf(stderr, "ERROR : WDC %s invalid output format\n", __func__); + close(fd); return fmt; } @@ -8905,6 +10217,7 @@ static int wdc_vs_drive_info(int argc, char **argv, if (ret) { fprintf(stderr, "ERROR : WDC %s: Identify Controller failed\n", __func__); + close(fd); return ret; } @@ -8912,24 +10225,72 @@ static int wdc_vs_drive_info(int argc, char **argv, wdc_check_device(r, fd); capabilities = wdc_get_drive_capabilities(r, fd); if ((capabilities & WDC_DRIVE_CAP_INFO) == WDC_DRIVE_CAP_INFO) { - ret = wdc_do_drive_info(fd, &result); + ret = wdc_get_pci_ids(r, &read_device_id, &read_vendor_id); + if (ret < 0) + { + fprintf(stderr, "ERROR : WDC: %s: failure to get pci ids, ret = %d\n", __func__, ret); + goto out; + } - if (!ret) { - size = (__u16)((cpu_to_le32(result) & 0xffff0000) >> 16); - rev = (double)(cpu_to_le32(result) & 0x0000ffff); + switch (read_device_id) { + case WDC_NVME_SN640_DEV_ID: + case WDC_NVME_SN640_DEV_ID_1: + case WDC_NVME_SN640_DEV_ID_2: + case WDC_NVME_SN640_DEV_ID_3: + case WDC_NVME_SN650_DEV_ID: + case WDC_NVME_SN650_DEV_ID_1: + case WDC_NVME_SN650_DEV_ID_2: + case WDC_NVME_SN650_DEV_ID_3: + case WDC_NVME_SN650_DEV_ID_4: + case WDC_NVME_SN655_DEV_ID: + case WDC_NVME_SN560_DEV_ID_1: + case WDC_NVME_SN560_DEV_ID_2: + case WDC_NVME_SN560_DEV_ID_3: + case WDC_NVME_SN550_DEV_ID: + case WDC_NVME_ZN350_DEV_ID: + case WDC_NVME_ZN350_DEV_ID_1: + ret = wdc_do_drive_info(fd, &result); + + if (!ret) { + size = (__u16)((cpu_to_le32(result) & 0xffff0000) >> 16); + rev = (double)(cpu_to_le32(result) & 0x0000ffff); + + if (fmt == NORMAL) { + printf("Drive HW Revison: %4.1f\n", (.1 * rev)); + printf("FTL Unit Size: 0x%x KB\n", size); + printf("Customer SN: %-.*s\n", (int)sizeof(ctrl.sn), &ctrl.sn[0]); + } + else if (fmt == JSON) { + root = json_create_object(); + sprintf(rev_str, "%4.1f", (.1 * rev)); + json_object_add_value_string(root, "Drive HW Revison", rev_str); + + json_object_add_value_int(root, "FTL Unit Size", le16_to_cpu(size)); + wdc_StrFormat(formatter, sizeof(formatter), &ctrl.sn[0], sizeof(ctrl.sn)); + json_object_add_value_string(root, "Customer SN", formatter); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); + } + } + break; + case WDC_NVME_SN730A_DEV_ID: + memcpy(vsData, &ctrl.vs[0], 32); + + major_rev = ctrl.sn[12]; + minor_rev = ctrl.sn[13]; if (fmt == NORMAL) { - printf("Drive HW Revison: %4.1f\n", (.1 * rev)); - printf("FTL Unit Size: 0x%x KB\n", size); - printf("Customer SN: %-.*s\n", (int)sizeof(ctrl.sn), &ctrl.sn[0]); + printf("Drive HW Revision: %c.%c \n", major_rev, minor_rev); + printf("Customer SN: %-.*s\n", 14, &ctrl.sn[0]); } else if (fmt == JSON) { root = json_create_object(); - sprintf(rev_str, "%4.1f", (.1 * rev)); + sprintf(rev_str, "%c.%c", major_rev, minor_rev); json_object_add_value_string(root, "Drive HW Revison", rev_str); - - json_object_add_value_int(root, "FTL Unit Size", le16_to_cpu(size)); - wdc_StrFormat(formatter, sizeof(formatter), &ctrl.sn[0], sizeof(ctrl.sn)); + wdc_StrFormat(formatter, sizeof(formatter), &ctrl.sn[0], 14); json_object_add_value_string(root, "Customer SN", formatter); json_print_object(root, NULL); @@ -8937,39 +10298,87 @@ static int wdc_vs_drive_info(int argc, char **argv, json_free_object(root); } - } - } - else if ((capabilities & WDC_DRIVE_CAP_INFO_2) == WDC_DRIVE_CAP_INFO_2) { - memcpy(vsData, &ctrl.vs[0], 32); + break; + case WDC_NVME_SN820CL_DEV_ID: + /* Get the Drive HW Rev from the C6 Log page */ + ret = nvme_get_hw_rev_log(fd, &data, 0, NVME_NSID_ALL); + if (ret == 0) { + wdc_nvme_hw_rev_log *log_data = (wdc_nvme_hw_rev_log *)data; + major_rev = log_data->hw_rev_gdr; - major_rev = ctrl.sn[12]; - minor_rev = ctrl.sn[13]; + free(data); + data = NULL; + } else { + fprintf(stderr, "ERROR : WDC: %s: failure to get hw revision log\n", __func__); + ret = -1; + goto out; + } - if (fmt == NORMAL) { - printf("Drive HW Revision: %c.%c \n", major_rev, minor_rev); - printf("Customer SN: %-.*s\n", 14, &ctrl.sn[0]); - } - else if (fmt == JSON) { - root = json_create_object(); - sprintf(rev_str, "%c.%c", major_rev, minor_rev); - json_object_add_value_string(root, "Drive HW Revison", rev_str); - wdc_StrFormat(formatter, sizeof(formatter), &ctrl.sn[0], 14); - json_object_add_value_string(root, "Customer SN", formatter); - - json_print_object(root, NULL); - printf("\n"); + /* Get the Smart C0 log page */ + if ((capabilities & WDC_DRIVE_CAP_CLOUD_LOG_PAGE) == 0) { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + goto out; + } + + ret = nvme_get_ext_smart_cloud_log(fd, &data, 0, NVME_NSID_ALL); + + if (ret == 0) { + ext_smart_log_ptr = (wdc_nvme_ext_smart_log *)data; + + /* Set the FTL Unit size */ + ftl_unit_size = le32_to_cpu(ext_smart_log_ptr->ext_smart_ftlus); + + /* Set the Boot Spec Version */ + boot_spec_major = le16_to_cpu(ext_smart_log_ptr->ext_smart_maj); + boot_spec_minor = le16_to_cpu(ext_smart_log_ptr->ext_smart_min); + + /* Set the Drive Ownership Status */ + tcg_dev_ownership = le32_to_cpu(ext_smart_log_ptr->ext_smart_tcgos); + free(data); + } else { + fprintf(stderr, "ERROR : WDC: %s: failure to get extended smart cloud log\n", __func__); + ret = -1; + goto out; + } + + if (fmt == NORMAL) { + printf("Drive HW Revision: %2d\n", major_rev); + printf("FTL Unit Size: %d\n", ftl_unit_size); + printf("HyperScale Boot Version Spec: %d.%d\n", boot_spec_major, boot_spec_minor); + printf("TCG Device Ownership Status: %2d\n", tcg_dev_ownership); + + } + else if (fmt == JSON) { + root = json_create_object(); + + json_object_add_value_int(root, "Drive HW Revison", major_rev); + json_object_add_value_int(root, "FTL Unit Size", ftl_unit_size); + sprintf(rev_str, "%d.%d", boot_spec_major, boot_spec_minor); + json_object_add_value_string(root, "HyperScale Boot Version Spec", rev_str); + json_object_add_value_int(root, "TCG Device Ownership Status", tcg_dev_ownership); + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); + } - json_free_object(root); + break; + default: + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + break; } } else { - fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); - nvme_free_tree(r); - return -1; + fprintf(stderr, "ERROR : WDC: capability not supported by this device\n"); + ret = -1; } - +out: nvme_show_status(ret); nvme_free_tree(r); + close(fd); return ret; } @@ -9008,7 +10417,7 @@ static int wdc_vs_temperature_stats(int argc, char **argv, if (fmt < 0) { fprintf(stderr, "ERROR : WDC : invalid output format\n"); ret = fmt; - goto END; + goto out; } /* check if command is supported */ @@ -9016,17 +10425,17 @@ static int wdc_vs_temperature_stats(int argc, char **argv, capabilities = wdc_get_drive_capabilities(r, fd); if ((capabilities & WDC_DRIVE_CAP_TEMP_STATS) != WDC_DRIVE_CAP_TEMP_STATS) { fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); - nvme_free_tree(r); - return -1; + ret = -1; + goto out; } /* get the temperature stats or report errors */ ret = nvme_identify_ctrl(fd, &id_ctrl); if (ret != 0) - goto END; + goto out; ret = nvme_get_log_smart(fd, NVME_NSID_ALL, false, &smart_log); if (ret != 0) - goto END; + goto out; /* convert from Kelvin to degrees Celsius */ temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]) - 273; @@ -9083,9 +10492,10 @@ static int wdc_vs_temperature_stats(int argc, char **argv, else printf("%s: Invalid format\n", __func__); -END: +out: nvme_show_status(ret); nvme_free_tree(r); + close(fd); return ret; } @@ -9165,7 +10575,7 @@ static int wdc_capabilities(int argc, char **argv, printf("namespace-resize : %s\n", capabilities & WDC_DRIVE_CAP_NS_RESIZE ? "Supported" : "Not Supported"); printf("vs-drive-info : %s\n", - capabilities & (WDC_DRIVE_CAP_INFO | WDC_DRIVE_CAP_INFO_2) ? "Supported" : "Not Supported"); + capabilities & WDC_DRIVE_CAP_INFO ? "Supported" : "Not Supported"); printf("vs-temperature-stats : %s\n", capabilities & WDC_DRIVE_CAP_TEMP_STATS ? "Supported" : "Not Supported"); printf("cloud-SSD-plugin-version : %s\n", @@ -9178,8 +10588,19 @@ static int wdc_capabilities(int argc, char **argv, capabilities & WDC_DRIVE_CAP_OCP_C4_LOG_PAGE ? "Supported" : "Not Supported"); printf("get-unsupported-reqs-log : %s\n", capabilities & WDC_DRIVE_CAP_OCP_C5_LOG_PAGE ? "Supported" : "Not Supported"); - printf("capabilities : Supported\n"); + printf("get-latency-monitor-log : %s\n", + capabilities & WDC_DRIVE_CAP_C3_LOG_PAGE ? "Supported" : "Not Supported"); + printf("cloud-boot-SSD-version : %s\n", + capabilities & WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION ? "Supported" : "Not Supported"); + printf("vs-cloud-log : %s\n", + capabilities & WDC_DRIVE_CAP_CLOUD_LOG_PAGE ? "Supported" : "Not Supported"); + printf("vs-hw-rev-log : %s\n", + capabilities & WDC_DRIVE_CAP_HW_REV_LOG_PAGE ? "Supported" : "Not Supported"); + printf("vs-device_waf : %s\n", + capabilities & WDC_DRIVE_CAP_DEVICE_WAF ? "Supported" : "Not Supported"); + printf("capabilities : Supported\n"); nvme_free_tree(r); + close(fd); return 0; } @@ -9212,9 +10633,72 @@ static int wdc_cloud_ssd_plugin_version(int argc, char **argv, } nvme_free_tree(r); + close(fd); return 0; } +static int wdc_cloud_boot_SSD_version(int argc, char **argv, + struct command *command, struct plugin *plugin) +{ + const char *desc = "Get Cloud Boot SSD Version command."; + const char *namespace_id = "desired namespace id"; + nvme_root_t r; + uint64_t capabilities = 0; + int fd, ret = -1; + int major = 0, minor = 0; + __u8 *data = NULL; + wdc_nvme_ext_smart_log *ext_smart_log_ptr = NULL; + + struct config { + __u32 namespace_id; + }; + + struct config cfg = { + .namespace_id = NVME_NSID_ALL, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) + return fd; + + /* get capabilities */ + r = nvme_scan(NULL); + wdc_check_device(r, fd); + capabilities = wdc_get_drive_capabilities(r, fd); + + if ((capabilities & WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION) == WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION) { + /* Get the 0xC0 Smart Cloud Attribute V1 log data */ + ret = nvme_get_ext_smart_cloud_log(fd, &data, 0, cfg.namespace_id); + + ext_smart_log_ptr = (wdc_nvme_ext_smart_log *)data; + if (ret == 0) { + major = le16_to_cpu(ext_smart_log_ptr->ext_smart_maj); + minor = le16_to_cpu(ext_smart_log_ptr->ext_smart_min); + + /* print the version returned from the log page */ + printf("HyperScale Boot Version: %d.%d\n", major, minor); + + } else { + fprintf(stderr, "ERROR : WDC : Unable to read Extended Smart/C0 Log Page data\n"); + ret = -1; + } + + if (data) + free(data); + } else { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + } + + nvme_free_tree(r); + close(fd); + return ret; +} + static int wdc_enc_get_log(int argc, char **argv, struct command *command, struct plugin *plugin) { @@ -9226,7 +10710,7 @@ static int wdc_enc_get_log(int argc, char **argv, struct command *command, int xfer_size = 0; int fd; int len; - int err; + int err = 0; struct config { char *file; @@ -9247,7 +10731,7 @@ static int wdc_enc_get_log(int argc, char **argv, struct command *command, OPT_END() }; - err = fd = parse_and_open(argc, argv, desc, opts); + fd = parse_and_open(argc, argv, desc, opts); if (fd < 0) { goto ret; } @@ -9402,9 +10886,9 @@ static int wdc_enc_submit_move_data(int fd, char *cmd, int len, int xfer_size, F } } } while (more); - free(buf); } + free(buf); return err; } diff --git a/plugins/wdc/wdc-nvme.h b/plugins/wdc/wdc-nvme.h index da21692..c7b7f4c 100644 --- a/plugins/wdc/wdc-nvme.h +++ b/plugins/wdc/wdc-nvme.h @@ -1,10 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #undef CMD_INC_FILE #define CMD_INC_FILE plugins/wdc/wdc-nvme #if !defined(WDC_NVME) || defined(CMD_HEADER_MULTI_READ) #define WDC_NVME -#define WDC_PLUGIN_VERSION "1.16.4" +#define WDC_PLUGIN_VERSION "2.0.3" #include "cmd.h" PLUGIN(NAME("wdc", "Western Digital vendor specific extensions", WDC_PLUGIN_VERSION), @@ -40,6 +41,10 @@ PLUGIN(NAME("wdc", "Western Digital vendor specific extensions", WDC_PLUGIN_VERS ENTRY("get-error-recovery-log", "WDC Get Error Recovery Log Page", wdc_get_error_recovery_log) ENTRY("get-dev-capabilities-log", "WDC Get Device Capabilities Log Page", wdc_get_dev_capabilities_log) ENTRY("get-unsupported-reqs-log", "WDC Get Unsupported Requirements Log Page", wdc_get_unsupported_reqs_log) + ENTRY("cloud-boot-SSD-version", "WDC Get the Cloud Boot SSD Version", wdc_cloud_boot_SSD_version) + ENTRY("vs-cloud-log", "WDC Get the Cloud Log Page", wdc_vs_cloud_log) + ENTRY("vs-hw-rev-log", "WDC Get the Hardware Revision Log Page", wdc_vs_hw_rev_log) + ENTRY("vs-device-waf", "WDC Calculate Device Write Amplication Factor", wdc_vs_device_waf) ) ); diff --git a/plugins/wdc/wdc-utils.c b/plugins/wdc/wdc-utils.c index 52c427b..38e61ed 100644 --- a/plugins/wdc/wdc-utils.c +++ b/plugins/wdc/wdc-utils.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2017-2018 Western Digital Corporation or its affiliates. * diff --git a/plugins/wdc/wdc-utils.h b/plugins/wdc/wdc-utils.h index 04d4b83..83b208e 100644 --- a/plugins/wdc/wdc-utils.h +++ b/plugins/wdc/wdc-utils.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (c) 2017-2018 Western Digital Corporation or its affiliates. * diff --git a/plugins/ymtc/ymtc-nvme.c b/plugins/ymtc/ymtc-nvme.c index dd5f004..cfbf6a6 100644 --- a/plugins/ymtc/ymtc-nvme.c +++ b/plugins/ymtc/ymtc-nvme.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -30,9 +31,21 @@ static int show_ymtc_smart_log(int fd, __u32 nsid, const char *devname, u8 *nm = malloc(NM_SIZE * sizeof(u8)); u8 *raw = malloc(RAW_SIZE * sizeof(u8)); + if (!nm) { + if (raw) + free(raw); + return -1; + } + if (!raw) { + free(nm); + return -1; + } err = nvme_identify_ctrl(fd, &ctrl); - if (err) + if (err) { + free(nm); + free(raw); return err; + } snprintf(fw_ver, sizeof(fw_ver), "%c.%c%c.%c%c%c%c", ctrl.fr[0], ctrl.fr[1], ctrl.fr[2], ctrl.fr[3], @@ -51,17 +64,17 @@ static int show_ymtc_smart_log(int fd, __u32 nsid, const char *devname, /* 02 SI_VD_WEARLEVELING_COUNT */ get_ymtc_smart_info(smart, SI_VD_WEARLEVELING_COUNT, nm, raw); printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n", *nm, - *raw, *(raw+2), *(raw+4)); + *(uint16_t *)raw, *(uint16_t *)(raw+2), *(uint16_t *)(raw+4)); /* 03 SI_VD_E2E_DECTECTION_COUNT */ get_ymtc_smart_info(smart, SI_VD_E2E_DECTECTION_COUNT, nm, raw); printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); /* 04 SI_VD_PCIE_CRC_ERR_COUNT */ get_ymtc_smart_info(smart, SI_VD_PCIE_CRC_ERR_COUNT, nm, raw); - printf("crc_error_count : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + printf("crc_error_count : %3d%% %"PRIu32"\n", *nm, *(uint32_t *)raw); /* 08 SI_VD_THERMAL_THROTTLE_STATUS */ get_ymtc_smart_info(smart, SI_VD_THERMAL_THROTTLE_STATUS, nm, raw); - printf("thermal_throttle_status : %3d%% %"PRIu64"%%, cnt: %"PRIu64"\n", *nm, - int48_to_long(raw), int48_to_long(raw+1)); + printf("thermal_throttle_status : %3d%% %d%%, cnt: %"PRIu32"\n", *nm, + *raw, *(uint32_t *)(raw+1)); /* 11 SI_VD_TOTAL_WRITE */ get_ymtc_smart_info(smart, SI_VD_TOTAL_WRITE, nm, raw); printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n", *nm, int48_to_long(raw)); @@ -74,15 +87,15 @@ static int show_ymtc_smart_log(int fd, __u32 nsid, const char *devname, /* 15 SI_VD_TEMPT_SINCE_BORN */ get_ymtc_smart_info(smart, SI_VD_TEMPT_SINCE_BORN, nm, raw); printf("tempt_since_born : %3d%% max: %u, min: %u, curr: %u\n", *nm, - *raw, *(raw+2), *(raw+4)); + *(uint16_t *)raw-273, *(uint16_t *)(raw+2)-273, *(int16_t *)(raw+4)-273); /* 16 SI_VD_POWER_CONSUMPTION */ get_ymtc_smart_info(smart, SI_VD_POWER_CONSUMPTION, nm, raw); printf("power_consumption : %3d%% max: %u, min: %u, curr: %u\n", *nm, - *raw, *(raw+2), *(raw+4)); + *(uint16_t *)raw, *(uint16_t *)(raw+2), *(uint16_t *)(raw+4)); /* 17 SI_VD_TEMPT_SINCE_BOOTUP */ get_ymtc_smart_info(smart, SI_VD_TEMPT_SINCE_BOOTUP, nm, raw); - printf("tempt_since_bootup : %3d%% max: %u, min: %u, curr: %u\n", *nm, *raw, - *(raw+2), *(raw+4)); + printf("tempt_since_bootup : %3d%% max: %u, min: %u, curr: %u\n", *nm, + *(uint16_t *)raw-273, *(uint16_t *)(raw+2)-273, *(uint16_t *)(raw+4)-273); /* 18 SI_VD_POWER_LOSS_PROTECTION */ get_ymtc_smart_info(smart, SI_VD_POWER_LOSS_PROTECTION, nm, raw); printf("power_loss_protection : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); @@ -91,7 +104,8 @@ static int show_ymtc_smart_log(int fd, __u32 nsid, const char *devname, printf("read_fail : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); /* 20 SI_VD_THERMAL_THROTTLE_TIME */ get_ymtc_smart_info(smart, SI_VD_THERMAL_THROTTLE_TIME, nm, raw); - printf("thermal_throttle_time : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); + printf("thermal_throttle_time : %3d%% %u, time: %"PRIu32"\n", *nm, + *raw, *(uint32_t *)(raw+1)); /* 21 SI_VD_FLASH_MEDIA_ERROR */ get_ymtc_smart_info(smart, SI_VD_FLASH_MEDIA_ERROR, nm, raw); printf("flash_error_media_count : %3d%% %"PRIu64"\n", *nm, int48_to_long(raw)); @@ -140,5 +154,6 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd, if (err > 0) nvme_show_status(err); + close(fd); return err; } diff --git a/plugins/ymtc/ymtc-nvme.h b/plugins/ymtc/ymtc-nvme.h index c1ebc11..df5d598 100644 --- a/plugins/ymtc/ymtc-nvme.h +++ b/plugins/ymtc/ymtc-nvme.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/ymtc/ymtc-nvme diff --git a/plugins/ymtc/ymtc-utils.h b/plugins/ymtc/ymtc-utils.h index 700c764..39f79cb 100644 --- a/plugins/ymtc/ymtc-utils.h +++ b/plugins/ymtc/ymtc-utils.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __YMTC_UTILS_H__ #define __YMTC_UTILS_H__ diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index 56e53af..0b96346 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include @@ -102,13 +103,12 @@ static int list(int argc, char **argv, struct command *cmd, nvme_root = nvme_scan(NULL); if (nvme_root) { err = print_zns_list(nvme_root); + nvme_free_tree(nvme_root); } else { fprintf(stderr, "Failed to scan nvme subsystems\n"); err = -errno; } - nvme_free_tree(nvme_root); - return err; } @@ -1018,9 +1018,9 @@ static int zone_append(int argc, char **argv, struct command *cmd, struct plugin const char *fua = "force unit access"; const char *prinfo = "protection information action and checks field"; const char *piremap = "protection information remap (for type 1 PI)"; - const char *ref_tag = "reference tag (for end to end PI)"; - const char *lbat = "logical block application tag (for end to end PI)"; - const char *lbatm = "logical block application tag mask (for end to end PI)"; + const char *ref_tag = "reference tag for end-to-end PI"; + const char *lbat = "logical block application tag for end-to-end PI"; + const char *lbatm = "logical block application tag mask for end-to-end PI"; const char *metadata_size = "size of metadata in bytes"; const char *data_size = "size of data in bytes"; const char *latency = "output latency statistics"; @@ -1044,7 +1044,7 @@ static int zone_append(int argc, char **argv, struct command *cmd, struct plugin bool limited_retry; bool fua; __u32 namespace_id; - __u32 ref_tag; + __u64 ref_tag; __u16 lbat; __u16 lbatm; __u8 prinfo; @@ -1063,7 +1063,7 @@ static int zone_append(int argc, char **argv, struct command *cmd, struct plugin OPT_FILE("metadata", 'M', &cfg.metadata, metadata), OPT_FLAG("limited-retry", 'l', &cfg.limited_retry, limited_retry), OPT_FLAG("force-unit-access", 'f', &cfg.fua, fua), - OPT_UINT("ref-tag", 'r', &cfg.ref_tag, ref_tag), + OPT_SUFFIX("ref-tag", 'r', &cfg.ref_tag, ref_tag), OPT_SHRT("app-tag-mask", 'm', &cfg.lbatm, lbatm), OPT_SHRT("app-tag", 'a', &cfg.lbat, lbat), OPT_BYTE("prinfo", 'p', &cfg.prinfo, prinfo), @@ -1184,7 +1184,7 @@ static int zone_append(int argc, char **argv, struct command *cmd, struct plugin .zslba = cfg.zslba, .nlb = nblocks, .control = control, - .ilbrt = cfg.ref_tag, + .ilbrt_u64 = cfg.ref_tag, .lbat = cfg.lbat, .lbatm = cfg.lbatm, .data_len = cfg.data_size, diff --git a/plugins/zns/zns.h b/plugins/zns/zns.h index 1bdd4d9..77bfdd6 100644 --- a/plugins/zns/zns.h +++ b/plugins/zns/zns.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #undef CMD_INC_FILE #define CMD_INC_FILE plugins/zns/zns -- cgit v1.2.3