1
0
Fork 0

Adding upstream version 1.12.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-16 11:09:01 +01:00
parent 3b95ae912c
commit ac60c09ef6
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
457 changed files with 159628 additions and 0 deletions

211
plugins/dera/dera-nvme.c Normal file
View file

@ -0,0 +1,211 @@
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "nvme.h"
#include "nvme-print.h"
#include "nvme-ioctl.h"
#include "plugin.h"
#include "argconfig.h"
#include "suffix.h"
#define CREATE_CMD
#include "dera-nvme.h"
static int char4_to_int(__u8 *data)
{
int i;
int result = 0;
for (i = 0; i < 4; i++) {
result = result << 8;
result += data[3 - i];
}
return result;
}
struct nvme_dera_smart_info_log
{
__u8 quick_rebuild_cnt0[4];
__u8 quick_rebuild_cnt1[4];
__u8 full_rebuild_cnt0[4];
__u8 full_rebuild_cnt1[4];
__u8 raw_rebuild_cnt0[4];
__u8 raw_rebuild_cnt1[4];
__u8 cap_aged;
__u8 cap_aged_ratio;
__u8 cap_status;
__u8 cap_voltage[4];
__u8 cap_charge_ctrl_en;
__u8 cap_charge_ctrl_val[2];
__u8 cap_charge_max_thr[2];
__u8 cap_charge_min_thr[2];
__u8 dev_status;
__u8 dev_status_up;
__u8 nand_erase_err_cnt[4];
__u8 nand_program_err_cnt[4];
__u8 ddra_1bit_err[2];
__u8 ddra_2bit_err[2];
__u8 ddrb_1bit_err[2];
__u8 ddrb_2bit_err[2];
__u8 ddr_err_bit;
__u8 pcie_corr_err[2];
__u8 pcie_uncorr_err[2];
__u8 pcie_fatal_err[2];
__u8 pcie_err_bit;
__u8 power_level;
__u8 current_power[2];
__u8 nand_init_fail[2];
__u8 fw_loader_version[8];
__u8 uefi_driver_version[8];
__u8 gpio0_err[2];
__u8 gpio5_err[2];
__u8 gpio_err_bit[2];
__u8 rebuild_percent;
__u8 pcie_volt_status;
__u8 current_pcie_volt[2];
__u8 init_pcie_volt_thr[2];
__u8 rt_pcie_volt_thr[2];
__u8 init_pcie_volt_low[2];
__u8 rt_pcie_volt_low[2];
__u8 temp_sensor_abnormal[2];
__u8 nand_read_retry_fail_cnt[4];
__u8 fw_slot_version[8];
__u8 rsved[395];
};
enum dera_device_status
{
DEVICE_STATUS_READY = 0x00,
DEVICE_STATUS_QUICK_REBUILDING = 0x01,
DEVICE_STATUS_FULL_REBUILDING = 0x02,
DEVICE_STATUS_RAW_REBUILDING = 0x03,
DEVICE_STATUS_CARD_READ_ONLY = 0x04,
DEVICE_STATUS_FATAL_ERROR = 0x05,
DEVICE_STATUS_BUSY = 0x06,
DEVICE_STAUTS_LOW_LEVEL_FORMAT = 0x07,
DEVICE_STAUTS_FW_COMMITING = 0x08,
DEVICE_STAUTS__OVER_TEMPRATURE = 0x09,
};
static int nvme_dera_get_device_status(int fd, enum dera_device_status *result)
{
int err = 0;
struct nvme_passthru_cmd cmd = {
.opcode = 0xc0,
.addr = (__u64)(uintptr_t)NULL,
.data_len = 0,
.cdw10 = 0,
.cdw12 = 0x104,
};
err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
if (!err && result) {
*result = cmd.result;
}
return err;
}
static int get_status(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
int fd, err;
struct nvme_dera_smart_info_log log;
enum dera_device_status state = DEVICE_STATUS_FATAL_ERROR;
char *desc = "Get the Dera device status";
OPT_ARGS(opts) = {
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0)
return fd;
err = nvme_get_log(fd, 0xffffffff, 0xc0, false, sizeof(log), &log);
if (err) {
goto exit;
}
const char* dev_status[] = {
"Normal",
"Quick Rebuilding",
"Full Rebuilding",
"Raw Rebuilding",
"Card Read Only",
"Fatal Error",
"Busy",
"Low Level Format",
"Firmware Committing",
"Over Temperature" };
const char *volt_status[] = {
"Normal",
"Initial Low",
"Runtime Low",
};
err = nvme_dera_get_device_status(fd, &state);
if (!err){
if (state > 0 && state < 4){
printf("device_status : %s %d%% completed\n", dev_status[state], log.rebuild_percent);
}
else{
printf("device_status : %s\n", dev_status[state]);
}
}
else {
goto exit;
}
printf("dev_status_up : %s\n", dev_status[log.dev_status_up]);
printf("cap_aged : %s\n", log.cap_aged == 1 ? "True" : "False");
printf("cap_aged_ratio : %d%%\n", log.cap_aged_ratio < 100 ? log.cap_aged_ratio : 100);
printf("cap_status : %s\n", log.cap_status == 0 ? "Normal" : (log.cap_status == 1 ? "Warning" : "Critical"));
printf("cap_voltage : %d mV\n", char4_to_int(log.cap_voltage));
printf("nand_erase_err_cnt : %d\n", char4_to_int(log.nand_erase_err_cnt));
printf("nand_program_err_cnt : %d\n", char4_to_int(log.nand_program_err_cnt));
printf("ddra_1bit_err : %d\n", log.ddra_1bit_err[1] << 8 | log.ddra_1bit_err[0]);
printf("ddra_2bit_err : %d\n", log.ddra_2bit_err[1] << 8 | log.ddra_2bit_err[0]);
printf("ddrb_1bit_err : %d\n", log.ddrb_1bit_err[1] << 8 | log.ddrb_1bit_err[0]);
printf("ddrb_2bit_err : %d\n", log.ddrb_2bit_err[1] << 8 | log.ddrb_2bit_err[0]);
printf("ddr_err_bit : %d\n", log.ddr_err_bit);
printf("pcie_corr_err : %d\n", log.pcie_corr_err[1] << 8 | log.pcie_corr_err[0]);
printf("pcie_uncorr_err : %d\n", log.pcie_uncorr_err[1] << 8 | log.pcie_uncorr_err[0]);
printf("pcie_fatal_err : %d\n", log.pcie_fatal_err[1] << 8 | log.pcie_fatal_err[0]);
printf("power_level : %d W\n", log.power_level);
printf("current_power : %d mW\n", log.current_power[1] << 8 | log.current_power[0]);
printf("nand_init_fail : %d\n", log.nand_init_fail[1] << 8 | log.nand_init_fail[0]);
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 >= 0 && log.pcie_volt_status <= sizeof(volt_status) / sizeof(const char *)){
printf("pcie_volt_status : %s\n", volt_status[log.pcie_volt_status]);
}
else{
printf("pcie_volt_status : Unknown\n");
}
printf("current_pcie_volt : %d mV\n", log.current_pcie_volt[1] << 8 | log.current_pcie_volt[0]);
printf("init_pcie_volt_low_cnt : %d\n", log.init_pcie_volt_low[1] << 8 | log.init_pcie_volt_low[0]);
printf("rt_pcie_volt_low_cnt : %d\n", log.rt_pcie_volt_low[1] << 8 | log.rt_pcie_volt_low[0]);
printf("temp_sensor_abnormal_cnt : %d\n", log.temp_sensor_abnormal[1] << 8 | log.temp_sensor_abnormal[0]);
printf("nand_read_retry_fail_cnt : %d\n", char4_to_int(log.nand_read_retry_fail_cnt));
printf("fw_slot_version : %.*s\n", 8, log.fw_slot_version);
exit:
if (err > 0)
fprintf(stderr, "\nNVMe status:%s(0x%x)\n", nvme_status_to_string(err), err);
return err;
}

17
plugins/dera/dera-nvme.h Normal file
View file

@ -0,0 +1,17 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/dera/dera-nvme
#if !defined(DERA_NVME) || defined(CMD_HEADER_MULTI_READ)
#define DERA_NVME
#include "cmd.h"
PLUGIN(NAME("dera", "Dera vendor specific extensions"),
COMMAND_LIST(
ENTRY("smart-log-add", "Retrieve Dera SMART Log, show it", get_status, "stat")
)
);
#endif
#include "define_cmd.h"

View file

@ -0,0 +1,374 @@
/*
* Copyright (c) 2017-2019 Huawei Corporation or its affiliates.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Author: Zou Ming<zouming.zouming@huawei.com>,
* Yang Feng <philip.yang@huawei.com>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include "linux/nvme_ioctl.h"
#include "nvme.h"
#include "nvme-print.h"
#include "nvme-ioctl.h"
#include "plugin.h"
#include "json.h"
#include "argconfig.h"
#include "suffix.h"
#include <sys/ioctl.h>
#define CREATE_CMD
#include "huawei-nvme.h"
#define HW_SSD_PCI_VENDOR_ID 0x19E5
#define ARRAY_NAME_LEN 80
#define NS_NAME_LEN 40
#define MIN_ARRAY_NAME_LEN 16
#define MIN_NS_NAME_LEN 16
struct huawei_list_item {
char node[1024];
struct nvme_id_ctrl ctrl;
int nsid;
struct nvme_id_ns ns;
unsigned block;
char ns_name[NS_NAME_LEN];
char array_name[ARRAY_NAME_LEN];
bool huawei_device;
};
struct huawei_list_element_len {
unsigned int node;
unsigned int ns_name;
unsigned int nguid;
unsigned int ns_id;
unsigned int usage;
unsigned int array_name;
};
static int huawei_get_nvme_info(int fd, struct huawei_list_item *item, const char *node)
{
int err;
int len;
struct stat nvme_stat_info;
memset(item, 0, sizeof(*item));
err = nvme_identify_ctrl(fd, &item->ctrl);
if (err)
return err;
/*identify huawei device*/
if (strstr(item->ctrl.mn, "Huawei") == NULL &&
le16_to_cpu(item->ctrl.vid) != HW_SSD_PCI_VENDOR_ID) {
item->huawei_device = false;
return 0;
}
item->huawei_device = true;
item->nsid = nvme_get_nsid(fd);
err = nvme_identify_ns(fd, item->nsid, 0, &item->ns);
if (err)
return err;
err = fstat(fd, &nvme_stat_info);
if (err < 0)
return err;
strcpy(item->node, node);
item->block = S_ISBLK(nvme_stat_info.st_mode);
if (item->ns.vs[0] == 0) {
len = snprintf(item->ns_name, NS_NAME_LEN, "%s", "----");
if (len < 0)
return -EINVAL;
}
else {
memcpy(item->ns_name, item->ns.vs, NS_NAME_LEN);
item->ns_name[NS_NAME_LEN - 1] = '\0';
}
if (item->ctrl.vs[0] == 0) {
len = snprintf(item->array_name, ARRAY_NAME_LEN, "%s", "----");
if (len < 0)
return -EINVAL;
}
else {
memcpy(item->array_name, item->ctrl.vs, ARRAY_NAME_LEN);
item->array_name[ARRAY_NAME_LEN - 1] = '\0';
}
return 0;
}
static void format(char *formatter, size_t fmt_sz, char *tofmt, size_t tofmtsz)
{
fmt_sz = snprintf(formatter,fmt_sz, "%-*.*s",
(int)tofmtsz, (int)tofmtsz, tofmt);
/* trim() the obnoxious trailing white lines */
while (fmt_sz) {
if (formatter[fmt_sz - 1] != ' ' && formatter[fmt_sz - 1] != '\0') {
formatter[fmt_sz] = '\0';
break;
}
fmt_sz--;
}
}
static void huawei_json_print_list_items(struct huawei_list_item *list_items,
unsigned len)
{
struct json_object *root;
struct json_array *devices;
struct json_object *device_attrs;
char formatter[128] = { 0 };
int index, i = 0;
root = json_create_object();
devices = json_create_array();
for (i = 0; i < len; i++) {
device_attrs = json_create_object();
json_object_add_value_string(device_attrs,
"DevicePath",
list_items[i].node);
if (sscanf(list_items[i].node, "/dev/nvme%d", &index) == 1)
json_object_add_value_int(device_attrs,
"Index",
index);
format(formatter, sizeof(formatter),
list_items[i].ns_name,
sizeof(list_items[i].ns_name));
json_object_add_value_string(device_attrs,
"NS Name",
formatter);
format(formatter, sizeof(formatter),
list_items[i].array_name,
sizeof(list_items[i].array_name));
json_object_add_value_string(device_attrs,
"Array Name",
formatter);
json_array_add_value_object(devices, device_attrs);
}
json_object_add_value_array(root, "Devices", devices);
json_print_object(root, NULL);
printf("\n");
json_free_object(root);
}
static void huawei_print_list_head(struct huawei_list_element_len element_len)
{
char dash[128];
int i;
for (i = 0; i < 128; i++)
dash[i] = '-';
dash[127] = '\0';
printf("%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s\n",
element_len.node, element_len.node, "Node",
element_len.ns_name, element_len.ns_name, "NS Name",
element_len.nguid, element_len.nguid, "Nguid",
element_len.ns_id, element_len.ns_id, "NS ID",
element_len.usage, element_len.usage, "Usage",
element_len.array_name, element_len.array_name, "Array Name");
printf("%-.*s %-.*s %-.*s %-.*s %-.*s %-.*s\n",
element_len.node, dash, element_len.ns_name, dash,
element_len.nguid, dash, element_len.ns_id, dash,
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)
{
long long int lba = 1 << list_item.ns.lbaf[(list_item.ns.flbas & 0x0f)].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 = nguid_buf;
int i;
sprintf(usage,"%6.2f %2sB / %6.2f %2sB", nuse, u_suffix,
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]);
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.nguid, element_len.nguid, nguid_buf,
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);
}
static unsigned int choose_len(unsigned int old_len, unsigned int cur_len, unsigned int default_len)
{
unsigned int temp_len;
temp_len = (cur_len > default_len) ? cur_len : default_len;
if (temp_len > old_len)
{
return temp_len;
}
return old_len;
}
static unsigned int huawei_get_ns_len(struct huawei_list_item *list_items, unsigned len, unsigned default_len)
{
int i;
unsigned int min_len = default_len;
for (i = 0 ; i < len ; i++)
min_len = choose_len(min_len , strlen(list_items->ns_name), default_len);
return min_len;
}
static int huawei_get_array_len(struct huawei_list_item *list_items, unsigned len, unsigned default_len)
{
int i;
int min_len = default_len;
for (i = 0 ; i < len ; i++)
min_len = choose_len(min_len , strlen(list_items->array_name), default_len);
return min_len;
}
static void huawei_print_list_items(struct huawei_list_item *list_items, unsigned len)
{
unsigned i;
struct huawei_list_element_len element_len;
element_len.node = 16;
element_len.nguid = 2 * sizeof(list_items->ns.nguid) + 1;
element_len.ns_id = 9;
element_len.usage = 26;
element_len.ns_name = huawei_get_ns_len(list_items, len, MIN_NS_NAME_LEN);
element_len.array_name = huawei_get_array_len(list_items, len, MIN_ARRAY_NAME_LEN);
huawei_print_list_head(element_len);
for (i = 0 ; i < len ; i++)
huawei_print_list_item(list_items[i], element_len);
}
static int huawei_list(int argc, char **argv, struct command *command,
struct plugin *plugin)
{
char path[264];
struct dirent **devices;
struct huawei_list_item *list_items;
unsigned int i, n, fd, ret;
unsigned int huawei_num = 0;
int fmt;
const char *desc = "Retrieve basic information for the given huawei device";
struct config {
char *output_format;
};
struct config cfg = {
.output_format = "normal",
};
OPT_ARGS(opts) = {
OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
OPT_END()
};
argconfig_parse(argc, argv, desc, opts);
fmt = validate_output_format(cfg.output_format);
if (fmt != JSON && fmt != NORMAL)
return -EINVAL;
n = scandir("/dev", &devices, scan_namespace_filter, alphasort);
if (n <= 0)
return n;
list_items = calloc(n, sizeof(*list_items));
if (!list_items) {
fprintf(stderr, "can not allocate controller list payload\n");
return ENOMEM;
}
for (i = 0; i < n; i++) {
snprintf(path, sizeof(path), "/dev/%s", devices[i]->d_name);
fd = open(path, O_RDONLY);
ret = huawei_get_nvme_info(fd, &list_items[huawei_num], path);
if (ret)
return ret;
if (list_items[huawei_num].huawei_device == true) {
huawei_num++;
}
}
if (huawei_num > 0){
if (fmt == JSON)
huawei_json_print_list_items(list_items, huawei_num);
else
huawei_print_list_items(list_items, huawei_num);
}
for (i = 0; i < n; i++)
free(devices[i]);
free(devices);
free(list_items);
return 0;
}
static void huawei_do_id_ctrl(__u8 *vs, struct json_object *root)
{
char array_name[ARRAY_NAME_LEN + 1] = {0};
memcpy(array_name, vs, ARRAY_NAME_LEN);
if (root)
json_object_add_value_string(root, "array name", strlen(array_name) > 1 ? array_name : "NULL");
else
printf("array name : %s\n", strlen(array_name) > 1 ? array_name : "NULL");
}
static int huawei_id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
return __id_ctrl(argc, argv, cmd, plugin, huawei_do_id_ctrl);
}

View file

@ -0,0 +1,18 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/huawei/huawei-nvme
#if !defined(HUAWEI_NVME) || defined(CMD_HEADER_MULTI_READ)
#define HUAWEI_NVME
#include "cmd.h"
PLUGIN(NAME("huawei", "Huawei vendor specific extensions"),
COMMAND_LIST(
ENTRY("list", "List all Huawei NVMe devices and namespaces on machine", huawei_list)
ENTRY("id-ctrl", "Huawei identify controller", huawei_id_ctrl)
)
);
#endif
#include "define_cmd.h"

1316
plugins/intel/intel-nvme.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,23 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/intel/intel-nvme
#if !defined(INTEL_NVME) || defined(CMD_HEADER_MULTI_READ)
#define INTEL_NVME
#include "cmd.h"
PLUGIN(NAME("intel", "Intel vendor specific extensions"),
COMMAND_LIST(
ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl)
ENTRY("internal-log", "Retrieve Intel internal firmware log, save it", get_internal_log)
ENTRY("lat-stats", "Retrieve Intel IO Latency Statistics log, show it", get_lat_stats_log)
ENTRY("lat-stats-tracking", "Enable and disable Latency Statistics logging.", enable_lat_stats_tracking)
ENTRY("market-name", "Retrieve Intel Marketing Name log, show it", get_market_log)
ENTRY("smart-log-add", "Retrieve Intel SMART Log, show it", get_additional_smart_log)
ENTRY("temp-stats", "Retrieve Intel Temperature Statistics log, show it", get_temp_stats_log)
)
);
#endif
#include "define_cmd.h"

436
plugins/lnvm/lnvm-nvme.c Normal file
View file

