From 155fbebfe7b72b7a9de6a6b3306d1fe322b42f0f Mon Sep 17 00:00:00 2001 From: Andreas Hindborg Date: Thu, 9 Sep 2021 07:27:25 +0000 Subject: [PATCH] Update effects-log to handle multiple command sets This patch updates the effects-log command to correctly handle multiple supported command sets. As each command set is allowed to support a distinct subset of commands, the log page must to be reported for each command set. Signed-off-by: Andreas Hindborg --- Makefile | 2 +- nvme-print.c | 61 ++++++++++++++++++++++++++++------ nvme-print.h | 10 +++++- nvme.c | 94 ++++++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 141 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 5fbdfd0b..2e43862f 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ default: $(NVME) NVME-VERSION-FILE: FORCE @$(SHELL_PATH) ./NVME-VERSION-GEN -include NVME-VERSION-FILE -override CFLAGS += -DNVME_VERSION='"$(NVME_VERSION)"' -I$(LIBNVMEDIR)src/ +override CFLAGS += -DNVME_VERSION='"$(NVME_VERSION)"' -I$(LIBNVMEDIR)src/ -I$(LIBNVMEDIR)/ccan NVME_DPKG_VERSION=1~`lsb_release -sc` diff --git a/nvme-print.c b/nvme-print.c index af00a0bf..4fdcecbe 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -807,7 +807,8 @@ add: json_free_object(root); } -static void json_effects_log(struct nvme_cmd_effects_log *effects_log) +struct json_object* json_effects_log(enum nvme_csi csi, + struct nvme_cmd_effects_log *effects_log) { struct json_object *root; struct json_object *acs; @@ -817,6 +818,8 @@ static void json_effects_log(struct nvme_cmd_effects_log *effects_log) __u32 effect; root = json_create_object(); + json_object_add_value_uint(root, "command_set_identifier", csi); + acs = json_create_object(); for (opcode = 0; opcode < 256; opcode++) { effect = le32_to_cpu(effects_log->acs[opcode]); @@ -840,9 +843,24 @@ static void json_effects_log(struct nvme_cmd_effects_log *effects_log) } json_object_add_value_object(root, "io_cmd_set", iocs); - json_print_object(root, NULL); + return root; +} + +void json_effects_log_list(struct list_head *list) { + struct json_object *json_list; + nvme_effects_log_node_t *node; + + json_list = json_create_array(); + + list_for_each(list, node, node) { + json_object *json_page = + json_effects_log(node->csi, &node->effects); + json_array_add_value_object(json_list, json_page); + } + + json_print_object(json_list, NULL); printf("\n"); - json_free_object(root); + json_free_object(json_list); } static void json_sanitize_log(struct nvme_sanitize_log_page *sanitize_log, @@ -4771,24 +4789,45 @@ void nvme_print_effects_log_segment(int admin, int a, int b, struct nvme_cmd_eff free(stream_location); } -void nvme_print_effects_log_page(struct nvme_cmd_effects_log *effects, int flags) { +void nvme_print_effects_log_page(enum nvme_csi csi, struct nvme_cmd_effects_log *effects, int flags) { int human = flags & VERBOSE; + switch (csi) { + case NVME_CSI_NVM: + printf("NVM Command Set Log Page\n"); + printf("%-.80s\n", dash); + break; + case NVME_CSI_ZNS: + printf("ZNS Command Set Log Page\n"); + printf("%-.80s\n", dash); + break; + default: + printf("Unknown Command Set Log Page\n"); + printf("%-.80s\n", dash); + break; + } + nvme_print_effects_log_segment(1, 0, 0xbf, effects, "Admin Commands", human); nvme_print_effects_log_segment(1, 0xc0, 0xff, effects, "Vendor Specific Admin Commands", human); nvme_print_effects_log_segment(0, 0, 0x80, effects, "I/O Commands", human); nvme_print_effects_log_segment(0, 0x80, 0x100, effects, "Vendor Specific I/O Commands", human); } -void nvme_show_effects_log(struct nvme_cmd_effects_log *effects, - unsigned int flags) +void nvme_print_effects_log_pages(struct list_head *list, + int flags) { - if (flags & BINARY) - return d_raw((unsigned char *)effects, sizeof(*effects)); - else if (flags & JSON) - return json_effects_log(effects); + if (flags & JSON) + return json_effects_log_list(list); - nvme_print_effects_log_page(effects, flags); + nvme_effects_log_node_t *node; + list_for_each(list, node, node) { + if (flags & BINARY) { + d_raw((unsigned char *)&node->effects, sizeof(node->effects)); + } + else { + nvme_print_effects_log_page(node->csi, &node->effects, flags); + } + } } uint64_t int48_to_long(__u8 *data) diff --git a/nvme-print.h b/nvme-print.h index c3ba64ae..aa153e0f 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -4,6 +4,14 @@ #include "nvme.h" #include +#include + +typedef struct nvme_effects_log_node { + enum nvme_csi csi; + struct nvme_cmd_effects_log effects; + struct list_node node; +} nvme_effects_log_node_t; + void d(unsigned char *buf, int len, int width, int group); void d_raw(unsigned char *buf, unsigned len); uint64_t int48_to_long(__u8 *data); @@ -31,7 +39,7 @@ void nvme_show_self_test_log(struct nvme_self_test_log *self_test, __u8 dst_entr __u32 size, const char *devname, enum nvme_print_flags flags); void nvme_show_fw_log(struct nvme_firmware_slot *fw_log, const char *devname, enum nvme_print_flags flags); -void nvme_show_effects_log(struct nvme_cmd_effects_log *effects, unsigned int flags); +void nvme_print_effects_log_pages(struct list_head *list, int flags); void nvme_show_changed_ns_list_log(struct nvme_ns_list *log, const char *devname, enum nvme_print_flags flags); void nvme_show_endurance_log(struct nvme_endurance_group_log *endurance_log, diff --git a/nvme.c b/nvme.c index e5b6d3bb..bfb2cf11 100644 --- a/nvme.c +++ b/nvme.c @@ -24,6 +24,8 @@ * This program uses NVMe IOCTLs to run native nvme commands to a device. */ +#include "nvme/tree.h" +#include "nvme/types.h" #include #include #include @@ -85,8 +87,9 @@ static struct program nvme = { const char *output_format = "Output format: normal|json|binary"; static const char *output_format_no_binary = "Output format: normal|json"; -static void *__nvme_alloc(size_t len, bool *huge) -{ +static void *mmap_registers(nvme_root_t r, const char *dev); + +static void *__nvme_alloc(size_t len, bool *huge) { void *p; if (!posix_memalign(&p, getpagesize(), len)) { @@ -489,30 +492,60 @@ ret: return nvme_status_to_errno(err, false); } +void collect_effects_log(int fd, enum nvme_csi csi, struct list_head *list, int flags) +{ + int err; + nvme_effects_log_node_t *node = malloc(sizeof(nvme_effects_log_node_t)); + if (!node) { + perror("Failed to allocate memory"); + return; + } + node->csi = csi; + + err = nvme_get_log_cmd_effects(fd, csi, &node->effects); + if (!err) { + list_add(list, &node->node); + return; + } + else if (err > 0) + nvme_show_status(err); + else + perror("effects log page"); + + free(node); +} + static int get_effects_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve command effects log page and print the table."; const char *raw = "show log in binary format"; const char *human_readable = "show log in readable format"; - struct nvme_cmd_effects_log effects; + const char *csi = ""; + struct list_head log_pages; + nvme_effects_log_node_t *node; + + void *bar = NULL; int err = -1, fd; enum nvme_print_flags flags; struct config { - int raw_binary; - int human_readable; - char *output_format; + int raw_binary; + int human_readable; + char *output_format; + int csi; }; struct config cfg = { .output_format = "normal", + .csi = -1, }; OPT_ARGS(opts) = { OPT_FMT("output-format", 'o', &cfg.output_format, output_format), OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable), OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw), + OPT_INT("csi", 'c', &cfg.csi, csi), OPT_END() }; @@ -528,14 +561,49 @@ static int get_effects_log(int argc, char **argv, struct command *cmd, struct pl if (cfg.human_readable) flags |= VERBOSE; - err = nvme_get_log_cmd_effects(fd, NVME_CSI_NVM, &effects); - if (!err) - nvme_show_effects_log(&effects, flags); - else if (err > 0) - nvme_show_status(err); - else - perror("effects log page"); + list_head_init(&log_pages); + + if (cfg.csi < 0) { + nvme_root_t nvme_root; + uint64_t cap_value; + int nvme_command_set_supported; + int other_command_sets_supported; + nvme_root = nvme_scan(NULL); + bar = mmap_registers(nvme_root, devicename); + nvme_free_tree(nvme_root); + + if (!bar) { + goto close_fd; + } + cap_value = nvme_mmio_read64(bar + NVME_REG_CAP); + munmap(bar, getpagesize()); + + nvme_command_set_supported = (cap_value & (1UL << 37)) != 0; + other_command_sets_supported = (cap_value & (1UL << (37+6))) != 0; + + + if (nvme_command_set_supported) { + collect_effects_log(fd, NVME_CSI_NVM, &log_pages, flags); + } + + if (other_command_sets_supported) { + collect_effects_log(fd, NVME_CSI_ZNS, &log_pages, flags); + } + + nvme_print_effects_log_pages(&log_pages, flags); + + } + else { + collect_effects_log(fd, cfg.csi, &log_pages, flags); + nvme_print_effects_log_pages(&log_pages, flags); + } + + close_fd: + while ((node = list_pop(&log_pages, nvme_effects_log_node_t, node))) { + free(node); + } + close(fd); ret: return nvme_status_to_errno(err, false); -- 2.50.1