]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
nvme: Add show-topology command
authorDaniel Wagner <dwagner@suse.de>
Mon, 26 Sep 2022 16:17:01 +0000 (18:17 +0200)
committerDaniel Wagner <dwagner@suse.de>
Wed, 26 Oct 2022 12:40:50 +0000 (14:40 +0200)
Add nvme show-topology which displays the entire nvme topology on the
host - all the subsystems with all the underlying controllers and its
states including ANA for all nvme devices.

Signed-off-by: Daniel Wagner <dwagner@suse.de>
nvme-builtin.h
nvme-print.c
nvme-print.h
nvme.c
nvme.h

index 43041ca923f9599f232ef1c8c2f3955055a4d479..c501b74895b112e7b76decee921c43a0ffd79761 100644 (file)
@@ -105,7 +105,8 @@ COMMAND_LIST(
        ENTRY("virt-mgmt", "Manage Flexible Resources between Primary and Secondary Controller ", virtual_mgmt)
        ENTRY("rpmb", "Replay Protection Memory Block commands", rpmb_cmd)
        ENTRY("lockdown", "Submit a Lockdown command,return result", lockdown_cmd)
-       ENTRY("dim", "Send Discovery Information Management command to a Discovery Controller", dim_cmd)
+       ENTRY("dim", "Send Discovery Information Management command to a Discovery Controller", dim_cmd) \
+       ENTRY("show-topology", "Show the topology", show_topology_cmd) \
 );
 
 #endif
index cfea03428761b165b11ea84ab4b6d2bb67070d94..a50d6363b0edd408e9c5e2c209e681feb91ffa28 100644 (file)
@@ -7519,3 +7519,247 @@ void nvme_show_list_items(nvme_root_t r, enum nvme_print_flags flags)
        else
                nvme_show_simple_list(r);
 }
