]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
nvme-cli: Implemented Device self test and log.
authorRevanth Rajashekar <revanth.rajashekar@intel.com>
Tue, 5 Jun 2018 20:32:51 +0000 (14:32 -0600)
committerKeith Busch <keith.busch@intel.com>
Fri, 8 Jun 2018 21:36:06 +0000 (15:36 -0600)
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 <revanth.rajashekar@intel.com>
[changelog, code and print formatting]
Signed-off-by: Keith Busch <keith.busch@intel.com>
linux/nvme.h
nvme-builtin.h
nvme-ioctl.c
nvme-ioctl.h
nvme-print.c
nvme-print.h
nvme.c

index 2df13671bf840d58ff8d0a9385ad79516139059e..982bd9e1f06c6a95cd81fb6272701dfe80b0aa14 100644 (file)
@@ -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;
index 75055c330fd0483cd465e3d1834297b6364e5763..adfa520095761ba5ca5742a2dbe60ee95585b908 100644 (file)
@@ -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)
index 68045bcf0198fd2109221ccbafc8e5ca9fc1cff6..4cf815b53bdcb5282e41e79341eca5f36779a484 100644 (file)
@@ -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);
+}
index 36f1661709463ebc083a7352872c00a2ed9c558a..75f8104e3754f82ede4c31a99cd03d8794f9a9c9 100644 (file)
@@ -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 */
index fccdac7491205fea00da69a36c00b1af1dd1fd1a..3f679d23cea0a9ad98196d929062dd201b6ff93c 100644 (file)
@@ -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;
index 3372b28ee3f4b4eeb50b10d3719390740ff688df..98e15021d4720059280e70b5f8870ba279c5a4d3 100644 (file)
@@ -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 d5492eda81bdcd8fea903810b49fdbee2f512735..440cb24668d14435883f23af8c11f99336a17032 100644 (file)
--- 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");