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 Laboratory
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 <string.h>
46#include <errno.h>
47#include <inttypes.h>
48
49#include <infiniband/umad.h>
50#include <infiniband/mad.h>
51
52#include <infiniband/ibnetdisc.h>
53#include <complib/cl_nodenamemap.h>
54
55#include "internal.h"
56#include "chassis.h"
57
58/* forward declarations */
59struct ni_cbdata
60{
61	ibnd_node_t *node;
62	int port_num;
63};
64static int query_node_info(smp_engine_t * engine, ib_portid_t * portid,
65			   struct ni_cbdata * cbdata);
66static int query_port_info(smp_engine_t * engine, ib_portid_t * portid,
67			   ibnd_node_t * node, int portnum);
68ibnd_port_t *ibnd_find_port_dr(ibnd_fabric_t * fabric, char *dr_str);
69
70static int recv_switch_info(smp_engine_t * engine, ibnd_smp_t * smp,
71			    uint8_t * mad, void *cb_data)
72{
73	uint8_t *switch_info = mad + IB_SMP_DATA_OFFS;
74	ibnd_node_t *node = cb_data;
75	memcpy(node->switchinfo, switch_info, sizeof(node->switchinfo));
76	mad_decode_field(node->switchinfo, IB_SW_ENHANCED_PORT0_F,
77			 &node->smaenhsp0);
78	return 0;
79}
80
81static int query_switch_info(smp_engine_t * engine, ib_portid_t * portid,
82			     ibnd_node_t * node)
83{
84	node->smaenhsp0 = 0;	/* assume base SP0 */
85	return issue_smp(engine, portid, IB_ATTR_SWITCH_INFO, 0,
86			 recv_switch_info, node);
87}
88
89static int add_port_to_dpath(ib_dr_path_t * path, int nextport)
90{
91	if (path->cnt > sizeof(path->p) - 2)
92		return -1;
93	++path->cnt;
94	path->p[path->cnt] = (uint8_t) nextport;
95	return path->cnt;
96}
97
98static int retract_dpath(smp_engine_t * engine, ib_portid_t * portid)
99{
100	ibnd_scan_t *scan = engine->user_data;
101	f_internal_t *f_int = scan->f_int;
102
103	if (scan->cfg->max_hops &&
104	    f_int->fabric.maxhops_discovered > scan->cfg->max_hops)
105		return 0;
106
107	/* this may seem wrong but the only time we would retract the path is
108	 * if the user specified a CA for the DR path and we are retracting
109	 * from that to find the node it is connected to.  This counts as a
110	 * positive hop discovered
111	 */
112	f_int->fabric.maxhops_discovered++;
113	portid->drpath.p[portid->drpath.cnt] = 0;
114	portid->drpath.cnt--;
115	return 1;
116}
117
118static int extend_dpath(smp_engine_t * engine, ib_portid_t * portid,
119			int nextport)
120{
121	ibnd_scan_t *scan = engine->user_data;
122	f_internal_t *f_int = scan->f_int;
123
124	if (scan->cfg->max_hops &&
125	    f_int->fabric.maxhops_discovered > scan->cfg->max_hops)
126		return 0;
127
128	if (portid->lid) {
129		/* If we were LID routed we need to set up the drslid */
130		portid->drpath.drslid = (uint16_t) scan->selfportid.lid;
131		portid->drpath.drdlid = 0xFFFF;
132	}
133
134	if (add_port_to_dpath(&portid->drpath, nextport) < 0) {
135		IBND_ERROR("add port %d to DR path failed; %s\n", nextport,
136			   portid2str(portid));
137		return -1;
138	}
139
140	if (((unsigned) portid->drpath.cnt - scan->initial_hops) >
141	    f_int->fabric.maxhops_discovered)
142		f_int->fabric.maxhops_discovered++;
143
144	return 1;
145}
146
147static int recv_node_desc(smp_engine_t * engine, ibnd_smp_t * smp,
148			  uint8_t * mad, void *cb_data)
149{
150	uint8_t *node_desc = mad + IB_SMP_DATA_OFFS;
151	ibnd_node_t *node = cb_data;
152	memcpy(node->nodedesc, node_desc, sizeof(node->nodedesc));
153	return 0;
154}
155
156static int query_node_desc(smp_engine_t * engine, ib_portid_t * portid,
157			   ibnd_node_t * node)
158{
159	return issue_smp(engine, portid, IB_ATTR_NODE_DESC, 0,
160			 recv_node_desc, node);
161}
162
163static void debug_port(ib_portid_t * portid, ibnd_port_t * port)
164{
165	char width[64], speed[64];
166	int iwidth;
167	int ispeed, fdr10, espeed;
168	uint8_t *info;
169	uint32_t cap_mask;
170
171	iwidth = mad_get_field(port->info, 0, IB_PORT_LINK_WIDTH_ACTIVE_F);
172	ispeed = mad_get_field(port->info, 0, IB_PORT_LINK_SPEED_ACTIVE_F);
173	fdr10 = mad_get_field(port->ext_info, 0,
174			      IB_MLNX_EXT_PORT_LINK_SPEED_ACTIVE_F);
175
176	if (port->node->type == IB_NODE_SWITCH)
177		info = (uint8_t *)&port->node->ports[0]->info;
178	else
179		info = (uint8_t *)&port->info;
180	cap_mask = mad_get_field(info, 0, IB_PORT_CAPMASK_F);
181	if (cap_mask & CL_NTOH32(IB_PORT_CAP_HAS_EXT_SPEEDS))
182		espeed = mad_get_field(port->info, 0, IB_PORT_LINK_SPEED_EXT_ACTIVE_F);
183	else
184		espeed = 0;
185	IBND_DEBUG
186	    ("portid %s portnum %d: base lid %d state %d physstate %d %s %s %s %s\n",
187	     portid2str(portid), port->portnum, port->base_lid,
188	     mad_get_field(port->info, 0, IB_PORT_STATE_F),
189	     mad_get_field(port->info, 0, IB_PORT_PHYS_STATE_F),
190	     mad_dump_val(IB_PORT_LINK_WIDTH_ACTIVE_F, width, 64, &iwidth),
191	     mad_dump_val(IB_PORT_LINK_SPEED_ACTIVE_F, speed, 64, &ispeed),
192	     (fdr10 & FDR10) ? "FDR10"  : "",
193	     mad_dump_val(IB_PORT_LINK_SPEED_EXT_ACTIVE_F, speed, 64, &espeed));
194}
195
196static int is_mlnx_ext_port_info_supported(ibnd_port_t * port)
197{
198	uint16_t devid = (uint16_t) mad_get_field(port->node->info, 0, IB_NODE_DEVID_F);
199	uint32_t vendorid = (uint32_t) mad_get_field(port->node->info, 0, IB_NODE_VENDORID_F);
200
201	if ((devid >= 0xc738 && devid <= 0xc73b) || devid == 0xcb20 || devid == 0xcf08 ||
202	    ((vendorid == 0x119f) &&
203	     /* Bull SwitchX */
204	     (devid == 0x1b02 || devid == 0x1b50 ||
205	      /* Bull SwitchIB and SwitchIB2 */
206	      devid == 0x1ba0 ||
207	      (devid >= 0x1bd0 && devid <= 0x1bd5))))
208		return 1;
209	if ((devid >= 0x1003 && devid <= 0x1017) ||
210	    ((vendorid == 0x119f) &&
211	     /* Bull ConnectX3 */
212	     (devid == 0x1b33 || devid == 0x1b73 ||
213	      devid == 0x1b40 || devid == 0x1b41 ||
214	      devid == 0x1b60 || devid == 0x1b61 ||
215	     /* Bull ConnectIB */
216	      devid == 0x1b83 ||
217	      devid == 0x1b93 || devid == 0x1b94 ||
218	      /* Bull ConnectX4 */
219	      devid == 0x1bb4 || devid == 0x1bb5 ||
220	      devid == 0x1bc4)))
221		return 1;
222	return 0;
223}
224
225int mlnx_ext_port_info_err(smp_engine_t * engine, ibnd_smp_t * smp,
226			   uint8_t * mad, void *cb_data)
227{
228	f_internal_t *f_int = ((ibnd_scan_t *) engine->user_data)->f_int;
229	ibnd_node_t *node = cb_data;
230	ibnd_port_t *port;
231	uint8_t port_num, local_port;
232
233	port_num = (uint8_t) mad_get_field(mad, 0, IB_MAD_ATTRMOD_F);
234	port = node->ports[port_num];
235	if (!port) {
236		IBND_ERROR("Failed to find 0x%" PRIx64 " port %u\n",
237			   node->guid, port_num);
238		return -1;
239	}
240
241	local_port = (uint8_t) mad_get_field(port->info, 0, IB_PORT_LOCAL_PORT_F);
242	debug_port(&smp->path, port);
243
244	if (port_num && mad_get_field(port->info, 0, IB_PORT_PHYS_STATE_F)
245	    == IB_PORT_PHYS_STATE_LINKUP
246	    && ((node->type == IB_NODE_SWITCH && port_num != local_port) ||
247		(node == f_int->fabric.from_node && port_num == f_int->fabric.from_portnum))) {
248		int rc = 0;
249		ib_portid_t path = smp->path;
250
251		if (node->type != IB_NODE_SWITCH &&
252		    node == f_int->fabric.from_node &&
253		    path.drpath.cnt > 1)
254			rc = retract_dpath(engine, &path);
255		else {
256			/* we can't proceed through an HCA with DR */
257			if (path.lid == 0 || node->type == IB_NODE_SWITCH)
258				rc = extend_dpath(engine, &path, port_num);
259		}
260
261		if (rc > 0) {
262			struct ni_cbdata * cbdata = malloc(sizeof(*cbdata));
263			cbdata->node = node;
264			cbdata->port_num = port_num;
265			query_node_info(engine, &path, cbdata);
266		}
267	}
268
269	return 0;
270}
271
272static int recv_mlnx_ext_port_info(smp_engine_t * engine, ibnd_smp_t * smp,
273				   uint8_t * mad, void *cb_data)
274{
275	f_internal_t *f_int = ((ibnd_scan_t *) engine->user_data)->f_int;
276	ibnd_node_t *node = cb_data;
277	ibnd_port_t *port;
278	uint8_t *ext_port_info = mad + IB_SMP_DATA_OFFS;
279	uint8_t port_num, local_port;
280
281	port_num = (uint8_t) mad_get_field(mad, 0, IB_MAD_ATTRMOD_F);
282	port = node->ports[port_num];
283	if (!port) {
284		IBND_ERROR("Failed to find 0x%" PRIx64 " port %u\n",
285			   node->guid, port_num);
286		return -1;
287	}
288
289	memcpy(port->ext_info, ext_port_info, sizeof(port->ext_info));
290	local_port = (uint8_t) mad_get_field(port->info, 0, IB_PORT_LOCAL_PORT_F);
291	debug_port(&smp->path, port);
292
293	if (port_num && mad_get_field(port->info, 0, IB_PORT_PHYS_STATE_F)
294	    == IB_PORT_PHYS_STATE_LINKUP
295	    && ((node->type == IB_NODE_SWITCH && port_num != local_port) ||
296		(node == f_int->fabric.from_node && port_num == f_int->fabric.from_portnum))) {
297		int rc = 0;
298		ib_portid_t path = smp->path;
299
300		if (node->type != IB_NODE_SWITCH &&
301		    node == f_int->fabric.from_node &&
302		    path.drpath.cnt > 1)
303			rc = retract_dpath(engine, &path);
304		else {
305			/* we can't proceed through an HCA with DR */
306			if (path.lid == 0 || node->type == IB_NODE_SWITCH)
307				rc = extend_dpath(engine, &path, port_num);
308		}
309
310		if (rc > 0) {
311			struct ni_cbdata * cbdata = malloc(sizeof(*cbdata));
312			cbdata->node = node;
313			cbdata->port_num = port_num;
314			query_node_info(engine, &path, cbdata);
315		}
316	}
317
318	return 0;
319}
320
321static int query_mlnx_ext_port_info(smp_engine_t * engine, ib_portid_t * portid,
322				    ibnd_node_t * node, int portnum)
323{
324	IBND_DEBUG("Query MLNX Extended Port Info; %s (0x%" PRIx64 "):%d\n",
325		   portid2str(portid), node->guid, portnum);
326	return issue_smp(engine, portid, IB_ATTR_MLNX_EXT_PORT_INFO, portnum,
327			 recv_mlnx_ext_port_info, node);
328}
329
330static int recv_port_info(smp_engine_t * engine, ibnd_smp_t * smp,
331			  uint8_t * mad, void *cb_data)
332{
333	ibnd_scan_t *scan = (ibnd_scan_t *)engine->user_data;
334	f_internal_t *f_int = scan->f_int;
335	ibnd_node_t *node = cb_data;
336	ibnd_port_t *port;
337	uint8_t *port_info = mad + IB_SMP_DATA_OFFS;
338	uint8_t port_num, local_port;
339	int phystate, ispeed, espeed;
340	uint8_t *info;
341	uint32_t cap_mask;
342
343	port_num = (uint8_t) mad_get_field(mad, 0, IB_MAD_ATTRMOD_F);
344	local_port = (uint8_t) mad_get_field(port_info, 0, IB_PORT_LOCAL_PORT_F);
345
346	/* this may have been created before */
347	port = node->ports[port_num];
348	if (!port) {
349		port = node->ports[port_num] = calloc(1, sizeof(*port));
350		if (!port) {
351			IBND_ERROR("Failed to allocate 0x%" PRIx64 " port %u\n",
352				    node->guid, port_num);
353			return -1;
354		}
355		port->guid =
356		    mad_get_field64(node->info, 0, IB_NODE_PORT_GUID_F);
357	}
358
359	memcpy(port->info, port_info, sizeof(port->info));
360	port->node = node;
361	port->portnum = port_num;
362	port->ext_portnum = 0;
363	port->base_lid = (uint16_t) mad_get_field(port->info, 0, IB_PORT_LID_F);
364	port->lmc = (uint8_t) mad_get_field(port->info, 0, IB_PORT_LMC_F);
365
366	if (port_num == 0) {
367		node->smalid = port->base_lid;
368		node->smalmc = port->lmc;
369	} else if (node->type == IB_NODE_SWITCH) {
370		port->base_lid = node->smalid;
371		port->lmc = node->smalmc;
372	}
373
374	int rc1 = add_to_portguid_hash(port, f_int->fabric.portstbl);
375	if (rc1)
376		IBND_ERROR("Error Occurred when trying"
377			   " to insert new port guid 0x%016" PRIx64 " to DB\n",
378			   port->guid);
379
380	add_to_portlid_hash(port, f_int->lid2guid);
381
382	if ((scan->cfg->flags & IBND_CONFIG_MLX_EPI)
383	    && is_mlnx_ext_port_info_supported(port)) {
384		phystate = mad_get_field(port->info, 0, IB_PORT_PHYS_STATE_F);
385		ispeed = mad_get_field(port->info, 0, IB_PORT_LINK_SPEED_ACTIVE_F);
386		if (port->node->type == IB_NODE_SWITCH)
387			info = (uint8_t *)&port->node->ports[0]->info;
388		else
389			info = (uint8_t *)&port->info;
390		cap_mask = mad_get_field(info, 0, IB_PORT_CAPMASK_F);
391		if (cap_mask & CL_NTOH32(IB_PORT_CAP_HAS_EXT_SPEEDS))
392			espeed = mad_get_field(port->info, 0, IB_PORT_LINK_SPEED_EXT_ACTIVE_F);
393		else
394			espeed = 0;
395
396		if (phystate == IB_PORT_PHYS_STATE_LINKUP &&
397		    ispeed == IB_LINK_SPEED_ACTIVE_10 &&
398		    espeed == IB_LINK_SPEED_EXT_ACTIVE_NONE) {	/* LinkUp/QDR */
399			query_mlnx_ext_port_info(engine, &smp->path,
400						 node, port_num);
401			return 0;
402		}
403	}
404
405	debug_port(&smp->path, port);
406
407	if (port_num && mad_get_field(port->info, 0, IB_PORT_PHYS_STATE_F)
408	    == IB_PORT_PHYS_STATE_LINKUP
409	    && ((node->type == IB_NODE_SWITCH && port_num != local_port) ||
410		(node == f_int->fabric.from_node && port_num == f_int->fabric.from_portnum))) {
411
412		int rc = 0;
413		ib_portid_t path = smp->path;
414
415		if (node->type != IB_NODE_SWITCH &&
416		    node == f_int->fabric.from_node &&
417		    path.drpath.cnt > 1)
418			rc = retract_dpath(engine, &path);
419		else {
420			/* we can't proceed through an HCA with DR */
421			if (path.lid == 0 || node->type == IB_NODE_SWITCH)
422				rc = extend_dpath(engine, &path, port_num);
423		}
424
425		if (rc > 0) {
426			struct ni_cbdata * cbdata = malloc(sizeof(*cbdata));
427			cbdata->node = node;
428			cbdata->port_num = port_num;
429			query_node_info(engine, &path, cbdata);
430		}
431	}
432
433	return 0;
434}
435
436static int recv_port0_info(smp_engine_t * engine, ibnd_smp_t * smp,
437			   uint8_t * mad, void *cb_data)
438{
439	ibnd_node_t *node = cb_data;
440	int i, status;
441
442	status = recv_port_info(engine, smp, mad, cb_data);
443	/* Query PortInfo on switch external/physical ports */
444	for (i = 1; i <= node->numports; i++)
445		query_port_info(engine, &smp->path, node, i);
446
447	return status;
448}
449
450static int query_port_info(smp_engine_t * engine, ib_portid_t * portid,
451			   ibnd_node_t * node, int portnum)
452{
453	IBND_DEBUG("Query Port Info; %s (0x%" PRIx64 "):%d\n",
454		   portid2str(portid), node->guid, portnum);
455	return issue_smp(engine, portid, IB_ATTR_PORT_INFO, portnum,
456			 portnum ? recv_port_info : recv_port0_info, node);
457}
458
459static ibnd_node_t *create_node(smp_engine_t * engine, ib_portid_t * path,
460				uint8_t * node_info)
461{
462	f_internal_t *f_int = ((ibnd_scan_t *) engine->user_data)->f_int;
463	ibnd_node_t *rc = calloc(1, sizeof(*rc));
464	if (!rc) {
465		IBND_ERROR("OOM: node creation failed\n");
466		return NULL;
467	}
468
469	/* decode just a couple of fields for quicker reference. */
470	mad_decode_field(node_info, IB_NODE_GUID_F, &rc->guid);
471	mad_decode_field(node_info, IB_NODE_TYPE_F, &rc->type);
472	mad_decode_field(node_info, IB_NODE_NPORTS_F, &rc->numports);
473
474	rc->ports = calloc(rc->numports + 1, sizeof(*rc->ports));
475	if (!rc->ports) {
476		free(rc);
477		IBND_ERROR("OOM: Failed to allocate the ports array\n");
478		return NULL;
479	}
480
481	rc->path_portid = *path;
482	memcpy(rc->info, node_info, sizeof(rc->info));
483
484	int rc1 = add_to_nodeguid_hash(rc, f_int->fabric.nodestbl);
485	if (rc1)
486		IBND_ERROR("Error Occurred when trying"
487			   " to insert new node guid 0x%016" PRIx64 " to DB\n",
488			   rc->guid);
489
490	/* add this to the all nodes list */
491	rc->next = f_int->fabric.nodes;
492	f_int->fabric.nodes = rc;
493
494	add_to_type_list(rc, f_int);
495
496	return rc;
497}
498
499static void link_ports(ibnd_node_t * node, ibnd_port_t * port,
500		       ibnd_node_t * remotenode, ibnd_port_t * remoteport)
501{
502	IBND_DEBUG("linking: 0x%" PRIx64 " %p->%p:%u and 0x%" PRIx64
503		   " %p->%p:%u\n", node->guid, node, port, port->portnum,
504		   remotenode->guid, remotenode, remoteport,
505		   remoteport->portnum);
506	if (port->remoteport)
507		port->remoteport->remoteport = NULL;
508	if (remoteport->remoteport)
509		remoteport->remoteport->remoteport = NULL;
510	port->remoteport = remoteport;
511	remoteport->remoteport = port;
512}
513
514static void dump_endnode(ib_portid_t * path, char *prompt,
515			 ibnd_node_t * node, ibnd_port_t * port)
516{
517	char type[64];
518	mad_dump_node_type(type, sizeof(type), &node->type, sizeof(int));
519	printf("%s -> %s %s {%016" PRIx64 "} portnum %d lid %d-%d \"%s\"\n",
520	       portid2str(path), prompt, type, node->guid,
521	       node->type == IB_NODE_SWITCH ? 0 : port->portnum,
522	       port->base_lid, port->base_lid + (1 << port->lmc) - 1,
523	       node->nodedesc);
524}
525
526static int recv_node_info(smp_engine_t * engine, ibnd_smp_t * smp,
527			  uint8_t * mad, void *cb_data)
528{
529	ibnd_scan_t *scan = engine->user_data;
530	f_internal_t *f_int = scan->f_int;
531	uint8_t *node_info = mad + IB_SMP_DATA_OFFS;
532	struct ni_cbdata *ni_cbdata = (struct ni_cbdata *)cb_data;
533	ibnd_node_t *rem_node = NULL;
534	int rem_port_num = 0;
535	ibnd_node_t *node;
536	int node_is_new = 0;
537	uint64_t node_guid = mad_get_field64(node_info, 0, IB_NODE_GUID_F);
538	uint64_t port_guid = mad_get_field64(node_info, 0, IB_NODE_PORT_GUID_F);
539	int port_num = mad_get_field(node_info, 0, IB_NODE_LOCAL_PORT_F);
540	ibnd_port_t *port = NULL;
541
542	if (ni_cbdata) {
543		rem_node = ni_cbdata->node;
544		rem_port_num = ni_cbdata->port_num;
545		free(ni_cbdata);
546	}
547
548	node = ibnd_find_node_guid(&f_int->fabric, node_guid);
549	if (!node) {
550		node = create_node(engine, &smp->path, node_info);
551		if (!node)
552			return -1;
553		node_is_new = 1;
554	}
555	IBND_DEBUG("Found %s node GUID 0x%" PRIx64 " (%s)\n",
556		   node_is_new ? "new" : "old", node->guid,
557		   portid2str(&smp->path));
558
559	port = node->ports[port_num];
560	if (!port) {
561		/* If we have not see this port before create a shell for it */
562		port = node->ports[port_num] = calloc(1, sizeof(*port));
563		if (!port)
564			return -1;
565		port->node = node;
566		port->portnum = port_num;
567	}
568	port->guid = port_guid;
569
570	if (scan->cfg->show_progress)
571		dump_endnode(&smp->path, node_is_new ? "new" : "known",
572			     node, port);
573
574	if (rem_node == NULL) {	/* this is the start node */
575		f_int->fabric.from_node = node;
576		f_int->fabric.from_portnum = port_num;
577	} else {
578		/* link ports... */
579		if (!rem_node->ports[rem_port_num]) {
580			IBND_ERROR("Internal Error; "
581				   "Node(%p) 0x%" PRIx64
582				   " Port %d no port created!?!?!?\n\n",
583				   rem_node, rem_node->guid, rem_port_num);
584			return -1;
585		}
586
587		link_ports(node, port, rem_node, rem_node->ports[rem_port_num]);
588	}
589
590	if (node_is_new) {
591		query_node_desc(engine, &smp->path, node);
592
593		if (node->type == IB_NODE_SWITCH) {
594			query_switch_info(engine, &smp->path, node);
595			/* Query PortInfo on Switch Port 0 first */
596			query_port_info(engine, &smp->path, node, 0);
597		}
598	}
599
600	if (node->type != IB_NODE_SWITCH)
601		query_port_info(engine, &smp->path, node, port_num);
602
603	return 0;
604}
605
606static int query_node_info(smp_engine_t * engine, ib_portid_t * portid,
607			   struct ni_cbdata * cbdata)
608{
609	IBND_DEBUG("Query Node Info; %s\n", portid2str(portid));
610	return issue_smp(engine, portid, IB_ATTR_NODE_INFO, 0,
611			 recv_node_info, (void *)cbdata);
612}
613
614ibnd_node_t *ibnd_find_node_guid(ibnd_fabric_t * fabric, uint64_t guid)
615{
616	int hash = HASHGUID(guid) % HTSZ;
617	ibnd_node_t *node;
618
619	if (!fabric) {
620		IBND_DEBUG("fabric parameter NULL\n");
621		return NULL;
622	}
623
624	for (node = fabric->nodestbl[hash]; node; node = node->htnext)
625		if (node->guid == guid)
626			return node;
627
628	return NULL;
629}
630
631ibnd_node_t *ibnd_find_node_dr(ibnd_fabric_t * fabric, char *dr_str)
632{
633	ibnd_port_t *rc = ibnd_find_port_dr(fabric, dr_str);
634	return rc->node;
635}
636
637int add_to_nodeguid_hash(ibnd_node_t * node, ibnd_node_t * hash[])
638{
639	int rc = 0;
640	ibnd_node_t *tblnode;
641	int hash_idx = HASHGUID(node->guid) % HTSZ;
642
643	for (tblnode = hash[hash_idx]; tblnode; tblnode = tblnode->htnext) {
644		if (tblnode == node) {
645			IBND_ERROR("Duplicate Node: Node with guid 0x%016"
646				   PRIx64 " already exists in nodes DB\n",
647				   node->guid);
648			return 1;
649		}
650	}
651	node->htnext = hash[hash_idx];
652	hash[hash_idx] = node;
653	return rc;
654}
655
656int add_to_portguid_hash(ibnd_port_t * port, ibnd_port_t * hash[])
657{
658	int rc = 0;
659	ibnd_port_t *tblport;
660	int hash_idx = HASHGUID(port->guid) % HTSZ;
661
662	for (tblport = hash[hash_idx]; tblport; tblport = tblport->htnext) {
663		if (tblport == port) {
664			IBND_ERROR("Duplicate Port: Port with guid 0x%016"
665				   PRIx64 " already exists in ports DB\n",
666				   port->guid);
667			return 1;
668		}
669	}
670	port->htnext = hash[hash_idx];
671	hash[hash_idx] = port;
672	return rc;
673}
674
675void create_lid2guid(f_internal_t *f_int)
676{
677	f_int->lid2guid = g_hash_table_new_full(g_direct_hash, g_direct_equal,
678				NULL, NULL);
679}
680
681void destroy_lid2guid(f_internal_t *f_int)
682{
683	if (f_int->lid2guid) {
684		g_hash_table_destroy(f_int->lid2guid);
685	}
686}
687
688void add_to_portlid_hash(ibnd_port_t * port, GHashTable *htable)
689{
690	uint16_t base_lid = port->base_lid;
691	uint16_t lid_mask = ((1 << port->lmc) -1);
692	uint16_t lid = 0;
693	/* 0 < valid lid <= 0xbfff */
694	if (base_lid > 0 && base_lid <= 0xbfff) {
695		/* We add the port for all lids
696		 * so it is easier to find any "random" lid specified */
697		for (lid = base_lid; lid <= (base_lid + lid_mask); lid++) {
698			g_hash_table_insert(htable, GINT_TO_POINTER(lid), port);
699		}
700	}
701}
702
703void add_to_type_list(ibnd_node_t * node, f_internal_t * f_int)
704{
705	ibnd_fabric_t *fabric = &f_int->fabric;
706	switch (node->type) {
707	case IB_NODE_CA:
708		node->type_next = fabric->ch_adapters;
709		fabric->ch_adapters = node;
710		break;
711	case IB_NODE_SWITCH:
712		node->type_next = fabric->switches;
713		fabric->switches = node;
714		break;
715	case IB_NODE_ROUTER:
716		node->type_next = fabric->routers;
717		fabric->routers = node;
718		break;
719	}
720}
721
722static int set_config(struct ibnd_config *config, struct ibnd_config *cfg)
723{
724	if (!config)
725		return (-EINVAL);
726
727	if (cfg)
728		memcpy(config, cfg, sizeof(*config));
729
730	if (!config->max_smps)
731		config->max_smps = DEFAULT_MAX_SMP_ON_WIRE;
732	if (!config->timeout_ms)
733		config->timeout_ms = DEFAULT_TIMEOUT;
734	if (!config->retries)
735		config->retries = DEFAULT_RETRIES;
736
737	return (0);
738}
739
740f_internal_t *allocate_fabric_internal(void)
741{
742	f_internal_t *f = calloc(1, sizeof(*f));
743	if (f)
744		create_lid2guid(f);
745
746	return (f);
747}
748
749ibnd_fabric_t *ibnd_discover_fabric(char * ca_name, int ca_port,
750				    ib_portid_t * from,
751				    struct ibnd_config *cfg)
752{
753	struct ibnd_config config = { 0 };
754	f_internal_t *f_int = NULL;
755	ib_portid_t my_portid = { 0 };
756	smp_engine_t engine;
757	ibnd_scan_t scan;
758	struct ibmad_port *ibmad_port;
759	int nc = 2;
760	int mc[2] = { IB_SMI_CLASS, IB_SMI_DIRECT_CLASS };
761
762	/* If not specified start from "my" port */
763	if (!from)
764		from = &my_portid;
765
766	if (set_config(&config, cfg)) {
767		IBND_ERROR("Invalid ibnd_config\n");
768		return NULL;
769	}
770
771	f_int = allocate_fabric_internal();
772	if (!f_int) {
773		IBND_ERROR("OOM: failed to calloc ibnd_fabric_t\n");
774		return NULL;
775	}
776
777	memset(&scan.selfportid, 0, sizeof(scan.selfportid));
778	scan.f_int = f_int;
779	scan.cfg = &config;
780	scan.initial_hops = from->drpath.cnt;
781
782	ibmad_port = mad_rpc_open_port(ca_name, ca_port, mc, nc);
783	if (!ibmad_port) {
784		IBND_ERROR("can't open MAD port (%s:%d)\n", ca_name, ca_port);
785		return (NULL);
786	}
787	mad_rpc_set_timeout(ibmad_port, cfg->timeout_ms);
788	mad_rpc_set_retries(ibmad_port, cfg->retries);
789	smp_mkey_set(ibmad_port, cfg->mkey);
790
791	if (ib_resolve_self_via(&scan.selfportid,
792				NULL, NULL, ibmad_port) < 0) {
793		IBND_ERROR("Failed to resolve self\n");
794		mad_rpc_close_port(ibmad_port);
795		return NULL;
796	}
797	mad_rpc_close_port(ibmad_port);
798
799	if (smp_engine_init(&engine, ca_name, ca_port, &scan, &config)) {
800		free(f_int);
801		return (NULL);
802	}
803
804	IBND_DEBUG("from %s\n", portid2str(from));
805
806	if (!query_node_info(&engine, from, NULL))
807		if (process_mads(&engine) != 0)
808			goto error;
809
810	f_int->fabric.total_mads_used = engine.total_smps;
811	f_int->fabric.maxhops_discovered += scan.initial_hops;
812
813	if (group_nodes(&f_int->fabric))
814		goto error;
815
816	smp_engine_destroy(&engine);
817	return (ibnd_fabric_t *)f_int;
818error:
819	smp_engine_destroy(&engine);
820	ibnd_destroy_fabric(&f_int->fabric);
821	return NULL;
822}
823
824void destroy_node(ibnd_node_t * node)
825{
826	int p = 0;
827
828	if (node->ports) {
829		for (p = 0; p <= node->numports; p++)
830			free(node->ports[p]);
831		free(node->ports);
832	}
833	free(node);
834}
835
836void ibnd_destroy_fabric(ibnd_fabric_t * fabric)
837{
838	ibnd_node_t *node = NULL;
839	ibnd_node_t *next = NULL;
840	ibnd_chassis_t *ch, *ch_next;
841
842	if (!fabric)
843		return;
844
845	ch = fabric->chassis;
846	while (ch) {
847		ch_next = ch->next;
848		free(ch);
849		ch = ch_next;
850	}
851	node = fabric->nodes;
852	while (node) {
853		next = node->next;
854		destroy_node(node);
855		node = next;
856	}
857	destroy_lid2guid((f_internal_t *)fabric);
858	free(fabric);
859}
860
861void ibnd_iter_nodes(ibnd_fabric_t * fabric, ibnd_iter_node_func_t func,
862		     void *user_data)
863{
864	ibnd_node_t *cur = NULL;
865
866	if (!fabric) {
867		IBND_DEBUG("fabric parameter NULL\n");
868		return;
869	}
870
871	if (!func) {
872		IBND_DEBUG("func parameter NULL\n");
873		return;
874	}
875
876	for (cur = fabric->nodes; cur; cur = cur->next)
877		func(cur, user_data);
878}
879
880void ibnd_iter_nodes_type(ibnd_fabric_t * fabric, ibnd_iter_node_func_t func,
881			  int node_type, void *user_data)
882{
883	ibnd_node_t *list = NULL;
884	ibnd_node_t *cur = NULL;
885
886	if (!fabric) {
887		IBND_DEBUG("fabric parameter NULL\n");
888		return;
889	}
890
891	if (!func) {
892		IBND_DEBUG("func parameter NULL\n");
893		return;
894	}
895
896	switch (node_type) {
897	case IB_NODE_SWITCH:
898		list = fabric->switches;
899		break;
900	case IB_NODE_CA:
901		list = fabric->ch_adapters;
902		break;
903	case IB_NODE_ROUTER:
904		list = fabric->routers;
905		break;
906	default:
907		IBND_DEBUG("Invalid node_type specified %d\n", node_type);
908		break;
909	}
910
911	for (cur = list; cur; cur = cur->type_next)
912		func(cur, user_data);
913}
914
915ibnd_port_t *ibnd_find_port_lid(ibnd_fabric_t * fabric,
916				uint16_t lid)
917{
918	ibnd_port_t *port;
919	f_internal_t *f = (f_internal_t *)fabric;
920
921	port = (ibnd_port_t *)g_hash_table_lookup(f->lid2guid,
922					GINT_TO_POINTER(lid));
923
924	return port;
925}
926
927ibnd_port_t *ibnd_find_port_guid(ibnd_fabric_t * fabric, uint64_t guid)
928{
929	int hash = HASHGUID(guid) % HTSZ;
930	ibnd_port_t *port;
931
932	if (!fabric) {
933		IBND_DEBUG("fabric parameter NULL\n");
934		return NULL;
935	}
936
937	for (port = fabric->portstbl[hash]; port; port = port->htnext)
938		if (port->guid == guid)
939			return port;
940
941	return NULL;
942}
943
944ibnd_port_t *ibnd_find_port_dr(ibnd_fabric_t * fabric, char *dr_str)
945{
946	int i = 0;
947	ibnd_node_t *cur_node;
948	ibnd_port_t *rc = NULL;
949	ib_dr_path_t path;
950
951	if (!fabric) {
952		IBND_DEBUG("fabric parameter NULL\n");
953		return NULL;
954	}
955
956	if (!dr_str) {
957		IBND_DEBUG("dr_str parameter NULL\n");
958		return NULL;
959	}
960
961	cur_node = fabric->from_node;
962
963	if (str2drpath(&path, dr_str, 0, 0) == -1)
964		return NULL;
965
966	for (i = 0; i <= path.cnt; i++) {
967		ibnd_port_t *remote_port = NULL;
968		if (path.p[i] == 0)
969			continue;
970		if (!cur_node->ports)
971			return NULL;
972
973		remote_port = cur_node->ports[path.p[i]]->remoteport;
974		if (!remote_port)
975			return NULL;
976
977		rc = remote_port;
978		cur_node = remote_port->node;
979	}
980
981	return rc;
982}
983
984void ibnd_iter_ports(ibnd_fabric_t * fabric, ibnd_iter_port_func_t func,
985			void *user_data)
986{
987	int i = 0;
988	ibnd_port_t *cur = NULL;
989
990	if (!fabric) {
991		IBND_DEBUG("fabric parameter NULL\n");
992		return;
993	}
994
995	if (!func) {
996		IBND_DEBUG("func parameter NULL\n");
997		return;
998	}
999
1000	for (i = 0; i<HTSZ; i++)
1001		for (cur = fabric->portstbl[i]; cur; cur = cur->htnext)
1002			func(cur, user_data);
1003}
1004