+
+static unsigned int json_subsystem_topology_multipath(nvme_subsystem_t s,
+                                                     json_object *namespaces)
+{
+       nvme_ns_t n;
+       nvme_path_t p;
+       unsigned int i = 0;
+
+       nvme_subsystem_for_each_ns(s, n) {
+               struct json_object *ns_attrs;
+               struct json_object *paths;
+
+               ns_attrs = json_create_object();
+               json_object_add_value_int(ns_attrs, "NSID",
+                                         nvme_ns_get_nsid(n));
+
+               paths = json_create_array();
+               nvme_namespace_for_each_path(n, p) {
+                       struct json_object *path_attrs;
+
+                       nvme_ctrl_t c = nvme_path_get_ctrl(p);
+
+                       path_attrs = json_create_object();
+                       json_object_add_value_string(path_attrs, "Name",
+                                                    nvme_ctrl_get_name(c));
+                       json_object_add_value_string(path_attrs, "Transport",
+                                                    nvme_ctrl_get_transport(c));
+                       json_object_add_value_string(path_attrs, "Address",
+                                                    nvme_ctrl_get_address(c));
+                       json_object_add_value_string(path_attrs, "State",
+                                                    nvme_ctrl_get_state(c));
+                       json_object_add_value_string(path_attrs, "ANAState",
+                                                    nvme_path_get_ana_state(p));
+                       json_array_add_value_object(paths, path_attrs);
+               }
+               json_object_add_value_array(ns_attrs, "Paths", paths);
+               json_array_add_value_object(namespaces, ns_attrs);
+               i++;
+       }
+
+       return i;
+}
+
+static void json_print_nvme_subsystem_topology(nvme_subsystem_t s,
+                                              json_object *namespaces)
+{
+       nvme_ctrl_t c;
+       nvme_ns_t n;
+
+       nvme_subsystem_for_each_ctrl(s, c) {
+               nvme_ctrl_for_each_ns(c, n) {
+                       struct json_object *ctrl_attrs;
+                       struct json_object *ns_attrs;
+                       struct json_object *ctrl;
+
+                       ns_attrs = json_create_object();
+                       json_object_add_value_int(ns_attrs, "NSID",
+                                                 nvme_ns_get_nsid(n));
+
+                       ctrl = json_create_array();
+                       ctrl_attrs = json_create_object();
+                       json_object_add_value_string(ctrl_attrs, "Name",
+                                                    nvme_ctrl_get_name(c));
+                       json_object_add_value_string(ctrl_attrs, "Transport",
+                                                    nvme_ctrl_get_transport(c));
+                       json_object_add_value_string(ctrl_attrs, "Address",
+                                                    nvme_ctrl_get_address(c));
+                       json_object_add_value_string(ctrl_attrs, "State",
+                                                    nvme_ctrl_get_state(c));
+
+                       json_array_add_value_object(ctrl, ctrl_attrs);
+                       json_object_add_value_array(ns_attrs, "Controller", ctrl);
+                       json_array_add_value_object(namespaces, ns_attrs);
+               }
+       }
+}
+
+static void json_simple_topology(nvme_root_t r)
+{
+       struct json_object *host_attrs, *subsystem_attrs;
+       struct json_object *subsystems, *namespaces;
+       struct json_object *root;
+       nvme_host_t h;
+
+       root = json_create_array();
+
+       nvme_for_each_host(r, h) {
+               nvme_subsystem_t s;
+               const char *hostid;
+
+               host_attrs = json_create_object();
+               json_object_add_value_string(host_attrs, "HostNQN",
+                                            nvme_host_get_hostnqn(h));
+               hostid = nvme_host_get_hostid(h);
+               if (hostid)
+                       json_object_add_value_string(host_attrs, "HostID", hostid);
+               subsystems = json_create_array();
+               nvme_for_each_subsystem(h, s) {
+                       subsystem_attrs = json_create_object();
+                       json_object_add_value_string(subsystem_attrs, "Name",
+                                                    nvme_subsystem_get_name(s));
+                       json_object_add_value_string(subsystem_attrs, "NQN",
+                                                    nvme_subsystem_get_nqn(s));
+
+                       json_array_add_value_object(subsystems, subsystem_attrs);
+                       namespaces = json_create_array();
+
+                       if (!json_subsystem_topology_multipath(s, namespaces))
+                               json_print_nvme_subsystem_topology(s, namespaces);
+
+                       json_object_add_value_array(subsystem_attrs, "Namespaces",
+                                                   namespaces);
+               }
+               json_object_add_value_array(host_attrs, "Subsystems", subsystems);
+               json_array_add_value_object(root, host_attrs);
+       }
+       json_print_object(root, NULL);
+       printf("\n");
+       json_free_object(root);
+}
+
+static bool nvme_is_multipath(nvme_subsystem_t s)
+{
+       nvme_ns_t n;
+       nvme_path_t p;
+
+       nvme_subsystem_for_each_ns(s, n)
+               nvme_namespace_for_each_path(n, p)
+                       return true;
+
+       return false;
+}
+
+static void nvme_show_subsystem_topology_multipath(nvme_subsystem_t s,
+                                         enum nvme_cli_topo_ranking ranking)
+{
+       nvme_ns_t n;
+       nvme_path_t p;
+       nvme_ctrl_t c;
+
+       if (ranking == NVME_CLI_TOPO_NAMESPACE) {
+               nvme_subsystem_for_each_ns(s, n) {
+                       printf(" +- ns %d\n", nvme_ns_get_nsid(n));
+                       printf(" \\\n");
+
+                       nvme_namespace_for_each_path(n, p) {
+                               c = nvme_path_get_ctrl(p);
+
+                               printf("  +- %s %s %s %s %s\n",
+                                      nvme_ctrl_get_name(c),
+                                      nvme_ctrl_get_transport(c),
+                                      nvme_ctrl_get_address(c),
+                                      nvme_ctrl_get_state(c),
+                                      nvme_path_get_ana_state(p));
+                       }
+               }
+       } else {
+               /* NVME_CLI_TOPO_CTRL */
+               nvme_subsystem_for_each_ctrl(s, c) {
+                       printf(" +- %s %s %s\n",
+                              nvme_ctrl_get_name(c),
+                              nvme_ctrl_get_transport(c),
+                              nvme_ctrl_get_address(c));
+                       printf(" \\\n");
+
+                       nvme_subsystem_for_each_ns(s, n) {
+                               nvme_namespace_for_each_path(n, p) {
+                                       if (nvme_path_get_ctrl(p) != c)
+                                               continue;
+
+                                       printf("  +- ns %d %s %s\n",
+                                              nvme_ns_get_nsid(n),
+                                              nvme_ctrl_get_state(c),
+                                              nvme_path_get_ana_state(p));
+                               }
+                       }
+               }
+       }
+}
+
+static void nvme_show_subsystem_topology(nvme_subsystem_t s,
+                                     enum nvme_cli_topo_ranking ranking)
+{
+       nvme_ctrl_t c;
+       nvme_ns_t n;
+
+       if (ranking == NVME_CLI_TOPO_NAMESPACE) {
+               nvme_subsystem_for_each_ctrl(s, c) {
+                       nvme_ctrl_for_each_ns(c, n) {
+                               printf(" +- ns %d\n", nvme_ns_get_nsid(n));
+                               printf(" \\\n");
+                               printf("  +- %s %s %s %s\n",
+                                      nvme_ctrl_get_name(c),
+                                      nvme_ctrl_get_transport(c),
+                                      nvme_ctrl_get_address(c),
+                                      nvme_ctrl_get_state(c));
+                       }
+               }
+       } else {
+               /* NVME_CLI_TOPO_CTRL */
+               nvme_subsystem_for_each_ctrl(s, c) {
+                       printf(" +- %s %s %s\n",
+                              nvme_ctrl_get_name(c),
+                              nvme_ctrl_get_transport(c),
+                              nvme_ctrl_get_address(c));
+                       printf(" \\\n");
+                       nvme_ctrl_for_each_ns(c, n) {
+                               printf("  +- ns %d %s\n",
+                                      nvme_ns_get_nsid(n),
+                                      nvme_ctrl_get_state(c));
+                       }
+               }
+       }
+}
+
+static void nvme_show_simple_topology(nvme_root_t r,
+                                     enum nvme_cli_topo_ranking ranking)
+{
+       nvme_host_t h;
+       nvme_subsystem_t s;
+
+       nvme_for_each_host(r, h) {
+               nvme_for_each_subsystem(h, s) {
+
+                       printf("%s - NQN=%s\n", nvme_subsystem_get_name(s),
+                              nvme_subsystem_get_nqn(s));
+                       printf("\\\n");
+
+                       if (nvme_is_multipath(s))
+                               nvme_show_subsystem_topology_multipath(s, ranking);
+                       else
+                               nvme_show_subsystem_topology(s, ranking);
+               }
+       }
+}
+
+void nvme_show_topology(nvme_root_t r, enum nvme_print_flags flags,
+                       enum nvme_cli_topo_ranking ranking)
+{
+       if (flags & JSON)
+               json_simple_topology(r);
+       else
+               nvme_show_simple_topology(r, ranking);
+}
index b2df61ffe0f1da61d607ebdb145d1e33da090a85..50ef5b6310d253ff72324c83afbc2b7c3b3e1e5c 100644 (file)
@@ -107,6 +107,8 @@ void nvme_show_endurance_group_list(struct nvme_id_endurance_group_list *endgrp_
        enum nvme_print_flags flags);
 void nvme_show_list_ns(struct nvme_ns_list *ns_list,
        enum nvme_print_flags flags);
