#define WDC_DRIVE_CAP_CLEAR_ASSERT 0x0000000000000040
#define WDC_DRIVE_CAP_CLEAR_PCIE 0x0000000000000080
#define WDC_DRIVE_CAP_RESIZE 0x0000000000000100
+#define WDC_DRIVE_CAP_NAND_STATS 0x0000000000000200
#define WDC_DRIVE_CAP_DRIVE_ESSENTIALS 0x0000000100000000
#define WDC_DRIVE_CAP_DUI_DATA 0x0000000200000000
#define WDC_DE_TAR_FILE_EXTN ".tar.gz"
#define WDC_DE_TAR_CMD "tar -czf"
+/* VS NAND Stats */
+#define WDC_NVME_NAND_STATS_LOG_ID 0xFB
+#define WDC_NVME_NAND_STATS_SIZE 0x200
+
/* VU Opcodes */
#define WDC_DE_VU_READ_SIZE_OPCODE 0xC0
#define WDC_DE_VU_READ_BUFFER_OPCODE 0xC2
__u8 rsvd_104[0x198]; /* 0x68-0x1FF Reserved */
};
+/* NAND Stats */
+struct __attribute__((__packed__)) wdc_nand_stats {
+ __u8 nand_write_tlc[16];
+ __u8 nand_write_slc[16];
+ __le32 nand_prog_failure;
+ __le32 nand_erase_failure;
+ __le32 bad_block_count;
+ __le64 nand_rec_trigger_event;
+ __u8 rsvd[460];
+};
+
static double safe_div_fp(double numerator, double denominator)
{
return denominator ? numerator / denominator : 0;
(uint64_t)(((double)numerator / (double)denominator) * 100) : 0;
}
+static long double int128_to_double(__u8 *data)
+{
+ int i;
+ long double result = 0;
+
+ for (i = 0; i < 16; i++) {
+ result *= 256;
+ result += data[15 - i];
+ }
+ return result;
+}
+
static int wdc_get_pci_ids(int *device_id, int *vendor_id)
{
int fd, ret = -1;
case WDC_NVME_SN520_DEV_ID_1:
/* FALLTHRU */
case WDC_NVME_SN520_DEV_ID_2:
- /* FALLTHRU */
- case WDC_NVME_SN720_DEV_ID:
capabilities = WDC_DRIVE_CAP_DUI_DATA;
+ case WDC_NVME_SN720_DEV_ID:
+ capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_NAND_STATS;
break;
default:
capabilities = 0;
fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
return ret;
}
+
+static void wdc_print_nand_stats_normal(struct wdc_nand_stats *data)
+{
+ printf(" NAND Statistics :- \n");
+ printf(" NAND Writes TLC (Bytes) %.0Lf\n",
+ int128_to_double(data->nand_write_tlc));
+ printf(" NAND Writes SLC (Bytes) %.0Lf\n",
+ int128_to_double(data->nand_write_slc));
+ printf(" NAND Program Failures %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(data->nand_prog_failure));
+ printf(" NAND Erase Failures %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(data->nand_erase_failure));
+ printf(" Bad Block Count %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(data->bad_block_count));
+ printf(" NAND XOR/RAID Recovery Trigger Events %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(data->nand_rec_trigger_event));
+}
+
+static void wdc_print_nand_stats_json(struct wdc_nand_stats *data)
+{
+ struct json_object *root;
+
+ root = json_create_object();
+ json_object_add_value_float(root, "NAND Writes TLC (Bytes)",
+ int128_to_double(data->nand_write_tlc));
+ json_object_add_value_float(root, "NAND Writes SLC (Bytes)",
+ int128_to_double(data->nand_write_slc));
+ json_object_add_value_uint(root, "NAND Program Failures",
+ le32_to_cpu(data->nand_prog_failure));
+ json_object_add_value_uint(root, "NAND Erase Failures",
+ le32_to_cpu(data->nand_erase_failure));
+ json_object_add_value_uint(root, "Bad Block Count",
+ le32_to_cpu(data->bad_block_count));
+ json_object_add_value_uint(root, "NAND XOR/RAID Recovery Trigger Events",
+ le64_to_cpu(data->nand_rec_trigger_event));
+
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+static int wdc_do_vs_nand_stats(int fd, char *format)
+{
+ int ret;
+ int fmt = -1;
+ uint8_t *output = NULL;
+ struct wdc_nand_stats *nand_stats;
+
+ if ((output = (uint8_t*)calloc(WDC_NVME_NAND_STATS_SIZE, sizeof(uint8_t))) == NULL) {
+ fprintf(stderr, "ERROR : WDC : calloc : %s\n", strerror(errno));
+ ret = -1;
+ goto out;
+ }
+
+ ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_NAND_STATS_LOG_ID,
+ false, WDC_NVME_NAND_STATS_SIZE, (void*)output);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : %s : Failed to retreive NAND stats\n", __func__);
+ goto out;
+ } else {
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ ret = fmt;
+ goto out;
+ }
+
+ /* parse the data */
+ nand_stats = (struct wdc_nand_stats *)(output);
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_nand_stats_normal(nand_stats);
+ break;
+ case JSON:
+ wdc_print_nand_stats_json(nand_stats);
+ break;
+ }
+ }
+
+out:
+ free(output);
+ return ret;
+}
+
+static int wdc_vs_nand_stats(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve NAND statistics.";
+ int fd;
+ int ret = 0;
+ __u64 capabilities = 0;
+
+ struct config {
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"output-format", 'o', "FMT", CFG_STRING, &cfg.output_format, required_argument, "Output Format: normal|json" },
+ { NULL, '\0', NULL, CFG_NONE, NULL, no_argument, desc },
+ };
+
+ fd = parse_and_open(argc, argv, desc, command_line_options, NULL, 0);
+ if (fd < 0)
+ return fd;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+
+ if ((capabilities & WDC_DRIVE_CAP_NAND_STATS) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ } else {
+ ret = wdc_do_vs_nand_stats(fd, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading NAND statistics, ret = %d\n", ret);
+ }
+
+ return ret;
+}