1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4219820Sjeff * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5219820Sjeff *
6219820Sjeff * This software is available to you under a choice of one of two
7219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
8219820Sjeff * General Public License (GPL) Version 2, available from the file
9219820Sjeff * COPYING in the main directory of this source tree, or the
10219820Sjeff * OpenIB.org BSD license below:
11219820Sjeff *
12219820Sjeff *     Redistribution and use in source and binary forms, with or
13219820Sjeff *     without modification, are permitted provided that the following
14219820Sjeff *     conditions are met:
15219820Sjeff *
16219820Sjeff *      - Redistributions of source code must retain the above
17219820Sjeff *        copyright notice, this list of conditions and the following
18219820Sjeff *        disclaimer.
19219820Sjeff *
20219820Sjeff *      - Redistributions in binary form must reproduce the above
21219820Sjeff *        copyright notice, this list of conditions and the following
22219820Sjeff *        disclaimer in the documentation and/or other materials
23219820Sjeff *        provided with the distribution.
24219820Sjeff *
25219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32219820Sjeff * SOFTWARE.
33219820Sjeff *
34219820Sjeff */
35219820Sjeff
36219820Sjeff/*
37219820Sjeff * Abstract:
38219820Sjeff *    Implementation of osm_physp_t.
39219820Sjeff * This object represents an Infiniband Port.
40219820Sjeff * This object is part of the opensm family of objects.
41219820Sjeff */
42219820Sjeff
43219820Sjeff#if HAVE_CONFIG_H
44219820Sjeff#  include <config.h>
45219820Sjeff#endif				/* HAVE_CONFIG_H */
46219820Sjeff
47219820Sjeff#include <stdlib.h>
48219820Sjeff#include <string.h>
49219820Sjeff#include <complib/cl_debug.h>
50219820Sjeff#include <iba/ib_types.h>
51219820Sjeff#include <opensm/osm_port.h>
52219820Sjeff#include <opensm/osm_node.h>
53219820Sjeff#include <opensm/osm_madw.h>
54219820Sjeff#include <opensm/osm_mcm_info.h>
55219820Sjeff#include <opensm/osm_switch.h>
56219820Sjeff
57219820Sjeff/**********************************************************************
58219820Sjeff **********************************************************************/
59219820Sjeffvoid osm_physp_construct(IN osm_physp_t * const p_physp)
60219820Sjeff{
61219820Sjeff	memset(p_physp, 0, sizeof(*p_physp));
62219820Sjeff	osm_dr_path_construct(&p_physp->dr_path);
63219820Sjeff	cl_ptr_vector_construct(&p_physp->slvl_by_port);
64219820Sjeff	osm_pkey_tbl_construct(&p_physp->pkeys);
65219820Sjeff}
66219820Sjeff
67219820Sjeff/**********************************************************************
68219820Sjeff **********************************************************************/
69219820Sjeffvoid osm_physp_destroy(IN osm_physp_t * const p_physp)
70219820Sjeff{
71219820Sjeff	size_t num_slvl, i;
72219820Sjeff
73219820Sjeff	/* the physp might be uninitialized */
74219820Sjeff	if (p_physp->port_guid) {
75219820Sjeff		/* free the SL2VL Tables */
76219820Sjeff		num_slvl = cl_ptr_vector_get_size(&p_physp->slvl_by_port);
77219820Sjeff		for (i = 0; i < num_slvl; i++)
78219820Sjeff			free(cl_ptr_vector_get(&p_physp->slvl_by_port, i));
79219820Sjeff		cl_ptr_vector_destroy(&p_physp->slvl_by_port);
80219820Sjeff
81219820Sjeff		/* free the P_Key Tables */
82219820Sjeff		osm_pkey_tbl_destroy(&p_physp->pkeys);
83219820Sjeff
84219820Sjeff		memset(p_physp, 0, sizeof(*p_physp));
85219820Sjeff		osm_dr_path_construct(&p_physp->dr_path);	/* clear dr_path */
86219820Sjeff	}
87219820Sjeff}
88219820Sjeff
89219820Sjeff/**********************************************************************
90219820Sjeff **********************************************************************/
91219820Sjeffvoid
92219820Sjeffosm_physp_init(IN osm_physp_t * const p_physp,
93219820Sjeff	       IN const ib_net64_t port_guid,
94219820Sjeff	       IN const uint8_t port_num,
95219820Sjeff	       IN const struct osm_node *const p_node,
96219820Sjeff	       IN const osm_bind_handle_t h_bind,
97219820Sjeff	       IN const uint8_t hop_count,
98219820Sjeff	       IN const uint8_t * const p_initial_path)
99219820Sjeff{
100219820Sjeff	uint16_t num_slvl, i;
101219820Sjeff	ib_slvl_table_t *p_slvl;
102219820Sjeff
103219820Sjeff	CL_ASSERT(p_node);
104219820Sjeff
105219820Sjeff	osm_physp_construct(p_physp);
106219820Sjeff	p_physp->port_guid = port_guid;
107219820Sjeff	p_physp->port_num = port_num;
108219820Sjeff	p_physp->healthy = TRUE;
109219820Sjeff	p_physp->need_update = 2;
110219820Sjeff	p_physp->p_node = (struct osm_node *)p_node;
111219820Sjeff
112219820Sjeff	osm_dr_path_init(&p_physp->dr_path, h_bind, hop_count, p_initial_path);
113219820Sjeff
114219820Sjeff	/* allocate enough SL2VL tables */
115219820Sjeff	if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH)
116219820Sjeff		/* we need node num ports + 1 SL2VL tables */
117219820Sjeff		num_slvl = osm_node_get_num_physp(p_node) + 1;
118219820Sjeff	else
119219820Sjeff		/* An end node - we need only one SL2VL */
120219820Sjeff		num_slvl = 1;
121219820Sjeff
122219820Sjeff	cl_ptr_vector_init(&p_physp->slvl_by_port, num_slvl, 1);
123219820Sjeff	for (i = 0; i < num_slvl; i++) {
124219820Sjeff		p_slvl = (ib_slvl_table_t *) malloc(sizeof(ib_slvl_table_t));
125219820Sjeff		if (!p_slvl)
126219820Sjeff			break;
127219820Sjeff		memset(p_slvl, 0, sizeof(ib_slvl_table_t));
128219820Sjeff		cl_ptr_vector_set(&p_physp->slvl_by_port, i, p_slvl);
129219820Sjeff	}
130219820Sjeff
131219820Sjeff	/* initialize the pkey table */
132219820Sjeff	osm_pkey_tbl_init(&p_physp->pkeys);
133219820Sjeff}
134219820Sjeff
135219820Sjeff/**********************************************************************
136219820Sjeff **********************************************************************/
137219820Sjeffvoid osm_port_delete(IN OUT osm_port_t ** const pp_port)
138219820Sjeff{
139219820Sjeff	/* cleanup all mcm recs attached */
140219820Sjeff	osm_port_remove_all_mgrp(*pp_port);
141219820Sjeff	free(*pp_port);
142219820Sjeff	*pp_port = NULL;
143219820Sjeff}
144219820Sjeff
145219820Sjeff/**********************************************************************
146219820Sjeff **********************************************************************/
147219820Sjeffstatic void
148219820Sjeffosm_port_init(IN osm_port_t * const p_port,
149219820Sjeff	      IN const ib_node_info_t * p_ni,
150219820Sjeff	      IN osm_node_t * const p_parent_node)
151219820Sjeff{
152219820Sjeff	ib_net64_t port_guid;
153219820Sjeff	osm_physp_t *p_physp;
154219820Sjeff	uint8_t port_num;
155219820Sjeff
156219820Sjeff	CL_ASSERT(p_port);
157219820Sjeff	CL_ASSERT(p_ni);
158219820Sjeff	CL_ASSERT(p_parent_node);
159219820Sjeff
160219820Sjeff	memset(p_port, 0, sizeof(*p_port));
161219820Sjeff	cl_qlist_init(&p_port->mcm_list);
162219820Sjeff	p_port->p_node = (struct osm_node *)p_parent_node;
163219820Sjeff	port_guid = p_ni->port_guid;
164219820Sjeff	p_port->guid = port_guid;
165219820Sjeff	port_num = p_ni->node_type == IB_NODE_TYPE_SWITCH ?
166219820Sjeff		0 : ib_node_info_get_local_port_num(p_ni);
167219820Sjeff
168219820Sjeff	/*
169219820Sjeff	   Get the pointers to the physical node objects "owned" by this
170219820Sjeff	   logical port GUID.
171219820Sjeff	   For switches, port '0' is owned; for HCA's and routers,
172219820Sjeff	   only the singular part that has this GUID is owned.
173219820Sjeff	 */
174219820Sjeff	p_physp = osm_node_get_physp_ptr(p_parent_node, port_num);
175219820Sjeff	CL_ASSERT(port_guid == osm_physp_get_port_guid(p_physp));
176219820Sjeff	p_port->p_physp = p_physp;
177219820Sjeff}
178219820Sjeff
179219820Sjeff/**********************************************************************
180219820Sjeff **********************************************************************/
181219820Sjeffosm_port_t *osm_port_new(IN const ib_node_info_t * p_ni,
182219820Sjeff			 IN osm_node_t * const p_parent_node)
183219820Sjeff{
184219820Sjeff	osm_port_t *p_port;
185219820Sjeff
186219820Sjeff	p_port = malloc(sizeof(*p_port));
187219820Sjeff	if (p_port != NULL) {
188219820Sjeff		memset(p_port, 0, sizeof(*p_port));
189219820Sjeff		osm_port_init(p_port, p_ni, p_parent_node);
190219820Sjeff	}
191219820Sjeff
192219820Sjeff	return (p_port);
193219820Sjeff}
194219820Sjeff
195219820Sjeff/**********************************************************************
196219820Sjeff **********************************************************************/
197219820Sjeffvoid
198219820Sjeffosm_port_get_lid_range_ho(IN const osm_port_t * const p_port,
199219820Sjeff			  IN uint16_t * const p_min_lid,
200219820Sjeff			  IN uint16_t * const p_max_lid)
201219820Sjeff{
202219820Sjeff	uint8_t lmc;
203219820Sjeff
204219820Sjeff	*p_min_lid = cl_ntoh16(osm_port_get_base_lid(p_port));
205219820Sjeff	lmc = osm_port_get_lmc(p_port);
206219820Sjeff	*p_max_lid = (uint16_t) (*p_min_lid + (1 << lmc) - 1);
207219820Sjeff}
208219820Sjeff
209219820Sjeff/**********************************************************************
210219820Sjeff **********************************************************************/
211219820Sjeffib_api_status_t
212219820Sjeffosm_get_port_by_base_lid(IN const osm_subn_t * const p_subn,
213219820Sjeff			 IN const ib_net16_t lid,
214219820Sjeff			 IN OUT const osm_port_t ** const pp_port)
215219820Sjeff{
216219820Sjeff	ib_api_status_t status;
217219820Sjeff	uint16_t base_lid;
218219820Sjeff	uint8_t lmc;
219219820Sjeff
220219820Sjeff	*pp_port = NULL;
221219820Sjeff
222219820Sjeff	/* Loop on lmc from 0 up through max LMC possible */
223219820Sjeff	for (lmc = 0; lmc <= IB_PORT_LMC_MAX; lmc++) {
224219820Sjeff		/* Calculate a base LID assuming this is the real LMC */
225219820Sjeff		base_lid = cl_ntoh16(lid) & ~((1 << lmc) - 1);
226219820Sjeff
227219820Sjeff		/* Look for a match */
228219820Sjeff		status = cl_ptr_vector_at(&p_subn->port_lid_tbl,
229219820Sjeff					  base_lid, (void **)pp_port);
230219820Sjeff		if ((status == CL_SUCCESS) && (*pp_port != NULL)) {
231219820Sjeff			/* Determine if base LID "tested" is the real base LID */
232219820Sjeff			/* This is true if the LMC "tested" is the port's actual LMC */
233219820Sjeff			if (lmc == osm_port_get_lmc(*pp_port)) {
234219820Sjeff				status = IB_SUCCESS;
235219820Sjeff				goto Found;
236219820Sjeff			}
237219820Sjeff		}
238219820Sjeff	}
239219820Sjeff	*pp_port = NULL;
240219820Sjeff	status = IB_NOT_FOUND;
241219820Sjeff
242219820SjeffFound:
243219820Sjeff	return status;
244219820Sjeff}
245219820Sjeff
246219820Sjeff/**********************************************************************
247219820Sjeff **********************************************************************/
248219820Sjeffib_api_status_t
249219820Sjeffosm_port_add_mgrp(IN osm_port_t * const p_port, IN const ib_net16_t mlid)
250219820Sjeff{
251219820Sjeff	ib_api_status_t status = IB_SUCCESS;
252219820Sjeff	osm_mcm_info_t *p_mcm;
253219820Sjeff
254219820Sjeff	p_mcm = osm_mcm_info_new(mlid);
255219820Sjeff	if (p_mcm)
256219820Sjeff		cl_qlist_insert_tail(&p_port->mcm_list,
257219820Sjeff				     (cl_list_item_t *) p_mcm);
258219820Sjeff	else
259219820Sjeff		status = IB_INSUFFICIENT_MEMORY;
260219820Sjeff
261219820Sjeff	return (status);
262219820Sjeff}
263219820Sjeff
264219820Sjeff/**********************************************************************
265219820Sjeff **********************************************************************/
266219820Sjeffstatic cl_status_t
267219820Sjeff__osm_port_mgrp_find_func(IN const cl_list_item_t * const p_list_item,
268219820Sjeff			  IN void *context)
269219820Sjeff{
270219820Sjeff	if (*((ib_net16_t *) context) == ((osm_mcm_info_t *) p_list_item)->mlid)
271219820Sjeff		return (CL_SUCCESS);
272219820Sjeff	else
273219820Sjeff		return (CL_NOT_FOUND);
274219820Sjeff}
275219820Sjeff
276219820Sjeff/**********************************************************************
277219820Sjeff **********************************************************************/
278219820Sjeffvoid
279219820Sjeffosm_port_remove_mgrp(IN osm_port_t * const p_port, IN const ib_net16_t mlid)
280219820Sjeff{
281219820Sjeff	cl_list_item_t *p_mcm;
282219820Sjeff
283219820Sjeff	p_mcm = cl_qlist_find_from_head(&p_port->mcm_list,
284219820Sjeff					__osm_port_mgrp_find_func, &mlid);
285219820Sjeff
286219820Sjeff	if (p_mcm != cl_qlist_end(&p_port->mcm_list)) {
287219820Sjeff		cl_qlist_remove_item(&p_port->mcm_list, p_mcm);
288219820Sjeff		osm_mcm_info_delete((osm_mcm_info_t *) p_mcm);
289219820Sjeff	}
290219820Sjeff}
291219820Sjeff
292219820Sjeff/**********************************************************************
293219820Sjeff **********************************************************************/
294219820Sjeffvoid osm_port_remove_all_mgrp(IN osm_port_t * const p_port)
295219820Sjeff{
296219820Sjeff	cl_list_item_t *p_mcm;
297219820Sjeff
298219820Sjeff	p_mcm = cl_qlist_remove_head(&p_port->mcm_list);
299219820Sjeff	while (p_mcm != cl_qlist_end(&p_port->mcm_list)) {
300219820Sjeff		osm_mcm_info_delete((osm_mcm_info_t *) p_mcm);
301219820Sjeff		p_mcm = cl_qlist_remove_head(&p_port->mcm_list);
302219820Sjeff	}
303219820Sjeff}
304219820Sjeff
305219820Sjeff/**********************************************************************
306219820Sjeff **********************************************************************/
307219820Sjeffuint8_t
308219820Sjeffosm_physp_calc_link_mtu(IN osm_log_t * p_log, IN const osm_physp_t * p_physp)
309219820Sjeff{
310219820Sjeff	const osm_physp_t *p_remote_physp;
311219820Sjeff	uint8_t mtu;
312219820Sjeff	uint8_t remote_mtu;
313219820Sjeff
314219820Sjeff	OSM_LOG_ENTER(p_log);
315219820Sjeff
316219820Sjeff	p_remote_physp = osm_physp_get_remote(p_physp);
317219820Sjeff	if (p_remote_physp) {
318219820Sjeff		/* use the available MTU */
319219820Sjeff		mtu = ib_port_info_get_mtu_cap(&p_physp->port_info);
320219820Sjeff
321219820Sjeff		remote_mtu =
322219820Sjeff		    ib_port_info_get_mtu_cap(&p_remote_physp->port_info);
323219820Sjeff
324219820Sjeff		OSM_LOG(p_log, OSM_LOG_DEBUG,
325219820Sjeff			"Remote port 0x%016" PRIx64 " port = %u : "
326219820Sjeff			"MTU = %u. This Port MTU: %u\n",
327219820Sjeff			cl_ntoh64(osm_physp_get_port_guid(p_remote_physp)),
328219820Sjeff			osm_physp_get_port_num(p_remote_physp),
329219820Sjeff			remote_mtu, mtu);
330219820Sjeff
331219820Sjeff		if (mtu != remote_mtu) {
332219820Sjeff			if (mtu > remote_mtu)
333219820Sjeff				mtu = remote_mtu;
334219820Sjeff
335219820Sjeff			OSM_LOG(p_log, OSM_LOG_VERBOSE,
336219820Sjeff				"MTU mismatch between ports."
337219820Sjeff				"\n\t\t\t\tPort 0x%016" PRIx64 ", port %u"
338219820Sjeff				" and port 0x%016" PRIx64 ", port %u."
339219820Sjeff				"\n\t\t\t\tUsing lower MTU of %u\n",
340219820Sjeff				cl_ntoh64(osm_physp_get_port_guid(p_physp)),
341219820Sjeff				osm_physp_get_port_num(p_physp),
342219820Sjeff				cl_ntoh64(osm_physp_get_port_guid(p_remote_physp)),
343219820Sjeff				osm_physp_get_port_num(p_remote_physp),mtu);
344219820Sjeff		}
345219820Sjeff	} else
346219820Sjeff		mtu = ib_port_info_get_neighbor_mtu(&p_physp->port_info);
347219820Sjeff
348219820Sjeff	if (mtu == 0) {
349219820Sjeff		OSM_LOG(p_log, OSM_LOG_DEBUG, "ERR 4101: "
350219820Sjeff			"Invalid MTU = 0. Forcing correction to 256\n");
351219820Sjeff		mtu = 1;
352219820Sjeff	}
353219820Sjeff
354219820Sjeff	OSM_LOG_EXIT(p_log);
355219820Sjeff	return (mtu);
356219820Sjeff}
357219820Sjeff
358219820Sjeff/**********************************************************************
359219820Sjeff **********************************************************************/
360219820Sjeffuint8_t
361219820Sjeffosm_physp_calc_link_op_vls(IN osm_log_t * p_log,
362219820Sjeff			   IN const osm_subn_t * p_subn,
363219820Sjeff			   IN const osm_physp_t * p_physp)
364219820Sjeff{
365219820Sjeff	const osm_physp_t *p_remote_physp;
366219820Sjeff	uint8_t op_vls;
367219820Sjeff	uint8_t remote_op_vls;
368219820Sjeff
369219820Sjeff	OSM_LOG_ENTER(p_log);
370219820Sjeff
371219820Sjeff	p_remote_physp = osm_physp_get_remote(p_physp);
372219820Sjeff	if (p_remote_physp) {
373219820Sjeff		/* use the available VLCap */
374219820Sjeff		op_vls = ib_port_info_get_vl_cap(&p_physp->port_info);
375219820Sjeff
376219820Sjeff		remote_op_vls =
377219820Sjeff		    ib_port_info_get_vl_cap(&p_remote_physp->port_info);
378219820Sjeff
379219820Sjeff		OSM_LOG(p_log, OSM_LOG_DEBUG,
380219820Sjeff			"Remote port 0x%016" PRIx64 " port = 0x%X : "
381219820Sjeff			"VL_CAP = %u. This port VL_CAP = %u\n",
382219820Sjeff			cl_ntoh64(osm_physp_get_port_guid(p_remote_physp)),
383219820Sjeff			osm_physp_get_port_num(p_remote_physp),
384219820Sjeff			remote_op_vls, op_vls);
385219820Sjeff
386219820Sjeff		if (op_vls != remote_op_vls) {
387219820Sjeff			if (op_vls > remote_op_vls)
388219820Sjeff				op_vls = remote_op_vls;
389219820Sjeff
390219820Sjeff			OSM_LOG(p_log, OSM_LOG_VERBOSE,
391219820Sjeff				"OP_VLS mismatch between ports."
392219820Sjeff				"\n\t\t\t\tPort 0x%016" PRIx64 ", port 0x%X"
393219820Sjeff				" and port 0x%016" PRIx64 ", port 0x%X."
394219820Sjeff				"\n\t\t\t\tUsing lower OP_VLS of %u\n",
395219820Sjeff				cl_ntoh64(osm_physp_get_port_guid(p_physp)),
396219820Sjeff				osm_physp_get_port_num(p_physp),
397219820Sjeff				cl_ntoh64(osm_physp_get_port_guid(p_remote_physp)),
398219820Sjeff				osm_physp_get_port_num(p_remote_physp), op_vls);
399219820Sjeff		}
400219820Sjeff	} else
401219820Sjeff		op_vls = ib_port_info_get_op_vls(&p_physp->port_info);
402219820Sjeff
403219820Sjeff	/* support user limitation of max_op_vls */
404219820Sjeff	if (op_vls > p_subn->opt.max_op_vls)
405219820Sjeff		op_vls = p_subn->opt.max_op_vls;
406219820Sjeff
407219820Sjeff	if (op_vls == 0) {
408219820Sjeff		OSM_LOG(p_log, OSM_LOG_DEBUG, "ERR 4102: "
409219820Sjeff			"Invalid OP_VLS = 0. Forcing correction to 1 (VL0)\n");
410219820Sjeff		op_vls = 1;
411219820Sjeff	}
412219820Sjeff
413219820Sjeff	OSM_LOG_EXIT(p_log);
414219820Sjeff	return (op_vls);
415219820Sjeff}
416219820Sjeff
417219820Sjeffstatic inline uint64_t __osm_ptr_to_key(void const *p)
418219820Sjeff{
419219820Sjeff	uint64_t k = 0;
420219820Sjeff
421219820Sjeff	memcpy(&k, p, sizeof(void *));
422219820Sjeff	return k;
423219820Sjeff}
424219820Sjeff
425219820Sjeffstatic inline void *__osm_key_to_ptr(uint64_t k)
426219820Sjeff{
427219820Sjeff	void *p = 0;
428219820Sjeff
429219820Sjeff	memcpy(&p, &k, sizeof(void *));
430219820Sjeff	return p;
431219820Sjeff}
432219820Sjeff
433219820Sjeff/**********************************************************************
434219820Sjeff Traverse the fabric from the SM node following the DR path given and
435219820Sjeff add every phys port traversed to the map. Avoid tracking the first and
436219820Sjeff last phys ports (going into the first switch and into the target port).
437219820Sjeff **********************************************************************/
438219820Sjeffstatic cl_status_t
439219820Sjeff__osm_physp_get_dr_physp_set(IN osm_log_t * p_log,
440219820Sjeff			     IN osm_subn_t const *p_subn,
441219820Sjeff			     IN osm_dr_path_t const *p_path,
442219820Sjeff			     OUT cl_map_t * p_physp_map)
443219820Sjeff{
444219820Sjeff	osm_port_t *p_port;
445219820Sjeff	osm_physp_t *p_physp;
446219820Sjeff	osm_node_t *p_node;
447219820Sjeff	uint8_t hop;
448219820Sjeff	cl_status_t status = CL_SUCCESS;
449219820Sjeff
450219820Sjeff	OSM_LOG_ENTER(p_log);
451219820Sjeff
452219820Sjeff	/* find the OSM node */
453219820Sjeff	p_port = osm_get_port_by_guid(p_subn, p_subn->sm_port_guid);
454219820Sjeff	if (!p_port) {
455219820Sjeff		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4103: "
456219820Sjeff			"Failed to find the SM own port by guid\n");
457219820Sjeff		status = CL_ERROR;
458219820Sjeff		goto Exit;
459219820Sjeff	}
460219820Sjeff
461219820Sjeff	/* get the node of the SM */
462219820Sjeff	p_node = p_port->p_node;
463219820Sjeff
464219820Sjeff	/*
465219820Sjeff	   traverse the path adding the nodes to the table
466219820Sjeff	   start after the first dummy hop and stop just before the
467219820Sjeff	   last one
468219820Sjeff	 */
469219820Sjeff	for (hop = 1; hop < p_path->hop_count - 1; hop++) {
470219820Sjeff		/* go out using the phys port of the path */
471219820Sjeff		p_physp = osm_node_get_physp_ptr(p_node, p_path->path[hop]);
472219820Sjeff
473219820Sjeff		/* make sure we got a valid port and it has a remote port */
474219820Sjeff		if (!p_physp) {
475219820Sjeff			OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4104: "
476219820Sjeff				"DR Traversal stopped on invalid port at hop:%u\n",
477219820Sjeff				hop);
478219820Sjeff			status = CL_ERROR;
479219820Sjeff			goto Exit;
480219820Sjeff		}
481219820Sjeff
482219820Sjeff		/* we track the ports we go out along the path */
483219820Sjeff		if (hop > 1)
484219820Sjeff			cl_map_insert(p_physp_map, __osm_ptr_to_key(p_physp),
485219820Sjeff				      NULL);
486219820Sjeff
487219820Sjeff		OSM_LOG(p_log, OSM_LOG_DEBUG,
488219820Sjeff			"Traversed through node: 0x%016" PRIx64
489219820Sjeff			" port:%u\n",
490219820Sjeff			cl_ntoh64(p_node->node_info.node_guid),
491219820Sjeff			p_path->path[hop]);
492219820Sjeff
493219820Sjeff		if (!(p_physp = osm_physp_get_remote(p_physp))) {
494219820Sjeff			OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4106: "
495219820Sjeff				"DR Traversal stopped on missing remote physp at hop:%u\n",
496219820Sjeff				hop);
497219820Sjeff			status = CL_ERROR;
498219820Sjeff			goto Exit;
499219820Sjeff		}
500219820Sjeff
501219820Sjeff		p_node = osm_physp_get_node_ptr(p_physp);
502219820Sjeff	}
503219820Sjeff
504219820SjeffExit:
505219820Sjeff	OSM_LOG_EXIT(p_log);
506219820Sjeff	return status;
507219820Sjeff}
508219820Sjeff
509219820Sjeff/**********************************************************************
510219820Sjeff **********************************************************************/
511219820Sjeffstatic void
512219820Sjeff__osm_physp_update_new_dr_path(IN osm_physp_t const *p_dest_physp,
513219820Sjeff			       IN cl_map_t * p_visited_map,
514219820Sjeff			       IN osm_bind_handle_t * h_bind)
515219820Sjeff{
516219820Sjeff	cl_list_t tmpPortsList;
517219820Sjeff	osm_physp_t *p_physp, *p_src_physp = NULL;
518219820Sjeff	uint8_t path_array[IB_SUBNET_PATH_HOPS_MAX];
519219820Sjeff	uint8_t i = 0;
520219820Sjeff	osm_dr_path_t *p_dr_path;
521219820Sjeff
522219820Sjeff	cl_list_construct(&tmpPortsList);
523219820Sjeff	cl_list_init(&tmpPortsList, 10);
524219820Sjeff
525219820Sjeff	cl_list_insert_head(&tmpPortsList, p_dest_physp);
526219820Sjeff	/* get the output port where we need to come from */
527219820Sjeff	p_physp = (osm_physp_t *) cl_map_get(p_visited_map,
528219820Sjeff					     __osm_ptr_to_key(p_dest_physp));
529219820Sjeff	while (p_physp != NULL) {
530219820Sjeff		cl_list_insert_head(&tmpPortsList, p_physp);
531219820Sjeff		/* get the input port through where we reached the output port */
532219820Sjeff		p_src_physp = p_physp;
533219820Sjeff		p_physp = (osm_physp_t *) cl_map_get(p_visited_map,
534219820Sjeff						     __osm_ptr_to_key(p_physp));
535219820Sjeff		/* if we reached a null p_physp - this means we are at the begining
536219820Sjeff		   of the path. Break. */
537219820Sjeff		if (p_physp == NULL)
538219820Sjeff			break;
539219820Sjeff		/* get the output port */
540219820Sjeff		p_physp = (osm_physp_t *) cl_map_get(p_visited_map,
541219820Sjeff						     __osm_ptr_to_key(p_physp));
542219820Sjeff	}
543219820Sjeff
544219820Sjeff	memset(path_array, 0, sizeof(path_array));
545219820Sjeff	p_physp = (osm_physp_t *) cl_list_remove_head(&tmpPortsList);
546219820Sjeff	while (p_physp != NULL) {
547219820Sjeff		i++;
548219820Sjeff		path_array[i] = p_physp->port_num;
549219820Sjeff		p_physp = (osm_physp_t *) cl_list_remove_head(&tmpPortsList);
550219820Sjeff	}
551219820Sjeff	if (p_src_physp) {
552219820Sjeff		p_dr_path = osm_physp_get_dr_path_ptr(p_src_physp);
553219820Sjeff		osm_dr_path_init(p_dr_path, h_bind, i, path_array);
554219820Sjeff	}
555219820Sjeff
556219820Sjeff	cl_list_destroy(&tmpPortsList);
557219820Sjeff}
558219820Sjeff
559219820Sjeff/**********************************************************************
560219820Sjeff **********************************************************************/
561219820Sjeffvoid
562219820Sjeffosm_physp_replace_dr_path_with_alternate_dr_path(IN osm_log_t * p_log,
563219820Sjeff						 IN osm_subn_t const *p_subn,
564219820Sjeff						 IN osm_physp_t const
565219820Sjeff						 *p_dest_physp,
566219820Sjeff						 IN osm_bind_handle_t * h_bind)
567219820Sjeff{
568219820Sjeff	cl_map_t physp_map;
569219820Sjeff	cl_map_t visited_map;
570219820Sjeff	osm_dr_path_t *p_dr_path;
571219820Sjeff	cl_list_t *p_currPortsList;
572219820Sjeff	cl_list_t *p_nextPortsList;
573219820Sjeff	osm_port_t *p_port;
574219820Sjeff	osm_physp_t *p_physp, *p_remote_physp;
575219820Sjeff	ib_net64_t port_guid;
576219820Sjeff	boolean_t next_list_is_full = TRUE, reached_dest = FALSE;
577219820Sjeff	uint8_t num_ports, port_num;
578219820Sjeff
579219820Sjeff	p_nextPortsList = (cl_list_t *) malloc(sizeof(cl_list_t));
580219820Sjeff	if (!p_nextPortsList)
581219820Sjeff		return;
582219820Sjeff
583219820Sjeff	/*
584219820Sjeff	   initialize the map of all port participating in current dr path
585219820Sjeff	   not including first and last switches
586219820Sjeff	 */
587219820Sjeff	cl_map_construct(&physp_map);
588219820Sjeff	cl_map_init(&physp_map, 4);
589219820Sjeff	cl_map_construct(&visited_map);
590219820Sjeff	cl_map_init(&visited_map, 4);
591219820Sjeff	p_dr_path = osm_physp_get_dr_path_ptr(p_dest_physp);
592219820Sjeff	__osm_physp_get_dr_physp_set(p_log, p_subn, p_dr_path, &physp_map);
593219820Sjeff
594219820Sjeff	/*
595219820Sjeff	   BFS from OSM port until we find the target physp but avoid
596219820Sjeff	   going through mapped ports
597219820Sjeff	 */
598219820Sjeff	cl_list_construct(p_nextPortsList);
599219820Sjeff	cl_list_init(p_nextPortsList, 10);
600219820Sjeff
601219820Sjeff	port_guid = p_subn->sm_port_guid;
602219820Sjeff
603219820Sjeff	CL_ASSERT(port_guid);
604219820Sjeff
605219820Sjeff	p_port = osm_get_port_by_guid(p_subn, port_guid);
606219820Sjeff	if (!p_port) {
607219820Sjeff		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4105: No SM port object\n");
608219820Sjeff		goto Exit;
609219820Sjeff	}
610219820Sjeff
611219820Sjeff	/*
612219820Sjeff	   HACK: We are assuming SM is running on HCA, so when getting the default
613219820Sjeff	   port we'll get the port connected to the rest of the subnet. If SM is
614219820Sjeff	   running on SWITCH - we should try to get a dr path from all switch ports.
615219820Sjeff	 */
616219820Sjeff	p_physp = p_port->p_physp;
617219820Sjeff
618219820Sjeff	CL_ASSERT(p_physp);
619219820Sjeff
620219820Sjeff	cl_list_insert_tail(p_nextPortsList, p_physp);
621219820Sjeff
622219820Sjeff	while (next_list_is_full == TRUE) {
623219820Sjeff		next_list_is_full = FALSE;
624219820Sjeff		p_currPortsList = p_nextPortsList;
625219820Sjeff		p_nextPortsList = (cl_list_t *) malloc(sizeof(cl_list_t));
626219820Sjeff		if (!p_nextPortsList) {
627219820Sjeff			p_nextPortsList = p_currPortsList;
628219820Sjeff			goto Exit;
629219820Sjeff		}
630219820Sjeff		cl_list_construct(p_nextPortsList);
631219820Sjeff		cl_list_init(p_nextPortsList, 10);
632219820Sjeff		p_physp = (osm_physp_t *) cl_list_remove_head(p_currPortsList);
633219820Sjeff		while (p_physp != NULL) {
634219820Sjeff			/* If we are in a switch - need to go out through all
635219820Sjeff			   the other physical ports of the switch */
636219820Sjeff			num_ports = osm_node_get_num_physp(p_physp->p_node);
637219820Sjeff
638219820Sjeff			for (port_num = 1; port_num < num_ports; port_num++) {
639219820Sjeff				if (osm_node_get_type(p_physp->p_node) ==
640219820Sjeff				    IB_NODE_TYPE_SWITCH)
641219820Sjeff					p_remote_physp =
642219820Sjeff					    osm_node_get_physp_ptr(p_physp->
643219820Sjeff								   p_node,
644219820Sjeff								   port_num);
645219820Sjeff				else
646219820Sjeff					/* this is HCA or router - the remote port is just the port connected
647219820Sjeff					   on the other side */
648219820Sjeff					p_remote_physp =
649219820Sjeff					    p_physp->p_remote_physp;
650219820Sjeff
651219820Sjeff				/*
652219820Sjeff				   make sure that all of the following occurred:
653219820Sjeff				   1. The port isn't NULL
654219820Sjeff				   2. This is not the port we came from
655219820Sjeff				   3. The port is not in the physp_map
656219820Sjeff				   4. This port haven't been visited before
657219820Sjeff				 */
658219820Sjeff				if (p_remote_physp &&
659219820Sjeff				    p_remote_physp != p_physp &&
660219820Sjeff				    cl_map_get(&physp_map,
661219820Sjeff					       __osm_ptr_to_key(p_remote_physp))
662219820Sjeff				    == NULL
663219820Sjeff				    && cl_map_get(&visited_map,
664219820Sjeff						  __osm_ptr_to_key
665219820Sjeff						  (p_remote_physp)) == NULL) {
666219820Sjeff					/* Insert the port into the visited_map, and save its source port */
667219820Sjeff					cl_map_insert(&visited_map,
668219820Sjeff						      __osm_ptr_to_key
669219820Sjeff						      (p_remote_physp),
670219820Sjeff						      p_physp);
671219820Sjeff
672219820Sjeff					/* Is this the p_dest_physp? */
673219820Sjeff					if (p_remote_physp == p_dest_physp) {
674219820Sjeff						/* update the new dr path */
675219820Sjeff						__osm_physp_update_new_dr_path
676219820Sjeff						    (p_dest_physp, &visited_map,
677219820Sjeff						     h_bind);
678219820Sjeff						reached_dest = TRUE;
679219820Sjeff						break;
680219820Sjeff					}
681219820Sjeff
682219820Sjeff					/* add the p_remote_physp to the nextPortsList */
683219820Sjeff					cl_list_insert_tail(p_nextPortsList,
684219820Sjeff							    p_remote_physp);
685219820Sjeff					next_list_is_full = TRUE;
686219820Sjeff				}
687219820Sjeff			}
688219820Sjeff
689219820Sjeff			p_physp = (osm_physp_t *)
690219820Sjeff			    cl_list_remove_head(p_currPortsList);
691219820Sjeff			if (reached_dest == TRUE) {
692219820Sjeff				/* free the rest of the currPortsList */
693219820Sjeff				while (p_physp != NULL)
694219820Sjeff					p_physp = (osm_physp_t *)
695219820Sjeff					    cl_list_remove_head
696219820Sjeff					    (p_currPortsList);
697219820Sjeff				/* free the nextPortsList, if items were added to it */
698219820Sjeff				p_physp = (osm_physp_t *)
699219820Sjeff				    cl_list_remove_head(p_nextPortsList);
700219820Sjeff				while (p_physp != NULL)
701219820Sjeff					p_physp = (osm_physp_t *)
702219820Sjeff					    cl_list_remove_head
703219820Sjeff					    (p_nextPortsList);
704219820Sjeff				next_list_is_full = FALSE;
705219820Sjeff			}
706219820Sjeff		}
707219820Sjeff		cl_list_destroy(p_currPortsList);
708219820Sjeff		free(p_currPortsList);
709219820Sjeff	}
710219820Sjeff
711219820Sjeff	/* cleanup */
712219820SjeffExit:
713219820Sjeff	cl_list_destroy(p_nextPortsList);
714219820Sjeff	free(p_nextPortsList);
715219820Sjeff	cl_map_destroy(&physp_map);
716219820Sjeff	cl_map_destroy(&visited_map);
717219820Sjeff}
718219820Sjeff
719219820Sjeff/**********************************************************************
720219820Sjeff **********************************************************************/
721219820Sjeffboolean_t osm_link_is_healthy(IN const osm_physp_t * const p_physp)
722219820Sjeff{
723219820Sjeff	osm_physp_t *p_remote_physp;
724219820Sjeff
725219820Sjeff	CL_ASSERT(p_physp);
726219820Sjeff	p_remote_physp = p_physp->p_remote_physp;
727219820Sjeff	if (p_remote_physp != NULL)
728219820Sjeff		return ((p_physp->healthy) & (p_remote_physp->healthy));
729219820Sjeff	/* the other side is not known - consider the link as healthy */
730219820Sjeff	return (TRUE);
731219820Sjeff}
732219820Sjeff
733219820Sjeff/**********************************************************************
734219820Sjeff **********************************************************************/
735219820Sjeffvoid
736219820Sjeffosm_physp_set_pkey_tbl(IN osm_log_t * p_log,
737219820Sjeff		       IN const osm_subn_t * p_subn,
738219820Sjeff		       IN osm_physp_t * const p_physp,
739219820Sjeff		       IN ib_pkey_table_t * p_pkey_tbl, IN uint16_t block_num)
740219820Sjeff{
741219820Sjeff	uint16_t max_blocks;
742219820Sjeff
743219820Sjeff	CL_ASSERT(p_pkey_tbl);
744219820Sjeff	/*
745219820Sjeff	   (14.2.5.7) - the block number valid values are 0-2047, and are
746219820Sjeff	   further limited by the size of the P_Key table specified by
747219820Sjeff	   the PartitionCap on the node.
748219820Sjeff	 */
749219820Sjeff	if (!p_physp->p_node->sw || p_physp->port_num == 0)
750219820Sjeff		/*
751219820Sjeff		   The maximum blocks is defined in the node info: partition cap
752219820Sjeff		   for CA, router, and switch management ports.
753219820Sjeff		 */
754219820Sjeff		max_blocks =
755219820Sjeff		    (cl_ntoh16(p_physp->p_node->node_info.partition_cap) +
756219820Sjeff		     IB_NUM_PKEY_ELEMENTS_IN_BLOCK - 1)
757219820Sjeff		    / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
758219820Sjeff	else
759219820Sjeff		/*
760219820Sjeff		   This is a switch, and not a management port. The maximum
761219820Sjeff		   blocks is defined in the switch info: partition enforcement
762219820Sjeff		   cap.
763219820Sjeff		 */
764219820Sjeff		max_blocks =
765219820Sjeff		    (cl_ntoh16(p_physp->p_node->sw->switch_info.enforce_cap) +
766219820Sjeff		     IB_NUM_PKEY_ELEMENTS_IN_BLOCK -
767219820Sjeff		     1) / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
768219820Sjeff
769219820Sjeff	if (block_num >= max_blocks) {
770219820Sjeff		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 4108: "
771219820Sjeff			"Got illegal set for block number:%u "
772219820Sjeff			"For GUID: %" PRIx64 " port number:%u\n",
773219820Sjeff			block_num,
774219820Sjeff			cl_ntoh64(p_physp->p_node->node_info.node_guid),
775219820Sjeff			p_physp->port_num);
776219820Sjeff		return;
777219820Sjeff	}
778219820Sjeff
779219820Sjeff	osm_pkey_tbl_set(&p_physp->pkeys, block_num, p_pkey_tbl);
780219820Sjeff}
781