@ -0,0 +1,436 @@
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include "nvme.h"
#include "nvme-print.h"
#include "nvme-ioctl.h"
#include "plugin.h"
#include "nvme-lightnvm.h"
#include "argconfig.h"
#include "suffix.h"
#define CREATE_CMD
#include "lnvm-nvme.h"
static int lnvm_init(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Initialize LightNVM device. A LightNVM/Open-Channel SSD"\
" must have a media manager associated before it can"\
" be exposed to the user. The default is to initialize"
" the general media manager on top of the device.\n\n"
"Example:"
" lnvm-init -d nvme0n1";
const char *devname = "identifier of desired device. e.g. nvme0n1.";
const char *mmtype = "media manager to initialize on top of device. Default: gennvm.";
int ret;
struct config {
char *devname;
char *mmtype;
};
struct config cfg = {
.devname = "",
.mmtype = "gennvm",
};
OPT_ARGS(opts) = {
OPT_STRING("device-name", 'd', "DEVICE", &cfg.devname, devname),
OPT_STRING("mediamgr-name", 'm', "MM", &cfg.mmtype, mmtype),
OPT_END()
};
ret = argconfig_parse(argc, argv, desc, opts);
if (ret < 0)
return ret;
if (!strlen(cfg.devname)) {
fprintf(stderr, "device name missing\n");
return -EINVAL;
}
return lnvm_do_init(cfg.devname, cfg.mmtype);
}
static int lnvm_list(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "List all devices registered with LightNVM.";
int ret;
OPT_ARGS(opts) = {
OPT_END()
};
ret = argconfig_parse(argc, argv, desc, opts);
if (ret < 0)
return ret;
return lnvm_do_list_devices();
}
static int lnvm_info(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Show general information and registered target types with LightNVM";
int ret;
OPT_ARGS(opts) = {
OPT_END()
};
ret = argconfig_parse(argc, argv, desc, opts);
if (ret < 0)
return ret;
return lnvm_do_info();
}
static int lnvm_id_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Send an Identify Geometry command to the "\
"given LightNVM device, returns properties of the specified "\
"namespace in either human-readable or binary format.";
const char *raw_binary = "show infos in binary format";
const char *human_readable = "show infos in readable format";
const char *namespace_id = "identifier of desired namespace. default: 1";
unsigned int flags = 0;
int fd;
struct config {
__u32 namespace_id;
int raw_binary;
int human_readable;
};
struct config cfg = {
.namespace_id = 1,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_binary),
OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0)
return fd;
if (cfg.human_readable)
flags |= VERBOSE;
else if (cfg.raw_binary)
flags |= BINARY;
return lnvm_do_id_ns(fd, cfg.namespace_id, flags);
}
static int lnvm_chunk_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Retrieve the chunk information log for the "\
"specified given LightNVM device, returns in either "\
"human-readable or binary format.\n"\
"This will request Geometry first to get the "\
"num_grp,num_pu,num_chk first to figure out the total size "\
"of the log pages."\
;
const char *output_format = "Output format: normal|binary";
const char *human_readable = "Print normal in readable format";
int err, fmt, fd;
struct nvme_nvm_id20 geo;
struct nvme_nvm_chunk_desc *chunk_log;
__u32 nsid;
__u32 data_len;
unsigned int flags = 0;
struct config {
char *output_format;
int human_readable;
};
struct config cfg = {
.output_format = "normal",
};
OPT_ARGS(opts) = {
OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0)
return fd;
fmt = validate_output_format(cfg.output_format);
if (fmt < 0) {
err = fmt;
goto close;
}
if (fmt == BINARY)
flags |= BINARY;
else if (cfg.human_readable)
flags |= VERBOSE;
nsid = nvme_get_nsid(fd);
/*
* It needs to figure out how many bytes will be requested by this
* subcommand by the (num_grp * num_pu * num_chk) from the Geometry.
*/
err = lnvm_get_identity(fd, nsid, (struct nvme_nvm_id *) &geo);
if (err)
goto close;
data_len = (geo.num_grp * geo.num_pu * geo.num_chk) *
sizeof(struct nvme_nvm_chunk_desc);
chunk_log = malloc(data_len);
if (!chunk_log) {
fprintf(stderr, "cound not alloc for chunk log %dbytes\n",
data_len);
err = -ENOMEM;
goto close;
}
err = lnvm_do_chunk_log(fd, nsid, data_len, chunk_log, flags);
if (err)
fprintf(stderr, "get log page for chunk information failed\n");
free(chunk_log);
close:
close(fd);
return err;
}
static int lnvm_create_tgt(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Instantiate a target on top of a LightNVM enabled device.";
const char *devname = "identifier of desired device. e.g. nvme0n1.";
const char *tgtname = "target name of the device to initialize. e.g. target0.";
const char *tgttype = "identifier of target type. e.g. pblk.";
const char *lun_begin = "Define begin of luns to use for target.";
const char *lun_end = "Define set of luns to use for target.";
const char *over_prov = "Define over-provision percentage for target.";
const char *flag_factory = "Create target in factory mode";
int flags;
int ret;
struct config {
char *devname;
char *tgtname;
char *tgttype;
__u32 lun_begin;
__u32 lun_end;
__u32 over_prov;
/* flags */
__u32 factory;
};
struct config cfg = {
.devname = "",
.tgtname = "",
.tgttype = "",
.lun_begin = -1,
.lun_end = -1,
.over_prov = -1,
.factory = 0,
};
OPT_ARGS(opts) = {
OPT_STRING("device-name", 'd', "DEVICE", &cfg.devname, devname),
OPT_STRING("target-name", 'n', "TARGET", &cfg.tgtname, tgtname),
OPT_STRING("target-type", 't', "TARGETTYPE", &cfg.tgttype, tgttype),
OPT_UINT("lun-begin", 'b', &cfg.lun_begin, lun_begin),
OPT_UINT("lun-end", 'e', &cfg.lun_end, lun_end),
OPT_UINT("over-prov", 'o', &cfg.over_prov, over_prov),
OPT_FLAG("factory", 'f', &cfg.factory, flag_factory),
OPT_END()
};
ret = argconfig_parse(argc, argv, desc, opts);
if (ret < 0)
return ret;
if (!strlen(cfg.devname)) {
fprintf(stderr, "device name missing\n");
return -EINVAL;
}
if (!strlen(cfg.tgtname)) {
fprintf(stderr, "target name missing\n");
return -EINVAL;
}
if (!strlen(cfg.tgttype)) {
fprintf(stderr, "target type missing\n");
return -EINVAL;
}
flags = 0;
if (cfg.factory)
flags |= NVM_TARGET_FACTORY;
return lnvm_do_create_tgt(cfg.devname, cfg.tgtname, cfg.tgttype, cfg.lun_begin, cfg.lun_end, cfg.over_prov, flags);
}
static int lnvm_remove_tgt(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Remove an initialized LightNVM target.";
const char *tgtname = "target name of the device to remove. e.g. target0.";
int ret;
struct config {
char *tgtname;
};
struct config cfg = {
.tgtname = "",
};
OPT_ARGS(opts) = {
OPT_STRING("target-name", 'n', "TARGET", &cfg.tgtname, tgtname),
OPT_END()
};
ret = argconfig_parse(argc, argv, desc, opts);
if (ret < 0)
return ret;
if (!strlen(cfg.tgtname)) {
fprintf(stderr, "target name missing\n");
return -EINVAL;
}
return lnvm_do_remove_tgt(cfg.tgtname);
}
static int lnvm_factory_init(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Factory initialize a LightNVM enabled device.";
const char *devname = "identifier of desired device. e.g. nvme0n1.";
const char *erase_only_marked = "only erase marked blocks. default: all blocks.";
const char *host_marks = "remove host side blocks list. default: keep.";
const char *bb_marks = "remove grown bad blocks list. default: keep";
int ret;
struct config {
char *devname;
int erase_only_marked;
int clear_host_marks;
int clear_bb_marks;
};
struct config cfg = {
.devname = "",
};
OPT_ARGS(opts) = {
OPT_STRING("device-name", 'd', "DEVICE", &cfg.devname, devname),
OPT_FLAG("erase-only-marked", 'e', &cfg.erase_only_marked, erase_only_marked),
OPT_FLAG("clear-host-side-blks", 's', &cfg.clear_host_marks, host_marks),
OPT_FLAG("clear-bb-blks", 'b', &cfg.clear_bb_marks, bb_marks),
OPT_END()
};
ret = argconfig_parse(argc, argv, desc, opts);
if (ret < 0)
return ret;
if (!strlen(cfg.devname)) {
fprintf(stderr, "device name missing\n");
return -EINVAL;
}
return lnvm_do_factory_init(cfg.devname, cfg.erase_only_marked,
cfg.clear_host_marks, cfg.clear_bb_marks);
}
static int lnvm_get_bbtbl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Receive bad block table from a LightNVM compatible"\
" device.";
const char *namespace = "(optional) desired namespace";
const char *ch = "channel identifier";
const char *lun = "lun identifier (within a channel)";
const char *raw_binary = "show infos in binary format";
unsigned int fd, flags = 0;
struct config {
__u32 namespace_id;
__u16 lunid;
__u16 chid;
int raw_binary;
};
struct config cfg = {
.namespace_id = 1,
.lunid = 0,
.chid = 0,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
OPT_SHRT("channel-id", 'c', &cfg.chid, ch),
OPT_SHRT("lun-id", 'l', &cfg.lunid, lun),
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_binary),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (cfg.raw_binary)
flags |= BINARY;
return lnvm_do_get_bbtbl(fd, cfg.namespace_id, cfg.lunid, cfg.chid, flags);
}
static int lnvm_set_bbtbl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Update bad block table on a LightNVM compatible"\
" device.";
const char *namespace = "(optional) desired namespace";
const char *ch = "channel identifier";
const char *lun = "lun identifier (within a channel)";
const char *pln = "plane identifier (within a lun)";
const char *blk = "block identifier (within a plane)";
const char *value = "value to update the specific block to.";
int fd;
struct config {
__u32 namespace_id;
__u16 lunid;
__u16 chid;
__u16 plnid;
__u16 blkid;
__u16 value;
};
struct config cfg = {
.namespace_id = 1,
.lunid = 0,
.chid = 0,
.plnid = 0,
.blkid = 0,
.value = 0,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
OPT_SHRT("channel-id", 'c', &cfg.chid, ch),
OPT_SHRT("lun-id", 'l', &cfg.lunid, lun),
OPT_SHRT("plane-id", 'p', &cfg.plnid, pln),
OPT_SHRT("block-id", 'b', &cfg.blkid, blk),
OPT_SHRT("value", 'v', &cfg.value, value),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
printf("Updating: Ch.: %u LUN: %u Plane: %u Block: %u -> %u\n",
cfg.chid, cfg.lunid, cfg.plnid, cfg.blkid, cfg.value);
return lnvm_do_set_bbtbl(fd, cfg.namespace_id, cfg.chid, cfg.lunid,
cfg.plnid, cfg.blkid, cfg.value);
}

27
plugins/lnvm/lnvm-nvme.h Normal file
View file

@ -0,0 +1,27 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/lnvm/lnvm-nvme
#if !defined(LNVM_NVME) || defined(CMD_HEADER_MULTI_READ)
#define LNVM_NVME
#include "cmd.h"
PLUGIN(NAME("lnvm", "LightNVM specific extensions"),
COMMAND_LIST(
ENTRY("list", "List available LightNVM devices", lnvm_list)
ENTRY("info", "List general information and available target engines", lnvm_info)
ENTRY("id-ns", "List geometry for LightNVM device", lnvm_id_ns, "geometry")
ENTRY("chunk-log", "Chunk Information Log Page", lnvm_chunk_log)
ENTRY("init", "Initialize media manager on LightNVM device", lnvm_init)
ENTRY("create", "Create target on top of a LightNVM device", lnvm_create_tgt)
ENTRY("remove", "Remove target from device", lnvm_remove_tgt)
ENTRY("factory", "Reset device to factory state", lnvm_factory_init)
ENTRY("diag-bbtbl", "Diagnose bad block table", lnvm_get_bbtbl)
ENTRY("diag-set-bbtbl", "Update bad block table", lnvm_set_bbtbl)
)
);
#endif
#include "define_cmd.h"

View file

