1/*
2 * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 *
6 * This software is available to you under a choice of one of two
7 * licenses.  You may choose to be licensed under the terms of the GNU
8 * General Public License (GPL) Version 2, available from the file
9 * COPYING in the main directory of this source tree, or the
10 * OpenIB.org BSD license below:
11 *
12 *     Redistribution and use in source and binary forms, with or
13 *     without modification, are permitted provided that the following
14 *     conditions are met:
15 *
16 *      - Redistributions of source code must retain the above
17 *        copyright notice, this list of conditions and the following
18 *        disclaimer.
19 *
20 *      - Redistributions in binary form must reproduce the above
21 *        copyright notice, this list of conditions and the following
22 *        disclaimer in the documentation and/or other materials
23 *        provided with the distribution.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 * SOFTWARE.
33 *
34 */
35
36/*
37 * Abstract:
38 *    Implementation of osm_physp_t.
39 * This object represents an Infiniband Port.
40 * This object is part of the opensm family of objects.
41 */
42
43#if HAVE_CONFIG_H
44#  include <config.h>
45#endif				/* HAVE_CONFIG_H */
46
47#include <stdlib.h>
48#include <string.h>
49#include <complib/cl_debug.h>
50#include <iba/ib_types.h>
51#include <opensm/osm_port.h>
52#include <opensm/osm_node.h>
53#include <opensm/osm_madw.h>
54#include <opensm/osm_mcm_info.h>
55#include <opensm/osm_switch.h>
56
57/**********************************************************************
58 **********************************************************************/
59void osm_physp_construct(IN osm_physp_t * const p_physp)
60{
61	memset(p_physp, 0, sizeof(*p_physp));
62	osm_dr_path_construct(&p_physp->dr_path);
63	cl_ptr_vector_construct(&p_physp->slvl_by_port);
64	osm_pkey_tbl_construct(&p_physp->pkeys);
65}
66
67/**********************************************************************
68 **********************************************************************/
69void osm_physp_destroy(IN osm_physp_t * const p_physp)
70{
71	size_t num_slvl, i;
72
73	/* the physp might be uninitialized */
74	if (p_physp->port_guid) {
75		/* free the SL2VL Tables */
76		num_slvl = cl_ptr_vector_get_size(&p_physp->slvl_by_port);
77		for (i = 0; i < num_slvl; i++)
78			free(cl_ptr_vector_get(&p_physp->slvl_by_port, i));
79		cl_ptr_vector_destroy(&p_physp->slvl_by_port);
80
81		/* free the P_Key Tables */
82		osm_pkey_tbl_destroy(&p_physp->pkeys);
83
84		memset(p_physp, 0, sizeof(*p_physp));
85		osm_dr_path_construct(&p_physp->dr_path);	/* clear dr_path */
86	}
87}
88
89/**********************************************************************
90 **********************************************************************/
91void
92osm_physp_init(IN osm_physp_t * const p_physp,
93	       IN const ib_net64_t port_guid,
94	       IN const uint8_t port_num,
95	       IN const struct osm_node *const p_node,
96	       IN const osm_bind_handle_t h_bind,
97	       IN const uint8_t hop_count,
98	       IN const uint8_t * const p_initial_path)
99{
100	uint16_t num_slvl, i;
101	ib_slvl_table_t *p_slvl;
102
103	CL_ASSERT(p_node);
104
105	osm_physp_construct(p_physp);
106	p_physp->port_guid = port_guid;
107	p_physp->port_num = port_num;
108	p_physp->healthy = TRUE;
109	p_physp->need_update = 2;
110	p_physp->p_node = (struct osm_node *)p_node;
111
112	osm_dr_path_init(&p_physp->dr_path, h_bind, hop_count, p_initial_path);
113
114	/* allocate enough SL2VL tables */
115	if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH)
116		/* we need node num ports + 1 SL2VL tables */
117		num_slvl = osm_node_get_num_physp(p_node) + 1;
118	else
119		/* An end node - we need only one SL2VL */
120		num_slvl = 1;
121
122	cl_ptr_vector_init(&p_physp->slvl_by_port, num_slvl, 1);
123	for (i = 0; i < num_slvl; i++) {
124		p_slvl = (ib_slvl_table_t *) malloc(sizeof(ib_slvl_table_t));
125		if (!p_slvl)
126			break;
127		memset(p_slvl, 0, sizeof(ib_slvl_table_t));
128		cl_ptr_vector_set(&p_physp->slvl_by_port, i, p_slvl);
129	}
130
131	/* initialize the pkey table */
132	osm_pkey_tbl_init(&p_physp->pkeys);
133}
134
135/**********************************************************************
136 **********************************************************************/
137void osm_port_delete(IN OUT osm_port_t ** const pp_port)
138{
139	/* cleanup all mcm recs attached */
140	osm_port_remove_all_mgrp(*pp_port);
141	free(*pp_port);
142	*pp_port = NULL;
143}
144
145/**********************************************************************
146 **********************************************************************/
147static void
148osm_port_init(IN osm_port_t * const p_port,
149	      IN const ib_node_info_t * p_ni,
150	      IN osm_node_t * const p_parent_node)
151{
152	ib_net64_t port_guid;
153	osm_physp_t *p_physp;
154	uint8_t port_num;
155
156	CL_ASSERT(p_port);
157	CL_ASSERT(p_ni);
158	CL_ASSERT(p_parent_node);
159
160	memset(p_port, 0, sizeof(*p_port));
161	cl_qlist_init(&p_port->mcm_list);
162	p_port->p_node = (struct osm_node *)p_parent_node;
163	port_guid = p_ni->port_guid;
164	p_port->guid = port_guid;
165	port_num = p_ni->node_type == IB_NODE_TYPE_SWITCH ?
166		0 : ib_node_info_get_local_port_num(p_ni);
167
168	/*
169	   Get the pointers to the physical node objects "owned" by this
170	   logical port GUID.
171	   For switches, port '0' is owned; for HCA's and routers,
172	   only the singular part that has this GUID is owned.
173	 */
174	p_physp = osm_node_get_physp_ptr(p_parent_node, port_num);
175	CL_ASSERT(port_guid == osm_physp_get_port_guid(p_physp));
176	p_port->p_physp = p_physp;
177}
178
179/**********************************************************************
180 **********************************************************************/
181osm_port_t *osm_port_new(IN const ib_node_info_t * p_ni,
182			 IN osm_node_t * const p_parent_node)
183{
184	osm_port_t *p_port;
185
186	p_port = malloc(sizeof(*p_port));
187	if (p_port != NULL) {
188		memset(p_port, 0, sizeof(*p_port));
189		osm_port_init(p_port, p_ni, p_parent_node);
190	}
191
192	return (p_port);
193}
194
195/**********************************************************************
196 **********************************************************************/
197void
198osm_port_get_lid_range_ho(IN const osm_port_t * const p_port,
199			  IN uint16_t * const p_min_lid,
200			  IN uint16_t * const p_max_lid)
201{
202	uint8_t lmc;
203
204	*p_min_lid = cl_ntoh16(osm_port_get_base_lid(p_port));
205	lmc = osm_port_get_lmc(p_port);
206	*p_max_lid = (uint16_t) (*p_min_lid + (1 << lmc) - 1);
207}
208
209/**********************************************************************
210 **********************************************************************/
211ib_api_status_t
212osm_get_port_by_base_lid(IN const osm_subn_t * const p_subn,
213			 IN const ib_net16_t lid,
214			 IN OUT const osm_port_t ** const pp_port)
215{
216	ib_api_status_t status;
217	uint16_t base_lid;
218	uint8_t lmc;
219
220	*pp_port = NULL;
221
222	/* Loop on lmc from 0 up through max LMC possible */
223	for (lmc = 0; lmc <= IB_PORT_LMC_MAX; lmc++) {
224		/* Calculate a base LID assuming this is the real LMC */
225		base_lid = cl_ntoh16(lid) & ~((1 << lmc) - 1);
226
227		/* Look for a match */
228		status = cl_ptr_vector_at(&p_subn->port_lid_tbl,
229					  base_lid, (void **)pp_port);
230		if ((status == CL_SUCCESS) && (*pp_port != NULL)) {
231			/* Determine if base LID "tested" is the real base LID */
232			/* This is true if the LMC "tested" is the port's actual LMC */
233			if (lmc == osm_port_get_lmc(*pp_port)) {
234				status = IB_SUCCESS;
235				goto Found;
236			}
237		}
238	}
239	*pp_port = NULL;
240	status = IB_NOT_FOUND;
241
242Found:
243	return status;
244}
245
246/**********************************************************************
247 **********************************************************************/
248ib_api_status_t
249osm_port_add_mgrp(IN osm_port_t * const p_port, IN const ib_net16_t mlid)
250{
251	ib_api_status_t status = IB_SUCCESS;
252	osm_mcm_info_t *p_mcm;
253
254	p_mcm = osm_mcm_info_new(mlid);
255	if (p_mcm)
256		cl_qlist_insert_tail(&p_port->mcm_list,
257				     (cl_list_item_t *) p_mcm);
258	else
259		status = IB_INSUFFICIENT_MEMORY;
260
261	return (status);
262}
263
264/**********************************************************************
265 **********************************************************************/
266static cl_status_t
267__osm_port_mgrp_find_func(IN const cl_list_item_t * const p_list_item,
268			  IN void *context)
269{
270	if (*((ib_net16_t *) context) == ((osm_mcm_info_t *) p_list_item)->mlid)
271		return (CL_SUCCESS);
272	else
273		return (CL_NOT_FOUND);
274}
275
276/**********************************************************************
277 **********************************************************************/
278void
279osm_port_remove_mgrp(IN osm_port_t * const p_port, IN const ib_net16_t mlid)
280{
281	cl_list_item_t *p_mcm;
282
283	p_mcm = cl_qlist_find_from_head(&p_port->mcm_list,
284					__osm_port_mgrp_find_func, &mlid);
285
286	if (p_mcm != cl_qlist_end(&p_port->mcm_list)) {
287		cl_qlist_remove_item(&p_port->mcm_list, p_mcm);
288		osm_mcm_info_delete((osm_mcm_info_t *) p_mcm);
289	}
290}
291
292/**********************************************************************
293 **********************************************************************/
294void osm_port_remove_all_mgrp(IN osm_port_t * const p_port)
295{
296	cl_list_item_t *p_mcm;
297
298	p_mcm = cl_qlist_remove_head(&p_port->mcm_list);
299	while (p_mcm != cl_qlist_end(&p_port->mcm_list)) {
300		osm_mcm_info_delete((osm_mcm_info_t *) p_mcm);
301		p_mcm = cl_qlist_remove_head(&p_port->mcm_list);
302	}
303}
304
305/**********************************************************************
306 **********************************************************************/
307uint8_t
308osm_physp_calc_link_mtu(IN osm_log_t * p_log, IN const osm_physp_t * p_physp)
309{
310	const osm_physp_t *p_remote_physp;
311	uint8_t mtu;
312	uint8_t remote_mtu;
313
314	OSM_LOG_ENTER(p_log);
315
316	p_remote_physp = osm_physp_get_remote(p_physp);
317	if (p_remote_physp) {
318		/* use the available MTU */
319		mtu = ib_port_info_get_mtu_cap(&p_physp->port_info);
320
321		remote_mtu =
322		    ib_port_info_get_mtu_cap(&p_remote_physp->port_info);
323
324		OSM_LOG(p_log, OSM_LOG_DEBUG,
325			"Remote port 0x%016" PRIx64 " port = %u : "
326			"MTU = %u. This Port MTU: %u\n",
327			cl_ntoh64(osm_physp_get_port_guid(p_remote_physp)),
328			osm_physp_get_port_num(p_remote_physp),
329			remote_mtu, mtu);
330
331		if (mtu != remote_mtu) {
332			if (mtu > remote_mtu)
333				mtu = remote_mtu;
334
335			OSM_LOG(p_log, OSM_LOG_VERBOSE,
336				"MTU mismatch between ports."
337				"\n\t\t\t\tPort 0x%016" PRIx64 ", port %u"
338				" and port 0x%016" PRIx64 ", port %u."
339				"\n\t\t\t\tUsing lower MTU of %u\n",
340				cl_ntoh64(osm_physp_get_port_guid(p_physp)),
341				osm_physp_get_port_num(p_physp),
342				cl_ntoh64(osm_physp_get_port_guid(p_remote_physp)),
343				osm_physp_get_port_num(p_remote_physp),mtu);
344		}
345	} else
346		mtu = ib_port_info_get_neighbor_mtu(&p_physp->port_info);
347
348	if (mtu == 0) {
349		OSM_LOG(p_log, OSM_LOG_DEBUG, "ERR 4101: "
350			"Invalid MTU = 0. Forcing correction to 256\n");
351		mtu = 1;
352	}
353
354	OSM_LOG_EXIT(p_log);
355	return (mtu);
356}
357
358/**********************************************************************
359 **********************************************************************/
360uint8_t
361osm_physp_calc_link_op_vls(IN osm_log_t * p_log,
362			   IN const osm_subn_t * p_subn,
363			   IN const osm_physp_t * p_physp)
364{
365	const osm_physp_t *p_remote_physp;
366	uint8_t op_vls;
367	uint8_t remote_op_vls;
368
369	OSM_LOG_ENTER(p_log);
370
371	p_remote_physp = osm_physp_get_remote(p_physp);
372	if (p_remote_physp) {
373		/* use the available VLCap */
374		op_vls = ib_port_info_get_vl_cap(&p_physp->port_info);
375
376		remote_op_vls =
377		    ib_port_info_get_vl_cap(&p_remote_physp->port_info);
378
379		OSM_LOG(p_log, OSM_LOG_DEBUG,
380			"Remote port 0x%016" PRIx64 " port = 0x%X : "
381			"VL_CAP = %u. This port VL_CAP = %u\n",
382			cl_ntoh64(osm_physp_get_port_guid(p_remote_physp)),
383			osm_physp_get_port_num(p_remote_physp),
384			remote_op_vls, op_vls);
385
386		if (op_vls != remote_op_vls) {
387			if (op_vls > remote_op_vls)
388				op_vls = remote_op_vls;
389
390			OSM_LOG(p_log, OSM_LOG_VERBOSE,
391				"OP_VLS mismatch between ports."
392				"\n\t\t\t\tPort 0x%016" PRIx64 ", port 0x%X"
393				" and port 0x%016" PRIx64 ", port 0x%X."
394				"\n\t\t\t\tUsing lower OP_VLS of %u\n",
395				cl_ntoh64(osm_physp_get_port_guid(p_physp)),
396				osm_physp_get_port_num(p_physp),
397				cl_ntoh64(osm_physp_get_port_guid(p_remote_physp)),
398				osm_physp_get_port_num(p_remote_physp), op_vls);
399		}
400	} else
401		op_vls = ib_port_info_get_op_vls(&p_physp->port_info);
402
403	/* support user limitation of max_op_vls */
404	if (op_vls > p_subn->opt.max_op_vls)
405		op_vls = p_subn->opt.max_op_vls;
406
407	if (op_vls == 0) {
408		OSM_LOG(p_log, OSM_LOG_DEBUG, "ERR 4102: "
409			"Invalid OP_VLS = 0. Forcing correction to 1 (VL0)\n");
410		op_vls = 1;
411	}
412
413	OSM_LOG_EXIT(p_log);
414	return (op_vls);
415}
416
417static inline uint64_t __osm_ptr_to_key(void const *p)
418{
419	uint64_t k = 0;
420
421	memcpy(&k, p, sizeof(void *));
422	return k;
423}
424
425static inline void *__osm_key_to_ptr(uint64_t k)
426{
427	void *p = 0;
428
429	memcpy(&p, &k, sizeof(void *));
430	return p;
431}
432
433/**********************************************************************
434 Traverse the fabric from the SM node following the DR path given and
435 add every phys port traversed to the map. Avoid tracking the first and
436 last phys ports (going into the first switch and into the target port).
437 **********************************************************************/
438static cl_status_t
439__osm_physp_get_dr_physp_set(IN osm_log_t * p_log,
440			     IN osm_subn_t const *p_subn,
441			     IN osm_dr_path_t const *p_path,
442			     OUT cl_map_t * p_physp_map)
443{
444	osm_port_t *p_port;
445	osm_physp_t *p_physp;
446	osm_node_t *p_node;
447	uint8_t hop;
448	cl_status_t status = CL_SUCCESS;
449
450	OSM_LOG_ENTER(p_log);
451
452	/* find the OSM node */
453	p_port = osm_get_port_by_guid(p_subn, p_subn->sm_port_guid);
454	if (!p_port) {
455		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4103: "
456			"Failed to find the SM own port by guid\n");
457		status = CL_ERROR;
458		goto Exit;
459	}
460
461	/* get the node of the SM */
462	p_node = p_port->p_node;
463
464	/*
465	   traverse the path adding the nodes to the table
466	   start after the first dummy hop and stop just before the
467	   last one
468	 */
469	for (hop = 1; hop < p_path->hop_count - 1; hop++) {
470		/* go out using the phys port of the path */
471		p_physp = osm_node_get_physp_ptr(p_node, p_path->path[hop]);
472
473		/* make sure we got a valid port and it has a remote port */
474		if (!p_physp) {
475			OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4104: "
476				"DR Traversal stopped on invalid port at hop:%u\n",
477				hop);
478			status = CL_ERROR;
479			goto Exit;
480		}
481
482		/* we track the ports we go out along the path */
483		if (hop > 1)
484			cl_map_insert(p_physp_map, __osm_ptr_to_key(p_physp),
485				      NULL);
486
487		OSM_LOG(p_log, OSM_LOG_DEBUG,
488			"Traversed through node: 0x%016" PRIx64
489			" port:%u\n",
490			cl_ntoh64(p_node->node_info.node_guid),
491			p_path->path[hop]);
492
493		if (!(p_physp = osm_physp_get_remote(p_physp))) {
494			OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4106: "
495				"DR Traversal stopped on missing remote physp at hop:%u\n",
496				hop);
497			status = CL_ERROR;
498			goto Exit;
499		}
500
501		p_node = osm_physp_get_node_ptr(p_physp);
502	}
503
504Exit:
505	OSM_LOG_EXIT(p_log);
506	return status;
507}
508
509/**********************************************************************
510 **********************************************************************/
511static void
512__osm_physp_update_new_dr_path(IN osm_physp_t const *p_dest_physp,
513			       IN cl_map_t * p_visited_map,
514			       IN osm_bind_handle_t * h_bind)
515{
516	cl_list_t tmpPortsList;
517	osm_physp_t *p_physp, *p_src_physp = NULL;
518	uint8_t path_array[IB_SUBNET_PATH_HOPS_MAX];
519	uint8_t i = 0;
520	osm_dr_path_t *p_dr_path;
521
522	cl_list_construct(&tmpPortsList);
523	cl_list_init(&tmpPortsList, 10);
524
525	cl_list_insert_head(&tmpPortsList, p_dest_physp);
526	/* get the output port where we need to come from */
527	p_physp = (osm_physp_t *) cl_map_get(p_visited_map,
528					     __osm_ptr_to_key(p_dest_physp));
529	while (p_physp != NULL) {
530		cl_list_insert_head(&tmpPortsList, p_physp);
531		/* get the input port through where we reached the output port */
532		p_src_physp = p_physp;
533		p_physp = (osm_physp_t *) cl_map_get(p_visited_map,
534						     __osm_ptr_to_key(p_physp));
535		/* if we reached a null p_physp - this means we are at the begining
536		   of the path. Break. */
537		if (p_physp == NULL)
538			break;
539		/* get the output port */
540		p_physp = (osm_physp_t *) cl_map_get(p_visited_map,
541						     __osm_ptr_to_key(p_physp));
542	}
543
544	memset(path_array, 0, sizeof(path_array));
545	p_physp = (osm_physp_t *) cl_list_remove_head(&tmpPortsList);
546	while (p_physp != NULL) {
547		i++;
548		path_array[i] = p_physp->port_num;
549		p_physp = (osm_physp_t *) cl_list_remove_head(&tmpPortsList);
550	}
551	if (p_src_physp) {
552		p_dr_path = osm_physp_get_dr_path_ptr(p_src_physp);
553		osm_dr_path_init(p_dr_path, h_bind, i, path_array);
554	}
555
556	cl_list_destroy(&tmpPortsList);
557}
558
559/**********************************************************************
560 **********************************************************************/
561void
562osm_physp_replace_dr_path_with_alternate_dr_path(IN osm_log_t * p_log,
563						 IN osm_subn_t const *p_subn,
564						 IN osm_physp_t const
565						 *p_dest_physp,
566						 IN osm_bind_handle_t * h_bind)
567{
568	cl_map_t physp_map;
569	cl_map_t visited_map;
570	osm_dr_path_t *p_dr_path;
571	cl_list_t *p_currPortsList;
572	cl_list_t *p_nextPortsList;
573	osm_port_t *p_port;
574	osm_physp_t *p_physp, *p_remote_physp;
575	ib_net64_t port_guid;
576	boolean_t next_list_is_full = TRUE, reached_dest = FALSE;
577	uint8_t num_ports, port_num;
578
579	p_nextPortsList = (cl_list_t *) malloc(sizeof(cl_list_t));
580	if (!p_nextPortsList)
581		return;
582
583	/*
584	   initialize the map of all port participating in current dr path
585	   not including first and last switches
586	 */
587	cl_map_construct(&physp_map);
588	cl_map_init(&physp_map, 4);
589	cl_map_construct(&visited_map);
590	cl_map_init(&visited_map, 4);
591	p_dr_path = osm_physp_get_dr_path_ptr(p_dest_physp);
592	__osm_physp_get_dr_physp_set(p_log, p_subn, p_dr_path, &physp_map);
593
594	/*
595	   BFS from OSM port until we find the target physp but avoid
596	   going through mapped ports
597	 */
598	cl_list_construct(p_nextPortsList);
599	cl_list_init(p_nextPortsList, 10);
600
601	port_guid = p_subn->sm_port_guid;
602
603	CL_ASSERT(port_guid);
604
605	p_port = osm_get_port_by_guid(p_subn, port_guid);
606	if (!p_port) {
607		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4105: No SM port object\n");
608		goto Exit;
609	}
610
611	/*
612	   HACK: We are assuming SM is running on HCA, so when getting the default
613	   port we'll get the port connected to the rest of the subnet. If SM is
614	   running on SWITCH - we should try to get a dr path from all switch ports.
615	 */
616	p_physp = p_port->p_physp;
617
618	CL_ASSERT(p_physp);
619
620	cl_list_insert_tail(p_nextPortsList, p_physp);
621
622	while (next_list_is_full == TRUE) {
623		next_list_is_full = FALSE;
624		p_currPortsList = p_nextPortsList;
625		p_nextPortsList = (cl_list_t *) malloc(sizeof(cl_list_t));
626		if (!p_nextPortsList) {
627			p_nextPortsList = p_currPortsList;
628			goto Exit;
629		}
630		cl_list_construct(p_nextPortsList);
631		cl_list_init(p_nextPortsList, 10);
632		p_physp = (osm_physp_t *) cl_list_remove_head(p_currPortsList);
633		while (p_physp != NULL) {
634			/* If we are in a switch - need to go out through all
635			   the other physical ports of the switch */
636			num_ports = osm_node_get_num_physp(p_physp->p_node);
637
638			for (port_num = 1; port_num < num_ports; port_num++) {
639				if (osm_node_get_type(p_physp->p_node) ==
640				    IB_NODE_TYPE_SWITCH)
641					p_remote_physp =
642					    osm_node_get_physp_ptr(p_physp->
643								   p_node,
644								   port_num);
645				else
646					/* this is HCA or router - the remote port is just the port connected
647					   on the other side */
648					p_remote_physp =
649					    p_physp->p_remote_physp;
650
651				/*
652				   make sure that all of the following occurred:
653				   1. The port isn't NULL
654				   2. This is not the port we came from
655				   3. The port is not in the physp_map
656				   4. This port haven't been visited before
657				 */
658				if (p_remote_physp &&
659				    p_remote_physp != p_physp &&
660				    cl_map_get(&physp_map,
661					       __osm_ptr_to_key(p_remote_physp))
662				    == NULL
663				    && cl_map_get(&visited_map,
664						  __osm_ptr_to_key
665						  (p_remote_physp)) == NULL) {
666					/* Insert the port into the visited_map, and save its source port */
667					cl_map_insert(&visited_map,
668						      __osm_ptr_to_key
669						      (p_remote_physp),
670						      p_physp);
671
672					/* Is this the p_dest_physp? */
673					if (p_remote_physp == p_dest_physp) {
674						/* update the new dr path */
675						__osm_physp_update_new_dr_path
676						    (p_dest_physp, &visited_map,
677						     h_bind);
678						reached_dest = TRUE;
679						break;
680					}
681
682					/* add the p_remote_physp to the nextPortsList */
683					cl_list_insert_tail(p_nextPortsList,
684							    p_remote_physp);
685					next_list_is_full = TRUE;
686				}
687			}
688
689			p_physp = (osm_physp_t *)
690			    cl_list_remove_head(p_currPortsList);
691			if (reached_dest == TRUE) {
692				/* free the rest of the currPortsList */
693				while (p_physp != NULL)
694					p_physp = (osm_physp_t *)
695					    cl_list_remove_head
696					    (p_currPortsList);
697				/* free the nextPortsList, if items were added to it */
698				p_physp = (osm_physp_t *)
699				    cl_list_remove_head(p_nextPortsList);
700				while (p_physp != NULL)
701					p_physp = (osm_physp_t *)
702					    cl_list_remove_head
703					    (p_nextPortsList);
704				next_list_is_full = FALSE;
705			}
706		}
707		cl_list_destroy(p_currPortsList);
708		free(p_currPortsList);
709	}
710
711	/* cleanup */
712Exit:
713	cl_list_destroy(p_nextPortsList);
714	free(p_nextPortsList);
715	cl_map_destroy(&physp_map);
716	cl_map_destroy(&visited_map);
717}
718
719/**********************************************************************
720 **********************************************************************/
721boolean_t osm_link_is_healthy(IN const osm_physp_t * const p_physp)
722{
723	osm_physp_t *p_remote_physp;
724
725	CL_ASSERT(p_physp);
726	p_remote_physp = p_physp->p_remote_physp;
727	if (p_remote_physp != NULL)
728		return ((p_physp->healthy) & (p_remote_physp->healthy));
729	/* the other side is not known - consider the link as healthy */
730	return (TRUE);
731}
732
733/**********************************************************************
734 **********************************************************************/
735void
736osm_physp_set_pkey_tbl(IN osm_log_t * p_log,
737		       IN const osm_subn_t * p_subn,
738		       IN osm_physp_t * const p_physp,
739		       IN ib_pkey_table_t * p_pkey_tbl, IN uint16_t block_num)
740{
741	uint16_t max_blocks;
742
743	CL_ASSERT(p_pkey_tbl);
744	/*
745	   (14.2.5.7) - the block number valid values are 0-2047, and are
746	   further limited by the size of the P_Key table specified by
747	   the PartitionCap on the node.
748	 */
749	if (!p_physp->p_node->sw || p_physp->port_num == 0)
750		/*
751		   The maximum blocks is defined in the node info: partition cap
752		   for CA, router, and switch management ports.
753		 */
754		max_blocks =
755		    (cl_ntoh16(p_physp->p_node->node_info.partition_cap) +
756		     IB_NUM_PKEY_ELEMENTS_IN_BLOCK - 1)
757		    / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
758	else
759		/*
760		   This is a switch, and not a management port. The maximum
761		   blocks is defined in the switch info: partition enforcement
762		   cap.
763		 */
764		max_blocks =
765		    (cl_ntoh16(p_physp->p_node->sw->switch_info.enforce_cap) +
766		     IB_NUM_PKEY_ELEMENTS_IN_BLOCK -
767		     1) / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
768
769	if (block_num >= max_blocks) {
770		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4108: "
771			"Got illegal set for block number:%u "
772			"For GUID: %" PRIx64 " port number:%u\n",
773			block_num,
774			cl_ntoh64(p_physp->p_node->node_info.node_guid),
775			p_physp->port_num);
776		return;
777	}
778
779	osm_pkey_tbl_set(&p_physp->pkeys, block_num, p_pkey_tbl);
780}
781