From: Revanth Rajashekar Date: Tue, 5 Jun 2018 20:32:51 +0000 (-0600) Subject: nvme-cli: Implemented Device self test and log. X-Git-Tag: v1.6~32 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=2dda3f710681260255f1296edbb453a3aa1c8935;p=users%2Fsagi%2Fnvme-cli.git nvme-cli: Implemented Device self test and log. Implmented device self test and log gathering described in the NVMe 1.3 specificiation. Link: https://github.com/linux-nvme/nvme-cli/pull/364 Signed-off-by: Revanth Rajashekar [changelog, code and print formatting] Signed-off-by: Keith Busch --- diff --git a/linux/nvme.h b/linux/nvme.h index 2df13671..982bd9e1 100644 --- a/linux/nvme.h +++ b/linux/nvme.h @@ -446,6 +446,26 @@ struct nvme_smart_log { __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]; @@ -826,6 +846,7 @@ enum { 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, @@ -866,6 +887,15 @@ enum { 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; diff --git a/nvme-builtin.h b/nvme-builtin.h index 75055c33..adfa5200 100644 --- a/nvme-builtin.h +++ b/nvme-builtin.h @@ -27,6 +27,8 @@ COMMAND_LIST( 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) diff --git a/nvme-ioctl.c b/nvme-ioctl.c index 68045bcf..4cf815b5 100644 --- a/nvme-ioctl.c +++ b/nvme-ioctl.c @@ -474,6 +474,12 @@ int nvme_smart_log(int fd, __u32 nsid, struct nvme_smart_log *smart_log) 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); @@ -813,3 +819,14 @@ int nvme_sanitize(int fd, __u8 sanact, __u8 ause, __u8 owpass, __u8 oipbp, 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); +} diff --git a/nvme-ioctl.h b/nvme-ioctl.h index 36f16617..75f8104e 100644 --- a/nvme-ioctl.h +++ b/nvme-ioctl.h @@ -142,5 +142,6 @@ int nvme_get_properties(int fd, void **pbar); 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 */ diff --git a/nvme-print.c b/nvme-print.c index fccdac74..3f679d23 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -1224,6 +1224,81 @@ void show_smart_log(struct nvme_smart_log *smart, unsigned int nsid, const char 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; @@ -2192,6 +2267,45 @@ void json_smart_log(struct nvme_smart_log *smart, unsigned int nsid, const char 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; diff --git a/nvme-print.h b/nvme-print.h index 3372b28e..98e15021 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -24,6 +24,7 @@ void show_nvme_resv_report(struct nvme_reservation_status *status, int bytes, __ 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, @@ -54,6 +55,7 @@ void json_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 diff --git a/nvme.c b/nvme.c index d5492eda..440cb246 100644 --- a/nvme.c +++ b/nvme.c @@ -1822,6 +1822,112 @@ static int get_ns_id(int argc, char **argv, struct command *cmd, struct plugin * 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 "\ @@ -1887,7 +1993,7 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin err = EINVAL; goto close_fd; } - + switch (cfg.feature_id) { case NVME_FEAT_LBA_RANGE: cfg.data_len = 4096; @@ -1911,7 +2017,7 @@ static int get_feature(int argc, char **argv, struct command *cmd, struct plugin 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");