@ -0,0 +1,759 @@
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "linux/nvme_ioctl.h"
#include "nvme.h"
#include "nvme-print.h"
#include "nvme-ioctl.h"
#include "plugin.h"
#include "argconfig.h"
#include "suffix.h"
#define CREATE_CMD
#include "memblaze-nvme.h"
#include "memblaze-utils.h"
enum {
MB_FEAT_POWER_MGMT = 0xc6,
};
/*
* Return -1 if @fw1 < @fw2
* Return 0 if @fw1 == @fw2
* Return 1 if @fw1 > @fw2
*/
static int compare_fw_version(const char *fw1, const char *fw2)
{
while (*fw1 != '\0') {
if (*fw2 == '\0' || *fw1 > *fw2)
return 1;
if (*fw1 < *fw2)
return -1;
fw1++;
fw2++;
}
if (*fw2 != '\0')
return -1;
return 0;
}
/**********************************************************
* input: firmware version string
* output:
* 1: new intel format
* 0: old memblaze format
* *******************************************************/
#define MEMBLAZE_FORMAT (0)
#define INTEL_FORMAT (1)
// 2.83 = raisin
#define IS_RAISIN(str) (!strcmp(str, "2.83"))
// 2.13 = papaya
#define IS_PAPAYA(str) (!strcmp(str, "2.13"))
#define STR_VER_SIZE 5
int getlogpage_format_type(char *fw_ver)
{
char fw_ver_local[STR_VER_SIZE];
strncpy(fw_ver_local, fw_ver, STR_VER_SIZE);
*(fw_ver_local + STR_VER_SIZE - 1) = '\0';
if ( IS_RAISIN(fw_ver_local) )
{
return INTEL_FORMAT;
}
else
{
return MEMBLAZE_FORMAT;
}
}
static __u32 item_id_2_u32(struct nvme_memblaze_smart_log_item *item)
{
__le32 __id = 0;
memcpy(&__id, item->id, 3);
return le32_to_cpu(__id);
}
static __u64 raw_2_u64(const __u8 *buf, size_t len)
{
__le64 val = 0;
memcpy(&val, buf, len);
return le64_to_cpu(val);
}
#define STRN2_01 "Additional Smart Log for NVME device"
#define STRN2_02 "namespace-id"
#define STRN1_01 "key"
#define STRN1_02 "normalized"
#define STRN1_03 "raw"
#define STR00_01 "program_fail_count"
#define STR01_01 "erase_fail_count"
#define STR02_01 "wear_leveling"
#define STR02_03 "min: "
#define STR02_04 ", max: "
#define STR02_05 ", avg: "
#define STR03_01 "end_to_end_error_detection_count"
#define STR04_01 "crc_error_count"
#define STR05_01 "timed_workload_media_wear"
#define STR06_01 "timed_workload_host_reads"
#define STR07_01 "timed_workload_timer"
#define STR07_02 " min"
#define STR08_01 "thermal_throttle_status"
#define STR08_02 ", cnt: "
#define STR09_01 "retry_buffer_overflow_count"
#define STR10_01 "pll_lock_loss_count"
#define STR11_01 "nand_bytes_written"
#define STR11_03 "sectors: "
#define STR12_01 "host_bytes_written"
#define STR12_03 "sectors: "
#define STR13_01 "system_area_life_left"
#define STR14_01 "total_read"
#define STR15_01 "tempt_since_born"
#define STR15_03 "max: "
#define STR15_04 ", min: "
#define STR15_05 ", curr: "
#define STR16_01 "power_consumption"
#define STR16_03 "max: "
#define STR16_04 ", min: "
#define STR16_05 ", curr: "
#define STR17_01 "tempt_since_bootup"
#define STR17_03 "max: "
#define STR17_04 ", min: "
#define STR17_05 ", curr: "
#define STR18_01 "power_loss_protection"
#define STR19_01 "read_fail"
#define STR20_01 "thermal_throttle_time"
#define STR21_01 "flash_media_error"
static void get_memblaze_new_smart_info(struct nvme_p4_smart_log *smart, int index, u8 *nm_val, u8 *raw_val)
{
memcpy(nm_val, smart->itemArr[index].nmVal, NM_SIZE);
memcpy(raw_val, smart->itemArr[index].rawVal, RAW_SIZE);
}
static void show_memblaze_smart_log_new(struct nvme_memblaze_smart_log *s,
unsigned int nsid, const char *devname)
{
struct nvme_p4_smart_log *smart = (struct nvme_p4_smart_log *)s;
u8 *nm = malloc(NM_SIZE * sizeof(u8));
u8 *raw = malloc(RAW_SIZE * sizeof(u8));
/* Table Title */
printf("%s:%s %s:%x\n", STRN2_01, devname, STRN2_02, nsid);
/* Clumn Name*/
printf("%-34s%-11s%s\n", STRN1_01, STRN1_02, STRN1_03);
/* 00 RAISIN_SI_VD_PROGRAM_FAIL */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PROGRAM_FAIL, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR00_01, *nm, int48_to_long(raw));
/* 01 RAISIN_SI_VD_ERASE_FAIL */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_ERASE_FAIL, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR01_01, *nm, int48_to_long(raw));
/* 02 RAISIN_SI_VD_WEARLEVELING_COUNT */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_WEARLEVELING_COUNT, nm, raw);
printf("%-31s : %3d%% %s%u%s%u%s%u\n", STR02_01, *nm,
STR02_03, *raw, STR02_04, *(raw+2), STR02_05, *(raw+4));
/* 03 RAISIN_SI_VD_E2E_DECTECTION_COUNT */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_E2E_DECTECTION_COUNT, nm, raw);
printf("%-31s: %3d%% %"PRIu64"\n", STR03_01, *nm, int48_to_long(raw));
/* 04 RAISIN_SI_VD_PCIE_CRC_ERR_COUNT */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PCIE_CRC_ERR_COUNT, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR04_01, *nm, int48_to_long(raw));
/* 05 RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR, nm, raw);
printf("%-32s: %3d%% %.3f%%\n", STR05_01, *nm, ((float)int48_to_long(raw))/1000);
/* 06 RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ, nm, raw);
printf("%-32s: %3d%% %"PRIu64"%%\n", STR06_01, *nm, int48_to_long(raw));
/* 07 RAISIN_SI_VD_TIMED_WORKLOAD_TIMER */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_TIMER, nm, raw);
printf("%-32s: %3d%% %"PRIu64"%s\n", STR07_01, *nm, int48_to_long(raw), STR07_02);
/* 08 RAISIN_SI_VD_THERMAL_THROTTLE_STATUS */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_THERMAL_THROTTLE_STATUS, nm, raw);
printf("%-32s: %3d%% %"PRIu64"%%%s%"PRIu64"\n", STR08_01, *nm,
int48_to_long(raw), STR08_02, int48_to_long(raw+1));
/* 09 RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR09_01, *nm, int48_to_long(raw));
/* 10 RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR10_01, *nm, int48_to_long(raw));
/* 11 RAISIN_SI_VD_TOTAL_WRITE */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TOTAL_WRITE, nm, raw);
printf("%-32s: %3d%% %s%"PRIu64"\n", STR11_01, *nm, STR11_03, int48_to_long(raw));
/* 12 RAISIN_SI_VD_HOST_WRITE */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_HOST_WRITE, nm, raw);
printf("%-32s: %3d%% %s%"PRIu64"\n", STR12_01, *nm, STR12_03, int48_to_long(raw));
/* 13 RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR13_01, *nm, int48_to_long(raw));
/* 14 RAISIN_SI_VD_TOTAL_READ */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TOTAL_READ, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR14_01, *nm, int48_to_long(raw));
/* 15 RAISIN_SI_VD_TEMPT_SINCE_BORN */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TEMPT_SINCE_BORN, nm, raw);
printf("%-32s: %3d%% %s%u%s%u%s%u\n", STR15_01, *nm,
STR15_03, *raw, STR15_04, *(raw+2), STR15_05, *(raw+4));
/* 16 RAISIN_SI_VD_POWER_CONSUMPTION */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_POWER_CONSUMPTION, nm, raw);
printf("%-32s: %3d%% %s%u%s%u%s%u\n", STR16_01, *nm,
STR16_03, *raw, STR16_04, *(raw+2), STR16_05, *(raw+4));
/* 17 RAISIN_SI_VD_TEMPT_SINCE_BOOTUP */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TEMPT_SINCE_BOOTUP, nm, raw);
printf("%-32s: %3d%% %s%u%s%u%s%u\n", STR17_01, *nm, STR17_03, *raw,
STR17_04, *(raw+2), STR17_05, *(raw+4));
/* 18 RAISIN_SI_VD_POWER_LOSS_PROTECTION */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_POWER_LOSS_PROTECTION, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR18_01, *nm, int48_to_long(raw));
/* 19 RAISIN_SI_VD_READ_FAIL */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_READ_FAIL, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR19_01, *nm, int48_to_long(raw));
/* 20 RAISIN_SI_VD_THERMAL_THROTTLE_TIME */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_THERMAL_THROTTLE_TIME, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR20_01, *nm, int48_to_long(raw));
/* 21 RAISIN_SI_VD_FLASH_MEDIA_ERROR */
get_memblaze_new_smart_info(smart, RAISIN_SI_VD_FLASH_MEDIA_ERROR, nm, raw);
printf("%-32s: %3d%% %"PRIu64"\n", STR21_01, *nm, int48_to_long(raw));
free(nm);
free(raw);
}
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];
struct nvme_memblaze_smart_log_item *item;
strncpy(fw_ver_local, fw_ver, STR_VER_SIZE);
*(fw_ver_local + STR_VER_SIZE - 1) = '\0';
printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", devname, nsid);
printf("Total write in GB since last factory reset : %"PRIu64"\n",
int48_to_long(smart->items[TOTAL_WRITE].rawval));
printf("Total read in GB since last factory reset : %"PRIu64"\n",
int48_to_long(smart->items[TOTAL_READ].rawval));
printf("Thermal throttling status[1:HTP in progress] : %u\n",
smart->items[THERMAL_THROTTLE].thermal_throttle.on);
printf("Total thermal throttling minutes since power on : %u\n",
smart->items[THERMAL_THROTTLE].thermal_throttle.count);
printf("Maximum temperature in Kelvin since last factory reset : %u\n",
le16_to_cpu(smart->items[TEMPT_SINCE_RESET].temperature.max));
printf("Minimum temperature in Kelvin since last factory reset : %u\n",
le16_to_cpu(smart->items[TEMPT_SINCE_RESET].temperature.min));
if (compare_fw_version(fw_ver, "0.09.0300") != 0) {
printf("Maximum temperature in Kelvin since power on : %u\n",
le16_to_cpu(smart->items[TEMPT_SINCE_BOOTUP].temperature_p.max));
printf("Minimum temperature in Kelvin since power on : %u\n",
le16_to_cpu(smart->items[TEMPT_SINCE_BOOTUP].temperature_p.min));
}
printf("Current temperature in Kelvin : %u\n",
le16_to_cpu(smart->items[TEMPT_SINCE_RESET].temperature.curr));
printf("Maximum power in watt since power on : %u\n",
le16_to_cpu(smart->items[POWER_CONSUMPTION].power.max));
printf("Minimum power in watt since power on : %u\n",
le16_to_cpu(smart->items[POWER_CONSUMPTION].power.min));
printf("Current power in watt : %u\n",
le16_to_cpu(smart->items[POWER_CONSUMPTION].power.curr));
item = &smart->items[POWER_LOSS_PROTECTION];
if (item_id_2_u32(item) == 0xEC)
printf("Power loss protection normalized value : %u\n",
item->power_loss_protection.curr);
item = &smart->items[WEARLEVELING_COUNT];
if (item_id_2_u32(item) == 0xAD) {
printf("Percentage of wearleveling count left : %u\n",
le16_to_cpu(item->nmval));
printf("Wearleveling count min erase cycle : %u\n",
le16_to_cpu(item->wearleveling_count.min));
printf("Wearleveling count max erase cycle : %u\n",
le16_to_cpu(item->wearleveling_count.max));
printf("Wearleveling count avg erase cycle : %u\n",
le16_to_cpu(item->wearleveling_count.avg));
}
item = &smart->items[HOST_WRITE];
if (item_id_2_u32(item) == 0xF5)
printf("Total host write in GiB since device born : %llu\n",
(unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
item = &smart->items[THERMAL_THROTTLE_CNT];
if (item_id_2_u32(item) == 0xEB)
printf("Thermal throttling count since device born : %u\n",
item->thermal_throttle_cnt.cnt);
item = &smart->items[CORRECT_PCIE_PORT0];
if (item_id_2_u32(item) == 0xED)
printf("PCIE Correctable Error Count of Port0 : %llu\n",
(unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
item = &smart->items[CORRECT_PCIE_PORT1];
if (item_id_2_u32(item) == 0xEE)
printf("PCIE Correctable Error Count of Port1 : %llu\n",
(unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
item = &smart->items[REBUILD_FAIL];
if (item_id_2_u32(item) == 0xEF)
printf("End-to-End Error Detection Count : %llu\n",
(unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
item = &smart->items[ERASE_FAIL];
if (item_id_2_u32(item) == 0xF0)
printf("Erase Fail Count : %llu\n",
(unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
item = &smart->items[PROGRAM_FAIL];
if (item_id_2_u32(item) == 0xF1)
printf("Program Fail Count : %llu\n",
(unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
item = &smart->items[READ_FAIL];
if (item_id_2_u32(item) == 0xF2)
printf("Read Fail Count : %llu\n",
(unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
if ( IS_PAPAYA(fw_ver_local) ) {
struct nvme_p4_smart_log *s = (struct nvme_p4_smart_log *)smart;
u8 *nm = malloc(NM_SIZE * sizeof(u8));
u8 *raw = malloc(RAW_SIZE * sizeof(u8));
/* 00 RAISIN_SI_VD_PROGRAM_FAIL */
get_memblaze_new_smart_info(s, PROGRAM_FAIL, nm, raw);
printf("%-32s : %3d%% %"PRIu64"\n",
STR00_01, *nm, int48_to_long(raw));
/* 01 RAISIN_SI_VD_ERASE_FAIL */
get_memblaze_new_smart_info(s, ERASE_FAIL, nm, raw);
printf("%-32s : %3d%% %"PRIu64"\n",
STR01_01, *nm, int48_to_long(raw));
/* 02 RAISIN_SI_VD_WEARLEVELING_COUNT */
get_memblaze_new_smart_info(s, WEARLEVELING_COUNT, nm, raw);
printf("%-31s : %3d%% %s%u%s%u%s%u\n",
STR02_01, *nm, STR02_03, *raw, STR02_04, *(raw+2), STR02_05, *(raw+4));
/* 11 RAISIN_SI_VD_TOTAL_WRITE */
get_memblaze_new_smart_info(s, TOTAL_WRITE, nm, raw);
printf("%-32s : %3d%% %"PRIu64"\n",
STR11_01, *nm, 32*int48_to_long(raw));
/* 12 RAISIN_SI_VD_HOST_WRITE */
get_memblaze_new_smart_info(s, HOST_WRITE, nm, raw);
printf("%-32s : %3d%% %"PRIu64"\n",
STR12_01, *nm, 32*int48_to_long(raw));
free(nm);
free(raw);
}
}
static int show_memblaze_smart_log(int fd, __u32 nsid, const char *devname,
struct nvme_memblaze_smart_log *smart)
{
struct nvme_id_ctrl ctrl;
char fw_ver[10];
int err = 0;
err = nvme_identify_ctrl(fd, &ctrl);
if (err)
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],
ctrl.fr[4], ctrl.fr[5], ctrl.fr[6]);
if (getlogpage_format_type(fw_ver)) // Intel Format & new format
{
show_memblaze_smart_log_new(smart, nsid, devname);
}
else // Memblaze Format & old format
{
show_memblaze_smart_log_old(smart, nsid, devname, fw_ver);
}
return err;
}
static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
struct nvme_memblaze_smart_log smart_log;
int err, fd;
char *desc = "Get Memblaze vendor specific additional smart log (optionally, "\
"for the specified namespace), and show it.";
const char *namespace = "(optional) desired namespace";
const char *raw = "dump output in binary format";
struct config {
__u32 namespace_id;
int raw_binary;
};
struct config cfg = {
.namespace_id = NVME_NSID_ALL,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0)
return fd;
err = nvme_get_log(fd, cfg.namespace_id, 0xca, false,
sizeof(smart_log), &smart_log);
if (!err) {
if (!cfg.raw_binary)
err = show_memblaze_smart_log(fd, cfg.namespace_id, devicename, &smart_log);
else
d_raw((unsigned char *)&smart_log, sizeof(smart_log));
}
if (err > 0)
fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err);
return err;
}
static char *mb_feature_to_string(int feature)
{
switch (feature) {
case MB_FEAT_POWER_MGMT: return "Memblaze power management";
default: return "Unknown";
}
}
static int get_additional_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Read operating parameters of the "\
"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 "\
"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 "\
"change saveable Features.\n\n"\
"Available additional feature id:\n"\
"0xc6: Memblaze power management\n"\
" (value 0 - 25w, 1 - 20w, 2 - 15w)";
const char *raw = "show feature in binary format";
const char *namespace_id = "identifier of desired namespace";
const char *feature_id = "hexadecimal feature name";
const char *sel = "[0-3]: curr./default/saved/supp.";
const char *data_len = "buffer len (if) data is returned";
const char *cdw11 = "dword 11 for interrupt vector config";
const char *human_readable = "show infos in readable format";
int err, fd;
__u32 result;
void *buf = NULL;
struct config {
__u32 namespace_id;
__u32 feature_id;
__u8 sel;
__u32 cdw11;
__u32 data_len;
int raw_binary;
int human_readable;
};
struct config cfg = {
.namespace_id = 1,
.feature_id = 0,
.sel = 0,
.cdw11 = 0,
.data_len = 0,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id),
OPT_BYTE("sel", 's', &cfg.sel, sel),
OPT_UINT("data-len", 'l', &cfg.data_len, data_len),
OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11),
OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0)
return fd;
if (cfg.sel > 7) {
fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel);
return EINVAL;
}
if (!cfg.feature_id) {
fprintf(stderr, "feature-id required param\n");
return EINVAL;
}
if (cfg.data_len) {
if (posix_memalign(&buf, getpagesize(), cfg.data_len))
exit(ENOMEM);
memset(buf, 0, cfg.data_len);
}
err = nvme_get_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.sel, cfg.cdw11,
cfg.data_len, buf, &result);
if (!err) {
printf("get-feature:0x%02x (%s), %s value: %#08x\n", cfg.feature_id,
mb_feature_to_string(cfg.feature_id),
nvme_select_to_string(cfg.sel), result);
if (cfg.human_readable)
nvme_feature_show_fields(cfg.feature_id, result, buf);
else {
if (buf) {
if (!cfg.raw_binary)
d(buf, cfg.data_len, 16, 1);
else
d_raw(buf, cfg.data_len);
}
}
} else if (err > 0)
fprintf(stderr, "NVMe Status:%s(%x)\n",
nvme_status_to_string(err), err);
if (buf)
free(buf);
return err;
}
static int set_additional_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Modify the saveable or changeable "\
"current operating parameters of the controller. Operating "\
"parameters are grouped and identified by Feature "\
"Identifiers. Feature settings can be applied to the entire "\
"controller and all associated namespaces, or to only a few "\
"namespace(s) associated with the controller. Default values "\
"for each Feature are vendor-specific and may not be modified."\
"Use get-feature to determine which Features are supported by "\
"the controller and are saveable/changeable.\n\n"\
"Available additional feature id:\n"\
"0xc6: Memblaze power management\n"\
" (value 0 - 25w, 1 - 20w, 2 - 15w)";
const char *namespace_id = "desired namespace";
const char *feature_id = "hex feature name (required)";
const char *data_len = "buffer length if data required";
const char *data = "optional file for feature data (default stdin)";
const char *value = "new value of feature (required)";
const char *save = "specifies that the controller shall save the attribute";
int err, fd;
__u32 result;
void *buf = NULL;
int ffd = STDIN_FILENO;
struct config {
char *file;
__u32 namespace_id;
__u32 feature_id;
__u32 value;
__u32 data_len;
int save;
};
struct config cfg = {
.file = "",
.namespace_id = 0,
.feature_id = 0,
.value = 0,
.data_len = 0,
.save = 0,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id),
OPT_UINT("value", 'v', &cfg.value, value),
OPT_UINT("data-len", 'l', &cfg.data_len, data_len),
OPT_FILE("data", 'd', &cfg.file, data),
OPT_FLAG("save", 's', &cfg.save, save),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0)
return fd;
if (!cfg.feature_id) {
fprintf(stderr, "feature-id required param\n");
return EINVAL;
}
if (cfg.data_len) {
if (posix_memalign(&buf, getpagesize(), cfg.data_len))
exit(ENOMEM);
memset(buf, 0, cfg.data_len);
}
if (buf) {
if (strlen(cfg.file)) {
ffd = open(cfg.file, O_RDONLY);
if (ffd <= 0) {
fprintf(stderr, "no firmware file provided\n");
err = EINVAL;
goto free;
}
}
if (read(ffd, (void *)buf, cfg.data_len) < 0) {
fprintf(stderr, "failed to read data buffer from input file\n");
err = EINVAL;
goto free;
}
}
err = nvme_set_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.value,
0, cfg.save, cfg.data_len, buf, &result);
if (err < 0) {
perror("set-feature");
goto free;
}
if (!err) {
printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id,
mb_feature_to_string(cfg.feature_id), cfg.value);
if (buf)
d(buf, cfg.data_len, 16, 1);
} else if (err > 0)
fprintf(stderr, "NVMe Status:%s(%x)\n",
nvme_status_to_string(err), err);
free:
if (buf)
free(buf);
return err;
}
static int memblaze_fw_commit(int fd, int select)
{
struct nvme_admin_cmd cmd = {
.opcode = nvme_admin_activate_fw,
.cdw10 = 8,
.cdw12 = select,
};
return nvme_submit_admin_passthru(fd, &cmd);
}
static int memblaze_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc =
"This performs a selective firmware download, which allows the user to "
"select which firmware binary to update for 9200 devices. This requires a power cycle once the "
"update completes. The options available are: \n\n"
"OOB - This updates the OOB and main firmware\n"
"EEP - This updates the eeprom and main firmware\n"
"ALL - This updates the eeprom, OOB, and main firmware";
const char *fw = "firmware file (required)";
const char *select = "FW Select (e.g., --select=OOB, EEP, ALL)";
int xfer = 4096;
void *fw_buf;
int fd, selectNo,fw_fd,fw_size,err,offset = 0;
struct stat sb;
int i;
struct config {
char *fw;
char *select;
};
struct config cfg = {
.fw = "",
.select = "\0",
};
OPT_ARGS(opts) = {
OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw),
OPT_STRING("select", 's', "flag", &cfg.select, select),
OPT_END()
};
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;
}
for (i = 0; i < 3; i++) {
cfg.select[i] = toupper(cfg.select[i]);
}
if (strncmp(cfg.select,"OOB", 3) == 0) {
selectNo = 18;
} else if (strncmp(cfg.select,"EEP", 3) == 0) {
selectNo = 10;
} else if (strncmp(cfg.select,"ALL", 3) == 0) {
selectNo = 26;
} else {
fprintf(stderr, "Invalid select flag\n");
err = EINVAL;
goto out;
}
fw_fd = open(cfg.fw, O_RDONLY);
if (fw_fd < 0) {
fprintf(stderr, "no firmware file provided\n");
err = EINVAL;
goto out;
}
err = fstat(fw_fd, &sb);
if (err < 0) {
perror("fstat");
err = errno;
}
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;
}
if (posix_memalign(&fw_buf, getpagesize(), fw_size)) {
fprintf(stderr, "No memory for f/w size:%d\n", fw_size);
err = ENOMEM;
goto out;
}
if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size)))
return EIO;
while (fw_size > 0) {
xfer = min(xfer, fw_size);
err = nvme_fw_download(fd, offset, xfer, fw_buf);
if (err < 0) {
perror("fw-download");
goto out;
} else if (err != 0) {
fprintf(stderr, "NVME Admin command error:%s(%x)\n",
nvme_status_to_string(err), err);
goto out;
}
fw_buf += xfer;
fw_size -= xfer;
offset += xfer;
}
err = memblaze_fw_commit(fd,selectNo);
if(err == 0x10B || err == 0x20B) {
err = 0;
fprintf(stderr, "Update successful! Please power cycle for changes to take effect\n");
}
out:
return err;
}

View file

@ -0,0 +1,27 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/memblaze/memblaze-nvme
#if !defined(MEMBLAZE_NVME) || defined(CMD_HEADER_MULTI_READ)
#define MEMBLAZE_NVME
#include "cmd.h"
#include "common.h"
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
PLUGIN(NAME("memblaze", "Memblaze vendor specific extensions"),
COMMAND_LIST(
ENTRY("smart-log-add", "Retrieve Memblaze SMART Log, show it", get_additional_smart_log)
ENTRY("get-feature-add", "Get Memblaze feature and show the resulting value", get_additional_feature)
ENTRY("set-feature-add", "Set a Memblaze feature and show the resulting value", set_additional_feature)
ENTRY("select-download", "Selective Firmware Download", memblaze_selective_download)
)
);
#endif
#include "define_cmd.h"

View file

@ -0,0 +1,164 @@
#ifndef __MEMBLAZE_UTILS_H__
#define __MEMBLAZE_UTILS_H__
#define SMART_INFO_OLD_SIZE 512
#define SMART_INFO_NEW_SIZE 4096
#define ID_SIZE 3
#define NM_SIZE 2
#define RAW_SIZE 7
typedef unsigned char u8;
// Intel Format & new format
/* Raisin Additional smart external ID */
#define RAISIN_SI_VD_PROGRAM_FAIL_ID 0xAB
#define RAISIN_SI_VD_ERASE_FAIL_ID 0xAC
#define RAISIN_SI_VD_WEARLEVELING_COUNT_ID 0xAD
#define RAISIN_SI_VD_E2E_DECTECTION_COUNT_ID 0xB8
#define RAISIN_SI_VD_PCIE_CRC_ERR_COUNT_ID 0xC7
#define RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR_ID 0xE2
#define RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ_ID 0xE3
#define RAISIN_SI_VD_TIMED_WORKLOAD_TIMER_ID 0xE4
#define RAISIN_SI_VD_THERMAL_THROTTLE_STATUS_ID 0xEA
#define RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT_ID 0xF0
#define RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT_ID 0xF3
#define RAISIN_SI_VD_TOTAL_WRITE_ID 0xF4
#define RAISIN_SI_VD_HOST_WRITE_ID 0xF5
#define RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT_ID 0xF6
#define RAISIN_SI_VD_TOTAL_READ_ID 0xFA
#define RAISIN_SI_VD_TEMPT_SINCE_BORN_ID 0xE7
#define RAISIN_SI_VD_POWER_CONSUMPTION_ID 0xE8
#define RAISIN_SI_VD_TEMPT_SINCE_BOOTUP_ID 0xAF
#define RAISIN_SI_VD_POWER_LOSS_PROTECTION_ID 0xEC
#define RAISIN_SI_VD_READ_FAIL_ID 0xF2
#define RAISIN_SI_VD_THERMAL_THROTTLE_TIME_ID 0xEB
#define RAISIN_SI_VD_FLASH_MEDIA_ERROR_ID 0xED
/* Raisin Addtional smart internal ID */
typedef enum
{
/* smart attr following intel */
RAISIN_SI_VD_PROGRAM_FAIL = 0, /* 0xAB */
RAISIN_SI_VD_ERASE_FAIL = 1, /* 0xAC */
RAISIN_SI_VD_WEARLEVELING_COUNT = 2,/* 0xAD */
RAISIN_SI_VD_E2E_DECTECTION_COUNT = 3, /* 0xB8 */
RAISIN_SI_VD_PCIE_CRC_ERR_COUNT = 4, /* 0xC7, 2 port data in one attribute */
RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR = 5, /* 0xE2 , unknown definition*/
RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ = 6, /* 0xE3 , unknown definition */
RAISIN_SI_VD_TIMED_WORKLOAD_TIMER = 7, /* 0xE4 , unknown definition */
RAISIN_SI_VD_THERMAL_THROTTLE_STATUS = 8, /* 0xEA */
RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT = 9, /* 0xF0, unknown definition*/
RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT = 10, /* 0xF3, unknown definition*/
RAISIN_SI_VD_TOTAL_WRITE = 11, /* 0xF4, unit is 32MiB */
RAISIN_SI_VD_HOST_WRITE = 12, /* 0xF5, unit is 32MiB */
RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT = 13, /* 0xF6, unknown definition*/
RAISIN_SI_VD_TOTAL_READ = 14, /* 0xFA, unit is 32MiB */
/* smart attr self defined */
RAISIN_SI_VD_TEMPT_SINCE_BORN = 15, /* 0xE7 */
RAISIN_SI_VD_POWER_CONSUMPTION = 16, /* 0xE8 */
RAISIN_SI_VD_TEMPT_SINCE_BOOTUP = 17, /* 0xAF */
RAISIN_SI_VD_POWER_LOSS_PROTECTION = 18, /* 0xEC */
RAISIN_SI_VD_READ_FAIL = 19, /* 0xF2 */
RAISIN_SI_VD_THERMAL_THROTTLE_TIME = 20, /* 0xEB */
RAISIN_SI_VD_FLASH_MEDIA_ERROR = 21, /* 0xED */
RAISIN_SI_VD_SMART_INFO_ITEMS_MAX,
} RAISIN_si_vendor_smart_item_e;
// Memblaze Format & old format
enum {
/*0*/TOTAL_WRITE = 0,
/*1*/TOTAL_READ,
/*2*/THERMAL_THROTTLE,
/*3*/TEMPT_SINCE_RESET,
/*4*/POWER_CONSUMPTION,
/*5*/TEMPT_SINCE_BOOTUP,
/*6*/POWER_LOSS_PROTECTION,
/*7*/WEARLEVELING_COUNT,
/*8*/HOST_WRITE,
/*9*/THERMAL_THROTTLE_CNT,
/*10*/CORRECT_PCIE_PORT0,
/*11*/CORRECT_PCIE_PORT1,
/*12*/REBUILD_FAIL,
/*13*/ERASE_FAIL,
/*14*/PROGRAM_FAIL,
/*15*/READ_FAIL,
/*16*/NR_SMART_ITEMS = RAISIN_SI_VD_SMART_INFO_ITEMS_MAX,
};
// Memblaze Format & old format
#pragma pack(push, 1)
struct nvme_memblaze_smart_log_item {
__u8 id[3];
union {
__u8 __nmval[2];
__le16 nmval;
};
union {
__u8 rawval[6];
struct temperature {
__le16 max;
__le16 min;
__le16 curr;
} temperature;
struct power {
__le16 max;
__le16 min;
__le16 curr;
} power;
struct thermal_throttle_mb {
__u8 on;
__u32 count;
} thermal_throttle;
struct temperature_p {
__le16 max;
__le16 min;
} temperature_p;
struct power_loss_protection {
__u8 curr;
} power_loss_protection;
struct wearleveling_count {
__le16 min;
__le16 max;
__le16 avg;
} wearleveling_count;
struct thermal_throttle_cnt {
__u8 active;
__le32 cnt;
} thermal_throttle_cnt;
};
__u8 resv;
};
#pragma pack(pop)
struct nvme_memblaze_smart_log {
struct nvme_memblaze_smart_log_item items[NR_SMART_ITEMS];
u8 resv[SMART_INFO_OLD_SIZE - sizeof(struct nvme_memblaze_smart_log_item) * NR_SMART_ITEMS];
};
// Intel Format & new format
struct nvme_p4_smart_log_item
{
/* Item identifier */
u8 id[ID_SIZE];
/* Normalized value or percentage. In the range from 0 to 100. */
u8 nmVal[NM_SIZE];
/* raw value */
u8 rawVal[RAW_SIZE];
};
struct nvme_p4_smart_log
{
struct nvme_p4_smart_log_item itemArr[NR_SMART_ITEMS];
/**
* change 512 to 4096.
* because micron's getlogpage request,the size of many commands have changed to 4k.
* request size > user malloc size,casuing parameters that are closed in momery are dirty.
*/
u8 resv[SMART_INFO_NEW_SIZE - sizeof(struct nvme_p4_smart_log_item) * NR_SMART_ITEMS];
};
#endif // __MEMBLAZE_UTILS_H__

