infiniband-diags: initial port of linux ib diags
[mirror/winof/.git] / tools / infiniband_diags / src / ibroute.c
1 /*\r
2  * Copyright (c) 2004-2008 Voltaire Inc.  All rights reserved.\r
3  *\r
4  * This software is available to you under a choice of one of two\r
5  * licenses.  You may choose to be licensed under the terms of the GNU\r
6  * General Public License (GPL) Version 2, available from the file\r
7  * COPYING in the main directory of this source tree, or the\r
8  * OpenIB.org BSD license below:\r
9  *\r
10  *     Redistribution and use in source and binary forms, with or\r
11  *     without modification, are permitted provided that the following\r
12  *     conditions are met:\r
13  *\r
14  *      - Redistributions of source code must retain the above\r
15  *        copyright notice, this list of conditions and the following\r
16  *        disclaimer.\r
17  *\r
18  *      - Redistributions in binary form must reproduce the above\r
19  *        copyright notice, this list of conditions and the following\r
20  *        disclaimer in the documentation and/or other materials\r
21  *        provided with the distribution.\r
22  *\r
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
30  * SOFTWARE.\r
31  *\r
32  */\r
33 \r
34 #if HAVE_CONFIG_H\r
35 #  include <config.h>\r
36 #endif /* HAVE_CONFIG_H */\r
37 \r
38 #include <stdio.h>\r
39 #include <stdlib.h>\r
40 #include <unistd.h>\r
41 #include <stdarg.h>\r
42 #include <time.h>\r
43 #include <string.h>\r
44 #include <inttypes.h>\r
45 #include <getopt.h>\r
46 #include <netinet/in.h>\r
47 #include <ctype.h>\r
48 \r
49 #include <infiniband/common.h>\r
50 #include <infiniband/umad.h>\r
51 #include <infiniband/mad.h>\r
52 #include <infiniband/complib/cl_nodenamemap.h>\r
53 \r
54 #include "ibdiag_common.h"\r
55 \r
56 static int dest_type = IB_DEST_LID;\r
57 static int brief;\r
58 static int verbose;\r
59 static int dump_all;\r
60 \r
61 char *argv0 = "ibroute";\r
62 \r
63 /*******************************************/\r
64 \r
65 char *\r
66 check_switch(ib_portid_t *portid, int *nports, uint64_t *guid,\r
67              uint8_t *sw, char *nd)\r
68 {\r
69         uint8_t ni[IB_SMP_DATA_SIZE] = {0};\r
70         int type;\r
71 \r
72         DEBUG("checking node type");\r
73         if (!smp_query(ni, portid, IB_ATTR_NODE_INFO, 0, 0)) {\r
74                 xdump(stderr, "nodeinfo\n", ni, sizeof ni);\r
75                 return "node info failed: valid addr?";\r
76         }\r
77 \r
78         if (!smp_query(nd, portid, IB_ATTR_NODE_DESC, 0, 0))\r
79                 return "node desc failed";\r
80 \r
81         mad_decode_field(ni, IB_NODE_TYPE_F, &type);\r
82         if (type != IB_NODE_SWITCH)\r
83                 return "not a switch";\r
84 \r
85         DEBUG("Gathering information about switch");\r
86         mad_decode_field(ni, IB_NODE_NPORTS_F, nports);\r
87         mad_decode_field(ni, IB_NODE_GUID_F, guid);\r
88 \r
89         if (!smp_query(sw, portid, IB_ATTR_SWITCH_INFO, 0, 0))\r
90                 return "switch info failed: is a switch node?";\r
91 \r
92         return 0;\r
93 }\r
94 \r
95 #define IB_MLIDS_IN_BLOCK       (IB_SMP_DATA_SIZE/2)\r
96 \r
97 int\r
98 dump_mlid(char *str, int strlen, int mlid, int nports,\r
99           uint16_t mft[16][IB_MLIDS_IN_BLOCK])\r
100 {\r
101         uint16_t mask;\r
102         int i, chunk, bit;\r
103         int nonzero = 0;\r
104 \r
105         if (brief) {\r
106                 int n = 0, chunks = ALIGN(nports + 1, 16) / 16;\r
107                 for (i = 0; i < chunks; i++) {\r
108                         mask = ntohs(mft[i][mlid%IB_MLIDS_IN_BLOCK]);\r
109                         if (mask)\r
110                                 nonzero++;\r
111                         n += snprintf(str + n, strlen - n, "%04hx", mask);\r
112                         if (n >= strlen) {\r
113                                 n = strlen;\r
114                                 break;\r
115                         }\r
116                 }\r
117                 if (!nonzero && !dump_all) {\r
118                         str[0] = 0;\r
119                         return 0;\r
120                 }\r
121                 return n;\r
122         }\r
123         for (i = 0; i <= nports; i++) {\r
124                 chunk = i / 16;\r
125                 bit = i % 16;\r
126 \r
127                 mask = ntohs(mft[chunk][mlid%IB_MLIDS_IN_BLOCK]);\r
128                 if (mask)\r
129                         nonzero++;\r
130                 str[i*2] = (mask & (1 << bit)) ? 'x' : ' ';\r
131                 str[i*2+1] = ' ';\r
132         }\r
133         if (!nonzero && !dump_all) {\r
134                 str[0] = 0;\r
135                 return 0;\r
136         }\r
137         str[i*2] = 0;\r
138         return i * 2;\r
139 }\r
140 \r
141 uint16_t mft[16][IB_MLIDS_IN_BLOCK];\r
142 \r
143 char *\r
144 dump_multicast_tables(ib_portid_t *portid, int startlid, int endlid)\r
145 {\r
146         char nd[IB_SMP_DATA_SIZE] = {0};\r
147         uint8_t sw[IB_SMP_DATA_SIZE] = {0};\r
148         char str[512];\r
149         char *s;\r
150         uint64_t nodeguid;\r
151         uint32_t mod;\r
152         int block, i, j, e, nports, cap, chunks;\r
153         int n = 0, startblock, lastblock;\r
154 \r
155         if ((s = check_switch(portid, &nports, &nodeguid, sw, nd)))\r
156                 return s;\r
157 \r
158         mad_decode_field(sw, IB_SW_MCAST_FDB_CAP_F, &cap);\r
159 \r
160         if (!endlid || endlid > IB_MIN_MCAST_LID + cap - 1)\r
161                 endlid = IB_MIN_MCAST_LID + cap - 1;\r
162 \r
163         if (!startlid)\r
164                 startlid = IB_MIN_MCAST_LID;\r
165 \r
166         if (startlid < IB_MIN_MCAST_LID) {\r
167                 IBWARN("illegal start mlid %x, set to %x", startlid, IB_MIN_MCAST_LID);\r
168                 startlid = IB_MIN_MCAST_LID;\r
169         }\r
170 \r
171         if (endlid > IB_MAX_MCAST_LID) {\r
172                 IBWARN("illegal end mlid %x, truncate to %x", endlid, IB_MAX_MCAST_LID);\r
173                 endlid = IB_MAX_MCAST_LID;\r
174         }\r
175 \r
176         printf("Multicast mlids [0x%x-0x%x] of switch %s guid 0x%016" PRIx64 " (%s):\n",\r
177                 startlid, endlid, portid2str(portid), nodeguid, clean_nodedesc(nd));\r
178 \r
179         if (brief)\r
180                 printf(" MLid       Port Mask\n");\r
181         else {\r
182                 if (nports > 9) {\r
183                         for (i = 0, s = str; i <= nports; i++) {\r
184                                 *s++ = (i%10) ? ' ' : '0' + i/10;\r
185                                 *s++ = ' ';\r
186                         }\r
187                         *s = 0;\r
188                         printf("            %s\n", str);\r
189                 }\r
190                 for (i = 0, s = str; i <= nports; i++)\r
191                         s += sprintf(s, "%d ", i%10);\r
192                 printf("     Ports: %s\n", str);\r
193                 printf(" MLid\n");\r
194         }\r
195         if (verbose)\r
196                 printf("Switch muticast mlids capability is 0x%d\n", cap);\r
197 \r
198         chunks = ALIGN(nports + 1, 16) / 16;\r
199 \r
200         startblock = startlid / IB_MLIDS_IN_BLOCK;\r
201         lastblock = endlid / IB_MLIDS_IN_BLOCK;\r
202         for (block = startblock; block <= lastblock; block++) {\r
203                 for (j = 0; j < chunks; j++) {\r
204                         mod = (block - IB_MIN_MCAST_LID/IB_MLIDS_IN_BLOCK) | (j << 28);\r
205 \r
206                         DEBUG("reading block %x chunk %d mod %x", block, j, mod);\r
207                         if (!smp_query(mft + j, portid, IB_ATTR_MULTICASTFORWTBL, mod, 0))\r
208                                 return "multicast forwarding table get failed";\r
209                 }\r
210 \r
211                 i = block * IB_MLIDS_IN_BLOCK;\r
212                 e = i + IB_MLIDS_IN_BLOCK;\r
213                 if (i < startlid)\r
214                         i = startlid;\r
215                 if (e > endlid + 1)\r
216                         e = endlid + 1;\r
217 \r
218                 for (; i < e; i++) {\r
219                         if (dump_mlid(str, sizeof str, i, nports, mft) == 0)\r
220                                 continue;\r
221                         printf("0x%04x      %s\n", i, str);\r
222                         n++;\r
223                 }\r
224         }\r
225 \r
226         printf("%d %smlids dumped \n", n, dump_all ? "" : "valid ");\r
227         return 0;\r
228 }\r
229 \r
230 int\r
231 dump_lid(char *str, int strlen, int lid, int valid)\r
232 {\r
233         char nd[IB_SMP_DATA_SIZE] = {0};\r
234         uint8_t ni[IB_SMP_DATA_SIZE] = {0};\r
235         uint8_t pi[IB_SMP_DATA_SIZE] = {0};\r
236         ib_portid_t lidport = {0};\r
237         static int last_port_lid, base_port_lid;\r
238         char ntype[50], sguid[30], desc[64];\r
239         static uint64_t portguid;\r
240         int baselid, lmc, type;\r
241 \r
242         if (brief) {\r
243                 str[0] = 0;\r
244                 return 0;\r
245         }\r
246 \r
247         if (lid <= last_port_lid) {\r
248                 if (!valid)\r
249                         return snprintf(str, strlen, ": (path #%d - illegal port)",\r
250                                         lid - base_port_lid);\r
251                 else if (!portguid)\r
252                         return snprintf(str, strlen,\r
253                                         ": (path #%d out of %d)",\r
254                                         lid - base_port_lid + 1,\r
255                                         last_port_lid - base_port_lid + 1);\r
256                 else {\r
257                         return snprintf(str, strlen,\r
258                                         ": (path #%d out of %d: portguid %s)",\r
259                                         lid - base_port_lid + 1,\r
260                                         last_port_lid - base_port_lid + 1,\r
261                                         mad_dump_val(IB_NODE_PORT_GUID_F, sguid, sizeof sguid, &portguid));\r
262                 }\r
263         }\r
264 \r
265         if (!valid)\r
266                 return snprintf(str, strlen, ": (illegal port)");\r
267 \r
268         portguid = 0;\r
269         lidport.lid = lid;\r
270 \r
271         if (!smp_query(nd, &lidport, IB_ATTR_NODE_DESC, 0, 100) ||\r
272             !smp_query(pi, &lidport, IB_ATTR_PORT_INFO, 0, 100) ||\r
273             !smp_query(ni, &lidport, IB_ATTR_NODE_INFO, 0, 100))\r
274                 return snprintf(str, strlen, ": (unknown node and type)");\r
275 \r
276         mad_decode_field(ni, IB_NODE_PORT_GUID_F, &portguid);\r
277         mad_decode_field(ni, IB_NODE_TYPE_F, &type);\r
278 \r
279         mad_decode_field(pi, IB_PORT_LID_F, &baselid);\r
280         mad_decode_field(pi, IB_PORT_LMC_F, &lmc);\r
281 \r
282         if (lmc > 0) {\r
283                 base_port_lid = baselid;\r
284                 last_port_lid = baselid + (1 << lmc) - 1;\r
285         }\r
286 \r
287         return snprintf(str, strlen, ": (%s portguid %s: %s)",\r
288                 mad_dump_val(IB_NODE_TYPE_F, ntype, sizeof ntype, &type),\r
289                 mad_dump_val(IB_NODE_PORT_GUID_F, sguid, sizeof sguid, &portguid),\r
290                 mad_dump_val(IB_NODE_DESC_F, desc, sizeof desc, clean_nodedesc(nd)));\r
291 }\r
292 \r
293 char *\r
294 dump_unicast_tables(ib_portid_t *portid, int startlid, int endlid)\r
295 {\r
296         char lft[IB_SMP_DATA_SIZE];\r
297         char nd[IB_SMP_DATA_SIZE];\r
298         uint8_t sw[IB_SMP_DATA_SIZE];\r
299         char str[200], *s;\r
300         uint64_t nodeguid;\r
301         int block, i, e, nports, top;\r
302         int n = 0, startblock, endblock;\r
303 \r
304         if ((s = check_switch(portid, &nports, &nodeguid, sw, nd)))\r
305                 return s;\r
306 \r
307         mad_decode_field(sw, IB_SW_LINEAR_FDB_TOP_F, &top);\r
308 \r
309         if (!endlid || endlid > top)\r
310                 endlid = top;\r
311 \r
312         if (endlid > IB_MAX_UCAST_LID) {\r
313                 IBWARN("ilegal lft top %d, truncate to %d", endlid, IB_MAX_UCAST_LID);\r
314                 endlid = IB_MAX_UCAST_LID;\r
315         }\r
316 \r
317         printf("Unicast lids [0x%x-0x%x] of switch %s guid 0x%016" PRIx64 " (%s):\n",\r
318                 startlid, endlid, portid2str(portid), nodeguid, clean_nodedesc(nd));\r
319 \r
320         DEBUG("Switch top is 0x%x\n", top);\r
321 \r
322         printf("  Lid  Out   Destination\n");\r
323         printf("       Port     Info \n");\r
324         startblock = startlid / IB_SMP_DATA_SIZE;\r
325         endblock = ALIGN(endlid, IB_SMP_DATA_SIZE) / IB_SMP_DATA_SIZE;\r
326         for (block = startblock; block <= endblock; block++) {\r
327                 DEBUG("reading block %d", block);\r
328                 if (!smp_query(lft, portid, IB_ATTR_LINEARFORWTBL, block, 0))\r
329                         return "linear forwarding table get failed";\r
330                 i = block * IB_SMP_DATA_SIZE;\r
331                 e = i + IB_SMP_DATA_SIZE;\r
332                 if (i < startlid)\r
333                         i = startlid;\r
334                 if (e > endlid + 1)\r
335                         e = endlid + 1;\r
336 \r
337                 for (;i < e; i++) {\r
338                         unsigned outport = lft[i % IB_SMP_DATA_SIZE];\r
339                         unsigned valid = (outport <= nports);\r
340 \r
341                         if (!valid && !dump_all)\r
342                                 continue;\r
343                         dump_lid(str, sizeof str, i, valid);\r
344                         printf("0x%04x %03u %s\n", i, outport & 0xff, str);\r
345                         n++;\r
346                 }\r
347         }\r
348 \r
349         printf("%d %slids dumped \n", n, dump_all ? "" : "valid ");\r
350         return 0;\r
351 }\r
352 \r
353 void\r
354 usage(void)\r
355 {\r
356         char *basename;\r
357 \r
358         if (!(basename = strrchr(argv0, '/')))\r
359                 basename = argv0;\r
360         else\r
361                 basename++;\r
362 \r
363         fprintf(stderr, "Usage: %s [-d(ebug)] -a(ll) -n(o_dests) -v(erbose) -D(irect) -G(uid) -M(ulticast) -s smlid -V(ersion) -C ca_name -P ca_port "\r
364                         "-t(imeout) timeout_ms] [<dest dr_path|lid|guid> [<startlid> [<endlid>]]]\n",\r
365                         basename);\r
366         fprintf(stderr, "\n\tUnicast examples:\n");\r
367         fprintf(stderr, "\t\t%s 4\t# dump all lids with valid out ports of switch with lid 4\n", basename);\r
368         fprintf(stderr, "\t\t%s -a 4\t# same, but dump all lids, even with invalid out ports\n", basename);\r
369         fprintf(stderr, "\t\t%s -n 4\t# simple dump format - no destination resolving\n", basename);\r
370         fprintf(stderr, "\t\t%s 4 10\t# dump lids starting from 10\n", basename);\r
371         fprintf(stderr, "\t\t%s 4 0x10 0x20\t# dump lid range\n", basename);\r
372         fprintf(stderr, "\t\t%s -G 0x08f1040023\t# resolve switch by GUID\n", basename);\r
373         fprintf(stderr, "\t\t%s -D 0,1\t# resolve switch by direct path\n", basename);\r
374 \r
375         fprintf(stderr, "\n\tMulticast examples:\n");\r
376         fprintf(stderr, "\t\t%s -M 4\t# dump all non empty mlids of switch with lid 4\n", basename);\r
377         fprintf(stderr, "\t\t%s -M 4 0xc010 0xc020\t# same, but with range\n", basename);\r
378         fprintf(stderr, "\t\t%s -M -n 4\t# simple dump format\n", basename);\r
379         exit(-1);\r
380 }\r
381 \r
382 int\r
383 main(int argc, char **argv)\r
384 {\r
385         int mgmt_classes[3] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, IB_SA_CLASS};\r
386         ib_portid_t portid = {0};\r
387         ib_portid_t *sm_id = 0, sm_portid = {0};\r
388         int timeout;\r
389         int multicast = 0, startlid = 0, endlid = 0;\r
390         char *err;\r
391         char *ca = 0;\r
392         int ca_port = 0;\r
393 \r
394         static char const str_opts[] = "C:P:t:s:danvDGMVhu";\r
395         static const struct option long_opts[] = {\r
396                 { "C", 1, 0, 'C'},\r
397                 { "P", 1, 0, 'P'},\r
398                 { "debug", 0, 0, 'd'},\r
399                 { "all", 0, 0, 'a'},\r
400                 { "no_dests", 0, 0, 'n'},\r
401                 { "verbose", 0, 0, 'v'},\r
402                 { "Direct", 0, 0, 'D'},\r
403                 { "Guid", 0, 0, 'G'},\r
404                 { "Multicast", 0, 0, 'M'},\r
405                 { "timeout", 1, 0, 't'},\r
406                 { "s", 1, 0, 's'},\r
407                 { "Version", 0, 0, 'V'},\r
408                 { "help", 0, 0, 'h'},\r
409                 { "usage", 0, 0, 'u'},\r
410                 { }\r
411         };\r
412 \r
413         argv0 = argv[0];\r
414 \r
415         while (1) {\r
416                 int ch = getopt_long(argc, argv, str_opts, long_opts, NULL);\r
417                 if ( ch == -1 )\r
418                         break;\r
419                 switch(ch) {\r
420                 case 'C':\r
421                         ca = optarg;\r
422                         break;\r
423                 case 'P':\r
424                         ca_port = strtoul(optarg, 0, 0);\r
425                         break;\r
426                 case 'a':\r
427                         dump_all++;\r
428                         break;\r
429                 case 'd':\r
430                         ibdebug++;\r
431                         break;\r
432                 case 'D':\r
433                         dest_type = IB_DEST_DRPATH;\r
434                         break;\r
435                 case 'G':\r
436                         dest_type = IB_DEST_GUID;\r
437                         break;\r
438                 case 'M':\r
439                         multicast++;\r
440                         break;\r
441                 case 'n':\r
442                         brief++;\r
443                         break;\r
444                 case 's':\r
445                         if (ib_resolve_portid_str(&sm_portid, optarg, IB_DEST_LID, 0) < 0)\r
446                                 IBERROR("can't resolve SM destination port %s", optarg);\r
447                         sm_id = &sm_portid;\r
448                         break;\r
449                 case 't':\r
450                         timeout = strtoul(optarg, 0, 0);\r
451                         madrpc_set_timeout(timeout);\r
452                         break;\r
453                 case 'v':\r
454                         madrpc_show_errors(1);\r
455                         verbose++;\r
456                         break;\r
457                 case 'V':\r
458                         fprintf(stderr, "%s %s\n", argv0, get_build_version() );\r
459                         exit(-1);\r
460                 default:\r
461                         usage();\r
462                         break;\r
463                 }\r
464         }\r
465         argc -= optind;\r
466         argv += optind;\r
467 \r
468         if (!argc)\r
469                 usage();\r
470 \r
471         if (argc > 1)\r
472                 startlid = strtoul(argv[1], 0, 0);\r
473         if (argc > 2)\r
474                 endlid = strtoul(argv[2], 0, 0);\r
475 \r
476         madrpc_init(ca, ca_port, mgmt_classes, 3);\r
477 \r
478         if (!argc) {\r
479                 if (ib_resolve_self(&portid, 0, 0) < 0)\r
480                         IBERROR("can't resolve self addr");\r
481         } else {\r
482                 if (ib_resolve_portid_str(&portid, argv[0], dest_type, sm_id) < 0)\r
483                         IBERROR("can't resolve destination port %s", argv[1]);\r
484         }\r
485 \r
486         if (multicast)\r
487                 err = dump_multicast_tables(&portid, startlid, endlid);\r
488         else\r
489                 err = dump_unicast_tables(&portid, startlid, endlid);\r
490 \r
491         if (err)\r
492                 IBERROR("dump tables: %s", err);\r
493 \r
494         exit(0);\r
495 }\r