1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2002-2008 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_pi_rcv_t.
39219820Sjeff * This object represents the PortInfo Receiver object.
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 <string.h>
48219820Sjeff#include <iba/ib_types.h>
49219820Sjeff#include <complib/cl_qmap.h>
50219820Sjeff#include <complib/cl_passivelock.h>
51219820Sjeff#include <complib/cl_debug.h>
52219820Sjeff#include <vendor/osm_vendor_api.h>
53219820Sjeff#include <opensm/osm_madw.h>
54219820Sjeff#include <opensm/osm_log.h>
55219820Sjeff#include <opensm/osm_node.h>
56219820Sjeff#include <opensm/osm_subnet.h>
57219820Sjeff#include <opensm/osm_mad_pool.h>
58219820Sjeff#include <opensm/osm_msgdef.h>
59219820Sjeff#include <opensm/osm_helper.h>
60219820Sjeff#include <opensm/osm_pkey.h>
61219820Sjeff#include <opensm/osm_remote_sm.h>
62219820Sjeff#include <opensm/osm_opensm.h>
63219820Sjeff#include <opensm/osm_ucast_mgr.h>
64219820Sjeff
65219820Sjeff/**********************************************************************
66219820Sjeff **********************************************************************/
67219820Sjeffstatic void
68219820Sjeff__osm_pi_rcv_set_sm(IN osm_sm_t * sm,
69219820Sjeff		    IN osm_physp_t * const p_physp)
70219820Sjeff{
71219820Sjeff	osm_bind_handle_t h_bind;
72219820Sjeff	osm_dr_path_t *p_dr_path;
73219820Sjeff
74219820Sjeff	OSM_LOG_ENTER(sm->p_log);
75219820Sjeff
76219820Sjeff	OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
77219820Sjeff		"Setting IS_SM bit in port attributes\n");
78219820Sjeff
79219820Sjeff	p_dr_path = osm_physp_get_dr_path_ptr(p_physp);
80219820Sjeff	h_bind = osm_dr_path_get_bind_handle(p_dr_path);
81219820Sjeff
82219820Sjeff	/*
83219820Sjeff	   The 'IS_SM' bit isn't already set, so set it.
84219820Sjeff	 */
85219820Sjeff	osm_vendor_set_sm(h_bind, TRUE);
86219820Sjeff
87219820Sjeff	OSM_LOG_EXIT(sm->p_log);
88219820Sjeff}
89219820Sjeff
90219820Sjeff/**********************************************************************
91219820Sjeff **********************************************************************/
92219820Sjeffstatic void pi_rcv_check_and_fix_lid(osm_log_t *log, ib_port_info_t * const pi,
93219820Sjeff				     osm_physp_t * p)
94219820Sjeff{
95219820Sjeff	if (cl_ntoh16(pi->base_lid) > IB_LID_UCAST_END_HO) {
96219820Sjeff		OSM_LOG(log, OSM_LOG_ERROR, "ERR 0F04: "
97219820Sjeff			"Got invalid base LID %u from the network. "
98219820Sjeff			"Corrected to %u.\n", cl_ntoh16(pi->base_lid),
99219820Sjeff			cl_ntoh16(p->port_info.base_lid));
100219820Sjeff		pi->base_lid = p->port_info.base_lid;
101219820Sjeff	}
102219820Sjeff}
103219820Sjeff
104219820Sjeff/**********************************************************************
105219820Sjeff **********************************************************************/
106219820Sjeffstatic void
107219820Sjeff__osm_pi_rcv_process_endport(IN osm_sm_t * sm,
108219820Sjeff			     IN osm_physp_t * const p_physp,
109219820Sjeff			     IN const ib_port_info_t * const p_pi)
110219820Sjeff{
111219820Sjeff	osm_madw_context_t context;
112219820Sjeff	ib_api_status_t status;
113219820Sjeff	ib_net64_t port_guid;
114219820Sjeff	uint8_t rate, mtu;
115219820Sjeff	cl_qmap_t *p_sm_tbl;
116219820Sjeff	osm_remote_sm_t *p_sm;
117219820Sjeff
118219820Sjeff	OSM_LOG_ENTER(sm->p_log);
119219820Sjeff
120219820Sjeff	port_guid = osm_physp_get_port_guid(p_physp);
121219820Sjeff
122219820Sjeff	/* HACK extended port 0 should be handled too! */
123219820Sjeff	if (osm_physp_get_port_num(p_physp) != 0) {
124219820Sjeff		/* track the minimal endport MTU and rate */
125219820Sjeff		mtu = ib_port_info_get_mtu_cap(p_pi);
126219820Sjeff		if (mtu < sm->p_subn->min_ca_mtu) {
127219820Sjeff			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
128219820Sjeff				"Setting endport minimal MTU to:%u defined by port:0x%"
129219820Sjeff				PRIx64 "\n", mtu, cl_ntoh64(port_guid));
130219820Sjeff			sm->p_subn->min_ca_mtu = mtu;
131219820Sjeff		}
132219820Sjeff
133219820Sjeff		rate = ib_port_info_compute_rate(p_pi);
134219820Sjeff		if (rate < sm->p_subn->min_ca_rate) {
135219820Sjeff			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
136219820Sjeff				"Setting endport minimal rate to:%u defined by port:0x%"
137219820Sjeff				PRIx64 "\n", rate, cl_ntoh64(port_guid));
138219820Sjeff			sm->p_subn->min_ca_rate = rate;
139219820Sjeff		}
140219820Sjeff	}
141219820Sjeff
142219820Sjeff	if (port_guid == sm->p_subn->sm_port_guid) {
143219820Sjeff		/*
144219820Sjeff		   We received the PortInfo for our own port.
145219820Sjeff		 */
146219820Sjeff		if (!(p_pi->capability_mask & IB_PORT_CAP_IS_SM))
147219820Sjeff			/*
148219820Sjeff			   Set the IS_SM bit to indicate our port hosts an SM.
149219820Sjeff			 */
150219820Sjeff			__osm_pi_rcv_set_sm(sm, p_physp);
151219820Sjeff	} else {
152219820Sjeff		p_sm_tbl = &sm->p_subn->sm_guid_tbl;
153219820Sjeff		if (p_pi->capability_mask & IB_PORT_CAP_IS_SM) {
154219820Sjeff			/*
155219820Sjeff			 * Before querying the SM - we want to make sure we
156219820Sjeff			 * clean its state, so if the querying fails we
157219820Sjeff			 * recognize that this SM is not active.
158219820Sjeff			 */
159219820Sjeff			p_sm = (osm_remote_sm_t *) cl_qmap_get(p_sm_tbl, port_guid);
160219820Sjeff			if (p_sm != (osm_remote_sm_t *) cl_qmap_end(p_sm_tbl))
161219820Sjeff				/* clean it up */
162219820Sjeff				p_sm->smi.pri_state = 0xF0 & p_sm->smi.pri_state;
163219820Sjeff			if (sm->p_subn->opt.ignore_other_sm)
164219820Sjeff				OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
165219820Sjeff					"Ignoring SM on port 0x%" PRIx64 "\n",
166219820Sjeff					cl_ntoh64(port_guid));
167219820Sjeff			else {
168219820Sjeff				OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
169219820Sjeff					"Detected another SM. Requesting SMInfo"
170219820Sjeff					"\n\t\t\t\tPort 0x%" PRIx64 "\n",
171219820Sjeff					cl_ntoh64(port_guid));
172219820Sjeff
173219820Sjeff				/*
174219820Sjeff				   This port indicates it's an SM and
175219820Sjeff				   it's not our own port.
176219820Sjeff				   Acquire the SMInfo Attribute.
177219820Sjeff				 */
178219820Sjeff				memset(&context, 0, sizeof(context));
179219820Sjeff				context.smi_context.set_method = FALSE;
180219820Sjeff				context.smi_context.port_guid = port_guid;
181219820Sjeff				status = osm_req_get(sm,
182219820Sjeff						     osm_physp_get_dr_path_ptr
183219820Sjeff						     (p_physp),
184219820Sjeff						     IB_MAD_ATTR_SM_INFO, 0,
185219820Sjeff						     CL_DISP_MSGID_NONE,
186219820Sjeff						     &context);
187219820Sjeff
188219820Sjeff				if (status != IB_SUCCESS)
189219820Sjeff					OSM_LOG(sm->p_log, OSM_LOG_ERROR,
190219820Sjeff						"ERR 0F05: "
191219820Sjeff						"Failure requesting SMInfo (%s)\n",
192219820Sjeff						ib_get_err_str(status));
193219820Sjeff			}
194219820Sjeff		} else {
195219820Sjeff			p_sm = (osm_remote_sm_t *) cl_qmap_remove(p_sm_tbl, port_guid);
196219820Sjeff			if (p_sm != (osm_remote_sm_t *) cl_qmap_end(p_sm_tbl))
197219820Sjeff				free(p_sm);
198219820Sjeff		}
199219820Sjeff	}
200219820Sjeff
201219820Sjeff	OSM_LOG_EXIT(sm->p_log);
202219820Sjeff}
203219820Sjeff
204219820Sjeff/**********************************************************************
205219820Sjeff The plock must be held before calling this function.
206219820Sjeff**********************************************************************/
207219820Sjeffstatic void
208219820Sjeff__osm_pi_rcv_process_switch_port(IN osm_sm_t * sm,
209219820Sjeff				 IN osm_node_t * const p_node,
210219820Sjeff				 IN osm_physp_t * const p_physp,
211219820Sjeff				 IN ib_port_info_t * const p_pi)
212219820Sjeff{
213219820Sjeff	ib_api_status_t status = IB_SUCCESS;
214219820Sjeff	osm_madw_context_t context;
215219820Sjeff	osm_physp_t *p_remote_physp;
216219820Sjeff	osm_node_t *p_remote_node;
217219820Sjeff	uint8_t port_num;
218219820Sjeff	uint8_t remote_port_num;
219219820Sjeff	osm_dr_path_t path;
220219820Sjeff
221219820Sjeff	OSM_LOG_ENTER(sm->p_log);
222219820Sjeff
223219820Sjeff	/*
224219820Sjeff	   Check the state of the physical port.
225219820Sjeff	   If there appears to be something on the other end of the wire,
226219820Sjeff	   then ask for NodeInfo.  Ignore the switch management port.
227219820Sjeff	 */
228219820Sjeff	port_num = osm_physp_get_port_num(p_physp);
229219820Sjeff	/* if in_sweep_hop_0 is TRUE, then this means the SM is on the switch,
230219820Sjeff	   and we got switchInfo of our local switch. Do not continue
231219820Sjeff	   probing through the switch. */
232219820Sjeff	if (port_num != 0 && sm->p_subn->in_sweep_hop_0 == FALSE) {
233219820Sjeff		switch (ib_port_info_get_port_state(p_pi)) {
234219820Sjeff		case IB_LINK_DOWN:
235219820Sjeff			p_remote_physp = osm_physp_get_remote(p_physp);
236219820Sjeff			if (p_remote_physp) {
237219820Sjeff				p_remote_node =
238219820Sjeff				    osm_physp_get_node_ptr(p_remote_physp);
239219820Sjeff				remote_port_num =
240219820Sjeff				    osm_physp_get_port_num(p_remote_physp);
241219820Sjeff
242219820Sjeff				OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
243219820Sjeff					"Unlinking local node 0x%" PRIx64
244219820Sjeff					", port %u"
245219820Sjeff					"\n\t\t\t\tand remote node 0x%" PRIx64
246219820Sjeff					", port %u\n",
247219820Sjeff					cl_ntoh64(osm_node_get_node_guid
248219820Sjeff						  (p_node)), port_num,
249219820Sjeff					cl_ntoh64(osm_node_get_node_guid
250219820Sjeff						  (p_remote_node)),
251219820Sjeff					remote_port_num);
252219820Sjeff
253219820Sjeff				if (sm->ucast_mgr.cache_valid)
254219820Sjeff					osm_ucast_cache_add_link(&sm->ucast_mgr,
255219820Sjeff								 p_physp,
256219820Sjeff								 p_remote_physp);
257219820Sjeff
258219820Sjeff				osm_node_unlink(p_node, (uint8_t) port_num,
259219820Sjeff						p_remote_node,
260219820Sjeff						(uint8_t) remote_port_num);
261219820Sjeff
262219820Sjeff			}
263219820Sjeff			break;
264219820Sjeff
265219820Sjeff		case IB_LINK_INIT:
266219820Sjeff		case IB_LINK_ARMED:
267219820Sjeff		case IB_LINK_ACTIVE:
268219820Sjeff			/*
269219820Sjeff			   To avoid looping forever, only probe the port if it
270219820Sjeff			   is NOT the port that responded to the SMP.
271219820Sjeff
272219820Sjeff			   Request node info from the other end of this link:
273219820Sjeff			   1) Copy the current path from the parent node.
274219820Sjeff			   2) Extend the path to the next hop thru this port.
275219820Sjeff			   3) Request node info with the new path
276219820Sjeff
277219820Sjeff			 */
278219820Sjeff			if (p_pi->local_port_num !=
279219820Sjeff			    osm_physp_get_port_num(p_physp)) {
280219820Sjeff				path = *osm_physp_get_dr_path_ptr(p_physp);
281219820Sjeff
282219820Sjeff				osm_dr_path_extend(&path,
283219820Sjeff						   osm_physp_get_port_num
284219820Sjeff						   (p_physp));
285219820Sjeff
286219820Sjeff				memset(&context, 0, sizeof(context));
287219820Sjeff				context.ni_context.node_guid =
288219820Sjeff				    osm_node_get_node_guid(p_node);
289219820Sjeff				context.ni_context.port_num =
290219820Sjeff				    osm_physp_get_port_num(p_physp);
291219820Sjeff
292219820Sjeff				status = osm_req_get(sm,
293219820Sjeff						     &path,
294219820Sjeff						     IB_MAD_ATTR_NODE_INFO,
295219820Sjeff						     0,
296219820Sjeff						     CL_DISP_MSGID_NONE,
297219820Sjeff						     &context);
298219820Sjeff
299219820Sjeff				if (status != IB_SUCCESS)
300219820Sjeff					OSM_LOG(sm->p_log, OSM_LOG_ERROR,
301219820Sjeff						"ERR 0F02: "
302219820Sjeff						"Failure initiating NodeInfo request (%s)\n",
303219820Sjeff						ib_get_err_str(status));
304219820Sjeff			} else
305219820Sjeff				OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
306219820Sjeff					"Skipping SMP responder port %u\n",
307219820Sjeff					p_pi->local_port_num);
308219820Sjeff			break;
309219820Sjeff
310219820Sjeff		default:
311219820Sjeff			OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F03: "
312219820Sjeff				"Unknown link state = %u, port = %u\n",
313219820Sjeff				ib_port_info_get_port_state(p_pi),
314219820Sjeff				p_pi->local_port_num);
315219820Sjeff			break;
316219820Sjeff		}
317219820Sjeff	}
318219820Sjeff
319219820Sjeff	if (ib_port_info_get_port_state(p_pi) > IB_LINK_INIT && p_node->sw &&
320219820Sjeff	    p_node->sw->need_update == 1)
321219820Sjeff		p_node->sw->need_update = 0;
322219820Sjeff
323219820Sjeff	if (p_physp->need_update)
324219820Sjeff		sm->p_subn->ignore_existing_lfts = TRUE;
325219820Sjeff
326219820Sjeff	if (port_num == 0)
327219820Sjeff		pi_rcv_check_and_fix_lid(sm->p_log, p_pi, p_physp);
328219820Sjeff
329219820Sjeff	/*
330219820Sjeff	   Update the PortInfo attribute.
331219820Sjeff	 */
332219820Sjeff	osm_physp_set_port_info(p_physp, p_pi);
333219820Sjeff
334219820Sjeff	if (port_num == 0) {
335219820Sjeff		/* Determine if base switch port 0 */
336219820Sjeff		if (p_node->sw &&
337219820Sjeff		    !ib_switch_info_is_enhanced_port0(&p_node->sw->switch_info))
338219820Sjeff			/* PortState is not used on BSP0 but just in case it is DOWN */
339219820Sjeff			p_physp->port_info = *p_pi;
340219820Sjeff		__osm_pi_rcv_process_endport(sm, p_physp, p_pi);
341219820Sjeff	}
342219820Sjeff
343219820Sjeff	OSM_LOG_EXIT(sm->p_log);
344219820Sjeff}
345219820Sjeff
346219820Sjeff/**********************************************************************
347219820Sjeff **********************************************************************/
348219820Sjeffstatic void
349219820Sjeff__osm_pi_rcv_process_ca_or_router_port(IN osm_sm_t * sm,
350219820Sjeff				       IN osm_node_t * const p_node,
351219820Sjeff				       IN osm_physp_t * const p_physp,
352219820Sjeff				       IN ib_port_info_t * const p_pi)
353219820Sjeff{
354219820Sjeff	OSM_LOG_ENTER(sm->p_log);
355219820Sjeff
356219820Sjeff	UNUSED_PARAM(p_node);
357219820Sjeff
358219820Sjeff	pi_rcv_check_and_fix_lid(sm->p_log, p_pi, p_physp);
359219820Sjeff
360219820Sjeff	osm_physp_set_port_info(p_physp, p_pi);
361219820Sjeff
362219820Sjeff	__osm_pi_rcv_process_endport(sm, p_physp, p_pi);
363219820Sjeff
364219820Sjeff	OSM_LOG_EXIT(sm->p_log);
365219820Sjeff}
366219820Sjeff
367219820Sjeff#define IBM_VENDOR_ID  (0x5076)
368219820Sjeff/**********************************************************************
369219820Sjeff **********************************************************************/
370219820Sjeffstatic void get_pkey_table(IN osm_log_t * p_log,
371219820Sjeff			   IN osm_sm_t * sm,
372219820Sjeff			   IN osm_node_t * const p_node,
373219820Sjeff			   IN osm_physp_t * const p_physp)
374219820Sjeff{
375219820Sjeff
376219820Sjeff	osm_madw_context_t context;
377219820Sjeff	ib_api_status_t status;
378219820Sjeff	osm_dr_path_t path;
379219820Sjeff	uint8_t port_num;
380219820Sjeff	uint16_t block_num, max_blocks;
381219820Sjeff	uint32_t attr_mod_ho;
382219820Sjeff
383219820Sjeff	OSM_LOG_ENTER(p_log);
384219820Sjeff
385219820Sjeff	path = *osm_physp_get_dr_path_ptr(p_physp);
386219820Sjeff
387219820Sjeff	context.pkey_context.node_guid = osm_node_get_node_guid(p_node);
388219820Sjeff	context.pkey_context.port_guid = osm_physp_get_port_guid(p_physp);
389219820Sjeff	context.pkey_context.set_method = FALSE;
390219820Sjeff
391219820Sjeff	port_num = p_physp->port_num;
392219820Sjeff
393219820Sjeff	if (!p_node->sw || port_num == 0)
394219820Sjeff		/* The maximum blocks is defined by the node info partition cap for CA,
395219820Sjeff		   router, and switch management ports. */
396219820Sjeff		max_blocks =
397219820Sjeff		    (cl_ntoh16(p_node->node_info.partition_cap) +
398219820Sjeff		     IB_NUM_PKEY_ELEMENTS_IN_BLOCK - 1)
399219820Sjeff		    / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
400219820Sjeff	else {
401219820Sjeff		/* This is a switch, and not a management port. The maximum blocks
402219820Sjeff		   is defined in the switch info partition enforcement cap. */
403219820Sjeff
404219820Sjeff		/* Check for IBM eHCA firmware defect in reporting partition enforcement cap */
405219820Sjeff		if (cl_ntoh32(ib_node_info_get_vendor_id(&p_node->node_info)) ==
406219820Sjeff		    IBM_VENDOR_ID)
407219820Sjeff			p_node->sw->switch_info.enforce_cap = 0;
408219820Sjeff
409219820Sjeff		/* Bail out if this is a switch with no partition enforcement capability */
410219820Sjeff		if (cl_ntoh16(p_node->sw->switch_info.enforce_cap) == 0)
411219820Sjeff			goto Exit;
412219820Sjeff
413219820Sjeff		max_blocks = (cl_ntoh16(p_node->sw->switch_info.enforce_cap) +
414219820Sjeff			      IB_NUM_PKEY_ELEMENTS_IN_BLOCK -
415219820Sjeff			      1) / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
416219820Sjeff	}
417219820Sjeff
418219820Sjeff	for (block_num = 0; block_num < max_blocks; block_num++) {
419219820Sjeff		if (osm_node_get_type(p_node) != IB_NODE_TYPE_SWITCH)
420219820Sjeff			attr_mod_ho = block_num;
421219820Sjeff		else
422219820Sjeff			attr_mod_ho = block_num | (port_num << 16);
423219820Sjeff		status = osm_req_get(sm, &path, IB_MAD_ATTR_P_KEY_TABLE,
424219820Sjeff				     cl_hton32(attr_mod_ho),
425219820Sjeff				     CL_DISP_MSGID_NONE, &context);
426219820Sjeff
427219820Sjeff		if (status != IB_SUCCESS) {
428219820Sjeff			OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0F12: "
429219820Sjeff				"Failure initiating PKeyTable request (%s)\n",
430219820Sjeff				ib_get_err_str(status));
431219820Sjeff			goto Exit;
432219820Sjeff		}
433219820Sjeff	}
434219820Sjeff
435219820SjeffExit:
436219820Sjeff	OSM_LOG_EXIT(p_log);
437219820Sjeff}
438219820Sjeff
439219820Sjeff/**********************************************************************
440219820Sjeff **********************************************************************/
441219820Sjeffstatic void
442219820Sjeff__osm_pi_rcv_get_pkey_slvl_vla_tables(IN osm_sm_t * sm,
443219820Sjeff				      IN osm_node_t * const p_node,
444219820Sjeff				      IN osm_physp_t * const p_physp)
445219820Sjeff{
446219820Sjeff	OSM_LOG_ENTER(sm->p_log);
447219820Sjeff
448219820Sjeff	get_pkey_table(sm->p_log, sm, p_node, p_physp);
449219820Sjeff
450219820Sjeff	OSM_LOG_EXIT(sm->p_log);
451219820Sjeff}
452219820Sjeff
453219820Sjeff/**********************************************************************
454219820Sjeff **********************************************************************/
455219820Sjeffstatic void
456219820Sjeffosm_pi_rcv_process_set(IN osm_sm_t * sm, IN osm_node_t * const p_node,
457219820Sjeff		       IN const uint8_t port_num, IN osm_madw_t * const p_madw)
458219820Sjeff{
459219820Sjeff	osm_physp_t *p_physp;
460219820Sjeff	ib_net64_t port_guid;
461219820Sjeff	ib_smp_t *p_smp;
462219820Sjeff	ib_port_info_t *p_pi;
463219820Sjeff	osm_pi_context_t *p_context;
464219820Sjeff	osm_log_level_t level;
465219820Sjeff
466219820Sjeff	OSM_LOG_ENTER(sm->p_log);
467219820Sjeff
468219820Sjeff	p_context = osm_madw_get_pi_context_ptr(p_madw);
469219820Sjeff
470219820Sjeff	CL_ASSERT(p_node);
471219820Sjeff
472219820Sjeff	p_physp = osm_node_get_physp_ptr(p_node, port_num);
473219820Sjeff	CL_ASSERT(p_physp);
474219820Sjeff
475219820Sjeff	port_guid = osm_physp_get_port_guid(p_physp);
476219820Sjeff
477219820Sjeff	p_smp = osm_madw_get_smp_ptr(p_madw);
478219820Sjeff	p_pi = (ib_port_info_t *) ib_smp_get_payload_ptr(p_smp);
479219820Sjeff
480219820Sjeff	/* check for error */
481219820Sjeff	if (cl_ntoh16(p_smp->status) & 0x7fff) {
482219820Sjeff		/* If port already ACTIVE, don't treat status 7 as error */
483219820Sjeff		if (p_context->active_transition &&
484219820Sjeff		    (cl_ntoh16(p_smp->status) & 0x7fff) == 0x1c) {
485219820Sjeff			level = OSM_LOG_INFO;
486219820Sjeff			OSM_LOG(sm->p_log, OSM_LOG_INFO,
487219820Sjeff				"Received error status 0x%x for SetResp() during ACTIVE transition\n",
488219820Sjeff				cl_ntoh16(p_smp->status) & 0x7fff);
489219820Sjeff			/* Should there be a subsequent Get to validate that port is ACTIVE ? */
490219820Sjeff		} else {
491219820Sjeff			level = OSM_LOG_ERROR;
492219820Sjeff			OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F10: "
493219820Sjeff				"Received error status for SetResp()\n");
494219820Sjeff		}
495219820Sjeff		osm_dump_port_info(sm->p_log,
496219820Sjeff				   osm_node_get_node_guid(p_node),
497219820Sjeff				   port_guid, port_num, p_pi, level);
498219820Sjeff	}
499219820Sjeff
500219820Sjeff	OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
501219820Sjeff		"Received logical SetResp() for GUID 0x%" PRIx64
502219820Sjeff		", port num %u"
503219820Sjeff		"\n\t\t\t\tfor parent node GUID 0x%" PRIx64
504219820Sjeff		" TID 0x%" PRIx64 "\n",
505219820Sjeff		cl_ntoh64(port_guid), port_num,
506219820Sjeff		cl_ntoh64(osm_node_get_node_guid(p_node)),
507219820Sjeff		cl_ntoh64(p_smp->trans_id));
508219820Sjeff
509219820Sjeff	osm_physp_set_port_info(p_physp, p_pi);
510219820Sjeff
511219820Sjeff	OSM_LOG_EXIT(sm->p_log);
512219820Sjeff}
513219820Sjeff
514219820Sjeff/**********************************************************************
515219820Sjeff **********************************************************************/
516219820Sjeffvoid osm_pi_rcv_process(IN void *context, IN void *data)
517219820Sjeff{
518219820Sjeff	osm_sm_t *sm = context;
519219820Sjeff	osm_madw_t *p_madw = data;
520219820Sjeff	ib_port_info_t *p_pi;
521219820Sjeff	ib_smp_t *p_smp;
522219820Sjeff	osm_port_t *p_port;
523219820Sjeff	osm_physp_t *p_physp;
524219820Sjeff	osm_dr_path_t *p_dr_path;
525219820Sjeff	osm_node_t *p_node;
526219820Sjeff	osm_pi_context_t *p_context;
527219820Sjeff	ib_net64_t port_guid;
528219820Sjeff	ib_net64_t node_guid;
529219820Sjeff	uint8_t port_num;
530219820Sjeff
531219820Sjeff	OSM_LOG_ENTER(sm->p_log);
532219820Sjeff
533219820Sjeff	CL_ASSERT(sm);
534219820Sjeff	CL_ASSERT(p_madw);
535219820Sjeff
536219820Sjeff	p_smp = osm_madw_get_smp_ptr(p_madw);
537219820Sjeff	p_context = osm_madw_get_pi_context_ptr(p_madw);
538219820Sjeff	p_pi = (ib_port_info_t *) ib_smp_get_payload_ptr(p_smp);
539219820Sjeff
540219820Sjeff	CL_ASSERT(p_smp->attr_id == IB_MAD_ATTR_PORT_INFO);
541219820Sjeff
542219820Sjeff	port_num = (uint8_t) cl_ntoh32(p_smp->attr_mod);
543219820Sjeff
544219820Sjeff	port_guid = p_context->port_guid;
545219820Sjeff	node_guid = p_context->node_guid;
546219820Sjeff
547219820Sjeff	osm_dump_port_info(sm->p_log,
548219820Sjeff			   node_guid, port_guid, port_num, p_pi, OSM_LOG_DEBUG);
549219820Sjeff
550219820Sjeff	/* On receipt of client reregister, clear the reregister bit so
551219820Sjeff	   reregistering won't be sent again and again */
552219820Sjeff	if (ib_port_info_get_client_rereg(p_pi)) {
553219820Sjeff		OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
554219820Sjeff			"Client reregister received on response\n");
555219820Sjeff		ib_port_info_set_client_rereg(p_pi, 0);
556219820Sjeff	}
557219820Sjeff
558219820Sjeff	/*
559219820Sjeff	   we might get a response during a light sweep looking for a change in
560219820Sjeff	   the status of a remote port that did not respond in earlier sweeps.
561219820Sjeff	   So if the context of the Get was light_sweep - we do not need to
562219820Sjeff	   do anything with the response - just flag that we need a heavy sweep
563219820Sjeff	 */
564219820Sjeff	if (p_context->light_sweep == TRUE) {
565219820Sjeff		OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
566219820Sjeff			"Got light sweep response from remote port of parent node "
567219820Sjeff			"GUID 0x%" PRIx64 " port 0x%016" PRIx64
568219820Sjeff			", Commencing heavy sweep\n",
569219820Sjeff			cl_ntoh64(node_guid), cl_ntoh64(port_guid));
570219820Sjeff		sm->p_subn->force_heavy_sweep = TRUE;
571219820Sjeff		sm->p_subn->ignore_existing_lfts = TRUE;
572219820Sjeff		goto Exit;
573219820Sjeff	}
574219820Sjeff
575219820Sjeff	CL_PLOCK_EXCL_ACQUIRE(sm->p_lock);
576219820Sjeff	p_port = osm_get_port_by_guid(sm->p_subn, port_guid);
577219820Sjeff	if (!p_port) {
578219820Sjeff		CL_PLOCK_RELEASE(sm->p_lock);
579219820Sjeff		OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F06: "
580219820Sjeff			"No port object for port with GUID 0x%" PRIx64
581219820Sjeff			"\n\t\t\t\tfor parent node GUID 0x%" PRIx64
582219820Sjeff			", TID 0x%" PRIx64 "\n",
583219820Sjeff			cl_ntoh64(port_guid),
584219820Sjeff			cl_ntoh64(node_guid), cl_ntoh64(p_smp->trans_id));
585219820Sjeff		goto Exit;
586219820Sjeff	}
587219820Sjeff
588219820Sjeff	p_node = p_port->p_node;
589219820Sjeff	CL_ASSERT(p_node);
590219820Sjeff
591219820Sjeff	/*
592219820Sjeff	   If we were setting the PortInfo, then receiving
593219820Sjeff	   this attribute was not part of sweeping the subnet.
594219820Sjeff	   In this case, just update the PortInfo attribute.
595219820Sjeff
596219820Sjeff	   In an unfortunate blunder, the IB spec defines the
597219820Sjeff	   return method for Set() as a GetResp().  Thus, we can't
598219820Sjeff	   use the method (what would have been SetResp()) to determine
599219820Sjeff	   our course of action.  So, we have to carry this extra
600219820Sjeff	   boolean around to determine if we were doing Get() or Set().
601219820Sjeff	 */
602219820Sjeff	if (p_context->set_method)
603219820Sjeff		osm_pi_rcv_process_set(sm, p_node, port_num, p_madw);
604219820Sjeff	else {
605219820Sjeff		p_port->discovery_count++;
606219820Sjeff
607219820Sjeff		/*
608219820Sjeff		   This PortInfo arrived because we did a Get() method,
609219820Sjeff		   most likely due to a subnet sweep in progress.
610219820Sjeff		 */
611219820Sjeff		OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
612219820Sjeff			"Discovered port num %u with GUID 0x%" PRIx64
613219820Sjeff			" for parent node GUID 0x%" PRIx64
614219820Sjeff			", TID 0x%" PRIx64 "\n",
615219820Sjeff			port_num, cl_ntoh64(port_guid),
616219820Sjeff			cl_ntoh64(node_guid), cl_ntoh64(p_smp->trans_id));
617219820Sjeff
618219820Sjeff		p_physp = osm_node_get_physp_ptr(p_node, port_num);
619219820Sjeff
620219820Sjeff		/*
621219820Sjeff		   Determine if we encountered a new Physical Port.
622219820Sjeff		   If so, initialize the new Physical Port then
623219820Sjeff		   continue processing as normal.
624219820Sjeff		 */
625219820Sjeff		if (!p_physp) {
626219820Sjeff			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
627219820Sjeff				"Initializing port number %u\n", port_num);
628219820Sjeff			p_physp = &p_node->physp_table[port_num];
629219820Sjeff			osm_physp_init(p_physp,
630219820Sjeff				       port_guid,
631219820Sjeff				       port_num,
632219820Sjeff				       p_node,
633219820Sjeff				       osm_madw_get_bind_handle(p_madw),
634219820Sjeff				       p_smp->hop_count, p_smp->initial_path);
635219820Sjeff		} else {
636219820Sjeff			/*
637219820Sjeff			   Update the directed route path to this port
638219820Sjeff			   in case the old path is no longer usable.
639219820Sjeff			 */
640219820Sjeff			p_dr_path = osm_physp_get_dr_path_ptr(p_physp);
641219820Sjeff			osm_dr_path_init(p_dr_path,
642219820Sjeff					 osm_madw_get_bind_handle(p_madw),
643219820Sjeff					 p_smp->hop_count, p_smp->initial_path);
644219820Sjeff		}
645219820Sjeff
646219820Sjeff		/* if port just inited or reached INIT state (external reset)
647219820Sjeff		   request update for port related tables */
648219820Sjeff		p_physp->need_update =
649219820Sjeff		    (ib_port_info_get_port_state(p_pi) == IB_LINK_INIT ||
650219820Sjeff		     p_physp->need_update > 1) ? 1 : 0;
651219820Sjeff
652219820Sjeff		switch (osm_node_get_type(p_node)) {
653219820Sjeff		case IB_NODE_TYPE_CA:
654219820Sjeff		case IB_NODE_TYPE_ROUTER:
655219820Sjeff			__osm_pi_rcv_process_ca_or_router_port(sm,
656219820Sjeff							       p_node, p_physp,
657219820Sjeff							       p_pi);
658219820Sjeff			break;
659219820Sjeff		case IB_NODE_TYPE_SWITCH:
660219820Sjeff			__osm_pi_rcv_process_switch_port(sm,
661219820Sjeff							 p_node, p_physp, p_pi);
662219820Sjeff			break;
663219820Sjeff		default:
664219820Sjeff			OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F07: "
665219820Sjeff				"Unknown node type %u with GUID 0x%" PRIx64
666219820Sjeff				"\n", osm_node_get_type(p_node),
667219820Sjeff				cl_ntoh64(node_guid));
668219820Sjeff			break;
669219820Sjeff		}
670219820Sjeff
671219820Sjeff		/*
672219820Sjeff		   Get the tables on the physp.
673219820Sjeff		 */
674219820Sjeff		if (p_physp->need_update || sm->p_subn->need_update)
675219820Sjeff			__osm_pi_rcv_get_pkey_slvl_vla_tables(sm, p_node,
676219820Sjeff							      p_physp);
677219820Sjeff
678219820Sjeff	}
679219820Sjeff
680219820Sjeff	CL_PLOCK_RELEASE(sm->p_lock);
681219820Sjeff
682219820SjeffExit:
683219820Sjeff	/*
684219820Sjeff	   Release the lock before jumping here!!
685219820Sjeff	 */
686219820Sjeff	OSM_LOG_EXIT(sm->p_log);
687219820Sjeff}
688