1180
plugins/micron/micron-nvme.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/micron/micron-nvme
#if !defined(MICRON_NVME) || defined(CMD_HEADER_MULTI_READ)
#define MICRON_NVME
#include "cmd.h"
PLUGIN(NAME("micron", "Micron vendor specific extensions"),
COMMAND_LIST(ENTRY("select-download", "Selective Firmware Download", micron_selective_download)
ENTRY("vs-temperature-stats", "Retrieve Micron temperature statistics ", micron_temp_stats)
ENTRY("vs-pcie-stats", "Retrieve Micron PCIe error stats", micron_pcie_stats)
ENTRY("clear-pcie-correctable-errors", "Clear correctable PCIe errors", micron_clear_pcie_correctable_errors)
ENTRY("vs-internal-log", "Retrieve Micron logs", micron_internal_logs)
ENTRY("vs-nand-stats", "Retrieve NAND Stats", micron_nand_stats)
)
);
#endif
#include "define_cmd.h"

View file

@ -0,0 +1,639 @@
/*
* Copyright (c) 2018 NetApp, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include "nvme.h"
#include "nvme-ioctl.h"
#include "json.h"
#include "suffix.h"
#define CREATE_CMD
#include "netapp-nvme.h"
#define ONTAP_C2_LOG_ID 0xC2
#define ONTAP_C2_LOG_SIZE 4096
#define ONTAP_LABEL_LEN 260
#define ONTAP_NS_PATHLEN 525
enum {
NNORMAL,
NJSON,
NCOLUMN,
};
enum {
ONTAP_C2_LOG_SUPPORTED_LSP = 0x0,
ONTAP_C2_LOG_NSINFO_LSP = 0x1,
};
enum {
ONTAP_VSERVER_TLV = 0x11,
ONTAP_VOLUME_TLV = 0x12,
ONTAP_NS_TLV = 0x13,
};
static const char *dev_path = "/dev/";
struct smdevice_info {
int nsid;
struct nvme_id_ctrl ctrl;
struct nvme_id_ns ns;
char dev[265];
};
struct ontapdevice_info {
int nsid;
struct nvme_id_ctrl ctrl;
struct nvme_id_ns ns;
char nsdesc[4096];
unsigned char log_data[ONTAP_C2_LOG_SIZE];
char dev[265];
};
#define ARRAY_LABEL_LEN 60
#define VOLUME_LABEL_LEN 60
/*
* Format of the string isn't tightly controlled yet. For now, squash UCS-2 into
* ASCII. dst buffer must be at least count + 1 bytes long
*/
static void netapp_convert_string(char *dst, char *src, unsigned int count)
{
int i;
if (!dst || !src || !count)
return;
memset(dst, 0, count + 1);
for (i = 0; i < count; i++)
dst[i] = src[i * 2 + 1];
/* the json routines won't accept empty strings */
if (strlen(dst) == 0 && count)
dst[0] = ' ';
}
static void netapp_nguid_to_str(char *str, __u8 *nguid)
{
int i;
memset(str, 0, 33);
for (i = 0; i < 16; i++)
str += sprintf(str, "%02x", nguid[i]);
}
static void netapp_get_ns_size(char *size, long long *lba,
struct nvme_id_ns *ns)
{
*lba = 1 << ns->lbaf[(ns->flbas & 0x0F)].ds;
double nsze = le64_to_cpu(ns->nsze) * (*lba);
const char *s_suffix = suffix_si_get(&nsze);
sprintf(size, "%.2f%sB", nsze, s_suffix);
}
static void netapp_uuid_to_str(char *str, void *data)
{
#ifdef LIBUUID
uuid_t uuid;
struct nvme_ns_id_desc *desc = data;
memcpy(uuid, data + sizeof(*desc), 16);
uuid_unparse_lower(uuid, str);
#endif
}
static void ontap_labels_to_str(char *dst, char *src, int count)
{
int i;
memset(dst, 0, ONTAP_LABEL_LEN);
for (i = 0; i < count; i++) {
if (src[i] >= '!' && src[i] <= '~')
dst[i] = src[i];
else
break;
}
dst[i] = '\0';
}
static void netapp_get_ontap_labels(char *vsname, char *nspath,
unsigned char *log_data)
{
int lsp, tlv, label_len;
char *vserver_name, *volume_name, *namespace_name;
char vol_name[ONTAP_LABEL_LEN], ns_name[ONTAP_LABEL_LEN];
const char *ontap_vol = "/vol/";
int i, j;
/* get the lsp */
lsp = (*(__u8 *)&log_data[16]) & 0x0F;
if (lsp != ONTAP_C2_LOG_NSINFO_LSP)
/* lsp not related to nsinfo */
return;
/* get the vserver tlv and name */
tlv = *(__u8 *)&log_data[32];
if (tlv == ONTAP_VSERVER_TLV) {
label_len = (*(__u16 *)&log_data[34]) * 4;
vserver_name = (char *)&log_data[36];
ontap_labels_to_str(vsname, vserver_name, label_len);
} else {
/* not the expected vserver tlv */
fprintf(stderr, "Unable to fetch ONTAP vserver name\n");
return;
}
i = 36 + label_len;
j = i + 2;
/* get the volume tlv and name */
tlv = *(__u8 *)&log_data[i];
if (tlv == ONTAP_VOLUME_TLV) {
label_len = (*(__u16 *)&log_data[j]) * 4;
volume_name = (char *)&log_data[j + 2];
ontap_labels_to_str(vol_name, volume_name, label_len);
} else {
/* not the expected volume tlv */
fprintf(stderr, "Unable to fetch ONTAP volume name\n");
return;
}
i += 4 + label_len;
j += 4 + label_len;
/* get the namespace tlv and name */
tlv = *(__u8 *)&log_data[i];
if (tlv == ONTAP_NS_TLV) {
label_len = (*(__u16 *)&log_data[j]) * 4;
namespace_name = (char *)&log_data[j + 2];
ontap_labels_to_str(ns_name, namespace_name, label_len);
} else {
/* not the expected namespace tlv */
fprintf(stderr, "Unable to fetch ONTAP namespace name\n");
return;
}
snprintf(nspath, ONTAP_NS_PATHLEN, "%s%s%s%s", ontap_vol,
vol_name, "/", ns_name);
}
static void netapp_smdevice_json(struct json_array *devices, char *devname,
char *arrayname, char *volname, int nsid, char *nguid,
char *ctrl, char *astate, char *size, long long lba,
long long nsze)
{
struct json_object *device_attrs;
device_attrs = json_create_object();
json_object_add_value_string(device_attrs, "Device", devname);
json_object_add_value_string(device_attrs, "Array_Name", arrayname);
json_object_add_value_string(device_attrs, "Volume_Name", volname);
json_object_add_value_int(device_attrs, "NSID", nsid);
json_object_add_value_string(device_attrs, "Volume_ID", nguid);
json_object_add_value_string(device_attrs, "Controller", ctrl);
json_object_add_value_string(device_attrs, "Access_State", astate);
json_object_add_value_string(device_attrs, "Size", size);
json_object_add_value_int(device_attrs, "LBA_Data_Size", lba);
json_object_add_value_int(device_attrs, "Namespace_Size", nsze);
json_array_add_value_object(devices, device_attrs);
}
static void netapp_ontapdevice_json(struct json_array *devices, char *devname,
char *vsname, char *nspath, int nsid, char *uuid,
char *size, long long lba, long long nsze)
{
struct json_object *device_attrs;
device_attrs = json_create_object();
json_object_add_value_string(device_attrs, "Device", devname);
json_object_add_value_string(device_attrs, "Vserver", vsname);
json_object_add_value_string(device_attrs, "Namespace_Path", nspath);
json_object_add_value_int(device_attrs, "NSID", nsid);
json_object_add_value_string(device_attrs, "UUID", uuid);
json_object_add_value_string(device_attrs, "Size", size);
json_object_add_value_int(device_attrs, "LBA_Data_Size", lba);
json_object_add_value_int(device_attrs, "Namespace_Size", nsze);
json_array_add_value_object(devices, device_attrs);
}
static void netapp_smdevices_print(struct smdevice_info *devices, int count, int format)
{
struct json_object *root = NULL;
struct json_array *json_devices = NULL;
int i, slta;
char array_label[ARRAY_LABEL_LEN / 2 + 1];
char volume_label[VOLUME_LABEL_LEN / 2 + 1];
char nguid_str[33];
char basestr[] = "%s, Array Name %s, Volume Name %s, NSID %d, "
"Volume ID %s, Controller %c, Access State %s, %s\n";
char columnstr[] = "%-16s %-30s %-30s %4d %32s %c %-12s %9s\n";
char *formatstr = basestr; /* default to "normal" output format */
if (format == NCOLUMN) {
/* for column output, change output string and print column headers */
formatstr = columnstr;
printf("%-16s %-30s %-30s %-4s %-32s %-4s %-12s %-9s\n",
"Device", "Array Name", "Volume Name", "NSID",
"Volume ID", "Ctrl", "Access State", " Size");
printf("%-16s %-30s %-30s %-4s %-32s %-4s %-12s %-9s\n",
"----------------", "------------------------------",
"------------------------------", "----",
"--------------------------------", "----",
"------------", "---------");
}
else if (format == NJSON) {
/* prepare for json output */
root = json_create_object();
json_devices = json_create_array();
}
for (i = 0; i < count; i++) {
long long int lba = 1 << devices[i].ns.lbaf[(devices[i].ns.flbas & 0x0F)].ds;
double nsze = le64_to_cpu(devices[i].ns.nsze) * lba;
const char *s_suffix = suffix_si_get(&nsze);
char size[128];
sprintf(size, "%.2f%sB", nsze, s_suffix);
netapp_convert_string(array_label, (char *)&devices[i].ctrl.vs[20],
ARRAY_LABEL_LEN / 2);
slta = devices[i].ctrl.vs[0] & 0x1;
netapp_convert_string(volume_label, (char *)devices[i].ns.vs,
VOLUME_LABEL_LEN / 2);
netapp_nguid_to_str(nguid_str, devices[i].ns.nguid);
if (format == NJSON)
netapp_smdevice_json(json_devices, devices[i].dev,
array_label, volume_label, devices[i].nsid,
nguid_str, slta ? "A" : "B", "unknown", size,
lba, le64_to_cpu(devices[i].ns.nsze));
else
printf(formatstr, devices[i].dev, array_label,
volume_label, devices[i].nsid, nguid_str,
slta ? 'A' : 'B', "unknown", size);
}
if (format == NJSON) {
/* complete the json output */
json_object_add_value_array(root, "SMdevices", json_devices);
json_print_object(root, NULL);
}
}
static void netapp_ontapdevices_print(struct ontapdevice_info *devices,
int count, int format)
{
struct json_object *root = NULL;
struct json_array *json_devices = NULL;
char vsname[ONTAP_LABEL_LEN] = " ";
char nspath[ONTAP_NS_PATHLEN] = " ";
long long lba;
char size[128];
char uuid_str[37] = " ";
int i;
char basestr[] = "%s, Vserver %s, Namespace Path %s, NSID %d, UUID %s, %s\n";
char columnstr[] = "%-16s %-25s %-50s %-4d %-38s %-9s\n";
/* default to 'normal' output format */
char *formatstr = basestr;
if (format == NCOLUMN) {
/* change output string and print column headers */
formatstr = columnstr;
printf("%-16s %-25s %-50s %-4s %-38s %-9s\n",
"Device", "Vserver", "Namespace Path",
"NSID", "UUID", "Size");
printf("%-16s %-25s %-50s %-4s %-38s %-9s\n",
"----------------", "-------------------------",
"--------------------------------------------------",
"----", "--------------------------------------",
"---------");
} else if (format == NJSON) {
/* prepare for json output */
root = json_create_object();
json_devices = json_create_array();
}
for (i = 0; i < count; i++) {
netapp_get_ns_size(size, &lba, &devices[i].ns);
netapp_uuid_to_str(uuid_str, devices[i].nsdesc);
netapp_get_ontap_labels(vsname, nspath, devices[i].log_data);
if (format == NJSON) {
netapp_ontapdevice_json(json_devices, devices[i].dev,
vsname, nspath, devices[i].nsid,
uuid_str, size, lba,
le64_to_cpu(devices[i].ns.nsze));
} else
printf(formatstr, devices[i].dev, vsname, nspath,
devices[i].nsid, uuid_str, size);
}
if (format == NJSON) {
/* complete the json output */
json_object_add_value_array(root, "ONTAPdevices", json_devices);
json_print_object(root, NULL);
}
}
static int nvme_get_ontap_c2_log(int fd, __u32 nsid, void *buf, __u32 buflen)
{
struct nvme_admin_cmd get_log;
int err;
memset(buf, 0, buflen);
memset(&get_log, 0, sizeof(struct nvme_admin_cmd));
get_log.opcode = nvme_admin_get_log_page;
get_log.nsid = nsid;
get_log.addr = (__u64)(uintptr_t)buf;
get_log.data_len = buflen;
__u32 numd = (get_log.data_len >> 2) - 1;
__u32 numdu = numd >> 16;
__u32 numdl = numd & 0xFFFF;
get_log.cdw10 = ONTAP_C2_LOG_ID | (numdl << 16);
get_log.cdw10 |= ONTAP_C2_LOG_NSINFO_LSP << 8;
get_log.cdw11 = numdu;
err = nvme_submit_admin_passthru(fd, &get_log);
if (err) {
fprintf(stderr, "ioctl error %0x\n", err);
return 1;
}
return 0;
}
static int netapp_smdevices_get_info(int fd, struct smdevice_info *item,
const char *dev)
{
int err;
err = nvme_identify_ctrl(fd, &item->ctrl);
if (err) {
fprintf(stderr, "Identify Controller failed to %s (%s)\n", dev,
strerror(err));
return 0;
}
if (strncmp("NetApp E-Series", item->ctrl.mn, 15) != 0)
return 0; /* not the right model of controller */
item->nsid = nvme_get_nsid(fd);
err = nvme_identify_ns(fd, item->nsid, 0, &item->ns);
if (err) {
fprintf(stderr, "Unable to identify namespace for %s (%s)\n",
dev, strerror(err));
return 0;
}
strncpy(item->dev, dev, sizeof(item->dev));
return 1;
}
static int netapp_ontapdevices_get_info(int fd, struct ontapdevice_info *item,
const char *dev)
{
int err;
err = nvme_identify_ctrl(fd, &item->ctrl);
if (err) {
fprintf(stderr, "Identify Controller failed to %s (%s)\n",
dev, strerror(err));
return 0;
}
if (strncmp("NetApp ONTAP Controller", item->ctrl.mn, 23) != 0)
/* not the right controller model */
return 0;
item->nsid = nvme_get_nsid(fd);
err = nvme_identify_ns(fd, item->nsid, 0, &item->ns);
if (err) {
fprintf(stderr, "Unable to identify namespace for %s (%s)\n",
dev, strerror(err));
return 0;
}
err = nvme_identify_ns_descs(fd, item->nsid, item->nsdesc);
if (err) {
fprintf(stderr, "Unable to identify namespace descriptor for %s (%s)\n",
dev, strerror(err));
return 0;
}
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));
return 0;
}
strncpy(item->dev, dev, sizeof(item->dev));
return 1;
}
static int netapp_nvme_filter(const struct dirent *d)
{
char path[264];
struct stat bd;
int ctrl, ns, partition;
if (d->d_name[0] == '.')
return 0;
if (strstr(d->d_name, "nvme")) {
snprintf(path, sizeof(path), "%s%s", dev_path, d->d_name);
if (stat(path, &bd))
return 0;
if (sscanf(d->d_name, "nvme%dn%d", &ctrl, &ns) != 2)
return 0;
if (sscanf(d->d_name, "nvme%dn%dp%d", &ctrl, &ns, &partition) == 3)
return 0;
return 1;
}
return 0;
}
static int netapp_output_format(char *format)
{
if (!format)
return -EINVAL;
if (!strcmp(format, "normal"))
return NNORMAL;
if (!strcmp(format, "json"))
return NJSON;
if (!strcmp(format, "column"))
return NCOLUMN;
return -EINVAL;
}
/* handler for 'nvme netapp smdevices' */
static int netapp_smdevices(int argc, char **argv, struct command *command,
struct plugin *plugin)
{
const char *desc = "Display information about E-Series volumes.";
struct dirent **devices;
int num, i, fd, ret, fmt;
struct smdevice_info *smdevices;
char path[264];
int num_smdevices = 0;
struct config {
char *output_format;
};
struct config cfg = {
.output_format = "normal",
};
OPT_ARGS(opts) = {
OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json|column"),
OPT_END()
};
ret = argconfig_parse(argc, argv, desc, opts);
if (ret < 0)
return ret;
fmt = netapp_output_format(cfg.output_format);
if (fmt != NNORMAL && fmt != NCOLUMN && fmt != NJSON) {
fprintf(stderr, "Unrecognized output format: %s\n", cfg.output_format);
return -EINVAL;
}
num = scandir(dev_path, &devices, netapp_nvme_filter, alphasort);
if (num <= 0) {
fprintf(stderr, "No NVMe devices detected.\n");
return num;
}
smdevices = calloc(num, sizeof(*smdevices));
if (!smdevices) {
fprintf(stderr, "Unable to allocate memory for devices.\n");
return ENOMEM;
}
for (i = 0; i < num; i++) {
snprintf(path, sizeof(path), "%s%s", dev_path,
devices[i]->d_name);
fd = open(path, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Unable to open %s: %s\n", path,
strerror(errno));
continue;
}
num_smdevices += netapp_smdevices_get_info(fd,
&smdevices[num_smdevices], path);
close(fd);
}
if (num_smdevices)
netapp_smdevices_print(smdevices, num_smdevices, fmt);
for (i = 0; i < num; i++)
free(devices[i]);
free(devices);
free(smdevices);
return 0;
}
/* handler for 'nvme netapp ontapdevices' */
static int netapp_ontapdevices(int argc, char **argv, struct command *command,
struct plugin *plugin)
{
const char *desc = "Display information about ONTAP devices.";
struct dirent **devices;
int num, i, fd, ret, fmt;
struct ontapdevice_info *ontapdevices;
char path[264];
int num_ontapdevices = 0;
struct config {
char *output_format;
};
struct config cfg = {
.output_format = "normal",
};
OPT_ARGS(opts) = {
OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json|column"),
OPT_END()
};
ret = argconfig_parse(argc, argv, desc, opts);
if (ret < 0)
return ret;
fmt = netapp_output_format(cfg.output_format);
if (fmt != NNORMAL && fmt != NCOLUMN && fmt != NJSON) {
fprintf(stderr, "Unrecognized output format: %s\n", cfg.output_format);
return -EINVAL;
}
num = scandir(dev_path, &devices, netapp_nvme_filter, alphasort);
if (num <= 0) {
fprintf(stderr, "No NVMe devices detected.\n");
return num;
}
ontapdevices = calloc(num, sizeof(*ontapdevices));
if (!ontapdevices) {
fprintf(stderr, "Unable to allocate memory for devices.\n");
return -ENOMEM;
}
for (i = 0; i < num; i++) {
snprintf(path, sizeof(path), "%s%s", dev_path,
devices[i]->d_name);
fd = open(path, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Unable to open %s: %s\n", path,
strerror(errno));
continue;
}
num_ontapdevices += netapp_ontapdevices_get_info(fd,
&ontapdevices[num_ontapdevices], path);
close(fd);
}
if (num_ontapdevices)
netapp_ontapdevices_print(ontapdevices, num_ontapdevices, fmt);
for (i = 0; i < num; i++)
free(devices[i]);
free(devices);
free(ontapdevices);
return 0;
}

