]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ofed/infiniband-diags/src/ibnetdiscover.c
Merge compiler-rt trunk r366426, resolve conflicts, and add
[FreeBSD/FreeBSD.git] / contrib / ofed / infiniband-diags / src / ibnetdiscover.c
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 }