From mboxrd@z Thu Jan 1 00:00:00 1970 From: hch@lst.de (Christoph Hellwig) Date: Fri, 27 Jul 2018 17:15:30 +0200 Subject: [PATCH 5/6] nvme-cli: Implement ana log page support In-Reply-To: <20180727102632.32455-6-hare@suse.de> References: <20180727102632.32455-1-hare@suse.de> <20180727102632.32455-6-hare@suse.de> Message-ID: <20180727151530.GA30341@lst.de> Chaitanya already sent a patch for this two days ago.. On Fri, Jul 27, 2018@12:26:31PM +0200, Hannes Reinecke wrote: > Signed-off-by: Hannes Reinecke > --- > Documentation/nvme-ana-log.txt | 62 ++++++++++++++++++++++++++++++ > linux/nvme.h | 19 ++++++++++ > nvme-builtin.h | 1 + > nvme-ioctl.c | 9 +++++ > nvme-ioctl.h | 2 + > nvme-print.c | 86 ++++++++++++++++++++++++++++++++++++++++++ > nvme-print.h | 2 + > nvme.c | 70 ++++++++++++++++++++++++++++++++++ > 8 files changed, 251 insertions(+) > create mode 100644 Documentation/nvme-ana-log.txt > > diff --git a/Documentation/nvme-ana-log.txt b/Documentation/nvme-ana-log.txt > new file mode 100644 > index 0000000..6ef33e7 > --- /dev/null > +++ b/Documentation/nvme-ana-log.txt > @@ -0,0 +1,62 @@ > +nvme-ana-log(1) > +=============== > + > +NAME > +---- > +nvme-ana-log - Send NVME ANA log page request, return result and log > + > +SYNOPSIS > +-------- > +[verse] > +'nvme ana-log' [--log-size= | -l ] > + [--raw-binary | -b ] > + [--output-format= | -o ] > + > +DESCRIPTION > +----------- > +Retrieves NVMe Asymmetric Namespace Access (ANA) log page from and > +NVMe device and prints the returned structure. > + > +The parameter is mandatory and may be either the NVMe character > +device (ex: /dev/nvme0), or a namespace block device (ex: /dev/nvme0n1). > + > +On success, the returned ANA log struct may be returned in one of several > +ways depending on the option flags; the structure may be parsed by the > +program and printed in readable format or the raw buffer may be printed to > +stdout for another program to parse. > + > +OPTIONS > +------- > +-l :: > +--log-size=:: > + Specify the size of the ANA log buffer. > + > +-b:: > +--raw-binary:: > + Print the raw error log buffer to stdout. > + > +-o :: > +--output-format=:: > + Set the reporting format to 'normal', 'json', or 'binary'. > + Only one output formath can be used at a time. > + > +EXAMPLES > +-------- > +* Get the ANA log and print it in a humen readable format: > +* > +------------ > +# nvme ana-log /dev/nvme0 > +------------ > ++ > + > +* Print the raw output to a file: > ++ > +------------ > +# nvme ana-log /dev/nvme0 --raw-binary > ana_log.raw > +------------ > ++ > +It is probably a bad idea to not redirect stdout when using this mode. > + > +NVME > +---- > +Part of the nvme-user suite > diff --git a/linux/nvme.h b/linux/nvme.h > index 55a197b..645e171 100644 > --- a/linux/nvme.h > +++ b/linux/nvme.h > @@ -889,6 +889,7 @@ enum { > NVME_LOG_TELEMETRY_HOST = 0x07, > NVME_LOG_TELEMETRY_CTRL = 0x08, > NVME_LOG_ENDURANCE_GROUP = 0x09, > + NVME_LOG_ANA = 0x0c, > NVME_LOG_DISC = 0x70, > NVME_LOG_RESERVATION = 0x80, > NVME_LOG_SANITIZE = 0x81, > @@ -901,6 +902,7 @@ enum { > NVME_NO_LOG_LSP = 0x0, > NVME_NO_LOG_LPO = 0x0, > NVME_TELEM_LSP_CREATE = 0x1, > + NVME_ANA_LSP_RGO = 0x1, > }; > > /* Sanitize and Sanitize Monitor/Log */ > @@ -1085,6 +1087,23 @@ struct nvme_sanitize_log_page { > __le32 est_crypto_erase_time; > }; > > +/* Asymmetric Namespace Access Log Page */ > +struct nvme_ana_group_descriptor { > + __le32 groupid; > + __le32 nsid_num; > + __le64 change_count; > + __u8 ana_state; > + __u8 resv1[7]; > + __le32 nsid[0]; > +}; > + > +struct nvme_ana_log_page { > + __le64 change_count; > + __le16 grpid_num; > + __le16 resv[3]; > + struct nvme_ana_group_descriptor desc[0]; > +}; > + > /* > * Fabrics subcommands. > */ > diff --git a/nvme-builtin.h b/nvme-builtin.h > index 2c26d57..df3eb4f 100644 > --- a/nvme-builtin.h > +++ b/nvme-builtin.h > @@ -28,6 +28,7 @@ COMMAND_LIST( > ENTRY("error-log", "Retrieve Error Log, show it", get_error_log) > 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("ana-log", "Retrieve ANA Log, show it", ana_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) > diff --git a/nvme-ioctl.c b/nvme-ioctl.c > index 9cf2a33..f88909d 100644 > --- a/nvme-ioctl.c > +++ b/nvme-ioctl.c > @@ -488,6 +488,15 @@ int nvme_sanitize_log(int fd, struct nvme_sanitize_log_page *sanitize_log) > return nvme_get_log(fd, 0, NVME_LOG_SANITIZE, sizeof(*sanitize_log), sanitize_log); > } > > +int nvme_ana_log(int fd, bool rgo, struct nvme_ana_log_page *ana_log, > + ssize_t ana_log_size) > +{ > + return nvme_get_log13(fd, 0, NVME_LOG_ANA, > + rgo ? NVME_ANA_LSP_RGO : NVME_NO_LOG_LSP, > + NVME_NO_LOG_LPO, 0, 1, > + ana_log_size, ana_log); > +} > + > int nvme_feature(int fd, __u8 opcode, __u32 nsid, __u32 cdw10, __u32 cdw11, > __u32 cdw12, __u32 data_len, void *data, __u32 *result) > { > diff --git a/nvme-ioctl.h b/nvme-ioctl.h > index b34f5de..9527cc3 100644 > --- a/nvme-ioctl.h > +++ b/nvme-ioctl.h > @@ -97,6 +97,8 @@ int nvme_discovery_log(int fd, struct nvmf_disc_rsp_page_hdr *log, __u32 size); > int nvme_sanitize_log(int fd, struct nvme_sanitize_log_page *sanitize_log); > int nvme_endurance_log(int fd, __u16 group_id, > struct nvme_endurance_group_log *endurance_log); > +int nvme_ana_log(int fd, bool rgo, struct nvme_ana_log_page *ana_log, > + ssize_t ana_log_size); > > int nvme_feature(int fd, __u8 opcode, __u32 nsid, __u32 cdw10, > __u32 cdw11, __u32 cdw12, __u32 data_len, void *data, > diff --git a/nvme-print.c b/nvme-print.c > index e4c90bf..487ea44 100644 > --- a/nvme-print.c > +++ b/nvme-print.c > @@ -1508,6 +1508,45 @@ void show_sanitize_log(struct nvme_sanitize_log_page *sanitize, unsigned int mod > printf("Estimated Time For Crypto Erase : %u\n", le32_to_cpu(sanitize->est_crypto_erase_time)); > } > > +static char *nvme_ana_state_to_string(uint8_t ana_state) > +{ > + switch (ana_state & 0x0f) { > + case 0x1: return "optimized"; > + case 0x2: return "non-optimized"; > + case 0x3: return "inaccessible"; > + case 0x4: return "persistent-loss"; > + case 0xf: return "change-state"; > + default: return "reserved"; > + } > +} > + > +void show_ana_log(struct nvme_ana_log_page *ana, const char *devname) > +{ > + int grpid_num = le16_to_cpu(ana->grpid_num), i, j; > + unsigned char *p; > + > + printf("ANA Log page for device:%s\n", devname); > + printf("desc_num : %d\n", grpid_num); > + printf("chgcnt : %"PRIu64"\n", > + (uint64_t)le64_to_cpu(ana->change_count)); > + p = (unsigned char *)&ana->desc[0]; > + for (i = 0; i < grpid_num; i++) { > + struct nvme_ana_group_descriptor *desc = > + (struct nvme_ana_group_descriptor *)p; > + int nsid_num = le32_to_cpu(desc->nsid_num); > + printf(" desc[%d] :\n", i); > + printf(" groupid : %x\n", le32_to_cpu(desc->groupid)); > + printf(" nsid_num : %d\n", nsid_num); > + printf(" chgcnt : %"PRId64"\n", > + (uint64_t)le64_to_cpu(desc->change_count)); > + printf(" state : %s\n", > + nvme_ana_state_to_string(desc->ana_state)); > + for (j = 0; j < nsid_num; j++) > + printf(" nsid[%d] : %x\n", j, le32_to_cpu(desc->nsid[j])); > + p += sizeof(*desc) + nsid_num * 4; > + } > +} > + > char *nvme_feature_to_string(int feature) > { > switch (feature) { > @@ -2571,6 +2610,53 @@ void json_sanitize_log(struct nvme_sanitize_log_page *sanitize_log, const char * > json_free_object(root); > } > > +void json_ana_log(struct nvme_ana_log_page *ana_log) > +{ > + struct json_object *root; > + struct json_array *ags; > + unsigned char *p; > + int grpid_num = le32_to_cpu(ana_log->grpid_num); > + int i, j; > + > + root = json_create_object(); > + > + json_object_add_value_int(root, "chgcnt", le64_to_cpu(ana_log->change_count)); > + json_object_add_value_int(root, "desc_num", grpid_num); > + ags = json_create_array(); > + p = (unsigned char *)&ana_log->desc[0]; > + for (i = 0; i < grpid_num; i++) { > + struct nvme_ana_group_descriptor *desc = > + (struct nvme_ana_group_descriptor *)p; > + struct json_object *ag = json_create_object(); > + struct json_array *nsids; > + int nsid_num = le32_to_cpu(desc->nsid_num); > + > + json_object_add_value_int(ag, "groupid", le32_to_cpu(desc->groupid)); > + json_object_add_value_int(ag, "nsid_num", nsid_num); > + json_object_add_value_int(ag, "chgcnt", le64_to_cpu(desc->change_count)); > + json_object_add_value_string(ag, "state", > + nvme_ana_state_to_string(desc->ana_state)); > + > + nsids = json_create_array(); > + for (j = 0; j < nsid_num; j++) { > + struct json_object *nsid = json_create_object(); > + > + json_object_add_value_int(nsid, "nsid", desc->nsid[j]); > + json_array_add_value_object(nsids, nsid); > + } > + if (j) > + json_object_add_value_array(ag, "nsids", nsids); > + json_array_add_value_object(ags, ag); > + > + p += sizeof(*desc) + nsid_num * 4; > + } > + if (i) > + json_object_add_value_array(root, "group_descriptors", ags); > + json_print_object(root, NULL); > + printf("\n"); > + json_free_object(root); > +} > + > static void show_nvme_subsystem(struct subsys_list_item *item) > { > int i; > diff --git a/nvme-print.h b/nvme-print.h > index da287c2..b3d16dc 100644 > --- a/nvme-print.h > +++ b/nvme-print.h > @@ -31,6 +31,7 @@ void show_changed_ns_list_log(struct nvme_changed_ns_list_log *log, const char * > void show_endurance_log(struct nvme_endurance_group_log *endurance_group, > __u16 group_id, const char *devname); > void show_sanitize_log(struct nvme_sanitize_log_page *sanitize, unsigned int mode, const char *devname); > +void show_ana_log(struct nvme_ana_log_page *ana, const char *devname); > void show_ctrl_registers(void *bar, unsigned int mode, bool fabrics); > void show_single_property(int offset, uint64_t prop, int human); > void show_nvme_id_ns_descs(void *data); > @@ -56,6 +57,7 @@ void json_fw_log(struct nvme_firmware_log_page *fw_log, const char *devname); > void json_changed_ns_list_log(struct nvme_changed_ns_list_log *log, const char *devname); > void json_endurance_log(struct nvme_endurance_group_log *endurance_group, > __u16 group_id, const char *devname); > +void json_ana_log(struct nvme_ana_log_page *ana_log); > 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); > diff --git a/nvme.c b/nvme.c > index 74dfb61..e463efc 100644 > --- a/nvme.c > +++ b/nvme.c > @@ -824,6 +824,76 @@ static int sanitize_log(int argc, char **argv, struct command *command, struct p > return ret; > } > > +static int ana_log(int argc, char **argv, struct command *command, struct plugin *plugin) > +{ > + const char *desc = "Retrieve ANA log and show it."; > + const char *raw_binary = "show infos in binary format"; > + const char *log_size = "Size of the ANA log page"; > + const char *rgo = "Retrieve ANA Group Descriptors only"; > + int fd; > + int ret; > + int fmt; > + struct nvme_ana_log_page *ana_log; > + > + struct config { > + int raw_binary; > + char *output_format; > + size_t log_size; > + int rgo; > + }; > + > + struct config cfg = { > + .output_format = "normal", > + .log_size = 4096, > + }; > + > + const struct argconfig_commandline_options command_line_options[] = { > + {"output-format", 'o', "FMT", CFG_STRING, &cfg.output_format, required_argument, output_format}, > + {"raw-binary", 'b', "", CFG_NONE, &cfg.raw_binary, no_argument, raw_binary}, > + {"log-size", 'l', "NUM", CFG_POSITIVE, &cfg.log_size, required_argument, log_size}, > + {"groups-only", 'r', "", CFG_NONE, &cfg.rgo, no_argument, rgo}, > + {NULL} > + }; > + > + fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg)); > + if (fd < 0) > + return fd; > + ana_log = calloc(cfg.log_size, sizeof(char)); > + if (!ana_log) { > + fprintf(stderr, "could not allocate buffer for ANA log\n"); > + ret = ENOMEM; > + goto close_fd; > + } > + fmt = validate_output_format(cfg.output_format); > + if (fmt < 0) { > + ret = fmt; > + goto close_fd_and_free; > + } > + if (cfg.raw_binary) > + fmt = BINARY; > + > + ret = nvme_ana_log(fd, cfg.rgo, ana_log, cfg.log_size); > + if (!ret) { > + if (fmt == BINARY) > + d_raw((unsigned char *)ana_log, cfg.log_size); > + else if (fmt == JSON) > + json_ana_log(ana_log); > + else > + show_ana_log(ana_log, devicename); > + } > + else if (ret > 0) > + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); > + else > + perror("ana status log"); > + > +close_fd_and_free: > + free(ana_log); > + close_fd: > + close(fd); > + > + return ret; > +} > + > static int list_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) > { > const char *desc = "Show controller list information for the subsystem the "\ > -- > 2.13.7 ---end quoted text---