View file

@ -0,0 +1,18 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/netapp/netapp-nvme
#if !defined(NETAPP_NVME) || defined(CMD_HEADER_MULTI_READ)
#define NETAPP_NVME
#include "cmd.h"
PLUGIN(NAME("netapp", "NetApp vendor specific extensions"),
COMMAND_LIST(
ENTRY("smdevices", "NetApp SMdevices", netapp_smdevices)
ENTRY("ontapdevices", "NetApp ONTAPdevices", netapp_ontapdevices)
)
);
#endif
#include "define_cmd.h"

View file

@ -0,0 +1,873 @@
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/fs.h>
#include <inttypes.h>
#include <asm/byteorder.h>
#include <sys/ioctl.h>
#include <sys/sysinfo.h>
#include "linux/nvme_ioctl.h"
#include "nvme.h"
#include "nvme-print.h"
#include "nvme-ioctl.h"
#include "nvme-status.h"
#include "json.h"
#include "plugin.h"
#include "argconfig.h"
#include "suffix.h"
#define CREATE_CMD
#include "sfx-nvme.h"
#define SFX_PAGE_SHIFT 12
#define SECTOR_SHIFT 9
#define SFX_GET_FREESPACE _IOWR('N', 0x240, struct sfx_freespace_ctx)
#define IDEMA_CAP(exp_GB) (((__u64)exp_GB - 50ULL) * 1953504ULL + 97696368ULL)
enum {
SFX_LOG_LATENCY_READ_STATS = 0xc1,
SFX_LOG_SMART = 0xc2,
SFX_LOG_LATENCY_WRITE_STATS = 0xc3,
SFX_LOG_QUAL = 0xc4,
SFX_LOG_MISMATCHLBA = 0xc5,
SFX_LOG_MEDIA = 0xc6,
SFX_LOG_BBT = 0xc7,
SFX_LOG_IDENTIFY = 0xcc,
SFX_FEAT_ATOMIC = 0x01,
};
enum sfx_nvme_admin_opcode {
nvme_admin_query_cap_info = 0xd3,
nvme_admin_change_cap = 0xd4,
nvme_admin_sfx_set_features = 0xd5,
nvme_admin_sfx_get_features = 0xd6,
};
struct sfx_freespace_ctx
{
__u64 free_space;
__u64 phy_cap; /* physical capacity, in unit of sector */
__u64 phy_space; /* physical space considering OP, in unit of sector */
__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 */
};
struct nvme_capacity_info {
__u64 lba_sec_sz;
__u64 phy_sec_sz;
__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;
} thermal_throttle;
};
uint8_t _rp;
};
struct nvme_additional_smart_log {
struct nvme_additional_smart_log_item program_fail_cnt;
struct nvme_additional_smart_log_item erase_fail_cnt;
struct nvme_additional_smart_log_item wear_leveling_cnt;
struct nvme_additional_smart_log_item e2e_err_cnt;
struct nvme_additional_smart_log_item crc_err_cnt;
struct nvme_additional_smart_log_item timed_workload_media_wear;
struct nvme_additional_smart_log_item timed_workload_host_reads;
struct nvme_additional_smart_log_item timed_workload_timer;
struct nvme_additional_smart_log_item thermal_throttle_status;
struct nvme_additional_smart_log_item retry_buffer_overflow_cnt;
struct nvme_additional_smart_log_item pll_lock_loss_cnt;
struct nvme_additional_smart_log_item nand_bytes_written;
struct nvme_additional_smart_log_item host_bytes_written;
struct nvme_additional_smart_log_item raid_recover_cnt; // errors which can be recovered by RAID
struct nvme_additional_smart_log_item prog_timeout_cnt;
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
};
int nvme_change_cap(int fd, __u32 nsid, __u64 capacity)
{
struct nvme_admin_cmd cmd = {
.opcode = nvme_admin_change_cap,
.nsid = nsid,
.cdw10 = (capacity & 0xffffffff),
.cdw11 = (capacity >> 32),
};
return nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD,&cmd);
}
int nvme_sfx_set_features(int fd, __u32 nsid, __u32 fid, __u32 value)
{
struct nvme_admin_cmd cmd = {
.opcode = nvme_admin_sfx_set_features,
.nsid = nsid,
.cdw10 = fid,
.cdw11 = value,
};
return nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD,&cmd);
}
int nvme_sfx_get_features(int fd, __u32 nsid, __u32 fid, __u32 *result)
{
int err = 0;
struct nvme_admin_cmd cmd = {
.opcode = nvme_admin_sfx_get_features,
.nsid = nsid,
.cdw10 = fid,
};
err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD,&cmd);
if (!err && result) {
*result = cmd.result;
}
return err;
}
static void show_sfx_smart_log_jsn(struct nvme_additional_smart_log *smart,
unsigned int nsid, const char *devname)
{
struct json_object *root, *entry_stats, *dev_stats, *multi;
root = json_create_object();
json_object_add_value_string(root, "Intel Smart log", devname);
dev_stats = json_create_object();
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->program_fail_cnt.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->program_fail_cnt.raw));
json_object_add_value_object(dev_stats, "program_fail_count", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->erase_fail_cnt.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->erase_fail_cnt.raw));
json_object_add_value_object(dev_stats, "erase_fail_count", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->wear_leveling_cnt.norm);
multi = json_create_object();
json_object_add_value_int(multi, "min", le16_to_cpu(smart->wear_leveling_cnt.wear_level.min));
json_object_add_value_int(multi, "max", le16_to_cpu(smart->wear_leveling_cnt.wear_level.max));
json_object_add_value_int(multi, "avg", le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg));
json_object_add_value_object(entry_stats, "raw", multi);
json_object_add_value_object(dev_stats, "wear_leveling", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->e2e_err_cnt.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->e2e_err_cnt.raw));
json_object_add_value_object(dev_stats, "end_to_end_error_detection_count", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->crc_err_cnt.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->crc_err_cnt.raw));
json_object_add_value_object(dev_stats, "crc_error_count", entry_stats);
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", ((float)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();
json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_host_reads.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->timed_workload_host_reads.raw));
json_object_add_value_object(dev_stats, "timed_workload_host_reads", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_timer.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->timed_workload_timer.raw));
json_object_add_value_object(dev_stats, "timed_workload_timer", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->thermal_throttle_status.norm);
multi = json_create_object();
json_object_add_value_int(multi, "pct", smart->thermal_throttle_status.thermal_throttle.pct);
json_object_add_value_int(multi, "cnt", smart->thermal_throttle_status.thermal_throttle.count);
json_object_add_value_object(entry_stats, "raw", multi);
json_object_add_value_object(dev_stats, "thermal_throttle_status", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->retry_buffer_overflow_cnt.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->retry_buffer_overflow_cnt.raw));
json_object_add_value_object(dev_stats, "retry_buffer_overflow_count", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->pll_lock_loss_cnt.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->pll_lock_loss_cnt.raw));
json_object_add_value_object(dev_stats, "pll_lock_loss_count", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->nand_bytes_written.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->nand_bytes_written.raw));
json_object_add_value_object(dev_stats, "nand_bytes_written", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->host_bytes_written.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->host_bytes_written.raw));
json_object_add_value_object(dev_stats, "host_bytes_written", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->raid_recover_cnt.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->raid_recover_cnt.raw));
json_object_add_value_object(dev_stats, "raid_recover_cnt", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->prog_timeout_cnt.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->prog_timeout_cnt.raw));
json_object_add_value_object(dev_stats, "prog_timeout_cnt", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->erase_timeout_cnt.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->erase_timeout_cnt.raw));
json_object_add_value_object(dev_stats, "erase_timeout_cnt", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->read_timeout_cnt.norm);
json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->read_timeout_cnt.raw));
json_object_add_value_object(dev_stats, "read_timeout_cnt", entry_stats);
entry_stats = json_create_object();
json_object_add_value_int(entry_stats, "normalized", smart->read_ecc_cnt.norm);
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);
json_object_add_value_object(root, "Device stats", dev_stats);
json_print_object(root, NULL);
printf("/n");
json_free_object(root);
}
static void show_sfx_smart_log(struct nvme_additional_smart_log *smart,
unsigned int nsid, const char *devname)
{
printf("Additional Smart Log for ScaleFlux device:%s namespace-id:%x\n",
devname, nsid);
printf("key normalized raw\n");
printf("program_fail_count : %3d%% %"PRIu64"\n",
smart->program_fail_cnt.norm,
int48_to_long(smart->program_fail_cnt.raw));
printf("erase_fail_count : %3d%% %"PRIu64"\n",
smart->erase_fail_cnt.norm,
int48_to_long(smart->erase_fail_cnt.raw));
printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n",
smart->wear_leveling_cnt.norm,
le16_to_cpu(smart->wear_leveling_cnt.wear_level.min),
le16_to_cpu(smart->wear_leveling_cnt.wear_level.max),
le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg));
printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n",
smart->e2e_err_cnt.norm,
int48_to_long(smart->e2e_err_cnt.raw));
printf("crc_error_count : %3d%% %"PRIu64"\n",
smart->crc_err_cnt.norm,
int48_to_long(smart->crc_err_cnt.raw));
printf("timed_workload_media_wear : %3d%% %.3f%%\n",
smart->timed_workload_media_wear.norm,
((float)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024);
printf("timed_workload_host_reads : %3d%% %"PRIu64"%%\n",
smart->timed_workload_host_reads.norm,
int48_to_long(smart->timed_workload_host_reads.raw));
printf("timed_workload_timer : %3d%% %"PRIu64" min\n",
smart->timed_workload_timer.norm,
int48_to_long(smart->timed_workload_timer.raw));
printf("thermal_throttle_status : %3d%% %u%%, cnt: %u\n",
smart->thermal_throttle_status.norm,
smart->thermal_throttle_status.thermal_throttle.pct,
smart->thermal_throttle_status.thermal_throttle.count);
printf("retry_buffer_overflow_count : %3d%% %"PRIu64"\n",
smart->retry_buffer_overflow_cnt.norm,
int48_to_long(smart->retry_buffer_overflow_cnt.raw));
printf("pll_lock_loss_count : %3d%% %"PRIu64"\n",
smart->pll_lock_loss_cnt.norm,
int48_to_long(smart->pll_lock_loss_cnt.raw));
printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n",
smart->nand_bytes_written.norm,
int48_to_long(smart->nand_bytes_written.raw));
printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n",
smart->host_bytes_written.norm,
int48_to_long(smart->host_bytes_written.raw));
printf("raid_recover_cnt : %3d%% %"PRIu64"\n",
smart->raid_recover_cnt.norm,
int48_to_long(smart->raid_recover_cnt.raw));
printf("read_ecc_cnt : %3d%% %"PRIu64"\n",
smart->read_ecc_cnt.norm,
int48_to_long(smart->read_ecc_cnt.raw));
printf("prog_timeout_cnt : %3d%% %"PRIu64"\n",
smart->prog_timeout_cnt.norm,
int48_to_long(smart->prog_timeout_cnt.raw));
printf("erase_timeout_cnt : %3d%% %"PRIu64"\n",
smart->erase_timeout_cnt.norm,
int48_to_long(smart->erase_timeout_cnt.raw));
printf("read_timeout_cnt : %3d%% %"PRIu64"\n",
smart->read_timeout_cnt.norm,
int48_to_long(smart->read_timeout_cnt.raw));
}
static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
struct nvme_additional_smart_log smart_log;
int err, fd;
char *desc = "Get ScaleFlux vendor specific additional smart log (optionally, "\
"for the specified namespace), and show it.";
const char *namespace = "(optional) desired namespace";
const char *raw = "dump output in binary format";
const char *json= "Dump output in json format";
struct config {
__u32 namespace_id;
int raw_binary;
int json;
};
struct config cfg = {
.namespace_id = 0xffffffff,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
OPT_FLAG("json", 'j', &cfg.json, json),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, sizeof(smart_log),
(void *)&smart_log);
if (!err) {
if (cfg.json)
show_sfx_smart_log_jsn(&smart_log, cfg.namespace_id, devicename);
else if (!cfg.raw_binary)
show_sfx_smart_log(&smart_log, cfg.namespace_id, devicename);
else
d_raw((unsigned char *)&smart_log, sizeof(smart_log));
}
else if (err > 0)
fprintf(stderr, "NVMe Status:%s(%x)\n",
nvme_status_to_string(err), err);
return err;
}
struct sfx_lat_stats {
__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 */
__u32 bucket_4[1]; /* 1s~2s, specifically 1024ms~2047ms */
__u32 bucket_5[1]; /* 2s~4s, specifically 2048ms~4095ms */
__u32 bucket_6[1]; /* 4s+, specifically 4096ms+ */
};
static void show_lat_stats(struct sfx_lat_stats *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 0-1ms, step is 32us\n");
for (i = 0; i < 32; i++)
printf("Bucket %2d: %u\n", i, stats->bucket_1[i]);
printf("\nGroup 2: Range is 1-32ms, step is 1ms\n");
for (i = 0; i < 31; i++)
printf("Bucket %2d: %u\n", i, stats->bucket_2[i]);
printf("\nGroup 3: Range is 32ms-1s, step is 32ms:\n");
for (i = 0; i < 31; i++)
printf("Bucket %2d: %u\n", i, stats->bucket_3[i]);
printf("\nGroup 4: Range is 1s-2s:\n");
printf("Bucket %2d: %u\n", 0, stats->bucket_4[0]);
printf("\nGroup 5: Range is 2s-4s:\n");
printf("Bucket %2d: %u\n", 0, stats->bucket_5[0]);
printf("\nGroup 6: Range is 4s+:\n");
printf("Bucket %2d: %u\n", 0, stats->bucket_6[0]);
}
static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
struct sfx_lat_stats stats;
int err, fd;
char *desc = "Get ScaleFlux Latency Statistics log and show it.";
const char *raw = "dump output in binary format";
const char *write = "Get write statistics (read default)";
struct config {
int raw_binary;
int write;
};
struct config cfg = {
};
OPT_ARGS(opts) = {
OPT_FLAG("write", 'w', &cfg.write, write),
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
err = nvme_get_log(fd, 0xffffffff, cfg.write ? 0xc3 : 0xc1, false, sizeof(stats), (void *)&stats);
if (!err) {
if (!cfg.raw_binary)
show_lat_stats(&stats, cfg.write);
else
d_raw((unsigned char *)&stats, sizeof(stats));
} else if (err > 0)
fprintf(stderr, "NVMe Status:%s(%x)\n",
nvme_status_to_string(err), err);
return err;
}
int sfx_nvme_get_log(int fd, __u32 nsid, __u8 log_id, __u32 data_len, void *data)
{
struct nvme_admin_cmd cmd = {
.opcode = nvme_admin_get_log_page,
.nsid = nsid,
.addr = (__u64)(uintptr_t) data,
.data_len = data_len,
};
__u32 numd = (data_len >> 2) - 1;
__u16 numdu = numd >> 16, numdl = numd & 0xffff;
cmd.cdw10 = log_id | (numdl << 16);
cmd.cdw11 = numdu;
return nvme_submit_admin_passthru(fd, &cmd);
}
/**
* @brief get bb table through admin_passthru
*
* @param fd
* @param buf
* @param size
*
* @return -1 fail ; 0 success
*/
static int get_bb_table(int fd, __u32 nsid, unsigned char *buf, __u64 size)
{
if (fd < 0 || !buf || size != 256*4096*sizeof(unsigned char)) {
fprintf(stderr, "Invalid Param \r\n");
return EINVAL;
}
return sfx_nvme_get_log(fd, nsid, SFX_LOG_BBT, size, (void *)buf);
}
/**
* @brief display bb table
*
* @param bd_table buffer that contain bb table dumped from drvier
* @param table_size buffer size (BYTES), should at least has 8 bytes for mf_bb_count and grown_bb_count
*/
static void bd_table_show(unsigned char *bd_table, __u64 table_size)
{
__u32 mf_bb_count = 0;
__u32 grown_bb_count = 0;
__u32 total_bb_count = 0;
__u32 remap_mfbb_count = 0;
__u32 remap_gbb_count = 0;
__u64 *bb_elem;
__u64 *elem_end = (__u64 *)(bd_table + table_size);
__u64 i;
/*buf should at least have 8bytes for mf_bb_count & total_bb_count*/
if (!bd_table || table_size < sizeof(__u64))
return;
mf_bb_count = *((__u32 *)bd_table);
grown_bb_count = *((__u32 *)(bd_table + sizeof(__u32)));
total_bb_count = *((__u32 *)(bd_table + 2 * sizeof(__u32)));
remap_mfbb_count = *((__u32 *)(bd_table + 3 * sizeof(__u32)));
remap_gbb_count = *((__u32 *)(bd_table + 4 * sizeof(__u32)));
bb_elem = (__u64 *)(bd_table + 5 * sizeof(__u32));
printf("Bad Block Table \n");
printf("MF_BB_COUNT: %u\n", mf_bb_count);
printf("GROWN_BB_COUNT: %u\n", grown_bb_count);
printf("TOTAL_BB_COUNT: %u\n", total_bb_count);
printf("REMAP_MFBB_COUNT: %u\n", remap_mfbb_count);
printf("REMAP_GBB_COUNT: %u\n", remap_gbb_count);
printf("REMAP_MFBB_TABLE [");
i = 0;
while (bb_elem < elem_end && i < remap_mfbb_count) {
printf(" 0x%llx", *(bb_elem++));
i++;
}
printf(" ]\n");
printf("REMAP_GBB_TABLE [");
i = 0;
while (bb_elem < elem_end && i < remap_gbb_count) {
printf(" 0x%llx",*(bb_elem++));
i++;
}
printf(" ]\n");
}
/**
* @brief "hooks of sfx get-bad-block"
*
* @param argc
* @param argv
* @param cmd
* @param plugin
*
* @return
*/
static int sfx_get_bad_block(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
int fd;
unsigned char *data_buf;
const __u64 buf_size = 256*4096*sizeof(unsigned char);
int err = 0;
char *desc = "Get bad block table of sfx block device.";
OPT_ARGS(opts) = {
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0) {
return fd;
}
data_buf = malloc(buf_size);
if (!data_buf) {
fprintf(stderr, "malloc fail, errno %d\r\n", errno);
return -1;
}
err = get_bb_table(fd, 0xffffffff, data_buf, buf_size);
if (err < 0) {
perror("get-bad-block");
} else if (err != 0) {
fprintf(stderr, "NVMe IO command error:%s(%x)\n",
nvme_status_to_string(err), err);
} else {
bd_table_show(data_buf, buf_size);
printf("ScaleFlux get bad block table: success\n");
}
free(data_buf);
return 0;
}
static void show_cap_info(struct sfx_freespace_ctx *ctx)
{
printf("user sectors: %#llx\n", ctx->user_space);
printf("totl physical sectors: %#llx\n", ctx->phy_space);
printf("free physical sectors: %#llx\n", ctx->free_space);
printf("used physical sectors: %#llx\n", ctx->phy_space - ctx->free_space);
}
static int query_cap_info(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
struct sfx_freespace_ctx ctx = { 0 };
int err = 0, 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";
struct config {
int raw_binary;
int 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()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0) {
return fd;
}
if (ioctl(fd, SFX_GET_FREESPACE, &ctx)) {
fprintf(stderr, "vu ioctl fail, errno %d\r\n", errno);
return -1;
}
show_cap_info(&ctx);
return err;
}
static int change_cap_mem_check(int fd, __u64 trg_in_4k)
{
struct sfx_freespace_ctx freespace_ctx = { 0 };
struct sysinfo s_info;
__u64 mem_need = 0;
__u64 cur_in_4k = 0;
__u32 cnt_ms = 0;
while (ioctl(fd, SFX_GET_FREESPACE, &freespace_ctx)) {
if (cnt_ms++ > 600) {//1min
fprintf(stderr, "vu ioctl fail, errno %d\r\n", errno);
return -1;
}
usleep(100000);
}
cur_in_4k = freespace_ctx.user_space >> (SFX_PAGE_SHIFT - SECTOR_SHIFT);
if (cur_in_4k > trg_in_4k) {
return 0;
}
if (sysinfo(&s_info) < 0) {
printf("change-cap query mem info fail\n");
return -1;
}
mem_need = (trg_in_4k - cur_in_4k) * 8;
if (s_info.freeram <= 10 || mem_need > s_info.freeram) {
fprintf(stderr, "WARNING: mem needed is %llu, free mem is %lu\n"
"Insufficient memory, please drop cache or add free memory and retry\n",
mem_need, s_info.freeram);
return -1;
}
return 0;
}
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";
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";
__u64 cap_in_4k = 0;
__u64 cap_in_sec = 0;
struct config {
__u64 cap_in_byte;
__u32 capacity_in_gb;
int raw_binary;
int json;
int force;
};
struct config cfg = {
.cap_in_byte = 0,
.capacity_in_gb = 0,
.force = 0,
};
OPT_ARGS(opts) = {
OPT_UINT("cap", 'c', &cfg.capacity_in_gb, cap_gb),
OPT_UINT("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()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0) {
return fd;
}
if (!cfg.force) {
fprintf(stderr, "WARNING: Changing capacity may irrevocably delete user data.\n"
"You have 10 seconds to press Ctrl-C to cancel this operation.\n\n"
"Use the force [--force|-f] option to suppress this warning.\n");
sleep(10);
fprintf(stderr, "Sending operation ... \n");
}
cap_in_sec = IDEMA_CAP(cfg.capacity_in_gb);
cap_in_4k = cap_in_sec >> 3;
if (cfg.cap_in_byte)
cap_in_4k = cfg.cap_in_byte >> 12;
printf("%dG %lluB %llu 4K\n",
cfg.capacity_in_gb, cfg.cap_in_byte, cap_in_4k);
if (change_cap_mem_check(fd, cap_in_4k))
return err;
err = nvme_change_cap(fd, 0xffffffff, cap_in_4k);
if (err < 0)
perror("sfx-change-cap");
else if (err != 0)
fprintf(stderr, "NVMe IO command error:%s(%x)\n",
nvme_status_to_string(err), err);
else {
printf("ScaleFlux change-capacity: success\n");
if(ioctl(fd, BLKRRPART) < 0) {
fprintf(stderr, "failed to re-read partition table\n");
err = EFAULT;
}
}
return err;
}
char *sfx_feature_to_string(int feature)
{
switch (feature) {
case SFX_FEAT_ATOMIC: return "ATOMIC";
default: return "Unknown";
}
}
static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
int err = 0, fd;
char *desc = "ScaleFlux internal set features\n"
"feature id 1: ATOMIC";
const char *value = "new value of feature (required)";
const char *feature_id = "hex feature name (required)";
const char *namespace_id = "desired namespace";
struct nvme_id_ns ns;
struct config {
__u32 namespace_id;
__u32 feature_id;
__u32 value;
};
struct config cfg = {
.namespace_id = 1,
.feature_id = 0,
.value = 0,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id),
OPT_UINT("value", 'v', &cfg.value, value),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0) {
return fd;
}
if (!cfg.feature_id) {
fprintf(stderr, "feature-id required param\n");
return EINVAL;
}
if (cfg.feature_id == SFX_FEAT_ATOMIC) {
if (cfg.namespace_id != 0xffffffff) {
err = nvme_identify_ns(fd, cfg.namespace_id, 0, &ns);
if (err) {
if (err < 0)
perror("identify-namespace");
else
fprintf(stderr,
"NVMe Admin command error:%s(%x)\n",
nvme_status_to_string(err), err);
return err;
}
/*
* atomic only support with sector-size = 4k now
*/
if ((ns.flbas & 0xf) != 1) {
printf("Please change-sector size to 4K, then retry\n");
return EFAULT;
}
}
}
err = nvme_sfx_set_features(fd, cfg.namespace_id, cfg.feature_id, cfg.value);
if (err < 0) {
perror("ScaleFlux-set-feature");
return errno;
} else if (!err) {
printf("ScaleFlux set-feature:%02x (%s), value:%#08x\n", cfg.feature_id,
sfx_feature_to_string(cfg.feature_id), cfg.value);
} else if (err > 0)
fprintf(stderr, "NVMe Status:%s(%x)\n",
nvme_status_to_string(err), err);
return err;
}
static int sfx_get_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
int err = 0, fd;
char *desc = "ScaleFlux internal set features\n"
"feature id 1: ATOMIC";
const char *feature_id = "hex feature name (required)";
const char *namespace_id = "desired namespace";
__u32 result = 0;
struct config {
__u32 namespace_id;
__u32 feature_id;
};
struct config cfg = {
.namespace_id = 0,
.feature_id = 0,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0) {
return fd;
}
if (!cfg.feature_id) {
fprintf(stderr, "feature-id required param\n");
return EINVAL;
}
err = nvme_sfx_get_features(fd, cfg.namespace_id, cfg.feature_id, &result);
if (err < 0) {
perror("ScaleFlux-get-feature");
return errno;
} else if (!err) {
printf("ScaleFlux get-feature:%02x (%s), value:%d\n", cfg.feature_id,
sfx_feature_to_string(cfg.feature_id), result);
} else if (err > 0)
fprintf(stderr, "NVMe Status:%s(%x)\n",
nvme_status_to_string(err), err);
return err;
}

