File 0019-nvme-Add-show-topology-command.patch of Package nvme-cli.26914
From 1158f9d91049edb6b32c7a620e6280a202a2a3e7 Mon Sep 17 00:00:00 2001
From: Daniel Wagner <dwagner@suse.de>
Date: Mon, 26 Sep 2022 18:17:01 +0200
Subject: [PATCH] nvme: Add show-topology command
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 | 3
nvme-print.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
nvme-print.h | 2
nvme.c | 70 ++++++++++++++++
nvme.h | 5 +
5 files changed, 323 insertions(+), 1 deletion(-)
--- a/nvme-builtin.h
+++ b/nvme-builtin.h
@@ -104,7 +104,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
--- a/nvme.c
+++ b/nvme.c
@@ -7916,6 +7916,76 @@ static int check_tls_key(int argc, char
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.";
--- a/nvme.h
+++ b/nvme.h
@@ -73,6 +73,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"
void register_extension(struct plugin *plugin);
--- a/nvme-print.c
+++ b/nvme-print.c
@@ -7487,3 +7487,247 @@ void nvme_show_list_items(nvme_root_t r,
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);
+}
--- a/nvme-print.h
+++ b/nvme-print.h
@@ -107,6 +107,8 @@ void nvme_show_endurance_group_list(stru
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,