1321936Shselasky/*
2321936Shselasky * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3321936Shselasky * Copyright (c) 2002-2012 Mellanox Technologies LTD. All rights reserved.
4321936Shselasky * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5321936Shselasky * Copyright (c) 2009 HNR Consulting. All rights reserved.
6321936Shselasky * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
7321936Shselasky *
8321936Shselasky * This software is available to you under a choice of one of two
9321936Shselasky * licenses.  You may choose to be licensed under the terms of the GNU
10321936Shselasky * General Public License (GPL) Version 2, available from the file
11321936Shselasky * COPYING in the main directory of this source tree, or the
12321936Shselasky * OpenIB.org BSD license below:
13321936Shselasky *
14321936Shselasky *     Redistribution and use in source and binary forms, with or
15321936Shselasky *     without modification, are permitted provided that the following
16321936Shselasky *     conditions are met:
17321936Shselasky *
18321936Shselasky *      - Redistributions of source code must retain the above
19321936Shselasky *        copyright notice, this list of conditions and the following
20321936Shselasky *        disclaimer.
21321936Shselasky *
22321936Shselasky *      - Redistributions in binary form must reproduce the above
23321936Shselasky *        copyright notice, this list of conditions and the following
24321936Shselasky *        disclaimer in the documentation and/or other materials
25321936Shselasky *        provided with the distribution.
26321936Shselasky *
27321936Shselasky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28321936Shselasky * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29321936Shselasky * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30321936Shselasky * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
31321936Shselasky * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
32321936Shselasky * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33321936Shselasky * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34321936Shselasky * SOFTWARE.
35321936Shselasky *
36321936Shselasky */
37321936Shselasky
38321936Shselasky/*
39321936Shselasky * Abstract:
40321936Shselasky *    Implementation of osm_pi_rcv_t.
41321936Shselasky * This object represents the PortInfo Receiver object.
42321936Shselasky * This object is part of the opensm family of objects.
43321936Shselasky */
44321936Shselasky
45321936Shselasky#if HAVE_CONFIG_H
46321936Shselasky#  include <config.h>
47321936Shselasky#endif				/* HAVE_CONFIG_H */
48321936Shselasky
49321936Shselasky#include <string.h>
50321936Shselasky#include <stdlib.h>
51321936Shselasky#include <iba/ib_types.h>
52321936Shselasky#include <complib/cl_qmap.h>
53321936Shselasky#include <complib/cl_passivelock.h>
54321936Shselasky#include <complib/cl_debug.h>
55321936Shselasky#include <opensm/osm_file_ids.h>
56321936Shselasky#define FILE_ID OSM_FILE_PORT_INFO_RCV_C
57321936Shselasky#include <vendor/osm_vendor_api.h>
58321936Shselasky#include <opensm/osm_madw.h>
59321936Shselasky#include <opensm/osm_log.h>
60321936Shselasky#include <opensm/osm_node.h>
61321936Shselasky#include <opensm/osm_subnet.h>
62321936Shselasky#include <opensm/osm_mad_pool.h>
63321936Shselasky#include <opensm/osm_msgdef.h>
64321936Shselasky#include <opensm/osm_helper.h>
65321936Shselasky#include <opensm/osm_pkey.h>
66321936Shselasky#include <opensm/osm_remote_sm.h>
67321936Shselasky#include <opensm/osm_opensm.h>
68321936Shselasky#include <opensm/osm_ucast_mgr.h>
69321936Shselasky
70321936Shselaskystatic void pi_rcv_check_and_fix_lid(osm_log_t * log, ib_port_info_t * pi,
71321936Shselasky				     osm_physp_t * p)
72321936Shselasky{
73321936Shselasky	if (PF(cl_ntoh16(pi->base_lid) > IB_LID_UCAST_END_HO)) {
74321936Shselasky		OSM_LOG(log, OSM_LOG_ERROR, "ERR 0F04: "
75321936Shselasky			"Got invalid base LID %u from the network. "
76321936Shselasky			"Corrected to %u\n", cl_ntoh16(pi->base_lid),
77321936Shselasky			cl_ntoh16(p->port_info.base_lid));
78321936Shselasky		pi->base_lid = p->port_info.base_lid;
79321936Shselasky	}
80321936Shselasky}
81321936Shselasky
82321936Shselaskystatic void pi_rcv_process_endport(IN osm_sm_t * sm, IN osm_physp_t * p_physp,
83321936Shselasky				   IN const ib_port_info_t * p_pi)
84321936Shselasky{
85321936Shselasky	osm_madw_context_t context;
86321936Shselasky	ib_api_status_t status;
87321936Shselasky	ib_net64_t port_guid;
88321936Shselasky	int extended;
89321936Shselasky	uint8_t rate, mtu, mpb;
90321936Shselasky	unsigned data_vls;
91321936Shselasky	cl_qmap_t *p_sm_tbl;
92321936Shselasky	osm_remote_sm_t *p_sm;
93321936Shselasky
94321936Shselasky	OSM_LOG_ENTER(sm->p_log);
95321936Shselasky
96321936Shselasky	port_guid = osm_physp_get_port_guid(p_physp);
97321936Shselasky
98321936Shselasky	/* HACK extended port 0 should be handled too! */
99321936Shselasky	if (osm_physp_get_port_num(p_physp) != 0 &&
100321936Shselasky	    ib_port_info_get_port_state(p_pi) != IB_LINK_DOWN) {
101321936Shselasky		/* track the minimal endport MTU, rate, and operational VLs */
102321936Shselasky		mtu = ib_port_info_get_mtu_cap(p_pi);
103321936Shselasky		if (mtu < sm->p_subn->min_ca_mtu) {
104321936Shselasky			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
105321936Shselasky				"Setting endport minimal MTU to:%u defined by port:0x%"
106321936Shselasky				PRIx64 "\n", mtu, cl_ntoh64(port_guid));
107321936Shselasky			sm->p_subn->min_ca_mtu = mtu;
108321936Shselasky		}
109321936Shselasky
110321936Shselasky		extended = p_pi->capability_mask & IB_PORT_CAP_HAS_EXT_SPEEDS;
111321936Shselasky		rate = ib_port_info_compute_rate(p_pi, extended);
112321936Shselasky		if (ib_path_compare_rates(rate, sm->p_subn->min_ca_rate) < 0) {
113321936Shselasky			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
114321936Shselasky				"Setting endport minimal rate to:%u defined by port:0x%"
115321936Shselasky				PRIx64 "\n", rate, cl_ntoh64(port_guid));
116321936Shselasky			sm->p_subn->min_ca_rate = rate;
117321936Shselasky		}
118321936Shselasky
119321936Shselasky		data_vls = 1U << (ib_port_info_get_vl_cap(p_pi) - 1);
120321936Shselasky		if (data_vls > 1U << (sm->p_subn->opt.max_op_vls - 1))
121321936Shselasky			data_vls = 1U << (sm->p_subn->opt.max_op_vls - 1);
122321936Shselasky		if (data_vls >= IB_MAX_NUM_VLS)
123321936Shselasky			data_vls = IB_MAX_NUM_VLS - 1;
124321936Shselasky		if ((uint8_t)data_vls < sm->p_subn->min_data_vls) {
125321936Shselasky			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
126321936Shselasky				"Setting endport minimal data VLs to:%u defined by port:0x%"
127321936Shselasky				PRIx64 "\n", data_vls, cl_ntoh64(port_guid));
128321936Shselasky			sm->p_subn->min_data_vls = data_vls;
129321936Shselasky		}
130321936Shselasky	}
131321936Shselasky
132321936Shselasky	/* Check M_Key vs M_Key protect, can we control the port ? */
133321936Shselasky	mpb = ib_port_info_get_mpb(p_pi);
134321936Shselasky	if (mpb > 0 && p_pi->m_key == 0) {
135321936Shselasky		OSM_LOG(sm->p_log, OSM_LOG_INFO,
136321936Shselasky			"Port 0x%" PRIx64 " has unknown M_Key, protection level %u\n",
137321936Shselasky			cl_ntoh64(port_guid), mpb);
138321936Shselasky	}
139321936Shselasky
140321936Shselasky	if (port_guid != sm->p_subn->sm_port_guid) {
141321936Shselasky		p_sm_tbl = &sm->p_subn->sm_guid_tbl;
142321936Shselasky		if (p_pi->capability_mask & IB_PORT_CAP_IS_SM) {
143321936Shselasky			/*
144321936Shselasky			 * Before querying the SM - we want to make sure we
145321936Shselasky			 * clean its state, so if the querying fails we
146321936Shselasky			 * recognize that this SM is not active.
147321936Shselasky			 */
148321936Shselasky			p_sm =
149321936Shselasky			    (osm_remote_sm_t *) cl_qmap_get(p_sm_tbl,
150321936Shselasky							    port_guid);
151321936Shselasky			if (p_sm != (osm_remote_sm_t *) cl_qmap_end(p_sm_tbl))
152321936Shselasky				/* clean it up */
153321936Shselasky				p_sm->smi.pri_state =
154321936Shselasky				    0xF0 & p_sm->smi.pri_state;
155321936Shselasky			if (sm->p_subn->opt.ignore_other_sm)
156321936Shselasky				OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
157321936Shselasky					"Ignoring SM on port 0x%" PRIx64 "\n",
158321936Shselasky					cl_ntoh64(port_guid));
159321936Shselasky			else {
160321936Shselasky				OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
161321936Shselasky					"Detected another SM. Requesting SMInfo "
162321936Shselasky					"from port 0x%" PRIx64 "\n",
163321936Shselasky					cl_ntoh64(port_guid));
164321936Shselasky
165321936Shselasky				/*
166321936Shselasky				   This port indicates it's an SM and
167321936Shselasky				   it's not our own port.
168321936Shselasky				   Acquire the SMInfo Attribute.
169321936Shselasky				 */
170321936Shselasky				memset(&context, 0, sizeof(context));
171321936Shselasky				context.smi_context.set_method = FALSE;
172321936Shselasky				context.smi_context.port_guid = port_guid;
173321936Shselasky				status = osm_req_get(sm,
174321936Shselasky						     osm_physp_get_dr_path_ptr
175321936Shselasky						     (p_physp),
176321936Shselasky						     IB_MAD_ATTR_SM_INFO, 0,
177321936Shselasky						     FALSE,
178321936Shselasky						     ib_port_info_get_m_key(&p_physp->port_info),
179321936Shselasky						     CL_DISP_MSGID_NONE,
180321936Shselasky						     &context);
181321936Shselasky
182321936Shselasky				if (status != IB_SUCCESS)
183321936Shselasky					OSM_LOG(sm->p_log, OSM_LOG_ERROR,
184321936Shselasky						"ERR 0F05: "
185321936Shselasky						"Failure requesting SMInfo (%s) "
186321936Shselasky						"from port 0x%" PRIx64 "\n",
187321936Shselasky						ib_get_err_str(status),
188321936Shselasky						cl_ntoh64(port_guid));
189321936Shselasky			}
190321936Shselasky		} else {
191321936Shselasky			p_sm =
192321936Shselasky			    (osm_remote_sm_t *) cl_qmap_remove(p_sm_tbl,
193321936Shselasky							       port_guid);
194321936Shselasky			if (p_sm != (osm_remote_sm_t *) cl_qmap_end(p_sm_tbl))
195321936Shselasky				free(p_sm);
196321936Shselasky		}
197321936Shselasky	}
198321936Shselasky
199321936Shselasky	OSM_LOG_EXIT(sm->p_log);
200321936Shselasky}
201321936Shselasky
202321936Shselasky/**********************************************************************
203321936Shselasky The plock must be held before calling this function.
204321936Shselasky**********************************************************************/
205321936Shselaskystatic void pi_rcv_process_switch_port0(IN osm_sm_t * sm,
206321936Shselasky					IN osm_node_t * p_node,
207321936Shselasky					IN osm_physp_t * p_physp,
208321936Shselasky					IN ib_port_info_t * p_pi)
209321936Shselasky{
210321936Shselasky	ib_api_status_t status;
211321936Shselasky	osm_madw_context_t context;
212321936Shselasky	uint8_t port, num_ports;
213321936Shselasky
214321936Shselasky	OSM_LOG_ENTER(sm->p_log);
215321936Shselasky
216321936Shselasky	if (p_physp->need_update)
217321936Shselasky		sm->p_subn->ignore_existing_lfts = TRUE;
218321936Shselasky
219321936Shselasky	pi_rcv_check_and_fix_lid(sm->p_log, p_pi, p_physp);
220321936Shselasky
221321936Shselasky	/* Update the PortInfo attribute */
222321936Shselasky	osm_physp_set_port_info(p_physp, p_pi, sm);
223321936Shselasky
224321936Shselasky	/* Determine if base switch port 0 */
225321936Shselasky	if (p_node->sw &&
226321936Shselasky	    !ib_switch_info_is_enhanced_port0(&p_node->sw->switch_info))
227321936Shselasky		/* PortState is not used on BSP0 but just in case it is DOWN */
228321936Shselasky		p_physp->port_info = *p_pi;
229321936Shselasky
230321936Shselasky	/* Now, query PortInfo for the switch external ports */
231321936Shselasky	num_ports = osm_node_get_num_physp(p_node);
232321936Shselasky
233321936Shselasky	context.pi_context.node_guid = osm_node_get_node_guid(p_node);
234321936Shselasky	context.pi_context.port_guid = osm_physp_get_port_guid(p_physp);
235321936Shselasky	context.pi_context.set_method = FALSE;
236321936Shselasky	context.pi_context.light_sweep = FALSE;
237321936Shselasky	context.pi_context.active_transition = FALSE;
238321936Shselasky	context.pi_context.client_rereg = FALSE;
239321936Shselasky
240321936Shselasky	for (port = 1; port < num_ports; port++) {
241321936Shselasky		status = osm_req_get(sm, osm_physp_get_dr_path_ptr(p_physp),
242321936Shselasky				     IB_MAD_ATTR_PORT_INFO, cl_hton32(port),
243321936Shselasky				     FALSE,
244321936Shselasky				     ib_port_info_get_m_key(&p_physp->port_info),
245321936Shselasky				     CL_DISP_MSGID_NONE, &context);
246321936Shselasky		if (status != IB_SUCCESS)
247321936Shselasky			OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F16: "
248321936Shselasky				"Failure initiating PortInfo request (%s)\n",
249321936Shselasky				ib_get_err_str(status));
250321936Shselasky	}
251321936Shselasky
252321936Shselasky	pi_rcv_process_endport(sm, p_physp, p_pi);
253321936Shselasky	OSM_LOG_EXIT(sm->p_log);
254321936Shselasky}
255321936Shselasky
256321936Shselasky/**********************************************************************
257321936Shselasky The plock must be held before calling this function.
258321936Shselasky**********************************************************************/
259321936Shselaskystatic void pi_rcv_process_switch_ext_port(IN osm_sm_t * sm,
260321936Shselasky					   IN osm_node_t * p_node,
261321936Shselasky					   IN osm_physp_t * p_physp,
262321936Shselasky					   IN ib_port_info_t * p_pi)
263321936Shselasky{
264321936Shselasky	ib_api_status_t status = IB_SUCCESS;
265321936Shselasky	osm_madw_context_t context;
266321936Shselasky	osm_physp_t *p_remote_physp, *physp0;
267321936Shselasky	osm_node_t *p_remote_node;
268321936Shselasky	ib_net64_t m_key;
269321936Shselasky	unsigned data_vls;
270321936Shselasky	uint8_t port_num;
271321936Shselasky	uint8_t remote_port_num;
272321936Shselasky	osm_dr_path_t path;
273321936Shselasky	int mlnx_epi_supported = 0;
274321936Shselasky
275321936Shselasky	OSM_LOG_ENTER(sm->p_log);
276321936Shselasky
277321936Shselasky	/*
278321936Shselasky	   Check the state of the physical port.
279321936Shselasky	   If there appears to be something on the other end of the wire,
280321936Shselasky	   then ask for NodeInfo.  Ignore the switch management port.
281321936Shselasky	 */
282321936Shselasky	port_num = osm_physp_get_port_num(p_physp);
283321936Shselasky
284321936Shselasky	if (sm->p_subn->opt.fdr10)
285321936Shselasky		mlnx_epi_supported = is_mlnx_ext_port_info_supported(
286321936Shselasky						ib_node_info_get_vendor_id(&p_node->node_info),
287321936Shselasky						p_node->node_info.device_id);
288321936Shselasky
289321936Shselasky	/* if in_sweep_hop_0 is TRUE, then this means the SM is on the switch,
290321936Shselasky	   and we got switchInfo of our local switch. Do not continue
291321936Shselasky	   probing through the switch. */
292321936Shselasky	switch (ib_port_info_get_port_state(p_pi)) {
293321936Shselasky	case IB_LINK_DOWN:
294321936Shselasky		p_remote_physp = osm_physp_get_remote(p_physp);
295321936Shselasky		if (p_remote_physp) {
296321936Shselasky			p_remote_node =
297321936Shselasky			    osm_physp_get_node_ptr(p_remote_physp);
298321936Shselasky			remote_port_num =
299321936Shselasky			    osm_physp_get_port_num(p_remote_physp);
300321936Shselasky
301321936Shselasky			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
302321936Shselasky				"Unlinking local node 0x%" PRIx64
303321936Shselasky				", port %u"
304321936Shselasky				"\n\t\t\t\tand remote node 0x%" PRIx64
305321936Shselasky				", port %u\n",
306321936Shselasky				cl_ntoh64(osm_node_get_node_guid
307321936Shselasky					  (p_node)), port_num,
308321936Shselasky				cl_ntoh64(osm_node_get_node_guid
309321936Shselasky					  (p_remote_node)),
310321936Shselasky				remote_port_num);
311321936Shselasky
312321936Shselasky			if (sm->ucast_mgr.cache_valid)
313321936Shselasky				osm_ucast_cache_add_link(&sm->ucast_mgr,
314321936Shselasky							 p_physp,
315321936Shselasky							 p_remote_physp);
316321936Shselasky
317321936Shselasky			osm_node_unlink(p_node, (uint8_t) port_num,
318321936Shselasky					p_remote_node,
319321936Shselasky					(uint8_t) remote_port_num);
320321936Shselasky
321321936Shselasky		}
322321936Shselasky		break;
323321936Shselasky
324321936Shselasky	case IB_LINK_INIT:
325321936Shselasky	case IB_LINK_ARMED:
326321936Shselasky	case IB_LINK_ACTIVE:
327321936Shselasky		physp0 = osm_node_get_physp_ptr(p_node, 0);
328321936Shselasky		if (mlnx_epi_supported) {
329321936Shselasky			m_key = ib_port_info_get_m_key(&physp0->port_info);
330321936Shselasky
331321936Shselasky			context.pi_context.node_guid = osm_node_get_node_guid(p_node);
332321936Shselasky			context.pi_context.port_guid = osm_physp_get_port_guid(p_physp);
333321936Shselasky			context.pi_context.set_method = FALSE;
334321936Shselasky			context.pi_context.light_sweep = FALSE;
335321936Shselasky			context.pi_context.active_transition = FALSE;
336321936Shselasky			context.pi_context.client_rereg = FALSE;
337321936Shselasky			status = osm_req_get(sm,
338321936Shselasky					     osm_physp_get_dr_path_ptr(p_physp),
339321936Shselasky					     IB_MAD_ATTR_MLNX_EXTENDED_PORT_INFO,
340321936Shselasky					     cl_hton32(port_num), FALSE, m_key,
341321936Shselasky					     CL_DISP_MSGID_NONE, &context);
342321936Shselasky			if (status != IB_SUCCESS)
343321936Shselasky				OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F11: "
344321936Shselasky					"Failure initiating MLNX ExtPortInfo request (%s)\n",
345321936Shselasky					ib_get_err_str(status));
346321936Shselasky		}
347321936Shselasky		if (sm->p_subn->in_sweep_hop_0 == FALSE) {
348321936Shselasky			/*
349321936Shselasky			   To avoid looping forever, only probe the port if it
350321936Shselasky			   is NOT the port that responded to the SMP.
351321936Shselasky
352321936Shselasky			   Request node info from the other end of this link:
353321936Shselasky			   1) Copy the current path from the parent node.
354321936Shselasky			   2) Extend the path to the next hop thru this port.
355321936Shselasky			   3) Request node info with the new path
356321936Shselasky
357321936Shselasky			 */
358321936Shselasky			if (p_pi->local_port_num !=
359321936Shselasky			    osm_physp_get_port_num(p_physp)) {
360321936Shselasky				path = *osm_physp_get_dr_path_ptr(p_physp);
361321936Shselasky
362321936Shselasky				if (osm_dr_path_extend(&path,
363321936Shselasky						       osm_physp_get_port_num
364321936Shselasky						       (p_physp))) {
365321936Shselasky					OSM_LOG(sm->p_log, OSM_LOG_ERROR,
366321936Shselasky						"ERR 0F08: "
367321936Shselasky						"DR path with hop count %d couldn't be extended\n",
368321936Shselasky						path.hop_count);
369321936Shselasky					break;
370321936Shselasky				}
371321936Shselasky
372321936Shselasky				memset(&context, 0, sizeof(context));
373321936Shselasky				context.ni_context.node_guid =
374321936Shselasky				    osm_node_get_node_guid(p_node);
375321936Shselasky				context.ni_context.port_num =
376321936Shselasky				    osm_physp_get_port_num(p_physp);
377321936Shselasky
378321936Shselasky				status = osm_req_get(sm, &path,
379321936Shselasky						     IB_MAD_ATTR_NODE_INFO, 0,
380321936Shselasky						     TRUE, 0,
381321936Shselasky						     CL_DISP_MSGID_NONE,
382321936Shselasky						     &context);
383321936Shselasky
384321936Shselasky				if (status != IB_SUCCESS)
385321936Shselasky					OSM_LOG(sm->p_log, OSM_LOG_ERROR,
386321936Shselasky						"ERR 0F02: "
387321936Shselasky						"Failure initiating NodeInfo request (%s)\n",
388321936Shselasky						ib_get_err_str(status));
389321936Shselasky			} else
390321936Shselasky				OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
391321936Shselasky					"Skipping SMP responder port %u\n",
392321936Shselasky					p_pi->local_port_num);
393321936Shselasky		}
394321936Shselasky		break;
395321936Shselasky
396321936Shselasky	default:
397321936Shselasky		OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F03: "
398321936Shselasky			"Unknown link state = %u, port = %u\n",
399321936Shselasky			ib_port_info_get_port_state(p_pi),
400321936Shselasky			p_pi->local_port_num);
401321936Shselasky		break;
402321936Shselasky	}
403321936Shselasky
404321936Shselasky	if (ib_port_info_get_port_state(p_pi) > IB_LINK_INIT && p_node->sw &&
405321936Shselasky	    !ib_switch_info_get_state_change(&p_node->sw->switch_info) &&
406321936Shselasky	    p_node->sw->need_update == 1)
407321936Shselasky		p_node->sw->need_update = 0;
408321936Shselasky
409321936Shselasky	if (p_physp->need_update)
410321936Shselasky		sm->p_subn->ignore_existing_lfts = TRUE;
411321936Shselasky
412321936Shselasky	/*
413321936Shselasky	   Update the PortInfo attribute.
414321936Shselasky	 */
415321936Shselasky	osm_physp_set_port_info(p_physp, p_pi, sm);
416321936Shselasky
417321936Shselasky	if (ib_port_info_get_port_state(p_pi) == IB_LINK_DOWN)
418321936Shselasky		goto Exit;
419321936Shselasky
420321936Shselasky	p_remote_physp = osm_physp_get_remote(p_physp);
421321936Shselasky	if (p_remote_physp) {
422321936Shselasky		p_remote_node = osm_physp_get_node_ptr(p_remote_physp);
423321936Shselasky		if (p_remote_node->sw) {
424321936Shselasky			data_vls = 1U << (ib_port_info_get_vl_cap(p_pi) - 1);
425321936Shselasky			if (data_vls > 1U << (sm->p_subn->opt.max_op_vls - 1))
426321936Shselasky				data_vls = 1U << (sm->p_subn->opt.max_op_vls - 1);
427321936Shselasky			if (data_vls >= IB_MAX_NUM_VLS)
428321936Shselasky				data_vls = IB_MAX_NUM_VLS - 1;
429321936Shselasky			if ((uint8_t)data_vls < sm->p_subn->min_sw_data_vls) {
430321936Shselasky				OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
431321936Shselasky					"Setting switch port minimal data VLs "
432321936Shselasky					"to:%u defined by node:0x%"
433321936Shselasky					PRIx64 ", port:%u\n", data_vls,
434321936Shselasky					cl_ntoh64(osm_node_get_node_guid(p_node)),
435321936Shselasky					port_num);
436321936Shselasky				sm->p_subn->min_sw_data_vls = data_vls;
437321936Shselasky			}
438321936Shselasky		}
439321936Shselasky	}
440321936Shselasky
441321936ShselaskyExit:
442321936Shselasky	OSM_LOG_EXIT(sm->p_log);
443321936Shselasky}
444321936Shselasky
445321936Shselaskystatic void pi_rcv_process_ca_or_router_port(IN osm_sm_t * sm,
446321936Shselasky					     IN osm_node_t * p_node,
447321936Shselasky					     IN osm_physp_t * p_physp,
448321936Shselasky					     IN ib_port_info_t * p_pi)
449321936Shselasky{
450321936Shselasky	OSM_LOG_ENTER(sm->p_log);
451321936Shselasky
452321936Shselasky	UNUSED_PARAM(p_node);
453321936Shselasky
454321936Shselasky	pi_rcv_check_and_fix_lid(sm->p_log, p_pi, p_physp);
455321936Shselasky
456321936Shselasky	osm_physp_set_port_info(p_physp, p_pi, sm);
457321936Shselasky
458321936Shselasky	pi_rcv_process_endport(sm, p_physp, p_pi);
459321936Shselasky
460321936Shselasky	OSM_LOG_EXIT(sm->p_log);
461321936Shselasky}
462321936Shselasky
463321936Shselasky#define IBM_VENDOR_ID  (0x5076)
464321936Shselaskystatic void get_pkey_table(IN osm_log_t * p_log, IN osm_sm_t * sm,
465321936Shselasky			   IN osm_node_t * p_node, IN osm_physp_t * p_physp)
466321936Shselasky{
467321936Shselasky
468321936Shselasky	osm_madw_context_t context;
469321936Shselasky	ib_api_status_t status;
470321936Shselasky	osm_dr_path_t path;
471321936Shselasky	osm_physp_t *physp0;
472321936Shselasky	ib_net64_t m_key;
473321936Shselasky	uint8_t port_num;
474321936Shselasky	uint16_t block_num, max_blocks;
475321936Shselasky	uint32_t attr_mod_ho;
476321936Shselasky
477321936Shselasky	OSM_LOG_ENTER(p_log);
478321936Shselasky
479321936Shselasky	path = *osm_physp_get_dr_path_ptr(p_physp);
480321936Shselasky
481321936Shselasky	context.pkey_context.node_guid = osm_node_get_node_guid(p_node);
482321936Shselasky	context.pkey_context.port_guid = osm_physp_get_port_guid(p_physp);
483321936Shselasky	context.pkey_context.set_method = FALSE;
484321936Shselasky
485321936Shselasky	port_num = p_physp->port_num;
486321936Shselasky
487321936Shselasky	if (!p_node->sw || port_num == 0)
488321936Shselasky		/* The maximum blocks is defined by the node info partition cap
489321936Shselasky		   for CA, router, and switch management ports. */
490321936Shselasky		max_blocks =
491321936Shselasky		    (cl_ntoh16(p_node->node_info.partition_cap) +
492321936Shselasky		     IB_NUM_PKEY_ELEMENTS_IN_BLOCK - 1)
493321936Shselasky		    / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
494321936Shselasky	else {
495321936Shselasky		/* This is a switch, and not a management port. The maximum blocks
496321936Shselasky		   is defined in the switch info partition enforcement cap. */
497321936Shselasky
498321936Shselasky		/* Check for IBM eHCA firmware defect in reporting partition enforcement cap */
499321936Shselasky		if (cl_ntoh32(ib_node_info_get_vendor_id(&p_node->node_info)) ==
500321936Shselasky		    IBM_VENDOR_ID)
501321936Shselasky			p_node->sw->switch_info.enforce_cap = 0;
502321936Shselasky
503321936Shselasky		/* Bail out if this is a switch with no partition enforcement capability */
504321936Shselasky		if (cl_ntoh16(p_node->sw->switch_info.enforce_cap) == 0)
505321936Shselasky			goto Exit;
506321936Shselasky
507321936Shselasky		max_blocks = (cl_ntoh16(p_node->sw->switch_info.enforce_cap) +
508321936Shselasky			      IB_NUM_PKEY_ELEMENTS_IN_BLOCK -
509321936Shselasky			      1) / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
510321936Shselasky	}
511321936Shselasky
512321936Shselasky	p_physp->pkeys.rcv_blocks_cnt = max_blocks;
513321936Shselasky	for (block_num = 0; block_num < max_blocks; block_num++) {
514321936Shselasky		if (osm_node_get_type(p_node) != IB_NODE_TYPE_SWITCH ||
515321936Shselasky		    osm_physp_get_port_num(p_physp) == 0) {
516321936Shselasky			attr_mod_ho = block_num;
517321936Shselasky			m_key = ib_port_info_get_m_key(&p_physp->port_info);
518321936Shselasky		} else {
519321936Shselasky			attr_mod_ho = block_num | (port_num << 16);
520321936Shselasky			physp0 = osm_node_get_physp_ptr(p_node, 0);
521321936Shselasky			m_key = ib_port_info_get_m_key(&physp0->port_info);
522321936Shselasky		}
523321936Shselasky		status = osm_req_get(sm, &path, IB_MAD_ATTR_P_KEY_TABLE,
524321936Shselasky				     cl_hton32(attr_mod_ho), FALSE,
525321936Shselasky				     m_key, CL_DISP_MSGID_NONE, &context);
526321936Shselasky
527321936Shselasky		if (status != IB_SUCCESS) {
528321936Shselasky			OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0F12: "
529321936Shselasky				"Failure initiating PKeyTable request (%s)\n",
530321936Shselasky				ib_get_err_str(status));
531321936Shselasky			goto Exit;
532321936Shselasky		}
533321936Shselasky	}
534321936Shselasky
535321936ShselaskyExit:
536321936Shselasky	OSM_LOG_EXIT(p_log);
537321936Shselasky}
538321936Shselasky
539321936Shselaskystatic void pi_rcv_get_pkey_slvl_vla_tables(IN osm_sm_t * sm,
540321936Shselasky					    IN osm_node_t * p_node,
541321936Shselasky					    IN osm_physp_t * p_physp)
542321936Shselasky{
543321936Shselasky	OSM_LOG_ENTER(sm->p_log);
544321936Shselasky
545321936Shselasky	get_pkey_table(sm->p_log, sm, p_node, p_physp);
546321936Shselasky
547321936Shselasky	OSM_LOG_EXIT(sm->p_log);
548321936Shselasky}
549321936Shselasky
550321936Shselaskystatic int osm_pi_rcv_update_self(IN osm_sm_t *sm, IN osm_physp_t *p_physp,
551321936Shselasky				  IN ib_port_info_t *p_pi)
552321936Shselasky{
553321936Shselasky	if (ib_port_info_get_port_state(p_pi) == IB_LINK_DOWN)
554321936Shselasky		return 0;
555321936Shselasky
556321936Shselasky	if (sm->p_subn->need_update || p_physp->need_update > 1 ||
557321936Shselasky	    ib_port_info_get_port_state(p_pi) == IB_LINK_INIT)
558321936Shselasky		return 1;
559321936Shselasky
560321936Shselasky	return 0;
561321936Shselasky}
562321936Shselasky
563321936Shselaskystatic void pi_rcv_process_set(IN osm_sm_t * sm, IN osm_node_t * p_node,
564321936Shselasky			       IN uint8_t port_num, IN osm_madw_t * p_madw)
565321936Shselasky{
566321936Shselasky	osm_physp_t *p_physp;
567321936Shselasky	ib_net64_t port_guid;
568321936Shselasky	ib_smp_t *p_smp;
569321936Shselasky	ib_port_info_t *p_pi;
570321936Shselasky	osm_pi_context_t *p_context;
571321936Shselasky	osm_log_level_t level;
572321936Shselasky
573321936Shselasky	OSM_LOG_ENTER(sm->p_log);
574321936Shselasky
575321936Shselasky	p_context = osm_madw_get_pi_context_ptr(p_madw);
576321936Shselasky
577321936Shselasky	CL_ASSERT(p_node);
578321936Shselasky
579321936Shselasky	p_physp = osm_node_get_physp_ptr(p_node, port_num);
580321936Shselasky	CL_ASSERT(p_physp);
581321936Shselasky
582321936Shselasky	port_guid = osm_physp_get_port_guid(p_physp);
583321936Shselasky
584321936Shselasky	p_smp = osm_madw_get_smp_ptr(p_madw);
585321936Shselasky	p_pi = ib_smp_get_payload_ptr(p_smp);
586321936Shselasky
587321936Shselasky	/* check for error */
588321936Shselasky	if (cl_ntoh16(p_smp->status) & 0x7fff) {
589321936Shselasky		/* If port already ACTIVE, don't treat status 7 as error */
590321936Shselasky		if (p_context->active_transition &&
591321936Shselasky		    (cl_ntoh16(p_smp->status) & 0x7fff) == 0x1c) {
592321936Shselasky			level = OSM_LOG_INFO;
593321936Shselasky			OSM_LOG(sm->p_log, OSM_LOG_INFO,
594321936Shselasky				"Received error status 0x%x for SetResp() during ACTIVE transition\n",
595321936Shselasky				cl_ntoh16(p_smp->status) & 0x7fff);
596321936Shselasky			/* Should there be a subsequent Get to validate that port is ACTIVE ? */
597321936Shselasky		} else {
598321936Shselasky			level = OSM_LOG_ERROR;
599321936Shselasky			OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F10: "
600321936Shselasky				"Received error status for SetResp()\n");
601321936Shselasky		}
602321936Shselasky		osm_dump_port_info_v2(sm->p_log, osm_node_get_node_guid(p_node),
603321936Shselasky				      port_guid, port_num, p_pi, FILE_ID, level);
604321936Shselasky	} else
605321936Shselasky		osm_physp_set_port_info(p_physp, p_pi, sm);
606321936Shselasky
607321936Shselasky	OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
608321936Shselasky		"Received logical SetResp() for GUID 0x%" PRIx64
609321936Shselasky		", port num %u"
610321936Shselasky		"\n\t\t\t\tfor parent node GUID 0x%" PRIx64
611321936Shselasky		" TID 0x%" PRIx64 "\n",
612321936Shselasky		cl_ntoh64(port_guid), port_num,
613321936Shselasky		cl_ntoh64(osm_node_get_node_guid(p_node)),
614321936Shselasky		cl_ntoh64(p_smp->trans_id));
615321936Shselasky
616321936Shselasky
617321936Shselasky	OSM_LOG_EXIT(sm->p_log);
618321936Shselasky}
619321936Shselasky
620321936Shselaskystatic int osm_pi_rcv_update_neighbor(IN osm_physp_t *p_physp)
621321936Shselasky{
622321936Shselasky	osm_physp_t *p_rem_physp = p_physp->p_remote_physp;
623321936Shselasky	osm_node_t *p_node;
624321936Shselasky
625321936Shselasky	/*
626321936Shselasky	 * Our own port - this is the only case where CA port
627321936Shselasky	 * is discovered before its' neighbor port
628321936Shselasky	 */
629321936Shselasky	if (!p_rem_physp)
630321936Shselasky		return p_physp->need_update;
631321936Shselasky
632321936Shselasky	p_node = osm_physp_get_node_ptr(p_rem_physp);
633321936Shselasky	CL_ASSERT(p_node);
634321936Shselasky
635321936Shselasky	/* CA/RTR to CA/RTR connection */
636321936Shselasky	if (!p_node->sw)
637321936Shselasky		return p_physp->need_update;
638321936Shselasky
639321936Shselasky	return (ib_switch_info_get_state_change(&p_node->sw->switch_info) ? 1 : p_physp->need_update);
640321936Shselasky}
641321936Shselasky
642321936Shselaskyvoid osm_pi_rcv_process(IN void *context, IN void *data)
643321936Shselasky{
644321936Shselasky	osm_sm_t *sm = context;
645321936Shselasky	osm_madw_t *p_madw = data;
646321936Shselasky	ib_port_info_t *p_pi;
647321936Shselasky	ib_smp_t *p_smp;
648321936Shselasky	osm_port_t *p_port;
649321936Shselasky	osm_physp_t *p_physp;
650321936Shselasky	osm_dr_path_t *p_dr_path;
651321936Shselasky	osm_node_t *p_node;
652321936Shselasky	osm_pi_context_t *p_context;
653321936Shselasky	ib_net64_t port_guid, node_guid;
654321936Shselasky	uint8_t port_num;
655321936Shselasky
656321936Shselasky	CL_ASSERT(sm);
657321936Shselasky
658321936Shselasky	OSM_LOG_ENTER(sm->p_log);
659321936Shselasky
660321936Shselasky	CL_ASSERT(p_madw);
661321936Shselasky
662321936Shselasky	p_smp = osm_madw_get_smp_ptr(p_madw);
663321936Shselasky	p_context = osm_madw_get_pi_context_ptr(p_madw);
664321936Shselasky	p_pi = ib_smp_get_payload_ptr(p_smp);
665321936Shselasky
666321936Shselasky	CL_ASSERT(p_smp->attr_id == IB_MAD_ATTR_PORT_INFO);
667321936Shselasky
668321936Shselasky	/*
669321936Shselasky	 * Attribute modifier has already been validated upon MAD receive,
670321936Shselasky	 * which means that port_num has to be valid - it originated from
671321936Shselasky	 * the request attribute modifier.
672321936Shselasky	 */
673321936Shselasky	port_num = (uint8_t) cl_ntoh32(p_smp->attr_mod);
674321936Shselasky
675321936Shselasky	port_guid = p_context->port_guid;
676321936Shselasky	node_guid = p_context->node_guid;
677321936Shselasky
678321936Shselasky	osm_dump_port_info_v2(sm->p_log, node_guid, port_guid, port_num, p_pi,
679321936Shselasky			      FILE_ID, OSM_LOG_DEBUG);
680321936Shselasky
681321936Shselasky	/* On receipt of client reregister, clear the reregister bit so
682321936Shselasky	   reregistering won't be sent again and again */
683321936Shselasky	if (p_context->set_method &&
684321936Shselasky	    (ib_port_info_get_client_rereg(p_pi) || p_context->client_rereg)) {
685321936Shselasky		OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
686321936Shselasky			"Client reregister received on response\n");
687321936Shselasky		ib_port_info_set_client_rereg(p_pi, 0);
688321936Shselasky		p_context->client_rereg = FALSE;
689321936Shselasky	}
690321936Shselasky
691321936Shselasky	/*
692321936Shselasky	   we might get a response during a light sweep looking for a change in
693321936Shselasky	   the status of a remote port that did not respond in earlier sweeps.
694321936Shselasky	   So if the context of the Get was light_sweep - we do not need to
695321936Shselasky	   do anything with the response - just flag that we need a heavy sweep
696321936Shselasky	 */
697321936Shselasky	if (p_context->light_sweep == TRUE) {
698321936Shselasky		OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
699321936Shselasky			"Got light sweep response from remote port of parent node "
700321936Shselasky			"GUID 0x%" PRIx64 " port 0x%016" PRIx64
701321936Shselasky			", Commencing heavy sweep\n",
702321936Shselasky			cl_ntoh64(node_guid), cl_ntoh64(port_guid));
703321936Shselasky		sm->p_subn->force_heavy_sweep = TRUE;
704321936Shselasky		sm->p_subn->ignore_existing_lfts = TRUE;
705321936Shselasky		goto Exit;
706321936Shselasky	}
707321936Shselasky
708321936Shselasky	CL_PLOCK_EXCL_ACQUIRE(sm->p_lock);
709321936Shselasky	p_port = osm_get_port_by_guid(sm->p_subn, port_guid);
710321936Shselasky	if (PF(!p_port)) {
711321936Shselasky		CL_PLOCK_RELEASE(sm->p_lock);
712321936Shselasky		OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F06: "
713321936Shselasky			"No port object for port with GUID 0x%" PRIx64
714321936Shselasky			"\n\t\t\t\tfor parent node GUID 0x%" PRIx64
715321936Shselasky			", TID 0x%" PRIx64 "\n",
716321936Shselasky			cl_ntoh64(port_guid),
717321936Shselasky			cl_ntoh64(node_guid), cl_ntoh64(p_smp->trans_id));
718321936Shselasky		goto Exit;
719321936Shselasky	}
720321936Shselasky
721321936Shselasky	p_node = p_port->p_node;
722321936Shselasky	CL_ASSERT(p_node);
723321936Shselasky
724321936Shselasky	if (PF(p_pi->local_port_num > p_node->node_info.num_ports)) {
725321936Shselasky		CL_PLOCK_RELEASE(sm->p_lock);
726321936Shselasky		OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F15: "
727321936Shselasky			"Received PortInfo for port GUID 0x%" PRIx64 " is "
728321936Shselasky			"non-compliant and is being ignored since the "
729321936Shselasky			"local port num %u > num ports %u\n",
730321936Shselasky			cl_ntoh64(port_guid), p_pi->local_port_num,
731321936Shselasky			p_node->node_info.num_ports);
732321936Shselasky		goto Exit;
733321936Shselasky	}
734321936Shselasky
735321936Shselasky	/*
736321936Shselasky	   If we were setting the PortInfo, then receiving
737321936Shselasky	   this attribute was not part of sweeping the subnet.
738321936Shselasky	   In this case, just update the PortInfo attribute.
739321936Shselasky
740321936Shselasky	   In an unfortunate blunder, the IB spec defines the
741321936Shselasky	   return method for Set() as a GetResp().  Thus, we can't
742321936Shselasky	   use the method (what would have been SetResp()) to determine
743321936Shselasky	   our course of action.  So, we have to carry this extra
744321936Shselasky	   boolean around to determine if we were doing Get() or Set().
745321936Shselasky	 */
746321936Shselasky	if (p_context->set_method)
747321936Shselasky		pi_rcv_process_set(sm, p_node, port_num, p_madw);
748321936Shselasky	else {
749321936Shselasky
750321936Shselasky		/*
751321936Shselasky		   This PortInfo arrived because we did a Get() method,
752321936Shselasky		   most likely due to a subnet sweep in progress.
753321936Shselasky		 */
754321936Shselasky		OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
755321936Shselasky			"Discovered port num %u with GUID 0x%" PRIx64
756321936Shselasky			" for parent node GUID 0x%" PRIx64
757321936Shselasky			", TID 0x%" PRIx64 "\n",
758321936Shselasky			port_num, cl_ntoh64(port_guid),
759321936Shselasky			cl_ntoh64(node_guid), cl_ntoh64(p_smp->trans_id));
760321936Shselasky
761321936Shselasky		p_physp = osm_node_get_physp_ptr(p_node, port_num);
762321936Shselasky
763321936Shselasky		CL_ASSERT(p_physp);
764321936Shselasky
765321936Shselasky		/* Update the directed route path to this port
766321936Shselasky		   in case the old path is no longer usable. */
767321936Shselasky		p_dr_path = osm_physp_get_dr_path_ptr(p_physp);
768321936Shselasky		osm_dr_path_init(p_dr_path, p_smp->hop_count,
769321936Shselasky				 p_smp->initial_path);
770321936Shselasky
771321936Shselasky		p_physp->need_update = osm_pi_rcv_update_self(sm, p_physp, p_pi);
772321936Shselasky
773321936Shselasky		switch (osm_node_get_type(p_node)) {
774321936Shselasky		case IB_NODE_TYPE_CA:
775321936Shselasky		case IB_NODE_TYPE_ROUTER:
776321936Shselasky			if (!p_node->physp_discovered[port_num]) {
777321936Shselasky				p_port->discovery_count++;
778321936Shselasky				p_node->physp_discovered[port_num] = 1;
779321936Shselasky			}
780321936Shselasky			p_physp->need_update = osm_pi_rcv_update_neighbor(p_physp);
781321936Shselasky			pi_rcv_process_ca_or_router_port(sm, p_node, p_physp,
782321936Shselasky							 p_pi);
783321936Shselasky			break;
784321936Shselasky		case IB_NODE_TYPE_SWITCH:
785321936Shselasky			if (!p_node->physp_discovered[port_num]) {
786321936Shselasky				p_port->discovery_count++;
787321936Shselasky				p_node->physp_discovered[port_num] = 1;
788321936Shselasky			}
789321936Shselasky			if (port_num == 0)
790321936Shselasky				pi_rcv_process_switch_port0(sm, p_node,
791321936Shselasky							    p_physp, p_pi);
792321936Shselasky			else
793321936Shselasky				pi_rcv_process_switch_ext_port(sm, p_node,
794321936Shselasky							       p_physp, p_pi);
795321936Shselasky			break;
796321936Shselasky		default:
797321936Shselasky			OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F07: "
798321936Shselasky				"Unknown node type %u with GUID 0x%" PRIx64
799321936Shselasky				"\n", osm_node_get_type(p_node),
800321936Shselasky				cl_ntoh64(node_guid));
801321936Shselasky			break;
802321936Shselasky		}
803321936Shselasky
804321936Shselasky		/*
805321936Shselasky		   Get the tables on the physp.
806321936Shselasky		 */
807321936Shselasky		if (p_physp->need_update || (p_node->sw &&
808321936Shselasky					     p_node->sw->need_update))
809321936Shselasky			pi_rcv_get_pkey_slvl_vla_tables(sm, p_node, p_physp);
810321936Shselasky
811321936Shselasky	}
812321936Shselasky
813321936Shselasky	CL_PLOCK_RELEASE(sm->p_lock);
814321936Shselasky
815321936ShselaskyExit:
816321936Shselasky	/*
817321936Shselasky	   Release the lock before jumping here!!
818321936Shselasky	 */
819321936Shselasky	OSM_LOG_EXIT(sm->p_log);
820321936Shselasky}
821