View file

@ -0,0 +1,25 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/scaleflux/sfx-nvme
#if !defined(SFX_NVME) || defined(CMD_HEADER_MULTI_READ)
#define SFX_NVME
#include "cmd.h"
PLUGIN(NAME("sfx", "ScaleFlux vendor specific extensions"),
COMMAND_LIST(
ENTRY("smart-log-add", "Retrieve ScaleFlux SMART Log, show it", get_additional_smart_log)
ENTRY("lat-stats", "Retrieve ScaleFlux IO Latency Statistics log, show it", get_lat_stats_log)
ENTRY("get-bad-block", "Retrieve bad block table of block device, show it", sfx_get_bad_block)
ENTRY("query-cap", "Query current capacity info", query_cap_info)
ENTRY("change-cap", "Dynamic change capacity", change_cap)
ENTRY("set-feature", "Set a feature", sfx_set_feature)
ENTRY("get-feature", "get a feature", sfx_get_feature)
)
);
#endif
#include "define_cmd.h"

View file

@ -0,0 +1,269 @@
/*
* Do NOT modify or remove this copyright and license
*
* Copyright (c) 2017-2018 Seagate Technology LLC and/or its Affiliates, All Rights Reserved
*
* ******************************************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* \file seagate-diag.h
* \brief This file defines the functions and macros to make building a nvme-cli seagate plug-in.
*/
#ifndef SEAGATE_NVME_H
#define SEAGATE_NVME_H
#define SEAGATE_PLUGIN_VERSION_MAJOR 1
#define SEAGATE_PLUGIN_VERSION_MINOR 1
#define PERSIST_FILE_SIZE (2764800)
#define ONE_MB (1048576) /* (1024 * 1024) */
#define PERSIST_CHUNK (65536) /* (1024 * 64) */
#define FOUR_KB (4096)
/***************************
*Supported Log-Pages from FW
***************************/
typedef struct log_page_map_entry {
__u32 LogPageID;
__u32 LogPageSignature;
__u32 LogPageVersion;
} log_page_map_entry;
#define MAX_SUPPORTED_LOG_PAGE_ENTRIES ((4096 - sizeof(__u32)) / sizeof(log_page_map_entry))
typedef struct log_page_map {
__u32 NumLogPages;
log_page_map_entry LogPageEntry[ MAX_SUPPORTED_LOG_PAGE_ENTRIES ];
} log_page_map;
/* EOF Supported Log-Pages from FW */
/***************************
* Extended-SMART Information
***************************/
#pragma pack(1)
#define NUMBER_EXTENDED_SMART_ATTRIBUTES 42
typedef enum _EXTENDED_SMART_VERSION_
{
EXTENDED_SMART_VERSION_NONE,
EXTENDED_SMART_VERSION_GEN,
EXTENDED_SMART_VERSION_VENDOR1,
} EXTENDED_SMART_VERSION;
typedef struct _SmartVendorSpecific
{
__u8 AttributeNumber;
__u16 SmartStatus;
__u8 NominalValue;
__u8 LifetimeWorstValue;
__u32 Raw0_3;
__u8 RawHigh[3];
} SmartVendorSpecific;
typedef struct _EXTENDED_SMART_INFO_T
{
__u16 Version;
SmartVendorSpecific vendorData[NUMBER_EXTENDED_SMART_ATTRIBUTES];
__u8 vendor_specific_reserved[6];
} EXTENDED_SMART_INFO_T;
typedef struct vendor_smart_attribute_data
{
__u8 AttributeNumber; /* 00 */
__u8 Rsvd[3]; /* 01 -03 */
__u32 LSDword; /* 04-07 */
__u32 MSDword; /* 08 - 11 */
} vendor_smart_attribute_data;
struct nvme_temetry_log_hdr
{
__u8 log_id;
__u8 rsvd1[4];
__u8 ieee_id[3];
__le16 tele_data_area1;
__le16 tele_data_area2;
__le16 tele_data_area3;
__u8 rsvd14[368];
__u8 tele_data_aval;
__u8 tele_data_gen_num;
__u8 reason_identifier[128];
};
typedef struct _U128
{
__u64 LS__u64;
__u64 MS__u64;
} U128;
typedef struct _vendor_log_page_CF_Attr
{
__u16 SuperCapCurrentTemperature; /* 00-01 */
__u16 SuperCapMaximumTemperature; /* 02-03 */
__u8 SuperCapStatus; /* 04 */
__u8 Reserved5to7[3]; /* 05-07 */
U128 DataUnitsReadToDramNamespace; /* 08-23 */
U128 DataUnitsWrittenToDramNamespace; /* 24-39 */
__u64 DramCorrectableErrorCount; /* 40-47 */
__u64 DramUncorrectableErrorCount; /* 48-55 */
}vendor_log_page_CF_Attr;
typedef struct _vendor_log_page_CF
{
vendor_log_page_CF_Attr AttrCF;
__u8 Vendor_Specific_Reserved[ 456 ]; /* 56-511 */
}vendor_log_page_CF;
#pragma pack()
/* EOF Extended-SMART Information*/
/**************************
* PCIE ERROR INFORMATION
**************************/
typedef struct pcie_error_log_page
{
__u32 Version;
__u32 BadDllpErrCnt;
__u32 BadTlpErrCnt;
__u32 RcvrErrCnt;
__u32 ReplayTOErrCnt;
__u32 ReplayNumRolloverErrCnt;
__u32 FCProtocolErrCnt;
__u32 DllpProtocolErrCnt;
__u32 CmpltnTOErrCnt;
__u32 RcvrQOverflowErrCnt;
__u32 UnexpectedCplTlpErrCnt;
__u32 CplTlpURErrCnt;
__u32 CplTlpCAErrCnt;
__u32 ReqCAErrCnt;
__u32 ReqURErrCnt;
__u32 EcrcErrCnt;
__u32 MalformedTlpErrCnt;
__u32 CplTlpPoisonedErrCnt;
__u32 MemRdTlpPoisonedErrCnt;
} pcie_error_log_page;
/*EOF PCIE ERROR INFORMATION */
typedef enum
{
VS_ATTR_SOFT_READ_ERROR_RATE, /* 0 OFFSET : 02 -13 bytes */
VS_ATTR_REALLOCATED_SECTOR_COUNT, /* 1 OFFSET : 14 -25 bytes */
VS_ATTR_POWER_ON_HOURS, /* 2 OFFSET : 26 -37 bytes */
VS_ATTR_POWER_FAIL_EVENT_COUNT, /* 3 OFFSET : 38 -49 bytes */
VS_ATTR_DEVICE_POWER_CYCLE_COUNT, /* 4 OFFSET : 50 -61 bytes */
VS_ATTR_GB_ERASED, /* 5 OFFSET : 62 -73 bytes */
VS_ATTR_LIFETIME_DEVSLEEP_EXIT_COUNT, /* 6 OFFSET : 74 -85 bytes */
VS_ATTR_LIFETIME_ENTERING_PS4_COUNT, /* 7 OFFSET : 86 -97 bytes */
VS_ATTR_LIFETIME_ENTERING_PS3_COUNT, /* 8 OFFSET : 98 -109 bytes */
VS_ATTR_RETIRED_BLOCK_COUNT, /* 9 OFFSET : 110 -121 bytes */
VS_ATTR_PROGRAM_FAILURE_COUNT, /* 10 OFFSET : 122 -133 bytes */
VS_ATTR_ERASE_FAIL_COUNT, /* 11 OFFSET : 134 -145 bytes */
VS_ATTR_AVG_ERASE_COUNT, /* 12 OFFSET : 146 -157 bytes */
VS_ATTR_UNEXPECTED_POWER_LOSS_COUNT, /* 13 OFFSET : 158 -169 bytes */
VS_ATTR_WEAR_RANGE_DELTA, /* 14 OFFSET : 170 -181 bytes */
VS_ATTR_SATA_INTERFACE_DOWNSHIFT_COUNT, /* 15 OFFSET : 182 -193 bytes */
VS_ATTR_END_TO_END_CRC_ERROR_COUNT, /* 16 OFFSET : 194 -205 bytes */
VS_ATTR_MAX_LIFE_TEMPERATURE, /* 17 OFFSET : 206 -217 bytes */
VS_ATTR_UNCORRECTABLE_RAISE_ERRORS, /* 18 OFFSET : 218 -229 bytes */
VS_ATTR_DRIVE_LIFE_PROTECTION_STATUS, /* 19 OFFSET : 230 -241 bytes */
VS_ATTR_REMAINING_SSD_LIFE, /* 20 OFFSET : 242 -253 bytes */
VS_ATTR_LIFETIME_WRITES_TO_FLASH, /* 21 OFFSET : 254 -265 bytes */
VS_ATTR_LIFETIME_WRITES_FROM_HOST, /* 22 OFFSET : 266 -277 bytes */
VS_ATTR_LIFETIME_READS_TO_HOST, /* 23 OFFSET : 278 -289 bytes */
VS_ATTR_FREE_SPACE, /* 24 OFFSET : 290 -301 bytes */
VS_ATTR_TRIM_COUNT_LSB, /* 25 OFFSET : 302 -313 bytes */
VS_ATTR_TRIM_COUNT_MSB, /* 26 OFFSET : 314 -325 bytes */
VS_ATTR_OP_PERCENTAGE, /* 27 OFFSET : 326 -337 bytes */
VS_ATTR_RAISE_ECC_CORRECTABLE_ERROR_COUNT, /* 28 OFFSET : 338 -349 bytes */
VS_ATTR_UNCORRECTABLE_ECC_ERRORS , /* 29 OFFSET : 350 -361 bytes */
VS_ATTR_LIFETIME_WRITES0_TO_FLASH, /* 30 362-372 */
VS_ATTR_LIFETIME_WRITES1_TO_FLASH, /* 31 374-385 */
VS_ATTR_LIFETIME_WRITES0_FROM_HOST, /* 32 386-397 */
VS_ATTR_LIFETIME_WRITES1_FROM_HOST, /* 33 398-409 */
VS_ATTR_LIFETIME_READ0_FROM_HOST, /* 34 410-421 */
VS_ATTR_LIFETIME_READ1_FROM_HOST, /* 35 422-433 */
VS_ATTR_PCIE_PHY_CRC_ERROR, /* 36 434-445 */
VS_ATTR_BAD_BLOCK_COUNT_SYSTEM, /* 37 446-457 */
VS_ATTR_BAD_BLOCK_COUNT_USER, /* 38 458-469 */
VS_ATTR_THERMAL_THROTTLING_STATUS, /* 39 470-481 */
VS_ATTR_POWER_CONSUMPTION, /* 40 482-493 */
VS_ATTR_MAX_SOC_LIFE_TEMPERATURE, /* 41 494-505 */
VS_MAX_ATTR_NUMBER,
} extended_smart_attributes;
/*Smart attribute IDs */
typedef enum
{
VS_ATTR_ID_SOFT_READ_ERROR_RATE = 1,
VS_ATTR_ID_REALLOCATED_SECTOR_COUNT = 5,
VS_ATTR_ID_POWER_ON_HOURS = 9,
VS_ATTR_ID_POWER_FAIL_EVENT_COUNT = 11,
VS_ATTR_ID_DEVICE_POWER_CYCLE_COUNT = 12,
VS_ATTR_ID_RAW_READ_ERROR_RATE = 13,
VS_ATTR_ID_GROWN_BAD_BLOCK_COUNT = 40,
VS_ATTR_ID_END_2_END_CORRECTION_COUNT = 41,
VS_ATTR_ID_MIN_MAX_WEAR_RANGE_COUNT = 42,
VS_ATTR_ID_REFRESH_COUNT = 43,
VS_ATTR_ID_BAD_BLOCK_COUNT_USER = 44,
VS_ATTR_ID_BAD_BLOCK_COUNT_SYSTEM = 45,
VS_ATTR_ID_THERMAL_THROTTLING_STATUS = 46,
VS_ATTR_ID_ALL_PCIE_CORRECTABLE_ERROR_COUNT = 47,
VS_ATTR_ID_ALL_PCIE_UNCORRECTABLE_ERROR_COUNT = 48,
VS_ATTR_ID_INCOMPLETE_SHUTDOWN_COUNT = 49,
VS_ATTR_ID_GB_ERASED_LSB = 100,
VS_ATTR_ID_GB_ERASED_MSB = 101,
VS_ATTR_ID_LIFETIME_ENTERING_PS4_COUNT = 102,
VS_ATTR_ID_LIFETIME_ENTERING_PS3_COUNT = 103,
VS_ATTR_ID_LIFETIME_DEVSLEEP_EXIT_COUNT = 104,
VS_ATTR_ID_RETIRED_BLOCK_COUNT = 170,
VS_ATTR_ID_PROGRAM_FAILURE_COUNT = 171,
VS_ATTR_ID_ERASE_FAIL_COUNT = 172,
VS_ATTR_ID_AVG_ERASE_COUNT = 173,
VS_ATTR_ID_UNEXPECTED_POWER_LOSS_COUNT = 174,
VS_ATTR_ID_WEAR_RANGE_DELTA = 177,
VS_ATTR_ID_SATA_INTERFACE_DOWNSHIFT_COUNT = 183,
VS_ATTR_ID_END_TO_END_CRC_ERROR_COUNT = 184,
VS_ATTR_ID_UNCORRECTABLE_READ_ERRORS = 188,
VS_ATTR_ID_MAX_LIFE_TEMPERATURE = 194,
VS_ATTR_ID_RAISE_ECC_CORRECTABLE_ERROR_COUNT = 195,
VS_ATTR_ID_UNCORRECTABLE_RAISE_ERRORS = 198,
VS_ATTR_ID_DRIVE_LIFE_PROTECTION_STATUS = 230,
VS_ATTR_ID_REMAINING_SSD_LIFE = 231,
VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB = 233,
VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_MSB = 234,
VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB = 241,
VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_MSB = 242,
VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB = 243,
VS_ATTR_ID_LIFETIME_READS_TO_HOST_MSB = 244,
VS_ATTR_ID_FREE_SPACE = 245,
VS_ATTR_ID_TRIM_COUNT_LSB = 250,
VS_ATTR_ID_TRIM_COUNT_MSB = 251,
VS_ATTR_ID_OP_PERCENTAGE = 252,
VS_ATTR_ID_MAX_SOC_LIFE_TEMPERATURE = 253,
} smart_attributes_ids;
#define TELEMETRY_BLOCKS_TO_READ 8
void seaget_d_raw(unsigned char *buf, int len, int fd);
#define DP_CLASS_ID_FULL 0
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
/*
* Do NOT modify or remove this copyright and license
*
* Copyright (c) 2017-2018 Seagate Technology LLC and/or its Affiliates, All Rights Reserved
*
* ******************************************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* \file seagate-nvme.h
* \brief This file defines the functions and macros to make building a nvme-cli seagate plug-in.
*/
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/seagate/seagate-nvme
#if !defined(SEAGATE_NVME) || defined(CMD_HEADER_MULTI_READ)
#define SEAGATE_NVME
#include "cmd.h"
PLUGIN(NAME("seagate", "Seagate vendor specific extensions"),
COMMAND_LIST(
ENTRY("vs-temperature-stats", "Retrieve Seagate temperature statistics ", temp_stats)
ENTRY("vs-log-page-sup", "Retrieve Seagate Supported Log-pages Information ", log_pages_supp)
ENTRY("vs-smart-add-log", "Retrieve Seagate extended-SMART Information ", vs_smart_log)
ENTRY("vs-pcie-stats", "Retrieve Seagate PCIe error statistics ", vs_pcie_error_log)
ENTRY("clear-pcie-correctable-errors", "Clear Seagate PCIe error statistics ", vs_clr_pcie_correctable_errs)
ENTRY("get-host-tele", "Retrieve Seagate Host-Initiated Telemetry ", get_host_tele)
ENTRY("get-ctrl-tele", "Retrieve Seagate Controller-Initiated Telemetry ", get_ctrl_tele)
ENTRY("vs-internal-log", "Retrieve Seagate Controller-Initiated Telemetry in binary format", vs_internal_log)
ENTRY("plugin-version", "Shows Seagate plugin's version information ", seagate_plugin_version)
)
);
#endif
#include "define_cmd.h"

