Adding upstream version 1.12.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
3b95ae912c
commit
ac60c09ef6
457 changed files with 159628 additions and 0 deletions
211
plugins/dera/dera-nvme.c
Normal file
211
plugins/dera/dera-nvme.c
Normal 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
17
plugins/dera/dera-nvme.h
Normal 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"
|
374
plugins/huawei/huawei-nvme.c
Normal file
374
plugins/huawei/huawei-nvme.c
Normal 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);
|
||||
}
|
18
plugins/huawei/huawei-nvme.h
Normal file
18
plugins/huawei/huawei-nvme.h
Normal 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
1316
plugins/intel/intel-nvme.c
Normal file
File diff suppressed because it is too large
Load diff
23
plugins/intel/intel-nvme.h
Normal file
23
plugins/intel/intel-nvme.h
Normal 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
436
plugins/lnvm/lnvm-nvme.c
Normal 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
27
plugins/lnvm/lnvm-nvme.h
Normal 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"
|
759
plugins/memblaze/memblaze-nvme.c
Normal file
759
plugins/memblaze/memblaze-nvme.c
Normal 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;
|
||||
}
|
||||
|
27
plugins/memblaze/memblaze-nvme.h
Normal file
27
plugins/memblaze/memblaze-nvme.h
Normal 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"
|
||||
|
164
plugins/memblaze/memblaze-utils.h
Normal file
164
plugins/memblaze/memblaze-utils.h
Normal 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
1180
plugins/micron/micron-nvme.c
Normal file
File diff suppressed because it is too large
Load diff
21
plugins/micron/micron-nvme.h
Normal file
21
plugins/micron/micron-nvme.h
Normal 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"
|
639
plugins/netapp/netapp-nvme.c
Normal file
639
plugins/netapp/netapp-nvme.c
Normal 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;
|
||||
}
|
18
plugins/netapp/netapp-nvme.h
Normal file
18
plugins/netapp/netapp-nvme.h
Normal 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"
|
873
plugins/scaleflux/sfx-nvme.c
Normal file
873
plugins/scaleflux/sfx-nvme.c
Normal 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;
|
||||
|
||||
}
|
25
plugins/scaleflux/sfx-nvme.h
Normal file
25
plugins/scaleflux/sfx-nvme.h
Normal 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"
|
||||
|
||||
|
269
plugins/seagate/seagate-diag.h
Normal file
269
plugins/seagate/seagate-diag.h
Normal 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
|
1397
plugins/seagate/seagate-nvme.c
Normal file
1397
plugins/seagate/seagate-nvme.c
Normal file
File diff suppressed because it is too large
Load diff
45
plugins/seagate/seagate-nvme.h
Normal file
45
plugins/seagate/seagate-nvme.h
Normal 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"
|
374
plugins/shannon/shannon-nvme.c
Normal file
374
plugins/shannon/shannon-nvme.c
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
20
plugins/shannon/shannon-nvme.h
Normal file
20
plugins/shannon/shannon-nvme.h
Normal 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"
|
563
plugins/toshiba/toshiba-nvme.c
Normal file
563
plugins/toshiba/toshiba-nvme.c
Normal 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;
|
||||
}
|
20
plugins/toshiba/toshiba-nvme.h
Normal file
20
plugins/toshiba/toshiba-nvme.h
Normal 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"
|
92
plugins/transcend/transcend-nvme.c
Normal file
92
plugins/transcend/transcend-nvme.c
Normal 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;
|
||||
}
|
20
plugins/transcend/transcend-nvme.h
Normal file
20
plugins/transcend/transcend-nvme.h
Normal 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"
|
1044
plugins/virtium/virtium-nvme.c
Normal file
1044
plugins/virtium/virtium-nvme.c
Normal file
File diff suppressed because it is too large
Load diff
21
plugins/virtium/virtium-nvme.h
Normal file
21
plugins/virtium/virtium-nvme.h
Normal 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 Virtium’s 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
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
38
plugins/wdc/wdc-nvme.h
Normal 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
146
plugins/wdc/wdc-utils.c
Normal 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
77
plugins/wdc/wdc-utils.h
Normal 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);
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue