infiniband-diags: initial port of linux ib diags
[mirror/winof/.git] / tools / infiniband_diags / src / ibnetdiscover.c
1 /*\r
2  * Copyright (c) 2004-2008 Voltaire Inc.  All rights reserved.\r
3  * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.\r
4  *\r
5  * This software is available to you under a choice of one of two\r
6  * licenses.  You may choose to be licensed under the terms of the GNU\r
7  * General Public License (GPL) Version 2, available from the file\r
8  * COPYING in the main directory of this source tree, or the\r
9  * OpenIB.org BSD license below:\r
10  *\r
11  *     Redistribution and use in source and binary forms, with or\r
12  *     without modification, are permitted provided that the following\r
13  *     conditions are met:\r
14  *\r
15  *      - Redistributions of source code must retain the above\r
16  *        copyright notice, this list of conditions and the following\r
17  *        disclaimer.\r
18  *\r
19  *      - Redistributions in binary form must reproduce the above\r
20  *        copyright notice, this list of conditions and the following\r
21  *        disclaimer in the documentation and/or other materials\r
22  *        provided with the distribution.\r
23  *\r
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
25  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
26  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
27  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
28  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
29  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
30  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
31  * SOFTWARE.\r
32  *\r
33  */\r
34 \r
35 #if HAVE_CONFIG_H\r
36 #  include <config.h>\r
37 #endif /* HAVE_CONFIG_H */\r
38 \r
39 #define _GNU_SOURCE\r
40 #include <stdio.h>\r
41 #include <stdlib.h>\r
42 #include <unistd.h>\r
43 #include <stdarg.h>\r
44 #include <time.h>\r
45 #include <string.h>\r
46 #include <getopt.h>\r
47 #include <errno.h>\r
48 #include <inttypes.h>\r
49 \r
50 #include <infiniband/common.h>\r
51 #include <infiniband/umad.h>\r
52 #include <infiniband/mad.h>\r
53 #include <infiniband/complib/cl_nodenamemap.h>\r
54 \r
55 #include "ibnetdiscover.h"\r
56 #include "grouping.h"\r
57 #include "ibdiag_common.h"\r
58 \r
59 static char *node_type_str[] = {\r
60         "???",\r
61         "ca",\r
62         "switch",\r
63         "router",\r
64         "iwarp rnic"\r
65 };\r
66 \r
67 static char *linkwidth_str[] = {\r
68         "??",\r
69         "1x",\r
70         "4x",\r
71         "??",\r
72         "8x",\r
73         "??",\r
74         "??",\r
75         "??",\r
76         "12x"\r
77 };\r
78 \r
79 static char *linkspeed_str[] = {\r
80         "???",\r
81         "SDR",\r
82         "DDR",\r
83         "???",\r
84         "QDR"\r
85 };\r
86 \r
87 static int timeout = 2000;              /* ms */\r
88 static int dumplevel = 0;\r
89 static int verbose;\r
90 static FILE *f;\r
91 \r
92 char *argv0 = "ibnetdiscover";\r
93 \r
94 static char *node_name_map_file = NULL;\r
95 static nn_map_t *node_name_map = NULL;\r
96 \r
97 Node *nodesdist[MAXHOPS+1];     /* last is Ca list */\r
98 Node *mynode;\r
99 int maxhops_discovered = 0;\r
100 \r
101 struct ChassisList *chassis = NULL;\r
102 \r
103 static char *\r
104 get_linkwidth_str(int linkwidth)\r
105 {\r
106         if (linkwidth > 8)\r
107                 return linkwidth_str[0];\r
108         else\r
109                 return linkwidth_str[linkwidth];\r
110 }\r
111 \r
112 static char *\r
113 get_linkspeed_str(int linkspeed)\r
114 {\r
115         if (linkspeed > 4)\r
116                 return linkspeed_str[0];\r
117         else\r
118                 return linkspeed_str[linkspeed];\r
119 }\r
120 \r
121 static inline const char*\r
122 node_type_str2(Node *node)\r
123 {\r
124         switch(node->type) {\r
125         case SWITCH_NODE: return "SW";\r
126         case CA_NODE:     return "CA";\r
127         case ROUTER_NODE: return "RT";\r
128         }\r
129         return "??";\r
130 }\r
131 \r
132 void\r
133 decode_port_info(void *pi, Port *port)\r
134 {\r
135         mad_decode_field(pi, IB_PORT_LID_F, &port->lid);\r
136         mad_decode_field(pi, IB_PORT_LMC_F, &port->lmc);\r
137         mad_decode_field(pi, IB_PORT_STATE_F, &port->state);\r
138         mad_decode_field(pi, IB_PORT_PHYS_STATE_F, &port->physstate);\r
139         mad_decode_field(pi, IB_PORT_LINK_WIDTH_ACTIVE_F, &port->linkwidth);\r
140         mad_decode_field(pi, IB_PORT_LINK_SPEED_ACTIVE_F, &port->linkspeed);\r
141 }\r
142 \r
143 \r
144 int\r
145 get_port(Port *port, int portnum, ib_portid_t *portid)\r
146 {\r
147         char portinfo[64];\r
148         void *pi = portinfo;\r
149 \r
150         port->portnum = portnum;\r
151 \r
152         if (!smp_query(pi, portid, IB_ATTR_PORT_INFO, portnum, timeout))\r
153                 return -1;\r
154         decode_port_info(pi, port);\r
155 \r
156         DEBUG("portid %s portnum %d: lid %d state %d physstate %d %s %s",\r
157                 portid2str(portid), portnum, port->lid, port->state, port->physstate, get_linkwidth_str(port->linkwidth), get_linkspeed_str(port->linkspeed));\r
158         return 1;\r
159 }\r
160 /*\r
161  * Returns 0 if non switch node is found, 1 if switch is found, -1 if error.\r
162  */\r
163 int\r
164 get_node(Node *node, Port *port, ib_portid_t *portid)\r
165 {\r
166         char portinfo[64];\r
167         char switchinfo[64];\r
168         void *pi = portinfo, *ni = node->nodeinfo, *nd = node->nodedesc;\r
169         void *si = switchinfo;\r
170 \r
171         if (!smp_query(ni, portid, IB_ATTR_NODE_INFO, 0, timeout))\r
172                 return -1;\r
173 \r
174         mad_decode_field(ni, IB_NODE_GUID_F, &node->nodeguid);\r
175         mad_decode_field(ni, IB_NODE_TYPE_F, &node->type);\r
176         mad_decode_field(ni, IB_NODE_NPORTS_F, &node->numports);\r
177         mad_decode_field(ni, IB_NODE_DEVID_F, &node->devid);\r
178         mad_decode_field(ni, IB_NODE_VENDORID_F, &node->vendid);\r
179         mad_decode_field(ni, IB_NODE_SYSTEM_GUID_F, &node->sysimgguid);\r
180         mad_decode_field(ni, IB_NODE_PORT_GUID_F, &node->portguid);\r
181         mad_decode_field(ni, IB_NODE_LOCAL_PORT_F, &node->localport);\r
182         port->portnum = node->localport;\r
183         port->portguid = node->portguid;\r
184 \r
185         if (!smp_query(nd, portid, IB_ATTR_NODE_DESC, 0, timeout))\r
186                 return -1;\r
187 \r
188         if (!smp_query(pi, portid, IB_ATTR_PORT_INFO, 0, timeout))\r
189                 return -1;\r
190         decode_port_info(pi, port);\r
191 \r
192         if (node->type != SWITCH_NODE)\r
193                 return 0;\r
194 \r
195         node->smalid = port->lid;\r
196         node->smalmc = port->lmc;\r
197 \r
198         /* after we have the sma information find out the real PortInfo for this port */\r
199         if (!smp_query(pi, portid, IB_ATTR_PORT_INFO, node->localport, timeout))\r
200                 return -1;\r
201         decode_port_info(pi, port);\r
202 \r
203         if (!smp_query(si, portid, IB_ATTR_SWITCH_INFO, 0, timeout))\r
204                 node->smaenhsp0 = 0;    /* assume base SP0 */\r
205         else\r
206                 mad_decode_field(si, IB_SW_ENHANCED_PORT0_F, &node->smaenhsp0);\r
207 \r
208         DEBUG("portid %s: got switch node %" PRIx64 " '%s'",\r
209               portid2str(portid), node->nodeguid, node->nodedesc);\r
210         return 1;\r
211 }\r
212 \r
213 static int\r
214 extend_dpath(ib_dr_path_t *path, int nextport)\r
215 {\r
216         if (path->cnt+2 >= sizeof(path->p))\r
217                 return -1;\r
218         ++path->cnt;\r
219         if (path->cnt > maxhops_discovered)\r
220                 maxhops_discovered = path->cnt;\r
221         path->p[path->cnt] = nextport;\r
222         return path->cnt;\r
223 }\r
224 \r
225 static void\r
226 dump_endnode(ib_portid_t *path, char *prompt, Node *node, Port *port)\r
227 {\r
228         if (!dumplevel)\r
229                 return;\r
230 \r
231         fprintf(f, "%s -> %s %s {%016" PRIx64 "} portnum %d lid %d-%d\"%s\"\n",\r
232                 portid2str(path), prompt,\r
233                 (node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"),\r
234                 node->nodeguid, node->type == SWITCH_NODE ? 0 : port->portnum,\r
235                 port->lid, port->lid + (1 << port->lmc) - 1,\r
236                 clean_nodedesc(node->nodedesc));\r
237 }\r
238 \r
239 #define HASHGUID(guid)          ((uint32_t)(((uint32_t)(guid) * 101) ^ ((uint32_t)((guid) >> 32) * 103)))\r
240 #define HTSZ 137\r
241 \r
242 static Node *nodestbl[HTSZ];\r
243 \r
244 static Node *\r
245 find_node(Node *new)\r
246 {\r
247         int hash = HASHGUID(new->nodeguid) % HTSZ;\r
248         Node *node;\r
249 \r
250         for (node = nodestbl[hash]; node; node = node->htnext)\r
251                 if (node->nodeguid == new->nodeguid)\r
252                         return node;\r
253 \r
254         return NULL;\r
255 }\r
256 \r
257 static Node *\r
258 create_node(Node *temp, ib_portid_t *path, int dist)\r
259 {\r
260         Node *node;\r
261         int hash = HASHGUID(temp->nodeguid) % HTSZ;\r
262 \r
263         node = malloc(sizeof(*node));\r
264         if (!node)\r
265                 return NULL;\r
266 \r
267         memcpy(node, temp, sizeof(*node));\r
268         node->dist = dist;\r
269         node->path = *path;\r
270 \r
271         node->htnext = nodestbl[hash];\r
272         nodestbl[hash] = node;\r
273 \r
274         if (node->type != SWITCH_NODE)\r
275                 dist = MAXHOPS;         /* special Ca list */\r
276 \r
277         node->dnext = nodesdist[dist];\r
278         nodesdist[dist] = node;\r
279 \r
280         return node;\r
281 }\r
282 \r
283 static Port *\r
284 find_port(Node *node, Port *port)\r
285 {\r
286         Port *old;\r
287 \r
288         for (old = node->ports; old; old = old->next)\r
289                 if (old->portnum == port->portnum)\r
290                         return old;\r
291 \r
292         return NULL;\r
293 }\r
294 \r
295 static Port *\r
296 create_port(Node *node, Port *temp)\r
297 {\r
298         Port *port;\r
299 \r
300         port = malloc(sizeof(*port));\r
301         if (!port)\r
302                 return NULL;\r
303 \r
304         memcpy(port, temp, sizeof(*port));\r
305         port->node = node;\r
306         port->next = node->ports;\r
307         node->ports = port;\r
308 \r
309         return port;\r
310 }\r
311 \r
312 static void\r
313 link_ports(Node *node, Port *port, Node *remotenode, Port *remoteport)\r
314 {\r
315         DEBUG("linking: 0x%" PRIx64 " %p->%p:%u and 0x%" PRIx64 " %p->%p:%u",\r
316                 node->nodeguid, node, port, port->portnum,\r
317                 remotenode->nodeguid, remotenode, remoteport, remoteport->portnum);\r
318         if (port->remoteport)\r
319                 port->remoteport->remoteport = NULL;\r
320         if (remoteport->remoteport)\r
321                 remoteport->remoteport->remoteport = NULL;\r
322         port->remoteport = remoteport;\r
323         remoteport->remoteport = port;\r
324 }\r
325 \r
326 static int\r
327 handle_port(Node *node, Port *port, ib_portid_t *path, int portnum, int dist)\r
328 {\r
329         Node node_buf;\r
330         Port port_buf;\r
331         Node *remotenode, *oldnode;\r
332         Port *remoteport, *oldport;\r
333 \r
334         memset(&node_buf, 0, sizeof(node_buf));\r
335         memset(&port_buf, 0, sizeof(port_buf));\r
336 \r
337         DEBUG("handle node %p port %p:%d dist %d", node, port, portnum, dist);\r
338         if (port->physstate != 5)       /* LinkUp */\r
339                 return -1;\r
340 \r
341         if (extend_dpath(&path->drpath, portnum) < 0)\r
342                 return -1;\r
343 \r
344         if (get_node(&node_buf, &port_buf, path) < 0) {\r
345                 IBWARN("NodeInfo on %s failed, skipping port",\r
346                         portid2str(path));\r
347                 path->drpath.cnt--;     /* restore path */\r
348                 return -1;\r
349         }\r
350 \r
351         oldnode = find_node(&node_buf);\r
352         if (oldnode)\r
353                 remotenode = oldnode;\r
354         else if (!(remotenode = create_node(&node_buf, path, dist + 1)))\r
355                 IBERROR("no memory");\r
356 \r
357         oldport = find_port(remotenode, &port_buf);\r
358         if (oldport) {\r
359                 remoteport = oldport;\r
360                 if (node != remotenode || port != remoteport)\r
361                         IBWARN("port moving...");\r
362         } else if (!(remoteport = create_port(remotenode, &port_buf)))\r
363                 IBERROR("no memory");\r
364 \r
365         dump_endnode(path, oldnode ? "known remote" : "new remote",\r
366                      remotenode, remoteport);\r
367 \r
368         link_ports(node, port, remotenode, remoteport);\r
369 \r
370         path->drpath.cnt--;     /* restore path */\r
371         return 0;\r
372 }\r
373 \r
374 /*\r
375  * Return 1 if found, 0 if not, -1 on errors.\r
376  */\r
377 static int\r
378 discover(ib_portid_t *from)\r
379 {\r
380         Node node_buf;\r
381         Port port_buf;\r
382         Node *node;\r
383         Port *port;\r
384         int i;\r
385         int dist = 0;\r
386         ib_portid_t *path;\r
387 \r
388         DEBUG("from %s", portid2str(from));\r
389 \r
390         memset(&node_buf, 0, sizeof(node_buf));\r
391         memset(&port_buf, 0, sizeof(port_buf));\r
392 \r
393         if (get_node(&node_buf, &port_buf, from) < 0) {\r
394                 IBWARN("can't reach node %s", portid2str(from));\r
395                 return -1;\r
396         }\r
397 \r
398         node = create_node(&node_buf, from, 0);\r
399         if (!node)\r
400                 IBERROR("out of memory");\r
401 \r
402         mynode = node;\r
403 \r
404         port = create_port(node, &port_buf);\r
405         if (!port)\r
406                 IBERROR("out of memory");\r
407 \r
408         if (node->type != SWITCH_NODE &&\r
409             handle_port(node, port, from, node->localport, 0) < 0)\r
410                 return 0;\r
411 \r
412         for (dist = 0; dist < MAXHOPS; dist++) {\r
413 \r
414                 for (node = nodesdist[dist]; node; node = node->dnext) {\r
415 \r
416                         path = &node->path;\r
417 \r
418                         DEBUG("dist %d node %p", dist, node);\r
419                         dump_endnode(path, "processing", node, port);\r
420 \r
421                         for (i = 1; i <= node->numports; i++) {\r
422                                 if (i == node->localport)\r
423                                         continue;\r
424 \r
425                                 if (get_port(&port_buf, i, path) < 0) {\r
426                                         IBWARN("can't reach node %s port %d", portid2str(path), i);\r
427                                         continue;\r
428                                 }\r
429 \r
430                                 port = find_port(node, &port_buf);\r
431                                 if (port)\r
432                                         continue;\r
433 \r
434                                 port = create_port(node, &port_buf);\r
435                                 if (!port)\r
436                                         IBERROR("out of memory");\r
437 \r
438                                 /* If switch, set port GUID to node GUID */\r
439                                 if (node->type == SWITCH_NODE)\r
440                                         port->portguid = node->portguid;\r
441 \r
442                                 handle_port(node, port, path, i, dist);\r
443                         }\r
444                 }\r
445         }\r
446 \r
447         return 0;\r
448 }\r
449 \r
450 char *\r
451 node_name(Node *node)\r
452 {\r
453         static char buf[256];\r
454 \r
455         switch(node->type) {\r
456         case SWITCH_NODE:\r
457                 sprintf(buf, "\"%s", "S");\r
458                 break;\r
459         case CA_NODE:\r
460                 sprintf(buf, "\"%s", "H");\r
461                 break;\r
462         case ROUTER_NODE:\r
463                 sprintf(buf, "\"%s", "R");\r
464                 break;\r
465         default:\r
466                 sprintf(buf, "\"%s", "?");\r
467                 break;\r
468         }\r
469         sprintf(buf+2, "-%016" PRIx64 "\"", node->nodeguid);\r
470 \r
471         return buf;\r
472 }\r
473 \r
474 void\r
475 list_node(Node *node)\r
476 {\r
477         char *node_type;\r
478         char *nodename = remap_node_name(node_name_map, node->nodeguid,\r
479                                               node->nodedesc);\r
480 \r
481         switch(node->type) {\r
482         case SWITCH_NODE:\r
483                 node_type = "Switch";\r
484                 break;\r
485         case CA_NODE:\r
486                 node_type = "Ca";\r
487                 break;\r
488         case ROUTER_NODE:\r
489                 node_type = "Router";\r
490                 break;\r
491         default:\r
492                 node_type = "???";\r
493                 break;\r
494         }\r
495         fprintf(f, "%s\t : 0x%016" PRIx64 " ports %d devid 0x%x vendid 0x%x \"%s\"\n",\r
496                 node_type,\r
497                 node->nodeguid, node->numports, node->devid, node->vendid,\r
498                 nodename);\r
499 \r
500         free(nodename);\r
501 }\r
502 \r
503 void\r
504 out_ids(Node *node, int group, char *chname)\r
505 {\r
506         fprintf(f, "\nvendid=0x%x\ndevid=0x%x\n", node->vendid, node->devid);\r
507         if (node->sysimgguid)\r
508                 fprintf(f, "sysimgguid=0x%" PRIx64, node->sysimgguid);\r
509         if (group\r
510             && node->chrecord && node->chrecord->chassisnum) {\r
511                 fprintf(f, "\t\t# Chassis %d", node->chrecord->chassisnum);\r
512                 if (chname)\r
513                         fprintf(f, " (%s)", chname);\r
514                 if (is_xsigo_tca(node->nodeguid) && node->ports->remoteport)\r
515                         fprintf(f, " slot %d", node->ports->remoteport->portnum);\r
516         }\r
517         fprintf(f, "\n");\r
518 }\r
519 \r
520 uint64_t\r
521 out_chassis(int chassisnum)\r
522 {\r
523         uint64_t guid;\r
524 \r
525         fprintf(f, "\nChassis %d", chassisnum);\r
526         guid = get_chassis_guid(chassisnum);\r
527         if (guid)\r
528                 fprintf(f, " (guid 0x%" PRIx64 ")", guid);\r
529         fprintf(f, "\n");\r
530         return guid;\r
531 }\r
532 \r
533 void\r
534 out_switch(Node *node, int group, char *chname)\r
535 {\r
536         char *str;\r
537         char *nodename = NULL;\r
538 \r
539         out_ids(node, group, chname);\r
540         fprintf(f, "switchguid=0x%" PRIx64, node->nodeguid);\r
541         fprintf(f, "(%" PRIx64 ")", node->portguid);\r
542         /* Currently, only if Voltaire chassis */\r
543         if (group\r
544             && node->chrecord && node->chrecord->chassisnum\r
545             && node->vendid == VTR_VENDOR_ID) {\r
546                 str = get_chassis_type(node->chrecord->chassistype);\r
547                 if (str)\r
548                         fprintf(f, "%s ", str);\r
549                 str = get_chassis_slot(node->chrecord->chassisslot);\r
550                 if (str)\r
551                         fprintf(f, "%s ", str);\r
552                 fprintf(f, "%d Chip %d", node->chrecord->slotnum, node->chrecord->anafanum);\r
553         }\r
554 \r
555         nodename = remap_node_name(node_name_map, node->nodeguid,\r
556                                 node->nodedesc);\r
557 \r
558         fprintf(f, "\nSwitch\t%d %s\t\t# \"%s\" %s port 0 lid %d lmc %d\n",\r
559                 node->numports, node_name(node),\r
560                 nodename,\r
561                 node->smaenhsp0 ? "enhanced" : "base",\r
562                 node->smalid, node->smalmc);\r
563 \r
564         free(nodename);\r
565 }\r
566 \r
567 void\r
568 out_ca(Node *node, int group, char *chname)\r
569 {\r
570         char *node_type;\r
571         char *node_type2;\r
572         char *nodename = remap_node_name(node_name_map, node->nodeguid,\r
573                                               node->nodedesc);\r
574 \r
575         out_ids(node, group, chname);\r
576         switch(node->type) {\r
577         case CA_NODE:\r
578                 node_type = "ca";\r
579                 node_type2 = "Ca";\r
580                 break;\r
581         case ROUTER_NODE:\r
582                 node_type = "rt";\r
583                 node_type2 = "Rt";\r
584                 break;\r
585         default:\r
586                 node_type = "???";\r
587                 node_type2 = "???";\r
588                 break;\r
589         }\r
590 \r
591         fprintf(f, "%sguid=0x%" PRIx64 "\n", node_type, node->nodeguid);\r
592         fprintf(f, "%s\t%d %s\t\t# \"%s\"",\r
593                 node_type2, node->numports, node_name(node),\r
594                 nodename);\r
595         if (group && is_xsigo_hca(node->nodeguid))\r
596                 fprintf(f, " (scp)");\r
597         fprintf(f, "\n");\r
598 \r
599         free(nodename);\r
600 }\r
601 \r
602 static char *\r
603 out_ext_port(Port *port, int group)\r
604 {\r
605         char *str = NULL;\r
606 \r
607         /* Currently, only if Voltaire chassis */\r
608         if (group\r
609             && port->node->chrecord && port->node->vendid == VTR_VENDOR_ID)\r
610                 str = portmapstring(port);\r
611 \r
612         return (str);\r
613 }\r
614 \r
615 void\r
616 out_switch_port(Port *port, int group)\r
617 {\r
618         char *ext_port_str = NULL;\r
619         char *rem_nodename = NULL;\r
620 \r
621         DEBUG("port %p:%d remoteport %p", port, port->portnum, port->remoteport);\r
622         fprintf(f, "[%d]", port->portnum);\r
623 \r
624         ext_port_str = out_ext_port(port, group);\r
625         if (ext_port_str)\r
626                 fprintf(f, "%s", ext_port_str);\r
627 \r
628         rem_nodename = remap_node_name(node_name_map,\r
629                                 port->remoteport->node->nodeguid,\r
630                                 port->remoteport->node->nodedesc);\r
631 \r
632         ext_port_str = out_ext_port(port->remoteport, group);\r
633         fprintf(f, "\t%s[%d]%s",\r
634                 node_name(port->remoteport->node),\r
635                 port->remoteport->portnum,\r
636                 ext_port_str ? ext_port_str : "");\r
637         if (port->remoteport->node->type != SWITCH_NODE)\r
638                 fprintf(f, "(%" PRIx64 ") ", port->remoteport->portguid);\r
639         fprintf(f, "\t\t# \"%s\" lid %d %s%s",\r
640                 rem_nodename,\r
641                 port->remoteport->node->type == SWITCH_NODE ? port->remoteport->node->smalid : port->remoteport->lid,\r
642                 get_linkwidth_str(port->linkwidth),\r
643                 get_linkspeed_str(port->linkspeed));\r
644 \r
645         if (is_xsigo_tca(port->remoteport->portguid))\r
646                 fprintf(f, " slot %d", port->portnum);\r
647         else if (is_xsigo_hca(port->remoteport->portguid))\r
648                 fprintf(f, " (scp)");\r
649         fprintf(f, "\n");\r
650 \r
651         free(rem_nodename);\r
652 }\r
653 \r
654 void\r
655 out_ca_port(Port *port, int group)\r
656 {\r
657         char *str = NULL;\r
658         char *rem_nodename = NULL;\r
659 \r
660         fprintf(f, "[%d]", port->portnum);\r
661         if (port->node->type != SWITCH_NODE)\r
662                 fprintf(f, "(%" PRIx64 ") ", port->portguid);\r
663         fprintf(f, "\t%s[%d]",\r
664                 node_name(port->remoteport->node),\r
665                 port->remoteport->portnum);\r
666         str = out_ext_port(port->remoteport, group);\r
667         if (str)\r
668                 fprintf(f, "%s", str);\r
669         if (port->remoteport->node->type != SWITCH_NODE)\r
670                 fprintf(f, " (%" PRIx64 ") ", port->remoteport->portguid);\r
671 \r
672         rem_nodename = remap_node_name(node_name_map,\r
673                                 port->remoteport->node->nodeguid,\r
674                                 port->remoteport->node->nodedesc);\r
675 \r
676         fprintf(f, "\t\t# lid %d lmc %d \"%s\" lid %d %s%s\n",\r
677                 port->lid, port->lmc, rem_nodename,\r
678                 port->remoteport->node->type == SWITCH_NODE ? port->remoteport->node->smalid : port->remoteport->lid,\r
679                 get_linkwidth_str(port->linkwidth),\r
680                 get_linkspeed_str(port->linkspeed));\r
681 \r
682         free(rem_nodename);\r
683 }\r
684 \r
685 int\r
686 dump_topology(int listtype, int group)\r
687 {\r
688         Node *node;\r
689         Port *port;\r
690         int i = 0, dist = 0;\r
691         time_t t = time(0);\r
692         uint64_t chguid;\r
693         char *chname = NULL;\r
694 \r
695         if (!listtype) {\r
696                 fprintf(f, "#\n# Topology file: generated on %s#\n", ctime(&t));\r
697                 fprintf(f, "# Max of %d hops discovered\n", maxhops_discovered);\r
698                 fprintf(f, "# Initiated from node %016" PRIx64 " port %016" PRIx64 "\n", mynode->nodeguid, mynode->portguid);\r
699         }\r
700 \r
701         /* Make pass on switches */\r
702         if (group && !listtype) {\r
703                 ChassisList *ch = NULL;\r
704 \r
705                 /* Chassis based switches first */\r
706                 for (ch = chassis; ch; ch = ch->next) {\r
707                         int n = 0;\r
708 \r
709                         if (!ch->chassisnum)\r
710                                 continue;\r
711                         chguid = out_chassis(ch->chassisnum);\r
712                         if (chname)\r
713                                 free(chname);\r
714                         chname = NULL;\r
715                         if (is_xsigo_guid(chguid)) {\r
716                                 for (node = nodesdist[MAXHOPS]; node; node = node->dnext) {\r
717                                         if (!node->chrecord ||\r
718                                             !node->chrecord->chassisnum)\r
719                                                 continue;\r
720 \r
721                                         if (node->chrecord->chassisnum != ch->chassisnum)\r
722                                                 continue;\r
723 \r
724                                         if (is_xsigo_hca(node->nodeguid)) {\r
725                                                 chname = remap_node_name(node_name_map,\r
726                                                                 node->nodeguid,\r
727                                                                 node->nodedesc);\r
728                                                 fprintf(f, "Hostname: %s\n", chname);\r
729                                         }\r
730                                 }\r
731                         }\r
732 \r
733                         fprintf(f, "\n# Spine Nodes");\r
734                         for (n = 1; n <= (SPINES_MAX_NUM+1); n++) {\r
735                                 if (ch->spinenode[n]) {\r
736                                         out_switch(ch->spinenode[n], group, chname);\r
737                                         for (port = ch->spinenode[n]->ports; port; port = port->next, i++)\r
738                                                 if (port->remoteport)\r
739                                                         out_switch_port(port, group);\r
740                                 }\r
741                         }\r
742                         fprintf(f, "\n# Line Nodes");\r
743                         for (n = 1; n <= (LINES_MAX_NUM+1); n++) {\r
744                                 if (ch->linenode[n]) {\r
745                                         out_switch(ch->linenode[n], group, chname);\r
746                                         for (port = ch->linenode[n]->ports; port; port = port->next, i++)\r
747                                                 if (port->remoteport)\r
748                                                         out_switch_port(port, group);\r
749                                 }\r
750                         }\r
751 \r
752                         fprintf(f, "\n# Chassis Switches");\r
753                         for (dist = 0; dist <= maxhops_discovered; dist++) {\r
754 \r
755                                 for (node = nodesdist[dist]; node; node = node->dnext) {\r
756 \r
757                                         /* Non Voltaire chassis */\r
758                                         if (node->vendid == VTR_VENDOR_ID)\r
759                                                 continue;\r
760                                         if (!node->chrecord ||\r
761                                             !node->chrecord->chassisnum)\r
762                                                 continue;\r
763 \r
764                                         if (node->chrecord->chassisnum != ch->chassisnum)\r
765                                                 continue;\r
766 \r
767                                         out_switch(node, group, chname);\r
768                                         for (port = node->ports; port; port = port->next, i++)\r
769                                                 if (port->remoteport)\r
770                                                         out_switch_port(port, group);\r
771 \r
772                                 }\r
773 \r
774                         }\r
775 \r
776                         fprintf(f, "\n# Chassis CAs");\r
777                         for (node = nodesdist[MAXHOPS]; node; node = node->dnext) {\r
778                                 if (!node->chrecord ||\r
779                                     !node->chrecord->chassisnum)\r
780                                         continue;\r
781 \r
782                                 if (node->chrecord->chassisnum != ch->chassisnum)\r
783                                         continue;\r
784 \r
785                                 out_ca(node, group, chname);\r
786                                 for (port = node->ports; port; port = port->next, i++)\r
787                                         if (port->remoteport)\r
788                                                 out_ca_port(port, group);\r
789 \r
790                         }\r
791 \r
792                 }\r
793 \r
794         } else {\r
795                 for (dist = 0; dist <= maxhops_discovered; dist++) {\r
796 \r
797                         for (node = nodesdist[dist]; node; node = node->dnext) {\r
798 \r
799                                 DEBUG("SWITCH: dist %d node %p", dist, node);\r
800                                 if (!listtype)\r
801                                         out_switch(node, group, chname);\r
802                                 else {\r
803                                         if (listtype & LIST_SWITCH_NODE)\r
804                                                 list_node(node);\r
805                                         continue;\r
806                                 }\r
807 \r
808                                 for (port = node->ports; port; port = port->next, i++)\r
809                                         if (port->remoteport)\r
810                                                 out_switch_port(port, group);\r
811                         }\r
812                 }\r
813         }\r
814 \r
815         if (chname)\r
816                 free(chname);\r
817         chname = NULL;\r
818         if (group && !listtype) {\r
819 \r
820                 fprintf(f, "\nNon-Chassis Nodes\n");\r
821 \r
822                 for (dist = 0; dist <= maxhops_discovered; dist++) {\r
823 \r
824                         for (node = nodesdist[dist]; node; node = node->dnext) {\r
825 \r
826                                 DEBUG("SWITCH: dist %d node %p", dist, node);\r
827                                 /* Now, skip chassis based switches */\r
828                                 if (node->chrecord &&\r
829                                     node->chrecord->chassisnum)\r
830                                         continue;\r
831                                 out_switch(node, group, chname);\r
832 \r
833                                 for (port = node->ports; port; port = port->next, i++)\r
834                                         if (port->remoteport)\r
835                                                 out_switch_port(port, group);\r
836                         }\r
837 \r
838                 }\r
839 \r
840         }\r
841 \r
842         /* Make pass on CAs */\r
843         for (node = nodesdist[MAXHOPS]; node; node = node->dnext) {\r
844 \r
845                 DEBUG("CA: dist %d node %p", dist, node);\r
846                 if (!listtype) {\r
847                         /* Now, skip chassis based CAs */\r
848                         if (group && node->chrecord &&\r
849                             node->chrecord->chassisnum)\r
850                                 continue;\r
851                         out_ca(node, group, chname);\r
852                 } else {\r
853                         if (((listtype & LIST_CA_NODE) && (node->type == CA_NODE)) ||\r
854                             ((listtype & LIST_ROUTER_NODE) && (node->type == ROUTER_NODE)))\r
855                                 list_node(node);\r
856                         continue;\r
857                 }\r
858 \r
859                 for (port = node->ports; port; port = port->next, i++)\r
860                         if (port->remoteport)\r
861                                 out_ca_port(port, group);\r
862         }\r
863 \r
864         if (chname)\r
865                 free(chname);\r
866 \r
867         return i;\r
868 }\r
869 \r
870 void dump_ports_report ()\r
871 {\r
872         int b, n = 0, p;\r
873         Node *node;\r
874         Port *port;\r
875 \r
876         // If switch and LID == 0, search of other switch ports with\r
877         // valid LID and assign it to all ports of that switch\r
878         for (b = 0; b <= MAXHOPS; b++)\r
879                 for (node = nodesdist[b]; node; node = node->dnext)\r
880                         if (node->type == SWITCH_NODE) {\r
881                                 int swlid = 0;\r
882                                 for (p = 0, port = node->ports;\r
883                                      p < node->numports && port && !swlid;\r
884                                      port = port->next)\r
885                                         if (port->lid != 0)\r
886                                                 swlid = port->lid;\r
887                                 for (p = 0, port = node->ports;\r
888                                      p < node->numports && port;\r
889                                      port = port->next)\r
890                                         port->lid = swlid;\r
891                         }\r
892 \r
893         for (b = 0; b <= MAXHOPS; b++)\r
894                 for (node = nodesdist[b]; node; node = node->dnext) {\r
895                         for (p = 0, port = node->ports;\r
896                              p < node->numports && port;\r
897                              p++, port = port->next) {\r
898                                 fprintf(stdout,\r
899                                         "%2s %5d %2d 0x%016" PRIx64 " %s %s",\r
900                                         node_type_str2(port->node), port->lid,\r
901                                         port->portnum,\r
902                                         port->portguid,\r
903                                         get_linkwidth_str(port->linkwidth),\r
904                                         get_linkspeed_str(port->linkspeed));\r
905                                 if (port->remoteport)\r
906                                         fprintf(stdout,\r
907                                                 " - %2s %5d %2d 0x%016" PRIx64\r
908                                                 " ( '%s' - '%s' )\n",\r
909                                                 node_type_str2(port->remoteport->node),\r
910                                                 port->remoteport->lid,\r
911                                                 port->remoteport->portnum,\r
912                                                 port->remoteport->portguid,\r
913                                                 port->node->nodedesc,\r
914                                                 port->remoteport->node->nodedesc);\r
915                                 else\r
916                                         fprintf(stdout, "%36s'%s'\n", "",\r
917                                                 port->node->nodedesc);\r
918                         }\r
919                         n++;\r
920                 }\r
921 }\r
922 \r
923 void\r
924 usage(void)\r
925 {\r
926         fprintf(stderr, "Usage: %s [-d(ebug)] -e(rr_show) -v(erbose) -s(how) -l(ist) -g(rouping) -H(ca_list) -S(witch_list) -R(outer_list) -V(ersion) -C ca_name -P ca_port "\r
927                         "-t(imeout) timeout_ms --node-name-map node-name-map] -p(orts) [<topology-file>]\n",\r
928                         argv0);\r
929         fprintf(stderr, "       --node-name-map <node-name-map> specify a node name map file\n");\r
930         exit(-1);\r
931 }\r
932 \r
933 int\r
934 main(int argc, char **argv)\r
935 {\r
936         int mgmt_classes[2] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS};\r
937         ib_portid_t my_portid = {0};\r
938         int udebug = 0, list = 0;\r
939         char *ca = 0;\r
940         int ca_port = 0;\r
941         int group = 0;\r
942         int ports_report = 0;\r
943 \r
944         static char const str_opts[] = "C:P:t:devslgHSRpVhu";\r
945         static const struct option long_opts[] = {\r
946                 { "C", 1, 0, 'C'},\r
947                 { "P", 1, 0, 'P'},\r
948                 { "debug", 0, 0, 'd'},\r
949                 { "err_show", 0, 0, 'e'},\r
950                 { "verbose", 0, 0, 'v'},\r
951                 { "show", 0, 0, 's'},\r
952                 { "list", 0, 0, 'l'},\r
953                 { "grouping", 0, 0, 'g'},\r
954                 { "Hca_list", 0, 0, 'H'},\r
955                 { "Switch_list", 0, 0, 'S'},\r
956                 { "Router_list", 0, 0, 'R'},\r
957                 { "timeout", 1, 0, 't'},\r
958                 { "node-name-map", 1, 0, 1},\r
959                 { "ports", 0, 0, 'p'},\r
960                 { "Version", 0, 0, 'V'},\r
961                 { "help", 0, 0, 'h'},\r
962                 { "usage", 0, 0, 'u'},\r
963                 { }\r
964         };\r
965 \r
966         f = stdout;\r
967 \r
968         argv0 = argv[0];\r
969 \r
970         while (1) {\r
971                 int ch = getopt_long(argc, argv, str_opts, long_opts, NULL);\r
972                 if ( ch == -1 )\r
973                         break;\r
974                 switch(ch) {\r
975                 case 1:\r
976                         node_name_map_file = strdup(optarg);\r
977                         break;\r
978                 case 'C':\r
979                         ca = optarg;\r
980                         break;\r
981                 case 'P':\r
982                         ca_port = strtoul(optarg, 0, 0);\r
983                         break;\r
984                 case 'd':\r
985                         ibdebug++;\r
986                         madrpc_show_errors(1);\r
987                         umad_debug(udebug);\r
988                         udebug++;\r
989                         break;\r
990                 case 't':\r
991                         timeout = strtoul(optarg, 0, 0);\r
992                         break;\r
993                 case 'v':\r
994                         verbose++;\r
995                         dumplevel++;\r
996                         break;\r
997                 case 's':\r
998                         dumplevel = 1;\r
999                         break;\r
1000                 case 'e':\r
1001                         madrpc_show_errors(1);\r
1002                         break;\r
1003                 case 'l':\r
1004                         list = LIST_CA_NODE | LIST_SWITCH_NODE | LIST_ROUTER_NODE;\r
1005                         break;\r
1006                 case 'g':\r
1007                         group = 1;\r
1008                         break;\r
1009                 case 'S':\r
1010                         list = LIST_SWITCH_NODE;\r
1011                         break;\r
1012                 case 'H':\r
1013                         list = LIST_CA_NODE;\r
1014                         break;\r
1015                 case 'R':\r
1016                         list = LIST_ROUTER_NODE;\r
1017                         break;\r
1018                 case 'V':\r
1019                         fprintf(stderr, "%s %s\n", argv0, get_build_version() );\r
1020                         exit(-1);\r
1021                 case 'p':\r
1022                         ports_report = 1;\r
1023                         break;\r
1024                 default:\r
1025                         usage();\r
1026                         break;\r
1027                 }\r
1028         }\r
1029         argc -= optind;\r
1030         argv += optind;\r
1031 \r
1032         if (argc && !(f = fopen(argv[0], "w")))\r
1033                 IBERROR("can't open file %s for writing", argv[0]);\r
1034 \r
1035         madrpc_init(ca, ca_port, mgmt_classes, 2);\r
1036         node_name_map = open_node_name_map(node_name_map_file);\r
1037 \r
1038         if (discover(&my_portid) < 0)\r
1039                 IBERROR("discover");\r
1040 \r
1041         if (group)\r
1042                 chassis = group_nodes();\r
1043 \r
1044         if (ports_report)\r
1045                 dump_ports_report();\r
1046         else\r
1047                 dump_topology(list, group);\r
1048 \r
1049         close_node_name_map(node_name_map);\r
1050         exit(0);\r
1051 }\r