View file

@ -0,0 +1,374 @@
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include "linux/nvme_ioctl.h"
#include "common.h"
#include "nvme.h"
#include "nvme-print.h"
#include "nvme-ioctl.h"
#include "json.h"
#include "plugin.h"
#include "argconfig.h"
#include "suffix.h"
#define CREATE_CMD
#include "shannon-nvme.h"
typedef enum {
PROGRAM_FAIL_CNT,
ERASE_FAIL_CNT,
WEARLEVELING_COUNT,
E2E_ERR_CNT,
CRC_ERR_CNT,
TIME_WORKLOAD_MEDIA_WEAR,
TIME_WORKLOAD_HOST_READS,
TIME_WORKLOAD_TIMER,
THERMAL_THROTTLE,
RETRY_BUFFER_OVERFLOW,
PLL_LOCK_LOSS,
NAND_WRITE,
HOST_WRITE,
SRAM_ERROR_CNT,
ADD_SMART_ITEMS,
}addtional_smart_items;
#pragma pack(push,1)
struct nvme_shannon_smart_log_item {
__u8 rsv1[3];
__u8 norm;
__u8 rsv2;
union {
__u8 item_val[6];
struct wear_level {
__le16 min;
__le16 max;
__le16 avg;
} wear_level ;
struct thermal_throttle {
__u8 st;
__u32 count;
} thermal_throttle;
};
__u8 _resv;
};
#pragma pack(pop)
struct nvme_shannon_smart_log {
struct nvme_shannon_smart_log_item items[ADD_SMART_ITEMS];
__u8 vend_spec_resv;
};
static void show_shannon_smart_log(struct nvme_shannon_smart_log *smart,
unsigned int nsid, const char *devname)
{
printf("Additional Smart Log for NVME device:%s namespace-id:%x\n",
devname, nsid);
printf("key normalized value\n");
printf("program_fail_count : %3d%% %"PRIu64"\n",
smart->items[PROGRAM_FAIL_CNT].norm,
int48_to_long(smart->items[PROGRAM_FAIL_CNT].item_val));
printf("erase_fail_count : %3d%% %"PRIu64"\n",
smart->items[ERASE_FAIL_CNT].norm,
int48_to_long(smart->items[ERASE_FAIL_CNT].item_val));
printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n",
smart->items[WEARLEVELING_COUNT].norm,
le16_to_cpu(smart->items[WEARLEVELING_COUNT].wear_level.min),
le16_to_cpu(smart->items[WEARLEVELING_COUNT].wear_level.max),
le16_to_cpu(smart->items[WEARLEVELING_COUNT].wear_level.avg));
printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n",
smart->items[E2E_ERR_CNT].norm,
int48_to_long(smart->items[E2E_ERR_CNT].item_val));
printf("crc_error_count : %3d%% %"PRIu64"\n",
smart->items[CRC_ERR_CNT].norm,
int48_to_long(smart->items[CRC_ERR_CNT].item_val));
printf("timed_workload_media_wear : %3d%% %.3f%%\n",
smart->items[TIME_WORKLOAD_MEDIA_WEAR].norm,
((float)int48_to_long(smart->items[TIME_WORKLOAD_MEDIA_WEAR].item_val)) / 1024);
printf("timed_workload_host_reads : %3d%% %"PRIu64"%%\n",
smart->items[TIME_WORKLOAD_HOST_READS].norm,
int48_to_long(smart->items[TIME_WORKLOAD_HOST_READS].item_val));
printf("timed_workload_timer : %3d%% %"PRIu64" min\n",
smart->items[TIME_WORKLOAD_TIMER].norm,
int48_to_long(smart->items[TIME_WORKLOAD_TIMER].item_val));
printf("thermal_throttle_status : %3d%% CurTTSta: %u%%, TTCnt: %u\n",
smart->items[THERMAL_THROTTLE].norm,
smart->items[THERMAL_THROTTLE].thermal_throttle.st,
smart->items[THERMAL_THROTTLE].thermal_throttle.count);
printf("retry_buffer_overflow_count : %3d%% %"PRIu64"\n",
smart->items[RETRY_BUFFER_OVERFLOW].norm,
int48_to_long(smart->items[RETRY_BUFFER_OVERFLOW].item_val));
printf("pll_lock_loss_count : %3d%% %"PRIu64"\n",
smart->items[PLL_LOCK_LOSS].norm,
int48_to_long(smart->items[PLL_LOCK_LOSS].item_val));
printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n",
smart->items[NAND_WRITE].norm,
int48_to_long(smart->items[NAND_WRITE].item_val));
printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n",
smart->items[HOST_WRITE].norm,
int48_to_long(smart->items[HOST_WRITE].item_val));
printf("sram_error_count : %3d%% %"PRIu64"\n",
smart->items[RETRY_BUFFER_OVERFLOW].norm,
int48_to_long(smart->items[SRAM_ERROR_CNT].item_val));
}
static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
struct nvme_shannon_smart_log smart_log;
int err, fd;
char *desc = "Get Shannon vendor specific additional smart log (optionally, "\
"for the specified namespace), and show it.";
const char *namespace = "(optional) desired namespace";
const char *raw = "dump output in binary format";
struct config {
__u32 namespace_id;
int raw_binary;
};
struct config cfg = {
.namespace_id = NVME_NSID_ALL,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
err = nvme_get_log(fd, cfg.namespace_id, 0xca, false,
sizeof(smart_log), &smart_log);
if (!err) {
if (!cfg.raw_binary)
show_shannon_smart_log(&smart_log, cfg.namespace_id, devicename);
else
d_raw((unsigned char *)&smart_log, sizeof(smart_log));
}
else if (err > 0)
fprintf(stderr, "NVMe Status:%s(%x)\n",
nvme_status_to_string(err), err);
return err;
}
static int get_additional_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Read operating parameters of the "\
"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 "\
"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 "\
"change saveable Features.\n\n"\
"Available additional feature id:\n"\
"0x02: Shannon power management\n";
const char *raw = "show infos in binary format";
const char *namespace_id = "identifier of desired namespace";
const char *feature_id = "hexadecimal feature name";
const char *sel = "[0-3]: curr./default/saved/supp.";
const char *data_len = "buffer len (if) data is returned";
const char *cdw11 = "dword 11 for interrupt vector config";
const char *human_readable = "show infos in readable format";
int err, fd;
__u32 result;
void *buf = NULL;
struct config {
__u32 namespace_id;
__u32 feature_id;
__u8 sel;
__u32 cdw11;
__u32 data_len;
int raw_binary;
int human_readable;
};
struct config cfg = {
.namespace_id = 1,
.feature_id = 0,
.sel = 0,
.cdw11 = 0,
.data_len = 0,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id),
OPT_BYTE("sel", 's', &cfg.sel, sel),
OPT_UINT("data-len", 'l', &cfg.data_len, data_len),
OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11),
OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0)
return fd;
if (cfg.sel > 7) {
fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel);
close(fd);
return EINVAL;
}
if (!cfg.feature_id) {
fprintf(stderr, "feature-id required param\n");
close(fd);
return EINVAL;
}
if (cfg.data_len) {
if (posix_memalign(&buf, getpagesize(), cfg.data_len))
{
close(fd);
exit(ENOMEM);
}
memset(buf, 0, cfg.data_len);
}
err = nvme_get_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.sel, cfg.cdw11,
cfg.data_len, buf, &result);
if (!err) {
printf("get-feature:0x%02x (%s), %s value: %#08x\n", cfg.feature_id,
nvme_feature_to_string(cfg.feature_id),
nvme_select_to_string(cfg.sel), result);
if (cfg.human_readable)
nvme_feature_show_fields(cfg.feature_id, result, buf);
else {
if (buf) {
if (!cfg.raw_binary)
d(buf, cfg.data_len, 16, 1);
else
d_raw(buf, cfg.data_len);
}
}
} else if (err > 0)
fprintf(stderr, "NVMe Status:%s(%x)\n",
nvme_status_to_string(err), err);
if (buf)
free(buf);
return err;
}
static int set_additional_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Modify the saveable or changeable "\
"current operating parameters of the controller. Operating "\
"parameters are grouped and identified by Feature "\
"Identifiers. Feature settings can be applied to the entire "\
"controller and all associated namespaces, or to only a few "\
"namespace(s) associated with the controller. Default values "\
"for each Feature are vendor-specific and may not be modified."\
"Use get-feature to determine which Features are supported by "\
"the controller and are saveable/changeable.\n\n"\
"Available additional feature id:\n"\
"0x02: Shannon power management\n";
const char *namespace_id = "desired namespace";
const char *feature_id = "hex feature name (required)";
const char *data_len = "buffer length if data required";
const char *data = "optional file for feature data (default stdin)";
const char *value = "new value of feature (required)";
const char *save = "specifies that the controller shall save the attribute";
int err, fd;
__u32 result;
void *buf = NULL;
int ffd = STDIN_FILENO;
struct config {
char *file;
__u32 namespace_id;
__u32 feature_id;
__u32 value;
__u32 data_len;
int save;
};
struct config cfg = {
.file = "",
.namespace_id = 0,
.feature_id = 0,
.value = 0,
.data_len = 0,
.save = 0,
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id),
OPT_UINT("value", 'v', &cfg.value, value),
OPT_UINT("data-len", 'l', &cfg.data_len, data_len),
OPT_FILE("data", 'd', &cfg.file, data),
OPT_FLAG("save", 's', &cfg.save, save),
OPT_END()
};
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;
}
if (cfg.data_len) {
if (posix_memalign(&buf, getpagesize(), cfg.data_len)){
fprintf(stderr, "can not allocate feature payload\n");
close(fd);
return ENOMEM;
}
memset(buf, 0, cfg.data_len);
}
if (buf) {
if (strlen(cfg.file)) {
ffd = open(cfg.file, O_RDONLY);
if (ffd <= 0) {
fprintf(stderr, "no firmware file provided\n");
err = EINVAL;
goto free;
}
}
err = read(ffd, (void *)buf, cfg.data_len);
if (err < 0) {
fprintf(stderr, "failed to read data buffer from input file\n");
err = EINVAL;
goto free;
}
}
err = nvme_set_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.value,
0, cfg.save, cfg.data_len, buf, &result);
if (err < 0) {
perror("set-feature");
goto free;
}
if (!err) {
printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id,
nvme_feature_to_string(cfg.feature_id), cfg.value);
if (buf)
d(buf, cfg.data_len, 16, 1);
} else if (err > 0)
fprintf(stderr, "NVMe Status:%s(%x)\n",
nvme_status_to_string(err), err);
free:
if (buf)
free(buf);
return err;
}
static int shannon_id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
return __id_ctrl(argc, argv, cmd, plugin, NULL);
}

View file

@ -0,0 +1,20 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/shannon/shannon-nvme
#if !defined(SHANNON_NVME) || defined(CMD_HEADER_MULTI_READ)
#define SHANNON_NVME
#include "cmd.h"
PLUGIN(NAME("shannon", "Shannon vendor specific extensions"),
COMMAND_LIST(
ENTRY("smart-log-add", "Retrieve Shannon SMART Log, show it", get_additional_smart_log)
ENTRY("get-feature-add", "Get Shannon feature and show the resulting value", get_additional_feature)
ENTRY("set-feature-add", "Set a Shannon feature and show the resulting value", set_additional_feature)
ENTRY("id-ctrl", "Shannon NVMe Identify Controller", shannon_id_ctrl)
)
);
#endif
#include "define_cmd.h"

View file

