From 67ab8c48094863c3ad5b28cf5234bbb7d188fe28 Mon Sep 17 00:00:00 2001 From: Nilay Shroff Date: Tue, 31 Mar 2026 19:42:08 +0530 Subject: [PATCH 1/2] nvme-cli: extend show-topology tabular output for non-multipath subsys The current tabular output of show-topology command does not display a controller if it has no associated namespaces. However, it is valid for a controller within a subsystem to have no namespaces attached. In such cases, it is still useful to display controller information such as the controller name, transport type, address, and state, while leaving the namespace-related fields (e.g., namespace and nsid) as "--". Update the tabular output of show-topology command to include controllers and their associated fields regardless of whether any namespaces are present. While we are at it, also dispaly the error message using nvme_show_error() instead of using printf(). This change applies to non-multipath subsystems. A subsequent commit will extend this behavior to multipath subsystems. Signed-off-by: Nilay Shroff [wagi: reformatted argument list and dropped redundant comments] Signed-off-by: Daniel Wagner --- nvme-print-stdout.c | 83 +++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 29 deletions(-) diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c index 2e469ea509..743f5ef518 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -6148,11 +6148,33 @@ static void stdout_subsystem_topology_multipath(nvme_subsystem_t s, } } +static int subsystem_topology_add_row(struct table *t, + const char *ns, const char *nsid, const char *ctrl, + const char *trtype, const char *address, const char *state) +{ + int row = table_get_row_id(t); + if (row < 0) { + nvme_show_error("Failed to add subsys topology row"); + return row; + } + + table_set_value_str(t, 0, row, ns, CENTERED); + table_set_value_str(t, 1, row, nsid, CENTERED); + table_set_value_str(t, 2, row, ctrl, CENTERED); + table_set_value_str(t, 3, row, trtype, CENTERED); + table_set_value_str(t, 4, row, address, CENTERED); + table_set_value_str(t, 5, row, state, CENTERED); + + table_add_row(t, row); + + return 0; +} + static void stdout_tabular_subsystem_topology(nvme_subsystem_t s) { nvme_ctrl_t c; nvme_ns_t n; - int row; + int ret, num_ns; struct table *t; struct table_column columns[] = { {"Namespace", LEFT, 0}, @@ -6165,45 +6187,48 @@ static void stdout_tabular_subsystem_topology(nvme_subsystem_t s) t = table_create(); if (!t) { - printf("Failed to init table\n"); + nvme_show_error("Failed to init subsys topology table"); return; } if (table_add_columns(t, columns, ARRAY_SIZE(columns)) < 0) { - printf("Failed to add columns\n"); + nvme_show_error("Failed to add subsys topology columns"); 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"); + num_ns = 0; + + nvme_ctrl_for_each_ns(c, n) + num_ns++; + + if (!num_ns) { + ret = subsystem_topology_add_row(t, + "--", /* Namespace */ + "--", /* NSID */ + nvme_ctrl_get_name(c), + nvme_ctrl_get_transport(c), + nvme_ctrl_get_traddr(c), + nvme_ctrl_get_state(c)); + if (ret < 0) goto free_tbl; - } + } else { + nvme_ctrl_for_each_ns(c, n) { + char nsid[32]; - /* col 0: Namespace */ - table_set_value_str(t, 0, row, - nvme_ns_get_name(n), LEFT); - /* col 1: NSID */ - table_set_value_int(t, 1, row, - nvme_ns_get_nsid(n), CENTERED); - /* col 2: Controller */ - table_set_value_str(t, 2, row, - nvme_ctrl_get_name(c), LEFT); - /* col 3: Trtype */ - table_set_value_str(t, 3, row, - nvme_ctrl_get_transport(c), LEFT); - /* col 4: Address */ - table_set_value_str(t, 4, row, - nvme_ctrl_get_traddr(c), LEFT); - /* col 5: State */ - table_set_value_str(t, 5, row, - nvme_ctrl_get_state(c), LEFT); + snprintf(nsid, sizeof(nsid), "%u", + nvme_ns_get_nsid(n)); - table_add_row(t, row); + ret = subsystem_topology_add_row(t, + nvme_ns_get_name(n), + (const char *)nsid, + nvme_ctrl_get_name(c), + nvme_ctrl_get_transport(c), + nvme_ctrl_get_traddr(c), + nvme_ctrl_get_state(c)); + if (ret < 0) + goto free_tbl; + } } } table_print(t); From c41a36731455d1fcdece195d0f1ec4c7f0cd9eb4 Mon Sep 17 00:00:00 2001 From: Nilay Shroff Date: Tue, 31 Mar 2026 19:42:09 +0530 Subject: [PATCH 2/2] nvme-cli: extend show-topology tabular output for multipath subsys The current tabular output of the show-topology command does not display a controller if it has no associated nvme path or namespaces. However, it is valid for a controller within a subsystem to have no namespaces or paths attached. In such cases, it is still useful to display controller information such as the controller name, transport type, address, and state, while leaving NVMe path and namespace-related fields (e.g., nsid, nshead, nspath, anastate etc.) as "--". Update the tabular output of the show-topology command to include controllers and their associated fields regardless of whether any namespaces are present. While we are at it, also dispaly the error message using nvme_show_error() instead of using printf(). This change applies to NVMe multipath subsystems. Signed-off-by: Nilay Shroff [wagi: reformatted argument list and redudant comments] Signed-off-by: Daniel Wagner --- nvme-print-stdout.c | 134 +++++++++++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 45 deletions(-) diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c index 743f5ef518..404dd363dc 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -5945,13 +5945,47 @@ static bool subsystem_iopolicy_filter(const char *name, void *arg) return true; } +static int subsystem_topology_multipath_add_row(struct table *t, + const char *iopolicy, const char *nshead, + const char *nsid, const char *nspath, + const char *anastate, const char *iopolicy_info, + const char *ctrl, const char *trtype, + const char *address, const char *state) +{ + int row; + int col = -1; + + row = table_get_row_id(t); + if (row < 0) { + nvme_show_error("Failed to add subsys topology multipath row"); + return row; + } + + table_set_value_str(t, ++col, row, nshead, CENTERED); + table_set_value_str(t, ++col, row, nsid, CENTERED); + table_set_value_str(t, ++col, row, nspath, CENTERED); + table_set_value_str(t, ++col, row, anastate, CENTERED); + if (!strcmp(iopolicy, "numa") || !strcmp(iopolicy, "queue-depth")) + table_set_value_str(t, ++col, row, iopolicy_info, CENTERED); + table_set_value_str(t, ++col, row, ctrl, CENTERED); + table_set_value_str(t, ++col, row, trtype, CENTERED); + table_set_value_str(t, ++col, row, address, CENTERED); + table_set_value_str(t, ++col, row, state, CENTERED); + + table_add_row(t, row); + + return 0; +} + 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, col; bool first; + char nshead[32], nsid[32]; + char iopolicy_info[256]; + int ret, num_path; struct table *t; const char *iopolicy = nvme_subsystem_get_iopolicy(s); struct table_column columns[] = { @@ -5969,13 +6003,13 @@ static void stdout_tabular_subsystem_topology_multipath(nvme_subsystem_t s) t = table_create(); if (!t) { - printf("Failed to init table\n"); + nvme_show_error("Failed to init subsys topology multipath table"); return; } if (table_add_columns_filter(t, columns, ARRAY_SIZE(columns), subsystem_iopolicy_filter, (void *)s) < 0) { - printf("Failed to add columns\n"); + nvme_show_error("Failed to add subsys topology multipath columns"); goto free_tbl; } @@ -5984,61 +6018,71 @@ static void stdout_tabular_subsystem_topology_multipath(nvme_subsystem_t s) 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, + /* + * 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. */ - col = -1; - /* col 0: NSHead */ if (first) { - table_set_value_str(t, ++col, row, - nvme_ns_get_name(n), LEFT); + snprintf(nshead, sizeof(nshead), "%s", + nvme_ns_get_name(n)); first = false; } else - table_set_value_str(t, ++col, row, - "-->", CENTERED); - /* col 1: NSID */ - table_set_value_int(t, ++col, row, - nvme_ns_get_nsid(n), CENTERED); - /* col 2: NSPath */ - table_set_value_str(t, ++col, row, - nvme_path_get_name(p), LEFT); - /* col 3: ANAState */ - table_set_value_str(t, ++col, row, - nvme_path_get_ana_state(p), LEFT); + snprintf(nshead, sizeof(nshead), "%s", "-->"); + + snprintf(nsid, sizeof(nsid), "%u", nvme_ns_get_nsid(n)); if (!strcmp(iopolicy, "numa")) - /* col 4: Nodes */ - table_set_value_str(t, ++col, row, - nvme_path_get_numa_nodes(p), CENTERED); + snprintf(iopolicy_info, sizeof(iopolicy_info), + "%s", nvme_path_get_numa_nodes(p)); else if (!strcmp(iopolicy, "queue-depth")) - /* col 4 : Qdepth */ - table_set_value_int(t, ++col, row, - nvme_path_get_queue_depth(p), CENTERED); - - /* col 5: Controller */ - table_set_value_str(t, ++col, row, - nvme_ctrl_get_name(c), LEFT); - /* col 6: TrType */ - table_set_value_str(t, ++col, row, - nvme_ctrl_get_transport(c), LEFT); - /* col 7: Address */ - table_set_value_str(t, ++col, row, - nvme_ctrl_get_traddr(c), LEFT); - /* col 8: State */ - table_set_value_str(t, ++col, row, - nvme_ctrl_get_state(c), LEFT); - - table_add_row(t, row); + snprintf(iopolicy_info, sizeof(iopolicy_info), + "%d", nvme_path_get_queue_depth(p)); + + ret = subsystem_topology_multipath_add_row(t, + iopolicy, + nshead, + nsid, + nvme_path_get_name(p), + nvme_path_get_ana_state(p), + iopolicy_info, + nvme_ctrl_get_name(c), + nvme_ctrl_get_transport(c), + nvme_ctrl_get_traddr(c), + nvme_ctrl_get_state(c)); + if (ret < 0) + goto free_tbl; + } + } + + /* + * Next we print controller in the subsystem which may not have any + * nvme path associated to it. + */ + nvme_subsystem_for_each_ctrl(s, c) { + num_path = 0; + nvme_ctrl_for_each_path(c, p) + num_path++; + + if (!num_path) { + ret = subsystem_topology_multipath_add_row(t, + iopolicy, + "--", /* NSHead */ + "--", /* NSID */ + "--", /* NSPath */ + "--", /* ANAState */ + "--", /* Nodes/Qdepth */ + nvme_ctrl_get_name(c), + nvme_ctrl_get_transport(c), + nvme_ctrl_get_traddr(c), + nvme_ctrl_get_state(c)); + if (ret < 0) + goto free_tbl; } } + table_print(t); free_tbl: table_free(t);