[PATCHv2 4/4] nvme: add support for printing show-topology in tabular form
Nilay Shroff
nilay at linux.ibm.com
Tue Aug 12 05:56:05 PDT 2025
The nvme CLI command show-topology currently prints the topology
output in a tree format. However, in some cases it is more convenient
and easier to read or interpret the output when displayed in a tabular
form.
This patch adds support for printing the show-topology output in a
tabular format. To achieve this, the --output-format option, which
previously supported only normal and json formats, is extended to
include a new tabular format.
With this change, the user can now choose to print the topology in any
of the following formats: normal, json, or tabular. The new tabular
output leverages the recently introduced table APIs to produce well-
aligned, easy-to-read output.
Suggested-by: Daniel Wagner <dwagner at suse.de>
Signed-off-by: Nilay Shroff <nilay at linux.ibm.com>
---
nvme-print-stdout.c | 172 ++++++++++++++++++++++++++++++++++++++++++++
nvme-print.c | 5 ++
nvme-print.h | 2 +
nvme.c | 8 ++-
nvme.h | 1 +
5 files changed, 187 insertions(+), 1 deletion(-)
diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c
index ef9ee881..fef688a3 100644
--- a/nvme-print-stdout.c
+++ b/nvme-print-stdout.c
@@ -20,6 +20,7 @@
#include "nvme-models.h"
#include "util/suffix.h"
#include "util/types.h"
+#include "util/table.h"
#include "logging.h"
#include "common.h"
@@ -5589,6 +5590,88 @@ static void stdout_list_items(nvme_root_t r)
stdout_simple_list(r);
}
+static void stdout_tabular_subsystem_topology_multipath(nvme_subsystem_t s)
+{
+ nvme_ns_t n;
+ nvme_path_t p;
+ nvme_ctrl_t c;
+ int row;
+ bool first;
+ struct table *t;
+ struct table_column columns[] = {
+ {"NSHead", LEFT, 0},
+ {"NSID", LEFT, 0},
+ {"NSPath", LEFT, 0},
+ {"ANAState", LEFT, 0},
+ {"Nodes", LEFT, 0},
+ {"Qdepth", LEFT, 0},
+ {"Controller", LEFT, 0},
+ {"TrType", LEFT, 0},
+ {"Address", LEFT, 0},
+ {"State", LEFT, 0},
+ };
+
+ t = table_init();
+ if (!t) {
+ printf("Failed to init table\n");
+ return;
+ }
+ if (table_add_columns(t, columns, ARRAY_SIZE(columns)) < 0) {
+ printf("Failed to add columns\n");
+ goto free_tbl;
+ }
+
+ nvme_subsystem_for_each_ns(s, n) {
+ first = true;
+ nvme_namespace_for_each_path(n, p) {
+ c = nvme_path_get_ctrl(p);
+
+ row = table_get_row_id(t);
+ if (row < 0) {
+ printf("Failed to add row\n");
+ goto free_tbl;
+ }
+ /* For the first row we print actual NSHead name,
+ * however, for the subsequent rows we print "arrow"
+ * ("-->") symbol for NSHead. This "arrow" style makes
+ * it visually obvious that susequenet entries (if
+ * present) are a path under the first NSHead.
+ */
+ if (first)
+ table_set_value_str(t, 0, row,
+ nvme_ns_get_name(n), LEFT);
+ else
+ table_set_value_str(t, 0, row,
+ "-->", CENTERED);
+
+ table_set_value_int(t, 1, row,
+ nvme_ns_get_nsid(n), CENTERED);
+ table_set_value_str(t, 2, row,
+ nvme_path_get_name(p), LEFT);
+ table_set_value_str(t, 3, row,
+ nvme_path_get_ana_state(p), LEFT);
+ table_set_value_str(t, 4, row,
+ nvme_path_get_numa_nodes(p), CENTERED);
+ table_set_value_int(t, 5, row,
+ nvme_path_get_queue_depth(p), CENTERED);
+ table_set_value_str(t, 6, row,
+ nvme_ctrl_get_name(c), LEFT);
+ table_set_value_str(t, 7, row,
+ nvme_ctrl_get_transport(c), LEFT);
+ table_set_value_str(t, 8, row,
+ nvme_ctrl_get_address(c), LEFT);
+ table_set_value_str(t, 9, row,
+ nvme_ctrl_get_state(c), LEFT);
+
+ table_add_row(t, row);
+ first = false;
+ }
+ }
+ table_print(t);
+free_tbl:
+ table_free(t);
+}
+
static void stdout_subsystem_topology_multipath(nvme_subsystem_t s,
enum nvme_cli_topo_ranking ranking)
{
@@ -5660,6 +5743,62 @@ static void stdout_subsystem_topology_multipath(nvme_subsystem_t s,
}
}
+static void stdout_tabular_subsystem_topology(nvme_subsystem_t s)
+{
+ nvme_ctrl_t c;
+ nvme_ns_t n;
+ int row;
+ struct table *t;
+ struct table_column columns[] = {
+ {"Namespace", LEFT, 0},
+ {"NSID", LEFT, 0},
+ {"Controller", LEFT, 0},
+ {"Trtype", LEFT, 0},
+ {"Address", LEFT, 0},
+ {"State", LEFT, 0},
+ };
+
+ t = table_init();
+ if (!t) {
+ printf("Failed to init table\n");
+ return;
+ }
+
+ if (table_add_columns(t, columns, ARRAY_SIZE(columns)) < 0) {
+ printf("Failed to add columns\n");
+ goto free_tbl;
+ }
+
+ nvme_subsystem_for_each_ctrl(s, c) {
+ nvme_ctrl_for_each_ns(c, n) {
+ c = nvme_ns_get_ctrl(n);
+
+ row = table_get_row_id(t);
+ if (row < 0) {
+ printf("Failed to add row\n");
+ goto free_tbl;
+ }
+ table_set_value_str(t, 0, row,
+ nvme_ns_get_name(n), LEFT);
+ table_set_value_int(t, 1, row,
+ nvme_ns_get_nsid(n), CENTERED);
+ table_set_value_str(t, 2, row,
+ nvme_ctrl_get_name(c), LEFT);
+ table_set_value_str(t, 3, row,
+ nvme_ctrl_get_transport(c), LEFT);
+ table_set_value_str(t, 4, row,
+ nvme_ctrl_get_address(c), LEFT);
+ table_set_value_str(t, 5, row,
+ nvme_ctrl_get_state(c), LEFT);
+
+ table_add_row(t, row);
+ }
+ }
+ table_print(t);
+free_tbl:
+ table_free(t);
+}
+
static void stdout_subsystem_topology(nvme_subsystem_t s,
enum nvme_cli_topo_ranking ranking)
{
@@ -5712,6 +5851,38 @@ static void stdout_subsystem_topology(nvme_subsystem_t s,
}
}
+static void stdout_topology_tabular(nvme_root_t r)
+{
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ bool first = true;
+
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ bool no_ctrl = true;
+ nvme_ctrl_t c;
+
+ nvme_subsystem_for_each_ctrl(s, c)
+ no_ctrl = false;
+
+ if (no_ctrl)
+ continue;
+
+ if (!first)
+ printf("\n");
+ first = false;
+
+ stdout_subsys_config(s);
+ printf("\n");
+
+ if (nvme_is_multipath(s))
+ stdout_tabular_subsystem_topology_multipath(s);
+ else
+ stdout_tabular_subsystem_topology(s);
+ }
+ }
+}
+
static void stdout_simple_topology(nvme_root_t r,
enum nvme_cli_topo_ranking ranking)
{
@@ -6330,6 +6501,7 @@ static struct print_ops stdout_print_ops = {
.topology_ctrl = stdout_topology_ctrl,
.topology_namespace = stdout_topology_namespace,
.topology_multipath = stdout_topology_multipath,
+ .topology_tabular = stdout_topology_tabular,
/* status and error messages */
.connect_msg = stdout_connect_msg,
diff --git a/nvme-print.c b/nvme-print.c
index 473a6814..d1af8284 100644
--- a/nvme-print.c
+++ b/nvme-print.c
@@ -1557,6 +1557,11 @@ void nvme_show_topology(nvme_root_t r,
nvme_print(topology_multipath, flags, r);
}
+void nvme_show_topology_tabular(nvme_root_t r, nvme_print_flags_t flags)
+{
+ nvme_print(topology_tabular, flags, r);
+}
+
void nvme_show_message(bool error, const char *msg, ...)
{
struct print_ops *ops = nvme_print_ops(NORMAL);
diff --git a/nvme-print.h b/nvme-print.h
index 0f23b711..a7982566 100644
--- a/nvme-print.h
+++ b/nvme-print.h
@@ -107,6 +107,7 @@ struct print_ops {
void (*topology_ctrl)(nvme_root_t r);
void (*topology_namespace)(nvme_root_t r);
void (*topology_multipath)(nvme_root_t r);
+ void (*topology_tabular)(nvme_root_t r);
/* status and error messages */
void (*connect_msg)(nvme_ctrl_t c);
@@ -251,6 +252,7 @@ void nvme_show_list_ns(struct nvme_ns_list *ns_list,
void nvme_show_topology(nvme_root_t t,
enum nvme_cli_topo_ranking ranking,
nvme_print_flags_t flags);
+void nvme_show_topology_tabular(nvme_root_t t, nvme_print_flags_t flags);
void nvme_feature_show(enum nvme_features_id fid, int sel, unsigned int result);
void nvme_feature_show_fields(enum nvme_features_id fid, unsigned int result, unsigned char *buf);
diff --git a/nvme.c b/nvme.c
index 86fb1d4b..c221bf24 100644
--- a/nvme.c
+++ b/nvme.c
@@ -544,6 +544,8 @@ int validate_output_format(const char *format, nvme_print_flags_t *flags)
#endif /* CONFIG_JSONC */
else if (!strcmp(format, "binary"))
f = BINARY;
+ else if (!strcmp(format, "tabular"))
+ f = TABULAR;
else
return -EINVAL;
@@ -10152,6 +10154,7 @@ static int tls_key(int argc, char **argv, struct command *command, struct plugin
static int show_topology_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
{
const char *desc = "Show the topology\n";
+ const char *output_format = "Output format: normal|json|binary|tabular";
const char *ranking = "Ranking order: namespace|ctrl|multipath";
nvme_print_flags_t flags;
_cleanup_nvme_root_ nvme_root_t r = NULL;
@@ -10220,7 +10223,10 @@ static int show_topology_cmd(int argc, char **argv, struct command *command, str
return err;
}
- nvme_show_topology(r, rank, flags);
+ if (flags & TABULAR)
+ nvme_show_topology_tabular(r, flags);
+ else
+ nvme_show_topology(r, rank, flags);
return err;
}
diff --git a/nvme.h b/nvme.h
index f02f39ca..c4f0f0cf 100644
--- a/nvme.h
+++ b/nvme.h
@@ -39,6 +39,7 @@ enum nvme_print_flags {
JSON = 1 << 1, /* display in json format */
VS = 1 << 2, /* hex dump vendor specific data areas */
BINARY = 1 << 3, /* binary dump raw bytes */
+ TABULAR = 1 << 4, /* prints aligned columns for easy reading */
};
typedef uint32_t nvme_print_flags_t;
--
2.50.1
More information about the Linux-nvme
mailing list