__u8 rsvd232[280];
};
+struct nvme_self_test_res {
+ __u8 device_self_test_status;
+ __u8 segment_num;
+ __u8 valid_diagnostic_info;
+ __u8 rsvd;
+ __le64 power_on_hours;
+ __le32 nsid;
+ __le64 failing_lba;
+ __u8 status_code_type;
+ __u8 status_code;
+ __u8 vendor_specific[2];
+} __attribute__((packed));
+
+struct nvme_self_test_log {
+ __u8 crnt_dev_selftest_oprn;
+ __u8 crnt_dev_selftest_compln;
+ __u8 rsvd[2];
+ struct nvme_self_test_res result[20];
+} __attribute__((packed));
+
struct nvme_fw_slot_info_log {
__u8 afi;
__u8 rsvd1[7];
NVME_LOG_SMART = 0x02,
NVME_LOG_FW_SLOT = 0x03,
NVME_LOG_CMD_EFFECTS = 0x05,
+ NVME_LOG_DEVICE_SELF_TEST = 0x06,
NVME_LOG_TELEMETRY_HOST = 0x07,
NVME_LOG_TELEMETRY_CTRL = 0x08,
NVME_LOG_ENDURANCE_GROUP = 0x09,
NVME_SANITIZE_LOG_COMPLETED_FAILED = 0x0003,
};
+enum {
+ /* Self-test log Validation bits */
+ NVME_SELF_TEST_VALID_NSID = 1 << 0,
+ NVME_SELF_TEST_VALID_FLBA = 1 << 1,
+ NVME_SELF_TEST_VALID_SCT = 1 << 2,
+ NVME_SELF_TEST_VALID_SC = 1 << 3,
+ NVME_SELF_TEST_REPORTS = 20,
+};
+
struct nvme_identify {
__u8 opcode;
__u8 flags;
ENTRY("effects-log", "Retrieve Command Effects Log, show it", get_effects_log)
ENTRY("endurance-log", "Retrieve Endurance Group Log, show it", get_endurance_log)
ENTRY("get-feature", "Get feature and show the resulting value", get_feature)
+ ENTRY("device-self-test", "Perform the necessary tests to observe the performance", device_self_test)
+ ENTRY("self-test-log", "Retrieve the SELF-TEST Log, show it", self_test_log)
ENTRY("set-feature", "Set a feature and show the resulting value", set_feature)
ENTRY("set-property", "Set a property and show the resulting value", set_property)
ENTRY("format", "Format namespace with new block format", format)
return nvme_get_log(fd, nsid, NVME_LOG_SMART, sizeof(*smart_log), smart_log);
}
+int nvme_self_test_log(int fd, struct nvme_self_test_log *self_test_log)
+{
+ return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_DEVICE_SELF_TEST,
+ sizeof(*self_test_log), self_test_log);
+}
+
int nvme_effects_log(int fd, struct nvme_effects_log_page *effects_log)
{
return nvme_get_log(fd, 0, NVME_LOG_CMD_EFFECTS, sizeof(*effects_log), effects_log);
return nvme_submit_admin_passthru(fd, &cmd);
}
+
+int nvme_self_test_start(int fd, __u32 nsid, __u32 cdw10)
+{
+ struct nvme_admin_cmd cmd = {
+ .opcode = nvme_admin_dev_self_test,
+ .nsid = nsid,
+ .cdw10 = cdw10,
+ };
+
+ return nvme_submit_admin_passthru(fd, &cmd);
+}
int nvme_set_property(int fd, int offset, int value);
int nvme_sanitize(int fd, __u8 sanact, __u8 ause, __u8 owpass, __u8 oipbp,
__u8 no_dealloc, __u32 ovrpat);
-
+int nvme_self_test_start(int fd, __u32 nsid, __u32 cdw10);
+int nvme_self_test_log(int fd, struct nvme_self_test_log *self_test_log);
#endif /* _NVME_LIB_H */
printf("Thermal Management T2 Total Time : %u\n", le32_to_cpu(smart->thm_temp2_total_time));
}
+void show_self_test_log(struct nvme_self_test_log *self_test, const char *devname)
+{
+ int i, temp;
+ const char *test_code_res;
+ const char *test_res[10] = {
+ "Operation completed without error",
+ "Operation was aborted by a Device Self-test command",
+ "Operation was aborted by a Controller Level Reset",
+ "Operation was aborted due to a removal of a namespace from the namespace inventory",
+ "Operation was aborted due to the processing of a Format NVM command",
+ "A fatal error or unknown test error occurred while the controller was executing the"\
+ " device self-test operation andthe operation did not complete",
+ "Operation completed with a segment that failed and the segment that failed is not known",
+ "Operation completed with one or more failed segments and the first segment that failed "\
+ "is indicated in the SegmentNumber field",
+ "Operation was aborted for unknown reason",
+ "Reserved"
+ };
+
+ printf("Device Self Test Log for NVME device:%s\n", devname);
+ printf("Current operation : %#x\n", self_test->crnt_dev_selftest_oprn);
+ printf("Current Completion : %u%%\n", self_test->crnt_dev_selftest_compln);
+ for (i = 0; i < NVME_SELF_TEST_REPORTS; i++) {
+ temp = self_test->result[i].device_self_test_status & 0xf;
+ if (temp == 0xf)
+ continue;
+
+ printf("Result[%d]:\n", i);
+ printf(" Test Result : %#x %s\n", temp,
+ test_res[temp > 9 ? 9 : temp]);
+
+ temp = self_test->result[i].device_self_test_status >> 4;
+ switch (temp) {
+ case 1:
+ test_code_res = "Short device self-test operation";
+ break;
+ case 2:
+ test_code_res = "Extended device self-test operation";
+ break;
+ case 0xe:
+ test_code_res = "Vendor specific";
+ break;
+ default :
+ test_code_res = "Reserved";
+ break;
+ }
+ printf(" Test Code : %#x %s\n", temp,
+ test_code_res);
+ if (temp == 7)
+ printf(" Segment number : %#x\n",
+ self_test->result[i].segment_num);
+
+ temp = self_test->result[i].valid_diagnostic_info;
+ printf(" Valid Diagnostic Information : %#x\n", temp);
+ printf(" Power on hours (POH) : %#"PRIx64"\n",
+ le64_to_cpu(self_test->result[i].power_on_hours));
+
+ if (temp & NVME_SELF_TEST_VALID_NSID)
+ printf(" Namespace Identifier : %#x\n",
+ le32_to_cpu(self_test->result[i].nsid));
+ if (temp & NVME_SELF_TEST_VALID_FLBA)
+ printf(" Failing LBA : %#"PRIx64"\n",
+ (uint64_t)le64_to_cpu(self_test->result[i].failing_lba));
+ if (temp & NVME_SELF_TEST_VALID_SCT)
+ printf(" Status Code Type : %#x\n",
+ self_test->result[i].status_code_type);
+ if (temp & NVME_SELF_TEST_VALID_SC)
+ printf(" Status Code : %#x\n",
+ self_test->result[i].status_code);
+ printf(" Vendor Specific : %x %x\n",
+ self_test->result[i].vendor_specific[0],
+ self_test->result[i].vendor_specific[0]);
+ }
+}
+
static void show_sanitize_log_sprog(__u32 sprog)
{
double percent;
json_free_object(root);
}
+void json_self_test_log(struct nvme_self_test_log *self_test, const char *devname)
+{
+ struct json_object *root;
+ struct json_array *valid;
+ struct json_object *valid_attrs;
+ int i;
+
+ root = json_create_object();
+ json_object_add_value_int(root, "Current Device Self-Test Operation", self_test->crnt_dev_selftest_oprn);
+ json_object_add_value_int(root, "Current Device Self-Test Completion", self_test->crnt_dev_selftest_compln);
+ valid = json_create_array();
+
+ for (i=0; i < NVME_SELF_TEST_REPORTS; i++) {
+ if ((self_test->result[i].device_self_test_status & 0xf) == 0xf)
+ continue;
+ valid_attrs = json_create_object();
+ json_object_add_value_int(valid_attrs, "Self test result", self_test->result[i].device_self_test_status & 0xf);
+ json_object_add_value_int(valid_attrs, "Self test code", self_test->result[i].device_self_test_status >> 4);
+ json_object_add_value_int(valid_attrs, "Segment number", self_test->result[i].segment_num);
+ json_object_add_value_int(valid_attrs, "Valid Diagnostic Information", self_test->result[i].valid_diagnostic_info);
+ json_object_add_value_uint(valid_attrs, "Power on hours (POH)",le64_to_cpu(self_test->result[i].power_on_hours));
+ if (self_test->result[i].valid_diagnostic_info & NVME_SELF_TEST_VALID_NSID)
+ json_object_add_value_int(valid_attrs, "Namespace Identifier (NSID)", le32_to_cpu(self_test->result[i].nsid));
+ if (self_test->result[i].valid_diagnostic_info & NVME_SELF_TEST_VALID_FLBA)
+ json_object_add_value_uint(valid_attrs, "Failing LBA",(uint64_t)le64_to_cpu(self_test->result[i].failing_lba));
+ if (self_test->result[i].valid_diagnostic_info & NVME_SELF_TEST_VALID_SCT)
+ json_object_add_value_int(valid_attrs, "Status Code Type",self_test->result[i].status_code_type);
+ if(self_test->result[i].valid_diagnostic_info & NVME_SELF_TEST_VALID_SC)
+ json_object_add_value_int(valid_attrs, "Status Code",self_test->result[i].status_code);
+ json_object_add_value_int(valid_attrs, "Vendor Specific",(self_test->result[i].vendor_specific[1] << 8) |
+ (self_test->result[i].vendor_specific[0]));
+ json_array_add_value_object(valid, valid_attrs);
+ }
+ json_object_add_value_array(root, "List of Valid Reports", valid);
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
void json_effects_log(struct nvme_effects_log_page *effects_log, const char *devname)
{
struct json_object *root;
void show_lba_range(struct nvme_lba_range_type *lbrt, int nr_ranges);
void show_error_log(struct nvme_error_log_page *err_log, int entries, const char *devname);
void show_smart_log(struct nvme_smart_log *smart, unsigned int nsid, const char *devname);
+void show_self_test_log(struct nvme_self_test_log *self_test, const char *devname);
void show_fw_log(struct nvme_firmware_log_page *fw_log, const char *devname);
void show_effects_log(struct nvme_effects_log_page *effects, unsigned int flags);
void show_endurance_log(struct nvme_endurance_group_log *endurance_group,
void json_print_list_items(struct list_item *items, unsigned amnt);
void json_nvme_id_ns_descs(void *data);
void json_print_nvme_subsystem_list(struct subsys_list_item *slist, int n);
+void json_self_test_log(struct nvme_self_test_log *self_test, const char *devname);
#endif
return 0;
}
+static int device_self_test(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Implementing the device self-test feature"\
+ " which provides the necessary log to determine the state of the device";
+ const char *namespace_id = "Indicate the namespace in which the device self-test"\
+ " has to be carried out";
+ const char * self_test_code = "This field specifies the action taken by the device self-test command : "\
+ "\n1h Start a short device self-test operation\n"\
+ "2h Start a extended device self-test operation\n"\
+ "eh Start a vendor specific device self-test operation\n"\
+ "fh abort the device self-test operation\n";
+ int fd, err;
+
+ struct config {
+ __u32 namespace_id;
+ __u32 cdw10;
+ };
+
+ struct config cfg = {
+ .namespace_id = NVME_NSID_ALL,
+ .cdw10 = 0,
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"namespace-id", 'n', "NUM", CFG_POSITIVE, &cfg.namespace_id, required_argument, namespace_id},
+ {"self-test-code", 's', "NUM", CFG_POSITIVE, &cfg.cdw10, required_argument, self_test_code},
+ {NULL}
+ };
+
+ fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+ if (fd < 0)
+ return fd;
+
+ err = nvme_self_test_start(fd, cfg.namespace_id, cfg.cdw10);
+ if (!err) {
+ if ((cfg.cdw10 & 0xf) == 0xf)
+ printf("Aborting device self-test operation\n");
+ else
+ printf("Device self-test started\n");
+ } else if (err > 0) {
+ fprintf(stderr, "NVMe Status:%s(%x) NSID:%d\n",
+ nvme_status_to_string(err), err, cfg.namespace_id);
+ } else
+ perror("Device self-test");
+
+ close(fd);
+ return err;
+}
+
+static int self_test_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct nvme_self_test_log self_test_log;
+ const char *desc = "Retrieve the self-test log for the given device and given test "\
+ "(or optionally a namespace) in either decoded format "\
+ "(default) or binary.";
+ int err, fmt, fd;
+
+ 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 },
+ {NULL}
+ };
+
+ fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+ if (fd < 0)
+ return fd;
+
+ fmt = validate_output_format(cfg.output_format);
+ if (fmt < 0) {
+ err = fmt;
+ goto close_fd;
+ }
+
+ err = nvme_self_test_log(fd, &self_test_log);
+ if (!err) {
+ if (self_test_log.crnt_dev_selftest_compln == 100) {
+ if (fmt == BINARY)
+ d_raw((unsigned char *)&self_test_log, sizeof(self_test_log));
+ else if (fmt == JSON)
+ json_self_test_log(&self_test_log, devicename);
+ else
+ show_self_test_log(&self_test_log, devicename);
+ } else {
+ printf("Test is %d%% complete and is still in progress.\n",
+ self_test_log.crnt_dev_selftest_compln);
+ }
+ } else if (err > 0) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ } else {
+ perror("self_test_log");
+ }
+
+ close_fd:
+ close(fd);
+
+ return err;
+}
+
static int get_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
{
const char *desc = "Read operating parameters of the "\
err = EINVAL;
goto close_fd;
}
-
+
switch (cfg.feature_id) {
case NVME_FEAT_LBA_RANGE:
cfg.data_len = 4096;
if (cfg.sel == 3)
cfg.data_len = 0;
-
+
if (cfg.data_len) {
if (posix_memalign(&buf, getpagesize(), cfg.data_len)) {
fprintf(stderr, "can not allocate feature payload\n");