+void nvme_show_topology(nvme_root_t t, enum nvme_print_flags flags,
+                       enum nvme_cli_topo_ranking ranking);
 
 void nvme_feature_show_fields(enum nvme_features_id fid, unsigned int result, unsigned char *buf);
 void nvme_directive_show(__u8 type, __u8 oper, __u16 spec, __u32 nsid, __u32 result,
diff --git a/nvme.c b/nvme.c
index ec0543e67a95eb8491b0dc3a1f261419e58d0b6e..96acde3958c5ea90f10c717f6c34477d9b3e6648 100644 (file)
--- a/nvme.c
+++ b/nvme.c
@@ -8255,6 +8255,76 @@ static int check_tls_key(int argc, char **argv, struct command *command, struct
        return 0;
 }
 
+static int show_topology_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
+{
+       const char *desc = "Show the topolog\n";
+       const char *verbose = "Increase output verbosity";
+       const char *ranking = "Ranking order: namespace|ctrl";
+       enum nvme_print_flags flags;
+       nvme_root_t r;
+       enum nvme_cli_topo_ranking rank;
+       int err;
+
+       struct config {
+               char    *output_format;
+               int     verbose;
+               char    *ranking;
+       };
+
+       struct config cfg = {
+               .output_format  = "normal",
+               .verbose        = 0,
+               .ranking        = "namespace",
+       };
+
+       OPT_ARGS(opts) = {
+               OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+               OPT_INCR("verbose",      'v', &cfg.verbose,       verbose),
+               OPT_FMT("ranking",       'r', &cfg.ranking,       ranking),
+               OPT_END()
+       };
+
+       err = argconfig_parse(argc, argv, desc, opts);
+       if (err)
+               return err;
+
+       err = flags = validate_output_format(cfg.output_format);
+       if (flags < 0)
+               return err;
+       if (cfg.verbose)
+               flags |= VERBOSE;
+
+       if (!strcmp(cfg.ranking, "namespace"))
+               rank = NVME_CLI_TOPO_NAMESPACE;
+       else if (!strcmp(cfg.ranking, "ctrl"))
+               rank = NVME_CLI_TOPO_CTRL;
+       else {
+               fprintf(stderr, "Invalid ranking argument: %s\n",
+                       cfg.ranking);
+               return -EINVAL;
+       }
+
+       r = nvme_create_root(stderr, map_log_level(cfg.verbose, false));
+       if (!r) {
+               fprintf(stderr, "Failed to create topology root: %s\n",
+                       nvme_strerror(errno));
+               return -errno;
+       }
+
+       err = nvme_scan_topology(r, NULL, NULL);
+       if (err < 0) {
+               fprintf(stderr, "Failed to scan topology: %s\n",
+                        nvme_strerror(errno));
+               nvme_free_tree(r);
+               return err;
+       }
+
+       nvme_show_topology(r, flags, rank);
+       nvme_free_tree(r);
+
+       return err;
+}
+
 static int discover_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
 {
        const char *desc = "Send Get Log Page request to Discovery Controller.";
diff --git a/nvme.h b/nvme.h
index 8ecc2918d95564db80a4a65bde7eddf149b9fd27..c13ca8e90bc9fbc9d96140394b67589adada4095 100644 (file)
--- a/nvme.h
+++ b/nvme.h
@@ -38,6 +38,11 @@ enum nvme_print_flags {
        BINARY  = 1 << 3,       /* binary dump raw bytes */
 };
 
+enum nvme_cli_topo_ranking {
+       NVME_CLI_TOPO_NAMESPACE,
+       NVME_CLI_TOPO_CTRL,
+};
+
 #define SYS_NVME "/sys/class/nvme"
 
 enum nvme_dev_type {