@ -0,0 +1,563 @@
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <inttypes.h>
#include <stdbool.h>
#include "linux/nvme_ioctl.h"
#include "nvme.h"
#include "nvme-print.h"
#include "nvme-ioctl.h"
#include "plugin.h"
#include "argconfig.h"
#include "suffix.h"
#define CREATE_CMD
#include "toshiba-nvme.h"
static const __u32 OP_SCT_STATUS = 0xE0;
static const __u32 OP_SCT_COMMAND_TRANSFER = 0xE0;
static const __u32 OP_SCT_DATA_TRANSFER = 0xE1;
static const __u32 DW10_SCT_STATUS_COMMAND = 0x0;
static const __u32 DW10_SCT_COMMAND_TRANSFER = 0x1;
static const __u32 DW11_SCT_STATUS_COMMAND = 0x0;
static const __u32 DW11_SCT_COMMAND_TRANSFER = 0x0;
static const __u16 INTERNAL_LOG_ACTION_CODE = 0xFFFB;
static const __u16 CURRENT_LOG_FUNCTION_CODE = 0x0001;
static const __u16 SAVED_LOG_FUNCTION_CODE = 0x0002;
/* A bitmask field for supported devices */
typedef enum {
MASK_0 = 1 << 0,
MASK_1 = 1 << 1,
/*
* Future devices can use the remaining 31 bits from this field
* and should use 1 << 2, 1 << 3, etc.
*/
MASK_IGNORE = 0
} DeviceMask;
/* Internal device codes */
typedef enum {
CODE_0 = 0x0D,
CODE_1 = 0x10
} DeviceCode;
static int nvme_sct_op(int fd, __u32 opcode, __u32 cdw10, __u32 cdw11, void* data, __u32 data_len )
{
void *metadata = NULL;
const __u32 cdw2 = 0;
const __u32 cdw3 = 0;
const __u32 cdw12 = 0;
const __u32 cdw13 = 0;
const __u32 cdw14 = 0;
const __u32 cdw15 = 0;
const __u32 timeout = 0;
const __u32 metadata_len = 0;
const __u32 namespace_id = 0x0;
const __u32 flags = 0;
const __u32 rsvd = 0;
int err = 0;
__u32 result;
err = nvme_passthru(fd, NVME_IOCTL_ADMIN_CMD, opcode, flags, rsvd,
namespace_id, cdw2, cdw3, cdw10,
cdw11, cdw12, cdw13, cdw14, cdw15,
data_len, data, metadata_len, metadata,
timeout, &result);
return err;
}
static int nvme_get_sct_status(int fd, __u32 device_mask)
{
int err;
void* data = NULL;
size_t data_len = 512;
unsigned char *status;
if (posix_memalign(&data, getpagesize(), data_len))
return ENOMEM;
memset(data, 0, data_len);
err = nvme_sct_op(fd, OP_SCT_STATUS, DW10_SCT_STATUS_COMMAND, DW11_SCT_STATUS_COMMAND, data, data_len);
if (err) {
fprintf(stderr, "%s: SCT status failed :%d\n", __func__, err);
goto end;
}
status = data;
if (status[0] != 1U) {
/* Eek, wrong version in status header */
fprintf(stderr, "%s: unexpected value in SCT status[0]:(%x)\n", __func__, status[0]);
err = -1;
errno = EINVAL;
goto end;
}
/* Check if device is supported */
if (device_mask != MASK_IGNORE) {
__u32 supported = 0;
switch (status[1]) {
case CODE_0:
supported = (device_mask & MASK_0);
break;
case CODE_1:
supported = (device_mask & MASK_1);
break;
default:
break;
};
if (0 == supported) {
fprintf(stderr, "%s: command unsupported on this device: (0x%x)\n",__func__, status[1]);
err = -1;
errno = EINVAL;
goto end;
}
}
end:
if (data)
free(data);
return err;
}
static int nvme_sct_command_transfer_log(int fd, bool current)
{
int err;
void *data = NULL;
size_t data_len = 512;
__u16 function_code, action_code = INTERNAL_LOG_ACTION_CODE;
if (current)
function_code = CURRENT_LOG_FUNCTION_CODE;
else
function_code = SAVED_LOG_FUNCTION_CODE;
if (posix_memalign(&data, getpagesize(), data_len))
return ENOMEM;
memset(data, 0, data_len);
memcpy(data, &action_code, sizeof(action_code));
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);
return err;
}
static int nvme_sct_data_transfer(int fd, void* data, size_t data_len, size_t offset)
{
__u32 dw10, dw11, lba_count = (data_len) / 512;
if (lba_count) {
/*
* the count is a 0-based value, which seems to mean
* that it's actually last lba
*/
--lba_count;
}
dw10 = (offset << 16) | lba_count;
dw11 = (offset >> 16);
return nvme_sct_op(fd, OP_SCT_DATA_TRANSFER, dw10, dw11, data, data_len);
}
static int d_raw_to_fd(const unsigned char *buf, unsigned len, int fd)
{
int written = 0;
int remaining = len;
while (remaining) {
written = write(fd, buf, remaining);
if (written < 0) {
remaining = written;
break;
} else if (written <= remaining) {
remaining -= written;
} else {
/* Unexpected overwrite */
break;
}
}
/* return 0 on success or remaining/error */
return remaining;
}
/* Display progress (incoming 0->1.0) */
static void progress_runner(float progress)
{
const size_t barWidth = 70;
size_t i, pos;
fprintf(stdout, "[");
pos = barWidth * progress;
for (i = 0; i < barWidth; ++i) {
if (i <= pos)
fprintf(stdout, "=");
else
fprintf(stdout, " ");
}
fprintf(stdout, "] %d %%\r",(int)(progress * 100.0));
fflush(stdout);
}
static int nvme_get_internal_log(int fd, const char* const filename, bool current)
{
int err;
int o_fd = -1;
void* page_data = NULL;
const size_t page_sector_len = 32;
const size_t page_data_len = page_sector_len * 512; /* 32 sectors per page */
uint32_t* area1_last_page;
uint32_t* area2_last_page;
uint32_t* area3_last_page;
uint32_t log_sectors = 0;
size_t pages;
/*
* By trial and error it seems that the largest transfer chunk size
* is 128 * 32 = 4k sectors = 2MB
*/
const __u32 max_pages = 128;
size_t i;
unsigned int j;
float progress = 0.0;
err = nvme_sct_command_transfer_log(fd, current);
if (err) {
fprintf(stderr, "%s: SCT command transfer failed\n", __func__);
goto end;
}
if (posix_memalign(&page_data, getpagesize(), max_pages * page_data_len)) {
err = ENOMEM;
goto end;
}
memset(page_data, 0, max_pages * page_data_len);
/* Read the header to get the last log page - offsets 8->11, 12->15, 16->19 */
err = nvme_sct_data_transfer(fd, page_data, page_data_len, 0);
if (err) {
fprintf(stderr, "%s: SCT data transfer failed, page 0\n",__func__);
goto end;
}
area1_last_page = (uint32_t *) (page_data + 8);
area2_last_page = (uint32_t *) (page_data + 12);
area3_last_page = (uint32_t *) (page_data + 16);
/* The number of total log sectors is the maximum + 1; */
if (*area1_last_page > log_sectors)
log_sectors = *area1_last_page;
if (*area2_last_page > log_sectors)
log_sectors = *area2_last_page;
if (*area3_last_page > log_sectors)
log_sectors = *area3_last_page;
++log_sectors;
pages = log_sectors / page_sector_len;
if (filename == NULL) {
fprintf(stdout, "Page: %u of %zu\n", 0u, pages);
d(page_data, page_data_len, 16, 1);
} else {
progress_runner(progress);
o_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (o_fd < 0) {
fprintf(stderr, "%s: couldn't output file %s\n", __func__, filename);
err = EINVAL;
goto end;
}
err = d_raw_to_fd(page_data, page_data_len, o_fd);
if (err) {
fprintf(stderr, "%s: couldn't write all data to output file\n", __func__);
goto end;
}
}
/* Now read the rest */
for (i = 1; i < pages;) {
__u32 pages_chunk = max_pages;
if (pages_chunk + i >= pages)
pages_chunk = pages - i;
err = nvme_sct_data_transfer(fd, page_data,
pages_chunk * page_data_len,
i * page_sector_len);
if (err) {
fprintf(stderr, "%s: SCT data transfer command failed\n", __func__);
goto end;
}
progress = (float) (i) / (float) (pages);
progress_runner(progress);
if (filename == NULL) {
for (j = 0; j < pages_chunk; ++j) {
fprintf(stdout, "Page: %zu of %zu\n", i + j, pages);
d(page_data + (j * page_data_len), page_data_len, 16, 1);
}
} else {
progress_runner(progress);
err = d_raw_to_fd(page_data, pages_chunk * page_data_len, o_fd);
if (err) {
fprintf(stderr, "%s: couldn't write all data to output file\n",
__func__);
goto end;
}
}
i += pages_chunk;
}
progress = 1.0f;
progress_runner(progress);
fprintf(stdout,"\n");
err = nvme_get_sct_status(fd, MASK_IGNORE);
if (err) {
fprintf(stderr, "%s: bad SCT status\n", __func__);
goto end;
}
end:
if (o_fd >= 0) {
close(o_fd);
}
if (page_data) {
free(page_data);
}
return err;
}
static int nvme_get_internal_log_file(int fd, const char* const filename, bool current)
{
int err;
/* Check device supported */
err = nvme_get_sct_status(fd, MASK_0 | MASK_1);
if (!err)
err = nvme_get_internal_log(fd, filename, current);
return err;
}
enum LOG_PAGE_C0 {
ERROR_LOG_C0 = 0,
SMART_HEALTH_LOG_C0,
FIRMWARE_SLOT_INFO_C0,
COMMAND_EFFECTS_C0,
DEVICE_SELF_TEST_C0,
LOG_PAGE_DIRECTORY_C0,
SMART_ATTRIBUTES_C0,
NR_SMART_ITEMS_C0,
};
struct nvme_xdn_smart_log_c0 {
__u8 items[NR_SMART_ITEMS_C0];
__u8 resv[512 - NR_SMART_ITEMS_C0];
};
static void default_show_vendor_log_c0(int fd, __u32 nsid, const char *devname,
struct nvme_xdn_smart_log_c0 *smart)
{
printf("Vendor Log Page Directory 0xC0 for NVME device:%s namespace-id:%x\n",
devname, nsid);
printf("Error Log : %u \n", smart->items[ERROR_LOG_C0]);
printf("SMART Health Log : %u \n", smart->items[SMART_HEALTH_LOG_C0]);
printf("Firmware Slot Info : %u \n", smart->items[FIRMWARE_SLOT_INFO_C0]);
printf("Command Effects : %u \n", smart->items[COMMAND_EFFECTS_C0]);
printf("Device Self Test : %u \n", smart->items[DEVICE_SELF_TEST_C0]);
printf("Log Page Directory : %u \n", smart->items[LOG_PAGE_DIRECTORY_C0]);
printf("SMART Attributes : %u \n", smart->items[SMART_ATTRIBUTES_C0]);
}
static int nvme_get_vendor_log(int fd, __u32 namespace_id, int log_page,
const char* const filename)
{
int err;
void* log = NULL;
size_t log_len = 512;
if (posix_memalign(&log, getpagesize(), log_len)) {
err = ENOMEM;
goto end;
}
/* Check device supported */
err = nvme_get_sct_status(fd, MASK_0 | MASK_1);
if (err) {
goto end;
}
err = nvme_get_log(fd, namespace_id, log_page, false,
log_len, log);
if (err) {
fprintf(stderr, "%s: couldn't get log 0x%x\n", __func__,
log_page);
goto end;
}
if (filename) {
int o_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (o_fd < 0) {
fprintf(stderr, "%s: couldn't output file %s\n",
__func__, filename);
err = EINVAL;
goto end;
}
err = d_raw_to_fd(log, log_len, o_fd);
if (err) {
fprintf(stderr, "%s: couldn't write all data to output file %s\n",
__func__, filename);
/* Attempt following close */
}
if (close(o_fd)) {
err = errno;
goto end;
}
} else {
if (log_page == 0xc0)
default_show_vendor_log_c0(fd, namespace_id, devicename,
(struct nvme_xdn_smart_log_c0 *)log);
else
d(log, log_len,16,1);
}
end:
if (log) {
free(log);
}
return err;
}
static int vendor_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
int err, fd;
char *desc = "Get extended SMART information and show it.";
const char *namespace = "(optional) desired namespace";
const char *output_file = "(optional) binary output filename";
const char *log = "(optional) log ID (0xC0, or 0xCA), default 0xCA";
struct config {
__u32 namespace_id;
const char* output_file;
int log;
};
struct config cfg = {
.namespace_id = 0xffffffff,
.output_file = NULL,
.log = 0xca
};
OPT_ARGS(opts) = {
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
OPT_FILE("output-file", 'o', &cfg.output_file, output_file),
OPT_UINT("log", 'l', &cfg.log, log),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0) {
fprintf(stderr,"%s: failed to parse arguments\n", __func__);
return EINVAL;
}
if ((cfg.log != 0xC0) && (cfg.log != 0xCA)) {
fprintf(stderr, "%s: invalid log page 0x%x - should be 0xC0 or 0xCA\n", __func__, cfg.log);
err = EINVAL;
goto end;
}
err = nvme_get_vendor_log(fd, cfg.namespace_id, cfg.log, cfg.output_file);
if (err)
fprintf(stderr, "%s: couldn't get vendor log 0x%x\n", __func__, cfg.log);
end:
if (err > 0)
fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(err), err);
return err;
}
static int internal_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
int err, fd;
char *desc = "Get internal status log and show it.";
const char *output_file = "(optional) binary output filename";
const char *prev_log = "(optional) use previous log. Otherwise uses current log.";
struct config {
const char* output_file;
bool prev_log;
};
struct config cfg = {
.output_file = NULL,
.prev_log = false
};
OPT_ARGS(opts) = {
OPT_FILE("output-file", 'o', &cfg.output_file, output_file),
OPT_FLAG("prev-log", 'p', &cfg.prev_log, prev_log),
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0) {
fprintf(stderr,"%s: failed to parse arguments\n", __func__);
return EINVAL;
}
if (cfg.prev_log)
printf("Getting previous log\n");
else
printf("Getting current log\n");
err = nvme_get_internal_log_file(fd, cfg.output_file, !cfg.prev_log);
if (err < 0)
fprintf(stderr, "%s: couldn't get fw log \n", __func__);
if (err > 0)
fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__,
nvme_status_to_string(err), err);
return err;
}
static int clear_correctable_errors(int argc, char **argv, struct command *cmd,
struct plugin *plugin)
{
int err, fd;
char *desc = "Clear PCIe correctable error count.";
const __u32 namespace_id = 0xFFFFFFFF;
const __u32 feature_id = 0xCA;
const __u32 value = 1; /* Bit0 - reset clear PCIe correctable count */
const __u32 cdw12 = 0;
const bool save = false;
__u32 result;
OPT_ARGS(opts) = {
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0) {
fprintf(stderr,"%s: failed to parse arguments\n", __func__);
return EINVAL;
}
/* Check device supported */
err = nvme_get_sct_status(fd, MASK_0 | MASK_1);
if (err)
goto end;
err = nvme_set_feature(fd, namespace_id, feature_id, value, cdw12, save,
0, NULL, &result);
if (err)
fprintf(stderr, "%s: couldn't clear PCIe correctable errors \n",
__func__);
end:
if (err > 0)
fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__,
nvme_status_to_string(err), err);
return err;
}

View file

@ -0,0 +1,20 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/toshiba/toshiba-nvme
#if !defined(TOSHIBA_NVME) || defined(CMD_HEADER_MULTI_READ)
#define TOSHIBA_NVME
#include "cmd.h"
#include "plugin.h"
PLUGIN(NAME("toshiba", "Toshiba NVME plugin"),
COMMAND_LIST(
ENTRY("vs-smart-add-log", "Extended SMART information", vendor_log)
ENTRY("vs-internal-log", "Get Internal Log", internal_log)
ENTRY("clear-pcie-correctable-errors", "Clear PCIe correctable error count", clear_correctable_errors)
)
);
#endif
#include "define_cmd.h"

View file

@ -0,0 +1,92 @@
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include "linux/nvme_ioctl.h"
#include "nvme.h"
#include "nvme-print.h"
#include "nvme-ioctl.h"
#include "plugin.h"
#include "argconfig.h"
#include "suffix.h"
#define CREATE_CMD
#include "transcend-nvme.h"
static const __u32 OP_BAD_BLOCK = 0xc2;
static const __u32 DW10_BAD_BLOCK = 0x400;
static const __u32 DW12_BAD_BLOCK = 0x5a;
static int getHealthValue(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
struct nvme_smart_log smart_log;
char *desc = "Get nvme health percentage.";
int result=0, fd;
int percent_used = 0, healthvalue=0;
OPT_ARGS(opts) = {
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0) {
printf("\nDevice not found \n");;
return -1;
}
result = nvme_smart_log(fd, 0xffffffff, &smart_log);
if (!result) {
printf("Transcend NVME heath value: ");
percent_used =smart_log.percent_used;
if(percent_used>100 || percent_used<0)
{
printf("0%%\n");
}
else
{
healthvalue = 100 - percent_used;
printf("%d%%\n",healthvalue);
}
}
return result;
}
static int getBadblock(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
char *desc = "Get nvme bad block number.";
int result=0, fd;
OPT_ARGS(opts) = {
OPT_END()
};
fd = parse_and_open(argc, argv, desc, opts);
if (fd < 0) {
printf("\nDevice not found \n");;
return -1;
}
unsigned char data[1]={0};
struct nvme_passthru_cmd nvmecmd;
memset(&nvmecmd,0,sizeof(nvmecmd));
nvmecmd.opcode=OP_BAD_BLOCK;
nvmecmd.cdw10=DW10_BAD_BLOCK;
nvmecmd.cdw12=DW12_BAD_BLOCK;
nvmecmd.addr = (__u64)(uintptr_t)data;
nvmecmd.data_len = 0x1;
result = nvme_submit_admin_passthru(fd,&nvmecmd);
if(!result) {
int badblock = data[0];
printf("Transcend NVME badblock count: %d\n",badblock);
}
return result;
}

View file

@ -0,0 +1,20 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/transcend/transcend-nvme
#if !defined(TRANSCEND_NVME) || defined(CMD_HEADER_MULTI_READ)
#define TRANSCEND_NVME
#include "cmd.h"
PLUGIN(NAME("transcend", "Transcend vendor specific extensions"),
COMMAND_LIST(
ENTRY("healthvalue", "NVME health percentage", getHealthValue)
ENTRY("badblock", "Get NVME bad block number", getBadblock)
)
);
#endif
#include "define_cmd.h"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/virtium/virtium-nvme
#if !defined(VIRTIUM_NVME) || defined(CMD_HEADER_MULTI_READ)
#define VIRTIUM_NVME
#include "cmd.h"
#include "plugin.h"
PLUGIN(NAME("virtium", "Virtium vendor specific extensions"),
COMMAND_LIST(
ENTRY("save-smart-to-vtview-log", "Periodically save smart attributes into a log file.\n\
The data in this log file can be analyzed using excel or using Virtiums vtView.\n\
Visit vtView.virtium.com to see full potential uses of the data", vt_save_smart_to_vtview_log)
ENTRY("show-identify", "Shows detail features and current settings", vt_show_identify)
)
);
#endif
#include "define_cmd.h"

5663
plugins/wdc/wdc-nvme.c Normal file

File diff suppressed because it is too large Load diff

38
plugins/wdc/wdc-nvme.h Normal file
View file

@ -0,0 +1,38 @@
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/wdc/wdc-nvme
#if !defined(WDC_NVME) || defined(CMD_HEADER_MULTI_READ)
#define WDC_NVME
#include "cmd.h"
PLUGIN(NAME("wdc", "Western Digital vendor specific extensions"),
COMMAND_LIST(
ENTRY("cap-diag", "WDC Capture-Diagnostics", wdc_cap_diag)
ENTRY("drive-log", "WDC Drive Log", wdc_drive_log)
ENTRY("get-crash-dump", "WDC Crash Dump", wdc_get_crash_dump)
ENTRY("get-pfail-dump", "WDC Pfail Dump", wdc_get_pfail_dump)
ENTRY("id-ctrl", "WDC identify controller", wdc_id_ctrl)
ENTRY("purge", "WDC Purge", wdc_purge)
ENTRY("purge-monitor", "WDC Purge Monitor", wdc_purge_monitor)
ENTRY("vs-internal-log", "WDC Internal Firmware Log", wdc_vs_internal_fw_log)
ENTRY("vs-nand-stats", "WDC NAND Statistics", wdc_vs_nand_stats)
ENTRY("vs-smart-add-log", "WDC Additional Smart Log", wdc_vs_smart_add_log)
ENTRY("clear-pcie-correctable-errors", "WDC Clear PCIe Correctable Error Count", wdc_clear_pcie_correctable_errors)
ENTRY("drive-essentials", "WDC Drive Essentials", wdc_drive_essentials)
ENTRY("get-drive-status", "WDC Get Drive Status", wdc_drive_status)
ENTRY("clear-assert-dump", "WDC Clear Assert Dump", wdc_clear_assert_dump)
ENTRY("drive-resize", "WDC Drive Resize", wdc_drive_resize)
ENTRY("vs-fw-activate-history", "WDC Get FW Activate History", wdc_vs_fw_activate_history)
ENTRY("clear-fw-activate-history", "WDC Clear FW Activate History", wdc_clear_fw_activate_history)
ENTRY("vs-telemetry-controller-option", "WDC Enable/Disable Controller Initiated Telemetry Log", wdc_vs_telemetry_controller_option)
ENTRY("vs-error-reason-identifier", "WDC Telemetry Reason Identifier", wdc_reason_identifier)
ENTRY("log-page-directory", "WDC Get Log Page Directory", wdc_log_page_directory)
ENTRY("namespace-resize", "WDC NamespaceDrive Resize", wdc_namespace_resize)
ENTRY("vs-drive-info", "WDC Get Drive Info", wdc_vs_drive_info)
)
);
#endif
#include "define_cmd.h"

146
plugins/wdc/wdc-utils.c Normal file
View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2017-2018 Western Digital Corporation or its affiliates.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* Author: Jeff Lien <jeff.lien@wdc.com>,
*/
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "wdc-utils.h"
int wdc_UtilsSnprintf(char *buffer, unsigned int sizeOfBuffer, const char *format, ...)
{
int res = 0;
va_list vArgs;
va_start(vArgs, format);
res = vsnprintf(buffer, sizeOfBuffer, format, vArgs);
va_end(vArgs);
return res;
}
void wdc_UtilsDeleteCharFromString(char* buffer, int buffSize, char charToRemove)
{
int i = 0;
int count = 0;
if (!buffer || !buffSize)
return;
/*
* Traverse the given string. If current character is not charToRemove,
* then place it at index count++
*/
for (i = 0; ((i < buffSize) && (buffer[i] != '\0')); i++) {
if (buffer[i] != charToRemove)
buffer[count++] = buffer[i];
}
buffer[count] = '\0';
}
int wdc_UtilsGetTime(PUtilsTimeInfo timeInfo)
{
time_t currTime;
struct tm currTimeInfo;
if(!timeInfo)
return WDC_STATUS_INVALID_PARAMETER;
tzset();
time(&currTime);
localtime_r(&currTime, &currTimeInfo);
timeInfo->year = currTimeInfo.tm_year + 1900;
timeInfo->month = currTimeInfo.tm_mon + 1;
timeInfo->dayOfWeek = currTimeInfo.tm_wday;
timeInfo->dayOfMonth = currTimeInfo.tm_mday;
timeInfo->hour = currTimeInfo.tm_hour;
timeInfo->minute = currTimeInfo.tm_min;
timeInfo->second = currTimeInfo.tm_sec;
timeInfo->msecs = 0;
timeInfo->isDST = currTimeInfo.tm_isdst;
timeInfo->zone = -currTimeInfo.tm_gmtoff / 60;
return WDC_STATUS_SUCCESS;
}
int wdc_UtilsCreateDir(char *path)
{
int retStatus;
int status = WDC_STATUS_SUCCESS;
if (!path )
return WDC_STATUS_INVALID_PARAMETER;
retStatus = mkdir(path, 0x999);
if (retStatus < 0) {
if (errno == EEXIST)
status = WDC_STATUS_DIR_ALREADY_EXISTS;
else if (errno == ENOENT)
status = WDC_STATUS_PATH_NOT_FOUND;
else
status = WDC_STATUS_CREATE_DIRECTORY_FAILED;
}
return status;
}
int wdc_WriteToFile(char *fileName, char *buffer, unsigned int bufferLen)
{
int status = WDC_STATUS_SUCCESS;
FILE *file;
size_t bytesWritten = 0;
file = fopen(fileName, "ab+");
if (!file) {
status = WDC_STATUS_UNABLE_TO_OPEN_FILE;
goto end;
}
bytesWritten = fwrite(buffer, 1, bufferLen, file);
if (bytesWritten != bufferLen)
status = WDC_STATUS_UNABLE_TO_WRITE_ALL_DATA;
end:
if(file)
fclose(file);
return status;
}
/**
* Compares the strings ignoring their cases.
*
* @param pcSrc Points to a null terminated string for comapring.
* @param pcDst Points to a null terminated string for comapring.
*
* @returns zero if the string matches or
* 1 if the pcSrc string is lexically higher than pcDst or
* -1 if the pcSrc string is lexically lower than pcDst.
*/
int wdc_UtilsStrCompare(char *pcSrc, char *pcDst)
{
while ((toupper(*pcSrc) == toupper(*pcDst)) && (*pcSrc != '\0')) {
pcSrc++;
pcDst++;
}
return *pcSrc - *pcDst;
}

77
plugins/wdc/wdc-utils.h Normal file
View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2017-2018 Western Digital Corporation or its affiliates.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* Author: Jeff Lien <jeff.lien@wdc.com>,
*/
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <assert.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
/* Create Dir Command Status */
#define WDC_STATUS_SUCCESS 0
#define WDC_STATUS_FAILURE -1
#define WDC_STATUS_INSUFFICIENT_MEMORY -2
#define WDC_STATUS_INVALID_PARAMETER -3
#define WDC_STATUS_FILE_SIZE_ZERO -27
#define WDC_STATUS_UNABLE_TO_WRITE_ALL_DATA -34
#define WDC_STATUS_DIR_ALREADY_EXISTS -36
#define WDC_STATUS_PATH_NOT_FOUND -37
#define WDC_STATUS_CREATE_DIRECTORY_FAILED -38
#define WDC_STATUS_DELETE_DIRECTORY_FAILED -39
#define WDC_STATUS_UNABLE_TO_OPEN_FILE -40
#define WDC_STATUS_UNABLE_TO_OPEN_ZIP_FILE -41
#define WDC_STATUS_UNABLE_TO_ARCHIVE_EXCEEDED_FILES_LIMIT -256
#define WDC_STATUS_NO_DATA_FILE_AVAILABLE_TO_ARCHIVE -271
#define WDC_NVME_FIRMWARE_REV_LEN 9 /* added 1 for end delimiter */
#define WDC_SERIAL_NO_LEN 20
#define SECONDS_IN_MIN 60
#define MAX_PATH_LEN 256
typedef struct _UtilsTimeInfo
{
unsigned int year;
unsigned int month;
unsigned int dayOfWeek;
unsigned int dayOfMonth;
unsigned int hour;
unsigned int minute;
unsigned int second;
unsigned int msecs;
unsigned char isDST; /*0 or 1 */
int zone; /* Zone value like +530 or -300 */
} UtilsTimeInfo, *PUtilsTimeInfo;
int wdc_UtilsSnprintf(char *buffer, unsigned int sizeOfBuffer, const char *format, ...);
void wdc_UtilsDeleteCharFromString(char* buffer, int buffSize, char charToRemove);
int wdc_UtilsGetTime(PUtilsTimeInfo timeInfo);
int wdc_UtilsStrCompare(char *pcSrc, char *pcDst);
int wdc_UtilsCreateDir(char *path);
int wdc_WriteToFile(char *fileName, char *buffer, unsigned int bufferLen);