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
71static FILE *f;
72
73static char *node_name_map_file = NULL;
74static nn_map_t *node_name_map = NULL;
75static char *cache_file = NULL;
76static char *load_cache_file = NULL;
77static char *diff_cache_file = NULL;
78static unsigned diffcheck_flags = DIFF_FLAG_DEFAULT;
79
80static int report_max_hops = 0;
81static 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 */
87char *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
103char *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
122char *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
144static 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
157char *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
180void 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
210void 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
220void 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
246uint64_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
258void 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
272void 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
298void 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
319void 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
346static 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
359void 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
430void 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
487struct iter_user_data {
488	int group;
489	int skip_chassis_nodes;
490};
491
492static 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
513static 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
532static 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
550int 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
692void 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
763struct 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
774static 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
784static 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
873static 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
928static 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
972int 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
992static int list, group, ports_report;
993
994static 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
1073int 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