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 <infiniband/complib/cl_nodenamemap.h>
54
55#include "ibnetdiscover.h"
56#include "grouping.h"
57#include "ibdiag_common.h"
58
59static char *node_type_str[] = {
60	"???",
61	"ca",
62	"switch",
63	"router",
64	"iwarp rnic"
65};
66
67static char *linkwidth_str[] = {
68	"??",
69	"1x",
70	"4x",
71	"??",
72	"8x",
73	"??",
74	"??",
75	"??",
76	"12x"
77};
78
79static char *linkspeed_str[] = {
80	"???",
81	"SDR",
82	"DDR",
83	"???",
84	"QDR"
85};
86
87static int timeout = 2000;		/* ms */
88static int dumplevel = 0;
89static int verbose;
90static FILE *f;
91
92char *argv0 = "ibnetdiscover";
93
94static char *node_name_map_file = NULL;
95static nn_map_t *node_name_map = NULL;
96
97Node *nodesdist[MAXHOPS+1];     /* last is Ca list */
98Node *mynode;
99int maxhops_discovered = 0;
100
101struct ChassisList *chassis = NULL;
102
103static char *
104get_linkwidth_str(int linkwidth)
105{
106	if (linkwidth > 8)
107		return linkwidth_str[0];
108	else
109		return linkwidth_str[linkwidth];
110}
111
112static char *
113get_linkspeed_str(int linkspeed)
114{
115	if (linkspeed > 4)
116		return linkspeed_str[0];
117	else
118		return linkspeed_str[linkspeed];
119}
120
121static inline const char*
122node_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
132void
133decode_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
144int
145get_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 */
163int
164get_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
213static int
214extend_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
225static void
226dump_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
242static Node *nodestbl[HTSZ];
243
244static Node *
245find_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
257static Node *
258create_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
283static Port *
284find_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
295static Port *
296create_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
312static void
313link_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
326static int
327handle_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 */
377static int
378discover(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
450char *
451node_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
474void
475list_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
503void
504out_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
520uint64_t
521out_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
533void
534out_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
567void
568out_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
602static char *
603out_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
615void
616out_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
654void
655out_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
685int
686dump_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
870void 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
923void
924usage(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
933int
934main(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