Merging upstream version 2.12.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
078c0dbcc0
commit
635faa7346
571 changed files with 10718 additions and 2738 deletions
|
@ -22,9 +22,11 @@
|
|||
#include "nvme-wrap.h"
|
||||
#include "nvme-print.h"
|
||||
#include "util/cleanup.h"
|
||||
#include "util/types.h"
|
||||
|
||||
#define CREATE_CMD
|
||||
#include "sfx-nvme.h"
|
||||
#include "sfx-types.h"
|
||||
|
||||
#define SFX_PAGE_SHIFT 12
|
||||
#define SECTOR_SHIFT 9
|
||||
|
@ -32,8 +34,10 @@
|
|||
#define SFX_GET_FREESPACE _IOWR('N', 0x240, struct sfx_freespace_ctx)
|
||||
#define NVME_IOCTL_CLR_CARD _IO('N', 0x47)
|
||||
|
||||
//See IDEMA LBA1-03
|
||||
#define IDEMA_CAP(exp_GB) (((__u64)exp_GB - 50ULL) * 1953504ULL + 97696368ULL)
|
||||
#define IDEMA_CAP2GB(exp_sector) (((__u64)exp_sector - 97696368ULL) / 1953504ULL + 50ULL)
|
||||
#define IDEMA_CAP2GB(exp_sector) (((__u64)exp_sector - 97696368ULL) / 1953504ULL + 50ULL)
|
||||
#define IDEMA_CAP2GB_LDS(exp_sector) (((__u64)exp_sector - 12212046ULL) / 244188ULL + 50ULL)
|
||||
|
||||
#define VANDA_MAJOR_IDX 0
|
||||
#define VANDA_MINOR_IDX 0
|
||||
|
@ -41,93 +45,7 @@
|
|||
#define MYRTLE_MAJOR_IDX 4
|
||||
#define MYRTLE_MINOR_IDX 1
|
||||
|
||||
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,
|
||||
SFX_FEAT_UP_P_CAP = 0xac,
|
||||
SFX_FEAT_CLR_CARD = 0xdc,
|
||||
};
|
||||
|
||||
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 */
|
||||
__u64 out_of_space;
|
||||
__u64 map_unit;
|
||||
__u64 max_user_space;
|
||||
__u64 extendible_user_cap_lba_count;
|
||||
__u64 friendly_change_cap_support;
|
||||
};
|
||||
|
||||
struct nvme_capacity_info {
|
||||
__u64 lba_sec_sz;
|
||||
__u64 phy_sec_sz;
|
||||
__u64 used_space;
|
||||
__u64 free_space;
|
||||
};
|
||||
|
||||
struct __packed nvme_additional_smart_log_item {
|
||||
__u8 key;
|
||||
__u8 _kp[2];
|
||||
__u8 norm;
|
||||
__u8 _np;
|
||||
union __packed {
|
||||
__u8 raw[6];
|
||||
struct __packed wear_level {
|
||||
__le16 min;
|
||||
__le16 max;
|
||||
__le16 avg;
|
||||
} wear_level;
|
||||
struct __packed thermal_throttle {
|
||||
__u8 pct;
|
||||
__u32 count;
|
||||
} thermal_throttle;
|
||||
};
|
||||
__u8 _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 */
|
||||
struct nvme_additional_smart_log_item non_media_crc_err_cnt;
|
||||
struct nvme_additional_smart_log_item compression_path_err_cnt;
|
||||
struct nvme_additional_smart_log_item out_of_space_flag;
|
||||
struct nvme_additional_smart_log_item physical_usage_ratio;
|
||||
struct nvme_additional_smart_log_item grown_bb; /* grown bad block */
|
||||
};
|
||||
|
||||
int nvme_query_cap(int fd, __u32 nsid, __u32 data_len, void *data)
|
||||
{
|
||||
|
@ -183,13 +101,14 @@ int nvme_sfx_get_features(int fd, __u32 nsid, __u32 fid, __u32 *result)
|
|||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JSONC
|
||||
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);
|
||||
json_object_add_value_string(root, "ScaleFlux Smart log", devname);
|
||||
|
||||
dev_stats = json_create_object();
|
||||
|
||||
|
@ -318,9 +237,12 @@ static void show_sfx_smart_log_jsn(struct nvme_additional_smart_log *smart,
|
|||
json_object_add_value_object(root, "Device stats", dev_stats);
|
||||
|
||||
json_print_object(root, NULL);
|
||||
printf("/n");
|
||||
printf("\n");
|
||||
json_free_object(root);
|
||||
}
|
||||
#else /* CONFIG_JSONC */
|
||||
#define show_sfx_smart_log_jsn(smart, nsid, devname)
|
||||
#endif /* CONFIG_JSONC */
|
||||
|
||||
static void show_sfx_smart_log(struct nvme_additional_smart_log *smart,
|
||||
unsigned int nsid, const char *devname)
|
||||
|
@ -410,7 +332,9 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd,
|
|||
"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";
|
||||
#ifdef CONFIG_JSONC
|
||||
const char *json = "Dump output in json format";
|
||||
#endif /* CONFIG_JSONC */
|
||||
struct nvme_dev *dev;
|
||||
struct config {
|
||||
__u32 namespace_id;
|
||||
|
@ -420,17 +344,16 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd,
|
|||
int err;
|
||||
|
||||
struct config cfg = {
|
||||
.namespace_id = 0xffffffff,
|
||||
.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_FLAG("json", 'j', &cfg.json, json),
|
||||
OPT_FLAG_JSON("json", 'j', &cfg.json, json),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
||||
err = parse_and_open(&dev, argc, argv, desc, opts);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -453,56 +376,6 @@ static int get_additional_smart_log(int argc, char **argv, struct command *cmd,
|
|||
return err;
|
||||
}
|
||||
|
||||
struct __packed sfx_lat_stats_vanda {
|
||||
__u16 maj;
|
||||
__u16 min;
|
||||
__u32 bucket_1[32]; /* 0~1ms, step 32us */
|
||||
__u32 bucket_2[31]; /* 1~32ms, step 1ms */
|
||||
__u32 bucket_3[31]; /* 32ms~1s, step 32ms */
|
||||
__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+ */
|
||||
};
|
||||
|
||||
struct __packed sfx_lat_stats_myrtle {
|
||||
__u16 maj;
|
||||
__u16 min;
|
||||
__u32 bucket_1[64]; /* 0us~63us, step 1us */
|
||||
__u32 bucket_2[64]; /* 63us~127us, step 1us */
|
||||
__u32 bucket_3[64]; /* 127us~255us, step 2us */
|
||||
__u32 bucket_4[64]; /* 255us~510us, step 4us */
|
||||
__u32 bucket_5[64]; /* 510us~1.02ms step 8us */
|
||||
__u32 bucket_6[64]; /* 1.02ms~2.04ms step 16us */
|
||||
__u32 bucket_7[64]; /* 2.04ms~4.08ms step 32us */
|
||||
__u32 bucket_8[64]; /* 4.08ms~8.16ms step 64us */
|
||||
__u32 bucket_9[64]; /* 8.16ms~16.32ms step 128us */
|
||||
__u32 bucket_10[64]; /* 16.32ms~32.64ms step 256us */
|
||||
__u32 bucket_11[64]; /* 32.64ms~65.28ms step 512us */
|
||||
__u32 bucket_12[64]; /* 65.28ms~130.56ms step 1.024ms */
|
||||
__u32 bucket_13[64]; /* 130.56ms~261.12ms step 2.048ms */
|
||||
__u32 bucket_14[64]; /* 261.12ms~522.24ms step 4.096ms */
|
||||
__u32 bucket_15[64]; /* 522.24ms~1.04s step 8.192ms */
|
||||
__u32 bucket_16[64]; /* 1.04s~2.09s step 16.384ms */
|
||||
__u32 bucket_17[64]; /* 2.09s~4.18s step 32.768ms */
|
||||
__u32 bucket_18[64]; /* 4.18s~8.36s step 65.536ms */
|
||||
__u32 bucket_19[64]; /* 8.36s~ step 131.072ms */
|
||||
__u64 average; /* average latency statistics */
|
||||
};
|
||||
|
||||
|
||||
struct __packed sfx_lat_status_ver {
|
||||
__u16 maj;
|
||||
__u16 min;
|
||||
};
|
||||
|
||||
struct sfx_lat_stats {
|
||||
union {
|
||||
struct sfx_lat_status_ver ver;
|
||||
struct sfx_lat_stats_vanda vanda;
|
||||
struct sfx_lat_stats_myrtle myrtle;
|
||||
};
|
||||
};
|
||||
|
||||
static void show_lat_stats_vanda(struct sfx_lat_stats_vanda *stats, int write)
|
||||
{
|
||||
int i;
|
||||
|
@ -713,7 +586,7 @@ static int get_bb_table(int fd, __u32 nsid, unsigned char *buf, __u64 size)
|
|||
/**
|
||||
* @brief display bb table
|
||||
*
|
||||
* @param bd_table buffer that contain bb table dumped from drvier
|
||||
* @param bd_table buffer that contain bb table dumped from driver
|
||||
* @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)
|
||||
|
@ -796,7 +669,7 @@ static int sfx_get_bad_block(int argc, char **argv, struct command *cmd, struct
|
|||
return -1;
|
||||
}
|
||||
|
||||
err = get_bb_table(dev_fd(dev), 0xffffffff, data_buf, buf_size);
|
||||
err = get_bb_table(dev_fd(dev), NVME_NSID_ALL, data_buf, buf_size);
|
||||
if (err < 0) {
|
||||
perror("get-bad-block");
|
||||
} else if (err) {
|
||||
|
@ -847,7 +720,7 @@ static int query_cap_info(int argc, char **argv, struct command *cmd, struct plu
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
if (nvme_query_cap(dev_fd(dev), 0xffffffff, sizeof(ctx), &ctx)) {
|
||||
if (nvme_query_cap(dev_fd(dev), NVME_NSID_ALL, sizeof(ctx), &ctx)) {
|
||||
perror("sfx-query-cap");
|
||||
err = -1;
|
||||
}
|
||||
|
@ -868,22 +741,22 @@ static int change_sanity_check(int fd, __u64 trg_in_4k, int *shrink)
|
|||
struct sysinfo s_info;
|
||||
__u64 mem_need = 0;
|
||||
__u64 cur_in_4k = 0;
|
||||
__u64 provisoned_cap_4k = 0;
|
||||
__u64 provisioned_cap_4k = 0;
|
||||
int extend = 0;
|
||||
|
||||
if (nvme_query_cap(fd, 0xffffffff, sizeof(freespace_ctx), &freespace_ctx))
|
||||
if (nvme_query_cap(fd, NVME_NSID_ALL, sizeof(freespace_ctx), &freespace_ctx))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* capacity illegal check
|
||||
*/
|
||||
provisoned_cap_4k = freespace_ctx.phy_space >>
|
||||
provisioned_cap_4k = freespace_ctx.phy_space >>
|
||||
(SFX_PAGE_SHIFT - SECTOR_SHIFT);
|
||||
if (trg_in_4k < provisoned_cap_4k ||
|
||||
trg_in_4k > ((__u64)provisoned_cap_4k * 4)) {
|
||||
if (trg_in_4k < provisioned_cap_4k ||
|
||||
trg_in_4k > ((__u64)provisioned_cap_4k * 4)) {
|
||||
fprintf(stderr,
|
||||
"WARNING: Only support 1.0~4.0 x provisioned capacity!\n");
|
||||
if (trg_in_4k < provisoned_cap_4k)
|
||||
if (trg_in_4k < provisioned_cap_4k)
|
||||
fprintf(stderr,
|
||||
"WARNING: The target capacity is less than 1.0 x provisioned capacity!\n");
|
||||
else
|
||||
|
@ -891,7 +764,7 @@ static int change_sanity_check(int fd, __u64 trg_in_4k, int *shrink)
|
|||
"WARNING: The target capacity is larger than 4.0 x provisioned capacity!\n");
|
||||
return -1;
|
||||
}
|
||||
if (trg_in_4k > ((__u64)provisoned_cap_4k*4)) {
|
||||
if (trg_in_4k > ((__u64)provisioned_cap_4k*4)) {
|
||||
fprintf(stderr, "WARNING: the target capacity is too large\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -997,7 +870,7 @@ static int change_cap(int argc, char **argv, struct command *cmd, struct plugin
|
|||
return 0;
|
||||
}
|
||||
|
||||
err = nvme_change_cap(dev_fd(dev), 0xffffffff, cap_in_4k);
|
||||
err = nvme_change_cap(dev_fd(dev), NVME_NSID_ALL, cap_in_4k);
|
||||
if (err < 0) {
|
||||
perror("sfx-change-cap");
|
||||
} else if (err) {
|
||||
|
@ -1112,7 +985,7 @@ static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct pl
|
|||
}
|
||||
|
||||
if (cfg.feature_id == SFX_FEAT_ATOMIC && cfg.value) {
|
||||
if (cfg.namespace_id != 0xffffffff) {
|
||||
if (cfg.namespace_id != NVME_NSID_ALL) {
|
||||
err = nvme_identify_ns(dev_fd(dev), cfg.namespace_id,
|
||||
&ns);
|
||||
if (err) {
|
||||
|
@ -1506,7 +1379,7 @@ static int sfx_dump_evtlog(int argc, char **argv, struct command *cmd, struct pl
|
|||
};
|
||||
struct config cfg = {
|
||||
.file = NULL,
|
||||
.namespace_id = 0xffffffff,
|
||||
.namespace_id = NVME_NSID_ALL,
|
||||
.storage_medium = 0,
|
||||
.parse = false,
|
||||
.output = NULL,
|
||||
|
@ -1634,7 +1507,7 @@ static int sfx_expand_cap(int argc, char **argv, struct command *cmd, struct plu
|
|||
__u32 units;
|
||||
};
|
||||
struct config cfg = {
|
||||
.namespace_id = 0xffffffff,
|
||||
.namespace_id = NVME_NSID_ALL,
|
||||
.lbaf = 0,
|
||||
.units = 0,
|
||||
};
|
||||
|
@ -1652,7 +1525,7 @@ static int sfx_expand_cap(int argc, char **argv, struct command *cmd, struct plu
|
|||
if (err)
|
||||
goto ret;
|
||||
|
||||
if (cfg.namespace_id == 0xffffffff) {
|
||||
if (cfg.namespace_id == NVME_NSID_ALL) {
|
||||
if (S_ISCHR(dev->direct.stat.st_mode)) {
|
||||
fprintf(stderr, "namespace_id or blk device required\n");
|
||||
err = EINVAL;
|
||||
|
@ -1685,3 +1558,528 @@ close_dev:
|
|||
ret:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sfx_status(int argc, char **argv, struct command *cmd, struct plugin *plugin)
|
||||
{
|
||||
const char *desc = "Get ScaleFlux specific status information and print it";
|
||||
const char *json_desc = "Print output in JSON format, otherwise human readable";
|
||||
struct nvme_dev *dev;
|
||||
struct nvme_id_ctrl id_ctrl = { 0 };
|
||||
struct extended_health_info_myrtle sfx_smart = { 0 };
|
||||
struct nvme_smart_log smart_log = { 0 };
|
||||
struct nvme_additional_smart_log additional_smart_log = { 0 };
|
||||
struct sfx_freespace_ctx sfx_freespace = { 0 };
|
||||
struct nvme_get_features_args get_feat_args = { 0 };
|
||||
unsigned int get_feat_result, pcie_correctable, pcie_fatal, pcie_nonfatal;
|
||||
unsigned long long capacity;
|
||||
bool capacity_valid = false;
|
||||
int err, fd, len, sector_size;
|
||||
char pci_vid[7], pci_did[7], pci_ssvid[7], link_speed[20], link_width[5], link_string[40];
|
||||
char path[512], numa_node[5], vendor[10], form_factor[15], temperature[10], io_speed[15];
|
||||
char chr_dev[8], serial_number[21], model_number[41], firmware_revision[9], pcie_status[9];
|
||||
struct json_object *root, *dev_stats, *link_stats, *crit_stats;
|
||||
double write_amp;
|
||||
|
||||
struct config {
|
||||
bool json;
|
||||
};
|
||||
struct config cfg = {
|
||||
.json = false
|
||||
};
|
||||
|
||||
OPT_ARGS(opts) = {
|
||||
OPT_FLAG("json-print", 'j', &cfg.json, json_desc),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
err = parse_and_open(&dev, argc, argv, desc, opts);
|
||||
|
||||
if (err)
|
||||
goto ret;
|
||||
|
||||
//Calculate formatted capacity, not concerned with errors, we may have a char device
|
||||
memset(&path, 0, 512);
|
||||
snprintf(path, 512, "/dev/%s", dev->name);
|
||||
fd = open(path, O_RDONLY | O_NONBLOCK);
|
||||
if (fd >= 0) {
|
||||
err = ioctl(fd, BLKSSZGET, §or_size);
|
||||
if (!err)
|
||||
err = ioctl(fd, BLKGETSIZE64, &capacity);
|
||||
capacity_valid = (!err);
|
||||
}
|
||||
|
||||
if (capacity_valid && sector_size == 512)
|
||||
capacity = IDEMA_CAP2GB(capacity/sector_size);
|
||||
else if (capacity_valid && sector_size == 4096)
|
||||
capacity = IDEMA_CAP2GB_LDS(capacity/sector_size);
|
||||
else
|
||||
capacity = capacity / (1000 * 1000 * 1000); //B --> GB
|
||||
|
||||
memset(&chr_dev, 0, 8);
|
||||
strcpy(chr_dev, dev->name);
|
||||
for (len = 2; len < 8; len++) {
|
||||
if (chr_dev[len] == 'n')
|
||||
chr_dev[len] = '\0';
|
||||
}
|
||||
|
||||
// Populate PCIe VID/DID/SS_VID, link speed/width, and NUMA node from /sys/
|
||||
snprintf(path, 512, "/sys/class/nvme/%s/device/vendor", chr_dev);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("Could not open PCIe VID in /sys/");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
memset(&pci_vid, 0, 7);
|
||||
len = read(fd, pci_vid, 6);
|
||||
if (len < 1) {
|
||||
perror("Could not read PCIe VID in /sys/");
|
||||
close(fd);
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
snprintf(path, 512, "/sys/class/nvme/%s/device/device", chr_dev);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("Could not open PCIe DID in /sys/");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
memset(&pci_did, 0, 7);
|
||||
len = read(fd, pci_did, 6);
|
||||
if (len < 1) {
|
||||
perror("Could not read PCIe DID in /sys/");
|
||||
close(fd);
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if (strncmp("0xcc53", pci_vid, 6) == 0)
|
||||
strncpy(vendor, "ScaleFlux", 10);
|
||||
else if (strncmp("0x1dfd", pci_vid, 6) == 0)
|
||||
strncpy(vendor, "DIGISTOR", 10);
|
||||
else {
|
||||
fprintf(stderr, "Please use on a ScaleFlux device\n");
|
||||
err = -1;
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
snprintf(path, 512, "/sys/class/nvme/%s/device/subsystem_vendor", chr_dev);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("Could not open PCIe Subsystem Vendor ID in /sys/");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
memset(&pci_ssvid, 0, 7);
|
||||
len = read(fd, pci_ssvid, 6);
|
||||
if (len < 1) {
|
||||
perror("could not read PCIe Subsystem Vendor ID in /sys/");
|
||||
close(fd);
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
snprintf(path, 512, "/sys/class/nvme/%s/device/current_link_speed", chr_dev);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("Could not open link speed in /sys/");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
memset(&link_speed, 0, 20);
|
||||
len = read(fd, link_speed, 20);
|
||||
if (len < 1) {
|
||||
perror("Could not read link speed in /sys/");
|
||||
close(fd);
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
close(fd);
|
||||
// Ending string before "PCIe" and newline
|
||||
for (len = 0; (len+2) < 20 && link_speed[len+2] != '\0'; ++len) {
|
||||
if (link_speed[len] == '/' && link_speed[len+1] == 's')
|
||||
link_speed[len+2] = '\0';
|
||||
}
|
||||
|
||||
snprintf(path, 512, "/sys/class/nvme/%s/device/current_link_width", chr_dev);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("Could not open link width in /sys/");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
memset(&link_width, 0, 5);
|
||||
len = read(fd, link_width, 5);
|
||||
if (len < 1) {
|
||||
perror("Could not read link width in /sys/");
|
||||
close(fd);
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
close(fd);
|
||||
// Ending string before newline
|
||||
for (len = 0; (len) < 5 ; ++len) {
|
||||
if (link_width[len] == '\n')
|
||||
link_width[len] = '\0';
|
||||
}
|
||||
|
||||
snprintf(link_string, 40, "Speed %s, Width x%s", link_speed, link_width);
|
||||
|
||||
snprintf(path, 512, "/sys/class/nvme/%s/device/numa_node", chr_dev);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("Could not open NUMA node in /sys/");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
memset(&numa_node, 0, 5);
|
||||
len = read(fd, numa_node, 5);
|
||||
if (len < 1) {
|
||||
perror("Could not read NUMA node in /sys/");
|
||||
close(fd);
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
for (len = 0; len < 5; ++len) {
|
||||
if (numa_node[len] == '\n')
|
||||
numa_node[len] = '\0';
|
||||
}
|
||||
|
||||
//Populate PCIe AER errors from /sys/
|
||||
snprintf(path, 512, "/sys/class/nvme/%s/device/aer_dev_correctable", chr_dev);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("Could not open PCIe AER Correctable errors in /sys/");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
len = read(fd, path, 512);
|
||||
if (len < 1) {
|
||||
perror("Could not read PCIe AER Correctable errors in /sys/");
|
||||
close(fd);
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
close(fd);
|
||||
len = sscanf(path, "%*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d TOTAL_ERR_COR %d", &pcie_correctable);
|
||||
len = 1;
|
||||
if (len < 1 || len == EOF) {
|
||||
perror("Could not parse PCIe AER Correctable errors in /sys/");
|
||||
err = -1;
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
snprintf(path, 512, "/sys/class/nvme/%s/device/aer_dev_nonfatal", chr_dev);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("Could not open PCIe AER Non-Fatal errors in /sys/");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
len = read(fd, path, 512);
|
||||
if (len < 1) {
|
||||
perror("Could not read PCIe AER Non-Fatal errors in /sys/");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
close(fd);
|
||||
len = sscanf(path, "%*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d TOTAL_ERR_NONFATAL %d", &pcie_nonfatal);
|
||||
if (len < 1) {
|
||||
perror("Could not parse PCIe AER Non-Fatal errors in /sys/");
|
||||
err = -1;
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
snprintf(path, 512, "/sys/class/nvme/%s/device/aer_dev_fatal", chr_dev);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("Could not open PCIe AER Fatal errors in /sys/");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
len = read(fd, path, 512);
|
||||
if (len < 1) {
|
||||
perror("Could not read PCIe AER Fatal errors in /sys/");
|
||||
close(fd);
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
}
|
||||
close(fd);
|
||||
len = sscanf(path, "%*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d %*s %*d TOTAL_ERR_FATAL %d", &pcie_fatal);
|
||||
if (len < 1) {
|
||||
perror("Could not parse PCIe AER Fatal errors in /sys/");
|
||||
close(fd);
|
||||
err = -1;
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
snprintf(pcie_status, 9, "%s", (pcie_fatal != 0 || pcie_nonfatal != 0 || pcie_correctable != 0) ? "Warning":"Good");
|
||||
|
||||
//Populate id-ctrl
|
||||
err = nvme_identify_ctrl(dev_fd(dev), &id_ctrl);
|
||||
if (err) {
|
||||
fprintf(stderr, "Unable to read nvme_identify_ctrl() error code:%x\n", err);
|
||||
goto close_dev;
|
||||
}
|
||||
//Re-format specific fields so they can be safely treated as strings later
|
||||
serial_number[20] = '\0';
|
||||
memcpy(serial_number, id_ctrl.sn, 20);
|
||||
model_number[40] = '\0';
|
||||
memcpy(model_number, id_ctrl.mn, 40);
|
||||
firmware_revision[8] = '\0';
|
||||
memcpy(firmware_revision, id_ctrl.fr, 8);
|
||||
|
||||
//Populate SMART log (0x02)
|
||||
err = nvme_cli_get_log_smart(dev, NVME_NSID_ALL, false, &smart_log);
|
||||
if (err < 0) {
|
||||
perror("Could not read SMART log (0x02)");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
} else if (err > 0) {
|
||||
nvme_show_status(err);
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
snprintf(temperature, 10, "%li", kelvin_to_celsius(smart_log.temperature[1]<<8 | smart_log.temperature[0]));
|
||||
|
||||
//Populate SFX Extended Health log (0xC2) or if PCIe DID ==0x20 (Quince) use 0xD2
|
||||
if (strncmp("0x0020", pci_did, 6) == 0)
|
||||
err = nvme_get_log_simple(dev_fd(dev), SFX_LOG_EXTENDED_HEALTH_ALT, sizeof(sfx_smart), (void *)&sfx_smart);
|
||||
else
|
||||
err = nvme_get_log_simple(dev_fd(dev), SFX_LOG_EXTENDED_HEALTH, sizeof(sfx_smart), (void *)&sfx_smart);
|
||||
if (err < 0) {
|
||||
perror("Could not read ScaleFlux SMART log");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
} else if (err > 0) {
|
||||
nvme_show_status(err);
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
//Make sure the OPN can be printed safely
|
||||
sfx_smart.opn[10] = '\0';
|
||||
|
||||
switch (sfx_smart.opn[3]) {
|
||||
case 'P':
|
||||
snprintf(form_factor, 15, "%s", "AIC");
|
||||
break;
|
||||
case 'U':
|
||||
snprintf(form_factor, 15, "%s", (sfx_smart.opn[4] == '8')?"U.3":"U.2");
|
||||
break;
|
||||
case 'E':
|
||||
snprintf(form_factor, 15, "%s", "E1.S");
|
||||
break;
|
||||
default:
|
||||
snprintf(form_factor, 15, "%s", "Incorrect OPN");
|
||||
}
|
||||
|
||||
//Populate Additional SMART log (0xCA)
|
||||
err = nvme_get_nsid_log(dev_fd(dev), false, 0xca, NVME_NSID_ALL, sizeof(struct nvme_additional_smart_log), (void *)&additional_smart_log);
|
||||
if (err < 0) {
|
||||
perror("Could not read ScaleFlux SMART log");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
} else if (err > 0) {
|
||||
nvme_show_status(err);
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
//OK with the '-nan' if host_bytes_written is zero
|
||||
write_amp = int48_to_long(additional_smart_log.nand_bytes_written.raw)/(1.0 * int48_to_long(additional_smart_log.host_bytes_written.raw));
|
||||
|
||||
//Get SFX freespace information
|
||||
err = nvme_query_cap(dev_fd(dev), NVME_NSID_ALL, sizeof(sfx_freespace), &sfx_freespace);
|
||||
if (err < 0) {
|
||||
perror("Could not query freespace information (0xD6)");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
} else if (err > 0) {
|
||||
nvme_show_status(err);
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
//Parse IO Speed information
|
||||
memset(&io_speed, 0, 15);
|
||||
switch (sfx_smart.io_speed) {
|
||||
case '1':
|
||||
if (strncmp("0x0020", pci_did, 6))
|
||||
strncpy(io_speed, "2.5MB/s", 15);
|
||||
else
|
||||
strncpy(io_speed, "10MB/s", 15);
|
||||
break;
|
||||
case '2':
|
||||
if (strncmp("0x0020", pci_did, 6))
|
||||
strncpy(io_speed, "128KB/s", 15);
|
||||
else
|
||||
strncpy(io_speed, "512KB/s", 15);
|
||||
break;
|
||||
case '3':
|
||||
strncpy(io_speed, "Write Reject", 15);
|
||||
break;
|
||||
default:
|
||||
strncpy(io_speed, "Normal", 15);
|
||||
}
|
||||
|
||||
if (sfx_smart.comp_ratio < 100)
|
||||
sfx_smart.comp_ratio = 100;
|
||||
else if (sfx_smart.comp_ratio > 800)
|
||||
sfx_smart.comp_ratio = 800;
|
||||
|
||||
//Get status of atomic write feature
|
||||
get_feat_args.args_size = sizeof(get_feat_args);
|
||||
get_feat_args.fid = 0x0A;
|
||||
get_feat_args.timeout = NVME_DEFAULT_IOCTL_TIMEOUT;
|
||||
get_feat_args.result = &get_feat_result;
|
||||
err = nvme_cli_get_features(dev, &get_feat_args);
|
||||
if (err < 0) {
|
||||
perror("Could not get feature (0x0A)");
|
||||
err = errno;
|
||||
goto close_dev;
|
||||
} else if (err > 0) {
|
||||
nvme_show_status(err);
|
||||
goto close_dev;
|
||||
}
|
||||
|
||||
if (cfg.json) {
|
||||
root = json_create_object();
|
||||
json_object_add_value_string(root, "ScaleFlux Status", dev->name);
|
||||
|
||||
dev_stats = json_create_object();
|
||||
link_stats = json_create_object();
|
||||
crit_stats = json_create_object();
|
||||
|
||||
json_object_add_value_string(dev_stats, "PCIe Vendor ID", pci_vid);
|
||||
json_object_add_value_string(dev_stats, "PCIe Subsystem Vendor ID", pci_ssvid);
|
||||
json_object_add_value_string(dev_stats, "Manufacturer", vendor);
|
||||
json_object_add_value_string(dev_stats, "Model", model_number);
|
||||
json_object_add_value_string(dev_stats, "Serial Number", serial_number);
|
||||
json_object_add_value_string(dev_stats, "OPN", (char *)sfx_smart.opn);
|
||||
json_object_add_value_string(dev_stats, "Drive Type", form_factor);
|
||||
json_object_add_value_string(dev_stats, "Firmware Revision", firmware_revision);
|
||||
json_object_add_value_string(dev_stats, "Temperature [C]", temperature);
|
||||
json_object_add_value_uint(dev_stats, "Power Consumption [mW]", sfx_smart.power_mw_consumption);
|
||||
json_object_add_value_uint(dev_stats, "Atomic Write Mode", (get_feat_result));
|
||||
json_object_add_value_int(dev_stats, "Percentage Used", smart_log.percent_used);
|
||||
json_object_add_value_string(dev_stats, "Data Read", uint128_t_to_si_string(le128_to_cpu(smart_log.data_units_read), 1000 * 512));
|
||||
json_object_add_value_string(dev_stats, "Data Written", uint128_t_to_si_string(le128_to_cpu(smart_log.data_units_written), 1000 * 512));
|
||||
json_object_add_value_int(dev_stats, "Correctable Error Count", sfx_smart.pcie_rx_correct_errs);
|
||||
json_object_add_value_int(dev_stats, "Uncorrectable Error Count", sfx_smart.pcie_rx_uncorrect_errs);
|
||||
json_object_add_value_string(link_stats, "PCIe Link Width", link_width);
|
||||
json_object_add_value_string(link_stats, "PCIe Link Speed", link_speed);
|
||||
json_object_add_value_int(link_stats, "PCIe Link Fatal Errors", pcie_fatal);
|
||||
json_object_add_value_int(link_stats, "PCIe Link Non-Fatal Errors", pcie_nonfatal);
|
||||
json_object_add_value_int(link_stats, "PCIe Link Correctable Errors", pcie_correctable);
|
||||
json_object_add_value_string(link_stats, "PCIe Device Status", pcie_status);
|
||||
json_object_add_value_object(dev_stats, "PCIe Link Status", link_stats);
|
||||
if (sfx_smart.friendly_changecap_support) {
|
||||
json_object_add_value_int(dev_stats, "Current Formatted Capacity [GB]", sfx_smart.cur_formatted_capability);
|
||||
json_object_add_value_int(dev_stats, "Max Formatted Capacity [GB]", sfx_smart.max_formatted_capability);
|
||||
json_object_add_value_int(dev_stats, "Extendible Capacity LBA count", sfx_smart.extendible_cap_lbacount);
|
||||
} else if (capacity_valid)
|
||||
json_object_add_value_int(dev_stats, "Formatted Capacity [GB]", capacity);
|
||||
|
||||
json_object_add_value_int(dev_stats, "Provisioned Capacity [GB]", IDEMA_CAP2GB(sfx_smart.total_physical_capability));
|
||||
json_object_add_value_int(dev_stats, "Compression Ratio", sfx_smart.comp_ratio);
|
||||
json_object_add_value_int(dev_stats, "Physical Used Ratio", sfx_smart.physical_usage_ratio);
|
||||
json_object_add_value_int(dev_stats, "Free Physical Space [GB]", IDEMA_CAP2GB(sfx_smart.free_physical_capability));
|
||||
json_object_add_value_int(dev_stats, "Firmware RSA Verification", (sfx_smart.otp_rsa_en));
|
||||
json_object_add_value_string(dev_stats, "IO Speed", io_speed);
|
||||
json_object_add_value_string(dev_stats, "NUMA Node", numa_node);
|
||||
json_object_add_value_int(dev_stats, "Indirection Unit [kiB]", (4*sfx_freespace.map_unit));
|
||||
json_object_add_value_double(dev_stats, "Lifetime WAF", write_amp);
|
||||
|
||||
json_object_add_value_int(crit_stats, "Thermal Throttling On", (sfx_smart.temp_throttle_info));
|
||||
json_object_add_value_int(crit_stats, "Backup Capacitor Status Bad", (smart_log.critical_warning & 0x10));
|
||||
json_object_add_value_int(crit_stats, "Bad block exceeds threshold", (smart_log.critical_warning & 0x01));
|
||||
json_object_add_value_int(crit_stats, "Media Error", (smart_log.critical_warning & 0x04));
|
||||
json_object_add_value_int(crit_stats, "Read only mode", (smart_log.critical_warning & 0x08));
|
||||
json_object_add_value_int(crit_stats, "Power Failure Data Loss", (sfx_smart.sfx_critical_warning & SFX_CRIT_PWR_FAIL_DATA_LOSS));
|
||||
json_object_add_value_int(crit_stats, "Exceed physical capacity limitation", (sfx_smart.sfx_critical_warning & SFX_CRIT_OVER_CAP));
|
||||
json_object_add_value_int(crit_stats, "Read/Write lock mode", (sfx_smart.sfx_critical_warning & SFX_CRIT_RW_LOCK));
|
||||
|
||||
json_object_add_value_object(dev_stats, "Critical Warning(s)", crit_stats);
|
||||
|
||||
json_object_add_value_object(root, "Device stats", dev_stats);
|
||||
|
||||
json_print_object(root, NULL);
|
||||
printf("\n");
|
||||
json_free_object(root);
|
||||
|
||||
} else {
|
||||
// Re-using path variable to hold critical warning text
|
||||
// order is to match sfx-status, done here to include color
|
||||
memset(path, 0, 512);
|
||||
len = snprintf(path, 512, FMT_RED "\n%s%s%s%s%s%s%s%s" FMT_RESET, \
|
||||
(sfx_smart.temp_throttle_info) ? "\tThermal Throttling On\n" : "", \
|
||||
(smart_log.critical_warning & 0x10) ? "\tBackup Capacitor Status Bad\n" : "", \
|
||||
(smart_log.critical_warning & 0x01) ? "\tBad block exceeds threshold\n" : "", \
|
||||
(smart_log.critical_warning & 0x04) ? "\tMedia Error\n" : "", \
|
||||
(smart_log.critical_warning & 0x08) ? "\tRead only mode\n" : "", \
|
||||
(sfx_smart.sfx_critical_warning & SFX_CRIT_PWR_FAIL_DATA_LOSS) ? "\tPower Failure Data Loss\n" : "", \
|
||||
(sfx_smart.sfx_critical_warning & SFX_CRIT_OVER_CAP) ? "\tExceed physical capacity limitation\n" : "", \
|
||||
(sfx_smart.sfx_critical_warning & SFX_CRIT_RW_LOCK) ? "\tRead/Write lock mode\n" : "" \
|
||||
);
|
||||
if (len < 11)
|
||||
strcpy(path, "None");
|
||||
|
||||
printf("%-35s%s%s\n", "ScaleFlux Drive:", "/dev/", dev->name);
|
||||
printf("%-35s%s\n", "PCIe Vendor ID:", pci_vid);
|
||||
printf("%-35s%s\n", "PCIe Subsystem Vendor ID:", pci_ssvid);
|
||||
printf("%-35s%s\n", "Manufacturer:", vendor);
|
||||
printf("%-35s%.*s\n", "Model:", 40, model_number);
|
||||
printf("%-35s%.*s\n", "Serial Number:", 20, serial_number);
|
||||
printf("%-35s%.*s\n", "OPN:", 32, sfx_smart.opn);
|
||||
printf("%-35s%s\n", "Drive Type:", form_factor);
|
||||
printf("%-35s%.*s\n", "Firmware Revision:", 8, firmware_revision);
|
||||
printf("%-35s%s C\n", "Temperature:", temperature);
|
||||
printf("%-35s%i mW\n", "Power Consumption:", sfx_smart.power_mw_consumption);
|
||||
printf("%-35s%s\n", "Atomic Write mode:", (get_feat_result)?"Off":"On");
|
||||
printf("%-35s%u%%\n", "Percentage Used:", smart_log.percent_used);
|
||||
printf("%-35s%s\n", "Host Data Read:", uint128_t_to_si_string( le128_to_cpu( \
|
||||
smart_log.data_units_read), 1000 * 512));
|
||||
printf("%-35s%s\n", "Host Data Written:", uint128_t_to_si_string(le128_to_cpu( \
|
||||
smart_log.data_units_written), 1000 * 512));
|
||||
write_amp = int48_to_long(additional_smart_log.nand_bytes_written.raw)/(1.0 * int48_to_long(additional_smart_log.host_bytes_written.raw));
|
||||
printf("%-35s%i\n", "Correctable Error Cnt:", sfx_smart.pcie_rx_correct_errs);
|
||||
printf("%-35s%i\n", "Uncorrectable Error Cnt:", sfx_smart.pcie_rx_uncorrect_errs);
|
||||
printf("%-35s%s\n", "PCIe Link Status:", link_string);
|
||||
printf("%-35s%s\n", "PCIe Device Status:", pcie_status);
|
||||
if (sfx_smart.friendly_changecap_support) {
|
||||
printf("%-35s%"PRIu64" GB\n", "Current Formatted Capacity:",
|
||||
(uint64_t)sfx_smart.cur_formatted_capability);
|
||||
printf("%-35s%"PRIu64" GB\n", "Max Formatted Capacity:",
|
||||
(uint64_t)sfx_smart.max_formatted_capability);
|
||||
printf("%-35s%"PRIu64"\n", "Extendible Capacity LBA count:",
|
||||
(uint64_t)sfx_smart.extendible_cap_lbacount);
|
||||
} else if (capacity_valid) {
|
||||
printf("%-35s%"PRIu64" GB\n", "Formatted Capacity:", (uint64_t)capacity);
|
||||
}
|
||||
printf("%-35s%"PRIu64" GB\n", "Provisioned Capacity:",
|
||||
(uint64_t)IDEMA_CAP2GB(sfx_smart.total_physical_capability));
|
||||
printf("%-35s%u%%\n", "Compression Ratio:", sfx_smart.comp_ratio);
|
||||
printf("%-35s%u%%\n", "Physical Used Ratio:", sfx_smart.physical_usage_ratio);
|
||||
printf("%-35s%"PRIu64" GB\n", "Free Physical Space:",
|
||||
(uint64_t)IDEMA_CAP2GB(sfx_smart.free_physical_capability));
|
||||
printf("%-35s%s\n", "Firmware Verification:", (sfx_smart.otp_rsa_en) ? "On":"Off");
|
||||
printf("%-35s%s\n", "IO Speed:", io_speed);
|
||||
printf("%-35s%s\n", "NUMA Node:", numa_node);
|
||||
printf("%-35s%"PRIu64"K\n", "Indirection Unit:",
|
||||
(uint64_t)(4*sfx_freespace.map_unit));
|
||||
printf("%-35s%.2f\n", "Lifetime WAF:", write_amp);
|
||||
printf("%-35s%s\n", "Critical Warning(s):", path);
|
||||
}
|
||||
|
||||
close_dev:
|
||||
dev_close(dev);
|
||||
ret:
|
||||
return err;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue