Skip to content

Commit 15f7bcc

Browse files
shroffniigaw
authored andcommitted
nvme: extend show-topology command to add support for multipath
This commit enhances the show-topology command by adding support for NVMe multipath. With this change, users can now list all paths to a namespace from its corresponding head node device. Each NVMe path entry then also includes additional details such as ANA state, NUMA node, and queue depth, improving visibility into multipath configs. This information can be particularly helpful for debugging and analyzing NVMe multipath setups. To support this functionality, the "--ranking" option of the nvme show-topology command has been extended with a new sub-option: "multipath". Since this enhancement is specific to NVMe multipath, the iopolicy configured under each subsystem is now always displayed. Previously, iopolicy was shown only with nvme show-topology verbose output, but it is now included by default to improve usability and provide better context when reviewing multipath configurations via show-topology. With this update, users can view the multipath topology of a multi controller/port NVMe disk. Examples: $ nvme show-topology -r multipath nvme-subsys2 - NQN=nvmet_subsystem hostnqn=nqn.2014-08.org.nvmexpress:uuid:12b49f6e-0276-4746-b10c-56815b7e6dc2 iopolicy=numa _ _ _<head-node> / _ _ _ <ana-state> / / _ _ _ <numa-node-list> / / / | / / +- nvme2n1 (ns 1) / / \ | | +- nvme2c2n1 optimized 1,2 nvme2 tcp traddr=127.0.0.2,trsvcid=4460,src_addr=127.0.0.1 live +- nvme2c3n1 optimized 3,4 nvme3 tcp traddr=127.0.0.3,trsvcid=4460,src_addr=127.0.0.1 live For iopolicy=numa, only NUMA node list is shown (queue depth is hidden). $ nvme show-topology -r multipath nvme-subsys2 - NQN=nvmet_subsystem hostnqn=nqn.2014-08.org.nvmexpress:uuid:12b49f6e-0276-4746-b10c-56815b7e6dc2 iopolicy=queue-depth _ _ _<head-node> / _ _ _ <ana-state> / / / / _ _ _<queue-depth> | / / +- nvme2n1 (ns 1) / / \ | | +- nvme2c2n1 optimized 0 nvme2 tcp traddr=127.0.0.2,trsvcid=4460,src_addr=127.0.0.1 live +- nvme2c3n1 optimized 0 nvme3 tcp traddr=127.0.0.3,trsvcid=4460,src_addr=127.0.0.1 live For iopolicy=queue-depth, queue depth is shown (NUMA node list is hidden). $ nvme show-topology -r multipath nvme-subsys2 - NQN=nvmet_subsystem hostnqn=nqn.2014-08.org.nvmexpress:uuid:12b49f6e-0276-4746-b10c-56815b7e6dc2 iopolicy=round-robin _ _ _<head-node> / _ _ _ <ana-state> / / / / | / +- nvme2n1 (ns 1) / \ | +- nvme2c2n1 optimized nvme2 tcp traddr=127.0.0.2,trsvcid=4460,src_addr=127.0.0.1 live +- nvme2c3n1 optimized nvme3 tcp traddr=127.0.0.3,trsvcid=4460,src_addr=127.0.0.1 live For iopolicy=round-robin, both NUMA node list and queue depth are hidden. Note: The annotations above (e.g., <numa-node-list>, <ana-state>, <head-node>, <queue-depth>) are for illustration only and are not part of the actual command output. A more human-friendly tabular format will be introduced in a follow-up patches. Signed-off-by: Nilay Shroff <[email protected]> Reviewed-by: Hannes Reinecke <[email protected]> Signed-off-by: Daniel Wagner <[email protected]>
1 parent 9d55162 commit 15f7bcc

7 files changed

Lines changed: 120 additions & 12 deletions

File tree

nvme-print-binary.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ static struct print_ops binary_print_ops = {
428428
.print_nvme_subsystem_list = NULL,
429429
.topology_ctrl = NULL,
430430
.topology_namespace = NULL,
431+
.topology_multipath = NULL,
431432

432433
/* status and error messages */
433434
.connect_msg = NULL,

nvme-print-json.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4743,26 +4743,48 @@ static unsigned int json_subsystem_topology_multipath(nvme_subsystem_t s,
47434743
nvme_ns_t n;
47444744
nvme_path_t p;
47454745
unsigned int i = 0;
4746+
const char *iopolicy = nvme_subsystem_get_iopolicy(s);
47464747

47474748
nvme_subsystem_for_each_ns(s, n) {
47484749
struct json_object *ns_attrs;
47494750
struct json_object *paths;
47504751

47514752
ns_attrs = json_create_object();
47524753
obj_add_int(ns_attrs, "NSID", nvme_ns_get_nsid(n));
4754+
obj_add_str(ns_attrs, "Name", nvme_ns_get_name(n));
47534755

47544756
paths = json_create_array();
47554757
nvme_namespace_for_each_path(n, p) {
47564758
struct json_object *path_attrs;
4757-
4758-
nvme_ctrl_t c = nvme_path_get_ctrl(p);
4759+
struct json_object *ctrls, *ctrl_attrs;
4760+
nvme_ctrl_t c;
47594761

47604762
path_attrs = json_create_object();
4761-
obj_add_str(path_attrs, "Name", nvme_ctrl_get_name(c));
4762-
obj_add_str(path_attrs, "Transport", nvme_ctrl_get_transport(c));
4763-
obj_add_str(path_attrs, "Address", nvme_ctrl_get_address(c));
4764-
obj_add_str(path_attrs, "State", nvme_ctrl_get_state(c));
4763+
obj_add_str(path_attrs, "Path", nvme_path_get_name(p));
47654764
obj_add_str(path_attrs, "ANAState", nvme_path_get_ana_state(p));
4765+
4766+
/*
4767+
* For iopolicy numa exclude "Qdepth", for iopolicy
4768+
* queue-depth exclude "NUMANodes" and for iopolicy
4769+
* round-robin exclude both "Qdepth" and "NUMANodes".
4770+
*/
4771+
if (!strcmp(iopolicy, "numa"))
4772+
obj_add_str(path_attrs, "NUMANodes",
4773+
nvme_path_get_numa_nodes(p));
4774+
else if (!strcmp(iopolicy, "queue-depth"))
4775+
obj_add_int(path_attrs, "Qdepth",
4776+
nvme_path_get_queue_depth(p));
4777+
4778+
c = nvme_path_get_ctrl(p);
4779+
ctrls = json_create_array();
4780+
ctrl_attrs = json_create_object();
4781+
obj_add_str(ctrl_attrs, "Name", nvme_ctrl_get_name(c));
4782+
obj_add_str(ctrl_attrs, "Transport", nvme_ctrl_get_transport(c));
4783+
obj_add_str(ctrl_attrs, "Address", nvme_ctrl_get_address(c));
4784+
obj_add_str(ctrl_attrs, "State", nvme_ctrl_get_state(c));
4785+
array_add_obj(ctrls, ctrl_attrs);
4786+
obj_add_array(path_attrs, "Controller", ctrls);
4787+
47664788
array_add_obj(paths, path_attrs);
47674789
}
47684790
obj_add_array(ns_attrs, "Paths", paths);
@@ -4787,6 +4809,7 @@ static void json_print_nvme_subsystem_topology(nvme_subsystem_t s,
47874809

47884810
ns_attrs = json_create_object();
47894811
obj_add_int(ns_attrs, "NSID", nvme_ns_get_nsid(n));
4812+
obj_add_str(ns_attrs, "Name", nvme_ns_get_name(n));
47904813

47914814
ctrl = json_create_array();
47924815
ctrl_attrs = json_create_object();
@@ -5546,6 +5569,7 @@ static struct print_ops json_print_ops = {
55465569
.print_nvme_subsystem_list = json_print_nvme_subsystem_list,
55475570
.topology_ctrl = json_simple_topology,
55485571
.topology_namespace = json_simple_topology,
5572+
.topology_multipath = json_simple_topology,
55495573

55505574
/* status and error messages */
55515575
.connect_msg = json_connect_msg,

nvme-print-stdout.c

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,8 @@ static void stdout_subsys_config(nvme_subsystem_t s)
11261126
nvme_subsystem_get_nqn(s));
11271127
printf("%*s hostnqn=%s\n", len, " ",
11281128
nvme_host_get_hostnqn(nvme_subsystem_get_host(s)));
1129+
printf("%*s iopolicy=%s\n", len, " ",
1130+
nvme_subsystem_get_iopolicy(s));
11291131

11301132
if (stdout_print_ops.flags & VERBOSE) {
11311133
printf("%*s model=%s\n", len, " ",
@@ -1134,8 +1136,6 @@ static void stdout_subsys_config(nvme_subsystem_t s)
11341136
nvme_subsystem_get_serial(s));
11351137
printf("%*s firmware=%s\n", len, " ",
11361138
nvme_subsystem_get_fw_rev(s));
1137-
printf("%*s iopolicy=%s\n", len, " ",
1138-
nvme_subsystem_get_iopolicy(s));
11391139
printf("%*s type=%s\n", len, " ",
11401140
nvme_subsystem_get_type(s));
11411141
}
@@ -5595,6 +5595,7 @@ static void stdout_subsystem_topology_multipath(nvme_subsystem_t s,
55955595
nvme_ns_t n;
55965596
nvme_path_t p;
55975597
nvme_ctrl_t c;
5598+
const char *iopolicy = nvme_subsystem_get_iopolicy(s);
55985599

55995600
if (ranking == NVME_CLI_TOPO_NAMESPACE) {
56005601
nvme_subsystem_for_each_ns(s, n) {
@@ -5615,7 +5616,7 @@ static void stdout_subsystem_topology_multipath(nvme_subsystem_t s,
56155616
nvme_path_get_ana_state(p));
56165617
}
56175618
}
5618-
} else {
5619+
} else if (ranking == NVME_CLI_TOPO_CTRL) {
56195620
/* NVME_CLI_TOPO_CTRL */
56205621
nvme_subsystem_for_each_ctrl(s, c) {
56215622
printf(" +- %s %s %s\n",
@@ -5636,6 +5637,59 @@ static void stdout_subsystem_topology_multipath(nvme_subsystem_t s,
56365637
}
56375638
}
56385639
}
5640+
} else {
5641+
/* NVME_CLI_TOPO_MULTIPATH */
5642+
nvme_subsystem_for_each_ns(s, n) {
5643+
printf(" +- %s (ns %d)\n",
5644+
nvme_ns_get_name(n),
5645+
nvme_ns_get_nsid(n));
5646+
printf(" \\\n");
5647+
nvme_namespace_for_each_path(n, p) {
5648+
c = nvme_path_get_ctrl(p);
5649+
5650+
if (!strcmp(iopolicy, "numa")) {
5651+
/*
5652+
* For iopolicy numa, exclude printing
5653+
* qdepth.
5654+
*/
5655+
printf(" +- %s %s %s %s %s %s %s\n",
5656+
nvme_path_get_name(p),
5657+
nvme_path_get_ana_state(p),
5658+
nvme_path_get_numa_nodes(p),
5659+
nvme_ctrl_get_name(c),
5660+
nvme_ctrl_get_transport(c),
5661+
nvme_ctrl_get_address(c),
5662+
nvme_ctrl_get_state(c));
5663+
5664+
} else if (!strcmp(iopolicy, "queue-depth")) {
5665+
/*
5666+
* For iopolicy queue-depth, exclude
5667+
* printing numa nodes.
5668+
*/
5669+
printf(" +- %s %s %d %s %s %s %s\n",
5670+
nvme_path_get_name(p),
5671+
nvme_path_get_ana_state(p),
5672+
nvme_path_get_queue_depth(p),
5673+
nvme_ctrl_get_name(c),
5674+
nvme_ctrl_get_transport(c),
5675+
nvme_ctrl_get_address(c),
5676+
nvme_ctrl_get_state(c));
5677+
5678+
} else { /* round-robin */
5679+
/*
5680+
* For iopolicy round-robin, exclude
5681+
* printing numa nodes and qdepth.
5682+
*/
5683+
printf(" +- %s %s %s %s %s %s\n",
5684+
nvme_path_get_name(p),
5685+
nvme_path_get_ana_state(p),
5686+
nvme_ctrl_get_name(c),
5687+
nvme_ctrl_get_transport(c),
5688+
nvme_ctrl_get_address(c),
5689+
nvme_ctrl_get_state(c));
5690+
}
5691+
}
5692+
}
56395693
}
56405694
}
56415695

@@ -5657,7 +5711,7 @@ static void stdout_subsystem_topology(nvme_subsystem_t s,
56575711
nvme_ctrl_get_state(c));
56585712
}
56595713
}
5660-
} else {
5714+
} else if (ranking == NVME_CLI_TOPO_CTRL) {
56615715
/* NVME_CLI_TOPO_CTRL */
56625716
nvme_subsystem_for_each_ctrl(s, c) {
56635717
printf(" +- %s %s %s\n",
@@ -5671,6 +5725,23 @@ static void stdout_subsystem_topology(nvme_subsystem_t s,
56715725
nvme_ctrl_get_state(c));
56725726
}
56735727
}
5728+
} else {
5729+
/* NVME_CLI_TOPO_MULTIPATH */
5730+
nvme_subsystem_for_each_ctrl(s, c) {
5731+
nvme_ctrl_for_each_ns(c, n) {
5732+
c = nvme_ns_get_ctrl(n);
5733+
5734+
printf(" +- %s (ns %d)\n",
5735+
nvme_ns_get_name(n),
5736+
nvme_ns_get_nsid(n));
5737+
printf(" \\\n");
5738+
printf(" +- %s %s %s %s\n",
5739+
nvme_ctrl_get_name(c),
5740+
nvme_ctrl_get_transport(c),
5741+
nvme_ctrl_get_address(c),
5742+
nvme_ctrl_get_state(c));
5743+
}
5744+
}
56745745
}
56755746
}
56765747

