From: Nilay Shroff <nilay@linux.ibm.com>
To: linux-nvme@lists.infradead.org
Cc: dwagner@suse.de, hare@suse.de, kbusch@kernel.org, gjoyce@ibm.com
Subject: [PATCHv2 4/4] nvme: add support for printing show-topology in tabular form
Date: Tue, 12 Aug 2025 18:26:05 +0530 [thread overview]
Message-ID: <20250812125614.164445-5-nilay@linux.ibm.com> (raw)
In-Reply-To: <20250812125614.164445-1-nilay@linux.ibm.com>
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@suse.de>
Signed-off-by: Nilay Shroff <nilay@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
prev parent reply other threads:[~2025-08-12 17:31 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-12 12:56 [PATCHv2 0/4] nvme-cli: enhance the visibility of multipath using show-topology command Nilay Shroff
2025-08-12 12:56 ` [PATCHv2 1/4] nvme: support <device> option in " Nilay Shroff
2025-08-18 7:12 ` Hannes Reinecke
2025-08-19 4:43 ` Nilay Shroff
2025-08-19 6:11 ` Hannes Reinecke
2025-08-12 12:56 ` [PATCHv2 2/4] nvme: extend show-topology command to add support for multipath Nilay Shroff
2025-08-18 7:22 ` Hannes Reinecke
2025-08-19 4:49 ` Nilay Shroff
2025-08-19 6:15 ` Hannes Reinecke
2025-08-19 10:31 ` Nilay Shroff
2025-08-19 11:05 ` Hannes Reinecke
2025-08-19 11:30 ` Nilay Shroff
2025-08-20 8:17 ` Daniel Wagner
2025-08-20 8:30 ` Hannes Reinecke
2025-08-20 11:59 ` Nilay Shroff
2025-09-01 9:21 ` Nilay Shroff
2025-09-01 16:36 ` Daniel Wagner
2025-09-02 6:26 ` Hannes Reinecke
2025-09-03 4:22 ` Nilay Shroff
2025-09-03 7:24 ` Daniel Wagner
2025-09-03 12:19 ` Nilay Shroff
2025-08-12 12:56 ` [PATCHv2 3/4] nvme: add common APIs for printing tabular format output Nilay Shroff
2025-08-18 7:27 ` Hannes Reinecke
2025-08-19 8:56 ` Nilay Shroff
2025-08-20 8:23 ` Daniel Wagner
2025-08-12 12:56 ` Nilay Shroff [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250812125614.164445-5-nilay@linux.ibm.com \
--to=nilay@linux.ibm.com \
--cc=dwagner@suse.de \
--cc=gjoyce@ibm.com \
--cc=hare@suse.de \
--cc=kbusch@kernel.org \
--cc=linux-nvme@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.