1 /*
2  * Copyright (c) 2004-2009 Voltaire Inc.  All rights reserved.
3  * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
4  * Copyright (c) 2008 Lawrence Livermore National Lab.  All rights reserved.
5  * Copyright (c) 2010,2011 Mellanox Technologies LTD.  All rights reserved.
6  *
7  * This software is available to you under a choice of one of two
8  * licenses.  You may choose to be licensed under the terms of the GNU
9  * General Public License (GPL) Version 2, available from the file
10  * COPYING in the main directory of this source tree, or the
11  * OpenIB.org BSD license below:
12  *
13  *     Redistribution and use in source and binary forms, with or
14  *     without modification, are permitted provided that the following
15  *     conditions are met:
16  *
17  *      - Redistributions of source code must retain the above
18  *        copyright notice, this list of conditions and the following
19  *        disclaimer.
20  *
21  *      - Redistributions in binary form must reproduce the above
22  *        copyright notice, this list of conditions and the following
23  *        disclaimer in the documentation and/or other materials
24  *        provided with the distribution.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33  * SOFTWARE.
34  *
35  */
36 
37 #if HAVE_CONFIG_H
38 #include <config.h>
39 #endif				/* HAVE_CONFIG_H */
40 
41 #define _GNU_SOURCE
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <time.h>
46 #include <string.h>
47 #include <getopt.h>
48 #include <inttypes.h>
49 
50 #include <infiniband/umad.h>
51 #include <infiniband/mad.h>
52 #include <complib/cl_nodenamemap.h>
53 #include <infiniband/ibnetdisc.h>
54 
55 #include "ibdiag_common.h"
56 
57 #define LIST_CA_NODE	 (1 << IB_NODE_CA)
58 #define LIST_SWITCH_NODE (1 << IB_NODE_SWITCH)
59 #define LIST_ROUTER_NODE (1 << IB_NODE_ROUTER)
60 
61 #define DIFF_FLAG_SWITCH           0x01
62 #define DIFF_FLAG_CA               0x02
63 #define DIFF_FLAG_ROUTER           0x04
64 #define DIFF_FLAG_PORT_CONNECTION  0x08
65 #define DIFF_FLAG_LID              0x10
66 #define DIFF_FLAG_NODE_DESCRIPTION 0x20
67 
68 #define DIFF_FLAG_DEFAULT (DIFF_FLAG_SWITCH | DIFF_FLAG_CA | DIFF_FLAG_ROUTER \
69 			   | DIFF_FLAG_PORT_CONNECTION)
70 
71 static FILE *f;
72 
73 static char *node_name_map_file = NULL;
74 static nn_map_t *node_name_map = NULL;
75 static char *cache_file = NULL;
76 static char *load_cache_file = NULL;
77 static char *diff_cache_file = NULL;
78 static unsigned diffcheck_flags = DIFF_FLAG_DEFAULT;
79 
80 static int report_max_hops = 0;
81 static int full_info;
82 
83 /**
84  * Define our own conversion functions to maintain compatibility with the old
85  * ibnetdiscover which did not use the ibmad conversion functions.
86  */
87 char *dump_linkspeed_compat(uint32_t speed)
88 {
89 	switch (speed) {
90 	case 1:
91 		return ("SDR");
92 		break;
93 	case 2:
94 		return ("DDR");
95 		break;
96 	case 4:
97 		return ("QDR");
98 		break;
99 	}
100 	return ("???");
101 }
102 
103 char *dump_linkspeedext_compat(uint32_t espeed, uint32_t speed, uint32_t fdr10)
104 {
105 	switch (espeed) {
106 	case 0:
107 		if (fdr10 & FDR10)
108 			return ("FDR10");
109 		else
110 			return dump_linkspeed_compat(speed);
111 		break;
112 	case 1:
113 		return ("FDR");
114 		break;
115 	case 2:
116 		return ("EDR");
117 		break;
118 	}
119 	return ("???");
120 }
121 
122 char *dump_linkwidth_compat(uint32_t width)
123 {
124 	switch (width) {
125 	case 1:
126 		return ("1x");
127 		break;
128 	case 2:
129 		return ("4x");
130 		break;
131 	case 4:
132 		return ("8x");
133 		break;
134 	case 8:
135 		return ("12x");
136 		break;
137 	case 16:
138 		return ("2x");
139 		break;
140 	}
141 	return ("??");
142 }
143 
144 static inline const char *ports_nt_str_compat(ibnd_node_t * node)
145 {
146 	switch (node->type) {
147 	case IB_NODE_SWITCH:
148 		return "SW";
149 	case IB_NODE_CA:
150 		return "CA";
151 	case IB_NODE_ROUTER:
152 		return "RT";
153 	}
154 	return "??";
155 }
156 
157 char *node_name(ibnd_node_t * node)
158 {
159 	static char buf[256];
160 
161 	switch (node->type) {
162 	case IB_NODE_SWITCH:
163 		sprintf(buf, "\"%s", "S");
164 		break;
165 	case IB_NODE_CA:
166 		sprintf(buf, "\"%s", "H");
167 		break;
168 	case IB_NODE_ROUTER:
169 		sprintf(buf, "\"%s", "R");
170 		break;
171 	default:
172 		sprintf(buf, "\"%s", "?");
173 		break;
174 	}
175 	sprintf(buf + 2, "-%016" PRIx64 "\"", node->guid);
176 
177 	return buf;
178 }
179 
180 void list_node(ibnd_node_t * node, void *user_data)
181 {
182 	char *node_type;
183 	char *nodename = remap_node_name(node_name_map, node->guid,
184 					 node->nodedesc);
185 
186 	switch (node->type) {
187 	case IB_NODE_SWITCH:
188 		node_type = "Switch";
189 		break;
190 	case IB_NODE_CA:
191 		node_type = "Ca";
192 		break;
193 	case IB_NODE_ROUTER:
194 		node_type = "Router";
195 		break;
196 	default:
197 		node_type = "???";
198 		break;
199 	}
200 	fprintf(f,
201 		"%s\t : 0x%016" PRIx64
202 		" ports %d devid 0x%x vendid 0x%x \"%s\"\n", node_type,
203 		node->guid, node->numports, mad_get_field(node->info, 0,
204 							  IB_NODE_DEVID_F),
205 		mad_get_field(node->info, 0, IB_NODE_VENDORID_F), nodename);
206 
207 	free(nodename);
208 }
209 
210 void list_nodes(ibnd_fabric_t * fabric, int list)
211 {
212 	if (list & LIST_CA_NODE)
213 		ibnd_iter_nodes_type(fabric, list_node, IB_NODE_CA, NULL);
214 	if (list & LIST_SWITCH_NODE)
215 		ibnd_iter_nodes_type(fabric, list_node, IB_NODE_SWITCH, NULL);
216 	if (list & LIST_ROUTER_NODE)
217 		ibnd_iter_nodes_type(fabric, list_node, IB_NODE_ROUTER, NULL);
218 }
219 
220 void out_ids(ibnd_node_t * node, int group, char *chname, char *out_prefix)
221 {
222 	uint64_t sysimgguid =
223 	    mad_get_field64(node->info, 0, IB_NODE_SYSTEM_GUID_F);
224 
225 	fprintf(f, "\n%svendid=0x%x\n", out_prefix ? out_prefix : "",
226 		mad_get_field(node->info, 0, IB_NODE_VENDORID_F));
227 	fprintf(f, "%sdevid=0x%x\n", out_prefix ? out_prefix : "",
228 		mad_get_field(node->info, 0, IB_NODE_DEVID_F));
229 	if (sysimgguid)
230 		fprintf(f, "%ssysimgguid=0x%" PRIx64,
231 			out_prefix ? out_prefix : "", sysimgguid);
232 	if (group && node->chassis && node->chassis->chassisnum) {
233 		fprintf(f, "\t\t# Chassis %d", node->chassis->chassisnum);
234 		if (chname)
235 			fprintf(f, " (%s)", clean_nodedesc(chname));
236 		if (ibnd_is_xsigo_tca(node->guid) && node->ports[1] &&
237 		    node->ports[1]->remoteport)
238 			fprintf(f, " slot %d",
239 				node->ports[1]->remoteport->portnum);
240 	}
241 	if (sysimgguid ||
242 	    (group && node->chassis && node->chassis->chassisnum))
243 		fprintf(f, "\n");
244 }
245 
246 uint64_t out_chassis(ibnd_fabric_t * fabric, unsigned char chassisnum)
247 {
248 	uint64_t guid;
249 
250 	fprintf(f, "\nChassis %u", chassisnum);
251 	guid = ibnd_get_chassis_guid(fabric, chassisnum);
252 	if (guid)
253 		fprintf(f, " (guid 0x%" PRIx64 ")", guid);
254 	fprintf(f, "\n");
255 	return guid;
256 }
257 
258 void out_switch_detail(ibnd_node_t * node, char *sw_prefix)
259 {
260 	char *nodename = NULL;
261 
262 	nodename = remap_node_name(node_name_map, node->guid, node->nodedesc);
263 
264 	fprintf(f, "%sSwitch\t%d %s\t\t# \"%s\" %s port 0 lid %d lmc %d",
265 		sw_prefix ? sw_prefix : "", node->numports, node_name(node),
266 		nodename, node->smaenhsp0 ? "enhanced" : "base",
267 		node->smalid, node->smalmc);
268 
269 	free(nodename);
270 }
271 
272 void out_switch(ibnd_node_t * node, int group, char *chname, char *id_prefix,
273 		char *sw_prefix)
274 {
275 	char *str;
276 	char str2[256];
277 
278 	out_ids(node, group, chname, id_prefix);
279 	fprintf(f, "%sswitchguid=0x%" PRIx64,
280 		id_prefix ? id_prefix : "", node->guid);
281 	fprintf(f, "(%" PRIx64 ")",
282 		mad_get_field64(node->info, 0, IB_NODE_PORT_GUID_F));
283 	if (group) {
284 		fprintf(f, "\t# ");
285 		str = ibnd_get_chassis_type(node);
286 		if (str)
287 			fprintf(f, "%s ", str);
288 		str = ibnd_get_chassis_slot_str(node, str2, 256);
289 		if (str)
290 			fprintf(f, "%s", str);
291 	}
292 	fprintf(f, "\n");
293 
294 	out_switch_detail(node, sw_prefix);
295 	fprintf(f, "\n");
296 }
297 
298 void out_ca_detail(ibnd_node_t * node, char *ca_prefix)
299 {
300 	char *node_type;
301 
302 	switch (node->type) {
303 	case IB_NODE_CA:
304 		node_type = "Ca";
305 		break;
306 	case IB_NODE_ROUTER:
307 		node_type = "Rt";
308 		break;
309 	default:
310 		node_type = "???";
311 		break;
312 	}
313 
314 	fprintf(f, "%s%s\t%d %s\t\t# \"%s\"", ca_prefix ? ca_prefix : "",
315 		node_type, node->numports, node_name(node),
316 		clean_nodedesc(node->nodedesc));
317 }
318 
319 void out_ca(ibnd_node_t * node, int group, char *chname, char *id_prefix,
320 	    char *ca_prefix)
321 {
322 	char *node_type;
323 
324 	out_ids(node, group, chname, id_prefix);
325 	switch (node->type) {
326 	case IB_NODE_CA:
327 		node_type = "ca";
328 		break;
329 	case IB_NODE_ROUTER:
330 		node_type = "rt";
331 		break;
332 	default:
333 		node_type = "???";
334 		break;
335 	}
336 
337 	fprintf(f, "%s%sguid=0x%" PRIx64 "\n",
338 		id_prefix ? id_prefix : "", node_type, node->guid);
339 	out_ca_detail(node, ca_prefix);
340 	if (group && ibnd_is_xsigo_hca(node->guid))
341 		fprintf(f, " (scp)");
342 	fprintf(f, "\n");
343 }
344 
345 #define OUT_BUFFER_SIZE 16
346 static char *out_ext_port(ibnd_port_t * port, int group)
347 {
348 	static char mapping[OUT_BUFFER_SIZE];
349 
350 	if (group && port->ext_portnum != 0) {
351 		snprintf(mapping, OUT_BUFFER_SIZE,
352 			 "[ext %d]", port->ext_portnum);
353 		return (mapping);
354 	}
355 
356 	return (NULL);
357 }
358 
359 void out_switch_port(ibnd_port_t * port, int group, char *out_prefix)
360 {
361 	char *ext_port_str = NULL;
362 	char *rem_nodename = NULL;
363 	uint32_t iwidth = mad_get_field(port->info, 0,
364 					IB_PORT_LINK_WIDTH_ACTIVE_F);
365 	uint32_t ispeed = mad_get_field(port->info, 0,
366 					IB_PORT_LINK_SPEED_ACTIVE_F);
367 	uint32_t vlcap = mad_get_field(port->info, 0,
368 				       IB_PORT_VL_CAP_F);
369 	uint32_t fdr10 = mad_get_field(port->ext_info, 0,
370 				       IB_MLNX_EXT_PORT_LINK_SPEED_ACTIVE_F);
371 	uint32_t cap_mask, espeed;
372 
373 	DEBUG("port %p:%d remoteport %p\n", port, port->portnum,
374 	      port->remoteport);
375 	fprintf(f, "%s[%d]", out_prefix ? out_prefix : "", port->portnum);
376 
377 	ext_port_str = out_ext_port(port, group);
378 	if (ext_port_str)
379 		fprintf(f, "%s", ext_port_str);
380 
381 	rem_nodename = remap_node_name(node_name_map,
382 				       port->remoteport->node->guid,
383 				       port->remoteport->node->nodedesc);
384 
385 	ext_port_str = out_ext_port(port->remoteport, group);
386 
387 	if (!port->node->ports[0]) {
388 		cap_mask = 0;
389 		ispeed = 0;
390 		espeed = 0;
391 	} else {
392 		cap_mask = mad_get_field(port->node->ports[0]->info, 0,
393 					 IB_PORT_CAPMASK_F);
394 		if (cap_mask & CL_NTOH32(IB_PORT_CAP_HAS_EXT_SPEEDS))
395 			espeed = mad_get_field(port->info, 0,
396 					       IB_PORT_LINK_SPEED_EXT_ACTIVE_F);
397 		else
398 			espeed = 0;
399 	}
400 	fprintf(f, "\t%s[%d]%s",
401 		node_name(port->remoteport->node), port->remoteport->portnum,
402 		ext_port_str ? ext_port_str : "");
403 	if (port->remoteport->node->type != IB_NODE_SWITCH)
404 		fprintf(f, "(%" PRIx64 ") ", port->remoteport->guid);
405 	fprintf(f, "\t\t# \"%s\" lid %d %s%s",
406 		rem_nodename,
407 		port->remoteport->node->type == IB_NODE_SWITCH ?
408 		port->remoteport->node->smalid :
409 		port->remoteport->base_lid,
410 		dump_linkwidth_compat(iwidth),
411 		(ispeed != 4 && !espeed) ?
412 			dump_linkspeed_compat(ispeed) :
413 			dump_linkspeedext_compat(espeed, ispeed, fdr10));
414 
415 	if (full_info) {
416 		fprintf(f, " s=%d w=%d v=%d", ispeed, iwidth, vlcap);
417 		if (espeed)
418 			fprintf(f, " e=%d", espeed);
419 	}
420 
421 	if (ibnd_is_xsigo_tca(port->remoteport->guid))
422 		fprintf(f, " slot %d", port->portnum);
423 	else if (ibnd_is_xsigo_hca(port->remoteport->guid))
424 		fprintf(f, " (scp)");
425 	fprintf(f, "\n");
426 
427 	free(rem_nodename);
428 }
429 
430 void out_ca_port(ibnd_port_t * port, int group, char *out_prefix)
431 {
432 	char *str = NULL;
433 	char *rem_nodename = NULL;
434 	uint32_t iwidth = mad_get_field(port->info, 0,
435 					IB_PORT_LINK_WIDTH_ACTIVE_F);
436 	uint32_t ispeed = mad_get_field(port->info, 0,
437 					IB_PORT_LINK_SPEED_ACTIVE_F);
438 	uint32_t vlcap = mad_get_field(port->info, 0,
439 				       IB_PORT_VL_CAP_F);
440 	uint32_t fdr10 = mad_get_field(port->ext_info, 0,
441 				       IB_MLNX_EXT_PORT_LINK_SPEED_ACTIVE_F);
442 	uint32_t cap_mask, espeed;
443 
444 	fprintf(f, "%s[%d]", out_prefix ? out_prefix : "", port->portnum);
445 	if (port->node->type != IB_NODE_SWITCH)
446 		fprintf(f, "(%" PRIx64 ") ", port->guid);
447 	fprintf(f, "\t%s[%d]",
448 		node_name(port->remoteport->node), port->remoteport->portnum);
449 	str = out_ext_port(port->remoteport, group);
450 	if (str)
451 		fprintf(f, "%s", str);
452 	if (port->remoteport->node->type != IB_NODE_SWITCH)
453 		fprintf(f, " (%" PRIx64 ") ", port->remoteport->guid);
454 
455 	rem_nodename = remap_node_name(node_name_map,
456 				       port->remoteport->node->guid,
457 				       port->remoteport->node->nodedesc);
458 
459 	cap_mask = mad_get_field(port->info, 0, IB_PORT_CAPMASK_F);
460 	if (cap_mask & CL_NTOH32(IB_PORT_CAP_HAS_EXT_SPEEDS))
461 		espeed = mad_get_field(port->info, 0,
462 				       IB_PORT_LINK_SPEED_EXT_ACTIVE_F);
463 	else
464 		espeed = 0;
465 
466 	fprintf(f, "\t\t# lid %d lmc %d \"%s\" lid %d %s%s",
467 		port->base_lid, port->lmc, rem_nodename,
468 		port->remoteport->node->type == IB_NODE_SWITCH ?
469 		port->remoteport->node->smalid :
470 		port->remoteport->base_lid,
471 		dump_linkwidth_compat(iwidth),
472 		(ispeed != 4 && !espeed) ?
473 			dump_linkspeed_compat(ispeed) :
474 			dump_linkspeedext_compat(espeed, ispeed, fdr10));
475 
476 	if (full_info) {
477 		fprintf(f, " s=%d w=%d v=%d", ispeed, iwidth, vlcap);
478 		if (espeed)
479 			fprintf(f, " e=%d", espeed);
480 	}
481 
482 	fprintf(f, "\n");
483 
484 	free(rem_nodename);
485 }
486 
487 struct iter_user_data {
488 	int group;
489 	int skip_chassis_nodes;
490 };
491 
492 static void switch_iter_func(ibnd_node_t * node, void *iter_user_data)
493 {
494 	ibnd_port_t *port;
495 	int p = 0;
496 	struct iter_user_data *data = (struct iter_user_data *)iter_user_data;
497 
498 	DEBUG("SWITCH: node %p\n", node);
499 
500 	/* skip chassis based switches if flagged */
501 	if (data->skip_chassis_nodes && node->chassis
502 	    && node->chassis->chassisnum)
503 		return;
504 
505 	out_switch(node, data->group, NULL, NULL, NULL);
506 	for (p = 1; p <= node->numports; p++) {
507 		port = node->ports[p];
508 		if (port && port->remoteport)
509 			out_switch_port(port, data->group, NULL);
510 	}
511 }
512 
513 static void ca_iter_func(ibnd_node_t * node, void *iter_user_data)
514 {
515 	ibnd_port_t *port;
516 	int p = 0;
517 	struct iter_user_data *data = (struct iter_user_data *)iter_user_data;
518 
519 	DEBUG("CA: node %p\n", node);
520 	/* Now, skip chassis based CAs */
521 	if (data->group && node->chassis && node->chassis->chassisnum)
522 		return;
523 	out_ca(node, data->group, NULL, NULL, NULL);
524 
525 	for (p = 1; p <= node->numports; p++) {
526 		port = node->ports[p];
527 		if (port && port->remoteport)
528 			out_ca_port(port, data->group, NULL);
529 	}
530 }
531 
532 static void router_iter_func(ibnd_node_t * node, void *iter_user_data)
533 {
534 	ibnd_port_t *port;
535 	int p = 0;
536 	struct iter_user_data *data = (struct iter_user_data *)iter_user_data;
537 
538 	DEBUG("RT: node %p\n", node);
539 	/* Now, skip chassis based RTs */
540 	if (data->group && node->chassis && node->chassis->chassisnum)
541 		return;
542 	out_ca(node, data->group, NULL, NULL, NULL);
543 	for (p = 1; p <= node->numports; p++) {
544 		port = node->ports[p];
545 		if (port && port->remoteport)
546 			out_ca_port(port, data->group, NULL);
547 	}
548 }
549 
550 int dump_topology(int group, ibnd_fabric_t * fabric)
551 {
552 	ibnd_node_t *node;
553 	ibnd_port_t *port;
554 	int i = 0, p = 0;
555 	time_t t = time(0);
556 	uint64_t chguid;
557 	char *chname = NULL;
558 	struct iter_user_data iter_user_data;
559 
560 	fprintf(f, "#\n# Topology file: generated on %s#\n", ctime(&t));
561 	if (report_max_hops)
562 		fprintf(f, "# Reported max hops discovered: %u\n"
563 			"# Total MADs used: %u\n",
564 			fabric->maxhops_discovered, fabric->total_mads_used);
565 	fprintf(f, "# Initiated from node %016" PRIx64 " port %016" PRIx64 "\n",
566 		fabric->from_node->guid,
567 		mad_get_field64(fabric->from_node->info, 0,
568 				IB_NODE_PORT_GUID_F));
569 
570 	/* Make pass on switches */
571 	if (group) {
572 		ibnd_chassis_t *ch = NULL;
573 
574 		/* Chassis based switches first */
575 		for (ch = fabric->chassis; ch; ch = ch->next) {
576 			int n = 0;
577 
578 			if (!ch->chassisnum)
579 				continue;
580 			chguid = out_chassis(fabric, ch->chassisnum);
581 			chname = NULL;
582 			if (ibnd_is_xsigo_guid(chguid)) {
583 				for (node = ch->nodes; node;
584 				     node = node->next_chassis_node) {
585 					if (ibnd_is_xsigo_hca(node->guid)) {
586 						chname = node->nodedesc;
587 						fprintf(f, "Hostname: %s\n",
588 							clean_nodedesc
589 							(node->nodedesc));
590 					}
591 				}
592 			}
593 
594 			fprintf(f, "\n# Spine Nodes");
595 			for (n = 1; n <= SPINES_MAX_NUM; n++) {
596 				if (ch->spinenode[n]) {
597 					out_switch(ch->spinenode[n], group,
598 						   chname, NULL, NULL);
599 					for (p = 1;
600 					     p <= ch->spinenode[n]->numports;
601 					     p++) {
602 						port =
603 						    ch->spinenode[n]->ports[p];
604 						if (port && port->remoteport)
605 							out_switch_port(port,
606 									group,
607 									NULL);
608 					}
609 				}
610 			}
611 			fprintf(f, "\n# Line Nodes");
612 			for (n = 1; n <= LINES_MAX_NUM; n++) {
613 				if (ch->linenode[n]) {
614 					out_switch(ch->linenode[n], group,
615 						   chname, NULL, NULL);
616 					for (p = 1;
617 					     p <= ch->linenode[n]->numports;
618 					     p++) {
619 						port =
620 						    ch->linenode[n]->ports[p];
621 						if (port && port->remoteport)
622 							out_switch_port(port,
623 									group,
624 									NULL);
625 					}
626 				}
627 			}
628 
629 			fprintf(f, "\n# Chassis Switches");
630 			for (node = ch->nodes; node;
631 			     node = node->next_chassis_node) {
632 				if (node->type == IB_NODE_SWITCH) {
633 					out_switch(node, group, chname, NULL,
634 						   NULL);
635 					for (p = 1; p <= node->numports; p++) {
636 						port = node->ports[p];
637 						if (port && port->remoteport)
638 							out_switch_port(port,
639 									group,
640 									NULL);
641 					}
642 				}
643 
644 			}
645 
646 			fprintf(f, "\n# Chassis CAs");
647 			for (node = ch->nodes; node;
648 			     node = node->next_chassis_node) {
649 				if (node->type == IB_NODE_CA) {
650 					out_ca(node, group, chname, NULL, NULL);
651 					for (p = 1; p <= node->numports; p++) {
652 						port = node->ports[p];
653 						if (port && port->remoteport)
654 							out_ca_port(port, group,
655 								    NULL);
656 					}
657 				}
658 			}
659 
660 		}
661 
662 	} else {		/* !group */
663 		iter_user_data.group = group;
664 		iter_user_data.skip_chassis_nodes = 0;
665 		ibnd_iter_nodes_type(fabric, switch_iter_func, IB_NODE_SWITCH,
666 				     &iter_user_data);
667 	}
668 
669 	chname = NULL;
670 	if (group) {
671 		iter_user_data.group = group;
672 		iter_user_data.skip_chassis_nodes = 1;
673 
674 		fprintf(f, "\nNon-Chassis Nodes\n");
675 
676 		ibnd_iter_nodes_type(fabric, switch_iter_func, IB_NODE_SWITCH,
677 				     &iter_user_data);
678 	}
679 
680 	iter_user_data.group = group;
681 	iter_user_data.skip_chassis_nodes = 0;
682 	/* Make pass on CAs */
683 	ibnd_iter_nodes_type(fabric, ca_iter_func, IB_NODE_CA, &iter_user_data);
684 
685 	/* Make pass on routers */
686 	ibnd_iter_nodes_type(fabric, router_iter_func, IB_NODE_ROUTER,
687 			     &iter_user_data);
688 
689 	return i;
690 }
691 
692 void dump_ports_report(ibnd_node_t * node, void *user_data)
693 {
694 	int p = 0;
695 	ibnd_port_t *port = NULL;
696 	char *nodename = NULL;
697 	char *rem_nodename = NULL;
698 
699 	/* for each port */
700 	for (p = node->numports, port = node->ports[p]; p > 0;
701 	     port = node->ports[--p]) {
702 		uint32_t iwidth, ispeed, fdr10, espeed, cap_mask;
703 		uint8_t *info = NULL;
704 		if (port == NULL)
705 			continue;
706 		iwidth =
707 		    mad_get_field(port->info, 0, IB_PORT_LINK_WIDTH_ACTIVE_F);
708 		ispeed =
709 		    mad_get_field(port->info, 0, IB_PORT_LINK_SPEED_ACTIVE_F);
710 		if (port->node->type == IB_NODE_SWITCH) {
711 			if (port->node->ports[0])
712 				info = (uint8_t *)&port->node->ports[0]->info;
713 		}
714 		else
715 			info = (uint8_t *)&port->info;
716 		if (info) {
717 			cap_mask = mad_get_field(info, 0, IB_PORT_CAPMASK_F);
718 			if (cap_mask & CL_NTOH32(IB_PORT_CAP_HAS_EXT_SPEEDS))
719 				espeed = mad_get_field(port->info, 0,
720 						       IB_PORT_LINK_SPEED_EXT_ACTIVE_F);
721 			else
722 				espeed = 0;
723 		} else {
724 			ispeed = 0;
725 			iwidth = 0;
726 			espeed = 0;
727 		}
728 		fdr10 = mad_get_field(port->ext_info, 0,
729 				      IB_MLNX_EXT_PORT_LINK_SPEED_ACTIVE_F);
730 		nodename = remap_node_name(node_name_map,
731 					   port->node->guid,
732 					   port->node->nodedesc);
733 		fprintf(stdout, "%2s %5d %2d 0x%016" PRIx64 " %s %s",
734 			ports_nt_str_compat(node),
735 			node->type ==
736 			IB_NODE_SWITCH ? node->smalid : port->base_lid,
737 			port->portnum, port->guid,
738 			dump_linkwidth_compat(iwidth),
739 			(ispeed != 4 && !espeed) ?
740 				dump_linkspeed_compat(ispeed) :
741 				dump_linkspeedext_compat(espeed, ispeed, fdr10));
742 		if (port->remoteport) {
743 			rem_nodename = remap_node_name(node_name_map,
744 					      port->remoteport->node->guid,
745 					      port->remoteport->node->nodedesc);
746 			fprintf(stdout,
747 				" - %2s %5d %2d 0x%016" PRIx64
748 				" ( '%s' - '%s' )\n",
749 				ports_nt_str_compat(port->remoteport->node),
750 				port->remoteport->node->type == IB_NODE_SWITCH ?
751 				port->remoteport->node->smalid :
752 				port->remoteport->base_lid,
753 				port->remoteport->portnum,
754 				port->remoteport->guid, nodename, rem_nodename);
755 			free(rem_nodename);
756 		} else
757 			fprintf(stdout, "%36s'%s'\n", "", nodename);
758 
759 		free(nodename);
760 	}
761 }
762 
763 struct iter_diff_data {
764 	uint32_t diff_flags;
765 	ibnd_fabric_t *fabric1;
766 	ibnd_fabric_t *fabric2;
767 	char *fabric1_prefix;
768 	char *fabric2_prefix;
769 	void (*out_header) (ibnd_node_t *, int, char *, char *, char *);
770 	void (*out_header_detail) (ibnd_node_t *, char *);
771 	void (*out_port) (ibnd_port_t *, int, char *);
772 };
773 
774 static void diff_iter_out_header(ibnd_node_t * node,
775 				 struct iter_diff_data *data,
776 				 int *out_header_flag)
777 {
778 	if (!(*out_header_flag)) {
779 		(*data->out_header) (node, 0, NULL, NULL, NULL);
780 		(*out_header_flag)++;
781 	}
782 }
783 
784 static void diff_ports(ibnd_node_t * fabric1_node, ibnd_node_t * fabric2_node,
785 		       int *out_header_flag, struct iter_diff_data *data)
786 {
787 	ibnd_port_t *fabric1_port;
788 	ibnd_port_t *fabric2_port;
789 	int p;
790 
791 	for (p = 1; p <= fabric1_node->numports; p++) {
792 		int fabric1_out = 0, fabric2_out = 0;
793 
794 		fabric1_port = fabric1_node->ports[p];
795 		fabric2_port = fabric2_node->ports[p];
796 
797 		if (data->diff_flags & DIFF_FLAG_PORT_CONNECTION) {
798 			if ((fabric1_port && !fabric2_port)
799 			    || ((fabric1_port && fabric2_port)
800 				&& (fabric1_port->remoteport
801 				    && !fabric2_port->remoteport)))
802 				fabric1_out++;
803 			else if ((!fabric1_port && fabric2_port)
804 				 || ((fabric1_port && fabric2_port)
805 				     && (!fabric1_port->remoteport
806 					 && fabric2_port->remoteport)))
807 				fabric2_out++;
808 			else if ((fabric1_port && fabric2_port)
809 				 && ((fabric1_port->guid != fabric2_port->guid)
810 				     ||
811 				     ((fabric1_port->remoteport
812 				       && fabric2_port->remoteport)
813 				      && (fabric1_port->remoteport->guid !=
814 					  fabric2_port->remoteport->guid)))) {
815 				fabric1_out++;
816 				fabric2_out++;
817 			}
818 		}
819 
820 		if ((data->diff_flags & DIFF_FLAG_LID)
821 		    && fabric1_port && fabric2_port
822 		    && fabric1_port->base_lid != fabric2_port->base_lid) {
823 			fabric1_out++;
824 			fabric2_out++;
825 		}
826 
827 		if (data->diff_flags & DIFF_FLAG_PORT_CONNECTION
828 		    && data->diff_flags & DIFF_FLAG_NODE_DESCRIPTION
829 		    && fabric1_port && fabric2_port
830 		    && fabric1_port->remoteport && fabric2_port->remoteport
831 		    && memcmp(fabric1_port->remoteport->node->nodedesc,
832 			      fabric2_port->remoteport->node->nodedesc,
833 			      IB_SMP_DATA_SIZE)) {
834 			fabric1_out++;
835 			fabric2_out++;
836 		}
837 
838 		if (data->diff_flags & DIFF_FLAG_PORT_CONNECTION
839 		    && data->diff_flags & DIFF_FLAG_NODE_DESCRIPTION
840 		    && fabric1_port && fabric2_port
841 		    && fabric1_port->remoteport && fabric2_port->remoteport
842 		    && memcmp(fabric1_port->remoteport->node->nodedesc,
843 			      fabric2_port->remoteport->node->nodedesc,
844 			      IB_SMP_DATA_SIZE)) {
845 			fabric1_out++;
846 			fabric2_out++;
847 		}
848 
849 		if (data->diff_flags & DIFF_FLAG_PORT_CONNECTION
850 		    && data->diff_flags & DIFF_FLAG_LID
851 		    && fabric1_port && fabric2_port
852 		    && fabric1_port->remoteport && fabric2_port->remoteport
853 		    && fabric1_port->remoteport->base_lid != fabric2_port->remoteport->base_lid) {
854 			fabric1_out++;
855 			fabric2_out++;
856 		}
857 
858 		if (fabric1_out) {
859 			diff_iter_out_header(fabric1_node, data,
860 					     out_header_flag);
861 			(*data->out_port) (fabric1_port, 0,
862 					   data->fabric1_prefix);
863 		}
864 		if (fabric2_out) {
865 			diff_iter_out_header(fabric1_node, data,
866 					     out_header_flag);
867 			(*data->out_port) (fabric2_port, 0,
868 					   data->fabric2_prefix);
869 		}
870 	}
871 }
872 
873 static void diff_iter_func(ibnd_node_t * fabric1_node, void *iter_user_data)
874 {
875 	struct iter_diff_data *data = iter_user_data;
876 	ibnd_node_t *fabric2_node;
877 	ibnd_port_t *fabric1_port;
878 	int p;
879 
880 	DEBUG("DEBUG: fabric1_node %p\n", fabric1_node);
881 
882 	fabric2_node = ibnd_find_node_guid(data->fabric2, fabric1_node->guid);
883 	if (!fabric2_node) {
884 		(*data->out_header) (fabric1_node, 0, NULL,
885 				     data->fabric1_prefix,
886 				     data->fabric1_prefix);
887 		for (p = 1; p <= fabric1_node->numports; p++) {
888 			fabric1_port = fabric1_node->ports[p];
889 			if (fabric1_port && fabric1_port->remoteport)
890 				(*data->out_port) (fabric1_port, 0,
891 						   data->fabric1_prefix);
892 		}
893 	} else if (data->diff_flags &
894 		   (DIFF_FLAG_PORT_CONNECTION | DIFF_FLAG_LID
895 		    | DIFF_FLAG_NODE_DESCRIPTION)) {
896 		int out_header_flag = 0;
897 
898 		if ((data->diff_flags & DIFF_FLAG_LID
899 		     && fabric1_node->smalid != fabric2_node->smalid) ||
900 		    (data->diff_flags & DIFF_FLAG_NODE_DESCRIPTION
901 		     && memcmp(fabric1_node->nodedesc, fabric2_node->nodedesc,
902 			       IB_SMP_DATA_SIZE))) {
903 			(*data->out_header) (fabric1_node, 0, NULL, NULL,
904 					     data->fabric1_prefix);
905 			(*data->out_header_detail) (fabric2_node,
906 						    data->fabric2_prefix);
907 			fprintf(f, "\n");
908 			out_header_flag++;
909 		}
910 
911 		if (fabric1_node->numports != fabric2_node->numports) {
912 			diff_iter_out_header(fabric1_node, data,
913 					     &out_header_flag);
914 			fprintf(f, "%snumports = %d\n", data->fabric1_prefix,
915 				fabric1_node->numports);
916 			fprintf(f, "%snumports = %d\n", data->fabric2_prefix,
917 				fabric2_node->numports);
918 			return;
919 		}
920 
921 		if (data->diff_flags & DIFF_FLAG_PORT_CONNECTION
922 		    || data->diff_flags & DIFF_FLAG_LID)
923 			diff_ports(fabric1_node, fabric2_node, &out_header_flag,
924 				   data);
925 	}
926 }
927 
928 static int diff_common(ibnd_fabric_t * orig_fabric, ibnd_fabric_t * new_fabric,
929 		       int node_type, uint32_t diff_flags,
930 		       void (*out_header) (ibnd_node_t *, int, char *, char *,
931 					   char *),
932 		       void (*out_header_detail) (ibnd_node_t *, char *),
933 		       void (*out_port) (ibnd_port_t *, int, char *))
934 {
935 	struct iter_diff_data iter_diff_data;
936 
937 	iter_diff_data.diff_flags = diff_flags;
938 	iter_diff_data.fabric1 = orig_fabric;
939 	iter_diff_data.fabric2 = new_fabric;
940 	iter_diff_data.fabric1_prefix = "< ";
941 	iter_diff_data.fabric2_prefix = "> ";
942 	iter_diff_data.out_header = out_header;
943 	iter_diff_data.out_header_detail = out_header_detail;
944 	iter_diff_data.out_port = out_port;
945 	ibnd_iter_nodes_type(orig_fabric, diff_iter_func, node_type,
946 			     &iter_diff_data);
947 
948 	/* Do opposite diff to find existence of node types
949 	 * in new_fabric but not in orig_fabric.
950 	 *
951 	 * In this diff, we don't need to check port connections,
952 	 * lids, or node descriptions since it has already been
953 	 * done (i.e. checks are only done when guid exists on both
954 	 * orig and new).
955 	 */
956 	iter_diff_data.diff_flags = diff_flags & ~DIFF_FLAG_PORT_CONNECTION;
957 	iter_diff_data.diff_flags &= ~DIFF_FLAG_LID;
958 	iter_diff_data.diff_flags &= ~DIFF_FLAG_NODE_DESCRIPTION;
959 	iter_diff_data.fabric1 = new_fabric;
960 	iter_diff_data.fabric2 = orig_fabric;
961 	iter_diff_data.fabric1_prefix = "> ";
962 	iter_diff_data.fabric2_prefix = "< ";
963 	iter_diff_data.out_header = out_header;
964 	iter_diff_data.out_header_detail = out_header_detail;
965 	iter_diff_data.out_port = out_port;
966 	ibnd_iter_nodes_type(new_fabric, diff_iter_func, node_type,
967 			     &iter_diff_data);
968 
969 	return 0;
970 }
971 
972 int diff(ibnd_fabric_t * orig_fabric, ibnd_fabric_t * new_fabric)
973 {
974 	if (diffcheck_flags & DIFF_FLAG_SWITCH)
975 		diff_common(orig_fabric, new_fabric, IB_NODE_SWITCH,
976 			    diffcheck_flags, out_switch, out_switch_detail,
977 			    out_switch_port);
978 
979 	if (diffcheck_flags & DIFF_FLAG_CA)
980 		diff_common(orig_fabric, new_fabric, IB_NODE_CA,
981 			    diffcheck_flags, out_ca, out_ca_detail,
982 			    out_ca_port);
983 
984 	if (diffcheck_flags & DIFF_FLAG_ROUTER)
985 		diff_common(orig_fabric, new_fabric, IB_NODE_ROUTER,
986 			    diffcheck_flags, out_ca, out_ca_detail,
987 			    out_ca_port);
988 
989 	return 0;
990 }
991 
992 static int list, group, ports_report;
993 
994 static int process_opt(void *context, int ch, char *optarg)
995 {
996 	struct ibnd_config *cfg = context;
997 	char *p;
998 
999 	switch (ch) {
1000 	case 1:
1001 		node_name_map_file = strdup(optarg);
1002 		break;
1003 	case 2:
1004 		cache_file = strdup(optarg);
1005 		break;
1006 	case 3:
1007 		load_cache_file = strdup(optarg);
1008 		break;
1009 	case 4:
1010 		diff_cache_file = strdup(optarg);
1011 		break;
1012 	case 5:
1013 		diffcheck_flags = 0;
1014 		p = strtok(optarg, ",");
1015 		while (p) {
1016 			if (!strcasecmp(p, "sw"))
1017 				diffcheck_flags |= DIFF_FLAG_SWITCH;
1018 			else if (!strcasecmp(p, "ca"))
1019 				diffcheck_flags |= DIFF_FLAG_CA;
1020 			else if (!strcasecmp(p, "router"))
1021 				diffcheck_flags |= DIFF_FLAG_ROUTER;
1022 			else if (!strcasecmp(p, "port"))
1023 				diffcheck_flags |= DIFF_FLAG_PORT_CONNECTION;
1024 			else if (!strcasecmp(p, "lid"))
1025 				diffcheck_flags |= DIFF_FLAG_LID;
1026 			else if (!strcasecmp(p, "nodedesc"))
1027 				diffcheck_flags |= DIFF_FLAG_NODE_DESCRIPTION;
1028 			else {
1029 				fprintf(stderr, "invalid diff check key: %s\n",
1030 					p);
1031 				return -1;
1032 			}
1033 			p = strtok(NULL, ",");
1034 		}
1035 		break;
1036 	case 's':
1037 		cfg->show_progress = 1;
1038 		break;
1039 	case 'f':
1040 		full_info = 1;
1041 		break;
1042 	case 'l':
1043 		list = LIST_CA_NODE | LIST_SWITCH_NODE | LIST_ROUTER_NODE;
1044 		break;
1045 	case 'g':
1046 		group = 1;
1047 		break;
1048 	case 'S':
1049 		list = LIST_SWITCH_NODE;
1050 		break;
1051 	case 'H':
1052 		list = LIST_CA_NODE;
1053 		break;
1054 	case 'R':
1055 		list = LIST_ROUTER_NODE;
1056 		break;
1057 	case 'p':
1058 		ports_report = 1;
1059 		break;
1060 	case 'm':
1061 		report_max_hops = 1;
1062 		break;
1063 	case 'o':
1064 		cfg->max_smps = strtoul(optarg, NULL, 0);
1065 		break;
1066 	default:
1067 		return -1;
1068 	}
1069 
1070 	return 0;
1071 }
1072 
1073 int main(int argc, char **argv)
1074 {
1075 	struct ibnd_config config = { 0 };
1076 	ibnd_fabric_t *fabric = NULL;
1077 	ibnd_fabric_t *diff_fabric = NULL;
1078 
1079 	const struct ibdiag_opt opts[] = {
1080 		{"full", 'f', 0, NULL, "show full information (ports' speed and width, vlcap)"},
1081 		{"show", 's', 0, NULL, "show more information"},
1082 		{"list", 'l', 0, NULL, "list of connected nodes"},
1083 		{"grouping", 'g', 0, NULL, "show grouping"},
1084 		{"Hca_list", 'H', 0, NULL, "list of connected CAs"},
1085 		{"Switch_list", 'S', 0, NULL, "list of connected switches"},
1086 		{"Router_list", 'R', 0, NULL, "list of connected routers"},
1087 		{"node-name-map", 1, 1, "<file>", "node name map file"},
1088 		{"cache", 2, 1, "<file>",
1089 		 "filename to cache ibnetdiscover data to"},
1090 		{"load-cache", 3, 1, "<file>",
1091 		 "filename of ibnetdiscover cache to load"},
1092 		{"diff", 4, 1, "<file>",
1093 		 "filename of ibnetdiscover cache to diff"},
1094 		{"diffcheck", 5, 1, "<key(s)>",
1095 		 "specify checks to execute for --diff"},
1096 		{"ports", 'p', 0, NULL, "obtain a ports report"},
1097 		{"max_hops", 'm', 0, NULL,
1098 		 "report max hops discovered by the library"},
1099 		{"outstanding_smps", 'o', 1, NULL,
1100 		 "specify the number of outstanding SMP's which should be "
1101 		 "issued during the scan"},
1102 		{0}
1103 	};
1104 	char usage_args[] = "[topology-file]";
1105 
1106 	ibdiag_process_opts(argc, argv, &config, "DGKLs", opts, process_opt,
1107 			    usage_args, NULL);
1108 
1109 	f = stdout;
1110 
1111 	argc -= optind;
1112 	argv += optind;
1113 
1114 	if (ibd_timeout)
1115 		config.timeout_ms = ibd_timeout;
1116 
1117 	config.flags = ibd_ibnetdisc_flags;
1118 
1119 	if (argc && !(f = fopen(argv[0], "w")))
1120 		IBEXIT("can't open file %s for writing", argv[0]);
1121 
1122 	config.mkey = ibd_mkey;
1123 
1124 	node_name_map = open_node_name_map(node_name_map_file);
1125 
1126 	if (diff_cache_file &&
1127 	    !(diff_fabric = ibnd_load_fabric(diff_cache_file, 0)))
1128 		IBEXIT("loading cached fabric for diff failed\n");
1129 
1130 	if (load_cache_file) {
1131 		if ((fabric = ibnd_load_fabric(load_cache_file, 0)) == NULL)
1132 			IBEXIT("loading cached fabric failed\n");
1133 	} else {
1134 		if ((fabric =
1135 		     ibnd_discover_fabric(ibd_ca, ibd_ca_port, NULL, &config)) == NULL)
1136 			IBEXIT("discover failed\n");
1137 	}
1138 
1139 	if (ports_report)
1140 		ibnd_iter_nodes(fabric, dump_ports_report, NULL);
1141 	else if (list)
1142 		list_nodes(fabric, list);
1143 	else if (diff_fabric)
1144 		diff(diff_fabric, fabric);
1145 	else
1146 		dump_topology(group, fabric);
1147 
1148 	if (cache_file)
1149 		if (ibnd_cache_fabric(fabric, cache_file, 0) < 0)
1150 			IBEXIT("caching ibnetdiscover data failed\n");
1151 
1152 	ibnd_destroy_fabric(fabric);
1153 	if (diff_fabric)
1154 		ibnd_destroy_fabric(diff_fabric);
1155 	close_node_name_map(node_name_map);
1156 	exit(0);
1157 }
1158