@@ -5717,6 +5788,11 @@ static void stdout_topology_ctrl(nvme_root_t r)
57175788
stdout_simple_topology(r, NVME_CLI_TOPO_CTRL);
57185789
}
57195790

5791+
static void stdout_topology_multipath(nvme_root_t r)
5792+
{
5793+
stdout_simple_topology(r, NVME_CLI_TOPO_MULTIPATH);
5794+
}
5795+
57205796
static void stdout_message(bool error, const char *msg, va_list ap)
57215797
{
57225798
vfprintf(error ? stderr : stdout, msg, ap);
@@ -6286,6 +6362,7 @@ static struct print_ops stdout_print_ops = {
62866362
.print_nvme_subsystem_list = stdout_subsystem_list,
62876363
.topology_ctrl = stdout_topology_ctrl,
62886364
.topology_namespace = stdout_topology_namespace,
6365+
.topology_multipath = stdout_topology_multipath,
62896366

62906367
/* status and error messages */
62916368
.connect_msg = stdout_connect_msg,

nvme-print.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1551,8 +1551,10 @@ void nvme_show_topology(nvme_root_t r,
15511551
{
15521552
if (ranking == NVME_CLI_TOPO_NAMESPACE)
15531553
nvme_print(topology_namespace, flags, r);
1554-
else
1554+
else if (ranking == NVME_CLI_TOPO_CTRL)
15551555
nvme_print(topology_ctrl, flags, r);
1556+
else
1557+
nvme_print(topology_multipath, flags, r);
15561558
}
15571559

15581560
void nvme_show_message(bool error, const char *msg, ...)

nvme-print.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ struct print_ops {
106106
void (*print_nvme_subsystem_list)(nvme_root_t r, bool show_ana);
107107
void (*topology_ctrl)(nvme_root_t r);
108108
void (*topology_namespace)(nvme_root_t r);
109+
void (*topology_multipath)(nvme_root_t r);
109110

110111
/* status and error messages */
111112
void (*connect_msg)(nvme_ctrl_t c);

nvme.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10204,7 +10204,7 @@ static int tls_key(int argc, char **argv, struct command *command, struct plugin
1020410204
static int show_topology_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
1020510205
{
1020610206
const char *desc = "Show the topology\n";
10207-
const char *ranking = "Ranking order: namespace|ctrl";
10207+
const char *ranking = "Ranking order: namespace|ctrl|multipath";
1020810208
nvme_print_flags_t flags;
1020910209
_cleanup_nvme_root_ nvme_root_t r = NULL;
1021010210
char *devname = NULL;
@@ -10240,6 +10240,8 @@ static int show_topology_cmd(int argc, char **argv, struct command *command, str
1024010240
rank = NVME_CLI_TOPO_NAMESPACE;
1024110241
} else if (!strcmp(cfg.ranking, "ctrl")) {
1024210242
rank = NVME_CLI_TOPO_CTRL;
10243+
} else if (!strcmp(cfg.ranking, "multipath")) {
10244+
rank = NVME_CLI_TOPO_MULTIPATH;
1024310245
} else {
1024410246
nvme_show_error("Invalid ranking argument: %s", cfg.ranking);
1024510247
return -EINVAL;

nvme.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ typedef uint32_t nvme_print_flags_t;
4646
enum nvme_cli_topo_ranking {
4747
NVME_CLI_TOPO_NAMESPACE,
4848
NVME_CLI_TOPO_CTRL,
49+
NVME_CLI_TOPO_MULTIPATH,
4950
};
5051

5152
#define SYS_NVME "/sys/class/nvme"

0 commit comments

Comments
 (0)