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