From f9ebefe27b0596006d76d58f3219a9fc12e88664 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Fri, 22 Nov 2019 09:12:59 +0900 Subject: [PATCH] Improve self test log command Show the page's valid entries even if no test is actively running, and be less verbose about the output unless requested. Signed-off-by: Keith Busch --- linux/nvme.h | 51 ++++++++++------ nvme-print.c | 162 +++++++++++++++++++++++++++------------------------ nvme.c | 7 ++- 3 files changed, 125 insertions(+), 95 deletions(-) diff --git a/linux/nvme.h b/linux/nvme.h index dda96334..3381c4d0 100644 --- a/linux/nvme.h +++ b/linux/nvme.h @@ -591,18 +591,42 @@ struct nvme_smart_log { }; struct nvme_self_test_res { - __u8 device_self_test_status; - __u8 segment_num; - __u8 valid_diagnostic_info; - __u8 rsvd; - __le64 power_on_hours; + __u8 dsts; + __u8 seg; + __u8 vdi; + __u8 rsvd3; + __le64 poh; __le32 nsid; - __le64 failing_lba; - __u8 status_code_type; - __u8 status_code; - __u8 vendor_specific[2]; + __le64 flba; + __u8 sct; + __u8 sc; + __u8 vs[2]; } __attribute__((packed)); +enum { + NVME_ST_CODE_SHIFT = 4, + NVME_ST_CODE_SHORT_OP = 0x1, + NVME_ST_CODE_EXT_OP = 0x2, + NVME_ST_CODE_VS = 0xe, + NVME_ST_RES_MASK = 0xf, + NVME_ST_RES_NO_ERR = 0x0, + NVME_ST_RES_ABORTED = 0x1, + NVME_ST_RES_CLR = 0x2, + NVME_ST_RES_NS_REMOVED = 0x3, + NVME_ST_RES_ABORTED_FORMAT = 0x4, + NVME_ST_RES_FATAL_ERR = 0x5, + NVME_ST_RES_UNKNOWN_SEG_FAIL = 0x6, + NVME_ST_RES_KNOWN_SEG_FAIL = 0x7, + NVME_ST_RES_ABORTED_UNKNOWN = 0x8, + NVME_ST_RES_ABORTED_SANITIZE = 0x9, + NVME_ST_RES_NOT_USED = 0xf, + NVME_ST_VALID_NSID = 1 << 0, + NVME_ST_VALID_FLBA = 1 << 1, + NVME_ST_VALID_SCT = 1 << 2, + NVME_ST_VALID_SC = 1 << 3, + NVME_ST_REPORTS = 20, +}; + struct nvme_self_test_log { __u8 crnt_dev_selftest_oprn; __u8 crnt_dev_selftest_compln; @@ -1039,15 +1063,6 @@ enum { NVME_SANITIZE_LOG_ND_COMPLETED_SUCCESS = 0x0004, }; -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, -}; - #define NVME_IDENTIFY_DATA_SIZE 4096 struct nvme_host_mem_buf_desc { diff --git a/nvme-print.c b/nvme-print.c index d5b08c3d..706d038c 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -730,35 +730,36 @@ static void json_self_test_log(struct nvme_self_test_log *self_test) 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; + for (i = 0; i < NVME_ST_REPORTS; i++) { valid_attrs = json_create_object(); json_object_add_value_int(valid_attrs, "Self test result", - self_test->result[i].device_self_test_status & 0xf); + self_test->result[i].dsts & 0xf); + if ((self_test->result[i].dsts & 0xf) == 0xf) + goto add; json_object_add_value_int(valid_attrs, "Self test code", - self_test->result[i].device_self_test_status >> 4); + self_test->result[i].dsts >> 4); json_object_add_value_int(valid_attrs, "Segment number", - self_test->result[i].segment_num); + self_test->result[i].seg); 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)", + self_test->result[i].vdi); + json_object_add_value_uint(valid_attrs, "Power on hours", + le64_to_cpu(self_test->result[i].poh)); + if (self_test->result[i].vdi & NVME_ST_VALID_NSID) + json_object_add_value_int(valid_attrs, "Namespace Identifier", le32_to_cpu(self_test->result[i].nsid)); - if (self_test->result[i].valid_diagnostic_info & NVME_SELF_TEST_VALID_FLBA) + if (self_test->result[i].vdi & NVME_ST_VALID_FLBA) json_object_add_value_uint(valid_attrs, "Failing LBA", - le64_to_cpu(self_test->result[i].failing_lba)); - if (self_test->result[i].valid_diagnostic_info & NVME_SELF_TEST_VALID_SCT) + le64_to_cpu(self_test->result[i].flba)); + if (self_test->result[i].vdi & NVME_ST_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) + self_test->result[i].sct); + if (self_test->result[i].vdi & NVME_ST_VALID_SC) json_object_add_value_int(valid_attrs, "Status Code", - self_test->result[i].status_code); + self_test->result[i].sc); json_object_add_value_int(valid_attrs, "Vendor Specific", - (self_test->result[i].vendor_specific[1] << 8) | - (self_test->result[i].vendor_specific[0])); + (self_test->result[i].vs[1] << 8) | + (self_test->result[i].vs[0])); +add: json_array_add_value_object(valid, valid_attrs); } json_object_add_value_array(root, "List of Valid Reports", valid); @@ -3505,11 +3506,9 @@ void nvme_show_ana_log(struct nvme_ana_rsp_hdr *ana_log, const char *devname, } } -void nvme_show_self_test_log(struct nvme_self_test_log *self_test, const char *devname, +static void nvme_show_self_test_result(struct nvme_self_test_res *res, enum nvme_print_flags flags) { - int i, temp; - const char *test_code_res; static const char *const test_res[] = { "Operation completed without error", "Operation was aborted by a Device Self-test command", @@ -3523,13 +3522,71 @@ void nvme_show_self_test_log(struct nvme_self_test_log *self_test, const char *d "is indicated in the SegmentNumber field", "Operation was aborted for unknown reason", "Operation was aborted due to a sanitize operation", - "Reserved" + "Reserved", + [NVME_ST_RES_NOT_USED] = "Entry not used (does not contain a result)", }; + __u8 op, code; - if (self_test->crnt_dev_selftest_oprn == 0) { - fprintf(stderr, "No device self-test operation in progress\n"); + op = res->dsts & NVME_ST_RES_MASK; + printf(" Operation Result : %#x", op); + if (flags & VERBOSE) + printf(" %s", (op < ARRAY_SIZE(test_res) && test_res[op]) ? + test_res[op] : test_res[ARRAY_SIZE(test_res) - 1]); + printf("\n"); + if (op == NVME_ST_RES_NOT_USED) return; + + code = res->dsts >> NVME_ST_CODE_SHIFT; + printf(" Self Test Code : %x", code); + + if (flags & VERBOSE) { + switch (code) { + case NVME_ST_CODE_SHORT_OP: + printf(" Short device self-test operation"); + break; + case NVME_ST_CODE_EXT_OP: + printf(" Extended device self-test operation"); + break; + case NVME_ST_CODE_VS: + printf(" Vendor specific"); + break; + default: + printf(" Reserved"); + break; + } } + printf("\n"); + + if (op == NVME_ST_RES_KNOWN_SEG_FAIL) + printf(" Segment Number : %#x\n", res->seg); + + printf(" Valid Diagnostic Information : %#x\n", res->vdi); + printf(" Power on hours (POH) : %#"PRIx64"\n", + (uint64_t)le64_to_cpu(res->poh)); + + if (res->vdi & NVME_ST_VALID_NSID) + printf(" Namespace Identifier : %#x\n", + le32_to_cpu(res->nsid)); + if (res->vdi & NVME_ST_VALID_FLBA) + printf(" Failing LBA : %#"PRIx64"\n", + (uint64_t)le64_to_cpu(res->flba)); + if (res->vdi & NVME_ST_VALID_SCT) + printf(" Status Code Type : %#x\n", res->sct); + if (res->vdi & NVME_ST_VALID_SC) { + printf(" Status Code : %#x", res->sc); + if (flags & VERBOSE) + printf(" %s", nvme_status_to_string( + (res->sct & 7) << 8 | res->sc)); + printf("\n"); + } + printf(" Vendor Specific : %#x %#x\n", + res->vs[0], res->vs[1]); +} + +void nvme_show_self_test_log(struct nvme_self_test_log *self_test, const char *devname, + enum nvme_print_flags flags) +{ + int i; if (flags & BINARY) return d_raw((unsigned char *)self_test, sizeof(*self_test)); @@ -3537,58 +3594,11 @@ void nvme_show_self_test_log(struct nvme_self_test_log *self_test, const char *d return json_self_test_log(self_test); printf("Device Self Test Log for NVME device:%s\n", devname); - printf("Current operation : %#x\n", self_test->crnt_dev_selftest_oprn); + 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 > 10 ? 10 : 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", - 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]); + for (i = 0; i < NVME_ST_REPORTS; i++) { + printf("Self Test Result[%d]:\n", i); + nvme_show_self_test_result(&self_test->result[i], flags); } } diff --git a/nvme.c b/nvme.c index 349e36d7..f8154940 100644 --- a/nvme.c +++ b/nvme.c @@ -1268,7 +1268,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, }; OPT_ARGS(opts) = { - OPT_FMT("output-format", 'o', &cfg.output_format, output_format_no_binary), + OPT_FMT("output-format", 'o', &cfg.output_format, output_format_no_binary), OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), OPT_END() }; @@ -1959,6 +1959,7 @@ static int self_test_log(int argc, char **argv, struct command *cmd, struct plug "(default) or binary."; const char *namespace_id = "Indicate the namespace from which the self-test "\ "log has to be obtained"; + const char *verbose = "Increase output verbosity"; struct nvme_self_test_log self_test_log; enum nvme_print_flags flags; @@ -1967,6 +1968,7 @@ static int self_test_log(int argc, char **argv, struct command *cmd, struct plug struct config { __u32 namespace_id; char *output_format; + int verbose; }; struct config cfg = { @@ -1977,6 +1979,7 @@ static int self_test_log(int argc, char **argv, struct command *cmd, struct plug OPT_ARGS(opts) = { OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), OPT_FMT("output-format", 'o', &cfg.output_format, output_format), + OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), OPT_END() }; @@ -1987,6 +1990,8 @@ static int self_test_log(int argc, char **argv, struct command *cmd, struct plug err = flags = validate_output_format(cfg.output_format); if (flags < 0) goto close_fd; + if (cfg.verbose) + flags |= VERBOSE; err = nvme_self_test_log(fd, cfg.namespace_id, &self_test_log); if (!err) -- 2.50.1